Tinker AI
Read reviews
intermediate 8 min read

Writing your first Claude Code skill: from idea to triggered execution

Published 2026-05-11 by Owner

Every team that uses Claude Code long enough ends up with the same six or seven workflows that someone re-types as a long prompt every time. PR summaries. Commit message drafts. Deployment checklists. Changelog entries. The skill system exists to stop that.

A skill is a directory with a single SKILL.md file. When Claude encounters a slash command that matches the skill’s name, it reads that file and follows the instructions inside. The whole thing is about 30 lines at minimum. The hard part is not the structure—it is writing the description field well enough that Claude invokes the skill at the right moments.

This walkthrough builds one concrete skill from start to finish: summarize-pr, a skill that instructs Claude to fetch a pull request, read its diff, and produce a structured summary for async review.

Anatomy of a skill

Every skill directory looks like this:

.claude/skills/summarize-pr/
└── SKILL.md

SKILL.md has two sections: a YAML frontmatter block and a body. The frontmatter carries metadata; the body is the actual prompt Claude receives as system instructions when the skill runs.

A minimal frontmatter:

---
name: summarize-pr
description: "Summarize a GitHub pull request for async review. Accepts a PR number or URL."
---

Two fields matter:

name is the skill identifier. It must be kebab-case (^[a-z0-9-]+$). This becomes the slash command: /summarize-pr. Claude Code also uses it to resolve the skill directory when you install multiple skills. Keep names short and verb-first where possible: summarize-pr, draft-release-notes, check-a11y. The name is what you’ll type; make it something you can type without thinking.

description is the trigger signal. Claude reads it to decide whether to invoke the skill. This is the field most people write poorly, and it is the direct cause of skills that never fire.

The description needs to answer three questions that Claude is implicitly asking: what does this skill accept as input, what does it produce as output, and what distinguishes it from the generic behavior Claude would use without the skill? A description that answers all three is usually 20-50 words. A description that is shorter has probably left one of those questions unanswered.

Bad descriptions for summarize-pr and why they fail:

  • "Summarize a pull request." — says nothing about input format, output structure, or how it differs from Claude’s default PR discussion behavior
  • "PR summary tool" — a label, not a trigger signal; Claude treats labels as weak hints
  • "Fetch a PR and write a summary with sections for what changed, why, risk, and focus" — closer, but doesn’t mention that the summary is for async reviewers, which is what distinguishes it from an inline discussion summary

Good description: "Summarize a GitHub pull request for async review. Accepts a PR number or URL. Produces a structured summary: what changed, why, risk areas, and reviewer focus." — 30 words, answers all three questions, the phrase “async review” creates a use-case boundary that keeps the skill quiet during normal PR discussion.

What the body actually is

The body is a system prompt. When Claude invokes the skill, it reads the body and follows it as instructions for the rest of that turn. Everything in the body should be written the way you’d write instructions for a capable assistant who has access to Claude Code’s tools—Bash, Read, web fetching if available—but who doesn’t know your project or your preferences.

Here is a body that does not work well:

---
name: summarize-pr
description: "Summarize a pull request."
---

Summarize the pull request. Include what changed and why. Be concise.

This fails in two ways. The description is too broad to trigger reliably on PRs while staying quiet elsewhere. The body gives Claude latitude to do almost anything—“summarize” could mean three lines or three pages, could read the diff or just the PR title, could use bullet points or paragraphs.

Here is a body that works:

---
name: summarize-pr
description: "Summarize a GitHub pull request for async review. Accepts a PR number or URL. Produces a structured summary: what changed, why, risk areas, and reviewer focus."
---

## summarize-pr

You are summarizing a GitHub pull request for someone who will review it asynchronously.

**Input:** The user has provided either a PR number (within the current repo) or a full PR URL.

**Steps:**
1. Fetch the PR metadata using `gh pr view <number-or-url> --json title,body,author,additions,deletions,changedFiles`
2. Fetch the diff: `gh pr diff <number-or-url>`
3. Read any files that are non-obvious from the diff alone (config changes, schema changes, new abstractions)

**Output format — use this exactly:**

### PR #<number>: <title>

**Author:** <author>  
**Size:** +<additions> / -<deletions> across <changedFiles> files

#### What changed
<2-4 sentences describing the functional change, not the implementation>

#### Why (from PR description)
<1-2 sentences paraphrasing the stated rationale>

#### Risk areas
<Bulleted list of files or patterns that deserve close scrutiny. If none, write "None identified.">

#### Suggested reviewer focus
<1-3 specific things the reviewer should verify>

Do not add commentary outside this structure. Do not repeat information already in one section in another.

The difference is density of constraint. The working version specifies exactly what commands to run, what JSON fields to request, what the output structure is, and what to omit. The model has no decisions to make except the ones that require judgment—identifying risk areas and what to tell the reviewer to focus on.

Installing and invoking

Skills can live in two places:

