✨ Add spire:adepts roster skill
Shell-injected skill that derives an adept domain roster from workpad frontmatter and journal filenames. Loaded by Igor at boot; any agent may invoke it on demand for fresh discovery.
This commit is contained in:
@@ -0,0 +1,14 @@
|
||||
---
|
||||
name: adepts
|
||||
description: Lists the adept domains in the workpad — what each covers, when it was last active, its status. Loaded by Igor at boot; any agent may invoke it for a fresh listing.
|
||||
allowed-tools: Bash(bash:*)
|
||||
---
|
||||
|
||||
<!-- Note: This skill uses Bash(bash:*) in allowed-tools — a novel pattern.
|
||||
All other Spire skills use Bash(cat:*). The bash permission is needed
|
||||
because the injection runs a script, not a simple cat. Per harness
|
||||
design this should pre-approve the command at load time. -->
|
||||
|
||||
!`bash "${CLAUDE_SKILL_DIR}/roster.sh"`
|
||||
|
||||
<!-- Empty roster: no adepts/ folder yet, or /spire:init has not been run. -->
|
||||
Executable
+47
@@ -0,0 +1,47 @@
|
||||
#!/usr/bin/env bash
|
||||
# Roster generator for spire:adepts — derived metadata from adept domain folders.
|
||||
# Called via skill shell injection: !`bash "${CLAUDE_SKILL_DIR}/roster.sh"`
|
||||
#
|
||||
# Design principle: adepts write knowledge; tooling derives metadata. This
|
||||
# script reads frontmatter declared once and journal filenames that already
|
||||
# exist. No adept ever maintains an index, a counter, or a timestamp by hand.
|
||||
|
||||
# Resolve workpad — same chain as spire:workpad, minus interactive steps.
|
||||
WP="${WORKPAD_FOLDER:-$(just workpad-folder 2>/dev/null)}"
|
||||
if [ -z "$WP" ]; then
|
||||
# Try .claude/spire/project.md for an explicit Workpad: line
|
||||
if [ -f .claude/spire/project.md ]; then
|
||||
WP=$(sed -n 's/^- \*\*Workpad\*\*: *`\{0,1\}\([^` ]*\).*/\1/p' .claude/spire/project.md | head -1)
|
||||
fi
|
||||
fi
|
||||
[ -z "$WP" ] && { [ -d docs/workpad ] && WP=docs/workpad || WP=.workpad; }
|
||||
[ -d "$WP/adepts" ] || { echo "## Adept roster — none (no $WP/adepts/)"; exit 0; }
|
||||
|
||||
echo "## Adept roster — $WP/adepts/ ($(date +%F))"
|
||||
for dir in "$WP"/adepts/*/; do
|
||||
[ -d "$dir" ] || continue
|
||||
slug=$(basename "$dir")
|
||||
[ "$slug" = "_template" ] && continue
|
||||
|
||||
# FC-6: Skip files without frontmatter early — avoid full-file reads through sed.
|
||||
if ! head -1 "$dir/CLAUDE.md" 2>/dev/null | grep -q '^---$'; then
|
||||
newest=$(ls "$dir"/journal/ "$dir" 2>/dev/null | grep -E '^[0-9]{4}-[0-9]{2}-[0-9]{2}-[0-9]{2}' | sort | tail -1 | cut -c1-13)
|
||||
echo "- **$slug** — (unmigrated: no frontmatter) · last active ${newest:-never}"
|
||||
continue
|
||||
fi
|
||||
|
||||
fm=$(sed -n '2,/^---$/p' "$dir/CLAUDE.md" 2>/dev/null)
|
||||
summary=$(echo "$fm" | sed -n 's/^summary: *//p')
|
||||
status=$(echo "$fm" | sed -n 's/^status: *//p')
|
||||
related=$(echo "$fm" | sed -n 's/^related: *//p')
|
||||
|
||||
# Recency and session count derived from journal filenames — nothing to maintain.
|
||||
newest=$(ls "$dir"/journal/ "$dir" 2>/dev/null | grep -E '^[0-9]{4}-[0-9]{2}-[0-9]{2}-[0-9]{2}' | sort | tail -1 | cut -c1-13)
|
||||
sessions=$(ls "$dir"/journal/ "$dir" 2>/dev/null | grep -Eo '^[0-9]{4}-[0-9]{2}-[0-9]{2}-[0-9]{2}' | sort -u | wc -l)
|
||||
|
||||
if [ -z "$summary" ]; then
|
||||
echo "- **$slug** — (unmigrated: no frontmatter) · last active ${newest:-never}"
|
||||
else
|
||||
echo "- **$slug** — $summary · last active ${newest:-never} · $sessions session(s) · ${status:-active}${related:+ · related: $related}"
|
||||
fi
|
||||
done
|
||||
Reference in New Issue
Block a user