@cleocode/core 2026.10.1 → 2026.10.3

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 (389) hide show
  1. package/dist/build-provenance.json +2 -2
  2. package/dist/cloud/http.d.ts +2 -0
  3. package/dist/cloud/http.d.ts.map +1 -1
  4. package/dist/cloud/http.js +3 -0
  5. package/dist/cloud/http.js.map +1 -1
  6. package/dist/cloud/journal.d.ts +9 -5
  7. package/dist/cloud/journal.d.ts.map +1 -1
  8. package/dist/cloud/journal.js +29 -8
  9. package/dist/cloud/journal.js.map +1 -1
  10. package/dist/cloud/manifest-check.d.ts +140 -4
  11. package/dist/cloud/manifest-check.d.ts.map +1 -1
  12. package/dist/cloud/manifest-check.js +322 -10
  13. package/dist/cloud/manifest-check.js.map +1 -1
  14. package/dist/cloud/nexus-attach.d.ts +25 -0
  15. package/dist/cloud/nexus-attach.d.ts.map +1 -1
  16. package/dist/cloud/nexus-attach.js +81 -6
  17. package/dist/cloud/nexus-attach.js.map +1 -1
  18. package/dist/cloud/nexus-cloud-activity.d.ts +37 -0
  19. package/dist/cloud/nexus-cloud-activity.d.ts.map +1 -0
  20. package/dist/cloud/nexus-cloud-activity.js +79 -0
  21. package/dist/cloud/nexus-cloud-activity.js.map +1 -0
  22. package/dist/cloud/nexus-cloud-status.d.ts.map +1 -1
  23. package/dist/cloud/nexus-cloud-status.js +36 -0
  24. package/dist/cloud/nexus-cloud-status.js.map +1 -1
  25. package/dist/cloud/nexus-device.d.ts +1 -1
  26. package/dist/cloud/nexus-enrol.d.ts.map +1 -1
  27. package/dist/cloud/nexus-enrol.js +5 -0
  28. package/dist/cloud/nexus-enrol.js.map +1 -1
  29. package/dist/cloud/nexus-home.d.ts +74 -0
  30. package/dist/cloud/nexus-home.d.ts.map +1 -0
  31. package/dist/cloud/nexus-home.js +97 -0
  32. package/dist/cloud/nexus-home.js.map +1 -0
  33. package/dist/cloud/nexus-vault-keys.d.ts +130 -0
  34. package/dist/cloud/nexus-vault-keys.d.ts.map +1 -0
  35. package/dist/cloud/nexus-vault-keys.js +347 -0
  36. package/dist/cloud/nexus-vault-keys.js.map +1 -0
  37. package/dist/cloud/nexus-vault-state.d.ts +121 -0
  38. package/dist/cloud/nexus-vault-state.d.ts.map +1 -0
  39. package/dist/cloud/nexus-vault-state.js +274 -0
  40. package/dist/cloud/nexus-vault-state.js.map +1 -0
  41. package/dist/cloud/nexus-vault.d.ts +160 -0
  42. package/dist/cloud/nexus-vault.d.ts.map +1 -0
  43. package/dist/cloud/nexus-vault.js +1745 -0
  44. package/dist/cloud/nexus-vault.js.map +1 -0
  45. package/dist/cloud/signing.d.ts +24 -8
  46. package/dist/cloud/signing.d.ts.map +1 -1
  47. package/dist/cloud/signing.js +61 -15
  48. package/dist/cloud/signing.js.map +1 -1
  49. package/dist/docs/export-document.js +8137 -3318
  50. package/dist/docs/export-document.js.map +3 -3
  51. package/dist/doctor/migrations.d.ts +75 -0
  52. package/dist/doctor/migrations.d.ts.map +1 -0
  53. package/dist/doctor/migrations.js +136 -0
  54. package/dist/doctor/migrations.js.map +1 -0
  55. package/dist/doctor/sync-sealer.d.ts +48 -0
  56. package/dist/doctor/sync-sealer.d.ts.map +1 -0
  57. package/dist/doctor/sync-sealer.js +109 -0
  58. package/dist/doctor/sync-sealer.js.map +1 -0
  59. package/dist/doctor/sync-triggers.d.ts +53 -0
  60. package/dist/doctor/sync-triggers.d.ts.map +1 -0
  61. package/dist/doctor/sync-triggers.js +157 -0
  62. package/dist/doctor/sync-triggers.js.map +1 -0
  63. package/dist/init.d.ts.map +1 -1
  64. package/dist/init.js +5 -1
  65. package/dist/init.js.map +1 -1
  66. package/dist/internal.d.ts +1 -1
  67. package/dist/internal.d.ts.map +1 -1
  68. package/dist/internal.js +1 -1
  69. package/dist/internal.js.map +1 -1
  70. package/dist/llm/plugin-facade.js +8442 -3623
  71. package/dist/llm/plugin-facade.js.map +3 -3
  72. package/dist/nexus/projects-clean.d.ts +3 -0
  73. package/dist/nexus/projects-clean.d.ts.map +1 -1
  74. package/dist/nexus/projects-clean.js +9 -3
  75. package/dist/nexus/projects-clean.js.map +1 -1
  76. package/dist/nexus/registry-roots.d.ts +3 -1
  77. package/dist/nexus/registry-roots.d.ts.map +1 -1
  78. package/dist/nexus/registry-roots.js +5 -2
  79. package/dist/nexus/registry-roots.js.map +1 -1
  80. package/dist/nexus/registry.d.ts +8 -1
  81. package/dist/nexus/registry.d.ts.map +1 -1
  82. package/dist/nexus/registry.js +24 -10
  83. package/dist/nexus/registry.js.map +1 -1
  84. package/dist/nexus/workspace.d.ts +4 -1
  85. package/dist/nexus/workspace.d.ts.map +1 -1
  86. package/dist/nexus/workspace.js +10 -4
  87. package/dist/nexus/workspace.js.map +1 -1
  88. package/dist/orchestrate/spawn-ops.d.ts.map +1 -1
  89. package/dist/orchestrate/spawn-ops.js +7 -2
  90. package/dist/orchestrate/spawn-ops.js.map +1 -1
  91. package/dist/orchestrate/worker-verify.d.ts +121 -12
  92. package/dist/orchestrate/worker-verify.d.ts.map +1 -1
  93. package/dist/orchestrate/worker-verify.js +172 -21
  94. package/dist/orchestrate/worker-verify.js.map +1 -1
  95. package/dist/orchestration/spawn-prompt.d.ts.map +1 -1
  96. package/dist/orchestration/spawn-prompt.js +15 -9
  97. package/dist/orchestration/spawn-prompt.js.map +1 -1
  98. package/dist/release/ci-evidence.d.ts +9 -0
  99. package/dist/release/ci-evidence.d.ts.map +1 -1
  100. package/dist/release/ci-evidence.js +9 -2
  101. package/dist/release/ci-evidence.js.map +1 -1
  102. package/dist/resources/backend.d.ts +15 -3
  103. package/dist/resources/backend.d.ts.map +1 -1
  104. package/dist/resources/backend.js +7 -3
  105. package/dist/resources/backend.js.map +1 -1
  106. package/dist/resources/darwin-backend.d.ts +142 -0
  107. package/dist/resources/darwin-backend.d.ts.map +1 -0
  108. package/dist/resources/darwin-backend.js +288 -0
  109. package/dist/resources/darwin-backend.js.map +1 -0
  110. package/dist/resources/governor.d.ts +44 -1
  111. package/dist/resources/governor.d.ts.map +1 -1
  112. package/dist/resources/governor.js +100 -36
  113. package/dist/resources/governor.js.map +1 -1
  114. package/dist/resources/heavy-command.d.ts +132 -0
  115. package/dist/resources/heavy-command.d.ts.map +1 -0
  116. package/dist/resources/heavy-command.js +768 -0
  117. package/dist/resources/heavy-command.js.map +1 -0
  118. package/dist/resources/monitor.d.ts +50 -1
  119. package/dist/resources/monitor.d.ts.map +1 -1
  120. package/dist/resources/monitor.js +83 -1
  121. package/dist/resources/monitor.js.map +1 -1
  122. package/dist/resources/run-admission.d.ts +303 -0
  123. package/dist/resources/run-admission.d.ts.map +1 -0
  124. package/dist/resources/run-admission.js +648 -0
  125. package/dist/resources/run-admission.js.map +1 -0
  126. package/dist/resources/run-class.d.ts +111 -0
  127. package/dist/resources/run-class.d.ts.map +1 -0
  128. package/dist/resources/run-class.js +658 -0
  129. package/dist/resources/run-class.js.map +1 -0
  130. package/dist/resources/run-governed.d.ts +158 -0
  131. package/dist/resources/run-governed.d.ts.map +1 -0
  132. package/dist/resources/run-governed.js +406 -0
  133. package/dist/resources/run-governed.js.map +1 -0
  134. package/dist/resources/slot-holder.d.ts +267 -0
  135. package/dist/resources/slot-holder.d.ts.map +1 -0
  136. package/dist/resources/slot-holder.js +505 -0
  137. package/dist/resources/slot-holder.js.map +1 -0
  138. package/dist/resources/tool-groups.d.ts +74 -0
  139. package/dist/resources/tool-groups.d.ts.map +1 -0
  140. package/dist/resources/tool-groups.js +118 -0
  141. package/dist/resources/tool-groups.js.map +1 -0
  142. package/dist/routing/build-command-groups.d.ts.map +1 -1
  143. package/dist/routing/build-command-groups.js +1 -0
  144. package/dist/routing/build-command-groups.js.map +1 -1
  145. package/dist/selfimprove/fix-gen.js +8446 -3627
  146. package/dist/selfimprove/fix-gen.js.map +3 -3
  147. package/dist/sentient/cross-project-hygiene.d.ts +3 -2
  148. package/dist/sentient/cross-project-hygiene.d.ts.map +1 -1
  149. package/dist/sentient/cross-project-hygiene.js +5 -2
  150. package/dist/sentient/cross-project-hygiene.js.map +1 -1
  151. package/dist/sentient/state.d.ts +7 -0
  152. package/dist/sentient/state.d.ts.map +1 -1
  153. package/dist/sentient/state.js.map +1 -1
  154. package/dist/sentient/tick.d.ts +14 -2
  155. package/dist/sentient/tick.d.ts.map +1 -1
  156. package/dist/sentient/tick.js +63 -4
  157. package/dist/sentient/tick.js.map +1 -1
  158. package/dist/store/backup-recover.d.ts.map +1 -1
  159. package/dist/store/backup-recover.js +5 -0
  160. package/dist/store/backup-recover.js.map +1 -1
  161. package/dist/store/data-accessor.d.ts +1 -0
  162. package/dist/store/data-accessor.d.ts.map +1 -1
  163. package/dist/store/data-accessor.js +7 -0
  164. package/dist/store/data-accessor.js.map +1 -1
  165. package/dist/store/dual-scope-db.d.ts +23 -1
  166. package/dist/store/dual-scope-db.d.ts.map +1 -1
  167. package/dist/store/dual-scope-db.js +126 -36
  168. package/dist/store/dual-scope-db.js.map +1 -1
  169. package/dist/store/exodus/recovery.d.ts.map +1 -1
  170. package/dist/store/exodus/recovery.js +15 -0
  171. package/dist/store/exodus/recovery.js.map +1 -1
  172. package/dist/store/legacy-tasks-lineage.d.ts.map +1 -1
  173. package/dist/store/legacy-tasks-lineage.js +5 -0
  174. package/dist/store/legacy-tasks-lineage.js.map +1 -1
  175. package/dist/store/lock.d.ts +8 -1
  176. package/dist/store/lock.d.ts.map +1 -1
  177. package/dist/store/lock.js +10 -2
  178. package/dist/store/lock.js.map +1 -1
  179. package/dist/store/migration-manager.d.ts +5 -0
  180. package/dist/store/migration-manager.d.ts.map +1 -1
  181. package/dist/store/migration-manager.js +11 -0
  182. package/dist/store/migration-manager.js.map +1 -1
  183. package/dist/store/migration-runner.d.ts +158 -0
  184. package/dist/store/migration-runner.d.ts.map +1 -0
  185. package/dist/store/migration-runner.js +493 -0
  186. package/dist/store/migration-runner.js.map +1 -0
  187. package/dist/store/portable-bundle-import.d.ts +15 -1
  188. package/dist/store/portable-bundle-import.d.ts.map +1 -1
  189. package/dist/store/portable-bundle-import.js +24 -4
  190. package/dist/store/portable-bundle-import.js.map +1 -1
  191. package/dist/store/portable-bundle-relocate.d.ts +43 -0
  192. package/dist/store/portable-bundle-relocate.d.ts.map +1 -1
  193. package/dist/store/portable-bundle-relocate.js +79 -5
  194. package/dist/store/portable-bundle-relocate.js.map +1 -1
  195. package/dist/store/portable-bundle-scan.d.ts +42 -0
  196. package/dist/store/portable-bundle-scan.d.ts.map +1 -1
  197. package/dist/store/portable-bundle-scan.js +132 -25
  198. package/dist/store/portable-bundle-scan.js.map +1 -1
  199. package/dist/store/portable-bundle.d.ts +40 -0
  200. package/dist/store/portable-bundle.d.ts.map +1 -1
  201. package/dist/store/portable-bundle.js +43 -7
  202. package/dist/store/portable-bundle.js.map +1 -1
  203. package/dist/store/row-identity-registry.d.ts +137 -7
  204. package/dist/store/row-identity-registry.d.ts.map +1 -1
  205. package/dist/store/row-identity-registry.js +364 -9
  206. package/dist/store/row-identity-registry.js.map +1 -1
  207. package/dist/store/row-identity.d.ts.map +1 -1
  208. package/dist/store/row-identity.js +9 -10
  209. package/dist/store/row-identity.js.map +1 -1
  210. package/dist/store/sqlite-data-accessor.d.ts.map +1 -1
  211. package/dist/store/sqlite-data-accessor.js +10 -0
  212. package/dist/store/sqlite-data-accessor.js.map +1 -1
  213. package/dist/store/sqlite.d.ts +31 -0
  214. package/dist/store/sqlite.d.ts.map +1 -1
  215. package/dist/store/sqlite.js +5 -1
  216. package/dist/store/sqlite.js.map +1 -1
  217. package/dist/store/sync/capture.d.ts +203 -0
  218. package/dist/store/sync/capture.d.ts.map +1 -0
  219. package/dist/store/sync/capture.js +562 -0
  220. package/dist/store/sync/capture.js.map +1 -0
  221. package/dist/store/sync/clock-store.d.ts.map +1 -1
  222. package/dist/store/sync/clock-store.js +2 -0
  223. package/dist/store/sync/clock-store.js.map +1 -1
  224. package/dist/store/sync/fk-orphans.d.ts +133 -0
  225. package/dist/store/sync/fk-orphans.d.ts.map +1 -0
  226. package/dist/store/sync/fk-orphans.js +265 -0
  227. package/dist/store/sync/fk-orphans.js.map +1 -0
  228. package/dist/store/sync/flags.d.ts +7 -0
  229. package/dist/store/sync/flags.d.ts.map +1 -1
  230. package/dist/store/sync/flags.js +16 -0
  231. package/dist/store/sync/flags.js.map +1 -1
  232. package/dist/store/sync/hlc.d.ts.map +1 -1
  233. package/dist/store/sync/hlc.js +4 -0
  234. package/dist/store/sync/hlc.js.map +1 -1
  235. package/dist/store/sync/identity-fill.d.ts +45 -0
  236. package/dist/store/sync/identity-fill.d.ts.map +1 -0
  237. package/dist/store/sync/identity-fill.js +85 -0
  238. package/dist/store/sync/identity-fill.js.map +1 -0
  239. package/dist/store/sync/machinery.d.ts +27 -0
  240. package/dist/store/sync/machinery.d.ts.map +1 -0
  241. package/dist/store/sync/machinery.js +44 -0
  242. package/dist/store/sync/machinery.js.map +1 -0
  243. package/dist/store/sync/netting.d.ts +120 -0
  244. package/dist/store/sync/netting.d.ts.map +1 -0
  245. package/dist/store/sync/netting.js +336 -0
  246. package/dist/store/sync/netting.js.map +1 -0
  247. package/dist/store/sync/replica.d.ts +27 -2
  248. package/dist/store/sync/replica.d.ts.map +1 -1
  249. package/dist/store/sync/replica.js +47 -3
  250. package/dist/store/sync/replica.js.map +1 -1
  251. package/dist/store/sync/schema.d.ts +12 -0
  252. package/dist/store/sync/schema.d.ts.map +1 -1
  253. package/dist/store/sync/schema.js +21 -0
  254. package/dist/store/sync/schema.js.map +1 -1
  255. package/dist/store/sync/seal-backlog.d.ts +21 -0
  256. package/dist/store/sync/seal-backlog.d.ts.map +1 -0
  257. package/dist/store/sync/seal-backlog.js +24 -0
  258. package/dist/store/sync/seal-backlog.js.map +1 -0
  259. package/dist/store/sync/sealer-values.d.ts +34 -0
  260. package/dist/store/sync/sealer-values.d.ts.map +1 -0
  261. package/dist/store/sync/sealer-values.js +53 -0
  262. package/dist/store/sync/sealer-values.js.map +1 -0
  263. package/dist/store/sync/sealer.d.ts +145 -0
  264. package/dist/store/sync/sealer.d.ts.map +1 -0
  265. package/dist/store/sync/sealer.js +906 -0
  266. package/dist/store/sync/sealer.js.map +1 -0
  267. package/dist/store/sync/structural.d.ts +63 -0
  268. package/dist/store/sync/structural.d.ts.map +1 -0
  269. package/dist/store/sync/structural.js +180 -0
  270. package/dist/store/sync/structural.js.map +1 -0
  271. package/dist/store/sync/timestamps.d.ts +92 -0
  272. package/dist/store/sync/timestamps.d.ts.map +1 -0
  273. package/dist/store/sync/timestamps.js +228 -0
  274. package/dist/store/sync/timestamps.js.map +1 -0
  275. package/dist/store/sync/tombstones.d.ts +75 -0
  276. package/dist/store/sync/tombstones.d.ts.map +1 -0
  277. package/dist/store/sync/tombstones.js +103 -0
  278. package/dist/store/sync/tombstones.js.map +1 -0
  279. package/dist/store/sync/trigger-classes.d.ts +221 -0
  280. package/dist/store/sync/trigger-classes.d.ts.map +1 -0
  281. package/dist/store/sync/trigger-classes.js +441 -0
  282. package/dist/store/sync/trigger-classes.js.map +1 -0
  283. package/dist/store/sync/trigger-suspend-at-path.d.ts +23 -0
  284. package/dist/store/sync/trigger-suspend-at-path.d.ts.map +1 -0
  285. package/dist/store/sync/trigger-suspend-at-path.js +44 -0
  286. package/dist/store/sync/trigger-suspend-at-path.js.map +1 -0
  287. package/dist/store/sync/writer-version.d.ts +43 -0
  288. package/dist/store/sync/writer-version.d.ts.map +1 -0
  289. package/dist/store/sync/writer-version.js +90 -0
  290. package/dist/store/sync/writer-version.js.map +1 -0
  291. package/dist/store/table-classification.d.ts.map +1 -1
  292. package/dist/store/table-classification.js +102 -0
  293. package/dist/store/table-classification.js.map +1 -1
  294. package/dist/store/trigger-suspend-schema.d.ts +44 -0
  295. package/dist/store/trigger-suspend-schema.d.ts.map +1 -0
  296. package/dist/store/trigger-suspend-schema.js +25 -0
  297. package/dist/store/trigger-suspend-schema.js.map +1 -0
  298. package/dist/store/vault-manifest.d.ts +365 -0
  299. package/dist/store/vault-manifest.d.ts.map +1 -0
  300. package/dist/store/vault-manifest.js +812 -0
  301. package/dist/store/vault-manifest.js.map +1 -0
  302. package/dist/store/writer-lease.d.ts +31 -0
  303. package/dist/store/writer-lease.d.ts.map +1 -1
  304. package/dist/store/writer-lease.js +65 -2
  305. package/dist/store/writer-lease.js.map +1 -1
  306. package/dist/system/health.d.ts.map +1 -1
  307. package/dist/system/health.js +21 -0
  308. package/dist/system/health.js.map +1 -1
  309. package/dist/system/project-health.d.ts.map +1 -1
  310. package/dist/system/project-health.js +3 -1
  311. package/dist/system/project-health.js.map +1 -1
  312. package/dist/tasks/affected-packages.d.ts +58 -0
  313. package/dist/tasks/affected-packages.d.ts.map +1 -1
  314. package/dist/tasks/affected-packages.js +75 -6
  315. package/dist/tasks/affected-packages.js.map +1 -1
  316. package/dist/tasks/affected-scope.d.ts +150 -23
  317. package/dist/tasks/affected-scope.d.ts.map +1 -1
  318. package/dist/tasks/affected-scope.js +320 -32
  319. package/dist/tasks/affected-scope.js.map +1 -1
  320. package/dist/tasks/change-set.d.ts +96 -0
  321. package/dist/tasks/change-set.d.ts.map +1 -1
  322. package/dist/tasks/change-set.js +169 -4
  323. package/dist/tasks/change-set.js.map +1 -1
  324. package/dist/tasks/complete-ci.d.ts +115 -0
  325. package/dist/tasks/complete-ci.d.ts.map +1 -0
  326. package/dist/tasks/complete-ci.js +171 -0
  327. package/dist/tasks/complete-ci.js.map +1 -0
  328. package/dist/tasks/complete.d.ts.map +1 -1
  329. package/dist/tasks/complete.js +50 -17
  330. package/dist/tasks/complete.js.map +1 -1
  331. package/dist/tasks/compute-task-view.d.ts.map +1 -1
  332. package/dist/tasks/compute-task-view.js +7 -0
  333. package/dist/tasks/compute-task-view.js.map +1 -1
  334. package/dist/tasks/done-plan.d.ts.map +1 -1
  335. package/dist/tasks/done-plan.js +51 -30
  336. package/dist/tasks/done-plan.js.map +1 -1
  337. package/dist/tasks/done-record.d.ts +6 -0
  338. package/dist/tasks/done-record.d.ts.map +1 -1
  339. package/dist/tasks/done-record.js +9 -3
  340. package/dist/tasks/done-record.js.map +1 -1
  341. package/dist/tasks/evidence.d.ts +3 -2
  342. package/dist/tasks/evidence.d.ts.map +1 -1
  343. package/dist/tasks/evidence.js +143 -12
  344. package/dist/tasks/evidence.js.map +1 -1
  345. package/dist/tasks/gate-notes.d.ts +22 -0
  346. package/dist/tasks/gate-notes.d.ts.map +1 -0
  347. package/dist/tasks/gate-notes.js +33 -0
  348. package/dist/tasks/gate-notes.js.map +1 -0
  349. package/dist/tasks/index.d.ts +2 -1
  350. package/dist/tasks/index.d.ts.map +1 -1
  351. package/dist/tasks/index.js +2 -1
  352. package/dist/tasks/index.js.map +1 -1
  353. package/dist/tasks/test-run-binding.d.ts +117 -0
  354. package/dist/tasks/test-run-binding.d.ts.map +1 -0
  355. package/dist/tasks/test-run-binding.js +365 -0
  356. package/dist/tasks/test-run-binding.js.map +1 -0
  357. package/dist/tasks/tool-cache-env.d.ts +123 -0
  358. package/dist/tasks/tool-cache-env.d.ts.map +1 -0
  359. package/dist/tasks/tool-cache-env.js +287 -0
  360. package/dist/tasks/tool-cache-env.js.map +1 -0
  361. package/dist/tasks/tool-cache-failed-first.d.ts +155 -0
  362. package/dist/tasks/tool-cache-failed-first.d.ts.map +1 -0
  363. package/dist/tasks/tool-cache-failed-first.js +285 -0
  364. package/dist/tasks/tool-cache-failed-first.js.map +1 -0
  365. package/dist/tasks/tool-cache.d.ts +375 -55
  366. package/dist/tasks/tool-cache.d.ts.map +1 -1
  367. package/dist/tasks/tool-cache.js +912 -303
  368. package/dist/tasks/tool-cache.js.map +1 -1
  369. package/dist/tasks/tool-resolver.d.ts +4 -3
  370. package/dist/tasks/tool-resolver.d.ts.map +1 -1
  371. package/dist/tasks/tool-resolver.js +15 -4
  372. package/dist/tasks/tool-resolver.js.map +1 -1
  373. package/dist/tasks/tool-semaphore.d.ts +67 -7
  374. package/dist/tasks/tool-semaphore.d.ts.map +1 -1
  375. package/dist/tasks/tool-semaphore.js +134 -56
  376. package/dist/tasks/tool-semaphore.js.map +1 -1
  377. package/dist/upgrade.d.ts.map +1 -1
  378. package/dist/upgrade.js +4 -0
  379. package/dist/upgrade.js.map +1 -1
  380. package/dist/validation/engine-ops.d.ts +6 -0
  381. package/dist/validation/engine-ops.d.ts.map +1 -1
  382. package/dist/validation/engine-ops.js +8 -0
  383. package/dist/validation/engine-ops.js.map +1 -1
  384. package/migrations/drizzle-cleo-project/20260930170000_t12819-trigger-suspend-clause/migration.sql +367 -0
  385. package/migrations/sync-journal/20260930120000_t12343-capture/migration.sql +47 -0
  386. package/migrations/sync-journal/20261001170000_t12984-sealer/migration.sql +101 -0
  387. package/package.json +13 -13
  388. package/templates/CLEO-INJECTION.md +5 -5
  389. package/templates/CLEO-REFERENCE.md +8 -4
