treebox 0.1.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 (59) hide show
  1. treebox-0.1.0/.agents/skills/no-mistakes/SKILL.md +247 -0
  2. treebox-0.1.0/.ai/docs/brainstorms/2026-07-02-branchless-create-brainstorm.md +132 -0
  3. treebox-0.1.0/.github/workflows/ci.yml +53 -0
  4. treebox-0.1.0/.github/workflows/claude.yml +50 -0
  5. treebox-0.1.0/.github/workflows/docs.yml +50 -0
  6. treebox-0.1.0/.github/workflows/release.yml +98 -0
  7. treebox-0.1.0/.gitignore +27 -0
  8. treebox-0.1.0/AGENTS.md +1 -0
  9. treebox-0.1.0/CLAUDE.md +98 -0
  10. treebox-0.1.0/CONTRIBUTING.md +31 -0
  11. treebox-0.1.0/LICENSE +73 -0
  12. treebox-0.1.0/PKG-INFO +239 -0
  13. treebox-0.1.0/README.md +205 -0
  14. treebox-0.1.0/ROADMAP.md +5 -0
  15. treebox-0.1.0/assets/treebox-logo.png +0 -0
  16. treebox-0.1.0/docs/agents.md +89 -0
  17. treebox-0.1.0/docs/assets/treebox-logo.png +0 -0
  18. treebox-0.1.0/docs/configuration.md +88 -0
  19. treebox-0.1.0/docs/how-it-works.md +107 -0
  20. treebox-0.1.0/docs/index.md +148 -0
  21. treebox-0.1.0/docs/install.md +123 -0
  22. treebox-0.1.0/docs/javascripts/treebox.js +97 -0
  23. treebox-0.1.0/docs/stylesheets/extra.css +491 -0
  24. treebox-0.1.0/docs/usage.md +216 -0
  25. treebox-0.1.0/install.sh +74 -0
  26. treebox-0.1.0/mkdocs.yml +80 -0
  27. treebox-0.1.0/pyproject.toml +86 -0
  28. treebox-0.1.0/scripts/validate.sh +79 -0
  29. treebox-0.1.0/skills/treebox/SKILL.md +94 -0
  30. treebox-0.1.0/src/treebox/__init__.py +8 -0
  31. treebox-0.1.0/src/treebox/assets/container/Dockerfile +103 -0
  32. treebox-0.1.0/src/treebox/assets/container/allowed-domains.sh +25 -0
  33. treebox-0.1.0/src/treebox/assets/container/container.json +27 -0
  34. treebox-0.1.0/src/treebox/assets/container/firewall.json +4 -0
  35. treebox-0.1.0/src/treebox/assets/container/init-firewall.sh +142 -0
  36. treebox-0.1.0/src/treebox/assets/container/post-create.sh +48 -0
  37. treebox-0.1.0/src/treebox/assets/pre-push +27 -0
  38. treebox-0.1.0/src/treebox/assets.py +69 -0
  39. treebox-0.1.0/src/treebox/cli.py +1061 -0
  40. treebox-0.1.0/src/treebox/config.py +118 -0
  41. treebox-0.1.0/src/treebox/ecosystems.py +222 -0
  42. treebox-0.1.0/src/treebox/git.py +528 -0
  43. treebox-0.1.0/src/treebox/locking.py +49 -0
  44. treebox-0.1.0/src/treebox/models.py +88 -0
  45. treebox-0.1.0/src/treebox/names.py +166 -0
  46. treebox-0.1.0/src/treebox/output.py +531 -0
  47. treebox-0.1.0/src/treebox/provision.py +382 -0
  48. treebox-0.1.0/src/treebox/py.typed +0 -0
  49. treebox-0.1.0/src/treebox/resolve.py +65 -0
  50. treebox-0.1.0/src/treebox/runners/__init__.py +19 -0
  51. treebox-0.1.0/src/treebox/runners/base.py +73 -0
  52. treebox-0.1.0/src/treebox/runners/docker.py +688 -0
  53. treebox-0.1.0/src/treebox/runners/host.py +110 -0
  54. treebox-0.1.0/src/treebox/state.py +57 -0
  55. treebox-0.1.0/src/treebox/system.py +43 -0
  56. treebox-0.1.0/tests/conftest.py +77 -0
  57. treebox-0.1.0/tests/test_integration.py +741 -0
  58. treebox-0.1.0/tests/test_units.py +1436 -0
  59. treebox-0.1.0/uv.lock +1036 -0
