tumwater 0.1.2 → 0.2.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 (361) hide show
  1. package/README.md +74 -108
  2. package/dist/build-info.json +2 -2
  3. package/dist/src/agent/agent-args.js +24 -0
  4. package/dist/src/agent/agent-backend.js +2 -2
  5. package/dist/src/agent/backend-traits.js +60 -0
  6. package/dist/src/agent/claude/claude-account.js +61 -8
  7. package/dist/src/agent/claude/claude-args.js +12 -15
  8. package/dist/src/agent/claude/claude-backend.js +51 -41
  9. package/dist/src/agent/claude/claude-env.js +7 -3
  10. package/dist/src/agent/claude/claude-stream.js +7 -16
  11. package/dist/src/agent/image-input.js +107 -0
  12. package/dist/src/agent/notes-shim.js +41 -0
  13. package/dist/src/agent/opencode/opencode-args.js +61 -0
  14. package/dist/src/agent/opencode/opencode-backend.js +255 -0
  15. package/dist/src/agent/opencode/opencode-catalog.js +184 -0
  16. package/dist/src/agent/opencode/opencode-config.js +188 -0
  17. package/dist/src/agent/opencode/opencode-env.js +36 -0
  18. package/dist/src/agent/opencode/opencode-session-id.js +37 -0
  19. package/dist/src/agent/opencode/opencode-stream.js +278 -0
  20. package/dist/src/agent/role-notes-write.js +16 -32
  21. package/dist/src/agent/run-agent.js +95 -18
  22. package/dist/src/agent/session-ledger.js +57 -1
  23. package/dist/src/backlog/backlog-archive.js +296 -0
  24. package/dist/src/backlog/backlog-cli.js +573 -0
  25. package/dist/src/backlog/backlog-commit.js +31 -0
  26. package/dist/src/backlog/backlog-eligibility.js +357 -33
  27. package/dist/src/backlog/backlog-md.js +98 -18
  28. package/dist/src/backlog/backlog-render.js +62 -11
  29. package/dist/src/backlog/backlog-structure.js +261 -58
  30. package/dist/src/backlog/backlog-write.js +197 -319
  31. package/dist/src/backlog/backlog.js +113 -16
  32. package/dist/src/backlog/question-answer.js +339 -0
  33. package/dist/src/baseline/main-baseline.js +75 -88
  34. package/dist/src/baseline/main-red.js +38 -30
  35. package/dist/src/brief/brief-edit.js +43 -0
  36. package/dist/src/{brief.js → brief/brief.js} +66 -10
  37. package/dist/src/budget/budget.js +32 -15
  38. package/dist/src/budget/fallback-breaker.js +8 -7
  39. package/dist/src/build/build-check-detect.js +8 -7
  40. package/dist/src/build/build-check-events.js +68 -22
  41. package/dist/src/build/build-check-report.js +22 -23
  42. package/dist/src/build/build-check-scoped.js +51 -62
  43. package/dist/src/build/build-check.js +15 -14
  44. package/dist/src/build/build-info.js +2 -1
  45. package/dist/src/build/build-stage.js +23 -22
  46. package/dist/src/build/dep-install.js +22 -21
  47. package/dist/src/build/host-sleep.js +1 -1
  48. package/dist/src/change/change-data.js +25 -14
  49. package/dist/src/change/change-render.js +11 -10
  50. package/dist/src/cli/brief-command.js +58 -0
  51. package/dist/src/cli/cli-args.js +25 -5
  52. package/dist/src/cli/cli-command-args.js +46 -14
  53. package/dist/src/cli/cli-editor.js +64 -0
  54. package/dist/src/cli/cli-flag-specs.js +54 -16
  55. package/dist/src/cli/cli-marker-commands.js +13 -10
  56. package/dist/src/cli/cli-output.js +9 -0
  57. package/dist/src/cli/cli-query-commands.js +458 -49
  58. package/dist/src/cli/cli-repo-gate.js +1 -1
  59. package/dist/src/cli/cli-run.js +208 -59
  60. package/dist/src/cli/completion.js +207 -0
  61. package/dist/src/cli/config-commands.js +14 -11
  62. package/dist/src/cli/help.js +305 -110
  63. package/dist/src/cli/log-commands.js +127 -21
  64. package/dist/src/cli/notify-command.js +33 -18
  65. package/dist/src/cli/question-commands.js +29 -224
  66. package/dist/src/cli.js +121 -40
  67. package/dist/src/collections.js +66 -16
  68. package/dist/src/concurrency/check-permit.js +16 -17
  69. package/dist/src/concurrency/lock.js +6 -5
  70. package/dist/src/concurrency/provider-lease.js +69 -0
  71. package/dist/src/concurrency/semaphore.js +258 -36
  72. package/dist/src/config/config-aliases.js +106 -0
  73. package/dist/src/config/config-editable-keys.js +3 -3
  74. package/dist/src/config/config-fallbacks.js +379 -0
  75. package/dist/src/config/config-field-checks.js +19 -10
  76. package/dist/src/config/config-keys.js +160 -0
  77. package/dist/src/config/config-live.js +64 -12
  78. package/dist/src/config/config-schema.js +34 -22
  79. package/dist/src/config/config-validation.js +132 -48
  80. package/dist/src/config/config-views.js +119 -337
  81. package/dist/src/config/config-write.js +138 -177
  82. package/dist/src/config/config.js +60 -49
  83. package/dist/src/config/model-selector.js +28 -2
  84. package/dist/src/doctor/doctor-backlog.js +546 -49
  85. package/dist/src/doctor/doctor-checks.js +534 -56
  86. package/dist/src/doctor/doctor-holds.js +193 -53
  87. package/dist/src/doctor/doctor-install.js +30 -0
  88. package/dist/src/doctor/doctor-launch-services.js +2 -2
  89. package/dist/src/doctor/doctor-model-checks.js +549 -157
  90. package/dist/src/doctor/doctor-notify.js +27 -0
  91. package/dist/src/doctor/doctor-orphans.js +10 -9
  92. package/dist/src/doctor/doctor-render.js +36 -9
  93. package/dist/src/doctor/doctor.js +80 -22
  94. package/dist/src/doctor/image-probe.js +244 -0
  95. package/dist/src/doctor/probe.js +64 -0
  96. package/dist/src/events/event-format.js +226 -102
  97. package/dist/src/events/event-labels.js +116 -0
  98. package/dist/src/events/event-legacy.js +140 -0
  99. package/dist/src/events/event-read.js +58 -22
  100. package/dist/src/events/event-window.js +27 -7
  101. package/dist/src/events/events.js +18 -14
  102. package/dist/src/events/notify.js +137 -38
  103. package/dist/src/failure/failure-cluster.js +12 -9
  104. package/dist/src/failure/failure-data.js +96 -48
  105. package/dist/src/failure/failure-render.js +69 -58
  106. package/dist/src/failure/failure-state-change.js +33 -26
  107. package/dist/src/failure/time-spend.js +79 -59
  108. package/dist/src/files/file-queue.js +16 -4
  109. package/dist/src/files/files.js +18 -6
  110. package/dist/src/files/json-object.js +18 -3
  111. package/dist/src/files/tail.js +1 -1
  112. package/dist/src/gates/bootstrap-gates.js +5 -4
  113. package/dist/src/gates/budget-gates.js +250 -34
  114. package/dist/src/gates/disk-gate.js +18 -18
  115. package/dist/src/gates/gate-polls.js +117 -69
  116. package/dist/src/gates/maintenance-quota.js +29 -29
  117. package/dist/src/gates/pause-gates.js +11 -11
  118. package/dist/src/gates/readiness.js +2 -1
  119. package/dist/src/gates/role-cap-gates.js +16 -21
  120. package/dist/src/gates/startup-gate.js +75 -31
  121. package/dist/src/gates/streak-gate.js +18 -16
  122. package/dist/src/git/commit-message.js +12 -5
  123. package/dist/src/git/git-diff.js +16 -16
  124. package/dist/src/git/git-run.js +8 -8
  125. package/dist/src/git/git.js +86 -55
  126. package/dist/src/git/slots-state.js +14 -5
  127. package/dist/src/git/worktree-pool.js +25 -18
  128. package/dist/src/git/worktree-use.js +15 -14
  129. package/dist/src/git/worktree.js +18 -18
  130. package/dist/src/gui/gui-args.js +10 -14
  131. package/dist/src/gui/gui-backlog-commands.js +273 -0
  132. package/dist/src/gui/gui-command.js +1 -1
  133. package/dist/src/gui/gui-config-commands.js +440 -0
  134. package/dist/src/gui/gui-endpoint-commands.js +16 -669
  135. package/dist/src/gui/gui-endpoint-common.js +49 -0
  136. package/dist/src/gui/gui-endpoints.js +116 -56
  137. package/dist/src/gui/gui-operator-commands.js +145 -0
  138. package/dist/src/gui/gui-prompt-commands.js +234 -0
  139. package/dist/src/gui/gui-server.js +28 -8
  140. package/dist/src/gui/gui-team-commands.js +164 -0
  141. package/dist/src/gui/http-body.js +17 -1
  142. package/dist/src/history/history-data.js +194 -98
  143. package/dist/src/history/history-render.js +76 -0
  144. package/dist/src/history/history.js +37 -58
  145. package/dist/src/inbox/inbox-attachments.js +75 -14
  146. package/dist/src/inbox/inbox-cancel.js +7 -5
  147. package/dist/src/inbox/inbox-edit.js +1 -1
  148. package/dist/src/inbox/inbox-submit.js +44 -4
  149. package/dist/src/inbox/inbox-top.js +83 -0
  150. package/dist/src/inbox/inbox.js +8 -2
  151. package/dist/src/inbox/pending-prompt.js +1 -1
  152. package/dist/src/inbox/prompt-commands.js +170 -80
  153. package/dist/src/init/init-templates.js +16 -14
  154. package/dist/src/init/init.js +12 -11
  155. package/dist/src/loop/leftover.js +28 -27
  156. package/dist/src/loop/loop-fallback.js +2 -2
  157. package/dist/src/loop/loop-pi.js +106 -52
  158. package/dist/src/loop/loop-recovery.js +15 -15
  159. package/dist/src/loop/loop-state.js +139 -13
  160. package/dist/src/loop/loop-tick.js +194 -86
  161. package/dist/src/loop/loop.js +72 -36
  162. package/dist/src/loop/model-fallback.js +3 -2
  163. package/dist/src/loop/revision.js +16 -7
  164. package/dist/src/merge/backlog-conflicts.js +155 -0
  165. package/dist/src/{landing/landing-batch.js → merge/merge-batch.js} +90 -89
  166. package/dist/src/{landing/landing-check-failures.js → merge/merge-check-failures.js} +87 -41
  167. package/dist/src/{landing/landing-core.js → merge/merge-core.js} +119 -95
  168. package/dist/src/{landing/landing-diff.js → merge/merge-diff.js} +1 -1
  169. package/dist/src/merge/merge-drain.js +212 -0
  170. package/dist/src/{landing/landing-git.js → merge/merge-git.js} +94 -21
  171. package/dist/src/merge/merge-pipeline.js +99 -0
  172. package/dist/src/{landing/landing-questions.js → merge/merge-questions.js} +9 -6
  173. package/dist/src/merge/merge-queue-render.js +75 -0
  174. package/dist/src/{landing/landing-queue.js → merge/merge-queue.js} +95 -36
  175. package/dist/src/merge/merge-slot.js +340 -0
  176. package/dist/src/{landing/landing-stack.js → merge/merge-stack.js} +66 -62
  177. package/dist/src/{landing/landing-merge.js → merge/merge-to-main.js} +72 -62
  178. package/dist/src/merge/merge-verify.js +212 -0
  179. package/dist/src/notes-cli.js +97 -0
  180. package/dist/src/operator/operator-commands.js +154 -47
  181. package/dist/src/operator/operator-intent.js +87 -49
  182. package/dist/src/operator/operator-requests.js +52 -52
  183. package/dist/src/operator/retire.js +62 -27
  184. package/dist/src/operator/revert.js +71 -0
  185. package/dist/src/orchestrator/orchestrator-launch.js +74 -14
  186. package/dist/src/orchestrator/orchestrator-scheduling.js +129 -44
  187. package/dist/src/orchestrator/orchestrator.js +158 -100
  188. package/dist/src/orchestrator/retention.js +29 -12
  189. package/dist/src/orchestrator/startup-migration-steps.js +51 -0
  190. package/dist/src/orchestrator/startup-migrations.js +40 -0
  191. package/dist/src/paths.js +128 -37
  192. package/dist/src/pi/pi-args.js +26 -3
  193. package/dist/src/pi/pi-backend.js +15 -23
  194. package/dist/src/pi/pi-bin.js +18 -12
  195. package/dist/src/pi/pi-event-line.js +17 -1
  196. package/dist/src/pi/pi-failure.js +87 -0
  197. package/dist/src/pi/pi-models.js +126 -38
  198. package/dist/src/pi/pi-run-result.js +1 -1
  199. package/dist/src/pi/pi-settings.js +86 -0
  200. package/dist/src/pi/pi-stream.js +131 -107
  201. package/dist/src/pi/pi-watchdogs.js +47 -43
  202. package/dist/src/pi-extension/bounded-output.js +20 -5
  203. package/dist/src/pi-extension/context-budget.js +3 -1
  204. package/dist/src/pi-extension/context-shake.js +54 -21
  205. package/dist/src/pi-extension/context-window.js +37 -0
  206. package/dist/src/pi-extension/full-output.js +2 -2
  207. package/dist/src/pi-extension/strip-thinking.js +80 -0
  208. package/dist/src/pi-extension/trim-system-prompt.js +38 -0
  209. package/dist/src/process/launch-services.js +11 -10
  210. package/dist/src/process/process-table.js +1 -1
  211. package/dist/src/process/process.js +7 -7
  212. package/dist/src/process/run-marker.js +29 -6
  213. package/dist/src/process/supervisor.js +47 -25
  214. package/dist/src/prompt/dialect.js +30 -10
  215. package/dist/src/prompt/gate-prompts.js +170 -67
  216. package/dist/src/prompt/principles.js +1 -0
  217. package/dist/src/prompt/prompt-followup.js +35 -20
  218. package/dist/src/prompt/prompt.js +249 -91
  219. package/dist/src/redeploy/redeploy-policy.js +10 -10
  220. package/dist/src/redeploy/redeploy.js +6 -4
  221. package/dist/src/redeploy/redeployer.js +63 -55
  222. package/dist/src/redeploy/self-reload.js +1 -1
  223. package/dist/src/report/report-data.js +264 -255
  224. package/dist/src/report/report-fold.js +353 -0
  225. package/dist/src/report/report-render.js +270 -33
  226. package/dist/src/request-timeouts.js +1 -1
  227. package/dist/src/review/known-flakes.js +34 -14
  228. package/dist/src/review/protected-edits.js +33 -0
  229. package/dist/src/review/review-followup.js +16 -4
  230. package/dist/src/review/review-precheck.js +14 -13
  231. package/dist/src/review/review-verdict.js +14 -0
  232. package/dist/src/review/review.js +81 -39
  233. package/dist/src/review/secret-leaks.js +127 -0
  234. package/dist/src/review/suite-rerun.js +4 -3
  235. package/dist/src/roles/legacy-role-ids.js +21 -0
  236. package/dist/src/roles/legacy-role-migration.js +62 -0
  237. package/dist/src/roles/loop-ids.js +19 -2
  238. package/dist/src/roles/role-catalog.js +125 -135
  239. package/dist/src/roles/role-guidance.js +64 -31
  240. package/dist/src/roles/role-labels.js +10 -0
  241. package/dist/src/roles/role-render.js +194 -18
  242. package/dist/src/roles/role-view.js +216 -17
  243. package/dist/src/roles/roles.js +18 -15
  244. package/dist/src/scheduling/backoff.js +29 -19
  245. package/dist/src/scheduling/claims.js +25 -9
  246. package/dist/src/scheduling/once-round.js +11 -11
  247. package/dist/src/scheduling/quiet-hours.js +25 -32
  248. package/dist/src/scheduling/scheduling.js +43 -24
  249. package/dist/src/scheduling/{work-landed-cache.js → work-merged-cache.js} +14 -14
  250. package/dist/src/status/status-budget.js +115 -0
  251. package/dist/src/status/status-data.js +77 -184
  252. package/dist/src/status/status-inbox.js +47 -0
  253. package/dist/src/status/status-install.js +27 -0
  254. package/dist/src/status/status-polls.js +68 -33
  255. package/dist/src/status/status-roles.js +105 -0
  256. package/dist/src/{fleet → team}/error-storm.js +10 -10
  257. package/dist/src/{fleet → team}/failure-spread.js +8 -8
  258. package/dist/src/{fleet → team}/orchestrator-info.js +86 -18
  259. package/dist/src/{fleet → team}/reclaim.js +5 -4
  260. package/dist/src/{fleet/fleet-hold.js → team/team-hold.js} +55 -21
  261. package/dist/src/{fleet/fleet-polls.js → team/team-polls.js} +116 -65
  262. package/dist/src/{fleet/fleet-state.js → team/team-state.js} +62 -37
  263. package/dist/src/text/datetime.js +42 -2
  264. package/dist/src/text/format.js +10 -1
  265. package/dist/src/text/markdown.js +1 -1
  266. package/dist/src/text/phrases.js +35 -34
  267. package/dist/src/text/suggest.js +3 -2
  268. package/dist/src/text/text.js +2 -2
  269. package/dist/src/tick/qa-coverage.js +2 -1
  270. package/dist/src/tick/stage-check.js +147 -26
  271. package/dist/src/tick/telemetry-digest.js +10 -9
  272. package/dist/src/tick/tick-apply.js +101 -72
  273. package/dist/src/tick/tick-detail-data.js +32 -18
  274. package/dist/src/tick/tick-detail.js +62 -21
  275. package/dist/src/tick/tick-finalize.js +29 -22
  276. package/dist/src/tick/tick-outcome.js +2 -1
  277. package/dist/src/tick/tick-prompt.js +94 -26
  278. package/dist/src/tick/tick-resume.js +29 -13
  279. package/dist/src/tick/tick-stage.js +91 -47
  280. package/dist/src/tick/tick-timing.js +60 -59
  281. package/dist/src/tick/tick-usage.js +53 -23
  282. package/dist/src/tick/tick-verdict.js +76 -10
  283. package/dist/src/ui/badges.js +118 -67
  284. package/dist/src/ui/gui/gui-client-boot.js +209 -30
  285. package/dist/src/ui/gui/gui-client-composer.js +102 -23
  286. package/dist/src/ui/gui/gui-client-drawer.js +330 -75
  287. package/dist/src/ui/gui/gui-client-history.js +96 -38
  288. package/dist/src/ui/gui/gui-client-labels.js +24 -0
  289. package/dist/src/ui/gui/gui-client-loops.js +94 -52
  290. package/dist/src/ui/gui/gui-client-markdown.js +1 -0
  291. package/dist/src/ui/gui/gui-client-model.js +63 -43
  292. package/dist/src/ui/gui/gui-client-operator.js +129 -39
  293. package/dist/src/ui/gui/gui-client-pending.js +36 -32
  294. package/dist/src/ui/gui/gui-client-report.js +339 -46
  295. package/dist/src/ui/gui/gui-client-settings.js +148 -14
  296. package/dist/src/ui/gui/gui-client-sound.js +2 -1
  297. package/dist/src/ui/gui/{gui-client-fleet.js → gui-client-team.js} +196 -72
  298. package/dist/src/ui/gui/gui-client.js +26 -20
  299. package/dist/src/ui/gui/gui-page.js +19 -19
  300. package/dist/src/ui/gui/gui-styles.js +12 -4
  301. package/dist/src/ui/progress-data.js +5 -3
  302. package/dist/src/ui/status-model.js +184 -122
  303. package/dist/src/ui/status-payload.js +119 -32
  304. package/dist/src/ui/status-render.js +122 -52
  305. package/dist/src/ui/team-alerts.js +341 -0
  306. package/dist/src/ui/tick-progress-model.js +14 -14
  307. package/dist/src/ui/tone.js +11 -8
  308. package/dist/src/ui/transcript-tail.js +1 -0
  309. package/dist/src/ui/transcript.js +23 -16
  310. package/dist/src/ui/tui/tui-app.js +1 -0
  311. package/dist/src/ui/tui/tui-backlog.js +85 -11
  312. package/dist/src/ui/tui/tui-editor-answer.js +97 -0
  313. package/dist/src/ui/tui/tui-editor-attach.js +107 -0
  314. package/dist/src/ui/tui/tui-editor-brief.js +107 -0
  315. package/dist/src/ui/tui/tui-editor-budget.js +155 -0
  316. package/dist/src/ui/tui/tui-editor-ctx.js +1 -0
  317. package/dist/src/ui/tui/tui-editor-entry.js +230 -0
  318. package/dist/src/ui/tui/tui-editor-interval.js +122 -0
  319. package/dist/src/ui/tui/tui-editor-notes.js +114 -0
  320. package/dist/src/ui/tui/tui-editor-pause.js +266 -0
  321. package/dist/src/ui/tui/tui-editor-prompt.js +95 -0
  322. package/dist/src/ui/tui/tui-editor-queued.js +98 -0
  323. package/dist/src/ui/tui/tui-editor-quiet.js +106 -0
  324. package/dist/src/ui/tui/tui-editor-retire.js +83 -0
  325. package/dist/src/ui/tui/tui-editor-search.js +73 -0
  326. package/dist/src/ui/tui/tui-editor-session.js +97 -0
  327. package/dist/src/ui/tui/tui-editors.js +152 -0
  328. package/dist/src/ui/tui/tui-frame.js +67 -55
  329. package/dist/src/ui/tui/tui-history.js +12 -7
  330. package/dist/src/ui/tui/tui-input.js +49 -44
  331. package/dist/src/ui/tui/tui-keymap.js +1 -0
  332. package/dist/src/ui/tui/tui-keys.js +91 -1704
  333. package/dist/src/ui/tui/tui-pane-activity.js +99 -0
  334. package/dist/src/ui/tui/tui-pane-backlog.js +224 -0
  335. package/dist/src/ui/tui/tui-pane-brief.js +30 -0
  336. package/dist/src/ui/tui/tui-pane-ctx.js +11 -0
  337. package/dist/src/ui/tui/tui-pane-health.js +124 -0
  338. package/dist/src/ui/tui/tui-pane-history.js +316 -0
  339. package/dist/src/ui/tui/tui-pane-pending.js +74 -0
  340. package/dist/src/ui/tui/tui-pane-queued.js +143 -0
  341. package/dist/src/ui/tui/tui-pane-report.js +122 -0
  342. package/dist/src/ui/tui/tui-pane-session.js +101 -0
  343. package/dist/src/ui/tui/tui-pane-transcript.js +150 -0
  344. package/dist/src/ui/tui/tui-pane.js +66 -28
  345. package/dist/src/ui/tui/tui-panes.js +294 -0
  346. package/dist/src/ui/tui/tui.js +45 -25
  347. package/dist/src/verdict/fix-claim.js +141 -38
  348. package/dist/src/verdict/no-change.js +19 -0
  349. package/dist/src/verdict/refusal.js +92 -8
  350. package/dist/src/verdict/reply-contract.js +17 -5
  351. package/dist/src/version.js +3 -2
  352. package/package.json +4 -2
  353. package/dist/src/agent/mcp/role-notes-server.js +0 -160
  354. package/dist/src/landing/backlog-conflicts.js +0 -116
  355. package/dist/src/landing/landing-drain.js +0 -204
  356. package/dist/src/landing/landing-pipeline.js +0 -96
  357. package/dist/src/landing/landing-queue-render.js +0 -31
  358. package/dist/src/landing/landing-slot.js +0 -246
  359. package/dist/src/landing/landing-vetting.js +0 -196
  360. package/dist/src/pi-extension/role-notes.js +0 -40
  361. package/dist/src/ui/fleet-alerts.js +0 -174
@@ -1,38 +1,58 @@
1
+ // vocabulary: plain
1
2
  /** The CLI layer of `tumwater prompt` — the steering-prompt command: submit a prompt to the
2
3
  * director (default) or one role's queue, `--list` what is queued with per-loop position
3
4
  * numbering, or `--cancel <n>` a queued prompt by position. Split out of
4
- * operator/operator-commands.ts because this command is not a fleet-control marker: its
5
+ * operator/operator-commands.ts because this command is not a team-control marker: its
5
6
  * sibling commands there ride the operator-intent marker protocol, while this command drives
6
7
  * the durable per-loop queues (src/inbox/inbox.ts) through the inbox-cancel, inbox-edit, and
7
8
  * operator-intent helpers, each with its own broken-config policy — so the command lives
8
- * beside the queue module it drives. The fleet-side half — the dequeue and requeue a loop
9
+ * beside the queue module it drives. The team-side half — the dequeue and requeue a loop
9
10
  * performs — is inbox.ts and pending-prompt.ts. */
10
11
  import { fail, say, sayJson } from "../cli/cli-output.js";
11
- import fs from "node:fs";
12
12
  import path from "node:path";
13
13
  import { parsePromptArgs } from "../cli/cli-command-args.js";
14
- import { cancelListedPrompt, cancelRolePrompt, } from "./inbox-cancel.js";
15
- import { editListedPrompt, editRolePrompt, } from "./inbox-edit.js";
16
- import { listWideScope, promptPreview, queuedListRows, renderQueuedList } from "./inbox.js";
14
+ import { cancelListedPrompt, cancelRolePrompt, resolveListedQueue, } from "./inbox-cancel.js";
15
+ import { editListedPrompt, editQueuedFile, editRolePrompt, readQueuedPromptBody, } from "./inbox-edit.js";
16
+ import { movePromptToTop } from "./inbox-top.js";
17
+ import { editInEditor } from "../cli/cli-editor.js";
18
+ import { listWideScope, queuedFileAtPosition, promptPreview, queuedListRows, renderQueuedList } from "./inbox.js";
17
19
  import { durationLabel } from "../cli/cli-args.js";