@@ -0,0 +1,768 @@
1
+ /**
2
+ * Heavy-command planner for the provider hook (`cleo hook heavy-command`).
3
+ *
4
+ * An agent harness hands the hook the shell command line an agent is about to
5
+ * run. This module finds the heavy commands in it (test runners, compilers,
6
+ * builds, installs) and rewrites each one to `cleo run --wait --class <c> --`,
7
+ * so it is admitted through the machine-wide ResourceGovernor like every other
8
+ * heavy job. Recognition is {@link looksHeavy} / {@link resolveRunClass} from
9
+ * the dependency-free `run-class.ts` (the same code `cleo run` uses to infer
10
+ * its class); this module only adds the shell structure around them.
11
+ *
12
+ * The rewrite never changes what the line does. It wraps the heavy simple
13
+ * command IN PLACE and leaves every operator to the agent's own shell, so
14
+ * pipelines, `pipefail`, `|&`, `&>`, globs and the `time` keyword keep their
15
+ * meaning in whatever shell runs the line (zsh, bash, dash):
16
+ *
17
+ * - `pnpm test | tail` → `cleo run … -- pnpm test | tail`
18
+ * - `pnpm test &> out.log` → `cleo run … -- pnpm test &> out.log`
19
+ * - `cd x && pnpm test` → `cd x && cleo run … -- pnpm test` (the `cd` still
20
+ * moves the agent's shell)
21
+ * - `CI=1 pnpm test` → `CI=1 cleo run … -- pnpm test` (the assignment reaches
22
+ * `cleo run`, which passes its environment on)
23
+ *
24
+ * The prefix asks `cleo run` for `--passthrough`: the child keeps the line's
25
+ * stdin, stdout and stderr, and its exit code comes back unchanged. Only the
26
+ * prefix is inserted; every other byte of the line stays as written.
27
+ *
28
+ * A heavy command is only reported (`warn`), never rewritten, when it runs in
29
+ * a subshell, inside `$(…)` or backticks, as a background job (`&`), inside a
30
+ * compound command (`if`, `for`, `while`, `case`, `{ …; }`, `!`), reads a
31
+ * heredoc, or shares a pipeline with another heavy command (both would start
32
+ * at once and could wait on each other through the pipe).
33
+ *
34
+ * What counts as heavy is entirely `run-class`'s call: only the command word
35
+ * counts (`git commit -m vitest` and `grep -rn tsc .` never do), and
36
+ * `--version`/`--help` and watch/dev/serve modes are never heavy, because a
37
+ * job that never exits would hold a slot forever.
38
+ *
39
+ * @module resources/heavy-command
40
+ * @task T12983
41
+ * @epic T12978
42
+ */
43
+ import { accessSync, existsSync, constants as fsConstants } from 'node:fs';
44
+ import { homedir } from 'node:os';
45
+ import { delimiter, dirname, isAbsolute, join, resolve } from 'node:path';
46
+ import { looksHeavy, resolveRunClass } from './run-class.js';
47
+ /** Operators, longest first so a prefix never shadows a longer one. */
48
+ const OPERATORS = [
49
+ ';;&',
50
+ '&>>',
51
+ '<<<',
52
+ '<<-',
53
+ '&&',
54
+ '||',
55
+ ';;',
56
+ ';&',
57
+ '|&',
58
+ '&>',
59
+ '<<',
60
+ '>>',
61
+ '<&',
62
+ '>&',
63
+ '<>',
64
+ '>|',
65
+ '&',
66
+ '|',
67
+ ';',
68
+ '<',
69
+ '>',
70
+ '(',
71
+ ')',
72
+ ];
73
+ const OPERATOR_START = new Set(['|', '&', ';', '<', '>', '(', ')']);
74
+ const NAME = /^[A-Za-z_][A-Za-z0-9_]*$/;
75
+ /**
76
+ * Lex `src` from `from` the way a POSIX shell tokenises it. In `paren` mode
77
+ * (a `$(…)` body) lexing stops at the unmatched `)`.
78
+ */
79
+ function lexShell(src, from, mode) {
80
+ const tokens = [];
81
+ const substitutions = [];
82
+ const pending = [];
83
+ let heredocDelimiterNext = null;
84
+ let depth = 0;
85
+ // Asserted so control flow does not narrow it to `null`: the closures below assign it.
86
+ let cur = null;
87
+ let i = from;
88
+ const fail = (error) => ({ src, tokens, substitutions, end: src.length, error });
89
+ const begin = (at) => {
90
+ cur ??= {
91
+ start: at,
92
+ value: '',
93
+ expands: false,
94
+ assignment: false,
95
+ bare: true,
96
+ };
97
+ return cur;
98
+ };
99
+ const finish = (at) => {
100
+ if (cur === null)
101
+ return;
102
+ const word = {
103
+ kind: 'word',
104
+ start: cur.start,
105
+ end: at,
106
+ value: cur.value,
107
+ expands: cur.expands,
108
+ assignment: cur.assignment,
109
+ bare: cur.bare,
110
+ };
111
+ tokens.push(word);
112
+ cur = null;
113
+ if (heredocDelimiterNext !== null) {
114
+ pending.push({ delimiter: word.value, stripTabs: heredocDelimiterNext.stripTabs });
115
+ heredocDelimiterNext = null;
116
+ }
117
+ };
118
+ /** Skip heredoc bodies that start at `at` (just past a newline). */
119
+ const skipHeredocs = (at) => {
120
+ let pos = at;
121
+ for (const doc of pending.splice(0)) {
122
+ while (pos < src.length) {
123
+ const nl = src.indexOf('\n', pos);
124
+ const lineEnd = nl === -1 ? src.length : nl;
125
+ let line = src.slice(pos, lineEnd);
126
+ if (doc.stripTabs)
127
+ line = line.replace(/^\t+/, '');
128
+ pos = nl === -1 ? src.length : nl + 1;
129
+ if (line === doc.delimiter)
130
+ break;
131
+ }
132
+ }
133
+ return pos;
134
+ };
135
+ /** Consume a `$` construct at `at`; returns the offset past it. */
136
+ const dollar = (at, draft) => {
137
+ const next = src[at + 1];
138
+ if (next === '(' && src[at + 2] === '(') {
139
+ const close = matchArithmetic(src, at + 3);
140
+ if (close === -1)
141
+ return fail('unterminated $((');
142
+ draft.value += src.slice(at, close);
143
+ draft.expands = true;
144
+ return close;
145
+ }
146
+ if (next === '(') {
147
+ const body = lexShell(src, at + 2, 'paren');
148
+ if (body.error !== null)
149
+ return body;
150
+ substitutions.push(body);
151
+ draft.value += src.slice(at, body.end);
152
+ draft.expands = true;
153
+ return body.end;
154
+ }
155
+ if (next === '{') {
156
+ const close = src.indexOf('}', at + 2);
157
+ if (close === -1)
158
+ return fail('unterminated ${');
159
+ draft.value += src.slice(at, close + 1);
160
+ draft.expands = true;
161
+ return close + 1;
162
+ }
163
+ if (next === "'") {
164
+ let j = at + 2;
165
+ while (j < src.length && src[j] !== "'")
166
+ j += src[j] === '\\' ? 2 : 1;
167
+ if (j >= src.length)
168
+ return fail("unterminated $'");
169
+ draft.value += src.slice(at + 2, j);
170
+ draft.bare = false;
171
+ return j + 1;
172
+ }
173
+ const name = /^(?:[A-Za-z_][A-Za-z0-9_]*|[0-9?$!#@*-])/.exec(src.slice(at + 1));
174
+ if (name) {
175
+ draft.value += `$${name[0]}`;
176
+ draft.expands = true;
177
+ return at + 1 + name[0].length;
178
+ }
179
+ draft.value += '$';
180
+ return at + 1;
181
+ };
182
+ /** Consume a backtick substitution at `at`; returns the offset past it. */
183
+ const backtick = (at, draft) => {
184
+ let j = at + 1;
185
+ while (j < src.length && src[j] !== '`')
186
+ j += src[j] === '\\' ? 2 : 1;
187
+ if (j >= src.length)
188
+ return fail('unterminated backtick');
189
+ substitutions.push(lexShell(src.slice(at + 1, j).replace(/\\`/g, '`'), 0, 'top'));
190
+ draft.value += src.slice(at, j + 1);
191
+ draft.expands = true;
192
+ return j + 1;
193
+ };
194
+ while (i < src.length) {
195
+ const ch = src[i];
196
+ if (ch === ' ' || ch === '\t') {
197
+ finish(i);
198
+ i++;
199
+ }
200
+ else if (ch === '\\') {
201
+ if (src[i + 1] === '\n') {
202
+ i += 2;
203
+ }
204
+ else if (i + 1 >= src.length) {
205
+ return fail('trailing backslash');
206
+ }
207
+ else {
208
+ const d = begin(i);
209
+ d.value += src[i + 1];
210
+ d.bare = false;
211
+ i += 2;
212
+ }
213
+ }
214
+ else if (ch === '\n') {
215
+ finish(i);
216
+ tokens.push({ kind: 'op', start: i, end: i + 1, op: '\n' });
217
+ i = pending.length > 0 ? skipHeredocs(i + 1) : i + 1;
218
+ }
219
+ else if (ch === '#' && cur === null) {
220
+ while (i < src.length && src[i] !== '\n')
221
+ i++;
222
+ }
223
+ else if (ch === "'") {
224
+ const close = src.indexOf("'", i + 1);
225
+ if (close === -1)
226
+ return fail('unterminated single quote');
227
+ const d = begin(i);
228
+ d.value += src.slice(i + 1, close);
229
+ d.bare = false;
230
+ i = close + 1;
231
+ }
232
+ else if (ch === '"') {
233
+ const d = begin(i);
234
+ d.bare = false;
235
+ let j = i + 1;
236
+ let closed = false;
237
+ while (j < src.length) {
238
+ const c = src[j];
239
+ if (c === '"') {
240
+ closed = true;
241
+ break;
242
+ }
243
+ if (c === '\\' && j + 1 < src.length && '$`"\\\n'.includes(src[j + 1])) {
244
+ if (src[j + 1] !== '\n')
245
+ d.value += src[j + 1];
246
+ j += 2;
247
+ }
248
+ else if (c === '$') {
249
+ const r = dollar(j, d);
250
+ if (typeof r !== 'number')
251
+ return r;
252
+ j = r;
253
+ }
254
+ else if (c === '`') {
255
+ const r = backtick(j, d);
256
+ if (typeof r !== 'number')
257
+ return r;
258
+ j = r;
259
+ }
260
+ else {
261
+ d.value += c;
262
+ j++;
263
+ }
264
+ }
265
+ if (!closed)
266
+ return fail('unterminated double quote');
267
+ i = j + 1;
268
+ }
269
+ else if (ch === '$') {
270
+ const r = dollar(i, begin(i));
271
+ if (typeof r !== 'number')
272
+ return r;
273
+ i = r;
274
+ }
275
+ else if (ch === '`') {
276
+ const r = backtick(i, begin(i));
277
+ if (typeof r !== 'number')
278
+ return r;
279
+ i = r;
280
+ }
281
+ else if ((ch === '<' || ch === '>') && src[i + 1] === '(') {
282
+ // Process substitution: one word.
283
+ const d = begin(i);
284
+ const body = lexShell(src, i + 2, 'paren');
285
+ if (body.error !== null)
286
+ return body;
287
+ substitutions.push(body);
288
+ d.value += src.slice(i, body.end);
289
+ d.expands = true;
290
+ i = body.end;
291
+ }
292
+ else if (OPERATOR_START.has(ch)) {
293
+ if (mode === 'paren' && ch === ')' && depth === 0) {
294
+ finish(i);
295
+ return { src, tokens, substitutions, end: i + 1, error: null };
296
+ }
297
+ // `2>&1`: an all-digit word glued to a redirection is its fd.
298
+ let start = i;
299
+ if ((ch === '<' || ch === '>') && cur?.bare && /^\d+$/.test(cur.value)) {
300
+ start = cur.start;
301
+ cur = null;
302
+ }
303
+ else {
304
+ finish(i);
305
+ }
306
+ const op = OPERATORS.find((o) => src.startsWith(o, i));
307
+ if (op === '(')
308
+ depth++;
309
+ if (op === ')')
310
+ depth--;
311
+ if (op === '<<' || op === '<<-')
312
+ heredocDelimiterNext = { stripTabs: op === '<<-' };
313
+ tokens.push({ kind: 'op', start, end: i + op.length, op });
314
+ i += op.length;
315
+ }
316
+ else if (ch === '=' && cur !== null && !cur.assignment && cur.bare && NAME.test(cur.value)) {
317
+ cur.assignment = true;
318
+ cur.value += ch;
319
+ i++;
320
+ }
321
+ else {
322
+ const d = begin(i);
323
+ d.value += ch;
324
+ i++;
325
+ }
326
+ }
327
+ finish(src.length);
328
+ if (mode === 'paren')
329
+ return fail('unterminated $(');
330
+ return { src, tokens, substitutions, end: src.length, error: null };
331
+ }
332
+ /** Offset past the `))` closing a `$((` that opened before `from`, or -1. */
333
+ function matchArithmetic(src, from) {
334
+ let depth = 0;
335
+ for (let j = from; j < src.length; j++) {
336
+ if (src[j] === '(')
337
+ depth++;
338
+ else if (src[j] === ')') {
339
+ if (depth === 0)
340
+ return src[j + 1] === ')' ? j + 2 : -1;
341
+ depth--;
342
+ }
343
+ }
344
+ return -1;
345
+ }
346
+ // ---------------------------------------------------------------------------
347
+ // Recognition
348
+ // ---------------------------------------------------------------------------
349
+ /** Shell words that open or continue a compound command. */
350
+ const RESERVED = new Set([
351
+ 'if',
352
+ 'then',
353
+ 'else',
354
+ 'elif',
355
+ 'fi',
356
+ 'do',
357
+ 'done',
358
+ 'case',
359
+ 'esac',
360
+ 'while',
361
+ 'until',
362
+ 'for',
363
+ 'in',
364
+ 'select',
365
+ 'function',
366
+ 'coproc',
367
+ '{',
368
+ '}',
369
+ '!',
370
+ '[[',
371
+ ']]',
372
+ ]);
373
+ /** Short `--class` names for the classes {@link resolveRunClass} infers. */
374
+ const CLASS_ALIAS = {
375
+ 'test-run': 'test',
376
+ 'scoped-build': 'build',
377
+ 'full-build': 'full-build',
378
+ 'db-heavy': 'db',
379
+ };
380
+ function base(token) {
381
+ return token.split('/').pop() ?? token;
382
+ }
383
+ /** Whether a command is already governed: `cleo run …` or `~/.cleo-heavy/run.sh …`. */
384
+ function isGoverned(argv) {
385
+ const head = argv[0] ?? '';
386
+ if ((base(head) === 'cleo' || base(head) === 'ct') && argv[1] === 'run')
387
+ return true;
388
+ return head.endsWith('.cleo-heavy/run.sh');
389
+ }
390
+ // ---------------------------------------------------------------------------
391
+ // Planning
392
+ // ---------------------------------------------------------------------------
393
+ const REDIRECTIONS = new Set([
394
+ '<',
395
+ '>',
396
+ '>>',
397
+ '<<',
398
+ '<<-',
399
+ '<<<',
400
+ '<&',
401
+ '>&',
402
+ '<>',
403
+ '>|',
404
+ '&>',
405
+ '&>>',
406
+ ]);
407
+ const SEPARATORS = new Set(['&&', '||', ';', '\n', '&', ';;', ';&', ';;&']);
408
+ /** Reserved words that open a compound command (in command position). */
409
+ const COMPOUND_OPEN = new Set(['if', 'while', 'until', 'for', 'case', 'select', '{', 'function']);
410
+ /** Reserved words that close one. */
411
+ const COMPOUND_CLOSE = new Set(['fi', 'done', 'esac', '}']);
412
+ /**
413
+ * Split a token stream into list elements, marking every element that sits in
414
+ * a subshell or compound command. Paren and compound depth are tracked across
415
+ * elements, so `if x; then pnpm test; fi` marks the middle element nested.
416
+ */
417
+ function splitElements(tokens) {
418
+ const out = [];
419
+ let group = [];
420
+ let paren = 0;
421
+ let compound = 0;
422
+ let nested = false;
423
+ let atCommand = true;
424
+ const flush = (background) => {
425
+ if (group.length > 0)
426
+ out.push({ tokens: group, background, nested });
427
+ group = [];
428
+ nested = paren > 0 || compound > 0;
429
+ atCommand = true;
430
+ };
431
+ for (const t of tokens) {
432
+ if (t.kind === 'op' && SEPARATORS.has(t.op)) {
433
+ flush(t.op === '&');
434
+ continue;
435
+ }
436
+ group.push(t);
437
+ if (t.kind === 'op') {
438
+ if (t.op === '(' || t.op === ')') {
439
+ nested = true;
440
+ paren += t.op === '(' ? 1 : -1;
441
+ }
442
+ if (t.op === '(' || t.op === '|' || t.op === '|&')
443
+ atCommand = true;
444
+ continue;
445
+ }
446
+ if (!atCommand)
447
+ continue;
448
+ if (t.bare && RESERVED.has(t.value)) {
449
+ nested = true;
450
+ if (COMPOUND_OPEN.has(t.value))
451
+ compound++;
452
+ if (COMPOUND_CLOSE.has(t.value))
453
+ compound--;
454
+ }
455
+ else if (!t.assignment) {
456
+ atCommand = false;
457
+ }
458
+ }
459
+ flush(false);
460
+ // An unbalanced line is not one this planner understands: treat it all as nested.
461
+ if (paren !== 0 || compound !== 0)
462
+ return out.map((e) => ({ ...e, nested: true }));
463
+ return out;
464
+ }
465
+ function splitStages(tokens) {
466
+ const stages = [];
467
+ let words = [];
468
+ let heredoc = false;
469
+ let skipNext = false;
470
+ for (const t of tokens) {
471
+ if (t.kind === 'op') {
472
+ if (t.op === '|' || t.op === '|&') {
473
+ stages.push({ words, heredoc });
474
+ words = [];
475
+ heredoc = false;
476
+ }
477
+ else if (REDIRECTIONS.has(t.op)) {
478
+ heredoc ||= t.op === '<<' || t.op === '<<-';
479
+ skipNext = true;
480
+ }
481
+ continue;
482
+ }
483
+ if (skipNext) {
484
+ skipNext = false;
485
+ continue;
486
+ }
487
+ words.push(t);
488
+ }
489
+ stages.push({ words, heredoc });
490
+ return stages;
491
+ }
492
+ /** The stage's words from its command word on (leading assignments dropped). */
493
+ function commandWords(stage) {
494
+ let k = 0;
495
+ while (k < stage.words.length && stage.words[k].assignment)
496
+ k++;
497
+ return stage.words.slice(k);
498
+ }
499
+ /** Drop a leading `time` keyword and its flags; returns the words after them. */
500
+ function afterTime(words) {
501
+ if (words[0]?.bare !== true || words[0].value !== 'time')
502
+ return words;
503
+ let k = 1;
504
+ while (k < words.length && words[k].value.startsWith('-'))
505
+ k++;
506
+ return words.slice(k);
507
+ }
508
+ function values(words) {
509
+ return words.map((w) => w.value);
510
+ }
511
+ /**
512
+ * The `cleo run` flag that keeps the child's stdio and exit code (#1777). The
513
+ * hook's rewrite depends on it: without it `cleo run` prints its own envelope
514
+ * on stdout, which a pipe or redirection would capture.
515
+ */
516
+ export const CLEO_RUN_PASSTHROUGH_FLAG = '--passthrough';
517
+ /** The `cleo run` prefix for one governed command. */
518
+ function governedPrefix(runClass, waitTimeoutSec) {
519
+ const timeout = waitTimeoutSec === undefined ? '' : ` --timeout ${waitTimeoutSec}`;
520
+ return `cleo run --wait ${CLEO_RUN_PASSTHROUGH_FLAG}${timeout} --class ${runClass} --`;
521
+ }
522
+ /** The literal source of a run of words (first word start to last word end). */
523
+ function spanOf(src, words) {
524
+ const first = words[0];
525
+ const last = words[words.length - 1];
526
+ return first && last ? src.slice(first.start, last.end) : '';
527
+ }
528
+ /** Where an element starts and ends in its source. */
529
+ function elementSpan(element) {
530
+ const first = element.tokens[0];
531
+ const last = element.tokens[element.tokens.length - 1];
532
+ return { start: first.start, end: last.end };
533
+ }
534
+ function analyse(lexed, opts, depth) {
535
+ const { src } = lexed;
536
+ const result = { segments: [], edits: [], blockers: [] };
537
+ for (const sub of lexed.substitutions) {
538
+ const inner = analyse(sub, opts, depth + 1);
539
+ result.segments.push(...inner.segments);
540
+ result.blockers.push(...inner.blockers);
541
+ }
542
+ let cwd = opts.cwd;
543
+ for (const element of splitElements(lexed.tokens)) {
544
+ const stages = splitStages(element.tokens);
545
+ const span = elementSpan(element);
546
+ const text = src.slice(span.start, span.end);
547
+ // Track `cd <literal>` so class inference sees the directory it runs in.
548
+ const simple = stages.length === 1 ? commandWords(stages[0]) : [];
549
+ if (simple[0]?.value === 'cd' && simple.length <= 2 && !simple.some((w) => w.expands)) {
550
+ const target = simple[1]?.value ?? '~';
551
+ if (target !== '-') {
552
+ cwd = target.startsWith('~')
553
+ ? join(homedir(), target.slice(1))
554
+ : isAbsolute(target)
555
+ ? target
556
+ : resolve(cwd, target);
557
+ }
558
+ continue;
559
+ }
560
+ // Reserved words are skipped only to find a heavy command to report.
561
+ const runWords = (s) => {
562
+ const words = commandWords(s);
563
+ let k = 0;
564
+ while (k < words.length && words[k].bare && RESERVED.has(words[k].value))
565
+ k++;
566
+ return afterTime(words.slice(k));
567
+ };
568
+ if (stages.some((s) => isGoverned(values(runWords(s)))))
569
+ continue;
570
+ const heavy = stages.filter((s) => looksHeavy(values(runWords(s))));
571
+ if (heavy.length === 0)
572
+ continue;
573
+ for (const stage of heavy) {
574
+ const words = runWords(stage);
575
+ const resolved = resolveRunClass(undefined, values(words), cwd);
576
+ const runClass = CLASS_ALIAS[resolved] ?? 'build';
577
+ const prefix = governedPrefix(runClass, opts.waitTimeoutSec);
578
+ const blocker = depth > 0
579
+ ? 'it runs inside a command substitution'
580
+ : element.background
581
+ ? 'it runs as a background job (&)'
582
+ : element.nested
583
+ ? 'it runs inside a subshell or a compound command'
584
+ : stage.heredoc
585
+ ? 'it reads a heredoc'
586
+ : heavy.length > 1
587
+ ? 'two heavy commands share one pipeline and would start at once'
588
+ : null;
589
+ if (blocker !== null) {
590
+ // Hint only: the heavy command itself in its governed form.
591
+ result.segments.push({
592
+ text,
593
+ runClass,
594
+ governed: `${prefix} ${spanOf(src, words)}`,
595
+ argv: values(words),
596
+ });
597
+ result.blockers.push(blocker);
598
+ continue;
599
+ }
600
+ // In place: the prefix goes right before the command word, after any
601
+ // assignments, redirections or `time` keyword in front of it.
602
+ const at = words[0].start;
603
+ const governed = `${src.slice(span.start, at)}${prefix} ${src.slice(at, span.end)}`;
604
+ result.segments.push({ text, runClass, governed, argv: values(words) });
605
+ result.edits.push({ at, text: `${prefix} ` });
606
+ }
607
+ }
608
+ return result;
609
+ }
610
+ /**
611
+ * Decide what the hook does with one shell command line.
612
+ *
613
+ * @param command - the command line as the agent wrote it.
614
+ * @param opts - where it starts, and how long a governed command may queue.
615
+ * @returns `none`, a `rewrite` with every heavy command governed in place, or
616
+ * a `warn` when a heavy command sits where a rewrite could change behaviour.
617
+ *
618
+ * @example
619
+ * ```ts
620
+ * planHeavyCommand('cd pkg && pnpm vitest run a.test.ts', { cwd: '/repo' });
621
+ * // { action: 'rewrite',
622
+ * // command: 'cd pkg && cleo run --wait --passthrough --class test -- pnpm vitest run a.test.ts', … }
623
+ * planHeavyCommand('npx tsc --noEmit 2>&1 | head -50', { cwd: '/repo', waitTimeoutSec: 300 });
624
+ * // command: 'cleo run --wait --passthrough --timeout 300 --class build -- npx tsc --noEmit 2>&1 | head -50'
625
+ * planHeavyCommand('for f in a b; do pnpm test $f; done', { cwd: '/repo' }).action; // 'warn'
626
+ * ```
627
+ */
628
+ export function planHeavyCommand(command, opts) {
629
+ const lexed = lexShell(command, 0, 'top');
630
+ if (lexed.error !== null)
631
+ return { action: 'none' };
632
+ const { segments, edits, blockers } = analyse(lexed, opts, 0);
633
+ if (segments.length === 0)
634
+ return { action: 'none' };
635
+ if (blockers.length > 0) {
636
+ return { action: 'warn', reason: [...new Set(blockers)].join('; '), segments };
637
+ }
638
+ let rewritten = command;
639
+ for (const edit of [...edits].sort((a, b) => b.at - a.at)) {
640
+ rewritten = rewritten.slice(0, edit.at) + edit.text + rewritten.slice(edit.at);
641
+ }
642
+ return { action: 'rewrite', command: rewritten, segments };
643
+ }
644
+ // ---------------------------------------------------------------------------
645
+ // Mode, availability, pressure
646
+ // ---------------------------------------------------------------------------
647
+ const MODES = new Set(['rewrite', 'warn', 'off']);
648
+ /**
649
+ * Whether `value` (trimmed, any case) names a hook mode. The hook reads the
650
+ * config only when the environment variable does not.
651
+ *
652
+ * @param value - e.g. `process.env.CLEO_HEAVY_COMMAND_HOOK`.
653
+ */
654
+ export function isHeavyCommandHookMode(value) {
655
+ return value !== undefined && MODES.has(value.trim().toLowerCase());
656
+ }
657
+ /**
658
+ * The hook mode: the `CLEO_HEAVY_COMMAND_HOOK` environment variable, else the
659
+ * `resources.heavyCommandHook` config value, else `rewrite`. Unknown values
660
+ * are ignored.
661
+ *
662
+ * @param envValue - `process.env.CLEO_HEAVY_COMMAND_HOOK`.
663
+ * @param configValue - the resolved `resources.heavyCommandHook`.
664
+ * @returns the effective mode.
665
+ */
666
+ export function resolveHeavyHookMode(envValue, configValue) {
667
+ for (const v of [envValue, configValue]) {
668
+ const mode = v?.trim().toLowerCase();
669
+ if (mode !== undefined && MODES.has(mode))
670
+ return mode;
671
+ }
672
+ return 'rewrite';
673
+ }
674
+ /**
675
+ * `resources.heavyCommandHook` from the merged global + project config, or
676
+ * `undefined` when it is unset, invalid or unreadable. `cleo init` and
677
+ * `cleo upgrade` install (or, for `off`, remove) the hook from this value
678
+ * alone; the environment variable only switches the hook at run time.
679
+ *
680
+ * @param projectRoot - the project whose `.cleo/config.json` to read.
681
+ */
682
+ export async function configuredHeavyHookMode(projectRoot) {
683
+ try {
684
+ const { getConfigValue } = await import('../config/registry.js');
685
+ const value = await getConfigValue('resources.heavyCommandHook', { projectRoot });
686
+ const mode = typeof value === 'string' ? value.trim().toLowerCase() : undefined;
687
+ return mode !== undefined && MODES.has(mode) ? mode : undefined;
688
+ }
689
+ catch {
690
+ return undefined;
691
+ }
692
+ }
693
+ /**
694
+ * The project whose config the hook reads: the nearest directory at or above
695
+ * `cwd` holding a `.cleo/` directory, so an opt-out in the project config
696
+ * still applies when the agent's shell sits in a subdirectory. Falls back to
697
+ * `cwd` (the global config still applies there). Never walks into `$HOME`.
698
+ *
699
+ * @param cwd - the directory the command line starts in.
700
+ */
701
+ export function heavyHookProjectRoot(cwd) {
702
+ const home = homedir();
703
+ let dir = resolve(cwd);
704
+ for (;;) {
705
+ if (dir === home)
706
+ break;
707
+ if (existsSync(join(dir, '.cleo')))
708
+ return dir;
709
+ const parent = dirname(dir);
710
+ if (parent === dir)
711
+ break;
712
+ dir = parent;
713
+ }
714
+ return cwd;
715
+ }
716
+ /**
717
+ * Whether `name` resolves to an executable on `pathValue` (a `PATH` string).
718
+ * A rewrite to `cleo run` only helps if the agent's shell can find `cleo`.
719
+ *
720
+ * @param name - the executable name, e.g. `cleo`.
721
+ * @param pathValue - the `PATH` to search.
722
+ */
723
+ export function executableOnPath(name, pathValue) {
724
+ for (const dir of (pathValue ?? '').split(delimiter)) {
725
+ if (dir === '')
726
+ continue;
727
+ try {
728
+ accessSync(join(dir, name), fsConstants.X_OK);
729
+ return true;
730
+ }
731
+ catch {
732
+ // Not here.
733
+ }
734
+ }
735
+ return false;
736
+ }
737
+ /**
738
+ * One context line when the machine is under pressure (`hold` = yellow,
739
+ * `backoff` = red), or `null` when it is fine or the sample fails. Takes one
740
+ * bounded sample through the platform backend (one `sysctl` exec on macOS,
741
+ * `/proc` reads on Linux).
742
+ *
743
+ * @param timeoutMs - give up and return `null` after this long.
744
+ */
745
+ export async function heavyPressureNotice(timeoutMs = 1500) {
746
+ try {
747
+ const { classifyPressure, defaultResourceBackend } = await import('./monitor.js');
748
+ let timer;
749
+ const timeout = new Promise((done) => {
750
+ timer = setTimeout(() => done(null), timeoutMs);
751
+ timer.unref();
752
+ });
753
+ const sample = await Promise.race([defaultResourceBackend().sample(), timeout]);
754
+ clearTimeout(timer);
755
+ if (sample === null)
756
+ return null;
757
+ const { state, reason } = classifyPressure(sample);
758
+ if (state === 'ok')
759
+ return null;
760
+ const level = state === 'backoff' ? 'red' : 'yellow';
761
+ return (`[cleo] Machine pressure is ${level} (${reason}). Heavy commands queue for the shared budget: ` +
762
+ 'run single test files, and prefer CI as evidence (cleo verify <id> --gate testsPassed --evidence "ci:<pr>").');
763
+ }
764
+ catch {
765
+ return null;
766
+ }
767
+ }
768
+ //# sourceMappingURL=heavy-command.js.map