This commit is contained in:
ypaymoore 2026-07-14 19:17:08 -07:00 committed by GitHub
commit da69d1b07d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 29 additions and 14 deletions

View File

@ -335,13 +335,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

View File

@ -467,7 +467,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.

25
setup
View File

@ -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"
@ -222,31 +224,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() {
@ -982,6 +988,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"

View File

@ -2450,10 +2450,13 @@ describe('setup script validation', () => {
expect(fnBody).not.toContain('_link_or_copy "$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) ---