fix(gstack): one helper-link tree for every hardcoded path, honest doctor stats

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.
This commit is contained in:
bastien
2026-09-28 11:48:45 +02:00
parent f83f8f755b
commit 02b62f787e
8 changed files with 463 additions and 68 deletions
+14 -32
View File
@@ -55,41 +55,23 @@ for item in hooks githooks agents skills lib templates rules; do
done
# GStack is exposed via per-skill symlinks under skills/ (browse,
# canary, autoplan, design-review, …) created by gstack's own
# `./setup`. A global `skills/gstack -> skills-external/gstack/`
# symlink duplicated the top-level gstack SKILL.md alongside those
# individual skills, producing two entries with the same description
# ("Fast headless browser for QA testing…"). Remove any stale global
# link — only per-skill entries remain.
if [ -L "$REPO/skills/gstack" ] || [ -L "$CLAUDE/skills/gstack" ]; then
rm -f "$REPO/skills/gstack" "$CLAUDE/skills/gstack"
CHANGED=$((CHANGED + 1))
fi
if [ ! -d "$REPO/skills-external/gstack" ]; then
# canary, autoplan, design-review, …) created by gstack's own `./setup`,
# PLUS a shared helper tree at skills/gstack/ mirroring every asset the
# skills hardcode (bin/, browse/dist/, ETHOS.md, …) that a per-skill
# symlink never exposes — see lib/gstack-links.sh. The helper tree
# never contains a SKILL.md at any depth, so it never duplicates a
# per-skill entry the way a flat `skills/gstack -> skills-external/gstack`
# link used to (removed by the lib's own stale-symlink guard).
# shellcheck source=lib/gstack-links.sh disable=SC1091
source "$REPO/lib/gstack-links.sh"
if [ -d "$REPO/skills-external/gstack" ]; then
n=$(link_gstack_helpers "$REPO/skills-external/gstack" \
"$CLAUDE/skills/gstack")
CHANGED=$((CHANGED + n))
else
echo "⚠️ GStack submodule not found — run: git submodule update --init"
fi
# GStack shared infrastructure: bin/ (CLI tools, config, analytics) and
# browse/dist/ (compiled browse binary). Per-skill SKILL.md symlinks don't
# expose these, but multiple skills hardcode ~/.claude/skills/gstack/bin/
# and ~/.claude/skills/gstack/browse/dist/. Create targeted symlinks.
GSTACK_SRC="$REPO/skills-external/gstack"
GSTACK_DST="$CLAUDE/skills/gstack"
if [ -d "$GSTACK_SRC/bin" ]; then
mkdir -p "$GSTACK_DST"
if [ ! -L "$GSTACK_DST/bin" ]; then
ln -sf "$GSTACK_SRC/bin" "$GSTACK_DST/bin"
CHANGED=$((CHANGED + 1))
fi
fi
if [ -d "$GSTACK_SRC/browse/dist" ]; then
mkdir -p "$GSTACK_DST/browse"
if [ ! -L "$GSTACK_DST/browse/dist" ]; then
ln -sf "$GSTACK_SRC/browse/dist" "$GSTACK_DST/browse/dist"
CHANGED=$((CHANGED + 1))
fi
fi
# impeccable is NOT here: its installer writes the skill straight into
# skills/ (and its agents into agents/) at --scope=global, so there is no
# skills-external/ copy to symlink. See install-plugins.sh Step 8d.