A macOS-first desktop app for managing Git worktrees — launch editors, terminals, and AI coding agents from worktrees, with lifecycle hooks for automating setup.
- Scan — Parses all worktrees via
git worktree list --porcelain, showing dirty / ahead / behind / prunable / locked status - Create — New branch, existing local branch, or remote tracking branch modes with branch dropdowns, random branch name suggestions, and auto-filled target paths
- Remove — Streamed execution logs, preview and execute
git worktree prune - Launch — Built-in launchers for Terminal, Ghostty, iTerm2, VS Code, Cursor, Claude CLI, Codex CLI, Gemini CLI, plus custom launchers
6 lifecycle hook events to automate custom actions during worktree creation, launch, and removal:
| Event | Fires | Working directory |
|---|---|---|
pre-create |
Before worktree creation | Repo root |
post-create |
After worktree creation | Worktree path |
pre-launch |
Before launcher execution | Worktree path |
post-launch |
After launcher execution | Worktree path |
pre-remove |
Before worktree removal | Worktree path |
post-remove |
After worktree removal | Repo root |
Each hook consists of one or more steps, with 4 step types:
[[hooks.post-create]]
type = "script"
run = "echo 'Worktree {branch} ready at {worktree_path}'"Without run, auto-detects: pnpm → bun → yarn → npm, poetry → pdm → pipenv → uv → pip, bundle, cargo build, go mod download, composer, dotnet restore, gradlew, mvn, etc. Or specify a custom command.
[[hooks.post-create]]
type = "install"
# Or specify manually
[[hooks.post-create]]
type = "install"
run = "pip install -r requirements.txt"For copying untracked config files like .env.local. Skips if target already exists.
[[hooks.post-create]]
type = "copy-files"
paths = [".env.local", ".npmrc", ".env.production"][[hooks.post-create]]
type = "launch"
launcherId = "vscode"Use {variable} syntax in run fields to reference context variables:
| Variable | Description | Example |
|---|---|---|
{repo_root} |
Repo root path | /Users/me/myrepo |
{worktree_path} |
Worktree path | /Users/me/myrepo/.worktrees/feat |
{branch} |
Branch name | feature/new-ui |
{base_branch} |
Base branch name | main |
{head_sha} |
HEAD commit SHA | a1b2c3d4... |
{default_remote} |
Default remote | origin |
{is_main_worktree} |
Is main worktree | true / false |
Scripts also receive uppercase environment variables: $REPO_ROOT, $WORKTREE_PATH, $BRANCH, $BASE_BRANCH, $HEAD_SHA, $IS_MAIN_WORKTREE, $DEFAULT_REMOTE.
Manually trigger any configured hook event from the "Re-run Hooks" section in the worktree detail panel.
Grove provides a grove CLI command (like VS Code's code), following the <noun> <verb> pattern:
grove . # Open current repo in Grove GUI
grove open [path] # Open a repository in the GUI
grove hook run <event> # Run hooks for a given event
grove hook list # List configured hooks
grove worktree list # List worktreesInstall the CLI via Settings → CLI Command → Install CLI, which creates a symlink at /usr/local/bin/grove. The hook run command auto-detects the repo root and worktree from the current directory.
- GitHub PR integration — Auto-queries and caches associated Pull Requests via
ghCLI - Single instance — Multiple
grove opencalls reuse the running app instance - i18n — Chinese (default) and English
- Frontend: React 19 + TypeScript + Vite
- Backend: Rust (Tauri 2), shelling out to system
git
pnpm install # Install frontend deps
pnpm build # Frontend type-check + build
pnpm tauri:dev # Run in dev mode
pnpm tauri:dist # Build .dmg
cd src-tauri && cargo test # Rust tests
cd src-tauri && cargo clippy # Rust lintAll config and app state is stored at ~/.grove/store.json (recent repos, per-repo config, worktree root settings, default terminal, etc.). Grove does not write config files into the repository.
Per-repo config is edited via the in-app settings page (TOML format). Config is merged in order (later overrides earlier):
- Built-in defaults — worktree root
.claude, base branchmain, built-in launchers - Repo config — TOML config edited in the UI
[settings]
worktree-root = ".worktrees"
default-base-branch = "main"
[[hooks.post-create]]
type = "copy-files"
paths = [".env.local", ".env.production"]
[[hooks.post-create]]
type = "install"
[[hooks.post-create]]
type = "script"
run = "echo 'Worktree ready at $WORKTREE_PATH'"
[[hooks.post-create]]
type = "launch"
launcherId = "vscode"
[[hooks.pre-remove]]
type = "script"
run = "echo 'Cleaning up {branch}...'"MIT
