Skills
It’s the fourth time this week. You’re reviewing a PR and you paste the same three sentences into the agent: “check the migration is reversible, confirm the feature flag is wired, make sure the integration test hits the real DB and not a mock.” You wrote it down once in a Notion doc. You wrote it again in a comment on your dotfiles. You’ve now typed it into three separate sessions because you keep forgetting where you saved it.
The instructions aren’t the problem. Reaching them at the right moment is the problem. That’s the gap skills exist to close: write the instructions once, attach a description that tells the model when to pull them in, and from then on the knowledge shows up the moment the situation does - without you remembering it exists.
Concretely, a skill is a folder you drop into your project (or your home directory) containing a SKILL.md file. The frontmatter describes when the skill should fire - a name, a one-line description, a “use this when…” hint. The body is whatever the agent needs to actually do the thing: a checklist, a procedure, domain knowledge, a workflow.
The agent reads every skill’s description at session start, but only loads a skill’s body when it decides the description matches what you just asked for. So a skill is cheap to keep around - it costs almost no context until the moment it’s relevant. In the three cost profiles, a description is always-loaded and a body is on-demand.
Real-world examples of what teams put in skills:
- A release checklist - every step of cutting a version, in order, with the exact commands. Fires when you say “let’s ship.”
- A library cheat-sheet - “when working with our internal
flux-dbSDK, here’s the gotchas, the right patterns, the deprecated APIs to avoid.” Fires when the model touches that import. - An incident runbook - “when prod is down, here’s the diagnostic order, the dashboards to check, the rollback procedure.” Fires when you paste a stack trace.
- A style guide - “how we write commit messages, name PRs, structure ADRs.” Fires when the model is drafting one.
- A domain-specific procedure - “how we model multi-tenant data in this codebase,” “how we handle timezones,” “how we write tests for the payments module.” Fires when the model is about to do that work.
The pattern: anything you’ve explained more than twice, anything that lives in a Notion doc nobody opens, anything where the failure mode is “the model didn’t know we do it this way here.” Skills turn institutional knowledge into something the agent reaches for on its own.
The test: if the knowledge is procedural and you can’t predict which session will need it, it wants to be a skill. A rule would pay for it in every session whether or not it comes up, and a slash command would sit there waiting for you to remember it exists.
Anatomy of a skill
Section titled “Anatomy of a skill”Click any part of this example skill to see what it controls - and which tools honor it:
Why this and not…
Section titled “Why this and not…”This is the part that trips people up. Skills overlap with several other primitives. Here’s how to decide:
| You want to… | Reach for | Not |
|---|---|---|
| Run a fixed sequence of commands you’ll type by name | Slash command | Skill |
| Encode “how we do X here” as knowledge the model uses contextually, without you typing a command | Skill | Slash command |
| Bundle a long, multi-file procedure with helper scripts and reference docs | Skill | Slash command (too thin) |
| Isolate a side-quest (research, audit, search) into its own context window | Subagent | Skill |
| Block, gate, or rewrite tool calls deterministically | Hook | Skill |
| Persist facts across every session in this repo | Rules | Skill |
| Add a new data source or external tool (Jira, Postgres, Figma) | MCP server | Skill |
The defining trait of a skill: the model decides when to use it, based on a description you wrote. A slash command waits for you to type its name. A hook fires on an event. A skill activates on intent.
How it works in each tool
Section titled “How it works in each tool”Locations (precedence: managed > user > project):
.claude/skills/<name>/SKILL.md- project~/.claude/skills/<name>/SKILL.md- user- Plugin-bundled skills (namespaced as
/plugin:name)
Frontmatter fields:
---name: deploydescription: Run the team's deploy checklist.disable-model-invocation: false # if true, only invokable via /<name>context: fork # optional: run in an isolated subagent---Loading model:
- Skill descriptions load at session start so Claude knows what’s available.
- Full skill body loads on invocation (manual
/<name>or model-decided). - With
disable-model-invocation: true, the skill is invisible until you type/<name>- useful for skills with side effects.
In subagents: a subagent’s skills: frontmatter field preloads skills into its context - different from on-demand loading in the main session.
Locations (discovered in scope order):
.agents/skills/<name>/SKILL.md- project (walked up from cwd to repo root)~/.agents/skills/<name>/SKILL.md- user/etc/codex/skills/- admin- Skills bundled with Codex by OpenAI - system
Invocation: type /skills to browse, or $<skill-name> to mention a skill directly in a prompt. Codex can also auto-select skills whose descriptions match the current task.
Standard: Codex implements the Agent Skills open standard. A skill folder with SKILL.md is portable to and from Claude Code for the basic shape; tool-specific extensions don’t cross-map.
Frontmatter fields: only name and description are required. Codex’s SKILL.md does not read Claude Code’s disable-model-invocation, context: fork, or allowed-tools frontmatter keys.
Auto-loader control: Codex’s equivalent of disable-model-invocation is a sidecar file agents/openai.yaml inside the skill, setting policy.allow_implicit_invocation: false (defaults to true). With that off, Codex won’t auto-invoke the skill - $<skill-name> still works.
Skill directory layout: SKILL.md (required), optional scripts/, references/, assets/, optional agents/openai.yaml (UI metadata, invocation policy, declared MCP/tool dependencies).
Locations: OpenCode reads skills from its own paths and from the Claude Code and Codex paths, walking up from cwd to the git worktree root:
- Project:
.opencode/skills/<name>/SKILL.md,.claude/skills/<name>/SKILL.md,.agents/skills/<name>/SKILL.md - Global:
~/.config/opencode/skills/<name>/SKILL.md,~/.claude/skills/<name>/SKILL.md,~/.agents/skills/<name>/SKILL.md
Frontmatter fields: name (1-64 chars, must match the directory) and description (1-1024 chars) are required; license, compatibility, and metadata are optional. There’s no OpenCode equivalent to disable-model-invocation or context: fork in the frontmatter.
Invocation: skills are exposed through a built-in skill tool - the agent sees the available skill names and descriptions and calls the tool to load a skill’s body on demand.
Because OpenCode reads .claude/skills/ and .agents/skills/ directly, a skill folder authored for Claude Code or Codex generally works in OpenCode unchanged; tool-specific frontmatter keys are simply ignored.
Cursor has a first-class Agent Skills primitive (shipped in Cursor 2.4). Skills are folders containing a SKILL.md (markdown + YAML frontmatter), discovered from:
- Project:
.cursor/skills/,.agents/skills/(plus legacy.claude/skills/,.codex/skills/) - User:
~/.cursor/skills/,~/.agents/skills/
Required frontmatter: name (lowercase-hyphenated, must match folder), description (the agent uses this to decide when to load the skill). Optional: paths (glob scoping), disable-model-invocation (forces explicit invocation only - behaves as a slash command), metadata.
Invocation: automatic via description-match, manual via /skill-name in chat, or @skill-name to attach without invoking.
Standard: Cursor positions Skills as an “open standard” and points at agentskills.io; the format is wire-compatible with Anthropic’s skill spec, which is why .claude/skills/ is a recognised fallback path.
Notepads, Cursor’s earlier reusable-prompt primitive, was deprecated in October 2025. The replacement story is Skills (for invokable procedures) plus Custom Commands (for prompt templates). A separate Prompts Library has been floated in the community but is not an official Cursor surface as of 2026-05-21.
Copilot has a real Agent Skills primitive, added in late 2025 / early 2026 and explicitly designed as a cross-tool open spec.
Skills are folders containing a SKILL.md file with YAML frontmatter plus optional scripts and resources. Required frontmatter: name (lowercase, hyphens, ≤64 chars), description (≤1024 chars). Optional: argument-hint, user-invocable, disable-model-invocation, context: inline | fork.
Locations (VS Code Chat):
- Project:
.github/skills/,.claude/skills/, or.agents/skills/ - Personal:
~/.copilot/skills/,~/.claude/skills/, or~/.agents/skills/ - Custom:
chat.agentSkillsLocationssetting
Invocation. Skills appear in the / menu (unless user-invocable: false) and can be auto-loaded by the model when relevant (unless disable-model-invocation: true). The dual model-invokable + user-invokable design matches Claude Code.
Standard. The format is explicitly compatible with Claude Code’s skills and the broader agentskills.io open spec - a SKILL.md written for Claude Code can drop into .github/skills/ and work in Copilot.
Related but distinct:
- Prompt files (
.github/prompts/*.prompt.md) - single-file, single-purpose, no bundled assets. The lightweight cousin of skills. - Custom agents (
.agent.md) - a persona with a model + toolset; skills are procedures invokable by any agent.
Adopt skills, not prompts, for anything multi-file or with bundled scripts.
Pi implements the standard Agent Skills format - a SKILL.md per skill, loaded on-demand rather than baked into context up front (progressive disclosure: the agent sees names and descriptions, and pulls the full body only when it decides a skill is relevant).
Locations, walking up from cwd:
- Project:
.pi/skills/<name>/SKILL.md,.agents/skills/<name>/SKILL.md - Global:
~/.pi/agent/skills/<name>/SKILL.md,~/.agents/skills/<name>/SKILL.md - Bundled inside installed Pi packages (see Plugins)
Invocation: /skill:name explicitly, or automatically when the model judges the skill’s description matches the task at hand.
A deliberate looseness: Pi doesn’t require a skill’s name field to match its directory name, which makes it easy to drop in a skill authored for another tool without renaming folders. The enableSkillCommands setting toggles whether skills also show up as /skill:name slash commands, if you want skills available only via model-initiated invocation.
The cross-tool standard
Section titled “The cross-tool standard”Several tools now support the SKILL.md format, but the rollout dates and compatibility details differ by product and release. Treat the comparison below as a current tool-by-tool map, not as a historical promise.
OpenCode’s skills documentation explicitly lists .claude/skills/ and .agents/skills/ among its search paths. Codex’s current skills documentation establishes the shared Skills standard and where Skills are available, but does not establish a .claude/skills/ fallback. Keep directory paths tool-specific unless that tool documents the path.
The practical upshot is that there is no single directory all six read. Use the comparison below as a tool-by-tool map, not as a promise that a path is portable across every release.
That is the unusual part of skills as a primitive: a skill you write today is largely portable across tools. You aren’t authoring a Claude Code skill or a Codex skill. You’re authoring a skill, and then choosing which directory to drop it in - see the Comparison for each tool’s paths.
What crosses the boundary is narrower than the whole folder, though, and the line is worth knowing before you write one.
Travels unchanged: the required frontmatter name and description, the Markdown body whatever is in it, and optional bundled scripts/, references/, and assets/ alongside SKILL.md. Copilot and OpenCode both cap name at 64 characters and description at 1024; the other tabs don’t state a limit, so write to the tighter pair.
Stays behind:
disable-model-invocation- four of the six honour the key; see the Comparison for what Codex and OpenCode do instead.context: fork- Claude Code and Copilot only.allowed-tools- Claude Code only; Codex ignores it outright.pathsglob scoping - Cursor only.- The folder name itself. Cursor and OpenCode require the
namefield to match the directory it sits in; Pi deliberately doesn’t, which is what makes dropping a foreign skill folder into Pi painless and what makes the reverse trip occasionally fail.
That portability is why teams converging on multiple coding agents tend to push knowledge into skills rather than slash commands or per-tool config - it’s the one extension format that survives the choice of CLI.
Comparison
Section titled “Comparison”| Aspect | Claude Code | Codex | OpenCode | Cursor | Copilot | Pi |
|---|---|---|---|---|---|---|
| Format | SKILL.md + assets | SKILL.md + assets | SKILL.md + assets (narrower frontmatter) | SKILL.md + assets | SKILL.md + assets | SKILL.md + assets |
| Cross-tool portable | Yes - .claude/skills/ is read by OpenCode, Cursor, and Copilot (Codex and Pi read .agents/skills/) | Yes - .agents/skills/ is read by OpenCode, Cursor, Copilot, and Pi; .codex/skills/ by Cursor | Yes (reads .claude/skills/ and .agents/skills/ natively; non-OpenCode frontmatter ignored) | Yes (reads .claude/skills/, .codex/skills/, .agents/skills/) | Yes (reads .claude/skills/, .agents/skills/) | Yes - reads standard SKILL.md; skill name need not match its directory name |
| Project path | .claude/skills/ | .agents/skills/ | .opencode/skills/ (+ reads .claude/, .agents/) | .cursor/skills/ (+ reads .agents/, .claude/, .codex/) | .github/skills/ (+ reads .claude/, .agents/) | .pi/skills/ + .agents/skills/ (+ user-global ~/.pi/agent/skills/) |
| Invocation | /<name> | /skills, $<skill> | Built-in skill tool, model-selected | /skill-name, @skill-name, or auto | / menu, or auto | /skill:name, or auto |
| Model auto-loads | Yes (by description) | Yes (by description) | Yes (by description) | Yes (by description) | Yes (by description) | Yes (by description) |
| Can be hidden from model | disable-model-invocation: true | agents/openai.yaml → policy.allow_implicit_invocation: false | - | disable-model-invocation: true | disable-model-invocation: true | disable-model-invocation: true |
| Subagent access control | skills: field preloads | [[skills.config]] in agent TOML (an earlier enable/disable override bug, openai/codex#14161, was fixed in Codex CLI 0.115.0) | permission.skill rules per agent gate which skills are visible | - | - | N/A (no built-in subagents) |
Name collisions
Section titled “Name collisions”- Don’t confuse a skill (reusable content bundle) with a subagent (isolated worker). They can combine - a skill can run inside a forked subagent context via
context: forkin Claude Code - but they solve different problems. See subagents. - Skill and slash command name the same surface in some tools and different ones in others. That comparison lives in slash commands, which is where you’ll be standing when it matters.
Your first skill costs one folder. Take the instruction you last pasted into a session for the third time, and put it in .agents/skills/<name>/SKILL.md - per the Comparison, the widest-read project path, covering every tool in scope except Claude Code, which wants the same folder under .claude/skills/. Then spend the effort on the description, not the body. The body only gets read after the model has already decided to load it. The description is what makes that decision, and it’s the only part sitting in the window beforehand.