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
@@ -0,0 +1,33 @@
1
+ import type { WorkflowCandidateCode } from "../src/workflow-candidate.ts";
2
+ import { type WorkflowConflictCode, type WorkflowReservation } from "../src/workflow-discovery.ts";
3
+ import type { CandidatePackageOptions } from "./workflow-candidate-program.ts";
4
+ import { type WorkflowCaptureCode } from "./workflow-folder-capture.ts";
5
+ export interface WorkflowFolderInput {
6
+ readonly repository: string;
7
+ readonly home: string;
8
+ readonly xdgConfigHome?: string | undefined;
9
+ readonly bundled: readonly WorkflowReservation[];
10
+ }
11
+ interface WorkflowLabel {
12
+ readonly scope: "project" | "user";
13
+ readonly label: string;
14
+ }
15
+ interface WorkflowSnapshot extends WorkflowLabel {
16
+ readonly baseName: string;
17
+ readonly source: string;
18
+ readonly declaration: string;
19
+ }
20
+ export interface WorkflowFolderCandidate extends WorkflowSnapshot {
21
+ readonly repository: string;
22
+ readonly artifactDigest: string;
23
+ }
24
+ export interface WorkflowFolderRefusal extends WorkflowLabel {
25
+ readonly codes: readonly (WorkflowCandidateCode | WorkflowCaptureCode | WorkflowConflictCode | "compile-failed")[];
26
+ }
27
+ export interface WorkflowFolderResult {
28
+ readonly repository: string;
29
+ readonly candidates: readonly WorkflowFolderCandidate[];
30
+ readonly refusals: readonly WorkflowFolderRefusal[];
31
+ }
32
+ export declare function loadWorkflowFolders(input: WorkflowFolderInput, installed?: CandidatePackageOptions): Promise<WorkflowFolderResult>;
33
+ export {};
@@ -0,0 +1,150 @@
1
+ import { createHash } from "node:crypto";
2
+ import { realpath } from "node:fs/promises";
3
+ import { isAbsolute, join } from "node:path";
4
+ import ts from "typescript";
5
+ import { record } from "../src/protocol.js";
6
+ import { workflowConflicts, } from "../src/workflow-discovery.js";
7
+ import { admitWorkflowCandidate } from "./admit-workflow.js";
8
+ import { captureWorkflowCandidate, inspectWorkflowFolder, workflowCaptureFailure, workflowFolderEntries, } from "./workflow-folder-capture.js";
9
+ function fileLabel(name) {
10
+ return /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}\.ts$/.test(name)
11
+ ? name
12
+ : "file-" + createHash("sha256").update(name).digest("hex").slice(0, 16);
13
+ }
14
+ function parsedDeclaration(text) {
15
+ try {
16
+ return JSON.parse(text);
17
+ }
18
+ catch {
19
+ return null;
20
+ }
21
+ }
22
+ function identity(entry) {
23
+ const snapshot = entry.kind === "captured" ? entry.snapshot : entry;
24
+ const source = ts.createSourceFile("candidate.ts", snapshot.source ?? "", ts.ScriptTarget.Latest, true);
25
+ const workflowId = record(record(parsedDeclaration(snapshot.declaration ?? ""))?.["workflow"])?.["workflowId"];
26
+ return {
27
+ baseName: snapshot.baseName,
28
+ workflowId: typeof workflowId === "string" ? workflowId : undefined,
29
+ functionNames: source.statements
30
+ .filter(ts.isFunctionDeclaration)
31
+ .filter((node) => (ts.getCombinedModifierFlags(node) & ts.ModifierFlags.Export) !== 0)
32
+ .flatMap((node) => (node.name ? [node.name.text] : [])),
33
+ };
34
+ }
35
+ function compareLabels(left, right) {
36
+ const a = `${left.scope}\0${left.label}`;
37
+ const b = `${right.scope}\0${right.label}`;
38
+ return a < b ? -1 : Number(a > b);
39
+ }
40
+ async function scanFolder(location, refusals) {
41
+ let folder;
42
+ let entries;
43
+ try {
44
+ folder = await inspectWorkflowFolder(location.root, location.parts);
45
+ entries = await workflowFolderEntries(folder);
46
+ }
47
+ catch (error) {
48
+ if (!(error instanceof Error && "code" in error && error.code === "ENOENT"))
49
+ refusals.push({
50
+ scope: location.scope,
51
+ label: "workflows/",
52
+ codes: [workflowCaptureFailure(error)],
53
+ });
54
+ return [];
55
+ }
56
+ const snapshots = [];
57
+ for (const name of entries
58
+ .filter((name) => name.endsWith(".ts") && !name.endsWith(".d.ts"))
59
+ .sort()) {
60
+ snapshots.push(await captureEntry(folder, {
61
+ scope: location.scope,
62
+ label: fileLabel(name),
63
+ name,
64
+ }));
65
+ }
66
+ return snapshots;
67
+ }
68
+ async function captureEntry(folder, file) {
69
+ const label = {
70
+ scope: file.scope,
71
+ label: file.label,
72
+ baseName: file.name.slice(0, -3),
73
+ };
74
+ try {
75
+ const capture = await captureWorkflowCandidate(folder, file.name);
76
+ if (capture.source === undefined ||
77
+ capture.declaration === undefined ||
78
+ capture.codes.length > 0)
79
+ return { kind: "refused", ...label, ...capture };
80
+ return {
81
+ kind: "captured",
82
+ snapshot: {
83
+ ...label,
84
+ source: capture.source,
85
+ declaration: capture.declaration,
86
+ },
87
+ };
88
+ }
89
+ catch (error) {
90
+ return {
91
+ kind: "refused",
92
+ ...label,
93
+ codes: [workflowCaptureFailure(error)],
94
+ source: undefined,
95
+ declaration: undefined,
96
+ };
97
+ }
98
+ }
99
+ async function scanFolders(input, refusals) {
100
+ const repository = await realpath(input.repository);
101
+ const configuration = input.xdgConfigHome && isAbsolute(input.xdgConfigHome)
102
+ ? input.xdgConfigHome
103
+ : join(input.home, ".config");
104
+ const snapshots = [
105
+ ...(await scanFolder({ scope: "project", root: repository, parts: [".cloving", "workflows"] }, refusals)),
106
+ ...(await scanFolder({ scope: "user", root: configuration, parts: ["cloving", "workflows"] }, refusals)),
107
+ ];
108
+ return { repository, snapshots };
109
+ }
110
+ export async function loadWorkflowFolders(input, installed = {}) {
111
+ const candidates = [];
112
+ const refusals = [];
113
+ const { repository, snapshots } = await scanFolders(input, refusals);
114
+ const conflicts = workflowConflicts(snapshots.map(identity), input.bundled);
115
+ for (const [index, entry] of snapshots.entries()) {
116
+ const label = entry.kind === "captured" ? entry.snapshot : entry;
117
+ const codes = [
118
+ ...new Set([
119
+ ...(entry.kind === "refused" ? entry.codes : []),
120
+ ...(conflicts[index] ?? []),
121
+ ]),
122
+ ].sort();
123
+ if (entry.kind === "refused" || codes.length > 0) {
124
+ refusals.push({ scope: label.scope, label: label.label, codes });
125
+ continue;
126
+ }
127
+ const { snapshot } = entry;
128
+ const result = await admitWorkflowCandidate({
129
+ source: snapshot.source,
130
+ declaration: parsedDeclaration(snapshot.declaration),
131
+ }, installed);
132
+ if (result.kind === "refused")
133
+ refusals.push({
134
+ scope: snapshot.scope,
135
+ label: snapshot.label,
136
+ codes: result.codes,
137
+ });
138
+ else
139
+ candidates.push({
140
+ ...snapshot,
141
+ repository,
142
+ artifactDigest: result.artifactDigest,
143
+ });
144
+ }
145
+ return {
146
+ repository,
147
+ candidates: candidates.sort(compareLabels),
148
+ refusals: refusals.sort(compareLabels),
149
+ };
150
+ }
@@ -0,0 +1,10 @@
1
+ import ts from "typescript";
2
+ export interface ReferenceContext {
3
+ readonly parent: ts.Node | undefined;
4
+ readonly checker: ts.TypeChecker;
5
+ readonly profile?: "candidate";
6
+ }
7
+ export declare function referenceSymbol(node: ts.Identifier, context: ReferenceContext): ts.Symbol | undefined;
8
+ export declare function typeOnlyEffectNode(node: ts.Node): boolean;
9
+ export declare function restrictedEffect(node: ts.Node, context: ReferenceContext): boolean;
10
+ export declare function sourceEffectNodes(source: ts.SourceFile, context: Omit<ReferenceContext, "parent">): readonly ts.Node[];
@@ -0,0 +1,213 @@
1
+ import ts from "typescript";
2
+ const ambientNames = new Set([
3
+ "globalThis",
4
+ "global",
5
+ "window",
6
+ "self",
7
+ "process",
8
+ "Date",
9
+ "performance",
10
+ "fetch",
11
+ "WebSocket",
12
+ "Worker",
13
+ "crypto",
14
+ "setTimeout",
15
+ "setInterval",
16
+ "setImmediate",
17
+ "queueMicrotask",
18
+ "eval",
19
+ "Function",
20
+ "require",
21
+ "console",
22
+ "navigator",
23
+ "Atomics",
24
+ "SharedArrayBuffer",
25
+ "WeakRef",
26
+ "FinalizationRegistry",
27
+ "Intl",
28
+ ]);
29
+ const candidateNames = new Set([
30
+ "Buffer",
31
+ "document",
32
+ "screen",
33
+ "history",
34
+ "location",
35
+ "localStorage",
36
+ "sessionStorage",
37
+ "indexedDB",
38
+ "caches",
39
+ "Deno",
40
+ "Bun",
41
+ "__dirname",
42
+ "__filename",
43
+ "File",
44
+ "FileReader",
45
+ "readFile",
46
+ "writeFile",
47
+ "XMLHttpRequest",
48
+ "EventSource",
49
+ "WebTransport",
50
+ "BroadcastChannel",
51
+ "MessageChannel",
52
+ "MessagePort",
53
+ "postMessage",
54
+ "clearTimeout",
55
+ "clearInterval",
56
+ "clearImmediate",
57
+ "requestAnimationFrame",
58
+ "requestIdleCallback",
59
+ "cancelAnimationFrame",
60
+ "cancelIdleCallback",
61
+ "AbortSignal",
62
+ "Reflect",
63
+ "Proxy",
64
+ "constructor",
65
+ "WebAssembly",
66
+ ]);
67
+ const reflectiveProperties = new Set(["constructor", "__proto__", "prototype"]);
68
+ const candidateProperties = new Set([
69
+ "getPrototypeOf",
70
+ "setPrototypeOf",
71
+ "getOwnPropertyDescriptor",
72
+ "getOwnPropertyDescriptors",
73
+ ]);
74
+ const pureMathMembers = new Set([
75
+ "E",
76
+ "LN10",
77
+ "LN2",
78
+ "LOG10E",
79
+ "LOG2E",
80
+ "PI",
81
+ "SQRT1_2",
82
+ "SQRT2",
83
+ "abs",
84
+ "acos",
85
+ "acosh",
86
+ "asin",
87
+ "asinh",
88
+ "atan",
89
+ "atan2",
90
+ "atanh",
91
+ "cbrt",
92
+ "ceil",
93
+ "clz32",
94
+ "cos",
95
+ "cosh",
96
+ "exp",
97
+ "expm1",
98
+ "floor",
99
+ "fround",
100
+ "hypot",
101
+ "imul",
102
+ "log",
103
+ "log10",
104
+ "log1p",
105
+ "log2",
106
+ "max",
107
+ "min",
108
+ "pow",
109
+ "round",
110
+ "sign",
111
+ "sin",
112
+ "sinh",
113
+ "sqrt",
114
+ "tan",
115
+ "tanh",
116
+ "trunc",
117
+ ]);
118
+ function localBinding(symbol) {
119
+ return (symbol.declarations ?? []).some((declaration) => !declaration.getSourceFile().isDeclarationFile &&
120
+ (ts.getCombinedModifierFlags(declaration) & ts.ModifierFlags.Ambient) ===
121
+ 0);
122
+ }
123
+ export function referenceSymbol(node, context) {
124
+ const { parent, checker } = context;
125
+ if (parent &&
126
+ (ts.isPropertyAccessExpression(parent) ||
127
+ ts.isPropertyAssignment(parent)) &&
128
+ parent.name === node)
129
+ return undefined;
130
+ if (parent && ts.isShorthandPropertyAssignment(parent))
131
+ return checker.getShorthandAssignmentValueSymbol(parent);
132
+ return checker.getSymbolAtLocation(node);
133
+ }
134
+ function referenceIdentifier(node, parent) {
135
+ if (!ts.isIdentifier(node))
136
+ return false;
137
+ if (!parent)
138
+ return true;
139
+ if ((ts.isPropertyAccessExpression(parent) ||
140
+ ts.isPropertyAssignment(parent)) &&
141
+ parent.name === node)
142
+ return false;
143
+ return !ts.isImportSpecifier(parent);
144
+ }
145
+ function restrictedAmbientName(name, context) {
146
+ return (ambientNames.has(name) ||
147
+ name === "Math" ||
148
+ (context.profile === "candidate" && candidateNames.has(name)));
149
+ }
150
+ function pureMathAccess(node, parent) {
151
+ return (!!parent &&
152
+ ts.isPropertyAccessExpression(parent) &&
153
+ parent.expression === node &&
154
+ pureMathMembers.has(parent.name.text));
155
+ }
156
+ function ambientReference(node, context) {
157
+ const { parent } = context;
158
+ if (!referenceIdentifier(node, parent))
159
+ return false;
160
+ if (!restrictedAmbientName(node.text, context))
161
+ return false;
162
+ const candidate = context.profile === "candidate";
163
+ const symbol = referenceSymbol(node, context);
164
+ if ((!symbol && !candidate) || (symbol && localBinding(symbol)))
165
+ return false;
166
+ return node.text !== "Math" || !pureMathAccess(node, parent);
167
+ }
168
+ function reflectiveAccess(node, context) {
169
+ const candidate = context.profile === "candidate";
170
+ const restricted = (name) => reflectiveProperties.has(name) ||
171
+ (candidate && candidateProperties.has(name));
172
+ if (ts.isPropertyAccessExpression(node))
173
+ return restricted(node.name.text);
174
+ if (ts.isElementAccessExpression(node) &&
175
+ ts.isStringLiteralLike(node.argumentExpression))
176
+ return restricted(node.argumentExpression.text);
177
+ if (!candidate || !ts.isBindingElement(node))
178
+ return false;
179
+ const name = node.propertyName ?? node.name;
180
+ return ((ts.isIdentifier(name) || ts.isStringLiteralLike(name)) &&
181
+ restricted(name.text));
182
+ }
183
+ function dynamicCapability(node, checker) {
184
+ if (!ts.isElementAccessExpression(node))
185
+ return false;
186
+ const target = checker.getTypeAtLocation(node.expression);
187
+ return (target.getCallSignatures().length > 0 ||
188
+ target.getConstructSignatures().length > 0);
189
+ }
190
+ export function typeOnlyEffectNode(node) {
191
+ return ((ts.isTypeNode(node) && !ts.isExpressionWithTypeArguments(node)) ||
192
+ ts.isInterfaceDeclaration(node) ||
193
+ ts.isTypeAliasDeclaration(node));
194
+ }
195
+ export function restrictedEffect(node, context) {
196
+ return (ambientReference(node, context) ||
197
+ reflectiveAccess(node, context) ||
198
+ dynamicCapability(node, context.checker));
199
+ }
200
+ export function sourceEffectNodes(source, context) {
201
+ const findings = [];
202
+ function visit(node, parent) {
203
+ if (typeOnlyEffectNode(node))
204
+ return;
205
+ if (restrictedEffect(node, { ...context, parent }))
206
+ findings.push(node);
207
+ ts.forEachChild(node, (child) => {
208
+ visit(child, node);
209
+ });
210
+ }
211
+ visit(source);
212
+ return findings;
213
+ }
@@ -0,0 +1,15 @@
1
+ import type { CycleOutcome } from "cloving/workflows";
2
+ interface Decision {
3
+ readonly gateId: string;
4
+ readonly decision: string;
5
+ readonly answer: string | null;
6
+ }
7
+ interface AnalysisInput {
8
+ readonly cycleId: string;
9
+ readonly repository: string;
10
+ readonly input: {
11
+ readonly importId: string;
12
+ };
13
+ }
14
+ export declare function analyzeChats(input: AnalysisInput, decision?: Decision): Promise<CycleOutcome>;
15
+ export {};
@@ -0,0 +1,22 @@
1
+ {
2
+ "workflowId": "analyze-chats",
3
+ "effectLimit": 11,
4
+ "capabilities": ["chats", "claude"],
5
+ "stages": [
6
+ {
7
+ "name": "read-input",
8
+ "label": "Read input",
9
+ "operations": ["read-analysis"]
10
+ },
11
+ { "name": "analyze", "label": "Analyze", "operations": ["turn-analysis"] },
12
+ {
13
+ "name": "save-result",
14
+ "label": "Save result",
15
+ "operations": ["save-analysis"]
16
+ }
17
+ ],
18
+ "input": {
19
+ "maximumBytes": 4096,
20
+ "fields": { "importId": { "type": "string" } }
21
+ }
22
+ }
@@ -0,0 +1,250 @@
1
+ import { decisionGate } from "cloving/workflows/steps/decision-gate";
2
+ import { protectedEffect } from "cloving/workflows/steps/protected-effect";
3
+ function request(importId, step) {
4
+ const { operation, payload } = step;
5
+ return {
6
+ scope: { phase: "analysis", operation, round: step.round ?? 1 },
7
+ capability: operation === "turn-analysis" ? "claude" : "chats",
8
+ method: operation === "turn-analysis" ? "run-turn:analyze-chats" : operation,
9
+ target: importId,
10
+ payload: JSON.stringify(payload),
11
+ units: 1,
12
+ };
13
+ }
14
+ function blocked(cycleId, failure) {
15
+ const { operation, result } = failure;
16
+ return {
17
+ kind: "blocked",
18
+ cycleId,
19
+ evidenceId: operation,
20
+ reason: "reason" in result && result.reason !== null
21
+ ? result.reason
22
+ : result.kind,
23
+ };
24
+ }
25
+ function record(value) {
26
+ return value !== null && typeof value === "object" && !Array.isArray(value);
27
+ }
28
+ function fields(result) {
29
+ try {
30
+ const value = JSON.parse(result ?? "null");
31
+ return record(value) ? value : {};
32
+ }
33
+ catch {
34
+ return {};
35
+ }
36
+ }
37
+ function usableDraft(cycleId, draft) {
38
+ return (draft["cycleId"] === cycleId &&
39
+ typeof draft["candidateDigest"] === "string" &&
40
+ /^[a-f0-9]{64}$/.test(draft["candidateDigest"]) &&
41
+ typeof draft["expiresAt"] === "string" &&
42
+ record(draft["question"]));
43
+ }
44
+ function draftAnswer(draft, decision) {
45
+ if (decision.decision === "expired")
46
+ return "analysis-answer-missing";
47
+ const question = draft["question"];
48
+ const choices = record(question) ? question["choices"] : undefined;
49
+ if (decision.gateId !== "analysis-question" ||
50
+ decision.decision !== "answered" ||
51
+ typeof decision.answer !== "string" ||
52
+ !Array.isArray(choices) ||
53
+ !choices.some((choice) => record(choice) && choice["id"] === decision.answer))
54
+ return "analysis-answer-invalid";
55
+ return null;
56
+ }
57
+ async function askDraft(cycleId, continuation) {
58
+ const { draft, decision } = continuation;
59
+ if (!usableDraft(cycleId, draft))
60
+ return {
61
+ kind: "blocked",
62
+ cycleId,
63
+ reason: "analysis-input-refused",
64
+ evidenceId: "save-analysis",
65
+ };
66
+ if (decision !== undefined) {
67
+ const reason = draftAnswer(draft, decision);
68
+ if (reason === null)
69
+ return { kind: "answered", answer: decision.answer ?? "" };
70
+ return {
71
+ kind: "blocked",
72
+ cycleId,
73
+ reason,
74
+ evidenceId: "analysis-question",
75
+ };
76
+ }
77
+ const gate = await decisionGate({
78
+ gateId: "analysis-question",
79
+ kind: "question",
80
+ prompt: JSON.stringify({
81
+ candidateDigest: draft["candidateDigest"],
82
+ question: draft["question"],
83
+ }),
84
+ expiresAt: draft.expiresAt,
85
+ });
86
+ if (gate.kind !== "opened")
87
+ return {
88
+ kind: "blocked",
89
+ cycleId,
90
+ reason: gate.reason,
91
+ evidenceId: "analysis-question",
92
+ };
93
+ return { kind: "waiting_for_decision", cycleId, decisionId: gate.decisionId };
94
+ }
95
+ async function firstTurn(input) {
96
+ const { cycleId } = input;
97
+ const { importId } = input.input;
98
+ const read = await protectedEffect(request(importId, { operation: "read-analysis", payload: { importId } }));
99
+ if (read.kind !== "completed")
100
+ return blocked(cycleId, { operation: "read-analysis", result: read });
101
+ const captured = fields(read.result);
102
+ const snapshot = captured["snapshot"];
103
+ if (!record(snapshot) || snapshot["importId"] !== importId)
104
+ return {
105
+ kind: "blocked",
106
+ cycleId,
107
+ reason: "analysis-input-refused",
108
+ evidenceId: "read-analysis",
109
+ };
110
+ return runTurn(input, batchData(captured, snapshot));
111
+ }
112
+ function batchData(value, snapshot) {
113
+ const context = value["context"];
114
+ const batch = value["batch"];
115
+ return {
116
+ snapshot,
117
+ ...(record(context) ? { context } : {}),
118
+ ...(record(batch) ? { batch } : {}),
119
+ };
120
+ }
121
+ async function runTurn(input, data) {
122
+ const { cycleId } = input;
123
+ const { snapshot, followUp, context } = data;
124
+ const turn = await protectedEffect(request(input.input.importId, {
125
+ operation: "turn-analysis",
126
+ payload: {
127
+ snapshot,
128
+ ...(context === undefined ? {} : { context }),
129
+ ...(followUp === undefined ? {} : { followUp }),
130
+ },
131
+ round: turnRound(data),
132
+ }));
133
+ if (turn.kind !== "completed")
134
+ return blocked(cycleId, { operation: "turn-analysis", result: turn });
135
+ const reply = fields(turn.result)["reply"];
136
+ if (typeof reply !== "string")
137
+ return {
138
+ kind: "blocked",
139
+ cycleId,
140
+ reason: "analysis-turn-failed",
141
+ evidenceId: "turn-analysis",
142
+ };
143
+ return { kind: "analyzed", data, reply };
144
+ }
145
+ function handoff(cycleId, importId) {
146
+ return {
147
+ kind: "handoff_requested",
148
+ cycleId,
149
+ promotionId: `analysis:${importId}:${cycleId}`,
150
+ };
151
+ }
152
+ function turnRound(data) {
153
+ if (data.followUp !== undefined)
154
+ return data.batch === undefined ? 2 : 5;
155
+ return Number(data.batch?.["batchIndex"] ?? 0) + 1;
156
+ }
157
+ async function saveTurn(input, turn) {
158
+ const { batch, followUp } = turn.data;
159
+ return protectedEffect(request(input.input.importId, {
160
+ operation: "save-analysis",
161
+ round: turnRound(turn.data),
162
+ payload: {
163
+ importId: input.input.importId,
164
+ cycleId: input.cycleId,
165
+ reply: turn.reply,
166
+ ...(batch === undefined ? {} : { batch }),
167
+ ...(followUp === undefined ? {} : { followUp }),
168
+ },
169
+ }));
170
+ }
171
+ export async function analyzeChats(input, decision) {
172
+ "use workflow";
173
+ const { cycleId } = input;
174
+ const { importId } = input.input;
175
+ let analyzed = await firstTurn(input);
176
+ let answered = false;
177
+ while (analyzed.kind === "analyzed") {
178
+ const { data, reply } = analyzed;
179
+ const save = await saveTurn(input, analyzed);
180
+ if (save.kind !== "completed")
181
+ return blocked(cycleId, { operation: "save-analysis", result: save });
182
+ const saved = fields(save.result);
183
+ if (saved["kind"] === "analysis_draft") {
184
+ analyzed = await questionTurn(input, {
185
+ saved,
186
+ data,
187
+ reply,
188
+ decision,
189
+ answered,
190
+ });
191
+ answered = true;
192
+ continue;
193
+ }
194
+ if (saved["kind"] !== "analysis_batch")
195
+ return finishBatch(input, {
196
+ saved,
197
+ decision: answered ? undefined : decision,
198
+ });
199
+ const nextSnapshot = saved["snapshot"];
200
+ if (!record(nextSnapshot) || nextSnapshot["importId"] !== importId)
201
+ return blocked(cycleId, {
202
+ operation: "save-analysis",
203
+ result: { kind: "refused", reason: "analysis-input-refused" },
204
+ });
205
+ analyzed = await runTurn(input, batchData(saved, nextSnapshot));
206
+ }
207
+ return analyzed;
208
+ }
209
+ async function questionTurn(input, continuation) {
210
+ const { saved, data, reply, decision, answered } = continuation;
211
+ if (answered)
212
+ return blocked(input.cycleId, {
213
+ operation: "save-analysis",
214
+ result: { kind: "refused", reason: "analysis-question-limit" },
215
+ });
216
+ const answer = await askDraft(input.cycleId, { draft: saved, decision });
217
+ if (answer.kind !== "answered")
218
+ return answer;
219
+ return runTurn(input, {
220
+ ...data,
221
+ followUp: { draft: fields(reply), answer: answer.answer },
222
+ });
223
+ }
224
+ function finishBatch(input, result) {
225
+ const { cycleId } = input;
226
+ const { importId } = input.input;
227
+ const { saved, decision } = result;
228
+ if (saved["kind"] === "analysis_partial")
229
+ return {
230
+ kind: "blocked",
231
+ cycleId,
232
+ reason: String(saved["reason"]),
233
+ evidenceId: "save-analysis",
234
+ };
235
+ if (decision !== undefined)
236
+ return {
237
+ kind: "blocked",
238
+ cycleId,
239
+ reason: "analysis-answer-invalid",
240
+ evidenceId: "analysis-question",
241
+ };
242
+ if (saved["kind"] !== "handoff_requested")
243
+ return {
244
+ kind: "blocked",
245
+ cycleId,
246
+ reason: "analysis-input-refused",
247
+ evidenceId: "save-analysis",
248
+ };
249
+ return handoff(cycleId, importId);
250
+ }