GitHub Copilot Agent Skills Explained
A skill is a folder containing a SKILL.md file and whatever supporting material
that file needs. Copilot loads it when the work at hand matches what the skill
describes.
That last clause is the whole design. Instructions are always in context. A prompt file is something you invoke deliberately. A skill sits available and unused until its description matches what is happening — which means you can have twenty of them without paying for twenty of them on every request.
Key takeaways
- A skill is a directory, not a single file:
SKILL.mdplus supporting resources. nameanddescriptionare required. The description is how Copilot decides whether the skill is relevant.- Loading is conditional, which is the property that distinguishes skills from instructions.
- A skill can include scripts, and a skill that runs code is a different security proposition from one that only contains text.
- Multiple discovery locations exist, including
.claude/skills/and.agents/skills/— the format is shared across tools.
The four containers, distinguished
Copilot now offers several places to put knowledge, and the overlap causes real confusion. The distinction is about when the content applies.
Which container to use
- Custom instructionsAlwaysRules that apply to every request in this repository.
- Prompt fileInvokedA specific task you choose to run.
- Custom agentSelectedA role with a tool boundary, chosen for a piece of work.
- SkillConditionalProcedural knowledge loaded when the work matches its description.
Worked through with one example: your team has a specific way of writing database migrations.
If the rule is short and universal — “migrations are always reversible” — it is an instruction. Everyone should see it always, and it costs a line.
If you want to generate a migration on demand, that is a prompt file. You invoke it, it does the thing.
If you want a constrained role that can write migrations and nothing else, that is an agent, and the value is the tool list.
If the knowledge is a page of procedure — the naming convention, the two-phase pattern for column renames, the check to run before merging, the script that validates it — that is a skill. It is too long to carry in every request and too situational to invoke manually, but it should appear whenever anyone touches migrations.
The format
A minimal skill is one file:
---
name: database-migrations
description: How this project writes and validates database
migrations. Use when adding, altering or removing database columns
or tables.
---
# Database migrations
## Naming
`NNNN_verb_object.sql`, zero-padded to four digits. The verb is one of
add, drop, alter, backfill.
## Renaming a column
Never rename in one step. Renaming breaks any process still running the
previous release.
1. Add the new column, nullable.
2. Backfill in a separate migration.
3. Deploy the code that writes both and reads the new one.
4. Drop the old column in a later release.
## Before merging
Run `scripts/check-migration.sh`. It rejects irreversible statements
and any migration that both adds and drops in one file.Nothing there is generic advice. Every line is something this project decided, and that is exactly the content that makes a skill worth having — the model already knows what a migration is.
Directory layout
Skills become genuinely useful when they carry more than prose.
database-migrations/ ├── SKILL.md the entry point Copilot reads ├── reference/ │ ├── patterns.md the long form: every migration shape we use │ └── incidents.md what went wrong before, and why ├── templates/ │ └── migration.sql the skeleton to copy └── scripts/ └── validate.sh the check that runs before merge
SKILL.md is what Copilot reads first. It should be short enough to be useful as
an overview and should point at the rest. Supporting files are consulted when the
work calls for them, which keeps the always-loaded portion small.
Where skills are discovered
Skill locations
- Project
.github/skills/<skill-name>/SKILL.md - Project
.claude/skills/<skill-name>/SKILL.md - Project
.agents/skills/<skill-name>/SKILL.md - Personal
~/.copilot/skills/<skill-name>/SKILL.md - Personal
~/.agents/skills/<skill-name>/SKILL.md
Two things are worth noticing about that list.
Project skills are committed. They live in the repository, which means they are reviewed, versioned and shared. That is the right default for anything describing how this codebase works.
.claude/skills/ and .agents/skills/ appear alongside .github/skills/.
The skill format is not Copilot-specific. A skill written for one tool is
generally readable by another, which makes skills the most portable of the four
containers — and worth preferring when you expect to use more than one agentic
tool.
What belongs inside
The difference between a skill that improves output and one that adds tokens is almost entirely about content selection.
Procedures with steps that matter. The migration example above is the shape: an ordered process where doing it in a different order causes a specific problem. Models are good at following a procedure and bad at guessing which of several plausible procedures your team uses.
Conventions that are arbitrary. Naming schemes, directory layouts, which of two equivalent approaches you picked. Arbitrary decisions are unguessable by definition, which makes writing them down high-value.
The reasons behind constraints. “Never rename in one step” is a rule. “Never rename in one step, because a process still running the previous release will break” is a rule the agent can apply to situations you did not anticipate.
Real failures. What went wrong, what caused it, what the fix was. This is the content nobody else can write, and it is the content most likely to prevent a repeat.
Concrete templates and examples. A skeleton file to copy removes an entire category of near-miss output.
What does not belong: general knowledge the model already has, aspirational descriptions of how you wish the codebase worked, and anything already enforced by a linter. A rule your tooling checks automatically does not need a document — and a document that disagrees with your tooling is worse than nothing.
Writing a description that gets matched
The description field decides whether the skill is ever loaded. Everything else
in the file is irrelevant if this sentence does not match the situation.
Write it as what it covers plus when to use it, in the vocabulary someone would actually use.
Weak: “Database helper.”
Strong: “How this project writes and validates database migrations. Use when adding, altering or removing database columns or tables.”
The second version names the trigger conditions explicitly. That is the part doing the matching work — “adding a column” appears in the description, so a request about adding a column has something to match against.
Skills that run code
allowed-tools lets a skill declare tools Copilot may use without asking each
time. Combined with a scripts/ directory, this makes a skill executable rather
than merely descriptive — and changes what it is.
The upside is real. A validation script that runs is worth more than a paragraph describing what to check, because the paragraph depends on the agent doing the checking correctly and the script does not.
Keeping skills accurate
A skill is trusted precisely because it is specific, which makes an outdated skill worse than no skill at all — it supplies confident, wrong specifics that the agent has no way to doubt.
Three habits keep this manageable.
Put skills next to what they describe. A skill about the API layer that lives in the repository containing the API layer gets updated when the API layer changes, because the change and the document are in the same pull request.
Reference rather than duplicate. Where a skill can point at a real file — the actual template, the actual validation script — it should. Duplicated content drifts; referenced content cannot.
Prefer scripts to descriptions of scripts. “Run scripts/check-migration.sh”
stays true when the check changes. A paragraph explaining what the check does does
not.
When a skill is the wrong answer
When the content is one sentence. Put it in instructions. A skill has overhead — a directory, a description, a matching decision — and one line does not justify it.
When it should always apply. Conditional loading means sometimes not loading. A security rule you need honoured every time belongs in instructions, where it cannot fail to match.
When you want to trigger it deliberately. That is a prompt file. Skills are matched, not invoked, and fighting that produces frustration.
When nobody has written the procedure down anywhere. A skill is a place to put an existing document, not a substitute for having thought the procedure through. Writing one forces the thinking, which is useful — but if the team does not agree on the procedure, the skill will encode one person’s version and make it authoritative.
When the knowledge changes weekly. A skill is a document, and stale documents are worse than absent ones because they are trusted. Content with a short half-life belongs closer to the thing it describes.
Personal versus project skills
The two personal locations exist for a reason worth using deliberately.
Draft personally. A skill you are still working out belongs in
~/.copilot/skills/ or ~/.agents/skills/, where you can iterate without anyone
else absorbing your half-formed procedure.
Promote when it stabilises. Once the content is right and would help a colleague, move it into the repository. The move is a copy and a pull request, and the review is the point: a second reader catches the assumptions you did not know you had written down.
Keep genuinely personal things personal. How you like output formatted, a workflow only you use, notes about a machine only you have. None of that belongs in a shared repository, and putting it there is how skill directories become unreadable.
The failure mode in both directions is real. Personal-only skills mean the team keeps solving the same problem separately. Everything-shared means nobody can find the four skills that matter among the thirty that do not.
How skills interact with agents
The two compose, and the composition is the point.
An agent has a role and a tool boundary. A skill has procedural knowledge. An agent working on migrations loads the migrations skill; so does a different agent working on migrations; so does plain agent mode with no custom agent at all.
That is why procedural knowledge belongs in a skill rather than in an agent file. Content in an agent file applies to that agent only, so three agents that all need the same procedure means three copies that will diverge. Content in a skill is available to whoever needs it.
The practical rule from the pillar’s architecture section applies directly: if you would want this content available when a different agent is running, it does not belong to one agent.
Knowing whether a skill fired
Conditional loading is the feature and also the confusion: when output does not reflect a skill, you cannot tell from the output whether the skill failed to load or loaded and was ignored.
Distinguishing the two is straightforward if you go looking.
Ask directly. Requesting that the agent state which skill it is applying is a crude test, but it separates “not loaded” from “loaded and unhelpful” in one turn.
Plant something identifiable. A distinctive naming convention or a specific phrase in the skill gives you a marker. If the output uses it, the skill loaded.
Vary the wording of the request. If a request phrased in the skill’s own vocabulary works and a paraphrase does not, the description is too narrow. Widen it with the words people actually use rather than the words you wish they used.
Check the location. A skill in the wrong directory is simply not found, and
this is a far more common cause than description quality — including the case
where the file is present but named something other than SKILL.md. Confirm the path against
the discovery locations above before rewriting anything.
Common questions
Does a skill work in every surface? Support varies; the matrix in the pillar records the current position. Skills are documented for the CLI and for agent surfaces generally.
How many skills should a repository have? As many as it has distinct procedures worth documenting — which for most repositories is a handful, not dozens. If you find yourself writing a skill for every module, the content probably belongs in the modules.
Can a skill reference another skill? Keep each self-contained. Cross-references between conditionally-loaded documents produce situations where half the content is present.
Do skills consume context when unused? The point of conditional loading is that the full content is not carried when the skill does not apply. The description is what has to be available for matching, which is why it should be one clear sentence rather than a paragraph.
Can I version a skill? It is a file in your repository, so it is versioned already. Resist adding a version number inside the content — the useful history is the commit log, and an internal version string is one more thing that goes stale.
Should skills be licensed? The optional license field exists for skills you
intend to share outside your organisation. For internal skills it is noise.
What if two skills both match? Both may load. That is usually fine and occasionally confusing; the fix is sharper descriptions rather than fewer skills.
Is this the same as Claude’s skills? The format is shared —
.claude/skills/ is one of the documented discovery locations. A skill written
carefully is portable across agentic tools, which is a good argument for keeping
tool-specific assumptions out of the content.
The description does the routing
Everything else in a skill is irrelevant if the description does not match. Two properties make it work.
It names the trigger situation. “Use when adding, altering or removing database columns or tables” describes a moment. “Database utilities” describes a category and matches nothing in particular.
It uses the words people say. If half your team says “column” and half says “field”, both belong in the description. Matching is against the request as phrased, not the request as you would have phrased it.
The consequence is that description-writing is not documentation-writing. A description is a routing rule wearing a sentence, and it is worth a disproportionate share of the time you spend on the skill.
A worked comparison
The clearest way to see the four containers is to watch one piece of knowledge move between them.
Suppose your project has a rule: all money is stored in integer minor units, never floats.
As an instruction. One line in .github/copilot-instructions.md. It applies to
every request, costs almost nothing, and is the right home for the rule itself.
Anyone writing anything gets it.
As part of a skill. The rule alone is too small for a skill. But how this project handles money — the integer rule, the currency column convention, the rounding policy at the boundary, the helper functions, the two bugs that came from getting it wrong — is a page of procedure and belongs in one.
As a prompt file. “Audit this module for float arithmetic on monetary values” is a task you might run occasionally. That is a prompt file: invoked, specific, producing a report.
As an agent. A read-only reviewer whose checks include monetary handling. The rule is one item among several, and what makes it an agent is the tool boundary, not the content.
All four are legitimate, and they are not alternatives — a mature repository has the rule in instructions, the procedure in a skill, the audit as a prompt file, and the check as one line in a reviewer. What goes wrong is putting the page of procedure in the instructions file, where it is carried on every request, or putting the one-line rule in a skill, where it applies only sometimes.
Next
Build your first agent skill walks through creating one from scratch, including the supporting files and a validation script. Custom agents covers the container skills most often pair with, and custom instructions covers the always-on alternative.
Sources
Every version-sensitive claim on this page was checked against first-party documentation. Only sources actually used are listed.
Your progress
Saved in this browser only. No account, no server, and nothing leaves your device. Clearing site data resets it.Sync across devices if you want it everywhere.
Saved in this browser and synced to your account, so it follows you between devices. Manage or delete it.
Was this lesson helpful?
We record which lesson you rated and whether it helped. Nothing identifies you — no account, no cookie, no session.