treebox 0.4.0__tar.gz → 0.5.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. {treebox-0.4.0 → treebox-0.5.0}/CLAUDE.md +16 -13
  2. {treebox-0.4.0 → treebox-0.5.0}/CONTRIBUTING.md +3 -2
  3. {treebox-0.4.0 → treebox-0.5.0}/PKG-INFO +68 -60
  4. {treebox-0.4.0 → treebox-0.5.0}/README.md +67 -59
  5. treebox-0.5.0/assets/treebox-demo.gif +0 -0
  6. treebox-0.5.0/assets/treebox-logo.png +0 -0
  7. {treebox-0.4.0 → treebox-0.5.0}/docs/agents.md +4 -3
  8. treebox-0.5.0/docs/configuration.md +160 -0
  9. {treebox-0.4.0 → treebox-0.5.0}/docs/how-it-works.md +14 -5
  10. {treebox-0.4.0 → treebox-0.5.0}/docs/index.md +8 -8
  11. {treebox-0.4.0 → treebox-0.5.0}/docs/install.md +5 -5
  12. {treebox-0.4.0 → treebox-0.5.0}/docs/usage.md +92 -13
  13. {treebox-0.4.0 → treebox-0.5.0}/install.sh +4 -12
  14. {treebox-0.4.0 → treebox-0.5.0}/pyproject.toml +1 -1
  15. {treebox-0.4.0 → treebox-0.5.0}/skills/treebox/SKILL.md +31 -29
  16. {treebox-0.4.0 → treebox-0.5.0}/src/treebox/assets.py +4 -3
  17. {treebox-0.4.0 → treebox-0.5.0}/src/treebox/cli.py +182 -91
  18. {treebox-0.4.0 → treebox-0.5.0}/src/treebox/config.py +74 -23
  19. {treebox-0.4.0 → treebox-0.5.0}/src/treebox/forge.py +8 -2
  20. {treebox-0.4.0 → treebox-0.5.0}/src/treebox/git.py +58 -25
  21. {treebox-0.4.0 → treebox-0.5.0}/src/treebox/locking.py +2 -1
  22. {treebox-0.4.0 → treebox-0.5.0}/src/treebox/models.py +26 -0
  23. {treebox-0.4.0 → treebox-0.5.0}/src/treebox/output.py +32 -18
  24. {treebox-0.4.0 → treebox-0.5.0}/src/treebox/provision.py +18 -7
  25. {treebox-0.4.0 → treebox-0.5.0}/src/treebox/runners/base.py +4 -2
  26. {treebox-0.4.0 → treebox-0.5.0}/src/treebox/runners/docker.py +55 -23
  27. {treebox-0.4.0 → treebox-0.5.0}/src/treebox/state.py +8 -0
  28. {treebox-0.4.0 → treebox-0.5.0}/src/treebox/system.py +2 -1
  29. treebox-0.5.0/tests/test_contract.py +366 -0
  30. {treebox-0.4.0 → treebox-0.5.0}/tests/test_integration.py +374 -8
  31. {treebox-0.4.0 → treebox-0.5.0}/tests/test_units.py +803 -15
  32. treebox-0.4.0/docs/assets/treebox-logo.png +0 -0
  33. treebox-0.4.0/docs/configuration.md +0 -130
  34. {treebox-0.4.0 → treebox-0.5.0}/.agents/skills/no-mistakes/SKILL.md +0 -0
  35. {treebox-0.4.0 → treebox-0.5.0}/.github/workflows/autofix.yml +0 -0
  36. {treebox-0.4.0 → treebox-0.5.0}/.github/workflows/ci.yml +0 -0
  37. {treebox-0.4.0 → treebox-0.5.0}/.github/workflows/claude.yml +0 -0
  38. {treebox-0.4.0 → treebox-0.5.0}/.github/workflows/docs.yml +0 -0
  39. {treebox-0.4.0 → treebox-0.5.0}/.github/workflows/release.yml +0 -0
  40. {treebox-0.4.0 → treebox-0.5.0}/.gitignore +0 -0
  41. {treebox-0.4.0 → treebox-0.5.0}/.pre-commit-config.yaml +0 -0
  42. {treebox-0.4.0 → treebox-0.5.0}/AGENTS.md +0 -0
  43. {treebox-0.4.0 → treebox-0.5.0}/LICENSE +0 -0
  44. {treebox-0.4.0 → treebox-0.5.0}/ROADMAP.md +0 -0
  45. {treebox-0.4.0 → treebox-0.5.0/docs}/assets/treebox-logo.png +0 -0
  46. {treebox-0.4.0 → treebox-0.5.0}/docs/javascripts/treebox.js +0 -0
  47. {treebox-0.4.0 → treebox-0.5.0}/docs/stylesheets/extra.css +0 -0
  48. {treebox-0.4.0 → treebox-0.5.0}/hooks/copy_page.py +0 -0
  49. {treebox-0.4.0 → treebox-0.5.0}/mkdocs.yml +0 -0
  50. {treebox-0.4.0 → treebox-0.5.0}/scripts/validate.sh +0 -0
  51. {treebox-0.4.0 → treebox-0.5.0}/src/treebox/__init__.py +0 -0
  52. {treebox-0.4.0 → treebox-0.5.0}/src/treebox/assets/container/Dockerfile +0 -0
  53. {treebox-0.4.0 → treebox-0.5.0}/src/treebox/assets/container/allowed-domains.sh +0 -0
  54. {treebox-0.4.0 → treebox-0.5.0}/src/treebox/assets/container/container.json +0 -0
  55. {treebox-0.4.0 → treebox-0.5.0}/src/treebox/assets/container/firewall.json +0 -0
  56. {treebox-0.4.0 → treebox-0.5.0}/src/treebox/assets/container/init-firewall.sh +0 -0
  57. {treebox-0.4.0 → treebox-0.5.0}/src/treebox/assets/container/post-create.sh +0 -0
  58. {treebox-0.4.0 → treebox-0.5.0}/src/treebox/assets/pre-push +0 -0
  59. {treebox-0.4.0 → treebox-0.5.0}/src/treebox/ecosystems.py +0 -0
  60. {treebox-0.4.0 → treebox-0.5.0}/src/treebox/names.py +0 -0
  61. {treebox-0.4.0 → treebox-0.5.0}/src/treebox/py.typed +0 -0
  62. {treebox-0.4.0 → treebox-0.5.0}/src/treebox/resolve.py +0 -0
  63. {treebox-0.4.0 → treebox-0.5.0}/src/treebox/runners/__init__.py +0 -0
  64. {treebox-0.4.0 → treebox-0.5.0}/src/treebox/runners/host.py +0 -0
  65. {treebox-0.4.0 → treebox-0.5.0}/src/treebox/status.py +0 -0
  66. {treebox-0.4.0 → treebox-0.5.0}/tests/conftest.py +0 -0
  67. {treebox-0.4.0 → treebox-0.5.0}/uv.lock +0 -0
