How to Create Claude Skills: Build One End to End, Then Test It
Type /pr-description develop in Claude Code and you get a pull request title and body drafted from your branch's commits. A plain request like "draft the PR description for this branch" can trigger the same skill without the slash. It takes three files: SKILL.md, a template and a short shell script.
If you want the concept first (what a skill is, how it compares with MCP or a project), start with the Claude Skills chapter. Ready-made skills are in the Claude Skills catalog, and the coding ones are collected under Claude Code skills.
Where the skill folder goes
Put a skill your team should share in the repository. Claude Code reads skills from several locations, and the folder decides which sessions load it:
| Location | Path | Loads in |
|---|---|---|
| Personal | ~/.claude/skills/<name>/SKILL.md | All your projects on this machine |
| Project | .claude/skills/<name>/SKILL.md | Sessions in this repository; commit it and your team gets it |
| Plugin | <plugin>/skills/<name>/SKILL.md | Wherever the plugin is enabled, as /plugin-name:skill-name |
Anthropic's Complete Guide to Building Skills for Claude sets three naming rules: the file is called exactly SKILL.md, case-sensitive; the folder name is kebab-case; and no README.md goes inside the folder. And you don't need to restart: Claude Code watches skill folders and picks up edits in the current session. If the top-level skills folder didn't exist when the session started, run /reload-skills after each change there.
The skill: pr-description
A PR description is a good first skill because you already explain it to Claude the same way every time: read the commits, follow the team template, flag migrations. The finished folder:
.claude/skills/pr-description/
├── SKILL.md
├── template.md
└── scripts/
└── branch-context.shHere's the full SKILL.md:
---
name: pr-description
description: Writes a pull request title and description from the commits and diff on the current branch. Use when the user asks for a PR description, PR body or pull request summary, or is about to open a pull request. Not for commit messages or code review.
argument-hint: "[base-branch]"
allowed-tools: Bash(${CLAUDE_SKILL_DIR}/scripts/branch-context.sh *) Bash(git diff *)
---
# PR description
Base branch: $ARGUMENTS (if empty, use main).
1. Run `${CLAUDE_SKILL_DIR}/scripts/branch-context.sh [base branch]` and read
the commits and changed files it prints. If it says the base branch wasn't
found, stop and ask which branch the PR targets.
2. For a file whose stat line doesn't explain the change, read it with
`git diff [base branch]...HEAD -- [file]`.
3. Fill in [template.md](template.md). Keep every heading. Write "None" under
a heading with nothing to report.
4. Title: imperative mood, under 72 characters, no trailing period.
5. Print the title and the filled template as one markdown block. Don't push,
and don't open the PR.
## Rules
- Take the what and the why from the commits and the diff. If the why isn't
in either, ask for it.
- Every migration, new environment variable and new config key goes under
"Deploy notes".
- Under "Testing", name the tests added or changed. If there are none, say so.
## Example
Commits "add retry to webhook sender", "cap retries at 3", "test retry backoff"
Title: Retry failed webhook deliveries up to 3 timesWhat each front-matter line does:
namesets the command you type. Leave it out and Claude Code uses the directory name. The platform spec allows 64 characters of lowercase letters, numbers and hyphens, with no reserved words like "claude" or "anthropic". Anthropic suggests gerund names such asprocessing-pdfs, and accepts noun phrases.descriptionis the line Claude reads to decide. Names and descriptions load into the system prompt at startup, and Claude matches your request against the description; the body loads only when the skill is invoked. The platform caps it at 1,024 characters, and Claude Code truncates description pluswhen_to_useat 1,536 characters in its listing, so the use case goes first.argument-hintshows[base-branch]in autocomplete. It's quoted because unquoted square brackets are a YAML list.allowed-toolslets Claude run the script andgit diffwithout a permission prompt. The grant lasts for the turn that invokes the skill and doesn't restrict other tools.
${CLAUDE_SKILL_DIR} expands to the skill's own folder in both the body and the allowed-tools rule, so the pre-approval matches the exact command the body tells Claude to run, wherever the skill is installed. $ARGUMENTS becomes whatever you type after the command.
The helper script
Gathering the commits is the fragile step, so it's a script. Claude runs a bundled script through bash, and only its output enters context, not its code. Save this as scripts/branch-context.sh and run chmod +x on it:
#!/usr/bin/env bash
# Prints what a PR description needs: commits and changed files vs. the base branch.
set -euo pipefail
base="${1:-main}"
if ! git rev-parse --verify --quiet "$base" > /dev/null; then
echo "Base branch '$base' not found. Local branches:"
git branch --format='%(refname:short)'
exit 0
fi
echo "Base: $base"
echo
echo "Commits:"
git log --oneline "$base"..HEAD
echo
echo "Files changed:"
git diff --stat "$base"...HEADA missing base branch doesn't crash it. The script prints the branches that do exist and exits cleanly, so Claude has something to ask you about.
The template file
template.md is plain markdown:
## Summary
What this PR does and why, in one to three sentences.
## Changes
- One line per change a reviewer should know about.
## Testing
Tests added or changed, and how to run them.
## Deploy notes
Migrations, environment variables, config keys. "None" if there are none.Four headings would fit inside SKILL.md. Keep them in their own file anyway: whoever owns the team's PR format can change it without touching the instructions. Claude Code's docs say to link each supporting file from SKILL.md and keep SKILL.md itself under 500 lines.
Run it
Start a message with the command and an optional base branch:
/pr-description developThe slash only runs the skill at the start of a message. Mid-sentence, "go ahead and /pr-description" counts as permission for Claude to use it, and Claude judges from your wording whether to.
Test that Claude picks it up
Seeing a skill trigger once proves Claude found it, nothing more. Anthropic's Claude Code docs say to run each test prompt in a fresh session, with the skill on and again with it off, because context left over from writing the skill hides gaps in it. Switch it off for the baseline with "pr-description": "off" under skillOverrides in your settings.
Two checks come before any prompt. Ask What skills are available? and confirm pr-description is listed. Then ask When would you use the pr-description skill?, which makes Claude quote the description back so you can see what it's matching against.
Then the prompts. The guide's triggering tests cover obvious tasks, paraphrased requests and unrelated topics. Anthropic's skill-creator adds that the useful negatives are near-misses that share keywords with the skill. For pr-description:
| Prompt | Expected | What it tests |
|---|---|---|
| I'm opening a pull request from this branch into develop, draft the description | Triggers | The obvious phrasing, with a base branch |
| write up what this branch changes so reviewers know what they're looking at | Triggers | A paraphrase with no "PR" in it |
| write a commit message for what's staged | Doesn't | The commit-message near-miss the description excludes |
| review the diff on this branch for bugs before I open the PR | Doesn't | Mentions a PR, but it's a code review |
| add a Screenshots section to .github/pull_request_template.md | Doesn't | Shares "pull request" and "template", wrong job |
Keep test prompts substantive. Claude consults skills for tasks it can't easily handle on its own, so a one-step request may skip even a perfect description. Rows two to five carry the information. A pass on row two means the description covers how people actually ask, passes on rows three and four mean the exclusion line works, and a pass on row five shows Claude isn't matching on shared words alone.
When it never fires, or fires too often
If the paraphrase row fails, the description lacks the words people use. Claude Code's checklist for a skill that doesn't trigger starts there:
- Add the phrases users actually type. Here that means adding "write-up for reviewers" next to "PR description".
- Check the YAML. If it doesn't parse, the skill loads with empty metadata:
/pr-descriptionstill works, but Claude can't match the description.claude plugin validate .claude/skillsfinds theSKILL.mdwhose front matter doesn't parse. - With many skills installed, Claude Code drops descriptions to fit a budget of 1% of the context window, starting with the skills you invoke least.
Anthropic's skill-creator goes further and calls for "pushy" descriptions, because Claude tends to undertrigger. That's the right call for a skill whose output Claude can't produce well alone.
When a near-miss row fires, narrow it. Claude Code's fix for a skill that triggers too often is a more specific description, or disable-model-invocation: true so only /pr-description starts it. The guide adds negative triggers, which is what "Not for commit messages or code review" is doing. Reach for disable-model-invocation only when the skill has side effects, like a deploy. A PR draft is harmless, so tighten the wording.
A third failure looks like a trigger problem and isn't. The skill fires, and three turns later Claude stops following it. The body enters the conversation once and isn't re-read, and after compaction Claude Code keeps the first 5,000 tokens of each skill. Claude Code's fix depends on the rule. One that must hold every time moves into a hook. Guidance Claude applies with judgment goes near the top of SKILL.md, worded for the whole task: "keep the PR body under 10 lines unless the diff touches more than one module".
Anthropic's best practices, as do and don't
Anthropic's skill authoring best practices, applied to pr-description:
| Rule | Don't | Do |
|---|---|---|
| Assume Claude already knows | "A pull request asks to merge one branch into another, and reviewers read its description to…" | "Title: imperative mood, under 72 characters." |
| Description in third person | "I can help you write PR descriptions" | "Writes a pull request title and description from…" |
| Match freedom to fragility | "Collect the commits somehow" | An exact script command for data; free wording for the summary |
| References one level deep | SKILL.md links template.md, which links sections.md | Every file linked straight from SKILL.md |
| Scripts handle their own errors | git log main..HEAD that dies on a repo without main | Print the branches that exist and exit 0 |
| One term per thing | "base branch" in step 1, "target" in step 2, "upstream" in the rules | "base branch" everywhere |
| Evaluations before instructions | Write 200 lines, then try it | Three tasks Claude gets wrong without the skill, then just enough text to fix them |
One more applies if your team mixes models: Anthropic says to test the skill with every model you plan to run it on, since what's enough for Opus may need more detail for Haiku.
Share it with your team
Commit .claude/skills/ and your team has it. Past one repo, Claude Code can share skills through a plugin, or through managed settings for a whole organization. A plugin is worth it when you're packaging several skills, subagents, hooks or MCP servers as one unit for many projects.
Two cautions before you push. Uploading to claude.ai or the Skills API accepts only six front-matter fields, so argument-hint would fail that upload with a hard error; drop it for a skill headed there. And a project skill's allowed-tools applies even in a folder you've never trusted, so read those lines in any repo you clone.
Project facts every session needs go in CLAUDE.md, not in a skill. When a job's output would flood the conversation, hand it to a subagent instead.
When skill-creator beats writing by hand
Write by hand when you already know the procedure and the skill fits on a page, as pr-description does. Use Anthropic's skill-creator when trigger accuracy matters or you need proof the skill helps. In Claude Code it runs each test in an isolated subagent, benchmarks with-skill against without, and tunes the description from should-trigger and should-not-trigger prompts. Install it with:
/plugin install skill-creator@claude-plugins-officialThen ask evaluate my pr-description skill with skill-creator. Each test runs twice, so it costs more usage than the five prompts above. Spend it on a skill the whole team will lean on.
Sources
- Anthropic, Extend Claude with skills, Claude Code Docs, accessed November 2026
- Anthropic, Agent Skills overview, Claude Platform Docs, accessed November 2026
- Anthropic, Skill authoring best practices, Claude Platform Docs, accessed November 2026
- Anthropic, The Complete Guide to Building Skills for Claude, accessed November 2026
- Anthropic, skill-creator SKILL.md, anthropics/skills on GitHub, accessed November 2026
- Anthropic, Plugins overview, Claude Code Docs, accessed November 2026