write-skill
Use when the user wants to create a new Claude Code skill. Guides skill creation with playbook patterns.
SKILL.mdhome/dot_claude/skills/write-skill/SKILL.md
GitHubSkill 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:
globalorin ~/.claude/skills/→ Skill goes in dotfiles via chezmoi, available everywherelocalorin .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:
- Analyze the conversation for the repeatable pattern
- Identify: trigger conditions, tools used, decision points, inputs/outputs
- Propose a skill name and description based on what was done
- Ask about scope (global vs local)
- Draft the skill capturing the workflow as a playbook
- 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 diffto previewchezmoi apply ~/.claude/skills/<name>to deploychezmoi statusto verify
For local skills: No deployment needed - the skill is immediately available in the project.
4. Principles
Frontmatter:
description: "Use when..." for auto-invokewhen_to_use: trigger phrases — appended todescription; combined cap is 1,536 chars, key use case firstpaths: glob likesrc/**/*.tsto scope auto-activation to matching filescontext: fork: noisy output that won't inform follow-updisable-model-invocation: true: side-effect skills (user-only trigger)user-invocable: false: hide from/menu — background knowledge Claude loads silentlymodel/effort: leave unset (see Draft Skill above)hooks: lifecycle hooks scoped to this skillallowed-tools: tools that run without approval. Omitted tools still work; whether they prompt is mode-dependent — underdefaultMode: autothey may run silently, so omission is never a gateSkill(<child>)inallowed-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 shorthandN(Nth positional, 0-indexed) - Named: declare
arguments: [issue, branch]in frontmatter → reference each by its name (e.g. the placeholder forissue) - 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
ultrathinkin 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: truefor 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 auditsallowed-toolsagainst 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-toolsbefore deployment - README-PATTERN.md - REQUIRED shape for the
README.mdevery skill ships; what belongs there vs inSKILL.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.mdinstead of arguing why the skill exists - Strict positional arguments
- Broad tool access
- Verbose prose over terse bullets
- Missing
context: forkfor 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-toolsas a gate