Skip to content

Windows (MSYS/Git Bash): link_claude_skill_dirs creates file copies, not symlinks — skills do not update on git pull #1456

@sarabvaruna-code

Description

@sarabvaruna-code

Summary

On Windows (MSYS2/Git Bash, MINGW64), link_claude_skill_dirs in setup uses ln -snf which silently creates file copies, not symlinks. As a result, every git pull of the gstack repo updates ~/.claude/skills/gstack/<name>/SKILL.md (the source) but leaves the top-level ~/.claude/skills/<name>/SKILL.md (the supposed-symlink) frozen at install-time content. New skills released in upstream get registered (because mkdir -p + new "copy" creation), but updates to existing skills go invisible.

This is silent — setup exits 0, no warning. Users only notice when v1.33.1's task-shaped learnings refresh fails to fire in /investigate / /ship / /qa, or when a skill references a feature that's documented but not present in their installed SKILL.md.

Steps to Reproduce

Minimal repro — no gstack needed, isolates the ln -snf behavior:

D=/tmp/ln-test
mkdir -p "$D"
echo "v1-original" > "$D/src.txt"
ln -snf "$D/src.txt" "$D/link.txt"
file "$D/link.txt"
# Output on Git Bash: /tmp/ln-test/link.txt: ASCII text  (NOT "symbolic link to ...")

echo "v2-UPDATED" > "$D/src.txt"
cat "$D/link.txt"
# Output: v1-original   <-- proves it's a copy, not a symlink

Full gstack repro:

cd ~/.claude/skills/gstack && git pull
./setup --host claude --quiet
ls -la ~/.claude/skills/investigate/SKILL.md
# First char is `-` (regular file), not `l` (symlink),
# despite setup's comment at line 368 promising "SKILL.md symlink inside".

Expected Behavior

Either:

  • Option A: Detect Windows + Developer-Mode-off, fall back to cp -f. State this clearly in setup output ("note: Windows install uses file copies; re-run setup after every git pull").
  • Option B: Set MSYS=winsymlinks:nativestrict for the link step and document that Developer Mode (or admin) is required on Windows.
  • Option C: Use a content-hash check so setup overwrites stale copies even when re-run. Combined with a .last-link-sha marker so users can detect drift.

Same pattern was applied at commit 999987ef ("fix(ci): use hardlink copy instead of symlink for node_modules cache") — precedent for Option A.

Actual Behavior

ln -snf on Git Bash without Developer Mode creates a plain file copy. setup prints linked skills: ... despite no symlinks existing. No exit-code signal, no warning.

Root Cause

setup line 404:

ln -snf "$gstack_dir/$dir_name/SKILL.md" "$target/SKILL.md"

No IS_WINDOWS branch around the link call. The script DOES detect Windows (line 28-30) and uses IS_WINDOWS in 3 other places (lines 208, 332, 354 — Playwright/Node fallbacks) but not here.

The bin/gstack-relink binary likely has the same issue if it uses ln -s — out of scope for this issue but worth a separate check.

Suggested Patch

Replace line 404:

-      ln -snf "$gstack_dir/$dir_name/SKILL.md" "$target/SKILL.md"
+      if [ "$IS_WINDOWS" -eq 1 ]; then
+        cp -f "$gstack_dir/$dir_name/SKILL.md" "$target/SKILL.md"
+      else
+        ln -snf "$gstack_dir/$dir_name/SKILL.md" "$target/SKILL.md"
+      fi

And at end of link_claude_skill_dirs, add a Windows-only note:

if [ "$IS_WINDOWS" -eq 1 ] && [ ${#linked[@]} -gt 0 ]; then
  echo "  note: Windows install uses file copies. Re-run ./setup after every 'git pull' to refresh skill files."
fi

Workaround (for users on current setup)

After every git pull of gstack:

cd ~/.claude/skills/gstack && for d in */; do
  d="${d%/}"
  [ "$d" = "node_modules" ] && continue
  [ ! -f "$d/SKILL.md" ] && continue
  n=$(grep -m1 '^name:' "$d/SKILL.md" 2>/dev/null | sed 's/^name:[[:space:]]*//' | tr -d '[:space:]')
  [ -z "$n" ] && n="$d"
  mkdir -p "$HOME/.claude/skills/$n"
  cp -f "$HOME/.claude/skills/gstack/$d/SKILL.md" "$HOME/.claude/skills/$n/SKILL.md"
done

Environment

  • Windows 11 Home 10.0.26200
  • Git Bash (MINGW64), bash 5.x
  • bun 1.3.13
  • gstack v1.33.2.0 (HEAD dc6252d1)
  • MSYS env var: unset (default)
  • Developer Mode: off

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions