diff --git a/.gitignore b/.gitignore index ef0aeaf5..5bfd6a1b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ node_modules/ browse/dist/ .gstack/ +.claude/ /tmp/ *.log bun.lock diff --git a/DEVELOPING_GSTACK.md b/DEVELOPING_GSTACK.md new file mode 100644 index 00000000..e4951130 --- /dev/null +++ b/DEVELOPING_GSTACK.md @@ -0,0 +1,93 @@ +# Developing gstack + +How to test gstack skills from within the gstack repo itself. + +## The problem + +Claude Code discovers skills from `.claude/skills/` (project-local) or `~/.claude/skills/` (global). When developing gstack, you want to edit a SKILL.md and test it immediately — without copying files or deploying to the global install. + +## Dev mode + +```bash +bin/dev-setup # activate — skills resolve from this working tree +bin/dev-teardown # deactivate — back to global install +``` + +### What `bin/dev-setup` does + +1. Creates `.claude/skills/` inside the repo (gitignored) +2. Symlinks `.claude/skills/gstack` → repo root +3. Runs `./setup` which: + - Builds the browse binary (if needed) + - Creates individual skill symlinks: `.claude/skills/review` → `.claude/skills/gstack/review`, etc. + +After this, Claude Code in this directory discovers skills from your working tree. Edit `review/SKILL.md`, run `/review` — changes take effect immediately. + +### What `bin/dev-teardown` does + +Removes all symlinks under `.claude/skills/` and cleans up the directory. Your global install (`~/.claude/skills/gstack`) becomes active again. + +## Directory structure in dev mode + +``` +gstack/ ← your working tree (repo root) +├── .claude/ ← gitignored +│ └── skills/ +│ ├── gstack → ../../ ← symlink back to repo root +│ ├── review → gstack/review +│ ├── ship → gstack/ship +│ ├── browse → gstack/browse +│ ├── qa → gstack/qa +│ ├── retro → gstack/retro +│ ├── plan-ceo-review → gstack/plan-ceo-review +│ ├── plan-eng-review → gstack/plan-eng-review +│ └── setup-browser-cookies → gstack/setup-browser-cookies +├── review/ +│ ├── SKILL.md ← edit this, test with /review +│ ├── checklist.md +│ └── greptile-triage.md +├── ship/ +│ └── SKILL.md +├── browse/ +│ ├── SKILL.md +│ ├── src/ ← TypeScript source +│ └── dist/ ← compiled binary (gitignored) +└── ... +``` + +## Workflow + +```bash +# 1. Start dev mode +bin/dev-setup + +# 2. Edit a skill +vim review/SKILL.md + +# 3. Test it — Claude Code picks up changes immediately +# (in Claude Code): /review + +# 4. Edit browse source? Rebuild the binary +bun run build + +# 5. Done developing? Tear down +bin/dev-teardown +``` + +## Gotchas + +- **Project-local skills override global.** While dev mode is active, the global install at `~/.claude/skills/gstack` is shadowed. `bin/dev-teardown` restores it. +- **Browse binary changes need a rebuild.** SKILL.md changes are instant (they're just Markdown). But if you edit `browse/src/*.ts`, run `bun run build` to recompile. +- **`.claude/` is gitignored.** The dev symlinks never get committed. This is intentional. +- **Conductor workspaces.** Each Conductor workspace is an independent clone. Run `bin/dev-setup` in the workspace you're developing in. Other workspaces are unaffected. +- **Don't mix dev and global.** If you have dev mode active and also update the global install, the project-local one wins. Tear down first if you want to test the global install. + +## Running tests + +```bash +bun test # all tests (browse integration + snapshot) +bun run dev # run CLI in dev mode, e.g. bun run dev goto https://example.com +bun run build # compile binary to browse/dist/browse +``` + +Tests don't require dev mode — they test the browse binary directly, not the skill prompts. diff --git a/bin/dev-setup b/bin/dev-setup new file mode 100755 index 00000000..709cca4d --- /dev/null +++ b/bin/dev-setup @@ -0,0 +1,36 @@ +#!/usr/bin/env bash +# Set up gstack for local development — test skills from within this repo. +# +# Creates .claude/skills/gstack → (symlink to repo root) so Claude Code +# discovers skills from your working tree. Changes take effect immediately. +# +# Usage: bin/dev-setup # set up +# bin/dev-teardown # clean up +set -e + +REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)" + +# 1. Create .claude/skills/ inside the repo +mkdir -p "$REPO_ROOT/.claude/skills" + +# 2. Symlink .claude/skills/gstack → repo root +# This makes setup think it's inside a real .claude/skills/ directory +GSTACK_LINK="$REPO_ROOT/.claude/skills/gstack" +if [ -L "$GSTACK_LINK" ]; then + echo "Updating existing symlink..." + rm "$GSTACK_LINK" +elif [ -d "$GSTACK_LINK" ]; then + echo "Error: .claude/skills/gstack is a real directory, not a symlink." >&2 + echo "Remove it manually if you want to use dev mode." >&2 + exit 1 +fi +ln -s "$REPO_ROOT" "$GSTACK_LINK" + +# 3. Run setup via the symlink so it detects .claude/skills/ as its parent +"$GSTACK_LINK/setup" + +echo "" +echo "Dev mode active. Skills resolve from this working tree." +echo "Edit any SKILL.md and test immediately — no copy/deploy needed." +echo "" +echo "To tear down: bin/dev-teardown" diff --git a/bin/dev-teardown b/bin/dev-teardown new file mode 100755 index 00000000..e333a75d --- /dev/null +++ b/bin/dev-teardown @@ -0,0 +1,39 @@ +#!/usr/bin/env bash +# Remove local dev skill symlinks. Restores global gstack as the active install. +set -e + +REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)" +SKILLS_DIR="$REPO_ROOT/.claude/skills" + +if [ ! -d "$SKILLS_DIR" ]; then + echo "Nothing to tear down — .claude/skills/ doesn't exist." + exit 0 +fi + +# Remove individual skill symlinks +removed=() +for link in "$SKILLS_DIR"/*/; do + name="$(basename "$link")" + [ "$name" = "gstack" ] && continue + if [ -L "${link%/}" ]; then + rm "${link%/}" + removed+=("$name") + fi +done + +# Remove the gstack symlink +if [ -L "$SKILLS_DIR/gstack" ]; then + rm "$SKILLS_DIR/gstack" + removed+=("gstack") +fi + +# Clean up empty dirs +rmdir "$SKILLS_DIR" 2>/dev/null || true +rmdir "$REPO_ROOT/.claude" 2>/dev/null || true + +if [ ${#removed[@]} -gt 0 ]; then + echo "Removed: ${removed[*]}" +else + echo "No symlinks found." +fi +echo "Dev mode deactivated. Global gstack (~/.claude/skills/gstack) is now active."