Guides
How to Write a Claude Code Skill That Triggers
Checked 2026-10-31
Loading poll...
A Claude Code skill is a folder with a SKILL.md file: YAML frontmatter that tells Claude when to use it, then Markdown instructions it follows when it runs. Save it in .claude/skills/<name>/ and type /name, or let Claude load it when your request matches the description. This guide builds three, from smallest to most useful. Facts checked 31 October 2026.
What you will have at the end
- A first skill that summarizes your uncommitted changes
- A task skill that only you can trigger, with arguments
- A skill that runs in its own subagent and returns only a summary
- A test routine so you know the skill works, not just that it loads
If you want the bigger picture of how skills fit next to subagents, hooks and MCP, read Claude Code best practices first. For ready-made skills to study, browse our skills directory.
Skills in one table
Question | Answer from the docs |
|---|---|
What is it? | A directory with |
Who can run it? | You, with |
What is always in context? | Only the description. The body loads when the skill is invoked |
Does the body stay loaded? | Yes. It enters the conversation once and stays for later turns |
What about old commands? |
|
Can it run isolated? | Yes, with |
The last row is the one most people miss. Custom commands were folded into skills, so a command file and a skill folder share the same frontmatter, and a skill can now ship scripts and reference files next to its instructions.
Step 1: Create the folder and the file
Where you save the skill decides who gets it.
Location | Path | Loads in |
|---|---|---|
Personal |
| All your projects on this machine |
Project |
| Sessions in this repository; commit it to share |
Nested |
| Sessions in or below that folder |
Plugin |
| Wherever the plugin is enabled, as |
Enterprise | The managed settings directory | Everyone your organization deploys it to |
Start with a personal skill, because you can throw it away without touching the repo.
mkdir -p ~/.claude/skills/summarize-changesStep 2: Write SKILL.md
The docs use a changes summarizer as the first example, and it is a good one because it shows the most useful trick: running a command before Claude reads the skill.
---
description: Summarizes uncommitted changes and flags anything risky. Use when the user asks what changed, wants a commit message, or asks to review their diff.
---
## Current changes
!`git diff HEAD`
## Instructions
Summarize the changes above in two or three bullet points, then list any risks you notice such as missing error handling, hardcoded values, or tests that need updating. If the diff is empty, say there are no uncommitted changes.The line starting with ! and a backtick is dynamic context injection. Claude Code runs the command and swaps the line for its output before Claude sees the skill. So Claude reads your real diff instead of guessing from open files.
Open a git project, change a file, run claude and test it two ways. Ask "What did I change?" and Claude should load the skill by itself. Then type /summarize-changes and it should run directly. If only the second works, the problem is the description, covered below.
Step 3: Write a description that triggers
The description is the only part Claude sees before it decides to load a skill, so it carries most of the weight. The docs give three rules worth following.
- Put the key use case first. The
descriptionandwhen_to_usetext are combined and cut at 1,536 characters in the skill listing, to save context. - Say what it does and when to use it. "Summarizes uncommitted changes" is the what. "Use when the user asks what changed" is the when.
- Use the words a person would type. If you say "diff" and your colleague says "what did I touch", add both.
You can split the text in two. when_to_use is appended to description for trigger phrases and example requests. If you omit description altogether, Claude falls back to the first non-empty line of the body, which is rarely what you want.
A weak description and a stronger one:
# Weak: names the topic, says nothing about when
description: Git helper
# Stronger: what, then when, with real phrasing
description: Writes a conventional commit message from the staged diff. Use when the user says commit, write a commit message, or what should I name this commit.Two cautions from the same page. Angle brackets in a description show up as < and >, so avoid them. And Claude Code ignores a frontmatter field it does not recognize without telling you, so a typo like disable_model_invocation fails silently. Field names use hyphens, with when_to_use the one exception.
Step 4: Choose who can run it
By default you and Claude can both run any skill. Two fields change that.
Frontmatter | You can run it | Claude can run it | Description in context |
|---|---|---|---|
(default) | Yes | Yes | Always |
| Yes | Not on its own | No |
| No | Yes | Always |
Use disable-model-invocation: true for anything with side effects: a deploy, a commit, a Slack message. You do not want Claude deciding to deploy because the code looks ready. If Claude tries anyway, Claude Code blocks the call and tells it to ask you to run the skill.
Use user-invocable: false for background knowledge, such as a note on how a legacy system works. Claude should know it when relevant, but typing /legacy-system-context is not a useful action.
---
name: deploy
description: Deploy the application to production
disable-model-invocation: true
---
Deploy $ARGUMENTS to production:
1. Run the test suite
2. Build the application
3. Push to the deployment target
4. Verify the deployment succeededRun it as /deploy staging and $ARGUMENTS becomes staging. For named positions, use the arguments field and $name substitutions, and add argument-hint: [environment] so autocomplete shows what to type.
Step 5: Keep the body short
A skill's body loads once and then stays in the conversation for later turns. Claude Code does not re-read the file each turn. So every line costs tokens for the rest of the session. The docs' advice is to state what to do rather than narrate how or why, and to apply the same test you would use for a CLAUDE.md line.
Two consequences follow.
- Write standing instructions, not one-time steps. "Always run the type check before reporting done" still applies on turn ten. "First, run the type check" reads like a step that is finished.
- Know the compaction budget. When Claude Code compacts a long conversation, it re-attaches the most recent invocation of each skill, keeping the first 5,000 tokens of each, within a combined 25,000-token budget. Skills you invoked early can be dropped. If Claude stops following a skill late in a session, run it again.
Move bulk out of SKILL.md. Large reference material goes in supporting files that load only when needed.
review-pr/
├── SKILL.md # overview and navigation
├── checklist.md # loaded when needed
├── examples.md # loaded when needed
└── scripts/
└── collect-diff.sh # executed, not loadedMention each file from SKILL.md with one line saying what it holds and when to read it. Scripts run and return output; they are not read into context.
Step 6: Pre-approve the tools it needs
A skill that runs git commands will stop and ask permission on every one unless you grant them. allowed-tools lists tools Claude can use without asking during the turn that runs the skill. The grant clears when you send your next message, so it is narrow by design.
---
name: commit
description: Stage and commit the current changes with a conventional message. Use when the user asks to commit.
disable-model-invocation: true
allowed-tools: Bash(git add *) Bash(git commit *) Bash(git status *)
---The opposite field is disallowed-tools, which removes tools from Claude's pool while the skill is active. Use it for autonomous skills that should never call something like AskUserQuestion in a background loop.
Permission rules still win where they apply. Treat allowed-tools as convenience, and treat deny rules and hooks as the real guard. Our hooks guide covers enforcement.
Step 7: Run it in a subagent when the output is noisy
Add context: fork and Claude Code starts a new subagent, hands it the skill body as its prompt, and returns a summary. The subagent does not see your conversation history, so the skill must stand on its own.
---
name: deep-research
description: Research a topic thoroughly
context: fork
agent: Explore
---
Research $ARGUMENTS thoroughly:
1. Find relevant files using Glob and Grep
2. Read and analyze the code
3. Summarize findings with specific file referencesThe agent field picks the execution environment. Options are the built-in Explore, Plan and general-purpose agents, or any custom subagent in .claude/agents/. Leave it out and you get general-purpose.
Four details that surprise people:
- It runs in the background by default. You keep working and the result arrives when it finishes. Set
background: falseto wait for it in the same turn. - Background forks get a narrower tool set. If your steps need a tool outside it, use
background: false. - Edits fall outside checkpoints. After a backgrounded fork,
/rewindonly restores files Claude had already edited in your conversation. Use git to revert the rest. - A guideline-only skill returns nothing.
context: forkneeds an actual task. "Use these API conventions" gives the subagent no job.
Explore and Plan start without your CLAUDE.md and git status, so put everything the task needs in the skill. The reverse direction, a subagent that preloads skills as reference material, is covered in our subagent guide. The trade-offs are laid out in subagents vs skills.
Step 8: Restrict a skill to certain files
If a skill only makes sense for part of the repo, add paths. Claude then loads it automatically only when working with files that match, using the same glob format as path-scoped rules. Patterns are relative to Claude's current working directory.
---
name: api-conventions
description: API design patterns for this codebase
paths: src/api/**/*.ts
---
When writing API endpoints:
- Use RESTful naming conventions
- Return consistent error formats
- Include request validationThis is reference content: knowledge applied to the work at hand rather than a task you trigger.
Step 9: Test the skill, not just the load
Seeing a skill trigger tells you Claude found it. It does not tell you the output is right. The docs recommend a baseline comparison.
- Collect a few realistic prompts.
- Run each in a fresh session with the skill on, then again with it off.
- Compare the results.
A fresh session matters, because leftover context from writing the skill hides gaps in the instructions. To turn a personal or project skill off for the second run, set it to "off" in skillOverrides. Plugin skills use claude plugin eval, which runs each prompt with and without the plugin and can fail CI under a threshold.
For one skill inside a conversation, the skill-creator plugin automates the loop. Install it with the command below, then ask Claude to evaluate your skill.
/plugin install skill-creator@claude-plugins-officialIt stores test cases in evals/evals.json, runs each in a clean subagent, grades assertions, and compares pass rate, time and tokens with and without the skill. It can also generate should-trigger and should-not-trigger prompts and propose description edits when a skill fires on the wrong request. See our skill-creator page.
Troubleshooting
Symptom | Likely cause | Fix |
|---|---|---|
Never triggers on its own | Description does not match how you ask | Rewrite it with what and when; add |
| Frontmatter not on line 1, or folder name wrong |
|
A field has no effect | Misspelled field name | Match the table exactly, with hyphens |
Stops being followed late | Compaction dropped it | Invoke it again |
Fork returns nothing | Body has guidelines but no task | Rewrite as numbered steps |
Two skills collide | Same name in two places | Rename one; higher-priority location wins |
If the YAML does not parse, the skill still loads with no fields set. That is why a broken skill can look present but behave like plain text.
Our take
Most skills fail at the description, not the body. The body is read after Claude has already chosen to load the skill, while the description is the entire pitch. We would spend half the writing time on those two sentences.
Keep a clear split in your head. A rule that must hold every time belongs in a hook or a permission rule, because a skill is guidance Claude weighs. A procedure you repeat belongs in a skill. A fact Claude should hold in every session belongs in CLAUDE.md. Putting a ten-step deploy procedure in CLAUDE.md costs context on every session, while the same text in a disable-model-invocation skill costs nothing until you type the command.
For a team, commit skills under .claude/skills/ so they get code review like any other file. For personal habits, keep them in ~/.claude/skills/.
Checklist
SKILL.mdhas---on its first line and valid YAML- The description says what the skill does and when to use it, key case first
- Anything with side effects has
disable-model-invocation: true - The body states standing instructions and stays short
- Bulky references live in supporting files
allowed-toolslists only what the skill needs- A forked skill has an explicit task and its own context
- You ran at least three realistic prompts with and without the skill
Related pages
Questions
Where does a Claude Code skill live?
Personal skills go in ~/.claude/skills/<name>/SKILL.md and load in all your projects. Project skills go in .claude/skills/<name>/SKILL.md and load in that repository. Plugins and managed settings can also deliver skills.
Are custom commands and skills the same thing now?
Mostly. A Markdown file in .claude/commands/ still works and accepts the same frontmatter except name and paths, but the docs say to prefer a skill for new work because skills support supporting files.
How do I stop Claude from running a skill on its own?
Set disable-model-invocation: true in the frontmatter. Then only you can run it by typing /skill-name. Use this for deploys, commits and anything with side effects.
Why does my skill never trigger?
Usually the description. Claude decides from the description and when_to_use text, which is cut at 1,536 characters in the skill listing. Put the key use case first and name the phrases a person would type.
Loading poll...