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) <noreply@anthropic.com>
This commit is contained in:
Garry Tan
2026-03-30 19:38:56 -07:00
parent d20df88470
commit cbced97d72
4 changed files with 51 additions and 0 deletions
+34
View File
@@ -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 <gstack-dir> <true|false|1|0>
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
+3
View File
@@ -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
+13
View File
@@ -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 */ }
}
+1
View File
@@ -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"