cloving 1.0.1 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (309) hide show
  1. package/README.md +251 -20
  2. package/assets/watch-fonts/fira-code-OFL.md +93 -0
  3. package/assets/watch-fonts/fira-code-latin-400-normal.woff2 +0 -0
  4. package/assets/watch-fonts/fira-code-latin-500-normal.woff2 +0 -0
  5. package/assets/watch-fonts/heebo-OFL.md +93 -0
  6. package/assets/watch-fonts/heebo-latin-500-normal.woff2 +0 -0
  7. package/assets/watch-fonts/heebo-latin-700-normal.woff2 +0 -0
  8. package/assets/watch-fonts/heebo-latin-800-normal.woff2 +0 -0
  9. package/assets/watch-fonts/manifest.json +112 -0
  10. package/assets/watch-fonts/space-grotesk-OFL.md +93 -0
  11. package/assets/watch-fonts/space-grotesk-latin-400-normal.woff2 +0 -0
  12. package/assets/watch-fonts/space-grotesk-latin-500-normal.woff2 +0 -0
  13. package/assets/watch-fonts/space-grotesk-latin-600-normal.woff2 +0 -0
  14. package/assets/watch-fonts/space-grotesk-latin-700-normal.woff2 +0 -0
  15. package/dist/src/authority/commands.d.ts +1 -0
  16. package/dist/src/authority/commands.js +33 -4
  17. package/dist/src/authority/dispatch-decision.d.ts +1 -0
  18. package/dist/src/authority/dispatch-decision.js +7 -4
  19. package/dist/src/authority/events.js +2 -2
  20. package/dist/src/authority/handoff-chain.d.ts +24 -0
  21. package/dist/src/authority/handoff-chain.js +46 -0
  22. package/dist/src/authority/reconciliation.d.ts +3 -0
  23. package/dist/src/authority/reconciliation.js +21 -5
  24. package/dist/src/authority/transitions.d.ts +1 -1
  25. package/dist/src/authority/transitions.js +5 -0
  26. package/dist/src/broker/actor/authority-queries.d.ts +4 -0
  27. package/dist/src/broker/actor/authority-queries.js +51 -2
  28. package/dist/src/broker/actor/authority-session.d.ts +2 -0
  29. package/dist/src/broker/actor/authority-session.js +3 -4
  30. package/dist/src/broker/actor/delivery-intents.d.ts +1 -0
  31. package/dist/src/broker/actor/effect-delivery.js +9 -0
  32. package/dist/src/broker/authority-admission.js +13 -5
  33. package/dist/src/chats/analysis-batches.d.ts +42 -0
  34. package/dist/src/chats/analysis-batches.js +97 -0
  35. package/dist/src/chats/analysis-checkpoint.d.ts +29 -0
  36. package/dist/src/chats/analysis-checkpoint.js +31 -0
  37. package/dist/src/chats/analysis-contract.d.ts +80 -0
  38. package/dist/src/chats/analysis-contract.js +16 -0
  39. package/dist/src/chats/analysis-data.d.ts +3 -0
  40. package/dist/src/chats/analysis-data.js +85 -0
  41. package/dist/src/chats/analysis-input.d.ts +18 -0
  42. package/dist/src/chats/analysis-input.js +97 -0
  43. package/dist/src/chats/analysis-links.d.ts +2 -0
  44. package/dist/src/chats/analysis-links.js +25 -0
  45. package/dist/src/chats/analysis-merge.d.ts +16 -0
  46. package/dist/src/chats/analysis-merge.js +134 -0
  47. package/dist/src/chats/analysis-prompt.d.ts +18 -0
  48. package/dist/src/chats/analysis-prompt.js +42 -0
  49. package/dist/src/chats/analysis-proposal.d.ts +4 -0
  50. package/dist/src/chats/analysis-proposal.js +41 -0
  51. package/dist/src/chats/analysis-provenance.d.ts +4 -0
  52. package/dist/src/chats/analysis-provenance.js +99 -0
  53. package/dist/src/chats/analysis-shape.d.ts +4 -0
  54. package/dist/src/chats/analysis-shape.js +235 -0
  55. package/dist/src/chats/analysis.d.ts +3 -0
  56. package/dist/src/chats/analysis.js +42 -0
  57. package/dist/src/chats/corpus-input.d.ts +5 -0
  58. package/dist/src/chats/corpus-input.js +288 -0
  59. package/dist/src/chats/destination.js +3 -1
  60. package/dist/src/chats/import.d.ts +8 -6
  61. package/dist/src/chats/import.js +195 -23
  62. package/dist/src/chats/input-file.d.ts +3 -0
  63. package/dist/src/chats/input-file.js +32 -0
  64. package/dist/src/chats/normalize.d.ts +11 -2
  65. package/dist/src/chats/normalize.js +18 -2
  66. package/dist/src/chats/previous-import.d.ts +5 -0
  67. package/dist/src/chats/previous-import.js +34 -0
  68. package/dist/src/chats/reader.d.ts +7 -1
  69. package/dist/src/chats/reader.js +46 -15
  70. package/dist/src/chats/redact-import.d.ts +14 -0
  71. package/dist/src/chats/redact-import.js +141 -0
  72. package/dist/src/chats/redaction.d.ts +41 -0
  73. package/dist/src/chats/redaction.js +161 -0
  74. package/dist/src/chats/reference-mapping.d.ts +16 -0
  75. package/dist/src/chats/reference-mapping.js +153 -0
  76. package/dist/src/chats/revision-input.d.ts +3 -0
  77. package/dist/src/chats/revision-input.js +180 -0
  78. package/dist/src/chats/revision.d.ts +48 -0
  79. package/dist/src/chats/revision.js +94 -0
  80. package/dist/src/chats/selection.d.ts +25 -0
  81. package/dist/src/chats/selection.js +109 -0
  82. package/dist/src/chats/source.d.ts +23 -0
  83. package/dist/src/chats/source.js +135 -0
  84. package/dist/src/chats/structure.d.ts +57 -0
  85. package/dist/src/chats/structure.js +216 -0
  86. package/dist/src/chats/workflow-generation-projection.d.ts +8 -0
  87. package/dist/src/chats/workflow-generation-projection.js +75 -0
  88. package/dist/src/chats/workflow-generation-source.d.ts +25 -0
  89. package/dist/src/chats/workflow-generation-source.js +124 -0
  90. package/dist/src/chats/workflow-generation-text.d.ts +23 -0
  91. package/dist/src/chats/workflow-generation-text.js +74 -0
  92. package/dist/src/chats/workflow-generation.d.ts +69 -0
  93. package/dist/src/chats/workflow-generation.js +142 -0
  94. package/dist/src/cli/arguments.d.ts +8 -0
  95. package/dist/src/cli/arguments.js +94 -13
  96. package/dist/src/cli/client.d.ts +1 -0
  97. package/dist/src/cli/client.js +38 -1
  98. package/dist/src/cli/commands.d.ts +3 -0
  99. package/dist/src/cli/commands.js +29 -11
  100. package/dist/src/cli/main.d.ts +2 -1
  101. package/dist/src/cli/main.js +61 -14
  102. package/dist/src/cli/render.d.ts +3 -0
  103. package/dist/src/cli/render.js +122 -7
  104. package/dist/src/cli/usage-render.d.ts +3 -0
  105. package/dist/src/cli/usage-render.js +28 -0
  106. package/dist/src/cli/usage.d.ts +3 -2
  107. package/dist/src/cli/usage.js +42 -7
  108. package/dist/src/cli/watch-actions.d.ts +10 -0
  109. package/dist/src/cli/watch-actions.js +58 -0
  110. package/dist/src/cli/watch-board-view.d.ts +44 -1
  111. package/dist/src/cli/watch-board-view.js +137 -18
  112. package/dist/src/cli/watch-board.d.ts +7 -1
  113. package/dist/src/cli/watch-board.js +48 -11
  114. package/dist/src/cli/watch-card-client.d.ts +1 -0
  115. package/dist/src/cli/watch-card-client.js +44 -0
  116. package/dist/src/cli/watch-card-forms.d.ts +2 -0
  117. package/dist/src/cli/watch-card-forms.js +33 -0
  118. package/dist/src/cli/watch-card-reply.d.ts +5 -0
  119. package/dist/src/cli/watch-card-reply.js +34 -0
  120. package/dist/src/cli/watch-card-view.d.ts +40 -0
  121. package/dist/src/cli/watch-card-view.js +86 -0
  122. package/dist/src/cli/watch-client.d.ts +1 -0
  123. package/dist/src/cli/watch-client.js +122 -0
  124. package/dist/src/cli/watch-model.d.ts +10 -4
  125. package/dist/src/cli/watch-model.js +61 -67
  126. package/dist/src/cli/watch-page.d.ts +1 -1
  127. package/dist/src/cli/watch-page.js +89 -86
  128. package/dist/src/cli/watch-phases.js +3 -3
  129. package/dist/src/cli/watch-pr-board.d.ts +3 -0
  130. package/dist/src/cli/watch-pr-board.js +26 -0
  131. package/dist/src/cli/watch-pr-command.d.ts +18 -0
  132. package/dist/src/cli/watch-pr-command.js +74 -0
  133. package/dist/src/cli/watch-pr-identity.d.ts +32 -0
  134. package/dist/src/cli/watch-pr-identity.js +87 -0
  135. package/dist/src/cli/watch-pr-observer.d.ts +20 -0
  136. package/dist/src/cli/watch-pr-observer.js +150 -0
  137. package/dist/src/cli/watch-pr-settings.d.ts +8 -0
  138. package/dist/src/cli/watch-pr-settings.js +47 -0
  139. package/dist/src/cli/watch-pull-request.d.ts +10 -0
  140. package/dist/src/cli/watch-pull-request.js +130 -0
  141. package/dist/src/cli/watch-reads.d.ts +7 -1
  142. package/dist/src/cli/watch-reads.js +44 -18
  143. package/dist/src/cli/watch-render.d.ts +29 -2
  144. package/dist/src/cli/watch-render.js +11 -3
  145. package/dist/src/cli/watch-run-input.d.ts +14 -0
  146. package/dist/src/cli/watch-run-input.js +49 -0
  147. package/dist/src/cli/watch-server.d.ts +3 -0
  148. package/dist/src/cli/watch-server.js +77 -8
  149. package/dist/src/cli/watch-start-view.d.ts +18 -0
  150. package/dist/src/cli/watch-start-view.js +77 -0
  151. package/dist/src/cli/watch-turn.d.ts +1 -0
  152. package/dist/src/cli/watch-turn.js +8 -3
  153. package/dist/src/cli/watch-usage.d.ts +6 -0
  154. package/dist/src/cli/watch-usage.js +65 -0
  155. package/dist/src/cli/watch-view.d.ts +22 -0
  156. package/dist/src/cli/watch-view.js +111 -19
  157. package/dist/src/cli.js +48 -6
  158. package/dist/src/daemon/analysis-launch.d.ts +5 -0
  159. package/dist/src/daemon/analysis-launch.js +81 -0
  160. package/dist/src/daemon/analysis-token.d.ts +6 -0
  161. package/dist/src/daemon/analysis-token.js +88 -0
  162. package/dist/src/daemon/analysis-worker.d.ts +20 -0
  163. package/dist/src/daemon/analysis-worker.js +271 -0
  164. package/dist/src/daemon/chats-batch-binding.d.ts +23 -0
  165. package/dist/src/daemon/chats-batch-binding.js +99 -0
  166. package/dist/src/daemon/chats-capture.d.ts +18 -0
  167. package/dist/src/daemon/chats-capture.js +194 -0
  168. package/dist/src/daemon/chats-checkpoints.d.ts +41 -0
  169. package/dist/src/daemon/chats-checkpoints.js +186 -0
  170. package/dist/src/daemon/chats-files.d.ts +42 -0
  171. package/dist/src/daemon/chats-files.js +252 -0
  172. package/dist/src/daemon/chats-follow-up.d.ts +38 -0
  173. package/dist/src/daemon/chats-follow-up.js +180 -0
  174. package/dist/src/daemon/chats-question.d.ts +15 -0
  175. package/dist/src/daemon/chats-question.js +93 -0
  176. package/dist/src/daemon/chats-worker.d.ts +13 -0
  177. package/dist/src/daemon/chats-worker.js +201 -0
  178. package/dist/src/daemon/claude-turn.d.ts +11 -0
  179. package/dist/src/daemon/claude-turn.js +96 -15
  180. package/dist/src/daemon/claude-worker.d.ts +2 -1
  181. package/dist/src/daemon/claude-worker.js +92 -45
  182. package/dist/src/daemon/codex-stream.js +16 -0
  183. package/dist/src/daemon/daemon.d.ts +8 -1
  184. package/dist/src/daemon/daemon.js +51 -14
  185. package/dist/src/daemon/delivery-router.js +6 -0
  186. package/dist/src/daemon/github-calls.d.ts +4 -0
  187. package/dist/src/daemon/github-calls.js +16 -7
  188. package/dist/src/daemon/github-ci.d.ts +6 -0
  189. package/dist/src/daemon/github-ci.js +170 -0
  190. package/dist/src/daemon/github-merge-call.d.ts +14 -0
  191. package/dist/src/daemon/github-merge-call.js +73 -0
  192. package/dist/src/daemon/github-merge.d.ts +3 -5
  193. package/dist/src/daemon/github-merge.js +52 -95
  194. package/dist/src/daemon/github-methods.d.ts +28 -0
  195. package/dist/src/daemon/github-methods.js +84 -16
  196. package/dist/src/daemon/github-operation.d.ts +2 -0
  197. package/dist/src/daemon/github-retry.d.ts +6 -0
  198. package/dist/src/daemon/github-retry.js +49 -0
  199. package/dist/src/daemon/github-ticket-status.d.ts +3 -0
  200. package/dist/src/daemon/github-ticket-status.js +294 -0
  201. package/dist/src/daemon/github-worker.js +2 -1
  202. package/dist/src/daemon/handoff-worker.d.ts +10 -0
  203. package/dist/src/daemon/handoff-worker.js +232 -0
  204. package/dist/src/daemon/issue-queue.d.ts +2 -0
  205. package/dist/src/daemon/issue-queue.js +186 -43
  206. package/dist/src/daemon/notify-journal.d.ts +18 -0
  207. package/dist/src/daemon/notify-journal.js +108 -0
  208. package/dist/src/daemon/notify-message.js +2 -1
  209. package/dist/src/daemon/notify.js +176 -37
  210. package/dist/src/daemon/operation-journal.d.ts +1 -0
  211. package/dist/src/daemon/operation-journal.js +4 -2
  212. package/dist/src/daemon/retrying-turn.d.ts +12 -0
  213. package/dist/src/daemon/retrying-turn.js +231 -0
  214. package/dist/src/daemon/ticket-dependency-waits.d.ts +18 -0
  215. package/dist/src/daemon/ticket-dependency-waits.js +60 -0
  216. package/dist/src/daemon/ticket-registration.d.ts +8 -4
  217. package/dist/src/daemon/ticket-registration.js +76 -22
  218. package/dist/src/daemon/ticket-source.d.ts +4 -1
  219. package/dist/src/daemon/ticket-source.js +8 -2
  220. package/dist/src/decision-visibility.d.ts +2 -0
  221. package/dist/src/decision-visibility.js +10 -0
  222. package/dist/src/execution-pin.d.ts +4 -0
  223. package/dist/src/execution-pin.js +23 -2
  224. package/dist/src/host/start-delivery.d.ts +2 -1
  225. package/dist/src/host/start-delivery.js +1 -1
  226. package/dist/src/host/step-bridge-host.d.ts +1 -0
  227. package/dist/src/host/step-bridge-host.js +16 -0
  228. package/dist/src/host/world-reconciliation.d.ts +3 -0
  229. package/dist/src/host/world-reconciliation.js +9 -0
  230. package/dist/src/host/world-runs.js +14 -1
  231. package/dist/src/issue-identity.d.ts +6 -0
  232. package/dist/src/issue-identity.js +23 -0
  233. package/dist/src/service/queue-runs.d.ts +3 -0
  234. package/dist/src/service/queue-runs.js +39 -0
  235. package/dist/src/service/registry.d.ts +8 -2
  236. package/dist/src/service/registry.js +20 -8
  237. package/dist/src/service/replies.d.ts +2 -0
  238. package/dist/src/service/run-handlers.js +54 -30
  239. package/dist/src/service/service.d.ts +3 -0
  240. package/dist/src/service/service.js +3 -1
  241. package/dist/src/service/submission-handler.d.ts +1 -0
  242. package/dist/src/service/submission-handler.js +92 -13
  243. package/dist/src/service/worker-totals.d.ts +3 -0
  244. package/dist/src/service/worker-totals.js +52 -0
  245. package/dist/src/step-bridge.d.ts +9 -0
  246. package/dist/src/steps/handoff.d.ts +2 -0
  247. package/dist/src/steps/handoff.js +13 -0
  248. package/dist/src/ticket-dependencies.d.ts +1 -0
  249. package/dist/src/ticket-dependencies.js +15 -0
  250. package/dist/src/ticket-status.d.ts +8 -0
  251. package/dist/src/ticket-status.js +48 -0
  252. package/dist/src/token-prices.d.ts +21 -0
  253. package/dist/src/token-prices.js +108 -0
  254. package/dist/src/worker-usage.d.ts +31 -0
  255. package/dist/src/worker-usage.js +44 -0
  256. package/dist/src/workflow-admission.d.ts +1 -0
  257. package/dist/src/workflow-candidate.d.ts +29 -0
  258. package/dist/src/workflow-candidate.js +60 -0
  259. package/dist/src/workflow-contracts.d.ts +4 -0
  260. package/dist/src/workflow-contracts.js +32 -5
  261. package/dist/src/workflow-declaration.d.ts +12 -1
  262. package/dist/src/workflow-declaration.js +71 -13
  263. package/dist/src/workflow-discovery.d.ts +18 -0
  264. package/dist/src/workflow-discovery.js +33 -0
  265. package/dist/src/workflow-refusals.d.ts +6 -0
  266. package/dist/src/workflow-refusals.js +39 -0
  267. package/dist/src/workflows.d.ts +3 -1
  268. package/dist/src/workflows.js +2 -0
  269. package/dist/tools/admit-workflow.d.ts +13 -0
  270. package/dist/tools/admit-workflow.js +94 -0
  271. package/dist/tools/self-host-processes.js +7 -2
  272. package/dist/tools/self-host-project.d.ts +17 -0
  273. package/dist/tools/self-host-project.js +177 -0
  274. package/dist/tools/self-host-workflows.d.ts +1 -1
  275. package/dist/tools/self-host-workflows.js +2 -2
  276. package/dist/tools/self-host.js +30 -84
  277. package/dist/tools/self-update-host.d.ts +7 -0
  278. package/dist/tools/self-update-host.js +48 -1
  279. package/dist/tools/self-update.d.ts +1 -0
  280. package/dist/tools/self-update.js +85 -13
  281. package/dist/tools/workflow-bundle-dependencies.d.ts +5 -0
  282. package/dist/tools/workflow-bundle-dependencies.js +93 -0
  283. package/dist/tools/workflow-bundles.d.ts +20 -0
  284. package/dist/tools/workflow-bundles.js +173 -0
  285. package/dist/tools/workflow-candidate-counts.d.ts +8 -0
  286. package/dist/tools/workflow-candidate-counts.js +251 -0
  287. package/dist/tools/workflow-candidate-program.d.ts +11 -0
  288. package/dist/tools/workflow-candidate-program.js +78 -0
  289. package/dist/tools/workflow-candidate-source.d.ts +6 -0
  290. package/dist/tools/workflow-candidate-source.js +107 -0
  291. package/dist/tools/workflow-candidate-syntax.d.ts +15 -0
  292. package/dist/tools/workflow-candidate-syntax.js +219 -0
  293. package/dist/tools/workflow-folder-capture.d.ts +17 -0
  294. package/dist/tools/workflow-folder-capture.js +216 -0
  295. package/dist/tools/workflow-folders.d.ts +33 -0
  296. package/dist/tools/workflow-folders.js +150 -0
  297. package/dist/tools/workflow-source-effects.d.ts +10 -0
  298. package/dist/tools/workflow-source-effects.js +213 -0
  299. package/dist/workflows/analyze-chats.d.ts +15 -0
  300. package/dist/workflows/analyze-chats.declaration.json +22 -0
  301. package/dist/workflows/analyze-chats.js +250 -0
  302. package/dist/workflows/develop-prd.js +71 -3
  303. package/dist/workflows/develop-ticket.d.ts +1 -7
  304. package/dist/workflows/develop-ticket.js +47 -141
  305. package/dist/workflows/issue-labels.d.ts +3 -1
  306. package/dist/workflows/issue-labels.js +34 -0
  307. package/dist/workflows/ticket-prompts.d.ts +35 -0
  308. package/dist/workflows/ticket-prompts.js +143 -0
  309. package/package.json +25 -5