@@ -21,8 +21,8 @@ uv run --extra dev python -m pytest # full unit + integration suite
21
21
  uv run --extra dev python -m pytest tests/test_units.py::test_name # single test (or -k <pattern>)
22
22
  uv run --extra dev ruff check src tests # lint
23
23
  uv run --extra dev ruff format --check src tests # format check (drop --check to apply)
24
- uv run --extra dev mypy # type check (config in pyproject.toml)
25
- uv run --extra dev pre-commit install # install lint/format/type hooks (see CONTRIBUTING.md)
24
+ uv run --extra dev mypy # strict type check (config in pyproject.toml)
25
+ uv run --extra dev pre-commit install # install lint/format/strict type-check hooks (see CONTRIBUTING.md)
26
26
  ./scripts/validate.sh # lint + format + tests + live host-runner smoke (real uv, throwaway local remote)
27
27
  uv pip install -e ".[dev]" # editable dev environment
28
28
  uv run --extra docs mkdocs serve # docs site (docs/ + mkdocs.yml), live-reloading
@@ -52,13 +52,13 @@ run (pluggable)**.
52
52
  git just works). `HARNESS_COMMANDS` maps `claude`/`codex` to their
53
53
  fully-autonomous launch argv.
54
54
  - **`cli.py`** (Typer) is the entry point: `create [NAME] / enter <ref> /
55
- list / teardown <ref>... / doctor` (`ls`/`rm` are hidden aliases of
56
- list/teardown). `enter`/`teardown` resolve a ref as
55
+ list / teardown <ref>... / doctor / version` (`ls`/`rm` are hidden aliases
56
+ of list/teardown). `enter`/`teardown` resolve a ref as
57
57
  name → live branch → unique substring (`resolve.py`); ambiguity exits 2.
58
- It enforces **stable exit codes** (`0` ok · `2` usage · `3` not-found · `4`
59
- auth/fetch · `5` conflict) and **`--json`** output carrying a
60
- `schemaVersion` that only gains fields within a version — a breaking reshape
61
- bumps it (git-porcelain discipline). Agents
58
+ It enforces **stable exit codes** (`0` ok · `1` runtime/doctor hard-check ·
59
+ `2` usage · `3` not-found · `4` auth/fetch · `5` conflict) and **`--json`**
60
+ output carrying a `schemaVersion` that only gains fields within a version —
61
+ a breaking reshape bumps it (git-porcelain discipline). Agents
62
62
  branch on these, so don't change their meanings casually.
63
63
  - **`ecosystems.py`** detects package managers (uv, npm, pnpm, go, cargo),
64
64
  drives their cache-backed setup, and defines which manifest files feed the
@@ -73,11 +73,14 @@ run (pluggable)**.
73
73
  with `git branch -m`). `create --checkout feature/auth` derives the name by
74
74
  flattening slashes to `--` (`feature--auth`); generated names come from
75
75
  `names.py`.
76
- - **`config.py`** is **user-level TOML only**, never read from the target repo —
77
- a repo-level config could run arbitrary host commands. Same principle in
78
- **`assets.py`**: the sandbox template is operator-owned and rendered into
79
- a host-side dir *beside* the worktree, never inside the mount, so a boxed agent
80
- cannot edit the config that defines its own sandbox.
76
+ - **`config.py`** is **user-level TOML only** (`$TREEBOX_CONFIG`, else
77
+ `$TREEBOX_HOME/config.toml`, default `~/.treebox/config.toml`), never read
78
+ from the target repo a repo-level config could run arbitrary host commands.
79
+ Same principle in **`assets.py`**: templates resolve from
80
+ `$TREEBOX_TEMPLATE_DIR`, then `$TREEBOX_HOME/templates/<name>` (default
81
+ `~/.treebox/templates/<name>`), and the sandbox template is operator-owned
82
+ and rendered into a host-side dir *beside* the worktree, never inside the
83
+ mount, so a boxed agent cannot edit the config that defines its own sandbox.
81
84
 
82
85
  ## Invariants to preserve
83
86
 
@@ -13,7 +13,7 @@ For larger feature ideas, open an issue first so we can decide whether they fit
13
13
  ## Local checks
14
14
 
15
15
  Install the pre-commit hooks once so every commit is auto-formatted, lint-fixed,
16
- and type-checked (`ruff format`, `ruff check --fix`, and `mypy`):
16
+ and type-checked in strict mode (`ruff format`, `ruff check --fix`, and `mypy`):
17
17
 
18
18
  ```bash
