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,8 +1,10 @@
1
1
  import ts from "typescript";
2
+ import { dirname, relative, sep } from "node:path";
2
3
  import { candidateProgram, resolvedSymbol, } from "./workflow-candidate-program.js";
3
4
  import { protectedEffectFact, passiveCandidateModule, unsupportedCandidateNode, workflowShape, } from "./workflow-candidate-syntax.js";
4
5
  import { sourceEffectNodes } from "./workflow-source-effects.js";
5
6
  import { candidateEffectTotals } from "./workflow-candidate-counts.js";
7
+ import { helperEffectTotals } from "./workflow-candidate-helper-counts.js";
6
8
  function exportSnapshot(modules, checker) {
7
9
  return Object.fromEntries([...modules].map(([name, declaration]) => {
8
10
  const symbol = checker.getSymbolAtLocation(declaration);
@@ -68,8 +70,21 @@ function sourceImport(node) {
68
70
  : [],
69
71
  };
70
72
  }
73
+ function mergeHelpers(modules, checker) {
74
+ return [
75
+ ["merge-when-green", "mergeWhenGreen"],
76
+ ["merge-by-person", "mergeByPerson"],
77
+ ].flatMap(([module, name]) => {
78
+ const symbol = protectedHelper(modules, {
79
+ checker,
80
+ subpath: `cloving/workflows/steps/${String(module)}`,
81
+ name: String(name),
82
+ });
83
+ return symbol === undefined ? [] : [symbol];
84
+ });
85
+ }
71
86
  export function analyzeWorkflowCandidate(text, installed = {}) {
72
- const { program, source, modules } = candidateProgram(text, installed);
87
+ const { program, source, sources, modules } = candidateProgram(text, installed);
73
88
  const checker = program.getTypeChecker();
74
89
  const symbols = {
75
90
  checker,
@@ -84,24 +99,71 @@ export function analyzeWorkflowCandidate(text, installed = {}) {
84
99
  subpath: "cloving/workflows/steps/handoff",
85
100
  name: "handoff",
86
101
  }),
102
+ commandSymbol: protectedHelper(modules, {
103
+ checker,
104
+ subpath: "cloving/workflows/steps/run-command",
105
+ name: "runCommand",
106
+ }),
107
+ reviewSymbol: protectedHelper(modules, {
108
+ checker,
109
+ subpath: "cloving/workflows/steps/review-by-agent",
110
+ name: "reviewByAgent",
111
+ }),
112
+ mergeSymbols: mergeHelpers(modules, checker),
113
+ };
114
+ const facts = sources.map((entry) => analyzedSource({ ...symbols, source: entry }, { program, workflow: source }));
115
+ return {
116
+ analysis: combineFacts({
117
+ localSources: Object.keys(installed.helpers ?? {}),
118
+ facts,
119
+ expanded: helperEffectTotals({
120
+ symbols: { ...symbols, source },
121
+ sources,
122
+ }),
123
+ }),
124
+ installedExports: exportSnapshot(modules, checker),
87
125
  };
126
+ }
127
+ function analyzedSource(symbols, input) {
128
+ const { source } = symbols;
88
129
  const facts = sourceFacts(symbols);
89
- const invalid = !workflowShape(source) ||
90
- source.referencedFiles.length > 0 ||
130
+ const kind = source === input.workflow ? "workflow" : "helper";
131
+ const path = relative(dirname(input.workflow.fileName), source.fileName)
132
+ .split(sep)
133
+ .join("/");
134
+ const invalid = source.referencedFiles.length > 0 ||
91
135
  source.typeReferenceDirectives.length > 0 ||
92
136
  source.libReferenceDirectives.length > 0 ||
93
- program.getSyntacticDiagnostics(source).length > 0;
137
+ input.program.getSyntacticDiagnostics(source).length > 0;
94
138
  return {
95
- analysis: {
96
- ...facts,
97
- unsupportedForms: [
98
- ...facts.unsupportedForms,
99
- ...(invalid ? ["workflow-shape"] : []),
100
- ...(facts.directEffects.length === 0 && !passiveCandidateModule(source)
101
- ? ["module-initialization"]
102
- : []),
103
- ],
104
- },
105
- installedExports: exportSnapshot(modules, checker),
139
+ ...facts,
140
+ imports: facts.imports.map((entry) => kind === "workflow" ? entry : { ...entry, source: path }),
141
+ unsupportedForms: [
142
+ ...facts.unsupportedForms,
143
+ ...(invalid || (kind === "workflow" && !workflowShape(source))
144
+ ? ["workflow-shape"]
145
+ : []),
146
+ ...(facts.directEffects.length === 0 &&
147
+ !passiveCandidateModule(source, kind)
148
+ ? ["module-initialization"]
149
+ : []),
150
+ ],
151
+ };
152
+ }
153
+ function combineFacts(input) {
154
+ const { localSources, facts } = input;
155
+ return {
156
+ localSources,
157
+ imports: facts.flatMap((entry) => entry.imports),
158
+ effects: facts.flatMap((entry) => entry.effects),
159
+ directEffects: facts.flatMap((entry) => entry.directEffects),
160
+ unsupportedForms: [
161
+ ...facts.flatMap((entry) => entry.unsupportedForms),
162
+ ...input.expanded.unsupportedForms,
163
+ ],
164
+ knownCallCount: input.expanded.totals?.knownCallCount ??
165
+ facts.reduce((total, entry) => total + entry.knownCallCount, 0),
166
+ knownUnitTotal: input.expanded.totals?.knownUnitTotal ??
167
+ facts.reduce((total, entry) => total + entry.knownUnitTotal, 0),
106
168
  };
107
169
  }
@@ -5,11 +5,17 @@ export interface CandidateSymbols {
5
5
  readonly source: ts.SourceFile;
6
6
  readonly protectedSymbol: ts.Symbol | undefined;
7
7
  readonly handoffSymbol: ts.Symbol | undefined;
8
+ readonly commandSymbol: ts.Symbol | undefined;
9
+ readonly reviewSymbol: ts.Symbol | undefined;
10
+ readonly mergeSymbols: readonly ts.Symbol[];
8
11
  }
9
- export declare function propertyName(name: ts.PropertyName): string | undefined;
10
12
  export declare function protectedCall(node: ts.Node, symbols: CandidateSymbols): node is ts.CallExpression;
11
13
  export declare function handoffCall(node: ts.CallExpression, symbols: CandidateSymbols): boolean;
14
+ export declare function commandCall(node: ts.CallExpression, symbols: CandidateSymbols): boolean;
15
+ export declare function reviewCall(node: ts.CallExpression, symbols: CandidateSymbols): boolean;
16
+ export declare function helperCall(node: ts.CallExpression, symbols: CandidateSymbols): boolean;
17
+ export declare function mergeCall(node: ts.CallExpression, symbols: CandidateSymbols): boolean;
12
18
  export declare function protectedEffectFact(node: ts.Node, symbols: CandidateSymbols): CandidateEffect | undefined;
13
19
  export declare function unsupportedCandidateNode(node: ts.Node, symbols: CandidateSymbols): boolean;
14
20
  export declare function workflowShape(source: ts.SourceFile): boolean;
15
- export declare function passiveCandidateModule(source: ts.SourceFile): boolean;
21
+ export declare function passiveCandidateModule(source: ts.SourceFile, kind?: "workflow" | "helper"): boolean;
@@ -1,29 +1,41 @@
1
1
  import ts from "typescript";
2
2
  import { resolvedSymbol } from "./workflow-candidate-program.js";
3
3
  import { referenceSymbol } from "./workflow-source-effects.js";
4
- export function propertyName(name) {
5
- if (ts.isIdentifier(name) || ts.isStringLiteral(name))
6
- return name.text;
7
- return undefined;
8
- }
4
+ import { invalidFields, literalField, propertyInitializer, } from "./workflow-candidate-fields.js";
9
5
  export function protectedCall(node, symbols) {
10
6
  if (!ts.isCallExpression(node) || !ts.isIdentifier(node.expression))
11
7
  return false;
12
8
  const symbol = resolvedSymbol(symbols.checker, symbols.checker.getSymbolAtLocation(node.expression));
13
9
  return (symbol !== undefined &&
14
- [symbols.protectedSymbol, symbols.handoffSymbol].includes(symbol));
10
+ [
11
+ symbols.protectedSymbol,
12
+ symbols.handoffSymbol,
13
+ symbols.commandSymbol,
14
+ symbols.reviewSymbol,
15
+ ...symbols.mergeSymbols,
16
+ ].includes(symbol));
17
+ }
18
+ function calledSymbol(node, symbols) {
19
+ return resolvedSymbol(symbols.checker, symbols.checker.getSymbolAtLocation(node.expression));
15
20
  }
16
21
  export function handoffCall(node, symbols) {
17
- return (symbols.handoffSymbol !== undefined &&
18
- resolvedSymbol(symbols.checker, symbols.checker.getSymbolAtLocation(node.expression)) === symbols.handoffSymbol);
22
+ const symbol = symbols.handoffSymbol;
23
+ return symbol !== undefined && calledSymbol(node, symbols) === symbol;
24
+ }
25
+ export function commandCall(node, symbols) {
26
+ const symbol = symbols.commandSymbol;
27
+ return symbol !== undefined && calledSymbol(node, symbols) === symbol;
19
28
  }
20
- function literalField(node, field) {
21
- const property = node.properties.find((entry) => entry.name && propertyName(entry.name) === field);
22
- if (!property || !ts.isPropertyAssignment(property))
23
- return null;
24
- return ts.isStringLiteral(property.initializer)
25
- ? property.initializer.text
26
- : null;
29
+ export function reviewCall(node, symbols) {
30
+ const symbol = symbols.reviewSymbol;
31
+ return symbol !== undefined && calledSymbol(node, symbols) === symbol;
32
+ }
33
+ export function helperCall(node, symbols) {
34
+ return [handoffCall, commandCall, reviewCall, mergeCall].some((call) => call(node, symbols));
35
+ }
36
+ export function mergeCall(node, symbols) {
37
+ const symbol = calledSymbol(node, symbols);
38
+ return symbol !== undefined && symbols.mergeSymbols.includes(symbol);
27
39
  }
28
40
  export function protectedEffectFact(node, symbols) {
29
41
  if (!protectedCall(node, symbols))
@@ -33,17 +45,45 @@ export function protectedEffectFact(node, symbols) {
33
45
  const argument = node.arguments[0];
34
46
  if (!argument || !ts.isObjectLiteralExpression(argument))
35
47
  return undefined;
48
+ if (reviewCall(node, symbols))
49
+ return reviewEffectFact(propertyInitializer(argument, "reviewer"));
50
+ if (mergeCall(node, symbols))
51
+ return { capability: "github", method: "merge" };
52
+ if (commandCall(node, symbols)) {
53
+ const name = literalField(argument, "name");
54
+ return {
55
+ capability: "command",
56
+ method: name === null ? null : `run:${name}`,
57
+ };
58
+ }
36
59
  return {
37
60
  capability: literalField(argument, "capability"),
38
61
  method: literalField(argument, "method"),
39
62
  };
40
63
  }
64
+ function reviewEffectFact(reviewer) {
65
+ const kind = literalField(reviewer, "kind");
66
+ if (kind === "greptile")
67
+ return { capability: "github", method: "wait-review" };
68
+ const profile = literalField(reviewer, "profile");
69
+ if (kind === "turn" &&
70
+ profile !== null &&
71
+ /^[A-Za-z0-9_-]{1,119}$/.test(profile))
72
+ return { capability: "claude", method: `run-turn:${profile}` };
73
+ return { capability: null, method: null };
74
+ }
41
75
  function escapedHelper(node, symbols) {
42
76
  if (!ts.isIdentifier(node))
43
77
  return false;
44
78
  const symbol = resolvedSymbol(symbols.checker, referenceSymbol(node, { parent: node.parent, checker: symbols.checker }));
45
79
  if (symbol === undefined ||
46
- ![symbols.protectedSymbol, symbols.handoffSymbol].includes(symbol))
80
+ ![
81
+ symbols.protectedSymbol,
82
+ symbols.handoffSymbol,
83
+ symbols.commandSymbol,
84
+ symbols.reviewSymbol,
85
+ ...symbols.mergeSymbols,
86
+ ].includes(symbol))
47
87
  return false;
48
88
  if (ts.isImportSpecifier(node.parent))
49
89
  return false;
@@ -71,10 +111,13 @@ function invalidRequest(node, symbols) {
71
111
  !argument ||
72
112
  !ts.isObjectLiteralExpression(argument))
73
113
  return true;
74
- const names = argument.properties.map((entry) => entry.name && propertyName(entry.name));
75
- return (names.includes(undefined) ||
76
- new Set(names).size !== names.length ||
77
- argument.properties.some((entry) => !ts.isPropertyAssignment(entry)));
114
+ const reviewer = reviewCall(node, symbols)
115
+ ? propertyInitializer(argument, "reviewer")
116
+ : undefined;
117
+ return (invalidFields(argument) ||
118
+ (reviewer !== undefined &&
119
+ ts.isObjectLiteralExpression(reviewer) &&
120
+ invalidFields(reviewer)));
78
121
  }
79
122
  function workflowBodyCall(node) {
80
123
  let child = node;
@@ -198,12 +241,13 @@ function passiveInitializer(node) {
198
241
  const inner = unwrappedInitializer(node);
199
242
  return inner ? passiveInitializer(inner) : passiveCompound(node);
200
243
  }
201
- function passiveStatement(node) {
244
+ function passiveStatement(node, kind) {
202
245
  if (ts.isVariableStatement(node))
203
246
  return ((node.declarationList.flags & ts.NodeFlags.Const) !== 0 &&
204
- node.declarationList.declarations.every((declaration) => (ts.getCombinedModifierFlags(declaration) &
205
- ts.ModifierFlags.Export) ===
206
- 0 &&
247
+ node.declarationList.declarations.every((declaration) => (kind === "helper" ||
248
+ (ts.getCombinedModifierFlags(declaration) &
249
+ ts.ModifierFlags.Export) ===
250
+ 0) &&
207
251
  ts.isIdentifier(declaration.name) &&
208
252
  passiveInitializer(declaration.initializer)));
209
253
  if (ts.isExpressionStatement(node))
@@ -214,6 +258,6 @@ function passiveStatement(node) {
214
258
  ts.isFunctionDeclaration(node) ||
215
259
  ts.isEmptyStatement(node));
216
260
  }
217
- export function passiveCandidateModule(source) {
218
- return source.statements.every(passiveStatement);
261
+ export function passiveCandidateModule(source, kind = "workflow") {
262
+ return source.statements.every((node) => passiveStatement(node, kind));
219
263
  }
@@ -0,0 +1 @@
1
+ export declare function workflowCommand(argumentsList: readonly string[]): Promise<number>;
@@ -0,0 +1,143 @@
1
+ import { absoluteWorkflowFolder } from "../src/project-settings.js";
2
+ import { projectConfiguration, projectRoot } from "./project-configuration.js";
3
+ import { spawn } from "node:child_process";
4
+ import { lstat, mkdtemp, rm } from "node:fs/promises";
5
+ import { homedir, tmpdir } from "node:os";
6
+ import { join, resolve } from "node:path";
7
+ import { parseArgs } from "node:util";
8
+ import { workflowTestLoader } from "./workflow-test-loader.js";
9
+ import { preparedProject } from "./self-host-project.js";
10
+ const usage = "Usage: cloving workflows list [--json] [--repository <directory>]\n cloving workflows test [<id>] [--repository <directory>]\n";
11
+ function invocation(argumentsList) {
12
+ const { values, positionals } = parseArgs({
13
+ args: [...argumentsList],
14
+ allowPositionals: true,
15
+ options: {
16
+ json: { type: "boolean" },
17
+ repository: { type: "string" },
18
+ help: { type: "boolean", short: "h" },
19
+ },
20
+ });
21
+ const [command, id, ...extra] = positionals;
22
+ if (extra.length > 0 || (command === "list" && id !== undefined))
23
+ throw new Error("unexpected-argument");
24
+ if (command !== "list" && command !== "test")
25
+ throw new Error("unknown-command");
26
+ if (command === "test" && values.json)
27
+ throw new Error("unexpected-option");
28
+ return { ...values, command, id };
29
+ }
30
+ export async function workflowCommand(argumentsList) {
31
+ let selected;
32
+ try {
33
+ selected = invocation(argumentsList);
34
+ }
35
+ catch {
36
+ process.stderr.write("invalid-invocation: " + usage);
37
+ return 2;
38
+ }
39
+ if (selected.help) {
40
+ process.stdout.write(usage);
41
+ return 0;
42
+ }
43
+ const temporary = await mkdtemp(join(tmpdir(), "cloving-workflows-"));
44
+ try {
45
+ const repository = resolve(selected.repository ?? process.cwd());
46
+ const result = await preparedProject({
47
+ product: resolve(import.meta.dirname, ".."),
48
+ project: join(temporary, "project"),
49
+ repository,
50
+ home: homedir(),
51
+ xdgConfigHome: process.env["XDG_CONFIG_HOME"],
52
+ userFolder: absoluteWorkflowFolder((await projectConfiguration(await projectRoot(repository), process.env))
53
+ .environment["CLOVING_WORKFLOW_FOLDER"]),
54
+ });
55
+ if (selected.command === "test")
56
+ return await testWorkflows(result.workflows, {
57
+ id: selected.id,
58
+ repository,
59
+ });
60
+ presentWorkflows(result.workflows, { json: selected.json === true });
61
+ return 0;
62
+ }
63
+ catch (error) {
64
+ process.stderr.write("workflow-inspection-failed: " +
65
+ (error instanceof Error ? error.message : "unavailable") +
66
+ "\n");
67
+ return 1;
68
+ }
69
+ finally {
70
+ await rm(temporary, { recursive: true, force: true });
71
+ }
72
+ }
73
+ function presentWorkflows(workflows, options) {
74
+ if (options.json) {
75
+ process.stdout.write(JSON.stringify(workflows) + "\n");
76
+ return;
77
+ }
78
+ for (const workflow of workflows)
79
+ process.stdout.write(`${workflow.workflowId ?? "unknown"} (${workflow.scope}, ${workflow.admitted ? "admitted" : "refused: " + workflow.codes.join(", ")}) ${JSON.stringify(workflow.file)}\n`);
80
+ }
81
+ async function testWorkflows(workflows, options) {
82
+ const { id, repository } = options;
83
+ const selected = workflows.filter((workflow) => workflow.scope !== "bundled" &&
84
+ (id === undefined || workflow.workflowId === id));
85
+ if (id !== undefined &&
86
+ !workflows.some((workflow) => workflow.workflowId === id)) {
87
+ process.stderr.write("unknown-workflow: " + id + "\n");
88
+ return 1;
89
+ }
90
+ const files = [];
91
+ for (const workflow of selected) {
92
+ const file = workflow.file.replace(/\.ts$/, ".test.ts");
93
+ const exists = await lstat(file).catch((error) => {
94
+ if (error instanceof Error && "code" in error && error.code === "ENOENT")
95
+ return undefined;
96
+ throw error;
97
+ });
98
+ if (exists?.isFile())
99
+ files.push(file);
100
+ }
101
+ if (files.length === 0) {
102
+ process.stderr.write("workflow-test-missing: " + (id ?? "no project or user tests") + "\n");
103
+ return 1;
104
+ }
105
+ return await runTests(files, repository);
106
+ }
107
+ function runTests(files, repository) {
108
+ return new Promise((settle, reject) => {
109
+ const child = spawn(process.execPath, [
110
+ "--experimental-strip-types",
111
+ "--import",
112
+ workflowTestLoader(),
113
+ "--test",
114
+ ...files,
115
+ ], {
116
+ cwd: repository,
117
+ stdio: "inherit",
118
+ env: { ...process.env, NODE_TEST_CONTEXT: undefined },
119
+ });
120
+ const interrupt = () => {
121
+ child.kill("SIGINT");
122
+ };
123
+ const terminate = () => {
124
+ child.kill("SIGTERM");
125
+ };
126
+ process.on("SIGINT", interrupt);
127
+ process.on("SIGTERM", terminate);
128
+ const cleanup = () => {
129
+ process.off("SIGINT", interrupt);
130
+ process.off("SIGTERM", terminate);
131
+ };
132
+ child.once("error", (error) => {
133
+ cleanup();
134
+ reject(error);
135
+ });
136
+ child.once("close", (code) => {
137
+ cleanup();
138
+ settle(code ?? 1);
139
+ });
140
+ });
141
+ }
142
+ if (process.argv[1] === import.meta.filename)
143
+ process.exitCode = await workflowCommand(process.argv.slice(2));
@@ -11,6 +11,7 @@ export declare function inspectWorkflowFolder(root: string, parts: readonly stri
11
11
  export declare function captureWorkflowCandidate(folder: WorkflowFolder, name: string): Promise<{
12
12
  source: string | undefined;
13
13
  declaration: string | undefined;
14
+ helpers: Record<string, string>;
14
15
  codes: WorkflowCaptureCode[];
15
16
  }>;
16
17
  export declare function workflowCaptureFailure(error: unknown): WorkflowCaptureCode;
@@ -1,6 +1,8 @@
1
1
  import { constants } from "node:fs";
2
2
  import { lstat, open, readdir, realpath, } from "node:fs/promises";
3
3
  import { isAbsolute, join, relative, sep } from "node:path";
4
+ import ts from "typescript";
5
+ import { workflowHelperPath } from "../src/workflow-candidate.js";
4
6
  function contained(root, path) {
5
7
  const suffix = relative(root, path);
6
8
  return (!isAbsolute(suffix) && suffix !== ".." && !suffix.startsWith(`..${sep}`));
@@ -92,6 +94,19 @@ async function openSource(folder, name) {
92
94
  }
93
95
  }
94
96
  async function inspectFile(folder, name) {
97
+ const parts = name.split("/");
98
+ const fileName = parts.pop();
99
+ if (!fileName || parts.some((part) => !safeComponent(part)))
100
+ throw new Error("unsafe-workflow-path");
101
+ let parent = folder;
102
+ for (const part of parts) {
103
+ const path = join(parent.path, part);
104
+ const stats = await lstat(path, { bigint: true });
105
+ if (!stats.isDirectory())
106
+ throw new Error("unsafe-workflow-path");
107
+ parent = { path, components: [...parent.components, { path, stats }] };
108
+ }
109
+ await verifyFolder(parent);
95
110
  const path = join(folder.path, name);
96
111
  const stats = await lstat(path, { bigint: true });
97
112
  if (!stats.isFile())
@@ -101,11 +116,12 @@ async function inspectFile(folder, name) {
101
116
  throw new Error("workflow-source-changed");
102
117
  if (!contained(folder.path, resolved))
103
118
  throw new Error("unsafe-workflow-path");
104
- return { path, name, stats };
119
+ return { path, name, stats, folder: parent, fileName };
105
120
  }
106
121
  async function captureFile(folder, file) {
107
122
  const { stats } = file;
108
- const handle = await openSource(folder, file.name).catch(changedPathError);
123
+ await verifyFolder(folder);
124
+ const handle = await openSource(file.folder, file.fileName).catch(changedPathError);
109
125
  try {
110
126
  if (!sameSnapshot(stats, await handle.stat({ bigint: true })))
111
127
  throw new Error("workflow-source-changed");
@@ -140,11 +156,38 @@ async function captureFiles(folder, names) {
140
156
  }
141
157
  }
142
158
  for (const file of captured) {
159
+ await verifyFolder(file.folder);
143
160
  if (!sameSnapshot(file.stats, await capturedStats(file.path)))
144
161
  throw new Error("workflow-source-changed");
145
162
  }
146
163
  return { captured, codes };
147
164
  }
165
+ async function captureImports(folder, snapshot) {
166
+ const { captured, codes } = snapshot;
167
+ const helpers = {};
168
+ for (const source of captured) {
169
+ if (!source.name.endsWith(".ts"))
170
+ continue;
171
+ for (const imported of ts.preProcessFile(source.bytes.toString("utf8"))
172
+ .importedFiles) {
173
+ const name = workflowHelperPath(source.name, imported.fileName);
174
+ if (!name || captured.some((file) => file.name === name))
175
+ continue;
176
+ const result = await captureFiles(folder, [name]);
177
+ codes.push(...result.codes);
178
+ captured.push(...result.captured);
179
+ const text = decodedFile({ bytes: result.captured[0]?.bytes, code: "unsupported-source" }, codes);
180
+ if (text !== undefined)
181
+ helpers[name] = text;
182
+ }
183
+ }
184
+ for (const file of captured) {
185
+ await verifyFolder(file.folder);
186
+ if (!sameSnapshot(file.stats, await capturedStats(file.path)))
187
+ throw new Error("workflow-source-changed");
188
+ }
189
+ return helpers;
190
+ }
148
191
  function decodedFile(input, codes) {
149
192
  if (input.bytes === undefined)
150
193
  return undefined;
@@ -163,6 +206,7 @@ export async function captureWorkflowCandidate(folder, name) {
163
206
  name,
164
207
  declarationName,
165
208
  ]);
209
+ const helpers = await captureImports(folder, { captured, codes });
166
210
  await verifyFolder(folder);
167
211
  const source = decodedFile({
168
212
  bytes: captured.find((file) => file.name === name)?.bytes,
@@ -175,6 +219,7 @@ export async function captureWorkflowCandidate(folder, name) {
175
219
  return {
176
220
  source,
177
221
  declaration,
222
+ helpers,
178
223
  codes: [
179
224
  ...new Set(codes.includes("unsafe-workflow-path")
180
225
  ? codes.filter((code) => code !== "workflow-read-failed")
@@ -1,4 +1,5 @@
1
- import type { WorkflowCandidateCode } from "../src/workflow-candidate.ts";
1
+ import type { WorkflowDeclaration } from "../src/workflow-declaration.ts";
2
+ import { type CandidateDeclaration, type WorkflowCandidateCode } from "../src/workflow-candidate.ts";
2
3
  import { type WorkflowConflictCode, type WorkflowReservation } from "../src/workflow-discovery.ts";
3
4
  import type { CandidatePackageOptions } from "./workflow-candidate-program.ts";
4
5
  import { type WorkflowCaptureCode } from "./workflow-folder-capture.ts";
@@ -6,6 +7,7 @@ export interface WorkflowFolderInput {
6
7
  readonly repository: string;
7
8
  readonly home: string;
8
9
  readonly xdgConfigHome?: string | undefined;
10
+ readonly userFolder?: string | undefined;
9
11
  readonly bundled: readonly WorkflowReservation[];
10
12
  }
11
13
  interface WorkflowLabel {
@@ -16,6 +18,7 @@ interface WorkflowSnapshot extends WorkflowLabel {
16
18
  readonly baseName: string;
17
19
  readonly source: string;
18
20
  readonly declaration: string;
21
+ readonly helpers: Readonly<Record<string, string>>;
19
22
  }
20
23
  export interface WorkflowFolderCandidate extends WorkflowSnapshot {
21
24
  readonly repository: string;
@@ -24,10 +27,23 @@ export interface WorkflowFolderCandidate extends WorkflowSnapshot {
24
27
  export interface WorkflowFolderRefusal extends WorkflowLabel {
25
28
  readonly codes: readonly (WorkflowCandidateCode | WorkflowCaptureCode | WorkflowConflictCode | "compile-failed")[];
26
29
  }
30
+ export interface WorkflowListing {
31
+ readonly scope: "bundled" | "project" | "user";
32
+ readonly workflowId: string | null;
33
+ readonly file: string;
34
+ readonly admitted: boolean;
35
+ readonly codes: readonly string[];
36
+ readonly declaration?: CandidateDeclaration | WorkflowDeclaration;
37
+ }
27
38
  export interface WorkflowFolderResult {
39
+ readonly workflows: readonly WorkflowListing[];
28
40
  readonly repository: string;
29
41
  readonly candidates: readonly WorkflowFolderCandidate[];
30
42
  readonly refusals: readonly WorkflowFolderRefusal[];
31
43
  }
44
+ export declare function userLocation(input: Pick<WorkflowFolderInput, "home" | "xdgConfigHome" | "userFolder">): {
45
+ readonly root: string;
46
+ readonly parts: readonly string[];
47
+ };
32
48
  export declare function loadWorkflowFolders(input: WorkflowFolderInput, installed?: CandidatePackageOptions): Promise<WorkflowFolderResult>;
33
49
  export {};