Project-local: .claude/skills/<skill-name>/SKILL.md inside your repository. Only applies when Claude Code is running in that project. The right location for skills that encode project-specific context—deployment steps, test patterns, how your PR descriptions are structured.

User-global: ~/.claude/skills/<skill-name>/SKILL.md. Available in every project. The right location for skills that are independent of project context—PR summaries, generic commit message formats, changelog templates.

Both locations are just directories. Create the directory, create the file, restart Claude Code (or reload the skill list if there is a hotkey for that in your version). The skill should appear in /skills list output.

Skills can also be grouped into plugins. A plugin is a directory with a manifest file that references a collection of skills by path. The manifest carries fields for plugin name, version, description, and an array of skill entries. A manifest for a set of PR-workflow skills might look roughly like this:

{
  "name": "pr-workflow",
  "version": "1.0.0",
  "description": "Skills for pull request review and release note workflows",
  "skills": [
    { "path": "./summarize-pr" },
    { "path": "./draft-release-notes" },
    { "path": "./check-pr-checklist" }
  ]
}

Installing the plugin installs all three skills at once. Exact field names in the manifest vary by Claude Code version—check the current documentation rather than treating the shape above as authoritative—but the structure is consistently: plugin-level metadata at the top level, an array of skill references.

The marketplace (when available) aggregates published plugins. Installing a marketplace plugin typically looks like a single command pointing at a registry identifier or Git URL. The skills land in ~/.claude/skills/ under a subdirectory named after the plugin. Local plugin directories work the same way and can be checked into source control alongside your project, which is how teams share skill sets without going through any registry.

For a single skill like summarize-pr, the plugin format adds ceremony without benefit. Use it when you have three or more related skills that travel together, or when you want to distribute the skill to other people without asking them to clone a directory manually.

Debugging a skill that doesn’t trigger

The most common failure: you type /summarize-pr 423 and Claude either doesn’t recognize the command or starts doing something generic rather than following the skill body.

The cause is almost always the description.

Claude uses the description—not the name—to match intent. A description like "Summarize a pull request." is too short to distinguish from a dozen adjacent requests Claude already handles without skills. Claude may decide it can satisfy this itself and never touch the skill.

The iteration loop:

  1. Make the description more specific. Add what the skill accepts as input, what it produces, and one key constraint that distinguishes it from generic behavior. The working example above does this: it names the input types (PR number or URL), names the output structure (structured summary with four named sections), and names the use case (async review).

  2. Test with an explicit slash command. /summarize-pr 423 forces skill resolution by name. If this works but "summarize pr 423" in natural language doesn’t, the description is still too generic. Keep making it more specific until both work.

  3. Check the body for implicit ambiguity. If your body says “fetch the PR details,” Claude has to decide what “details” means. Each undefined term is a place where the model may do something reasonable but not what you wanted. Replace every implicit decision with an explicit instruction.

  4. Length is not the problem. A 10-line body that is fully constrained beats a 40-line body with wiggle room. The body is not documentation for humans—it is instructions for a model. Write it like you would write a machine-readable spec, not a README.

One signal that a description has become specific enough: reading it should make clear to a human, without context, exactly when this skill should and should not fire. If a colleague could look at the description and say “yes, I’d trigger this for a PR review and not for anything else,” the description is probably good enough.

What makes a skill worth writing

Not every workflow is a good skill candidate. Signs that something is worth encoding:

  • It involves a predictable sequence of tool calls that Claude would otherwise figure out through trial and error (wasting tokens and time)
  • The output format matters and varies across runs without constraints
  • It runs at least a few times per week

Signs that something is not worth it yet:

  • The task changes shape frequently—skills work best for stable workflows
  • The one-line prompt already works reliably—if "summarize this PR in four sections" does what you need every time, a skill adds nothing

The summarize-pr skill above earns its existence because the sequence (fetch metadata, fetch diff, read affected files, produce structured output) is stable, the output format matters for async review, and it runs multiple times per week on any active team.

The skill system is a way to make that institutional knowledge portable. Once the skill exists, anyone on the team—or any project that installs the plugin—gets the same workflow without having to know how to prompt for it.

There is a compounding effect here worth naming. A skill codifies not just “what Claude should do” but “what a correct output looks like.” Teams that ship a structured PR summary skill train reviewers to expect a consistent format. After a few months, the format itself becomes infrastructure—tickets get linked, CHANGELOG entries get written against it, onboarding docs reference it. The skill is load-bearing in ways that weren’t visible when you wrote the 30-line SKILL.md.

This is the hidden value of the skills system over ad-hoc prompts. A prompt lives in someone’s head or in a Notion page that rots. A skill lives in the repository, gets reviewed, gets improved, and runs identically for everyone who checks out the code. The gap between “Claude is useful to me personally” and “Claude is useful to our team” closes faster when that shared context is encoded in files rather than folklore.

The next skill worth writing is probably the one you just re-typed from memory for the third time today. The third time is the signal. Write the skill after the second repetition and you will have paid it off by the end of the week.