forked from bchanot/claude
gstack skills hardcode ~/.claude/skills/gstack/<path> for 83 shared assets (bin, scripts/jargon-list.json, ETHOS.md, */sections, review/specialists, make-pdf/dist, lib/diagram-render/dist, freeze/bin...) but only bin and browse/dist were linked: make-pdf and diagram failed on every run, cso and plan-*-review could not read their sections, the freeze hook exited 127. lib/gstack-links.sh links every top-level entry except SKILL.md, skips non-skill dirs holding a nested SKILL.md (browser-skills, openclaw, node_modules), removes the global symlink gstack ./setup plants and refuses a destination inside the submodule. link.sh, install-plugins.sh and update-all.sh all call it (three hand-copied blocks gone). doctor.sh counted 34 skills (find without -L) and zero chars for block scalar descriptions; lib/doctor-skills.sh reuses the census parser and counts through the symlinks. Plugin constants re-based on measured values; install-plugins.sh notes why frontend-design@claude-plugins-official and brightdata-plugin@synced stay off and describes security-guidance truthfully.
92 lines
2.3 KiB
Bash
Executable File
92 lines
2.3 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# lib/tests/doctor-skills.test.sh — lib/doctor-skills.sh's
|
|
# skill_catalog_stats(): an inline scalar description, a `|` block
|
|
# scalar, a `>-` folded block, a SKILL.md with no description, a
|
|
# symlinked skill dir (counted, matching Python glob.glob's symlink
|
|
# behavior), and an absent skills dir ("0 0", rc 0 — doctor.sh runs
|
|
# under `set -euo pipefail` and must never abort on this check).
|
|
set -u
|
|
ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
|
|
LIB="$ROOT/lib/doctor-skills.sh"
|
|
pass=0; fail=0
|
|
check() { if [ "$2" = "$3" ]; then pass=$((pass+1)); else fail=$((fail+1));
|
|
printf 'FAIL %s: got[%s] want[%s]\n' "$1" "$2" "$3"; fi; }
|
|
|
|
WORK="$(mktemp -d)"; trap 'rm -rf "$WORK"' EXIT
|
|
SKILLS="$WORK/skills"
|
|
REAL="$WORK/real-skill"
|
|
mkdir -p "$SKILLS/inline" "$SKILLS/pipe-block" "$SKILLS/fold-block" \
|
|
"$SKILLS/no-desc" "$REAL"
|
|
|
|
cat > "$SKILLS/inline/SKILL.md" <<'EOF'
|
|
---
|
|
name: inline
|
|
description: "Inline description, sixteen."
|
|
---
|
|
body
|
|
EOF
|
|
|
|
cat > "$SKILLS/pipe-block/SKILL.md" <<'EOF'
|
|
---
|
|
name: pipe-block
|
|
description: |
|
|
Block scalar description
|
|
spanning two lines.
|
|
---
|
|
body
|
|
EOF
|
|
|
|
cat > "$SKILLS/fold-block/SKILL.md" <<'EOF'
|
|
---
|
|
name: fold-block
|
|
description: >-
|
|
Folded block description
|
|
on two lines too.
|
|
---
|
|
body
|
|
EOF
|
|
|
|
cat > "$SKILLS/no-desc/SKILL.md" <<'EOF'
|
|
---
|
|
name: no-desc
|
|
---
|
|
body
|
|
EOF
|
|
|
|
cat > "$REAL/SKILL.md" <<'EOF'
|
|
---
|
|
name: symlinked
|
|
description: "Symlinked skill description."
|
|
---
|
|
body
|
|
EOF
|
|
ln -s "$REAL" "$SKILLS/symlinked"
|
|
|
|
# ── hand-computed expectations (mirrors extract_description()'s scalar
|
|
# and block-scalar handling) ──
|
|
INLINE_DESC="Inline description, sixteen."
|
|
PIPE_DESC="Block scalar description spanning two lines."
|
|
FOLD_DESC="Folded block description on two lines too."
|
|
SYM_DESC="Symlinked skill description."
|
|
EXP_COUNT=5
|
|
EXP_CHARS=$((${#INLINE_DESC} + ${#PIPE_DESC} + ${#FOLD_DESC} + ${#SYM_DESC}))
|
|
|
|
# shellcheck source=../doctor-skills.sh disable=SC1091
|
|
source "$LIB"
|
|
|
|
out="$(skill_catalog_stats "$SKILLS")"
|
|
rc=$?
|
|
got_count="${out%% *}"
|
|
got_chars="${out##* }"
|
|
check T1-rc "$rc" 0
|
|
check T1-count "$got_count" "$EXP_COUNT"
|
|
check T1-chars "$got_chars" "$EXP_CHARS"
|
|
|
|
# ── T2: absent dir — "0 0", rc 0 ──
|
|
out2="$(skill_catalog_stats "$WORK/does-not-exist")"
|
|
rc2=$?
|
|
check T2-rc "$rc2" 0
|
|
check T2-zeroes "$out2" "0 0"
|
|
|
|
printf 'PASS=%s FAIL=%s\n' "$pass" "$fail"; [ "$fail" -eq 0 ]
|