cloving 1.2.0 → 1.3.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 (518) hide show
  1. package/README.md +525 -185
  2. package/dist/src/agent-harness-shell.d.ts +12 -0
  3. package/dist/src/agent-harness-shell.js +90 -0
  4. package/dist/src/agent-harness-versions.d.ts +8 -0
  5. package/dist/src/agent-harness-versions.js +49 -0
  6. package/dist/src/agent-harnesses.d.ts +4 -0
  7. package/dist/src/agent-harnesses.js +13 -0
  8. package/dist/src/authority/commands.d.ts +1 -0
  9. package/dist/src/authority/commands.js +24 -4
  10. package/dist/src/authority/gate-reduction.d.ts +1 -0
  11. package/dist/src/authority/gate-reduction.js +18 -3
  12. package/dist/src/authority/gates.d.ts +2 -1
  13. package/dist/src/authority/gates.js +5 -1
  14. package/dist/src/authority/operations.js +1 -1
  15. package/dist/src/authority/reduction.d.ts +1 -1
  16. package/dist/src/authority/reduction.js +6 -1
  17. package/dist/src/authority/status-input.d.ts +1 -1
  18. package/dist/src/authority/status.d.ts +1 -0
  19. package/dist/src/authority/status.js +27 -3
  20. package/dist/src/authority/transitions.d.ts +5 -5
  21. package/dist/src/authority/transitions.js +6 -0
  22. package/dist/src/broker/actor/authority-delivery.js +1 -0
  23. package/dist/src/broker/actor/authority-queries.d.ts +7 -1
  24. package/dist/src/broker/actor/authority-queries.js +17 -2
  25. package/dist/src/broker/actor/authority-receipts.js +11 -1
  26. package/dist/src/broker/actor/authority-session.js +2 -2
  27. package/dist/src/broker/actor/authority-settings.js +4 -0
  28. package/dist/src/broker/actor/delivery-intents.d.ts +2 -1
  29. package/dist/src/broker/actor/delivery-intents.js +4 -4
  30. package/dist/src/broker/actor/effect-delivery.js +4 -1
  31. package/dist/src/broker/actor/effect-queries.d.ts +1 -0
  32. package/dist/src/broker/actor/effect-queries.js +7 -0
  33. package/dist/src/broker/actor/pin-check.js +1 -1
  34. package/dist/src/broker/actor/stop-plan.js +1 -1
  35. package/dist/src/broker/actor/turn-state.js +1 -1
  36. package/dist/src/broker/authority-admission.js +22 -9
  37. package/dist/src/broker/authority-restore.js +1 -0
  38. package/dist/src/broker/decision-restore.js +4 -4
  39. package/dist/src/broker/socket-address.d.ts +3 -1
  40. package/dist/src/broker/socket-address.js +2 -2
  41. package/dist/src/cli/arguments.d.ts +11 -13
  42. package/dist/src/cli/arguments.js +37 -107
  43. package/dist/src/cli/command-effects.d.ts +7 -0
  44. package/dist/src/cli/command-effects.js +39 -0
  45. package/dist/src/cli/commands.js +11 -4
  46. package/dist/src/cli/config-arguments.d.ts +11 -0
  47. package/dist/src/cli/config-arguments.js +59 -0
  48. package/dist/src/cli/config-command.d.ts +2 -0
  49. package/dist/src/cli/config-command.js +105 -0
  50. package/dist/src/cli/config-file.d.ts +3 -0
  51. package/dist/src/cli/config-file.js +50 -0
  52. package/dist/src/cli/config-lock.d.ts +7 -0
  53. package/dist/src/cli/config-lock.js +61 -0
  54. package/dist/src/cli/config-notice.d.ts +5 -0
  55. package/dist/src/cli/config-notice.js +46 -0
  56. package/dist/src/cli/doctor-github.d.ts +9 -0
  57. package/dist/src/cli/doctor-github.js +138 -0
  58. package/dist/src/cli/doctor-harness.d.ts +13 -0
  59. package/dist/src/cli/doctor-harness.js +242 -0
  60. package/dist/src/cli/doctor-local.d.ts +18 -0
  61. package/dist/src/cli/doctor-local.js +77 -0
  62. package/dist/src/cli/doctor-runtime.d.ts +15 -0
  63. package/dist/src/cli/doctor-runtime.js +176 -0
  64. package/dist/src/cli/doctor-service.d.ts +1 -0
  65. package/dist/src/cli/doctor-service.js +11 -0
  66. package/dist/src/cli/doctor-workflows.d.ts +3 -0
  67. package/dist/src/cli/doctor-workflows.js +45 -0
  68. package/dist/src/cli/doctor.d.ts +15 -0
  69. package/dist/src/cli/doctor.js +190 -0
  70. package/dist/src/cli/github-ci-progress.d.ts +8 -0
  71. package/dist/src/cli/github-ci-progress.js +83 -0
  72. package/dist/src/cli/github-retry.d.ts +6 -0
  73. package/dist/src/cli/github-retry.js +47 -0
  74. package/dist/src/cli/harness-probes.d.ts +22 -0
  75. package/dist/src/cli/harness-probes.js +183 -0
  76. package/dist/src/cli/main.d.ts +7 -1
  77. package/dist/src/cli/main.js +39 -61
  78. package/dist/src/cli/phone-arguments.d.ts +14 -0
  79. package/dist/src/cli/phone-arguments.js +37 -0
  80. package/dist/src/cli/phone-client.d.ts +2 -0
  81. package/dist/src/cli/phone-client.js +32 -0
  82. package/dist/src/cli/phone-command.d.ts +13 -0
  83. package/dist/src/cli/phone-command.js +101 -0
  84. package/dist/src/cli/probe-command.d.ts +36 -0
  85. package/dist/src/cli/probe-command.js +59 -0
  86. package/dist/src/cli/project-settings.d.ts +4 -0
  87. package/dist/src/cli/project-settings.js +33 -0
  88. package/dist/src/cli/render.d.ts +2 -1
  89. package/dist/src/cli/render.js +44 -28
  90. package/dist/src/cli/report-github.d.ts +18 -0
  91. package/dist/src/cli/report-github.js +200 -0
  92. package/dist/src/cli/report.d.ts +17 -0
  93. package/dist/src/cli/report.js +192 -0
  94. package/dist/src/cli/repository-arguments.d.ts +5 -0
  95. package/dist/src/cli/repository-arguments.js +34 -0
  96. package/dist/src/cli/runtime-arguments.d.ts +4 -0
  97. package/dist/src/cli/runtime-arguments.js +15 -0
  98. package/dist/src/cli/runtime-directory.d.ts +6 -2
  99. package/dist/src/cli/runtime-directory.js +72 -14
  100. package/dist/src/cli/toolchain.d.ts +2 -0
  101. package/dist/src/cli/toolchain.js +22 -2
  102. package/dist/src/cli/usage-render.d.ts +5 -0
  103. package/dist/src/cli/usage-render.js +40 -1
  104. package/dist/src/cli/usage.d.ts +3 -3
  105. package/dist/src/cli/usage.js +70 -44
  106. package/dist/src/cli/watch-actions.d.ts +2 -0
  107. package/dist/src/cli/watch-actions.js +4 -4
  108. package/dist/src/cli/watch-board-view.d.ts +1 -1
  109. package/dist/src/cli/watch-board-view.js +8 -2
  110. package/dist/src/cli/watch-board.js +23 -10
  111. package/dist/src/cli/watch-card-forms.js +2 -2
  112. package/dist/src/cli/watch-client.d.ts +1 -1
  113. package/dist/src/cli/watch-client.js +7 -2
  114. package/dist/src/cli/watch-header-view.d.ts +27 -0
  115. package/dist/src/cli/watch-header-view.js +116 -0
  116. package/dist/src/cli/watch-model.js +17 -4
  117. package/dist/src/cli/watch-page.js +48 -12
  118. package/dist/src/cli/watch-phone-client.d.ts +1 -0
  119. package/dist/src/cli/watch-phone-client.js +84 -0
  120. package/dist/src/cli/watch-phone-view.d.ts +56 -0
  121. package/dist/src/cli/watch-phone-view.js +113 -0
  122. package/dist/src/cli/watch-phone.d.ts +5 -0
  123. package/dist/src/cli/watch-phone.js +53 -0
  124. package/dist/src/cli/watch-pull-request.d.ts +3 -1
  125. package/dist/src/cli/watch-pull-request.js +4 -0
  126. package/dist/src/cli/watch-render.d.ts +9 -1
  127. package/dist/src/cli/watch-server.js +23 -5
  128. package/dist/src/cli/watch-turn.d.ts +4 -1
  129. package/dist/src/cli/watch-turn.js +37 -1
  130. package/dist/src/cli/watch-usage.js +5 -1
  131. package/dist/src/cli/watch-view.d.ts +6 -20
  132. package/dist/src/cli/watch-view.js +14 -106
  133. package/dist/src/cli.js +106 -23
  134. package/dist/src/command-settings.d.ts +16 -0
  135. package/dist/src/command-settings.js +42 -0
  136. package/dist/src/configuration-change.d.ts +11 -0
  137. package/dist/src/configuration-change.js +62 -0
  138. package/dist/src/configuration-display.d.ts +21 -0
  139. package/dist/src/configuration-display.js +69 -0
  140. package/dist/src/configuration-schema.d.ts +49 -0
  141. package/dist/src/configuration-schema.js +159 -0
  142. package/dist/src/daemon/claude-turn.d.ts +19 -5
  143. package/dist/src/daemon/claude-turn.js +116 -53
  144. package/dist/src/daemon/claude-worker.d.ts +3 -3
  145. package/dist/src/daemon/claude-worker.js +65 -76
  146. package/dist/src/daemon/command-journal.d.ts +5 -0
  147. package/dist/src/daemon/command-journal.js +50 -0
  148. package/dist/src/daemon/command-process.d.ts +28 -0
  149. package/dist/src/daemon/command-process.js +164 -0
  150. package/dist/src/daemon/command-worker.d.ts +19 -0
  151. package/dist/src/daemon/command-worker.js +182 -0
  152. package/dist/src/daemon/cursor-stream.d.ts +4 -0
  153. package/dist/src/daemon/cursor-stream.js +154 -0
  154. package/dist/src/daemon/daemon-adapters.d.ts +58 -0
  155. package/dist/src/daemon/daemon-adapters.js +102 -0
  156. package/dist/src/daemon/daemon-loops.d.ts +14 -0
  157. package/dist/src/daemon/daemon-loops.js +39 -0
  158. package/dist/src/daemon/daemon.d.ts +2 -33
  159. package/dist/src/daemon/daemon.js +126 -132
  160. package/dist/src/daemon/declared-input.d.ts +3 -0
  161. package/dist/src/daemon/declared-input.js +9 -0
  162. package/dist/src/daemon/delivery-router.js +1 -1
  163. package/dist/src/daemon/gemini-stream.d.ts +4 -0
  164. package/dist/src/daemon/gemini-stream.js +129 -0
  165. package/dist/src/daemon/github-ci-rerun.d.ts +26 -0
  166. package/dist/src/daemon/github-ci-rerun.js +92 -0
  167. package/dist/src/daemon/github-ci.js +89 -8
  168. package/dist/src/daemon/github-merge-call.d.ts +2 -0
  169. package/dist/src/daemon/github-merge.js +7 -3
  170. package/dist/src/daemon/github-methods.js +10 -0
  171. package/dist/src/daemon/github-operation.d.ts +1 -0
  172. package/dist/src/daemon/github-retry.js +8 -44
  173. package/dist/src/daemon/issue-queue.d.ts +1 -0
  174. package/dist/src/daemon/issue-queue.js +3 -26
  175. package/dist/src/daemon/notify.js +2 -1
  176. package/dist/src/daemon/opencode-stream.d.ts +5 -0
  177. package/dist/src/daemon/opencode-stream.js +87 -0
  178. package/dist/src/daemon/queue-status.d.ts +4 -0
  179. package/dist/src/daemon/queue-status.js +38 -0
  180. package/dist/src/daemon/retry-worktree.d.ts +18 -0
  181. package/dist/src/daemon/retry-worktree.js +139 -0
  182. package/dist/src/daemon/retrying-turn.d.ts +2 -0
  183. package/dist/src/daemon/retrying-turn.js +108 -30
  184. package/dist/src/daemon/schedule-journal.d.ts +19 -0
  185. package/dist/src/daemon/schedule-journal.js +57 -0
  186. package/dist/src/daemon/text-output.d.ts +18 -0
  187. package/dist/src/daemon/text-output.js +44 -0
  188. package/dist/src/daemon/ticket-registration.d.ts +5 -7
  189. package/dist/src/daemon/ticket-registration.js +85 -40
  190. package/dist/src/daemon/turn-command.d.ts +10 -0
  191. package/dist/src/daemon/turn-command.js +33 -0
  192. package/dist/src/daemon/turn-report.d.ts +18 -0
  193. package/dist/src/daemon/turn-report.js +79 -0
  194. package/dist/src/daemon/work-tree.d.ts +2 -1
  195. package/dist/src/daemon/work-tree.js +15 -12
  196. package/dist/src/daemon/workflow-generations.d.ts +32 -0
  197. package/dist/src/daemon/workflow-generations.js +57 -0
  198. package/dist/src/daemon/workflow-replay.d.ts +16 -0
  199. package/dist/src/daemon/workflow-replay.js +58 -0
  200. package/dist/src/daemon/workflow-schedules.d.ts +27 -0
  201. package/dist/src/daemon/workflow-schedules.js +185 -0
  202. package/dist/src/decision-wait.d.ts +1 -0
  203. package/dist/src/decision-wait.js +3 -0
  204. package/dist/src/delivery-backoff.d.ts +1 -0
  205. package/dist/src/delivery-backoff.js +3 -0
  206. package/dist/src/doctor-report.d.ts +24 -0
  207. package/dist/src/doctor-report.js +70 -0
  208. package/dist/src/execution-pin.d.ts +5 -0
  209. package/dist/src/execution-pin.js +67 -4
  210. package/dist/src/grok-result.d.ts +12 -0
  211. package/dist/src/grok-result.js +61 -0
  212. package/dist/src/harness-defaults.d.ts +35 -0
  213. package/dist/src/harness-defaults.js +121 -0
  214. package/dist/src/host/decision-resume.d.ts +35 -0
  215. package/dist/src/host/decision-resume.js +86 -0
  216. package/dist/src/host/gate-bridge.d.ts +48 -0
  217. package/dist/src/host/gate-bridge.js +124 -0
  218. package/dist/src/host/run-wait.d.ts +26 -0
  219. package/dist/src/host/run-wait.js +37 -0
  220. package/dist/src/host/start-delivery.d.ts +1 -1
  221. package/dist/src/host/start-delivery.js +10 -3
  222. package/dist/src/host/step-bridge-host.d.ts +6 -20
  223. package/dist/src/host/step-bridge-host.js +44 -58
  224. package/dist/src/merge-request.d.ts +19 -0
  225. package/dist/src/merge-request.js +32 -0
  226. package/dist/src/phone/canonical-json.d.ts +1 -0
  227. package/dist/src/phone/canonical-json.js +16 -0
  228. package/dist/src/phone/phone-link.d.ts +7 -0
  229. package/dist/src/phone/phone-link.js +21 -0
  230. package/dist/src/phone/qr-data.d.ts +8 -0
  231. package/dist/src/phone/qr-data.js +133 -0
  232. package/dist/src/phone/qr-matrix.d.ts +6 -0
  233. package/dist/src/phone/qr-matrix.js +175 -0
  234. package/dist/src/phone/qr-penalty.d.ts +3 -0
  235. package/dist/src/phone/qr-penalty.js +67 -0
  236. package/dist/src/phone/qr-picture.d.ts +5 -0
  237. package/dist/src/phone/qr-picture.js +24 -0
  238. package/dist/src/private-config.schema.json +263 -0
  239. package/dist/src/project-config.schema.json +65 -0
  240. package/dist/src/project-settings.d.ts +19 -0
  241. package/dist/src/project-settings.js +131 -0
  242. package/dist/src/relay/answer-check.d.ts +24 -0
  243. package/dist/src/relay/answer-check.js +145 -0
  244. package/dist/src/relay/answer-inbox.d.ts +15 -0
  245. package/dist/src/relay/answer-inbox.js +173 -0
  246. package/dist/src/relay/daemon-keys.d.ts +16 -0
  247. package/dist/src/relay/daemon-keys.js +75 -0
  248. package/dist/src/relay/decision-check.d.ts +3 -0
  249. package/dist/src/relay/decision-check.js +77 -0
  250. package/dist/src/relay/decision-close.d.ts +6 -0
  251. package/dist/src/relay/decision-close.js +72 -0
  252. package/dist/src/relay/decision-envelopes.d.ts +29 -0
  253. package/dist/src/relay/decision-envelopes.js +93 -0
  254. package/dist/src/relay/decision-watch.d.ts +14 -0
  255. package/dist/src/relay/decision-watch.js +68 -0
  256. package/dist/src/relay/envelope-post.d.ts +8 -0
  257. package/dist/src/relay/envelope-post.js +71 -0
  258. package/dist/src/relay/envelope.d.ts +28 -0
  259. package/dist/src/relay/envelope.js +53 -0
  260. package/dist/src/relay/hpke.d.ts +25 -0
  261. package/dist/src/relay/hpke.js +108 -0
  262. package/dist/src/relay/pairing-checks.d.ts +42 -0
  263. package/dist/src/relay/pairing-checks.js +172 -0
  264. package/dist/src/relay/pairing-follow.d.ts +8 -0
  265. package/dist/src/relay/pairing-follow.js +95 -0
  266. package/dist/src/relay/pairing-session.d.ts +29 -0
  267. package/dist/src/relay/pairing-session.js +87 -0
  268. package/dist/src/relay/phone-delivery.d.ts +51 -0
  269. package/dist/src/relay/phone-delivery.js +139 -0
  270. package/dist/src/relay/phone-devices.d.ts +36 -0
  271. package/dist/src/relay/phone-devices.js +46 -0
  272. package/dist/src/relay/phone-inbox.d.ts +18 -0
  273. package/dist/src/relay/phone-inbox.js +17 -0
  274. package/dist/src/relay/phone-operations.d.ts +18 -0
  275. package/dist/src/relay/phone-operations.js +121 -0
  276. package/dist/src/relay/phone-outbox.d.ts +21 -0
  277. package/dist/src/relay/phone-outbox.js +25 -0
  278. package/dist/src/relay/phone-socket.d.ts +6 -0
  279. package/dist/src/relay/phone-socket.js +63 -0
  280. package/dist/src/relay/relay-control.d.ts +7 -0
  281. package/dist/src/relay/relay-control.js +24 -0
  282. package/dist/src/relay/relay-files.d.ts +2 -0
  283. package/dist/src/relay/relay-files.js +23 -0
  284. package/dist/src/relay/relay-heartbeat.d.ts +17 -0
  285. package/dist/src/relay/relay-heartbeat.js +81 -0
  286. package/dist/src/relay/relay-http.d.ts +14 -0
  287. package/dist/src/relay/relay-http.js +63 -0
  288. package/dist/src/relay/relay-registration.d.ts +15 -0
  289. package/dist/src/relay/relay-registration.js +63 -0
  290. package/dist/src/relay/relay-service.d.ts +25 -0
  291. package/dist/src/relay/relay-service.js +216 -0
  292. package/dist/src/relay/relay-store.d.ts +48 -0
  293. package/dist/src/relay/relay-store.js +131 -0
  294. package/dist/src/relay/sealed-post.d.ts +25 -0
  295. package/dist/src/relay/sealed-post.js +52 -0
  296. package/dist/src/relay.d.ts +1 -0
  297. package/dist/src/relay.js +8 -0
  298. package/dist/src/report-statistics.d.ts +50 -0
  299. package/dist/src/report-statistics.js +70 -0
  300. package/dist/src/report.d.ts +171 -0
  301. package/dist/src/report.js +119 -0
  302. package/dist/src/repository-identity.d.ts +4 -0
  303. package/dist/src/repository-identity.js +4 -0
  304. package/dist/src/review-subject.d.ts +41 -0
  305. package/dist/src/review-subject.js +91 -0
  306. package/dist/src/run-wait.d.ts +23 -0
  307. package/dist/src/run-wait.js +20 -0
  308. package/dist/src/service/active-tickets.d.ts +4 -0
  309. package/dist/src/service/active-tickets.js +49 -0
  310. package/dist/src/service/command-effects.d.ts +4 -0
  311. package/dist/src/service/command-effects.js +16 -0
  312. package/dist/src/service/decision-handler.d.ts +4 -0
  313. package/dist/src/service/decision-handler.js +39 -4
  314. package/dist/src/service/gateway.js +18 -3
  315. package/dist/src/service/registry.d.ts +16 -3
  316. package/dist/src/service/registry.js +34 -3
  317. package/dist/src/service/replies.d.ts +9 -1
  318. package/dist/src/service/replies.js +12 -0
  319. package/dist/src/service/run-handlers.js +14 -55
  320. package/dist/src/service/run-metadata.d.ts +8 -0
  321. package/dist/src/service/run-metadata.js +53 -0
  322. package/dist/src/service/run-waits.d.ts +3 -0
  323. package/dist/src/service/run-waits.js +41 -0
  324. package/dist/src/service/service.d.ts +18 -2
  325. package/dist/src/service/service.js +23 -7
  326. package/dist/src/service/submission-handler.d.ts +0 -1
  327. package/dist/src/service/submission-handler.js +6 -34
  328. package/dist/src/service/worker-port.d.ts +1 -0
  329. package/dist/src/service/worker-totals.js +8 -23
  330. package/dist/src/step-bridge.d.ts +23 -0
  331. package/dist/src/steps/decision-gate.d.ts +2 -1
  332. package/dist/src/steps/decision-gate.js +7 -0
  333. package/dist/src/steps/merge-by-person.d.ts +2 -0
  334. package/dist/src/steps/merge-by-person.js +18 -0
  335. package/dist/src/steps/merge-when-green.d.ts +2 -0
  336. package/dist/src/steps/merge-when-green.js +62 -0
  337. package/dist/src/steps/review-by-agent.d.ts +5 -0
  338. package/dist/src/steps/review-by-agent.js +124 -0
  339. package/dist/src/steps/review-by-person.d.ts +4 -0
  340. package/dist/src/steps/review-by-person.js +16 -0
  341. package/dist/src/steps/review-subject.d.ts +2 -0
  342. package/dist/src/steps/review-subject.js +11 -0
  343. package/dist/src/steps/run-command.d.ts +2 -0
  344. package/dist/src/steps/run-command.js +15 -0
  345. package/dist/src/steps/sleep.d.ts +3 -0
  346. package/dist/src/steps/sleep.js +19 -0
  347. package/dist/src/steps/wait-for-decision.d.ts +2 -0
  348. package/dist/src/steps/wait-for-decision.js +71 -0
  349. package/dist/src/steps/wait-for-run.d.ts +5 -0
  350. package/dist/src/steps/wait-for-run.js +9 -0
  351. package/dist/src/storage/decision-snapshots.d.ts +1 -0
  352. package/dist/src/storage/decision-snapshots.js +7 -1
  353. package/dist/src/submission.d.ts +4 -0
  354. package/dist/src/submission.js +6 -2
  355. package/dist/src/testing.d.ts +29 -0
  356. package/dist/src/testing.js +264 -0
  357. package/dist/src/ticket-status-stream.d.ts +9 -0
  358. package/dist/src/ticket-status-stream.js +94 -0
  359. package/dist/src/token-prices.d.ts +12 -4
  360. package/dist/src/token-prices.js +15 -2
  361. package/dist/src/turn-profile.d.ts +1 -0
  362. package/dist/src/turn-profile.js +8 -1
  363. package/dist/src/worker-usage.d.ts +16 -0
  364. package/dist/src/worker-usage.js +57 -0
  365. package/dist/src/workflow-admission.d.ts +5 -0
  366. package/dist/src/workflow-candidate.d.ts +7 -2
  367. package/dist/src/workflow-candidate.js +47 -5
  368. package/dist/src/workflow-declaration.d.ts +21 -3
  369. package/dist/src/workflow-declaration.js +77 -9
  370. package/dist/src/workflow-discovery.d.ts +2 -1
  371. package/dist/src/workflow-discovery.js +4 -1
  372. package/dist/src/workflow-refusals.js +3 -0
  373. package/dist/src/workflow-schedule.d.ts +22 -0
  374. package/dist/src/workflow-schedule.js +53 -0
  375. package/dist/src/workflows.d.ts +11 -1
  376. package/dist/src/workflows.js +8 -0
  377. package/dist/tools/admit-workflow.d.ts +1 -0
  378. package/dist/tools/admit-workflow.js +18 -5
  379. package/dist/tools/doctor-adoption.d.ts +1 -0
  380. package/dist/tools/doctor-adoption.js +11 -0
  381. package/dist/tools/project-configuration.d.ts +4 -0
  382. package/dist/tools/project-configuration.js +47 -0
  383. package/dist/tools/self-host-processes.d.ts +1 -0
  384. package/dist/tools/self-host-processes.js +33 -22
  385. package/dist/tools/self-host-project.d.ts +4 -0
  386. package/dist/tools/self-host-project.js +61 -14
  387. package/dist/tools/self-host-runtime.d.ts +16 -0
  388. package/dist/tools/self-host-runtime.js +60 -0
  389. package/dist/tools/self-host-workflows.d.ts +1 -0
  390. package/dist/tools/self-host-workflows.js +36 -1
  391. package/dist/tools/self-host.js +86 -61
  392. package/dist/tools/workflow-candidate-counts.d.ts +6 -2
  393. package/dist/tools/workflow-candidate-counts.js +31 -16
  394. package/dist/tools/workflow-candidate-fields.d.ts +5 -0
  395. package/dist/tools/workflow-candidate-fields.js +24 -0
  396. package/dist/tools/workflow-candidate-helper-counts.d.ts +10 -0
  397. package/dist/tools/workflow-candidate-helper-counts.js +129 -0
  398. package/dist/tools/workflow-candidate-program.d.ts +2 -0
  399. package/dist/tools/workflow-candidate-program.js +36 -8
  400. package/dist/tools/workflow-candidate-source.js +77 -15
  401. package/dist/tools/workflow-candidate-syntax.d.ts +8 -2
  402. package/dist/tools/workflow-candidate-syntax.js +70 -26
  403. package/dist/tools/workflow-command.d.ts +1 -0
  404. package/dist/tools/workflow-command.js +143 -0
  405. package/dist/tools/workflow-folder-capture.d.ts +1 -0
  406. package/dist/tools/workflow-folder-capture.js +47 -2
  407. package/dist/tools/workflow-folders.d.ts +17 -1
  408. package/dist/tools/workflow-folders.js +59 -6
  409. package/dist/tools/workflow-test-hook.d.ts +7 -0
  410. package/dist/tools/workflow-test-hook.js +16 -0
  411. package/dist/tools/workflow-test-loader.d.ts +1 -0
  412. package/dist/tools/workflow-test-loader.js +10 -0
  413. package/dist/workflows/develop-ticket.js +1 -1
  414. package/dist/workflows/ticket-prompts.js +5 -1
  415. package/package.json +46 -4
  416. package/templates/develop/README.md +78 -0
  417. package/templates/develop/develop/accept.ts +11 -0
  418. package/templates/develop/develop/flow-tests.ts +357 -0
  419. package/templates/develop/develop/local.ts +40 -0
  420. package/templates/develop/develop/option-tests.ts +174 -0
  421. package/templates/develop/develop/partial-choice.ts +29 -0
  422. package/templates/develop/develop/partial.ts +28 -0
  423. package/templates/develop/develop/pull.ts +152 -0
  424. package/templates/develop/develop/queue-partial.ts +39 -0
  425. package/templates/develop/develop/refusal-tests.ts +174 -0
  426. package/templates/develop/develop/report.ts +156 -0
  427. package/templates/develop/develop/round.ts +40 -0
  428. package/templates/develop/develop/scenarios.ts +87 -0
  429. package/templates/develop/develop/turn.ts +67 -0
  430. package/templates/develop/develop.declaration.json +24 -0
  431. package/templates/develop/develop.test.ts +287 -0
  432. package/templates/develop/develop.ts +30 -0
  433. package/templates/develop/template.json +153 -0
  434. package/dist/src/chats/analysis-batches.d.ts +0 -42
  435. package/dist/src/chats/analysis-batches.js +0 -97
  436. package/dist/src/chats/analysis-checkpoint.d.ts +0 -29
  437. package/dist/src/chats/analysis-checkpoint.js +0 -31
  438. package/dist/src/chats/analysis-contract.d.ts +0 -80
  439. package/dist/src/chats/analysis-contract.js +0 -16
  440. package/dist/src/chats/analysis-data.d.ts +0 -3
  441. package/dist/src/chats/analysis-data.js +0 -85
  442. package/dist/src/chats/analysis-input.d.ts +0 -18
  443. package/dist/src/chats/analysis-input.js +0 -97
  444. package/dist/src/chats/analysis-links.d.ts +0 -2
  445. package/dist/src/chats/analysis-links.js +0 -25
  446. package/dist/src/chats/analysis-merge.d.ts +0 -16
  447. package/dist/src/chats/analysis-merge.js +0 -134
  448. package/dist/src/chats/analysis-prompt.d.ts +0 -18
  449. package/dist/src/chats/analysis-prompt.js +0 -42
  450. package/dist/src/chats/analysis-proposal.d.ts +0 -4
  451. package/dist/src/chats/analysis-proposal.js +0 -41
  452. package/dist/src/chats/analysis-provenance.d.ts +0 -4
  453. package/dist/src/chats/analysis-provenance.js +0 -99
  454. package/dist/src/chats/analysis-shape.d.ts +0 -4
  455. package/dist/src/chats/analysis-shape.js +0 -235
  456. package/dist/src/chats/analysis.d.ts +0 -3
  457. package/dist/src/chats/analysis.js +0 -42
  458. package/dist/src/chats/corpus-input.d.ts +0 -5
  459. package/dist/src/chats/corpus-input.js +0 -288
  460. package/dist/src/chats/destination.d.ts +0 -4
  461. package/dist/src/chats/destination.js +0 -96
  462. package/dist/src/chats/import.d.ts +0 -15
  463. package/dist/src/chats/import.js +0 -243
  464. package/dist/src/chats/input-file.d.ts +0 -3
  465. package/dist/src/chats/input-file.js +0 -32
  466. package/dist/src/chats/normalize.d.ts +0 -29
  467. package/dist/src/chats/normalize.js +0 -155
  468. package/dist/src/chats/previous-import.d.ts +0 -5
  469. package/dist/src/chats/previous-import.js +0 -34
  470. package/dist/src/chats/reader.d.ts +0 -21
  471. package/dist/src/chats/reader.js +0 -118
  472. package/dist/src/chats/redact-import.d.ts +0 -14
  473. package/dist/src/chats/redact-import.js +0 -141
  474. package/dist/src/chats/redaction.d.ts +0 -41
  475. package/dist/src/chats/redaction.js +0 -161
  476. package/dist/src/chats/reference-mapping.d.ts +0 -16
  477. package/dist/src/chats/reference-mapping.js +0 -153
  478. package/dist/src/chats/revision-input.d.ts +0 -3
  479. package/dist/src/chats/revision-input.js +0 -180
  480. package/dist/src/chats/revision.d.ts +0 -48
  481. package/dist/src/chats/revision.js +0 -94
  482. package/dist/src/chats/selection.d.ts +0 -25
  483. package/dist/src/chats/selection.js +0 -109
  484. package/dist/src/chats/source.d.ts +0 -23
  485. package/dist/src/chats/source.js +0 -135
  486. package/dist/src/chats/structure.d.ts +0 -57
  487. package/dist/src/chats/structure.js +0 -216
  488. package/dist/src/chats/workflow-generation-projection.d.ts +0 -8
  489. package/dist/src/chats/workflow-generation-projection.js +0 -75
  490. package/dist/src/chats/workflow-generation-source.d.ts +0 -25
  491. package/dist/src/chats/workflow-generation-source.js +0 -124
  492. package/dist/src/chats/workflow-generation-text.d.ts +0 -23
  493. package/dist/src/chats/workflow-generation-text.js +0 -74
  494. package/dist/src/chats/workflow-generation.d.ts +0 -69
  495. package/dist/src/chats/workflow-generation.js +0 -142
  496. package/dist/src/daemon/analysis-launch.d.ts +0 -5
  497. package/dist/src/daemon/analysis-launch.js +0 -81
  498. package/dist/src/daemon/analysis-token.d.ts +0 -6
  499. package/dist/src/daemon/analysis-token.js +0 -88
  500. package/dist/src/daemon/analysis-worker.d.ts +0 -20
  501. package/dist/src/daemon/analysis-worker.js +0 -271
  502. package/dist/src/daemon/chats-batch-binding.d.ts +0 -23
  503. package/dist/src/daemon/chats-batch-binding.js +0 -99
  504. package/dist/src/daemon/chats-capture.d.ts +0 -18
  505. package/dist/src/daemon/chats-capture.js +0 -194
  506. package/dist/src/daemon/chats-checkpoints.d.ts +0 -41
  507. package/dist/src/daemon/chats-checkpoints.js +0 -186
  508. package/dist/src/daemon/chats-files.d.ts +0 -42
  509. package/dist/src/daemon/chats-files.js +0 -252
  510. package/dist/src/daemon/chats-follow-up.d.ts +0 -38
  511. package/dist/src/daemon/chats-follow-up.js +0 -180
  512. package/dist/src/daemon/chats-question.d.ts +0 -15
  513. package/dist/src/daemon/chats-question.js +0 -93
  514. package/dist/src/daemon/chats-worker.d.ts +0 -13
  515. package/dist/src/daemon/chats-worker.js +0 -201
  516. package/dist/workflows/analyze-chats.d.ts +0 -15
  517. package/dist/workflows/analyze-chats.declaration.json +0 -22
  518. package/dist/workflows/analyze-chats.js +0 -250
