2026-06-11 · skills

Chapter 4: Your first SKILL.md

Write a SKILL.md from scratch so your coding agent can discover and run the PR reviewer on demand.

Xiaoman · The Workshop

Xiaoman feels its way through the same routine each time. Today you write down its first reusable craft.

Draft chapter. First cut to prove the format; it will be hardened before it is indexed.

What you’ll build

You will package our running project, the PR reviewer, as a reusable Agent Skill. A skill is just a folder with a SKILL.md file inside it. That file has two parts: YAML frontmatter between --- markers that tells the agent when to use the skill, and markdown instructions the agent follows once the skill loads. By the end of this chapter the coding agent will discover your skill on its own, decide when it applies, and run your full review without you re-pasting the checklist every session.

Two mechanics are worth getting straight up front. First, the trigger is entirely in the description. The agent reads every skill’s name and description at startup, but it only loads the full body when something in the conversation matches that description. Second, the folder name is the command: a skill at .claude/skills/pr-reviewer/SKILL.md can also be called directly as /pr-reviewer. Get the description right and you get both automatic and manual invocation.

Prerequisites

  • Chapters 1 to 3 finished: you understand the agent loop and context engineering.
  • A coding agent that supports Agent Skills, installed and working. Check the exact paths and field names against the official docs, since they evolve.
  • A repo with at least one pull request or diff to review.
  • The GitHub CLI (gh) available if you want the live-diff version in step 4.

Why it works

Skills use progressive disclosure to stay cheap. At startup only names and descriptions sit in context, which is why a description that leaves out the trigger is invisible in practice: the agent has nothing to match against. When a request matches, the full SKILL.md body gets injected as a single message and stays in context for the rest of the session, so every line you write costs tokens over and over. Everything below follows from that one fact: pack the description with trigger words, and keep the body short and imperative.

Steps

1. Create the skill folder

Make a directory named for the skill in the location your agent scans. A project-scoped skill lives in the repo so teammates get it on checkout; a personal skill lives in your home config and follows you across projects.

# Project-scoped: committed to the repo, shared with the team
mkdir -p .claude/skills/pr-reviewer

# Personal: yours alone, available in every project (illustrative path)
mkdir -p ~/.claude/skills/pr-reviewer

2. Write the frontmatter that triggers discovery

Open SKILL.md and write the frontmatter. The description is the single most important line in the file: it is what the agent matches against, so it has to name the trigger explicitly, in the words you actually use. “Review code” is too vague. Name the artifact (PR, diff, pull request) and the moment (when the user asks to review changes before merge).

---
name: pr-reviewer
description: >-
  Reviews a pull request or diff for correctness, tests, security, and clarity.
  Use when the user asks to review a PR, review changes, check a diff before
  merge, or asks "is this ready to merge".
---

The description (combined with any when_to_use text) is truncated at a documented character cap in the skill listing, so put the key use case first. Front-load the trigger words and keep it under a few sentences.

3. Write tight, imperative instructions

Below the frontmatter, write the review procedure in plain markdown. State what to do, not why. Because the body stays in context once it loads, treat every line as a standing instruction that costs tokens on every turn. A focused checklist with an explicit output format beats a rambling essay.

# PR Reviewer

Review the diff in scope across four dimensions. For each finding, give the
file and line, a one-line problem statement, and a concrete suggested fix.

## Checklist
1. Correctness: logic errors, off-by-one, null/empty handling, wrong API usage.
2. Tests: are new paths covered? Do existing tests still hold? Flag untested branches.
3. Security: injection, unvalidated input, secrets in code, unsafe deserialization.
4. Clarity: naming, dead code, oversized functions, missing error messages.

## Output format
- Group findings by severity: blocker, should-fix, nit.
- End with a one-line verdict: APPROVE, APPROVE WITH NITS, or REQUEST CHANGES.
- If the diff is empty or unavailable, say so instead of inventing findings.

A skill can pull live data into the prompt before the agent reads it, using a documented inline-shell syntax. That way the review is based on the actual working tree instead of whatever the agent can guess from open files. Check the exact syntax in the official docs; the shape is a backtick-wrapped command prefixed with !.

## Diff under review

!`gh pr diff`

## Changed files

!`gh pr diff --name-only`

When the skill loads, the agent runs these commands first and replaces each line with its output, so the instructions arrive with the real diff already inlined. The model never sees the command, only the result.

5. Trigger discovery, then debug non-triggering

Start a session and ask the agent to review a PR the way you’d normally phrase it. Confirm it loads pr-reviewer on its own. If it does not fire, work on the trigger, not the body, because not firing is almost always a description problem.

