A git worktree pool, built for parallel work with coding agents. beaver get hands you an isolated checkout of the current repo in seconds, with warm dependency caches and your local .env files already in place. beaver release returns it to the pool. Nothing piles up, nothing starts cold.
Coding agents made N parallel checkouts of the same repo the normal way to work, and every tool answered with git worktrees: Claude Code creates one per session or subagent, Cursor one per parallel agent, the Codex app one per thread. The approach is right. The implementations all share three problems:
- Every worktree starts cold. A fresh checkout has no
node_modules, no build cache. Each agent pays the full install, and each worktree duplicates those gigabytes β Cursor's docs recommend fast package managers for exactly this reason, and one user measured 9.8 GB of worktrees in minutes on a 2 GB repo. - Local config doesn't travel.
.envand friends are gitignored, so they don't exist in a new worktree. The app won't boot, tests fail, agents improvise. Each tool has its own per-repo opt-in fix (.worktreeinclude, setup scripts) that you have to remember to configure everywhere. - Cleanup leaks. Claude Code auto-removes only untouched worktrees and never cleans non-interactive runs; Cursor sweeps its stash on a timer; Codex keeps its last 15. Three tools, three directories, three policies, and no single answer to "what worktrees exist on this machine and which still hold unpushed work?"
beaver replaces that with one pool of stable, reusable worktrees shared by you and every agent:
- Reuse is warm.
releaseresets tracked files but keeps gitignored caches, sonode_modulessurvives and the nextgetis ready in seconds. Disk converges to your real level of parallelism instead of multiplying. - Configs travel. Every
getre-copies loose gitignored files (.env,.env.local,.claude/settings.local.json, β¦) from the main checkout. One global rule, no per-repo setup. - One lifecycle.
getβ work βrelease, andbeaver status/beaver guishow every worktree of every repo with label, branch state, dirty files and age.
Claude Code, natively. Claude creates worktrees on its own β claude --worktree, subagents with isolation: worktree, desktop sessions. One command routes all of that through the pool:
beaver init-agents --claude-hooksIt points Claude's WorktreeCreate/WorktreeRemove hooks at beaver: sessions start in a warm pool worktree (labeled with the name Claude picked) and removal returns it to the pool instead of deleting it. Opt-in, refuses to touch a WorktreeCreate hook you already have, undone by beaver uninstall. One caveat: PR worktrees (claude --worktree "#123") land at the default-branch tip, not at the PR head.
Every CLI agent, through its memory file. beaver init-agents upserts a short marker-delimited block into the agent memory files that exist on your machine β ~/.claude/CLAUDE.md, ~/.codex/AGENTS.md, opencode's global AGENTS.md, ~/AGENTS.md β and any file you point it at (beaver init-agents ~/.gemini/GEMINI.md). From then on agents acquire their own isolated checkout:
WT=$(beaver get --print-path fix-login) # acquire, label, print the path
cd "$WT" # work in isolation
beaver release # done: back to the poolThe full reference ships inside the binary (beaver agent-guide), so it never drifts from the installed version, and beaver status --json gives orchestrators machine-readable pool state for auditing what a fleet of subagents left behind.
Prerequisites: macOS or Linux Β· git β₯ 2.31 Β· zsh or bash. Building from source needs stable Rust β₯ 1.88; the other methods need nothing else.
Prebuilt binary:
curl -fsSL https://raw.githubusercontent.com/cristianelias/beaver/main/install.sh | shnpm (same native binary, node not involved at runtime) or cargo:
npm i -g @cristian-elias/beaver
cargo install --git https://github.com/cristianelias/beaverThen wire the shell integration (what lets beaver cd you into worktrees; safe to re-run):
{ grep -qs 'beaver init zsh' ~/.zshrc || printf '\n# beaver β git worktree pool manager\neval "$(beaver init zsh)"\n' >> ~/.zshrc; } && exec zshOn bash, swap in ~/.bashrc, beaver init bash and exec bash. Finally, teach your agents: beaver init-agents (plus --claude-hooks if you use Claude Code).
Or let an agent install it for you
Paste this into Claude Code, Codex, or any coding agent on your machine:
Set up beaver (https://github.com/cristianelias/beaver), a git worktree
pool manager, on this machine. Steps must be idempotent.
1. Install the binary, then verify with `beaver --version`. Try in order:
a. curl -fsSL https://raw.githubusercontent.com/cristianelias/beaver/main/install.sh | sh
b. npm i -g @cristian-elias/beaver
c. cargo install --git https://github.com/cristianelias/beaver
2. Wire the shell integration for the user's login shell, unless its rc
file already has a `beaver init` line. zsh β append to ~/.zshrc:
eval "$(beaver init zsh)"
(bash: ~/.bashrc and `beaver init bash`.)
3. Run `beaver init-agents`. If your own memory file lives somewhere it
did not list, run `beaver init-agents <that file>` too. If Claude Code
is installed, also run `beaver init-agents --claude-hooks`.
4. Report exactly which files changed.
All commands work from anywhere inside a repo β the main checkout or any beaver worktree.
beaver get payments # get a worktree (reuse free / create), label it,
# refresh to origin/main, copy configs, cd into it
beaver release # discard everything, return it to the pool
beaver destroy # delete the worktree you're standing in
beaver status # this repo's pool (--all: every repo, --json: machine-readable)
beaver gui # interactive browser
beaver init-agents # teach the agents on this machine (--claude-hooks: wire Claude Code)getpicks the warmest free worktree (or creates one), fetches, and parks it on a detached HEAD atorigin/<default>. Pooled worktrees never sit on branches, so there are no "branch already checked out" collisions; create a branch inside one when you're ready to push.releaseis deliberately destructive:reset --hard+clean -fd, then re-detach at the fresh tip. That contract is what keeps the pool clean without confirmations. Anything committed and pushed is safe; gitignored files survive (that's the warm cache); branches you created are left alone. Agents are instructed to release only merged or abandoned work.destroydeletes the worktree for real. If it holds uncommitted changes or commits that exist nowhere else, beaver asks first (--forceskips).
On every get: loose gitignored files from the main repo β matched by .gitignore but not inside an ignored directory. That's .env, apps/web/.env.local, .claude/settings.local.json; never node_modules/ or dist/. Files over 5 MB are skipped. Everything stays on your machine, same user, same disk; excludes are configurable.
beaver gui opens the pool browser (--all starts at the repo list, .. switches views). Arrows or wasd to move, g gets a worktree, Enter opens the action menu β open, release, destroy (with a confirmation if work would be lost), c edits the on_get hook.
Optional, at ~/.beaver/config.toml:
[copy]
max_file_size_mb = 5
exclude = ["*.log", ".DS_Store"] # never copy these
include = ["fixtures/big.db"] # always copy these (bypasses cap & excludes)
[hooks]
on_get = "claude" # run in the worktree after every `beaver get`
[repos."/Users/me/dev/api"]
on_get = "npm install && claude" # per-repo override
copy_exclude = ["*.pem"]State lives in ~/.beaver/state.json: file-locked (concurrent gets from parallel agents serialize instead of racing) and self-healing (reconciled against git worktree list on every run, so hand-deleted worktrees just drop out). The test suite runs the real binary against real git repos β 55 tests at last count.
- Cursor's parallel-agents UI and the Codex app manage their own worktrees and expose no hook to redirect them. beaver covers CLI agents and all of Claude Code.
- Pool worktrees live at the default-branch tip. beaver won't check out an arbitrary branch or PR for you; it's the wrong tool for long-lived per-branch checkouts.
- File isolation only: parallel agents still share ports, databases and other runtime state.
- macOS and Linux, zsh and bash. No Windows or fish yet.
If your repos have no dependencies and no local config, or your agents run only in cloud sandboxes, native worktree handling is probably enough. The pool starts paying for itself the second time an agent skips npm install.
beaver uninstallShows the plan, asks once, then removes everything: pooled worktrees (with git metadata pruned), ~/.beaver, shell integration lines, the beaver block in every agent memory file it wrote, the Claude Code hook entries, and every install it can find (npm, cargo, binaries on PATH β each verified to be beaver before deletion).
beaver grew out of wt, a personal bash function that created named worktrees and copied .env files into them. Plenty of tools wrap git worktree; the parts here that earn their keep are the warm pool, the copy rule, and the agent wiring.
MIT β built by Cris