@@ -1,4 +1,5 @@
1
1
  import { protectedEffect } from "cloving/workflows/steps/protected-effect";
2
+ import { handoff } from "cloving/workflows/steps/handoff";
2
3
  const draft = "cloving-prd-draft.md";
3
4
  const noCommit = "Do not commit and do not push. Work only in this work tree.";
4
5
  function issueNumber(result) {
@@ -67,9 +68,76 @@ function requests(input) {
67
68
  units: 1,
68
69
  }));
69
70
  }
71
+ function readyIssue(input, issueResult) {
72
+ const { number, result } = issueResult;
73
+ const created = JSON.parse(result ?? "null");
74
+ const url = typeof created === "object" && created !== null && "url" in created
75
+ ? created.url
76
+ : undefined;
77
+ const issue = typeof url === "string"
78
+ ? /^https?:\/\/[^/\s]+\/([\w.-]+)\/([\w.-]+)\/issues\/([1-9]\d*)$/.exec(url)
79
+ : null;
80
+ if (issue === null || Number(issue[3]) !== number)
81
+ throw new Error("invalid-issue-result");
82
+ return {
83
+ scope: { phase: "prd", operation: "github-ready", round: 1 },
84
+ capability: "github",
85
+ method: "label",
86
+ target: input.cycleId,
87
+ units: 1,
88
+ payload: JSON.stringify({
89
+ owner: issue[1],
90
+ repository: issue[2],
91
+ number,
92
+ add: ["cloving: ready"],
93
+ remove: [],
94
+ }),
95
+ };
96
+ }
97
+ async function handoffIssue(input, created) {
98
+ const { number } = created;
99
+ const outcome = {
100
+ kind: "handoff_requested",
101
+ cycleId: input.cycleId,
102
+ promotionId: `issue-${String(number)}`,
103
+ };
104
+ try {
105
+ await handoff({
106
+ workflowId: "develop-issue",
107
+ input: { ticket: String(number) },
108
+ });
109
+ return outcome;
110
+ }
111
+ catch (error) {
112
+ const reason = handoffReason(error);
113
+ const queued = await protectedEffect(readyIssue(input, created));
114
+ return {
115
+ ...outcome,
116
+ reason,
117
+ ...(queued.kind === "completed" ? {} : { issueUpdate: "failed" }),
118
+ };
119
+ }
120
+ }
121
+ function handoffReason(error) {
122
+ const cause = typeof error === "object" && error !== null && "cause" in error
123
+ ? error.cause
124
+ : error;
125
+ if (typeof cause !== "object" ||
126
+ cause === null ||
127
+ !("message" in cause) ||
128
+ typeof cause.message !== "string")
129
+ return "handoff-failed";
130
+ return /^[a-z][a-z0-9-]{0,127}$/.test(cause.message)
131
+ ? cause.message
132
+ : "handoff-failed";
133
+ }
70
134
  export async function developPrd(input) {
71
135
  "use workflow";
72
- let promotionId = "";
136
+ let outcome = {
137
+ kind: "handoff_requested",
138
+ cycleId: input.cycleId,
139
+ promotionId: "",
140
+ };
73
141
  for (const request of requests(input)) {
74
142
  const result = await protectedEffect(request);
75
143
  if (result.kind !== "completed")
@@ -91,7 +159,7 @@ export async function developPrd(input) {
91
159
  reason: "invalid-issue-result",
92
160
  evidenceId: request.scope.operation,
93
161
  };
94
- promotionId = `issue-${String(number)}`;
162
+ outcome = await handoffIssue(input, { number, result: result.result });
95
163
  }
96
- return { kind: "handoff_requested", cycleId: input.cycleId, promotionId };
164
+ return outcome;
97
165
  }
