Skip to content
HUIAHUB

GuideAgent skills

How to write a SKILL.md for Claude Code: structure, examples, pitfalls

Write a SKILL.md for Claude Code that loads at the right time: frontmatter fields, trigger-ready descriptions, scripts, file locations and testing.

Edited by Published 8 min read4 sources

To write a SKILL.md for Claude Code, create a folder under .claude/skills/ (or ~/.claude/skills/ for personal use), add a SKILL.md file that starts with YAML frontmatter, and put your instructions in Markdown below it. The description field decides when Claude loads the skill on its own, so it has to say what the skill does and when to use it. Everything else in the frontmatter is optional.

What a Claude Code skill is and how Claude picks one

A skill is a folder of instructions, and optionally scripts and reference files, that Claude loads when a task calls for it. The Claude Code skills documentation (opens in a new tab) suggests making one when you keep pasting the same checklist or procedure into chat, or when a section of CLAUDE.md has grown into a procedure.

Skills load in stages. At the start of a session, Claude Code puts each skill's name and description into a listing in Claude's context. The body of SKILL.md loads only when you or Claude invoke the skill. Anthropic's Agent Skills overview (opens in a new tab) puts the metadata at roughly 100 tokens per skill and recommends keeping the instructions under 5,000 tokens. Bundled files cost nothing until Claude reads them.

Claude decides to load a skill by matching your request against the descriptions in that listing. You can also run any skill yourself by typing / and its name at the start of a message.

Skills replaced custom commands. A file at .claude/commands/deploy.md and a skill at .claude/skills/deploy/SKILL.md both create /deploy, and old command files still work. Skills add a folder for supporting files and frontmatter that controls who can invoke them.

How to write a SKILL.md for Claude Code: layout and frontmatter

A minimal skill is one folder with one file:

.claude/skills/release-notes/
└── SKILL.md

The frontmatter sits between two --- lines, and the opening --- must be the very first line of the file. If it is not, Claude Code reads the whole file, markers included, as plain skill content. If the YAML fails to parse, the skill still loads but with no fields set, so Claude cannot match it by description.

Every field is optional, and description is the one the docs recommend. Field names must match exactly: Claude Code silently ignores a field it does not recognize. These are the fields most skills need:

FieldWhat it doesDefault
nameCommand name in the / menuThe folder name
descriptionWhat the skill does and when to use it. Claude matches requests against itFirst non-empty line of the body
when_to_useExtra trigger phrases, appended to the descriptionNone
disable-model-invocationtrue stops Claude from loading the skill on its ownfalse
user-invocablefalse hides the skill from the / menu so only Claude can use ittrue
allowed-toolsTools Claude may use without asking during the turn that invokes the skillNone
pathsGlob patterns. Claude auto-loads the skill only when working on matching filesNone
contextfork runs the skill in a separate subagent without your chat historyInline
argument-hintAutocomplete hint, such as [issue-number]None

Claude Code also accepts model, effort, agent, hooks, shell, arguments, disallowed-tools, background, metadata, license and compatibility. If the same skill will be uploaded to claude.ai or the Skills API, stick to name, description, license, compatibility, metadata and allowed-tools. Any other field makes packaging or upload fail with an "Unexpected key(s)" error.

The overview page sets stricter rules for those surfaces: name up to 64 characters, lowercase letters, numbers and hyphens only, and without the words "anthropic" or "claude". The description there is capped at 1,024 characters.

A SKILL.md template you can copy

The template below is illustrative. It drafts release notes from recent commits, runs one bundled script and points to a style file:

---
name: release-notes
description: Drafts release notes from recent git commits, grouped into features, fixes and breaking changes. Use when the user asks for release notes, a changelog entry or a summary of what shipped.
argument-hint: "[version]"
allowed-tools: Bash(git log *) Bash(python3 ${CLAUDE_SKILL_DIR}/scripts/group_commits.py *)
---

