A Claude Code skill is a folder with a SKILL.md file that teaches Claude how to do one repeatable task. In this tutorial you will build two skills: a minimal one made of instructions only, and a multi-file one with a reference document and an executable script.

You will also learn where to store skills, how to write a description that triggers reliably, and how to test the result. Anthropic documents Skills as a beta feature for Claude Code users (help article dated July 22, 2026), so check the current docs if a detail differs from what is shown here.

How a skill is structured

A skill is a directory. The only required file is SKILL.md, which has two parts: YAML frontmatter and Markdown instructions. The frontmatter holds a name and a description. Claude uses the description to decide when the skill is relevant, so it matters more than any other line in the file.

Skills can live in two places:

  • ~/.claude/skills/<skill-name>/ for personal skills available in every project.
  • .claude/skills/<skill-name>/ inside a repository, so the whole team gets the skill through version control.

Skills can also be shipped in a skills/ directory at the root of a plugin. Custom slash commands have been merged into skills, so a skill named pr-description can be invoked directly as /pr-description.

Anthropic's guidance for good skills is short:

  • Solve one specific, repeatable task.
  • Write clear instructions and include examples when they help.
  • State when the skill should be used.
  • Focus on a single workflow rather than a grab bag.

Example 1: A minimal instructions-only skill

The first skill writes pull request descriptions in a consistent format. Create the folder and file inside a repository:

mkdir -p .claude/skills/pr-description
touch .claude/skills/pr-description/SKILL.md

Put the following in SKILL.md:

---
name: pr-description
description: Writes a pull request description from the current git changes. Use when the user asks to write a PR description, summarize a branch, or says things like "describe my changes" or "what should I put in the PR".
---

# PR description

## Steps

1. Run `git diff main...HEAD --stat` to see which files changed.
2. Run `git log main..HEAD --oneline` to read the commit messages.
3. Read the diffs of the most important files if the intent is unclear.
4. Write the description using the template below.

## Template

### Summary
One or two sentences on what changed and why.

### Changes
- Bullet list of notable changes, grouped by area.

### Testing
How the change was verified. If nothing was run, say so.

## Rules

- Do not invent test results.
- Keep the summary under 50 words.
- Mention breaking changes in a separate **Breaking** line.

Note how the description lists concrete phrases a user might actually type. Specific, real-world wording in the description helps a skill get picked up automatically, while a vague description such as "helps with git" tends to be ignored or to fire at the wrong time.

Result: start Claude Code in the repository and run /pr-description. Claude reads the instructions, runs the git commands, and returns a description in the template format. Then test automatic selection: in a new session, type "can you describe my changes for the PR?" without the slash command and see whether the skill is used.

Example 2: A multi-file skill with a reference file and a script

Skills can grow beyond one file. A skill can include supplemental material, such as a REFERENCE.md, and executable code. Claude Code can also install packages from standard repositories like PyPI and npm when a skill needs them. The script below uses only the Python standard library, so no installation is needed.

This skill reviews a codebase for leftover work markers. The layout is:

.claude/skills/todo-audit/
├── SKILL.md
├── REFERENCE.md
└── scripts/
    └── find_todos.py

First, the script scripts/find_todos.py:

import re
import sys
from pathlib import Path

PATTERN = re.compile(r"(TODO|FIXME|HACK)[:\\s]*(.*)")
SKIP_DIRS = {".git", "node_modules", ".venv", "__pycache__", "dist"}


def scan(root):
    for path in root.rglob("*"):
        if not path.is_file() or SKIP_DIRS & set(path.parts):
            continue
        try:
            text = path.read_text(encoding="utf-8")
        except (UnicodeDecodeError, OSError):
            continue
        for number, line in enumerate(text.splitlines(), start=1):
            match = PATTERN.search(line)
            if match:
                yield path, number, match.group(1), match.group(2).strip()


def main():
    root = Path(sys.argv[1] if len(sys.argv) > 1 else ".")
    count = 0
    for path, number, tag, note in scan(root):
        print(f"{path}:{number} [{tag}] {note}")
        count += 1
    print(f"Total: {count}")


if __name__ == "__main__":
    main()

Next, REFERENCE.md, which holds the rules for how to rank findings. Keeping this out of SKILL.md means Claude only reads it when it needs it:

# Severity reference

- **FIXME** in code on a request path (API handlers, auth, payments): high.
- **FIXME** elsewhere: medium.
- **HACK**: medium. Ask whether a proper fix is feasible.
- **TODO** with no owner or ticket: low.
- Markers inside tests or docs: low, unless they say a test is disabled.

Finally, SKILL.md:

---
name: todo-audit
description: Audits a codebase for TODO, FIXME and HACK comments and ranks them by severity. Use when the user asks for a TODO audit, leftover work, tech debt markers, or "what is unfinished in this repo".
---

# TODO audit

## Steps

1. Run the bundled script from this skill's folder: `python scripts/find_todos.py .`
2. Read `REFERENCE.md` and rank every finding by severity.
3. Open the surrounding code for any high-severity item to confirm it is real.
4. Report a table with columns: severity, file:line, marker, note.

## Rules

- Do not edit files. This skill only reports.
- If the script prints `Total: 0`, say that no markers were found.

Result: when you ask "what is unfinished in this repo?", Claude runs the script, reads the reference, and returns a ranked table such as src/api/auth.py:42 | high | FIXME | token expiry not checked. The deterministic scanning is done by code, and Claude only does the judgment work.

Testing and improving a skill

Treat a skill like any other piece of code and test it in two ways:

  • Direct invocation: run /skill-name and check that the output follows your instructions.
  • Automatic selection: phrase a request naturally, without the slash command, and see whether the skill is chosen.

If a skill does not trigger, rewrite the description first. Add the phrases your team actually says, and state the situation in which the skill applies. If a skill triggers too often, narrow the description. If the output is inconsistent, add a template or a short example to the instructions.

Keep each skill focused. If SKILL.md keeps growing, move stable details into files like REFERENCE.md and link to them from the instructions.

Sharing the skill

For team use, commit .claude/skills/ to the repository. For personal use across projects, copy the folder to ~/.claude/skills/.

The same SKILL.md format also works in claude.ai, the Claude API, and other tools that adopted the Agent Skills standard. For claude.ai, package the skill folder as a ZIP file and upload it under Customize > Skills, where uploaded skills can be toggled on or off.

Conclusion

A custom skill is a folder, a SKILL.md file with a precise description, and clear instructions. Start with instructions only, as in the pr-description example, and add reference files or scripts when the task needs deterministic work, as in todo-audit.

The quality of a skill comes down to focus and testing. Pick one repeatable workflow, write the description in the words users really use, and check both slash-command and automatic invocation before sharing it with your team.