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,77 +1,74 @@
1
+ import { turnHarness, harnessDefaults, nativeHarness, } from "../src/harness-defaults.js";
2
+ import { absoluteWorkflowFolder } from "../src/project-settings.js";
3
+ import { projectConfiguration, projectRoot, topLevel, } from "./project-configuration.js";
1
4
  import { isProfilesFile } from "../src/turn-profile.js";
5
+ import { parseCommands, parseCommandSeconds, } from "../src/command-settings.js";
2
6
  import { parseFallbackModels, parseTokenPrices, } from "../src/token-prices.js";
3
- import { execFileSync } from "node:child_process";
4
- import { readFile, realpath, writeFile } from "node:fs/promises";
7
+ import { readFile, writeFile } from "node:fs/promises";
5
8
  import { homedir } from "node:os";
6
9
  import { dirname, extname, join, resolve } from "node:path";
7
10
  import { pathToFileURL } from "node:url";
8
11
  import { preparedProject, } from "./self-host-project.js";
9
12
  import { selfUpdateMilliseconds } from "./self-update-host.js";
10
13
  import { hostedPrograms } from "./self-host-processes.js";
14
+ import { workflowBundleLoader } from "./self-host-workflows.js";
15
+ import { adoptRuntimeDirectory } from "./self-host-runtime.js";
16
+ import { parseSchedulesSetting } from "../src/workflow-schedule.js";
11
17
  const product = resolve(import.meta.dirname, "..");
12
18
  const flags = process.argv.slice(2);
13
19
  function flag(name) {
14
- const index = flags.lastIndexOf(name);
15
- return index === -1 ? undefined : flags[index + 1];
16
- }
17
- function topLevel(given) {
18
- try {
19
- return execFileSync("git", ["-C", given, "rev-parse", "--show-toplevel"], {
20
- encoding: "utf8",
21
- stdio: ["ignore", "pipe", "ignore"],
22
- }).trim();
23
- }
24
- catch {
25
- return given;
26
- }
20
+ const index = flags.findLastIndex((argument) => argument === name || argument.startsWith(`${name}=`));
21
+ const argument = flags[index];
22
+ return argument === name
23
+ ? flags[index + 1]
24
+ : argument?.slice(name.length + 1);
27
25
  }
28
26
  const runtimeArgument = flag("--runtime");
29
27
  if (runtimeArgument === undefined)
30
28
  throw new Error("The start program needs --runtime. Use: cloving start");
31
- const runtime = resolve(runtimeArgument);
29
+ const repository = await projectRoot(flag("--repository") ?? process.cwd());
30
+ const configuration = await projectConfiguration(repository, process.env);
31
+ const environment = configuration.environment;
32
+ const selectedRuntime = await adoptRuntimeDirectory({
33
+ runtime: resolve(runtimeArgument),
34
+ repository,
35
+ legacyRuntime: flag("--legacy-runtime"),
36
+ adoptionRecord: flag("--adoption-record"),
37
+ });
38
+ const runtime = selectedRuntime.runtime;
32
39
  const directory = dirname(runtime);
33
- const repository = await realpath(topLevel(resolve(flag("--repository") ?? process.cwd())));
34
40
  const updateMilliseconds = await selfUpdateMilliseconds({
35
- seconds: process.env["CLOVING_SELF_UPDATE_SECONDS"],
41
+ seconds: environment["CLOVING_SELF_UPDATE_SECONDS"],
36
42
  repository,
37
43
  product: extname(import.meta.filename) === ".ts" ? topLevel(product) : product,
38
44
  });
39
45
  const client = flag("--client") ?? "cloving";
40
46
  const project = join(directory, "project");