18
20
  import { knownRoleIds, knownRoleIdsCached, loadConfig } from "../config/config.js";
19
21
  import { errorMessage } from "../text/text.js";
20
- import { promptImageExtensionProblem, promptImageSizeProblem, promptImagesCountProblem, promptImagesProblem, } from "./inbox-attachments.js";
22
+ import { readPromptImageFiles } from "./inbox-attachments.js";
21
23
  import { DIRECTOR_ROLE, unknownRoleMessage } from "../roles/roles.js";
22
24
  import { submitRolePromptAndWake } from "../operator/operator-intent.js";
25
+ import { imagePromptGuidance } from "./inbox-submit.js";
26
+ import { roleLabel } from "../roles/role-labels.js";
27
+ import { canonicalRoleId } from "../roles/legacy-role-ids.js";
28
+ /** Fail with the ambiguity or miss message of a list-wide position resolution, else return
29
+ * the resolved value narrowed. Shared by sayListedOutcome and the editor form so the wordings
30
+ * cannot drift. */
31
+ function failQueueMiss(listed, position) {
32
+ if (isQueueMiss(listed)) {
33
+ if (listed.status === "ambiguous") {
34
+ fail(`position ${position} is in more than one role's inbox (${listed.roles.join(", ")}) — name one with --role <id>`);
35
+ }
36
+ // `queued` is the longest queue's length — the highest position any loop's numbering can
37
+ // reach — so a miss names it as the valid range when at least one loop holds a prompt.
38
+ const range = listed.queued > 0 ? ` — valid: 1-${listed.queued}` : "";
39
+ fail(`no prompt at position ${position} (longest inbox holds ${listed.queued})${range}`);
40
+ }
41
+ return listed;
42
+ }
43
+ /** Whether a list-wide outcome is one of the two miss shapes — the guard that narrows the
44
+ * miss fields (`roles`, `queued`) without a cast. */
45
+ function isQueueMiss(listed) {
46
+ return listed.status === "ambiguous" || listed.status === "missing";
47
+ }
23
48
  /** The shared tail of the list-wide cancel/edit branches: report an ambiguity (naming the
24
49
  * rival loops, with the `--role` escape hatch) or a miss (carrying the length of the longest
25
50
  * queue, the highest position numbering any loop can hold), else render the resolved outcome
26
51
  * through sayPromptOutcome. One helper so the
27
52
  * two verbs' handling of the miss shapes cannot drift. */