@@ -1,11 +1,5 @@
1
1
  import type { CycleOutcome } from "cloving/workflows";
2
- interface Ticket {
3
- readonly cycleId: string;
4
- readonly ticket: string;
5
- readonly ticketId?: string;
6
- readonly repository: string;
7
- readonly delivery?: string;
8
- }
2
+ import { type Ticket } from "./ticket-prompts.ts";
9
3
  interface Decision {
10
4
  readonly gateId: string;
11
5
  readonly decision: string;
@@ -1,77 +1,11 @@
1
1
  import { decisionGate } from "cloving/workflows/steps/decision-gate";
2
2
  import { protectedEffect } from "cloving/workflows/steps/protected-effect";
3
3
  import { finishIssue, labelIssue } from "./issue-labels.js";
4
- const lastRound = 3;
4
+ import { fields, part, lastRound, review, prompt, turnPrompt, pullTitle, pullBody, } from "./ticket-prompts.js";
5
5
  const lastFix = 3;
6
- const publicRule = "The branch becomes a public pull request. Do not write local file paths, such as the path of your home folder, in files or in commit messages.";
7
- function instructions(repository) {
8
- return [
9
- "You work on one ticket of this repository. Your current directory is a git work tree on its own branch.",
10
- `The main repository is at ${repository}. Do not change files there.`,
11
- `If this work tree has no node_modules folder and the main repository has one, make a copy-on-write clone: cp -c -R ${repository}/node_modules node_modules`,
12
- "Follow the instructions of the repository (AGENTS.md and CLAUDE.md).",
13
- "Use test-driven development: write a test that fails, see it fail, then write the code.",
14
- "Run the checks of the repository before you finish.",
15
- "Commit your work on the current branch. Do not push. Do not change a different branch.",
16
- "End with a short report: what you changed, the checks that you ran with their results, and what is not done.",
17
- ].join("\n");
18
- }
19
- function named(input) {
20
- return input.ticketId === undefined
21
- ? "this ticket"
22
- : `the ticket ${input.ticketId}`;
23
- }
24
- function fields(text) {
25
- const value = JSON.parse(text ?? "{}");
26
- return typeof value === "object" && value !== null ? { ...value } : {};
27
- }
28
- function part(turn, name) {
29
- const value = turn[name];
30
- if (Array.isArray(value))
31
- return value.join("\n");
32
- if (typeof value === "number")
33
- return value.toFixed(2);
34
- return typeof value === "string" ? value : "";
35
- }
36
- function denial(round) {
37
- return round < lastRound
38
- ? "Deny with a feedback text to send the work back to the worker for one more round. Deny with no text to reject the work."
39
- : "Deny to reject the work. This is the last round.";
40
- }
41
- function review(input, turnResult) {
42
- const turn = fields(turnResult.result);
43
- return [
44
- `Review the work of the Claude worker for ${named(input)}.`,
45
- `Round: ${String(turnResult.round)} of ${String(lastRound)}`,
46
- `Branch: ${part(turn, "branch")}`,
47
- `Work tree: ${part(turn, "worktree")}`,
48
- `Cost (USD): ${part(turn, "costUsd")}`,
49
- "",
50
- "Commits:",
51
- part(turn, "commits"),
52
- "",
53
- "Changed files:",
54
- part(turn, "files"),
55
- "",
56
- "Not committed:",
57
- part(turn, "uncommitted"),
58
- "",
59
- "Report of the worker:",
60
- part(turn, "summary"),
61
- "",
62
- "Approve to accept the branch, then merge the branch yourself.",
63
- denial(turnResult.round),
64
- ].join("\n");
65
- }
66
6
  function blocked(cycleId, cause) {
67
7
  return { kind: "blocked", cycleId, ...cause };
68
8
  }
69
- function prompt(input, round) {
70
- const ticket = `${instructions(input.repository)}\n\nThe text of ${named(input)}:\n\n${input.ticket}`;
71
- if (round.feedback === null)
72
- return ticket;
73
- return `${ticket}\n\nThis is round ${String(round.number)}. The branch has the commits of the earlier rounds. The operator denied the work of round ${String(round.number - 1)} with this feedback:\n\n${round.feedback}`;
74
- }
75
9
  function decided(cycleId, decision) {
76
10
  const number = Number(decision.gateId.slice("review-".length));
77
11
  if (decision.decision === "approved")
@@ -92,61 +26,6 @@ function decided(cycleId, decision) {
92
26
  });
93
27
  return { number: number + 1, feedback: decision.answer };
94
28
  }
