CoreMesh

Claude Code Skills: How to Write a SKILL.md That Works

Learn what a Claude Code skill is, the SKILL.md frontmatter and body format, where skills live, how to install them from GitHub, and copyable examples.

Claude Code Skills: How to Write a SKILL.md That Works

A Claude Code skill is a folder with a SKILL.md file in it. The file holds YAML frontmatter (at minimum a name and a description) followed by markdown instructions. Claude Code reads the description so it knows when the skill applies, and loads the full body only when the skill is used, either because Claude decided it's relevant or because you typed /skill-name.

Skills live in .claude/skills/<skill-name>/ for a project, ~/.claude/skills/<skill-name>/ for your personal set, or inside a plugin's skills/ directory. Everything below comes from the official Claude Code skills documentation as of October 18, 2026, and the examples are ready to copy.

What a Claude Code skill is#

Think of a skill as a procedure you've written down once so you never paste it into chat again. The docs give two signals that something should become a skill: you keep sending the same checklist or multi-step instructions, or a section of your CLAUDE.md has turned into a procedure rather than a fact.

The difference from CLAUDE.md matters for cost. CLAUDE.md content is in context on every turn. A skill's body loads only when it's used, so a long runbook sits idle until needed. If you've been fighting context bloat, moving procedures out of CLAUDE.md into skills is one of the cheaper fixes, and it pairs well with the advice in why Claude Code uses so many tokens.

Two more things to know up front:

  • Custom commands and skills are the same mechanism now. A file at .claude/commands/deploy.md and a skill at .claude/skills/deploy/SKILL.md both create /deploy. Old command files keep working. Skills add a directory for supporting files, frontmatter that controls who can invoke them, and automatic loading when Claude judges them relevant. The slash commands cheat sheet covers the command side.
  • Skills follow the Agent Skills open standard, which other AI tools also read. Claude Code adds its own frontmatter fields on top: invocation control, running in a subagent, and dynamic context injection. If you plan to share a skill across tools, keep to name, description, and the markdown body.

Bundled skills such as /debug and /code-review ship with Claude Code. Built-in commands like /help and /compact are a separate thing and aren't skills you can edit.

The SKILL.md format#

The smallest working skill:

markdown
---
name: changelog-line
description: Write a one-line changelog entry for the current change. Use when asked for release notes or a changelog line.
---

Look at the staged diff and write one changelog line in the imperative mood,
under 100 characters, starting with Added, Fixed, Changed or Removed.

Save it as .claude/skills/changelog-line/SKILL.md and /changelog-line exists. Claude can also pick it up on its own when a request matches the description.

The frontmatter fields documented for Claude Code, as of October 18, 2026:

Field What it does Notes
name The skill's identifier and slash command Match it to the folder name
description Tells Claude when the skill applies This is what Claude reads to decide relevance
disable-model-invocation true stops Claude from invoking the skill on its own You can still run it with /name
context fork runs the skill in an isolated subagent The subagent doesn't see your conversation
agent Which subagent type runs a forked skill Explore, Plan, general-purpose, or a custom agent from .claude/agents/. Defaults to general-purpose
background false makes a forked skill block the turn until it finishes Forked skills run in the background by default since v2.1.218

Inside the body, $ARGUMENTS is replaced with whatever you typed after the slash command, so /deep-research auth middleware hands the text "auth middleware" to the skill.

The body can also pull in live command output. A line like !`gh pr diff` runs the command when the skill is invoked and injects the result into the prompt. That's the dynamic context injection feature, and it's what makes review-style skills useful.

Where Claude Code skills live#

Scope Path Who sees it
Project .claude/skills/<skill-name>/SKILL.md Everyone who clones the repo, if committed
Personal ~/.claude/skills/<skill-name>/SKILL.md You, in every project
Plugin skills/ directory inside the plugin Anyone who installs the plugin
Enterprise .claude/skills/ inside the managed settings directory, for example /etc/claude-code/.claude/skills/<skill-name>/ on Linux Everyone on a managed machine
Synced from claude.ai ~/.claude/skills/synced/ You, mirrored from skills you enabled on claude.ai

