Start Debugging

Migrate Copilot Prompt Files to Agent Skills: the Full Checklist

How to convert .github/prompts/*.prompt.md into .github/skills/<name>/SKILL.md so GitHub Copilot loads them automatically. What breaks, what maps 1:1, and how to verify each skill fires.

If your repo has a .github/prompts/ folder full of .prompt.md files that everyone forgets to run, this is the migration that fixes it. Moving from Copilot prompt files to Agent Skills usually takes an afternoon for a handful of prompts, and the payoff is that Copilot starts invoking the right instructions on its own instead of waiting for someone to type /review-migration. The catch: it is not a rename. Prompt files are manually invoked, single-file, and IDE-only; Agent Skills are model-invoked folders that also run on the Copilot cloud agent and CLI. A few of your prompts should not become skills at all. This post is the checklist I run when converting a real repo, pinned to GitHub Copilot in VS Code 1.99+, Visual Studio 2026 18.5.0, and the GitHub CLI gh 2.90.0+, against the Agent Skills specification as of Q2 2026.

Why move prompt files to skills at all

Prompt files (.prompt.md) were the first reusable-instruction format Copilot shipped, and they are still fine for what they do. But they have three hard limits that skills remove:

If you are still deciding whether a given behavior even belongs in a skill versus a subagent versus an MCP server, Claude Code skills vs subagents vs MCP servers draws the same lines that apply here.

What actually changes

This is a migration, not a find-and-replace, because the two formats have different invocation models. Here is the breaking-change table I hand to teams before they start.

AreaPrompt file (.prompt.md)Agent Skill (SKILL.md)Severity
InvocationManual /name slash commandModel-invoked via description match (or /skills if user-invocable)high
Location.github/prompts/name.prompt.md (one file).github/skills/name/SKILL.md (folder)high
Body loadingWhole body injected every time you run itname + description at startup, body only on activationmedium
model fieldPins the model per promptNo per-skill model pin; the session model decidesmedium
agent / mode fieldask, agent, plan, or custom agentNo equivalent; skills compose with agents, not vice versamedium
tools fieldAllowlist of tool names for that runallowed-tools pre-approves narrow command patternsmedium
Variables${input:name}, ${input:name:placeholder}, ${selection}Not supported; skills describe intent, they do not template inputhigh
SurfacesVS Code, Visual Studio, JetBrainsVS Code, VS 2026, Copilot CLI, cloud agent, plus other spec clientslow (gain)

The two rows that trip people up are variables and the model field. If a prompt file leans on ${input:...} or ${selection} to slot in the code the user highlighted, there is no direct skill equivalent, because a skill is guidance the agent reads, not a template the user fills. And if a prompt pins model: Claude Opus 4.7 because the task genuinely needs the stronger model, a skill cannot carry that; you set the model at the session level instead.

Which prompts should not become skills

Before converting anything, sort your prompt files into three buckets. Only the first is a clean migration.

  1. Convention and review prompts. “Review this migration,” “check this PR against our rules,” “scaffold a new endpoint the way we do it.” These are the whole point of skills. Their instructions apply when a matching task shows up, which is exactly what description-based routing is for. Convert these.
  2. On-demand generators the user triggers deliberately. “Generate a conventional-commit message from the staged diff,” “write release notes for this milestone.” These are genuinely user-initiated. You can still make them skills, but set user-invocable: true and lean on the /skills menu, or leave them as prompt files. There is no rule that says every prompt must migrate.
  3. Prompts that depend on ${selection} or ${input}. A prompt whose entire job is “explain the code I have selected” is a templating tool, not reusable guidance. Skills cannot express that. Keep these as prompt files.

Getting this split right up front saves you from writing skills that never fire, or worse, skills that fire on everything. The Copilot cloud agent docs are explicit that always-true, project-wide identity belongs in custom instructions, not in a skill at all; if a prompt file is really just “here is our stack,” it may belong in copilot-instructions.md. See why Copilot ignores repository custom instructions in VS Code for the layer below skills.

Pre-flight checklist

Do these before touching a file:

Migration steps

Here is the conversion, one bucket-1 prompt at a time. The example is a real prompt file that reviews EF Core migrations.

1. Create the skill folder. Every skill is a folder whose name matches the name frontmatter field: lowercase, hyphens only, no leading or trailing hyphen, no consecutive hyphens, max 64 characters.

# Agent Skills spec (agentskills.io), Q2 2026
mkdir -p .github/skills/efcore-migration-review/references

Verify: test -d .github/skills/efcore-migration-review && echo ok.

2. Rewrite the frontmatter. The prompt file’s frontmatter does not carry over field-for-field. Start from this prompt file:

---
description: 'Review an EF Core migration for our data-safety rules'
agent: 'agent'
model: 'Claude Sonnet 4.6'
tools: ['codebase', 'search']
---
Review the migration in ${selection} against our rules: no column drop without
a backfill, reversible Down(), and flag AlterColumn on large tables.

The migrated SKILL.md frontmatter drops agent and model, replaces tools with allowed-tools, and, critically, moves the routing intent into description:

---
name: efcore-migration-review
description: Reviews EF Core migration files in this repo. Use whenever the user asks to add, squash, or review a migration under src/Infrastructure/Migrations, or whenever a generated migration touches AlterColumn, DropColumn, or RenameColumn. Do not use for general EF Core query questions.
license: Proprietary
metadata:
  owner: platform-team
  version: "1.0"
allowed-tools: Read Bash(dotnet ef:*) Bash(git:*)
---

Verify: the name value equals the folder name exactly, and description is under the 1024-character ceiling. A near-miss on the folder name is the single most common reason a skill silently never loads.

3. Convert the body, and delete the variables. The prompt body referenced ${selection}. A skill has no selection to interpolate, so rephrase the instruction as guidance the agent applies to whatever migration is in play:

# EF Core migration review

When the user asks you to add, squash, or review a migration under
`src/Infrastructure/Migrations/`, walk this checklist before producing code:

1. Reject any column drop without a retry-safe backfill in the same `Up()`.
2. Flag `AlterColumn` nullability changes on tables in
   `references/large-table-playbook.md` and require a batched-update plan.
3. Require `Down()` to be a true inverse, not an empty stub.

Verify: read the body back and confirm there are zero ${...} tokens left. Any survivor is a bug, because Copilot will treat it as literal text.

4. Move bundled context into references/. If the prompt file inlined a long “large table playbook,” pull it into references/large-table-playbook.md and point at it from the body with a relative path. Files under references/ load only when the body tells the agent to read them, so the skill body stays small and cheap. Keep references one level deep; multi-hop “see X which references Y” chains lose the model partway through.

Verify: npx skills-ref validate .github/skills/efcore-migration-review exits clean. It catches a mismatched name, an over-length description, and the naming-rule violations.

5. Delete the prompt file. Once the skill validates, remove the source prompt so you are not maintaining both. git rm .github/prompts/efcore-migration-review.prompt.md.

Verify: git status shows the prompt deleted and the skill folder added, and a grep -r efcore-migration-review .github/prompts returns nothing.

6. Repeat per bucket-1 prompt, then commit. Convert each convention prompt into its own single-concern skill rather than one mega-skill. Narrow skills route better and stay quiet on unrelated tasks. The routing patterns are the same ones covered in how to give a Copilot Agent Skill access to your repo conventions.

Verification after the migration

Run this smoke test before you merge:

Test activation with the model your team actually runs. A description that fires reliably on claude-sonnet-4-6 may need to be more explicit on a smaller local model, since smaller models are noticeably worse at skill selection. The same discipline that makes a CLAUDE.md change behavior applies to a skill description: see how to write a CLAUDE.md that actually changes model behaviour.

Rollback plan

This migration is fully reversible, which is a nice change from most. Because you did it on a branch, reverting is git revert on the merge commit, or just delete .github/skills/<name>/ and restore the .prompt.md from history:

git checkout HEAD~1 -- .github/prompts/efcore-migration-review.prompt.md
git rm -r .github/skills/efcore-migration-review

There is no server-side state to unwind and no cache to purge beyond restarting the chat session. That said, do not run both formats for the same concern in parallel for long; a prompt file and a skill enforcing the same rule will step on each other, and you lose the clarity of a single source.

Gotchas we hit

Real issues from converting a mid-size repo:

The honest summary: the mechanical conversion is quick, but the value is entirely in the description. A migrated skill with a lazy one-line description is strictly worse than the prompt file you deleted, because at least the prompt file fired when you asked it to. Spend the time you save on the mechanics writing descriptions that route.

Sources

Comments

Sign in with GitHub to comment. Reactions and replies thread back to the comments repo.

< Back