Skip to content

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-db SDK, 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.

Click any part of this example skill to see what it controls - and which tools honor it:

SKILL.md
------
description

The trigger. Every skill’s description is loaded into context at session start; the model matches your intent against it to decide when to pull the body in. It’s the most important line in the file - write it as a "use when…" hint, not a summary.

Capped at 1024 characters where a limit is documented (OpenCode, Copilot).

  • Claude Coderequired · drives auto-invocation
  • Codexrequired · drives auto-invocation
  • OpenCoderequired · 1–1024 chars
  • Cursorrequired · drives auto-select
  • Copilotrequired · ≤1024 chars

This is the part that trips people up. Skills overlap with several other primitives. Here’s how to decide:

You want to…Reach forNot
Run a fixed sequence of commands you’ll type by nameSlash commandSkill
Encode “how we do X here” as knowledge the model uses contextually, without you typing a commandSkillSlash command
Bundle a long, multi-file procedure with helper scripts and reference docsSkillSlash command (too thin)
Isolate a side-quest (research, audit, search) into its own context windowSubagentSkill
Block, gate, or rewrite tool calls deterministicallyHookSkill
Persist facts across every session in this repoRulesSkill
Add a new data source or external tool (Jira, Postgres, Figma)MCP serverSkill

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.

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: deploy
description: 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.

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.
  • paths glob scoping - Cursor only.
  • The folder name itself. Cursor and OpenCode require the name field 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.

AspectClaude CodeCodexOpenCodeCursorCopilotPi
FormatSKILL.md + assetsSKILL.md + assetsSKILL.md + assets (narrower frontmatter)SKILL.md + assetsSKILL.md + assetsSKILL.md + assets
Cross-tool portableYes - .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 CursorYes (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-loadsYes (by description)Yes (by description)Yes (by description)Yes (by description)Yes (by description)Yes (by description)
Can be hidden from modeldisable-model-invocation: trueagents/openai.yamlpolicy.allow_implicit_invocation: false-disable-model-invocation: truedisable-model-invocation: truedisable-model-invocation: true
Subagent access controlskills: 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)
  • 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: fork in 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.