## Recent commits

!`git log --oneline -30`

## Instructions

1. Run `python3 ${CLAUDE_SKILL_DIR}/scripts/group_commits.py --limit 30`
   and use its groups as the outline.
2. Title the notes with version $ARGUMENTS.
3. Follow the tone rules in `reference/style.md`.
4. List breaking changes first, each with the migration step.
5. If there are no commits, say so and stop.

The !`git log --oneline -30` line is dynamic context injection: Claude Code runs the command before Claude sees the skill and replaces the line with its output. $ARGUMENTS becomes whatever follows the command, so /release-notes 2.4.0 fills in the version.

Writing a description that triggers at the right time

The description is the only part of the skill Claude reads before deciding to load it. Anthropic's examples follow one pattern: a sentence on what the skill does, then the situations and words that should trigger it. The pdf skill in Anthropic's repository (opens in a new tab) lists concrete tasks (merging, splitting, filling forms, OCR) and ends with a plain rule that any mention of a .pdf file should trigger it.

Some practical rules from the Claude Code docs:

  • Put the main use case first. The combined description and when_to_use text is cut at 1,536 characters in the listing.
  • Use the words people actually type. If users say "changelog", the description should too.
  • If the skill fires when you do not want it, make the description narrower, or set disable-model-invocation: true.
  • When you have many skills, Claude Code drops some descriptions to fit a listing budget of 1% of the context window, starting with the skills you use least. /doctor estimates the listing's cost, and /skill-doctor (v2.1.252 or later) shows which skills you never use.

Use disable-model-invocation: true for anything with side effects, such as deploying or sending a message. The docs say plainly that you do not want Claude deciding to deploy because the code looks ready. A skill set that way keeps its description out of Claude's context until you call it.

Adding scripts and reference files

Supporting files keep SKILL.md short. The Claude Code docs recommend staying under 500 lines and moving detail into separate files that the main file links to, with a note on what each contains.

release-notes/
├── SKILL.md
├── reference/
│   └── style.md
└── scripts/
    └── group_commits.py

Reference files enter context only when Claude reads them. Scripts are executed, and only their output enters context, which suits work that should give the same answer every time, such as parsing or validation.

Refer to bundled files through ${CLAUDE_SKILL_DIR}, which expands to the folder that holds SKILL.md wherever the skill is installed. Claude Code expands it in the body and in Bash rules in allowed-tools. Using the same variable in both places, as the template does, lets the script run without a permission prompt. ${CLAUDE_PROJECT_DIR} does the same for project files and needs v2.1.196 or later.

Injected commands have limits worth knowing. They never stop to ask for permission: outside auto mode, a command your permission rules do not allow aborts the skill, which is why the template pre-approves git log in allowed-tools. Each one runs under the Bash tool's two-minute timeout, and a command that fails aborts the whole invocation, so Claude never sees the skill. Append || true to a command you expect to exit non-zero.

Personal vs project skills

Where the folder lives decides which sessions load it:

LocationPathWho gets it
Personal~/.claude/skills/<name>/SKILL.mdYou, in every project on this machine
Project.claude/skills/<name>/SKILL.mdEveryone working in the repo, once committed
Nested<subdir>/.claude/skills/<name>/SKILL.mdSessions working on files in that subdirectory
Plugin<plugin>/skills/<name>/SKILL.mdWherever the plugin is enabled, as /plugin-name:skill-name

When two skills share a name, enterprise beats personal and personal beats project. Plugin skills never clash because they carry the plugin prefix. A skill also wins over a command file of the same name in .claude/commands/.

Claude Code watches these folders and picks up new or edited skills during the session without a restart. If you create a top-level skills directory that did not exist at startup, run /reload-skills. Personal skills do not load in cloud or Cowork sessions, which read skills from your claude.ai account and, for cloud sessions, from the repo.

How to test a skill

