Skills — User-Invocable Workflows
Bundled prompt + tool + instruction packages that Claude Code can invoke. How they differ from sub-agents and custom slash commands.
What it is#
A skill is a packaged workflow that Claude Code can invoke either by user request or by intent-matching. A skill bundles three things:
- A description — short text the harness uses to match the user’s request to the skill. “Review a pull request”, “scan transcripts for common Bash commands”, “configure the Claude Code harness via settings.json”.
- Instructions — the body of the skill: what the model should do when it runs, in what order, with what conventions and guardrails.
- Optional context — files, examples, templates the skill can reference. Some skills include scripts that get executed; some are pure prompt bundles.
Skills are the third customisation layer in Claude Code, alongside slash commands and sub-agents. They sit between a slash command (user types a name) and a sub-agent (runs in its own context). The user can invoke a skill explicitly (“use the security-review skill”) or the harness can suggest one based on the user’s request — which is the part that makes skills feel different from the other two layers.
When to use it#
Reach for a skill when:
- The workflow is invocable by intent, not just by name. “When the user asks me to review a PR, run this checklist” — that is a skill. If the only way to trigger it is to type a slash, a slash command might be enough.
- The workflow has structure but should still feel like a normal turn. Skills run in the parent session’s context (unlike sub-agents) and produce normal model output. They are right when you want a packaged workflow with no isolation boundary.
- The workflow may include scripts or auxiliary files. Skills can include reference templates, example payloads, helper scripts. A slash command is a single markdown file; a skill is a directory.
- Multiple skills should coexist with clear descriptions. A project with eight skills gives the model a small menu of workflows to choose from. The harness picks the best match based on the request.
Avoid skills when:
- The workflow is a one-liner. A skill is overkill for “run pnpm test and tell me the result” — that’s just a prompt.
- The workflow needs context isolation. A long investigation that should not bloat the parent’s context belongs in a sub-agent, not a skill.
- The workflow runs without user prompting. Skills run when invoked. For “always do X on tool call Y”, use a hook.
How it works#
Skill layout#
A skill is a directory under ~/.claude/skills/<name>/ (user-scoped) or <project>/.claude/skills/<name>/ (project-scoped). The directory contains at minimum:
~/.claude/skills/review/├── SKILL.md # frontmatter + instructions├── checklist.md # referenced template (optional)└── examples/ └── good-review.md # auxiliary file (optional)The SKILL.md file has YAML frontmatter and a markdown body. Frontmatter fields commonly used:
---name: reviewdescription: Review a pull request — diff, concerns, verdictmodel: opusallowed-tools: [Bash, Read, Grep, Glob, WebFetch]---The description is what the harness shows to the model. It is the matcher for “the user wants X, which skill fits?”.
Invocation paths#
Three ways a skill gets invoked:
- Explicit user request. “Use the review skill on PR 1234.” The harness picks
reviewand runs it. - Slash command. A
/reviewcommand whose body says “use thereviewskill” — the slash is just a typed shortcut to the skill. - Intent matching. The user asks something whose intent matches a skill’s description; the harness suggests or auto-invokes the skill. The exact behaviour here depends on settings — some skills are auto-invoked, others require confirmation.
When invoked, the harness loads the skill’s instructions into the model’s prompt for the current turn. The model executes the workflow inline — same context window, same tool budget, no sub-process. The skill body shapes what the model does; the model’s normal output is what the user sees.
Skills vs sub-agents vs slash commands#
This is the trio worth keeping straight:
- Slash command — typed by name, runs a prompt the model executes inline. No isolation. No tool allowlist beyond the session’s defaults.
- Skill — invoked by name or by intent, runs instructions the model executes inline. No isolation. Can declare an
allowed-toolslist. - Sub-agent — invoked via the Task tool, runs in a fresh context window. Full isolation. Tool allowlist is the safety boundary.
A workflow that should run in the parent’s context and be reachable by intent is a skill. A workflow that should be typed by name is a slash command. A workflow that should run in isolation is a sub-agent.
Configuration#
Where skills live#
| Layer | Path | Available in |
|---|---|---|
| User-scoped | ~/.claude/skills/<name>/SKILL.md | Every session |
| Project-scoped | <project>/.claude/skills/<name>/SKILL.md | This project only |
Project-scoped skills committed to the repo give every contributor the same workflows. The pattern matches slash commands and sub-agents.
Frontmatter fields#
---name: security-reviewdescription: Complete a security review of the pending changes on the current branchmodel: opus # optional overrideallowed-tools: [Bash, Read, Grep, Glob]---name— the canonical name. Used in explicit invocations and slash shortcuts.description— the matcher and the help-text. Write it as if it will be matched by intent.model— optional override for which model runs the skill.allowed-tools— optional tool allowlist. The skill body cannot exceed this list.
How descriptions get matched#
The harness compares the user’s request against each available skill’s description. Good descriptions are:
- Active. “Review a pull request” beats “PR review tool”.
- Specific. “Configure the Claude Code harness via settings.json” beats “configure things”.
- Disambiguating. Two skills with overlapping descriptions force the harness to ask which one — costly. Keep them distinct.
Examples#
A security-review skill#
<project>/.claude/skills/security-review/SKILL.md:
---name: security-reviewdescription: Complete a security review of the pending changes on the current branchmodel: opusallowed-tools: [Bash, Read, Grep, Glob]---
Run a security pass over the diff between the current branch and `main`.
Steps:1. Run `git diff main...HEAD` and read the full diff.2. For each modified file, run Grep for these patterns: - `eval(`, `Function(`, `child_process.exec` - `dangerouslySetInnerHTML` - `process.env.*` without validation - SQL constructed by string concatenation3. Read any files where matches appear and assess.
Produce a report with:- One-line verdict: pass / concerns / blocked.- Per-finding: file + line + the risk + the suggested fix.- A summary of patterns that did not appear (negative findings).
Do not modify any files. Do not commit.The user types “security review the current branch” — the harness matches the description and invokes the skill.
An init skill#
~/.claude/skills/init/SKILL.md:
---name: initdescription: Initialize a new CLAUDE.md file with codebase documentation---
You are creating a CLAUDE.md for the current repository.
Steps:1. List the top-level files and directories. Read package.json (or pyproject.toml, Cargo.toml, etc.).2. Identify the build system, the test runner, the linter.3. Look for unusual conventions in scripts/, config files, or .github/workflows/.4. Draft a CLAUDE.md with sections: - Commands - Architecture (only things not obvious from the file tree) - Conventions - Gotchas (only if any genuine ones surface)
Style:- Concise. Each section under 200 lines.- No emojis.- The reader is a competent engineer new to the repo.
Write the file to ./CLAUDE.md only after the user has reviewed the draft.A skill with a clear single purpose. Description matches “set up CLAUDE.md” style requests.
A skill that runs a script#
Some skills include an executable script in the directory and reference it from the body:
---name: fewer-permission-promptsdescription: Scan transcripts and add a prioritized allowlist to settings.jsonallowed-tools: [Bash, Read, Edit]---
Run the bundled scanner to find common Bash commands across recent transcripts:
```bashbash ~/.claude/skills/fewer-permission-prompts/scan.shBased on the output, propose additions to the project’s .claude/settings.json permissions.allow list. Show the diff before applying.
The script does the heavy data work; the skill body does the human-facing reasoning and the edit proposal.
### Intent matching in action
The user types: "set up a recurring task to check the deploy every 5 minutes". The harness sees the `schedule` skill ("Create, update, list, or run scheduled remote agents") and the `loop` skill ("Run a prompt or slash command on a recurring interval"). Both might fit; the harness picks the closer match — likely `loop` for the in-session case, `schedule` for the cron case — or asks.
## Gotchas
- **Descriptions are how skills get found.** A skill with a vague description never gets auto-invoked. Write the description as if a stranger had to decide whether it applies.- **Two overlapping descriptions cause friction.** The harness has to disambiguate, which means more prompts to the user. Curate the skill set so each description is distinct.- **Skills run inline — no context isolation.** A skill that reads a hundred files dumps a hundred files into the parent's context. For isolated work, use a sub-agent instead.- **`allowed-tools` is enforced.** A skill cannot call tools outside its allowlist even if the body asks. This is a feature for safety; an inconvenience if the allowlist is too tight.- **Skill bodies are still prompts.** Anything in the body is text the model reads. Hostile or malformed content in a skill body could mislead the model. Audit committed skills the same way you audit committed code.- **Project skills travel with the repo.** Every contributor gets every project skill. Coordinate before merging a new one.- **A skill is not a substitute for a slash command for muscle-memory cases.** If you type `/review` ten times a day, the slash is still the right entry point — wire the slash to invoke the skill if you want both surfaces.- **No interactive prompts.** Like slash commands, skills cannot stop and ask. Design for "make the sensible call, surface the rationale" instead of "stop and ask".
<Callout type="info" title="When the harness suggests a skill, the description did the work"> The moment a user types "review this PR" and Claude Code says "I'll use the review skill" — that handoff is the description doing its job. A well-written description is the single highest-leverage line in the entire skill file. Spend more time on it than on the body; the body only runs if the description matches.</Callout>
### Skill vs slash vs sub-agent at a glance
<Compare> <div slot="left"> <strong>Skill</strong> <ul> <li>Invoked by name or by intent</li> <li>Runs inline in the parent session</li> <li>Tool allowlist via <code>allowed-tools</code></li> <li>Description is the matcher</li> <li>A directory with markdown + optional scripts</li> </ul> </div> <div slot="right"> <strong>Slash command / Sub-agent</strong> <ul> <li>Slash: invoked by typed name only</li> <li>Sub-agent: invoked via the Task tool, runs in isolated context</li> <li>Slash: a single markdown file; Sub-agent: a directory with config</li> <li>Slash is a typed shortcut; sub-agent is an isolated worker</li> <li>Skill sits between: discoverable like slash, structured like sub-agent</li> </ul> </div></Compare>
## Related features
- [Slash Commands](/claude-code/slash-commands)- [Sub-Agents](/claude-code/sub-agents)- [Writing Custom Slash Commands](/claude-code/writing-custom-slash-commands)- [Custom Sub-Agents](/claude-code/custom-sub-agents)- [Settings and Configuration](/claude-code/settings-and-configuration)
<Spoiler summary="The matcher quality determines whether a skill earns its place"> An unused skill is worse than no skill — it bloats the description list the harness has to consider on every request. The test for whether a skill is paying its rent is simple: over the last week, has the harness invoked it without an explicit user request? If yes, the description is doing real work and the skill belongs. If not, either the description is wrong or the skill should be a slash command instead. Prune ruthlessly; a tight set of five well-described skills beats fifteen mediocre ones.</Spoiler>