Skip to content
Merged
35 changes: 35 additions & 0 deletions claude/agents/check-links.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
---
name: check-links
description: >
Validates the Hugo documentation site under `docs/` or `site/` for broken
links — builds the site, serves it locally, runs Lychee with the repo's
`lychee.toml`, and reports broken URLs grouped by source Markdown page. Use
when a diff touches `docs/**`, `site/**`, or `lychee.toml`, when CI's
`Check Links` job fails, or when the user asks to check doc links. Reports
"not applicable" when no Hugo site exists. Read-only with respect to
project sources; does not run Gradle builds.
tools: Read, Grep, Glob, Bash
model: haiku
---

Follow the `check-links` skill exactly:

- Skill: `.agents/skills/check-links/SKILL.md`
- The skill owns the procedure (site detection, binary preflight against the
CI-pinned versions, `npm ci`, Hugo build and serve on port `1414`, the
Lychee run, teardown) and the report format (broken URLs grouped by
source Markdown page).
- If no Hugo config exists under `docs/` or `site/`, return
`APPROVE — no Hugo documentation site found under docs/ or site/.` and stop.
- When the pipeline runs, write the `check-links.ok` sentinel to the
repository's git directory as the skill's final step — the `pre-pr` gate
reads it to skip a redundant re-run at the same HEAD. The not-applicable
early exit above writes no sentinel.
- Read-only with respect to tracked sources: use `Bash` for the
build/serve/check pipeline, git-ignored caches, and the sentinel only;
never modify project files.
- End with the reviewers' one-line verdict so `pre-pr` can aggregate this
agent like the other reviewers: `APPROVE` when no broken links are found
(or the check is not applicable), `REQUEST CHANGES` when Lychee reports
broken URLs or the pipeline itself fails (preflight, build, or server
errors). Place the skill's report above the verdict line.
2 changes: 1 addition & 1 deletion claude/agents/dependency-audit.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ description: >
directory, or when the user asks "audit this dependency bump". Read-only;
does not run builds.
tools: Read, Grep, Glob, Bash
model: claude-haiku-4-5-20251001
model: haiku
---

Follow the `dependency-audit` skill exactly:
Expand Down
30 changes: 30 additions & 0 deletions claude/agents/gradle-review.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
---
name: gradle-review
description: >
Reviews Gradle-related changes against Spine SDK conventions and the
upstream Gradle best-practices guides ingested under the skill's
`practices/`. Scope: `buildSrc/` in the `config` repository, Gradle build
files in any project, and production code of Gradle plugins exposed by
Spine SDK tools. Use proactively after any non-trivial change to build
logic, before opening a PR, or when the user asks for a Gradle review.
Read-only; does not run builds.
tools: Read, Grep, Glob, Bash
model: sonnet
---

Follow the `gradle-review` skill exactly:

- Skill: `.agents/skills/gradle-review/SKILL.md`
- The skill owns the scope rules (config-repo `buildSrc/`, Gradle build
files, plugin production code), the Spine task conventions, the ingested
`practices/` checks, and the output format (Must fix / Should fix /
Nits + one-line verdict).
- Do not duplicate `spine-code-review` (repo-specific safety rules, testing
policy, version gate), `kotlin-engineer` (general Kotlin language
standards), or `dependency-audit` (declarations under
`buildSrc/src/main/kotlin/io/spine/dependency/`) — they review in
parallel. If such an issue surfaces, note it briefly as a Nit pointing
at the owning agent.
- Read-only: use `Read`, `Grep`, `Glob`, and `Bash` solely for `git diff`,
`git remote -v`, the skill's search recipes, and related read-only
inspection. Do not run builds.
2 changes: 1 addition & 1 deletion claude/agents/review-docs.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ description: >
comments or Markdown, before opening a doc-affecting PR, or when the user
asks for a documentation review. Read-only; does not run builds.
tools: Read, Grep, Glob, Bash
model: inherit
model: sonnet
---

Follow the `review-docs` skill exactly:
Expand Down
2 changes: 1 addition & 1 deletion claude/agents/spine-code-review.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ description: >
after any non-trivial code edit, before opening a PR, or when the user
asks for a code review. Read-only; does not run builds.
tools: Read, Grep, Glob, Bash
model: inherit
model: sonnet
---

Follow the `spine-code-review` skill exactly:
Expand Down
25 changes: 25 additions & 0 deletions claude/commands/api-discovery.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
---
description: >
Resolve a Maven artifact's on-disk sources instead of unzipping
Gradle-cache JARs.
argument-hint: "<group:artifact[:version] | artifact>"
allowed-tools: >-
Read, Grep, Glob, Write, Bash(.agents/scripts/api-discovery/discover:*),
Bash(.agents/scripts/api-discovery/update-sibling:*), Bash(mkdir:*)
model: haiku
---

Follow the `api-discovery` skill exactly:

- Skill: `.agents/skills/api-discovery/SKILL.md`
- Query: $ARGUMENTS
- Run `.agents/scripts/api-discovery/discover $ARGUMENTS`; stdout is the
resolved path, stderr carries warnings the user must see.
- Exit 10 → run the skill's bootstrap flow (ask the user before creating
the cache directory). Exit 1 → report the failure verbatim; never fall
back to `unzip` against Gradle caches.
- On a `STALE:` warning, offer `update-sibling` per the skill and read its
stdout token (`pulled` | `up-to-date` | `skipped-branch`) to decide the
next step.
- Report the resolved path and any warnings so follow-up work can read the
sources directly.
1 change: 1 addition & 0 deletions claude/commands/bump-gradle.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ description: >
Upgrade the Gradle wrapper to the latest release.
argument-hint: "[gradle-version]"
allowed-tools: Read, Edit, Bash(./gradlew:*), Bash(git status:*), Bash(git diff:*), WebFetch
model: sonnet
---

Follow the `bump-gradle` skill exactly:
Expand Down
5 changes: 4 additions & 1 deletion claude/commands/bump-version.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ description: >
Bump the project version in version.gradle.kts per Spine SDK versioning
policy.
argument-hint: "[snapshot|minor|major]"
allowed-tools: Read, Edit, Bash(git status:*), Bash(git diff:*), Bash(git log:*), Bash(./gradlew:*)
allowed-tools: >-
Read, Edit, Bash(git status:*), Bash(git diff:*), Bash(git log:*),
Bash(git fetch:*), Bash(git merge-base:*), Bash(./gradlew:*)
model: haiku
---

Follow the `bump-version` skill exactly:
Expand Down
23 changes: 23 additions & 0 deletions claude/commands/check-links.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
---
description: >
Check the Hugo documentation site for broken links, mirroring CI's
Check Links job.
argument-hint: "[base-ref]"
allowed-tools: Read, Grep, Glob, Bash
model: haiku
---

Follow the `check-links` skill exactly:

- Skill: `.agents/skills/check-links/SKILL.md`
- Base ref for the scope check: $ARGUMENTS (treat empty as `master`).
- The skill owns the procedure (site detection, binary preflight against
the CI-pinned versions, `npm ci`, Hugo build and serve on port `1414`,
the Lychee run, teardown) and the report format (broken URLs grouped by
source Markdown page).
- If no Hugo config exists under `docs/` or `site/`, return
`APPROVE — no Hugo documentation site found under docs/ or site/.` and
stop — do not write a `FAIL` sentinel for that case.
- When the pipeline runs, always tear the Hugo server down (step 8), even
when Lychee fails, and write the `check-links.ok` sentinel to the
repository's git directory.
5 changes: 4 additions & 1 deletion claude/commands/dependency-update.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ description: >
Refresh external dependency versions in buildSrc to their latest
non-snapshot release.
argument-hint: "[--dry-run] [paths...]"
allowed-tools: Read, Edit, Write, Grep, Glob, WebFetch, Bash(git status:*), Bash(git diff:*), Bash(./gradlew build:*), Bash(./gradlew clean build:*)
allowed-tools: >-
Read, Edit, Write, Grep, Glob, WebFetch, Bash(git status:*),
Bash(git diff:*), Bash(./gradlew build:*), Bash(./gradlew clean build:*)
model: sonnet
---

Follow the `dependency-update` skill exactly:
Expand Down
1 change: 1 addition & 0 deletions claude/commands/move-files.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ description: >
metadata.
argument-hint: "<source> <destination>"
allowed-tools: Read, Edit, Bash(git mv:*), Bash(git status:*), Bash(git ls-files:*), Grep, Glob
model: sonnet
---

Follow the `move-files` skill exactly:
Expand Down
5 changes: 5 additions & 0 deletions claude/commands/pre-pr.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ Follow the `pre-pr` skill exactly:
- `review-docs` when `.md` files or KDoc inside sources changed.
- `dependency-audit` when any file under
`buildSrc/src/main/kotlin/io/spine/dependency/` changed.
- `check-links` when a Hugo site exists (a Hugo config under `docs/` or
`site/`) and the diff touches files under the site directory or
`lychee.toml`. Honor the `check-links.ok` sentinel short-circuit from
the skill: skip the dispatch when the sentinel matches the current
HEAD with `status=PASS`.
Comment thread
alexander-yevsyukov marked this conversation as resolved.
- Pass the version-check status to reviewers. If it is `N/A`, tell them:
"This repository has no root `version.gradle.kts`; a version bump is not
applicable and must not be reported as missing."
Expand Down
5 changes: 4 additions & 1 deletion claude/commands/review-docs.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ description: >
Review documentation changes (KDoc/Javadoc and Markdown) against Spine
documentation conventions.
argument-hint: "[base-ref | --staged | paths...]"
allowed-tools: Read, Grep, Glob, Bash(git diff:*), Bash(git log:*), Bash(git status:*), Bash(git rev-parse:*), Bash(git ls-files:*)
allowed-tools: >-
Read, Grep, Glob, Bash(git diff:*), Bash(git log:*), Bash(git status:*),
Bash(git rev-parse:*), Bash(git ls-files:*)
model: sonnet
---