28
53
  function sayListedOutcome(listed, position) {
29
- if (listed.status === "ambiguous") {
30
- fail(`position ${position} is queued for more than one loop (${listed.roles.join(", ")}) — name one with --role <id>`);
31
- }
32
- if (listed.status === "missing") {
33
- fail(`no prompt at position ${position} (longest queue holds ${listed.queued})`);
34
- }
35
- sayPromptOutcome(position, listed.role, listed.outcome, true);
54
+ const found = failQueueMiss(listed, position);
55
+ sayPromptOutcome(position, found.role, found.outcome, true);
36
56
  }
37
57
  /** Run one loop-targeted queue verb (cancelRolePrompt/editRolePrompt) for a `--role`-scoped
38
58
  * command: a thrown failure (a broken config, an unwritable queue) exits through fail with
@@ -55,77 +75,56 @@ function runRolePromptCommand(root, role, position, verb) {
55
75
  * command. One helper so the cancel and edit wordings cannot drift. */
56
76
  function sayPromptOutcome(position, role, outcome, labelRole) {
57
77
  if (outcome.status === "gone") {
58
- say(`prompt ${position} is no longer queued — ${role} already took it`);
78
+ sayPromptGone(position, role);
59
79
  return;
60
80
  }
61
81
  // A resolved cancel carries the removed text; a resolved edit carries what it wrote.
62
82
  const text = outcome.status === "cancelled" ? outcome.text : outcome.newText;
63
83
  say(labelRole ? `${outcome.status} (${role}): ${promptPreview(text)}` : `${outcome.status}: ${promptPreview(text)}`);
64
84
  }
65
- /** Refuse an `--attach` path list from what the paths alone reveal, before any file is read:
66
- * the shared image-count cap first (so a glob that expanded past it is rejected without
67
- * reading or base64-encoding dozens of files), then each path's extension, regular-file
68
- * shape, and on-disk size. The regular-file check is not just a nicer message for a
69
- * directory named `shot.png` (whose stat size is small enough to pass the cap): it also keeps
70
- * `readPromptImages` from calling readFileSync on a FIFO or device node that would block the
71
- * command indefinitely. A path that cannot be statted is left for the read below to report
72
- * with its own missing-file wording. Returns the first problem, or null when every path
73
- * survives. */
74
- function attachPathsProblem(paths) {
75
- const countProblem = promptImagesCountProblem(paths.length);
76
- if (countProblem !== null)
77
- return countProblem;
78
- for (const p of paths) {
79
- const extProblem = promptImageExtensionProblem(p);
80
- if (extProblem !== null)
81
- return extProblem;
82
- try {
83
- const stat = fs.statSync(p);
84
- if (!stat.isFile())
85
- return `attached image ${JSON.stringify(p)} is not a regular file — pass a path to an image file`;
86
- const sizeProblem = promptImageSizeProblem(path.basename(p), stat.size);
87
- if (sizeProblem !== null)
88
- return sizeProblem;
89
- }
90
- catch {
91
- // Missing or unreadable: the read below names it.
92
- }
85
+ /** The gone-case tail shared by every resolved queue-verb reply: a concurrent dequeue is a
86
+ * normal race, not an error, so report it and exit clean, naming the loop that took the
87
+ * prompt. One helper so the cancel, edit and top verbs' gone wording cannot drift. */
88
+ function sayPromptGone(position, role) {
89
+ say(`prompt ${position} is no longer in the inbox — ${roleLabel(role)} already took it`);
90
+ }
91
+ /** The user-facing reply for one resolved top — the two branches of cmdPrompt's top mode
92
+ * (list-wide and `--role`-scoped): a concurrent dequeue is a normal race, not an error, so it
93
+ * is reported and exited clean; a resolved one previews the moved prompt. When `labelRole` the
94
+ * line names the loop — the no-`--role` command resolves across every loop, so its output must
95
+ * say where the prompt moved — while a `--role`-scoped command already names it in the user's
96
+ * own command. The `sayPromptOutcome` contract, so the verbs cannot drift. */
97
+ function sayTopOutcome(position, role, outcome, labelRole) {
98
+ if (outcome.status === "gone") {
99
+ sayPromptGone(position, role);
100
+ return;
93
101
  }
94
- return null;
102
+ say(labelRole ? `topped (${role}): ${outcome.preview}` : `topped: ${outcome.preview}`);
95
103
  }
96
- /** Read and validate an `--attach <path>` list into the GUI's image input shape: refuse the
97
- * cheap path-only rules first (attachPathsProblem above — count, extension, on-disk size),
98
- * read each surviving file whole, and re-run the endpoint's validation client-side
99
- * (promptImagesProblem) so the CLI's error names the broken rule with the same wording the
100
- * dashboard answers 400 with. Fails through `fail` with the same messages the enqueue branch
101
- * always raised, so the enqueue and `--edit` forms share one definition and cannot drift. */
104
+ /** Read and validate an `--attach <path>` list into the GUI's image input shape, through the
105
+ * shared throwing core both surfaces use (readPromptImageFiles in inbox-attachments.ts), with
106
+ * each failure exiting through `fail` with the same messages the enqueue branch always
107
+ * raised — so the enqueue and `--edit` forms share one definition and cannot drift from the
108
+ * TUI's prompt-line attach. */
102
109
  function readPromptImages(paths) {
103
- const pathProblem = attachPathsProblem(paths);
104
- if (pathProblem !== null)
105
- fail(pathProblem);
106
- const images = paths.map((p) => {
107
- let bytes;
108
- try {
109
- bytes = fs.readFileSync(p);
110
- }
111
- catch (err) {
112
- return fail(`cannot read attached image ${JSON.stringify(p)}: ${errorMessage(err)}`);
113
- }
114
- return { name: path.basename(p), dataBase64: bytes.toString("base64") };
115
- });
116
- const problem = promptImagesProblem(images);
117
- if (problem !== null)
118
- fail(problem);
119
- return images;
110
+ try {
111
+ return readPromptImageFiles(paths);
112
+ }
113
+ catch (err) {
114
+ return fail(errorMessage(err));
115
+ }
120
116
  }
121
- /** `tumwater prompt [--role <id>] <text|list|cancel <n>>`: submit a steering prompt to the
117
+ /** `tumwater prompt [--role <id>] <text|list|cancel <n>|top <n>>`: submit a steering prompt to the
122
118
  * director (default) or one role's queue, list what is queued with per-loop position
123
- * numbering, or cancel a queued prompt by position. The dispatcher in cli.ts gates on a ready
119
+ * numbering, cancel a queued prompt by position, or move one to the head of its
120
+ * queue. The dispatcher in cli.ts gates on a ready
124
121
  * repo and delegates here; list output is grouped by loop — the director first (its queue is
125
122
  * the shared one every pre-1/2 prompt landed in), then each role with queued prompts — so a
126
123
  * per-role queue's position numbering stays unambiguous. */
127
124
  export async function cmdPrompt(root, args) {
128
125
  const parsed = parsePromptArgs(args);
126
+ if (parsed.role !== null)
127
+ parsed.role = canonicalRoleId(parsed.role);
129
128
  // `--role <id>` scopes every mode to one loop's queue; the value is validated here, with
130
129
  // the same message every other --role consumer uses. The director is always valid: its
131
130
  // queue is the historical inbox (inbox.ts).
@@ -162,7 +161,7 @@ export async function cmdPrompt(root, args) {
162
161
  return;
163
162
  }
164
163
  if (prompts.length === 0) {
165
- say(role !== null ? `nothing queued for ${role}` : "nothing queued");
164
+ say(role !== null ? `${roleLabel(role)}'s inbox is empty` : "every inbox is empty");
166
165
  return;
167
166
  }
168
167
  say(renderQueuedList(prompts, Date.now()));
@@ -189,12 +188,96 @@ export async function cmdPrompt(root, args) {
189
188
  // `--attach` list is read through the same helper the enqueue form uses and appended to
190
189
  // the prompt's images in place.
191
190
  const images = parsed.attachPaths.length > 0 ? readPromptImages(parsed.attachPaths) : undefined;
191
+ // Image-input gate, `--edit`'s twin of the submit branch below (plans/image-input.md
192
+ // "Attachments", part 5/5): editing an attachment into a queued prompt is the same
193
+ // tick-time discovery the submit gate prevents, so the refusal promise holds across every
194
+ // CLI attach surface — an image no configured model can read is refused before a byte is
195
+ // written, and a loop that reroutes a blind read is told where the prompt will run. The
196
+ // target loop is resolved first (the same resolution the verb below makes) so the gate can
197
+ // name it.
198
+ if (images !== undefined && images.length > 0) {
199
+ const loop = role ?? failQueueMiss(resolveListedQueue(root, listWideScope(root), parsed.position), parsed.position).role;
200
+ const guidance = imagePromptGuidance(root, loop);
201
+ if (guidance.refusal)
202
+ return fail(guidance.refusal);
203
+ if (guidance.notice)
204
+ say(guidance.notice);
205
+ }
206
+ const typed = parsed.text;
207
+ if (typed === null) {
208
+ // Bare `--edit <n>`: open the prompt's body in the editor. The loop is resolved first
209
+ // and the write goes by file name, so a dequeue while the editor is open reports gone
210
+ // instead of editing the neighbour that shifted into the position.
211
+ let target = role;
212
+ if (target === null) {
213
+ const resolved = resolveListedQueue(root, listWideScope(root), parsed.position);
214
+ target = failQueueMiss(resolved, parsed.position).role;
215
+ }
216
+ const loop = target;
217
+ let name;
218
+ let body;
219
+ try {
220
+ name = path.basename(queuedFileAtPosition(root, loop, parsed.position));
221
+ body = readQueuedPromptBody(root, loop, name);
222
+ }
223
+ catch (err) {
224
+ return fail(errorMessage(err));
225
+ }
226
+ if (body === null) {
227
+ sayPromptOutcome(parsed.position, loop, { status: "gone" }, role === null);
228
+ return;
229
+ }
230
+ const after = editInEditor(body, "prompt.md");
231
+ // An emptied buffer is an abort, not a blank prompt: the queue would otherwise hold a
232
+ // prompt with no text, which the enqueue forms refuse.
233
+ if (after.trim() === "" && body.trim() !== "")
234
+ fail("the edited prompt is empty; nothing changed (use --cancel <n> to drop it)");
235
+ if (after.trim() === body.trim() && images === undefined) {
236
+ say(`prompt ${parsed.position} unchanged`);
237
+ return;
238
+ }
239
+ try {
240
+ sayPromptOutcome(parsed.position, loop, editQueuedFile(root, loop, name, after, images), role === null);
241
+ }
242
+ catch (err) {
243
+ fail(errorMessage(err));
244
+ }
245
+ return;
246
+ }
247
+ if (role === null) {
248
+ sayListedOutcome(editListedPrompt(root, listWideScope(root), parsed.position, typed, images), parsed.position);
249
+ return;
250
+ }
251
+ const target = role ?? DIRECTOR_ROLE;
252
+ sayPromptOutcome(parsed.position, target, runRolePromptCommand(root, target, parsed.position, (r, role, position) => editRolePrompt(r, role, position, typed, images)), false);
253
+ return;
254
+ }
255
+ if (parsed.mode === "top") {
256
+ // The same list-wide resolution the cancel branch uses, one verb on: no --role resolves the
257
+ // position across `--list`'s per-loop sections and moves it in the resolved loop's queue;
258
+ // with --role it is the role-scoped call. A concurrent dequeue reports gone and exits
259
+ // clean; position 1 of the resolved loop is "already first".
192
260
  if (role === null) {
193
- sayListedOutcome(editListedPrompt(root, listWideScope(root), parsed.position, parsed.text, images), parsed.position);
261
+ const loop = failQueueMiss(resolveListedQueue(root, listWideScope(root), parsed.position), parsed.position).role;
262
+ let outcome;
263
+ try {
264
+ outcome = movePromptToTop(root, loop, parsed.position);
265
+ }
266
+ catch (err) {
267
+ return fail(errorMessage(err));
268
+ }
269
+ sayTopOutcome(parsed.position, loop, outcome, true);
194
270
  return;
195
271
  }
196
272
  const target = role ?? DIRECTOR_ROLE;
197
- sayPromptOutcome(parsed.position, target, runRolePromptCommand(root, target, parsed.position, (r, role, position) => editRolePrompt(r, role, position, parsed.text, images)), false);
273
+ let outcome;
274
+ try {
275
+ outcome = movePromptToTop(root, target, parsed.position);
276
+ }
277
+ catch (err) {
278
+ return fail(errorMessage(err));
279
+ }
280
+ sayTopOutcome(parsed.position, target, outcome, false);
198
281
  return;
199
282
  }
200
283
  const target = role ?? DIRECTOR_ROLE;
@@ -202,21 +285,28 @@ export async function cmdPrompt(root, args) {
202
285
  // parsed duration, computed at submit so the deferral starts when the operator queued it.
203
286
  const dueMs = parsed.atDelayMs !== null ? Date.now() + parsed.atDelayMs : undefined;
204
287
  // `--attach <path>`: read and validate through the shared helper (a bad path fails by name
205
- // before anything is queued).
288
+ // before anything is queued). An attachment the target loop cannot read is refused here by
289
+ // name — the same gate submitRolePrompt enforces, surfaced early so the CLI exits non-zero
290
+ // with the fix before anything is written or queued; a loop that reroutes a blind read is
291
+ // not refused, but the operator is told where the prompt will actually run.
206
292
  const images = parsed.attachPaths.length > 0 ? readPromptImages(parsed.attachPaths) : undefined;
293
+ if (images && images.length > 0) {
294
+ const guidance = imagePromptGuidance(root, target);
295
+ if (guidance.refusal)
296
+ return fail(guidance.refusal);
297
+ if (guidance.notice)
298
+ say(guidance.notice);
299
+ }
207
300
  const wake = submitRolePromptAndWake(root, target, parsed.text, images, dueMs);
208
301
  const attached = images !== undefined ? ` with ${images.length} image(s)` : "";
302
+ const owner = target === DIRECTOR_ROLE ? "the director's" : `${roleLabel(target)}'s`;
209
303
  if (parsed.atDelayMs !== null) {
210
304
  // A deferred prompt's confirmation names the delivery delay, in the same duration
211
- // vocabulary --at parsed (durationLabel), so the operator can re-type it.
212
- const deferred = ` — delivers in ${durationLabel(parsed.atDelayMs)}`;
213
- say(role === null ? `queued for the director loop${deferred}${attached}` : `queued for the ${role} loop${deferred}${attached}`);
214
- }
215
- else if (role === null) {
216
- say(`queued for the director loop${attached}`);
305
+ // vocabulary --at parsed (durationLabel), so the user can re-type it.
306
+ say(`added to ${owner} inbox — delivers in ${durationLabel(parsed.atDelayMs)}${attached}`);
217
307
  }
218
308
  else {
219
- say(`queued for the ${role} loop${attached}`);
309
+ say(`added to ${owner} inbox${attached}`);
220
310
  }
221
311
  say(wake);
222
312
  }
@@ -1,20 +1,21 @@
1
+ // vocabulary: plain
1
2
  /** The bundled project templates for `tumwater init --template`
2
3
  * (docs/feature-project-templates.md). Static string data, no file reads and no runtime
3
4
  * dependencies: a template is a brief preamble (the operator's own words stay appended after
4
5
  * it) plus a seeded PLANS.md backlog and a starter directory layout — never code scaffolds,
5
- * since the fleet's own first ticks write any code.
6
+ * since the team's own first ticks write any code.
6
7
  * `blank` is the default and reproduces today's init output byte-identically. */
7
8
  import { typoSuffix } from "../text/suggest.js";
8
9
  const PYTHON_CLI_PREAMBLE = `This project is a Python command-line tool.
9
10
  The entry point is a \`main()\` function guarded by \`if __name__ == "__main__":\` in a single
10
11
  module at the project root; the CLI parses arguments with the standard library's argparse.
11
12
  Every behavior change ships with tests runnable by \`python -m pytest\` (or unittest, once a
12
- suite exists), and the fleet keeps a README documenting install and usage as the tool grows.
13
+ suite exists), and the team keeps a README documenting install and usage as the tool grows.
13
14
  Prefer the standard library over third-party dependencies.`;
14
15
  const NODE_CLI_PREAMBLE = `This project is a Node.js command-line tool.
15
16
  The entry point is \`bin.js\` (or \`src/main.js\`) wired to a \`bin\` field in package.json;
16
17
  argument parsing uses the standard library's util.parseArgs. Every behavior change ships with
17
- tests runnable by \`npm test\`, and the fleet keeps a README documenting install and usage as
18
+ tests runnable by \`npm test\`, and the team keeps a README documenting install and usage as
18
19
  the tool grows. Prefer node built-ins over third-party dependencies.`;
19
20
  const STATIC_SITE_PREAMBLE = `This project is a small static website: plain HTML, CSS, and
20
21
  minimal vanilla JavaScript served as-is, with no build step. Pages live at the project root
@@ -23,7 +24,7 @@ change keeps the site renderable by opening the files directly. Keep pages acces
23
24
  markup, alt text, and keyboard-navigable controls.`;
24
25
  const blank = {
25
26
  id: "blank",
26
- description: "no preamble, no seeded backlog — the fleet self-plans from your brief",
27
+ description: "no preamble, no seeded backlog — the team plans itself from your brief",
27
28
  briefPreamble: "",
28
29
  starterPlans: [],
29
30
  starterDirs: [],
@@ -94,8 +95,9 @@ export function templateCatalog() {
94
95
  * into PLANS_TEMPLATE under `## Planned`; the other three files land byte-identically. */
95
96
  export const PLANS_TEMPLATE = `# Plans
96
97
 
97
- Planned features, written by the plan loop and implemented by the feature loop.
98
- Each plan: goal, approach, files touched, acceptance criteria. Move finished plans to Done.
98
+ Planned features, written by the plan role and implemented by the feature role.
99
+ Each plan: goal, approach, files touched, acceptance criteria. Move finished plans to Done;
100
+ tumwater then archives them in PLANS-DONE.md, so this file holds only open work.
99
101
 
100
102
  ## Planned
101
103
 
@@ -107,11 +109,11 @@ _None yet._
107
109
  `;
108
110
  export const BUGS_TEMPLATE = `# Bugs
109
111
 
110
- Known bugs, recorded by any loop and fixed by the bugfix loop.
112
+ Known bugs, recorded by any role and fixed by the bugfix role.
111
113
  Each bug: symptom, how to reproduce, suspected cause if known. Move fixed bugs to Fixed, with the
112
114
  required \`**Validation gap:** <tag> — <one sentence>\` line recording what made the bug hard to
113
115
  confirm (tag one of: none, no-repro, no-fake, real-run-needed, no-observability, slow-check,
114
- unclear-invariant).
116
+ unclear-invariant); tumwater then archives them in BUGS-FIXED.md, so this file holds only open work.
115
117
 
116
118
  ## Open
117
119
 
@@ -123,9 +125,9 @@ _None yet._
123
125
  `;
124
126
  export const QUESTIONS_TEMPLATE = `# Questions
125
127
 
126
- Open questions loops have posted for a human decision — each with context, the options, and the
127
- loop's recommendation. Answer by moving an entry to ## Answered with your decision (or tell the
128
- director). Loops never block on their own questions; they check here at the start of each tick.
128
+ Open questions roles have posted for a human decision — each with context, the options, and the
129
+ role's recommendation. Answer by moving an entry to ## Answered with your decision (or tell the
130
+ director). Roles never block on their own questions; they check here at the start of each run.
129
131
 
130
132
  ## Open
131
133
 
@@ -138,7 +140,7 @@ _None yet._
138
140
  export const PRINCIPLES_TEMPLATE = `# Principles
139
141
 
140
142
  Design principles this project holds — the codified answer to "what would a senior engineer on
141
- this team always do." Every loop's prompt carries these; uphold them in everything you produce.
143
+ this team always do." Every role's instructions carry these; uphold them in everything you produce.
142
144
  Only the director and steward roles may edit this file. Phrase new principles positively: state
143
145
  what to do, not what to avoid. This list is a starting point: the director and steward own it
144
146
  and will tune it to this project.
@@ -146,7 +148,7 @@ and will tune it to this project.
146
148
  - Prefer the standard library over a new dependency.
147
149
  - Keep each file focused on one responsibility, and small enough to read in one sitting.
148
150
  - Every behavior change ships with a test.
149
- - Small, complete, and correct beats big and half-done: one focused change per tick. Focused
151
+ - Small, complete, and correct beats big and half-done: one focused change per run. Focused
150
152
  means one theme, not one site: a kind of fix applied everywhere it holds is one change, and
151
- the same fix landed one site per tick spends a review and a landing on each.
153
+ the same fix applied one site per run spends a review and a merge on each.
152
154
  `;
@@ -1,3 +1,4 @@
1
+ // vocabulary: plain
1
2
  import fs from "node:fs";
2
3
  import path from "node:path";
3
4
  import { saveConfig } from "../config/config.js";
@@ -5,7 +6,7 @@ import { seedConfig } from "../config/config-example.js";
5
6
  import { findOnPath, writeTextAtomic } from "../files/files.js";
6
7
  import { COMMIT_IDENT, GIT_MISSING_MESSAGE, git, gitTry } from "../git/git-run.js";
7
8
  import { hasCommits, isGitRepo } from "../git/git.js";
8
- import { INITIAL_PROMPT_MAX_CHARS, PROMPT_END, PROMPT_START, briefFile, briefTemplate, readInitialPrompt, readmeTemplate, } from "../brief.js";
9
+ import { INITIAL_PROMPT_MAX_CHARS, PROMPT_END, PROMPT_START, briefFile, briefTemplate, readInitialPrompt, readmeTemplate, } from "../brief/brief.js";
9
10
  import { CONFIG_BASENAME, STATE_DIR, configPath } from "../paths.js";
10
11
  import { projectName } from "../project-name.js";
11
12
  import { tooLongMessage } from "../text/text.js";
@@ -97,7 +98,7 @@ export async function initProject(root, initialPrompt, branch, opts = {}) {
97
98
  ? `${tpl.briefPreamble}\n\n${initialPrompt.trim()}`
98
99
  : initialPrompt.trim();
99
100
  if (combinedPrompt.length > INITIAL_PROMPT_MAX_CHARS) {
100
- throw new Error(tooLongMessage(`the initial prompt with the ${tpl.id} template's preamble`, combinedPrompt.length, INITIAL_PROMPT_MAX_CHARS, PREFILL_REASON));
101
+ throw new Error(tooLongMessage(`the brief with the ${tpl.id} template's preamble`, combinedPrompt.length, INITIAL_PROMPT_MAX_CHARS, PREFILL_REASON));
101
102
  }
102
103
  if (!findOnPath("git"))
103
104
  throw new Error(GIT_MISSING_MESSAGE);
@@ -117,17 +118,17 @@ export async function initProject(root, initialPrompt, branch, opts = {}) {
117
118
  const readmeHint = fs.existsSync(path.join(root, "README.md"))
118
119
  ? ` (a bare \`tumwater init\` reads the prompt from README.md between ${PROMPT_START} and ${PROMPT_END}, but README.md carries none)`
119
120
  : "";
120
- throw new Error(`an initial prompt is required: tumwater init <prompt | --file prompt.md>${readmeHint}`);
121
+ throw new Error(`a brief is required: tumwater init <prompt | --file prompt.md>${readmeHint}`);
121
122
  }
122
123
  // `--branch` seeds a NEW repository only: an existing repo's checked-out branch is the
123
- // fleet's business (`tumwater run --branch` or baseBranch chooses it at runtime), so a
124
+ // team's business (`tumwater start --branch` or baseBranch chooses it at runtime), so a
124
125
  // --branch given here would otherwise be silently ignored — the exact failure mode
125
126
  // rejectUnknownArgs exists to prevent. Fail before any side effect, dry-run included.
126
127
  // Whether `root` already is a repo is asked once here; nothing below until the `git init`
127
128
  // can change it.
128
129
  const wasRepo = await isGitRepo(root);
129
130
  if (branch !== undefined && wasRepo) {
130
- throw new Error(`--branch only seeds a new repository: this directory is already a git repository and init leaves its checked-out branch alone — drop --branch, or point the fleet at a branch with \`tumwater run --branch ${branch}\` (or baseBranch in tumwater.json)`);
131
+ throw new Error(`--branch only seeds a new repository: this directory is already a git repository and init leaves its checked-out branch alone — drop --branch, or point the team at a branch with \`tumwater start --branch ${branch}\` (or baseBranch in tumwater.json)`);
131
132
  }
132
133
  // The loops read the project's reason to exist back out of the project brief on every
133
134
  // tick (readInitialPrompt). A README.md without the managed section is the project's own
@@ -142,7 +143,7 @@ export async function initProject(root, initialPrompt, branch, opts = {}) {
142
143
  // untouched and the prompt silently dropped — every loop would then run blind. Fail before
143
144
  // creating anything so the user fixes it and re-runs.
144
145
  if (adopted && fs.existsSync(path.join(root, "TUMWATER.md"))) {
145
- throw new Error(`TUMWATER.md already exists without an initial prompt between the tumwater:prompt markers, so your prompt would be lost — add it to TUMWATER.md between ${PROMPT_START} and ${PROMPT_END} (or delete TUMWATER.md so init creates one), then re-run \`tumwater init\``);
146
+ throw new Error(`TUMWATER.md already exists without a brief between the tumwater:prompt markers, so your prompt would be lost — add it to TUMWATER.md between ${PROMPT_START} and ${PROMPT_END} (or delete TUMWATER.md so init creates one), then re-run \`tumwater init\``);
146
147
  }
147
148
  // The same loss one step later: a brief that already owns the managed sections is never
148
149
  // rewritten (init only creates what is absent), so a DIFFERENT prompt given here would be
@@ -151,7 +152,7 @@ export async function initProject(root, initialPrompt, branch, opts = {}) {
151
152
  // so pointing at README.md would send the user to edit a file nobody reads. A re-run with the
152
153
  // same prompt, or a bare init, is the idempotent re-seed and passes.
153
154
  if (prompt && brief !== null && combinedPrompt !== readInitialPrompt(root)) {
154
- throw new Error(`${brief} already carries a different initial prompt between the tumwater:prompt markers, and init never rewrites an existing project brief, so your prompt would be lost — to change the prompt, edit it in ${brief} between ${PROMPT_START} and ${PROMPT_END}; to re-seed with the current one, re-run a bare \`tumwater init\``);
155
+ throw new Error(`${brief} already carries a different brief between the tumwater:prompt markers, and init never rewrites an existing project brief, so your prompt would be lost — to change the prompt, edit it in ${brief} between ${PROMPT_START} and ${PROMPT_END}; to re-seed with the current one, re-run a bare \`tumwater init\``);
155
156
  }
156
157
  let repoInitialized = false;
157
158
  let createdBranch;
@@ -159,7 +160,7 @@ export async function initProject(root, initialPrompt, branch, opts = {}) {
159
160
  // Branch precedence: the caller's explicit --branch, else git's own init.defaultBranch
160
161
  // preference, else main. `git init -b` does not consult init.defaultBranch, so the
161
162
  // preference is read here: an operator who configured `init.defaultBranch=trunk`
162
- // expects `git init`-compatible behavior, and the fleet then targets that branch.
163
+ // expects `git init`-compatible behavior, and the team then targets that branch.
163
164
  const preferred = branch ?? ((await gitTry(root, "config", "--get", "init.defaultBranch")) || "main");
164
165
  // A branch name git rejects (a space, a leading dash, "@{…}" history syntax, …) would
165
166
  // abort `git init -b` AFTER it has created .git — a half-initialized directory whose
@@ -219,7 +220,7 @@ export async function initProject(root, initialPrompt, branch, opts = {}) {
219
220
  else
220
221
  write("README.md", readmeTemplate(name, combinedPrompt));
221
222
  // A template with starter plans seeds the backlog with them, rendered as `### ` entries —
222
- // the primary entry format every backlog reader parses — so the fleet's first ticks land on
223
+ // the primary entry format every backlog reader parses — so the team's first ticks land on
223
224
  // real work. blank keeps the `_None yet._` placeholder byte-identical to today.
224
225
  const plansContent = tpl.starterPlans.length > 0
225
226
  ? PLANS_TEMPLATE.replace("_None yet._", tpl.starterPlans.map((p) => `### ${p}`).join("\n\n"))
@@ -245,7 +246,7 @@ export async function initProject(root, initialPrompt, branch, opts = {}) {
245
246
  created.push(".gitignore");
246
247
  else if (fs.existsSync(path.join(root, ".gitignore")))
247
248
  leftAlone.push(".gitignore");
248
- // The template's starter directories: empty ones only, before the first tick — the fleet's
249
+ // The template's starter directories: empty ones only, before the first tick — the team's
249
250
  // own ticks write any code. Existing directories are left alone, like existing files above.
250
251
  for (const dir of tpl.starterDirs) {
251
252
  claim(dir, path.join(root, dir), () => fs.mkdirSync(path.join(root, dir), { recursive: true }));
@@ -263,7 +264,7 @@ export async function initProject(root, initialPrompt, branch, opts = {}) {
263
264
  const staged = await gitTry(root, "diff", "--cached", "--quiet");
264
265
  if (staged === null) {
265
266
  // Non-zero exit = something is staged.
266
- const message = withCommits ? "tumwater: init harness files" : "tumwater: init";
267
+ const message = withCommits ? "tumwater: init files" : "tumwater: init";
267
268
  await git(root, ...COMMIT_IDENT, "commit", "-m", message, "--", ...committable);
268
269
  committed = true;
269
270
  }