mirror of
https://github.com/garrytan/gstack.git
synced 2026-05-02 03:35:09 +02:00
feat: add local dev mode for testing skills from within the repo
bin/dev-setup creates .claude/skills/gstack symlink to the working tree so Claude Code discovers skills locally. bin/dev-teardown cleans up. DEVELOPING_GSTACK.md documents the workflow. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
node_modules/
|
||||
browse/dist/
|
||||
.gstack/
|
||||
.claude/
|
||||
/tmp/
|
||||
*.log
|
||||
bun.lock
|
||||
|
||||
@@ -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 <cmd> # 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.
|
||||
Executable
+36
@@ -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"
|
||||
Executable
+39
@@ -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."
|
||||
Reference in New Issue
Block a user