write-skill

Use when the user wants to create a new Claude Code skill. Guides skill creation with playbook patterns.

SKILL.md

GitHub

Skill Creation Playbook

Autonomy: human-only · acts autonomously — writes the skill and README files, spawns the single reviewer pass, and deploys with chezmoi apply without further confirmation

CRITICAL: Before deploying, spawn exactly one reviewer agent with REVIEW.md to audit allowed-tools — overly permissive access (e.g. Bash(git:*)) can cause data loss or leak secrets. The reviewer advises on facts and has no standing on intent: never apply its opinion about how much autonomy a skill should have. One agent, one pass — see step 5.

Note on substitutions (workaround): This playbook is itself loaded as a skill, so Claude Code would substitute platform variables — ARGUMENTS, CLAUDE_SKILL_DIR, CLAUDE_SESSION_ID, etc. — in this file at load time if they appeared in their literal $-prefixed form. To teach the syntax without triggering premature substitution, the playbook below names variables without the leading $ and describes the syntax in prose. In the new skill file you write, always prefix with $ (or use the braced form for clarity). The body-structure example below uses \!\`echo` to inject one literal placeholder for copy/paste — dynamic-context output is not re-scanned for substitutions. The supporting files (LIFECYCLE.md, REVIEW.md, SHIM-PATTERN.md) are not loaded as skill bodies, so they show literal forms directly.

Create skills as flexible playbooks, not rigid scripts.

Arguments

$ARGUMENTS

Pre-computed Context

The following paths and listings were computed before this skill was invoked:

Chezmoi source path: !chezmoi source-path 2>/dev/null || echo "(chezmoi not available)" Global skills source: !chezmoi source-path 2>/dev/null/dot_claude/skills Global skills deployed: ~/.claude/skills

Process

0. Determine Scope

Check if arguments specify global or local:

  • global or in ~/.claude/skills/ → Skill goes in dotfiles via chezmoi, available everywhere
  • local or in .claude/skills/ → Skill goes in current project's .claude/skills/, scoped to this repo

If scope is unclear, ask:

"Should this skill be global (available in all projects via dotfiles) or local (scoped to this project only)?"

1. No Arguments? Extract from Conversation

If arguments are empty, the user wants to codify the task just performed:

  1. Analyze the conversation for the repeatable pattern
  2. Identify: trigger conditions, tools used, decision points, inputs/outputs
  3. Propose a skill name and description based on what was done
  4. Ask about scope (global vs local)
  5. Draft the skill capturing the workflow as a playbook
  6. Ask user to confirm or refine before writing

2. Gather Context (when args provided)

If unclear, ask:

  • Trigger? (user/auto/always)
  • Tools needed?
  • Fork context? (noisy output)
  • Side effects? (commits/deploys/APIs)

3. Draft Skill

Every skill is two files: the SKILL.md the agent runs and a README.md a human reads. Write both — a skill without a README is not finished. Shape and section-by-section guidance: README-PATTERN.md.

For global skills: Create $(chezmoi source-path)/dot_claude/skills/<name>/SKILL.md and README.md alongside it. Use a .tmpl suffix on SKILL.md only when the body needs a chezmoi directive (.chezmoi.os, lookPath, …); the README is never templated.

For local skills: Create .claude/skills/<name>/SKILL.md and README.md in the current working directory (plain .md, no template).

Frontmatter template (both scopes):

---
name: <kebab-case>
description: <When to use + what it does>
argument-hint: <flexible, use brackets>
disable-model-invocation: <true if user-only>
context: <fork if output not needed>
allowed-tools: <minimal safe subset>
---

Leave model: and effort: unset. A skill runs in the main conversation, and a value that differs from the session's forces a full prompt-cache miss into and out of that turn (ADR-009, docs/adrs/ in the dotfiles repo). Pin a model on a subagent instead — it has its own context.

Both follow the same body structure (the line inside the Arguments block uses \!\`echo` to print a literal ARGUMENTS placeholder, $-prefixed — copy that placeholder as-is into your skill):

# <Title>

**Autonomy:** <trigger> · <commit posture>

