cloving 1.3.0 → 1.5.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 (484) hide show
  1. package/README.md +329 -215
  2. package/dist/src/agent-harness-shell.d.ts +2 -1
  3. package/dist/src/agent-harness-shell.js +8 -5
  4. package/dist/src/authority/command-wait-checks.d.ts +8 -0
  5. package/dist/src/authority/command-wait-checks.js +88 -0
  6. package/dist/src/authority/command-wait-results.d.ts +15 -0
  7. package/dist/src/authority/command-wait-results.js +73 -0
  8. package/dist/src/authority/command-wait-view.d.ts +21 -0
  9. package/dist/src/authority/command-wait-view.js +34 -0
  10. package/dist/src/authority/command-waits.d.ts +39 -0
  11. package/dist/src/authority/command-waits.js +97 -0
  12. package/dist/src/authority/commands.js +22 -1
  13. package/dist/src/authority/dispatch-decision.d.ts +8 -0
  14. package/dist/src/authority/dispatch-decision.js +26 -1
  15. package/dist/src/authority/effect-requests.js +6 -3
  16. package/dist/src/authority/effect-results.js +4 -2
  17. package/dist/src/authority/history-state.d.ts +2 -0
  18. package/dist/src/authority/history-state.js +13 -1
  19. package/dist/src/authority/instants.d.ts +1 -0
  20. package/dist/src/authority/instants.js +21 -0
  21. package/dist/src/authority/reduction-state.d.ts +2 -0
  22. package/dist/src/authority/reduction-state.js +5 -0
  23. package/dist/src/authority/reduction.js +24 -0
  24. package/dist/src/authority/status-input.d.ts +2 -0
  25. package/dist/src/authority/status.d.ts +2 -0
  26. package/dist/src/authority/status.js +1 -0
  27. package/dist/src/authority/stop-sequence.d.ts +1 -0
  28. package/dist/src/authority/stop-sequence.js +2 -1
  29. package/dist/src/authority/transitions.d.ts +6 -4
  30. package/dist/src/authority/transitions.js +7 -0
  31. package/dist/src/broker/actor/authority-health.d.ts +9 -0
  32. package/dist/src/broker/actor/authority-health.js +24 -0
  33. package/dist/src/broker/actor/authority-queries.d.ts +2 -1
  34. package/dist/src/broker/actor/authority-queries.js +14 -5
  35. package/dist/src/broker/actor/authority-receipts.js +1 -1
  36. package/dist/src/broker/actor/authority-runtime.d.ts +4 -2
  37. package/dist/src/broker/actor/authority-runtime.js +29 -20
  38. package/dist/src/broker/actor/authority-session.d.ts +6 -19
  39. package/dist/src/broker/actor/authority-session.js +66 -88
  40. package/dist/src/broker/actor/authority-settings.d.ts +2 -1
  41. package/dist/src/broker/actor/authority-settings.js +6 -3
  42. package/dist/src/broker/actor/authority-upgrades.d.ts +23 -2
  43. package/dist/src/broker/actor/authority-upgrades.js +8 -2
  44. package/dist/src/broker/actor/authority-watchdog.d.ts +6 -2
  45. package/dist/src/broker/actor/authority-watchdog.js +31 -9
  46. package/dist/src/broker/actor/authority.js +3 -1
  47. package/dist/src/broker/actor/delivery-intents.js +2 -0
  48. package/dist/src/broker/actor/effect-delivery.d.ts +1 -1
  49. package/dist/src/broker/actor/effect-delivery.js +15 -2
  50. package/dist/src/broker/actor/effect-plan.js +11 -2
  51. package/dist/src/broker/actor/effect-queries.d.ts +4 -0
  52. package/dist/src/broker/actor/effect-queries.js +9 -1
  53. package/dist/src/broker/actor/effect-turn.js +2 -0
  54. package/dist/src/broker/actor/projection-memory.d.ts +43 -0
  55. package/dist/src/broker/actor/projection-memory.js +207 -0
  56. package/dist/src/broker/actor/runtime-parts.d.ts +5 -0
  57. package/dist/src/broker/actor/runtime-parts.js +8 -3
  58. package/dist/src/broker/actor/stop-plan.js +9 -3
  59. package/dist/src/broker/actor/stop-turn.d.ts +2 -0
  60. package/dist/src/broker/actor/stop-turn.js +24 -11
  61. package/dist/src/broker/actor/turn-draft.d.ts +1 -0
  62. package/dist/src/broker/actor/turn-draft.js +4 -0
  63. package/dist/src/broker/actor/turn-state.js +5 -1
  64. package/dist/src/broker/authority-admission.js +60 -0
  65. package/dist/src/broker/authority-restore.d.ts +2 -0
  66. package/dist/src/broker/authority-restore.js +35 -1
  67. package/dist/src/broker/command-wait-restore.d.ts +31 -0
  68. package/dist/src/broker/command-wait-restore.js +86 -0
  69. package/dist/src/broker/decision-restore.d.ts +1 -1
  70. package/dist/src/broker/decision-restore.js +1 -0
  71. package/dist/src/broker/effect-upgrade.d.ts +3 -0
  72. package/dist/src/broker/effect-upgrade.js +11 -2
  73. package/dist/src/cli/arguments.d.ts +13 -0
  74. package/dist/src/cli/arguments.js +43 -3
  75. package/dist/src/cli/chat-stores.d.ts +2 -0
  76. package/dist/src/cli/chat-stores.js +113 -0
  77. package/dist/src/cli/client.d.ts +7 -2
  78. package/dist/src/cli/client.js +13 -7
  79. package/dist/src/cli/command-requests.d.ts +46 -0
  80. package/dist/src/cli/command-requests.js +84 -0
  81. package/dist/src/cli/command-waits.d.ts +9 -0
  82. package/dist/src/cli/command-waits.js +51 -0
  83. package/dist/src/cli/commands.js +70 -88
  84. package/dist/src/cli/config-file.d.ts +2 -2
  85. package/dist/src/cli/config-file.js +4 -2
  86. package/dist/src/cli/config-notice.js +5 -2
  87. package/dist/src/cli/doctor-retention.d.ts +2 -0
  88. package/dist/src/cli/doctor-retention.js +93 -0
  89. package/dist/src/cli/doctor-workflows.d.ts +5 -2
  90. package/dist/src/cli/doctor-workflows.js +12 -7
  91. package/dist/src/cli/doctor.js +11 -9
  92. package/dist/src/cli/harness-probes.d.ts +2 -0
  93. package/dist/src/cli/harness-probes.js +1 -1
  94. package/dist/src/cli/harness-processes.d.ts +6 -0
  95. package/dist/src/cli/harness-processes.js +36 -0
  96. package/dist/src/cli/init-command.d.ts +9 -0
  97. package/dist/src/cli/init-command.js +52 -0
  98. package/dist/src/cli/init-prompt-command.d.ts +10 -0
  99. package/dist/src/cli/init-prompt-command.js +145 -0
  100. package/dist/src/cli/init-prompt.d.ts +35 -0
  101. package/dist/src/cli/init-prompt.js +74 -0
  102. package/dist/src/cli/init-selection.d.ts +13 -0
  103. package/dist/src/cli/init-selection.js +75 -0
  104. package/dist/src/cli/main.d.ts +2 -0
  105. package/dist/src/cli/main.js +37 -7
  106. package/dist/src/cli/mcp-server.d.ts +1 -0
  107. package/dist/src/cli/mcp-server.js +156 -0
  108. package/dist/src/cli/mcp-tools.d.ts +15 -0
  109. package/dist/src/cli/mcp-tools.js +242 -0
  110. package/dist/src/cli/queue-arguments.d.ts +15 -0
  111. package/dist/src/cli/queue-arguments.js +31 -0
  112. package/dist/src/cli/queue-render.d.ts +3 -0
  113. package/dist/src/cli/queue-render.js +26 -0
  114. package/dist/src/cli/reload-render.d.ts +2 -0
  115. package/dist/src/cli/reload-render.js +12 -0
  116. package/dist/src/cli/render.d.ts +1 -1
  117. package/dist/src/cli/render.js +54 -38
  118. package/dist/src/cli/report-ci-jobs.d.ts +23 -0
  119. package/dist/src/cli/report-ci-jobs.js +103 -0
  120. package/dist/src/cli/report-ci-usage.d.ts +9 -0
  121. package/dist/src/cli/report-ci-usage.js +185 -0
  122. package/dist/src/cli/report-ci.d.ts +13 -0
  123. package/dist/src/cli/report-ci.js +74 -0
  124. package/dist/src/cli/report-closures.d.ts +6 -0
  125. package/dist/src/cli/report-closures.js +97 -0
  126. package/dist/src/cli/report-flow.d.ts +7 -0
  127. package/dist/src/cli/report-flow.js +122 -0
  128. package/dist/src/cli/report-github-reader.d.ts +29 -0
  129. package/dist/src/cli/report-github-reader.js +157 -0
  130. package/dist/src/cli/report-github.d.ts +15 -8
  131. package/dist/src/cli/report-github.js +131 -150
  132. package/dist/src/cli/report-operator.d.ts +3 -0
  133. package/dist/src/cli/report-operator.js +40 -0
  134. package/dist/src/cli/report-ready.d.ts +3 -0
  135. package/dist/src/cli/report-ready.js +96 -0
  136. package/dist/src/cli/report-text.d.ts +9 -0
  137. package/dist/src/cli/report-text.js +33 -0
  138. package/dist/src/cli/report.d.ts +1 -0
  139. package/dist/src/cli/report.js +104 -27
  140. package/dist/src/cli/runtime-arguments.d.ts +18 -3
  141. package/dist/src/cli/runtime-arguments.js +29 -20
  142. package/dist/src/cli/schedule-arguments.d.ts +14 -0
  143. package/dist/src/cli/schedule-arguments.js +26 -0
  144. package/dist/src/cli/schedule-render.d.ts +22 -0
  145. package/dist/src/cli/schedule-render.js +73 -0
  146. package/dist/src/cli/terminal-text.d.ts +3 -0
  147. package/dist/src/cli/terminal-text.js +28 -0
  148. package/dist/src/cli/toolchain.d.ts +1 -0
  149. package/dist/src/cli/toolchain.js +1 -0
  150. package/dist/src/cli/usage-render.d.ts +1 -0
  151. package/dist/src/cli/usage-render.js +28 -1
  152. package/dist/src/cli/usage.d.ts +2 -2
  153. package/dist/src/cli/usage.js +89 -11
  154. package/dist/src/cli/watch-actions.js +2 -2
  155. package/dist/src/cli/watch-api-document.d.ts +430 -0
  156. package/dist/src/cli/watch-api-document.js +291 -0
  157. package/dist/src/cli/watch-api-run.d.ts +56 -0
  158. package/dist/src/cli/watch-api-run.js +112 -0
  159. package/dist/src/cli/watch-api.d.ts +17 -0
  160. package/dist/src/cli/watch-api.js +230 -0
  161. package/dist/src/cli/watch-board-view.js +19 -2
  162. package/dist/src/cli/watch-board.d.ts +4 -2
  163. package/dist/src/cli/watch-board.js +53 -43
  164. package/dist/src/cli/watch-card-forms.d.ts +1 -1
  165. package/dist/src/cli/watch-card-forms.js +11 -10
  166. package/dist/src/cli/watch-client.d.ts +1 -1
  167. package/dist/src/cli/watch-client.js +2 -11
  168. package/dist/src/cli/watch-header-view.d.ts +1 -5
  169. package/dist/src/cli/watch-header-view.js +17 -21
  170. package/dist/src/cli/watch-model.d.ts +5 -1
  171. package/dist/src/cli/watch-model.js +20 -11
  172. package/dist/src/cli/watch-page.js +15 -15
  173. package/dist/src/cli/watch-phases.d.ts +12 -3
  174. package/dist/src/cli/watch-phases.js +68 -11
  175. package/dist/src/cli/watch-pr-board.js +1 -1
  176. package/dist/src/cli/watch-pr-command.d.ts +1 -0
  177. package/dist/src/cli/watch-pull-request.js +18 -8
  178. package/dist/src/cli/watch-reads.d.ts +3 -0
  179. package/dist/src/cli/watch-reads.js +22 -20
  180. package/dist/src/cli/watch-render.d.ts +15 -0
  181. package/dist/src/cli/watch-run-input.d.ts +0 -1
  182. package/dist/src/cli/watch-run-input.js +0 -13
  183. package/dist/src/cli/watch-server.js +24 -10
  184. package/dist/src/cli/watch-view.d.ts +0 -3
  185. package/dist/src/cli/watch-view.js +13 -13
  186. package/dist/src/cli.js +73 -12
  187. package/dist/src/command-wait.d.ts +4 -0
  188. package/dist/src/command-wait.js +45 -0
  189. package/dist/src/configuration-change.d.ts +7 -0
  190. package/dist/src/configuration-change.js +14 -0
  191. package/dist/src/configuration-display.js +2 -0
  192. package/dist/src/configuration-schema.d.ts +14 -0
  193. package/dist/src/configuration-schema.js +6 -2
  194. package/dist/src/daemon/attachment-file.d.ts +6 -0
  195. package/dist/src/daemon/attachment-file.js +86 -0
  196. package/dist/src/daemon/attachments.d.ts +14 -0
  197. package/dist/src/daemon/attachments.js +17 -0
  198. package/dist/src/daemon/claude-recovery.d.ts +50 -0
  199. package/dist/src/daemon/claude-recovery.js +208 -0
  200. package/dist/src/daemon/claude-stream.d.ts +27 -0
  201. package/dist/src/daemon/claude-stream.js +63 -0
  202. package/dist/src/daemon/claude-turn.d.ts +11 -25
  203. package/dist/src/daemon/claude-turn.js +64 -81
  204. package/dist/src/daemon/claude-worker.d.ts +5 -4
  205. package/dist/src/daemon/claude-worker.js +127 -126
  206. package/dist/src/daemon/daemon-adapters.d.ts +9 -0
  207. package/dist/src/daemon/daemon-adapters.js +33 -5
  208. package/dist/src/daemon/daemon-loops.d.ts +5 -1
  209. package/dist/src/daemon/daemon-loops.js +18 -10
  210. package/dist/src/daemon/daemon.js +80 -61
  211. package/dist/src/daemon/github-attach.d.ts +2 -0
  212. package/dist/src/daemon/github-attach.js +222 -0
  213. package/dist/src/daemon/github-calls.js +1 -1
  214. package/dist/src/daemon/github-ci-filters.d.ts +6 -0
  215. package/dist/src/daemon/github-ci-filters.js +138 -0
  216. package/dist/src/daemon/github-ci-workflows.d.ts +10 -0
  217. package/dist/src/daemon/github-ci-workflows.js +148 -0
  218. package/dist/src/daemon/github-ci.js +22 -4
  219. package/dist/src/daemon/github-issue-methods.d.ts +2 -0
  220. package/dist/src/daemon/github-issue-methods.js +37 -2
  221. package/dist/src/daemon/github-merge-call.d.ts +2 -1
  222. package/dist/src/daemon/github-merge-call.js +10 -3
  223. package/dist/src/daemon/github-merge.js +52 -24
  224. package/dist/src/daemon/github-methods.js +18 -4
  225. package/dist/src/daemon/github-operation.d.ts +4 -0
  226. package/dist/src/daemon/github-operation.js +6 -0
  227. package/dist/src/daemon/github-worker.d.ts +4 -0
  228. package/dist/src/daemon/github-worker.js +6 -0
  229. package/dist/src/daemon/issue-queue.d.ts +4 -1
  230. package/dist/src/daemon/issue-queue.js +76 -78
  231. package/dist/src/daemon/merge-turns.d.ts +10 -0
  232. package/dist/src/daemon/merge-turns.js +37 -0
  233. package/dist/src/daemon/notify-events.d.ts +6 -0
  234. package/dist/src/daemon/notify-events.js +84 -0
  235. package/dist/src/daemon/notify-journal.js +1 -1
  236. package/dist/src/daemon/notify-message.d.ts +4 -1
  237. package/dist/src/daemon/notify-message.js +36 -0
  238. package/dist/src/daemon/notify.d.ts +1 -1
  239. package/dist/src/daemon/notify.js +5 -77
  240. package/dist/src/daemon/operation-journal.d.ts +1 -0
  241. package/dist/src/daemon/queue-control.d.ts +13 -0
  242. package/dist/src/daemon/queue-control.js +86 -0
  243. package/dist/src/daemon/queue-journal.d.ts +20 -0
  244. package/dist/src/daemon/queue-journal.js +89 -0
  245. package/dist/src/daemon/queue-status.d.ts +9 -3
  246. package/dist/src/daemon/queue-status.js +17 -4
  247. package/dist/src/daemon/record-archive.d.ts +19 -0
  248. package/dist/src/daemon/record-archive.js +82 -0
  249. package/dist/src/daemon/record-sweep.d.ts +24 -0
  250. package/dist/src/daemon/record-sweep.js +147 -0
  251. package/dist/src/daemon/resume-index-lock.d.ts +13 -0
  252. package/dist/src/daemon/resume-index-lock.js +160 -0
  253. package/dist/src/daemon/resume-worktree.d.ts +7 -0
  254. package/dist/src/daemon/resume-worktree.js +29 -0
  255. package/dist/src/daemon/retrying-turn.d.ts +2 -12
  256. package/dist/src/daemon/retrying-turn.js +98 -172
  257. package/dist/src/daemon/schedule-control.d.ts +26 -0
  258. package/dist/src/daemon/schedule-control.js +110 -0
  259. package/dist/src/daemon/schedule-journal.d.ts +2 -15
  260. package/dist/src/daemon/schedule-journal.js +67 -4
  261. package/dist/src/daemon/settings-reload.d.ts +26 -0
  262. package/dist/src/daemon/settings-reload.js +82 -0
  263. package/dist/src/daemon/ticket-registration.d.ts +2 -7
  264. package/dist/src/daemon/ticket-registration.js +15 -81
  265. package/dist/src/daemon/turn-attempt-journal.d.ts +38 -0
  266. package/dist/src/daemon/turn-attempt-journal.js +136 -0
  267. package/dist/src/daemon/turn-attempts.d.ts +47 -0
  268. package/dist/src/daemon/turn-attempts.js +166 -0
  269. package/dist/src/daemon/turn-details.d.ts +4 -0
  270. package/dist/src/daemon/turn-details.js +33 -0
  271. package/dist/src/daemon/turn-report.d.ts +8 -1
  272. package/dist/src/daemon/turn-report.js +39 -0
  273. package/dist/src/daemon/work-tree.d.ts +9 -0
  274. package/dist/src/daemon/work-tree.js +20 -0
  275. package/dist/src/daemon/worker-profile.d.ts +9 -0
  276. package/dist/src/daemon/worker-profile.js +44 -0
  277. package/dist/src/daemon/workflow-generations.d.ts +7 -4
  278. package/dist/src/daemon/workflow-generations.js +22 -14
  279. package/dist/src/daemon/workflow-reload.d.ts +41 -0
  280. package/dist/src/daemon/workflow-reload.js +244 -0
  281. package/dist/src/daemon/workflow-replay.d.ts +2 -1
  282. package/dist/src/daemon/workflow-replay.js +7 -3
  283. package/dist/src/daemon/workflow-schedules.d.ts +3 -0
  284. package/dist/src/daemon/workflow-schedules.js +98 -45
  285. package/dist/src/daemon/worktree-processes.d.ts +9 -0
  286. package/dist/src/daemon/worktree-processes.js +151 -0
  287. package/dist/src/doctor-report.d.ts +4 -6
  288. package/dist/src/doctor-report.js +8 -13
  289. package/dist/src/harness-start.d.ts +15 -0
  290. package/dist/src/harness-start.js +37 -0
  291. package/dist/src/host/command-wait.d.ts +38 -0
  292. package/dist/src/host/command-wait.js +115 -0
  293. package/dist/src/host/start-delivery.d.ts +5 -7
  294. package/dist/src/host/start-delivery.js +3 -15
  295. package/dist/src/host/step-bridge-host.d.ts +2 -0
  296. package/dist/src/host/step-bridge-host.js +21 -0
  297. package/dist/src/operation-identity.d.ts +1 -0
  298. package/dist/src/operation-identity.js +4 -0
  299. package/dist/src/private-config.schema.json +71 -0
  300. package/dist/src/project-config.schema.json +40 -0
  301. package/dist/src/project-settings.d.ts +16 -2
  302. package/dist/src/project-settings.js +35 -1
  303. package/dist/src/queue-spend.d.ts +4 -0
  304. package/dist/src/queue-spend.js +21 -0
  305. package/dist/src/queue-state.d.ts +26 -0
  306. package/dist/src/queue-state.js +34 -0
  307. package/dist/src/record-retention.d.ts +46 -0
  308. package/dist/src/record-retention.js +81 -0
  309. package/dist/src/relay/relay-service.d.ts +3 -0
  310. package/dist/src/relay/relay-service.js +5 -3
  311. package/dist/src/report-ci.d.ts +164 -0
  312. package/dist/src/report-ci.js +32 -0
  313. package/dist/src/report-effects.d.ts +15 -0
  314. package/dist/src/report-effects.js +212 -0
  315. package/dist/src/report-flow-types.d.ts +38 -0
  316. package/dist/src/report-flow-types.js +7 -0
  317. package/dist/src/report-flow.d.ts +43 -0
  318. package/dist/src/report-flow.js +193 -0
  319. package/dist/src/report-operator.d.ts +67 -0
  320. package/dist/src/report-operator.js +25 -0
  321. package/dist/src/report-quality.d.ts +221 -0
  322. package/dist/src/report-quality.js +94 -0
  323. package/dist/src/report-ready.d.ts +23 -0
  324. package/dist/src/report-ready.js +117 -0
  325. package/dist/src/report-statistics.d.ts +2 -1
  326. package/dist/src/report-statistics.js +40 -4
  327. package/dist/src/report.d.ts +588 -47
  328. package/dist/src/report.js +156 -41
  329. package/dist/src/schedule-progress.d.ts +33 -0
  330. package/dist/src/schedule-progress.js +45 -0
  331. package/dist/src/service/active-tickets.js +11 -6
  332. package/dist/src/service/command-waits.d.ts +34 -0
  333. package/dist/src/service/command-waits.js +86 -0
  334. package/dist/src/service/dispatcher.d.ts +4 -0
  335. package/dist/src/service/dispatcher.js +33 -0
  336. package/dist/src/service/expiry.js +2 -0
  337. package/dist/src/service/gateway.js +3 -1
  338. package/dist/src/service/queue-handlers.d.ts +4 -0
  339. package/dist/src/service/queue-handlers.js +32 -0
  340. package/dist/src/service/queue-spend.d.ts +6 -0
  341. package/dist/src/service/queue-spend.js +68 -0
  342. package/dist/src/service/registry.d.ts +28 -2
  343. package/dist/src/service/registry.js +48 -0
  344. package/dist/src/service/replies.d.ts +22 -8
  345. package/dist/src/service/replies.js +16 -2
  346. package/dist/src/service/run-handlers.js +37 -12
  347. package/dist/src/service/run-metadata.d.ts +15 -1
  348. package/dist/src/service/run-metadata.js +93 -8
  349. package/dist/src/service/service.d.ts +11 -3
  350. package/dist/src/service/service.js +22 -37
  351. package/dist/src/service/submission-handler.js +18 -4
  352. package/dist/src/service/worker-port.js +9 -1
  353. package/dist/src/service/worker-totals.d.ts +6 -0
  354. package/dist/src/service/worker-totals.js +8 -1
  355. package/dist/src/service/workflow-reload.d.ts +35 -0
  356. package/dist/src/service/workflow-reload.js +52 -0
  357. package/dist/src/step-bridge.d.ts +20 -0
  358. package/dist/src/steps/command-wait.d.ts +12 -0
  359. package/dist/src/steps/command-wait.js +26 -0
  360. package/dist/src/steps/open-decision-wait.d.ts +2 -0
  361. package/dist/src/steps/{decision-gate.js → open-decision-wait.js} +0 -7
  362. package/dist/src/steps/review-by-person.js +4 -1
  363. package/dist/src/steps/wait-for-command.d.ts +3 -0
  364. package/dist/src/steps/wait-for-command.js +17 -0
  365. package/dist/src/steps/wait-for-decision.js +1 -1
  366. package/dist/src/storage/command-wait-rows.d.ts +30 -0
  367. package/dist/src/storage/command-wait-rows.js +96 -0
  368. package/dist/src/storage/decision-snapshots.d.ts +8 -2
  369. package/dist/src/storage/decision-snapshots.js +16 -8
  370. package/dist/src/storage/domain-changes.d.ts +3 -1
  371. package/dist/src/storage/domain-changes.js +4 -0
  372. package/dist/src/storage/domain-schema.d.ts +16 -1
  373. package/dist/src/storage/domain-schema.js +35 -4
  374. package/dist/src/storage/effect-schema.d.ts +2 -0
  375. package/dist/src/storage/effect-schema.js +9 -0
  376. package/dist/src/storage/projection-changes.d.ts +12 -0
  377. package/dist/src/storage/projection-changes.js +194 -0
  378. package/dist/src/storage/projection-lookups.d.ts +7 -0
  379. package/dist/src/storage/projection-lookups.js +30 -0
  380. package/dist/src/testing-requests.d.ts +15 -0
  381. package/dist/src/testing-requests.js +93 -0
  382. package/dist/src/testing.d.ts +4 -10
  383. package/dist/src/testing.js +33 -86
  384. package/dist/src/worker-usage.d.ts +1 -0
  385. package/dist/src/worker-usage.js +3 -0
  386. package/dist/src/workflow-candidate.d.ts +1 -0
  387. package/dist/src/workflow-candidate.js +12 -1
  388. package/dist/src/workflow-contracts.d.ts +1 -0
  389. package/dist/src/workflow-contracts.js +9 -2
  390. package/dist/src/workflow-declaration.d.ts +1 -0
  391. package/dist/src/workflow-declaration.js +3 -0
  392. package/dist/src/workflow-discovery.d.ts +2 -13
  393. package/dist/src/workflow-discovery.js +1 -13
  394. package/dist/src/workflow-refusals.js +0 -1
  395. package/dist/src/workflows.d.ts +1 -1
  396. package/dist/src/workflows.js +1 -1
  397. package/dist/tools/compile-workflows.js +3 -0
  398. package/dist/tools/project-configuration.d.ts +7 -2
  399. package/dist/tools/project-configuration.js +30 -1
  400. package/dist/tools/red-green-command.d.ts +1 -0
  401. package/dist/tools/red-green-command.js +174 -0
  402. package/dist/tools/red-green-process.d.ts +10 -0
  403. package/dist/tools/red-green-process.js +59 -0
  404. package/dist/tools/self-host-processes.d.ts +2 -0
  405. package/dist/tools/self-host-processes.js +69 -26
  406. package/dist/tools/self-host-project.d.ts +2 -0
  407. package/dist/tools/self-host-project.js +30 -77
  408. package/dist/tools/self-host-runtime.js +1 -1
  409. package/dist/tools/self-host.js +76 -34
  410. package/dist/tools/template-command.d.ts +1 -0
  411. package/dist/tools/template-command.js +160 -0
  412. package/dist/tools/template-copy.d.ts +11 -0
  413. package/dist/tools/template-copy.js +130 -0
  414. package/dist/tools/template-files.d.ts +17 -0
  415. package/dist/tools/template-files.js +74 -0
  416. package/dist/tools/workflow-bundles.js +1 -0
  417. package/dist/tools/workflow-candidate-counts.js +2 -2
  418. package/dist/tools/workflow-candidate-program.d.ts +4 -2
  419. package/dist/tools/workflow-candidate-program.js +30 -11
  420. package/dist/tools/workflow-candidate-shape.d.ts +3 -0
  421. package/dist/tools/workflow-candidate-shape.js +90 -0
  422. package/dist/tools/workflow-candidate-source.d.ts +1 -0
  423. package/dist/tools/workflow-candidate-source.js +26 -4
  424. package/dist/tools/workflow-candidate-syntax.d.ts +2 -2
  425. package/dist/tools/workflow-candidate-syntax.js +24 -94
  426. package/dist/tools/workflow-command.js +1 -2
  427. package/dist/tools/workflow-folders.d.ts +2 -3
  428. package/dist/tools/workflow-folders.js +1 -1
  429. package/package.json +17 -9
  430. package/templates/develop/README.md +93 -4
  431. package/templates/develop/develop/flow-tests.ts +38 -1
  432. package/templates/develop/develop/interruption-tests.ts +157 -0
  433. package/templates/develop/develop/local.ts +1 -0
  434. package/templates/develop/develop/option-tests.ts +138 -9
  435. package/templates/develop/develop/pull.ts +5 -1
  436. package/templates/develop/develop/red-green.ts +55 -0
  437. package/templates/develop/develop/refusal-tests.ts +77 -0
  438. package/templates/develop/develop/report.ts +31 -11
  439. package/templates/develop/develop/round.ts +1 -0
  440. package/templates/develop/develop/scenarios.ts +27 -0
  441. package/templates/develop/develop/turn.ts +28 -7
  442. package/templates/develop/develop.declaration.json +1 -1
  443. package/templates/develop/develop.test.ts +4 -0
  444. package/templates/develop/template.json +29 -2
  445. package/templates/idea-to-issue/README.md +27 -0
  446. package/templates/idea-to-issue/idea-to-issue/effects.ts +52 -0
  447. package/templates/idea-to-issue/idea-to-issue/leave.ts +10 -0
  448. package/templates/idea-to-issue/idea-to-issue/next.ts +29 -0
  449. package/templates/idea-to-issue/idea-to-issue/post-person.ts +18 -0
  450. package/templates/idea-to-issue/idea-to-issue/post.ts +50 -0
  451. package/templates/idea-to-issue/idea-to-issue/queue.ts +27 -0
  452. package/templates/idea-to-issue/idea-to-issue/review.ts +42 -0
  453. package/templates/idea-to-issue/idea-to-issue/scenarios.ts +50 -0
  454. package/templates/idea-to-issue/idea-to-issue/turn.ts +40 -0
  455. package/templates/idea-to-issue/idea-to-issue.declaration.json +16 -0
  456. package/templates/idea-to-issue/idea-to-issue.test.ts +79 -0
  457. package/templates/idea-to-issue/idea-to-issue.ts +49 -0
  458. package/templates/idea-to-issue/template.json +81 -0
  459. package/templates/review-day/README.md +97 -0
  460. package/templates/review-day/review-day/critique.ts +58 -0
  461. package/templates/review-day/review-day/decision.ts +29 -0
  462. package/templates/review-day/review-day/facts.ts +69 -0
  463. package/templates/review-day/review-day/label.ts +29 -0
  464. package/templates/review-day/review-day/next.ts +28 -0
  465. package/templates/review-day/review-day/post.ts +27 -0
  466. package/templates/review-day/review-day/report.ts +98 -0
  467. package/templates/review-day/review-day/rules.ts +12 -0
  468. package/templates/review-day/review-day/scenarios.ts +76 -0
  469. package/templates/review-day/review-day/turn.ts +73 -0
  470. package/templates/review-day/review-day.declaration.json +16 -0
  471. package/templates/review-day/review-day.test.ts +362 -0
  472. package/templates/review-day/review-day.ts +37 -0
  473. package/templates/review-day/template.json +56 -0
  474. package/dist/src/cli/watch-start-view.d.ts +0 -18
  475. package/dist/src/cli/watch-start-view.js +0 -77
  476. package/dist/src/steps/decision-gate.d.ts +0 -3
  477. package/dist/workflows/develop-prd.d.ts +0 -7
  478. package/dist/workflows/develop-prd.js +0 -165
  479. package/dist/workflows/develop-ticket.d.ts +0 -9
  480. package/dist/workflows/develop-ticket.js +0 -207
  481. package/dist/workflows/issue-labels.d.ts +0 -16
  482. package/dist/workflows/issue-labels.js +0 -70
  483. package/dist/workflows/ticket-prompts.d.ts +0 -35
  484. package/dist/workflows/ticket-prompts.js +0 -147
