The repo distributes three ways — the Claude Code plugin, the symlinked library for Crush/Pi/OpenCode, and claude.ai, which has no fetch path at all — and treated every skill as belonging to all of them. claude.ai is not a fourth host to mirror: gitea-ci, playwright and web-shot have no shell and no cluster there, so a flat mirror installs skills that can never fire and degrades selection for the ones that can. Every SKILL.md now carries `surfaces:`, defaulting to [claude-code, library]; dream, own-the-task and grill-me add `chat`, matching what is actually installed on claude.ai today. Claude Code accepts the extra key — `claude plugin validate` passes and the skills still load. bin/check-surfaces validates values, not just presence, because the failure mode here is silent: a wrong surface removes a skill from a harness rather than erroring. install.sh links only skills whose surfaces include `library`, and a source that declares nothing at all is treated as foreign and linked as-is — Anthropic's official set has no reason to know this convention. doctor's missing-link check skips non-library skills for the same reason. Refs #29 Bump-Type: minor
50 lines
1.6 KiB
Bash
Executable File
50 lines
1.6 KiB
Bash
Executable File
#!/bin/sh
|
|
# check-surfaces — every skill declares which surfaces it ships to, and only
|
|
# names surfaces that exist.
|
|
#
|
|
# The repo distributes three ways: the Claude Code plugin, the symlinked
|
|
# library (Crush/Pi/OpenCode), and claude.ai, which has no fetch path at all.
|
|
# Treating every skill as belonging to all three installs skills that can never
|
|
# fire — gitea-ci, playwright and web-shot have no shell or cluster in a chat
|
|
# surface — and degrades selection for the ones that can (#29).
|
|
#
|
|
# Values are validated, not merely counted: a wrong surface silently removes a
|
|
# skill from a harness rather than erroring, which is the failure mode this
|
|
# whole file exists to prevent.
|
|
#
|
|
# Usage: check-surfaces [checkout-root]
|
|
|
|
set -u
|
|
|
|
ROOT=${1:-$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)}
|
|
ALLOWED='claude-code library chat'
|
|
fail=0
|
|
|
|
for dir in "$ROOT"/skills/*/; do
|
|
[ -f "$dir/SKILL.md" ] || continue
|
|
name=$(basename "$dir")
|
|
|
|
line=$(sed -n 's/^surfaces:[[:space:]]*//p' "$dir/SKILL.md" | head -1)
|
|
if [ -z "$line" ]; then
|
|
echo "ERROR: skills/$name declares no 'surfaces:' — say where it ships (${ALLOWED% *}, ${ALLOWED##* })" >&2
|
|
fail=1
|
|
continue
|
|
fi
|
|
|
|
values=$(printf '%s' "$line" | tr -d '[]' | tr ',' ' ')
|
|
count=0
|
|
for v in $values; do
|
|
count=$((count + 1))
|
|
case " $ALLOWED " in
|
|
*" $v "*) ;;
|
|
*) echo "ERROR: skills/$name names surface '$v', which does not exist — allowed: $ALLOWED" >&2; fail=1 ;;
|
|
esac
|
|
done
|
|
[ "$count" -gt 0 ] || {
|
|
echo "ERROR: skills/$name has an empty 'surfaces:' list — a skill that ships nowhere should be deleted, not declared" >&2
|
|
fail=1
|
|
}
|
|
done
|
|
|
|
exit $fail
|