Skip to content

gstack-uninstall leaves orphaned ~/.claude/skills/gstack-* directories (prefix-mode install) #896

Closed

Description

@xiaodream551-a11y

Summary

gstack-uninstall removes ~/.claude/skills/gstack/ but leaves all the per-skill ~/.claude/skills/gstack-* directories behind as empty shells containing dangling SKILL.md symlinks. This happens when gstack is installed in "prefix" mode (where each skill lives in its own directory rather than as a top-level symlink).

Environment

  • macOS 15.4 (Darwin 25.4.0), Apple Silicon
  • gstack installed via prefix-mode setup at ~/.claude/skills/gstack
  • 36 per-skill directories: gstack-browse, gstack-ship, gstack-plan-ceo-review, etc.

Repro

  1. Install gstack in prefix mode. The installer creates:
    ~/.claude/skills/gstack/                 (real directory, the toolkit)
    ~/.claude/skills/gstack-browse/          (real directory, NOT a symlink)
    ~/.claude/skills/gstack-browse/SKILL.md  (symlink → ../gstack/browse/SKILL.md)
    ~/.claude/skills/gstack-ship/            (real directory, NOT a symlink)
    ~/.claude/skills/gstack-ship/SKILL.md    (symlink → ../gstack/ship/SKILL.md)
    ... 36 total
    
  2. Run ~/.claude/skills/gstack/bin/gstack-uninstall --force
  3. Output:
    Removed: ~/.claude/skills/gstack /Users/<me>/.gstack
    gstack uninstalled.
    
  4. Inspect:
    $ ls ~/.claude/skills/ | grep gstack
    gstack-autoplan
    gstack-benchmark
    gstack-browse
    ... (35 leftover directories)
    
    $ ls -la ~/.claude/skills/gstack-browse
    SKILL.md -> /Users/<me>/.claude/skills/gstack/browse/SKILL.md   (target gone)
    

Expected

All ~/.claude/skills/gstack-* directories created by the installer should be removed, matching the script header docs:

~/.claude/skills/{skill}      — per-skill symlinks created by setup

Actual

35 orphaned directories remain. Each contains a single dangling SKILL.md symlink. They show up in Claude Code's skill list at session start until manually rm -rf'd.

Root cause

bin/gstack-uninstall lines 135-143:

for _LINK in "$CLAUDE_SKILLS"/*; do
  [ -L "$_LINK" ] || continue            # ← bails out for prefix-mode dirs
  _NAME="$(basename "$_LINK")"
  [ "$_NAME" = "gstack" ] && continue
  _TARGET="$(readlink "$_LINK" 2>/dev/null || true)"
  case "$_TARGET" in
    gstack/*|*/gstack/*) rm -f "$_LINK"; REMOVED+=("claude/$_NAME") ;;
  esac
done

The loop only handles entries where ~/.claude/skills/<entry> is itself a symlink. In prefix-mode, each gstack-<name> is a regular directory whose inner SKILL.md is the symlink, so [ -L "$_LINK" ] || continue skips them on the outer pass and the inner symlink is never inspected.

Suggested fix

After the existing loop, add a second pass that handles prefix-mode directories — match gstack-* directories whose SKILL.md is a symlink pointing into gstack/:

for _DIR in "$CLAUDE_SKILLS"/gstack-*; do
  [ -d "$_DIR" ] || continue
  [ -L "$_DIR/SKILL.md" ] || continue
  _TARGET="$(readlink "$_DIR/SKILL.md" 2>/dev/null || true)"
  case "$_TARGET" in
    *gstack/*) rm -rf "$_DIR"; REMOVED+=("claude/$(basename "$_DIR")") ;;
  esac
done

Workaround

rm -rf ~/.claude/skills/gstack-*

Activity

  1. added a commit that references this issue on Apr 8, 2026
    a976540
  2. mvanhorn commented on Apr 8, 2026

    @mvanhorn
    Contributor

    Implemented in #902 - adds glob-based cleanup for prefix-mode gstack-* directories in the Claude skills section, matching the pattern already used for Codex/Factory/Kiro hosts.

  3. garrytan commented on Apr 16, 2026

    @garrytan
    Owner

    Closing — fixed in the current bin/gstack-uninstall script, which now comprehensively removes all host directories (claude, codex, factory, kiro, sidecar) and gstack-* prefixed dirs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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