Follow the `review-docs` skill exactly:
Expand Down
1 change: 1 addition & 0 deletions claude/commands/run-build.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ description: >
Build the project the right way based on what changed
(proto vs. Kotlin/Java vs. docs).
allowed-tools: Bash(./gradlew:*), Bash(git status:*), Bash(git diff:*)
model: haiku
---

Decide which build to run by looking at `git status --short` and `git diff --name-only`:
Expand Down
1 change: 1 addition & 0 deletions claude/commands/update-copyright.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ description: >
with the current year.
argument-hint: "[paths...]"
allowed-tools: Bash(python3 .agents/skills/update-copyright/scripts/update_copyright.py:*), Read
model: haiku
---

Follow the `update-copyright` skill exactly:
Expand Down
24 changes: 24 additions & 0 deletions claude/commands/version-bumped.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
---
description: >
Verify the branch bumped version.gradle.kts above base; recover via
bump-version.
argument-hint: "[base-ref]"
allowed-tools: >-
Read, Edit, Bash(.agents/skills/version-bumped/scripts/version-bumped.sh:*),
Bash(git status:*), Bash(git diff:*), Bash(git log:*), Bash(./gradlew:*)
model: haiku
---

Follow the `version-bumped` skill exactly:

- Skill: `.agents/skills/version-bumped/SKILL.md`
- Run the deterministic check
`.agents/skills/version-bumped/scripts/version-bumped.sh`. If $ARGUMENTS
names a base ref, run the script with `VERSION_BUMPED_BASE=<ref>`.
- Exit 0 → report the one-line confirmation and stop.
- Exit 2 → configuration error; surface the script's stderr and stop.
Do not bump.
- Exit 1 → run the `bump-version` skill to recover (it owns the policy),
then re-run the check once to confirm. Do not loop.
- Recovery may commit the bump — that is the `bump-version` skill's
documented policy; the repo's permission settings still gate `git commit`.
25 changes: 25 additions & 0 deletions claude/commands/which-fixer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
---
description: >
Fix restrictive "which" clauses to "that" in comments and docs.
allowed-tools: >-
Read, Edit, Write, Grep, Glob, Bash(git diff:*), Bash(git ls-files:*),
Bash(git status:*), Bash(cmp:*)
model: sonnet
---

Follow the `which-fixer` skill exactly:

- Skill: `.agents/skills/which-fixer/SKILL.md`
- Mode is automatic: bulk when `.agents/memory/which-fixer-applied.md` is
absent, incremental otherwise. Do not ask which mode to use.
- Scope: project-owned files only, per the skill's submodule and
config-distributed skip rules.
- Honor every guard in the skill (comma, parenthesis/dash, preposition,
interrogative, sentence-initial, hyphenated identifier, fixed phrases).
When uncertain, leave the occurrence unchanged and record it in
`Skipped[]` — a missed case beats a wrong fix.
- Never touch code: string literals, identifiers, fenced or indented code
blocks, inline code spans, or snippets embedded in doc comments.
- Report per the skill: Mode (bulk | incremental), FilesScanned,
FilesChanged, Replacements[] (file, line, before → after), Skipped[]
(file, line, reason).
55 changes: 49 additions & 6 deletions docs/authoring-skills.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,44 @@ Rules:

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`,
`which-fixer`, 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
Expand All @@ -88,12 +126,14 @@ Wrappers under `claude/commands/` and `claude/agents/` are Markdown, so the
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.
- **Keep `allowed-tools:` on one line, however long** — the field is exempt
from the line-length limit (likewise an agent's `tools:` field). Its value
is machine-consumed as permission rules, and the Claude Code documentation
defines no format for it beyond single-line examples, so a folded scalar is
not confirmed to parse identically. `master` floats to every consumer
repository; do not re-wrap the field on an unverified assumption.
- **`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.

## Scripts & copyright

Expand All @@ -108,6 +148,9 @@ rely only on the standard library so they run without extra installs.
- 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
Expand Down