Skill Authoring Guide
This skill documents the rules for creating and maintaining agent skills in
the stackhawk/agent-skills repository.
Authoring Rules (Quick Reference)
These are the non-negotiable rules. CI validates them on every PR.
| Rule | Requirement |
|---|---|
| SKILL.md length | Body ≤ 500 lines (excluding frontmatter) |
| Frontmatter | name:, version:, description: all required |
| Description | Third person; max 1024 characters |
| Reference files | All linked from SKILL.md; must not link to each other |
| Reference ToC | Required when file > 100 lines |
| Progressive disclosure | Summary + link in SKILL.md; detail in references/ |
SKILL.md Rules
Frontmatter
Every SKILL.md must open with a YAML frontmatter block:
---
name: <skill-name> # matches the plugin directory name and symlink folder name
version: <semver> # kept in sync by bump-version.sh
description: > # third person; max 1024 characters; describes when to trigger
...
---
Description writing rules:
- Third person ("Guides authoring...", not "I help you author...")
- Opens with what the skill does (verb phrase)
- Includes "Use when" trigger conditions
- Includes "Do NOT trigger for" exclusions
- Max 1024 characters — CI rejects longer descriptions
Body length
The SKILL.md body (everything after frontmatter) must stay at or under 500 lines. Use the progressive disclosure pattern to stay within this limit:
- Keep decision logic, commands, and critical rules in SKILL.md
- Move deep detail to
references/*.mdfiles and link them from SKILL.md - Link format:
→ [references/filename.md](references/filename.md)
What belongs in SKILL.md
- Trigger/skip conditions (when to run, when to stop)
- Step-by-step orchestration logic the agent follows
- Decision tables and critical rules
- Commands the agent runs directly
- Links to reference files (one line per link)
What belongs in references/
- Full command references with all flags
- Pattern tables too large for SKILL.md
- Deep technical detail referenced only in specific situations
- Content > ~30 lines that supports one topic
Reference File Rules
Linking (critical — enforced by best practices)
- All reference files must be linked from SKILL.md directly
- Reference files must NOT link to other reference files
- One level deep only: SKILL.md → reference files; references do not link to references
- When a reference file needs to point the reader elsewhere, write "See Step X in SKILL.md" rather than linking to another reference file
Table of Contents
Reference files longer than 100 lines must include a ## Contents ToC with anchor links
immediately before the first ## section. Format:
## Contents
- [Section Name](#section-name)
- [Another Section](#another-section)
---
## Section Name
Shorter files may omit the ToC.
Examples vs. placeholders (avoid literal-match traps)
Agents copy runnable code blocks verbatim far more readily than they adapt them. A concrete app-specific value baked into a command is therefore a trap: the agent runs it literally against a different codebase and silently does the wrong thing (curls a route that doesn't exist, greps a directory that isn't there).
The rule that prevents this:
- Runnable code blocks use
<placeholder>s that fail loudly if pasted as-is. Prefercurl "$HOST<spec-path>"overcurl "$HOST/authors"— a literal paste of the placeholder form breaks visibly, while a literal paste of/authorshits the wrong route silently. Put the concrete value in an adjacent comment as a clearly-labelede.g. - Concrete examples live in prose or comments, marked
e.g.There they aid comprehension without being executable, so literal-matching them is harmless. - Literal values are fine only when they are genuinely universal — e.g. probing the real,
fixed served-spec paths (
/openapi.json,/v3/api-docs) is correct precisely because those are the same across every app of that framework. If a value changes per codebase, it is a placeholder, not a literal. - Command tables (grep/derivation recipes) are illustrative starting points. Say so once
near the table, and point them at the repo's real source root and filenames rather than a
literal
src/oropenapi.yaml.
The test: "if an agent runs this exact text against a completely different repo, does it fail loudly or mislead silently?" Silent-mislead means parameterize it.
Creating a New Plugin
Follow these steps in order. All are required.
1. Create directory structure:
plugins/<name>/
skills/<name>/
SKILL.md
references/ # create if needed
.claude-plugin/
plugin.json
.codex-plugin/
plugin.json
2. Write SKILL.md following the rules above. Use current VERSION file for the
version field (cat VERSION).
3. Create manifests — copy an existing plugin's plugin.json as a template:
# .claude-plugin/plugin.json and .codex-plugin/plugin.json
# Set name, version (match VERSION file), description, keywords
4. Create symlinks for platform discovery (use relative paths):
cd skills/ && ln -s ../plugins/<name>/skills/<name> <name>
cd .opencode/skills/ && ln -s ../../plugins/<name>/skills/<name> <name>
cd .cursor/skills/ && ln -s ../../plugins/<name>/skills/<name> <name>
5. Add Cursor mapping in scripts/generate-cursor-rules.sh MAPPINGS array:
"<name>/skills/<name>/SKILL.md|stackhawk-<name>|<cursor description>|<globs>|<alwaysApply>"
6. Add to .version-bump.json — add two entries (one per manifest):
{ "path": "plugins/<name>/.claude-plugin/plugin.json", "field": "version", "type": "json" },
{ "path": "plugins/<name>/.codex-plugin/plugin.json", "field": "version", "type": "json" },
{ "path": "plugins/<name>/skills/<name>/SKILL.md", "field": "version", "type": "yaml-frontmatter" }
7. Bump version (new skill = --minor):
bash scripts/bump-version.sh --minor
8. Regenerate Cursor rules:
bash scripts/generate-cursor-rules.sh
9. Verify idempotency:
bash scripts/generate-cursor-rules.sh && git diff cursor/
# → no diff means rules are in sync
Updating an Existing Skill
After any edit to SKILL.md or references/*.md:
Verify line count (SKILL.md body ≤ 500 lines):
wc -l plugins/<name>/skills/<name>/SKILL.mdVerify reference linking — no reference file links to another reference file:
grep -rn "\](.*\.md)" plugins/<name>/skills/<name>/references/ # Any hit means a reference file is linking to another file — remove itRegenerate Cursor rules:
bash scripts/generate-cursor-rules.sh && git diff cursor/Bump version — include in the same commit as the content change:
bash scripts/bump-version.sh --patch # for fixes and content updates bash scripts/bump-version.sh --minor # for new capabilities
Version Bump Rules
| Change type | Flag |
|---|---|
| Bug fix, content correction, typo | --patch |
| New skill, new reference file, new capability | --minor |
| Breaking change (removed step, changed trigger conditions) | --major |
VERSION is the single source of truth. bump-version.sh reads .version-bump.json
and updates all manifests and SKILL.md frontmatter atomically. Never edit version numbers
manually in individual files.
CI Validation
generate-and-validate.yml runs on every PR and checks:
- Version consistency across all manifests
- Cursor rules are up to date (no diff after running
generate-cursor-rules.sh) - SKILL.md frontmatter has
name:,version:,description:
The PR will be blocked if any check fails. Fix by running:
bash scripts/bump-version.sh --patch # or --minor/--major
bash scripts/generate-cursor-rules.sh
git add plugins/ cursor/ scripts/ && git commit --amend --no-edit