Run /skills inside a session to see what's loaded. Deleting a skill directory drops it from that list in the current session, no restart needed.

The skill directory can hold more than SKILL.md. Put reference docs, templates or scripts alongside it and tell Claude in the body when to read them. Since the body only loads on use, those supporting files cost nothing until the skill fires.

How to create a Claude Code skill step by step#

  1. Pick the scope. Project skills go in the repo and get reviewed like code. Personal skills are for your own habits.
  2. Create the folder and file: .claude/skills/<name>/SKILL.md.
  3. Write a description that says what the skill does and when to use it. Claude relies on this line to decide whether to load the skill, so a vague description means the skill never triggers automatically.
  4. Write the body as instructions to Claude, not as notes to yourself. Numbered steps work well. Name the files or commands it should look at.
  5. Run /skills to confirm it loaded, then invoke it with /<name> once to check the output.
  6. If Claude starts invoking it at the wrong times, add disable-model-invocation: true and call it yourself.

Here's a small scaffold generator you can adapt. It prints the file content and the path it belongs in:

javascript
const name = "ui-component";
const description = "Build a React component using this project's design tokens";
const scope = "project"; // or "personal"

const dir = scope === "personal"
  ? `~/.claude/skills/${name}/`
  : `.claude/skills/${name}/`;

const skill = [
  "---",
  `name: ${name}`,
  `description: ${description}`,
  "---",
  "",
  "When asked to build a component:",
  "1. Read src/styles/tokens.css and reuse existing tokens.",
  "2. Match the props pattern in src/components/Button.tsx.",
  "3. Add a story and a basic render test.",
  "",
].join("\n");

console.log(`Write this to ${dir}SKILL.md, then run /skills or /${name}:\n`);
console.log(skill);

And a lint script worth running before you commit a skill. It checks the basics: frontmatter present, name and description set, the name matching the folder, a non-empty body, and a task verb when the skill is forked:

python
import re

SKILL = """---
name: pr-review
description: Review the open pull request for correctness and style
disable-model-invocation: true
---

Review the pull request and list findings by severity.
"""

def lint(text, folder):
    m = re.match(r"^---\n(.*?)\n---\n(.*)$", text, re.S)
    if not m:
        return ["no frontmatter block at the top of the file"]
    frontmatter, body = m.groups()
    fields = {}
    for line in frontmatter.splitlines():
        if ":" in line:
            key, value = line.split(":", 1)
            fields[key.strip()] = value.strip()
    problems = []
    for key in ("name", "description"):
        if not fields.get(key):
            problems.append(f"missing {key}")
    name = fields.get("name", "")
    if name and not re.fullmatch(r"[a-z0-9]+(-[a-z0-9]+)*", name):
        problems.append(f"name {name!r} is not lowercase kebab-case")
    if name and name != folder:
        problems.append(f"name {name!r} does not match folder {folder!r}")
    if not body.strip():
        problems.append("body is empty, so Claude gets no instructions")
    if fields.get("context") == "fork" and not re.search(
        r"\b(review|research|find|write|run|check|list|fix)\b", body, re.I
    ):
        problems.append("context: fork but the body has no task verb")
    return problems

for folder in ("pr-review", "review"):
    issues = lint(SKILL, folder)
    print(f"{folder}: {'ok' if not issues else '; '.join(issues)}")

Claude Code skills examples#

A code review skill with live PR context#

This is the pattern shown in the official docs for pulling a pull request into a skill. It's set to user-only invocation so Claude doesn't run gh commands every time a conversation mentions a PR:

markdown
---
name: pr-review
description: Review the open pull request for correctness, missing tests and style problems
disable-model-invocation: true
---