@@ -0,0 +1,247 @@
1
+ ---
2
+ name: no-mistakes
3
+ description: Validate your code changes through the no-mistakes pipeline - automated code review, tests, lint, docs, push, PR, and CI - before they reach the configured push target. Use when the user asks to run no-mistakes, gate or ship or validate their changes, push safely, asks you to do a task and then validate it, or invokes /no-mistakes.
4
+ user-invocable: true
5
+ ---
6
+
7
+ # no-mistakes
8
+
9
+ `no-mistakes` is a local gate that validates your code changes through a pipeline
10
+ (intent, rebase, review, test, document, lint, push, PR, CI) before they reach
11
+ the configured push target. You drive it through the `no-mistakes axi` command family, which prints
12
+ machine-readable [TOON](https://toonformat.dev) to stdout and progress to stderr.
13
+
14
+ When the user invokes `/no-mistakes`, report the outcome at the end. If the user
15
+ asks for something specific, translate that request into the matching `axi run`
16
+ flags yourself - for example, "skip the lint step" becomes `--skip=lint`. Run
17
+ `no-mistakes axi run --help` to see the available flags.
18
+
19
+ ## Two ways to invoke
20
+
21
+ `/no-mistakes` works in two modes, depending on whether the user hands you a
22
+ task along with the command:
23
+
24
+ - **Validate-only** - bare `/no-mistakes` (optionally with flag-style requests
25
+ like "skip the lint step"). The user's code changes are already committed;
26
+ validate them and report the outcome.
27
+ - **Task-first** - `/no-mistakes <task>`, e.g.
28
+ `/no-mistakes add a --json flag to the status command`. First carry out the
29
+ task yourself, then validate the result through the pipeline:
30
+ 1. **Check scope.** Inspect `git status` before you change or commit anything.
31
+ Preserve unrelated pre-existing uncommitted changes, and when you commit,
32
+ commit only the changes that belong to the user's task.
33
+ 2. **Do the work.** Make the changes the task describes, then **commit them on
34
+ a feature branch**. If the user is on the repository's default branch,
35
+ create a feature branch first - the gate validates committed history on a
36
+ non-default branch, so the work must land there before you run.
37
+ 3. **Then validate**, passing the user's task as your `--intent`. The task
38
+ text is exactly what the user set out to accomplish, in their own words, so
39
+ it *is* the intent - pass it through, enriched with the decisions and
40
+ tradeoffs you made while doing the work (see
41
+ [Intent is required](#intent-is-required)).
42
+
43
+ Everything below - preconditions, intent, the validate-and-decide loop - applies
44
+ the same way once the work is committed on a feature branch.
45
+
46
+ ## Before you start
47
+
48
+ - The work you want validated must be **committed** on a branch. The gate
49
+ validates committed history, not your uncommitted working tree.
50
+ - You must be on a **feature branch**, not the repository's default branch.
51
+ - The repository must already be initialized with `no-mistakes init`.
52
+
53
+ If any of these is not met, `axi run` returns an `error:` with the exact command
54
+ to fix it - read it and act on it (commit your work, or create a branch). If the
55
+ repository is not initialized, run `no-mistakes init` first; if the `no-mistakes`
56
+ command itself is missing or misbehaving, `no-mistakes doctor` reports what is
57
+ wrong.
58
+ Before starting, run `no-mistakes axi` (home view).
59
+ If it shows an active run on your current branch, inspect it with `no-mistakes axi status`.
60
+ If it is parked at a gate, drive it with `no-mistakes axi respond`.
61
+ Reattach an in-flight run by re-running `no-mistakes axi run` when it still matches your current `HEAD`.
62
+ Only `no-mistakes axi abort` it when you mean to discard that run before starting over; aborting is a between-runs action, never a way to take over or bypass a gate while a run is still going (see [Validate and decide](#validate-and-decide)).
63
+ If it shows an active run on another branch, leave that run alone and start validation for your current branch with `no-mistakes axi run --intent "..."`.
64
+
65
+ ## Intent is required
66
+
67
+ When you start a run you must pass `--intent`: **what the user set out to
68
+ accomplish** - the goal or request behind this work, in their terms. This is not
69
+ a description of the diff or the files you changed; it is the objective the
70
+ change is meant to achieve. You know it from the conversation, so pass it
71
+ directly - no-mistakes uses it verbatim instead of inferring it from local agent
72
+ transcripts (slower and flakier).
73
+
74
+ Err on the side of completeness, not brevity. The review step uses `--intent`
75
+ to tell a deliberate decision apart from a mistake, so a thin one-line summary
76
+ makes it flag things the user already chose. Capture the nuance: the user's
77
+ goal, the specific decisions and tradeoffs they made along the way, any
78
+ constraints or approaches they ruled in or out, and anything they explicitly
79
+ asked for that might otherwise look surprising in the diff. A few sentences to a
80
+ short paragraph is normal - write down what you learned from the conversation
81
+ that a reviewer reading only the diff would not know.
82
+
83
+ ## Validate and decide
84
+
85
+ Run the pipeline and decide on its findings as they come up:
86
+
87
+ 1. Start the run. It blocks until the first decision point or the end:
88
+ ```sh
89
+ no-mistakes axi run --intent "<what the user set out to accomplish>"
90
+ ```
91
+ `axi run` and every `axi respond` block synchronously - the review, test,
92
+ and CI steps can each take **several minutes**, so a single call may not
93
+ return for a while. That is normal; allow a long timeout and do not cancel
94
+ or re-issue the command because it seems slow. To check progress without
95
+ disturbing the run, use `no-mistakes axi status` from a separate call.
96
+ A long-running call is working, not stalled - background it if your harness
97
+ needs to, but the run **never advances past a gate on its own**. Read every
98
+ return; on a `gate:`, respond; loop until an `outcome:`. Never idle-wait
99
+ for the run to move forward by itself.
100
+ When that status output includes `awaiting_agent: parked <duration>` under the run,
101
+ the run is parked at an approval or fix-review gate and waiting for you to
102
+ send `axi respond`. The field is observability only: it does not change
103
+ gate resolution, auto-resume the run, or make `--yes` the default.
104
+ 2. If the output contains a `gate:` object, the pipeline is waiting on you.
105
+ Read its `findings` table. Each finding has an `id`, `severity`,
106
+ `file`, `description`, and an `action` that tells you how the
107
+ pipeline classified it:
108
+ - `auto-fix` - mechanical and low-risk; you can authorize the fix on
109
+ your own judgment by responding with `--action fix`.
110
+ - `no-op` - informational only; nothing to do.
111
+ - `ask-user` - the finding challenges the user's deliberate intent or
112
+ touches product behavior. This is a call only the user can make - see
113
+ [Escalate `ask-user` findings](#escalate-ask-user-findings) below.
114
+
115
+ **Review auto-fix is disabled by default** (`auto_fix.review: 0`; a repo
116
+ or global `auto_fix.review > 0` override re-enables it), so blocking and
117
+ ask-user review findings park for your decision rather than being silently
118
+ self-fixed. (Other steps such as test and lint may auto-fix within the
119
+ pipeline and re-run before they ever gate.)
120
+
121
+ Choose one response:
122
+ ```sh
123
+ # accept the step as-is and continue
124
+ no-mistakes axi respond --action approve
125
+
126
+ # have the pipeline fix specific findings, then continue
127
+ no-mistakes axi respond --action fix --findings <id1,id2> --instructions "<optional guidance>"
128
+
129
+ # skip this step
130
+ no-mistakes axi respond --action skip
131
+ ```
132
+ While a run is active, never fix findings by editing the code yourself -
133
+ the pipeline owns both the findings and the fixes. Your job at a gate is to
134
+ decide and respond; `--action fix` has the pipeline apply the fix and
135
+ re-review the result. For the same reason, while a run is active do **not**
136
+ `abort` or `rerun` to go fix a finding yourself - even a real bug in
137
+ your own code - because that discards the pipeline's in-flight work and
138
+ forces a full re-validation. `abort` and `rerun` are for *between*
139
+ runs (after a `failed` or `cancelled` outcome), never to circumvent a
140
+ gate.
141
+
142
+ Each `respond` blocks until the next `gate:`, `checks-passed` decision point, or final outcome.
143
+
144
+ Two extra flags are available on `respond` when you need them:
145
+ - `--add-finding '<json>'` (with `--action fix`) folds a finding you
146
+ spotted yourself - one the pipeline did not surface - into the fix round,
147
+ as a JSON finding object. Use it for a problem you noticed that is not in
148
+ the gate's own `findings` table.
149
+ - `--step <name>` responds to a specific step instead of the one currently
150
+ awaiting approval. You rarely need this; omit it to answer the active gate.
151
+ 3. Repeat step 2 until the output has an `outcome:` instead of a `gate:`. The
152
+ outcomes are:
153
+ - `checks-passed` - the change is validated and CI is green, but the PR is
154
+ not merged yet. **You are done driving the pipeline.** Do not wait for the
155
+ merge: tell the user the PR is ready and ask them to review and merge it
156
+ (the PR link is in the `help` line). no-mistakes keeps monitoring the PR
157
+ in the background until it is merged, closed, or its configured idle
158
+ timeout elapses, so a human can watch it in the TUI.
159
+ - `passed` - the changes cleared the gate and the PR was merged or closed.
160
+ - `failed` or `cancelled` - they did not; read the output and address it.
161
+ Fix whatever the output points at (a failing test, a lint error, a finding
162
+ you skipped), commit the fix on the same feature branch, then drive the
163
+ pipeline again - `no-mistakes axi run --intent "..."` starts a fresh run,
164
+ or `no-mistakes rerun` re-runs the pipeline for the current branch. This
165
+ is the right place to start over: a fresh run or `rerun` is a
166
+ *between-runs* action, correct only after a terminal outcome like this -
167
+ never mid-run to circumvent a gate. Do not leave the user at a `failed`
168
+ outcome without either retrying or explaining what blocks it.
169
+
170
+ The CI step deliberately keeps watching the PR after checks pass, so
171
+ `axi run` returns `checks-passed` the moment checks are green rather than
172
+ blocking on the human merge. Never poll or re-run waiting for the merge yourself.
173
+
174
+ On a successful outcome (`checks-passed` or `passed`), close the loop with the
175
+ user: summarize what happened during the pipeline in a concise, easily readable
176
+ format - what was validated and what was found. If the output includes a
177
+ `fixes` table, the pipeline fixed findings your original change missed:
178
+ acknowledge those misses and explicitly list each fix so the user can easily
179
+ review them.
180
+
181
+ ## Escalate `ask-user` findings
182
+
183
+ A gate whose findings are all `auto-fix` or `no-op` is safe to drive on your
184
+ own judgment: respond with `--action fix` or `--action approve` as
185
+ appropriate. But a finding marked
186
+ `ask-user` is a decision that belongs to the user, not you - the pipeline
187
+ flagged it because it challenges their deliberate intent or changes product
188
+ behavior. Do not approve, fix, or skip it on your own. Instead, stop and bring
189
+ it to the user before you respond:
190
+
191
+ - Relay each `ask-user` finding to them as the pipeline wrote it - its
192
+ `id`, `file`, and full `description` verbatim. Do not paraphrase,
193
+ summarize away the detail, or pre-judge the answer.
194
+ - Ask how they want to proceed, then translate their decision into the matching
195
+ `respond` call: `--action fix` (pass their guidance through
196
+ `--instructions`), `--action approve`, or `--action skip`.
197
+
198
+ The one exception is `--yes` (below): it is the user's standing consent to
199
+ drive every gate unattended, so under `--yes` you resolve `ask-user`
200
+ findings automatically instead of stopping to ask.
201
+
202
+ If you have clear consent to drive the run automatically, pass `--yes` to `axi run`
203
+ or `axi respond`. It treats every actionable finding - `auto-fix` and
204
+ `ask-user` alike - as consent to fix it, selects every current finding for one
205
+ fix round, accepts the resulting fix review, and approves gates with only
206
+ `no-op` findings. Only use it when the user has asked you to drive the whole
207
+ run without checking back.
208
+
209
+ ## Inspecting state
210
+
211
+ ```sh
212
+ no-mistakes axi # home view: current branch, active runs, next steps
213
+ no-mistakes axi status # full detail of the resolved run
214
+ no-mistakes axi logs --step <name> --full # full log output of one step
215
+ no-mistakes axi abort # cancel the current-branch active run
216
+ no-mistakes axi abort --run <id> # cancel a specific run by id (works outside its worktree)
217
+ ```
218
+
219
+ ## Reading the output
220
+
221
+ - Output is TOON: `key: value` pairs, `name[N]{cols}:` tables, and `help[N]:` hints.
222
+ - A non-terminal run object may include `awaiting_agent: parked <duration>` immediately after `status`; that means the run is parked at a gate awaiting your `axi respond`.
223
+ - The `help` list at the bottom of most responses tells you the next commands to run.
224
+ - Errors are printed as `error: ...` on stdout with a `help` list; act on the suggestion.
225
+ - Exit codes: `0` success, no-op, or normal decision gates, `1` failed or cancelled final outcomes, `2` bad usage.
226
+
227
+ A `gate:` waiting on you looks roughly like this - a `gate:` line naming the step, optional step-specific fields such as `note`, a `findings[N]{...}:` table with one row per finding, and a `help[N]:` list of next commands:
228
+
229
+ ```
230
+ gate: review
231
+ note: Review auto-fix is disabled by default (auto_fix.review: 0; a repo or global auto_fix.review > 0 override re-enables it), so blocking and ask-user review findings park for your decision rather than being silently self-fixed.
232
+ findings[2]{id,severity,file,line,action,description}:
233
+ r1,warning,internal/pipeline/executor.go,,auto-fix,Error from os.Remove is ignored
234
+ r2,error,cmd/no-mistakes/main.go,,ask-user,New --force flag bypasses the confirm prompt
235
+ help[3]:
236
+ no-mistakes axi respond --action fix --findings r1
237
+ no-mistakes axi respond --action approve
238
+ A long-running call is working, not stalled - background it if your harness needs to, but the run never advances past a gate on its own. Read every return; on a gate, respond; loop until an outcome.
239
+ ```
240
+
241
+ Read the `action` column per row: decide `r1` (auto-fix) on your own
242
+ judgment - `respond --action fix --findings r1` hands it to the pipeline to
243
+ fix - but stop and escalate `r2` (ask-user) to the user before responding. A
244
+ final state
245
+ instead shows `outcome: <checks-passed|passed|failed|cancelled>` with no
246
+ `findings` table. Field names and exact columns can vary by step and version,
247
+ so read the actual `findings` header rather than assuming this layout.
@@ -0,0 +1,132 @@
1
+ ---
2
+ date: 2026-07-02
3
+ topic: branchless-create
4
+ ---
5
+
6
+ # Branchless `treebox create`
7
+
8
+ ## What We're Building
9
+
10
+ Make the branch argument to `treebox create` optional so users don't have to
11
+ invent a branch name up front. A worktree gets a stable generated identity at
12
+ create time; the branch becomes a *mutable attribute* that the agent renames
13
+ when the work takes shape, and `treebox list` reports it live. The directory
14
+ is never renamed.
15
+
16
+ Final arg structure (opinionated, **no backwards compat** — Seth's call
17
+ 2026-07-02; the old `create <branch>` positional and the interim `-m` flag are
18
+ both gone; a variadic sentence positional was considered and rejected as a
19
+ CLI smell — one clean token only):
20
+
21
+ treebox create [NAME] # optional slug token; omitted → petname
22
+ -b, --branch <ref> # existing/exact branch (resume work, PR review)
23
+ --base <branch> # unchanged
24
+ treebox enter <ref> # name, branch, or unique substring of either
25
+ treebox teardown <ref>... # variadic
26
+ treebox list
27
+
28
+ - `treebox create fix-auth` → dir `fix-auth`, placeholder branch
29
+ `treebox/fix-auth`. NAME is validated as a slug (`[a-z0-9-]+`); anything
30
+ else exits 2 loudly. No slugification magic, no description storage, no
31
+ agent-prompt side effects.
32
+ - `treebox create` (no args) → petname (`brave-otter`) +
33
+ `treebox/brave-otter`.
34
+ - `treebox create -b feature/auth` → worktree on that exact existing branch;
35
+ the only path that skips the placeholder.
36
+ - **No-exceptions rule:** every branch treebox *creates* is `treebox/<slug>`
37
+ and un-pushable (pre-push guard). The agent/human always `git branch -m`
38
+ to a deliberate name before push — so machine-derived names can never
39
+ reach a PR. (Supersedes the earlier "bare slug is pushable" draft.)
40
+ - `enter`/`teardown` resolve name → branch → unique substring, live from
41
+ `git worktree list --porcelain`; ambiguity errors loudly (exit 2).
42
+ - `list` shows `NAME · BRANCH · LAST COMMIT · AGE · DEPS · ENV`, sorted by
43
+ recency; `treebox/*` branches render dim + `⚠ unnamed`. `--json` gains
44
+ `name`/`description` (additive, schemaVersion discipline).
45
+
46
+ ## Why This Approach
47
+
48
+ Three approaches considered:
49
+
50
+ - **A — stable generated name, branch as live attribute** (chosen core):
51
+ no rename fragility; `list` already reads branches live from porcelain, so
52
+ most of the plumbing exists.
53
+ - **B — intent slug via `-m`** (chosen add-on): the mental load isn't typing
54
+ a name, it's committing to a *git branch name*; a plain-words task
55
+ description is cheap and makes dirs self-describing.
56
+ - **C — rename the directory to match the branch later** (rejected as
57
+ automatic behavior): the docker runner bind-mounts the absolute path and a
58
+ live agent's CWD sits in the dir, so renames are only safe when the tree is
59
+ idle; requires liveness detection; stales paths the agent already logged.
60
+ At most an explicit opt-in `treebox rename` later.
61
+
62
+ Key code facts grounding this:
63
+
64
+ - Branch is currently the primary key: dir name via `flatten_branch`
65
+ (`models.py`), lock key, and `enter`/`teardown` lookup.
66
+ - `list` (`cli.py` `list_cmd`) already shows the live branch from
67
+ `git worktree list --porcelain` — a renamed branch already displays
68
+ correctly today; only the branch→dir lookup breaks.
69
+ - `state.py` stores per-worktree state in the private git dir
70
+ (`.git/worktrees/<id>/`) — the natural home for the `-m` description
71
+ (pruned with the worktree, invisible to `git status`).
72
+
73
+ ## Key Decisions
74
+
75
+ - **Directory name = permanent identity; never renamed.** Avoids breaking
76
+ docker absolute-path mounts, running agents' CWDs, and dir-keyed locks/state.
77
+ - **Placeholder branch, not detached HEAD.** The agent can commit from second
78
+ one; renaming a branch is trivial and `list` follows automatically.
79
+ - **`enter`/`teardown` resolve name-or-branch.** Exact branch match first,
80
+ then dir-name match.
81
+ - **Recognition lives in `list`, not the dir name.** Last-commit subject +
82
+ optional description carry the "which worktree was doing what" load.
83
+ - **`--json` only gains fields** (`name`, `description`) — schemaVersion 1
84
+ discipline preserved.
85
+
86
+ ## Decisions confirmed by Seth (2026-07-02)
87
+
88
+ - **Placeholder branch prefix is `treebox/`** (e.g. `treebox/brave-otter`).
89
+ - **Slug collisions error loudly**: exit 5 (conflict), `error.code:
90
+ "SLUG_CONFLICT"`, hints offering `enter`, `teardown`, or rewording `-m`.
91
+ - Seth's core worry: an agent pushes the placeholder name and the PR reads
92
+ `treebox/brave-otter → main` — meaningless when juggling 10 worktrees.
93
+
94
+ ## Pre-push guard (answer to the PR-name worry)
95
+
96
+ Placeholder branches are **un-pushable by design**. `create` installs a
97
+ pre-push hook scoped to the worktree (via `extensions.worktreeConfig` +
98
+ per-worktree `core.hooksPath` pointing into the private git dir — never the
99
+ shared repo hooks, never inside the mount). The hook rejects any push of a
100
+ `treebox/*` ref with a loud, instructive error:
101
+
102
+ ✗ treebox: refusing to push placeholder branch 'treebox/quiet-lake'.
103
+ ↳ name this work first: git branch -m fix/flaky-teardown — then push again.
104
+ ↳ the treebox/ prefix marks auto-generated names; PRs should never carry them.
105
+
106
+ This turns the weird placeholder into a forcing function: the agent *must*
107
+ choose a descriptive branch name before it can open a PR, and agents follow
108
+ error hints reliably. `git push --no-verify` remains the deliberate human
109
+ escape hatch. Rule of thumb: `treebox/` prefix = machine-generated = blocked;
110
+ `-m` slug branches are bare (`speed-up-ci-caching`) because the words are
111
+ human-chosen, so they push and PR fine as-is.
112
+
113
+ `list` surfaces the same state: `treebox/*` branches render dim with
114
+ `⚠ unnamed`, the caption counts unnamed worktrees, and a footer explains the
115
+ `git branch -m` fix. Prototype: scratchpad `proto_list.py` (session dir),
116
+ rendered with treebox's real Reporter/theme.
117
+
118
+ ## Open Questions
119
+
120
+ - **Explicit `treebox rename` command?** Recommendation: skip (YAGNI) — the
121
+ enriched list makes dir names irrelevant and worktrees are short-lived.
122
+ - **Where does this land?** This worktree is on the Python codebase, but
123
+ PR #96 (Rust port) deletes all of it. Options: design-doc only for now /
124
+ implement in Python and port later / implement on the Rust branch.
125
+ Needs Seth's call before implementation.
126
+ - Docker runner: verify the per-worktree hooksPath resolves inside the
127
+ container (the worktree's `.git` file points at the host-side private git
128
+ dir; hook path must be valid from both sides or host-only pushes assumed).
129
+
130
+ ## Next Steps
131
+
132
+ → `/plan-feature` once the approach + target codebase are confirmed.
@@ -0,0 +1,53 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ concurrency:
9
+ group: ci-${{ github.ref }}
10
+ cancel-in-progress: true
11
+
12
+ jobs:
13
+ test:
14
+ name: ${{ matrix.os }} · py${{ matrix.python-version }}
15
+ runs-on: ${{ matrix.os }}
16
+ strategy:
17
+ fail-fast: false
18
+ matrix:
19
+ os: [ubuntu-latest, macos-latest]
20
+ python-version: ["3.11", "3.12", "3.13", "3.14"]
21
+ steps:
22
+ - uses: actions/checkout@v7
23
+
24
+ - name: Install uv
25
+ uses: astral-sh/setup-uv@v8.2.0
26
+ with:
27
+ enable-cache: true
28
+
29
+ - name: Lint
30
+ run: uv run -p ${{ matrix.python-version }} --extra dev ruff check src tests
31
+
32
+ - name: Type check
33
+ run: uv run -p ${{ matrix.python-version }} --extra dev mypy
34
+
35
+ - name: Test
36
+ # Report-only coverage: surfaces untested branches without gating merges.
37
+ run: uv run -p ${{ matrix.python-version }} --extra dev python -m pytest --cov=treebox --cov-report=term-missing
38
+
39
+ smoke:
40
+ name: live host-runner smoke
41
+ runs-on: ubuntu-latest
42
+ steps:
43
+ - uses: actions/checkout@v7
44
+
45
+ - name: Install uv
46
+ uses: astral-sh/setup-uv@v8.2.0
47
+ with:
48
+ enable-cache: true
49
+
50
+ # Full reproducible validation: lint, tests, and a live `treebox create`
51
+ # against a throwaway local git remote.
52
+ - name: validate.sh
53
+ run: ./scripts/validate.sh
@@ -0,0 +1,50 @@
1
+ name: Claude Code
2
+
3
+ on:
4
+ issue_comment:
5
+ types: [created]
6
+ pull_request_review_comment:
7
+ types: [created]
8
+ issues:
9
+ types: [opened, assigned]
10
+ pull_request_review:
11
+ types: [submitted]
12
+
13
+ jobs:
14
+ claude:
15
+ if: |
16
+ (github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) ||
17
+ (github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) ||
18
+ (github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) ||
19
+ (github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude')))
20
+ runs-on: ubuntu-latest
21
+ permissions:
22
+ contents: read
23
+ pull-requests: read
24
+ issues: read
25
+ id-token: write
26
+ actions: read # Required for Claude to read CI results on PRs
27
+ steps:
28
+ - name: Checkout repository
29
+ uses: actions/checkout@v4
30
+ with:
31
+ fetch-depth: 1
32
+
33
+ - name: Run Claude Code
34
+ id: claude
35
+ uses: anthropics/claude-code-action@v1
36
+ with:
37
+ claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
38
+
39
+ # This is an optional setting that allows Claude to read CI results on PRs
40
+ additional_permissions: |
41
+ actions: read
42
+
43
+ # Optional: Give a custom prompt to Claude. If this is not specified, Claude will perform the instructions specified in the comment that tagged it.
44
+ # prompt: 'Update the pull request description to include a summary of changes.'
45
+
46
+ # Optional: Add claude_args to customize behavior and configuration
47
+ # See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md
48
+ # or https://code.claude.com/docs/en/cli-reference for available options
49
+ # claude_args: '--allowed-tools Bash(gh pr *)'
50
+
@@ -0,0 +1,50 @@
1
+ name: docs
2
+
3
+ # Build the MkDocs site on every push to main and publish it to GitHub Pages.
4
+ # Until the repo is public (or Pages is enabled under Settings → Pages →
5
+ # Source: GitHub Actions), the deploy job fails harmlessly; the build job
6
+ # still validates the site with --strict.
7
+
8
+ on:
9
+ push:
10
+ branches: [main]
11
+ workflow_dispatch:
12
+
13
+ permissions:
14
+ contents: read
15
+ pages: write
16
+ id-token: write
17
+
18
+ concurrency:
19
+ group: pages
20
+ cancel-in-progress: false
21
+
22
+ jobs:
23
+ build:
24
+ runs-on: ubuntu-latest
25
+ steps:
26
+ - uses: actions/checkout@v7
27
+ with:
28
+ fetch-depth: 0 # full history + tags so hatch-vcs can derive the version
29
+
30
+ - name: Install uv
31
+ uses: astral-sh/setup-uv@v8.2.0
32
+ with:
33
+ enable-cache: true
34
+
35
+ - name: Build site
36
+ run: uv run --extra docs mkdocs build --strict
37
+
38
+ - uses: actions/upload-pages-artifact@v5
39
+ with:
40
+ path: site
41
+
42
+ deploy:
43
+ needs: build
44
+ runs-on: ubuntu-latest
45
+ environment:
46
+ name: github-pages
47
+ url: ${{ steps.deployment.outputs.page_url }}
48
+ steps:
49
+ - id: deployment
50
+ uses: actions/deploy-pages@v5
@@ -0,0 +1,98 @@
1
+ name: Release
2
+
3
+ # Build and publish treebox.
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.
9
+ #
10
+ # 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:
15
+ # owner = Seth-Peters, repo = treebox, workflow = release.yml, environment = pypi
16
+
17
+ on:
18
+ push:
19
+ tags:
20
+ - "v*"
21
+ workflow_dispatch: {}
22
+
23
+ jobs:
24
+ build:
25
+ name: Build distributions
26
+ runs-on: ubuntu-latest
27
+ steps:
28
+ - uses: actions/checkout@v7
29
+ with:
30
+ fetch-depth: 0 # full history + tags so hatch-vcs can derive the version
31
+
32
+ - name: Install uv
33
+ uses: astral-sh/setup-uv@v8.2.0
34
+
35
+ - name: Build sdist + wheel
36
+ run: uv build
37
+
38
+ - name: Show version being published
39
+ run: ls -1 dist/
40
+
41
+ - uses: actions/upload-artifact@v7
42
+ with:
43
+ name: dist
44
+ path: dist/
45
+
46
+ publish-testpypi:
47
+ name: Publish to TestPyPI
48
+ needs: build
49
+ runs-on: ubuntu-latest
50
+ environment: testpypi
51
+ permissions:
52
+ id-token: write # required for trusted publishing (OIDC)
53
+ steps:
54
+ - uses: actions/download-artifact@v8
55
+ with:
56
+ name: dist
57
+ path: dist/
58
+
59
+ - name: Publish
60
+ uses: pypa/gh-action-pypi-publish@release/v1
61
+ with:
62
+ repository-url: https://test.pypi.org/legacy/
63
+
64
+ publish-pypi:
65
+ name: Publish to PyPI
66
+ needs: build
67
+ if: startsWith(github.ref, 'refs/tags/v')
68
+ runs-on: ubuntu-latest
69
+ environment: pypi
70
+ permissions:
71
+ id-token: write # required for trusted publishing (OIDC)
72
+ steps:
73
+ - uses: actions/download-artifact@v8
74
+ with:
75
+ name: dist
76
+ path: dist/
77
+
78
+ - name: Publish
79
+ uses: pypa/gh-action-pypi-publish@release/v1
80
+
81
+ github-release:
82
+ name: GitHub Release
83
+ needs: [publish-testpypi, publish-pypi]
84
+ if: startsWith(github.ref, 'refs/tags/v')
85
+ runs-on: ubuntu-latest
86
+ permissions:
87
+ contents: write
88
+ steps:
89
+ - uses: actions/download-artifact@v8
90
+ with:
91
+ name: dist
92
+ path: dist/
93
+
94
+ - name: Create release
95
+ uses: softprops/action-gh-release@v3
96
+ with:
97
+ generate_release_notes: true
98
+ files: dist/*
@@ -0,0 +1,27 @@
1
+ .DS_Store
2
+ .treebox/
3
+ *.log
4
+ tmp/
5
+
6
+ # Auto-generated by `playwright-cli install --skills` (container post-create).
7
+ # Regenerated per container, so committing it just dirties worktrees.
8
+ .claude/skills/playwright-cli/
9
+
10
+ # Python
11
+ __pycache__/
12
+ *.egg-info/
13
+ .venv/
14
+ dist/
15
+ build/
16
+ .pytest_cache/
17
+ .ruff_cache/
18
+ .coverage
19
+ .coverage.*
20
+ htmlcov/
21
+ .env
22
+
23
+ # MkDocs build output
24
+ site/
25
+
26
+ # playwright-cli session artifacts
27
+ .playwright-cli/
@@ -0,0 +1 @@
1
+ CLAUDE.md