writing-skills · git:20260805.006833f · 2026-08-05 · sha256 d3aba81289125707

writing-skills git:20260805.006833fA

Immutable. This exact content never changes and is served at /api/v1/blob/d3aba81289125707.

---
name: writing-skills
description: Use when creating new skills, editing existing skills, or verifying skills work before deployment.
---

# Writing Skills

_Adapted from the MIT-licensed superpowers plugin by Jesse Vincent. See NOTICE.md._

## Overview

**Writing a skill is Test-Driven Development applied to process documentation.** Watch an
agent fail without the skill (baseline), write the skill, then watch a fresh agent comply.
If you never watched the failure, you don't know whether the skill teaches the right thing —
or whether there was anything to teach at all.

A skill is a reusable technique, pattern, or reference — not a narrative about how you solved
something once. Don't create one for a one-off fix or a project-specific convention (put that
in the project's own instructions file instead).

## Start from the template

Copy `templates/SKILL.template.md` as the starting structure for a new skill. It carries the
required frontmatter shape and the section skeleton (When to use / Process / Red flags) —
fill it in rather than improvising frontmatter from scratch.

## Rules this plugin's test suite enforces

These are checked mechanically by `tests/skills.test.mjs` and `tests/skill-contracts.test.mjs`
on every skill under `skills/` — get them wrong and the suite fails, not just a reviewer:

- **`name` matches the containing folder exactly.** `skills/writing-skills/SKILL.md` must
  declare `name: writing-skills`.
- **`description` starts with `Use when`.** It states triggering conditions, not what the
  skill does — see "Write the description as a trigger" below.
- **Every CLI subcommand named in the body must actually exist.** Only `init-run`, `gate`,
  `digest`, `claim`, `unclaim`, and `workflow` are real subcommands of the CLI entrypoint.
  Naming a subcommand that doesn't exist fails the suite, not just the reader.
- **Every `tm-*` agent named in the body must actually exist.** Only `tm-implementer`,
  `tm-reviewer`, and `tm-integrator` exist under `agents/`.
- **The CLI is always invoked through `$CLAUDE_PLUGIN_ROOT`**, never a relative path:
  `node "$CLAUDE_PLUGIN_ROOT/scripts/cli.mjs" <subcommand> --root <project root>`. A relative
  invocation works from the author's shell and breaks for everyone else.

## Write the description as a trigger, not a summary

The description is what a future agent reads to decide whether to load the skill at all — it
answers "should I read this right now?", not "what will I learn if I do?" Name concrete
situations: symptoms, error messages, the moment in a workflow where the need arises. Don't
summarize the skill's process or steps in the description — an agent that reads a workflow
summary in the description will sometimes act on the summary and skip the body, which is worse
than not finding the skill at all.

```yaml
# BAD: abstract capability, not a triggering situation
description: Use when working with async tests

# GOOD: concrete symptom
description: Use when tests have race conditions, timing dependencies, or pass/fail inconsistently

# BAD: summarizes the workflow instead of the trigger
description: Use when executing plans - dispatches one implementer per task with review between tasks

# GOOD: trigger only
description: Use when you have a written implementation plan to execute in a separate session with review checkpoints
```

## A skill must be verified to trigger

Writing a skill and shipping it without confirming it fires is not done. Start a fresh session,
give it a prompt that matches the situations named in the description, and confirm the skill
actually loads and is followed — don't just re-read the description and judge by eye whether it
sounds like it would match.

**A skill that never triggers is worse than no skill.** It sits in the directory creating false
confidence that a process is covered, while every real invocation silently falls through to
default behavior. No skill at all is at least honestly absent.

## Red flags

| Thought | Reality |
|---------|---------|
| "The description is close enough, no need to test triggering" | Close enough to you is not close enough to the matcher. An untested description is an unverified claim that the skill will ever run. |
| "This is a small edit, I don't need to re-verify" | An edit that changes the description or the triggering conditions can silently break discovery. Re-verify triggering after any change to those sections. |
| "I'll skip the template, my frontmatter is fine" | The template's frontmatter shape is exactly what the suite checks. Freehand frontmatter is how a missing field or a name/folder mismatch slips through. |

## Applying this

Both authoring a brand-new skill and editing an existing one under `skills/` go through this
cycle: start from `templates/SKILL.template.md`, write to the rules the suite enforces, write
the description as a trigger, and verify the skill actually fires in a fresh session before
calling it done.