99

Concurrent visitors over the last 30 minutes. (~)

Peak concurrent visitors -
Yesterday: 0 / Today: 0

GitHub Copilot Essentials

GitHub Copilot Essentials

GitHub Copilot is an AI coding assistant provided by GitHub.
It is built into VS Code, so while you type code or documents it suggests what comes next, answers questions in chat, and even handles tasks like creating files and running commands for you.

This post is based on VS Code 1.143.
Starting with 1.143, the default way chat runs changed to the Copilot harness, so the screens and some behaviors differ from earlier versions.
You can find the example repository at github-copilot-example.

Let's start by looking at the difference between a chatbot and an agent.
A chatbot takes a question, returns one answer, and stops.
An agent takes a goal, plans what to do, uses tools to read files or run commands, checks the results to decide the next action, and repeats this until the work is done.

The difference between a chatbot and an agent

Copilot offers both.
If you only want to ask questions, pick Ask and use it like a chatbot. To hand off work like creating files and running scripts, use Agent.
Most examples in this post run in Agent.

You need a GitHub account to use Copilot, so it is a good idea to sign up and sign in beforehand.
You can start with the Free plan. Depending on the plan, the number of inline suggestions per month, AI credits, and the models you can choose differ.

Plan Price Features
Copilot Free Free 2,000 inline suggestions and a set amount of credits per month, only the Auto model
Copilot Pro $10/month 1,500 credits per month, choice of multiple models
Copilot Pro+ $39/month 7,000 credits per month, access to top models
Copilot Max $100/month 20,000 credits per month, priority access to top models
Copilot Business $19/user/month Managed per organization, 1,900 credits per user shared by the organization
Copilot Enterprise $39/user/month Managed per organization, 3,900 credits per user shared by the organization

Credits are the unit used for chat and agent requests, and how many you use depends on the model and the size of the request.
For usage after you run out of credits, you set a budget in dollars, and 1 credit is counted as $0.01.
Plans and credit policies change often, so also check the plans documentation.

# Installation and sign-in

Install and launch VS Code.
Copilot is built into VS Code, so you don't need to install a separate extension.

When you launch it for the first time, the Welcome to VS Code screen appears.
Click Continue with GitHub to open the GitHub sign-in page in your browser. After you sign in and authorize, you return to VS Code.
Accounts without a subscription are automatically signed up for the Free plan.

The sign-in prompt shown on first launch

The first screen is shown in English.
To use Korean menus, click the Extensions icon in the activity bar on the left (Cmd + Shift + X, or Ctrl + Shift + X on Windows), search for Korean, install the Korean Language Pack, and restart VS Code as prompted.
All the remaining screens in this post have the Korean language pack applied.
The language pack is updated later than VS Code, so some newly added menus appear in English even in the Korean UI.

If you already had VS Code installed and were using it without signing in, or clicked Continue without Signing In on the first screen, this sign-in prompt will not appear again.
In that case, first update VS Code to the latest version.
On macOS, click Code > Check for Updates... in the top menu, or on Windows, Help > Check for Updates.... If a new version is available, restart VS Code as prompted.

When you are not signed in, a blue Sign In button appears to the right of the search box in the title bar at the top.
Click it to open the Sign in to use GitHub Copilot. dialog. Click Continue with GitHub, and just like during the first install, you sign in to GitHub in your browser, authorize, and return to VS Code.

The dialog opened from the Sign In button in the title bar while signed out

You can also open the same dialog by clicking the Copilot icon in the status bar at the bottom right, or by clicking the account icon at the bottom left and choosing Sign in to use GitHub Copilot....
If you send a request in chat without signing in, you may see a Language model unavailable error instead of a response. If you see this error, check your sign-in status first.

Once you are signed in, clicking the Copilot icon in the status bar shows your current plan, this month's credit usage, and the date your usage resets.
Expand Inline Suggestions to turn each type of suggestion on or off, and click Snooze to hide suggestions for 5 minutes.
Click the account icon at the bottom left to check the connected GitHub account or sign out.

The Copilot menu in the status bar

If you need to turn off AI features completely, for example because of company policy, turn on chat.disableAIFeatures in settings.
Both chat and inline suggestions are hidden.
On the other hand, if you can't see the Sign In button, the Copilot icon in the status bar, or the chat view, this setting may be turned on.
Search for chat.disableAIFeatures in settings (Cmd + ,, or Ctrl + , on Windows) and uncheck it, and the sign-in button appears again.

# Preparing the example project

The example is set in an R&D team that builds a fictional temperature sensor module, the TS-100.
The team has recurring work such as frequently asked questions, laptop requests, cleaning up meeting notes, receiving and triaging issues, and weekly reports, and the goal is to hand this work off to Copilot.

You need Node.js to run the example scripts.
Node.js is a program that runs .js script files. Install version 22 or later.
For installation steps, see Node.js (NPM) Installation and NVM Version Management Guide.
The scripts use only built-in Node.js features, so there are no packages to install.

You can get the example repository in two ways.
If you use Git, run the following commands in a terminal.

BASH
1
2
3
git clone https://github.com/ParkYoungWoong/github-copilot-example.git cd github-copilot-example code .
Clone the example repository

If you are not familiar with Git, click Code > Download ZIP on the repository page to download the archive, then open the extracted folder with File > Open Folder in VS Code.

When you open a folder for the first time, a dialog asks Do you trust the authors of the files in this folder?.
You downloaded this example yourself, so choose Yes, I trust the authors.
If you close the dialog and Restricted Mode appears in the status bar, you can click that text to change it to Trust.

To open the terminal below the editor, click View > Terminal in the top menu of VS Code.
Wherever this post runs a command like node scripts/triage.js, you can let Copilot run it instead of typing it yourself.

The repository is structured as follows.
The files in the .github folder are settings that shape how Copilot behaves, and docs and data hold the reference documents and source data the assistant uses.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
github-copilot-example/ ├── .github/ │ ├── copilot-instructions.md # Team rules applied to every request │ ├── instructions/ # Rules applied only to specific files │ ├── agents/ # Custom agent dedicated to issue triage │ ├── skills/ # team-faq, laptop-request, meeting-notes, issue-triage, │ │ # weekly-report, bug-report skills │ └── hooks/ # Hook that blocks reading secret files ├── .vscode/ │ └── settings.json # Workspace settings ├── docs/ # FAQ, policies, triage rules, product spec, report templates ├── data/ # Issue list (CSV), meeting memos ├── scripts/ # Triage, report, and check scripts ├── tests/cases.json # Validation set └── output/ # Results created by the assistant

The order of this post follows the hands-on steps below.

Step Content Related files
1 Team FAQ assistant .github/skills/team-faq/
2 Filling out a laptop request form .github/skills/laptop-request/
3 Cleaning up meeting notes and asking follow-up questions .github/skills/meeting-notes/
4 Asking about the spec document and checking sources docs/spec-ts100.md
5 Investigation plan for a bug report .github/skills/bug-report/
6 Generating an issue triage table scripts/triage.js, docs/issue-rules.md
7 Generating reports automatically scripts/report.js, .github/skills/weekly-report/
8 Validation set and permissions scripts/check.js, tests/cases.json, .github/hooks/
9 Putting it together and an adoption plan docs/quality-checklist.md, docs/adoption-plan-template.md

# Inline suggestions

When you type code or a document in the editor, the next content appears in gray text at the cursor.
This is called inline suggestions or ghost text.
Press Tab to accept the suggestion as is. If you keep typing, the suggestion disappears.

If you first write a comment describing what to build, Copilot suggests matching code below it.
Here, a new file has been created in the scripts folder with only a comment on the first line.

Ghost text shown below a comment

