From 54284df62c7ce3e65f602ffa9f7f7d3936b4c41a Mon Sep 17 00:00:00 2001 From: ec Date: Mon, 20 Apr 2026 18:05:27 +0800 Subject: [PATCH] fix: centralize gstack host installs --- CONTRIBUTING.md | 7 ++++++- README.md | 2 +- setup | 25 ++++++++++++++++--------- test/gen-skill-docs.test.ts | 9 ++++++--- 4 files changed, 29 insertions(+), 14 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 523887510..345d35e1e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -280,13 +280,18 @@ Each host config (`hosts/*.ts`) controls: |--------|---------------------------| | Output directory | `{skill}/SKILL.md` vs `.agents/skills/gstack-{skill}/SKILL.md` | | Frontmatter | Full (name, description, hooks, version) vs minimal (name + description) | -| Paths | `~/.claude/skills/gstack` vs `$GSTACK_ROOT` | +| Paths | `~/.claude/skills/gstack` vs `$GSTACK_ROOT` (setup may migrate the source checkout to `~/.gstack/repos/gstack` and recreate host runtime links) | | Tool names | "use the Bash tool" vs same (Factory rewrites to "run this command") | | Hook skills | `hooks:` frontmatter vs inline safety advisory prose | | Suppressed sections | None vs Codex self-invocation sections stripped | See `scripts/host-config.ts` for the full `HostConfig` interface. +Global installs now use one canonical source checkout at `~/.gstack/repos/gstack` +whenever setup detects that gstack was cloned directly into a host discovery path +such as `~/.claude/skills/gstack`, `~/.codex/skills/gstack`, +`~/.factory/skills/gstack`, or `~/.config/opencode/skills/gstack`. + ### Testing host output ```bash diff --git a/README.md b/README.md index de28bbc65..a2ad1fb3b 100644 --- a/README.md +++ b/README.md @@ -394,7 +394,7 @@ Data is stored in [Supabase](https://supabase.com) (open source Firebase alterna **Want namespaced commands?** `cd ~/.claude/skills/gstack && ./setup --prefix` — switches from `/qa` to `/gstack-qa`. Useful if you run other skill packs alongside gstack. -**Codex says "Skipped loading skill(s) due to invalid SKILL.md"?** Your Codex skill descriptions are stale. Fix: `cd ~/.codex/skills/gstack && git pull && ./setup --host codex` — or for repo-local installs: `cd "$(readlink -f .agents/skills/gstack)" && git pull && ./setup --host codex` +**Codex says "Skipped loading skill(s) due to invalid SKILL.md"?** Your Codex skill descriptions are stale. Fix: update the canonical source repo and rerun `./setup --host codex`. If setup migrated an old direct host-root install, the source repo now lives at `~/.gstack/repos/gstack`. **Windows users:** gstack works on Windows 11 via Git Bash or WSL. Node.js is required in addition to Bun — Bun has a known bug with Playwright's pipe transport on Windows ([bun#4253](https://github.com/oven-sh/bun/issues/4253)). The browse server automatically falls back to Node.js. Make sure both `bun` and `node` are on your PATH. diff --git a/setup b/setup index 4c1763f9f..3bef7f823 100755 --- a/setup +++ b/setup @@ -18,6 +18,8 @@ INSTALL_GSTACK_DIR="$(cd "$(dirname "$0")" && pwd)" SOURCE_GSTACK_DIR="$(cd "$(dirname "$0")" && pwd -P)" INSTALL_SKILLS_DIR="$(dirname "$INSTALL_GSTACK_DIR")" BROWSE_BIN="$SOURCE_GSTACK_DIR/browse/dist/browse" +CLAUDE_SKILLS="$HOME/.claude/skills" +CLAUDE_GSTACK="$CLAUDE_SKILLS/gstack" CODEX_SKILLS="$HOME/.codex/skills" CODEX_GSTACK="$CODEX_SKILLS/gstack" FACTORY_SKILLS="$HOME/.factory/skills" @@ -177,31 +179,35 @@ elif [ "$HOST" = "opencode" ]; then INSTALL_OPENCODE=1 fi -migrate_direct_codex_install() { +migrate_direct_global_install() { local gstack_dir="$1" - local codex_gstack="$2" local migrated_dir="$HOME/.gstack/repos/gstack" + local source_kind="" - [ "$gstack_dir" = "$codex_gstack" ] || return 0 + case "$gstack_dir" in + "$CLAUDE_GSTACK") source_kind="Claude" ;; + "$CODEX_GSTACK") source_kind="Codex" ;; + "$FACTORY_GSTACK") source_kind="Factory" ;; + "$OPENCODE_GSTACK") source_kind="OpenCode" ;; + *) return 0 ;; + esac [ -L "$gstack_dir" ] && return 0 mkdir -p "$(dirname "$migrated_dir")" if [ -e "$migrated_dir" ] && [ "$migrated_dir" != "$gstack_dir" ]; then - echo "gstack setup failed: direct Codex install detected at $gstack_dir" >&2 + echo "gstack setup failed: direct $source_kind install detected at $gstack_dir" >&2 echo "A migrated repo already exists at $migrated_dir; move one of them aside and rerun setup." >&2 exit 1 fi - log "Migrating direct Codex install to $migrated_dir to avoid duplicate skill discovery..." + log "Migrating direct $source_kind install to $migrated_dir so all hosts share one source repo..." mv "$gstack_dir" "$migrated_dir" SOURCE_GSTACK_DIR="$migrated_dir" - INSTALL_GSTACK_DIR="$migrated_dir" - INSTALL_SKILLS_DIR="$(dirname "$INSTALL_GSTACK_DIR")" BROWSE_BIN="$SOURCE_GSTACK_DIR/browse/dist/browse" } -if [ "$INSTALL_CODEX" -eq 1 ]; then - migrate_direct_codex_install "$SOURCE_GSTACK_DIR" "$CODEX_GSTACK" +if [ "$LOCAL_INSTALL" -eq 0 ]; then + migrate_direct_global_install "$SOURCE_GSTACK_DIR" fi ensure_playwright_browser() { @@ -773,6 +779,7 @@ fi if [ "$INSTALL_CLAUDE" -eq 1 ]; then if [ "$SKILLS_BASENAME" = "skills" ]; then + ln -snf "$SOURCE_GSTACK_DIR" "$CLAUDE_GSTACK" # Clean up stale symlinks from the opposite prefix mode if [ "$SKILL_PREFIX" -eq 1 ]; then cleanup_old_claude_symlinks "$SOURCE_GSTACK_DIR" "$INSTALL_SKILLS_DIR" diff --git a/test/gen-skill-docs.test.ts b/test/gen-skill-docs.test.ts index 1895db254..2a5d5aec1 100644 --- a/test/gen-skill-docs.test.ts +++ b/test/gen-skill-docs.test.ts @@ -2203,10 +2203,13 @@ describe('setup script validation', () => { expect(fnBody).not.toContain('ln -snf "$gstack_dir" "$codex_gstack"'); }); - test('direct Codex installs are migrated out of ~/.codex/skills/gstack', () => { - expect(setupContent).toContain('migrate_direct_codex_install'); + test('direct global host-root installs are migrated into one shared repo', () => { + expect(setupContent).toContain('migrate_direct_global_install'); expect(setupContent).toContain('$HOME/.gstack/repos/gstack'); - expect(setupContent).toContain('avoid duplicate skill discovery'); + expect(setupContent).toContain('$CLAUDE_GSTACK'); + expect(setupContent).toContain('$CODEX_GSTACK'); + expect(setupContent).toContain('$FACTORY_GSTACK'); + expect(setupContent).toContain('$OPENCODE_GSTACK'); }); // --- Symlink prefix tests (PR #503) ---