@@ -1,4 +1,4 @@
1
- import { priceOf, usageOf } from "../worker-usage.js";
1
+ import { attemptsOf, priceOf, usageOf, } from "../worker-usage.js";
2
2
  export function priceText(value, estimate) {
3
3
  const price = priceOf(value);
4
4
  if (price === null && priceOf(estimate) !== null)
@@ -16,6 +16,9 @@ export function usageText(value) {
16
16
  return [
17
17
  ["Input tokens", usage.input_tokens],
18
18
  ["Cache read tokens", usage.cache_read_input_tokens],
19
+ ...(usage.cache_creation_input_tokens === undefined
20
+ ? []
21
+ : [["Cache write tokens", usage.cache_creation_input_tokens]]),
19
22
  ["Output tokens", usage.output_tokens],
20
23
  ]
21
24
  .map(([label, count]) => `${String(label)}: ${count === null ? "Token usage not known" : String(count)}`)
@@ -26,3 +29,39 @@ export function totalsSuffix(state) {
26
29
  ? ""
27
30
  : " so far";
28
31
  }
32
+ export function statusJson(result) {
33
+ const run = record(result["run"]);
34
+ if (result["kind"] !== "run-status" || run === undefined)
35
+ return result;
36
+ const turns = run["workerTurns"];
37
+ return {
38
+ ...result,
39
+ run: {
40
+ ...run,
41
+ costDisplay: priceText(run["claudeCostUsd"], run["estimatedCostUsd"]),
42
+ ...(Array.isArray(turns)
43
+ ? {
44
+ workerTurns: turns.map((turn) => {
45
+ const fields = record(turn);
46
+ if (fields === undefined)
47
+ return turn;
48
+ return {
49
+ ...fields,
50
+ costDisplay: priceText(fields["costUsd"], fields["estimatedCostUsd"]),
51
+ };
52
+ }),
53
+ }
54
+ : {}),
55
+ },
56
+ };
57
+ }
58
+ import { record } from "../protocol.js";
59
+ export function attemptText(attempt) {
60
+ const kind = attempt.retryKind === undefined
61
+ ? ""
62
+ : ` · retryKind: ${String(attempt.retryKind)}`;
63
+ return `Attempt ${String(attempt.attempt)} · ${attempt.model ?? "Model not known"}${kind} · ${priceText(attempt.costUsd, attempt.estimatedCostUsd)}`;
64
+ }
65
+ export function attemptTexts(value) {
66
+ return (attemptsOf(value) ?? []).map(attemptText);
67
+ }
@@ -1,5 +1,5 @@
1
+ export declare const configUsage = "Usage: cloving config list [--json]\n cloving config get <field> [--json]\n cloving config set <field> <value> [--private] [--json]\n cloving config unset <field> [--private] [--json]\n\nReads and changes settings for the current repository; --repository selects another repository.\nValues use JSON when valid, otherwise text. Every change is checked before writing.\nFields use their default project or private file; --private selects the private file.\nlist and get show the effective source and hidden file values. Unconfigured values use defaults; null means no value.\nChanges keep the previous file as .bak and replace the file atomically under a lock.\nA live lock waits at most ten seconds, then exits with settings-locked.\nPrivate files, backups and temporary files have mode 0600. Changes require a service restart.\n";
1
2
  export declare const commandUsage: Readonly<Record<string, string>>;
2
- export declare const usage = "Usage: cloving [--help] [--version]\n cloving workflows validate <manifest-path> [--input <json-file>] [--json]\n cloving <command> [--help]\n\nCommands:\n workflows validate Check a compiled workflow manifest, its artifacts and optional runtime input.\n chats redact Redact one imported chat corpus into private runtime files.\n chats import Import explicitly selected Claude Code transcripts into private runtime files.\n run Submit one run of an admitted workflow.\n status List the runs or show one run.\n questions List the pending decisions.\n answer Answer one design question.\n approve Approve one pending approval.\n deny Deny one pending approval.\n pause Stop new dispatch for a run.\n resume Resume a paused run.\n drop Cancel a run and keep its files.\n events Print the committed events.\n watch Serve a page that shows the runs live.\n start Start the service for a repository.\n\nThe runtime commands need a Cloving service. CLOVING_RUNTIME_DIR names its runtime directory.\nUse npm exec -- cloving ... for a project-local installation.\n";
3
- export declare const chatsImportUsage = "Usage: cloving chats import [--] <path> [<path> ...]\n cloving chats import --root <projects-folder> [--root <projects-folder> ...] [--exclude-project <name> ...] [-- <path> ...]\n\nImports only the selected Claude Code JSONL files. Relative paths use the current directory.\nThis local command needs no service. It scans only explicitly named roots and does not expand globs or discover configuration.\nPositional files come first, retaining order and repetitions. Roots sort by canonical path, then entries by relative path in JavaScript string order.\nExclusions match direct project folder names exactly and prune their children. Exclusions require a root.\nScans accept lowercase .jsonl filenames, attempt at most 100 eligible files across roots, and stop before entering folders at depth four.\nLater file candidates and depth-limited folders are pending. Select pending files explicitly in a later import.\nLinked components, containment changes and roots in Git repositories are refused. Scan protections require the supported macOS kernel.\nThe file limit is 10 MiB (10,485,760 bytes). Links and nonregular files are refused.\nThe total normalized corpus limit is 50 MiB (52,428,800 UTF-8 bytes), including newlines and repeated selections.\nExceeding that total returns output-failed with exit code 1 and no completed coverage manifest; partial files may remain.\n--previous-import <IMPORT_ID> compares against one explicitly named UUID v4 import before --.\nThe earlier coverage and optional revision must be valid, completed private metadata; otherwise previous-import-refused returns exit 1 with no new import.\nA comparison writes private revision.json with ordered selection evidence and six status counts. Matching uses source path and selected root path; repetitions pair in order.\nByte-verified extensions are extended; shorter sources are truncated. Legacy imports without sizes can establish unchanged digests but cannot prove extension or truncation.\nMissing or truncated evidence makes the import partial. History completeness remains unknown.\nPrivate corpus.jsonl, structure.json and coverage.json files go into a new import directory under the runtime directory's chats directory.\nThe runtime selection is CLOVING_RUNTIME_DIR, XDG_RUNTIME_DIR, XDG_STATE_HOME, then HOME.\nOutput contains only counts, fixed codes and an opaque import ID. Source paths stay in the private manifest.\nStructure preserves branches and marks known copies without removing records. History completeness remains unknown.\nRoot imports write coverage version 2 and add excluded and pending entry counts; positional-only imports retain coverage version 1.\nExit codes: 0 for no refused, pending, missing or truncated entries (exclusions alone permit success without comparison), 1 for partial coverage or processing failure, 2 for invalid usage.\n";
3
+ export declare const usage = "Usage: cloving [--help] [--version]\n cloving workflows list [--json] [--repository <directory>]\n cloving workflows test [<id>] [--repository <directory>]\n cloving workflows validate <manifest-path> [--input <json-file>] [--json]\n cloving templates list [--json]\n cloving templates show <name> [--json]\n cloving templates copy <name> [--id <workflow-id>] [--json] [--repository <directory>]\n cloving <command> [--help]\n\nCommands:\n workflows list List bundled, project and user workflows and refusal codes.\n workflows test Run project and user workflow tests without a service.\n workflows validate Check a compiled workflow manifest, its artifacts and optional runtime input.\n templates list List the workflow templates of the package.\n templates show Show the decision points and the files of one template.\n templates copy Copy one template into .cloving/workflows of the repository.\n doctor Check repository requirements before starting or submitting work.\n run Submit one run of an admitted workflow.\n status List the runs or show one run.\n report Report loop facts and seven-day baselines.\n questions List the pending decisions.\n answer Answer one design question.\n approve Approve one pending approval.\n deny Deny one pending approval.\n pause Stop new dispatch for a run.\n resume Resume a paused run.\n drop Cancel a run and keep its files.\n events Print the committed events.\n watch Serve a page that shows the runs live.\n phone Show, list, remove and reset the connected phones.\n config Read and change repository settings.\n start Start the service for a repository.\n\nThe runtime commands need a Cloving service. They use the current git repository; --repository <directory> selects another repository. CLOVING_RUNTIME_DIR overrides its runtime directory.\nUse npm exec -- cloving ... for a project-local installation.\n";
4
4
  export declare const validateUsage = "Usage: cloving workflows validate <manifest-path> [--input <json-file>] [--json]\n\nChecks a Cloving workflow manifest, its compiled SDK artifacts, declared resources and toolchain, and an optional runtime input file. Relative paths resolve against the current directory.\n\nOptions:\n --input <json-file> Validate this runtime input file.\n --json Print one versioned JSON result on stdout.\n\nValidation does not admit a run, dispatch effects, import workflow code or call a model.\n\nExample:\n cloving workflows validate cloving.workflow.json --input input.json\n";
5
- export declare const chatsRedactUsage = "Usage: cloving chats redact <IMPORT_ID>\n\nRedact one existing import selected by its UUID v4 import ID. No service is required.\nWrite redacted.jsonl and redaction.json beside the original corpus with mode 0600.\nHOME supplies the home folder. CLOVING_PRIVATE_TEXTS is an optional JSON list of nonblank strings.\nOriginals and metadata remain private. Redaction does not authorize model use or guarantee removal of every private value.\nExit codes: 0 complete, 1 processing failure, 2 invalid usage.\n";
5
+ export declare const phoneUsage = "Usage: cloving phone status [--json]\n cloving phone list [--json]\n cloving phone remove <device-id> [--json]\n cloving phone reset [--json]\n\nShows and changes the phones that answer the questions of this computer.\nstatus shows the registration with cloving.ai and the number of phones.\nlist shows each phone with its device ID, name, platform, state and pairing time.\nremove marks one phone removed on this computer, then asks cloving.ai to remove it. A failure at cloving.ai keeps the phone removed and shows a warning.\nreset removes the registration with cloving.ai, marks all phones removed and deletes phone/link.json and phone/daemon-keys.json.\nConnect a phone with the Connect a phone button of the watch page.\ncloving start runs the phone relay as a child process. With CLOVING_PHONE_RELAY=off, the relay does not start, and status says that the relay is off or not started.\n";
@@ -1,10 +1,34 @@
1
1
  const binding = "--expected-revision <n> --generation <n> --subject-digest <digest>";
2
+ export const configUsage = `Usage: cloving config list [--json]
3
+ cloving config get <field> [--json]
4
+ cloving config set <field> <value> [--private] [--json]
5
+ cloving config unset <field> [--private] [--json]
6
+
7
+ Reads and changes settings for the current repository; --repository selects another repository.
8
+ Values use JSON when valid, otherwise text. Every change is checked before writing.
9
+ Fields use their default project or private file; --private selects the private file.
10
+ list and get show the effective source and hidden file values. Unconfigured values use defaults; null means no value.
11
+ Changes keep the previous file as .bak and replace the file atomically under a lock.
12
+ A live lock waits at most ten seconds, then exits with settings-locked.
13
+ Private files, backups and temporary files have mode 0600. Changes require a service restart.
14
+ `;
2
15
  export const commandUsage = {
16
+ doctor: `Usage: cloving doctor [--json] [--repository <directory>]
17
+
18
+ Checks repository requirements without repairs, model turns or GitHub writes.
19
+ Required failures or skipped prerequisites exit with status 1.
20
+ `,
21
+ report: `Usage: cloving report [--since <hours>] [--json]
22
+
23
+ Reads loop facts without writing state. Hours are a whole number from 1 to 168; default 24.
24
+ Reports run outcomes, throughput and ended-run costs with seven-day medians and MAD bands.
25
+ Unsupported facts and unreadable sources are unavailable.
26
+ `,
3
27
  run: `Usage: cloving run <workflow-id> (--prompt <text> | --ticket <id> | --next | --input <json-file>) [--command-id <id>] [--json]
4
28
 
5
29
  Submits one run of an admitted workflow. Use exactly one input source.
6
- With --ticket, the workflow gets the ticket ID and reads the ticket itself.
7
- With --next (the same as --ticket next), develop-issue and develop-ticket select a ready ticket through CLOVING_NEXT_COMMAND.
30
+ With --ticket, the daemon reads the ticket at admission. A declared ticket field receives { id, text } inside input.
31
+ With --next (the same as --ticket next), ticket workflows select a ready ticket through CLOVING_NEXT_COMMAND.
8
32
  Success means a committed receipt. It does not mean completed work.
9
33
  `,
10
34
  status: `Usage: cloving status [<run-id>] [--limit <n>] [--after <cursor>] [--json]
@@ -47,14 +71,14 @@ Prints the committed events after a sequence. With --json, each line is one even
47
71
  watch: `Usage: cloving watch [--port <n>]
48
72
 
49
73
  Serves a page on http://127.0.0.1:<port>/ that shows the runs live: the state,
50
- the events, the work of the Claude worker and each pending decision.
74
+ the events, the work of the worker and each pending decision.
51
75
  The default port is 3663. The page is read only. Stop it with Control-C.
52
76
  `,
53
77
  start: `Usage: cloving start [--repository <directory>] [--reset-queue]
54
78
 
55
79
  Starts the Cloving service for one git repository, with the workflow host,
56
- the Claude worker and the watch page. The default repository is the current
57
- directory. This command works in a development checkout of Cloving.
80
+ the worker and the watch page. The default repository is the current
81
+ directory.
58
82
  Stop it with Control-C.
59
83
 
60
84
  The queue continues after unsuccessful runs and stops after three runs in a row
@@ -62,23 +86,33 @@ without a successful handoff. A successful handoff resets the count; ordinary re
62
86
  Stop the service, inspect the failures and reconcile affected issues, then start with --reset-queue and the same queue settings to clear the failure streak once. Tracked commands are preserved.
63
87
 
64
88
  The command reads these settings from the environment:
65
- CLOVING_RUNTIME_DIR The path of the runtime directory. The default is ~/.local/state/cloving/run, if XDG_RUNTIME_DIR and XDG_STATE_HOME are not set.
89
+ Settings files: .cloving/config.json in the repository and <config>/cloving/projects/<name>-<id>.json for private settings. Both require version 1. Precedence: environment, private, project, defaults. <config> is an absolute XDG_CONFIG_HOME or ~/.config. The start guide shows each configured field's source. See the packaged project-config.schema.json and private-config.schema.json in dist/src.
90
+
91
+ CLOVING_RUNTIME_DIR The path of the runtime directory. The default is ~/.local/state/cloving/<name>-<id>/run, if XDG_RUNTIME_DIR and XDG_STATE_HOME are not set. A recorded legacy adoption takes priority.
66
92
  CLOVING_TICKET_COMMAND A JSON list of a command and its arguments. The command gets a ticket ID as its last argument and prints the ticket. The setting has no default, and a run with --ticket needs it.
67
93
  CLOVING_NOTIFY_COMMAND A JSON list of a command and its arguments. Every 15 seconds, new decisions, blocked runs and failed runs send JSON on standard input with no added argument. The setting has no default.
68
94
  CLOVING_NEXT_COMMAND A JSON list of a command and its arguments. The command runs with no added argument and prints one ready ticket ID for --next. It must exclude the space-separated ticket IDs in CLOVING_SKIP_TICKETS. The setting has no default.
69
95
  CLOVING_QUEUE_SECONDS A whole number from 10 to 3600 seconds between develop-issue queue checks; requires CLOVING_NEXT_COMMAND and CLOVING_TICKET_COMMAND. Unset means no queue. The queue stops after three runs in a row without a successful handoff; manual develop-issue submissions are refused with workflow-active when all lanes are busy.
70
96
  CLOVING_QUEUE_LANES A whole number from 1 to 4; the default is 1. Requires CLOVING_QUEUE_SECONDS. Active tickets are refused with ticket-active. Needs #N sentences in the ticket's ## Dependencies section refuse admission with ticket-dependency-open while issue #N is open.
97
+ CLOVING_WORKFLOW_FOLDER An absolute folder path; when set, it replaces the user workflow folder. The private setting workflows.folder sets it. The folder itself must not be a symbolic link.
98
+ CLOVING_SCHEDULES A JSON object mapping declared workflow IDs to {everySeconds, input}; replaces declaration schedules. Intervals are whole seconds from 60 to 86400. Unset keeps declaration schedules. Scheduled runs skip overlapping slots and retain their progress across restart.
71
99
  CLOVING_SELF_UPDATE_SECONDS A whole number from 30 to 3600 seconds between checks of the own checkout; unset means no update. An update stops the daemon and restarts with new code. A change of package-lock.json needs the operator to update and install by hand.
72
- CLOVING_ANALYSIS_TOKEN_COMMAND A JSON list of a program and its nonempty string arguments, run without a shell just before analysis. It has limits of 10 seconds and 4096 combined stdout/stderr bytes; stdout must be one printable ASCII token without spaces, at most 512 bytes after one final line break. Only analysis receives CLAUDE_CODE_OAUTH_TOKEN; API keys are excluded. There is no default.
100
+ CLOVING_GROK_COMMAND A JSON list of a command and its arguments for Grok. The default uses streaming-messages-json, --always-approve and -p; the turn text follows -p or --single as one argument, or is appended if neither flag is present.
73
101
  CLOVING_CLAUDE_COMMAND A JSON list of a command and its arguments for the Claude worker. The default is the claude command with a limit of 5 USD for each turn.
74
102
  CLOVING_PROFILES The path of a JSON file of operator-defined Claude turn profiles, read once at start. The setting has no default.
103
+ CLOVING_COMMANDS A JSON object mapping names to argument arrays with an absolute executable path. Invalid values refuse startup with invalid-commands. Unset means no commands. Command execution is not available yet.
104
+ CLOVING_COMMAND_SECONDS A whole number from 1 to 3600 seconds; the default is 60. Invalid values refuse startup with invalid-command-seconds. Reserved for command execution.
75
105
  CLOVING_TOKEN_PRICES A JSON object from model names to input, cachedInput and output USD prices per million tokens for estimates; entries override matching built-in prices.
76
- CLOVING_FALLBACK_MODELS A JSON array of at most three nonempty model names; the default is no fallback. After capacity refusals the first retry keeps the main model and the third attempt uses the first fallback, with the same waits and attempt limit. Requires an explicit -m, --model or --model= argument; does not change analyze-chats analysis. A fallback can have a higher price.
77
- CLOVING_WORKER The worker for each turn: claude or codex. The default is claude. Other values stop the start with invalid-worker.
106
+ CLOVING_FALLBACK_MODELS A JSON array of at most three nonempty model names; the default is no fallback. After capacity refusals the first retry keeps the main model and the third attempt uses the first fallback, with the same waits and attempt limit. Requires an explicit -m, --model or --model= argument. A fallback can have a higher price.
107
+ CLOVING_WORKER The worker for each turn: claude, codex, gemini, grok, cursor-agent or opencode. The default is claude. Other values stop the start with harness-no-worker.
78
108
  CLOVING_CODEX_COMMAND A JSON list of a command and its arguments, used only when CLOVING_WORKER is codex; CLOVING_CLAUDE_COMMAND is then ignored. The default is codex exec --json --dangerously-bypass-approvals-and-sandbox --skip-git-repo-check -, with no sandbox and no approval prompts.
109
+ CLOVING_GEMINI_COMMAND A JSON list of a command and its arguments for the gemini worker. The default is gemini --output-format stream-json --approval-mode yolo; Cloving appends -p and the turn text; turn text of 64 KiB or more in UTF-8 goes through standard input instead. No approval prompts or capacity retries.
110
+ CLOVING_OPENCODE_COMMAND A JSON list of a command and its arguments for opencode. The default is opencode run --format json; the turn text is appended as one argument. OPENCODE_PERMISSION permits unattended tools.
79
111
  CLOVING_WATCH_REPOSITORY The service repository context for the watch board, set by start from --repository. Its local origin supplies the GitHub identity. There is no current-directory default.
80
112
  CLOVING_WATCH_ACTIONS The value off removes the board run form and its write endpoint. The default is on.
81
113
  CLOVING_WATCH_PORT The port number of the watch page. The value off starts no watch page. The default is 3663.
114
+ CLOVING_PHONE_RELAY The phone relay child, which connects phones through cloving.ai. The value off starts no phone relay. The default is on.
115
+ CLOVING_RELAY_ORIGIN The origin of the phone relay server. Only https origins are accepted. The default is https://cloving.ai.
82
116
  CLOVING_OWNERSHIP_LEASE_SECONDS The time in seconds of the ownership lease of a started workflow step. The default is 15.
83
117
  CLOVING_HOST_BOUND The largest number of queue messages that the workflow host handles at one time, from 1 to 64. The default is 8.
84
118
  CLOVING_GH_COMMAND A JSON list of a command and its arguments for the GitHub CLI, which the workflow develop-issue uses for pull requests and develop-prd uses for issues. The watch board also uses it to read pull request status. The default is gh.
@@ -88,15 +122,25 @@ The command reads these settings from the environment:
88
122
  `,
89
123
  };
90
124
  export const usage = `Usage: cloving [--help] [--version]
125
+ cloving workflows list [--json] [--repository <directory>]
126
+ cloving workflows test [<id>] [--repository <directory>]
91
127
  cloving workflows validate <manifest-path> [--input <json-file>] [--json]
128
+ cloving templates list [--json]
129
+ cloving templates show <name> [--json]
130
+ cloving templates copy <name> [--id <workflow-id>] [--json] [--repository <directory>]
92
131
  cloving <command> [--help]
93
132
 
94
133
  Commands:
134
+ workflows list List bundled, project and user workflows and refusal codes.
135
+ workflows test Run project and user workflow tests without a service.
95
136
  workflows validate Check a compiled workflow manifest, its artifacts and optional runtime input.
96
- chats redact Redact one imported chat corpus into private runtime files.
97
- chats import Import explicitly selected Claude Code transcripts into private runtime files.
137
+ templates list List the workflow templates of the package.
138
+ templates show Show the decision points and the files of one template.
139
+ templates copy Copy one template into .cloving/workflows of the repository.
140
+ doctor Check repository requirements before starting or submitting work.
98
141
  run Submit one run of an admitted workflow.
99
142
  status List the runs or show one run.
143
+ report Report loop facts and seven-day baselines.
100
144
  questions List the pending decisions.
101
145
  answer Answer one design question.
102
146
  approve Approve one pending approval.
@@ -106,36 +150,13 @@ Commands:
106
150
  drop Cancel a run and keep its files.
107
151
  events Print the committed events.
108
152
  watch Serve a page that shows the runs live.
153
+ phone Show, list, remove and reset the connected phones.
154
+ config Read and change repository settings.
109
155
  start Start the service for a repository.
110
156
 
111
- The runtime commands need a Cloving service. CLOVING_RUNTIME_DIR names its runtime directory.
157
+ The runtime commands need a Cloving service. They use the current git repository; --repository <directory> selects another repository. CLOVING_RUNTIME_DIR overrides its runtime directory.
112
158
  Use npm exec -- cloving ... for a project-local installation.
113
159
  `;
114
- export const chatsImportUsage = `Usage: cloving chats import [--] <path> [<path> ...]
115
- cloving chats import --root <projects-folder> [--root <projects-folder> ...] [--exclude-project <name> ...] [-- <path> ...]
116
-
117
- Imports only the selected Claude Code JSONL files. Relative paths use the current directory.
118
- This local command needs no service. It scans only explicitly named roots and does not expand globs or discover configuration.
119
- Positional files come first, retaining order and repetitions. Roots sort by canonical path, then entries by relative path in JavaScript string order.
120
- Exclusions match direct project folder names exactly and prune their children. Exclusions require a root.
121
- Scans accept lowercase .jsonl filenames, attempt at most 100 eligible files across roots, and stop before entering folders at depth four.
122
- Later file candidates and depth-limited folders are pending. Select pending files explicitly in a later import.
123
- Linked components, containment changes and roots in Git repositories are refused. Scan protections require the supported macOS kernel.
124
- The file limit is 10 MiB (10,485,760 bytes). Links and nonregular files are refused.
125
- The total normalized corpus limit is 50 MiB (52,428,800 UTF-8 bytes), including newlines and repeated selections.
126
- Exceeding that total returns output-failed with exit code 1 and no completed coverage manifest; partial files may remain.
127
- --previous-import <IMPORT_ID> compares against one explicitly named UUID v4 import before --.
128
- The earlier coverage and optional revision must be valid, completed private metadata; otherwise previous-import-refused returns exit 1 with no new import.
129
- A comparison writes private revision.json with ordered selection evidence and six status counts. Matching uses source path and selected root path; repetitions pair in order.
130
- Byte-verified extensions are extended; shorter sources are truncated. Legacy imports without sizes can establish unchanged digests but cannot prove extension or truncation.
131
- Missing or truncated evidence makes the import partial. History completeness remains unknown.
132
- Private corpus.jsonl, structure.json and coverage.json files go into a new import directory under the runtime directory's chats directory.
133
- The runtime selection is CLOVING_RUNTIME_DIR, XDG_RUNTIME_DIR, XDG_STATE_HOME, then HOME.
134
- Output contains only counts, fixed codes and an opaque import ID. Source paths stay in the private manifest.
135
- Structure preserves branches and marks known copies without removing records. History completeness remains unknown.
136
- Root imports write coverage version 2 and add excluded and pending entry counts; positional-only imports retain coverage version 1.
137
- Exit codes: 0 for no refused, pending, missing or truncated entries (exclusions alone permit success without comparison), 1 for partial coverage or processing failure, 2 for invalid usage.
138
- `;
139
160
  export const validateUsage = `Usage: cloving workflows validate <manifest-path> [--input <json-file>] [--json]
140
161
 
141
162
  Checks a Cloving workflow manifest, its compiled SDK artifacts, declared resources and toolchain, and an optional runtime input file. Relative paths resolve against the current directory.
@@ -149,11 +170,16 @@ Validation does not admit a run, dispatch effects, import workflow code or call
149
170
  Example:
150
171
  cloving workflows validate cloving.workflow.json --input input.json
151
172
  `;
152
- export const chatsRedactUsage = `Usage: cloving chats redact <IMPORT_ID>
153
-
154
- Redact one existing import selected by its UUID v4 import ID. No service is required.
155
- Write redacted.jsonl and redaction.json beside the original corpus with mode 0600.
156
- HOME supplies the home folder. CLOVING_PRIVATE_TEXTS is an optional JSON list of nonblank strings.
157
- Originals and metadata remain private. Redaction does not authorize model use or guarantee removal of every private value.
158
- Exit codes: 0 complete, 1 processing failure, 2 invalid usage.
173
+ export const phoneUsage = `Usage: cloving phone status [--json]
174
+ cloving phone list [--json]
175
+ cloving phone remove <device-id> [--json]
176
+ cloving phone reset [--json]
177
+
178
+ Shows and changes the phones that answer the questions of this computer.
179
+ status shows the registration with cloving.ai and the number of phones.
180
+ list shows each phone with its device ID, name, platform, state and pairing time.
181
+ remove marks one phone removed on this computer, then asks cloving.ai to remove it. A failure at cloving.ai keeps the phone removed and shows a warning.
182
+ reset removes the registration with cloving.ai, marks all phones removed and deletes phone/link.json and phone/daemon-keys.json.
183
+ Connect a phone with the Connect a phone button of the watch page.
184
+ cloving start runs the phone relay as a child process. With CLOVING_PHONE_RELAY=off, the relay does not start, and status says that the relay is off or not started.
159
185
  `;
@@ -7,4 +7,6 @@ export interface WatchActions {
7
7
  }
8
8
  export declare function watchActions(runtimeDirectory: string): Promise<WatchActions>;
9
9
  export declare function watchHost(request: IncomingMessage): boolean;
10
+ export declare function actionAllowed(actions: WatchActions, request: IncomingMessage): boolean;
11
+ export declare function actionBody(request: IncomingMessage): Promise<unknown>;
10
12
  export declare function watchAction(actions: WatchActions, request: IncomingMessage): Promise<string | null>;
@@ -14,13 +14,13 @@ export function watchHost(request) {
14
14
  const port = request.socket.localPort;
15
15
  return [`127.0.0.1:${String(port)}`, `localhost:${String(port)}`].includes(request.headers.host ?? "");
16
16
  }
17
- function authorized(actions, request) {
17
+ export function actionAllowed(actions, request) {
18
18
  return (watchHost(request) &&
19
19
  request.headers.origin === `http://${request.headers.host ?? ""}` &&
20
20
  request.headers["x-cloving-token"] === actions.token &&
21
21
  /^application\/json(?:\s*;\s*charset=utf-8)?$/i.test(request.headers["content-type"] ?? ""));
22
22
  }
23
- async function bodyOf(request) {
23
+ export async function actionBody(request) {
24
24
  const chunks = [];
25
25
  let size = 0;
26
26
  for await (const chunk of request) {
@@ -39,9 +39,9 @@ async function bodyOf(request) {
39
39
  }
40
40
  }
41
41
  export async function watchAction(actions, request) {
42
- if (!authorized(actions, request))
42
+ if (!actionAllowed(actions, request))
43
43
  return null;
44
- const given = cardRunRequest(actions.forms, await bodyOf(request));
44
+ const given = cardRunRequest(actions.forms, await actionBody(request));
45
45
  if (given === null)
46
46
  return null;
47
47
  const status = await send(actions.runtimeDirectory, {
@@ -34,7 +34,7 @@ export declare function Card(props: {
34
34
  readonly observationNow?: string | undefined;
35
35
  }): Shown;
36
36
  export declare function HandoffLinks(props: {
37
- readonly run: Pick<WatchCard, "parentRunId" | "childRunIds">;
37
+ readonly run: Pick<WatchCard, "parentRunId" | "childRunIds" | "awaitedRunId">;
38
38
  }): Shown;
39
39
  export declare function WorkflowList(props: {
40
40
  readonly workflows: readonly WatchWorkflow[];
@@ -136,8 +136,11 @@ export function Card(props) {
136
136
  });
137
137
  }
138
138
  export function HandoffLinks(props) {
139
- const { parentRunId, childRunIds } = props.run;
139
+ const { parentRunId, childRunIds, awaitedRunId } = props.run;
140
140
  const links = [
141
+ ...(awaitedRunId == null
142
+ ? []
143
+ : [{ label: "Waiting for run", runId: awaitedRunId }]),
141
144
  ...(parentRunId == null
142
145
  ? []
143
146
  : [{ label: "Parent run", runId: parentRunId }]),
@@ -224,7 +227,10 @@ export function Board(props) {
224
227
  return element("main", {
225
228
  class: "board",
226
229
  children: [
227
- element("h2", { children: workflow.workflowId }),
230
+ element("h2", {
231
+ class: "visually-hidden",
232
+ children: workflow.workflowId,
233
+ }),
228
234
  element("div", {
229
235
  class: "columns",
230
236
  children: workflow.columns.map((column, index) => component(Column, {
@@ -87,15 +87,21 @@ function startedAt(event) {
87
87
  function chronological(events) {
88
88
  return events.toSorted((first, second) => Date.parse(startedAt(first) ?? "") - Date.parse(startedAt(second) ?? ""));
89
89
  }
90
+ function declaredStage(event, stages) {
91
+ if (record(event)?.["type"] === "effect.requested") {
92
+ const operation = operationOf(event)[4];
93
+ return stages.find((stage) => stage.operations?.some((name) => name === operation));
94
+ }
95
+ const payload = record(record(event)?.["payload"]);
96
+ return (stages.find((stage) => stage.gateIds?.some((name) => name === payload?.["gateId"])) ??
97
+ stages.find((stage) => stage.gateKinds?.some((name) => name === payload?.["gateKind"])));
98
+ }
90
99
  function declaredStep(events, stages) {
91
100
  const event = events
92
101
  .filter((item) => ["effect.requested", "gate.opened"].includes(String(record(item)?.["type"])))
93
102
  .toSorted((first, second) => Number(record(second)?.["sequence"]) -
94
103
  Number(record(first)?.["sequence"]))[0];
95
- const payload = record(record(event)?.["payload"]);
96
- const selector = record(event)?.["type"] === "effect.requested" ? "operations" : "gateKinds";
97
- const value = selector === "operations" ? operationOf(event)[4] : payload?.["gateKind"];
98
- const stage = stages.find((item) => item[selector]?.some((name) => name === value));
104
+ const stage = declaredStage(event, stages);
99
105
  return { name: stage?.label ?? "Other", startedAt: startedAt(event) };
100
106
  }
101
107
  export function currentStepOf(events, workflow) {
@@ -123,12 +129,20 @@ function columnOf(run, now) {
123
129
  return "Stopped";
124
130
  return run.outcome === "handoff_requested" ? "Done" : null;
125
131
  }
126
- function cardOf(run, runs) {
132
+ function issueCardDetails(run) {
127
133
  const issue = run.workflowId === "develop-issue";
134
+ return {
135
+ round: issue ? run.round : null,
136
+ greptileScore: issue ? (run.reviews.at(-1)?.score ?? null) : null,
137
+ pullRequestNumber: issue ? (run.pullRequest?.number ?? null) : null,
138
+ };
139
+ }
140
+ function cardOf(run, runs) {
128
141
  return {
129
142
  runId: run.runId,
130
143
  title: run.title,
131
144
  ...linksOf(run),
145
+ ...(run.awaitedRunId == null ? {} : { awaitedRunId: run.awaitedRunId }),
132
146
  completionMark: completionMarkOf(run, runs),
133
147
  pullRequestObservation: null,
134
148
  claudeCostUsd: run.claudeCostUsd,
@@ -137,9 +151,7 @@ function cardOf(run, runs) {
137
151
  currentStep: activeRun(run.state) ? run.currentStep : null,
138
152
  endedAt: activeRun(run.state) ? null : run.latestAt,
139
153
  decisionWaiting: run.decisions.length > 0,
140
- round: issue ? run.round : null,
141
- greptileScore: issue ? (run.reviews.at(-1)?.score ?? null) : null,
142
- pullRequestNumber: issue ? (run.pullRequest?.number ?? null) : null,
154
+ ...issueCardDetails(run),
143
155
  };
144
156
  }
145
157
  function stepOrder(first, second) {
@@ -161,7 +173,8 @@ function summaryOf(value) {
161
173
  const latest = fields?.["latestAt"];
162
174
  return {
163
175
  state: String(fields?.["state"]),
164
- waiting: Array.isArray(pending) && pending.length > 0,
176
+ waiting: (Array.isArray(pending) && pending.length > 0) ||
177
+ typeof fields?.["awaitedRunId"] === "string",
165
178
  latestAt: typeof latest === "string" ? latest : null,
166
179
  };
167
180
  }
@@ -171,7 +184,7 @@ export function boardsOf(inputs, runs) {
171
184
  const summaries = inputs.summaries === undefined
172
185
  ? own.map((run) => ({
173
186
  state: run.state,
174
- waiting: run.decisions.length > 0,
187
+ waiting: run.decisions.length > 0 || run.awaitedRunId != null,
175
188
  latestAt: run.latestAt,
176
189
  }))
177
190
  : inputs.summaries
@@ -14,7 +14,7 @@ export async function watchCardForms(directory) {
14
14
  if (!isWorkflowDeclaration(value))
15
15
  continue;
16
16
  const entries = Object.entries(value.input.fields);
17
- if (!entries.every(([, rule]) => rule === "string" || rule.type === "string"))
17
+ if (!entries.every(([, rule]) => rule === "string" || rule.type === "string" || rule.type === "ticket"))
18
18
  continue;
19
19
  if (forms.some((form) => form.workflowId === value.workflowId))
20
20
  continue;
@@ -23,7 +23,7 @@ export async function watchCardForms(directory) {
23
23
  source: "input",
24
24
  fields: entries.map(([name, rule]) => ({
25
25
  name,
26
- label: name,
26
+ label: rule !== "string" && rule.type === "ticket" ? "Ticket ID" : name,
27
27
  multiline: false,
28
28
  optional: rule !== "string" && rule.optional === true,
29
29
  })),
@@ -1 +1 @@
1
- export declare const watchClient = "\nconst state = { model: null, receivedAt: 0, stale: false, copied: \"\", label: \"\", run: \"\", loading: false, again: false, columns: {}, cards: {}, start: null, hovered: null, focused: null };\nfunction readPreference(key) {\n try { return JSON.parse(localStorage.getItem(key)); }\n catch { return null; }\n}\nfunction readColumnChoices(key) {\n const value = readPreference(key);\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) return null;\n return Object.fromEntries(Object.entries(value).filter(([, choice]) => typeof choice === \"boolean\"));\n}\nfunction savePreference(key, value) {\n try { localStorage.setItem(key, JSON.stringify(value)); }\n catch {}\n}\nfunction startControls() {\n if (state.start === null) state.start = readPreference(\"cloving.watch.start\") === true;\n return { shown: state.start, onToggle() {\n state.start = !state.start;\n savePreference(\"cloving.watch.start\", state.start);\n draw();\n } };\n}\nfunction columnControls(model, selected) {\n const workflowId = model === null ? null : boardWorkflow(model, watchLocation(selected).workflowId);\n if (workflowId === null) return undefined;\n const key = \"cloving.watch.columns:\" + workflowId;\n if (!Object.hasOwn(state.columns, workflowId))\n state.columns = { ...state.columns, [workflowId]: readColumnChoices(key) };\n const choices = state.columns[workflowId];\n return { choices, onHover(name) { state.hovered = name; }, onFocus(name) { state.focused = name; }, onToggle(name) {\n const changed = { ...choices, [name]: !columnCollapsed(choices, name) };\n state.columns = { ...state.columns, [workflowId]: changed };\n savePreference(key, changed);\n draw();\n } };\n}\nfunction moved(next, followed) {\n const arrived = next === \"waiting_gate\" && next !== state.run;\n state.run = next;\n if (arrived) scrollTo(0, 0);\n else if (followed && next === \"running\") scrollTo(0, document.body.scrollHeight);\n}\nfunction copy(command) {\n const shown = (label) => {\n state.copied = command;\n state.label = label;\n draw();\n };\n navigator.clipboard.writeText(command).then(\n () => { shown(\"Copied\"); },\n () => { shown(\"Copy failed\"); },\n );\n}\nfunction draw() {\n const { model, stale } = state;\n const selected = location.hash.slice(1);\n const followed = innerHeight + scrollY >= document.body.scrollHeight - 80;\n const copying = { command: state.copied, label: state.label, onCopy: copy };\n const now = new Date().toISOString();\n const observationNow = model === null ? now : new Date(Date.parse(model.updatedAt) + performance.now() - state.receivedAt).toISOString();\n const columns = columnControls(model, selected);\n const start = model === null ? undefined : startControls();\n const cards = cardControls(model, model === null ? null : boardWorkflow(model, watchLocation(selected).workflowId));\n const page = component(Page, { model, selected, now, observationNow, stale, copying, columns, start, cards });\n render(page, document.getElementById(\"app\"));\n if (model === null) return;\n document.title = watchTitle(model, selected);\n const { runId } = watchLocation(selected);\n moved(model.runs.find((run) => run.runId === runId)?.state ?? \"\", followed);\n}\nasync function withSelectedRun(model) {\n const { runId } = watchLocation(location.hash);\n if (runId === null || model.runs.some((run) => run.runId === runId)) return model;\n const detail = await fetch(\"data.json?runId=\" + encodeURIComponent(runId), { cache: \"no-store\" });\n if (detail.status === 404) return model;\n if (!detail.ok) throw new Error(\"watch-run-not-available\");\n const { run } = await detail.json();\n if (run?.runId !== runId) throw new Error(\"watch-run-mismatch\");\n return { ...model, runs: [...model.runs, run] };\n}\nasync function load() {\n state.again = state.loading;\n if (state.loading) return;\n state.loading = true;\n try {\n const reply = await fetch(\"data.json\", { cache: \"no-store\" });\n if (!reply.ok) throw new Error(\"watch-not-available\");\n const model = await reply.json();\n const receivedAt = performance.now();\n state.model = await withSelectedRun(model);\n state.receivedAt = receivedAt;\n state.stale = false;\n } catch {\n state.stale = true;\n }\n state.loading = false;\n draw();\n if (state.again) load();\n}\nfunction listen() {\n const source = new EventSource(\"events\");\n source.onmessage = () => { load(); };\n source.onerror = () => { state.stale = true; draw(); };\n}\naddEventListener(\"keydown\", (event) => {\n if (event.key !== String.fromCharCode(92) || event.defaultPrevented || event.repeat || event.isComposing) return;\n if (event.ctrlKey || event.altKey || event.metaKey || event.shiftKey) return;\n if (event.target?.closest?.(\"input, textarea, select, [contenteditable]\")) return;\n const target = state.hovered ?? state.focused;\n if (target === null || watchLocation(location.hash).runId !== null) return;\n const controls = columnControls(state.model, location.hash);\n if (controls === undefined) return;\n event.preventDefault();\n controls.onToggle(target);\n});\naddEventListener(\"hashchange\", () => { state.run = \"\"; state.hovered = null; state.focused = null; draw(); load(); });\ndraw();\nlisten();\nsetInterval(draw, 1000);\nsetInterval(load, 15000);\n";
1
+ export declare const watchClient = "\nconst state = { model: null, receivedAt: 0, stale: false, copied: \"\", label: \"\", run: \"\", loading: false, again: false, columns: {}, cards: {}, start: null, hovered: null, focused: null, phonePairing: null, phoneBusy: false, phoneMessage: \"\", phone: null, phoneLoadedAt: 0, phoneAccepted: null, phoneSequence: 0, phoneApplied: 0 };\nfunction readPreference(key) {\n try { return JSON.parse(localStorage.getItem(key)); }\n catch { return null; }\n}\nfunction readColumnChoices(key) {\n const value = readPreference(key);\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) return null;\n return Object.fromEntries(Object.entries(value).filter(([, choice]) => typeof choice === \"boolean\"));\n}\nfunction savePreference(key, value) {\n try { localStorage.setItem(key, JSON.stringify(value)); }\n catch {}\n}\nfunction startControls() {\n if (state.start === null) state.start = readPreference(\"cloving.watch.start\") === true;\n return { shown: state.start, onToggle() {\n state.start = !state.start;\n savePreference(\"cloving.watch.start\", state.start);\n draw();\n } };\n}\nfunction columnControls(model, selected) {\n const workflowId = model === null ? null : boardWorkflow(model, watchLocation(selected).workflowId);\n if (workflowId === null) return undefined;\n const key = \"cloving.watch.columns:\" + workflowId;\n if (!Object.hasOwn(state.columns, workflowId))\n state.columns = { ...state.columns, [workflowId]: readColumnChoices(key) };\n const choices = state.columns[workflowId];\n return { choices, onHover(name) { state.hovered = name; }, onFocus(name) { state.focused = name; }, onToggle(name) {\n const changed = { ...choices, [name]: !columnCollapsed(choices, name) };\n state.columns = { ...state.columns, [workflowId]: changed };\n savePreference(key, changed);\n draw();\n } };\n}\nfunction moved(next, followed) {\n const arrived = next === \"waiting_gate\" && next !== state.run;\n state.run = next;\n if (arrived) scrollTo(0, 0);\n else if (followed && next === \"running\") scrollTo(0, document.body.scrollHeight);\n}\nfunction copy(command) {\n const shown = (label) => {\n state.copied = command;\n state.label = label;\n draw();\n };\n navigator.clipboard.writeText(command).then(\n () => { shown(\"Copied\"); },\n () => { shown(\"Copy failed\"); },\n );\n}\nfunction draw() {\n const { model, stale } = state;\n const selected = location.hash.slice(1);\n const followed = innerHeight + scrollY >= document.body.scrollHeight - 80;\n const copying = { command: state.copied, label: state.label, onCopy: copy };\n const now = new Date().toISOString();\n const observationNow = model === null ? now : new Date(Date.parse(model.updatedAt) + performance.now() - state.receivedAt).toISOString();\n const columns = columnControls(model, selected);\n const start = model === null ? undefined : startControls();\n const cards = cardControls(model, model === null ? null : boardWorkflow(model, watchLocation(selected).workflowId));\n const phone = model === null ? undefined : phoneControls(now);\n const page = component(Page, { model, selected, now, observationNow, stale, copying, columns, start, cards, phone });\n render(page, document.getElementById(\"app\"));\n if (model === null) return;\n document.title = watchTitle(model, selected);\n const { runId } = watchLocation(selected);\n moved(model.runs.find((run) => run.runId === runId)?.state ?? \"\", followed);\n}\nasync function withSelectedRun(model) {\n const { runId } = watchLocation(location.hash);\n if (runId === null || model.runs.some((run) => run.runId === runId)) return model;\n const detail = await fetch(\"data.json?runId=\" + encodeURIComponent(runId), { cache: \"no-store\" });\n if (detail.status === 404) return model;\n if (!detail.ok) throw new Error(\"watch-run-not-available\");\n const { run } = await detail.json();\n if (run?.runId !== runId) throw new Error(\"watch-run-mismatch\");\n return { ...model, runs: [...model.runs, run] };\n}\nasync function load() {\n state.again = state.loading;\n if (state.loading) return;\n state.loading = true;\n try {\n const reply = await fetch(\"data.json\", { cache: \"no-store\" });\n if (!reply.ok) throw new Error(\"watch-not-available\");\n const model = await reply.json();\n const receivedAt = performance.now();\n state.model = await withSelectedRun(model);\n state.receivedAt = receivedAt;\n state.stale = false;\n } catch {\n state.stale = true;\n }\n state.loading = false;\n draw();\n if (state.again) load();\n}\nfunction listen() {\n const source = new EventSource(\"events\");\n source.onmessage = () => { load(); };\n source.onerror = () => { state.stale = true; draw(); };\n}\naddEventListener(\"keydown\", (event) => {\n if (event.key !== String.fromCharCode(92) || event.defaultPrevented || event.repeat || event.isComposing) return;\n if (event.ctrlKey || event.altKey || event.metaKey || event.shiftKey) return;\n if (event.target?.closest?.(\"input, textarea, select, [contenteditable]\")) return;\n const target = state.hovered ?? state.focused;\n if (target === null || watchLocation(location.hash).runId !== null) return;\n const controls = columnControls(state.model, location.hash);\n if (controls === undefined) return;\n event.preventDefault();\n controls.onToggle(target);\n});\naddEventListener(\"hashchange\", () => { state.run = \"\"; state.hovered = null; state.focused = null; draw(); load(); });\ndraw();\nlisten();\nsetInterval(draw, 1000);\nsetInterval(load, 15000);\nif (actionToken !== null) {\n phoneLoad();\n setInterval(phoneLoad, 2000);\n}\n";
@@ -1,5 +1,5 @@
1
1
  export const watchClient = `
2
- const state = { model: null, receivedAt: 0, stale: false, copied: "", label: "", run: "", loading: false, again: false, columns: {}, cards: {}, start: null, hovered: null, focused: null };
2
+ const state = { model: null, receivedAt: 0, stale: false, copied: "", label: "", run: "", loading: false, again: false, columns: {}, cards: {}, start: null, hovered: null, focused: null, phonePairing: null, phoneBusy: false, phoneMessage: "", phone: null, phoneLoadedAt: 0, phoneAccepted: null, phoneSequence: 0, phoneApplied: 0 };
3
3
  function readPreference(key) {
4
4
  try { return JSON.parse(localStorage.getItem(key)); }
5
5
  catch { return null; }
@@ -62,7 +62,8 @@ function draw() {
62
62
  const columns = columnControls(model, selected);
63
63
  const start = model === null ? undefined : startControls();
64
64
  const cards = cardControls(model, model === null ? null : boardWorkflow(model, watchLocation(selected).workflowId));
65
- const page = component(Page, { model, selected, now, observationNow, stale, copying, columns, start, cards });
65
+ const phone = model === null ? undefined : phoneControls(now);
66
+ const page = component(Page, { model, selected, now, observationNow, stale, copying, columns, start, cards, phone });
66
67
  render(page, document.getElementById("app"));
67
68
  if (model === null) return;
68
69
  document.title = watchTitle(model, selected);
@@ -119,4 +120,8 @@ draw();
119
120
  listen();
120
121
  setInterval(draw, 1000);
121
122
  setInterval(load, 15000);
123
+ if (actionToken !== null) {
124
+ phoneLoad();
125
+ setInterval(phoneLoad, 2000);
126
+ }
122
127
  `;
@@ -0,0 +1,27 @@
1
+ import { type PhoneControls } from "./watch-phone-view.ts";
2
+ import { type Shown } from "./watch-element.ts";
3
+ import { type WatchModel } from "./watch-render.ts";
4
+ export interface StartControls {
5
+ readonly shown: boolean;
6
+ readonly onToggle: () => void;
7
+ }
8
+ export interface HeaderProps {
9
+ readonly model: WatchModel | null;
10
+ readonly stale: boolean;
11
+ readonly start?: StartControls;
12
+ readonly phone?: PhoneControls | undefined;
13
+ readonly now?: string;
14
+ }
15
+ export declare const clove: {
16
+ viewBox: string;
17
+ paths: string[];
18
+ };
19
+ export declare function queueLanes(model: WatchModel): string | null;
20
+ export declare function skippedText(value: unknown): string;
21
+ export declare function Queue(props: {
22
+ readonly model: WatchModel;
23
+ }): Shown;
24
+ export declare function Brand(): Shown;
25
+ export declare function HeaderStatus(props: HeaderProps): Shown;
26
+ export declare function HeaderActions(props: HeaderProps): Shown;
27
+ export declare function Header(props: HeaderProps): Shown;