treebox 0.1.0__tar.gz → 0.2.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 (62) hide show
  1. treebox-0.2.0/.github/workflows/auto-release.yml +155 -0
  2. {treebox-0.1.0 → treebox-0.2.0}/.github/workflows/release.yml +16 -19
  3. {treebox-0.1.0 → treebox-0.2.0}/.gitignore +1 -0
  4. {treebox-0.1.0 → treebox-0.2.0}/CLAUDE.md +7 -5
  5. {treebox-0.1.0 → treebox-0.2.0}/PKG-INFO +19 -14
  6. {treebox-0.1.0 → treebox-0.2.0}/README.md +17 -13
  7. {treebox-0.1.0 → treebox-0.2.0}/docs/agents.md +3 -3
  8. {treebox-0.1.0 → treebox-0.2.0}/docs/configuration.md +7 -7
  9. {treebox-0.1.0 → treebox-0.2.0}/docs/how-it-works.md +8 -7
  10. {treebox-0.1.0 → treebox-0.2.0}/docs/index.md +10 -10
  11. {treebox-0.1.0 → treebox-0.2.0}/docs/install.md +8 -8
  12. {treebox-0.1.0 → treebox-0.2.0}/docs/usage.md +88 -21
  13. {treebox-0.1.0 → treebox-0.2.0}/pyproject.toml +1 -1
  14. {treebox-0.1.0 → treebox-0.2.0}/scripts/validate.sh +1 -1
  15. {treebox-0.1.0 → treebox-0.2.0}/skills/treebox/SKILL.md +18 -18
  16. {treebox-0.1.0 → treebox-0.2.0}/src/treebox/cli.py +542 -173
  17. {treebox-0.1.0 → treebox-0.2.0}/src/treebox/config.py +14 -13
  18. treebox-0.2.0/src/treebox/forge.py +195 -0
  19. {treebox-0.1.0 → treebox-0.2.0}/src/treebox/git.py +93 -1
  20. {treebox-0.1.0 → treebox-0.2.0}/src/treebox/models.py +2 -2
  21. {treebox-0.1.0 → treebox-0.2.0}/src/treebox/output.py +5 -5
  22. {treebox-0.1.0 → treebox-0.2.0}/src/treebox/provision.py +37 -15
  23. {treebox-0.1.0 → treebox-0.2.0}/src/treebox/runners/__init__.py +3 -3
  24. {treebox-0.1.0 → treebox-0.2.0}/src/treebox/runners/base.py +3 -3
  25. {treebox-0.1.0 → treebox-0.2.0}/src/treebox/runners/docker.py +14 -14
  26. {treebox-0.1.0 → treebox-0.2.0}/src/treebox/runners/host.py +8 -8
  27. {treebox-0.1.0 → treebox-0.2.0}/src/treebox/state.py +12 -4
  28. treebox-0.2.0/src/treebox/status.py +86 -0
  29. {treebox-0.1.0 → treebox-0.2.0}/src/treebox/system.py +4 -4
  30. {treebox-0.1.0 → treebox-0.2.0}/tests/test_integration.py +354 -20
  31. {treebox-0.1.0 → treebox-0.2.0}/tests/test_units.py +372 -47
  32. {treebox-0.1.0 → treebox-0.2.0}/uv.lock +35 -0
  33. treebox-0.1.0/.ai/docs/brainstorms/2026-07-02-branchless-create-brainstorm.md +0 -132
  34. {treebox-0.1.0 → treebox-0.2.0}/.agents/skills/no-mistakes/SKILL.md +0 -0
  35. {treebox-0.1.0 → treebox-0.2.0}/.github/workflows/ci.yml +0 -0
  36. {treebox-0.1.0 → treebox-0.2.0}/.github/workflows/claude.yml +0 -0
  37. {treebox-0.1.0 → treebox-0.2.0}/.github/workflows/docs.yml +0 -0
  38. {treebox-0.1.0 → treebox-0.2.0}/AGENTS.md +0 -0
  39. {treebox-0.1.0 → treebox-0.2.0}/CONTRIBUTING.md +0 -0
  40. {treebox-0.1.0 → treebox-0.2.0}/LICENSE +0 -0
  41. {treebox-0.1.0 → treebox-0.2.0}/ROADMAP.md +0 -0
  42. {treebox-0.1.0 → treebox-0.2.0}/assets/treebox-logo.png +0 -0
  43. {treebox-0.1.0 → treebox-0.2.0}/docs/assets/treebox-logo.png +0 -0
  44. {treebox-0.1.0 → treebox-0.2.0}/docs/javascripts/treebox.js +0 -0
  45. {treebox-0.1.0 → treebox-0.2.0}/docs/stylesheets/extra.css +0 -0
  46. {treebox-0.1.0 → treebox-0.2.0}/install.sh +0 -0
  47. {treebox-0.1.0 → treebox-0.2.0}/mkdocs.yml +0 -0
  48. {treebox-0.1.0 → treebox-0.2.0}/src/treebox/__init__.py +0 -0
  49. {treebox-0.1.0 → treebox-0.2.0}/src/treebox/assets/container/Dockerfile +0 -0
  50. {treebox-0.1.0 → treebox-0.2.0}/src/treebox/assets/container/allowed-domains.sh +0 -0
  51. {treebox-0.1.0 → treebox-0.2.0}/src/treebox/assets/container/container.json +0 -0
  52. {treebox-0.1.0 → treebox-0.2.0}/src/treebox/assets/container/firewall.json +0 -0
  53. {treebox-0.1.0 → treebox-0.2.0}/src/treebox/assets/container/init-firewall.sh +0 -0
  54. {treebox-0.1.0 → treebox-0.2.0}/src/treebox/assets/container/post-create.sh +0 -0
  55. {treebox-0.1.0 → treebox-0.2.0}/src/treebox/assets/pre-push +0 -0
  56. {treebox-0.1.0 → treebox-0.2.0}/src/treebox/assets.py +0 -0
  57. {treebox-0.1.0 → treebox-0.2.0}/src/treebox/ecosystems.py +0 -0
  58. {treebox-0.1.0 → treebox-0.2.0}/src/treebox/locking.py +0 -0
  59. {treebox-0.1.0 → treebox-0.2.0}/src/treebox/names.py +0 -0
  60. {treebox-0.1.0 → treebox-0.2.0}/src/treebox/py.typed +0 -0
  61. {treebox-0.1.0 → treebox-0.2.0}/src/treebox/resolve.py +0 -0
  62. {treebox-0.1.0 → treebox-0.2.0}/tests/conftest.py +0 -0
