Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions installer/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
node_modules/
dist/
*.tgz
.DS_Store
5 changes: 5 additions & 0 deletions installer/.npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
src/
tsconfig.json
node_modules/
*.tgz
.DS_Store
57 changes: 57 additions & 0 deletions installer/PUBLISHING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# Publishing `@garrytan/gstack`

This installer is designed to be published to the `@garrytan` npm scope. That scope is owned by [@garrytan](https://github.com/garrytan), so merging this PR by itself does not put it on npm — Garry (or a maintainer with scope access) needs to run `npm publish`.

## Steps for Garry / scope owner

```bash
cd installer
npm install
npm run build
npm publish --access public
```

Users can then run:

```bash
npx @garrytan/gstack
```

## Testing before publish (non-scope-owners)

If you want to verify `npx @garrytan/gstack` end-to-end without waiting for a publish, you can temporarily publish under your own npm scope:

1. Change `"name"` in `installer/package.json` to your scope — e.g. `"@your-handle/gstack"`.
2. `npm login`
3. `npm publish --access public`
4. `npx @your-handle/gstack`

Revert the `name` field before merging the PR. **Do not** commit a scope change — the upstream PR should land with `@garrytan/gstack`.

## Version bumps

The installer has its own `version` independent of the main `gstack` `VERSION` file. Bump `installer/package.json` when:

- New command or flag added
- Existing command behavior changes
- A bug fix ships

Follow semver: `0.x.y` while the API is still settling, `1.0.0` when the command surface is stable.

## What the installer does at runtime

The installer is a thin wrapper — it clones `https://github.com/garrytan/gstack.git` into `~/.claude/skills/gstack` and shells out to that repo's `./setup` script. So publishing a new installer version **does not** ship a new gstack — users always get the latest `main` branch of the main repo at install time.

This means the installer rarely needs to change. The main reasons would be:

- Host registry expands (new agent supported by gstack)
- `./setup` learns a new flag the installer wants to surface
- Bug in the installer itself

## CI (future)

Not wired yet. A reasonable path:

- `installer/` gets its own workflow at `.github/workflows/installer-ci.yml`
- Runs `npm install`, `npm run build`, `npm pack` on PRs
- On release tag `installer-v*`, runs `npm publish`
83 changes: 83 additions & 0 deletions installer/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# `@garrytan/gstack` — installer CLI

Interactive installer for [gstack](https://github.com/garrytan/gstack), Garry Tan's Claude Code skill pack and workflow tooling.

## Usage

```bash
# Zero-friction: interactive wizard
npx @garrytan/gstack

# Scripted: verb-based subcommands
npx @garrytan/gstack install --host claude,codex
npx @garrytan/gstack install --local # vendored (deprecated — prefer team mode)
npx @garrytan/gstack init --tier required
npx @garrytan/gstack upgrade
npx @garrytan/gstack uninstall --project --yes
npx @garrytan/gstack uninstall --local --yes # remove vendored project install
npx @garrytan/gstack doctor
npx @garrytan/gstack status
npx @garrytan/gstack list
npx @garrytan/gstack disable /qa
npx @garrytan/gstack enable /qa
```

Works with `npx`, `bunx`, and `pnpm dlx`.

## What it does

**`install`** — clones gstack into `~/.claude/skills/gstack`, builds the browse/design binaries via `bun`, registers with your chosen AI hosts (Claude Code, Codex, Factory Droid, OpenCode, Kiro), and inserts a `<!-- gstack:begin -->` / `<!-- gstack:end -->` block into `~/.claude/CLAUDE.md` documenting the available skills.

**`install --local`** — vendored mode: installs gstack into `<cwd>/.claude/skills/gstack` instead of the home directory. Everything stays inside the project. **Deprecated upstream** in favor of team mode (`init`) because vendoring means no cross-project auto-update and ~100MB duplicated per project. Exposed here because `./setup --local` still supports it. Claude Code only (other hosts skipped).

**`init`** — runs inside a git repo. Installs globally if needed, enables team mode (the SessionStart auto-update hook), runs `gstack-team-init <tier>` to bootstrap the repo, and stages/commits the changes. Teammates get gstack automatically on their next session.

**`uninstall`** — removes the install and walks every host's skills directory (`~/.claude/skills`, `~/.codex/skills`, `~/.factory/skills`, `~/.config/opencode/skills`, `~/.kiro/skills`) removing any symlink or directory whose `SKILL.md` points into the gstack install. Cleans the CLAUDE.md block and scrubs the PreToolUse hook from project `settings.json`. `~/.gstack/` (session state) is preserved.

**`upgrade`** — `git fetch` + hard reset to `origin/main` in `~/.claude/skills/gstack`, then re-runs `./setup --host auto` to rebuild and re-link.

**`doctor`** — checks git, bun, install state, binary freshness, skill count, and per-host registration. Exit code 1 if any check fails.

**`status`** — one-screen summary: version, install path, team mode, auto-upgrade, skill prefix mode, per-host registration, per-project disabled-skills list.

**`list`** — enumerates installed skills with descriptions parsed from each `SKILL.md` frontmatter.

**`enable <skill>` / `disable <skill>`** — toggle skills per-project via `.claude/settings.local.json`'s `disabledSkills` array. Names can be `qa`, `/qa`, or `gstack-qa` — all normalize to the same entry.

## Requirements

- Node.js 18+ (for the installer itself)
- [bun](https://bun.sh/) 1.0+ (for building gstack binaries)
- git
- bash (Windows: Git Bash or WSL)

## Philosophy

The installer is a thin wrapper around gstack's existing [`./setup`](https://github.com/garrytan/gstack/blob/main/setup) bash script — no logic is duplicated. This keeps the installer small, auditable, and guaranteed to stay in sync with upstream. If `setup` learns a new flag, the installer picks it up by exposing a new option.

## Development

```bash
cd installer
npm install
npm run build # compile TS to dist/
npm start -- --help # run the built CLI

# Watch mode
npm run dev

# Smoke test locally
npm link
gstack --help
```

To test without publishing:

```bash
# From anywhere, use the local checkout:
npx /absolute/path/to/gstack/installer install
```

## License

MIT — same as gstack.
127 changes: 127 additions & 0 deletions installer/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

55 changes: 55 additions & 0 deletions installer/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
{
"name": "@garrytan/gstack",
"version": "0.1.0",
"description": "Interactive installer for gstack — Garry Tan's Claude Code skills, hosts, and workflow tooling.",
"license": "MIT",
"type": "module",
"bin": {
"gstack": "./dist/cli.js"
},
"files": [
"dist",
"README.md",
"PUBLISHING.md"
],
"engines": {
"node": ">=18"
},
"scripts": {
"build": "tsc",
"prepublishOnly": "npm run build",
"dev": "tsc --watch",
"start": "node dist/cli.js",
"clean": "rm -rf dist",
"test": "bun test test/",
"test:unit": "bun test test/unit/",
"test:integration": "bun run build && bun test test/integration/",
"pretest": "bun run build"
},
"keywords": [
"gstack",
"claude-code",
"skills",
"installer",
"cli",
"ai-agents"
],
"repository": {
"type": "git",
"url": "git+https://github.com/garrytan/gstack.git",
"directory": "installer"
},
"homepage": "https://github.com/garrytan/gstack#readme",
"bugs": {
"url": "https://github.com/garrytan/gstack/issues"
},
"dependencies": {
"@clack/prompts": "^0.7.0",
"picocolors": "^1.0.1"
},
"devDependencies": {
"@types/bun": "^1.3.13",
"@types/node": "^20.11.0",
"typescript": "^5.4.0"
}
}
Loading