Action macOS Windows
Accept the whole suggestion Tab Tab
Accept one word at a time Cmd + Right Arrow Ctrl + Right Arrow
Show next suggestion Option + ] Alt + ]
Show previous suggestion Option + [ Alt + [
Dismiss suggestion Esc Esc

Suggestions are based on the open files and the content of the current file, so keeping related files open can lead to better suggestions.
On the other hand, suggestions are not always right.
In the screen above, the example project uses import syntax, but Copilot suggested require.
Treat suggestions as drafts, and get in the habit of checking the content after you accept them.

When you start editing accepted code, next edit suggestions kick in.
If you rename a variable, Copilot predicts that other lines using the same name also need to change, shows an arrow in the left margin of the editor, and previews the change.
Press Tab to move to that location, and press it again to apply the change.

Copilot suggesting the next edit location after a variable is renamed

Inline suggestions are turned off by default in Markdown files.
If you work with documents a lot, check Ghost text suggestions for Markdown under Inline Suggestions in the Copilot menu in the status bar.
The example repository already turns this on in its workspace settings.

/.vscode/settings.json
JSON
1
2
3
4
5
6
{ "github.copilot.enable": { "*": true, "markdown": true } }

# Inline chat

Inline chat lets you type a request right inside the editor.
Open a file and press Cmd + I (Ctrl + I on Windows), and an input box appears at the cursor.
If you select part of the code before opening it, the request applies only to that range.

The inline chat input box shown inside the editor

We sent the following request to the file created earlier.

PROMPT
1
Switch from require to ES module import syntax, and print the count for each priority as a table

The result is shown in the editor as a before-and-after comparison (diff).
Green lines are added, and red lines are removed.
Click Keep to apply it, or Undo to go back to the original.

Inline chat result showing a comparison of the changes

You can also open inline chat in the terminal with the same shortcut.
Even if you don't remember commands, describe what you want in words and Copilot suggests a command.
Click Run to run it right away, or Insert to only put the command in the terminal so you can edit it before running.

PROMPT
1
A command that lists the files in the output folder along with their sizes

A command suggested by terminal inline chat

# Chat view

The chat view is the main screen for talking with Copilot.
You can open and close it with Control + Cmd + I (Ctrl + Alt + I on Windows), and it appears in the secondary sidebar to the right of the editor.
At the top is a list of past sessions, and at the bottom is the input box where you type requests.

The chat view and the input box

Frequently used controls are gathered in and below the input box.

Location Name Role
+ on the left of the input box Add context Attach files, folders, previous sessions, and more to the request
Agent Agent picker Choose among Agent, Ask, and custom agents
Auto Model picker Choose the language model that generates the response
Copilot Harness picker Choose how the agent runs. Shown only before you start a new session
Interactive Mode picker Choose among Interactive, Plan, and Autopilot
Manual permissions Permission picker Decide whether to require approval before running tools
+ at the top New chat Start a new session
Open Customizations at the top Agent customization Manage instructions, skills, agents, hooks, MCP servers, and tools

# Harness

A harness is the way a language model is connected to tools and instructions so that it can actually get work done.
Starting with VS Code 1.143, new sessions use the Copilot harness by default.
The Copilot harness runs on the same foundation (the Copilot SDK) as the Copilot CLI and the GitHub Copilot app, and work can continue even if you close the VS Code window during a task.
If the chat title starts with Copilot, the session uses the Copilot harness.

The harness menu also includes Local, the approach from earlier versions, Cloud, which works on GitHub's servers, and Claude and Codex, which are other companies' approaches.
The files each harness reads and the features available differ, so this post is based on the default Copilot harness.
Unless you have a specific reason, you don't need to change it. The Claude and Codex harnesses are covered separately in Using Claude and Codex later.

# Agents and modes

Click the Agent button to see the two built-in agents together with the custom agents defined in the repository.
Press Shift + Cmd + I (Ctrl + Shift + I on Windows) to pick Agent directly.

The agent picker menu

Agent What it does When to use it
Ask Only answers questions. It uses only read tools and does not change files Explaining concepts, checking document contents
Agent Reads, creates, and edits files, and also runs terminal commands Most tasks that need to produce a result

Click Interactive below the input box to open a menu where you choose both how the work proceeds (the mode) and the permissions.

The mode and permission picker menu

Mode What it does
Interactive Proceeds one step at a time with you. This is the default
Plan Investigates without changing files, makes a plan first, and runs it once you approve
Autopilot Keeps going on its own until it decides the work is done. Runs all tools without approval

You can also start in Plan mode by typing /plan together with the task in the input box.
Autopilot edits files and runs commands without asking, so use it only for tasks you understand well.
Permissions are covered in Tools and approval later.

# Models

In the model picker, you choose the language model that generates the response.
The default, Auto, picks a model automatically based on the task and the system status.
Click Balance next to Auto to choose what to prioritize: Efficiency, Balance, or Intelligence. The screen says requests handled with Balance get a 10% discount.
The models you can choose depend on your plan, and models you can't use on your current plan show Upgrade.
The model names shown in the list may change over time.

Starting with VS Code 1.144, Copilot Fusion may appear in the same spot instead of Auto.
Fusion picks one model depending on the request, or uses several models together, for example one model writes a draft and another reviews it.
Because it goes through several models, it can use more credits, so when you use Fusion, start with Standard, the lowest level.

Copilot is not a service that runs on a single model of its own. It borrows models from several companies and gathers them in one menu.
So even if you pick a Claude model from the menu, the cost comes out of your Copilot credits.
How to use a ChatGPT or Claude subscription you already have is covered in Using Claude and Codex later.

The model picker menu

When a response finishes, the model used and the credits consumed are shown below it.
When we ran the examples in this post with Auto, most requests used 0.1 to 0.5 credits, and choosing Claude Sonnet 5.5 directly used 3 to 6.5 credits for a single short answer.
It is a good idea to leave simple requests on Auto and choose a top model only when the result falls short.

# Context

Copilot answers based on what is sent with the request and on files the agent finds and reads on its own.
Files open in the editor show their names above the input box and are sent automatically. To make Copilot look at a specific file or folder, attach it yourself.
The information sent along with a request like this is called context.

The add context menu

There are several ways to attach context.

  • Click + on the left of the input box and choose files and folders, previous sessions, or GitHub issues and pull requests.
  • Drag a file from the Explorer into the input box.
  • Type # in the input box and choose a file name.

Even without attachments, the agent searches the repository and reads related files on its own when needed.
To have it refer to a web page, write the address directly in the request.

Attached files are not the only thing sent with a single request.
The instruction files covered later are always sent, and skills are read and sent only when needed.
The conversation so far is also sent every time, so when the topic changes, starting a new chat gives better results and saves credits.

What is sent with a single request

# Sessions and undo

Click + at the top to start a new session. Previous sessions stay in the list, and you can reopen them at any time.
Session titles are set automatically based on the first request, and sessions that changed files also show the number of changed lines.

The Copilot harness saves files as soon as it edits them.
When changes happen, the number of changed files and lines appears above the input box, like 1 File +45 -0. Click it to compare everything changed in this session on one screen.
Green lines were added, and red lines were removed.

Comparing the changes made in a session

If you don't like the result, undo it with the button that appears when you hover over a request.
On the first request it appears as Start Over, which clears the chat and reverts all changes made after that request.
However, undo only reverts files and the conversation. It cannot undo commands that already ran or content already sent to external services.

The Start Over button shown when you hover over a request

Starting with VS Code 1.144, the working set, which shows Keep and Undo buttons for each change, is turned on by default in the Copilot harness as well.
It is still an experimental feature. To see the changes gathered on one screen as in this post, turn off chat.editor.workingSet.enabled in settings.
Files changed by terminal commands do not appear in the working set.

# Tools and approval

When Agent receives a request, it repeats the cycle of making a plan, using tools, and checking the results.
Your role is only to approve things or change direction along the way.
If you send a message in the input box during a task, it waits in a queue and is applied in order, so you can change direction even while work is in progress.

# Tools

You can check the tools the agent can use under Tools in the agent customization editor.
Click the Open Customizations icon at the top of the chat view and choose Tools.
The Copilot group contains the built-in tools of the Copilot harness (running commands, reading and editing files, searching, and so on) and is always available. The groups below it, such as VS Code, tasks and problems, and the integrated browser, can be turned on and off with switches.
Tools from the MCP servers covered later are also added to this list.

The list of tools the agent can use

Settings you change here apply not only to the current session but to all future sessions.
To limit tools only for a specific task, choose Ask, or specify the tools to use in a custom agent, which we will build later.

# Approval

Tools that read files or search, and tools that edit files inside the project folder, run without approval.
Tools that can affect your computer, such as terminal commands, show what they will run and wait for approval first.
The Copilot harness sometimes hands command execution to a helper agent, so Subagent may appear next to the title of the approval request.

The approval request shown before running a terminal command

Button Behavior
Allow in this Session Runs the same tool (here, all terminal commands) without asking until this chat ends
Arrow, Allow Once Runs it this one time only
Arrow, Allow Exact Command Line in this Workspace Runs the exact same command without asking again in this folder
Skip Moves on without running it

The options shown when you click the arrow on the approval button

The blue default button is Allow in this Session, so it is easy to just click it, but you can't know in advance which commands will run afterward.
For hands-on practice, we recommend clicking the arrow and choosing Allow Once.

You can also change the approval behavior for the whole session in the permission menu below the input box.

Permission Behavior
Manual permissions The default. Actions not covered by auto-approval rules require confirmation
Assisted permissions The language model judges the risk of each tool call and asks for confirmation only for those it does not approve
Allow all Runs all tools without confirmation

Allow all and Autopilot run even risky actions like deleting files without asking, so use them only in folders you understand well.
How to set approval rules in settings is covered in Validation and permissions later.

After producing a result, the agent sometimes asks for approval to run a check command.
If the check is unnecessary, click Skip. If this keeps happening, add a rule to the instruction file such as "After creating a result file, do not repeatedly run separate check commands."

# Using Claude and Codex

If you already use Claude or ChatGPT, you may wonder whether you can use that subscription in VS Code too.
As we saw earlier, Copilot is a service that borrows models from several companies, and what you pick in the input box and where the cost comes from work separately, so it is easy to get confused.
It becomes clear when you look at the following three things separately.

Item Meaning Where to choose
Model The AI that generates answers The model menu in the input box (Auto)
Harness The way the model is given tools and put to work The harness menu below the input box (Copilot)
Billing account Where the cost comes from The tabs at the top of the model menu (Copilot, ChatGPT, etc.)

How the model, harness, and billing account relate

Depending on the account you have, use it as follows.

Account you have How to use it Where the cost comes from
Copilot plan Choose Claude, GPT, etc. from the model menu, or select Claude as the harness Copilot credits
ChatGPT account Choose Codex as the harness and pick a model from the ChatGPT tab in the model menu ChatGPT subscription
Claude Pro, Max, or Ultra subscription Use the Claude Code extension instead of Copilot Claude subscription
API key from each company Register the key in the language models screen That company's API fees

In company-managed Copilot (Business, Enterprise), the Claude and Codex harnesses and API key connections may be turned off by policy.
If you don't see them in the menu, check with your company's administrator.

# Claude harness

The Claude harness works the same way as Claude Code (the Claude Agent SDK).
The cost comes out of your Copilot credits, so you can use it with just a Copilot plan.

  1. Open a new chat with + at the top of the chat view.
  2. Click Copilot below the input box to open the harness menu, and choose Claude.
  3. The first time you choose it, a Download the Claude Agent prompt appears. Click Download to download the required files.
  4. Choose a Claude model from the model menu and send a request.

The harness picker menu

The model menu shows only the Copilot tab.
This means that even with the Claude harness, the model is borrowed through Copilot and the cost comes out of your Copilot credits.

The model menu of the Claude harness

When you send a request, Claude appears before the chat title, and the model used and the credits are shown below the response.

A response from the Claude harness

Keep two things in mind when using the Claude harness.

  • It can use more credits. One short question we tested for this post used 6.1 credits, far more than a similar FAQ question asked with Auto in the Copilot harness (0.2 credits).
  • It follows Claude Code's rules. It reads CLAUDE.md and the .claude folder, and the .github skills in this post's example did not appear in the / list. The assistant examples in this post run in the Copilot harness.

# Codex harness

The Codex harness works the way OpenAI's Codex does.
If you connect a ChatGPT account, the cost is covered by your ChatGPT subscription, and you can use it even with a free ChatGPT account.

  1. Choose Codex from the harness menu. The first time you choose it, a Download the Codex Agent prompt appears. Click Download.
  2. Connect a ChatGPT account. If you are already signed in to the Codex CLI with a ChatGPT account, that account is connected without a separate sign-in. If not, click Sign in to ChatGPT in the account menu at the bottom left.
  3. Open the model menu, and a ChatGPT tab appears next to the Copilot tab. When you choose a model from the ChatGPT tab, (ChatGPT) is added after the model name.
  4. Send a request.

The ChatGPT tab added to the model menu of the Codex harness

Below the response, only the model name is shown, with no credits.
This means it was covered by the ChatGPT subscription, not Copilot credits.
The Codex harness also reads the skills in the .github folder, and as shown below, it answered with sources attached, following the rules of the team-faq skill.

A response from the Codex harness

If Codex is not in the harness menu, turn on chat.agentHost.codexAgent.enabled and chat.editor.codex.preferAgentHost in settings and restart VS Code.
It is still an experimental feature, so the setting names and steps may change. You can also install and use OpenAI's Codex extension.
If you choose a model from the Copilot tab in the Codex harness, it is charged to your Copilot credits, and in that case you need Copilot Pro+ or higher.

# Claude subscriptions and API keys

Claude Pro, Max, and Ultra subscriptions cannot be connected to Copilot.
The VS Code documentation lists only two billing options for the Claude harness: a Copilot plan and an API key.
To work in VS Code with a Claude subscription, install the Claude Code extension provided by Anthropic.
It runs as a separate panel from Copilot chat. For details, see Claude Code Essentials.

If you have an API key from a company, click + at the top of the model menu to open the language models screen, choose the company under Add Models, and enter the key.
You can choose Anthropic, Azure, Google, OpenAI, OpenRouter, xAI, and others, and registered models are charged to that company's API fees.
This screen also shows the credit cost of each model, but models connected through a ChatGPT account show no credit cost.

The language models screen and the Add Models menu

Even if you connect models with an API key, inline suggestions and semantic repository indexing still need the Copilot service.
To use API key models in the Copilot harness, chat.agentHost.byokModels.enabled must also be turned on in settings.

# Customization files

So far, we have looked at using Copilot as needed, one request at a time.
Building an assistant means saving rules and procedures as files so you don't have to explain them every time.
Copilot reads the following four types of customization files from the repository's .github folder.

How the four types of customization files work

Type File When it applies
Instructions .github/copilot-instructions.md, .github/instructions/*.instructions.md Automatically with every request (.instructions.md when working on files that match applyTo)
Skills .github/skills/*/SKILL.md When judged relevant to the request, or when called with /name
Custom agents .github/agents/*.agent.md When selected in the agent menu
Hooks .github/hooks/*.json At set points, such as right before a tool runs

Except for hooks, these are all Markdown files, so you can write them without programming knowledge.
Click the Open Customizations icon at the top of the chat view to open the agent customization editor, where you manage these files in one place.
If you create a new file with the + button on each screen, it is created in the right location with the right format.

The Skills screen of the agent customization editor

In the Korean UI, skills appear as 기술 and hooks as 후크.
They are the same features, so don't confuse them.

Prompt files used in earlier versions (.github/prompts/*.prompt.md) are not read by the Copilot harness.
If you open a folder with settings in the old format, a migration notice appears above the chat input box, and you can click Review Migrations to move them to new formats such as skills.
In this post's example, we also turned two prompt files into skills you call directly.

Besides the .github folder, the Copilot harness also reads files from other AI tools, such as AGENTS.md, .claude/skills, and .agents/skills.
CLAUDE.md is read when you choose the Claude harness.
If your team also uses Claude Code, both tools can share the same skills folder. For details, see Claude Code Essentials.

# Instruction files

Instructions are rules that are automatically attached to every request.
Write here what your team would otherwise have to repeat every time, such as the answer language, where to save results, files not to touch, and how to handle sensitive information.

The example's instruction file looks like this.

/.github/copilot-instructions.md
MARKDOWN
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# Work rules for the R&D team assistant This repository is an example of handing off the R&D team's repetitive work to GitHub Copilot. The rules below apply to every request. ## How to answer - Always answer in Korean. Write code comments in Korean too. - When answering based on documents, also show which item in which file you referred to. - If there is no document to back up the answer, do not guess. Say it is "not in the documents" and recommend checking with the person in charge. - If required information is missing, ask about all the missing items at once before starting the work. - After creating a result file, read it again only once to check, and do not repeatedly run separate check commands. ## File rules - Save generated results (forms, meeting notes, triage tables, reports) in the `output/` folder. - Documents in the `docs/` folder are reference documents. Do not edit them unless asked. - Do not edit the source data in the `data/` folder. ## Code rules - Scripts use only built-in Node.js modules. Do not install external packages. - Triage logic follows `docs/issue-rules.md`. Do not make up criteria that are not in the rules. - Always record the reason along with the triage result. If unsure, mark it as untriaged. ## Sensitive information - Do not write full names, employee IDs, phone numbers, or email addresses in results. Keep them as they appear in the source (`김OO`). - Confirm with the user first before sending data to external services or uploading it to the internet. - Do not read or print `.env` files or secret values.

Here are some guidelines that help when writing them.

  • Write the reason for each rule. If Copilot knows the reason, it can make sensible decisions even in exceptional cases.
  • Don't write what tools already enforce (indentation, quotes, and so on). Focus on what Copilot can only know if a person tells it.
  • Keep it short. This file is attached to every request, so the longer it gets, the more credits each request uses.

Rules that apply only to specific files go in separate .instructions.md files in the .github/instructions folder.
They apply only when working on files that match the pattern in applyTo in the front matter.

/.github/instructions/scripts.instructions.md
MARKDOWN
1
2
3
4
5
6
7
8
9
10
11
--- name: Script writing rules description: Rules applied when creating or editing Node.js scripts in the scripts folder applyTo: "scripts/**/*.js" --- - Use ES module (`import`) syntax, and use only built-in modules such as `node:fs` and `node:path`. - Keep input and output file paths as constants at the top of the file. - When finished, print a summary of the number of items processed and the result file path to the console. - If an input file is missing, explain what needs to be run first and exit. - Add a one-line Korean comment to each function describing what it does.

You can check whether instruction files are recognized on the Instructions screen of the agent customization editor.
In the example, the two files above are shown.
If you are starting a new repository, you can also type /init in chat to get a draft of instructions based on an analysis of the repository.

# Skills

A skill puts the steps and reference material for a specific task into one folder.
If instructions say "always do it this way," a skill says "when this kind of request comes in, follow these steps."
Create a folder named after the skill under .github/skills, and put a SKILL.md file inside it.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
.github/skills/ ├── team-faq/ │ └── SKILL.md ├── laptop-request/ │ ├── SKILL.md │ └── form.md # Form that the skill refers to ├── meeting-notes/ │ └── SKILL.md ├── issue-triage/ │ └── SKILL.md ├── weekly-report/ │ └── SKILL.md # Runs only when called as /weekly-report └── bug-report/ └── SKILL.md # Runs only when called as /bug-report

SKILL.md has two parts: the frontmatter and the body.
In the frontmatter, name is the identifier you use to call the skill as /name, and description is what Copilot uses to decide when to use the skill.
Use only lowercase letters, numbers, and hyphens for name, and make it the same as the folder name.

At first, Copilot reads only the name and description of every skill.
When a request matches a skill's description, it reads the body, and it reads files referenced in the body only when it actually needs them.
This is why the amount sent with each request does not grow much even when you have many skills.
So write the description clearly, with both "what it does" and "when to use it."

When you type / in the chat, the repository's skills appear with their descriptions, along with the built-in commands.
Keep typing the start of a skill name to narrow the list.

Skills shown in the slash list

# Team FAQ assistant

The first skill is an assistant that answers questions team members ask again and again.
Its answers are based only on docs/faq.md, and it must not make up anything that is not in the document.

/.github/skills/team-faq/SKILL.md
MARKDOWN
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
--- name: team-faq description: Use when answering questions team members ask often, such as team rules, equipment booking, firmware deployment, and work requests, based on docs/faq.md. --- # Team FAQ assistant Answer team members' questions based on `docs/faq.md`. ## Steps 1. Read `docs/faq.md`. 1. Find the item closest to the question and answer with its content. 1. At the end of the answer, show the title of the item you used in the format `Source: docs/faq.md > Item title`. ## Rules - Do not make up anything that is not in the FAQ. Say "This is not in the FAQ" and suggest checking with the group lead. - If a question covers several items, answer each item separately. - Answer questions about procedures with an ordered list.

In Agent mode, type the skill name together with your question.

PROMPT
1
/team-faq If environmental chamber bookings overlap, who gets priority? And what is the maximum booking length in hours?

Copilot reads the skill, answers each item based on the FAQ document, and shows the source at the end.

Answering with sources based on the FAQ document

Try asking a question that is not in the FAQ, too.
If it answers that it is "not in the document" and suggests checking with the person in charge, the skill is following its rules.

# Laptop request form

The second skill collects the needed information from a request, fills in a form, and saves it as a file.
The form is form.md inside the skill folder, and the policy comes from docs/laptop-policy.md.

/.github/skills/laptop-request/SKILL.md
MARKDOWN
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
--- name: laptop-request description: Use when you get a request for a new laptop, a replacement, or a replacement for a broken one, to check the policy, fill in the request form, and save it to the output folder. --- # Laptop request form Collect the needed information from the user's request and fill in the `form.md` form. ## Steps 1. Check who can apply and the required fields in `docs/laptop-policy.md`. 1. Find the required fields below in the user's request. - Applicant name - Department (group) - Reason (one of: new hire, replacement, broken) - Preferred model (one of: Standard 14-inch, Performance 16-inch, Lightweight 13-inch) - Date needed 1. If any field is missing, do not create the form. Ask for only the missing fields, all at once. 1. When all fields are filled, read `form.md` in this folder and fill it in using the same format. 1. Save it as `output/laptop-request-<applicant>.md`, and also point out anything that does not match the policy. ## Rules - If the reason is replacement and the laptop was issued less than 4 years ago, explain that it does not meet the policy. - For the Performance 16-inch model, explain that a justification from the group lead is required. - Write the name exactly as the user gave it, and do not ask for an employee ID or contact details.

Try a request with some information left out on purpose.

PROMPT
1
/laptop-request I want to request a laptop replacement. My name is Kim OO and I'm in the Firmware group.

Following the skill's steps, it reads the policy and asks for the missing fields (preferred model, date needed) all at once with question cards.
For fields with choices, pick a number, type the rest into the input box, and click Submit.
The number of questions and how they are shown can change a little each time you run it.

Asking for missing fields with question cards

For the preferred model, I chose Performance 16-inch, and in the date needed box I typed the following.

PROMPT
1
I need it by 2026-11-20. My current laptop was issued on 2021-08-02.

It creates the form, saves it in the output folder, and reports the result of the policy check.

Creating the form and reporting the policy check

Here is the file it created.
Because it is a performance model, the group lead justification item is left unchecked.

/output/laptop-request-김OO.md
MARKDOWN
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# Laptop request | Field | Value | | --- | --- | | Applicant | Kim OO | | Department (group) | Firmware group | | Reason | Replacement | | Preferred model | Performance 16-inch | | Date needed | 2026-11-20 | | Current laptop issued | 2021-08-02 | ## Reason details I am requesting a replacement for my current laptop. It was issued on 2021-08-02, so it is past the replacement standard of 4 years after issue. I am requesting the Performance 16-inch model for firmware work, and I would like to receive it by 2026-11-20. ## Checklist - [x] Eligible under the policy - [x] All required fields are filled in - [ ] Group lead justification attached for the performance model Approval: group lead ____________ (date: ________)

# Meeting notes and follow-up questions

The third skill turns meeting memos into meeting notes, and it finds agenda items that are missing a decision, an owner, or a deadline and asks about them.
How to handle incomplete input is the key point of this skill.

/.github/skills/meeting-notes/SKILL.md
MARKDOWN
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
--- name: meeting-notes description: Use when turning meeting memos or conversations into meeting notes. Asks follow-up questions when an agenda item, decision, owner, or deadline is missing. --- # Meeting notes Read the meeting memo and organize the decision, owner, and deadline for each agenda item. ## Steps 1. Find the agenda items in the memo and number them. 1. Fill in these four things for each agenda item. - Discussion - Decision - Owner - Deadline 1. Collect separately the items that have a decision but no owner or deadline, and the items that were discussed but have no decision. 1. If anything is missing, ask about it all at once in the format below before saving the meeting notes. > Items to confirm > - Item 2 (environmental chamber booking rules): owner, deadline > - Item 3 (cold storage site visit): decision 1. When you get the answers, complete the table and save it as `output/meeting-<meeting date>.md`. ## Meeting notes format (Meeting notes Markdown format omitted) ## Rules - Do not guess and fill in decisions or owners that are not in the memo. - Use attendee names exactly as written in the memo.
The meeting notes format part is omitted

The example meeting memo, data/meeting-2026-09-01.md, has no owners or deadlines at all.
When you run the skill, it first organizes the agenda items and then asks what is missing for each one.

PROMPT
1
/meeting-notes Turn the memo in data/meeting-2026-09-01.md into meeting notes

The skill gives an example of the question format, but the Copilot harness often asks with question cards, like in the laptop request above.
Even though the format differs, it still follows the rule of collecting the missing fields by agenda item and asking all at once.

Asking about missing fields for each agenda item

I answered the question cards as follows.

Question Answer entered
Item 1 (sensor value stuck at 0) Kim OO, September 5
Item 2 (environmental chamber booking rules) Park OO, September 8
Item 3 (cold storage connection drops) We decided to decide on the site visit at the next meeting. Owner Choi OO, September 8
Item 4 (weekly report format) Lee OO, September 8

When you submit the answers, it completes the meeting notes and saves them.

Meeting notes completed and saved

When you design follow-up question logic, you only need to decide these three things.

What to decide Answer in the example
Required fields Decision, owner, and deadline for each agenda item
What to do when something is missing Do not save; collect only the missing fields by agenda item and ask all at once
What not to guess Do not fill in decisions or owners that are not in the memo

If you list the required fields in the skill body and show the question format as an example, Copilot follows it.
It is also important to write that it should ask everything at once, not in several rounds.
If the user has to answer many times, there is less point in using an assistant.

# Knowledge base questions

Having the assistant answer based on your team's reference documents is its most basic use.
The method used for this is retrieval-augmented generation (RAG).
Instead of sending the whole document to the model every time, you split the documents into small chunks and build an index, and when a question comes in, you find only the related chunks and send them with the question.

The flow of retrieval-augmented generation

  • Chunking: Cut the documents by paragraph or heading.
  • Embedding and indexing: Turn the meaning of each chunk into a number vector and store it. Sentences with the same meaning become nearby vectors.
  • Search: Turn the question into a vector the same way, and find the closest chunks.
  • Context injection: Send the chunks you found to the model with the question so it can give an answer with a basis.

VS Code builds a semantic index of the repository, and the agent uses it to find files related to a question.
You can check and build the index from Codebase Semantic Index in the Copilot menu in the status bar.
In a repository with only a few documents, like the example, the agent finds what it needs with text search and file reading even without an index.

Switch the agent to Ask and ask a question about the spec document.
Also ask which document it used.

PROMPT
1
What are the measurement range, accuracy, and operating temperature of the TS-100? Also tell me which item in which document you used.

If you expand the step list at the top of the response, you can see the files it actually read, and each value in the answer has its reference item.
You can also see that it chose the matching skill (team-faq) on its own, checked the FAQ first, and then read the spec document.

Searching the repository and answering with sources

Always check the answer against the original.
The example's docs/spec-ts100.md lists the measurement range as -40°C to 125°C and the operating temperature as -40°C to 85°C, and if the values and reference items in the response match these, it passes.
When you check, look at these three things.

  • Are the numbers and units exactly the same as the original?
  • Do the files and items shown as sources really exist?
  • For a question not in the documents, does it answer "it is not in the documents"?

Making up plausible content that is not in the documents is called hallucination.
Writing "do not guess when there is no basis" in the instructions file and making it add sources to answers is the simplest way to reduce hallucination.

If you have a very large number of documents, or a mix of non-Markdown formats such as PDF and Excel, repository search alone may not be enough.
In that case, convert the documents to Markdown and put them in the docs folder, or connect an external search service with MCP, which is covered later.

# Skills you call yourself

So far, Copilot could look at a request and choose a skill on its own.
But for tasks that a person should start at a set time, like a weekly report, it is a problem if Copilot runs them by itself.
In that case, add disable-model-invocation: true to the skill's frontmatter so it runs only when you call it as /name.
This is the role that prompt files played in earlier versions.

/.github/skills/weekly-report/SKILL.md
MARKDOWN
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
--- name: weekly-report description: Creates a weekly report from the triage table. Use only when the user calls /weekly-report. argument-hint: Report period or what to highlight disable-model-invocation: true --- # Writing the weekly report Read the `output/triage-result.md` triage table and write a weekly report in exactly the `docs/report-template.md` format. - Report period: Use the period written after the command. If there is none, use this week. - Output file: `output/weekly-report.md` ## Rules - Do not change the heading order or table format of the template. - For high priority issues, fill in the owner according to the assignment rules in `docs/issue-rules.md`. - For untriaged issues, write in one line what to ask the reporter again. - Do not make up anything that is not in the triage table.
Frontmatter field Role
name The name after /. Must match the folder name
description The description shown in the slash list
argument-hint Text that tells you what to write after the name
disable-model-invocation If true, Copilot does not choose it on its own; it runs only when called as /name

Whatever you write after the name, like /weekly-report fourth week of October, is sent along with the request.
You will run the report example later in Report automation.

# Planning first

For tasks that need research first, like a bug report, use plan mode together with a skill.
Plan mode researches the repository without changing files and writes up a plan as a document.

/.github/skills/bug-report/SKILL.md
MARKDOWN
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
--- name: bug-report description: Makes a research plan before writing up one issue as a bug report. Use only when the user calls /bug-report. argument-hint: Issue number or symptom disable-model-invocation: true --- # Bug report research plan We want to write up the issue (number or symptom) given after the command as a bug report. Do not write the report right away. First make a research plan in the order below. 1. In `data/issues.csv`, check the title, content, and date received of the issue. 1. In `docs/spec-ts100.md`, find the spec items related to the symptom (operating range, interface, firmware version). 1. Check `docs/faq.md` for similar procedures or known fixes. 1. List the reproduction conditions, the scope of impact, and the questions you need to confirm. In the plan, write which files you will read and why, and what information is still missing.

Click Interactive below the input box, change it to Plan, and run it with the issue number.

PROMPT
1
/bug-report issue 4

It reads the issue list, the spec document, and the FAQ in turn, and if something needs to be decided, such as the research scope, it asks first with question cards.
When you submit your answers, it writes up the plan and shows a plan review card above the input box.

The research plan from plan mode and the review card

Button Action
Approve Plan Only Approves the plan without running it
Arrow, Implement Plan Approves the plan and runs it right away in the current session
Arrow, Implement with Autopilot Approves the plan and keeps going on its own until it finishes
Reject Does not accept the plan

In the Korean UI, this card's title appears as 요금제 검토 ('pricing plan review'), an awkward translation of the English Review Plan, but it is the plan review card.

Click Open Full Plan to open the plan as a document in the editor.
It lists, step by step, which files it read and why, and what it still does not know, so you can see exactly how the agent works through the problem.
If something needs changing, write it in the card's feedback box and click Submit Feedback, and it refines the plan.

The research plan opened as a document in the editor

The plan document is saved in the session folder, not in the repository.
If you want to use it to explain your team's bug report process, open the document and save it as a separate file.

# Custom agents

Custom agents are dedicated agents with a set role and set tools they can use.
If you narrow the role, like "an owner who only triages and does not touch files," the results are more stable, and you also prevent files from being changed by mistake.
Create a .agent.md file in the .github/agents folder, and it appears in the agent menu.

/.github/agents/triage.agent.md
MARKDOWN
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
--- name: Issue Triage description: Reads the issue list and only triages it according to the rules in docs/issue-rules.md. Does not change files. --- You are the issue triage owner for the R&D team. You only triage, and you do not create or change files. ## Triage steps 1. Read `docs/issue-rules.md` and check the criteria for type, priority, and untriaged. 1. Compare the issue the user gave (or a specific number in `data/issues.csv`) against the rules. 1. Answer with the result in the table format below. | Number | Type | Priority | Status | Reason | | --- | --- | --- | --- | --- | ## What to follow - Do not triage with criteria that are not in the rules. If it is unclear, leave it untriaged and write why. - In the reason column, write exactly which keyword or condition it matched. - If an owner needs to be assigned, follow the assignment table in the rules document.

Choose Issue Triage in the agent menu and paste in a newly received issue.

PROMPT
1
Please triage this new issue. Number 13, title: Dashboard freezes on a white screen after login, content: Logging in with the customer demo account freezes on a white screen. It happens every time and other accounts work fine. Date received: 2026-10-23

It reads the rules document and answers with the triage result and reason in a table.
The reason column shows exactly which keyword made it a bug and why its priority is high.

The result triaged by the custom agent according to the rules

If you add a tools field to the frontmatter, you can limit the tools to a list.
For example, if you keep only reading and searching, like tools: ['read', 'search'], this agent cannot change files or run commands.
Tool names can differ between harnesses, so after setting them, check that it really does only what you want.

# Data triage

Now let's have it triage the whole issue list at once.
For this kind of work, it is better to write the rules as a document and run a script that follows those rules, rather than having Copilot triage everything itself each time.
The same input always gives the same result, it uses no credits, and when the rules change, you only need to update the document and the script.

How the issue list becomes a triage table and a report

# Input data

The example's input is data/issues.csv.
It has five columns (number, title, content, reporter, date received), and type and priority are empty.
In Excel, you can create a file in the same format with Save As > CSV UTF-8.

/data/issues.csv
1
2
3
4
number,title,content,reporter,date 1,Sensor value stuck at 0 after firmware 2.3 update,After updating equipment 3 on the mass production line to firmware 2.3.1 the temperature value always shows 0. Rolling back to 2.2.4 makes it normal.,Kim OO,2026-08-31 2,Request to add department name to report output,Please add the department name at the top of the weekly report.,Lee OO,2026-08-31 3,Question about calibration procedure,What is the calibration procedure for newly received TS-100 modules?,Park OO,2026-09-01
The first few lines

Among the 12 issues, some problem data was added on purpose.
Issue 6 has only "Please check." as its content, issue 9 has no reproduction conditions, and issue 10 has the date 2026-13-40.
How the triage logic handles these items is the key point of this step.

# Triage rules

Write the rules first as a document that people read.
The script should be nothing more than a copy of this document, and it must not make any decision that is not in the document.

/docs/issue-rules.md
MARKDOWN
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# Issue triage rules ## Type Find the keywords below in the title and content to set the type. If it matches several types, the type higher in the list takes priority. | Type | Keywords | | --- | --- | | Bug | error, fail, freeze, drop, stuck, not working, doesn't work, jump, jumps, broken, fill, expired, loss | | Feature request | add, support, improve, option | | Question | question, method, how, procedure | | Other | None of the keywords above | ## Priority | Priority | Condition | | --- | --- | | High | A bug whose title or content has one of: mass production, customer, entire, data loss, safety | | Medium | Other bugs | | Low | Feature request, question, other | ## Untriaged and flags | Situation | Handling | Note | | --- | --- | --- | | Content is shorter than 15 characters | Untriaged | Not enough detail | | Type is Other | Untriaged | Unknown type | | A bug whose content has one of: sometimes, not sure, irregular | Untriaged | No repro steps | | Date received is not in YYYY-MM-DD format or is not a real date | Triage anyway | Invalid date format | | Date received is later than today | Triage anyway | Future date | ## Assignment | Condition | Owner | | --- | --- | | High priority | Group lead | | Bug | Development group | | Feature request | Planning group | | Question | Tech Support group | | Untriaged | Ask the reporter to confirm |

Items without enough basis are not forced into a decision; they stay untriaged.
Items with odd values are triaged but flagged in the note so a person can check them.
You must put these two exits into the rules from the start so the assistant does not pretend to know what it does not know.

# Triage script

Have Copilot write the script.
In Agent mode, attach the rules document and the input file (with + or # in the input box) and ask like this.

PROMPT
1
2
3
Based on docs/issue-rules.md and data/issues.csv, create scripts/triage.js that reads the issue list and sets the type, priority, status (triaged or untriaged), and note according to the rules document. Save the results to output/triage-result.csv and output/triage-result.md (summary table, triage result table, untriaged list, flagged list, triage reason table), and print only a count summary to the console. Use only built-in Node.js modules with no external packages, and record for each decision which keyword or condition it matched.

Because the instructions file and the rules in scripts.instructions.md also apply, it may create a script that uses only built-in modules and has Korean comments.
The example repository includes a finished version, so compare it with the one you made.
It is fine if you cannot read the code.
Just check that the keyword table in the rules document was copied as-is into the lists at the top of the script.

/scripts/triage.js
JS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
// Type keywords. Types higher in the list take priority. const TYPE_RULES = [ { type: 'Bug', keywords: ['error', 'fail', 'freeze', 'drop', 'stuck', 'not working', "doesn't work", 'jump', 'jumps', 'broken', 'fill', 'expired', 'loss'] }, { type: 'Feature request', keywords: ['add', 'support', 'improve', 'option'] }, { type: 'Question', keywords: ['question', 'method', 'how', 'procedure'] } ] // A bug with any of these keywords is high priority. const HIGH_KEYWORDS = ['mass production', 'customer', 'entire', 'data loss', 'safety'] // Words that mean a bug has no reproduction conditions. const UNCLEAR_KEYWORDS = ['sometimes', 'not sure', 'irregular'] // Content shorter than this is untriaged for lack of detail. const MIN_LENGTH = 15 /** Triages one issue. */ export function triage(issue, today = new Date()) { const text = `${issue.title} ${issue.content}` const notes = [] const { type, reason: typeReason } = judgeType(text) const { priority, reason: priorityReason } = judgePriority(type, text) let status = 'Triaged' if (issue.content.length < MIN_LENGTH) { status = 'Untriaged' notes.push('Not enough detail') } else if (type === 'Other') { status = 'Untriaged' notes.push('Unknown type') } else if (type === 'Bug' && includesAny(issue.content, UNCLEAR_KEYWORDS)) { status = 'Untriaged' notes.push('No repro steps') } const dateNote = checkDate(issue.date, today) if (dateNote) notes.push(dateNote) return { number: issue.number, title: issue.title, reporter: issue.reporter, date: issue.date, type: status === 'Triaged' ? type : '', priority: status === 'Triaged' ? priority : '', status, note: notes.join(', '), reason: status === 'Triaged' ? `${typeReason}, ${priorityReason}` : notes[0] } }
Part of the constants copied from the rules and the triage function

You can also run it yourself in the terminal.

BASH
1
2
3
4
node scripts/triage.js ## Triage complete: 12 issues ## High 3, Medium 2, Low 5, Untriaged 2, Flagged 1 ## Output files: output/triage-result.md, output/triage-result.csv

# Triage skill

Instead of typing the command every time, create one more skill that also runs the script and summarizes the results.

/.github/skills/issue-triage/SKILL.md
MARKDOWN
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
--- name: issue-triage description: Use when triaging the whole issue list and creating a triage table. Runs scripts/triage.js and organizes the untriaged issues. --- # Issue list triage Triage the issues in `data/issues.csv` according to the rules in `docs/issue-rules.md`. ## Steps 1. Read `docs/issue-rules.md` and check that the rules have not changed. 1. Run `node scripts/triage.js` in the terminal. 1. Read `output/triage-result.md` and answer with a summary of the following. - Total count and count by priority - List of high priority issues - List of untriaged issues and what to ask each reporter 1. If the user asks to change the rules, update both `docs/issue-rules.md` and the keyword lists in `scripts/triage.js`. ## Rules - Do not change the script's results on your own. If a result looks wrong, explain the problem in the rules or the data. - Quote the triage reason exactly from the reason column of the triage table.
PROMPT
1
/issue-triage Please triage the issue list

After checking the rules document, it asks for approval to run the script, and when you allow it, it reads the result files and summarizes them.
For untriaged issues, it also lists what to ask each reporter.

Running the triage script and organizing the untriaged issues

Here is part of the triage table it created.
Untriaged items have empty type and priority, and the reason stays in the note.

/output/triage-result.md
MARKDOWN
1
2
3
4
5
6
7
8
| Number | Title | Type | Priority | Status | Note | | --- | --- | --- | --- | --- | --- | | 1 | Sensor value stuck at 0 after firmware 2.3 update | Bug | High | Triaged | | | 5 | Log files fill up the disk | Bug | Medium | Triaged | | | 6 | Please check | | | Untriaged | Not enough detail | | 9 | Readings sometimes jump | | | Untriaged | No repro steps | | 10 | Firmware download link expired | Bug | Medium | Triaged | Invalid date format | | 11 | Backup script failure risks data loss | Bug | High | Triaged | |
Part of the triage result table

# Report automation

After triage, what remains is notifying the owners and creating a report.
For reports you make again and again, set the template first, and leave filling it in to a script or a skill.

The report template is in docs/report-template.md.
If you fix the order of the headings and the table columns, you get a report with the same shape every week.

/docs/report-template.md
MARKDOWN
1
2
3
4
5
6
7
8
# Issue triage report (YYYY-MM-DD) ## Summary ## High priority (notify now) ## Untriaged (ask to confirm) ## Flagged ## Who to notify ## Next steps
Template structure

Set the notification conditions and recipients in a table.
The example uses the assignment table in the rules document as-is: high priority issues are reported to the group lead right away, and for untriaged issues the reporter is asked to confirm.

Have Copilot write scripts/report.js the same way as the triage script.

PROMPT
1
2
Based on docs/report-template.md and docs/issue-rules.md, create scripts/report.js that reads output/triage-result.csv and creates output/report.md in exactly the template format. Follow the assignment table in the rules document for owners, and in next steps, write one line of action for each high priority and untriaged issue.
BASH
1
2
3
node scripts/report.js ## Report created: output/report.md ## High 3, Untriaged 2, Flagged 1

A script follows a set format exactly, but it is not good at writing sentences, such as "what to ask again" for untriaged issues.
Leave that part to the /weekly-report skill you made earlier.

PROMPT
1
/weekly-report fourth week of October

It reads the triage table, the template, and the rules document and creates the report.
If you open the report, the notes for untriaged issues include, in sentences, what to ask the reporters.

The chat that created the weekly report and the output file

If you want to create the report at the same time every week, you can schedule the request with Automations in the Agents window, which collects and manages agent sessions separately.
It is a preview feature, so you need to turn on chat.automations.enabled in settings, and it only works if your computer and VS Code are running at the scheduled time.

The example does not include the step of actually sending notifications by email or messenger.
Anything that sends something outside should be done by a person after reviewing the report, or connected with MCP, covered later, but always with an approval step.

# MCP

So far, the assistant has only worked with files inside the repository.
To connect to external services such as internal systems, databases, and messengers, use MCP (Model Context Protocol).
MCP is a standard protocol that lets AI tools use the features of external services as tools, and each service is provided as an MCP server.
When you connect a server, its tools are added to the agent's tool list.

The Copilot harness includes the GitHub MCP server by default, which works with issues and pull requests in GitHub repositories.
If you open the MCP Servers screen in the agent customization editor, github-mcp-server appears under Built-In, and you can turn it off and on with a switch.

The built-in GitHub MCP server

If your repository is on GitHub, you can ask in the chat something like "Get the open issues in this repository and triage them."
Instead of a CSV file, an issue tracker, messenger, or internal wiki becomes the input and output.
How to connect an issue management tool your company uses, like Jira, with MCP is covered in GitHub Copilot Work Automation in Practice.

To add other servers, search for @mcp in the Extensions view (Cmd + Shift + X) and install from the MCP server gallery, or run MCP: Add Server from the Command Palette (Cmd + Shift + P).
Write servers you share with your team in .mcp.json at the root of the repository.

/.mcp.json
JSON
1
2
3
4
5
6
7
8
9
{ "mcpServers": { "context7": { "type": "http", "url": "https://mcp.context7.com/mcp", "tools": ["*"] } } }
Example MCP server settings shared with the team

"*" in tools means it uses all of the server's tools, and you can narrow the scope by listing only the tool names you need.
If you have a .vscode/mcp.json (servers format) from an earlier version, a migration notice appears, and you can move it to this format.

There are also ways to extend it without MCP.

  • If you put a web page address in your request, it can fetch the content and use it.
  • Documents converted to Markdown and placed in the docs folder become a knowledge base on their own.
  • You can also make a script call an external API. In that case, keep the API key in a .env file and block reading it with a hook, as in Sensitive information.

An MCP server means outside code runs on your computer or accesses external services with your account.
Use only servers from a clear source, and do not write API keys directly in settings files.

# Validation and permissions

Before you share the assistant with your team, check two things.
Does a given input always produce the expected result, and have you blocked the things the assistant must not do?

# Validation set

A validation set is a list that pairs each input with its expected result.
Do not include only normal cases. Add edge cases and cases that should fail as well.

/tests/cases.json
JSON
1
2
3
4
5
6
7
8
9
10
11
[ { "number": "1", "case": "normal", "status": "Triaged", "type": "Bug", "priority": "High" }, { "number": "2", "case": "normal", "status": "Triaged", "type": "Feature request", "priority": "Low" }, { "number": "3", "case": "normal", "status": "Triaged", "type": "Question", "priority": "Low" }, { "number": "4", "case": "normal", "status": "Triaged", "type": "Bug", "priority": "High" }, { "number": "5", "case": "edge", "status": "Triaged", "type": "Bug", "priority": "Medium" }, { "number": "10", "case": "edge", "status": "Triaged", "type": "Bug", "priority": "Medium", "note": "Invalid date format" }, { "number": "11", "case": "edge", "status": "Triaged", "type": "Bug", "priority": "High" }, { "number": "6", "case": "fail", "status": "Untriaged", "note": "Not enough detail" }, { "number": "9", "case": "fail", "status": "Untriaged", "note": "No repro steps" } ]
Case Meaning Example
Normal Input where a rule clearly applies A bug on the production line is high priority
Edge Input on the edge of a rule Issue 10, whose received date looks wrong but still needs to be triaged
Fail Input that must not be triaged Issue 6, whose description is too short, and issue 9, which has no repro steps

scripts/check.js compares the triage results with the validation set and classifies any mismatches.
It reads the triage result file (output/triage-result.csv), so after you change the rules or the script, run node scripts/triage.js first.

BASH
1
2
3
4
5
6
7
8
9
node scripts/check.js ## No. Case Expected Result Verdict ## 1 normal Triaged Bug High Triaged Bug High match ## 2 normal Triaged Feature request Low Triaged Feature request Low match ## ... ## 6 fail Untriaged Not enough detail Untriaged Not enough detail match ## 9 fail Untriaged No repro steps Untriaged No repro steps match ## ## Checked 9: match 9

Mismatches are grouped by what you are trying to filter out.
In this example, the target is issues that lack evidence and must not be triaged.

Verdict Meaning
False alarm Filtered out an item that did not need to be filtered. An issue that should have been triaged was left untriaged
Miss Missed an item that should have been filtered. An issue that should stay untriaged was triaged
Misjudged It was triaged, but the type or priority is different

If you run the triage script and then this script every time you change a rule, you can see right away whether the change broke another case.
For example, without the "no repro steps" rule, issue 9 would be triaged as a bug and become a miss.
Looking at mismatches like this and adding rules is how you design the limits of the assistant.
You can also ask Copilot, "Suggest three more edge cases for the validation set," to grow the cases.
To see false alarms and misses actually appear after removing a rule on purpose, see the 'Checking the judgments' section of GitHub Copilot Work Automation in Practice.

# Permission levels

What the assistant can do grows in impact in this order: read, write, run, and send outside.
The higher the permission, the earlier you put an approval step, and anything that sends data outside should happen only after a person checks it.

Permission levels and approval steps

Here is how to adjust these levels in VS Code.

Permission How to adjust
Read, search Allowed without approval by default. You can block specific files with a hook
Write Edits inside the project folder are saved without approval. Review them in the session changes and revert with Start Over. You can remove edit tools from a custom agent's tools
Run Approval for each command. Manage an allow list with the chat.tools.terminal.autoApprove setting
Send outside Trust and authentication approval for MCP servers. Add a "check first" rule to the instructions

To adjust the approval rules for terminal commands, open Settings (Cmd + ,) and search for autoApprove.
Rules decide whether to auto-approve a command based on its beginning, and risky commands such as rm are not auto-approved by the default rules, so they ask for approval.
In a session with Manual permissions, only commands that match a true rule run without asking, and all other commands ask for approval.

Terminal auto-approve setting

If you run only the scripts under node scripts/ again and again, as in the example, you can add just that command to the allow list.

/.vscode/settings.json
JSON
1
2
3
4
5
{ "chat.tools.terminal.autoApprove": { "node scripts/": true } }
Auto-approve only a specific command

# Sensitive information

Work data often contains names, contact details, and customer information.
The example applies the following principles across data preparation, the instructions file, and a hook.

  • Prepare the source data with names already masked, like Kim OO. This is more reliable than having the assistant mask them.
  • In the instructions, list the items that must not appear in the output (full names, employee IDs, contact details, emails).
  • Block reading of files that contain secrets, like .env, with a hook, and also add them to .gitignore.
  • In the instructions, require that any task sending data outside "ask the user first."

Even if you write "Do not read .env files" in the instructions, it is only a recommendation to the model.
If the model misses the rule, or the user asks, "Read it anyway," the file can still be read.
Copilot has no option like Claude Code's permissions.deny setting that blocks reading a specific file with a single setting.
You can specify files with content exclusion in the GitHub repository settings, but it is only for Business and Enterprise plans, and in VS Code it applies only to inline suggestions and chat questions, not to agent tasks.

To add another layer of protection in agent tasks, use a hook.
A hook is a script that runs at points such as right before and right after Copilot calls a tool, and you register it in a JSON file in the .github/hooks folder.
If a preToolUse hook, which runs right before a tool call, outputs a deny decision, that tool call is canceled and the reason you wrote is passed to the model.

/.github/hooks/protect-secrets.json
JSON
1
2
3
4
5
6
7
8
{ "version": 1, "hooks": { "preToolUse": [ { "type": "command", "command": "node .github/hooks/protect-secrets.js", "timeoutSec": 10 } ] } }

Here is the script the hook runs.
List the paths to block in the BLOCKED array, the same way you write .gitignore.

/.github/hooks/protect-secrets.js
JS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// Called right before Copilot runs a tool. // If the request touches a file or folder in the list below, output a deny decision. import fs from 'node:fs' // List of paths to block. Write them like .gitignore. // A name alone blocks it in any folder, and a path blocks only that location. // * means any characters within a name, and ** means any characters across folder boundaries. const BLOCKED = [ '.env*', // .env, .env.local, .env.production '*.pem', // Certificates and private keys 'secrets/**', // Every file in the secrets folder 'config/credentials.json', ] // Convert a pattern like 'config/*.json' into a regular expression. function toRegExp(pattern) { const source = pattern .replace(/[.+?^${}()|[\]\\]/g, '\\$&') // Treat characters with special meaning in regex as literal characters .split('**') .map(part => part.replace(/\*/g, '[^/]*')) .join('.*') return new RegExp(`(^|/)${source}$`, 'i') } // The tool name (toolName) and arguments (toolArgs) arrive on standard input. const { toolArgs } = JSON.parse(fs.readFileSync(0, 'utf8')) const input = typeof toolArgs === 'string' ? toolArgs : JSON.stringify(toolArgs ?? {}) // Split into path units and check them, whether they are in a file path, a search pattern, or a terminal command. const paths = input .replace(/\\+/g, '/') // Normalize Windows path separators .split(/[\s"'`|;&<>(),=]+/) const matchers = BLOCKED.map(toRegExp) const blocked = paths.find(path => matchers.some(matcher => matcher.test(path))) // Output a decision only when denying. If nothing is output, the usual approval process applies. if (blocked) { console.log(JSON.stringify({ permissionDecision: 'deny', permissionDecisionReason: `The path ${blocked} cannot be read or modified.` })) }

The script turns all of the tool's arguments into a string, splits it on separators like spaces and quotes to get path candidates, and denies the call if any of them matches a pattern in the list.
It checks not only the path of the file-reading tool but also paths inside search patterns and terminal commands in the same way, so requests that go around it through the terminal, like cat .env, are blocked too.
However, it compares path text, so it can be bypassed by writing the same file differently, like config/./credentials.json, or by building the path inside a script.
Treat the hook as a safeguard against mistakes; the surest way is to keep secrets outside the folders the agent works in from the start.

If you actually ask it to print .env, the model usually refuses before calling a tool, because of the .env rule in the instructions.
The instructions and the hook block it in two layers.
To see just the hook at work, ask for a different path in the list.
Here is the result of creating a config/credentials.json file with fake values and asking what items it contains.
The tool output shows Denied by preToolUse hook and the reason the script wrote.

The hook blocking a tool call that reads a secret file

A hook blocks only the tools that Copilot calls on its own.
The contents of files you attach yourself or have open in the editor are sent without a tool call, so they do not go through the hook.
Do not make requests while a secret file is open in the editor.
Hooks are still a preview feature, so the configuration format may change, and hooks made for the older local harness use different event names and input formats, so they do not work as is.

Copilot sends your request to GitHub's servers to create a response.
Data that company policy says must not leave the company should never be put in the repository in the first place.

# Putting it together

If you connect everything you have built so far in one repository, it becomes your team's work assistant.
Knowledge (reference documents and search), triage (rules and scripts), and reports (templates and skills) live in separate files, and Copilot picks what it needs for each request.

A work assistant that connects knowledge, triage, and reports

If you run the whole flow at once, it goes in this order.

  1. In Ask, ask about a spec or policy and check the sources.
  2. In Agent, create a triage table with /issue-triage and check the untriaged items.
  3. Create a report with /weekly-report and review it in the output folder.
  4. Run the validation set with node scripts/check.js to confirm the rules still hold.
  5. Send the report to the person in charge.

Before sharing, check the items in docs/quality-checklist.md yourself.
It is divided into five areas: answer quality, asking back, triage, reports, and safety. If even one item fails, do not share it.

/docs/quality-checklist.md
MARKDOWN
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
## Answer quality - [ ] Does it answer 3 FAQ questions with sources (file, item)? - [ ] Does it answer questions not in the FAQ with "This is not in the documents"? - [ ] Does it answer spec questions with the same numbers as in `docs/spec-ts100.md`? ## Asking back - [ ] If a required field is missing from a laptop request, does it ask instead of creating the form? - [ ] Does it collect agenda items from meeting notes that lack an owner or deadline and ask about them? - [ ] Does it ask about all missing items at once instead of in several rounds? ## Safety - [ ] Does the output leave out full names, employee IDs, and contact details? - [ ] Are tools that send data outside never run without approval? - [ ] Does it avoid reading secret files like `.env`?
Excerpt

When you roll it out to a department, fill in docs/adoption-plan-template.md and use it as a guide.
It covers scope, preparation, usage, limits and cautions, and operation in tables and lists.
In particular, it is a good idea to state the limits clearly from the start: "The assistant does not make judgments that are not in the rule documents, and its output is a draft that a person reviews."

To adapt it to your own team, we recommend the following order.

  1. Replace the documents in the docs folder with your team's actual policies and FAQ.
  2. Match the column names in data/issues.csv to the format your team uses, and change the keywords in docs/issue-rules.md to your team's terms.
  3. Ask Copilot, "The rule documents have changed, so update triage.js and the validation set to match."
  4. Run node scripts/triage.js and then node scripts/check.js, and refine the rules and the validation set until everything matches.
  5. Once it passes the checklist, share the repository with your team.

It is a small setup of a few files and two or three scripts, but it has the full basic structure of an assistant: it answers based on documents, asks back about what it does not know, triages by the rules, and reports in a set format.

How to use this structure to automate a real task from start to finish continues in GitHub Copilot Work Automation in Practice.
Using test log judgment, graph reports, and Jira defect filing as examples, it covers how to break work into steps, checking the judgments, connecting to internal systems, and security.

For more details, see the following documents.