## Arguments
\`\`\`
\!\`echo '$ARGUMENTS'`
\`\`\`

## Instructions
<Decision-tree playbook>

## Examples
<Varied inputs and outcomes>

For global skills: After writing, run:

  • chezmoi diff to preview
  • chezmoi apply ~/.claude/skills/<name> to deploy
  • chezmoi status to verify

For local skills: No deployment needed - the skill is immediately available in the project.

4. Principles

Frontmatter:

  • description: "Use when..." for auto-invoke
  • when_to_use: trigger phrases — appended to description; combined cap is 1,536 chars, key use case first
  • paths: glob like src/**/*.ts to scope auto-activation to matching files
  • context: fork: noisy output that won't inform follow-up
  • disable-model-invocation: true: side-effect skills (user-only trigger)
  • user-invocable: false: hide from / menu — background knowledge Claude loads silently
  • model / effort: leave unset (see Draft Skill above)
  • hooks: lifecycle hooks scoped to this skill
  • allowed-tools: tools that run without approval. Omitted tools still work; whether they prompt is mode-dependent — under defaultMode: auto they may run silently, so omission is never a gate
  • Skill(<child>) in allowed-tools: declares the delegation graph — the parent may invoke exactly these children, and its authorization flows down to them. It never relaxes a child's evidence bar

Arguments (prefix each name with $ in your skill — see note at top):

  • Free-form input pattern: [package | url...] not <package>
  • Positional substitutions: ARGUMENTS (full string), ARGUMENTS[N] or shorthand N (Nth positional, 0-indexed)
  • Named: declare arguments: [issue, branch] in frontmatter → reference each by its name (e.g. the placeholder for issue)
  • Show varied inputs in examples; parse flexibly, not strictly

Content:

  • Decision trees, not linear scripts
  • <60 lines; externalize reference material (skill body persists across turns — see LIFECYCLE.md)
  • Refer to "arguments" after the Arguments section
  • Dynamic values via braced ${...} substitutions: CLAUDE_SESSION_ID, CLAUDE_EFFORT, CLAUDE_SKILL_DIR (full list in LIFECYCLE.md)
  • Multi-line shell injection: open a fenced code block with three backticks + ! instead of the inline single-backtick ! form
  • Include the keyword ultrathink in the body to request deep reasoning at runtime

Autonomy (three independent axes — conflating them is the most common bug):

  • A. Trigger — who invokes. disable-model-invocation: true for human-only. Use it when unrequested invocation is the risk; never to make a skill timid.
  • B. Judgment — never gated. A skill always reaches a verdict, gives the one-line reason, and says what would change its mind. "The user should decide" is not a posture: abstention deadlocks any orchestrator that composes the skill.
  • C. Commit — who performs the irreversible act. Prefer withholding the capability over prompting for it. A gate that must hold in every mode is an explicit in-chat confirmation in the body — never omission from allowed-tools.
  • Declare A and C in the body's **Autonomy:** line; the reviewer audits allowed-tools against it. Depth: AUTONOMY.md.
  • A skill named with a verb acts. If the request already carries the decision, act on it.

5. Review (REQUIRED — one agent, one pass)

Spawn one reviewer agent, once. Give it REVIEW.md, the drafted files (SKILL.md and README.md), the user's request verbatim, and the **Autonomy:** line — a reviewer that can't see the intent will invent one.

Dispose of findings by the reviewer's standing, not by how urgent they sound:

Finding Standing Disposition
Fact — checkable against source, docs, or observed behavior Full; it read the code, you didn't Verify, then apply. Mechanical and small.
Intent — what the skill should do, who decides, how much autonomy None; it never saw the request Surface verbatim, unapplied. The user decides.

Never spawn a second reviewer, re-review after fixing, or run reviewers in parallel. And never apply an intent finding on the reviewer's authority — it is an amnesiac with no memory of why this skill exists, and deferring to it overwrites the user's stated intent with a stranger's risk appetite. A reviewer that recommends a skill withhold a judgment is wrong by construction (axis B).

Quick Reference

Goal Frontmatter
User-only trigger disable-model-invocation: true
Auto-invoke description with "Use when..."
Extra trigger phrases when_to_use: ... (counts toward 1,536-char cap)
Scope by file path paths: "src/**/*.ts"
Hidden from / menu user-invocable: false
Isolate context context: fork
Specific agent context: fork + agent: Explore
Named arguments arguments: [foo, bar] → reference each by its $-prefixed name
Lifecycle hooks hooks: ...
Declare autonomy **Autonomy:** line in the body (axes A + C)
Delegate to sub-skills Skill(<child>) in allowed-tools

Supplementary Docs

  • REVIEW.md - REQUIRED checklist for the single reviewer pass; audits allowed-tools before deployment
  • README-PATTERN.md - REQUIRED shape for the README.md every skill ships; what belongs there vs in SKILL.md
  • AUTONOMY.md - The three axes, declaration grammar, delegation graphs, and writing skills that compose into unattended loops
  • LIFECYCLE.md - Skill content lifecycle, compaction budget, description cap, discovery rules
  • SHIM-PATTERN.md - Wrapper scripts for enforcing constraints (advanced)
Safe Unsafe
Bash(git status:*) Bash(git:*)
Bash(npm test:*) Bash(npm:*)
Read, Grep, Glob Bash(rm:*)

Examples

/write-skill                              → extract skill from current conversation (will ask about scope)
/write-skill global deploy                → global skill for deployments (via chezmoi)
/write-skill local lint-fix               → project-local skill for this repo only
/write-skill in ~/.claude/skills/ search  → global skill (explicit path)
/write-skill in .claude/skills/ format    → local skill (explicit path)

Scope Decision Guide

Choose global when... Choose local when...
Skill is useful across many projects Skill is specific to this codebase
General-purpose workflow (git, testing) Project-specific conventions
You want it in your dotfiles Collaborators should have it too
Personal preference/style Team or repo-specific process

Anti-patterns

  • Shipping a skill without a README.md, or templating one as .md.tmpl
  • A README that restates SKILL.md instead of arguing why the skill exists
  • Strict positional arguments
  • Broad tool access
  • Verbose prose over terse bullets
  • Missing context: fork for noisy ops
  • Hardcoded paths vs arguments
  • Global skill for project-specific logic
  • Local skill for personal workflows you'd want everywhere
  • More than one reviewer agent, or a second review pass (see step 5)
  • Applying a reviewer's intent findings without the user — the crux failure
  • Gating judgment: a skill that reports findings without reaching a verdict
  • A confirmation prompt where withholding the capability would do
  • Treating omission from allowed-tools as a gate