gw-cli 0.12.10__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. gw_cli-0.12.10/.github/workflows/ci.yml +27 -0
  2. gw_cli-0.12.10/.github/workflows/release.yml +86 -0
  3. gw_cli-0.12.10/.gitignore +14 -0
  4. gw_cli-0.12.10/.python-version +1 -0
  5. gw_cli-0.12.10/CLAUDE.md +62 -0
  6. gw_cli-0.12.10/Justfile +66 -0
  7. gw_cli-0.12.10/PKG-INFO +194 -0
  8. gw_cli-0.12.10/README.md +182 -0
  9. gw_cli-0.12.10/TODO.md +71 -0
  10. gw_cli-0.12.10/assets/logo-light.png +0 -0
  11. gw_cli-0.12.10/assets/social-preview.png +0 -0
  12. gw_cli-0.12.10/pyproject.toml +40 -0
  13. gw_cli-0.12.10/release_notes.md +9 -0
  14. gw_cli-0.12.10/scripts/generate_formula.py +101 -0
  15. gw_cli-0.12.10/shell/grove.sh +37 -0
  16. gw_cli-0.12.10/specs/binary-build.md +95 -0
  17. gw_cli-0.12.10/specs/dash-kanban.md +356 -0
  18. gw_cli-0.12.10/specs/dashboard-todo.md +21 -0
  19. gw_cli-0.12.10/src/grove/__init__.py +17 -0
  20. gw_cli-0.12.10/src/grove/__main__.py +5 -0
  21. gw_cli-0.12.10/src/grove/claude.py +149 -0
  22. gw_cli-0.12.10/src/grove/cli.py +1403 -0
  23. gw_cli-0.12.10/src/grove/config.py +95 -0
  24. gw_cli-0.12.10/src/grove/console.py +33 -0
  25. gw_cli-0.12.10/src/grove/dash/__init__.py +1 -0
  26. gw_cli-0.12.10/src/grove/dash/__main__.py +5 -0
  27. gw_cli-0.12.10/src/grove/dash/app.py +302 -0
  28. gw_cli-0.12.10/src/grove/dash/constants.py +70 -0
  29. gw_cli-0.12.10/src/grove/dash/hook.py +348 -0
  30. gw_cli-0.12.10/src/grove/dash/installer.py +196 -0
  31. gw_cli-0.12.10/src/grove/dash/manager.py +118 -0
  32. gw_cli-0.12.10/src/grove/dash/models.py +261 -0
  33. gw_cli-0.12.10/src/grove/dash/widgets/__init__.py +1 -0
  34. gw_cli-0.12.10/src/grove/dash/widgets/agent_detail.py +135 -0
  35. gw_cli-0.12.10/src/grove/dash/widgets/header_bar.py +68 -0
  36. gw_cli-0.12.10/src/grove/dash/widgets/kanban_board.py +139 -0
  37. gw_cli-0.12.10/src/grove/dash/widgets/kanban_column.py +90 -0
  38. gw_cli-0.12.10/src/grove/dash/widgets/session_list.py +122 -0
  39. gw_cli-0.12.10/src/grove/dash/widgets/task_card.py +182 -0
  40. gw_cli-0.12.10/src/grove/discover.py +279 -0
  41. gw_cli-0.12.10/src/grove/git.py +243 -0
  42. gw_cli-0.12.10/src/grove/log.py +68 -0
  43. gw_cli-0.12.10/src/grove/models.py +104 -0
  44. gw_cli-0.12.10/src/grove/py.typed +0 -0
  45. gw_cli-0.12.10/src/grove/state.py +99 -0
  46. gw_cli-0.12.10/src/grove/stats.py +310 -0
  47. gw_cli-0.12.10/src/grove/tui.py +392 -0
  48. gw_cli-0.12.10/src/grove/update.py +62 -0
  49. gw_cli-0.12.10/src/grove/workspace.py +935 -0
  50. gw_cli-0.12.10/src/grove/zellij.py +294 -0
  51. gw_cli-0.12.10/tests/__init__.py +0 -0
  52. gw_cli-0.12.10/tests/conftest.py +101 -0
  53. gw_cli-0.12.10/tests/test_claude.py +264 -0
  54. gw_cli-0.12.10/tests/test_cli.py +1185 -0
  55. gw_cli-0.12.10/tests/test_config.py +132 -0
  56. gw_cli-0.12.10/tests/test_dash_hook.py +261 -0
  57. gw_cli-0.12.10/tests/test_dash_installer.py +137 -0
  58. gw_cli-0.12.10/tests/test_dash_models.py +185 -0
  59. gw_cli-0.12.10/tests/test_discover.py +254 -0
  60. gw_cli-0.12.10/tests/test_git.py +253 -0
  61. gw_cli-0.12.10/tests/test_models.py +98 -0
  62. gw_cli-0.12.10/tests/test_state.py +90 -0
  63. gw_cli-0.12.10/tests/test_stats.py +237 -0
  64. gw_cli-0.12.10/tests/test_tui.py +107 -0
  65. gw_cli-0.12.10/tests/test_workspace.py +1426 -0
  66. gw_cli-0.12.10/uv.lock +322 -0
@@ -0,0 +1,27 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [master]
6
+ pull_request:
7
+
8
+ jobs:
9
+ check:
10
+ runs-on: ubuntu-latest
11
+ steps:
12
+ - uses: actions/checkout@v4
13
+
14
+ - uses: astral-sh/setup-uv@v5
15
+ with:
16
+ enable-cache: true
17
+
18
+ - run: uv sync --dev
19
+
20
+ - name: Lint
21
+ run: uv run ruff check src/ tests/
22
+
23
+ - name: Format
24
+ run: uv run ruff format --check src/ tests/
25
+
26
+ - name: Test
27
+ run: uv run pytest tests/ -v
@@ -0,0 +1,86 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*"
7
+
8
+ permissions:
9
+ contents: write
10
+ id-token: write
11
+
12
+ jobs:
13
+ release:
14
+ runs-on: ubuntu-latest
15
+ steps:
16
+ - uses: actions/checkout@v4
17
+
18
+ - name: Extract version from tag
19
+ id: version
20
+ run: echo "version=${GITHUB_REF_NAME#v}" >> "$GITHUB_OUTPUT"
21
+
22
+ - name: Verify version matches pyproject.toml
23
+ run: |
24
+ project_version=$(python3 -c "
25
+ import re
26
+ with open('pyproject.toml') as f:
27
+ match = re.search(r'version\s*=\s*\"(.+?)\"', f.read())
28
+ print(match.group(1))
29
+ ")
30
+ if [ "$project_version" != "${{ steps.version.outputs.version }}" ]; then
31
+ echo "::error::Tag version (${{ steps.version.outputs.version }}) does not match pyproject.toml version ($project_version)"
32
+ exit 1
33
+ fi
34
+
35
+ - name: Create GitHub Release
36
+ run: |
37
+ if [ -f release_notes.md ]; then
38
+ gh release create "$GITHUB_REF_NAME" \
39
+ --title "Grove ${{ steps.version.outputs.version }}" \
40
+ --notes-file release_notes.md
41
+ else
42
+ gh release create "$GITHUB_REF_NAME" \
43
+ --title "Grove ${{ steps.version.outputs.version }}" \
44
+ --generate-notes
45
+ fi
46
+ env:
47
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
48
+
49
+ - name: Set up Python
50
+ uses: actions/setup-python@v5
51
+ with:
52
+ python-version: "3.12"
53
+
54
+ - name: Build and publish to PyPI
55
+ run: |
56
+ pip install build
57
+ python -m build
58
+ - name: Publish to PyPI
59
+ uses: pypa/gh-action-pypi-publish@release/v1
60
+
61
+ - name: Update Homebrew tap
62
+ env:
63
+ TAP_GITHUB_TOKEN: ${{ secrets.TAP_GITHUB_TOKEN }}
64
+ run: |
65
+ # Install grove to resolve all dependencies
66
+ pip install .
67
+
68
+ # Compute SHA256 of the release tarball
69
+ tarball_url="https://github.com/${{ github.repository }}/archive/refs/tags/${GITHUB_REF_NAME}.tar.gz"
70
+ sha256=$(curl -sL "$tarball_url" | sha256sum | awk '{print $1}')
71
+
72
+ # Generate the full formula from installed packages
73
+ python3 scripts/generate_formula.py "$tarball_url" "$sha256" > /tmp/grove.rb
74
+
75
+ # Clone the tap repo and update
76
+ git clone "https://x-access-token:${TAP_GITHUB_TOKEN}@github.com/nicksenap/homebrew-grove.git" tap
77
+ cd tap
78
+ cp /tmp/grove.rb Formula/grove.rb
79
+
80
+ # Commit and push
81
+ git config user.name "github-actions[bot]"
82
+ git config user.email "github-actions[bot]@users.noreply.github.com"
83
+ git add Formula/grove.rb
84
+ git diff --cached --quiet && exit 0
85
+ git commit -m "grove ${{ steps.version.outputs.version }}"
86
+ git push
@@ -0,0 +1,14 @@
1
+ # Python-generated files
2
+ __pycache__/
3
+ *.py[oc]
4
+ build/
5
+ dist/
6
+ wheels/
7
+ *.egg-info
8
+
9
+ # Virtual environments
10
+ .venv
11
+
12
+ # Tool caches
13
+ .pytest_cache/
14
+ .ruff_cache/
@@ -0,0 +1 @@
1
+ 3.14
@@ -0,0 +1,62 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+ ## What is Grove?
6
+
7
+ Git Worktree Workspace Orchestrator — CLI tool invoked as `gw`. Manages multi-repo worktree-based workspaces so developers can spin up isolated branches across several repos at once.
8
+
9
+ ## Development
10
+
11
+ - Python 3.12+, managed with `uv`
12
+ - Run `just check` for lint + format + tests
13
+ - Run `just dev` for editable install
14
+ - Run `just install` to install globally via uv tool
15
+ - Run a single test: `uv run pytest tests/test_workspace.py::test_name -v`
16
+ - Auto-fix lint: `just fix` / auto-format: `just fmt`
17
+ - Linter: ruff (line-length 100, rules: E, F, I, N, UP, B, SIM)
18
+
19
+ ## Release Process
20
+
21
+ 1. Bump version in `pyproject.toml`
22
+ 2. Run `uv lock` to update the lockfile
23
+ 3. Optionally write release notes in `release_notes.md` (root of repo)
24
+ 4. Commit everything
25
+ 5. Tag + push: `just release X.Y.Z`
26
+ - Validates version in pyproject.toml matches
27
+ - Creates annotated tag `vX.Y.Z`
28
+ - Pushes tag to origin (triggers release workflow)
29
+ - Workflow uses `release_notes.md` if present, otherwise auto-generates
30
+ - Workflow auto-generates Homebrew formula with all Python resources
31
+
32
+ ## Per-repo config
33
+
34
+ Repos managed by Grove can have a `.grove.toml` at their root:
35
+ - `base_branch` — override the default branch for new worktrees (e.g. `stage`)
36
+ - `setup` — command(s) to run after worktree creation (string or list of strings)
37
+
38
+ ## Architecture
39
+
40
+ All source lives in `src/grove/`. Entry point: `gw` → `grove.cli:app` (Typer).
41
+
42
+ ### Data flow
43
+
44
+ `cli.py` → `workspace.py` → `git.py` (subprocess) + `state.py` (JSON persistence) + `config.py` (TOML)
45
+
46
+ - **cli.py** — Typer commands and interactive pickers (simple-term-menu). Orchestrates user interaction.
47
+ - **workspace.py** — Core worktree orchestration (create, delete, status). Uses `_parallel()` for concurrent multi-repo operations via ThreadPoolExecutor.
48
+ - **git.py** — Thin wrappers around `git` subprocess calls. Raises `GitError` on failure. Includes `read_grove_config()` (LRU-cached — tests must clear it).
49
+ - **state.py** — Workspace state persisted to `~/.grove/state.json`. Uses atomic writes.
50
+ - **config.py** — Global config from `~/.grove/config.toml`. Defines `GROVE_DIR`, `CONFIG_PATH`, `DEFAULT_WORKSPACE_DIR` constants (patched in tests).
51
+ - **models.py** — Pure dataclasses: `Config`, `Workspace`, `RepoWorktree` with `to_dict`/`from_dict` serialization.
52
+ - **discover.py** — Finds git repos in configured directories. Caches remote URLs on disk (`~/.grove/cache/remotes.json`, 24h TTL).
53
+ - **tui.py** — Textual TUI for running workspace processes with sidebar + log pane.
54
+ - **claude.py** — Syncs Claude Code memory directories between source repos and worktrees.
55
+ - **console.py** — Rich output helpers (`success`, `error`, `info`, `warning`, `make_table`).
56
+ - **update.py** — Non-blocking version check (cached, background refresh).
57
+
58
+ ### Testing patterns
59
+
60
+ - Tests use `tmp_grove` fixture (from `conftest.py`) which patches `GROVE_DIR`, `CONFIG_PATH`, `DEFAULT_WORKSPACE_DIR`, and `STATE_PATH` to temp directories.
61
+ - `fake_repos` fixture creates mock repo directories with `.git` dirs (not real git repos).
62
+ - The `read_grove_config` LRU cache is auto-cleared between tests via autouse fixture.
@@ -0,0 +1,66 @@
1
+ set dotenv-load := false
2
+
3
+ # List available recipes
4
+ default:
5
+ @just --list
6
+
7
+ # Run all checks (lint + format + tests)
8
+ check: lint fmt-check test
9
+
10
+ # Run ruff linter
11
+ lint:
12
+ uv run ruff check src/ tests/
13
+
14
+ # Auto-fix lint errors
15
+ fix:
16
+ uv run ruff check --fix src/ tests/
17
+
18
+ # Check formatting
19
+ fmt-check:
20
+ uv run ruff format --check src/ tests/
21
+
22
+ # Auto-format code
23
+ fmt:
24
+ uv run ruff format src/ tests/
25
+
26
+ # Run tests
27
+ test *args:
28
+ uv run pytest tests/ {{ args }}
29
+
30
+ # Run tests with verbose output
31
+ test-v *args:
32
+ uv run pytest tests/ -v {{ args }}
33
+
34
+ # Install gw as editable for development (local venv only)
35
+ dev:
36
+ uv pip install -e .
37
+
38
+ # Install gw globally, linked to local source (changes reflect immediately)
39
+ dev-global:
40
+ uv tool install --editable . --force --reinstall
41
+
42
+ # Switch back to Homebrew-installed gw
43
+ undev:
44
+ -uv tool uninstall grove
45
+ @echo "Homebrew gw is now active (if installed)"
46
+
47
+ # Install gw as a uv tool (globally)
48
+ install:
49
+ uv tool install . --force --reinstall
50
+
51
+ # Reinstall and reload shell integration
52
+ reload: install
53
+ @echo 'Run: eval "$(gw shell-init)"'
54
+
55
+ # Tag a new release (usage: just release 0.4.0)
56
+ release version:
57
+ #!/usr/bin/env bash
58
+ set -euo pipefail
59
+ current=$(python3 -c "import re; f=open('pyproject.toml').read(); print(re.search(r'version\s*=\s*\"(.+?)\"', f).group(1))")
60
+ if [ "$current" != "{{ version }}" ]; then
61
+ echo "Error: pyproject.toml version ($current) does not match {{ version }}"
62
+ echo "Update pyproject.toml first, then run this again."
63
+ exit 1
64
+ fi
65
+ git tag -a "v{{ version }}" -m "Release {{ version }}"
66
+ git push origin "v{{ version }}"
@@ -0,0 +1,194 @@
1
+ Metadata-Version: 2.4
2
+ Name: gw-cli
3
+ Version: 0.12.10
4
+ Summary: Git Worktree Workspace Orchestrator
5
+ Author-email: Nick Song <nick.song@footway.com>
6
+ Requires-Python: >=3.12
7
+ Requires-Dist: rich>=13.0
8
+ Requires-Dist: simple-term-menu>=1.6.6
9
+ Requires-Dist: textual>=1.0
10
+ Requires-Dist: typer>=0.12
11
+ Description-Content-Type: text/markdown
12
+
13
+ <p align="center">
14
+ <img src="assets/logo-light.png" alt="Grove logo" width="120">
15
+ </p>
16
+
17
+ <h1 align="center">Grove (<code>gw</code>)</h1>
18
+
19
+ <p align="center"><b>grove</b> /ɡrōv/ <i>noun</i> — a small group of trees growing together.</p>
20
+
21
+ ## Why?
22
+
23
+ Monorepos solve cross-project work, but not everyone has one. You've got separate repos, separate CI, separate deploys — and that's fine until you need to work across them.
24
+
25
+ One feature across three services means `git worktree add` three times, tracking three branches, jumping between three directories, cleaning up three worktrees when you're done. It's annoying.
26
+
27
+ Grove gives you the multi-repo worktree workflow that monorepos get for free. One command, one workspace, all repos on the same branch.
28
+
29
+ ## Install
30
+
31
+ ### Homebrew
32
+
33
+ ```bash
34
+ brew tap nicksenap/grove
35
+ brew install grove
36
+ ```
37
+
38
+ ### PyPI
39
+
40
+ ```bash
41
+ pipx install gw-cli
42
+ # or
43
+ pip install gw-cli
44
+ ```
45
+
46
+ ### From source
47
+
48
+ ```bash
49
+ uv tool install .
50
+ ```
51
+
52
+ Then add shell integration to your `.zshrc` (or `.bashrc`):
53
+
54
+ ```bash
55
+ eval "$(gw shell-init)"
56
+ ```
57
+
58
+ This enables `gw go` to change your working directory and auto-cds into new workspaces after `gw create`.
59
+
60
+ ## Usage
61
+
62
+ ```bash
63
+ # Setup — register one or more directories containing your repos
64
+ gw init ~/dev ~/work/microservices
65
+ gw add-dir ~/other/repos
66
+ gw remove-dir ~/old/repos
67
+ gw explore # deep-scan for repos (2–3 levels)
68
+
69
+ # Workspaces
70
+ gw create my-feature -r svc-a,svc-b -b feat/login # create workspace
71
+ gw list # list workspaces
72
+ gw list -s # list with git status summary
73
+ gw status my-feature # git status across repos
74
+ gw sync my-feature # rebase all repos onto base branch
75
+ gw go my-feature # cd into workspace
76
+ gw run my-feature # run dev processes (TUI)
77
+ gw add-repo my-feature -r svc-c # add a repo to existing workspace
78
+ gw remove-repo my-feature -r svc-a # remove a repo from workspace
79
+ gw rename my-feature --to new-name # rename a workspace
80
+ gw doctor # diagnose workspace health issues
81
+ gw delete my-feature # clean up (worktrees + branches)
82
+
83
+ # Presets — save repo groups for quick workspace creation
84
+ gw preset add backend -r svc-auth,svc-api,svc-worker
85
+ gw preset list
86
+ gw preset remove backend
87
+ gw create my-feature -p backend # use a preset instead of -r
88
+ ```
89
+
90
+ All interactive menus support **type-to-search** filtering, arrow-key navigation (single-select), or arrow + tab (multi-select) with an `(all)` shortcut.
91
+
92
+ ## Per-repo config
93
+
94
+ Drop a `.grove.toml` in any repo to override defaults:
95
+
96
+ ```toml
97
+ # merchant-portal/.grove.toml
98
+ base_branch = "stage" # branch from origin/stage instead of origin/main
99
+ setup = "pnpm install" # run after worktree creation
100
+ teardown = "rm -rf node_modules" # run before worktree removal
101
+ pre_sync = "pnpm run build:check" # run before rebase during sync
102
+ post_sync = "pnpm install" # run after successful rebase
103
+ pre_run = "docker compose pull" # run before gw run starts
104
+ run = "pnpm dev" # started by gw run (foreground)
105
+ post_run = "docker compose down" # run on gw run exit / Ctrl+C
106
+ ```
107
+
108
+ All hook keys accept a string or a list of commands:
109
+
110
+ ```toml
111
+ setup = ["uv sync", "uv run pre-commit install"]
112
+ ```
113
+
114
+ Hook failures are warnings — they never block the operation they're attached to.
115
+
116
+ ### Design philosophy
117
+
118
+ The hook system follows the npm-style `pre`/`post` convention: one primitive (`run`, `sync`) with optional `pre_` and `post_` counterparts that fire around it. Instead of building special-purpose features (secret injection, dependency installs, container management), Grove gives you generic hook points and gets out of the way.
119
+
120
+ | When | Hooks | Example use |
121
+ |------|-------|-------------|
122
+ | Worktree created | `setup` | `pnpm install`, inject secrets, seed DB |
123
+ | Worktree removed | `teardown` | `rm -rf node_modules`, revoke temp creds |
124
+ | Before/after rebase | `pre_sync`, `post_sync` | Type-check before rebase, reinstall after |
125
+ | Dev session | `pre_run`, `run`, `post_run` | Pull containers, start dev server, tear down |
126
+
127
+ ### `gw run`
128
+
129
+ `gw run` launches a [Textual](https://github.com/Textualize/textual) TUI that manages `run` hooks across all repos. Each repo gets its own log pane with a sidebar showing status indicators (green = running, yellow = starting, red = exited with error).
130
+
131
+ | Key | Action |
132
+ |-----|--------|
133
+ | `j` / `k` / `↑` / `↓` | Navigate repos |
134
+ | `g` / `G` | Jump to first / last |
135
+ | `1`–`9` | Quick-select repo by number |
136
+ | `r` | Restart selected repo |
137
+ | `q` | Quit (terminates all processes) |
138
+
139
+ Pre-run hooks fire before the TUI launches, post-run hooks fire after it exits.
140
+
141
+ ## Dashboard (`gw dash`)
142
+
143
+ A Textual TUI for monitoring Claude Code agents across all your workspaces. Agents are sorted into a kanban board with four columns — **Active**, **Attention**, **Idle**, **Done** — based on live status. Inspired by [Clorch](https://github.com/androsovm/clorch).
144
+
145
+ ```bash
146
+ gw dash install # install Claude Code hooks
147
+ gw dash # launch the dashboard
148
+ gw dash uninstall # remove hooks
149
+ ```
150
+
151
+ | Key | Action |
152
+ |-----|--------|
153
+ | `h` / `l` | Navigate columns |
154
+ | `j` / `k` | Navigate cards |
155
+ | `Enter` | Jump to agent's Zellij tab |
156
+ | `y` / `n` | Approve / deny permission request |
157
+ | `/` | Search / filter agents |
158
+ | `Escape` | Clear search |
159
+ | `r` | Refresh |
160
+ | `q` | Quit |
161
+
162
+ ### How it works
163
+
164
+ Claude Code hooks write agent state to `~/.grove/status/<session_id>.json` on every event. The dashboard polls these files every 500ms and renders a real-time kanban view of all active agents.
165
+
166
+ **Tracked per agent:** status, working directory, git branch, dirty file count, last tool used, tool/error/subagent counts, activity sparkline, permission request details, and initial prompt.
167
+
168
+ ### Zellij tab matching
169
+
170
+ When you press `Enter` to jump to an agent, the dashboard finds the right Zellij tab using a multi-step strategy:
171
+
172
+ | Priority | Strategy | Example |
173
+ |----------|----------|---------|
174
+ | 1 | Exact tab name = project name | `grove` → tab `grove` |
175
+ | 2 | Case-insensitive tab name | `Grove` → tab `grove` |
176
+ | 3 | Workspace name from CWD matched against tab names | CWD has `feat-rewrite` → tab matching |
177
+ | 4 | CWD path match via `zellij action dump-layout` | Agent CWD under tab CWD or vice versa |
178
+ | 5 | Project name substring in tab name | `api` → tab `public-api` |
179
+
180
+ ## Works with AI coding tools
181
+
182
+ Worktrees mean isolation. That makes Grove a natural fit for tools like [Claude Code](https://docs.anthropic.com/en/docs/claude-code) — spin up a workspace, let your AI agent work across repos without touching anything else, clean up when done:
183
+
184
+ ```bash
185
+ gw create -p backend -b fix/auth-bug
186
+ claude "fix the auth token expiry bug across svc-auth and api-gateway"
187
+ gw delete fix-auth-bug # removes worktrees, branches, and workspace
188
+ ```
189
+
190
+ Grove copies your `CLAUDE.md` into new workspaces, so your agent gets project context from the start.
191
+
192
+ ## Requirements
193
+
194
+ Python 3.12+ (installed automatically by Homebrew)
@@ -0,0 +1,182 @@
1
+ <p align="center">
2
+ <img src="assets/logo-light.png" alt="Grove logo" width="120">
3
+ </p>
4
+
5
+ <h1 align="center">Grove (<code>gw</code>)</h1>
6
+
7
+ <p align="center"><b>grove</b> /ɡrōv/ <i>noun</i> — a small group of trees growing together.</p>
8
+
9
+ ## Why?
10
+
11
+ Monorepos solve cross-project work, but not everyone has one. You've got separate repos, separate CI, separate deploys — and that's fine until you need to work across them.
12
+
13
+ One feature across three services means `git worktree add` three times, tracking three branches, jumping between three directories, cleaning up three worktrees when you're done. It's annoying.
14
+
15
+ Grove gives you the multi-repo worktree workflow that monorepos get for free. One command, one workspace, all repos on the same branch.
16
+
17
+ ## Install
18
+
19
+ ### Homebrew
20
+
21
+ ```bash
22
+ brew tap nicksenap/grove
23
+ brew install grove
24
+ ```
25
+
26
+ ### PyPI
27
+
28
+ ```bash
29
+ pipx install gw-cli
30
+ # or
31
+ pip install gw-cli
32
+ ```
33
+
34
+ ### From source
35
+
36
+ ```bash
37
+ uv tool install .
38
+ ```
39
+
40
+ Then add shell integration to your `.zshrc` (or `.bashrc`):
41
+
42
+ ```bash
43
+ eval "$(gw shell-init)"
44
+ ```
45
+
46
+ This enables `gw go` to change your working directory and auto-cds into new workspaces after `gw create`.
47
+
48
+ ## Usage
49
+
50
+ ```bash
51
+ # Setup — register one or more directories containing your repos
52
+ gw init ~/dev ~/work/microservices
53
+ gw add-dir ~/other/repos
54
+ gw remove-dir ~/old/repos
55
+ gw explore # deep-scan for repos (2–3 levels)
56
+
57
+ # Workspaces
58
+ gw create my-feature -r svc-a,svc-b -b feat/login # create workspace
59
+ gw list # list workspaces
60
+ gw list -s # list with git status summary
61
+ gw status my-feature # git status across repos
62
+ gw sync my-feature # rebase all repos onto base branch
63
+ gw go my-feature # cd into workspace
64
+ gw run my-feature # run dev processes (TUI)
65
+ gw add-repo my-feature -r svc-c # add a repo to existing workspace
66
+ gw remove-repo my-feature -r svc-a # remove a repo from workspace
67
+ gw rename my-feature --to new-name # rename a workspace
68
+ gw doctor # diagnose workspace health issues
69
+ gw delete my-feature # clean up (worktrees + branches)
70
+
71
+ # Presets — save repo groups for quick workspace creation
72
+ gw preset add backend -r svc-auth,svc-api,svc-worker
73
+ gw preset list
74
+ gw preset remove backend
75
+ gw create my-feature -p backend # use a preset instead of -r
76
+ ```
77
+
78
+ All interactive menus support **type-to-search** filtering, arrow-key navigation (single-select), or arrow + tab (multi-select) with an `(all)` shortcut.
79
+
80
+ ## Per-repo config
81
+
82
+ Drop a `.grove.toml` in any repo to override defaults:
83
+
84
+ ```toml
85
+ # merchant-portal/.grove.toml
86
+ base_branch = "stage" # branch from origin/stage instead of origin/main
87
+ setup = "pnpm install" # run after worktree creation
88
+ teardown = "rm -rf node_modules" # run before worktree removal
89
+ pre_sync = "pnpm run build:check" # run before rebase during sync
90
+ post_sync = "pnpm install" # run after successful rebase
91
+ pre_run = "docker compose pull" # run before gw run starts
92
+ run = "pnpm dev" # started by gw run (foreground)
93
+ post_run = "docker compose down" # run on gw run exit / Ctrl+C
94
+ ```
95
+
96
+ All hook keys accept a string or a list of commands:
97
+
98
+ ```toml
99
+ setup = ["uv sync", "uv run pre-commit install"]
100
+ ```
101
+
102
+ Hook failures are warnings — they never block the operation they're attached to.
103
+
104
+ ### Design philosophy
105
+
106
+ The hook system follows the npm-style `pre`/`post` convention: one primitive (`run`, `sync`) with optional `pre_` and `post_` counterparts that fire around it. Instead of building special-purpose features (secret injection, dependency installs, container management), Grove gives you generic hook points and gets out of the way.
107
+
108
+ | When | Hooks | Example use |
109
+ |------|-------|-------------|
110
+ | Worktree created | `setup` | `pnpm install`, inject secrets, seed DB |
111
+ | Worktree removed | `teardown` | `rm -rf node_modules`, revoke temp creds |
112
+ | Before/after rebase | `pre_sync`, `post_sync` | Type-check before rebase, reinstall after |
113
+ | Dev session | `pre_run`, `run`, `post_run` | Pull containers, start dev server, tear down |
114
+
115
+ ### `gw run`
116
+
117
+ `gw run` launches a [Textual](https://github.com/Textualize/textual) TUI that manages `run` hooks across all repos. Each repo gets its own log pane with a sidebar showing status indicators (green = running, yellow = starting, red = exited with error).
118
+
119
+ | Key | Action |
120
+ |-----|--------|
121
+ | `j` / `k` / `↑` / `↓` | Navigate repos |
122
+ | `g` / `G` | Jump to first / last |
123
+ | `1`–`9` | Quick-select repo by number |
124
+ | `r` | Restart selected repo |
125
+ | `q` | Quit (terminates all processes) |
126
+
127
+ Pre-run hooks fire before the TUI launches, post-run hooks fire after it exits.
128
+
129
+ ## Dashboard (`gw dash`)
130
+
131
+ A Textual TUI for monitoring Claude Code agents across all your workspaces. Agents are sorted into a kanban board with four columns — **Active**, **Attention**, **Idle**, **Done** — based on live status. Inspired by [Clorch](https://github.com/androsovm/clorch).
132
+
133
+ ```bash
134
+ gw dash install # install Claude Code hooks
135
+ gw dash # launch the dashboard
136
+ gw dash uninstall # remove hooks
137
+ ```
138
+
139
+ | Key | Action |
140
+ |-----|--------|
141
+ | `h` / `l` | Navigate columns |
142
+ | `j` / `k` | Navigate cards |
143
+ | `Enter` | Jump to agent's Zellij tab |
144
+ | `y` / `n` | Approve / deny permission request |
145
+ | `/` | Search / filter agents |
146
+ | `Escape` | Clear search |
147
+ | `r` | Refresh |
148
+ | `q` | Quit |
149
+
150
+ ### How it works
151
+
152
+ Claude Code hooks write agent state to `~/.grove/status/<session_id>.json` on every event. The dashboard polls these files every 500ms and renders a real-time kanban view of all active agents.
153
+
154
+ **Tracked per agent:** status, working directory, git branch, dirty file count, last tool used, tool/error/subagent counts, activity sparkline, permission request details, and initial prompt.
155
+
156
+ ### Zellij tab matching
157
+
158
+ When you press `Enter` to jump to an agent, the dashboard finds the right Zellij tab using a multi-step strategy:
159
+
160
+ | Priority | Strategy | Example |
161
+ |----------|----------|---------|
162
+ | 1 | Exact tab name = project name | `grove` → tab `grove` |
163
+ | 2 | Case-insensitive tab name | `Grove` → tab `grove` |
164
+ | 3 | Workspace name from CWD matched against tab names | CWD has `feat-rewrite` → tab matching |
165
+ | 4 | CWD path match via `zellij action dump-layout` | Agent CWD under tab CWD or vice versa |
166
+ | 5 | Project name substring in tab name | `api` → tab `public-api` |
167
+
168
+ ## Works with AI coding tools
169
+
170
+ Worktrees mean isolation. That makes Grove a natural fit for tools like [Claude Code](https://docs.anthropic.com/en/docs/claude-code) — spin up a workspace, let your AI agent work across repos without touching anything else, clean up when done:
171
+
172
+ ```bash
173
+ gw create -p backend -b fix/auth-bug
174
+ claude "fix the auth token expiry bug across svc-auth and api-gateway"
175
+ gw delete fix-auth-bug # removes worktrees, branches, and workspace
176
+ ```
177
+
178
+ Grove copies your `CLAUDE.md` into new workspaces, so your agent gets project context from the start.
179
+
180
+ ## Requirements
181
+
182
+ Python 3.12+ (installed automatically by Homebrew)