## Pull request context
- PR diff: !`gh pr diff`
- PR comments: !`gh pr view --comments`
- Changed files: !`gh pr diff --name-only`

Review the pull request above. For each finding give the file, the line,
what breaks, and a suggested fix. List blocking issues first. Don't comment
on formatting that a linter would catch.

You need the GitHub CLI installed and authenticated for the injected commands to return anything.

A frontend design skill#

Design conventions are a good fit because they're long, stable, and only needed when building UI. This one is meant to be invoked by Claude automatically, so the description is specific about the trigger:

markdown
---
name: ui-component
description: Build or restyle a React component using this project's design tokens and component patterns. Use whenever a task adds or changes UI.
---

Before writing any component:

1. Read src/styles/tokens.css. Use existing color, spacing and radius tokens. Never hard-code hex values.
2. Match the props and file layout of src/components/Button.tsx.
3. Every interactive element needs a visible focus style and an accessible name.
4. Add a Storybook story next to the component and a render test.
5. If a token you need doesn't exist, add it to tokens.css and say so in your summary.

Don't give a skill like this context: fork. The docs warn that a skill made of guidelines with no task returns nothing useful from a subagent, because the subagent gets rules but no job.

A research skill that runs in a subagent#

This example is from the skills docs. context: fork starts an isolated subagent, and agent: Explore gives it read-only tools suited to searching code:

yaml
---
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 references

Three things to know about forked skills:

  • The subagent doesn't see your conversation. Put everything the task needs in the body. The Explore and Plan agents also start without CLAUDE.md, so repo conventions have to be in the skill too.
  • Since v2.1.218 the fork runs in the background and its result arrives when it's done. Set background: false to wait. Claude Code waits regardless in -p mode, when CLAUDE_CODE_DISABLE_BACKGROUND_TASKS is 1, when the same skill is already running, or when a scheduled task triggers it.
  • A background fork's edits sit outside your session's checkpoints, so /rewind won't undo them. Use git.

The inverse pattern, where a custom subagent preloads skills as reference material through a skills field, is in the subagents documentation and in how to create Claude Code subagents.

How to install Claude Code skills from GitHub, plugins and marketplaces#

There's no separate installer for a bare skill folder, and you don't need one. A skill is a directory, so installing one from a GitHub repo means copying that directory into one of the locations above:

bash
git clone https://github.com/someone/their-skills.git /tmp/their-skills
cp -r /tmp/their-skills/skills/pr-review ~/.claude/skills/pr-review

Read the SKILL.md before you copy it. A skill can run shell commands through dynamic context injection, so treat a third-party skill like any other code you'd execute.

For anything you want to distribute to a team, use one of the three documented sharing routes:

  • Commit .claude/skills/ to version control. Everyone who clones the repo gets the skills. This is the default for team conventions.
  • Package skills in a plugin. Put them in the plugin's skills/ directory. Plugins come from marketplaces, are managed through the /plugin menu, and are addressed as <plugin-name>@<marketplace-name>. Uninstalling the plugin removes its skills. See the plugins overview for the plugin layout.
  • Deploy through managed settings. Administrators place skills under .claude/skills/ in the managed settings directory and they appear for everyone on the machine.

If you've enabled skills in your claude.ai account, Claude Code also syncs those into ~/.claude/skills/synced/. To remove one, turn it off on claude.ai. Deleting the folder by hand just gets it re-downloaded on the next sync.

Common mistakes and gotchas#

The description is too vague to trigger. Claude decides relevance from the description alone. A description like "helpful utilities" never fires. "Write a database migration for this project; use when a schema change is requested" does.

Guidelines in a forked skill. context: fork needs a task. A skill that says "follow these API conventions" with no verb hands the subagent rules and nothing to do.

Expecting the fork to see your conversation. Despite the name, context: fork does not fork the conversation. It starts a fresh subagent with only the skill body. If the task needs chat history, fork the conversation instead, which is a subagent feature rather than a skill one.

