From writing
Guide for authoring well-structured agent skills using a knowledge/procedures/decisions taxonomy. Use when users want to create a new skill, improve an existing skill's structure, or convert a workflow into a skill. Triggers on phrases like "create a skill", "write a skill", "turn this into a skill", "structure this skill", "improve this skill", or any request to encode a workflow, runbook, or process as a reusable SKILL.md. Also use when a user asks how to organise skill content, or wants to add state-machine logic to an existing skill.
How this skill is triggered — by the user, by Claude, or both
Slash command
/writing:skill-authoringThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
This skill teaches you how to write effective SKILL.md files using a structured taxonomy. Every skill encodes some combination of six concerns. Recognising which concerns are present — and separating them cleanly — is what makes a skill reliable and maintainable.
This skill teaches you how to write effective SKILL.md files using a structured taxonomy. Every skill encodes some combination of six concerns. Recognising which concerns are present — and separating them cleanly — is what makes a skill reliable and maintainable.
| Concern | What it answers | Required? |
|---|---|---|
| Variables | What external names/refs does this skill depend on? | Recommended |
| Prerequisites | What must be true before execution? | If applicable |
| Knowledge | What facts/conventions does the agent need? | Usually |
| Procedures | What steps does the agent follow? | Usually |
| Decisions | What branching/state logic governs the flow? | If applicable |
| Constraints | What must the agent never do? | Recommended |
| Validation | How does the agent know it succeeded? | Recommended |
Not every skill needs all six. A pure knowledge skill (e.g. "how our API auth works") may only have Variables, Knowledge, and Constraints. A workflow skill will have most or all.
The rest of this guide explains each concern, its position in the document, and how to write it well.
Skills should follow this order. The rationale: the agent reads top-to-bottom, so it needs context (variables, prerequisites) before instructions (procedures, decisions), and boundaries (constraints, validation) bookending the whole thing.
1. Variables ← names, refs, config that may change
2. Prerequisites ← what to check/verify before starting
3. Knowledge ← facts, conventions, reference material
4. Decisions ← state machine / branching logic (the map)
5. Procedures ← step-by-step actions (the directions)
6. Constraints ← boundaries, invariants, never-do rules
7. Validation ← success criteria, verification steps
Note: Decisions comes before Procedures. The agent should see the full map of possible states before reading the detailed steps for each state. This prevents the model from charging down a happy path and losing track of branches.
A lookup table of external references. Anything that could change independently of the skill's logic — agent names, tool names, repo paths, branch conventions, complementary skills — goes here. This avoids scattering references through prose where they fall out of sync.
Use a simple key-value table. Keys should be UPPER_SNAKE_CASE so they stand out when referenced later in the document.
## Variables
| Variable | Value | Notes |
| ------------- | -------------------------------------- | ------------------------------ |
| PRIMARY_AGENT | scout | The exploration/research agent |
| REVIEW_AGENT | sentinel | The code review agent |
| RELEASE_SKILL | npm-release | Complementary release workflow |
| MAIN_BRANCH | main | Protected branch |
| REGISTRY | https://registry.npmjs.org | Publish target |
| COMMIT_TYPES | feat, fix, chore, docs, refactor, test | Allowed semantic prefixes |
Then later in the document, refer to these by name:
### From DIRTY_WORKTREE
1. Classify changes using COMMIT_TYPES
2. Push to MAIN_BRANCH
3. Hand off to REVIEW_AGENT for verification
Push to the main branch (we call it "main" but some repos use "master")
and then the sentinel agent will review it, or if you renamed it, whatever
the review agent is called now.
This buries references in prose. When the user renames "sentinel" to "guardian", they have to find-and-replace through the entire skill and hope they caught everything.
Very simple skills with no external references. If the skill is self-contained (e.g. "how to write a good commit message"), you probably don't need this section.
Minimal checks the agent must perform before starting. These are conditions, not steps. Keep this section short — if it's longer than 5-6 lines, some of it probably belongs in Procedures or Knowledge.
## Prerequisites
- Working directory is a git repository
- `npm` is available on PATH
- User has publish permissions to REGISTRY
- No merge conflicts in current branch
## Prerequisites
- First, check if git is installed by running `git --version` and parsing the output
to ensure it's at least version 2.x. If it's not, suggest the user install...
- Then verify npm by running `npm --version` and check that...
This is procedures disguised as prerequisites. Prerequisites state what must be true, not how to check it. The agent can figure out how to verify "npm is available on PATH".
Facts, conventions, reference material, and domain context the agent needs but wouldn't know from training. This is declarative — it tells the agent what things are, not what to do with them.
cat for detail## Knowledge
### Commit message format
We use Conventional Commits: `type(scope): description`
- type: one of COMMIT_TYPES
- scope: the package or area affected (optional for single-package repos)
- description: imperative mood, lowercase, no period
### Version strategy
We follow semver. The relationship between commit types and version bumps:
| Commit type | Version bump |
| ------------------------------- | ------------ |
| fix | patch |
| feat | minor |
| any with BREAKING CHANGE footer | major |
### Package structure
For detailed package layout, read `references/package-structure.md`.
## Knowledge
Conventional Commits is a specification for commit messages. You can read about it
at conventionalcommits.org. It was created in 2017 and has gained wide adoption...
Don't teach the model things it already knows. Focus on your specific application of general concepts. The model knows what Conventional Commits is; it doesn't know which types your team allows or how you map them to version bumps.
The state machine. This section provides the map of all possible states and transitions before the agent reads any detailed procedures. Use XState-inspired prose — named states, explicit guards, and unambiguous transitions.
If your skill is purely linear (do A, then B, then C), skip this section and just use Procedures.
Borrow XState's concepts (states, guards, transitions) but write them in markdown the model can follow as instructions. Do not use actual XState JSON or mermaid — these are data formats, not instruction formats.
## Decisions
Entry state: CHECK_WORKTREE
### CHECK_WORKTREE
- guard: `git status --porcelain` is empty
→ BUMP_VERSION
- guard: has unstaged changes
→ SEMANTIC_COMMIT
- guard: has merge conflicts
→ STOP with error: "Resolve merge conflicts before releasing"
### SEMANTIC_COMMIT
- action: create a semantic commit (see Procedures)
- always → CHECK_WORKTREE
### BUMP_VERSION
- action: bump version based on commits since last tag (see Procedures)
- guard: bump succeeded
→ BUILD
- guard: bump failed
→ STOP with error
### BUILD
- action: run build (see Procedures)
- guard: build passes
→ PUBLISH
- guard: build fails
→ REVERT_VERSION
### PUBLISH
- action: publish to REGISTRY (see Procedures)
- guard: publish succeeded
→ DONE
- guard: publish failed
→ REVERT_VERSION
### REVERT_VERSION
- action: revert the version bump commit
- always → STOP with error: "Release failed. Version bump reverted."
### DONE
- terminal state, release complete
git status --porcelain produces output.## Workflow
First check if the worktree is clean. If not, you should probably commit. Then bump the version. If that works, try building. If the build fails, you might want to revert. Then publish.
Problems: no named states, ambiguous transitions ("you should probably"), no explicit error handling, no entry point, "might want to" gives the model permission to skip critical steps.
If multiple skills share the same decision logic (e.g. a release state machine used by both npm-release and pypi-release), extract it to references/release-states.md and have each skill reference it. But for single-skill use, keep it inline.
Step-by-step instructions for each action referenced in the Decisions section. If there's no Decisions section, this is the main body of the skill.
Organise procedures under the state they belong to. Each procedure is a numbered list of imperative steps.
## Procedures
### SEMANTIC_COMMIT
1. Run `git diff --name-only` to list changed files
2. Group changes by area/scope
3. For each group:
a. Stage files: `git add <files>`
b. Determine the appropriate type from COMMIT_TYPES based on the nature of changes
c. Commit: `git commit -m "type(scope): description"`
4. Verify all changes are committed: `git status --porcelain` should be empty
### BUMP_VERSION
1. Collect commits since last git tag: `git log $(git describe --tags --abbrev=0)..HEAD --oneline`
2. Determine the highest-priority bump using the version strategy in Knowledge
3. Run `npm version <patch|minor|major> --no-git-tag-version`
4. Commit the version bump: `git commit -am "chore(release): vX.Y.Z"`
5. Tag: `git tag vX.Y.Z`
## Steps
1. Check worktree
2. Maybe commit
3. Bump version
4. Build
5. If build fails, revert
6. Publish
7. If publish fails, also revert
Problems: mixes decisions and procedures, steps are vague ("maybe commit"), no actual commands, conditional logic buried in a linear list.
Just write numbered steps directly:
## Procedures
1. Read the input CSV from the provided path
2. Validate headers match expected schema (see Knowledge)
3. Transform date columns to ISO 8601
4. Write output to `output/cleaned.csv`
Things the agent must never do, always do, or invariants that must hold throughout execution. These are not steps — they're boundaries.
## Constraints
- Never force-push to MAIN_BRANCH
- Never publish with uncommitted changes in the worktree
- Always run the build before publishing — never publish from source directly
- Version tags must match the format `vX.Y.Z` (no prefix variations like `version-X.Y.Z`)
- If any step fails, never continue to the next state — follow the error transition
## Important notes
- Try to avoid force-pushing if possible
- It's generally a good idea to build before publishing
"Try to" and "generally" give the model wiggle room to ignore constraints. Constraints should be absolute. If something is merely preferred, put it in Knowledge as a convention.
How the agent confirms the skill executed successfully. This is the last thing the agent does — a checklist of observable conditions that prove success.
## Validation
Verify all of the following before reporting success:
- [ ] `git status --porcelain` is empty (no uncommitted changes)
- [ ] `git tag --list` includes the new version tag
- [ ] `npm view <package-name> version` returns the new version
- [ ] Build artefacts exist in `dist/`
- [ ] No errors in the publish output
## Done
You're done! Let the user know everything went well.
No verification, no observable conditions. The agent might report success even if the publish silently failed.
Here is a minimal but complete example of a well-structured skill:
---
name: npm-release
description: >
Handles the full npm package release workflow: committing uncommitted changes
with semantic commits, version bumping, building, and publishing to npm.
Use when the user says "release", "publish to npm", "bump version and publish",
or "do a release". Also use when the user asks to publish a package or push a
new version.
---
# npm-release
## Variables
| Variable | Value | Notes |
| ------------ | -------------------------------------- | -------------------------- |
| MAIN_BRANCH | main | Protected branch |
| REGISTRY | https://registry.npmjs.org | Publish target |
| COMMIT_TYPES | feat, fix, chore, docs, refactor, test | Semantic prefixes |
| BUILD_CMD | npm run build | Project build command |
| REVIEW_AGENT | sentinel | Hands off for verification |
## Prerequisites
- Working directory is a git repository with a `package.json`
- `npm` is available on PATH
- User has publish permissions to REGISTRY
- Current branch is MAIN_BRANCH
## Knowledge
### Commit format
Conventional Commits: `type(scope): description`
- type: one of COMMIT_TYPES
- scope: package or area (optional for monorepos)
- description: imperative, lowercase, no trailing period
### Version mapping
| Commit type | Bump |
| ------------------------------- | ----- |
| fix | patch |
| feat | minor |
| any with BREAKING CHANGE footer | major |
## Decisions
Entry state: CHECK_WORKTREE
### CHECK_WORKTREE
- guard: `git status --porcelain` is empty → BUMP_VERSION
- guard: unstaged or uncommitted changes exist → SEMANTIC_COMMIT
- guard: merge conflicts present → STOP with error
### SEMANTIC_COMMIT
- action: create semantic commit(s) per Procedures
- always → CHECK_WORKTREE
### BUMP_VERSION
- action: determine and apply version bump per Procedures
- guard: succeeded → BUILD
- guard: failed → STOP with error
### BUILD
- action: run BUILD_CMD
- guard: exit code 0 → PUBLISH
- guard: non-zero exit → REVERT_VERSION
### PUBLISH
- action: publish to REGISTRY per Procedures
- guard: succeeded → DONE
- guard: failed → REVERT_VERSION
### REVERT_VERSION
- action: `git reset --hard HEAD~1` and delete tag if created
- always → STOP with error: "Release failed, version bump reverted"
### DONE
- terminal state
## Procedures
### SEMANTIC_COMMIT
1. `git diff --name-only` to list changed files
2. Group by area/scope
3. For each group:
a. `git add <files>`
b. Choose type from COMMIT_TYPES
c. `git commit -m "type(scope): description"`
4. Confirm: `git status --porcelain` is empty
### BUMP_VERSION
1. `git log $(git describe --tags --abbrev=0)..HEAD --oneline`
2. Determine bump level using version mapping
3. `npm version <level> --no-git-tag-version`
4. `git commit -am "chore(release): vX.Y.Z"`
5. `git tag vX.Y.Z`
### PUBLISH
1. `npm publish --registry REGISTRY`
2. `git push origin MAIN_BRANCH --tags`
## Constraints
- Never force-push to MAIN_BRANCH
- Never publish with uncommitted changes
- Never skip BUILD — always build before publish
- If any state transitions to STOP, do not continue
- Tags must match `vX.Y.Z` exactly
## Validation
- [ ] `git status --porcelain` is empty
- [ ] `git tag -l` includes new version
- [ ] `npm view <package> version` matches new version
- [ ] `dist/` directory contains build output
- [ ] `git log -1` shows the version bump commit
If you find yourself writing "if X then do Y, otherwise do Z" inside a numbered list, extract it into the Decisions section as a named state with guards.
Every guard in Decisions should account for failure. If bumping the version can fail, there must be a transition for that. The model will improvise if you leave gaps — and improvisation is exactly what skills are meant to prevent.
Don't put things in Variables that are intrinsic to the skill and would never change (e.g. GIT_COMMAND = git). Variables are for things the user might customise.
Don't explain what git is, what npm does, or how semver works. Focus on your specific conventions and choices. The model's training covers general knowledge; your skill covers what's specific to your team/project.
Procedures should read like a recipe, not an essay. If a step takes more than two lines, it's either multiple steps or it belongs in Knowledge as context.
Without validation, the agent reports "done" based on vibes. Always include observable conditions that prove success.
npx claudepluginhub codethread/agents --plugin writingCreates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.