19
19
  uv run --extra dev pre-commit install
@@ -32,7 +32,8 @@ will have no reported checks and the PR will stall; the remedy is to push from
32
32
  the workflow using a PAT or GitHub App token stored as a repo secret (e.g.
33
33
  `AUTOFIX_TOKEN`) instead of `GITHUB_TOKEN`.
34
34
 
35
- For code changes, run the relevant checks before opening a PR:
35
+ For code changes, run the relevant checks before opening a PR. Mypy runs in
36
+ strict mode (configured in `pyproject.toml`):
36
37
 
37
38
  ```bash
38
39
  uv run --extra dev python -m pytest
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: treebox
3
- Version: 0.4.0
3
+ Version: 0.5.0
4
4
  Summary: Isolated, ready-to-run git worktrees for AI coding agents — host-native or docker-sandboxed.
5
5
  Project-URL: Homepage, https://github.com/Seth-Peters/treebox
6
6
  Project-URL: Documentation, https://seth-peters.github.io/treebox/
@@ -36,13 +36,13 @@ Requires-Dist: mkdocs-material>=9.5; extra == 'docs'
36
36
  Description-Content-Type: text/markdown
37
37
 
38
38
  <p align="center">
39
- <img src="assets/treebox-logo.png" alt="treebox" width="220">
39
+ <img src="https://raw.githubusercontent.com/Seth-Peters/treebox/main/assets/treebox-logo.png" alt="treebox logo: a small tree growing inside a glass box" width="300">
40
40
  </p>
41
41
 
42
- <h1 align="center">treebox</h1>
42
+ <h1 align="center">Spin up worktrees, put them in a box,<br>tear them down - treebox</h1>
43
43
 
44
44
  <p align="center">
45
- <strong>Isolated, ready-to-run git worktrees for AI coding agents.</strong>
45
+ <strong>Isolated, ready-to-run git worktrees for coding agents.</strong>
46
46
  </p>
47
47
 
48
48
  <p align="center">
@@ -61,17 +61,19 @@ Description-Content-Type: text/markdown
61
61
  <a href="https://www.linkedin.com/in/seth-peters/">Seth Peters</a>
62
62
  </p>
63
63
 
64
+ <p align="center">
65
+ <img src="https://raw.githubusercontent.com/Seth-Peters/treebox/main/assets/treebox-demo.gif" alt="treebox lifecycle demo: create, enter, ls, and an interactive teardown" width="900">
66
+ </p>
67
+
64
68
  ---
65
69
 
66
- Run `treebox create` and it fetches, cuts a worktree from a fresh
67
- `origin/<base>`, copies your `.env`, installs dependencies from a shared
70
+ `treebox` hands coding agents isolated, ready-to-run git worktrees one
71
+ directory per worktree name. `treebox create` fetches, cuts a worktree from a
72
+ fresh `origin/<base>`, copies your `.env`, installs dependencies from a shared
68
73
  cache, and launches `claude` or `codex` inside. No branch name needed up
69
- front: the worktree gets a stable name (yours, or a generated petname) and an
70
- un-pushable `treebox/<name>` placeholder branch the agent renames when the
71
- work takes shape. Agents work the same repo in parallel without collisions —
72
- on a laptop or over plain SSH.
73
-
74
- Built by [Seth Peters](https://www.linkedin.com/in/seth-peters/) as a small, operator-focused layer for AI agent infrastructure: git worktrees, sandbox boundaries, subscription auth, and repeatable developer environments. If that is the kind of problem you are solving, the [docs](https://seth-peters.github.io/treebox/) go deeper on the design tradeoffs — and I'm [on LinkedIn](https://www.linkedin.com/in/seth-peters/) if you want to compare notes.
74
+ front: the worktree gets a stable name and an un-pushable `treebox/<name>`
75
+ placeholder branch the agent renames when the work takes shape. Agents work the same repo
76
+ in parallel without collisions on a laptop or over plain SSH.
75
77
 
76
78
  Provisioning is identical everywhere; a pluggable **isolation mode** decides
77
79
  where the agent runs:
@@ -81,26 +83,6 @@ where the agent runs:
81
83
  | `host` (default) | none | the worktree shell |
82
84
  | `docker` | sandboxed | a docker container, with your `.env` + caches mounted |
83
85
 
84
- ## Every agent ships its own cage
85
-
86
- Every coding agent invents its own answer to "run me in parallel" and "don't
87
- let me touch the wrong thing" — a different config file, a different schema, a
88
- different word for the same idea:
89
-
90
- | Agent | Sandbox / permission config | Built-in worktrees | Config lives in |
91
- | ----- | --------------------------- | ------------------ | --------------- |
92
- | **Claude Code** | `permissions` allow/ask/deny **+** native OS sandbox (Seatbelt/bubblewrap) **+** dev container | Yes (`--worktree`) | `.claude/settings.json`, `.devcontainer/` |
93
- | **OpenAI Codex** | `sandbox_mode` × `approval_policy` (Seatbelt / Landlock+seccomp) | No in CLI (app only) | `~/.codex/config.toml`, `[profiles.*]` |
94
- | **opencode** | `permission` per-tool allow/ask/deny (no OS sandbox) | No (community plugins) | `opencode.json` |
95
- | **pi** | none built-in ("all permissions by default"); BYO Docker/VM + trust prompt | No in core (`pi-subagents`) | `~/.pi/agent/settings.json` |
96
-
97
- Learn one and it teaches you nothing about the next, and none of it ports
98
- across tools. treebox owns the *isolation* layer instead — **one
99
- worktree-per-branch layout, one operator-owned sandbox, one config file** — and
100
- launches your agent of choice inside it. Learn treebox once; swap the agent,
101
- keep the box. Full comparison with citations:
102
- [Agents & sandboxing](https://seth-peters.github.io/treebox/agents/).
103
-
104
86
  ## Install
105
87
 
106
88
  ```bash
