Oppgave 0 (#44): søk-først (claude-code-guide mot offisiell docs) verifiserte at `auto_discover` IKKE er et gyldig Claude Code plugin.json-felt — auto-discovery er implisitt default (code.claude.com/docs/en/plugins-reference; ukjent felt gir kun valideringsadvarsel, ikke feil). → stale konvensjon, IKKE manglende felt. - tests/test-plugin-discovery.sh: stale hard-assertion (krevde `auto_discover: true`) → optional-felt-logikk som speiler validate-plugin.sh (fravær = PASS «auto-discovery implisitt»; present må være true). plugin.json URØRT (la IKKE til ugyldig felt). 12/13 → 13/13. validate 239/0 (ingen regresjon). STATE oppdatert: NESTE = C1 Tier 2. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
172 lines
5 KiB
Bash
172 lines
5 KiB
Bash
#!/bin/bash
|
|
# test-plugin-discovery.sh — Smoke test for auto-discovery chain
|
|
# Validates that plugin.json, hooks.json, and script references are consistent
|
|
# Usage: bash tests/test-plugin-discovery.sh
|
|
|
|
set -euo pipefail
|
|
|
|
PLUGIN_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
|
PASS=0
|
|
FAIL=0
|
|
|
|
pass() { echo -e "\033[0;32m ✓ $1\033[0m"; PASS=$((PASS + 1)); }
|
|
fail() { echo -e "\033[0;31m ✗ $1\033[0m"; FAIL=$((FAIL + 1)); }
|
|
|
|
echo "=== Plugin Discovery Smoke Test ==="
|
|
echo ""
|
|
|
|
# -------------------------------------------------------
|
|
# Check 1: plugin.json
|
|
# -------------------------------------------------------
|
|
echo "--- Check 1: plugin.json ---"
|
|
|
|
PLUGIN_JSON="$PLUGIN_ROOT/.claude-plugin/plugin.json"
|
|
|
|
if [ -f "$PLUGIN_JSON" ]; then
|
|
pass "plugin.json exists"
|
|
else
|
|
fail "plugin.json missing at .claude-plugin/plugin.json"
|
|
fi
|
|
|
|
# auto_discover is NOT a standard Claude Code plugin.json field — auto-discovery
|
|
# of skills/commands/agents/hooks is the implicit default (verified against
|
|
# code.claude.com/docs/en/plugins-reference; an unrecognized field only triggers a
|
|
# validation warning). Treat it as optional: if present it must be true, if absent
|
|
# the plugin is still auto-discovered. Mirrors validate-plugin.sh.
|
|
if grep -q '"auto_discover"' "$PLUGIN_JSON" 2>/dev/null; then
|
|
if grep -Eq '"auto_discover"[[:space:]]*:[[:space:]]*true' "$PLUGIN_JSON" 2>/dev/null; then
|
|
pass "auto_discover present and true"
|
|
else
|
|
fail "auto_discover present but not true"
|
|
fi
|
|
else
|
|
pass "auto-discovery is implicit (Claude Code default; no auto_discover field needed)"
|
|
fi
|
|
|
|
# plugin.json must NOT have a "hooks" key
|
|
if grep -q '"hooks"' "$PLUGIN_JSON" 2>/dev/null; then
|
|
fail "plugin.json contains 'hooks' key (should be in hooks/hooks.json only)"
|
|
else
|
|
pass "plugin.json does not contain 'hooks' key"
|
|
fi
|
|
|
|
echo ""
|
|
|
|
# -------------------------------------------------------
|
|
# Check 2: hooks.json
|
|
# -------------------------------------------------------
|
|
echo "--- Check 2: hooks.json ---"
|
|
|
|
HOOKS_JSON="$PLUGIN_ROOT/hooks/hooks.json"
|
|
|
|
if [ -f "$HOOKS_JSON" ]; then
|
|
pass "hooks.json exists"
|
|
else
|
|
fail "hooks.json missing at hooks/hooks.json"
|
|
fi
|
|
|
|
if node -e "JSON.parse(require('fs').readFileSync('$HOOKS_JSON', 'utf-8'))" 2>/dev/null; then
|
|
pass "hooks.json is valid JSON"
|
|
else
|
|
fail "hooks.json is invalid JSON"
|
|
fi
|
|
|
|
# Check structure: hooks key is an object
|
|
if node -e "
|
|
const h = JSON.parse(require('fs').readFileSync('$HOOKS_JSON', 'utf-8'));
|
|
if (!h.hooks || typeof h.hooks !== 'object') process.exit(1);
|
|
" 2>/dev/null; then
|
|
pass "hooks.json has 'hooks' object at root"
|
|
else
|
|
fail "hooks.json missing root 'hooks' object"
|
|
fi
|
|
|
|
echo ""
|
|
|
|
# -------------------------------------------------------
|
|
# Check 3: Script references
|
|
# -------------------------------------------------------
|
|
echo "--- Check 3: Script References ---"
|
|
|
|
# Extract script paths from hooks.json
|
|
SCRIPT_PATHS=$(node -e "
|
|
const h = JSON.parse(require('fs').readFileSync('$HOOKS_JSON', 'utf-8'));
|
|
for (const [event, handlers] of Object.entries(h.hooks)) {
|
|
for (const handler of handlers) {
|
|
for (const hook of (handler.hooks || [])) {
|
|
if (hook.command) {
|
|
const match = hook.command.match(/\\\$\\{CLAUDE_PLUGIN_ROOT\\}\\/(.+)/);
|
|
if (match) console.log(match[1]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
" 2>/dev/null)
|
|
|
|
for script_path in $SCRIPT_PATHS; do
|
|
full_path="$PLUGIN_ROOT/$script_path"
|
|
if [ -f "$full_path" ]; then
|
|
pass "Script exists: $script_path"
|
|
else
|
|
fail "Script missing: $script_path"
|
|
fi
|
|
done
|
|
|
|
echo ""
|
|
|
|
# -------------------------------------------------------
|
|
# Check 4: Event names
|
|
# -------------------------------------------------------
|
|
echo "--- Check 4: Event Names ---"
|
|
|
|
VALID_EVENTS="SessionStart UserPromptSubmit PreToolUse PostToolUse Stop"
|
|
|
|
HOOK_EVENTS=$(node -e "
|
|
const h = JSON.parse(require('fs').readFileSync('$HOOKS_JSON', 'utf-8'));
|
|
for (const event of Object.keys(h.hooks)) console.log(event);
|
|
" 2>/dev/null)
|
|
|
|
for event in $HOOK_EVENTS; do
|
|
if echo "$VALID_EVENTS" | grep -qw "$event"; then
|
|
pass "Valid event: $event"
|
|
else
|
|
fail "Invalid event: $event"
|
|
fi
|
|
done
|
|
|
|
echo ""
|
|
|
|
# -------------------------------------------------------
|
|
# Check 5: Documentation
|
|
# -------------------------------------------------------
|
|
echo "--- Check 5: Documentation ---"
|
|
|
|
CLAUDE_MD="$PLUGIN_ROOT/CLAUDE.md"
|
|
|
|
if grep -q "## Hooks" "$CLAUDE_MD" 2>/dev/null; then
|
|
pass "CLAUDE.md has Hooks section"
|
|
else
|
|
fail "CLAUDE.md missing Hooks section"
|
|
fi
|
|
|
|
# pre-edit-secrets removed in v1.7.0 (consolidated to llm-security); only the
|
|
# two live hooks are documented.
|
|
for script in session-start-context stop-assessment-reminder; do
|
|
if grep -q "$script" "$CLAUDE_MD" 2>/dev/null; then
|
|
pass "CLAUDE.md documents $script"
|
|
else
|
|
fail "CLAUDE.md missing $script documentation"
|
|
fi
|
|
done
|
|
|
|
echo ""
|
|
|
|
# -------------------------------------------------------
|
|
# Summary
|
|
# -------------------------------------------------------
|
|
TOTAL=$((PASS + FAIL))
|
|
echo "=== Results: $PASS/$TOTAL passed, $FAIL failed ==="
|
|
|
|
if [ "$FAIL" -gt 0 ]; then
|
|
exit 1
|
|
fi
|