Skip to content

cristianelias/beaver

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

23 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

🦫 beaver

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.

the beaver gui: worktree cards with status pills, pool gauge and key-cap hints

Why

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:

  1. 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.
  2. Local config doesn't travel. .env and 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.
  3. 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. release resets tracked files but keeps gitignored caches, so node_modules survives and the next get is ready in seconds. Disk converges to your real level of parallelism instead of multiplying.
  • Configs travel. Every get re-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, and beaver status / beaver gui show every worktree of every repo with label, branch state, dirty files and age.

How it plugs into your tools

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-hooks

It 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 pool

The 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.

Install

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 | sh

npm (same native binary, node not involved at runtime) or cargo:

npm i -g @cristian-elias/beaver
cargo install --git https://github.com/cristianelias/beaver

Then 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 zsh

On 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.

Usage

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)

The lifecycle, and what it discards

  • get picks the warmest free worktree (or creates one), fetches, and parks it on a detached HEAD at origin/<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.
  • release is 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.
  • destroy deletes the worktree for real. If it holds uncommitted changes or commits that exist nowhere else, beaver asks first (--force skips).

What gets copied in

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.

GUI

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.

Configuration

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.

Limits

  • 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.

Uninstall

beaver uninstall

Shows 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).

Prior art

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.

License

MIT β€” built by Cris

About

🦫 A git worktree pool manager β€” grab a fresh worktree, work, release it back. Warm caches, .env files carried, TUI included.

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors