| name | skill-authoring |
| version | 2.4.0 |
| description | Guides authoring and maintaining agent skills in this repository — enforcing Anthropic best practices, bumping versions, regenerating Cursor rules, and keeping the plugin structure consistent. Use when editing SKILL.md files, reference files, or plugin manifests; creating a new plugin; or reviewing a skill for compliance with authoring standards. Do NOT trigger for general coding tasks, security scanning, or platform API queries.
|
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>
version: <semver>
description: >
...
---
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/*.md files 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. Prefer
curl "$HOST<spec-path>" over curl "$HOST/authors" — a literal paste of the placeholder
form breaks visibly, while a literal paste of /authors hits the wrong route silently. Put
the concrete value in an adjacent comment as a clearly-labeled e.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/ or openapi.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:
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/
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.md
-
Verify reference linking — no reference file links to another reference file:
grep -rn "\](.*\.md)" plugins/<name>/skills/<name>/references/
-
Regenerate 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
bash scripts/bump-version.sh --minor
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
bash scripts/generate-cursor-rules.sh
git add plugins/ cursor/ scripts/ && git commit --amend --no-edit