A Zsh function that manages multi-repo git worktrees with tmux integration. Group multiple projects into a workspace so they all get worktrees with the same branch name, inside one tmux session.
You're working on features that span multiple repos — a frontend and backend, a server and admin panel. You need worktrees in each repo with the same branch name, set up together, accessible from one tmux session. Grove does this in one command.
You define workspaces that group projects together. gv <workspace> <name> is all you need:
- Single-project: Any git repo is automatically a workspace.
- Multi-project: Define workspace groups in config.
grove fullstack fix-authcreates worktrees in bothfrontendandbackend.
For each project in the workspace:
- Fetches from origin
- If the branch exists on any remote, tracks it where available; creates new branch elsewhere
- Creates worktrees for all projects under one workspace directory
- For multi-project: copies
.claude/skills/and.cursor/(with prefixed names) from each project into the workspace root - Opens a tmux session and runs post-create + post-startup hooks
- Multi-repo workspaces: Group projects together — one command creates worktrees across all of them
- Smart branch resolution: Tracks existing remote branches where they exist, creates new branches elsewhere
- Consistent branch naming: All projects in a workspace get the same branch name
- Agent config merging:
.claude/skills/and.cursor/from each project are copied into the workspace root (skill names are prefixed to avoid collisions) - Post-create hooks: Per-project setup commands (dependency install, codegen)
- Post-startup hooks: Per-workspace commands (launch AI agents, tmux layouts)
- tmux session integration: One session per workspace instance
- Fuzzy-find completion: fzf-powered Tab completion for workspaces and instances
- direnv support: Auto-runs
direnv allowfor new worktrees - Rollback on failure: If any worktree creation fails, all are cleaned up
Copy this prompt into Claude Code (or your AI tool of choice):
Clone [grove](https://github.com/shivgodhia/grove) to ~/.zsh/grove and add `source ~/.zsh/grove/grove.zsh`
to my .zshrc. Then walk me through setting up ~/.zsh/grove/grove.local.zsh step by step,
asking me one question at a time.
Before anything else, check that all required dependencies are installed by running
`command -v git && command -v tmux && command -v fzf` and inspecting the output. The requirements are:
- git (2.5+ for worktree support) — check version with `git --version`
- tmux — required for workspace session management
- fzf — required for the interactive TUI dashboard and fuzzy Tab completion
For any missing tool, help the user install it:
- On macOS: `brew install <tool>` (if Homebrew is missing, walk them through installing it first
via https://brew.sh)
- On Debian/Ubuntu: `sudo apt install <tool>`
- On Arch: `sudo pacman -S <tool>`
- On Fedora: `sudo dnf install <tool>`
After installing, re-verify each tool is available before continuing. Do NOT proceed with the
rest of the setup until all three are confirmed present.
Then walk through configuration:
1. Ask where my "projects directory" is — explain this is a single parent folder where all my git clones
live for grove, and that workspaces get created in a `workspaces/` subdirectory inside it.
Suggest ~/groveyard as a default.
2. Ask what branch prefix I want (default: $USER). Explain this is used for naming new branches as
<prefix>/branch-name.
3. Iteratively ask me for git repos to clone into the projects directory. For each one:
- Clone it into the projects directory.
- Read the project's README to figure out what setup commands are needed (e.g. npm install,
pnpm install, yarn && npx prisma generate) and suggest a post-create hook for it.
- After each clone, ask if I want to add another repo or if I'm done.
4. Ask if I want to define any multi-project workspaces. Explain the concept: a workspace groups
multiple repos so they all get worktrees with the same branch name in one command. For example,
grove_workspaces[fullstack]="frontend backend" means `gv fullstack fix-auth` creates
worktrees in both repos. Workspace names must be distinct from project directory names (project
names are auto-claimed as implicit single-project workspaces). Iteratively ask for workspace
definitions until done.
5. Ask which AI coding agent they use. Present these options:
- **Claude Code** — launches `claude` in the tmux session (runs inside the terminal alongside the workspace)
- **Codex** — launches `codex` in the tmux session
- **OpenCode** — launches `opencode` in the tmux session
- **Cursor** — launches `cursor .` to open the workspace directory in Cursor IDE
- **None / other** — skip, or let them type a custom command
Ask which one they use. Based on their choice, set GROVE_DEFAULT_POST_STARTUP_COMMAND to the
appropriate command (`claude`, `codex`, `cursor .`, or their custom command).
Explain that this is a post-startup hook that runs every time a tmux session is created.
Then ask if any specific workspaces need a different startup command (e.g. a different agent,
or a tmux split pane layout) — if so, configure those as per-workspace overrides
with grove_post_startup_commands[workspace].
6. Copy grove.local.example.zsh to grove.local.zsh, then edit it with all the
collected configuration.
7. Ask if I want terminal tab titles to automatically show the workspace name. Explain that this
makes tmux set the terminal tab title to the session name (e.g. "grove/fullstack/fix-auth"), so
each tab is easy to identify. If yes, find my tmux config (~/.config/tmux/tmux.conf or
~/.tmux.conf) and add `set-option -g set-titles on` and `set-option -g set-titles-string '#S'`
if they aren't already present. Then ask which terminal emulator they use (e.g. iTerm2, Alacritty,
Kitty, WezTerm, Terminal.app) and walk them through enabling the setting that lets applications
change the tab/window title — for example, in iTerm2 this is under Profiles → General → Title
where "Applications in terminal may change the title" must be checked.
8. Ask if they want recommended tmux settings for a better workspace experience. Explain that
`set -g mouse on` enables mouse support (scroll through output, click to switch panes, drag
to resize them) and `set -g history-limit 50000` increases the scrollback buffer so you don't
lose output from long-running commands. If yes, find their tmux config and add these settings
if they aren't already present, then reload with `tmux source-file <path-to-config>`.
Once installation and config are complete, walk the user through a hands-on test drive:
9. Tell the user to open a new terminal tab (so the freshly sourced .zshrc takes effect).
10. Generate a `gv` command for them based on the workspaces they just configured. For example,
if they set up a workspace called "backend", suggest: `gv backend test-drive`. Copy the
command to the clipboard for them if possible. Explain what will happen (worktree created,
tmux session opened, hooks run).
11. Wait for the user to confirm they've created the workspace. Once they say it's done, tell
them: "Great — now open another new terminal tab and just type `gv`." Explain that this
opens the interactive TUI dashboard where they can see all their workspace instances, search
and filter them, and open or delete them with keyboard shortcuts (Enter to open, Ctrl-N to
create new, Ctrl-X or Del to remove).
12. Once they've tried the TUI, tell them they can go back to the original tab and clean up.
Explain the two ways to remove a workspace:
- From inside the workspace: `gv --kms` (or `gv --kms --force` if there are uncommitted changes)
- From the TUI: highlight the workspace and press Ctrl-X or Del
Suggest they try `gv --kms` from the tab where the workspace is running.
Or do it manually:
-
Clone this repo:
git clone <repo-url> ~/.zsh/grove
-
Add to your
.zshrc:source ~/.zsh/grove/grove.zsh
-
Copy and edit the example config:
cp ~/.zsh/grove/grove.local.example.zsh \ ~/.zsh/grove/grove.local.zsh
-
Restart your terminal or run
source ~/.zshrc.
Default: ~/groveyard.
Edit grove.local.zsh:
GROVE_PROJECTS_DIR— where your git repos live (default:~/groveyard)GROVE_WORKSPACES_DIR— where workspaces are created (default:$GROVE_PROJECTS_DIR/workspaces)GROVE_BASE_BRANCH— base branch for new worktrees (default:origin/main)GROVE_BRANCH_PREFIX— prefix for new branch names (default:$USER)
Group projects into named workspaces. Project names are auto-claimed as implicit single-project workspaces, so workspace names must be distinct from project directory names.
grove_workspaces[fullstack]="frontend backend"
grove_workspaces[admin]="backend admin-panel"Per-project commands that run when a worktree is first created:
grove_post_create_commands[backend]="yarn && npx prisma generate"
grove_post_create_commands[frontend]="pnpm install"Per-workspace commands that run every time a new tmux session is created:
GROVE_DEFAULT_POST_STARTUP_COMMAND="claude --dangerously-skip-permissions" # or "codex", "cursor .", etc.
grove_post_startup_commands[fullstack]="cursor ."gv # open interactive TUI dashboard
gv <workspace> <name> # create/attach to workspace
gv <workspace> <name> <command> # run command in workspace (no tmux)
gv --list # list all workspaces and instances
gv --ls # show branch tree for current workspace
gv --rm <workspace> <name> # remove workspace instance
gv --rm --force <workspace> <name> # force remove (uncommitted changes)
gv --kms [--force] # remove current workspace (from inside it)
gv --home # cd to projects directory
gv --update # pull latest grove updates (must be on main)
gv --help # show usage guideRunning gv with no arguments opens an fzf-powered dashboard showing all your workspace instances with a live preview pane.
Keybindings:
| Key | Action |
|---|---|
| Enter | Open/attach to selected workspace |
| Ctrl-N | Create a new workspace instance |
| Ctrl-X or Del | Delete selected workspace (with confirmation) |
| Type to search | Filter by workspace, project, or instance name |
The preview pane (right side) shows the git branch tree for the selected workspace, including per-project branch info and PR status (if gh is installed).
From inside a workspace, run gv --ls to see the branch tree — the same view shown in the TUI preview pane. Shows per-project branches, current HEAD, and dirty/clean status.
# Open the TUI dashboard
gv
# Single-project workspace (implicit)
gv backend fix-auth
# Multi-project workspace
gv fullstack fix-auth
# Check out a teammate's branch across all workspace projects
gv fullstack someone/fix-bug
# Run a command in the workspace
gv fullstack fix-auth git status
# See branch tree for current workspace
gv --ls
# List everything
gv --list
# Clean up when done
gv --rm fullstack fix-auth
# Or clean up from inside the workspace
gv --kms~/groveyard/ # git repos live here
├── frontend/ # main repo checkout
├── backend/ # another repo
└── workspaces/ # all workspace instances
├── fullstack/ # multi-project workspace
│ └── fix-auth/ # instance
│ ├── .claude/skills/ # copied from children (names prefixed)
│ ├── frontend/ # worktree → branch
│ └── backend/ # worktree → branch
└── backend/ # single-project workspace
└── add-caching/ # instance
└── backend/ # worktree → branch
This repo includes a Claude Code skill for managing workspaces with /grove inside Claude Code.
Install:
mkdir -p ~/.claude/skills
ln -s ~/.zsh/grove/skills/grove ~/.claude/skills/groveUsage:
/grove create fix the auth bug
/grove list
/grove cd fix-auth-bug
/grove delete fix-auth-bug
Add these to your tmux config (~/.config/tmux/tmux.conf or ~/.tmux.conf):
# Enable mouse support (scroll, click panes, resize)
set -g mouse on
# Increase scrollback buffer
set -g history-limit 50000
# Show workspace name as terminal tab title
set-option -g set-titles on
set-option -g set-titles-string '#S'
Then reload your config:
tmux source-file ~/.config/tmux/tmux.confMouse support lets you scroll through output, click to switch panes, and drag to resize them — it just works so much better.
Tab titles — gv creates tmux sessions named grove/<workspace>/<name>, and set-titles pushes that to your terminal as the tab name. Instead of a sea of identical "zsh" tabs, you see exactly which workspace each tab is for. Ghostty picks up the tmux title automatically — no extra config needed. In iTerm2, you'll also need to enable Profiles → General → Title → "Applications in terminal may change the title".
- Zsh
- Git 2.5+ (for worktree support)
- tmux (for workspace session management)
- fzf (for interactive TUI dashboard and fuzzy Tab completion)
- gh (optional, for PR status in TUI preview pane)