#!/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