41
- const concurrency = Number(process.env["CLOVING_HOST_BOUND"] ?? "8");
42
- const leaseText = process.env["CLOVING_OWNERSHIP_LEASE_SECONDS"];
47
+ const concurrency = Number(environment["CLOVING_HOST_BOUND"] ?? "8");
48
+ const leaseText = environment["CLOVING_OWNERSHIP_LEASE_SECONDS"];
43
49
  const leaseSeconds = leaseText === undefined ? undefined : Number(leaseText);
44
- const defaultCommand = [
45
- "claude",
46
- "-p",
47
- "--output-format",
48
- "stream-json",
49
- "--verbose",
50
- "--permission-mode",
51
- "acceptEdits",
52
- "--allowedTools",
53
- "Bash,Read,Edit,Write,Glob,Grep,WebSearch,WebFetch",
54
- "--max-budget-usd",
55
- "5",
56
- ];
57
- const defaultCodexCommand = [
58
- "codex",
59
- "exec",
60
- "--json",
61
- "--dangerously-bypass-approvals-and-sandbox",
62
- "--skip-git-repo-check",
63
- "-",
64
- ];
65
50
  function selectedWorker() {
66
- const worker = process.env["CLOVING_WORKER"] ?? "claude";
67
- if (worker === "claude" || worker === "codex")
68
- return worker;
69
- throw new Error(`The daemon did not start: invalid-worker: ${worker}`);
51
+ return checkedWorker(environment["CLOVING_WORKER"] ?? "claude");
52
+ }
53
+ function checkedWorker(name) {
54
+ try {
55
+ return turnHarness({
56
+ worker: name,
57
+ ...(configuration.harness === undefined
58
+ ? {}
59
+ : { harness: configuration.harness }),
60
+ }).worker;
61
+ }
62
+ catch (error) {
63
+ throw new Error(`The daemon did not start: harness-no-worker: ${name}`, {
64
+ cause: error,
65
+ });
66
+ }
70
67
  }