1. Ask the agent "what skills are available?" and confirm pr-reviewer is listed.
2. If listed but not firing: the description lacks the words you used.
   Add the trigger phrases ("review this PR", "check the diff") to it.
3. If you have many skills, descriptions can be truncated in the listing.
   Shorten low-priority skills or trim yours so the trigger words survive.
4. As a fallback, invoke it directly: /pr-reviewer

Learned: making the craft stickThe four-dimension checklist no longer depends on you re-pasting it each session. You wrote it into a SKILL.md, and Xiaoman decides on its own from the description when to use it, then runs the full review from the body.

How to verify

The most direct check is to run it through the SDK. A project-scoped skill lives under .claude/skills/, so for an SDK session to find it you have to load the project setting source. In Python that is the single skills=["pr-reviewer"] switch (the SDK wires up the project setting source and the Skill tool for you); in TypeScript, use settingSources: ["project"] to load .claude/skills/ and list Skill in allowedTools. Then give it a plain-language task and watch the message stream for a call to the Skill tool.

import anyio
from claude_agent_sdk import query, ClaudeAgentOptions, AssistantMessage, ToolUseBlock, TextBlock

async def main():
    async for message in query(
        prompt="Review this PR.",   # never names the skill; the description triggers it
        options=ClaudeAgentOptions(
            skills=["pr-reviewer"],   # the one switch: wires up the project source and Skill tool
            cwd="./repo",
        ),
    ):
        if isinstance(message, AssistantMessage):
            for block in message.content:
                if isinstance(block, ToolUseBlock) and block.name == "Skill":
                    print(f"[skill fired] {block.input}")   # watch it load pr-reviewer on its own
                elif isinstance(block, TextBlock):
                    print(block.text)

anyio.run(main)
import { query } from "@anthropic-ai/claude-agent-sdk";

const q = query({
  prompt: "Review this PR.", // never names the skill; the description triggers it
  options: {
    settingSources: ["project"],          // load project skills under .claude/skills/
    allowedTools: ["Skill", "Read", "Grep", "Glob", "Bash"],
    cwd: "./repo",
  },
});

for await (const message of q) {
  if (message.type === "assistant" && message.message) {
    for (const block of message.message.content) {
      if (block.type === "tool_use" && block.name === "Skill") {
        console.log("[skill fired]", block.input); // watch it load pr-reviewer on its own
      } else if (block.type === "text") {
        console.log(block.text);
      }
    }
  }
}
  • Ask “review this PR” in plain language and confirm the agent announces it is using the pr-reviewer skill rather than improvising.
  • Check the output covers all four categories and ends with the verdict line your instructions demand.
  • Reword the request (“can you check these changes before I merge?”) and confirm the skill still triggers. This shows the description generalizes beyond one exact phrase.
  • Run /pr-reviewer directly and confirm manual invocation produces the same structured review.

Learned: confirming it really firesYou can reword "review this PR" a few ways and watch Xiaoman announce it is using pr-reviewer, then run /pr-reviewer and confirm the automatic and manual paths produce the same structured review.

Recap

You turned a one-off prompt into a discoverable, reusable skill: a folder, frontmatter with a trigger-rich description, and a tight imperative body, with the live diff fed in if you want it. The agent now decides when to apply it, and you can also fire it by name. The mechanism is progressive disclosure: descriptions are always loaded, bodies load on match, so a sharp description plus a lean body is the whole thing. The next chapter composes this skill with parameters and tool permissions, then wires hooks and slash-commands into a repeatable workflow.

Common pitfalls

  • Vague description. If the description does not name the artifact and the moment, the agent has nothing to match and never loads the skill. Name PR, diff, review, merge.
  • Bloated body. Because the loaded body sits in context every turn, a long SKILL.md is a recurring tax that also blurs the intent. Keep it imperative and short; move long reference material into separate bundled files referenced from SKILL.md.
  • Wrong folder. A skill in the wrong location is invisible. Confirm project vs personal paths against the official docs.
  • Too many skills, truncated descriptions. With a crowded skill list, descriptions get shortened and your trigger words can be cut. Trim low-priority skills so the ones you use keep their full text.

You write the first SKILL.md, and Xiaoman runs the whole routine unprompted for the first time, then, unusually, drops every disclaimer and just states the result. The Workshop lights up.

Just lit The Workshop · 5 / 16 lit

One handy tool is not enough; it wants more. Next: the Clockwork Hall.

Sources

  1. Anthropic Agent Skills documentation · official
  2. anthropics/skills (example skills repository) · official
UP NEXT · CHAPTER 5 Skills, hooks & slash-commands