@@ -0,0 +1,155 @@
1
+ name: Auto release
2
+
3
+ # After CI succeeds on a push to main: read the Conventional Commit subjects
4
+ # since the last `v*` tag, derive the semver bump (`!`/BREAKING CHANGE ->
5
+ # major, feat -> minor, fix/perf -> patch), push the new `vX.Y.Z` tag, publish
6
+ # to PyPI, and create a GitHub Release. Merges with no release-worthy commits
7
+ # (docs, chore, refactor, ...) are a no-op — no tag, no upload. A red CI run
8
+ # never releases: the tag job only fires via workflow_run when CI concluded
9
+ # successfully, and it tags the exact commit CI validated (head_sha), not
10
+ # whatever main points at by then.
11
+ #
12
+ # While the major version is 0, breaking changes bump the minor instead
13
+ # (0.4.x -> 0.5.0), matching cargo/semantic-release conventions.
14
+ #
15
+ # Publishing uses PyPI Trusted Publishing (OIDC) like release.yml — no API
16
+ # tokens. One-time setup on https://pypi.org:
17
+ # Account -> Publishing -> add a "pending" trusted publisher with:
18
+ # owner = Seth-Peters, repo = treebox,
19
+ # workflow = auto-release.yml, environment = pypi
20
+ #
21
+ # The tag is pushed with the workflow's GITHUB_TOKEN, which GitHub
22
+ # deliberately does not let trigger other workflows — so release.yml does NOT
23
+ # fire and nothing publishes twice. release.yml stays as the manual /
24
+ # TestPyPI path.
25
+
26
+ on:
27
+ workflow_run:
28
+ workflows: [CI]
29
+ types: [completed]
30
+ branches:
31
+ - main
32
+
33
+ # Serialize runs so back-to-back merges tag sequentially instead of racing.
34
+ concurrency:
35
+ group: auto-release
36
+ cancel-in-progress: false
37
+
38
+ jobs:
39
+ tag:
40
+ name: Compute version bump
41
+ # Only a green CI run on a push to main releases; the branches filter above
42
+ # matches head_branch, so the event guard keeps PR runs from a branch that
43
+ # happens to be named main from slipping through.
44
+ if: >-
45
+ github.event.workflow_run.conclusion == 'success' &&
46
+ github.event.workflow_run.event == 'push'
47
+ runs-on: ubuntu-latest
48
+ permissions:
49
+ contents: write # push the version tag
50
+ outputs:
51
+ released: ${{ steps.bump.outputs.released }}
52
+ tag: ${{ steps.bump.outputs.tag }}
53
+ steps:
54
+ - uses: actions/checkout@v7
55
+ with:
56
+ ref: ${{ github.event.workflow_run.head_sha }} # the commit CI validated
57
+ fetch-depth: 0 # full history + tags to find the last release and scan commits
58
+
59
+ - name: Derive next version from Conventional Commits
60
+ id: bump
61
+ run: |
62
+ last=$(git describe --tags --abbrev=0 --match 'v[0-9]*')
63
+ echo "last release: $last"
64
+
65
+ rank=0 # 0 none · 1 patch · 2 minor · 3 major
66
+ for sha in $(git rev-list "$last"..HEAD); do
67
+ subject=$(git log -1 --format=%s "$sha")
68
+ body=$(git log -1 --format=%b "$sha")
69
+ if [[ "$subject" =~ ^[a-z]+(\(.+\))?!: ]] || grep -q '^BREAKING CHANGE' <<<"$body"; then
70
+ rank=3
71
+ elif [[ "$subject" =~ ^feat(\(.+\))?: ]]; then
72
+ ((rank < 2)) && rank=2
73
+ elif [[ "$subject" =~ ^(fix|perf)(\(.+\))?: ]]; then
74
+ ((rank < 1)) && rank=1
75
+ fi
76
+ done
77
+
78
+ ver=${last#v}
79
+ IFS=. read -r maj min pat <<<"$ver"
80
+ case $rank in
81
+ 3) if ((maj == 0)); then new="0.$((min + 1)).0"; else new="$((maj + 1)).0.0"; fi ;;
82
+ 2) new="$maj.$((min + 1)).0" ;;
83
+ 1) new="$maj.$min.$((pat + 1))" ;;
84
+ 0)
85
+ echo "no release-worthy commits since $last — skipping release"
86
+ echo "released=false" >>"$GITHUB_OUTPUT"
87
+ exit 0
88
+ ;;
89
+ esac
90
+
91
+ echo "tagging v$new"
92
+ git tag "v$new"
93
+ git push origin "v$new"
94
+ echo "released=true" >>"$GITHUB_OUTPUT"
95
+ echo "tag=v$new" >>"$GITHUB_OUTPUT"
96
+
97
+ build:
98
+ name: Build distributions
99
+ needs: tag
100
+ if: needs.tag.outputs.released == 'true'
101
+ runs-on: ubuntu-latest
102
+ steps:
103
+ - uses: actions/checkout@v7
104
+ with:
105
+ ref: ${{ needs.tag.outputs.tag }}
106
+ fetch-depth: 0 # full history + tags so hatch-vcs can derive the version
107
+
108
+ - name: Install uv
109
+ uses: astral-sh/setup-uv@v8.2.0
110
+
111
+ - name: Build sdist + wheel
112
+ run: uv build
113
+
114
+ - name: Show version being published
115
+ run: ls -1 dist/
116
+
117
+ - uses: actions/upload-artifact@v7
118
+ with:
119
+ name: dist
120
+ path: dist/
121
+
122
+ publish-pypi:
123
+ name: Publish to PyPI
124
+ needs: build
125
+ runs-on: ubuntu-latest
126
+ environment: pypi
127
+ permissions:
128
+ id-token: write # required for trusted publishing (OIDC)
129
+ steps:
130
+ - uses: actions/download-artifact@v8
131
+ with:
132
+ name: dist
133
+ path: dist/
134
+
135
+ - name: Publish
136
+ uses: pypa/gh-action-pypi-publish@release/v1
137
+
138
+ github-release:
139
+ name: GitHub Release
140
+ needs: [tag, publish-pypi]
141
+ runs-on: ubuntu-latest
142
+ permissions:
143
+ contents: write
144
+ steps:
145
+ - uses: actions/download-artifact@v8
146
+ with:
147
+ name: dist
148
+ path: dist/
149
+
150
+ - name: Create release
151
+ uses: softprops/action-gh-release@v3
152
+ with:
153
+ tag_name: ${{ needs.tag.outputs.tag }}
154
+ generate_release_notes: true
155
+ files: dist/*
@@ -2,17 +2,14 @@ name: Release
2
2
 
3
3
  # Build and publish treebox.
4
4
  #
5
- # * Push a tag like `v0.4.0` -> clean release version (0.4.0), publish to
6
- # TestPyPI + PyPI, then create GitHub Release.
7
- # * Manually run via "Run workflow" -> build current commit as a dev version and
8
- # publish to TestPyPI only.
5
+ # * Push a tag like `v0.4.0` -> clean release version (0.4.0), publish to PyPI + GitHub Release.
6
+ # * Manually run via "Run workflow" -> builds the current commit as a dev version, publishes to TestPyPI.
9
7
  #
10
8
  # Publishing uses PyPI Trusted Publishing (OIDC) — there are NO API tokens or
11
- # secrets to manage. One-time setup:
12
- # test.pypi.org project -> Publishing -> GitHub publisher:
13
- # owner = Seth-Peters, repo = treebox, workflow = release.yml, environment = testpypi
14
- # pypi.org project -> Publishing -> GitHub publisher:
9
+ # secrets to manage. One-time setup, once per index:
10
+ # https://pypi.org -> Account -> Publishing -> add a "pending" trusted publisher:
15
11
  # owner = Seth-Peters, repo = treebox, workflow = release.yml, environment = pypi
12
+ # https://test.pypi.org -> same, with environment = testpypi
16
13
 
17
14
  on:
18
15
  push:
@@ -43,11 +40,12 @@ jobs:
43
40
  name: dist
44
41
  path: dist/
45
42
 
46
- publish-testpypi:
47
- name: Publish to TestPyPI
43
+ publish-pypi:
44
+ name: Publish to PyPI
48
45
  needs: build
46
+ if: startsWith(github.ref, 'refs/tags/v')
49
47
  runs-on: ubuntu-latest
50
- environment: testpypi
48
+ environment: pypi
51
49
  permissions:
52
50
  id-token: write # required for trusted publishing (OIDC)
53
51
  steps:
@@ -58,15 +56,13 @@ jobs:
58
56
 
59
57
  - name: Publish
60
58
  uses: pypa/gh-action-pypi-publish@release/v1
61
- with:
62
- repository-url: https://test.pypi.org/legacy/
63
59
 
64
- publish-pypi:
65
- name: Publish to PyPI
60
+ publish-testpypi:
61
+ name: Publish dev build to TestPyPI
66
62
  needs: build
67
- if: startsWith(github.ref, 'refs/tags/v')
63
+ if: github.event_name == 'workflow_dispatch'
68
64
  runs-on: ubuntu-latest
69
- environment: pypi
65
+ environment: testpypi
70
66
  permissions:
71
67
  id-token: write # required for trusted publishing (OIDC)
72
68
  steps:
@@ -77,11 +73,12 @@ jobs:
77
73
 
78
74
  - name: Publish
79
75
  uses: pypa/gh-action-pypi-publish@release/v1
76
+ with:
77
+ repository-url: https://test.pypi.org/legacy/
80
78
 
81
79
  github-release:
82
80
  name: GitHub Release
83
- needs: [publish-testpypi, publish-pypi]
84
- if: startsWith(github.ref, 'refs/tags/v')
81
+ needs: publish-pypi
85
82
  runs-on: ubuntu-latest
86
83
  permissions:
87
84
  contents: write
@@ -1,5 +1,6 @@
1
1
  .DS_Store
2
2
  .treebox/
3
+ .ai/
3
4
  *.log
4
5
  tmp/
5
6
 
@@ -41,20 +41,22 @@ run (pluggable)**.
41
41
  record lockfile hash → hand to runner`. Every branch treebox creates is a
42
42
  `treebox/<name>` placeholder made un-pushable by a per-worktree pre-push
43
43
  hook (`extensions.worktreeConfig` + `core.hooksPath` into the private git
44
- dir); `create -b <existing-branch>` is the only path that skips it.
44
+ dir); `create --checkout <existing-branch>` is the only path that skips it.
45
45
  - **`runners/base.py`** defines the `Runner` protocol — the *only* thing that
46
46
  differs between modes — implemented by **`runners/host.py`** (setup + agent in
47
47
  the worktree shell) and **`runners/docker.py`** (plain `docker build/run`,
48
48
  setup via a baked-in `post-create.sh`, agent via `docker exec`; the worktree
49
49
  and its git common dir are bind-mounted at their host paths so in-container
50
- git just works). `TOOL_COMMANDS` maps `claude`/`codex` to their
50
+ git just works). `HARNESS_COMMANDS` maps `claude`/`codex` to their
51
51
  fully-autonomous launch argv.
52
52
  - **`cli.py`** (Typer) is the entry point: `create [NAME] / enter <ref> /
53
- list / teardown <ref>... / doctor`. `enter`/`teardown` resolve a ref as
53
+ list / teardown <ref>... / doctor` (`ls`/`rm` are hidden aliases of
54
+ list/teardown). `enter`/`teardown` resolve a ref as
54
55
  name → live branch → unique substring (`resolve.py`); ambiguity exits 2.
55
56
  It enforces **stable exit codes** (`0` ok · `2` usage · `3` not-found · `4`
56
57
  auth/fetch · `5` conflict) and **`--json`** output carrying a
57
- `schemaVersion` that only gains fields (git-porcelain discipline). Agents
58
+ `schemaVersion` that only gains fields within a version — a breaking reshape
59
+ bumps it (git-porcelain discipline). Agents
58
60
  branch on these, so don't change their meanings casually.
59
61
  - **`ecosystems.py`** detects package managers (uv, npm, pnpm, go, cargo),
60
62
  drives their cache-backed setup, and defines which manifest files feed the
@@ -66,7 +68,7 @@ run (pluggable)**.
66
68
  - **`models.py`** holds the `Worktree` value object and the name-as-identity
67
69
  rule: the *name* is the directory leaf and lock key, never renamed; the
68
70
  *branch* is a mutable attribute read live from git (the agent renames it
69
- with `git branch -m`). `create -b feature/auth` derives the name by
71
+ with `git branch -m`). `create --checkout feature/auth` derives the name by
70
72
  flattening slashes to `--` (`feature--auth`); generated names come from
71
73
  `names.py`.
72
74
  - **`config.py`** is **user-level TOML only**, never read from the target repo —
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: treebox
3
- Version: 0.1.0
3
+ Version: 0.2.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/
@@ -22,6 +22,7 @@ Classifier: Programming Language :: Python :: 3.13
22
22
  Classifier: Programming Language :: Python :: 3.14
23
23
  Classifier: Topic :: Software Development :: Version Control :: Git
24
24
  Requires-Python: >=3.11
25
+ Requires-Dist: questionary<3,>=2
25
26
  Requires-Dist: typer<1.0,>=0.12
26
27
  Provides-Extra: dev
27
28
  Requires-Dist: mypy>=1.14; extra == 'dev'
@@ -69,10 +70,10 @@ on a laptop or over plain SSH.
69
70
 
70
71
  Built by [Seth Peters](https://github.com/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.
71
72
 
72
- Provisioning is identical everywhere; a pluggable **runner** decides where the
73
- agent runs:
73
+ Provisioning is identical everywhere; a pluggable **isolation mode** decides
74
+ where the agent runs:
74
75
 
75
- | Runner | Isolation | Agent runs in |
76
+ | `--isolation` | Sandbox | Agent runs in |
76
77
  | ---------------- | --------- | ----------------------------------------------------- |
77
78
  | `host` (default) | none | the worktree shell |
78
79
  | `docker` | sandboxed | a docker container, with your `.env` + caches mounted |
@@ -111,8 +112,8 @@ your back. Or install directly:
111
112
  uv tool install git+https://github.com/Seth-Peters/treebox
112
113
  ```
113
114
 
114
- The host runner needs only `git` and a logged-in agent CLI (`claude` /
115
- `codex`); the docker runner additionally needs just `docker` — no Node.js, no
115
+ Host isolation needs only `git` and a logged-in agent CLI (`claude` /
116
+ `codex`); docker isolation additionally needs just `docker` — no Node.js, no
116
117
  extra CLIs. See the
117
118
  [install guide](https://seth-peters.github.io/treebox/install/) for
118
119
  requirements and installer overrides.
@@ -137,8 +138,8 @@ launches the agent:
137
138
  ```bash
138
139
  treebox create # generated name (brave-otter), host-native
139
140
  treebox create fix-auth # named up front
140
- treebox create fix-auth --runner docker # sandboxed
141
- treebox create -b feature/auth # exact existing branch (resume, PR review)
141
+ treebox create fix-auth --isolation docker # sandboxed
142
+ treebox create --checkout feature/auth # exact existing branch (resume, PR review)
142
143
  treebox create auth-fixes --base feature/auth # stack on any base branch
143
144
  ```
144
145
 
@@ -161,8 +162,8 @@ unique substring of either. Dependencies re-sync only if the lockfile changed
161
162
  since last time:
162
163
 
163
164
  ```bash
164
- treebox enter fix-auth --tool claude
165
- treebox enter fix-auth --tool codex -- --resume # args after -- go to the agent
165
+ treebox enter fix-auth --harness claude
166
+ treebox enter fix-auth --harness codex -- --resume # args after -- go to the agent
166
167
  ```
167
168
 
168
169
  **List.** See what exists, what each worktree was last doing, and what has
@@ -174,15 +175,19 @@ treebox list
174
175
  ```
175
176
 
176
177
  **Tear down.** Remove one or more worktrees — and, when you're done, their
177
- branches. Refuses to delete uncommitted work unless forced:
178
+ branches. Refuses to delete uncommitted work unless forced. Run it with no
179
+ refs and treebox walks you through an arrow-key picker, each worktree
180
+ annotated with a "will I lose work?" badge (dirty/ahead/merged, plus PR state
181
+ when `gh`/`glab` is present):
178
182
 
179
183
  ```bash
180
184
  treebox teardown fix-auth brave-otter --delete-branch
185
+ treebox teardown # pick interactively
181
186
  ```
182
187
 
183
188
  treebox is built to be scripted, including by agents: every command takes
184
189
  `--json` (data to stdout, diagnostics to stderr, a schema that only gains
185
- fields), `--dry-run` prints the exact commands without running them, and exit
190
+ fields within a version), `--dry-run` prints the exact commands without running them, and exit
186
191
  codes are stable (`0` ok · `2` usage · `3` not found · `4` auth · `5`
187
192
  conflict). Full reference in the
188
193
  [usage guide](https://seth-peters.github.io/treebox/usage/).
@@ -212,8 +217,8 @@ Optional, and user-level only (`~/.config/treebox/config.toml`) — treebox
212
217
  never reads config from the target repo:
213
218
 
214
219
  ```toml
215
- runner = "docker" # host | docker
216
- tool = "claude" # claude | codex
220
+ isolation = "docker" # host | docker
221
+ harness = "claude" # claude | codex
217
222
  base = "main"
218
223
  ```
219
224
 
@@ -35,10 +35,10 @@ on a laptop or over plain SSH.
35
35
 
36
36
  Built by [Seth Peters](https://github.com/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.
37
37
 
38
- Provisioning is identical everywhere; a pluggable **runner** decides where the
39
- agent runs:
38
+ Provisioning is identical everywhere; a pluggable **isolation mode** decides
39
+ where the agent runs:
40
40
 
41
- | Runner | Isolation | Agent runs in |
41
+ | `--isolation` | Sandbox | Agent runs in |
42
42
  | ---------------- | --------- | ----------------------------------------------------- |
43
43
  | `host` (default) | none | the worktree shell |
44
44
  | `docker` | sandboxed | a docker container, with your `.env` + caches mounted |
@@ -77,8 +77,8 @@ your back. Or install directly:
77
77
  uv tool install git+https://github.com/Seth-Peters/treebox
78
78
  ```
79
79
 
80
- The host runner needs only `git` and a logged-in agent CLI (`claude` /
81
- `codex`); the docker runner additionally needs just `docker` — no Node.js, no
80
+ Host isolation needs only `git` and a logged-in agent CLI (`claude` /
81
+ `codex`); docker isolation additionally needs just `docker` — no Node.js, no
82
82
  extra CLIs. See the
83
83
  [install guide](https://seth-peters.github.io/treebox/install/) for
84
84
  requirements and installer overrides.
@@ -103,8 +103,8 @@ launches the agent:
103
103
  ```bash
104
104
  treebox create # generated name (brave-otter), host-native
105
105
  treebox create fix-auth # named up front
106
- treebox create fix-auth --runner docker # sandboxed
107
- treebox create -b feature/auth # exact existing branch (resume, PR review)
106
+ treebox create fix-auth --isolation docker # sandboxed
107
+ treebox create --checkout feature/auth # exact existing branch (resume, PR review)
108
108
  treebox create auth-fixes --base feature/auth # stack on any base branch
109
109
  ```
110
110
 
@@ -127,8 +127,8 @@ unique substring of either. Dependencies re-sync only if the lockfile changed
127
127
  since last time:
128
128
 
129
129
  ```bash
130
- treebox enter fix-auth --tool claude
131
- treebox enter fix-auth --tool codex -- --resume # args after -- go to the agent
130
+ treebox enter fix-auth --harness claude
131
+ treebox enter fix-auth --harness codex -- --resume # args after -- go to the agent
132
132
  ```
133
133
 
134
134
  **List.** See what exists, what each worktree was last doing, and what has
@@ -140,15 +140,19 @@ treebox list
140
140
  ```
141
141
 
142
142
  **Tear down.** Remove one or more worktrees — and, when you're done, their
143
- branches. Refuses to delete uncommitted work unless forced:
143
+ branches. Refuses to delete uncommitted work unless forced. Run it with no
144
+ refs and treebox walks you through an arrow-key picker, each worktree
145
+ annotated with a "will I lose work?" badge (dirty/ahead/merged, plus PR state
146
+ when `gh`/`glab` is present):
144
147
 
145
148
  ```bash
146
149
  treebox teardown fix-auth brave-otter --delete-branch
150
+ treebox teardown # pick interactively
147
151
  ```
148
152
 
149
153
  treebox is built to be scripted, including by agents: every command takes
150
154
  `--json` (data to stdout, diagnostics to stderr, a schema that only gains
151
- fields), `--dry-run` prints the exact commands without running them, and exit
155
+ fields within a version), `--dry-run` prints the exact commands without running them, and exit
152
156
  codes are stable (`0` ok · `2` usage · `3` not found · `4` auth · `5`
153
157
  conflict). Full reference in the
154
158
  [usage guide](https://seth-peters.github.io/treebox/usage/).
@@ -178,8 +182,8 @@ Optional, and user-level only (`~/.config/treebox/config.toml`) — treebox
178
182
  never reads config from the target repo:
179
183
 
180
184
  ```toml
181
- runner = "docker" # host | docker
182
- tool = "claude" # claude | codex
185
+ isolation = "docker" # host | docker
186
+ harness = "claude" # claude | codex
183
187
  base = "main"
184
188
  ```
185
189
 
@@ -54,7 +54,7 @@ whichever agent you launch:
54
54
 
55
55
  ---
56
56
 
57
- `~/.config/treebox/config.toml` picks the runner, tool, base, caches, and
57
+ `~/.config/treebox/config.toml` picks the isolation mode, harness, base, caches, and
58
58
  sandbox template. It's the single source of truth, and it's never read
59
59
  from the target repo.
60
60
 
@@ -62,7 +62,7 @@ whichever agent you launch:
62
62
 
63
63
  ---
64
64
 
65
- `treebox enter feature/auth --tool claude` and `--tool codex` launch into
65
+ `treebox enter feature/auth --harness claude` and `--harness codex` launch into
66
66
  the *same* provisioned, isolated tree. The isolation doesn't change when
67
67
  the agent does.
68
68
 
@@ -75,7 +75,7 @@ are added. You learn treebox once — not each agent's cage.
75
75
  !!! note "treebox complements these systems, it doesn't replace them"
76
76
  An agent's own permission rules still apply inside the box. treebox's job
77
77
  is the layer they all leave to you: a consistent isolated worktree and,
78
- with the `docker` runner, a consistent sandbox — defined by *your*
78
+ with `docker` isolation, a consistent sandbox — defined by *your*
79
79
  template, [rendered outside the box](how-it-works.md#the-sandbox-config-lives-outside-the-box)
80
80
  so the agent can't edit its own cage.
81
81
 
@@ -10,12 +10,12 @@ Lives at `$TREEBOX_CONFIG` if set, else `$XDG_CONFIG_HOME/treebox/config.toml`
10
10
  (defaulting to `~/.config/treebox/config.toml`). All keys optional:
11
11
 
12
12
  ```toml
13
- runner = "host" # host | docker
14
- tool = "claude" # claude | codex
13
+ isolation = "host" # host | docker
14
+ harness = "claude" # claude | codex
15
15
  base = "main" # default base branch
16
16
  root = ".treebox/worktrees"
17
17
  env_file = ".env" # canonical secrets path, copied into each worktree
18
- firewall = false # container firewall (docker runner)
18
+ firewall = false # container firewall (docker isolation)
19
19
  template = "default" # sandbox template name
20
20
 
21
21
  # Replace auto-detected dependency setup with your own shell commands,
@@ -24,7 +24,7 @@ setup_hook = ["uv sync --frozen", "uv run python -m scripts.seed"]
24
24
 
25
25
  # Override where shared package caches live, per ecosystem
26
26
  # (uv, npm, pnpm, go, cargo). Defaults honor the standard env vars
27
- # (UV_CACHE_DIR, GOMODCACHE, …); the docker runner bind-mounts
27
+ # (UV_CACHE_DIR, GOMODCACHE, …); docker isolation bind-mounts
28
28
  # these into the container.
29
29
  [caches]
30
30
  uv = "/mnt/fast/cache/uv"
@@ -39,12 +39,12 @@ command-line flag > config.toml > built-in default
39
39
 
40
40
  | Key | Default | What it controls |
41
41
  | ---------- | -------------------- | --------------------------------------------------------- |
42
- | `runner` | `host` | Where agents run: the worktree shell, or a docker sandbox. |
43
- | `tool` | `claude` | Which agent `create`/`enter` launch by default. |
42
+ | `isolation`| `host` | Where agents run: the worktree shell, or a docker sandbox. |
43
+ | `harness` | `claude` | Which agent `create`/`enter` launch by default. |
44
44
  | `base` | `main` | Base branch for new branches (resolved as `origin/<base>`). |
45
45
  | `root` | `.treebox/worktrees` | Where worktree directories are created, relative to the repo. |
46
46
  | `env_file` | `.env` | The secrets file copied into every new worktree. |
47
- | `firewall` | `false` | Restrict container egress (docker runner). |
47
+ | `firewall` | `false` | Restrict container egress (docker isolation). |
48
48
  | `template` | `default` | Which operator template defines the sandbox. |
49
49
  | `setup_hook` | *(auto-detect)* | Your own setup commands instead of the detected package manager's. |
50
50
  | `caches` | *(standard env vars)* | Per-ecosystem shared cache locations treebox installs from and mounts. |
@@ -6,7 +6,7 @@ than reimplementing them. The whole tool is organized around one seam:
6
6
 
7
7
  ## The provisioning pipeline
8
8
 
9
- Every `create` walks the same host-side pipeline, regardless of runner:
9
+ Every `create` walks the same host-side pipeline, regardless of isolation mode:
10
10
 
11
11
  ```text
12
12
  fetch origin # required — a failure exits 4, loudly
@@ -20,14 +20,15 @@ fetch origin # required — a failure exits 4, loudly
20
20
  └─ hand off to the runner
21
21
  ```
22
22
 
23
- The runner only decides **where** the last two steps happen:
23
+ The runner — picked with `--isolation` — only decides **where** the last two
24
+ steps happen:
24
25
 
25
- | Runner | `setup` runs… | Agent runs… |
26
+ | `--isolation` | `setup` runs… | Agent runs… |
26
27
  | -------- | ------------------------------- | --------------------------------------- |
27
28
  | `host` | in the worktree shell | in the worktree shell |
28
29
  | `docker` | inside the container, on create | via `docker exec`, inside the sandbox |
29
30
 
30
- The docker runner builds and starts the sandbox with plain `docker build` /
31
+ Docker isolation builds and starts the sandbox with plain `docker build` /
31
32
  `docker run`. The worktree and the repo's git dir are bind-mounted at their
32
33
  host paths — mirrored 1:1 — so in-container `git` resolves the worktree's
33
34
  pointers exactly as the host does, with no extra tooling.
@@ -35,7 +36,7 @@ pointers exactly as the host does, with no extra tooling.
35
36
  ## Names are identity; branches are mutable
36
37
 
37
38
  The worktree **name** (the directory leaf) is the permanent identity: the
38
- docker runner bind-mounts the absolute path and a live agent's CWD sits in
39
+ docker sandbox bind-mounts the absolute path and a live agent's CWD sits in
39
40
  it, so the directory is never renamed. The **branch** is just an attribute —
40
41
  created as a `treebox/<name>` placeholder and expected to be renamed
41
42
  (`git branch -m`) once the work has a shape. `list`, `enter`, and `teardown`
@@ -75,7 +76,7 @@ re-download. `--cold` bypasses the caches when you want a from-source build.
75
76
 
76
77
  ## The sandbox config lives outside the box
77
78
 
78
- The docker runner's threat model is simple: **an agent must not be able to
79
+ Docker isolation's threat model is simple: **an agent must not be able to
79
80
  edit the config that defines its own sandbox.**
80
81
 
81
82
  - The container template (Dockerfile, `container.json`, firewall setup) is
@@ -100,7 +101,7 @@ treebox assumes the caller is often another program:
100
101
  automatically when stderr isn't a TTY.
101
102
  - **Stable exit codes** — `0` ok · `2` usage · `3` not-found · `4` auth ·
102
103
  `5` conflict — so callers can branch without parsing prose.
103
- - **`--json` with a `schemaVersion`** that only ever gains fields
104
+ - **`--json` with a `schemaVersion`** that only gains fields within a version
104
105
  (git-porcelain discipline), plus `--print` and `--dry-run` for scripts that
105
106
  want the commands, not the side effects.
106
107
  - **Per-worktree locking**, so two concurrent `create fix-auth` calls
@@ -34,7 +34,7 @@ sandbox.
34
34
  <div class="t-line" style="--d:1.8s"> </div>
35
35
  <div class="t-line" style="--d:1.8s"> <span class="c-dim">branch</span> treebox/brave-otter <span class="c-dim">· placeholder — rename before push</span></div>
36
36
  <div class="t-line" style="--d:1.9s"> <span class="c-dim">base</span> main</div>
37
- <div class="t-line" style="--d:2.0s"> <span class="c-dim">runner</span> host <span class="c-dim">→</span> claude</div>
37
+ <div class="t-line" style="--d:2.0s"> <span class="c-dim">isolation</span> host <span class="c-dim">→</span> claude</div>
38
38
  <div class="t-line" style="--d:2.2s"> </div>
39
39
  <div class="t-line" style="--d:2.2s;--run:.8s"> <span class="t-g"><span class="t-spin"></span><span class="t-check">✓</span></span> fetch <span class="c-dim">origin up to date</span></div>
40
40
  <div class="t-line" style="--d:3.0s;--run:.5s"> <span class="t-g"><span class="t-spin"></span><span class="t-check">✓</span></span> worktree <span class="c-dim">.treebox/worktrees/brave-otter</span></div>
@@ -86,23 +86,23 @@ collisions — on a laptop or over plain SSH.
86
86
 
87
87
  </div>
88
88
 
89
- ## Two runners, one pipeline
89
+ ## Two isolation modes, one pipeline
90
90
 
91
- Provisioning is identical either way; a pluggable **runner** decides where the
92
- agent runs:
91
+ Provisioning is identical either way; a pluggable **isolation mode** decides
92
+ where the agent runs:
93
93
 
94
- | Runner | Isolation | Agent runs in |
94
+ | `--isolation` | Sandbox | Agent runs in |
95
95
  | ---------------- | --------- | ----------------------------------------------------- |
96
96
  | `host` (default) | none | the worktree shell |
97
97
  | `docker` | sandboxed | a docker container, with your `.env` + caches mounted |
98
98
 
99
99
  ```bash
100
100
  treebox create fix-auth # placeholder branch off fresh origin/main
101
- treebox create fix-auth --runner docker # same provisioning, sandboxed
101
+ treebox create fix-auth --isolation docker # same provisioning, sandboxed
102
102
  ```
103
103
 
104
- The docker runner needs exactly one extra thing: Docker — see
105
- [what the runners need](install.md#what-the-runners-need).
104
+ Docker isolation needs exactly one extra thing: Docker — see
105
+ [what each isolation mode needs](install.md#what-each-isolation-mode-needs).
106
106
 
107
107
  ## What it is — and isn't
108
108
 
@@ -127,7 +127,7 @@ The docker runner needs exactly one extra thing: Docker — see
127
127
  - :material-close:{ .tx-no } Manage API keys — it never uses `ANTHROPIC_API_KEY`
128
128
  - :material-close:{ .tx-no } Review, merge, or push your branches
129
129
  - :material-close:{ .tx-no } Trust the target repo's config — its container config and hooks are ignored
130
- - :material-close:{ .tx-no } Isolate anything on the `host` runner — that's what `docker` is for
130
+ - :material-close:{ .tx-no } Isolate anything in `host` isolation — that's what `docker` is for
131
131
  - :material-close:{ .tx-no } Replace CI, or orchestrate fleets of agents
132
132
  - :material-close:{ .tx-no } Install a package manager behind your back
133
133
 
@@ -139,7 +139,7 @@ The docker runner needs exactly one extra thing: Docker — see
139
139
  ```bash
140
140
  treebox doctor # verify the host is ready
141
141
  treebox create # provision + launch claude (name generated)
142
- treebox enter brave-otter --tool codex # re-enter later, pick agent per entry
142
+ treebox enter brave-otter --harness codex # re-enter later, pick agent per entry
143
143
  treebox list # what exists, what's stale
144
144
  treebox teardown brave-otter --delete-branch
145
145
  ```