feat(skills): declare which surfaces each skill ships to
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
This commit is contained in:
+6
-1
@@ -99,7 +99,12 @@ tasks:
|
||||
# tests/check_index_test.sh rather than living untested here.
|
||||
sh '{{.TASKFILE_DIR}}/bin/check-index' '{{.TASKFILE_DIR}}' || fail=1
|
||||
|
||||
# 4. Plugin manifest version must match the latest tag, or installers
|
||||
# 4. Every skill says which surfaces it ships to, and names only
|
||||
# surfaces that exist — a wrong value silently drops a skill from a
|
||||
# harness instead of erroring.
|
||||
sh '{{.TASKFILE_DIR}}/bin/check-surfaces' '{{.TASKFILE_DIR}}' || fail=1
|
||||
|
||||
# 5. Plugin manifest version must match the latest tag, or installers
|
||||
# never see the update (Claude Code gates updates on this field).
|
||||
if [ -f '{{.PLUGIN_MANIFEST}}' ]; then
|
||||
manifest_v=$(sed -n 's/.*"version"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' '{{.PLUGIN_MANIFEST}}' | head -1)
|
||||
|
||||
Executable
+49
@@ -0,0 +1,49 @@
|
||||
#!/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
|
||||
@@ -153,6 +153,13 @@ elif [ -d "$AGENTS_DIR" ]; then
|
||||
for dir in "$SKILLS_DIR"/*/; do
|
||||
[ -f "$dir/SKILL.md" ] || continue
|
||||
name=$(basename "$dir")
|
||||
# Only skills that ship to the library belong here. A chat-only skill
|
||||
# missing from a Crush directory is correct, not drift (#29).
|
||||
surfaces=$(sed -n 's/^surfaces:[[:space:]]*//p' "$dir/SKILL.md" | head -1)
|
||||
if [ -n "$surfaces" ] \
|
||||
&& ! printf '%s' "$surfaces" | tr -d '[]' | tr ',' ' ' | grep -qw library; then
|
||||
continue
|
||||
fi
|
||||
[ -L "$AGENTS_DIR/$name" ] \
|
||||
|| add FAIL missing "$name is in the checkout but has no link in $AGENTS_DIR — re-run install.sh"
|
||||
done
|
||||
|
||||
+11
@@ -131,6 +131,17 @@ wire_skills() {
|
||||
name="$(basename "$dir")"
|
||||
target="${dir%/}"
|
||||
|
||||
# A skill declares its surfaces; this installer serves exactly one of
|
||||
# them. A chat-only skill linked into Crush can never fire and competes
|
||||
# for selection with ones that can (#29). A source with no declaration
|
||||
# is foreign — Anthropic's official set has no reason to know this
|
||||
# convention — so silence means "link it".
|
||||
surfaces="$(sed -n 's/^surfaces:[[:space:]]*//p' "$dir/SKILL.md" | head -1)"
|
||||
if [ -n "$surfaces" ] && ! printf '%s' "$surfaces" | tr -d '[]' | tr ',' ' ' | grep -qw library; then
|
||||
log "skipping $name — surfaces: $surfaces does not include the library"
|
||||
continue
|
||||
fi
|
||||
|
||||
if printf '%s' "$desired" | grep -qx -- "$name"; then
|
||||
log "COLLISION: $name is offered by $target too; keeping $AGENTS_DIR/$name -> $(readlink "$AGENTS_DIR/$name" 2>/dev/null || echo '?')"
|
||||
continue
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
name: atdd
|
||||
surfaces: [claude-code, library]
|
||||
description: Implement user stories using the ATDD workflow — Red (failing acceptance test) → Green (minimal implementation) → Refactor. One story at a time.
|
||||
---
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
name: cad-babysit
|
||||
surfaces: [claude-code, library]
|
||||
description: Focused human-in-the-loop session working CAD's stuck queue (cad-stuck output). Read-only — diagnoses, never dispatches, arms a label, or closes an issue. Use when picking up whatever CAD has run into trouble on, with a human present.
|
||||
---
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
name: clean-code
|
||||
surfaces: [claude-code, library]
|
||||
description: Write code that humans can read, understand, and safely change. Apply SOLID, GRASP, and Clean Code principles during the REFACTOR phase of TDD.
|
||||
---
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
name: code-review
|
||||
surfaces: [claude-code, library]
|
||||
description: Systematic code smell detection and Go-specific review checklist. Load before any pre-merge review.
|
||||
---
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
name: cognitive-load
|
||||
surfaces: [claude-code, library]
|
||||
description: Measure how much mental effort a codebase demands from developers. Use when identifying hotspots, before architectural decisions, or when code feels hard to understand.
|
||||
---
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
name: debug
|
||||
surfaces: [claude-code, library]
|
||||
description: Systematic hypothesis-first debugging. Generate 3-5 ranked hypotheses with verification steps before suggesting any fix. Use when encountering a bug, test failure, or unexpected behavior.
|
||||
---
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
name: discovery-framing
|
||||
surfaces: [claude-code, library]
|
||||
description: Run the discovery / framing phase of product innovation — define and quantify a focused customer problem BEFORE any solution exists. Four movements: understand the problem space, learn about customers, identify and size problems, decide pursue/table/kill. Produces a problem brief per prioritized opportunity. Use when an idea is still vague, when someone is jumping to solutions before the problem is validated, when scoping early research, or when deciding which problems are worth pursuing. Trigger phrases include "frame the problem", "discovery", "problem statement", "is this worth building", "early research", "opportunity sizing", "we have an idea for", "how might we", "what problem are we solving".
|
||||
---
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
name: dream
|
||||
surfaces: [claude-code, library, chat]
|
||||
description: >
|
||||
Run a "dream" — a reflective memory consolidation pass over an agent's memory
|
||||
directory. Use this skill whenever the user says "dream", "run a dream", "consolidate
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
name: experiment-spec
|
||||
surfaces: [claude-code, library]
|
||||
description: Write a rigorous experiment spec for a research phase before any code or training runs. Use instead of feature-spec for scientific/ML research projects. Enforces falsifiable hypothesis, quantitative acceptance criteria, baseline comparison, and null-result protocol.
|
||||
---
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
name: feature-spec
|
||||
surfaces: [claude-code, library]
|
||||
description: Write a tight implementation-level spec for a single feature or component before coding it. Use after a project-level spec exists (see spec-driven-dev) and you need to nail down one feature inside it.
|
||||
---
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
name: gitea-ci
|
||||
surfaces: [claude-code, library]
|
||||
description: End-to-end Gitea CI/CD setup with self-hosted act_runner, quality gate pipeline, shared buildctl image-build script, k3s deploy, and GitHub mirror. Covers act_runner-specific gotchas that are not documented anywhere.
|
||||
---
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
name: grill-me
|
||||
surfaces: [claude-code, library, chat]
|
||||
description: Critically interrogates plans, ideas, and proposals to make them sharper, more robust, and better grounded — with the explicit goal of producing a meaningfully better plan by the end. Use this skill whenever the user wants to stress-test a plan, prepare for hard questions, check if an idea is ready to act on, validate a hypothesis, or explicitly asks to be challenged, questioned, or grilled. Also trigger when a conversation has been exploring an idea for a while and it's time to converge — use this skill to check if the idea is actually ready. Trigger phrases include "grill me", "challenge this", "poke holes", "am I missing something", "is this ready", "stress-test this", "devil's advocate", "what am I not seeing".
|
||||
---
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
name: own-the-task
|
||||
surfaces: [claude-code, library, chat]
|
||||
description: Take ownership of a delegated task like a hired employee — interview first, write a handover brief, propose a plan, then execute. Use whenever the user says "take this off my plate", "own this", "act as my AI employee", "interview me first", "ask me questions before you start", or delegates anything open-ended where the real goal, the why, and the definition of done are not yet on the table. Trigger even when "interview" is never said — any delegation where getting the intent wrong would waste the whole effort belongs here.
|
||||
---
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
name: planning
|
||||
surfaces: [claude-code, library]
|
||||
description: Decompose work into small, verifiable, ordered tasks. Use when you have a spec or clear requirements and need to break work into implementable units.
|
||||
---
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
name: playwright
|
||||
surfaces: [claude-code, library]
|
||||
description: Add and run Playwright E2E tests for any project with a web UI — covers setup, HTMX-specific patterns, SSE/streaming, Taskfile integration, and CI wiring.
|
||||
---
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
name: problem-analysis
|
||||
surfaces: [claude-code, library]
|
||||
description: Deeply understand a problem before coding begins. Identify the real problem, stakeholders, constraints, and success criteria. Use before starting any non-trivial feature.
|
||||
---
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
name: refactoring
|
||||
surfaces: [claude-code, library]
|
||||
description: Systematic refactoring methodology based on Martin Fowler's catalog. Apply during the REFACTOR phase of TDD, never while making code changes that modify behavior.
|
||||
---
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
name: regulatory-risk-assessment
|
||||
surfaces: [claude-code, library]
|
||||
description: Produce a structured regulatory risk assessment / risk-register entry for regulated-industry features (banking, payments, PSD2/PSR, DORA, AML). Use when filing a CAD issue that needs a Risk:LOW/MEDIUM/HIGH level, or designing features touching payments, auth, data storage, or external APIs ("what are the risks?", "compliance gate", "risk register").
|
||||
---
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
name: secret-hygiene
|
||||
surfaces: [claude-code, library]
|
||||
description: Prevent agent sessions from rendering credentials into the transcript, and recover safely when one leaks. Load BEFORE any command that touches credentials - kubectl exec/env/describe, kubectl get secret, whole-file SOPS decryption, token generation or rotation, envFrom inspection, or edits to encrypted config. Also load when a credential has already been exposed and a rotation chain is needed. Triggers - "rotate this token", "decrypt the config", "check the env var", "leaked credential", "did that print a secret", "why is this secret wrong".
|
||||
---
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
name: session-retrospective
|
||||
surfaces: [claude-code, library]
|
||||
description: Review a completed coding session (from brain/sessions/*.jsonl) and surface non-obvious learnings worth preserving. Use after a session ends, before context is lost.
|
||||
---
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
name: solid
|
||||
surfaces: [claude-code, library]
|
||||
description: Apply SOLID design principles in Go. Use during architecture decisions, design reviews, and when adding new abstractions.
|
||||
---
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
name: spec-driven-dev
|
||||
surfaces: [claude-code, library]
|
||||
description: Write a structured specification before writing any code. Use when starting a new project, feature, or significant change. Adapted for a PM-first workflow where why comes before how.
|
||||
---
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
name: stage-gate-review
|
||||
surfaces: [claude-code, library]
|
||||
description: Run or prepare a stage-gate product governance review — the go/no-go checkpoint where a council approves an initiative to advance through fixed innovation stages (Frame → Concept → Prototype → Validate → Scale), each gate guarded by a per-discipline readiness checklist. Use when preparing a Product Council / steering-group ask, building a go/no-go checklist, designing an innovation governance process, or auditing whether an initiative is genuinely ready to advance vs faking the gate. Trigger phrases include "stage gate", "product council", "go/no-go", "advance to the next stage", "is this ready to progress", "gate review", "phase gate", "innovation governance".
|
||||
---
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
name: brainstorming
|
||||
surfaces: [claude-code, library]
|
||||
description: "You MUST use this before any creative work - creating features, building components, adding functionality, or modifying behavior. Explores user intent, requirements and design before implementation."
|
||||
---
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
name: dispatching-parallel-agents
|
||||
surfaces: [claude-code, library]
|
||||
description: Use when facing 2+ independent tasks that can be worked on without shared state or sequential dependencies
|
||||
---
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
name: executing-plans
|
||||
surfaces: [claude-code, library]
|
||||
description: Use when you have a written implementation plan to execute in a separate session with review checkpoints
|
||||
---
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
name: finishing-a-development-branch
|
||||
surfaces: [claude-code, library]
|
||||
description: Use when implementation is complete, all tests pass, and you need to decide how to integrate the work - guides completion of development work by presenting structured options for merge, PR, or cleanup
|
||||
---
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
name: receiving-code-review
|
||||
surfaces: [claude-code, library]
|
||||
description: Use when receiving code review feedback, before implementing suggestions, especially if feedback seems unclear or technically questionable - requires technical rigor and verification, not performative agreement or blind implementation
|
||||
---
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
name: requesting-code-review
|
||||
surfaces: [claude-code, library]
|
||||
description: Use when completing tasks, implementing major features, or before merging to verify work meets requirements
|
||||
---
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
name: subagent-driven-development
|
||||
surfaces: [claude-code, library]
|
||||
description: Use when executing implementation plans with independent tasks in the current session
|
||||
---
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
name: systematic-debugging
|
||||
surfaces: [claude-code, library]
|
||||
description: Use when encountering any bug, test failure, or unexpected behavior, before proposing fixes
|
||||
---
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
name: using-git-worktrees
|
||||
surfaces: [claude-code, library]
|
||||
description: Use when starting feature work that needs isolation from current workspace or before executing implementation plans - ensures an isolated workspace exists via native tools or git worktree fallback
|
||||
---
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
name: using-superpowers
|
||||
surfaces: [claude-code, library]
|
||||
description: Use when starting any conversation - establishes how to find and use skills, requiring Skill tool invocation before ANY response including clarifying questions
|
||||
---
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
name: verification-before-completion
|
||||
surfaces: [claude-code, library]
|
||||
description: Use when about to claim work is complete, fixed, or passing, before committing or creating PRs - requires running verification commands and confirming output before making any success claims; evidence before assertions always
|
||||
---
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
name: writing-plans
|
||||
surfaces: [claude-code, library]
|
||||
description: Use when you have a spec or requirements for a multi-step task, before touching code
|
||||
---
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
name: writing-skills
|
||||
surfaces: [claude-code, library]
|
||||
description: Use when creating new skills, editing existing skills, or verifying skills work before deployment
|
||||
---
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
name: tdd
|
||||
surfaces: [claude-code, library]
|
||||
description: Write failing tests first, then minimal code to pass. Non-negotiable for all new features and bug fixes.
|
||||
---
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
name: telos-load
|
||||
surfaces: [claude-code, library]
|
||||
description: Load TELOS — the intention substrate (problems, goals, strategies, status) — into a session at start so every decision traces to a goal. Use when starting any koala session, before architectural decisions, or as the CAD pipeline entry point ("load TELOS", "what are we optimizing toward?").
|
||||
---
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
name: test-design
|
||||
surfaces: [claude-code, library]
|
||||
description: Evaluate test quality using Dave Farley's 8 Properties of Good Tests. Use when reviewing or writing tests to ensure they provide genuine verification.
|
||||
---
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
name: trainer
|
||||
surfaces: [claude-code, library]
|
||||
description: Two-phase brain curation — scan session logs to score candidate learnings (READ), then write the ones that pass the quality gate as generalized knowledge entries (WRITE). Use periodically to keep the brain useful, not noisy.
|
||||
---
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
name: user-stories
|
||||
surfaces: [claude-code, library]
|
||||
description: Decompose problems into thin, deliverable user stories using Elephant Carpaccio and INVEST criteria. Use after problem analysis, before planning and implementation.
|
||||
---
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
name: web-shot
|
||||
surfaces: [claude-code, library]
|
||||
description: Screenshot any running web UI by spawning a one-shot Playwright Job on the koala k3s cluster. Auto-detects the calling host's Tailscale name so a dev server on flamingo:8099, iguana:3000 or koala:8181 just works. Use when the user asks for a screenshot, visual verification, "what does the UI look like", or any time chromium / firefox / a browser is requested but none is installed on the host.
|
||||
---
|
||||
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
#!/bin/sh
|
||||
# Tests for bin/check-surfaces.
|
||||
#
|
||||
# The repo ships to three surfaces — the Claude Code plugin, the symlinked
|
||||
# library (Crush/Pi/OpenCode) and claude.ai, which has no fetch path at all —
|
||||
# and used to treat every skill as belonging to all of them. gitea-ci,
|
||||
# playwright and web-shot cannot fire in a chat surface; a flat mirror installs
|
||||
# skills that can never trigger and degrades selection for the ones that can
|
||||
# (#29).
|
||||
#
|
||||
# The risk this gate exists for: a wrong `surfaces:` value silently removes a
|
||||
# skill from a harness rather than erroring. So presence is not enough — values
|
||||
# are validated against the allowed set.
|
||||
|
||||
set -u
|
||||
|
||||
ROOT=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
|
||||
SUT="$ROOT/bin/check-surfaces"
|
||||
|
||||
pass=0
|
||||
fail=0
|
||||
WORK=${TMPDIR:-/tmp}/check_surfaces_test.$$
|
||||
|
||||
if [ ! -f "$SUT" ]; then
|
||||
printf 'FAIL: %s does not exist\n' "$SUT"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cleanup() { rm -rf "$WORK"; }
|
||||
trap cleanup EXIT INT TERM
|
||||
|
||||
# fixture <name> <surfaces-line-or-empty> — a checkout holding one skill.
|
||||
fixture() {
|
||||
_f="$WORK/$1"
|
||||
mkdir -p "$_f/skills/alpha"
|
||||
if [ -n "$2" ]; then
|
||||
printf -- '---\nname: alpha\n%s\ndescription: a\n---\n' "$2" > "$_f/skills/alpha/SKILL.md"
|
||||
else
|
||||
printf -- '---\nname: alpha\ndescription: a\n---\n' > "$_f/skills/alpha/SKILL.md"
|
||||
fi
|
||||
printf '%s' "$_f"
|
||||
}
|
||||
|
||||
# expect <rc> <label> <fixture> [needle]
|
||||
expect() {
|
||||
_want=$1; _label=$2; _fx=$3; _needle=${4:-}
|
||||
_out=$(sh "$SUT" "$_fx" 2>&1); _rc=$?
|
||||
if [ "$_rc" -ne "$_want" ]; then
|
||||
fail=$((fail + 1)); printf ' FAIL %s (want rc=%s got %s)\n%s\n' "$_label" "$_want" "$_rc" "$_out"; return
|
||||
fi
|
||||
if [ -n "$_needle" ] && ! printf '%s' "$_out" | grep -q "$_needle"; then
|
||||
fail=$((fail + 1)); printf ' FAIL %s (rc ok, report never says %s)\n%s\n' "$_label" "$_needle" "$_out"; return
|
||||
fi
|
||||
pass=$((pass + 1)); printf ' ok %s\n' "$_label"
|
||||
}
|
||||
|
||||
expect 0 'the default pair is accepted' "$(fixture ok 'surfaces: [claude-code, library]')"
|
||||
expect 0 'all three surfaces are accepted' "$(fixture three 'surfaces: [claude-code, library, chat]')"
|
||||
expect 0 'a single surface is accepted' "$(fixture one 'surfaces: [chat]')"
|
||||
expect 1 'a missing surfaces field fails' "$(fixture missing '')" 'alpha'
|
||||
expect 1 'an unknown surface value fails' "$(fixture bogus 'surfaces: [claude-code, desktop]')" 'desktop'
|
||||
expect 1 'an empty surfaces list fails' "$(fixture empty 'surfaces: []')" 'alpha'
|
||||
|
||||
printf '\ncheck_surfaces: %d passed, %d failed\n' "$pass" "$fail"
|
||||
[ "$fail" -eq 0 ]
|
||||
@@ -127,6 +127,13 @@ f=$(new_fixture missing); wire_clean "$f"
|
||||
rm -f "$f/home/.agents/skills/beta"
|
||||
assert_fails "$f" 'beta' 'a checkout skill with no link is reported'
|
||||
|
||||
# ...but only for skills that ship to the library. A chat-only skill has no
|
||||
# business in a Crush directory, so its absence is correct, not drift (#29).
|
||||
f=$(new_fixture chatonly); wire_clean "$f"
|
||||
mkdir -p "$f/ck/skills/gamma"
|
||||
printf -- '---\nname: gamma\nsurfaces: [chat]\ndescription: g\n---\n' > "$f/ck/skills/gamma/SKILL.md"
|
||||
assert_clean "$f" 'a chat-only skill needs no library link'
|
||||
|
||||
# --- 5. stray entry (the loose secret-hygiene.md that loaded nowhere) --------
|
||||
f=$(new_fixture stray); wire_clean "$f"
|
||||
printf 'orphan\n' > "$f/ck/skills/loose-note.md"
|
||||
|
||||
@@ -156,5 +156,32 @@ else
|
||||
no 'configured sources are recorded for doctor' "$out"
|
||||
fi
|
||||
|
||||
# --- 8. surfaces: the library link set is not "every skill" -----------------
|
||||
# claude.ai has no shell, no cluster and no checkout, so a chat-only skill
|
||||
# linked into Crush is a skill that can never fire, competing for selection
|
||||
# with ones that can (#29).
|
||||
f=$(new_fixture surfaces)
|
||||
mkdir -p "$f/ck/skills/chatonly"
|
||||
printf -- '---\nname: chatonly\nsurfaces: [chat]\ndescription: c\n---\n' > "$f/ck/skills/chatonly/SKILL.md"
|
||||
printf -- '---\nname: alpha\nsurfaces: [claude-code, library]\ndescription: a\n---\n' > "$f/ck/skills/alpha/SKILL.md"
|
||||
out=$(run_install "$f")
|
||||
if [ -L "$f/home/.agents/skills/alpha" ] && [ ! -e "$f/home/.agents/skills/chatonly" ]; then
|
||||
ok 'a skill that does not ship to the library is not linked'
|
||||
else
|
||||
no 'a skill that does not ship to the library is not linked' "$out"
|
||||
fi
|
||||
|
||||
# A foreign source has no reason to know this convention, so no declaration
|
||||
# means "link it" — otherwise adding Anthropic's set would link nothing.
|
||||
f=$(new_fixture foreign)
|
||||
mkdir -p "$f/other/xlsx"
|
||||
printf -- '---\nname: xlsx\ndescription: x\n---\n' > "$f/other/xlsx/SKILL.md"
|
||||
out=$(run_install "$f" "$f/other")
|
||||
if [ -L "$f/home/.agents/skills/xlsx" ]; then
|
||||
ok 'a source with no surfaces convention is linked as-is'
|
||||
else
|
||||
no 'a source with no surfaces convention is linked as-is' "$out"
|
||||
fi
|
||||
|
||||
printf '\ninstall: %d passed, %d failed\n' "$pass" "$fail"
|
||||
[ "$fail" -eq 0 ]
|
||||
|
||||
Reference in New Issue
Block a user