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
This commit is contained in:
@@ -1,42 +0,0 @@
|
||||
#!/bin/sh
|
||||
# check-index — asserts SKILLS_INDEX.md lists exactly the skills that exist.
|
||||
#
|
||||
# The index is hand-maintained and drifts silently in both directions: a skill
|
||||
# ships with no row (`dream` and `experiment-spec` did — their rows lived on
|
||||
# branches squash-merged after a directory reorg, so the SKILL.md landed and
|
||||
# the row did not, #15), or a row outlives the skill it names. Neither errors
|
||||
# anywhere; the library just misreports itself to every harness that reads it.
|
||||
#
|
||||
# Usage: check-index [checkout-root] (defaults to this script's repo root)
|
||||
|
||||
set -u
|
||||
|
||||
ROOT=${1:-$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)}
|
||||
INDEX="$ROOT/SKILLS_INDEX.md"
|
||||
fail=0
|
||||
|
||||
if [ ! -f "$INDEX" ]; then
|
||||
echo "ERROR: missing SKILLS_INDEX.md" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# A row is a table line whose first cell is the skill name in backticks.
|
||||
listed=$(sed -n 's/^|[[:space:]]*`\([^`]*\)`.*/\1/p' "$INDEX" | sort -u)
|
||||
|
||||
for dir in "$ROOT"/skills/*/; do
|
||||
name=$(basename "$dir")
|
||||
[ -f "$dir/SKILL.md" ] || continue
|
||||
printf '%s\n' "$listed" | grep -qx "$name" || {
|
||||
echo "ERROR: skills/$name has no row in SKILLS_INDEX.md" >&2
|
||||
fail=1
|
||||
}
|
||||
done
|
||||
|
||||
for name in $listed; do
|
||||
[ -f "$ROOT/skills/$name/SKILL.md" ] || {
|
||||
echo "ERROR: SKILLS_INDEX.md lists '$name', which has no skills/$name/SKILL.md" >&2
|
||||
fail=1
|
||||
}
|
||||
done
|
||||
|
||||
exit $fail
|
||||
Executable
+85
@@ -0,0 +1,85 @@
|
||||
#!/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"
|
||||
Reference in New Issue
Block a user