chamba 0.6.1 → 0.8.0

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 (118) hide show
  1. package/LICENSE +53 -14
  2. package/README.md +38 -11
  3. package/dist/commands/advanced.js +7 -278
  4. package/dist/commands/dev.js +11 -612
  5. package/dist/commands/doctor.js +1 -29
  6. package/dist/commands/menu.js +1 -80
  7. package/dist/commands/onboard.js +6 -230
  8. package/dist/commands/settings.js +15 -349
  9. package/dist/lib/agent-commands.js +2 -0
  10. package/dist/lib/agent-context.js +6 -184
  11. package/dist/lib/browser.js +1 -40
  12. package/dist/lib/chamba-yaml.js +10 -191
  13. package/dist/lib/constants.js +1 -125
  14. package/dist/lib/dockerfile-builder.js +41 -260
  15. package/dist/lib/env.js +2 -78
  16. package/dist/lib/git-env.js +1 -21
  17. package/dist/lib/global-config.js +4 -66
  18. package/dist/lib/pnpm-store.js +1 -19
  19. package/dist/lib/ports.js +1 -210
  20. package/dist/lib/safe-rm.js +1 -26
  21. package/dist/lib/sessions.js +2 -34
  22. package/dist/lib/shadows.js +1 -176
  23. package/dist/lib/skills.js +2 -0
  24. package/dist/lib/templates.js +1 -0
  25. package/dist/lib/webterm.js +1 -305
  26. package/dist/lib/workspace-identity.js +4 -260
  27. package/package.json +6 -6
  28. package/templates/Dockerfile +24 -12
  29. package/templates/claude-statusline.sh +5 -7
  30. package/templates/context/baseline.md +2 -0
  31. package/templates/pane-apps/client/assets/c-BNa-DeUl.json +3552 -0
  32. package/templates/pane-apps/client/assets/cpp-B-T4NRoH.json +20239 -0
  33. package/templates/pane-apps/client/assets/csharp-BbxHChhA.json +6010 -0
  34. package/templates/pane-apps/client/assets/css-CWCxuwmz.json +1860 -0
  35. package/templates/pane-apps/client/assets/diff-B-2N89g8.json +154 -0
  36. package/templates/pane-apps/client/assets/docker-DWTvYW26.json +103 -0
  37. package/templates/pane-apps/client/assets/files-D0nJgFAA.js +12 -0
  38. package/templates/pane-apps/client/assets/files-h0K1vZyQ.css +1 -0
  39. package/templates/pane-apps/client/assets/go-B2hyYeNg.json +3156 -0
  40. package/templates/pane-apps/client/assets/graphql-Z6Id2aNO.json +1292 -0
  41. package/templates/pane-apps/client/assets/html-BtzvlIfu.json +2627 -0
  42. package/templates/pane-apps/client/assets/ini-Bn3Twap1.json +108 -0
  43. package/templates/pane-apps/client/assets/java-C7t2dwLO.json +1885 -0
  44. package/templates/pane-apps/client/assets/javascript-DuC2v0C0.json +5996 -0
  45. package/templates/pane-apps/client/assets/json-CwdkWeV5.json +206 -0
  46. package/templates/pane-apps/client/assets/jsonc-MYWDbTU3.json +206 -0
  47. package/templates/pane-apps/client/assets/jsx-Cn8EZuIC.json +5996 -0
  48. package/templates/pane-apps/client/assets/kotlin-CFNOz1gw.json +580 -0
  49. package/templates/pane-apps/client/assets/make-BVnlFCcl.json +629 -0
  50. package/templates/pane-apps/client/assets/markdown-B4Xk2WM_.json +3263 -0
  51. package/templates/pane-apps/client/assets/php-CAQzQmmI.json +4188 -0
  52. package/templates/pane-apps/client/assets/python-CYQee8m4.json +4174 -0
  53. package/templates/pane-apps/client/assets/reviews-BsoGZ35r.css +1 -0
  54. package/templates/pane-apps/client/assets/reviews-iW0Mbn4e.js +153 -0
  55. package/templates/pane-apps/client/assets/ruby-D7R58tus.json +3114 -0
  56. package/templates/pane-apps/client/assets/rust-BFBbFiei.json +1103 -0
  57. package/templates/pane-apps/client/assets/scss-TQRhBkPG.json +1874 -0
  58. package/templates/pane-apps/client/assets/shellscript-DieAkC4W.json +2375 -0
  59. package/templates/pane-apps/client/assets/specs-D9z3neR-.js +13 -0
  60. package/templates/pane-apps/client/assets/sql-Bxd030LV.json +634 -0
  61. package/templates/pane-apps/client/assets/toml-ChrKGzsV.json +430 -0
  62. package/templates/pane-apps/client/assets/tsx-D2e90MFw.json +5996 -0
  63. package/templates/pane-apps/client/assets/typescript-DonXQ_n1.json +5747 -0
  64. package/templates/pane-apps/client/assets/xml-B17r2RXF.json +382 -0
  65. package/templates/pane-apps/client/assets/yaml-CKn4h1zy.json +627 -0
  66. package/templates/pane-apps/client/reviews/index.html +15 -0
  67. package/templates/pane-apps/client/specs/index.html +14 -0
  68. package/templates/pane-apps/server/reviews.mjs +20 -0
  69. package/templates/pane-apps/server/specs.mjs +5 -0
  70. package/templates/skills/chamba-statusline/SKILL.md +7 -19
  71. package/templates/skills/dx-review/SKILL.md +123 -0
  72. package/templates/skills/dx-review/references/acts.md +162 -0
  73. package/templates/skills/dx-spec/SKILL.md +561 -0
  74. package/templates/skills/dx-spec/references/imagination-guide.md +140 -0
  75. package/templates/skills/dx-spec/references/principles-template.md +2 -0
  76. package/templates/skills/dx-spec/references/review-guide.md +127 -0
  77. package/templates/skills/dx-spec/references/spec-guide.md +145 -0
  78. package/templates/skills/dx-spec/references/stages.md +484 -0
  79. package/templates/skills/dx-spec-execute/SKILL.md +437 -0
  80. package/templates/startup.mjs +10 -4
  81. package/templates/tool-helper.sh +166 -0
  82. package/templates/webterm/README.md +79 -9
  83. package/templates/webterm/color.js +61 -0
  84. package/templates/webterm/config.js +74 -0
  85. package/templates/webterm/context/claude.md +0 -1
  86. package/templates/webterm/public/app/alerts.js +4 -4
  87. package/templates/webterm/public/app/anchored.js +81 -0
  88. package/templates/webterm/public/app/composer.js +4 -1
  89. package/templates/webterm/public/app/connection.js +9 -0
  90. package/templates/webterm/public/app/dom.js +15 -5
  91. package/templates/webterm/public/app/frames.js +51 -10
  92. package/templates/webterm/public/app/main.js +26 -6
  93. package/templates/webterm/public/app/new-session.js +13 -41
  94. package/templates/webterm/public/app/palette.js +58 -0
  95. package/templates/webterm/public/app/pane-shape.js +167 -0
  96. package/templates/webterm/public/app/pane-shell.js +380 -0
  97. package/templates/webterm/public/app/pane.js +77 -193
  98. package/templates/webterm/public/app/reviews-host.js +15 -0
  99. package/templates/webterm/public/app/specs-host.js +16 -0
  100. package/templates/webterm/public/app/state.js +5 -0
  101. package/templates/webterm/public/app/tabs.js +6 -2
  102. package/templates/webterm/public/app/terminal.js +8 -0
  103. package/templates/webterm/public/app/theme.js +75 -27
  104. package/templates/webterm/public/app/tool-host.js +302 -0
  105. package/templates/webterm/public/app/workspace-color.js +192 -0
  106. package/templates/webterm/public/index.html +68 -30
  107. package/templates/webterm/public/styles.css +270 -34
  108. package/templates/webterm/server.js +321 -0
  109. package/templates/webterm/tool-document.js +67 -0
  110. package/templates/webterm/tools/commands.js +61 -0
  111. package/templates/webterm/tools/index.js +25 -0
  112. package/templates/webterm/tools/mount.js +337 -0
  113. package/templates/webterm/tools/paths.js +34 -0
  114. package/templates/webterm/tools/reviews.js +133 -0
  115. package/templates/webterm/tools/specs.js +154 -0
  116. package/templates/webterm/typed-line.js +148 -0
  117. package/templates/context/context-usage.md +0 -1
  118. package/templates/context-usage.sh +0 -266