Start with the two ways the docs suggest. Ask a question that matches the description and see if Claude loads the skill, then run it directly with /release-notes. Asking "What skills are available?" shows whether Claude Code found it at all.

If the skill does not trigger:

  1. Run claude --debug to see YAML parse errors.
  2. Run claude plugin validate .claude/skills (or ~/.claude/skills) to find frontmatter that does not parse. This needs v2.1.233 or later.
  3. Check that the description contains the words you used in the request.

Triggering is half the test. The docs recommend running a few realistic prompts in fresh sessions, once with the skill and once with it set to "off" in skillOverrides, and comparing the results. A fresh session matters because context left over from writing the skill hides gaps in the instructions. The skill-creator plugin automates that loop, including description tuning, and installs with /plugin install skill-creator@claude-plugins-official.

If Claude follows a skill at first and drifts later, remember that the file is read once per invocation. Word rules so they apply to the whole task, and put the most important ones near the top, because compaction keeps only the first 5,000 tokens of each invoked skill.

Security: reviewing skills from other people

The Agent Skills overview tells you to use skills only from trusted sources, meaning ones you wrote or got from Anthropic. A skill can direct Claude to run tools and code in ways that do not match its stated purpose. Anthropic's public skills repository (opens in a new tab) labels its own skills as demonstrations and asks you to test them before relying on them.

Three Claude Code behaviors make review important:

  • Commands inside !`...` run on your machine before Claude reads the skill.
  • A project skill's allowed-tools applies even in a -p run in a folder you have never trusted, so a repo's skill can grant itself tool access.
  • A skill folder that contains .claude-plugin/plugin.json loads as a plugin and can bring hooks and MCP servers. In a project's .claude/skills/, that needs workspace trust first.

Before you install or commit someone else's skill, check:

  • To do: Every file in the folder, including scripts, images and reference files
  • To do: Each injected command and what it reads or sends
  • To do: The allowed-tools list, for broad patterns such as Bash(*)
  • To do: Any URLs the skill fetches, since fetched text can carry instructions and can change after you review it
  • To do: Whether the folder contains a .claude-plugin/plugin.json

Organizations can turn off injected commands for user, project and plugin skills with "disableSkillShellExecution": true in managed settings. Bundled and managed skills are not affected.

Frequently asked questions

What is the difference between a skill and CLAUDE.md?

`CLAUDE.md` content loads into every session. A skill's body loads only when someone invokes it, so long procedures and reference material cost almost nothing until they are needed. Facts that apply to every task belong in `CLAUDE.md`, and multi-step procedures belong in a skill.

Can a skill force Claude to follow a rule every time?

No. A skill is instructions, and Claude can drift from them later in a long session. For a rule that must hold on every edit or command, use a hook, which Claude Code runs each time its event fires. A skill can register hooks through its `hooks` frontmatter field, active from invocation until the session ends.

Do Claude Code skills work in other AI tools?

Claude Code skills follow the Agent Skills open standard published at agentskills.io, which other tools can read. Fields outside the standard, such as `context` or `disable-model-invocation`, and features like injected shell commands are Claude Code extensions. Keep to the six standard frontmatter fields if portability matters.

How do I share a skill with my team?

Commit the folder under `.claude/skills/` in the repository so everyone who works there gets it. For use across many repos, package it in a plugin's `skills/` directory. Administrators can also deploy skills to every user through managed settings.

Sources

  1. 1.Extend Claude with skills - Claude Code DocsOfficial docsPrimarycode.claude.com
  2. 2.anthropics/skillsRepositoryPrimarygithub.com
  3. 3.Agent Skills - Claude Platform DocsOfficial docsPrimaryplatform.claude.com
  4. 4.pdf skill folderRepositoryPrimarygithub.com

Spotted an error? See our corrections policy.

Enjoyed this? Get the weekly brief.

New guides, Project & Tool of the Week and the AI developments that matter. One email, no spam.

Read next