Commit Graph

3 Commits

Author SHA1 Message Date
jkrperson 1a9970b354 feat(installer): project-only (vendored) install mode
Adds a third install scope: `gstack install --local` vendors gstack into
<cwd>/.claude/skills/gstack/ instead of ~/.claude/skills/gstack/. Surfaces
./setup --local, which upstream deprecated in favor of team mode but still
supports. Guarded with an explicit deprecation notice in both the CLI
output and the interactive wizard confirmation step.

Why expose a deprecated mode:
- Some users genuinely want vendored installs (offline machines, strict
  "one project = one dir" policies, air-gapped CI, forked gstack per repo)
- ./setup already supports it, so the CLI would be lying by omission
- The wizard makes the deprecation cost visible before commit

Changes:
- paths.ts: resolveProjectInstallPaths(dir), findLocalInstall(startDir)
  walking up to find <dir>/.claude/skills/gstack, and a resolveActiveInstall
  helper that returns {paths, mode: "global" | "project-local" | "none"}
  so status/doctor/list/upgrade all detect both install kinds
- install.ts: accepts local + projectDir, routes to project paths, passes
  --local through to ./setup, restricts hosts to claude (matching setup
  behavior), prints deprecation warning
- uninstall.ts: new `gstack uninstall --local` removes the project-local
  install (searches cwd upward), distinct from `--project` which removes
  team-mode config
- wizard.ts: fourth top-level option "Install inside this project only
  (vendored)" with a confirm-before-commit step spelling out the tradeoff
- cli.ts: --local flag on install and uninstall, help text updated
- status: shows "Mode: project-local (vendored)" when applicable, yellow

Tests: +8 (85 total, still green)
- resolveProjectInstallPaths roots correctly
- findLocalInstall at cwd + walks up to parent
- status shows project-local mode when only vendored install exists
- list discovers skills in vendored install
- uninstall --local removes the vendored dir
- uninstall --local exits 1 with clear message when no install present
2026-04-25 03:00:59 +08:00
jkrperson f6299c4d67 feat(installer): clearer wizard labels for install vs team mode
"Add to this project (team mode)" implied team mode replaces the global
install when it actually stacks on top of it. Relabels the wizard so the
relationship is explicit:

- "Install gstack on this machine" (was "Install globally (on this machine)")
- "Enable team mode for this repo" (was "Add to this project (team mode)")
  with hint: "global install + commits team-sync config to this repo so
  teammates auto-update"

Pre-select intro note now says upfront that there are two install modes
and that team mode is a superset of the machine install.

Required/optional tier hints now say what each actually does (PreToolUse
hook block vs CLAUDE.md nudge) instead of vague "block sessions" /
"nudge teammates".

No behavior changes — labels and hint text only. 77 tests still pass.
2026-04-25 02:53:59 +08:00
jkrperson 714e968d69 feat(installer): npx @garrytan/gstack — interactive installer CLI
Adds installer/ — a TypeScript CLI that wraps the existing ./setup bash
script behind a zero-friction `npx` entry point. Replaces the paste-prompt
install flow with an interactive wizard while preserving ./setup as the
source of truth for host registration and symlinks.

Commands:
  install | init | uninstall (--project) | upgrade | doctor
  status | list | enable <skill> | disable <skill>

No-args launches a @clack/prompts wizard that auto-detects git repos +
installed hosts (claude, codex, factory, opencode, kiro), collects
multi-select host + prefix + CLAUDE.md choices, and routes to install or
team-mode init.

The CLI:
- clones garrytan/gstack into ~/.claude/skills/gstack
- shells out to ./setup once per selected host (or --host auto)
- for init, runs ./setup --team + bin/gstack-team-init <required|optional>,
  stages .claude/ + CLAUDE.md
- inserts/updates a fenced <!-- gstack:begin --> block in CLAUDE.md listing
  every discovered skill
- uninstall walks ~/.claude, ~/.codex, ~/.factory, ~/.config/opencode,
  ~/.kiro skills dirs and removes symlinks/dirs pointing into the gstack
  install (canonicalized with fs.realpathSync to handle macOS /var vs
  /private/var), then removes the CLAUDE.md block and scrubs gstack
  PreToolUse hooks from project .claude/settings.json
- preserves ~/.gstack/ session state across uninstalls

Testing:
- 77 tests, bun:test runner, 3s runtime
- Unit: claude-md (11), skills (12), project-config (11), cleanup (12 + a
  /var realpath regression test), paths (9)
- Integration: spawns dist/cli.js against fake HOME fixtures — verifies
  exit codes, enable/disable round-trip + name normalization, uninstall
  scrubs settings.json preserving non-gstack hooks and top-level keys,
  EPIPE handling under `gstack list | head`, wizard intro without TTY

End-to-end dry-run completed via `npm link` and `npm pack` against a real
clone + real build (clone -> browse binary -> Playwright Chromium -> 40
skills linked -> CLAUDE.md written -> doctor green -> uninstall leaves 0
zombies).

Live demo published under @jkresabal/gstack for testing:
  npx @jkresabal/gstack

Upstream publish (after this PR): cd installer && npm publish --access public

Philosophy: thin wrapper. ~2.3K LOC, 16KB packed, two runtime deps
(@clack/prompts + picocolors), Node 18+. No logic duplicated from ./setup.
If ./setup learns a new flag, the CLI surfaces it with one line.
2026-04-25 02:37:27 +08:00