@@ -1,10 +1,12 @@
1
1
  import { join } from "node:path";
2
2
  import { setTimeout as delay } from "node:timers/promises";
3
3
  import { mutate } from "../cli/client.js";
4
- import { record } from "../protocol.js";
4
+ import { record, } from "../protocol.js";
5
5
  import { runIdentity } from "../service/submission-handler.js";
6
6
  import { configuredSchedules } from "../workflow-schedule.js";
7
7
  import { declaredInput } from "./declared-input.js";
8
+ import { finishedScheduleRun } from "../schedule-progress.js";
9
+ import { scheduleControl } from "./schedule-control.js";
8
10
  import { scheduleJournal, } from "./schedule-journal.js";
9
11
  export function scheduledDeclarations(settings, limits) {
10
12
  try {
@@ -60,6 +62,8 @@ async function pending(settings, entry) {
60
62
  const { pending: planned, ...rest } = progress;
61
63
  if (planned === undefined)
62
64
  return progress;
65
+ if (progress.state !== "active")
66
+ throw new Error("invalid-schedule-state");
63
67
  const identity = commandId(workflowId, progress.lastSlot);
64
68
  if (planned.kind === "skip") {
65
69
  const receipt = await settings.submit({
@@ -86,10 +90,7 @@ async function pending(settings, entry) {
86
90
  throw new Error(reply.error.code);
87
91
  return { ...rest, activeCommandId: identity };
88
92
  }
89
- async function active(settings, progress) {
90
- if (progress.activeCommandId === undefined)
91
- return undefined;
92
- const runId = runIdentity(progress.activeCommandId);
93
+ async function scheduledRunStatus(settings, runId) {
93
94
  const reply = await request(settings, {
94
95
  operation: "status",
95
96
  parameters: { runId },
@@ -99,45 +100,76 @@ async function active(settings, progress) {
99
100
  const run = record(reply.result["run"]);
100
101
  if (typeof run?.["state"] !== "string")
101
102
  throw new Error("invalid-schedule-run");
103
+ return run;
104
+ }
105
+ async function active(settings, progress) {
106
+ if (progress.activeCommandId === undefined)
107
+ return progress;
108
+ const now = (settings.scheduleClock ?? Date.now)();
109
+ const observedAt = settings.observationClock();
110
+ const next = settings.nextObservations.get(progress.activeCommandId) ?? 0;
111
+ const due = progress.state === "active" && now >= progress.nextAt;
112
+ if (!due && observedAt < next)
113
+ return progress;
114
+ const runId = runIdentity(progress.activeCommandId);
115
+ const run = await scheduledRunStatus(settings, runId);
116
+ settings.nextObservations.set(progress.activeCommandId, observedAt + 5000);
102
117
  if (run["condition"] === "cancelling")
103
- return runId;
104
- return ["completed", "failed", "cancelled"].includes(run["state"])
118
+ return progress;
119
+ if (!["completed", "failed", "cancelled"].includes(String(run["state"])))
120
+ return progress;
121
+ settings.nextObservations.delete(progress.activeCommandId);
122
+ return finishedScheduleRun(progress, {
123
+ runId,
124
+ status: run,
125
+ now: (settings.scheduleClock ?? Date.now)(),
126
+ });
127
+ }
128
+ async function advance(settings, declaration) {
129
+ settings.signal.throwIfAborted();
130
+ const { workflowId, schedule } = declaration;
131
+ if (schedule === undefined)
132
+ return;
133
+ let progress = settings.journal.entries.get(workflowId);
134
+ if (progress === undefined)
135
+ throw new Error("missing-schedule-state");
136
+ if (progress.pending !== undefined) {
137
+ progress = await pending(settings, { workflowId, progress });
138
+ settings.journal.entries.set(workflowId, progress);
139
+ await settings.journal.save();
140
+ if (settings.now < progress.nextAt)
141
+ return;
142
+ }
143
+ const observed = await active(settings, progress);
144
+ if (observed !== progress) {
145
+ progress = observed;
146
+ settings.journal.entries.set(workflowId, progress);
147
+ await settings.journal.save();
148
+ }
149
+ if (progress.state !== "active" || settings.now < progress.nextAt)
150
+ return;
151
+ const runId = progress.activeCommandId === undefined
105
152
  ? undefined
106
- : runId;
153
+ : runIdentity(progress.activeCommandId);
154
+ const planned = runId === undefined
155
+ ? { kind: "run", input: schedule.input }
156
+ : { kind: "skip", runId };
157
+ const elapsed = Math.floor((settings.now - progress.nextAt) / (schedule.everySeconds * 1000)) + 1;
158
+ progress = {
159
+ ...progress,
160
+ lastSlot: progress.lastSlot + elapsed,
161
+ nextAt: progress.nextAt + elapsed * schedule.everySeconds * 1000,
162
+ pending: planned,
163
+ };
164
+ settings.journal.entries.set(workflowId, progress);
165
+ await settings.journal.save();
166
+ settings.journal.entries.set(workflowId, await pending(settings, { workflowId, progress }));
167
+ await settings.journal.save();
107
168
  }
108
169
  async function tick(settings, journal) {
109
170
  const now = (settings.scheduleClock ?? Date.now)();
110
- for (const declaration of settings.declarations ?? []) {
111
- settings.signal.throwIfAborted();
112
- const { workflowId, schedule } = declaration;
113
- if (schedule === undefined)
114
- continue;
115
- let progress = journal.entries.get(workflowId);
116
- if (progress === undefined)
117
- throw new Error("missing-schedule-state");
118
- if (progress.pending !== undefined) {
119
- progress = await pending(settings, { workflowId, progress });
120
- journal.entries.set(workflowId, progress);
121
- await journal.save();
122
- }
123
- if (now < progress.nextAt)
124
- continue;
125
- const runId = await active(settings, progress);
126
- const planned = runId === undefined
127
- ? { kind: "run", input: schedule.input }
128
- : { kind: "skip", runId };
129
- const elapsed = Math.floor((now - progress.nextAt) / (schedule.everySeconds * 1000)) + 1;
130
- progress = {
131
- ...progress,
132
- lastSlot: progress.lastSlot + elapsed,
133
- nextAt: progress.nextAt + elapsed * schedule.everySeconds * 1000,
134
- pending: planned,
135
- };
136
- journal.entries.set(workflowId, progress);
137
- await journal.save();
138
- journal.entries.set(workflowId, await pending(settings, { workflowId, progress }));
139
- await journal.save();
140
- }
171
+ for (const declaration of settings.declarations ?? [])
172
+ await advance({ ...settings, journal, now }, declaration);
141
173
  }
142
174
  async function initialized(settings) {
143
175
  const journal = await scheduleJournal(join(settings.directory, "state"));
@@ -150,36 +182,57 @@ async function initialized(settings) {
150
182
  if (prior?.everySeconds === schedule.everySeconds)
151
183
  continue;
152
184
  journal.entries.set(workflowId, {
153
- ...prior,
185
+ ...(prior ?? {
186
+ lastSlot: 0,
187
+ state: "active",
188
+ failuresInRow: 0,
189
+ failedRunIds: [],
190
+ }),
154
191
  everySeconds: schedule.everySeconds,
155
- lastSlot: prior?.lastSlot ?? 0,
156
192
  nextAt: now + schedule.everySeconds * 1000,
157
193
  });
158
194
  }
159
- await journal.save();
195
+ if (settings.declarations?.some((entry) => entry.schedule !== undefined))
196
+ await journal.save();
160
197
  return journal;
161
198
  }
162
199
  export async function workflowSchedules(settings) {
163
- if (!(settings.declarations ?? []).some((entry) => entry.schedule !== undefined))
164
- return { stop: () => Promise.resolve() };
200
+ const declarations = settings.declarations ?? [];
165
201
  const journal = await initialized(settings);
166
202
  const stopping = new AbortController();
167
203
  const stopped = () => stopping.signal.aborted;
204
+ const context = {
205
+ ...settings,
206
+ signal: stopping.signal,
207
+ nextObservations: new Map(),
208
+ observationClock: settings.scheduleObservationClock ?? (() => performance.now()),
209
+ };
210
+ const control = scheduleControl({
211
+ journal,
212
+ declarations,
213
+ clock: settings.scheduleClock ?? Date.now,
214
+ signal: stopping.signal,
215
+ recoverPending: (entry) => pending(context, entry),
216
+ tick: () => tick(context, journal),
217
+ });
168
218
  const running = (async () => {
169
219
  while (!stopped()) {
170
220
  await delay(250, undefined, { signal: stopping.signal }).catch(() => undefined);
171
221
  if (stopped())
172
222
  return;
173
- await tick({ ...settings, signal: stopping.signal }, journal);
223
+ await control.tick();
174
224
  }
175
225
  })().catch((error) => {
176
226
  if (!stopped())
177
227
  process.stderr.write(`Schedules stopped: ${String(error)}\n`);
228
+ stopping.abort();
178
229
  });
179
230
  return {
231
+ request: control.request,
180
232
  stop: async () => {
181
233
  stopping.abort();
182
234
  await running;
235
+ await control.drain();
183
236
  },
184
237
  };
185
238
  }
@@ -0,0 +1,9 @@
1
+ export declare function recordedProcessDirectory(processId: number): Promise<string | undefined>;
2
+ interface StopRequest {
3
+ readonly directory: string;
4
+ readonly folder: string;
5
+ readonly signal: AbortSignal;
6
+ readonly stopMilliseconds?: number;
7
+ }
8
+ export declare function stopTreeProcesses(request: StopRequest): Promise<boolean>;
9
+ export {};
@@ -0,0 +1,151 @@
1
+ import { execFile } from "node:child_process";
2
+ import { readdir, readlink, realpath } from "node:fs/promises";
3
+ import { resolve, sep } from "node:path";
4
+ import { setTimeout as delay } from "node:timers/promises";
5
+ import { promisify } from "node:util";
6
+ import { writeRecord } from "./operation-journal.js";
7
+ const execute = promisify(execFile);
8
+ async function output(command, arguments_) {
9
+ return (await execute(command, arguments_, {
10
+ encoding: "utf8",
11
+ timeout: 5000,
12
+ maxBuffer: 8 * 1024 * 1024,
13
+ })).stdout;
14
+ }
15
+ async function directories() {
16
+ if (process.platform === "linux") {
17
+ const entries = await readdir("/proc");
18
+ const links = await Promise.all(entries
19
+ .filter((entry) => /^\d+$/.test(entry))
20
+ .map(async (entry) => {
21
+ const directory = await readlink(`/proc/${entry}/cwd`).catch(() => undefined);
22
+ return [Number(entry), directory];
23
+ }));
24
+ return new Map(links.filter((entry) => entry[1] !== undefined));
25
+ }
26
+ const text = await output("lsof", ["-d", "cwd", "-Fpn", "-n", "-P"]);
27
+ const found = new Map();
28
+ let processId = 0;
29
+ for (const line of text.split("\n")) {
30
+ if (line.startsWith("p"))
31
+ processId = Number(line.slice(1));
32
+ if (line.startsWith("n"))
33
+ found.set(processId, line.slice(1));
34
+ }
35
+ return found;
36
+ }
37
+ async function processes() {
38
+ try {
39
+ const text = await output("ps", ["-axo", "pid=,pgid=,uid="]);
40
+ const entries = text
41
+ .trim()
42
+ .split("\n")
43
+ .map((line) => {
44
+ const [processId = 0, group = 0, user = -1] = line
45
+ .trim()
46
+ .split(/\s+/)
47
+ .map(Number);
48
+ return { processId, group, user };
49
+ });
50
+ const working = await directories();
51
+ return entries.map((entry) => {
52
+ const directory = working.get(entry.processId);
53
+ return { ...entry, ...(directory === undefined ? {} : { directory }) };
54
+ });
55
+ }
56
+ catch {
57
+ return undefined;
58
+ }
59
+ }
60
+ function inTree(entry, directory) {
61
+ if (entry.processId <= 1 ||
62
+ entry.processId === process.pid ||
63
+ entry.directory === undefined)
64
+ return false;
65
+ const root = resolve(directory);
66
+ return entry.directory === root || entry.directory.startsWith(root + sep);
67
+ }
68
+ export async function recordedProcessDirectory(processId) {
69
+ return (await processes())?.find((entry) => entry.processId === processId)
70
+ ?.directory;
71
+ }
72
+ function terminate(entries, request) {
73
+ const { directory, signal } = request;
74
+ const maySignal = (entry) => entry.user === process.getuid?.() && inTree(entry, directory);
75
+ const found = entries.filter(maySignal);
76
+ const groups = new Set();
77
+ for (const entry of found) {
78
+ const safeGroup = entry.group > 1 &&
79
+ entries.filter((other) => other.group === entry.group).every(maySignal);
80
+ const target = safeGroup ? -entry.group : entry.processId;
81
+ if (groups.has(target))
82
+ continue;
83
+ groups.add(target);
84
+ try {
85
+ process.kill(target, signal);
86
+ }
87
+ catch {
88
+ continue;
89
+ }
90
+ }
91
+ return found.map((entry) => entry.processId);
92
+ }
93
+ async function treeSnapshot(directory) {
94
+ const entries = await processes();
95
+ return entries?.some((entry) => inTree(entry, directory)) === false
96
+ ? processes()
97
+ : entries;
98
+ }
99
+ async function waitForTree(request, deadline) {
100
+ while (!request.signal.aborted) {
101
+ const entries = await treeSnapshot(request.directory);
102
+ if (entries === undefined ||
103
+ !entries.some((entry) => inTree(entry, request.directory)) ||
104
+ performance.now() >= deadline)
105
+ return entries;
106
+ await delay(Math.min(250, Math.max(0, deadline - performance.now())));
107
+ }
108
+ return undefined;
109
+ }
110
+ export async function stopTreeProcesses(request) {
111
+ const directory = await realpath(request.directory).catch(() => undefined);
112
+ return directory === undefined
113
+ ? false
114
+ : stopCanonicalTree({ ...request, directory });
115
+ }
116
+ async function stopCanonicalTree(request) {
117
+ const entries = await treeSnapshot(request.directory);
118
+ const aborted = () => request.signal.aborted;
119
+ if (entries === undefined || aborted())
120
+ return false;
121
+ const processIds = terminate(entries, {
122
+ directory: request.directory,
123
+ signal: "SIGTERM",
124
+ });
125
+ let remaining = processIds.length === 0
126
+ ? entries
127
+ : await waitForTree(request, performance.now() + (request.stopMilliseconds ?? 15_000));
128
+ if (remaining === undefined || aborted())
129
+ return recordCleanup(request, { processIds, remaining });
130
+ const killed = terminate(remaining, {
131
+ directory: request.directory,
132
+ signal: "SIGKILL",
133
+ });
134
+ processIds.push(...killed);
135
+ if (killed.length > 0)
136
+ remaining = await waitForTree(request, performance.now() + 1000);
137
+ return recordCleanup(request, { processIds, remaining });
138
+ }
139
+ async function recordCleanup(request, cleanup) {
140
+ const remainingProcessIds = cleanup.remaining
141
+ ?.filter((entry) => inTree(entry, request.directory))
142
+ .map((entry) => entry.processId);
143
+ await writeRecord(request.folder, {
144
+ name: "resume-processes.json",
145
+ value: {
146
+ processIds: [...new Set(cleanup.processIds)],
147
+ remainingProcessIds,
148
+ },
149
+ });
150
+ return !request.signal.aborted && remainingProcessIds?.length === 0;
151
+ }
@@ -1,8 +1,8 @@
1
1
  import { type SubmissionContract } from "./submission.ts";
2
- export declare const doctorCodes: readonly ["git-repository", "node-version", "harnesses", "default-harness", "gh-login", "github-origin", "greptile-review", "settings-files", "runtime-directory", "service", "ticket-command", "next-command", "check-command", "queue-interval", "private-texts"];
2
+ export declare const doctorCodes: readonly ["git-repository", "node-version", "harnesses", "default-harness", "gh-login", "github-origin", "greptile-review", "settings-files", "no-workflow", "runtime-directory", "service", "ticket-command", "next-command", "check-command", "queue-interval", "private-texts", "retention"];
3
3
  export type DoctorCode = (typeof doctorCodes)[number];
4
4
  export interface DoctorObservation {
5
- readonly result: "ok" | "fail" | "skip";
5
+ readonly result: "ok" | "warn" | "fail" | "skip";
6
6
  readonly detail: string;
7
7
  }
8
8
  export interface DoctorCheck extends DoctorObservation {
@@ -14,11 +14,9 @@ export declare const doctorFixes: Readonly<Record<DoctorCode, string>>;
14
14
  export declare function doctorExitCode(checks: readonly DoctorCheck[]): number;
15
15
  export interface DoctorWorkflow {
16
16
  readonly queue?: boolean;
17
- readonly scope: "bundled" | "project" | "user";
17
+ readonly scope: "project" | "user";
18
18
  readonly workflowId: string;
19
19
  readonly capabilities: readonly string[];
20
20
  readonly input?: SubmissionContract;
21
21
  }
22
- export declare function requiredDoctorCodes(workflows: readonly DoctorWorkflow[], options: {
23
- readonly queueSeconds: string | undefined;
24
- }): readonly DoctorCode[];
22
+ export declare function requiredDoctorCodes(workflows: readonly DoctorWorkflow[]): readonly DoctorCode[];
@@ -8,6 +8,7 @@ export const doctorCodes = [
8
8
  "github-origin",
9
9
  "greptile-review",
10
10
  "settings-files",
11
+ "no-workflow",
11
12
  "runtime-directory",
12
13
  "service",
13
14
  "ticket-command",
@@ -15,6 +16,7 @@ export const doctorCodes = [
15
16
  "check-command",
16
17
  "queue-interval",
17
18
  "private-texts",
19
+ "retention",
18
20
  ];
19
21
  export const doctorFixes = {
20
22
  "git-repository": "Change to the repository, or run: git init",
@@ -25,6 +27,7 @@ export const doctorFixes = {
25
27
  "github-origin": "Set a GitHub origin: git remote add origin git@github.com:OWNER/REPO.git || git remote set-url origin git@github.com:OWNER/REPO.git",
26
28
  "greptile-review": "Install the Greptile app at https://github.com/apps/greptile, then open a first pull request: gh pr create",
27
29
  "settings-files": "Fix the project JSON: ${EDITOR:-vi} .cloving/config.json; for private JSON, edit <config>/cloving/projects/<repository-id>.json",
30
+ "no-workflow": "cloving init",
28
31
  "runtime-directory": 'Select a private writable runtime: CLOVING_RUNTIME_DIR="$HOME/.local/state/cloving/doctor/run" cloving doctor',
29
32
  service: "Start the service: cloving start",
30
33
  "ticket-command": "Set tickets.command to an executable list: ${EDITOR:-vi} .cloving/config.json",
@@ -32,39 +35,31 @@ export const doctorFixes = {
32
35
  "check-command": "Set checks.beforeMerge to an executable list: ${EDITOR:-vi} .cloving/config.json",
33
36
  "queue-interval": "Set an interval from 10 to 3600: CLOVING_QUEUE_SECONDS=30 cloving doctor",
34
37
  "private-texts": `Set a nonblank private text: CLOVING_PRIVATE_TEXTS='["TEXT TO KEEP PRIVATE"]' cloving doctor`,
38
+ retention: "Set a retention period: cloving config set retention.days 30 --private",
35
39
  };
36
40
  export function doctorExitCode(checks) {
37
41
  return checks.some((check) => check.required && check.result !== "ok")
38
42
  ? 1
39
43
  : 0;
40
44
  }
41
- export function requiredDoctorCodes(workflows, options) {
45
+ export function requiredDoctorCodes(workflows) {
42
46
  const codes = new Set([
43
47
  "git-repository",
44
48
  "node-version",
45
49
  "default-harness",
46
50
  "settings-files",
51
+ "no-workflow",
47
52
  "runtime-directory",
48
53
  ]);
49
54
  if (workflows.some((workflow) => workflow.capabilities.includes("github"))) {
50
55
  codes.add("gh-login");
51
56
  codes.add("github-origin");
52
57
  }
53
- const bundled = workflows
54
- .filter((workflow) => workflow.scope === "bundled")
55
- .map((workflow) => workflow.workflowId);
56
- const issue = bundled.includes("develop-issue");
57
- if (issue)
58
- codes.add("check-command");
59
- if (workflows.some((workflow) => workflow.queue === true) ||
60
- (issue && options.queueSeconds !== undefined)) {
58
+ if (workflows.some((workflow) => workflow.queue === true)) {
61
59
  codes.add("next-command");
62
60
  codes.add("queue-interval");
63
61
  }
64
- if (workflows.some((workflow) => hasTicketField(workflow.input)) ||
65
- bundled.some((name) => ["develop-ticket", "develop-issue"].includes(name)))
62
+ if (workflows.some((workflow) => hasTicketField(workflow.input)))
66
63
  codes.add("ticket-command");
67
- if (bundled.includes("develop-prd"))
68
- codes.add("private-texts");
69
64
  return [...codes];
70
65
  }
@@ -0,0 +1,15 @@
1
+ import { type ShellMode } from "./agent-harness-shell.ts";
2
+ import type { HarnessSelection } from "./configuration-change.ts";
3
+ export interface HarnessStartRequest {
4
+ readonly executable: string;
5
+ readonly arguments: readonly string[];
6
+ readonly directory: string;
7
+ readonly environment: Readonly<Record<string, string | undefined>>;
8
+ }
9
+ export declare function harnessStartRequest(entry: HarnessSelection & {
10
+ readonly shellMode?: ShellMode;
11
+ }, context: {
12
+ readonly repository: string;
13
+ readonly environment: Readonly<NodeJS.ProcessEnv>;
14
+ readonly message: string;
15
+ }): HarnessStartRequest;
@@ -0,0 +1,37 @@
1
+ import { shellArguments, shellRunScript, } from "./agent-harness-shell.js";
2
+ const shellStartScript = `
3
+ set +m
4
+ kind=
5
+ if [ -n "\${ZSH_VERSION-}" ]; then
6
+ kind=$(builtin whence -w -- "$1" 2>/dev/null) || kind=
7
+ kind="\${kind##*: }"
8
+ elif [ -n "\${BASH_VERSION-}" ]; then
9
+ kind=$(builtin type -t -- "$1" 2>/dev/null) || kind=
10
+ fi
11
+ case "$kind" in
12
+ function|alias) ;;
13
+ *) printf '%s\\n' 'Open your agent in this repository and tell it: run \`cloving init-prompt\`.' >&2; exit 1 ;;
14
+ esac
15
+ ${shellRunScript}
16
+ `;
17
+ export function harnessStartRequest(entry, context) {
18
+ const argumentsList = entry.name === "opencode"
19
+ ? ["--prompt", context.message]
20
+ : [context.message];
21
+ const executable = entry.kind === "shell" ? (context.environment["SHELL"] ?? "") : entry.path;
22
+ const args = entry.kind === "shell"
23
+ ? [
24
+ ...shellArguments(entry.shellMode ?? "login"),
25
+ shellStartScript,
26
+ "cloving-init",
27
+ entry.path,
28
+ ...argumentsList,
29
+ ]
30
+ : argumentsList;
31
+ return {
32
+ executable,
33
+ arguments: args,
34
+ directory: context.repository,
35
+ environment: context.environment,
36
+ };
37
+ }
@@ -0,0 +1,38 @@
1
+ import type { CommandWaitState } from "../authority/command-waits.ts";
2
+ import type { CommandWaitRequest, CommandWaitProgress } from "../step-bridge.ts";
3
+ interface WaitName {
4
+ readonly runId: string;
5
+ readonly name: string;
6
+ }
7
+ interface WaitCall extends WaitName {
8
+ readonly commandId: string;
9
+ }
10
+ interface WaitReceipt {
11
+ readonly kind: string;
12
+ readonly reason?: string;
13
+ }
14
+ export interface CommandWaitPorts {
15
+ readonly open: (request: WaitCall & {
16
+ readonly input: string;
17
+ }) => Promise<WaitReceipt>;
18
+ readonly check: (request: WaitCall) => Promise<WaitReceipt>;
19
+ readonly read: (request: WaitName) => Promise<CommandWaitState | undefined>;
20
+ }
21
+ interface WaitHost {
22
+ readonly commandWait?: CommandWaitPorts;
23
+ readonly runStatus: (runId: string) => Promise<string | undefined>;
24
+ readonly subject: () => Promise<{
25
+ readonly runId: string;
26
+ readonly commandId: string;
27
+ } | undefined>;
28
+ readonly stopped: AbortSignal;
29
+ readonly pollMilliseconds: number;
30
+ readonly retryMilliseconds: number;
31
+ }
32
+ export declare function commandWaitBridge(host: WaitHost): {
33
+ openCommandWait: (request: CommandWaitRequest) => Promise<CommandWaitProgress>;
34
+ checkCommandWait: (request: {
35
+ readonly name: string;
36
+ }) => Promise<CommandWaitProgress>;
37
+ };
38
+ export {};
@@ -0,0 +1,115 @@
1
+ import { setTimeout as delay } from "node:timers/promises";
2
+ import { commandWaitValue, isCommandWaitRequest } from "../command-wait.js";
3
+ function progress(wait) {
4
+ if (wait === undefined)
5
+ return { kind: "refused", reason: "unknown-command-wait" };
6
+ if (wait.state === "waiting")
7
+ return waiting(wait);
8
+ if (wait.state === "timed-out")
9
+ return { kind: "timed-out", reason: "command-wait-timeout" };
10
+ if (wait.state !== "completed")
11
+ return { kind: "failed", reason: wait.reason ?? "command-wait-failed" };
12
+ const value = JSON.parse(wait.result ?? "null");
13
+ return commandWaitValue(value)
14
+ ? { kind: "ready", value }
15
+ : { kind: "failed", reason: "command-wait-invalid-output" };
16
+ }
17
+ function refused(receipt) {
18
+ if (["accepted", "duplicate"].includes(receipt.kind))
19
+ return undefined;
20
+ return {
21
+ kind: receipt.kind === "failed" ? "failed" : "refused",
22
+ reason: receipt.reason ?? receipt.kind,
23
+ };
24
+ }
25
+ async function requested(context, send) {
26
+ const { host, call, retryReasons = ["service-busy"] } = context;
27
+ for (;;) {
28
+ if (host.stopped.aborted)
29
+ await new Promise(() => undefined);
30
+ const receipt = await send();
31
+ if (receipt.kind !== "rejected" ||
32
+ !retryReasons.includes(receipt.reason ?? ""))
33
+ return receipt;
34
+ if (receipt.reason === "command-wait-run-not-running" &&
35
+ !["queued", "running", "waiting_gate", "paused"].includes((await host.runStatus(call.runId)) ?? ""))
36
+ return receipt;
37
+ await delay(host.retryMilliseconds, undefined, {
38
+ signal: host.stopped,
39
+ }).catch(() => undefined);
40
+ }
41
+ }
42
+ async function settled(context, request) {
43
+ const { host, port } = context;
44
+ for (;;) {
45
+ if (host.stopped.aborted)
46
+ await new Promise(() => undefined);
47
+ const wait = await port.read(request);
48
+ if (wait?.state !== "waiting" || wait.checkOperation === null)
49
+ return progress(wait);
50
+ await delay(host.pollMilliseconds, undefined, {
51
+ signal: host.stopped,
52
+ }).catch(() => undefined);
53
+ }
54
+ }
55
+ export function commandWaitBridge(host) {
56
+ return {
57
+ openCommandWait: async (request) => {
58
+ if (!isCommandWaitRequest(request))
59
+ return { kind: "refused", reason: "invalid-command-wait-request" };
60
+ const subject = await host.subject();
61
+ const port = host.commandWait;
62
+ if (subject === undefined || port === undefined)
63
+ return { kind: "unavailable", reason: "cloving-host-unavailable" };
64
+ const call = {
65
+ ...subject,
66
+ name: request.name,
67
+ input: JSON.stringify(request.input),
68
+ };
69
+ const receipt = await requested({
70
+ host,
71
+ call,
72
+ retryReasons: ["service-busy", "command-wait-run-not-running"],
73
+ }, () => port.open(call));
74
+ return (refused(receipt) ??
75
+ progress(await port.read({ ...subject, name: request.name })));
76
+ },
77
+ checkCommandWait: async (request) => {
78
+ const subject = await host.subject();
79
+ const port = host.commandWait;
80
+ if (subject === undefined || port === undefined)
81
+ return { kind: "unavailable", reason: "cloving-host-unavailable" };
82
+ const call = { ...subject, name: request.name };
83
+ const wait = await port.read(call);
84
+ if (wait?.state !== "waiting")
85
+ return progress(wait);
86
+ if (wait.checkOperation !== null)
87
+ return await settled({ host, port }, call);
88
+ if (wait.nextCheckAt !== null &&
89
+ Date.now() < Math.min(wait.nextCheckAt, wait.deadline))
90
+ return progress(wait);
91
+ const receipt = await requested({ host, call }, () => port.check(call));
92
+ return checkReply(receipt, wait) ?? (await settled({ host, port }, call));
93
+ },
94
+ };
95
+ }
96
+ function checkReply(receipt, wait) {
97
+ if (receipt.reason === "command-wait-timeout")
98
+ return { kind: "timed-out", reason: receipt.reason };
99
+ if (receipt.reason === "command-wait-run-not-running")
100
+ return waiting(wait, Date.now() + 1000);
101
+ if ([
102
+ "command-wait-check-in-flight",
103
+ "command-wait-check-not-due",
104
+ "command-wait-ended",
105
+ ].includes(receipt.reason ?? ""))
106
+ return undefined;
107
+ return refused(receipt);
108
+ }
109
+ function waiting(wait, nextCheckAt = wait.nextCheckAt ?? wait.deadline) {
110
+ return {
111
+ kind: "pending",
112
+ deadline: wait.deadline,
113
+ nextCheckAt,
114
+ };
115
+ }