Bundled skills went missing. If /code-review or /debug vanished, check your settings for disableBundledSkills set to true, or for the skill set to "off" under skillOverrides. The same skillOverrides setting accepts "user-invocable-only" when you want to keep a skill but stop Claude from calling it without editing its file. The settings.json locations guide shows where those settings go.

Personal skills disappeared. Look in ~/.claude/skills/.trash/. Before v2.1.280, a manifest.json file in ~/.claude/skills/ caused Claude Code to move the skills it listed into a timestamped trash folder. Move the folders back out. Trash entries are deleted after 30 days by default, so don't wait. The fix landed well before the newest release in the Claude Code changelog, 2.1.302 as of October 18, 2026, but older installs can still carry trashed folders.

Rewind doesn't undo a background fork's edits. Covered above, but it bites people. Commit before invoking a forked skill that edits files.

Removed the skill, Claude still follows it. Deleting the directory drops the skill from /skills in the current session. Content that was already loaded into context stays until the conversation moves on, so start a new session if you need a clean slate.

Quick reference#

  • Minimum file: SKILL.md with name and description frontmatter plus a markdown body.
  • Project path: .claude/skills/<name>/SKILL.md. Personal path: ~/.claude/skills/<name>/SKILL.md.
  • Invoke with /<name>. Pass text after it and read it with $ARGUMENTS.
  • Inject live command output with !`command` in the body.
  • disable-model-invocation: true makes a skill manual only.
  • context: fork plus agent: Explore for isolated, read-only research. Add background: false to wait for it.
  • /skills lists what's loaded. /plugin uninstall <plugin-name>@<marketplace-name> removes a plugin's skills.
  • Share by committing .claude/skills/, packaging a plugin, or using managed settings.
  • Lost personal skills: check ~/.claude/skills/.trash/ within 30 days.

Quick answers

How do Claude Code skills work?

Claude Code reads each skill's description to decide when it's relevant, and loads the full SKILL.md body only when the skill is used. You can also invoke any skill directly by typing /skill-name.

How do I install Claude Code skills from GitHub?

Clone the repo and copy the skill's folder into ~/.claude/skills/ for personal use or .claude/skills/ inside a project. Read the SKILL.md first, since a skill can run shell commands through dynamic context injection.

Is there a Claude Code skills marketplace?

Skills are distributed through plugins, and plugins come from marketplaces managed in the /plugin menu. A plugin's skills sit in its skills/ directory and are removed when you uninstall it with /plugin uninstall <plugin-name>@<marketplace-name>.

What's the difference between a skill and a custom command?

Custom commands were merged into skills. A file at .claude/commands/deploy.md and .claude/skills/deploy/SKILL.md both create /deploy, but the skill version can carry supporting files, frontmatter such as disable-model-invocation, and can be loaded by Claude automatically.

Is there a Claude Code skill creator?

The official skills documentation, as of October 18, 2026, doesn't list a dedicated creator skill. Writing one by hand takes a folder, a SKILL.md with name and description, and a markdown body, or you can ask Claude in a session to draft the file for you.

Did this solve it?

One tap. No account.

Saved you an hour? Send a tip.

USDC or ETH on Base, Ethereum, Arbitrum and more. Keeps the notes free and the examples running.

Base

Network

0xc783bc5c3fa14cEC3f09A22284bC91C5AF0d59e1
Open in wallet · 5 USDC

On a computer? Scan the code with your phone wallet, or copy the address.

Pick the network your wallet or exchange uses. USDC works on all six; ETH on Base, Ethereum, Arbitrum and Optimism. Don't send on Tron or Solana: this address doesn't exist there. Tips are optional and unlock nothing; every guide stays free.

Discussion

Start the discussion

Indra Wijaya

Builds and runs AI agents, bots and content pipelines, and writes down what works and what broke.

About · Email · RSS

Keep reading

New notes, no spam.

One post at a time, only when there's something worth reading. Follow in any feed reader.

Subscribe via RSS