@@ -112,7 +94,7 @@ instructions if uv is missing — it never installs a package manager behind
112
94
  your back. Or install directly:
113
95
 
114
96
  ```bash
115
- uv tool install git+https://github.com/Seth-Peters/treebox
97
+ uv tool install treebox
116
98
  ```
117
99
 
118
100
  Host isolation needs only `git` and a logged-in agent CLI (`claude` /
@@ -123,8 +105,7 @@ requirements and installer overrides.
123
105
 
124
106
  ## Usage
125
107
 
126
- The whole surface is five commands, and a worktree's life runs through all of
127
- them.
108
+ The worktree lifecycle is five commands.
128
109
 
129
110
  **Check the host.** `doctor` verifies exactly what `create` will need — git,
130
111
  agent logins, `.env`, credentials for the required fetch — and prints the fix
@@ -189,12 +170,32 @@ treebox teardown fix-auth brave-otter --delete-branch
189
170
  treebox teardown # pick interactively
190
171
  ```
191
172
 
192
- treebox is built to be scripted, including by agents: every command takes
193
- `--json` (data to stdout, diagnostics to stderr, a schema that only gains
194
- fields within a version), `--dry-run` prints the exact commands without running them, and exit
195
- codes are stable (`0` ok · `2` usage · `3` not found · `4` auth · `5`
196
- conflict). Full reference in the
197
- [usage guide](https://seth-peters.github.io/treebox/usage/).
173
+ treebox is built to be scripted, including by agents: the worktree commands and
174
+ `doctor` take `--json` (data to stdout, diagnostics to stderr, a schema that
175
+ only gains fields within a version), `--dry-run` prints the exact commands
176
+ without running them, and exit codes are stable (`0` ok · `1` runtime/doctor
177
+ blocked · `2` usage · `3` not found · `4` auth · `5` conflict). Full
178
+ reference in the [usage guide](https://seth-peters.github.io/treebox/usage/).
179
+
180
+ ## Every agent ships its own cage
181
+
182
+ Every coding agent invents its own answer to "run me in parallel" and "don't
183
+ let me touch the wrong thing" — a different config file, a different schema, a
184
+ different word for the same idea:
185
+
186
+ | Agent | Sandbox / permission config | Built-in worktrees | Config lives in |
187
+ | ----- | --------------------------- | ------------------ | --------------- |
188
+ | **Claude Code** | `permissions` allow/ask/deny **+** native OS sandbox (Seatbelt/bubblewrap) **+** dev container | Yes (`--worktree`) | `.claude/settings.json`, `.devcontainer/` |
189
+ | **OpenAI Codex** | `sandbox_mode` × `approval_policy` (Seatbelt / Landlock+seccomp) | No in CLI (app only) | `~/.codex/config.toml`, `[profiles.*]` |
190
+ | **opencode** | `permission` per-tool allow/ask/deny (no OS sandbox) | No (community plugins) | `opencode.json` |
191
+ | **pi** | none built-in ("all permissions by default"); BYO Docker/VM + trust prompt | No in core (`pi-subagents`) | `~/.pi/agent/settings.json` |
192
+
193
+ Learn one and it teaches you nothing about the next, and none of it ports
194
+ across tools. treebox owns the *isolation* layer instead — **one
195
+ named-worktree layout, one operator-owned sandbox, one config file** — and
196
+ launches your agent of choice inside it. Learn treebox once; swap the agent,
197
+ keep the box. Full comparison with citations:
198
+ [Agents & sandboxing](https://seth-peters.github.io/treebox/agents/).
198
199
 
199
200
  ## Design
200
201
 
@@ -211,14 +212,14 @@ conflict). Full reference in the
211
212
  - **Credentials go in as scoped copies.** Only the agents' login files are
212
213
  copied into a throwaway per-worktree dir — never the live `~/.claude` /
213
214
  `~/.codex` — refreshed on every entry so a host logout or a fresh login
214
- reaches the sandbox next time, and treebox uses your subscription login,
215
- never `ANTHROPIC_API_KEY`.
215
+ reaches the sandbox next time; treebox uses your subscription login.
216
216
 
217
217
  More in [how it works](https://seth-peters.github.io/treebox/how-it-works/).
218
218
 
219
219
  ## Configuration
220
220
 
221
- Optional, and user-level only (`~/.config/treebox/config.toml`) — treebox
221
+ Optional, and user-level only (`$TREEBOX_CONFIG`, else
222
+ `$TREEBOX_HOME/config.toml`, default `~/.treebox/config.toml`) — treebox
222
223
  never reads config from the target repo:
223
224
 
224
225
  ```toml
@@ -233,44 +234,51 @@ covered in the
233
234
 
234
235
  ## Customizing isolation
235
236
 
236
- `docker` isolation is defined by an **operator-owned template** — a
237
- `Dockerfile` + `container.json` you copy out and edit, kept beside the worktree
238
- so a boxed agent can't touch its own cage. The shipped image bundles Node 22,
239
- uv, `gh`, ripgrep, and the agent CLIs, so a Node project often needs no image
240
- change. For a genuinely separate, non-Python box, copy the template and edit
241
- two things global tooling in the `Dockerfile`, and `postCreate` to install
242
- your repo's deps (in docker, setup runs `postCreate`, not the host-mode
243
- ecosystem auto-detect):
237
+ `docker` isolation builds from a **template you own** — a directory of files
238
+ treebox ships and you edit. treebox renders it beside the worktree so a boxed
239
+ agent can't touch its own cage. Three steps:
240
+
241
+ **1. Copy the template out** you edit a copy, never the shipped one. Name it
242
+ (here `node`); a `node` box and a `python` box can coexist, one per stack:
244
243
 
