Isolated, ready-to-run git worktrees for AI coding agents — host-native or docker-sandboxed.
Project description
Spin up worktrees, put them in a box,
tear them down - treebox
Isolated, ready-to-run git worktrees for coding agents.
Documentation · Install · Usage · How it works · Seth Peters
treebox hands coding agents isolated, ready-to-run git worktrees — one
directory per worktree name. treebox create fetches, cuts a worktree from a
fresh origin/<base>, copies your .env, installs dependencies from a shared
cache, and launches claude or codex inside. No branch name needed up
front: the worktree gets a stable name and an un-pushable treebox/<name>
placeholder branch the agent renames when the work takes shape. Agents work the same repo
in parallel without collisions — on a laptop or over plain SSH.
Provisioning is identical everywhere; a pluggable isolation mode decides where the agent runs:
--isolation |
Sandbox | Agent runs in |
|---|---|---|
host (default) |
none | the worktree shell |
docker |
sandboxed | a docker container, with your .env + caches mounted |
Install
curl -fsSL https://raw.githubusercontent.com/Seth-Peters/treebox/main/install.sh | sh
The script installs with uv and stops with instructions if uv is missing — it never installs a package manager behind your back. Or install directly:
uv tool install treebox
Host isolation needs only git and a logged-in agent CLI (claude /
codex); docker isolation additionally needs just docker — no Node.js, no
extra CLIs. See the
install guide for
requirements and installer overrides.
Usage
The worktree lifecycle is five commands.
Check the host. doctor verifies exactly what create will need — git,
agent logins, .env, credentials for the required fetch — and prints the fix
for anything missing:
treebox doctor
Create. Fetches origin, cuts a worktree from the fresh origin/main,
copies .env and submodules, syncs dependencies from the shared cache, and
launches the agent:
treebox create # generated name (brave-otter), host-native
treebox create fix-auth # named up front
treebox create fix-auth --isolation docker # sandboxed
treebox create --checkout feature/auth # exact existing branch (resume, PR review)
treebox create auth-fixes --base feature/auth # stack on any base branch
The optional name is one slug token and is the worktree's permanent identity;
the branch starts as a treebox/<name> placeholder that a per-worktree
pre-push guard keeps un-pushable — rename it conventionally
(git branch -m feature/user-auth, fix/login-race, chore/bump-deps, …)
when the work has a shape, then push. So a machine-generated name can never
become a PR title. --checkout is the one path that skips the placeholder: it checks out an
existing branch exactly.
--base takes any branch, not just main — branch off dev, or stack a new
worktree on top of an existing PR's branch, even while that branch is checked
out in another worktree. It resolves as the freshly fetched origin/<base>,
so push the base first if its latest commits only exist locally.
Enter. Come back to an existing worktree. By default it reuses the harness
the worktree was created with; an explicit --harness overrides it for that
session only, without changing what's recorded on disk. The ref is the name,
the current branch (renames are followed live), or a unique substring of
either. Dependencies re-sync only if the lockfile changed since last time:
treebox enter fix-auth --harness claude
treebox enter fix-auth --harness codex -- --resume # args after -- go to the agent
List. See what exists, what each worktree was last doing, and what has
gone stale — sorted by recency, with treebox/* placeholders flagged
⚠ unnamed:
treebox list
Tear down. Remove one or more worktrees — and, when you're done, their
branches. Refuses to delete uncommitted work unless forced. Run it with no
refs and treebox walks you through an arrow-key picker, each worktree
annotated with a "will I lose work?" badge (dirty/ahead/merged, plus PR state
when gh/glab is present):
treebox teardown fix-auth brave-otter --delete-branch
treebox teardown # pick interactively
treebox is built to be scripted, including by agents: the worktree commands and
doctor take --json (data to stdout, diagnostics to stderr, a schema that
only gains fields within a version), --dry-run prints the exact commands
without running them, and exit codes are stable (0 ok · 1 runtime/doctor
blocked · 2 usage · 3 not found · 4 auth · 5 conflict). Full
reference in the usage guide.
Every agent ships its own cage
Every coding agent invents its own answer to "run me in parallel" and "don't let me touch the wrong thing" — a different config file, a different schema, a different word for the same idea:
| Agent | Sandbox / permission config | Built-in worktrees | Config lives in |
|---|---|---|---|
| Claude Code | permissions allow/ask/deny + native OS sandbox (Seatbelt/bubblewrap) + dev container |
Yes (--worktree) |
.claude/settings.json, .devcontainer/ |
| OpenAI Codex | sandbox_mode × approval_policy (Seatbelt / Landlock+seccomp) |
No in CLI (app only) | ~/.codex/config.toml, [profiles.*] |
| opencode | permission per-tool allow/ask/deny (no OS sandbox) |
No (community plugins) | opencode.json |
| pi | none built-in ("all permissions by default"); BYO Docker/VM + trust prompt | No in core (pi-subagents) |
~/.pi/agent/settings.json |
Learn one and it teaches you nothing about the next, and none of it ports across tools. treebox owns the isolation layer instead — one named-worktree layout, one operator-owned sandbox, one config file — and launches your agent of choice inside it. Learn treebox once; swap the agent, keep the box. Full comparison with citations: Agents & sandboxing.
Design
- Never silently stale.
createrequires a successfulgit fetch originand branches from the freshorigin/<base>; a failed fetch exits4loudly.--no-fetchis the only (explicit) escape. - Warmth lives in the cache, not the tree. Installs hardlink from shared
caches (
~/.cache/uv, the pnpm store, …) reused across worktrees and containers. - The sandbox config lives outside the box. The container is rendered from your operator-owned template beside the worktree, never mounted — a boxed agent can't edit its own cage, and the target repo's container config and hooks are ignored.
- Credentials go in as scoped copies. Only the agents' login files are
copied into a throwaway per-worktree dir — never the live
~/.claude/~/.codex— refreshed on every entry so a host logout or a fresh login reaches the sandbox next time; treebox uses your subscription login.
More in how it works.
Configuration
Optional, and user-level only ($TREEBOX_CONFIG, else
$TREEBOX_HOME/config.toml, default ~/.treebox/config.toml) — treebox
never reads config from the target repo:
isolation = "docker" # host | docker
harness = "claude" # claude | codex
base = "main"
All keys, shared-cache overrides, setup hooks, and sandbox templates are covered in the configuration guide.
Customizing isolation
docker isolation builds from a template you own — a directory of files
treebox ships and you edit. treebox renders it beside the worktree so a boxed
agent can't touch its own cage. Three steps:
1. Copy the template out — you edit a copy, never the shipped one. Name it
(here node); a node box and a python box can coexist, one per stack:
mkdir -p ~/.treebox/templates
cp -R "$(python -c 'import treebox.assets, pathlib; print(pathlib.Path(treebox.assets.__file__).parent)')/container" \
~/.treebox/templates/node
2. Edit two files. The shipped image already bundles Node 22, uv, gh,
ripgrep, and the agent CLIs, so most projects touch only these:
# ~/.treebox/templates/node/Dockerfile — global tooling
USER root
RUN npm install -g pnpm@9 typescript tsx
USER ${USERNAME}
// ~/.treebox/templates/node/container.json — your install command
"postCreate": "if [ -f pnpm-lock.yaml ]; then pnpm install --frozen-lockfile; elif [ -f package-lock.json ]; then npm ci; else npm install; fi"
(In docker, setup runs postCreate, not the host-mode ecosystem auto-detect,
so a non-Python project wires its install here.)
3. Point treebox at it — per run with --isolation docker --template node,
or set template = "node" in ~/.treebox/config.toml.
Full walkthrough — every file in a template and the container.json schema — in
the configuration guide.
Development
git clone https://github.com/Seth-Peters/treebox && cd treebox
uv run treebox ... # run the CLI from the working tree
uv run --extra dev pre-commit install # lint/format/strict type-check hooks (see CONTRIBUTING.md)
uv run --extra dev python -m pytest # unit + integration suite
uv run --extra dev mypy # strict type check
./scripts/validate.sh # lint + format + tests + live host-runner smoke
Contributing
Small fixes and docs improvements are welcome. See CONTRIBUTING.md. The roadmap is intentionally light for now: ROADMAP.md.
License
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file treebox-0.6.0.tar.gz.
File metadata
- Download URL: treebox-0.6.0.tar.gz
- Upload date:
- Size: 1.9 MB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c90f135a2a736914c182d921acfc67067bd2b96a8c55624ac3e8bf09ff2c8d40
|
|
| MD5 |
4dca5d8dd59da67dae18b770eaace5e0
|
|
| BLAKE2b-256 |
a9b9adf1d6c02441ccd2c5992a1853cc3f31a70d93e1ec7e0bf9a685af725adb
|
Provenance
The following attestation bundles were made for treebox-0.6.0.tar.gz:
Publisher:
release.yml on Seth-Peters/treebox
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
treebox-0.6.0.tar.gz -
Subject digest:
c90f135a2a736914c182d921acfc67067bd2b96a8c55624ac3e8bf09ff2c8d40 - Sigstore transparency entry: 2063753025
- Sigstore integration time:
-
Permalink:
Seth-Peters/treebox@6d596123b0141b1b8bb33fdcaaa4597a9baa81b2 -
Branch / Tag:
refs/tags/v0.6.0 - Owner: https://github.com/Seth-Peters
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@6d596123b0141b1b8bb33fdcaaa4597a9baa81b2 -
Trigger Event:
push
-
Statement type:
File details
Details for the file treebox-0.6.0-py3-none-any.whl.
File metadata
- Download URL: treebox-0.6.0-py3-none-any.whl
- Upload date:
- Size: 92.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f663094809503e6695a154de78cfc5829fb6d4dc30e1dcd52b18c07c86f691c4
|
|
| MD5 |
9d27789934f76aa0402e7224a05f7362
|
|
| BLAKE2b-256 |
2457094c212ef3ef0305cd06a42ada39061876d556eda16af507620e6bf80341
|
Provenance
The following attestation bundles were made for treebox-0.6.0-py3-none-any.whl:
Publisher:
release.yml on Seth-Peters/treebox
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
treebox-0.6.0-py3-none-any.whl -
Subject digest:
f663094809503e6695a154de78cfc5829fb6d4dc30e1dcd52b18c07c86f691c4 - Sigstore transparency entry: 2063753050
- Sigstore integration time:
-
Permalink:
Seth-Peters/treebox@6d596123b0141b1b8bb33fdcaaa4597a9baa81b2 -
Branch / Tag:
refs/tags/v0.6.0 - Owner: https://github.com/Seth-Peters
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@6d596123b0141b1b8bb33fdcaaa4597a9baa81b2 -
Trigger Event:
push
-
Statement type: