Authoring skills

July 17, 2026 · View on GitHub

How to create or edit a skill in this repository. Use the author-skill skill for the general authoring workflow (draft → test → review → improve); this document is the Spine-specific layer of conventions that workflow does not itself enforce, so follow both.

Heads-up: author-skill is derived from Anthropic's skill-creator (Apache-2.0; license text in skills/author-skill/LICENSE.txt) and is being adapted and extended for this repository's needs, so expect it to diverge from the upstream over time. It doesn't yet match every convention here — notably, its SKILL.md keeps the upstream guide's structure rather than the Workflow / Repo Notes / Report shape — so when authoring a new skill, treat this document (not author-skill's current form) as the source of truth.

Anatomy of a skill

skills/<name>/
├── SKILL.md              # required — the skill definition
├── agents/
│   └── openai.yaml       # required — Codex/OpenAI interface metadata
├── references/           # optional — long reference docs, loaded on demand
├── scripts/              # optional — deterministic helpers the skill runs
└── assets/               # optional — templates and other static files

Naming

  • Lowercase, hyphen-separated (raise-coverage, not raiseCoverage).
  • The directory name MUST equal the name: in the SKILL.md frontmatter.
  • Avoid names reserved by other agents (e.g. Codex's create-skill, Anthropic's skill-creator). When unsure, choose a distinct verb or a spine- prefix.

SKILL.md

Frontmatter followed by the body:

---
name: <kebab-case-name>          # == directory name
description: >                   # one folded paragraph, < 1024 chars, no angle brackets
  What the skill does AND when to use it — this is the text an agent matches
  a request against, so make the trigger conditions explicit.
---

# <Title>

## Workflow         # numbered, deterministic steps
## Repo Notes       # repo-specific pointers, e.g. `.agents/guidelines/<file>.md`
## Report           # what the skill returns to the caller

Rules:

  • Write the description: as shown — a > folded scalar, even when the text would fit on one line — and wrap its lines near 80 columns (the hard limit is 100, per .agents/guidelines/coding.md).
  • Keep SKILL.md under ~500 lines; move long material into references/ and link to it.
  • Reference shared guidance with repo-rooted paths (.agents/guidelines/<file>.md). These resolve here (via the in-repo dogfood symlinks) and in every consumer repo.
  • Write agent-neutral instructions that work for Claude, Codex, and Junie — don't hard-code a single runtime's slash-command syntax in the body.
  • Never reference a task plan. No part of a skill — SKILL.md, references/, scripts/, assets/, or agents/openai.yaml — may link to or cite a path under .agents/tasks/ or tasks/. Task plans are volatile: they are deleted during or soon after the PR they track (see the lifecycle in .agents/tasks/README.md), so the reference rots. Point at a durable target instead — a .agents/guidelines/ page, the relevant source, or its KDoc — or inline the stable fact.

agents/openai.yaml

interface:
  display_name: "<Human Readable Name>"
  short_description: "<one line>"
  default_prompt: "Use $<name> to …"     # refer to the skill as $<name>

Keep default_prompt short and aligned with the SKILL.md description.

Claude wrappers and model tiers

Claude Code consumes a skill through thin wrappers: claude/commands/ (slash commands) and claude/agents/ (subagents). Model selection is a Claude-specific concern, so it lives only in wrapper frontmatter — never in SKILL.md, which other runtimes also parse.

  • Declare models by alias (haiku, sonnet, opus; agents may also inherit) — never by dated model ID. Consumers float to master, so a dated ID pins every repository to an aging snapshot that eventually retires; an alias upgrades for free.
  • Pick the cheapest tier that runs the skill reliably:
    • haiku — formulaic work: run a script, a deterministic pipeline, or a tightly specified procedure and relay the result (update-copyright, run-build, api-discovery, check-links, bump-version, version-bumped), or a checklist audit already optimized for batched tool calls (dependency-audit).
    • sonnet — procedural edits and checklist reviews that involve some judgment (bump-gradle, dependency-update, move-files, proofread, and the spine-code-review, review-docs, and gradle-review agents).
    • omit the field (commands) or inherit (agents) — deep-reasoning work where the user's session model should govern: code translation, test and prose authoring, and the kotlin-engineer review.
  • Leave pre-pr without a model: a command's model applies to the turn that runs it, and a subagent declaring inherit inherits from that turn — pinning the orchestrator would silently downgrade the reviewers it dispatches.
  • Tier paired wrappers together: version-bumped runs bump-version as its recovery path, so the guard must never sit on a lower tier than the action it embeds.
  • Some skills run inline at the session model by design: author-skill and co-author-docs are interactive workflows that converse with the user, while kotlin-jvm-tester and kotlin-engineer (when used directly as a skill for writing Kotlin, as opposed to its review-agent wrapper) are convention packs whose guidance must land in the calling context while it writes code. A wrapper cannot re-tier them without severing that interaction, so their cost deliberately follows the session.

Claude wrapper frontmatter

Wrappers under claude/commands/ and claude/agents/ are Markdown, so the 100-character line limit from .agents/guidelines/coding.md applies to them (see .agents/guidelines/documentation.md). Two conventions follow:

  • Always write description: as a > block scalar — even when the text would fit on one line — wrapping its lines near 80 columns, the same idiom skills/*/SKILL.md files use. YAML folds the lines back into a single-line string, so wrapping never changes the wording that drives command or agent dispatch, and the uniform shape keeps a later edit from drifting past the 100-character limit.
  • allowed-tools: may stay on one line or be folded with >- — the field is machine-consumed as permission rules (likewise an agent's tools: field), and a >- folded scalar parses to the byte-identical single-line string, so both forms reach Claude Code the same way. This equivalence is verified — with a strict YAML parser over every wrapper, and by live command registration of folded wrappers — so fold the field when a line grows unwieldy, or leave a long line in place: it is exempt from the line-length limit.

Put a skill's own helpers in skills/<name>/scripts/; promote a helper to the top-level scripts/ only when more than one skill (or an agent hook) uses it. Make scripts executable; write them as POSIX bash or Python. Source files that carry code get the standard Apache/TeamDev copyright header. Python helpers should rely only on the standard library so they run without extra installs.

Validate before opening a PR

  • Directory name == frontmatter name.
  • description < 1024 characters; SKILL.md < ~500 lines.
  • agents/openai.yaml present, with a $<name> default_prompt.
  • If a claude/ wrapper sets model:, the value is an alias — never a dated model ID: haiku/sonnet/opus for commands and agents, plus inherit for agents only (a command inherits by omitting the field).
  • Skill files and Claude wrappers stay within the 100-character line limit; only allowed-tools:/tools: lines are exempt, and every description: is a > block scalar wrapped near 80 columns (see "Claude wrapper frontmatter").
  • Every .agents/... reference resolves (check through the in-repo symlinks).
  • No skill file references a task plan: grep -rnE '(^|[^[:alnum:]])(\.agents/)?tasks/' skills/<name>/ returns nothing that links to or cites .agents/tasks/ or tasks/ (the boundary guard avoids matching unrelated words like subtasks/).
  • Any shipped script parses — shell scripts with bash -n, Python helpers with python -m py_compile — and, where practical, has a test.

Remember: this is production

master floats to every Spine repository, so a merged change is live everywhere on the next pull. Land changes through a reviewed pull request — never commit or push directly unless explicitly asked.