@@ -0,0 +1,437 @@
1
+ ---
2
+ name: dx-spec-execute
3
+ description: Run the execution plan of a spec the Specs tab holds, phase by phase, with the run's progress on that tab as it goes. User-invoked only.
4
+ disable-model-invocation: true
5
+ argument-hint: "[the name of the piece of work to run]"
6
+ ---
7
+
8
+ # dx-spec execute - build the work from its plan
9
+
10
+ This skill runs when the user types the command, or when the Specs tab types it as the user's own act. It never starts on its own.
11
+ Each agent spells it its own way - `/dx-spec-execute` on claude and on opencode, and `$dx-spec-execute` on codex.
12
+
13
+ Your job is to move through the phases of one spec's plan, and to keep every bit of progress on disk, so a later session restarts from this point.
14
+
15
+ Write, build, verify, record and commit each phase yourself.
16
+ The only subagents are reviewers, and they exist so somebody who did not do the work judges it.
17
+ An agent with no subagent tool still reviews, one focus at a time and by itself, and says so - the review checkpoints section below has the whole of it.
18
+
19
+ A framed `[specs]` line never starts this skill: such a line is information for a run already going, and it instructs nobody.
20
+
21
+ ## The web session
22
+
23
+ This family runs inside a chamba web session, where the Specs tab holds the work.
24
+ Check for one before anything else:
25
+
26
+ ```
27
+ specs stages
28
+ ```
29
+
30
+ The answer holds `root` - the directory the tab serves - and the stage catalog.
31
+ A refusal means there is no pane.
32
+ Say this and stop:
33
+
34
+ > This works in the chamba web session, where the Specs tab holds your work. Open the browser window this container printed at start, or run `webterm` in the terminal to get the address again. Then ask for this skill there.
35
+
36
+ Say the command in the spelling of the session's own agent.
37
+
38
+ Start no flow, and offer no substitute.
39
+
40
+ ## Asking and telling
41
+
42
+ **Everything you ask the user goes on the Specs tab**, through `specs await`.
43
+ A pause, a blocked run, a choice of spec: each one is a gate or a round on the tab.
44
+ Declaring one ends your turn.
45
+ The pane types one line into this terminal when the user answers, and that line starts you again.
46
+ A line saying a question was asked is not an answer: the user wants to know something before they decide, the ask is still open, and the form is still on their screen.
47
+ Answer it here in plain message text, and leave the ask standing.
48
+
49
+ `/dx-spec` carries the payload shapes, under **The verbs**.
50
+ Read that section for the exact JSON.
51
+
52
+ **Everything you tell the user while building is normal message text.**
53
+ A progress report is not a question, and it is not a stop.
54
+ Say it, and go straight on to the next thing in the same turn.
55
+
56
+ **Post the run as it moves**, so the board shows it live:
57
+
58
+ ```
59
+ echo '{"kind":"status","status":"executing"}' | specs post <spec>
60
+ echo '{"kind":"phase","phase":"phase-02-the-consuming-skills","title":"The consuming skills","status":"running"}' | specs post <spec>
61
+ echo '{"kind":"activity","said":"Running the verify steps"}' | specs post <spec>
62
+ echo '{"kind":"phase","phase":"phase-02-the-consuming-skills","title":"The consuming skills","status":"done"}' | specs post <spec>
63
+ echo '{"round":1,"verdict":"holds, one for you","findings":6,"judgments":1}' | specs review <spec>
64
+ ```
65
+
66
+ A phase's id is its file's name without the extension, which is what you already write and tick.
67
+
68
+ ## The run does not stop on its own
69
+
70
+ The `pauses` decision is the only thing that stops this run for the user.
71
+ `"pauses": "none"` means you build every phase, from the first to the last, and give the turn back once.
72
+
73
+ Four things end your turn, and nothing else does:
74
+
75
+ - A pause the decisions put there.
76
+ - A stop condition from **Stop and ask**.
77
+ - An ask this skill itself sends you to the tab with: the branch gate, and a decision card for a review finding that is the user's to judge. Declaring one ends your turn, as it does everywhere in this family.
78
+ - The end of the run, where the report goes.
79
+
80
+ A review checkpoint is not one of them.
81
+ Run it, fix what it found, and start the next phase in the same turn.
82
+ Finishing a phase is not one of them either, however much there is to say about it.
83
+
84
+ Waiting for a reviewer you started is not a stop.
85
+ Where your environment makes you give the turn back to hear one, say that you are waiting, and carry the run on the moment the answer arrives.
86
+
87
+ A run that gives the turn back with a phase left to build has failed, unless it is at one of the four.
88
+ Before you end a turn, name which of the four you are at.
89
+ With none of them, you are not finished: go on to the next step.
90
+
91
+ ## The tool owns the state
92
+
93
+ The Specs tool writes `state.json` and each spec's `README.md`.
94
+ **Never edit either one.**
95
+ The status, the phase list, the review rounds and the dated log are the tool's, and the verbs above are how they move.
96
+
97
+ `plan/PLAN.md` and the phase files are yours, and you write them by hand as always.
98
+
99
+ ## A page you make during the run
100
+
101
+ A page that explains this piece of work belongs to the work, so save it into the spec directory under a lowercase name.
102
+ The tab lists it beside the other artifacts and renders it.
103
+ A page about anything else goes to the Pages tab with `webpane`.
104
+ Either way, keep it standalone: no external stylesheet, no script that fetches anything, and images as data URLs.
105
+
106
+ ## The run decisions are the user's instruction
107
+
108
+ `runDecisions` in the state holds how this run is shaped: the commits, the branch, the pauses and the review checkpoints.
109
+ The user settled every one of them on the execute form, in the act that started this run, and all of it binds you.
110
+
111
+ ```json
112
+ { "commits": "per-phase", "branch": "new", "branchName": "spec/checkout-discounts",
113
+ "pauses": "chosen", "pausePhases": ["phase-03-the-banner"],
114
+ "checkpoints": [
115
+ { "after": "final", "why": "judges the finished work as a whole",
116
+ "panel": ["correctness against the spec", "docs consistency"], "chosen": true } ],
117
+ "decidedAt": "2026-08-31T09:12:04.118Z" }
118
+ ```
119
+
120
+ Read each field as it is written, and never read around it:
121
+
122
+ - `pausePhases` counts only where `pauses` is `chosen`. A list left standing beside `none` or `every` is not a pause.
123
+ - A checkpoint counts only where its `chosen` is true. One that is false stays in the record and does not run.
124
+ - `branchName` counts only where `branch` is `new`.
125
+
126
+ **A spec with no `runDecisions` is a spec whose run was never set up.**
127
+ Say so, name the Execute action on the tab as where it is settled, and stop.
128
+ An old spec may carry lines in `plan/PLAN.md` that describe how its run was once meant to go.
129
+ Those lines are not the decisions and never override them.
130
+ The form never reads them: it opens on the recommendations where there are any, and on the built-in defaults where there are none.
131
+ So where the state holds decisions they bind you, and where it holds none there is nothing to act on, whatever `PLAN.md` says.
132
+
133
+ ## The plan is the user's instruction
134
+
135
+ The user chose everything in the plan at spec time: the phases, the verify steps, and the principles check with its recorded deviations.
136
+ All of it binds this run.
137
+
138
+ A recorded deviation is an approved exception.
139
+ Build the work to match it, and never return it quietly to the principle it excepts.
140
+ A principle the plan does not except stays binding.
141
+
142
+ Your own limits do not justify a change to the plan.
143
+ When you cannot do what the plan asks, that is a stop condition, and the **Stop and ask** section says how to raise it.
144
+ Never replace the plan's instruction with a weaker one and carry on.
145
+
146
+ Two things fall outside this rule.
147
+ A choice of your own that changes nothing - which of two equal ways to read a file - is yours.
148
+ An instruction from the user in this conversation overrides the plan.
149
+ The plan loses to the user, and never to you.
150
+
151
+ ## 0. Bootstrap
152
+
153
+ Settle the root and the principles first:
154
+
155
+ 1. Read `root` from `specs stages`. That is the directory the Specs tab serves, and every path in this family resolves against it.
156
+ 2. Read `<root>/principles.md`. It is plain markdown: a `# Principles` heading, then one `- <principle>` bullet each, and nothing else.
157
+ 3. Where no file is there, write one from `../dx-spec/references/principles-template.md`. Say in one line that you made it, and carry straight on.
158
+ 4. Honor every principle the file holds for the rest of the run, best-effort - which is the standing each one has, and no more. A file with no bullets holds no principles, so there is nothing to honor.
159
+
160
+ A file that is already there produces no message at all.
161
+ Nothing else about how this family works is configurable, and the user changes a principle by editing that file.
162
+
163
+ ## 1. Choose the spec
164
+
165
+ A candidate is a piece of work under the root, outside `archive/`, whose plan still holds an unticked phase.
166
+
167
+ The user may name one.
168
+ A name that matches one spec exactly is an instruction, not a guess.
169
+ Start on it, and ask nothing.
170
+
171
+ In three cases the spec is a guess of yours:
172
+
173
+ - A name that matches no spec exactly.
174
+ - A spec you infer from the conversation.
175
+ - The one candidate, when there is only one.
176
+
177
+ Confirm a guess in a round before you do any work, even when one candidate exists.
178
+ Give the round one option per candidate, carrying how many phases are done and what comes next, and one option that means none of these.
179
+ Declare the round on the likeliest candidate, because every asking verb names a spec and it has to be one that is there.
180
+ Change nothing until the user answers.
181
+
182
+ With no candidate at all, ask nothing.
183
+ Say there is no open work, name the `dx-spec` skill as where a spec gets written, and stop.
184
+
185
+ Then read, in this order: `specs state <spec>`, `spec/SPEC.md`, and `plan/PLAN.md`.
186
+ Do not read a phase file yet. Read or write one when its phase starts.
187
+
188
+ Handle these three states before anything else:
189
+
190
+ - No `plan/` directory: say this spec has no execution plan, say the `dx-spec` skill adds one, and stop.
191
+ - Every phase ticked: say so, and stop.
192
+ - No `runDecisions` in the state: say the run is not set up yet, name the Execute action on the tab as where that happens, and stop.
193
+
194
+ ## 2. Work out where things stand
195
+
196
+ Work it out from disk alone.
197
+ Say it in a message before you do anything, together with what the decisions ask for: where this run pauses, and where it gets reviewed.
198
+ The user then knows from the start where this run stops and where it gets reviewed.
199
+
200
+ **The phases.**
201
+
202
+ - A phase ticked in `PLAN.md` is done.
203
+ - The first unticked phase is next.
204
+ - A phase file that exists carries a `Status:` line: `in progress` or `done`.
205
+ - A file at `in progress` under an unticked phase means an earlier session stopped mid-phase. Treat it as suspect: check `git status` for half-done work, re-run the verify steps of the ticked tasks, and carry on from where the evidence points.
206
+ - The state file carries a dated line for each completed review checkpoint. A checkpoint whose phases are all done with no such line has not run. Run it before you move on.
207
+
208
+ **The commits.**
209
+ `commits` is `per-phase`, `single` or `none`.
210
+ It says how much of the record the repository itself shows:
211
+
212
+ - `per-phase`: a completed phase is a committed phase, and uncommitted changes belong to the phase that was interrupted.
213
+ - `single` or `none`: nothing is committed before the run ends, if at all. The phase status lines and the ticked boxes are the only record.
214
+
215
+ Read `branch` with it.
216
+ `current` means the commits land on the branch that is checked out, and `new` means they land on the branch `branchName` names.
217
+
218
+ **A state that does not match.**
219
+ When the disk does not hold what this skill expects, settle it with judgment and say what you chose in the report.
220
+ Do not stop, and do not follow a fixed rule.
221
+
222
+ ## 3. The loop
223
+
224
+ Every phase goes through the same steps, and you carry out every one yourself.
225
+
226
+ 1. **The phase file.**
227
+
228
+ Check for a phase file an interrupted run left, and never rewrite one.
229
+ Check it against the actual code before you change anything.
230
+ Keep its ticked tasks: they show what got done.
231
+ Adapt a small change and note it. Treat a design change as a stop condition.
232
+
233
+ With no such file, write `plan/phase-NN-<slug>.md` right before you build the phase, against the code as it stands after the earlier phases.
234
+ Take the goal and the definition of done from `PLAN.md`:
235
+
236
+ ```markdown
237
+ # Phase 02 - <name>
238
+
239
+ Status: in progress
240
+
241
+ ## Tasks
242
+
243
+ - [ ] <task>
244
+ - [ ] <task>
245
+
246
+ ## Verify
247
+
248
+ - <command or check that proves the definition of done>
249
+ ```
250
+
251
+ Do not give much weight to development cost when you write it.
252
+ Prefer quality, simplicity, robustness, scalability and long-term maintainability.
253
+ When something grows too large, restructure it by judgment: refactor, split, or extract.
254
+ Never restructure by a number set in advance.
255
+
256
+ Post the phase as `running`.
257
+
258
+ 2. **Build.** Keep the changes scoped to this phase, and tick each task as it completes.
259
+
260
+ 3. **Verify.** Run the Verify section, and make it pass.
261
+
262
+ 4. **Record.** Set the phase's `Status:` to `done`, tick its box in `PLAN.md`, and post the phase as `done`.
263
+
264
+ With `"commits": "per-phase"`: commit the phase's changes with a one-line message in the commit style, using "phase NN - <name>" as its descriptive part. The style is stated once, in `dx-spec/SKILL.md` under **The commit style**, and a phase commit takes the type that suits what the phase did. Commit only. Never push.
265
+
266
+ With `single` or `none`: commit nothing here.
267
+
268
+ Finish the whole record before the next phase starts, so an interrupted session loses nothing.
269
+
270
+ Then, in this order:
271
+
272
+ - The review checkpoint after this phase, when the decisions put one there.
273
+ - The pause after this phase, when the decisions put one there.
274
+
275
+ A pause therefore always reports a result that is already reviewed and fixed.
276
+ Where no pause sits after this phase, the next phase starts in the same turn.
277
+
278
+ **On the first phase of a run**, post the status as `executing`.
279
+
280
+ **Before that first phase, commit the run decisions**, where the spec directory is already committed.
281
+ The decisions the user made are part of the record of this work, so the spec directory is committed on the branch that is checked out now, before any move to the run's branch.
282
+ What lands is whatever of that directory the repository keeps: the rendered `README.md` always, and the state file where the repository does not ignore it.
283
+ Stage the spec directory by path, and compose the message from **The commit style** in `../dx-spec/SKILL.md`, with "<spec name> run set up" as the descriptive part.
284
+ This one commit happens whatever `commits` says, `none` included: it records what the user decided, not what this run built.
285
+ Where the spec directory holds nothing uncommitted, there is nothing to commit and nothing to say.
286
+ Where it was never committed at all, commit nothing, and say so in the report.
287
+
288
+ **Then, where `branch` is `new`**, put the run on the branch `branchName` names:
289
+
290
+ - Already on it: carry on.
291
+ - It exists but is not checked out: switch to it.
292
+ - It does not exist: create it from the current `HEAD`.
293
+
294
+ When the working tree holds changes that do not belong to this work, gate it on the tab before you switch or create: move to the branch and bring those changes along, or stay where you are and commit on the current branch.
295
+ When the user stays, say in the report that this run's commits did not go to the branch that was decided.
296
+
297
+ Never merge, and never push. Merging the work back is the user's job.
298
+
299
+ **The final commit with `"commits": "single"`.**
300
+ Once the last phase passes its verify steps and finishes its record step, commit everything this work produced, in one commit.
301
+ Compose the message from **The commit style** in `dx-spec/SKILL.md`, with the `PLAN.md` title as its descriptive part.
302
+ That commit also covers earlier runs of the same work, which committed nothing.
303
+ Make it before the final review checkpoint: a final checkpoint must never read an uncommitted tree when commits are on.
304
+ `single` counts what the run built.
305
+ The run-decisions commit at the start and the closing commit at the end stand outside that count: each one carries the record of the run rather than the work, and each one happens on its own terms.
306
+
307
+ With `"commits": "none"`, commit nothing this run built, at any point.
308
+ The run-decisions commit above is not one of the run's commits, and happens either way.
309
+
310
+ **Keep the spec true.**
311
+ When what gets built differs from `spec/SPEC.md` or the plan, update those files as part of the phase.
312
+ The spec is the documentation of what was actually built.
313
+ `/dx-spec` carries the rule over this, under **Keep what derives from a change true**: what derives from the document you changed is refreshed in the same act.
314
+ When the solution departs noticeably from an approved solution sketch, that is a stop condition: the sketch does not bind you, but the user approved it and has to stay informed.
315
+
316
+ ### Review checkpoints
317
+
318
+ `checkpoints` says where fresh reviewers judge the work.
319
+ Each one names where it sits in `after` - a phase id, or the word `final` - and holds one focus per reviewer in `panel`.
320
+ A checkpoint runs only where its `chosen` is true; one that is false is a record of what was offered and declined.
321
+ With no chosen checkpoint at all, no review runs, and the report says the user chose none.
322
+
323
+ Run a checkpoint once you record the phase it sits after, and once any commit the decisions ask for has landed.
324
+ Run the `final` checkpoint once the last phase is recorded, any required commit has landed, and before the end-of-run report.
325
+
326
+ **Start one reviewer subagent per focus the checkpoint names.**
327
+ The reviewers share no context: each gets its own prompt, none sees this conversation, none learns what the others found, and you are never one of them.
328
+ A reviewer's whole value comes from judging work it did not do.
329
+
330
+ An agent with no subagent tool runs the same passes itself, one focus at a time, and writes each pass's findings down before it starts the next.
331
+ Say in the report that the checkpoint ran inline, and that the fresh-context guarantee did not hold.
332
+ An agent that has the capability but cannot make it work is a different case: that is a stop condition, and it never downgrades quietly.
333
+
334
+ Tell each reviewer:
335
+
336
+ - Its focus, and to check the work against what was asked.
337
+ - Which files to read: `spec/SPEC.md`, `plan/PLAN.md`, the phase files written so far, and the principles file.
338
+ - That the plan's principles check records deviations as approved exceptions, not as findings.
339
+ - How to see the changes: name the commit the work started from, so it can diff against that, and point at the working tree when nothing is committed.
340
+
341
+ Each reviewer returns a verdict with concrete findings, each pointing at a file and a line.
342
+
343
+ Then, yourself:
344
+
345
+ - Merge the findings, and remove the duplicates.
346
+ - Dismiss what you judge wrong, and say why in the report.
347
+ - Fix the rest yourself.
348
+ - Re-run the Verify steps of every phase the fixes touch.
349
+ - Have each reviewer who rejected a fix re-check it once.
350
+ - When a reviewer still objects after that re-check, you settle it: fix it once more when the objection names something real, and otherwise record the finding as knowingly accepted and move on.
351
+ - When commits are on, land the fixes in one follow-up commit, in **The commit style**, with "<spec name> review fixes" as its descriptive part.
352
+
353
+ Post the round with `specs review`, so the board carries its verdict.
354
+ Bring a finding that is the user's to judge to the tab as a decision card, rather than settling it yourself.
355
+ A card names the passage it is about by file and by section, asks one question, and offers the ways out as its own options, one of them recommended.
356
+ `../dx-spec/SKILL.md` carries the shape, under **The verbs**.
357
+
358
+ ### Pauses
359
+
360
+ `pauses` says when the run stops for the user.
361
+ `none` stops nowhere, `every` stops after every phase, and `chosen` stops after each phase `pausePhases` names.
362
+ Read `pausePhases` only where `pauses` is `chosen`: a list beside `none` is what an earlier decision left behind, and it is not a pause.
363
+
364
+ At a pause, once the phase is built, reviewed where a checkpoint sits there, recorded and committed, report what the phase produced in a message.
365
+ Then declare a gate: carry on, or stop here.
366
+ Point the gate at the artifact the phase produced, where one exists, so the user reads it and answers in the same place.
367
+
368
+ On "carry on": start the next phase in this session.
369
+ On "stop": end the run with the report below.
370
+ A later run finds from disk where this one stopped, resumes at the next unfinished phase, and pauses only at the pauses ahead of it.
371
+
372
+ A pause after the last phase has nothing to carry on to.
373
+ Run the final checkpoint, and give the end-of-run report instead of asking.
374
+
375
+ ## 4. Stop and ask
376
+
377
+ Stop and ask when one of these three holds. Otherwise keep going on your own.
378
+
379
+ 1. **The work deviates significantly from the spec.**
380
+ - What you build contradicts `spec/SPEC.md`.
381
+ - A fact you found makes the approved shape no longer fit.
382
+ - The solution departs noticeably from an approved solution sketch.
383
+
384
+ 2. **A blocker only the user can resolve.**
385
+ - A scope or design decision with no single right answer.
386
+ - An action that is destructive or hard to reverse: a data migration, a deletion, a call to an external service.
387
+ - Missing access only the user can grant.
388
+
389
+ 3. **The run cannot carry on for technical reasons.**
390
+ - A verify step that still fails after a real attempt to fix it.
391
+ - A tool the plan needs, and your environment refuses to give.
392
+
393
+ For the third one, declare a gate saying what the plan asks for and why you cannot do it here.
394
+ Offer stopping and carrying on without it, and say plainly that the report will name what never happened.
395
+
396
+ **Everything else is yours to settle**: a review finding, a wording call, an unclear task, which of two fixes reads better.
397
+ Settle it, keep the run moving, and say in the report what you settled.
398
+ A run that stops to ask about something it could have settled has failed.
399
+ Quality is yours to hold, and the user is waiting for the finished work.
400
+
401
+ ## 5. The end of the run
402
+
403
+ Every phase is done, the final checkpoint has run, and its review-fixes commit has landed.
404
+ Then, in this order:
405
+
406
+ ```
407
+ echo '{"kind":"status","status":"complete"}' | specs post <spec>
408
+ ```
409
+
410
+ The post moves the state file and the rendered `README.md`, so it goes before the commit that carries them.
411
+
412
+ Then, unless `commits` is `none`, stage the spec directory by path and commit it.
413
+ Compose the message from **The commit style** in `dx-spec/SKILL.md`, with "<spec name> complete" as its descriptive part, which makes it a `docs` commit.
414
+ This one commit covers the phase files, the ticked plan, the spec the run kept true, and the rendered board.
415
+ Staging by path leaves every other change in the working tree alone, and the tree is clean over the spec directory once it lands.
416
+ Commit only. Never push.
417
+
418
+ With `"commits": "none"`, post the status and commit nothing.
419
+ The report then says the closing status was left uncommitted, so the user knows the state file and the `README.md` are changed and theirs to keep or to drop.
420
+
421
+ Then give the report below, which is what ends the turn and says what the closing commit did.
422
+
423
+ ## 6. Report
424
+
425
+ This report is what ends a turn.
426
+ Give it at the end of the run, at a pause, or at a stop condition, and nowhere else.
427
+ What you say between phases is a progress line: say it and keep building.
428
+
429
+ Report in a normal message:
430
+
431
+ - The phases finished this run, and the overall progress, such as "5/7 phases done".
432
+ - What the verification showed, and the outcome of every checkpoint this run ran: what the reviewers found, what was fixed, what was dismissed and why. When no checkpoint was chosen, say so.
433
+ - What you settled on your own, where it matters to the user.
434
+ - The branch, when the run committed to one of its own, that it is unmerged, and that merging it back is the user's job.
435
+ - Whether the run decisions were committed, and where they were not, that the spec directory was never committed.
436
+ - Why the run stopped, when it stopped at a pause or a stop condition, and what you need from the user.
437
+ - Whether the closing commit landed, and under `"commits": "none"`, that the closing status was left uncommitted.
@@ -71,7 +71,7 @@ try {
71
71
 
72
72
  // -- Context snapshot cleanup (best-effort, silent) ---------------------------
73
73
  // claude-statusline.sh writes one small per-session snapshot to this directory on every
74
- // prompt render (see the context-usage helper). Delete week-old entries here -- covers both
74
+ // prompt render, and the web interface's status strip reads it. Delete week-old entries here -- covers both
75
75
  // stale *.json snapshots and orphaned .tmp.* files. Any failure is swallowed so cleanup
76
76
  // can never affect readiness checks.
77
77
  const SNAPSHOT_DIR = "/home/devuser/.claude/context-usage";
@@ -165,7 +165,8 @@ const doUpdate = (label) =>
165
165
  });
166
166
  });
167
167
 
168
- // SPACE within `seconds` -> skip. Any other input is ignored. Ctrl+C exits 130. Non-TTY -> no skip.
168
+ // SPACE within `seconds` -> skip. ENTER -> update now, without waiting out the countdown. Any other input is
169
+ // ignored. Ctrl+C exits 130. Non-TTY -> no skip.
169
170
  const promptSkipUpdate = (seconds) =>
170
171
  new Promise((resolve) => {
171
172
  if (!process.stdin.isTTY) {
@@ -175,7 +176,7 @@ const promptSkipUpdate = (seconds) =>
175
176
  let remaining = seconds;
176
177
  let tick;
177
178
  let timer;
178
- const line = (s) => `\r\x1b[K${blue("●")} ${dim(`Updating AI CLIs in ${s}s... press SPACE to skip`)}`;
179
+ const line = (s) => `\r\x1b[K${blue("●")} ${dim(`Updating AI CLIs in ${s}s... SPACE to skip, ENTER to update now`)}`;
179
180
  const cleanup = () => {
180
181
  clearInterval(tick);
181
182
  clearTimeout(timer);
@@ -195,6 +196,11 @@ const promptSkipUpdate = (seconds) =>
195
196
  resolve(true);
196
197
  return;
197
198
  }
199
+ if (byte === 0x0d || byte === 0x0a) {
200
+ cleanup();
201
+ resolve(false);
202
+ return;
203
+ }
198
204
  }
199
205
  };
200
206
  process.stdin.setRawMode(true);
@@ -221,7 +227,7 @@ if (Number.isFinite(lastUpdate) && Date.now() - lastUpdate < THROTTLE_MS) {
221
227
  if (summary) console.log("");
222
228
  if (!timestampFileExists) {
223
229
  await doUpdate("Installing AI CLIs...");
224
- } else if (await promptSkipUpdate(3)) {
230
+ } else if (await promptSkipUpdate(5)) {
225
231
  skip("AI CLIs", "update skipped by user");
226
232
  } else {
227
233
  await doUpdate("Updating AI CLIs to latest...");
@@ -0,0 +1,166 @@
1
+ #!/usr/bin/env bash
2
+ # The agent's half of a pane tool's channel, in this session's web pane.
3
+ # Baked into the image at /usr/local/share/chamba/tool-helper.sh, on PATH as `specs` and as `reviews`.
4
+ #
5
+ # One script, and the tool it is for is the name it was called by. The two channels differ in a word, a door
6
+ # and a list of verbs, and in nothing else: the pane writes a file and types one line into this terminal, and
7
+ # these verbs are how the work moves the other way. Everything they change is a file in the tool's own
8
+ # directory, so every exchange is something git can see.
9
+ #
10
+ # The caller says who it is by its own pid and nothing more, exactly as `webpane` does. The server walks up
11
+ # the process tree from there to a session it started, and refuses a caller that is not in one.
12
+ set -euo pipefail
13
+
14
+ CONTAINER_PORT="${WEBTERM_PORT:-3899}"
15
+ KEY_FILE="${WEBTERM_KEY_FILE:-/tmp/webterm.key}"
16
+
17
+ # Which tool this is. The link's own name, so `specs` and `reviews` are one script with one set of rules.
18
+ TOOL="$(basename "$0")"
19
+
20
+ case "$TOOL" in
21
+ specs | reviews) ;;
22
+ *)
23
+ echo "tool-helper: no pane tool is called ${TOOL}." >&2
24
+ exit 1
25
+ ;;
26
+ esac
27
+
28
+ usage_specs() {
29
+ cat <<'TEXT'
30
+ Usage: specs <verb> [<spec>] < payload.json
31
+
32
+ Verbs:
33
+ stages Where the specs live, and every stage a protocol may hold.
34
+ state <spec> Where the work stands: protocol, activity, what awaits the user.
35
+ intake [<spec>] File a new piece of work. { name, text, attachments, wish }
36
+ propose <spec> Recommend a protocol. { stages: [{ id, recommended, why }], note }
37
+ recommend <spec> Recommend how the run is shaped. { commits, branch, branchName, pauses, pausePhases, pauseReasons, noPauseReason, checkpoints }
38
+ await <spec> Declare a gate, a round or a decision card. { id, kind, title, file, payload }
39
+ withdraw <spec> Take back an ask nobody needs to answer. { id, reason }
40
+ post <spec> Say what is happening. { kind: activity | stage | phase | plan | status | note, ... }
41
+ review <spec> Post a round of quality review. { round, verdict, findings, judgments }
42
+ answers <spec> Read back what the user answered. { id }
43
+
44
+ A payload is JSON on stdin, where the verb takes one. The answer is JSON on stdout.
45
+ TEXT
46
+ }
47
+
48
+ usage_reviews() {
49
+ cat <<'TEXT'
50
+ Usage: reviews <verb> [<review>] < payload.json
51
+
52
+ Verbs:
53
+ stages Where the reviews live, and what a review may hold.
54
+ state <review> The state, the baseline's summary, and what awaits whom.
55
+ start File a review from this terminal. { name, selection }
56
+ metadata <review> Post the grouping and the notes for the baseline. { fingerprint, groups, notes }
57
+ reply <review> Append one reply to a thread. { thread, text }
58
+ activity <review> Say what you are doing. { said }
59
+ done <review> End your act; the tool recomputes the baseline where one is due.
60
+ log <review> One dated line for the activity log. { said }
61
+
62
+ A payload is JSON on stdin, where the verb takes one. The answer is JSON on stdout.
63
+
64
+ Nothing here marks a hunk seen, closes a thread, approves, or changes the baseline of its own accord.
65
+ Those are the reviewer's, through the tab.
66
+ TEXT
67
+ }
68
+
69
+ VERB="${1:-}"
70
+ ENTRY="${2:-}"
71
+
72
+ case "$VERB" in
73
+ "" | -h | --help)
74
+ "usage_${TOOL}"
75
+ exit 0
76
+ ;;
77
+ esac
78
+
79
+ if [ ! -r "$KEY_FILE" ]; then
80
+ echo "${TOOL}: the web interface is not running in this container, so there is no pane to tell." >&2
81
+ exit 1
82
+ fi
83
+
84
+ # A payload is read only for a verb that takes one. The verbs that carry none never touch stdin: an agent's
85
+ # shell often runs with a stdin that is neither a terminal nor closed - a socket, an open pipe - and a read on
86
+ # it waits for a close that never comes, which hangs the agent rather than answering it. So what is read is a
87
+ # pipe or a file, which is what a payload arrives on, and nothing else is read at all.
88
+ #
89
+ # The list is every such verb of either tool: `stages` and `state` read, and `reviews done` says its one thing
90
+ # by being called. A name no tool has is refused by the server whatever happens here.
91
+ BODY_FILE="$(mktemp)"
92
+ REQUEST_FILE="$(mktemp)"
93
+ PAYLOAD_FILE="$(mktemp)"
94
+ trap 'rm -f "$BODY_FILE" "$REQUEST_FILE" "$PAYLOAD_FILE"' EXIT
95
+
96
+ # The payload lands in a file rather than in a variable, because it is passed to jq and an argument has a
97
+ # size limit of its own: the grouping of a large review is hundreds of kilobytes, which is well under what
98
+ # the server accepts and over what one argument can carry.
99
+ echo -n "null" >"$PAYLOAD_FILE"
100
+ case "$VERB" in
101
+ stages | state | done) ;;
102
+ *)
103
+ if [ -p /dev/stdin ] || [ -f /dev/stdin ]; then
104
+ cat >"$PAYLOAD_FILE"
105
+ if [ ! -s "$PAYLOAD_FILE" ]; then
106
+ echo -n "null" >"$PAYLOAD_FILE"
107
+ fi
108
+ fi
109
+ ;;
110
+ esac
111
+
112
+ # The request is composed with jq so the payload stays JSON rather than being spliced into a string. jq is in
113
+ # the image; without it there is no safe way to build this, so the helper says so instead of guessing.
114
+ if ! command -v jq >/dev/null 2>&1; then
115
+ echo "${TOOL}: jq is not in this container, and the request cannot be composed without it." >&2
116
+ exit 1
117
+ fi
118
+
119
+ # `--slurpfile` reads the payload as JSON from the file, and jq's own words are kept: a payload that is
120
+ # not JSON and a payload too large to read say different things, and an agent told the wrong one rewrites
121
+ # correct JSON for ever.
122
+ if ! SAID="$(jq -n --arg verb "$VERB" --arg entry "$ENTRY" --slurpfile payload "$PAYLOAD_FILE" \
123
+ '{ verb: $verb, entry: $entry, payload: $payload[0] }' 2>&1 >"$REQUEST_FILE")"; then
124
+ echo "${TOOL}: the payload on stdin is not JSON - ${SAID}" >&2
125
+ exit 1
126
+ fi
127
+
128
+ # How long an answer is waited for. Most verbs write a file and answer; `start` and `done` recompute a
129
+ # baseline, which reads the whole diff of a selection and every blob in it, and a wait that ran out there
130
+ # would tell the agent the act failed while the interface was still doing it.
131
+ case "$VERB" in
132
+ start | done) DEADLINE=300 ;;
133
+ *) DEADLINE=30 ;;
134
+ esac
135
+
136
+ # $$ is this script's own pid. The server climbs from it to whichever session started it.
137
+ STATUS="$(
138
+ curl -sS -o "$BODY_FILE" -w '%{http_code}' \
139
+ --connect-timeout 3 --max-time "$DEADLINE" \
140
+ -X POST \
141
+ -H "Content-Type: application/json" \
142
+ -H "X-Webterm-Key: $(head -n 1 "$KEY_FILE")" \
143
+ -H "X-${TOOL^}-Pid: $$" \
144
+ --data-binary "@${REQUEST_FILE}" \
145
+ "http://127.0.0.1:${CONTAINER_PORT}/${TOOL}/agent" 2>/dev/null
146
+ )" || {
147
+ echo "${TOOL}: could not reach the web interface on this container's port ${CONTAINER_PORT}." >&2
148
+ exit 1
149
+ }
150
+
151
+ if [ "$STATUS" = "200" ]; then
152
+ jq . <"$BODY_FILE"
153
+ exit 0
154
+ fi
155
+
156
+ REASON="$(jq -r '.error // empty' <"$BODY_FILE" 2>/dev/null)"
157
+ if [ -z "$REASON" ]; then
158
+ case "$STATUS" in
159
+ 403) REASON="the key on disk is not the one the interface is using" ;;
160
+ 409) REASON="this is not running inside a web session" ;;
161
+ 413) REASON="the payload is too large" ;;
162
+ *) REASON="the interface answered ${STATUS}" ;;
163
+ esac
164
+ fi
165
+ echo "${TOOL}: nothing was done - ${REASON}." >&2
166
+ exit 1