treebox 0.3.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.
- treebox-0.5.0/.github/workflows/autofix.yml +69 -0
- {treebox-0.3.0 → treebox-0.5.0}/.github/workflows/ci.yml +3 -0
- treebox-0.5.0/.pre-commit-config.yaml +25 -0
- {treebox-0.3.0 → treebox-0.5.0}/CLAUDE.md +19 -14
- treebox-0.5.0/CONTRIBUTING.md +53 -0
- {treebox-0.3.0 → treebox-0.5.0}/PKG-INFO +96 -50
- {treebox-0.3.0 → treebox-0.5.0}/README.md +93 -49
- treebox-0.5.0/assets/treebox-demo.gif +0 -0
- treebox-0.5.0/assets/treebox-logo.png +0 -0
- {treebox-0.3.0 → treebox-0.5.0}/docs/agents.md +4 -3
- treebox-0.5.0/docs/configuration.md +160 -0
- {treebox-0.3.0 → treebox-0.5.0}/docs/how-it-works.md +18 -7
- {treebox-0.3.0 → treebox-0.5.0}/docs/index.md +64 -11
- {treebox-0.3.0 → treebox-0.5.0}/docs/install.md +6 -5
- {treebox-0.3.0 → treebox-0.5.0}/docs/javascripts/treebox.js +20 -13
- {treebox-0.3.0 → treebox-0.5.0}/docs/stylesheets/extra.css +59 -8
- {treebox-0.3.0 → treebox-0.5.0}/docs/usage.md +107 -17
- {treebox-0.3.0 → treebox-0.5.0}/install.sh +4 -12
- {treebox-0.3.0 → treebox-0.5.0}/pyproject.toml +5 -3
- {treebox-0.3.0 → treebox-0.5.0}/scripts/validate.sh +1 -0
- {treebox-0.3.0 → treebox-0.5.0}/skills/treebox/SKILL.md +32 -30
- {treebox-0.3.0 → treebox-0.5.0}/src/treebox/assets.py +24 -5
- {treebox-0.3.0 → treebox-0.5.0}/src/treebox/cli.py +292 -115
- {treebox-0.3.0 → treebox-0.5.0}/src/treebox/config.py +79 -20
- {treebox-0.3.0 → treebox-0.5.0}/src/treebox/forge.py +8 -2
- {treebox-0.3.0 → treebox-0.5.0}/src/treebox/git.py +68 -25
- {treebox-0.3.0 → treebox-0.5.0}/src/treebox/locking.py +2 -1
- {treebox-0.3.0 → treebox-0.5.0}/src/treebox/models.py +26 -0
- {treebox-0.3.0 → treebox-0.5.0}/src/treebox/output.py +32 -18
- {treebox-0.3.0 → treebox-0.5.0}/src/treebox/provision.py +139 -26
- {treebox-0.3.0 → treebox-0.5.0}/src/treebox/runners/base.py +10 -2
- {treebox-0.3.0 → treebox-0.5.0}/src/treebox/runners/docker.py +118 -33
- {treebox-0.3.0 → treebox-0.5.0}/src/treebox/runners/host.py +17 -4
- {treebox-0.3.0 → treebox-0.5.0}/src/treebox/state.py +16 -0
- {treebox-0.3.0 → treebox-0.5.0}/src/treebox/system.py +2 -1
- treebox-0.5.0/tests/test_contract.py +366 -0
- {treebox-0.3.0 → treebox-0.5.0}/tests/test_integration.py +747 -8
- {treebox-0.3.0 → treebox-0.5.0}/tests/test_units.py +1033 -21
- {treebox-0.3.0 → treebox-0.5.0}/uv.lock +93 -0
- treebox-0.3.0/CONTRIBUTING.md +0 -31
- treebox-0.3.0/docs/assets/treebox-logo.png +0 -0
- treebox-0.3.0/docs/configuration.md +0 -88
- {treebox-0.3.0 → treebox-0.5.0}/.agents/skills/no-mistakes/SKILL.md +0 -0
- {treebox-0.3.0 → treebox-0.5.0}/.github/workflows/claude.yml +0 -0
- {treebox-0.3.0 → treebox-0.5.0}/.github/workflows/docs.yml +0 -0
- {treebox-0.3.0 → treebox-0.5.0}/.github/workflows/release.yml +0 -0
- {treebox-0.3.0 → treebox-0.5.0}/.gitignore +0 -0
- {treebox-0.3.0 → treebox-0.5.0}/AGENTS.md +0 -0
- {treebox-0.3.0 → treebox-0.5.0}/LICENSE +0 -0
- {treebox-0.3.0 → treebox-0.5.0}/ROADMAP.md +0 -0
- {treebox-0.3.0 → treebox-0.5.0/docs}/assets/treebox-logo.png +0 -0
- {treebox-0.3.0 → treebox-0.5.0}/hooks/copy_page.py +0 -0
- {treebox-0.3.0 → treebox-0.5.0}/mkdocs.yml +0 -0
- {treebox-0.3.0 → treebox-0.5.0}/src/treebox/__init__.py +0 -0
- {treebox-0.3.0 → treebox-0.5.0}/src/treebox/assets/container/Dockerfile +0 -0
- {treebox-0.3.0 → treebox-0.5.0}/src/treebox/assets/container/allowed-domains.sh +0 -0
- {treebox-0.3.0 → treebox-0.5.0}/src/treebox/assets/container/container.json +0 -0
- {treebox-0.3.0 → treebox-0.5.0}/src/treebox/assets/container/firewall.json +0 -0
- {treebox-0.3.0 → treebox-0.5.0}/src/treebox/assets/container/init-firewall.sh +0 -0
- {treebox-0.3.0 → treebox-0.5.0}/src/treebox/assets/container/post-create.sh +0 -0
- {treebox-0.3.0 → treebox-0.5.0}/src/treebox/assets/pre-push +0 -0
- {treebox-0.3.0 → treebox-0.5.0}/src/treebox/ecosystems.py +0 -0
- {treebox-0.3.0 → treebox-0.5.0}/src/treebox/names.py +0 -0
- {treebox-0.3.0 → treebox-0.5.0}/src/treebox/py.typed +0 -0
- {treebox-0.3.0 → treebox-0.5.0}/src/treebox/resolve.py +0 -0
- {treebox-0.3.0 → treebox-0.5.0}/src/treebox/runners/__init__.py +0 -0
- {treebox-0.3.0 → treebox-0.5.0}/src/treebox/status.py +0 -0
- {treebox-0.3.0 → treebox-0.5.0}/tests/conftest.py +0 -0
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
name: Autofix
|
|
2
|
+
|
|
3
|
+
# Applies ruff's auto-fixes (`ruff check --fix` + `ruff format`) to pull requests
|
|
4
|
+
# and commits the result back to the PR branch. This is the CI counterpart to the
|
|
5
|
+
# pre-commit hooks: if a change lands unformatted (hooks skipped), the branch is
|
|
6
|
+
# fixed in place instead of just failing the CI gate. Skips fork PRs, where the
|
|
7
|
+
# workflow token cannot push, and its own commits, to avoid loops.
|
|
8
|
+
#
|
|
9
|
+
# Gotcha: pushes made with the default GITHUB_TOKEN (from actions/checkout) do
|
|
10
|
+
# NOT trigger new workflow runs. So the auto-fixed commit this job pushes will
|
|
11
|
+
# not re-run ci.yml. Two implications:
|
|
12
|
+
# - If branch protection REQUIRES the CI status checks, the auto-fixed SHA will
|
|
13
|
+
# have no reported checks and the PR will stall. The remedy is to check out
|
|
14
|
+
# and push using a PAT or GitHub App token stored as a repo secret (e.g.
|
|
15
|
+
# AUTOFIX_TOKEN) instead of GITHUB_TOKEN, so the push re-triggers CI.
|
|
16
|
+
# - To close the "merged with a commit CI never validated" hole regardless,
|
|
17
|
+
# this job re-runs the ruff + mypy checks on the fixed tree below and fails
|
|
18
|
+
# rather than pushing if they don't pass, so the exact pushed commit is
|
|
19
|
+
# validated within this run.
|
|
20
|
+
|
|
21
|
+
on:
|
|
22
|
+
pull_request:
|
|
23
|
+
|
|
24
|
+
concurrency:
|
|
25
|
+
group: autofix-${{ github.ref }}
|
|
26
|
+
cancel-in-progress: true
|
|
27
|
+
|
|
28
|
+
permissions:
|
|
29
|
+
contents: write
|
|
30
|
+
|
|
31
|
+
jobs:
|
|
32
|
+
autofix:
|
|
33
|
+
name: ruff --fix
|
|
34
|
+
runs-on: ubuntu-latest
|
|
35
|
+
if: >-
|
|
36
|
+
github.event.pull_request.head.repo.full_name == github.repository &&
|
|
37
|
+
github.actor != 'github-actions[bot]'
|
|
38
|
+
steps:
|
|
39
|
+
- uses: actions/checkout@v7
|
|
40
|
+
with:
|
|
41
|
+
ref: ${{ github.head_ref }}
|
|
42
|
+
|
|
43
|
+
- name: Install uv
|
|
44
|
+
uses: astral-sh/setup-uv@v8.2.0
|
|
45
|
+
with:
|
|
46
|
+
enable-cache: true
|
|
47
|
+
|
|
48
|
+
- name: ruff check --fix
|
|
49
|
+
run: uv run --extra dev ruff check --fix src tests
|
|
50
|
+
|
|
51
|
+
- name: ruff format
|
|
52
|
+
run: uv run --extra dev ruff format src tests
|
|
53
|
+
|
|
54
|
+
- name: Validate fixed tree
|
|
55
|
+
run: |
|
|
56
|
+
uv run --extra dev ruff check src tests
|
|
57
|
+
uv run --extra dev ruff format --check src tests
|
|
58
|
+
uv run --extra dev mypy
|
|
59
|
+
|
|
60
|
+
- name: Commit fixes
|
|
61
|
+
run: |
|
|
62
|
+
if git diff --quiet; then
|
|
63
|
+
echo "No formatting or lint fixes needed."
|
|
64
|
+
exit 0
|
|
65
|
+
fi
|
|
66
|
+
git config user.name "github-actions[bot]"
|
|
67
|
+
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
|
68
|
+
git commit -am "style: apply ruff --fix and format"
|
|
69
|
+
git push
|
|
@@ -29,6 +29,9 @@ jobs:
|
|
|
29
29
|
- name: Lint
|
|
30
30
|
run: uv run -p ${{ matrix.python-version }} --extra dev ruff check src tests
|
|
31
31
|
|
|
32
|
+
- name: Format
|
|
33
|
+
run: uv run -p ${{ matrix.python-version }} --extra dev ruff format --check src tests
|
|
34
|
+
|
|
32
35
|
- name: Type check
|
|
33
36
|
run: uv run -p ${{ matrix.python-version }} --extra dev mypy
|
|
34
37
|
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Pre-commit hooks: keep every commit lint-clean, formatted, and type-checked.
|
|
2
|
+
# Install once with `uv run --extra dev pre-commit install` (or `pre-commit install`).
|
|
3
|
+
# Run against everything with `uv run --extra dev pre-commit run --all-files`.
|
|
4
|
+
#
|
|
5
|
+
# Keep the ruff rev in sync with the `ruff>=…` pin in pyproject.toml's [dev] extra.
|
|
6
|
+
repos:
|
|
7
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
8
|
+
rev: v0.15.20
|
|
9
|
+
hooks:
|
|
10
|
+
# Lint and auto-fix (import sorting, pyupgrade, simplify, …), then format.
|
|
11
|
+
- id: ruff-check
|
|
12
|
+
args: [--fix]
|
|
13
|
+
- id: ruff-format
|
|
14
|
+
|
|
15
|
+
# mypy runs from the project's own dev environment so it sees typer/questionary
|
|
16
|
+
# stubs and the [tool.mypy] config (files = ["src"]). pass_filenames is off
|
|
17
|
+
# because that config already selects the files to check.
|
|
18
|
+
- repo: local
|
|
19
|
+
hooks:
|
|
20
|
+
- id: mypy
|
|
21
|
+
name: mypy
|
|
22
|
+
entry: uv run --extra dev mypy
|
|
23
|
+
language: system
|
|
24
|
+
types: [python]
|
|
25
|
+
pass_filenames: false
|
|
@@ -19,9 +19,11 @@ template it provisions is separately pinned to CPython 3.14.6).
|
|
|
19
19
|
uv run treebox ... # run the CLI from the working tree
|
|
20
20
|
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
|
-
ruff check src tests
|
|
23
|
-
uv run --extra dev
|
|
24
|
-
|
|
22
|
+
uv run --extra dev ruff check src tests # lint
|
|
23
|
+
uv run --extra dev ruff format --check src tests # format check (drop --check to apply)
|
|
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
|
+
./scripts/validate.sh # lint + format + tests + live host-runner smoke (real uv, throwaway local remote)
|
|
25
27
|
uv pip install -e ".[dev]" # editable dev environment
|
|
26
28
|
uv run --extra docs mkdocs serve # docs site (docs/ + mkdocs.yml), live-reloading
|
|
27
29
|
uv run --extra docs mkdocs build --strict # build docs to site/ (gitignored)
|
|
@@ -50,13 +52,13 @@ run (pluggable)**.
|
|
|
50
52
|
git just works). `HARNESS_COMMANDS` maps `claude`/`codex` to their
|
|
51
53
|
fully-autonomous launch argv.
|
|
52
54
|
- **`cli.py`** (Typer) is the entry point: `create [NAME] / enter <ref> /
|
|
53
|
-
list / teardown <ref>... / doctor` (`ls`/`rm` are hidden aliases
|
|
54
|
-
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
|
|
55
57
|
name → live branch → unique substring (`resolve.py`); ambiguity exits 2.
|
|
56
|
-
It enforces **stable exit codes** (`0` ok · `
|
|
57
|
-
auth/fetch · `5` conflict) and **`--json`**
|
|
58
|
-
`schemaVersion` that only gains fields within a version —
|
|
59
|
-
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
|
|
60
62
|
branch on these, so don't change their meanings casually.
|
|
61
63
|
- **`ecosystems.py`** detects package managers (uv, npm, pnpm, go, cargo),
|
|
62
64
|
drives their cache-backed setup, and defines which manifest files feed the
|
|
@@ -71,11 +73,14 @@ run (pluggable)**.
|
|
|
71
73
|
with `git branch -m`). `create --checkout feature/auth` derives the name by
|
|
72
74
|
flattening slashes to `--` (`feature--auth`); generated names come from
|
|
73
75
|
`names.py`.
|
|
74
|
-
- **`config.py`** is **user-level TOML only
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
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.
|
|
79
84
|
|
|
80
85
|
## Invariants to preserve
|
|
81
86
|
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
treebox is a small, one-maintainer project. Contributions are welcome, but the scope is intentionally modest.
|
|
4
|
+
|
|
5
|
+
## Good contributions
|
|
6
|
+
|
|
7
|
+
- Bug fixes with a clear reproduction.
|
|
8
|
+
- Documentation fixes that make install, usage, or runner behavior clearer.
|
|
9
|
+
- Small compatibility fixes that preserve the existing CLI and scripting behavior.
|
|
10
|
+
|
|
11
|
+
For larger feature ideas, open an issue first so we can decide whether they fit the project.
|
|
12
|
+
|
|
13
|
+
## Local checks
|
|
14
|
+
|
|
15
|
+
Install the pre-commit hooks once so every commit is auto-formatted, lint-fixed,
|
|
16
|
+
and type-checked in strict mode (`ruff format`, `ruff check --fix`, and `mypy`):
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
uv run --extra dev pre-commit install
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Note: `pre-commit install` has no effect inside a treebox worktree, because
|
|
23
|
+
treebox sets `core.hooksPath` for its own pre-push guard, so git ignores the
|
|
24
|
+
hook pre-commit writes to `.git/hooks`. Run the hooks manually there with
|
|
25
|
+
`uv run --extra dev pre-commit run --all-files`.
|
|
26
|
+
|
|
27
|
+
If the hooks are skipped, the Autofix CI workflow applies the same `ruff --fix`
|
|
28
|
+
and `ruff format` to same-repo PRs and pushes the result back. Note that pushes
|
|
29
|
+
made with the default `GITHUB_TOKEN` do not re-trigger CI, so if this repo ever
|
|
30
|
+
requires the CI status checks under branch protection, the auto-fixed commit
|
|
31
|
+
will have no reported checks and the PR will stall; the remedy is to push from
|
|
32
|
+
the workflow using a PAT or GitHub App token stored as a repo secret (e.g.
|
|
33
|
+
`AUTOFIX_TOKEN`) instead of `GITHUB_TOKEN`.
|
|
34
|
+
|
|
35
|
+
For code changes, run the relevant checks before opening a PR. Mypy runs in
|
|
36
|
+
strict mode (configured in `pyproject.toml`):
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
uv run --extra dev python -m pytest
|
|
40
|
+
uv run --extra dev ruff check src tests
|
|
41
|
+
uv run --extra dev ruff format --check src tests
|
|
42
|
+
uv run --extra dev mypy
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
For docs-only changes, this is enough:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
uv run --extra docs mkdocs build --strict
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Boundaries
|
|
52
|
+
|
|
53
|
+
Please preserve the basics: fresh refs by default, no trust in target-repo sandbox config, stable CLI output for scripts, and subscription-based agent auth.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: treebox
|
|
3
|
-
Version: 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/
|
|
@@ -23,9 +23,11 @@ Classifier: Programming Language :: Python :: 3.14
|
|
|
23
23
|
Classifier: Topic :: Software Development :: Version Control :: Git
|
|
24
24
|
Requires-Python: >=3.11
|
|
25
25
|
Requires-Dist: questionary<3,>=2
|
|
26
|
+
Requires-Dist: rich<16,>=13
|
|
26
27
|
Requires-Dist: typer<1.0,>=0.12
|
|
27
28
|
Provides-Extra: dev
|
|
28
29
|
Requires-Dist: mypy>=1.14; extra == 'dev'
|
|
30
|
+
Requires-Dist: pre-commit>=4; extra == 'dev'
|
|
29
31
|
Requires-Dist: pytest-cov>=6; extra == 'dev'
|
|
30
32
|
Requires-Dist: pytest>=8; extra == 'dev'
|
|
31
33
|
Requires-Dist: ruff>=0.15; extra == 'dev'
|
|
@@ -34,13 +36,13 @@ Requires-Dist: mkdocs-material>=9.5; extra == 'docs'
|
|
|
34
36
|
Description-Content-Type: text/markdown
|
|
35
37
|
|
|
36
38
|
<p align="center">
|
|
37
|
-
<img src="assets/treebox-logo.png" alt="treebox" width="
|
|
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">
|
|
38
40
|
</p>
|
|
39
41
|
|
|
40
|
-
<h1 align="center">treebox</h1>
|
|
42
|
+
<h1 align="center">Spin up worktrees, put them in a box,<br>tear them down - treebox</h1>
|
|
41
43
|
|
|
42
44
|
<p align="center">
|
|
43
|
-
<strong>Isolated, ready-to-run git worktrees for
|
|
45
|
+
<strong>Isolated, ready-to-run git worktrees for coding agents.</strong>
|
|
44
46
|
</p>
|
|
45
47
|
|
|
46
48
|
<p align="center">
|
|
@@ -59,17 +61,19 @@ Description-Content-Type: text/markdown
|
|
|
59
61
|
<a href="https://www.linkedin.com/in/seth-peters/">Seth Peters</a>
|
|
60
62
|
</p>
|
|
61
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
|
+
|
|
62
68
|
---
|
|
63
69
|
|
|
64
|
-
|
|
65
|
-
`
|
|
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
|
|
66
73
|
cache, and launches `claude` or `codex` inside. No branch name needed up
|
|
67
|
-
front: the worktree gets a stable name
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
on a laptop or over plain SSH.
|
|
71
|
-
|
|
72
|
-
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.
|
|
73
77
|
|
|
74
78
|
Provisioning is identical everywhere; a pluggable **isolation mode** decides
|
|
75
79
|
where the agent runs:
|
|
@@ -79,26 +83,6 @@ where the agent runs:
|
|
|
79
83
|
| `host` (default) | none | the worktree shell |
|
|
80
84
|
| `docker` | sandboxed | a docker container, with your `.env` + caches mounted |
|
|
81
85
|
|
|
82
|
-
## Every agent ships its own cage
|
|
83
|
-
|
|
84
|
-
Every coding agent invents its own answer to "run me in parallel" and "don't
|
|
85
|
-
let me touch the wrong thing" — a different config file, a different schema, a
|
|
86
|
-
different word for the same idea:
|
|
87
|
-
|
|
88
|
-
| Agent | Sandbox / permission config | Built-in worktrees | Config lives in |
|
|
89
|
-
| ----- | --------------------------- | ------------------ | --------------- |
|
|
90
|
-
| **Claude Code** | `permissions` allow/ask/deny **+** native OS sandbox (Seatbelt/bubblewrap) **+** dev container | Yes (`--worktree`) | `.claude/settings.json`, `.devcontainer/` |
|
|
91
|
-
| **OpenAI Codex** | `sandbox_mode` × `approval_policy` (Seatbelt / Landlock+seccomp) | No in CLI (app only) | `~/.codex/config.toml`, `[profiles.*]` |
|
|
92
|
-
| **opencode** | `permission` per-tool allow/ask/deny (no OS sandbox) | No (community plugins) | `opencode.json` |
|
|
93
|
-
| **pi** | none built-in ("all permissions by default"); BYO Docker/VM + trust prompt | No in core (`pi-subagents`) | `~/.pi/agent/settings.json` |
|
|
94
|
-
|
|
95
|
-
Learn one and it teaches you nothing about the next, and none of it ports
|
|
96
|
-
across tools. treebox owns the *isolation* layer instead — **one
|
|
97
|
-
worktree-per-branch layout, one operator-owned sandbox, one config file** — and
|
|
98
|
-
launches your agent of choice inside it. Learn treebox once; swap the agent,
|
|
99
|
-
keep the box. Full comparison with citations:
|
|
100
|
-
[Agents & sandboxing](https://seth-peters.github.io/treebox/agents/).
|
|
101
|
-
|
|
102
86
|
## Install
|
|
103
87
|
|
|
104
88
|
```bash
|
|
@@ -110,7 +94,7 @@ instructions if uv is missing — it never installs a package manager behind
|
|
|
110
94
|
your back. Or install directly:
|
|
111
95
|
|
|
112
96
|
```bash
|
|
113
|
-
uv tool install
|
|
97
|
+
uv tool install treebox
|
|
114
98
|
```
|
|
115
99
|
|
|
116
100
|
Host isolation needs only `git` and a logged-in agent CLI (`claude` /
|
|
@@ -121,8 +105,7 @@ requirements and installer overrides.
|
|
|
121
105
|
|
|
122
106
|
## Usage
|
|
123
107
|
|
|
124
|
-
The
|
|
125
|
-
them.
|
|
108
|
+
The worktree lifecycle is five commands.
|
|
126
109
|
|
|
127
110
|
**Check the host.** `doctor` verifies exactly what `create` will need — git,
|
|
128
111
|
agent logins, `.env`, credentials for the required fetch — and prints the fix
|
|
@@ -149,7 +132,7 @@ the branch starts as a `treebox/<name>` placeholder that a per-worktree
|
|
|
149
132
|
pre-push guard keeps **un-pushable** — rename it conventionally
|
|
150
133
|
(`git branch -m feature/user-auth`, `fix/login-race`, `chore/bump-deps`, …)
|
|
151
134
|
when the work has a shape, then push. So a machine-generated name can never
|
|
152
|
-
become a PR title.
|
|
135
|
+
become a PR title. `--checkout` is the one path that skips the placeholder: it checks out an
|
|
153
136
|
existing branch exactly.
|
|
154
137
|
|
|
155
138
|
`--base` takes any branch, not just `main` — branch off `dev`, or stack a new
|
|
@@ -157,10 +140,11 @@ worktree on top of an existing PR's branch, even while that branch is checked
|
|
|
157
140
|
out in another worktree. It resolves as the freshly fetched `origin/<base>`,
|
|
158
141
|
so push the base first if its latest commits only exist locally.
|
|
159
142
|
|
|
160
|
-
**Enter.** Come back to an existing worktree
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
143
|
+
**Enter.** Come back to an existing worktree. By default it reuses the harness
|
|
144
|
+
the worktree was created with; an explicit `--harness` overrides it for that
|
|
145
|
+
session only, without changing what's recorded on disk. The ref is the name,
|
|
146
|
+
the *current* branch (renames are followed live), or a unique substring of
|
|
147
|
+
either. Dependencies re-sync only if the lockfile changed since last time:
|
|
164
148
|
|
|
165
149
|
```bash
|
|
166
150
|
treebox enter fix-auth --harness claude
|
|
@@ -186,12 +170,32 @@ treebox teardown fix-auth brave-otter --delete-branch
|
|
|
186
170
|
treebox teardown # pick interactively
|
|
187
171
|
```
|
|
188
172
|
|
|
189
|
-
treebox is built to be scripted, including by agents:
|
|
190
|
-
`--json` (data to stdout, diagnostics to stderr, a schema that
|
|
191
|
-
fields within a version), `--dry-run` prints the exact commands
|
|
192
|
-
codes are stable (`0` ok · `
|
|
193
|
-
conflict). Full
|
|
194
|
-
[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/).
|
|
195
199
|
|
|
196
200
|
## Design
|
|
197
201
|
|
|
@@ -207,14 +211,15 @@ conflict). Full reference in the
|
|
|
207
211
|
hooks are ignored.
|
|
208
212
|
- **Credentials go in as scoped copies.** Only the agents' login files are
|
|
209
213
|
copied into a throwaway per-worktree dir — never the live `~/.claude` /
|
|
210
|
-
`~/.codex` —
|
|
211
|
-
|
|
214
|
+
`~/.codex` — refreshed on every entry so a host logout or a fresh login
|
|
215
|
+
reaches the sandbox next time; treebox uses your subscription login.
|
|
212
216
|
|
|
213
217
|
More in [how it works](https://seth-peters.github.io/treebox/how-it-works/).
|
|
214
218
|
|
|
215
219
|
## Configuration
|
|
216
220
|
|
|
217
|
-
Optional, and user-level only (
|
|
221
|
+
Optional, and user-level only (`$TREEBOX_CONFIG`, else
|
|
222
|
+
`$TREEBOX_HOME/config.toml`, default `~/.treebox/config.toml`) — treebox
|
|
218
223
|
never reads config from the target repo:
|
|
219
224
|
|
|
220
225
|
```toml
|
|
@@ -227,13 +232,54 @@ All keys, shared-cache overrides, setup hooks, and sandbox templates are
|
|
|
227
232
|
covered in the
|
|
228
233
|
[configuration guide](https://seth-peters.github.io/treebox/configuration/).
|
|
229
234
|
|
|
235
|
+
## Customizing isolation
|
|
236
|
+
|
|
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:
|
|
243
|
+
|
|
244
|
+
```bash
|
|
245
|
+
mkdir -p ~/.treebox/templates
|
|
246
|
+
cp -R "$(python -c 'import treebox.assets, pathlib; print(pathlib.Path(treebox.assets.__file__).parent)')/container" \
|
|
247
|
+
~/.treebox/templates/node
|
|
248
|
+
```
|
|
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
|
+
|
|
253
|
+
```dockerfile
|
|
254
|
+
# ~/.treebox/templates/node/Dockerfile — global tooling
|
|
255
|
+
USER root
|
|
256
|
+
RUN npm install -g pnpm@9 typescript tsx
|
|
257
|
+
USER ${USERNAME}
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
```json
|
|
261
|
+
// ~/.treebox/templates/node/container.json — your install command
|
|
262
|
+
"postCreate": "if [ -f pnpm-lock.yaml ]; then pnpm install --frozen-lockfile; elif [ -f package-lock.json ]; then npm ci; else npm install; fi"
|
|
263
|
+
```
|
|
264
|
+
|
|
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).
|
|
273
|
+
|
|
230
274
|
## Development
|
|
231
275
|
|
|
232
276
|
```bash
|
|
233
277
|
git clone https://github.com/Seth-Peters/treebox && cd treebox
|
|
234
278
|
uv run treebox ... # run the CLI from the working tree
|
|
279
|
+
uv run --extra dev pre-commit install # lint/format/strict type-check hooks (see CONTRIBUTING.md)
|
|
235
280
|
uv run --extra dev python -m pytest # unit + integration suite
|
|
236
|
-
|
|
281
|
+
uv run --extra dev mypy # strict type check
|
|
282
|
+
./scripts/validate.sh # lint + format + tests + live host-runner smoke
|
|
237
283
|
```
|
|
238
284
|
|
|
239
285
|
## Contributing
|