From cbced97d729269f39049c59e2852b08fbd3c6a28 Mon Sep 17 00:00:00 2001 From: Garry Tan Date: Mon, 30 Mar 2026 19:38:56 -0700 Subject: [PATCH] fix: patch name: in SKILL.md frontmatter for prefix mode (#620, #578) ./setup --prefix creates gstack-* symlinks but SKILL.md still says name: qa, so Claude Code ignores the prefix. Now: - New bin/gstack-patch-names shared helper patches name: field via sed - setup calls it after link_claude_skill_dirs - gstack-relink calls it after symlink loop - gen-skill-docs.ts prints warning when skill_prefix is true Edge cases: gstack-upgrade not double-prefixed, root gstack skill never prefixed, prefix removal restores original names, SKILL.md without frontmatter is a safe no-op. Co-Authored-By: Claude Opus 4.6 (1M context) --- bin/gstack-patch-names | 34 ++++++++++++++++++++++++++++++++++ bin/gstack-relink | 3 +++ scripts/gen-skill-docs.ts | 13 +++++++++++++ setup | 1 + 4 files changed, 51 insertions(+) create mode 100755 bin/gstack-patch-names diff --git a/bin/gstack-patch-names b/bin/gstack-patch-names new file mode 100755 index 00000000..bef02aae --- /dev/null +++ b/bin/gstack-patch-names @@ -0,0 +1,34 @@ +#!/usr/bin/env bash +# gstack-patch-names — patch name: field in SKILL.md frontmatter for prefix mode +# Usage: gstack-patch-names +set -euo pipefail + +GSTACK_DIR="$1" +DO_PREFIX="$2" + +# Normalize prefix arg +case "$DO_PREFIX" in true|1) DO_PREFIX=1 ;; *) DO_PREFIX=0 ;; esac + +PATCHED=0 +for skill_dir in "$GSTACK_DIR"/*/; do + [ -f "$skill_dir/SKILL.md" ] || continue + dir_name="$(basename "$skill_dir")" + [ "$dir_name" = "node_modules" ] && continue + cur=$(grep -m1 '^name:' "$skill_dir/SKILL.md" 2>/dev/null | sed 's/^name:[[:space:]]*//' | tr -d '[:space:]' || true) + [ -z "$cur" ] && continue + [ "$cur" = "gstack" ] && continue # never prefix root skill + if [ "$DO_PREFIX" -eq 1 ]; then + case "$cur" in gstack-*) continue ;; esac + new="gstack-$cur" + else + case "$cur" in gstack-*) ;; *) continue ;; esac + [ "$dir_name" = "$cur" ] && continue # inherently prefixed (gstack-upgrade) + new="${cur#gstack-}" + fi + tmp="$(mktemp "${skill_dir}/SKILL.md.XXXXXX")" + sed "1,/^---$/s/^name:[[:space:]]*${cur}/name: ${new}/" "$skill_dir/SKILL.md" > "$tmp" && mv "$tmp" "$skill_dir/SKILL.md" + PATCHED=$((PATCHED + 1)) +done +if [ "$PATCHED" -gt 0 ]; then + echo " patched name: field in $PATCHED skills" +fi diff --git a/bin/gstack-relink b/bin/gstack-relink index 49d0ccac..4647f6df 100755 --- a/bin/gstack-relink +++ b/bin/gstack-relink @@ -66,6 +66,9 @@ for skill_dir in "$INSTALL_DIR"/*/; do SKILL_COUNT=$((SKILL_COUNT + 1)) done +# Patch SKILL.md name: fields to match prefix setting +"$INSTALL_DIR/bin/gstack-patch-names" "$INSTALL_DIR" "$PREFIX" + if [ "$PREFIX" = "true" ]; then echo "Relinked $SKILL_COUNT skills as gstack-*" else diff --git a/scripts/gen-skill-docs.ts b/scripts/gen-skill-docs.ts index 94f39101..ec495189 100644 --- a/scripts/gen-skill-docs.ts +++ b/scripts/gen-skill-docs.ts @@ -472,3 +472,16 @@ if (failures.length > 0 && HOST_ARG_VAL === 'all') { if (failures.some(f => f.host === 'claude')) process.exit(1); } // Single host dry-run failure already handled above + +// After all hosts processed, warn if prefix patches may need re-applying +if (!DRY_RUN) { + try { + const configPath = path.join(process.env.HOME || '', '.gstack', 'config.yaml'); + if (fs.existsSync(configPath)) { + const config = fs.readFileSync(configPath, 'utf-8'); + if (/^skill_prefix:\s*true/m.test(config)) { + console.log('\nNote: skill_prefix is true. Run gstack-relink to re-apply name: patches.'); + } + } + } catch { /* non-fatal */ } +} diff --git a/setup b/setup index d2836245..609311d5 100755 --- a/setup +++ b/setup @@ -567,6 +567,7 @@ if [ "$INSTALL_CLAUDE" -eq 1 ]; then cleanup_prefixed_claude_symlinks "$SOURCE_GSTACK_DIR" "$INSTALL_SKILLS_DIR" fi link_claude_skill_dirs "$SOURCE_GSTACK_DIR" "$INSTALL_SKILLS_DIR" + "$SOURCE_GSTACK_DIR/bin/gstack-patch-names" "$SOURCE_GSTACK_DIR" "$SKILL_PREFIX" if [ "$LOCAL_INSTALL" -eq 1 ]; then echo "gstack ready (project-local)." echo " skills: $INSTALL_SKILLS_DIR"