# Claude Code Skills: What They Are, Where They Live, and How to Write One

> Source: <https://chudi.dev/blog/claude-code-skills>
> Published: 2026-09-29 00:00:00+00:00

A Claude Code skill is a folder containing a `SKILL.md` file: a few lines of frontmatter and a body of instructions. Claude adds it to its toolkit, uses it when your task matches the description, or runs it when you type `/skill-name`. This page covers where skills live, how Claude decides to load one, how to write one you can copy, and the skills I run myself.

Everything about product behavior here comes from the Claude Code docs as of 09/29/2026. Where I describe my own setup, I say so.

## What a Claude Code skill is

The docs put it in one sentence: create a `SKILL.md` file with instructions, and Claude adds it to its toolkit. Claude uses skills when relevant, or you invoke one directly with `/skill-name`.

The docs give two signals for when a skill is worth writing. You keep pasting the same instructions, checklist or multi-step procedure into chat. Or a section of your CLAUDE.md has grown into a procedure instead of a fact. A skill body loads only when it is used, so long reference material costs almost nothing until you need it.

Skills follow the Agent Skills open standard, which works across multiple AI tools. Claude Code extends that standard with invocation control, subagent execution and dynamic context injection. So a skill written for Claude Code may use fields that other tools do not read.

Claude Code also ships bundled skills. The docs name `/doctor`, `/code-review`, `/batch`, `/debug`, `/loop` and `/claude-api`, and a trio for running your app: `/run`, `/verify` and `/run-skill-generator`. Bundled skills are prompt-based. They give Claude detailed instructions and let it orchestrate the work with its own tools.

### Where skills live

Where you save a skill decides which sessions load it. The docs list these locations:

| Location | Path | Loads in | 
|---|---|---|
| Personal | `~/.claude/skills/<skill-name>/SKILL.md` | All your projects on this machine | 
| Project | `.claude/skills/<skill-name>/SKILL.md` | Sessions in this repository. Commit it so your team gets it too | 
| Nested | `<subdir>/.claude/skills/<skill-name>/SKILL.md` | Sessions started in or below that subdirectory | 
| Plugin | `<plugin>/skills/<skill-name>/SKILL.md` | Wherever the plugin is enabled, as `/plugin-name:skill-name` | 
| Enterprise | `.claude/skills/<skill-name>/SKILL.md` in the managed settings directory | All users where the organization deploys it | 
| claude.ai account | Skills enabled for your claude.ai account | Cowork sessions, cloud sessions, and terminal sessions signed in with that account | 

Three details that catch people. First, if the same name exists in two places, enterprise wins over personal and personal wins over project. Second, personal skills in `~/.claude/skills/` are not read by Cowork or cloud sessions, so a routine that calls a personal-only skill will report that it was not found. Third, project skills in a subdirectory below where you started do not load at startup. They load the first time Claude reads or edits a file in that subdirectory.

Claude Code watches these folders for changes, so adding or editing a skill takes effect within the session. The one exception is a top-level skills folder that did not exist when the session started. For that, run `/reload-skills`.

## How Claude decides to load a skill

Claude loads a listing of every skill name and description into context so it knows what is available. It matches your task against those descriptions. The full body loads only when you invoke the skill or Claude picks it.

That gives you three modes, controlled by two frontmatter fields:

| Frontmatter | You can invoke | Claude can invoke | Context behavior | 
|---|---|---|---|
| (default) | Yes | Yes | Description always in context, full skill loads when invoked | 
| `disable-model-invocation: true` | Yes | No | Description not in context, full skill loads when you invoke | 
| `user-invocable: false` | No | Yes | Description always in context, full skill loads when invoked | 

Use `disable-model-invocation: true` for anything with side effects, like a deploy or a commit. The docs say you do not want Claude deciding to deploy because your code looks ready. Use `user-invocable: false` for background knowledge that is not a meaningful command.

Two costs are worth knowing before you write your fiftieth skill.

**The listing has a budget.** It scales at 1% of the model’s context window. When you have many skills, Claude Code drops descriptions to fit, starting with the skills you invoke least. The docs say the dropped text is exactly the keywords Claude needs to match your request. Each entry is also capped at 1,536 characters for `description` and `when_to_use` combined, so put the key use case first.

