Языки: EN RU

Skills — portable expertise

In the previous article we covered subagents — a context isolation mechanism for self-contained tasks. Skills solve a different problem: how do you reuse complex expertise (checklists, procedures, conventions) without polluting the context window every time, and without spinning up a subagent just to carry a set of instructions?

The answer is progressive disclosure: Claude always knows what a skill can do (its description), but loads how to do it (the SKILL.md body) only at the moment of invocation. A lengthy deploy checklist, review guidelines, an API specification — all of it sits on disk and consumes zero tokens until it's actually needed.


Anatomy of a SKILL.md

A skill is a directory containing a mandatory SKILL.md file. The directory is the unit: scripts, templates, and examples can live alongside the file and become part of the skill.

~/.claude/skills/pr-checklist/
├── SKILL.md          # main instructions (required)
├── security.md       # detailed security checklist
├── examples/
│   └── good-pr.md    # example of a good PR for reference
└── scripts/
    └── check-size.sh # script for checking PR size

SKILL.md itself has two parts: YAML frontmatter between --- delimiters and a regular Markdown body.

---
name: pr-checklist
description: Code review checklist for pull requests. Use when user asks to review a PR or code changes.
when_to_use: Triggered by "review PR", "check this PR", "pre-merge checklist".
disable-model-invocation: false
allowed-tools: Read Glob Grep Bash(gh *)
---


::widget{id="rc-2"}


::widget{id="rc-1"}

## Pull request review checklist

Before approving, verify:

- [ ] Tests cover the new logic
- [ ] No hardcoded secrets or credentials
- [ ] Error handling is explicit
- [ ] API changes are backward-compatible

For security details, see [security.md](security.md).
For size check, run: `bash scripts/check-size.sh`

Notice the last few lines: SKILL.md is intentionally concise — it merely points to the details. Claude reads security.md only when necessary; the main file stays lean.

Rule of thumb: keep SKILL.md under ~500 lines. Put the details in supporting files.

Проверь себя
Look at the skill directory structure: SKILL.md, security.md, examples/good-pr.md, scripts/check-size.sh. Which of these files will Claude load into context at session start?

Where to store skills

Storage location determines visibility:

LevelPathVisibility
Personal~/.claude/skills/<name>/SKILL.mdAll your projects
Project.claude/skills/<name>/SKILL.mdThis project only
Plugin<plugin>/skills/<name>/SKILL.mdWherever the plugin is installed
EnterpriseManaged settingsAll users in the organization

Project-level skills are worth committing to the repository — the whole team gets them automatically. If you still have legacy custom commands in .claude/commands/, they continue to work, but skills are preferred: they support supporting files and a richer set of frontmatter fields.

Claude Code watches skill directories in real time: adding or editing a SKILL.md takes effect in the current session without a restart.


Progressive disclosure in detail

The mechanism operates on three levels:

flowchart TD A["Session Start"] --> B["Level 1: Discovery\nname + description of all skills\n(a few lines per skill)"] B --> C{"Does the user request\nmatch the description?"} C -- Yes --> D["Level 2: Activation\nLoading the SKILL.md body\n+ executing !`commands`"] C -- No --> E["Skill is not loaded\nno tokens are spent"] D --> F["Level 3: Instructions in context\nRemain until end of session\n(or until compaction)"] F --> G["Claude works\nwith up-to-date data"]
flowchart TD
    A["Session Start"] --> B["Level 1: Discovery\nname + description of all skills\n(a few lines per skill)"] 
    B --> C{"Does the user request\nmatch the description?"}
    C -- Yes --> D["Level 2: Activation\nLoading the SKILL.md body\n+ executing !`commands`"]
    C -- No --> E["Skill is not loaded\nno tokens are spent"]
    D --> F["Level 3: Instructions in context\nRemain until end of session\n(or until compaction)"] 
    F --> G["Claude works\nwith up-to-date data"]
Three levels of progressive disclosure: from description to full skill content

Level 1 — Discovery. When a session starts, Claude receives only the names and descriptions of all skills (name, description, when_to_use fields) in its context. That's a few lines per skill — minimal overhead even with dozens of skills installed.

Level 2 — Activation. When a user's request matches a skill's description, Claude loads the full SKILL.md body. Dynamic content (shell commands) is executed right then and inlined into the body before Claude ever sees it.

Level 3 — Instructions persist. Once loaded, the skill body remains in context for the rest of the session (or until auto-compaction). During compaction, Claude Code preserves the first 5,000 tokens of each activated skill; if many skills have been activated, less recently used ones may be dropped.

To prevent a skill from activating automatically (manual /name invocation only), add disable-model-invocation: true. Conversely, user-invocable: false hides the skill from the / menu but lets Claude still load it on its own — useful for background conventions.

---
name: deploy
description: Deploy the application to production
disable-model-invocation: true  # only /deploy, no surprises
allowed-tools: Bash(./deploy.sh *)
---
Проверь себя
You need a `/deploy` skill that Claude should never invoke on its own — only manually via a command. Which frontmatter field ensures this, and why is simply omitting the description not enough?

Dynamic context: ` !command `

This is arguably the most powerful feature of skills. The ` !<command> ` syntax runs a shell command before Claude sees the skill's content. The output is inserted directly into the body:

---
name: diff-review
description: Review the current git diff for risks and quality issues.
---


::widget{id="rc-3"}

## Current changes

!`git diff HEAD`

## Your task

Analyze the diff above. Flag: missing error handling, hardcoded values,
breaking API changes, missing tests. Be specific about line numbers.

