mirror of
https://github.com/garrytan/gstack.git
synced 2026-09-12 07:59:02 +02:00
fix(setup): alias skills install as rewritten copies, never symlinks
The two back-compat alias dirs — _gstack-command (root router) and connect-chrome (→ open-gstack-browser) — symlinked the canonical SKILL.md verbatim, so each alias re-served the canonical frontmatter name:. Claude Code keys skills on that name and requires global uniqueness: the connect-chrome duplicate silently shadowed /open-gstack-browser (whichever readdir returned first won), and the _gstack-command duplicate could drop the ENTIRE personal-skills set — every /gstack command vanished until the user hand-deleted the alias dirs, and the next setup re-broke it. Fix: copy-then-rewrite. A shared _install_alias_skill_md helper reads the SOURCE SKILL.md and writes a fresh copy with name: rewritten to the alias dir's own name (_gstack-command / connect-chrome / gstack-connect-chrome). sed never edits in place: on Unix the old install was a symlink into the repo, and an in-place rewrite through it would have corrupted the generated source (eng review E2). bin/gstack-relink gets the same treatment for its root-alias helper, and its discovery loop now skips symlinked source dirs so the connect-chrome repo symlink can't re-mint the duplicate. Tests assert: installed aliases are NOT symlinks, carry their own unique names, all installed frontmatter names are globally unique, re-runs refresh cleanly, legacy symlinked aliases are replaced not written through, and the source files stay byte-intact. Fixes #2511 Fixes #2201 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
0f38feee78
commit
663aca3b05
@@ -784,24 +784,40 @@ link_claude_skill_dirs() {
|
||||
fi
|
||||
}
|
||||
|
||||
# ─── Helper: install an alias SKILL.md as a rewritten COPY ───────────────────
|
||||
# Alias dirs (_gstack-command, connect-chrome) must NOT symlink the canonical
|
||||
# SKILL.md: the alias then carries the canonical frontmatter name:, Claude Code
|
||||
# sees two skills with the same name, and drops the ENTIRE personal-skills set
|
||||
# (#2511, #2201). Copy-then-rewrite instead: sed reads the SOURCE and writes a
|
||||
# fresh copy with name: set to the alias. It must never edit through an
|
||||
# existing symlink — that would rewrite the generated source file itself.
|
||||
_install_alias_skill_md() {
|
||||
local src_skill_md="$1"
|
||||
local dst_dir="$2"
|
||||
local alias_name="$3"
|
||||
[ -f "$src_skill_md" ] || return 0
|
||||
# Old installs left the alias as a whole-dir symlink — replace it.
|
||||
if [ -L "$dst_dir" ]; then rm -f "$dst_dir"; fi
|
||||
mkdir -p "$dst_dir"
|
||||
# Remove any prior symlinked SKILL.md so the redirect below cannot write
|
||||
# through it into the generated source.
|
||||
rm -f "$dst_dir/SKILL.md"
|
||||
sed "1,/^---\$/ s/^name:[[:space:]].*/name: $alias_name/" "$src_skill_md" > "$dst_dir/SKILL.md"
|
||||
}
|
||||
|
||||
# Claude Code skips the repo-shaped ~/.claude/skills/gstack directory when
|
||||
# building the user-facing slash-command list. Keep the repo path for runtime
|
||||
# assets, and add a separate thin wrapper whose frontmatter name remains
|
||||
# `gstack` so `/gstack` can autocomplete.
|
||||
# assets, and add a separate thin wrapper. Its frontmatter name is rewritten to
|
||||
# `_gstack-command` (the dir name) so it never collides with the canonical
|
||||
# `gstack` name (#2511).
|
||||
link_claude_root_skill_alias() {
|
||||
local gstack_dir="$1"
|
||||
local skills_dir="$2"
|
||||
local target="$skills_dir/_gstack-command"
|
||||
|
||||
[ -f "$gstack_dir/SKILL.md" ] || return 0
|
||||
if [ -L "$target" ]; then
|
||||
rm -f "$target"
|
||||
fi
|
||||
mkdir -p "$target"
|
||||
if [ -L "$target/SKILL.md" ]; then rm "$target/SKILL.md"; fi
|
||||
_link_or_copy "$gstack_dir/SKILL.md" "$target/SKILL.md"
|
||||
_install_alias_skill_md "$gstack_dir/SKILL.md" "$target" "_gstack-command"
|
||||
echo " linked root skill alias: gstack"
|
||||
_print_windows_copy_note_once
|
||||
}
|
||||
|
||||
# ─── Helper: remove old unprefixed Claude skill entries ───────────────────────
|
||||
@@ -1231,13 +1247,16 @@ if [ "$INSTALL_CLAUDE" -eq 1 ]; then
|
||||
GSTACK_SKILLS_DIR="$INSTALL_SKILLS_DIR" GSTACK_INSTALL_DIR="$SOURCE_GSTACK_DIR" "$GSTACK_RELINK" >/dev/null 2>&1 || true
|
||||
fi
|
||||
# Backwards-compat alias: /connect-chrome → /open-gstack-browser
|
||||
# Rewritten copy, not a symlink: a symlinked alias re-serves the canonical
|
||||
# name: open-gstack-browser, so one of the two silently shadows the other
|
||||
# (#2201) — and duplicate names can drop the whole skill set (#2511).
|
||||
_OGB_LINK="$INSTALL_SKILLS_DIR/connect-chrome"
|
||||
_OGB_ALIAS_NAME="connect-chrome"
|
||||
if [ "$SKILL_PREFIX" -eq 1 ]; then
|
||||
_OGB_LINK="$INSTALL_SKILLS_DIR/gstack-connect-chrome"
|
||||
_OGB_ALIAS_NAME="gstack-connect-chrome"
|
||||
fi
|
||||
if [ -L "$_OGB_LINK" ] || [ ! -e "$_OGB_LINK" ]; then
|
||||
_link_or_copy "gstack/open-gstack-browser" "$_OGB_LINK"
|
||||
fi
|
||||
_install_alias_skill_md "$SOURCE_GSTACK_DIR/open-gstack-browser/SKILL.md" "$_OGB_LINK" "$_OGB_ALIAS_NAME"
|
||||
if [ "$LOCAL_INSTALL" -eq 1 ]; then
|
||||
log "gstack ready (project-local)."
|
||||
log " skills: $INSTALL_SKILLS_DIR"
|
||||
@@ -1299,13 +1318,17 @@ if [ "$INSTALL_CLAUDE" -eq 1 ]; then
|
||||
if [ -x "$GSTACK_RELINK" ]; then
|
||||
GSTACK_SKILLS_DIR="$INSTALL_SKILLS_DIR" GSTACK_INSTALL_DIR="$SOURCE_GSTACK_DIR" "$GSTACK_RELINK" >/dev/null 2>&1 || true
|
||||
fi
|
||||
# Rewritten copy, not a symlink: a symlinked alias re-serves the
|
||||
# canonical name: open-gstack-browser, so one of the two silently
|
||||
# shadows the other (#2201) — and duplicate names can drop the whole
|
||||
# skill set (#2511).
|
||||
_OGB_LINK="$INSTALL_SKILLS_DIR/connect-chrome"
|
||||
_OGB_ALIAS_NAME="connect-chrome"
|
||||
if [ "$SKILL_PREFIX" -eq 1 ]; then
|
||||
_OGB_LINK="$INSTALL_SKILLS_DIR/gstack-connect-chrome"
|
||||
_OGB_ALIAS_NAME="gstack-connect-chrome"
|
||||
fi
|
||||
if [ -L "$_OGB_LINK" ] || [ ! -e "$_OGB_LINK" ]; then
|
||||
_link_or_copy "gstack/open-gstack-browser" "$_OGB_LINK"
|
||||
fi
|
||||
_install_alias_skill_md "$SOURCE_GSTACK_DIR/open-gstack-browser/SKILL.md" "$_OGB_LINK" "$_OGB_ALIAS_NAME"
|
||||
log "gstack ready (claude)."
|
||||
log " browse: $BROWSE_BIN"
|
||||
fi
|
||||
|
||||
Reference in New Issue
Block a user