**A loaded body stays.** Once a skill runs, its rendered content enters the conversation as one message and stays across later turns. Claude Code does not re-read the file. After auto-compaction, it re-attaches the most recent invocation of each skill, keeping the first 5,000 tokens of each, within a combined budget of 25,000 tokens. Older skills can be dropped entirely if you invoked many in one session.

If you worry about what all of this costs against your plan, see [Claude usage limits, explained](https://chudi.dev/blog/claude-usage-limits). The short version is that descriptions are the always-on cost and bodies are the pay-as-you-go cost.

## How to write a Claude Code skill

Every skill needs a `SKILL.md`. It has two parts: YAML frontmatter between `---` markers that tells Claude when to use the skill, and markdown with the instructions Claude follows when it runs.

All frontmatter fields are optional. Only `description` is recommended. The fields I use most, from the docs’ reference table:

- `name` : the command name. It defaults to the directory name.
- `description` : what the skill does and when to use it. Claude uses this to decide.
- `disable-model-invocation` :`true` means only you can invoke it.
- `allowed-tools` : tools Claude can use without asking during the turn that invokes the skill. The grant clears when you send your next message.
- `argument-hint` : a hint shown in autocomplete, such as`[issue-number]` .

Field names must match the table exactly. Claude Code ignores a field it does not recognize and does not report an error. Also, the frontmatter is read only when the opening `---` is the first line of the file.

Here is a small skill you can copy. It writes a changelog entry from recent commits. Save it as `~/.claude/skills/changelog-entry/SKILL.md`:

```
---
name: changelog-entry
description: Drafts a changelog entry from recent git commits. 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 *)
---

## Recent commits

!`git log --oneline -30`

## Instructions

Write a changelog entry for version $ARGUMENTS from the commits above.

- Group changes under Added, Changed and Fixed.
- One line per change, in plain language, starting with a verb.
- Skip merge commits and commits that only touch tests.
- If no version was given, use the heading "Unreleased".
```

Four things in that file are doing real work.

The description names both the job and the phrases a person would say: release notes, changelog entry, what shipped. That is what Claude matches against.

The `` !` git log --oneline -30` `` line is dynamic context injection. Claude Code runs the command and replaces the line with its output before Claude sees the skill, so the instructions arrive with the commits already inlined.

The `allowed-tools` line is not decoration. The docs say injected commands never prompt for permission while a skill renders, and outside auto mode a command whose permission check does not return allow aborts the whole invocation. Pre-approving `git log` here keeps the skill from failing.

`$ARGUMENTS` receives whatever you type after the skill name. Run `/changelog-entry 2.4.0` and the body reads “for version 2.4.0”.

To test it, open a git project and ask “write release notes for the last few commits”. Then invoke it directly with `/changelog-entry 2.4.0`. If the first works, the description is doing its job. If only the second works, the description is the problem.

### Supporting files

A skill can be more than one file. The docs show a `SKILL.md` for overview and navigation, plus optional reference files and a `scripts/` folder. Reference the extra files from `SKILL.md` so Claude knows what each contains and when to load it. The docs recommend keeping `SKILL.md` under 500 lines and moving detailed reference material to separate files.

## The skills I actually run

I keep my personal skills in `~/.claude/skills/`. I counted 152 folders there with `ls -d */` on 09/29/2026. One of them is `synced`, the folder Claude Code manages for skills downloaded from claude.ai, so the number I wrote myself is smaller. That is far more skills than I need loaded at once, and the listing budget above is the reason I care about description length.

Here are four, all technical, read from their `SKILL.md` files on 09/29/2026. I picked them because each teaches a different lesson.

**`codex-search`**, at `~/.claude/skills/codex-search/SKILL.md`. It runs a ranked local search over my own notes. The description is one sentence of what it does, then a “Skip when” clause: “Skip when the exact node id is already known.” It is one file, 80 lines. The body has sections for when to fire, when to skip, how to run it, known limits, and anti-patterns. The lesson is that a small skill with a clear skip condition is easy for Claude to route.

**`blog-seo-gate`**, at `~/.claude/skills/blog-seo-gate/SKILL.md`. It checks a blog post title and metadata before I publish. It is one file of 237 lines. Its frontmatter carries `version` and a `triggers` list of phrases like “new blog post” and “check title”. Neither field is in the docs’ frontmatter table. Since the docs say unrecognized fields are ignored, I read that list as decoration, and the description is what routes the skill. I would not copy it.

**`conductor`**, at `~/.claude/skills/conductor/SKILL.md`. It classifies a goal and hands it to other skills in order. It is one file of 383 lines, with a “When this skill applies” section and a “Do NOT trigger when” list in the body. My reading, which is an inference and not something the docs state: the body loads only after Claude has already chosen the skill, so a “do not trigger” list in the body cannot stop a wrong trigger. That work belongs in the description.

**`hallmark`**, at `~/.claude/skills/hallmark/SKILL.md`. It is a design builder and critic. The description is one short line. The `SKILL.md` is 549 lines, over the docs’ 500-line guidance, and the folder has references, docs and site subfolders, with 221 files by my `find` count. The lesson runs both ways. Supporting folders are the right home for bulk. A 549-line entry file is a recurring context cost every time it loads.

None of these are clever. Each replaces a procedure I would otherwise retype or forget.

## Skills vs slash commands vs subagents vs CLAUDE.md

These four get confused, and the docs draw the lines like this.

**Slash commands.** Custom commands have been merged into skills. A file at `.claude/commands/deploy.md` and a skill at `.claude/skills/deploy/SKILL.md` both create `/deploy`. Old command files keep working. If a skill and a command file share a name, the skill wins. Prefer a skill for new work, since skills also support supporting files.

**CLAUDE.md.** It loads every session. The docs say to put “always do X” rules there, and to put reference material Claude needs only sometimes, or a workflow you trigger with `/name`, in a skill. Their rule of thumb is to keep CLAUDE.md under 200 lines. For how project instructions work, see [Claude Code AGENTS.md and CLAUDE.md](https://chudi.dev/blog/claude-code-agents-md).

**Subagents.** A subagent is an isolated worker with its own context window. Only a summary returns to your main conversation. A skill adds to your main window. They combine: a skill with `context: fork` runs in a subagent, and a subagent can preload skills through its `skills` field. If you are deciding between them, the full comparison is in [Claude Code skills vs subagents](https://chudi.dev/blog/claude-code-skills-vs-subagents).

**Hooks.** The docs draw this line clearly: a hook always fires on its event, while a skill is interpreted by Claude and the outcome can vary. An instruction like “never edit `.env`” inside a skill is a request, not a guarantee. If a rule must hold every time, use a hook. I cover that in [the Claude Code hooks tutorial](https://chudi.dev/blog/claude-code-hooks-tutorial).

For the whole picture of how these pieces fit into one setup, start at [the Claude Code complete guide](https://chudi.dev/blog/claude-code-complete-guide).

## Common mistakes, and when a skill does not trigger

The docs give a short debugging path for a skill that does not fire. Work through it in order.

1. **Check the description.** It should include keywords a person would naturally say. This is the most common cause.
2. **Check the skill is listed.** Ask “What skills are available?” If it is missing, the file is in the wrong place or the folder name is wrong.
3. **Rephrase your request** to match the description more closely.
4. **Invoke it directly** with`/skill-name` . If that works, the body is fine and the description is the problem.

If the YAML is malformed, Claude Code loads the skill body with empty metadata. `/skill-name` still works, but Claude cannot match against the description. Run with `--debug` to see the parse error. The docs also say `claude plugin validate ~/.claude/skills` finds files whose frontmatter does not parse, and it needs Claude Code v2.1.233 or later.

A few other mistakes I see, each grounded in the docs:

- **A vague description for a skill that fires too often.** Make it more specific, or add`disable-model-invocation: true` if you only want it by hand.
- **Guidelines with no task under `context: fork`.** The docs warn that a forked subagent given only guidelines returns without meaningful output. The subagent does not see your conversation, so the instructions must stand alone.
- **Expecting a skill to stick after compaction.** If a skill seems to stop influencing behavior, the docs suggest strengthening the description and instructions, or re-invoking it after compaction.
- **Never checking what your skills cost.** Run`/doctor` for an estimate of the listing’s context cost. Run`/skill-doctor` to see what each skill costs and how often it gets used. The docs say`/skill-doctor` requires v2.1.252 or later.
- **Never testing against a baseline.** Seeing a skill trigger tells you Claude found it, not that it did what you meant. The docs recommend running a few realistic prompts in a fresh session with the skill and again with it disabled, then comparing.

Skills are the right tool for procedures Claude should apply with judgment. When you catch yourself pasting the same checklist for the third time, that is the moment to write one.
