Writing Your First Skill
A step-by-step guide to packaging a reusable procedure as a Claude Code skill that loads exactly when it's needed.
A skill is the cheapest way to give Claude Code a capability it doesn't already have — a recurring procedure, a house convention, a multi-step workflow — without bloating your context or your CLAUDE.md. Done well, a skill sits dormant until the moment its task comes up, then loads its instructions, runs the work, and gets out of the way. Done poorly, it either never triggers or it loads on every unrelated request and burns context you needed elsewhere.
This guide walks through authoring your first one: where it lives, the frontmatter that controls when it fires, the progressive-disclosure model that makes skills cheap, and how to bundle scripts and extra files when one Markdown page isn't enough.
What a SKILL.md actually is
A skill is a folder containing a SKILL.md file. Project skills live in .claude/skills/<name>/ at your repo root; personal skills live in ~/.claude/skills/<name>/ and follow you across every project. The name field is optional — it sets the display label in skill listings and defaults to the directory name. The command you invoke (/<folder>) always comes from the directory name, not from this field. The file has YAML frontmatter plus a Markdown body that becomes the skill's instructions.
---
name: changelog-writer
description: Generates a release changelog from git history. Use when cutting a release or when the user asks for release notes.
---
# Changelog Writer
When asked to produce a changelog:
1. Run `git log <last-tag>..HEAD --oneline` to get the commits since the last tag.
2. Group commits into Added / Changed / Fixed / Removed by reading the message.
3. Write the result to `CHANGELOG.md` under a new `## [version] - date` heading,
following Keep a Changelog format.That's the whole format. A folder, a SKILL.md, a couple of key frontmatter fields, and a body. Every frontmatter field is technically optional — only description is genuinely recommended, since it's what decides when the skill fires. Everything below is about filling them in so the skill fires at the right time and does its job.
NOTE
Don't confuse skills with the other two extension points. A subagent (.claude/agents/) is a delegate Claude calls in its own context window. A slash command (.claude/commands/) is a prompt you trigger by name. A skill is a procedure Claude pulls in on its own when the task matches — no separate context, no manual trigger. See Skills vs Agents vs Commands for the full decision tree.
How progressive disclosure works
This is the idea that makes skills worth using, so it's worth understanding before you write one.
Claude does not load every skill's body into context up front. At the start of a session it reads only the name and description of each installed skill — a few dozen tokens apiece. The full body stays on disk. When the user's request matches a skill's description, Claude loads that skill's body, and only then. Bundled files (scripts, templates, reference docs) load later still, when the instructions actually reach for them.
So a skill costs almost nothing until it's relevant:
| Stage | What's loaded | When |
|---|---|---|
| Session start | name + description only | Always |
| Skill triggered | The SKILL.md body | When the description matches the task |
| Resource used | A bundled script or file | When the body references it |
This is why you can install twenty skills without drowning your context window. It's also why the description carries so much weight — it's the only thing Claude sees most of the time, and it's the sole signal for whether the rest ever loads.
Step 1: Pick one repeatable procedure
The best skills capture a task you do the same way every time and would rather not re-explain. "Generate a release changelog." "Scaffold a new React component with its test and story." "Convert a Figma export into our token format." Each has stable steps and a clear trigger.
Skip the skill if the task is a one-off, or if it's so simple a single sentence in your prompt covers it. Skills earn their keep through repetition. A quick test: if you've typed roughly the same multi-step instructions into Claude three times, that's a skill.
As with subagents, keep the scope to one job. If your skill body sprouts an "...and it can also..." branch, that's a second skill. Narrow skills trigger more reliably and stay easier to keep accurate.
Step 2: Write a description that triggers at the right time
The description is not documentation — it's the routing signal, and the only field loaded until the skill fires. A vague description means the skill never triggers; an over-broad one means it loads on requests it has no business handling.
Write it as what the skill does plus when to use it, and name the concrete situations that should activate it:
description: >
Scaffolds a new React component with a colocated test and Storybook
story following the repo's conventions. Use when the user asks to
create, add, or generate a new component.The trigger words — "create, add, or generate a new component" — are what Claude pattern-matches against the real request. Use the verbs and nouns people actually say.
TIP
Front-load the description with the trigger, not the implementation. Claude is matching the user's phrasing against your words, so "Use when migrating a database schema" fires more reliably than "Employs a multi-phase reconciliation strategy for schema evolution." Save the mechanics for the body.
WARNING
An over-eager description is a real cost. "Helps with code" will load on nearly every request and waste the budget you saved by using a skill at all. Make the description specific enough that it stays quiet when the task isn't yours.
Step 3: Scope tools and invocation (optional)
Two optional frontmatter fields tune how the skill runs:
allowed-tools— pre-approves a comma-separated list of tools so Claude can invoke them without a per-use permission prompt while the skill is active. It does not sandbox the skill: every other tool remains callable under your normal permission settings. Use it to make a frequently-run skill frictionless (e.g. one that callsgit). To actually block tools from a skill, usedisallowed-tools.user-invocable— skills are user-invocable by default (you can type/<name>to invoke them directly). Set it tofalseto hide a skill from the/menu when it holds background knowledge you don't want users triggering as a command. To stop Claude from auto-loading a skill while keeping it user-only, usedisable-model-invocation: trueinstead.
---
name: dependency-audit
description: Audits dependencies for known vulnerabilities and reports findings. Use when reviewing dependencies or before a release.
allowed-tools: Read, Grep, Glob, Bash
disable-model-invocation: false
---Pre-approve only the tools the procedure runs often enough that a prompt each time would be annoying — pre-approval is about friction, not safety, so there's no harm in keeping the list short. When you genuinely need to keep a tool out of a skill's reach, that's what disallowed-tools is for.
Step 4: Write a tight, instructional body
The body is the procedure Claude follows once the skill loads. Treat it like a runbook, not an essay. The same rule that governs subagent prompts applies here: long bodies dilute attention, accumulate quiet contradictions, and rot because nobody re-reads them.
Structure it as concrete steps:
# New Component
When asked to create a component named `<Name>`:
1. Create `src/components/<Name>/<Name>.tsx` with a typed props interface
and a named export.
2. Create `src/components/<Name>/<Name>.test.tsx` with a render smoke test.
3. Create `src/components/<Name>/index.ts` re-exporting the component.
4. Match the existing component style — check `src/components/Button/` for
the canonical pattern before writing.
Do not add the component to any barrel file unless asked.Notice the last two lines: point at a canonical example to anchor the style, and state the boundary so the skill doesn't overreach. Leave out generic advice the model already has. Spend the body only on what's specific to this procedure.
Step 5: Bundle resources and scripts
When a single Markdown page isn't enough, a skill folder can hold more than SKILL.md. Drop scripts, templates, schemas, or reference docs alongside it, and reference them from the body by relative path. These files follow the same progressive-disclosure rule — they load only when the instructions reach for them, so a 300-line helper script costs nothing until it runs.
~/.claude/skills/changelog-writer/
├── SKILL.md
├── format.py # deterministic formatter the body calls
└── template.md # the changelog skeleton to fill in
Reference them plainly in the body so Claude knows they exist and how to use them:
Run `python format.py <last-tag>` to produce the grouped commit list,
then fill `template.md` with the result.TIP
Push deterministic work into a bundled script rather than asking the model to do it by hand. Parsing git output, transforming JSON, or validating a format is more reliable as ten lines of Python the skill runs than as prose the model interprets — and it keeps the body short.
For larger skills, you can split reference material into separate Markdown files (reference.md, examples.md) and link to them from SKILL.md. This is the multi-file pattern: keep the entry point lean and let Claude pull deeper files only when a step needs them.
A worked example
Here's a complete, installable skill that scaffolds a database migration following a house format. Save it as ~/.claude/skills/new-migration/SKILL.md:
---
name: new-migration
description: Creates a timestamped, reversible database migration following the repo's conventions. Use when adding, creating, or generating a migration.
allowed-tools: Read, Grep, Glob, Write, Bash
---
# New Migration
When asked to create a migration for `<change>`:
1. Generate the filename: `migrations/<UTC-timestamp>_<snake_case_change>.sql`.
Get the timestamp with `date -u +%Y%m%d%H%M%S`.
2. Read the two most recent files in `migrations/` to match the house style
(transaction wrapping, comment header, naming).
3. Write the migration with a clearly labeled `-- Up` and `-- Down` section.
Every Up must have a corresponding Down; never write an irreversible
migration without flagging it explicitly.
4. Print the path you created and a one-line summary of what it does.
Do not run the migration. Creating the file is the whole job.Drop that folder in place and ask: "add a migration that adds a last_login column to users." The description matches "add a migration," the body loads, and Claude produces a correctly named, reversible file in your format — without you re-explaining the convention.
NOTE
Claude Code watches skill directories live — adding or editing a SKILL.md under ~/.claude/skills/ or a project's .claude/skills/ takes effect in the current session without restarting. The one exception: if you create a top-level skills directory that didn't exist when the session started, restart so it can be watched.
Putting it together
If your skill never fires, the description is the first thing to fix — it's the lever that controls everything downstream. If it fires too often, the description is too broad. If it fires but does the wrong thing, the body is too long or too vague.
Start small and iterate. The best skills grow the way the best subagents do: a sharp description, the minimum tools, a body that reads like a runbook, and bundled scripts for anything deterministic. For the delegate-shaped counterpart to this pattern, see Writing Your First Custom Agent.
Related
- Writing Your First Custom AgentA step-by-step guide to authoring a focused, effective custom subagent.
- Skills vs Agents vs CommandsHow Claude Code's two extension mechanisms — subagents and skills — differ across three invocation patterns, with a decision table for choosing the right one.
- Test ScaffolderScaffold a test file with sensible cases for a given module or function. Use when adding tests to untested code and you want a fast, structured starting point.