When someone invokes /diff-review, Claude Code runs git diff HEAD, substitutes the output in place of the ! line, and only then passes the text to Claude. The model works with live data rather than its own assumptions.

For multi-line commands there is a block form:

````markdown

```!

node --version

npm list --depth=0

git log --oneline -5

````

Important nuance: shell commands are executed by the preprocessor, not by Claude. The model sees only the textual result — it has no knowledge that a `!` directive was ever there.

---

### Arguments

Skills accept arguments via `$ARGUMENTS` (everything after the skill name), `$ARGUMENTS[N]`, or the shorthand `$N`:

name: fix-issue

description: Fix a GitHub issue by number

disable-model-invocation: true

arguments: [issue, priority]

allowed-tools: Bash(gh *) Read Glob


Fix GitHub issue #$issue with priority $priority.

1. Read the issue: !gh issue view $issue

2. Find affected files

3. Implement the fix following our conventions

4. Write tests and create a commit


Invoking `/fix-issue 234 high` substitutes `234` for `$issue` and `high` for `$priority`. And note: `` !`gh issue view $issue` `` is executed **with the argument already resolved** — the actual text of issue #234 lands in context.

Another useful variable is `${CLAUDE_SKILL_DIR}`: the absolute path to the skill's directory. This makes it easy to reference scripts inside the skill regardless of the current working directory:

!bash ${CLAUDE_SKILL_DIR}/scripts/check-size.sh


---

### context: fork — running a skill in an isolated subagent

A skill can run not in the main conversation but in an isolated subagent — add `context: fork` to enable this. The SKILL.md body becomes the subagent's task:

name: deep-explore

description: Research a module or concept in depth

context: fork

agent: Explore


Research $ARGUMENTS thoroughly:

1. Find all relevant files using Glob and Grep

2. Read and understand the code, tests, and docs

3. Summarize findings with specific file references and line numbers


When `/deep-explore auth module` is invoked, Claude Code launches the built-in Explore subagent with the SKILL.md body as its task. The result is returned to the main conversation without cluttering it with intermediate searches. The `agent` field accepts the name of any built-in subagent (`Explore`, `Plan`, `general-purpose`) or a custom subagent defined in `.claude/agents/`.

This is an interesting intersection with Subagents: `context: fork` is a convenient wrapper when the task is well-described by a static prompt. For more flexible scenarios that require a custom system prompt or conditional logic, a full subagent is the better choice.

::widget{type="checkpoint" id="cp-3"}

---

### Practical example: a release skill

.claude/skills/release/

├── SKILL.md

├── changelog-template.md

└── scripts/

├── bump-version.sh

└── check-ci.sh


name: release

description: Prepare and publish a release

disable-model-invocation: true

allowed-tools: Bash(git ) Bash(npm ) Bash(./scripts/*) Read Write


Release context

  • Current version: !node -e "console.log(require('./package.json').version)"
  • Latest tag: !git describe --tags --abbrev=0
  • Changes since tag: !git log $(git describe --tags --abbrev=0)..HEAD --oneline
  • CI status: !bash ${CLAUDE_SKILL_DIR}/scripts/check-ci.sh

Instructions

Prepare release $ARGUMENTS (e.g. "patch", "minor", "major"):

1. Verify CI is green (see above)

2. Bump version: bash ${CLAUDE_SKILL_DIR}/scripts/bump-version.sh $ARGUMENTS

3. Fill in changelog-template.md based on the commits above

4. Commit and tag: git commit -am "chore: release vX.Y.Z" && git tag vX.Y.Z

5. Prompt user to confirm before pushing


The first lines of the body contain live data about the project's current state. Claude does not guess the version or read extra files: everything is inlined before the model begins work. Scripts are stored in `scripts/` and invoked via `${CLAUDE_SKILL_DIR}`, so they work correctly regardless of which directory the session was opened in.

---

### When to use a skill vs. a subagent?

Both tools extend Claude's capabilities, but in different ways:

**Use a skill when:**
- You need to reuse instructions or conventions within the main context
- You want Claude to apply knowledge *in the flow* of work rather than as a separate task
- You have supporting files (templates, scripts) that belong together
- You want to invoke it via `/name` or have Claude activate it automatically

**Use a subagent when:**
- The task is self-contained and its result is not needed in the main conversation
- You need full context isolation
- You require a custom system prompt and toolset
- You plan to run multiple instances in parallel

The two mechanisms complement each other: a subagent with a `skills:` field receives skills as preloaded context, and a skill with `context: fork` runs inside an isolated subagent.

::widget{type="interactive" id="int-1"}

---

## See also

- Slash commands: built-in and custom — the lightweight predecessor to skills; `.claude/commands/` still works
- Subagents and context isolation — full context isolation for self-contained tasks; `context: fork` bridges both worlds
- Hooks — lifecycle events — deterministic shell commands on lifecycle events; complement skills where enforced logic is required
- Plugins and marketplace — how to bundle skills together with subagents and hooks into a single reusable package
- Choosing between a command, skill, subagent, MCP, or hook — the full decision matrix
- CLAUDE.md and the memory system — how persistent CLAUDE.md context differs from the "lazy" loading of skills
- Managing the context window — why progressive disclosure beats stuffing everything into CLAUDE.md
- Dynamic workflows and agent orchestration — scaling skills up to full multi-agent pipelines

Источники

  1. Extend Claude with skills — Claude Code Documentation
  2. Agent Skills: Progressive Disclosure as a System Design Pattern — Swirl AI Newsletter
  3. Claude Skills: The Complete 2026 Guide — Build Fast With AI
  4. Agent Skills — Claude API Docs