95
- function pullPrompt(input) {
96
- return `${instructions(input.repository)}\n${publicRule}\n\nThe text of ${named(input)}:\n\n${input.ticket}`;
97
- }
98
- function findingLines(findings) {
99
- if (!Array.isArray(findings))
100
- return "";
101
- return findings
102
- .map((item) => {
103
- const finding = fields(JSON.stringify(item));
104
- const line = finding["line"];
105
- const place = typeof line === "number" ? `:${String(line)}` : "";
106
- return `- ${part(finding, "path")}${place}: ${part(finding, "body")}`;
107
- })
108
- .join("\n");
109
- }
110
- function turnPrompt(input, fix) {
111
- if (fix === null)
112
- return pullPrompt(input);
113
- const { round, pull, review } = fix;
114
- if (typeof review["reason"] === "string")
115
- return [
116
- pullPrompt(input),
117
- "",
118
- `This is round ${String(round)}. The merge of the pull request ${pull.url} needs a fix: ${review["reason"]}.`,
119
- review["reason"] === "merge-conflict"
120
- ? "Merge the default branch of origin into the branch, resolve each conflict, run the tests, and commit."
121
- : "The check failed on the tree of the merge of the branch with the newest commit of the default branch. Fix the cause and commit the fix.",
122
- "",
123
- part(review, "output"),
124
- ].join("\n");
125
- return [
126
- pullPrompt(input),
127
- "",
128
- `This is round ${String(round)}. The branch has the commits of the earlier rounds. Greptile reviewed the pull request ${pull.url} at the commit ${pull.commit.slice(0, 7)} and gave the confidence score ${String(review["score"])}/5. Fix each problem of the review, then commit the fixes. Cloving merges the pull request when the score is 5/5.`,
129
- "",
130
- "Summary of the review:",
131
- "",
132
- part(review, "summary"),
133
- "",
134
- "Findings on lines:",
135
- "",
136
- findingLines(review["findings"]),
137
- ].join("\n");
138
- }
139
- function pullTitle(input) {
140
- const [first = ""] = input.ticket.split("\n", 1);
141
- return first.startsWith("# ")
142
- ? first.slice(2).trim().slice(0, 200)
143
- : `Cloving task ${input.cycleId}`;
144
- }
145
- function pullBody(input) {
146
- const issue = /^GitHub issue: ([\w.-]+[/][\w.-]+#\d+)/m.exec(input.ticket);
147
- const about = `Cloving made this change for ${named(input)}. Greptile reviews the pull request, and Cloving merges it when the confidence score is 5/5.`;
148
- return issue === null ? about : `Closes ${String(issue[1])}\n\n${about}`;
149
- }
150
29
  async function stepped(input, call) {
151
30
  const { phase, operation, round, capability, method, payload } = call;
152
31
  const done = await protectedEffect({
@@ -197,12 +76,40 @@ async function merged(input, done) {
197
76
  cycleId: input.cycleId,
198
77
  promotionId: `pull-request-${String(number)}`,
199
78
  };
79
+ if (merge.result["reason"] === "ci-pending")
80
+ return blocked(input.cycleId, {
81
+ reason: `The merge has the result ci-pending: ${part(merge.result, "output")}. The pull request stays open: ${done.pull.url}`,
82
+ evidenceId: "github-merge",
83
+ });
200
84
  if (done.round > lastFix)
201
85
  return blocked(input.cycleId, {
202
86
  reason: `The merge has the result ${merge.result["reason"]} in round ${String(done.round)}, and a run has at most ${String(lastFix)} fix rounds. The pull request stays open: ${done.pull.url}`,
203
87
  evidenceId: "github-merge",
204
88
  });
205
- return { round: done.round + 1, pull: done.pull, review: merge.result };
89
+ return {
90
+ round: done.round + 1,
91
+ pull: done.pull,
92
+ review: merge.result,
93
+ report: done.report,
94
+ };
95
+ }
96
+ async function reviewedPull(input, round) {
97
+ const opened = await github(input, {
98
+ method: "open-pr",
99
+ round,
100
+ payload: { title: pullTitle(input), body: pullBody(input) },
101
+ });
102
+ if (opened.kind !== "done")
103
+ return opened;
104
+ const pull = pullOf(opened.result);
105
+ const reviewed = await github(input, {
106
+ method: "wait-review",
107
+ round,
108
+ payload: { number: pull.number, commit: pull.commit },
109
+ });
110
+ return reviewed.kind === "done"
111
+ ? { kind: "reviewed", pull, review: reviewed.result }
112
+ : reviewed;
206
113
  }
207
114
  async function pullRequest(input) {
208
115
  const prepared = await github(input, {
@@ -225,26 +132,20 @@ async function pullRequest(input) {
225
132
  });
226
133
  if (turn.kind !== "done")
227
134
  return finishIssue(input, { round, outcome: turn });
228
- const opened = await github(input, {
229
- method: "open-pr",
230
- round,
231
- payload: { title: pullTitle(input), body: pullBody(input) },
232
- });
233
- if (opened.kind !== "done")
234
- return finishIssue(input, { round, outcome: opened });
235
- const pull = pullOf(opened.result);
236
- const reviewed = await github(input, {
237
- method: "wait-review",
238
- round,
239
- payload: { number: pull.number, commit: pull.commit },
240
- });
241
- if (reviewed.kind !== "done")
135
+ const reviewed = await reviewedPull(input, round);
136
+ if (reviewed.kind !== "reviewed")
242
137
  return finishIssue(input, { round, outcome: reviewed });
243
- const score = reviewed.result["score"];
138
+ const { pull } = reviewed;
139
+ const score = reviewed.review["score"];
244
140
  if (score === 5) {
245
- const result = await merged(input, { round, pull });
141
+ const result = await merged(input, { round, pull, report: turn.result });
246
142
  if ("kind" in result)
247
- return finishIssue(input, { round, outcome: result });
143
+ return finishIssue(input, {
144
+ round,
145
+ outcome: result,
146
+ summary: part(turn.result, "summary"),
147
+ status: turn.result["ticketStatus"],
148
+ });
248
149
  fix = result;
249
150
  continue;
250
151
  }
@@ -256,7 +157,12 @@ async function pullRequest(input) {
256
157
  evidenceId: "github-wait-review",
257
158
  }),
258
159
  });
259
- fix = { round: round + 1, pull, review: reviewed.result };
160
+ fix = {
161
+ round: round + 1,
162
+ pull,
163
+ review: reviewed.review,
164
+ report: turn.result,
165
+ };
260
166
  }
261
167
  }