71
68
  async function profilesFile() {
72
- const path = process.env["CLOVING_PROFILES"];
69
+ const path = environment["CLOVING_PROFILES"];
73
70
  if (path === undefined)
74
- return {};
71
+ return configuration.profiles;
75
72
  try {
76
73
  const value = JSON.parse(await readFile(path, "utf8"));
77
74
  if (isProfilesFile(value))
@@ -83,17 +80,17 @@ async function profilesFile() {
83
80
  throw new Error("The daemon did not start: invalid-profiles");
84
81
  }
85
82
  function queueLanes() {
86
- const value = process.env["CLOVING_QUEUE_LANES"];
83
+ const value = environment["CLOVING_QUEUE_LANES"];
87
84
  if (value === undefined)
88
85
  return 1;
89
86
  if (!/^[1-4]$/.test(value))
90
87
  throw new Error("The daemon did not start: invalid-queue-lanes");
91
- if (process.env["CLOVING_QUEUE_SECONDS"] === undefined)
88
+ if (environment["CLOVING_QUEUE_SECONDS"] === undefined)
92
89
  throw new Error("The daemon did not start: queue-lanes-needs-queue");
93
90
  return Number(value);
94
91
  }
95
92
  function queueInterval(commands) {
96
- const value = process.env["CLOVING_QUEUE_SECONDS"];
93
+ const value = environment["CLOVING_QUEUE_SECONDS"];
97
94
  if (value === undefined)
98
95
  return undefined;
99
96
  const seconds = Number(value);
@@ -109,7 +106,7 @@ function queueInterval(commands) {
109
106
  return seconds * 1000;
110
107
  }
111
108
  function listed(name) {
112
- const given = process.env[name];
109
+ const given = environment[name];
113
110
  if (given === undefined)
114
111
  return undefined;
115
112
  const list = JSON.parse(given);
@@ -135,22 +132,27 @@ await import("./main.mjs");
135
132
  await writeFile(join(project, "main.mjs"), `import * as workflows from "./artifacts/client.mjs";
136
133
  import { POST } from "./artifacts/flow.mjs";
137
134
  import { startDaemon } from ${source("src/daemon/daemon.ts")};
138
- const opening = await startDaemon({ ...JSON.parse(${JSON.stringify(JSON.stringify(settings))}), workflows, handler: POST });
135
+ ${workflowBundleLoader()}
136
+ const opening = await startDaemon({ ...JSON.parse(${JSON.stringify(JSON.stringify(settings))}), workflows, handler: POST, loadBundle: loadWorkflowBundle });
139
137
  if (opening.kind !== "started") {
140
138
  process.stderr.write("The daemon did not start: " + opening.reason + ": " + opening.detail + "\\n");
141
139
  process.exit(1);
142
140
  }
143
- process.stdout.write(${JSON.stringify(guide())});
141
+ process.stdout.write(${JSON.stringify(guide(workflows))});
144
142
  const stop = async () => { await opening.daemon.close(); process.exit(0); };
145
143
  process.once("SIGINT", stop);
146
144
  process.once("SIGTERM", stop);
147
145
  `);
148
146
  }
149
- function guide() {
147
+ function guide(workflows) {
150
148
  return [
151
149
  "Cloving runs. Stop it with Control-C.",
152
150
  `Repository: ${repository}`,
151
+ `Runtime directory: ${runtime}`,
153
152
  `Work trees: ${join(directory, "worktrees")}`,
153
+ ...(selectedRuntime.note === "" ? [] : [selectedRuntime.note]),
154
+ ...Object.entries(configuration.sources).map(([field, source]) => `${field}: ${source}`),
155
+ ...workflows.workflowRefusals.map((refusal) => `Refused workflow ${refusal.scope} ${refusal.label}: ${refusal.codes.join(", ")}`),
154
156
  "Commands for a second terminal:",
155
157
  ` ${client} run develop-ticket --ticket <ticket-id>`,
156
158
  ` ${client} run develop-issue --ticket <ticket-id>`,
@@ -162,13 +164,27 @@ function guide() {
162
164
  "",
163
165
  ].join("\n");
164
166
  }
165
- const tokenPrices = parseTokenPrices(process.env["CLOVING_TOKEN_PRICES"]);
166
- const fallbackModels = parseFallbackModels(process.env["CLOVING_FALLBACK_MODELS"]);
167
+ const tokenPrices = parseTokenPrices(environment["CLOVING_TOKEN_PRICES"]);
168
+ const commands = parseCommands(environment["CLOVING_COMMANDS"]);
169
+ const commandSeconds = parseCommandSeconds(environment["CLOVING_COMMAND_SECONDS"]);
170
+ const fallbackModels = parseFallbackModels(environment["CLOVING_FALLBACK_MODELS"]);
167
171
  const profiles = await profilesFile();
172
+ const schedules = parseSchedulesSetting(environment["CLOVING_SCHEDULES"]);
168
173
  const worker = selectedWorker();
169
- const command = worker === "codex"
170
- ? (listed("CLOVING_CODEX_COMMAND") ?? defaultCodexCommand)
171
- : (listed("CLOVING_CLAUDE_COMMAND") ?? defaultCommand);
174
+ function nativeCommands() {
175
+ const names = new Set([
176
+ worker,
177
+ ...Object.values(profiles).map((profile) => checkedWorker(profile.harness ?? worker)),
178
+ ]);
179
+ return Object.fromEntries([...names].filter(nativeHarness).flatMap((name) => {
180
+ const variable = harnessDefaults(name).variable;
181
+ const command = variable === undefined ? undefined : listed(variable);
182
+ return command === undefined ? [] : [[name, command]];
183
+ }));
184
+ }
185
+ const workerCommands = nativeCommands();
186
+ const harness = configuration.harness ?? {};
187
+ const command = turnHarness({ worker, workerCommands, harness }).command;
172
188
  const nextCommand = listed("CLOVING_NEXT_COMMAND");
173
189
  const ticketCommand = listed("CLOVING_TICKET_COMMAND");
174
190
  const lanes = queueLanes();
@@ -178,12 +194,19 @@ const manifests = await preparedProject({
178
194
  project,
179
195
  repository,
180
196
  home: homedir(),
181
- xdgConfigHome: process.env["XDG_CONFIG_HOME"],
197
+ xdgConfigHome: environment["XDG_CONFIG_HOME"],
198
+ userFolder: absoluteWorkflowFolder(environment["CLOVING_WORKFLOW_FOLDER"]),
182
199
  });
183
200
  await launchers({
201
+ commands,
202
+ commandSeconds,
203
+ schedules,
184
204
  tokenPrices,
185
205
  fallbackModels,
186
206
  worker,
207
+ workerCommands,
208
+ harness,
209
+ shell: environment["SHELL"] ?? "/bin/sh",
187
210
  profiles,
188
211
  command,
189
212
  ticketCommand,
@@ -193,6 +216,7 @@ await launchers({
193
216
  resetQueue: flags.includes("--reset-queue"),
194
217
  notifyCommand: listed("CLOVING_NOTIFY_COMMAND"),
195
218
  githubCommand: listed("CLOVING_GH_COMMAND"),
219
+ requiredChecks: listed("CLOVING_REQUIRED_CHECKS") ?? null,
196
220
  checkCommand: listed("CLOVING_CHECK_COMMAND"),
197
221
  privateTexts: listed("CLOVING_PRIVATE_TEXTS"),
198
222
  }, manifests);
@@ -203,5 +227,6 @@ process.exitCode = await hostedPrograms({
203
227
  directory,
204
228
  repository,
205
229
  updateMilliseconds,
206
- watchPort: process.env["CLOVING_WATCH_PORT"] ?? "3663",
230
+ watchPort: environment["CLOVING_WATCH_PORT"] ?? "3663",
231
+ phoneRelay: environment["CLOVING_PHONE_RELAY"] ?? "on",
207
232
  });
@@ -1,8 +1,12 @@
1
1
  import ts from "typescript";
2
2
  import { type CandidateSymbols } from "./workflow-candidate-syntax.ts";
3
- interface Totals {
3
+ export interface Totals {
4
4
  readonly knownCallCount: number;
5
5
  readonly knownUnitTotal: number;
6
6
  }
7
- export declare function candidateEffectTotals(source: ts.SourceFile, symbols: CandidateSymbols): Totals;
7
+ interface CountSymbols extends CandidateSymbols {
8
+ readonly localTotals?: (node: ts.Node) => Totals | undefined;
9
+ }
10
+ export declare function candidateEffectTotals(source: ts.Node, symbols: CountSymbols): Totals;
11
+ export declare function candidateLoopRepetitions(node: ts.IterationStatement, symbols: CandidateSymbols): number | undefined;
8
12
  export {};
@@ -1,5 +1,6 @@
1
1
  import ts from "typescript";
2
- import { protectedCall, handoffCall, propertyName, } from "./workflow-candidate-syntax.js";
2
+ import { protectedCall, helperCall, } from "./workflow-candidate-syntax.js";
3
+ import { propertyName } from "./workflow-candidate-fields.js";
3
4
  import { referenceSymbol } from "./workflow-source-effects.js";
4
5
  const zero = { knownCallCount: 0, knownUnitTotal: 0 };
5
6
  function add(first, second) {
@@ -21,7 +22,7 @@ function multiply(total, repetitions) {
21
22
  };
22
23
  }
23
24
  function units(node, symbols) {
24
- if (handoffCall(node, symbols))
25
+ if (helperCall(node, symbols))
25
26
  return 1;
26
27
  const argument = node.arguments[0];
27
28
  if (!argument || !ts.isObjectLiteralExpression(argument))
@@ -29,10 +30,8 @@ function units(node, symbols) {
29
30
  const property = argument.properties.find((entry) => entry.name && propertyName(entry.name) === "units");
30
31
  if (!property || !ts.isPropertyAssignment(property))
31
32
  return 0;
32
- const value = constantNumber(property.initializer);
33
- return value !== undefined && Number.isSafeInteger(value) && value > 0
34
- ? value
35
- : 0;
33
+ const value = Number(constantNumber(property.initializer));
34
+ return Number.isSafeInteger(value) && value > 0 ? value : 0;
36
35
  }
37
36
  function loopIncrement(node, variable) {
38
37
  if ((ts.isPostfixUnaryExpression(node) || ts.isPrefixUnaryExpression(node)) &&
@@ -210,19 +209,35 @@ export function candidateEffectTotals(source, symbols) {
210
209
  return add(count(node.expression), multiply(add(count(node.initializer), count(node.statement)), arrayIterations(node, symbols)));
211
210
  if (ts.isSwitchStatement(node))
212
211
  return add(count(node.expression), switchTotals(node.caseBlock, count));
213
- let total = protectedCall(node, symbols)
214
- ? {
215
- knownCallCount: 1,
216
- knownUnitTotal: units(node, symbols),
217
- }
218
- : zero;
219
- ts.forEachChild(node, (child) => {
220
- total = add(total, count(child));
221
- });
222
- return total;
212
+ return nodeTotals(node, { symbols, count });
223
213
  }
224
214
  return count(source);
225
215
  }
216
+ function nodeTotals(node, input) {
217
+ const local = input.symbols.localTotals?.(node);
218
+ if (local && ts.isFunctionLike(node))
219
+ return local;
220
+ let total = local ??
221
+ (protectedCall(node, input.symbols)
222
+ ? { knownCallCount: 1, knownUnitTotal: units(node, input.symbols) }
223
+ : zero);
224
+ ts.forEachChild(node, (child) => {
225
+ total = add(total, input.count(child));
226
+ });
227
+ return total;
228
+ }
229
+ export function candidateLoopRepetitions(node, symbols) {
230
+ if (ts.isForStatement(node))
231
+ return loopIterations(node, symbols);
232
+ if (ts.isForOfStatement(node) &&
233
+ ts.isArrayLiteralExpression(node.expression) &&
234
+ !uncertainLoopBody(node.statement, {
235
+ checker: symbols.checker,
236
+ counter: undefined,
237
+ }))
238
+ return node.expression.elements.length;
239
+ return undefined;
240
+ }
226
241
  function arrayIterations(node, symbols) {
227
242
  if (!ts.isArrayLiteralExpression(node.expression))
228
243
  return 1;
@@ -0,0 +1,5 @@
1
+ import ts from "typescript";
2
+ export declare function propertyName(name: ts.PropertyName): string | undefined;
3
+ export declare function propertyInitializer(node: ts.Node | undefined, field: string): ts.Expression | undefined;
4
+ export declare function literalField(node: ts.Node | undefined, field: string): string | null;
5
+ export declare function invalidFields(object: ts.ObjectLiteralExpression): boolean;
@@ -0,0 +1,24 @@
1
+ import ts from "typescript";
2
+ export function propertyName(name) {
3
+ if (ts.isIdentifier(name) || ts.isStringLiteral(name))
4
+ return name.text;
5
+ return undefined;
6
+ }
7
+ export function propertyInitializer(node, field) {
8
+ if (!node || !ts.isObjectLiteralExpression(node))
9
+ return undefined;
10
+ const property = node.properties.find((entry) => entry.name && propertyName(entry.name) === field);
11
+ return property && ts.isPropertyAssignment(property)
12
+ ? property.initializer
13
+ : undefined;
14
+ }
15
+ export function literalField(node, field) {
16
+ const value = propertyInitializer(node, field);
17
+ return value && ts.isStringLiteral(value) ? value.text : null;
18
+ }
19
+ export function invalidFields(object) {
20
+ const names = object.properties.map((entry) => entry.name && propertyName(entry.name));
21
+ return (names.includes(undefined) ||
22
+ new Set(names).size !== names.length ||
23
+ object.properties.some((entry) => !ts.isPropertyAssignment(entry)));
24
+ }
@@ -0,0 +1,10 @@
1
+ import ts from "typescript";
2
+ import { type Totals } from "./workflow-candidate-counts.ts";
3
+ import { type CandidateSymbols } from "./workflow-candidate-syntax.ts";
4
+ export declare function helperEffectTotals(input: {
5
+ readonly symbols: CandidateSymbols;
6
+ readonly sources: readonly ts.SourceFile[];
7
+ }): {
8
+ readonly totals?: Totals;
9
+ readonly unsupportedForms: readonly string[];
10
+ };
@@ -0,0 +1,129 @@
1
+ import ts from "typescript";
2
+ import { candidateEffectTotals, candidateLoopRepetitions, } from "./workflow-candidate-counts.js";
3
+ import { protectedCall, } from "./workflow-candidate-syntax.js";
4
+ import { resolvedSymbol } from "./workflow-candidate-program.js";
5
+ import { referenceSymbol, typeOnlyEffectNode, } from "./workflow-source-effects.js";
6
+ const zero = { knownCallCount: 0, knownUnitTotal: 0 };
7
+ export function helperEffectTotals(input) {
8
+ const { symbols, sources } = input;
9
+ const unsupportedForms = [];
10
+ if (sources.length === 1)
11
+ return { unsupportedForms };
12
+ const active = new Set();
13
+ const totals = new Map();
14
+ const functions = localFunctions(sources);
15
+ const functionAt = (node) => localFunctionAt(node, { checker: symbols.checker, functions });
16
+ function count(declaration) {
17
+ const cached = totals.get(declaration);
18
+ if (cached)
19
+ return cached;
20
+ if (active.has(declaration)) {
21
+ unsupportedForms.push("recursive-helper-effects");
22
+ return zero;
23
+ }
24
+ active.add(declaration);
25
+ const result = candidateEffectTotals(declaration, {
26
+ ...symbols,
27
+ localTotals: (node) => {
28
+ if (ts.isFunctionLike(node) && node !== declaration)
29
+ return zero;
30
+ if (!ts.isCallExpression(node) || !ts.isIdentifier(node.expression))
31
+ return undefined;
32
+ const called = functionAt(node.expression);
33
+ return called && effectful.has(called) ? count(called) : undefined;
34
+ },
35
+ });
36
+ active.delete(declaration);
37
+ totals.set(declaration, result);
38
+ return result;
39
+ }
40
+ const effectful = effectfulFunctions({ functions, functionAt, symbols });
41
+ if ([...effectful].every((entry) => entry.getSourceFile() === symbols.source))
42
+ return { unsupportedForms };
43
+ for (const source of sources)
44
+ inspectHelperReferences(source, {
45
+ functionAt,
46
+ effectful,
47
+ symbols,
48
+ unsupportedForms,
49
+ });
50
+ for (const declaration of effectful)
51
+ count(declaration);
52
+ const workflow = [...functions].find((entry) => entry.getSourceFile() === symbols.source &&
53
+ (ts.getCombinedModifierFlags(entry) & ts.ModifierFlags.Export) !== 0);
54
+ return { totals: workflow ? count(workflow) : zero, unsupportedForms };
55
+ }
56
+ function localFunctions(sources) {
57
+ return new Set(sources.flatMap((source) => source.statements.filter((node) => ts.isFunctionDeclaration(node) && node.body !== undefined)));
58
+ }
59
+ function localFunctionAt(node, input) {
60
+ const symbol = resolvedSymbol(input.checker, referenceSymbol(node, {
61
+ parent: node.parent,
62
+ checker: input.checker,
63
+ }));
64
+ return symbol?.declarations?.find((entry) => ts.isFunctionDeclaration(entry) && input.functions.has(entry));
65
+ }
66
+ function effectfulFunctions(input) {
67
+ const effectful = new Set();
68
+ const calls = (node) => {
69
+ if (typeOnlyEffectNode(node))
70
+ return false;
71
+ if (protectedCall(node, input.symbols))
72
+ return true;
73
+ const called = ts.isIdentifier(node) ? input.functionAt(node) : undefined;
74
+ if (called && effectful.has(called))
75
+ return true;
76
+ return ts.forEachChild(node, (child) => calls(child) || undefined) ?? false;
77
+ };
78
+ let previous = -1;
79
+ while (previous !== effectful.size) {
80
+ previous = effectful.size;
81
+ for (const declaration of input.functions)
82
+ if (calls(declaration))
83
+ effectful.add(declaration);
84
+ }
85
+ return effectful;
86
+ }
87
+ function inspectHelperReferences(source, input) {
88
+ function visit(node) {
89
+ if (typeOnlyEffectNode(node))
90
+ return;
91
+ if (source !== input.symbols.source &&
92
+ protectedCall(node, input.symbols) &&
93
+ !checkedHelperContext(node, input.symbols))
94
+ input.unsupportedForms.push("uncheckable-helper-effects");
95
+ const declaration = ts.isIdentifier(node)
96
+ ? input.functionAt(node)
97
+ : undefined;
98
+ if (declaration &&
99
+ input.effectful.has(declaration) &&
100
+ ts.isIdentifier(node) &&
101
+ !checkedHelperReference(node, input.symbols))
102
+ input.unsupportedForms.push("uncheckable-helper-effects");
103
+ ts.forEachChild(node, visit);
104
+ }
105
+ visit(source);
106
+ }
107
+ function checkedHelperReference(node, symbols) {
108
+ if (ts.isFunctionDeclaration(node.parent) && node.parent.name === node)
109
+ return true;
110
+ if (ts.isImportSpecifier(node.parent))
111
+ return true;
112
+ if (!ts.isCallExpression(node.parent) ||
113
+ node.parent.expression !== node ||
114
+ node.parent.questionDotToken !== undefined)
115
+ return false;
116
+ return checkedHelperContext(node.parent, symbols);
117
+ }
118
+ function checkedHelperContext(node, symbols) {
119
+ let parent = node;
120
+ while (!ts.isFunctionLike(parent)) {
121
+ if (ts.isSourceFile(parent))
122
+ return false;
123
+ if (ts.isIterationStatement(parent, false) &&
124
+ candidateLoopRepetitions(parent, symbols) === undefined)
125
+ return false;
126
+ parent = parent.parent;
127
+ }
128
+ return ts.isFunctionDeclaration(parent) && ts.isSourceFile(parent.parent);
129
+ }
@@ -2,10 +2,12 @@ import ts from "typescript";
2
2
  export declare const candidatePackageRoot: string;
3
3
  export interface CandidatePackageOptions {
4
4
  readonly packageRoot?: string;
5
+ readonly helpers?: Readonly<Record<string, string>>;
5
6
  }
6
7
  export declare function resolvedSymbol(checker: ts.TypeChecker, symbol: ts.Symbol | undefined): ts.Symbol | undefined;
7
8
  export declare function candidateProgram(text: string, installed?: CandidatePackageOptions): {
8
9
  readonly program: ts.Program;
9
10
  readonly source: ts.SourceFile;
11
+ readonly sources: readonly ts.SourceFile[];
10
12
  readonly modules: ReadonlyMap<string, ts.SourceFile>;
11
13
  };
@@ -2,6 +2,7 @@ import { dirname, join, resolve, sep } from "node:path";
2
2
  import { fileURLToPath } from "node:url";
3
3
  import ts from "typescript";
4
4
  import metadata from "#package" with { type: "json" };
5
+ import { workflowHelperPath } from "../src/workflow-candidate.js";
5
6
  export const candidatePackageRoot = dirname(fileURLToPath(import.meta.resolve("#package")));
6
7
  export function resolvedSymbol(checker, symbol) {
7
8
  if (!symbol || (symbol.flags & ts.SymbolFlags.Alias) === 0)
@@ -11,6 +12,12 @@ export function resolvedSymbol(checker, symbol) {
11
12
  export function candidateProgram(text, installed = {}) {
12
13
  const packageRoot = resolve(installed.packageRoot ?? candidatePackageRoot);
13
14
  const candidate = join(packageRoot, "candidate.ts");
15
+ const sources = new Map([[candidate, text]]);
16
+ for (const [name, content] of Object.entries(installed.helpers ?? {})) {
17
+ if (workflowHelperPath("candidate.ts", `./${name}`) !== name)
18
+ throw new Error("invalid-workflow-helper");
19
+ sources.set(join(packageRoot, name), content);
20
+ }
14
21
  const entries = new Map(Object.entries(metadata.exports).map(([name, entry]) => [
15
22
  `cloving${name.slice(1)}`,
16
23
  resolve(packageRoot, entry.types),
@@ -24,8 +31,8 @@ export function candidateProgram(text, installed = {}) {
24
31
  strict: true,
25
32
  noEmit: true,
26
33
  };
27
- const host = trustedHost({ candidate, text, options, entries, packageRoot });
28
- const program = ts.createProgram([candidate, ...entries.values()], options, host);
34
+ const host = trustedHost({ sources, options, entries, packageRoot });
35
+ const program = ts.createProgram([...sources.keys(), ...entries.values()], options, host);
29
36
  const source = program.getSourceFile(candidate);
30
37
  if (!source)
31
38
  throw new Error("candidate-analysis-unavailable");
@@ -36,15 +43,25 @@ export function candidateProgram(text, installed = {}) {
36
43
  throw new Error("candidate-surface-unavailable");
37
44
  modules.set(name, declaration);
38
45
  }
39
- return { program, source, modules };
46
+ return {
47
+ program,
48
+ source,
49
+ modules,
50
+ sources: [...sources.keys()].map((name) => {
51
+ const entry = program.getSourceFile(name);
52
+ if (!entry)
53
+ throw new Error("candidate-analysis-unavailable");
54
+ return entry;
55
+ }),
56
+ };
40
57
  }
41
58
  function trustedHost(input) {
42
- const { candidate, text, options, entries, packageRoot } = input;
59
+ const { sources, options, entries, packageRoot } = input;
43
60
  const libraries = dirname(ts.getDefaultLibFilePath(options)) + sep;
44
61
  const declarations = join(packageRoot, "dist/src") + sep;
45
62
  const readFile = (file) => {
46
- if (file === candidate)
47
- return text;
63
+ if (sources.has(file))
64
+ return sources.get(file);
48
65
  const absolute = resolve(file);
49
66
  if (!absolute.endsWith(".d.ts"))
50
67
  return undefined;
@@ -70,8 +87,19 @@ function trustedHost(input) {
70
87
  extension: ts.Extension.Dts,
71
88
  },
72
89
  };
73
- if (containingFile === candidate)
74
- return { resolvedModule: undefined };
90
+ if (sources.has(containingFile)) {
91
+ const importer = containingFile
92
+ .slice(packageRoot.length + 1)
93
+ .split(sep)
94
+ .join("/");
95
+ const local = workflowHelperPath(importer, name);
96
+ const target = local && join(packageRoot, local);
97
+ return {
98
+ resolvedModule: target && sources.has(target)
99
+ ? { resolvedFileName: target, extension: ts.Extension.Ts }
100
+ : undefined,
101
+ };
102
+ }
75
103
  return ts.resolveModuleName(name, containingFile, options, host);
76
104
  });
77
105
  return host;