245
244
  ```bash
245
+ mkdir -p ~/.treebox/templates
246
246
  cp -R "$(python -c 'import treebox.assets, pathlib; print(pathlib.Path(treebox.assets.__file__).parent)')/container" \
247
- ~/.config/treebox/templates/node
247
+ ~/.treebox/templates/node
248
248
  ```
249
249
 
250
+ **2. Edit two files.** The shipped image already bundles Node 22, uv, `gh`,
251
+ ripgrep, and the agent CLIs, so most projects touch only these:
252
+
250
253
  ```dockerfile
251
- # ~/.config/treebox/templates/node/Dockerfile — Node 22 is already baked in
254
+ # ~/.treebox/templates/node/Dockerfile — global tooling
252
255
  USER root
253
256
  RUN npm install -g pnpm@9 typescript tsx
254
257
  USER ${USERNAME}
255
258
  ```
256
259
 
257
260
  ```json
258
- // ~/.config/treebox/templates/node/container.json
261
+ // ~/.treebox/templates/node/container.json — your install command
259
262
  "postCreate": "if [ -f pnpm-lock.yaml ]; then pnpm install --frozen-lockfile; elif [ -f package-lock.json ]; then npm ci; else npm install; fi"
260
263
  ```
261
264
 
262
- Then select it per run (`--isolation docker --template node`) or set
263
- `template = "node"` in your config — a `node` box and a `python` box coexist,
264
- one per stack. Full walkthrough in the
265
- [configuration guide](https://seth-peters.github.io/treebox/configuration/#worked-example-a-node-sandbox-not-python).
265
+ (In docker, setup runs `postCreate`, not the host-mode ecosystem auto-detect,
266
+ so a non-Python project wires its install here.)
267
+
268
+ **3. Point treebox at it** — per run with `--isolation docker --template node`,
269
+ or set `template = "node"` in `~/.treebox/config.toml`.
270
+
271
+ Full walkthrough — every file in a template and the `container.json` schema — in
272
+ the [configuration guide](https://seth-peters.github.io/treebox/configuration/#customizing-the-sandbox).
266
273
 
267
274
  ## Development
268
275
 
269
276
  ```bash
270
277
  git clone https://github.com/Seth-Peters/treebox && cd treebox
271
278
  uv run treebox ... # run the CLI from the working tree
272
- uv run --extra dev pre-commit install # lint/format/type hooks (see CONTRIBUTING.md)
279
+ uv run --extra dev pre-commit install # lint/format/strict type-check hooks (see CONTRIBUTING.md)
273
280
  uv run --extra dev python -m pytest # unit + integration suite
281
+ uv run --extra dev mypy # strict type check
274
282
  ./scripts/validate.sh # lint + format + tests + live host-runner smoke
275
283
  ```
276
284
 
@@ -1,11 +1,11 @@
1
1
  <p align="center">
2
- <img src="assets/treebox-logo.png" alt="treebox" width="220">
2
+ <img src="https://raw.githubusercontent.com/Seth-Peters/treebox/main/assets/treebox-logo.png" alt="treebox logo: a small tree growing inside a glass box" width="300">
3
3
  </p>
4
4
 
5
- <h1 align="center">treebox</h1>
5
+ <h1 align="center">Spin up worktrees, put them in a box,<br>tear them down - treebox</h1>
6
6
 
7
7
  <p align="center">
8
- <strong>Isolated, ready-to-run git worktrees for AI coding agents.</strong>
8
+ <strong>Isolated, ready-to-run git worktrees for coding agents.</strong>
9
9
  </p>
10
10
 
11
11
  <p align="center">
@@ -24,17 +24,19 @@
24
24
  <a href="https://www.linkedin.com/in/seth-peters/">Seth Peters</a>
25
25
  </p>
26
26
 
27
+ <p align="center">
28
+ <img src="https://raw.githubusercontent.com/Seth-Peters/treebox/main/assets/treebox-demo.gif" alt="treebox lifecycle demo: create, enter, ls, and an interactive teardown" width="900">
29
+ </p>
30
+
27
31
  ---
28
32
 
29
- Run `treebox create` and it fetches, cuts a worktree from a fresh
30
- `origin/<base>`, copies your `.env`, installs dependencies from a shared
33
+ `treebox` hands coding agents isolated, ready-to-run git worktrees one
34
+ directory per worktree name. `treebox create` fetches, cuts a worktree from a
35
+ fresh `origin/<base>`, copies your `.env`, installs dependencies from a shared
31
36
  cache, and launches `claude` or `codex` inside. No branch name needed up
32
- front: the worktree gets a stable name (yours, or a generated petname) and an
33
- un-pushable `treebox/<name>` placeholder branch the agent renames when the
34
- work takes shape. Agents work the same repo in parallel without collisions —
35
- on a laptop or over plain SSH.
36
-
37
- Built by [Seth Peters](https://www.linkedin.com/in/seth-peters/) as a small, operator-focused layer for AI agent infrastructure: git worktrees, sandbox boundaries, subscription auth, and repeatable developer environments. If that is the kind of problem you are solving, the [docs](https://seth-peters.github.io/treebox/) go deeper on the design tradeoffs — and I'm [on LinkedIn](https://www.linkedin.com/in/seth-peters/) if you want to compare notes.
37
+ front: the worktree gets a stable name and an un-pushable `treebox/<name>`
38
+ placeholder branch the agent renames when the work takes shape. Agents work the same repo
39
+ in parallel without collisions on a laptop or over plain SSH.
38
40
 
39
41
  Provisioning is identical everywhere; a pluggable **isolation mode** decides
40
42
  where the agent runs:
@@ -44,26 +46,6 @@ where the agent runs:
44
46
  | `host` (default) | none | the worktree shell |
45
47
  | `docker` | sandboxed | a docker container, with your `.env` + caches mounted |
46
48
 
47
- ## Every agent ships its own cage
48
-
49
- Every coding agent invents its own answer to "run me in parallel" and "don't
50
- let me touch the wrong thing" — a different config file, a different schema, a
51
- different word for the same idea:
52
-
53
- | Agent | Sandbox / permission config | Built-in worktrees | Config lives in |
54
- | ----- | --------------------------- | ------------------ | --------------- |
55
- | **Claude Code** | `permissions` allow/ask/deny **+** native OS sandbox (Seatbelt/bubblewrap) **+** dev container | Yes (`--worktree`) | `.claude/settings.json`, `.devcontainer/` |
56
- | **OpenAI Codex** | `sandbox_mode` × `approval_policy` (Seatbelt / Landlock+seccomp) | No in CLI (app only) | `~/.codex/config.toml`, `[profiles.*]` |
57
- | **opencode** | `permission` per-tool allow/ask/deny (no OS sandbox) | No (community plugins) | `opencode.json` |
58
- | **pi** | none built-in ("all permissions by default"); BYO Docker/VM + trust prompt | No in core (`pi-subagents`) | `~/.pi/agent/settings.json` |
59
-
60
- Learn one and it teaches you nothing about the next, and none of it ports
61
- across tools. treebox owns the *isolation* layer instead — **one
62
- worktree-per-branch layout, one operator-owned sandbox, one config file** — and
63
- launches your agent of choice inside it. Learn treebox once; swap the agent,
64
- keep the box. Full comparison with citations:
65
- [Agents & sandboxing](https://seth-peters.github.io/treebox/agents/).
66
-
67
49
  ## Install
68
50
 
69
51
  ```bash
@@ -75,7 +57,7 @@ instructions if uv is missing — it never installs a package manager behind
75
57
  your back. Or install directly:
76
58
 
77
59
  ```bash
78
- uv tool install git+https://github.com/Seth-Peters/treebox
60
+ uv tool install treebox
79
61
  ```
80
62
 
81
63
  Host isolation needs only `git` and a logged-in agent CLI (`claude` /
@@ -86,8 +68,7 @@ requirements and installer overrides.
86
68
 
87
69
  ## Usage
88
70
 
89
- The whole surface is five commands, and a worktree's life runs through all of
90
- them.
71
+ The worktree lifecycle is five commands.
91
72
 
92
73
  **Check the host.** `doctor` verifies exactly what `create` will need — git,
93
74
  agent logins, `.env`, credentials for the required fetch — and prints the fix
@@ -152,12 +133,32 @@ treebox teardown fix-auth brave-otter --delete-branch
152
133
  treebox teardown # pick interactively
153
134
  ```
154
135
 
155
- treebox is built to be scripted, including by agents: every command takes
156
- `--json` (data to stdout, diagnostics to stderr, a schema that only gains
157
- fields within a version), `--dry-run` prints the exact commands without running them, and exit
158
- codes are stable (`0` ok · `2` usage · `3` not found · `4` auth · `5`
159
- conflict). Full reference in the
160
- [usage guide](https://seth-peters.github.io/treebox/usage/).
136
+ treebox is built to be scripted, including by agents: the worktree commands and
137
+ `doctor` take `--json` (data to stdout, diagnostics to stderr, a schema that
138
+ only gains fields within a version), `--dry-run` prints the exact commands
139
+ without running them, and exit codes are stable (`0` ok · `1` runtime/doctor
140
+ blocked · `2` usage · `3` not found · `4` auth · `5` conflict). Full
141
+ reference in the [usage guide](https://seth-peters.github.io/treebox/usage/).
142
+
143
+ ## Every agent ships its own cage
144
+
145
+ Every coding agent invents its own answer to "run me in parallel" and "don't
146
+ let me touch the wrong thing" — a different config file, a different schema, a
147
+ different word for the same idea:
148
+
149
+ | Agent | Sandbox / permission config | Built-in worktrees | Config lives in |
150
+ | ----- | --------------------------- | ------------------ | --------------- |
151
+ | **Claude Code** | `permissions` allow/ask/deny **+** native OS sandbox (Seatbelt/bubblewrap) **+** dev container | Yes (`--worktree`) | `.claude/settings.json`, `.devcontainer/` |
152
+ | **OpenAI Codex** | `sandbox_mode` × `approval_policy` (Seatbelt / Landlock+seccomp) | No in CLI (app only) | `~/.codex/config.toml`, `[profiles.*]` |
153
+ | **opencode** | `permission` per-tool allow/ask/deny (no OS sandbox) | No (community plugins) | `opencode.json` |
154
+ | **pi** | none built-in ("all permissions by default"); BYO Docker/VM + trust prompt | No in core (`pi-subagents`) | `~/.pi/agent/settings.json` |
155
+
156
+ Learn one and it teaches you nothing about the next, and none of it ports
157
+ across tools. treebox owns the *isolation* layer instead — **one
158
+ named-worktree layout, one operator-owned sandbox, one config file** — and
159
+ launches your agent of choice inside it. Learn treebox once; swap the agent,
160
+ keep the box. Full comparison with citations:
161
+ [Agents & sandboxing](https://seth-peters.github.io/treebox/agents/).
161
162
 
162
163
  ## Design
163
164
 
@@ -174,14 +175,14 @@ conflict). Full reference in the
174
175
  - **Credentials go in as scoped copies.** Only the agents' login files are
175
176
  copied into a throwaway per-worktree dir — never the live `~/.claude` /
176
177
  `~/.codex` — refreshed on every entry so a host logout or a fresh login
177
- reaches the sandbox next time, and treebox uses your subscription login,
178
- never `ANTHROPIC_API_KEY`.
178
+ reaches the sandbox next time; treebox uses your subscription login.
179
179
 
180
180
  More in [how it works](https://seth-peters.github.io/treebox/how-it-works/).
181
181
 
182
182
  ## Configuration
183
183
 
184
- Optional, and user-level only (`~/.config/treebox/config.toml`) — treebox
184
+ Optional, and user-level only (`$TREEBOX_CONFIG`, else
185
+ `$TREEBOX_HOME/config.toml`, default `~/.treebox/config.toml`) — treebox
185
186
  never reads config from the target repo:
186
187
 
187
188
  ```toml
@@ -196,44 +197,51 @@ covered in the
196
197
 
197
198
  ## Customizing isolation
198
199
 
199
- `docker` isolation is defined by an **operator-owned template** — a
200
- `Dockerfile` + `container.json` you copy out and edit, kept beside the worktree
201
- so a boxed agent can't touch its own cage. The shipped image bundles Node 22,
202
- uv, `gh`, ripgrep, and the agent CLIs, so a Node project often needs no image
203
- change. For a genuinely separate, non-Python box, copy the template and edit
204
- two things global tooling in the `Dockerfile`, and `postCreate` to install
205
- your repo's deps (in docker, setup runs `postCreate`, not the host-mode
206
- ecosystem auto-detect):
200
+ `docker` isolation builds from a **template you own** — a directory of files
201
+ treebox ships and you edit. treebox renders it beside the worktree so a boxed
202
+ agent can't touch its own cage. Three steps:
203
+
204
+ **1. Copy the template out** you edit a copy, never the shipped one. Name it
205
+ (here `node`); a `node` box and a `python` box can coexist, one per stack:
207
206
 
208
207
  ```bash
208
+ mkdir -p ~/.treebox/templates
209
209
  cp -R "$(python -c 'import treebox.assets, pathlib; print(pathlib.Path(treebox.assets.__file__).parent)')/container" \
210
- ~/.config/treebox/templates/node
210
+ ~/.treebox/templates/node
211
211
  ```
212
212
 
213
+ **2. Edit two files.** The shipped image already bundles Node 22, uv, `gh`,
214
+ ripgrep, and the agent CLIs, so most projects touch only these:
215
+
213
216
  ```dockerfile
214
- # ~/.config/treebox/templates/node/Dockerfile — Node 22 is already baked in
217
+ # ~/.treebox/templates/node/Dockerfile — global tooling
215
218
  USER root
216
219
  RUN npm install -g pnpm@9 typescript tsx
217
220
  USER ${USERNAME}
218
221
  ```
219
222
 
220
223
  ```json
221
- // ~/.config/treebox/templates/node/container.json
224
+ // ~/.treebox/templates/node/container.json — your install command
222
225
  "postCreate": "if [ -f pnpm-lock.yaml ]; then pnpm install --frozen-lockfile; elif [ -f package-lock.json ]; then npm ci; else npm install; fi"
223
226
  ```
224
227
 
225
- Then select it per run (`--isolation docker --template node`) or set
226
- `template = "node"` in your config — a `node` box and a `python` box coexist,
227
- one per stack. Full walkthrough in the
228
- [configuration guide](https://seth-peters.github.io/treebox/configuration/#worked-example-a-node-sandbox-not-python).
228
+ (In docker, setup runs `postCreate`, not the host-mode ecosystem auto-detect,
229
+ so a non-Python project wires its install here.)
230
+
231
+ **3. Point treebox at it** — per run with `--isolation docker --template node`,
232
+ or set `template = "node"` in `~/.treebox/config.toml`.
233
+
234
+ Full walkthrough — every file in a template and the `container.json` schema — in
235
+ the [configuration guide](https://seth-peters.github.io/treebox/configuration/#customizing-the-sandbox).
229
236
 
230
237
  ## Development
231
238
 
232
239
  ```bash
233
240
  git clone https://github.com/Seth-Peters/treebox && cd treebox
234
241
  uv run treebox ... # run the CLI from the working tree
235
- uv run --extra dev pre-commit install # lint/format/type hooks (see CONTRIBUTING.md)
242
+ uv run --extra dev pre-commit install # lint/format/strict type-check hooks (see CONTRIBUTING.md)
236
243
  uv run --extra dev python -m pytest # unit + integration suite
244
+ uv run --extra dev mypy # strict type check
237
245
  ./scripts/validate.sh # lint + format + tests + live host-runner smoke
238
246
  ```
239
247
 
Binary file
Binary file
@@ -8,7 +8,7 @@ of words for the same ideas. Learn one and it teaches you nothing about the
8
8
  next.
9
9
 
10
10
  That fragmentation is treebox's reason to exist. treebox owns the *isolation*
11
- layer — one worktree-per-branch layout and one operator-owned sandbox — and
11
+ layer — one named-worktree layout and one operator-owned sandbox — and
12
12
  launches your agent of choice inside it. **One config to learn, any agent in
13
13
  the box.**
14
14
 
@@ -54,8 +54,9 @@ whichever agent you launch:
54
54
 
55
55
  ---
56
56
 
57
- `~/.config/treebox/config.toml` picks the isolation mode, harness, base, caches, and
58
- sandbox template. It's the single source of truth, and it's never read
57
+ `$TREEBOX_CONFIG`, else `$TREEBOX_HOME/config.toml` (default
58
+ `~/.treebox/config.toml`), picks the isolation mode, harness, base, caches,
59
+ and sandbox template. It's the single source of truth, and it's never read
59
60
  from the target repo.
60
61
 
61
62
  - :material-swap-horizontal:{ .lg .middle } **Swap the agent, keep the box**