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.
- gw_cli-0.12.10/.github/workflows/ci.yml +27 -0
- gw_cli-0.12.10/.github/workflows/release.yml +86 -0
- gw_cli-0.12.10/.gitignore +14 -0
- gw_cli-0.12.10/.python-version +1 -0
- gw_cli-0.12.10/CLAUDE.md +62 -0
- gw_cli-0.12.10/Justfile +66 -0
- gw_cli-0.12.10/PKG-INFO +194 -0
- gw_cli-0.12.10/README.md +182 -0
- gw_cli-0.12.10/TODO.md +71 -0
- gw_cli-0.12.10/assets/logo-light.png +0 -0
- gw_cli-0.12.10/assets/social-preview.png +0 -0
- gw_cli-0.12.10/pyproject.toml +40 -0
- gw_cli-0.12.10/release_notes.md +9 -0
- gw_cli-0.12.10/scripts/generate_formula.py +101 -0
- gw_cli-0.12.10/shell/grove.sh +37 -0
- gw_cli-0.12.10/specs/binary-build.md +95 -0
- gw_cli-0.12.10/specs/dash-kanban.md +356 -0
- gw_cli-0.12.10/specs/dashboard-todo.md +21 -0
- gw_cli-0.12.10/src/grove/__init__.py +17 -0
- gw_cli-0.12.10/src/grove/__main__.py +5 -0
- gw_cli-0.12.10/src/grove/claude.py +149 -0
- gw_cli-0.12.10/src/grove/cli.py +1403 -0
- gw_cli-0.12.10/src/grove/config.py +95 -0
- gw_cli-0.12.10/src/grove/console.py +33 -0
- gw_cli-0.12.10/src/grove/dash/__init__.py +1 -0
- gw_cli-0.12.10/src/grove/dash/__main__.py +5 -0
- gw_cli-0.12.10/src/grove/dash/app.py +302 -0
- gw_cli-0.12.10/src/grove/dash/constants.py +70 -0
- gw_cli-0.12.10/src/grove/dash/hook.py +348 -0
- gw_cli-0.12.10/src/grove/dash/installer.py +196 -0
- gw_cli-0.12.10/src/grove/dash/manager.py +118 -0
- gw_cli-0.12.10/src/grove/dash/models.py +261 -0
- gw_cli-0.12.10/src/grove/dash/widgets/__init__.py +1 -0
- gw_cli-0.12.10/src/grove/dash/widgets/agent_detail.py +135 -0
- gw_cli-0.12.10/src/grove/dash/widgets/header_bar.py +68 -0
- gw_cli-0.12.10/src/grove/dash/widgets/kanban_board.py +139 -0
- gw_cli-0.12.10/src/grove/dash/widgets/kanban_column.py +90 -0
- gw_cli-0.12.10/src/grove/dash/widgets/session_list.py +122 -0
- gw_cli-0.12.10/src/grove/dash/widgets/task_card.py +182 -0
- gw_cli-0.12.10/src/grove/discover.py +279 -0
- gw_cli-0.12.10/src/grove/git.py +243 -0
- gw_cli-0.12.10/src/grove/log.py +68 -0
- gw_cli-0.12.10/src/grove/models.py +104 -0
- gw_cli-0.12.10/src/grove/py.typed +0 -0
- gw_cli-0.12.10/src/grove/state.py +99 -0
- gw_cli-0.12.10/src/grove/stats.py +310 -0
- gw_cli-0.12.10/src/grove/tui.py +392 -0
- gw_cli-0.12.10/src/grove/update.py +62 -0
- gw_cli-0.12.10/src/grove/workspace.py +935 -0
- gw_cli-0.12.10/src/grove/zellij.py +294 -0
- gw_cli-0.12.10/tests/__init__.py +0 -0
- gw_cli-0.12.10/tests/conftest.py +101 -0
- gw_cli-0.12.10/tests/test_claude.py +264 -0
- gw_cli-0.12.10/tests/test_cli.py +1185 -0
- gw_cli-0.12.10/tests/test_config.py +132 -0
- gw_cli-0.12.10/tests/test_dash_hook.py +261 -0
- gw_cli-0.12.10/tests/test_dash_installer.py +137 -0
- gw_cli-0.12.10/tests/test_dash_models.py +185 -0
- gw_cli-0.12.10/tests/test_discover.py +254 -0
- gw_cli-0.12.10/tests/test_git.py +253 -0
- gw_cli-0.12.10/tests/test_models.py +98 -0
- gw_cli-0.12.10/tests/test_state.py +90 -0
- gw_cli-0.12.10/tests/test_stats.py +237 -0
- gw_cli-0.12.10/tests/test_tui.py +107 -0
- gw_cli-0.12.10/tests/test_workspace.py +1426 -0
- 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 @@
|
|
|
1
|
+
3.14
|
gw_cli-0.12.10/CLAUDE.md
ADDED
|
@@ -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.
|
gw_cli-0.12.10/Justfile
ADDED
|
@@ -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 }}"
|
gw_cli-0.12.10/PKG-INFO
ADDED
|
@@ -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)
|
gw_cli-0.12.10/README.md
ADDED
|
@@ -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)
|