262
168
  export async function developTicket(input, decision) {
@@ -1,4 +1,4 @@
1
- import type { CycleOutcome } from "cloving/workflows";
1
+ import { type CycleOutcome } from "cloving/workflows";
2
2
  interface IssueTicket {
3
3
  readonly cycleId: string;
4
4
  readonly ticket: string;
@@ -10,5 +10,7 @@ export declare function labelIssue(input: IssueTicket, state: {
10
10
  export declare function finishIssue(input: IssueTicket, ending: {
11
11
  readonly round: number;
12
12
  readonly outcome: CycleOutcome;
13
+ readonly summary?: string;
14
+ readonly status?: unknown;
13
15
  }): Promise<CycleOutcome>;
14
16
  export {};
@@ -1,3 +1,4 @@
1
+ import { ticketStatus } from "cloving/workflows";
1
2
  import { protectedEffect } from "cloving/workflows/steps/protected-effect";
2
3
  export async function labelIssue(input, state) {
3
4
  const issue = /^GitHub issue: ([\w.-]+)\/([\w.-]+)#(\d+)/m.exec(input.ticket);
@@ -28,9 +29,42 @@ export async function labelIssue(input, state) {
28
29
  });
29
30
  }
30
31
  export async function finishIssue(input, ending) {
32
+ if (ending.outcome.kind === "handoff_requested")
33
+ return finishMergedIssue(input, ending);
31
34
  await labelIssue(input, {
32
35
  round: ending.round,
33
36
  key: ending.outcome.kind === "blocked" ? "blocked" : "done",
34
37
  });
35
38
  return ending.outcome;
36
39
  }
40
+ async function finishMergedIssue(input, ending) {
41
+ if (ending.outcome.kind !== "handoff_requested")
42
+ return ending.outcome;
43
+ const status = ticketStatus(ending.summary ?? "", ending.status);
44
+ const method = status.kind === "complete" ? "close-issue" : "partial-issue";
45
+ const issue = /^GitHub issue: ([\w.-]+)\/([\w.-]+)#(\d+)/m.exec(input.ticket);
46
+ const outcome = { ...ending.outcome, ticketStatus: status.kind };
47
+ if (issue === null)
48
+ return outcome;
49
+ const update = await protectedEffect({
50
+ scope: {
51
+ phase: "pull-request",
52
+ operation: `github-${method}`,
53
+ round: ending.round,
54
+ },
55
+ capability: "github",
56
+ method,
57
+ target: input.cycleId,
58
+ units: 1,
59
+ payload: JSON.stringify({
60
+ owner: issue[1],
61
+ repository: issue[2],
62
+ number: Number(issue[3]),
63
+ pullRequest: Number(ending.outcome.promotionId.slice("pull-request-".length)),
64
+ ...(status.kind === "partial" ? { remaining: status.remaining } : {}),
65
+ }),
66
+ });
67
+ if (update.kind === "completed")
68
+ return outcome;
69
+ return { ...outcome, issueUpdate: "failed" };
70
+ }
@@ -0,0 +1,35 @@
1
+ export interface Ticket {
2
+ readonly cycleId: string;
3
+ readonly ticket: string;
4
+ readonly ticketId?: string;
5
+ readonly repository: string;
6
+ readonly delivery?: string;
7
+ }
8
+ export interface Round {
9
+ readonly number: number;
10
+ readonly feedback: string | null;
11
+ }
12
+ export interface Pull {
13
+ readonly number: number;
14
+ readonly url: string;
15
+ readonly commit: string;
16
+ }
17
+ export interface Fix {
18
+ readonly round: number;
19
+ readonly pull: Pull;
20
+ readonly review: Fields;
21
+ readonly report: Fields;
22
+ }
23
+ export declare const lastRound = 3;
24
+ type Fields = Readonly<Record<string, unknown>>;
25
+ export declare function fields(text: string | null): Readonly<Record<string, unknown>>;
26
+ export declare function part(turn: Readonly<Record<string, unknown>>, name: string): string;
27
+ export declare function review(input: Ticket, turnResult: {
28
+ readonly round: number;
29
+ readonly result: string | null;
30
+ }): string;
31
+ export declare function prompt(input: Ticket, round: Round): string;
32
+ export declare function turnPrompt(input: Ticket, fix: Fix | null): string;
33
+ export declare function pullTitle(input: Ticket): string;
34
+ export declare function pullBody(input: Ticket): string;
35
+ export {};
@@ -0,0 +1,143 @@
1
+ import { ticketStatus } from "cloving/workflows";
2
+ export const lastRound = 3;
3
+ const publicRule = "The branch becomes a public pull request. Do not write local file paths, such as the path of your home folder, in files or in commit messages.";
4
+ function instructions(repository) {
5
+ return [
6
+ "You work on one ticket of this repository. Your current directory is a git work tree on its own branch.",
7
+ `The main repository is at ${repository}. Do not change files there.`,
8
+ `If this work tree has no node_modules folder and the main repository has one, make a copy-on-write clone: cp -c -R ${repository}/node_modules node_modules`,
9
+ "Follow the instructions of the repository (AGENTS.md and CLAUDE.md).",
10
+ "Use test-driven development: write a test that fails, see it fail, then write the code.",
11
+ "Run the checks of the repository before you finish. Commit your work before the report. If the repository provides npm run check:clean, run it after committing.",
12
+ "Treat a check time warning as work to do in the same ticket when your change made the check slower.",
13
+ "Commit your work on the current branch. Do not push. Do not change a different branch.",
14
+ "End with a short report: what you changed, the checks that you ran with their results, and what is not done. End the report with exactly one of the lines Ticket status: complete or Ticket status: partial. Before a partial status line, include Remaining: followed by a list with one item on each line, using a dash and a space before each item. Blank lines are allowed around and between list items.",
15
+ ].join("\n");
16
+ }
17
+ function named(input) {
18
+ return input.ticketId === undefined
19
+ ? "this ticket"
20
+ : `the ticket ${input.ticketId}`;
21
+ }
22
+ export function fields(text) {
23
+ const value = JSON.parse(text ?? "{}");
24
+ return typeof value === "object" && value !== null ? { ...value } : {};
25
+ }
26
+ export function part(turn, name) {
27
+ const value = turn[name];
28
+ if (Array.isArray(value))
29
+ return value.join("\n");
30
+ if (typeof value === "number")
31
+ return value.toFixed(2);
32
+ return typeof value === "string" ? value : "";
33
+ }
34
+ function denial(round) {
35
+ return round < lastRound
36
+ ? "Deny with a feedback text to send the work back to the worker for one more round. Deny with no text to reject the work."
37
+ : "Deny to reject the work. This is the last round.";
38
+ }
39
+ export function review(input, turnResult) {
40
+ const turn = fields(turnResult.result);
41
+ return [
42
+ `Review the work of the Claude worker for ${named(input)}.`,
43
+ `Round: ${String(turnResult.round)} of ${String(lastRound)}`,
44
+ `Branch: ${part(turn, "branch")}`,
45
+ `Work tree: ${part(turn, "worktree")}`,
46
+ `Cost (USD): ${part(turn, "costUsd")}`,
47
+ "",
48
+ "Commits:",
49
+ part(turn, "commits"),
50
+ "",
51
+ "Changed files:",
52
+ part(turn, "files"),
53
+ "",
54
+ "Not committed:",
55
+ part(turn, "uncommitted"),
56
+ "",
57
+ "Report of the worker:",
58
+ part(turn, "summary"),
59
+ "",
60
+ "Approve to accept the branch, then merge the branch yourself.",
61
+ denial(turnResult.round),
62
+ ].join("\n");
63
+ }
64
+ export function prompt(input, round) {
65
+ const ticket = `${instructions(input.repository)}\n\nThe text of ${named(input)}:\n\n${input.ticket}`;
66
+ if (round.feedback === null)
67
+ return ticket;
68
+ return `${ticket}\n\nThis is round ${String(round.number)}. The branch has the commits of the earlier rounds. The operator denied the work of round ${String(round.number - 1)} with this feedback:\n\n${round.feedback}`;
69
+ }
70
+ function pullPrompt(input) {
71
+ return `${instructions(input.repository)}\n${publicRule}\n\nThe text of ${named(input)}:\n\n${input.ticket}`;
72
+ }
73
+ function findingLines(findings) {
74
+ if (!Array.isArray(findings))
75
+ return "";
76
+ return findings
77
+ .map((item) => {
78
+ const finding = fields(JSON.stringify(item));
79
+ const line = finding["line"];
80
+ const place = typeof line === "number" ? `:${String(line)}` : "";
81
+ return `- ${part(finding, "path")}${place}: ${part(finding, "body")}`;
82
+ })
83
+ .join("\n");
84
+ }
85
+ export function turnPrompt(input, fix) {
86
+ if (fix === null)
87
+ return pullPrompt(input);
88
+ const { round, pull, review } = fix;
89
+ const status = ticketStatus(part(fix.report, "summary"), fix.report["ticketStatus"]);
90
+ const continuation = status.kind === "complete"
91
+ ? ""
92
+ : [
93
+ "",
94
+ "Remaining work from the previous worker report:",
95
+ ...status.remaining.map((item) => `- ${item}`),
96
+ "Reassess each remaining item before reporting Ticket status: complete. Keep every unfinished item in the next Remaining: list.",
97
+ ].join("\n");
98
+ const ticket = pullPrompt(input) + continuation;
99
+ if (review["reason"] === "ci-failed")
100
+ return [
101
+ ticket,
102
+ "",
103
+ `This is round ${String(round)}. The CI checks of the pull request ${pull.url} failed.`,
104
+ "Fix the failed CI checks, including failures that the pull request did not cause, and commit the fix.",
105
+ "",
106
+ part(review, "output"),
107
+ ].join("\n");
108
+ if (typeof review["reason"] === "string")
109
+ return [
110
+ ticket,
111
+ "",
112
+ `This is round ${String(round)}. The merge of the pull request ${pull.url} needs a fix: ${review["reason"]}.`,
113
+ review["reason"] === "merge-conflict"
114
+ ? "Merge the default branch of origin into the branch, resolve each conflict, run the tests, and commit."
115
+ : "The check failed on the tree of the merge of the branch with the newest commit of the default branch. Fix the cause and commit the fix.",
116
+ "",
117
+ part(review, "output"),
118
+ ].join("\n");
119
+ return [
120
+ ticket,
121
+ "",
122
+ `This is round ${String(round)}. The branch has the commits of the earlier rounds. Greptile reviewed the pull request ${pull.url} at the commit ${pull.commit.slice(0, 7)} and gave the confidence score ${String(review["score"])}/5. Fix each problem of the review, then commit the fixes. Cloving merges the pull request when the score is 5/5.`,
123
+ "",
124
+ "Summary of the review:",
125
+ "",
126
+ part(review, "summary"),
127
+ "",
128
+ "Findings on lines:",
129
+ "",
130
+ findingLines(review["findings"]),
131
+ ].join("\n");
132
+ }
133
+ export function pullTitle(input) {
134
+ const [first = ""] = input.ticket.split("\n", 1);
135
+ return first.startsWith("# ")
136
+ ? first.slice(2).trim().slice(0, 200)
137
+ : `Cloving task ${input.cycleId}`;
138
+ }
139
+ export function pullBody(input) {
140
+ const issue = /^GitHub issue: ([\w.-]+[/][\w.-]+#\d+)/m.exec(input.ticket);
141
+ const about = `Cloving made this change for ${named(input)}. Greptile reviews the pull request, and Cloving merges it when the confidence score is 5/5.`;
142
+ return issue === null ? about : `Refs ${String(issue[1])}\n\n${about}`;
143
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cloving",
3
- "version": "1.0.1",
3
+ "version": "1.2.0",
4
4
  "description": "Local durable development workflow service",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -34,28 +34,48 @@
34
34
  "./workflows/steps/decision-gate": {
35
35
  "types": "./dist/src/steps/decision-gate.d.ts",
36
36
  "import": "./dist/src/steps/decision-gate.js"
37
+ },
38
+ "./workflows/steps/handoff": {
39
+ "types": "./dist/src/steps/handoff.d.ts",
40
+ "import": "./dist/src/steps/handoff.js"
37
41
  }
38
42
  },
39
43
  "files": [
40
44
  "dist/src",
41
45
  "dist/tools",
42
- "dist/workflows"
46
+ "dist/workflows",
47
+ "assets/watch-fonts/fira-code-latin-400-normal.woff2",
48
+ "assets/watch-fonts/fira-code-latin-500-normal.woff2",
49
+ "assets/watch-fonts/fira-code-OFL.md",
50
+ "assets/watch-fonts/heebo-latin-500-normal.woff2",
51
+ "assets/watch-fonts/heebo-latin-700-normal.woff2",
52
+ "assets/watch-fonts/heebo-latin-800-normal.woff2",
53
+ "assets/watch-fonts/heebo-OFL.md",
54
+ "assets/watch-fonts/space-grotesk-latin-400-normal.woff2",
55
+ "assets/watch-fonts/space-grotesk-latin-500-normal.woff2",
56
+ "assets/watch-fonts/space-grotesk-latin-600-normal.woff2",
57
+ "assets/watch-fonts/space-grotesk-latin-700-normal.woff2",
58
+ "assets/watch-fonts/space-grotesk-OFL.md",
59
+ "assets/watch-fonts/manifest.json"
43
60
  ],
44
61
  "engines": {
45
62
  "node": ">=26.9.0 <27",
46
63
  "npm": ">=11.19.1 <12"
47
64
  },
48
65
  "os": [
49
- "darwin"
66
+ "darwin",
67
+ "linux"
50
68
  ],
51
69
  "cpu": [
52
- "arm64"
70
+ "arm64",
71
+ "x64"
53
72
  ],
54
73
  "scripts": {
55
74
  "build": "tsc --project tsconfig.build.json && node tools/copy-workflow-declarations.ts workflows dist/workflows",
56
75
  "build:workflows": "node tools/compile-workflows.ts",
57
76
  "check:setup": "tsc --project tsconfig.json --noEmit",
58
77
  "check:quality": "node tools/check-quality.ts",
78
+ "check:clean": "node tools/check-clean.ts",
59
79
  "check:lint": "eslint src tools quality test eslint.config.mjs --max-warnings 0",
60
80
  "check:format": "prettier --check src tools quality test eslint.config.mjs package.json tsconfig.json tsconfig.build.json",
61
81
  "test:quality": "node --test test/quality/*.test.ts",
@@ -74,7 +94,6 @@
74
94
  "istanbul-lib-coverage": "3.2.2",
75
95
  "jscpd": "5.4.0",
76
96
  "prettier": "3.9.9",
77
- "typescript": "6.0.3",
78
97
  "typescript-eslint": "8.71.1"
79
98
  },
80
99
  "overrides": {
@@ -88,6 +107,7 @@
88
107
  "entities": "8.1.0",
89
108
  "preact": "11.0.0",
90
109
  "solid-objects": "0.17.8",
110
+ "typescript": "6.0.3",
91
111
  "workflow": "5.1.0"
92
112
  }
93
113
  }