Files
skills/bin/generate-index
mathias c6787e04d7
CI / Check / Test (push) Failing after 1s
CI / Tag release (push) Skipped
feat(index): generate the skills table from frontmatter
Second half of #29. The index was hand-maintained and had already drifted
once: dream and experiment-spec shipped with no row, their rows lost to a
squash-merge after a directory reorg. bin/check-index caught that after the
fact; generating removes the class.

The curated summary and trigger move into each SKILL.md as `summary:` and
`use_when:`, carried over verbatim from the existing table rather than derived
from `description:` — description is a paragraph written for model selection,
and truncating it would have produced a worse table than the one it replaced.
Knowledge now lives once, next to the skill it describes, which is the argument
that made a hand-maintained index the wrong shape to begin with.

The table is written between markers, so the wiring docs, the relationships
tree and the trigger table below it are untouched — those are prose, not data.
Rows are alphabetical now; the conceptual grouping the old order carried lives
in the relationships tree, which is the better place for it. A fourth column
shows each skill's surfaces, which is what makes #29's field visible rather
than merely enforced.

bin/check-index and its test are deleted: a generated table cannot drift in
either direction, so `generate-index --check` subsumes both of its assertions.
Verified by mutation — deleting the tdd row turns `task check` red with
"SKILLS_INDEX.md is out of date".

Closes #29
Bump-Type: minor
2026-08-29 11:23:14 +02:00

86 lines
2.7 KiB
Bash
Executable File

#!/bin/sh
# generate-index — rebuild SKILLS_INDEX.md's table from skill frontmatter.
#
# The index was hand-maintained and drifted: `dream` and `experiment-spec`
# shipped with no row at all, because their rows lived on branches that were
# squash-merged after a directory reorg (#15). A gate catches that after the
# fact; generating removes the class (#29).
#
# The curated summary and trigger live in each SKILL.md as `summary:` and
# `use_when:` rather than being derived from `description:` — description is a
# paragraph written for model selection, and truncating it produces a worse
# table than the one being replaced.
#
# Everything outside the markers is left untouched: the wiring docs, the
# relationships tree and the trigger table are prose, not data.
#
# generate-index [root] rewrite the table in place
# generate-index --check [root] exit 1 if the table is out of date
set -u
CHECK=0
if [ "${1:-}" = "--check" ]; then CHECK=1; shift; fi
ROOT=${1:-$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)}
INDEX="$ROOT/SKILLS_INDEX.md"
BEGIN='<!-- BEGIN skills-table -->'
END='<!-- END skills-table -->'
[ -f "$INDEX" ] || { echo "ERROR: missing $INDEX" >&2; exit 1; }
grep -qF "$BEGIN" "$INDEX" || { echo "ERROR: $INDEX has no $BEGIN marker" >&2; exit 1; }
grep -qF "$END" "$INDEX" || { echo "ERROR: $INDEX has no $END marker" >&2; exit 1; }
# field <file> <key> — a scalar frontmatter value, quotes stripped.
field() {
sed -n "s/^$2:[[:space:]]*//p" "$1" | head -1 | sed 's/^"//; s/"$//'
}
table=$(mktemp)
fail=0
{
printf '| Skill | Description | Use when | Surfaces |\n'
printf '|-------|-------------|----------|----------|\n'
} > "$table"
for dir in "$ROOT"/skills/*/; do
[ -f "$dir/SKILL.md" ] || continue
name=$(basename "$dir")
summary=$(field "$dir/SKILL.md" summary)
use_when=$(field "$dir/SKILL.md" use_when)
surfaces=$(field "$dir/SKILL.md" surfaces | tr -d '[]')
if [ -z "$summary" ] || [ -z "$use_when" ]; then
echo "ERROR: skills/$name has no 'summary:'/'use_when:' — the index table is generated from them" >&2
fail=1
continue
fi
printf '| `%s` | %s | %s | %s |\n' "$name" "$summary" "$use_when" "$surfaces" >> "$table"
done
if [ "$fail" -ne 0 ]; then
rm -f "$table"
exit 1
fi
out=$(mktemp)
awk -v begin="$BEGIN" -v end="$END" -v tablefile="$table" '
$0 == begin { print; while ((getline line < tablefile) > 0) print line; skip = 1; next }
$0 == end { print; skip = 0; next }
!skip { print }
' "$INDEX" > "$out"
rm -f "$table"
if [ "$CHECK" -eq 1 ]; then
if cmp -s "$out" "$INDEX"; then
rm -f "$out"
exit 0
fi
rm -f "$out"
echo "ERROR: SKILLS_INDEX.md is out of date — run 'task index' and commit the result" >&2
exit 1
fi
mv "$out" "$INDEX"