@fitlab-ai/agent-infra 0.9.13 → 0.9.15

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 (428) hide show
  1. package/README.md +10 -0
  2. package/README.zh-CN.md +10 -0
  3. package/bin/cli.ts +26 -9
  4. package/bin/internal-cli.sh +45 -0
  5. package/dist/bin/cli.js +173 -157
  6. package/dist/bin/internal-cli.js +126 -42
  7. package/dist/lib/agent-client.js +5 -0
  8. package/dist/lib/agent-clients/adapters/codex-lifecycle/capability-store.js +165 -27
  9. package/dist/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.js +3 -2
  10. package/dist/lib/defaults.json +103 -103
  11. package/dist/lib/fs/durable-write.js +37 -0
  12. package/dist/lib/fs/file-lock.js +111 -0
  13. package/dist/lib/host-control/audit.js +19 -0
  14. package/dist/lib/host-control/client.js +142 -0
  15. package/dist/lib/host-control/command.js +49 -0
  16. package/dist/lib/host-control/path.js +160 -0
  17. package/dist/lib/host-control/secure-fs.js +140 -0
  18. package/dist/lib/host-control/server.js +159 -0
  19. package/dist/lib/host-control/service.js +112 -0
  20. package/dist/lib/init.js +11 -2
  21. package/dist/lib/internal/agent-client.js +5 -2
  22. package/dist/lib/internal/cli-route-inventory.js +154 -0
  23. package/dist/lib/internal/codex-lifecycle.js +15 -12
  24. package/dist/lib/internal/codex-sandbox-controller.js +8 -2
  25. package/dist/lib/internal/demo-promotion.js +322 -0
  26. package/dist/lib/internal/demo-transcript.js +454 -0
  27. package/dist/lib/internal/git-workflow.js +10 -6
  28. package/dist/lib/internal/host-control.js +28 -0
  29. package/dist/lib/internal/manual-validation.js +322 -0
  30. package/dist/lib/internal/platform-checks.js +16 -4
  31. package/dist/lib/internal/platform-comment.js +16 -4
  32. package/dist/lib/internal/platform-context.js +4 -1
  33. package/dist/lib/internal/platform-issue.js +12 -4
  34. package/dist/lib/internal/platform-metadata.js +88 -0
  35. package/dist/lib/internal/platform-pr-review.js +16 -3
  36. package/dist/lib/internal/platform-pr.js +42 -14
  37. package/dist/lib/internal/platform-release-notes.js +6 -3
  38. package/dist/lib/internal/platform-security.js +93 -0
  39. package/dist/lib/internal/pr-review-grade.js +14 -3
  40. package/dist/lib/internal/release-workflow.js +82 -89
  41. package/dist/lib/internal/sandbox-control.js +171 -7
  42. package/dist/lib/internal/task-activity.js +13 -4
  43. package/dist/lib/internal/task-artifact.js +19 -107
  44. package/dist/lib/internal/task-context.js +4 -1
  45. package/dist/lib/internal/task-create.js +48 -14
  46. package/dist/lib/internal/task-delivery.js +4 -1
  47. package/dist/lib/internal/task-event.js +38 -35
  48. package/dist/lib/internal/task-finalization.js +4 -1
  49. package/dist/lib/internal/task-invalidation.js +4 -1
  50. package/dist/lib/internal/task-ledger.js +12 -1
  51. package/dist/lib/internal/task-lifecycle.js +4 -1
  52. package/dist/lib/internal/task-operation-registry.js +519 -0
  53. package/dist/lib/internal/task-orchestration.js +12 -0
  54. package/dist/lib/internal/task-override.js +10 -3
  55. package/dist/lib/internal/task-qualification.js +90 -0
  56. package/dist/lib/internal/task-review.js +10 -52
  57. package/dist/lib/internal/task-short-id.js +11 -1
  58. package/dist/lib/internal/task-snapshot.js +4 -1
  59. package/dist/lib/internal/task-validate.js +37 -12
  60. package/dist/lib/internal/task-verify.js +5 -1
  61. package/dist/lib/internal/task-warning.js +8 -1
  62. package/dist/lib/owned-path.js +33 -0
  63. package/dist/lib/platform/check-status.js +11 -0
  64. package/dist/lib/platform/comment-safety.js +229 -0
  65. package/dist/lib/platform/completion-backfill.js +1 -1
  66. package/dist/lib/platform/github-data.js +241 -0
  67. package/dist/lib/platform/github-provider.js +248 -26
  68. package/dist/lib/platform/issue-comments.js +146 -34
  69. package/dist/lib/platform/issues.js +1 -179
  70. package/dist/lib/platform/pr-change-report.js +6 -12
  71. package/dist/lib/platform/pr-checks.js +12 -65
  72. package/dist/lib/platform/pr-summary.js +83 -10
  73. package/dist/lib/platform/provider-contract.d.ts +67 -1
  74. package/dist/lib/platform/provider-contract.js +6 -2
  75. package/dist/lib/platform/provider-validation.js +76 -3
  76. package/dist/lib/platform/pull-requests.js +117 -275
  77. package/dist/lib/platform/repository-metadata.js +377 -0
  78. package/dist/lib/platform/security-alerts.js +155 -0
  79. package/dist/lib/platform/snapshots.js +2 -0
  80. package/dist/lib/platform/verification-sync.js +154 -162
  81. package/dist/lib/process-data/index.js +7 -0
  82. package/dist/lib/release/workflow.js +1 -1
  83. package/dist/lib/run/index.js +5 -0
  84. package/dist/lib/sandbox/commands/control-status.js +22 -0
  85. package/dist/lib/sandbox/commands/create.js +39 -8
  86. package/dist/lib/sandbox/commands/ls.js +5 -0
  87. package/dist/lib/sandbox/commands/rm.js +3 -807
  88. package/dist/lib/sandbox/commands/show.js +16 -0
  89. package/dist/lib/sandbox/control/audit.js +203 -0
  90. package/dist/lib/sandbox/control/client.js +125 -12
  91. package/dist/lib/sandbox/control/completed-reentry.js +85 -0
  92. package/dist/lib/sandbox/control/container-identity.js +28 -31
  93. package/dist/lib/sandbox/control/executor.js +233 -34
  94. package/dist/lib/sandbox/control/finalization-response.js +27 -0
  95. package/dist/lib/sandbox/control/identity-sentinel.js +107 -0
  96. package/dist/lib/sandbox/control/lifecycle.js +656 -128
  97. package/dist/lib/sandbox/control/native-file-lock.js +85 -0
  98. package/dist/lib/sandbox/control/protocol.js +55 -4
  99. package/dist/lib/sandbox/control/server.js +720 -79
  100. package/dist/lib/sandbox/control/state.js +63 -3
  101. package/dist/lib/sandbox/control/task-view.js +183 -0
  102. package/dist/lib/sandbox/control/task-workflow.js +74 -0
  103. package/dist/lib/sandbox/control/workflow-executor.js +99 -0
  104. package/dist/lib/sandbox/engines/authority.js +289 -0
  105. package/dist/lib/sandbox/engines/docker-exact-discovery.js +97 -0
  106. package/dist/lib/sandbox/index.js +7 -2
  107. package/dist/lib/sandbox/recovery.js +49 -5
  108. package/dist/lib/sandbox/removal.js +1717 -0
  109. package/dist/lib/sandbox/task-cleanup.js +217 -0
  110. package/dist/lib/sandbox/workspace-view.js +15 -15
  111. package/dist/lib/sandbox/worktree-safety.js +4 -1
  112. package/dist/lib/server/index.js +8 -0
  113. package/dist/lib/task/activity-intent.js +2 -1
  114. package/dist/lib/task/activity-log.js +30 -24
  115. package/dist/lib/task/artifact-command.js +101 -0
  116. package/dist/lib/task/artifact-lifecycle.js +71 -40
  117. package/dist/lib/task/artifact-name.js +36 -0
  118. package/dist/lib/task/artifact-operations.js +474 -0
  119. package/dist/lib/task/artifact-receipts.js +3 -15
  120. package/dist/lib/task/artifact-repair-intent.js +130 -0
  121. package/dist/lib/task/artifact-schema.js +127 -0
  122. package/dist/lib/task/capabilities.js +83 -19
  123. package/dist/lib/task/codex-orchestration.js +71 -25
  124. package/dist/lib/task/commit-identity.js +28 -0
  125. package/dist/lib/task/commit-operation.js +2 -16
  126. package/dist/lib/task/control-authority.js +375 -5
  127. package/dist/lib/task/control-recovery.js +128 -0
  128. package/dist/lib/task/create-service.js +103 -4
  129. package/dist/lib/task/create.js +80 -2
  130. package/dist/lib/task/decision-details.js +2 -46
  131. package/dist/lib/task/events.js +306 -76
  132. package/dist/lib/task/finalization-state.js +9 -0
  133. package/dist/lib/task/human-override.js +3 -6
  134. package/dist/lib/task/implementation-inputs.js +3 -3
  135. package/dist/lib/task/index.js +11 -3
  136. package/dist/lib/task/intermediate-cleanup.js +383 -0
  137. package/dist/lib/task/invalidation.js +18 -65
  138. package/dist/lib/task/ledger-intents.js +7 -22
  139. package/dist/lib/task/ledger.js +1 -3
  140. package/dist/lib/task/lifecycle.js +19 -1
  141. package/dist/lib/task/local-artifact-finalization.js +190 -290
  142. package/dist/lib/task/manual-validation-completion.js +26 -0
  143. package/dist/lib/task/manual-validation-evidence.js +130 -0
  144. package/dist/lib/task/manual-validation-receipt.js +101 -0
  145. package/dist/lib/task/manual-validation-shared.js +40 -0
  146. package/dist/lib/task/manual-validation-transaction.js +243 -0
  147. package/dist/lib/task/markdown.js +47 -0
  148. package/dist/lib/task/orchestration.js +206 -69
  149. package/dist/lib/task/qualification-audit.js +476 -0
  150. package/dist/lib/task/qualification-confirmation.js +109 -0
  151. package/dist/lib/task/qualification-intents.js +148 -0
  152. package/dist/lib/task/review-artifacts.js +23 -40
  153. package/dist/lib/task/review-command.js +25 -0
  154. package/dist/lib/task/review-finalization.js +146 -97
  155. package/dist/lib/task/review-fingerprint.js +2 -2
  156. package/dist/lib/task/sections.js +34 -54
  157. package/dist/lib/task/verification-config.js +11 -0
  158. package/dist/lib/task/verification-engine.js +170 -327
  159. package/dist/lib/task/verification.js +1 -1
  160. package/dist/lib/task/workflow-command.js +17 -0
  161. package/dist/lib/task/workflow-warning-intents.js +0 -16
  162. package/dist/lib/task/workflow-warnings.js +28 -2
  163. package/dist/lib/update.js +2 -0
  164. package/dist/lifecycle-build-manifest.json +3 -3
  165. package/lib/agent-client.ts +9 -0
  166. package/lib/agent-clients/adapters/codex-lifecycle/capability-store.ts +223 -33
  167. package/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.ts +3 -2
  168. package/lib/fs/durable-write.ts +29 -0
  169. package/lib/fs/file-lock.ts +113 -0
  170. package/lib/host-control/audit.ts +22 -0
  171. package/lib/host-control/client.ts +172 -0
  172. package/lib/host-control/command.ts +60 -0
  173. package/lib/host-control/path.ts +167 -0
  174. package/lib/host-control/secure-fs.ts +135 -0
  175. package/lib/host-control/server.ts +180 -0
  176. package/lib/host-control/service.ts +133 -0
  177. package/lib/init.ts +15 -2
  178. package/lib/internal/agent-client.ts +4 -2
  179. package/lib/internal/cli-route-inventory.ts +142 -0
  180. package/lib/internal/codex-lifecycle.ts +14 -12
  181. package/lib/internal/codex-sandbox-controller.ts +7 -2
  182. package/lib/internal/demo-promotion.ts +354 -0
  183. package/lib/internal/demo-transcript.ts +419 -0
  184. package/lib/internal/git-workflow.ts +9 -6
  185. package/lib/internal/host-control.ts +28 -0
  186. package/lib/internal/manual-validation.ts +306 -0
  187. package/lib/internal/platform-checks.ts +12 -4
  188. package/lib/internal/platform-comment.ts +12 -4
  189. package/lib/internal/platform-context.ts +3 -1
  190. package/lib/internal/platform-issue.ts +11 -4
  191. package/lib/internal/platform-metadata.ts +70 -0
  192. package/lib/internal/platform-pr-review.ts +14 -3
  193. package/lib/internal/platform-pr.ts +38 -14
  194. package/lib/internal/platform-release-notes.ts +5 -3
  195. package/lib/internal/platform-security.ts +83 -0
  196. package/lib/internal/pr-review-grade.ts +10 -3
  197. package/lib/internal/release-workflow.ts +91 -87
  198. package/lib/internal/sandbox-control.ts +181 -6
  199. package/lib/internal/task-activity.ts +12 -4
  200. package/lib/internal/task-artifact.ts +15 -79
  201. package/lib/internal/task-context.ts +3 -1
  202. package/lib/internal/task-create.ts +76 -15
  203. package/lib/internal/task-delivery.ts +3 -1
  204. package/lib/internal/task-event.ts +21 -10
  205. package/lib/internal/task-finalization.ts +3 -1
  206. package/lib/internal/task-invalidation.ts +3 -1
  207. package/lib/internal/task-ledger.ts +11 -1
  208. package/lib/internal/task-lifecycle.ts +3 -1
  209. package/lib/internal/task-operation-registry.ts +590 -0
  210. package/lib/internal/task-orchestration.ts +12 -0
  211. package/lib/internal/task-override.ts +9 -3
  212. package/lib/internal/task-qualification.ts +61 -0
  213. package/lib/internal/task-review.ts +7 -50
  214. package/lib/internal/task-short-id.ts +10 -1
  215. package/lib/internal/task-snapshot.ts +3 -1
  216. package/lib/internal/task-validate.ts +32 -12
  217. package/lib/internal/task-verify.ts +4 -1
  218. package/lib/internal/task-warning.ts +7 -1
  219. package/lib/owned-path.ts +26 -0
  220. package/lib/platform/check-status.ts +9 -0
  221. package/lib/platform/comment-safety.ts +255 -0
  222. package/lib/platform/completion-backfill.ts +1 -1
  223. package/lib/platform/github-data.ts +322 -0
  224. package/lib/platform/github-provider.ts +244 -27
  225. package/lib/platform/issue-comments.ts +159 -35
  226. package/lib/platform/issues.ts +4 -245
  227. package/lib/platform/pr-change-report.ts +6 -12
  228. package/lib/platform/pr-checks.ts +13 -77
  229. package/lib/platform/pr-summary.ts +88 -11
  230. package/lib/platform/provider-contract.ts +78 -3
  231. package/lib/platform/provider-validation.ts +85 -3
  232. package/lib/platform/pull-requests.ts +129 -348
  233. package/lib/platform/repository-metadata.ts +453 -0
  234. package/lib/platform/security-alerts.ts +191 -0
  235. package/lib/platform/snapshots.ts +53 -0
  236. package/lib/platform/verification-sync.ts +171 -177
  237. package/lib/process-data/index.ts +7 -0
  238. package/lib/release/workflow.ts +3 -3
  239. package/lib/run/index.ts +5 -0
  240. package/lib/sandbox/commands/control-status.ts +26 -0
  241. package/lib/sandbox/commands/create.ts +53 -12
  242. package/lib/sandbox/commands/ls.ts +7 -0
  243. package/lib/sandbox/commands/rm.ts +3 -996
  244. package/lib/sandbox/commands/show.ts +16 -0
  245. package/lib/sandbox/control/audit.ts +253 -0
  246. package/lib/sandbox/control/client.ts +150 -13
  247. package/lib/sandbox/control/completed-reentry.ts +98 -0
  248. package/lib/sandbox/control/container-identity.ts +27 -43
  249. package/lib/sandbox/control/executor.ts +262 -37
  250. package/lib/sandbox/control/finalization-response.ts +29 -0
  251. package/lib/sandbox/control/identity-sentinel.ts +137 -0
  252. package/lib/sandbox/control/lifecycle.ts +672 -18
  253. package/lib/sandbox/control/native-file-lock.ts +108 -0
  254. package/lib/sandbox/control/protocol.ts +80 -8
  255. package/lib/sandbox/control/server.ts +779 -82
  256. package/lib/sandbox/control/state.ts +84 -3
  257. package/lib/sandbox/control/task-view.ts +240 -0
  258. package/lib/sandbox/control/task-workflow.ts +95 -0
  259. package/lib/sandbox/control/workflow-executor.ts +102 -0
  260. package/lib/sandbox/engines/authority.ts +365 -0
  261. package/lib/sandbox/engines/docker-exact-discovery.ts +132 -0
  262. package/lib/sandbox/index.ts +9 -2
  263. package/lib/sandbox/recovery.ts +49 -6
  264. package/lib/sandbox/removal.ts +2144 -0
  265. package/lib/sandbox/task-cleanup.ts +209 -0
  266. package/lib/sandbox/workspace-view.ts +26 -18
  267. package/lib/sandbox/worktree-safety.ts +4 -1
  268. package/lib/server/index.ts +10 -0
  269. package/lib/task/activity-intent.ts +2 -2
  270. package/lib/task/activity-log.ts +27 -19
  271. package/lib/task/artifact-command.ts +104 -0
  272. package/lib/task/artifact-lifecycle.ts +66 -39
  273. package/lib/task/artifact-name.ts +55 -0
  274. package/lib/task/artifact-operations.ts +580 -0
  275. package/lib/task/artifact-receipts.ts +3 -14
  276. package/lib/task/artifact-repair-intent.ts +152 -0
  277. package/lib/task/artifact-schema.ts +163 -0
  278. package/lib/task/capabilities.ts +82 -22
  279. package/lib/task/codex-orchestration.ts +85 -24
  280. package/lib/task/commit-identity.ts +31 -0
  281. package/lib/task/commit-operation.ts +2 -16
  282. package/lib/task/control-authority.ts +513 -6
  283. package/lib/task/control-recovery.ts +174 -0
  284. package/lib/task/create-service.ts +131 -6
  285. package/lib/task/create.ts +83 -1
  286. package/lib/task/decision-details.ts +3 -71
  287. package/lib/task/events.ts +292 -75
  288. package/lib/task/finalization-state.ts +11 -0
  289. package/lib/task/human-override.ts +3 -8
  290. package/lib/task/implementation-inputs.ts +3 -3
  291. package/lib/task/index.ts +16 -3
  292. package/lib/task/intermediate-cleanup.ts +455 -0
  293. package/lib/task/invalidation.ts +18 -62
  294. package/lib/task/ledger-intents.ts +7 -24
  295. package/lib/task/ledger.ts +1 -3
  296. package/lib/task/lifecycle.ts +17 -1
  297. package/lib/task/local-artifact-finalization.ts +223 -388
  298. package/lib/task/manual-validation-completion.ts +40 -0
  299. package/lib/task/manual-validation-evidence.ts +209 -0
  300. package/lib/task/manual-validation-receipt.ts +152 -0
  301. package/lib/task/manual-validation-shared.ts +50 -0
  302. package/lib/task/manual-validation-transaction.ts +304 -0
  303. package/lib/task/markdown.ts +70 -0
  304. package/lib/task/orchestration.ts +191 -68
  305. package/lib/task/qualification-audit.ts +588 -0
  306. package/lib/task/qualification-confirmation.ts +117 -0
  307. package/lib/task/qualification-intents.ts +150 -0
  308. package/lib/task/review-artifacts.ts +21 -41
  309. package/lib/task/review-command.ts +23 -0
  310. package/lib/task/review-finalization.ts +154 -153
  311. package/lib/task/review-fingerprint.ts +2 -2
  312. package/lib/task/sections.ts +33 -52
  313. package/lib/task/verification-config.ts +12 -0
  314. package/lib/task/verification-engine.ts +165 -370
  315. package/lib/task/verification-types.ts +17 -0
  316. package/lib/task/verification.ts +1 -1
  317. package/lib/task/workflow-command.ts +18 -0
  318. package/lib/task/workflow-warning-intents.ts +0 -13
  319. package/lib/task/workflow-warnings.ts +21 -2
  320. package/lib/update.ts +6 -0
  321. package/package.json +7 -2
  322. package/templates/.agents/hooks/lifecycle-delegation.js +6 -6
  323. package/templates/.agents/rules/README.en.md +2 -0
  324. package/templates/.agents/rules/README.zh-CN.md +2 -0
  325. package/templates/.agents/rules/decision-qualification.en.md +22 -0
  326. package/templates/.agents/rules/decision-qualification.zh-CN.md +22 -0
  327. package/templates/.agents/rules/evidence-reporting.en.md +28 -0
  328. package/templates/.agents/rules/evidence-reporting.zh-CN.md +28 -0
  329. package/templates/.agents/rules/label-milestone-setup.en.md +7 -7
  330. package/templates/.agents/rules/label-milestone-setup.zh-CN.md +7 -7
  331. package/templates/.agents/rules/local-artifact-repair.en.md +5 -1
  332. package/templates/.agents/rules/local-artifact-repair.zh-CN.md +5 -1
  333. package/templates/.agents/rules/security-alerts.en.md +8 -8
  334. package/templates/.agents/rules/security-alerts.zh-CN.md +8 -8
  335. package/templates/.agents/rules/testing-discipline.en.md +2 -0
  336. package/templates/.agents/rules/testing-discipline.zh-CN.md +2 -0
  337. package/templates/.agents/scripts/package.json +3 -0
  338. package/templates/.agents/skills/analyze-task/SKILL.en.md +15 -2
  339. package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +19 -2
  340. package/templates/.agents/skills/analyze-task/config/verify.en.json +1 -12
  341. package/templates/.agents/skills/analyze-task/config/verify.zh-CN.json +1 -12
  342. package/templates/.agents/skills/close-codescan/SKILL.en.md +2 -2
  343. package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +2 -2
  344. package/templates/.agents/skills/close-dependabot/SKILL.en.md +2 -2
  345. package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +2 -2
  346. package/templates/.agents/skills/code-task/SKILL.en.md +14 -1
  347. package/templates/.agents/skills/code-task/SKILL.zh-CN.md +14 -1
  348. package/templates/.agents/skills/code-task/config/verify.en.json +5 -14
  349. package/templates/.agents/skills/code-task/config/verify.zh-CN.json +5 -14
  350. package/templates/.agents/skills/code-task/reference/code-rules.en.md +5 -4
  351. package/templates/.agents/skills/code-task/reference/code-rules.zh-CN.md +5 -4
  352. package/templates/.agents/skills/code-task/reference/dual-mode.en.md +1 -1
  353. package/templates/.agents/skills/code-task/reference/dual-mode.zh-CN.md +1 -1
  354. package/templates/.agents/skills/code-task/reference/fix-mode.en.md +3 -1
  355. package/templates/.agents/skills/code-task/reference/fix-mode.zh-CN.md +3 -1
  356. package/templates/.agents/skills/code-task/reference/report-template.en.md +42 -15
  357. package/templates/.agents/skills/code-task/reference/report-template.zh-CN.md +39 -12
  358. package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +37 -21
  359. package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +37 -21
  360. package/templates/.agents/skills/complete-manual-validation/config/verify.en.json +1 -1
  361. package/templates/.agents/skills/complete-manual-validation/config/verify.zh-CN.json +1 -1
  362. package/templates/.agents/skills/complete-manual-validation/reference/report-template.en.md +4 -0
  363. package/templates/.agents/skills/complete-manual-validation/reference/report-template.zh-CN.md +4 -0
  364. package/templates/.agents/skills/complete-manual-validation/reference/summary-update.en.md +16 -7
  365. package/templates/.agents/skills/complete-manual-validation/reference/summary-update.zh-CN.md +16 -7
  366. package/templates/.agents/skills/complete-task/SKILL.en.md +10 -23
  367. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +10 -23
  368. package/templates/.agents/skills/create-task/SKILL.en.md +8 -0
  369. package/templates/.agents/skills/create-task/SKILL.zh-CN.md +7 -0
  370. package/templates/.agents/skills/import-codescan/SKILL.en.md +1 -1
  371. package/templates/.agents/skills/import-codescan/SKILL.zh-CN.md +1 -1
  372. package/templates/.agents/skills/import-dependabot/SKILL.en.md +1 -1
  373. package/templates/.agents/skills/import-dependabot/SKILL.zh-CN.md +1 -1
  374. package/templates/.agents/skills/init-labels/SKILL.en.md +8 -8
  375. package/templates/.agents/skills/init-labels/SKILL.zh-CN.md +8 -8
  376. package/templates/.agents/skills/init-milestones/SKILL.en.md +9 -11
  377. package/templates/.agents/skills/init-milestones/SKILL.zh-CN.md +9 -11
  378. package/templates/.agents/skills/plan-task/SKILL.en.md +14 -1
  379. package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +16 -1
  380. package/templates/.agents/skills/plan-task/config/verify.en.json +4 -14
  381. package/templates/.agents/skills/plan-task/config/verify.zh-CN.json +4 -14
  382. package/templates/.agents/skills/release/SKILL.en.md +7 -3
  383. package/templates/.agents/skills/release/SKILL.zh-CN.md +7 -3
  384. package/templates/.agents/skills/review-analysis/SKILL.en.md +14 -1
  385. package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +14 -1
  386. package/templates/.agents/skills/review-analysis/config/verify.en.json +1 -18
  387. package/templates/.agents/skills/review-analysis/config/verify.zh-CN.json +1 -18
  388. package/templates/.agents/skills/review-analysis/reference/report-template.en.md +50 -23
  389. package/templates/.agents/skills/review-analysis/reference/report-template.zh-CN.md +45 -18
  390. package/templates/.agents/skills/review-code/SKILL.en.md +14 -1
  391. package/templates/.agents/skills/review-code/SKILL.zh-CN.md +14 -1
  392. package/templates/.agents/skills/review-code/config/verify.en.json +1 -23
  393. package/templates/.agents/skills/review-code/config/verify.zh-CN.json +1 -23
  394. package/templates/.agents/skills/review-code/reference/report-template.en.md +49 -22
  395. package/templates/.agents/skills/review-code/reference/report-template.zh-CN.md +45 -18
  396. package/templates/.agents/skills/review-plan/SKILL.en.md +14 -1
  397. package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +14 -1
  398. package/templates/.agents/skills/review-plan/config/verify.en.json +1 -18
  399. package/templates/.agents/skills/review-plan/config/verify.zh-CN.json +1 -18
  400. package/templates/.agents/skills/review-plan/reference/report-template.en.md +49 -22
  401. package/templates/.agents/skills/review-plan/reference/report-template.zh-CN.md +45 -18
  402. package/templates/.agents/skills/review-pr/SKILL.en.md +5 -1
  403. package/templates/.agents/skills/review-pr/SKILL.zh-CN.md +5 -1
  404. package/templates/.agents/skills/review-pr/reference/report-template.en.md +4 -3
  405. package/templates/.agents/skills/review-pr/reference/report-template.zh-CN.md +4 -3
  406. package/templates/.agents/skills/run-manual-validation/SKILL.en.md +25 -4
  407. package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +25 -4
  408. package/templates/.agents/skills/run-manual-validation/reference/discovery-and-execution.en.md +4 -1
  409. package/templates/.agents/skills/run-manual-validation/reference/discovery-and-execution.zh-CN.md +4 -1
  410. package/templates/.agents/skills/run-manual-validation/reference/report-template.en.md +5 -1
  411. package/templates/.agents/skills/run-manual-validation/reference/report-template.zh-CN.md +5 -1
  412. package/templates/.agents/skills/run-task/SKILL.en.md +1 -1
  413. package/templates/.agents/skills/run-task/SKILL.zh-CN.md +1 -1
  414. package/templates/.agents/skills/test/SKILL.en.md +29 -7
  415. package/templates/.agents/skills/test/SKILL.zh-CN.md +29 -7
  416. package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +14 -0
  417. package/templates/.agents/templates/task.en.md +6 -0
  418. package/templates/.agents/templates/task.zh-CN.md +6 -0
  419. package/templates/.claude/commands/run-manual-validation.en.md +1 -1
  420. package/templates/.claude/commands/run-manual-validation.zh-CN.md +1 -1
  421. package/dist/lib/platform/adapters.js +0 -56
  422. package/lib/platform/adapters.ts +0 -197
  423. package/templates/.agents/scripts/security-alerts.github.sh +0 -151
  424. package/templates/.agents/scripts/security-alerts.sh +0 -31
  425. package/templates/.agents/skills/init-labels/scripts/init-labels.github.sh +0 -146
  426. package/templates/.agents/skills/init-labels/scripts/init-labels.sh +0 -32
  427. package/templates/.agents/skills/init-milestones/scripts/init-milestones.github.sh +0 -294
  428. package/templates/.agents/skills/init-milestones/scripts/init-milestones.sh +0 -6
@@ -14,6 +14,7 @@ import {
14
14
  import type { PullRequestResult } from '../platform/pull-requests.ts';
15
15
  import { reportWrite, summaryContext, syncPullRequestSummary } from '../platform/pr-summary.ts';
16
16
  import type { PlatformResult } from '../platform/types.ts';
17
+ import { ensureInternalHandlerRoute, internalHandlerRoute } from './cli-route-inventory.ts';
17
18
 
18
19
  const USAGE = `Usage: agent-infra-internal platform-pr inspect <task-ref> [--cwd <path>]
19
20
  agent-infra-internal platform-pr resolve-external <task-ref> --agent <agent> [--pr <token>] [--dry-run] [--cwd <path>]
@@ -24,11 +25,11 @@ const USAGE = `Usage: agent-infra-internal platform-pr inspect <task-ref> [--cwd
24
25
  agent-infra-internal platform-pr sync-in-labels --pr <N> [--dry-run] [--cwd <path>]
25
26
  agent-infra-internal platform-pr summary-context <task-ref> [--cwd <path>]
26
27
  agent-infra-internal platform-pr change-report <task-ref> --agent <agent> --mechanical-file <path> --precheck-file <path> [--dry-run] [--cwd <path>]
27
- agent-infra-internal platform-pr summary-sync <task-ref> --agent <agent> --body-file <path|-> --change-report-file <path> --result <pr_created|pr_reused|no_op> [--strict] [--dry-run] [--cwd <path>]
28
+ agent-infra-internal platform-pr summary-sync <task-ref> --agent <agent> --body-file <path|-> --change-report-file <path> --result <pr_created|pr_reused|no_op> [--manual-phase pending|final ...] [--strict] [--dry-run] [--cwd <path>]
28
29
  `;
29
30
 
30
31
  const BOOLEAN_FLAGS = new Set(['--draft', '--dry-run', '--metadata', '--closing-issue', '--strict']);
31
- const VALUE_FLAGS = new Set(['--cwd', '--agent', '--base', '--head', '--title-file', '--body-file', '--change-report-file', '--mechanical-file', '--precheck-file', '--pr', '--result']);
32
+ const VALUE_FLAGS = new Set(['--cwd', '--agent', '--base', '--head', '--title-file', '--body-file', '--change-report-file', '--mechanical-file', '--precheck-file', '--pr', '--result', '--manual-phase', '--transaction-id', '--receipt-digest', '--pr-head-sha']);
32
33
 
33
34
  function key(flag: string): string {
34
35
  return flag.slice(2).replace(/-([a-z])/g, (_, letter: string) => letter.toUpperCase());
@@ -67,10 +68,22 @@ function readFile(value: string, cwd: string): string {
67
68
  }
68
69
 
69
70
  async function platformPr(args: string[] = []): Promise<void> {
71
+ if (!ensureInternalHandlerRoute('platform-pr', args)) return;
70
72
  if (args[0] === '--help' || args[0] === '-h') { process.stdout.write(USAGE); return; }
71
73
  const operation = args[0];
72
- if (!operation || !['inspect', 'resolve-external', 'create', 'bind', 'skip', 'sync', 'sync-in-labels', 'summary-context', 'change-report', 'summary-sync'].includes(operation)) { fail('a valid operation is required'); return; }
73
- if (operation === 'sync-in-labels') {
74
+ if (!operation || ![
75
+ internalHandlerRoute('platform-pr', 'inspect', operation),
76
+ internalHandlerRoute('platform-pr', 'resolve-external', operation),
77
+ internalHandlerRoute('platform-pr', 'create', operation),
78
+ internalHandlerRoute('platform-pr', 'bind', operation),
79
+ internalHandlerRoute('platform-pr', 'skip', operation),
80
+ internalHandlerRoute('platform-pr', 'sync', operation),
81
+ internalHandlerRoute('platform-pr', 'sync-in-labels', operation),
82
+ internalHandlerRoute('platform-pr', 'summary-context', operation),
83
+ internalHandlerRoute('platform-pr', 'change-report', operation),
84
+ internalHandlerRoute('platform-pr', 'summary-sync', operation)
85
+ ].some(Boolean)) { fail('a valid operation is required'); return; }
86
+ if (internalHandlerRoute('platform-pr', 'sync-in-labels', operation)) {
74
87
  const parsed = parse(args, 1);
75
88
  if (parsed.error) { fail(parsed.error); return; }
76
89
  const values = parsed.values;
@@ -98,24 +111,24 @@ async function platformPr(args: string[] = []): Promise<void> {
98
111
  'sync-in-labels': ['cwd', 'pr', 'dryRun'],
99
112
  'summary-context': ['cwd'],
100
113
  'change-report': ['cwd', 'agent', 'mechanicalFile', 'precheckFile', 'dryRun'],
101
- 'summary-sync': ['cwd', 'agent', 'bodyFile', 'changeReportFile', 'result', 'dryRun', 'strict']
114
+ 'summary-sync': ['cwd', 'agent', 'bodyFile', 'changeReportFile', 'result', 'manualPhase', 'transactionId', 'receiptDigest', 'prHeadSha', 'dryRun', 'strict']
102
115
  };
103
116
  const unexpected = Object.keys(values).find((name) => !allowed[operation]!.includes(name));
104
117
  if (unexpected) { fail(`${operation} does not accept --${unexpected}`); return; }
105
- if (operation === 'inspect') { finish(await inspectPlatformPullRequest(taskRef, { cwd })); return; }
106
- if (operation === 'summary-context') { finish(await summaryContext(taskRef, { cwd })); return; }
118
+ if (internalHandlerRoute('platform-pr', 'inspect', operation)) { finish(await inspectPlatformPullRequest(taskRef, { cwd })); return; }
119
+ if (internalHandlerRoute('platform-pr', 'summary-context', operation)) { finish(await summaryContext(taskRef, { cwd })); return; }
107
120
  if (typeof values.agent !== 'string' || !values.agent) { fail(`${operation} requires --agent`); return; }
108
121
  const agent = normalizeAgentToken(values.agent);
109
122
  if (!agent) { fail(`invalid --agent '${values.agent}': ${AGENT_USAGE_HINT}`); return; }
110
123
  values.agent = agent;
111
- if (operation === 'change-report') {
124
+ if (internalHandlerRoute('platform-pr', 'change-report', operation)) {
112
125
  if (typeof values.mechanicalFile !== 'string' || typeof values.precheckFile !== 'string') { fail('change-report requires --mechanical-file and --precheck-file'); return; }
113
126
  finish(await reportWrite(taskRef, {
114
127
  cwd, agent: values.agent, mechanicalFile: values.mechanicalFile, precheckFile: values.precheckFile, dryRun: values.dryRun === true
115
128
  }));
116
129
  return;
117
130
  }
118
- if (operation === 'skip') {
131
+ if (internalHandlerRoute('platform-pr', 'skip', operation)) {
119
132
  finish(await skipPlatformPullRequestFact(taskRef, { cwd, agent, dryRun: values.dryRun === true }));
120
133
  return;
121
134
  }
@@ -124,30 +137,41 @@ async function platformPr(args: string[] = []): Promise<void> {
124
137
  fail('--result must be pr_created, pr_reused, or no_op');
125
138
  return;
126
139
  }
127
- if (operation === 'resolve-external') {
140
+ if (internalHandlerRoute('platform-pr', 'resolve-external', operation)) {
128
141
  const pr = values.pr === undefined ? undefined : values.pr;
129
142
  if (pr !== undefined && (typeof pr !== 'string' || !pr)) { fail('resolve-external requires --pr <token>'); return; }
130
143
  finish(await resolveExternalPullRequest(taskRef, { cwd, agent: values.agent, pr, dryRun: values.dryRun === true }));
131
144
  return;
132
145
  }
133
- if (operation === 'bind') {
146
+ if (internalHandlerRoute('platform-pr', 'bind', operation)) {
134
147
  const pr = values.pr;
135
148
  if (typeof pr !== 'string' || !pr) { fail('bind requires --pr <token>'); return; }
136
149
  finish(await bindPlatformPullRequest(taskRef, { cwd, agent: values.agent, pr, dryRun: values.dryRun === true }));
137
150
  return;
138
151
  }
139
- if (operation === 'sync') {
152
+ if (internalHandlerRoute('platform-pr', 'sync', operation)) {
140
153
  if (values.metadata !== true && values.closingIssue !== true) { fail('sync requires --metadata or --closing-issue'); return; }
141
154
  if (!primaryResult) { fail('sync requires --result pr_created, pr_reused, or no_op'); return; }
142
155
  finish(await syncPlatformPullRequest(taskRef, { cwd, agent: values.agent, metadata: values.metadata === true, closingIssue: values.closingIssue === true, primaryResult: primaryResult as 'pr_created' | 'pr_reused' | 'no_op', dryRun: values.dryRun === true }));
143
156
  return;
144
157
  }
145
- if (operation === 'summary-sync') {
158
+ if (internalHandlerRoute('platform-pr', 'summary-sync', operation)) {
146
159
  if (typeof values.bodyFile !== 'string') { fail('summary-sync requires --body-file'); return; }
147
160
  if (typeof values.changeReportFile !== 'string') { fail('summary-sync requires --change-report-file'); return; }
148
161
  if (!primaryResult) { fail('summary-sync requires --result pr_created, pr_reused, or no_op'); return; }
149
162
  try {
150
- finish(await syncPullRequestSummary(taskRef, { cwd, agent: values.agent, body: readFile(values.bodyFile, cwd), changeReportFile: values.changeReportFile, primaryResult: primaryResult as 'pr_created' | 'pr_reused' | 'no_op', dryRun: values.dryRun === true, strict: values.strict === true }));
163
+ const manualPhase = values.manualPhase;
164
+ if (manualPhase !== undefined && manualPhase !== 'pending' && manualPhase !== 'final') { fail('--manual-phase must be pending or final'); return; }
165
+ finish(await syncPullRequestSummary(taskRef, {
166
+ cwd, agent: values.agent, body: readFile(values.bodyFile, cwd), changeReportFile: values.changeReportFile,
167
+ primaryResult: primaryResult as 'pr_created' | 'pr_reused' | 'no_op', dryRun: values.dryRun === true, strict: values.strict === true,
168
+ ...(manualPhase ? { manualValidation: {
169
+ phase: manualPhase,
170
+ ...(typeof values.transactionId === 'string' ? { transactionId: values.transactionId } : {}),
171
+ ...(typeof values.receiptDigest === 'string' ? { receiptDigest: values.receiptDigest } : {}),
172
+ ...(typeof values.prHeadSha === 'string' ? { prHeadSha: values.prHeadSha } : {})
173
+ } } : {})
174
+ }));
151
175
  } catch (error) {
152
176
  fail(`unable to read body file: ${error instanceof Error ? error.message : String(error)}`);
153
177
  }
@@ -2,6 +2,7 @@ import fs from 'node:fs';
2
2
  import path from 'node:path';
3
3
 
4
4
  import { publishReleaseNotes, releaseNoteContext, stageReleaseNotes } from '../platform/release-notes.ts';
5
+ import { ensureInternalHandlerRoute, internalHandlerRoute } from './cli-route-inventory.ts';
5
6
 
6
7
  type ParsedOptions = Readonly<{ values: ReadonlyMap<string, string>; switches: ReadonlySet<string> }>;
7
8
 
@@ -37,8 +38,9 @@ function finish(result: { status: string; [key: string]: unknown }): void {
37
38
  }
38
39
 
39
40
  async function platformReleaseNotes(args: string[] = []): Promise<void> {
41
+ if (!ensureInternalHandlerRoute('platform-release-notes', args)) return;
40
42
  const [action, ...rest] = args;
41
- if (action === 'context') {
43
+ if (internalHandlerRoute('platform-release-notes', 'context', action ?? '')) {
42
44
  const parsed = parseOptions(rest, ['--cwd', '--history-limit', '--from-tag', '--to-tag', '--branch']);
43
45
  if (!parsed) return finish(invalidInput('invalid context options'));
44
46
  const cwd = path.resolve(parsed.values.get('--cwd') || process.cwd());
@@ -51,7 +53,7 @@ async function platformReleaseNotes(args: string[] = []): Promise<void> {
51
53
  }, { cwd }));
52
54
  return;
53
55
  }
54
- if (action === 'stage') {
56
+ if (internalHandlerRoute('platform-release-notes', 'stage', action ?? '')) {
55
57
  const parsed = parseOptions(rest, ['--cwd', '--notes-file']);
56
58
  if (!parsed) return finish(invalidInput('invalid stage options'));
57
59
  const notesFile = parsed.values.get('--notes-file');
@@ -62,7 +64,7 @@ async function platformReleaseNotes(args: string[] = []): Promise<void> {
62
64
  ));
63
65
  return;
64
66
  }
65
- if (action === 'publish') {
67
+ if (internalHandlerRoute('platform-release-notes', 'publish', action ?? '')) {
66
68
  const parsed = parseOptions(
67
69
  rest,
68
70
  ['--cwd', '--notes-file', '--tag', '--title', '--expected-sha256'],
@@ -0,0 +1,83 @@
1
+ import path from 'node:path';
2
+
3
+ import { dismissSecurityAlert, readCommentFile, readSecurityAlert } from '../platform/security-alerts.ts';
4
+ import { ensureInternalHandlerRoute, internalHandlerRoute } from './cli-route-inventory.ts';
5
+
6
+ const USAGE = `Usage: agent-infra-internal platform-security read --kind <dependabot|code-scanning> --number <number> [--cwd <path>]
7
+ agent-infra-internal platform-security dismiss --kind <dependabot|code-scanning> --number <number> --reason <reason> --comment-file <path> [--cwd <path>]
8
+ `;
9
+
10
+ function fail(message: string): void {
11
+ process.stdout.write(`${JSON.stringify({ status: 'failed', changed: false, error: { code: 'SECURITY_INPUT_INVALID', message, retryable: false } })}\n`);
12
+ process.stderr.write(USAGE);
13
+ process.exitCode = 1;
14
+ }
15
+
16
+ function finish(result: { status: string; [key: string]: unknown }): void {
17
+ process.stdout.write(`${JSON.stringify(result)}\n`);
18
+ process.exitCode = result.status === 'blocked' ? 2 : result.status === 'failed' ? 1 : 0;
19
+ }
20
+
21
+ function parseOptions(args: string[]): { values: Map<string, string>; switches: Set<string> } | null {
22
+ const values = new Map<string, string>();
23
+ const switches = new Set<string>();
24
+ const valueFlags = new Set(['--cwd', '--kind', '--number', '--reason', '--comment-file']);
25
+ for (let index = 0; index < args.length; index += 1) {
26
+ const flag = args[index]!;
27
+ if (flag === '--help' || flag === '-h') return null;
28
+ if (!valueFlags.has(flag) || values.has(flag) || switches.has(flag)) return null;
29
+ const value = args[++index];
30
+ if (!value || value.startsWith('--')) return null;
31
+ values.set(flag, value);
32
+ }
33
+ return { values, switches };
34
+ }
35
+
36
+ function required(values: Map<string, string>, flag: string): string | null {
37
+ const value = values.get(flag);
38
+ return value && !value.startsWith('--') ? value : null;
39
+ }
40
+
41
+ async function platformSecurity(args: string[] = []): Promise<void> {
42
+ if (!ensureInternalHandlerRoute('platform-security', args)) return;
43
+ if (args[0] === '--help' || args[0] === '-h') { process.stdout.write(USAGE); return; }
44
+ const action = args[0];
45
+ if (!internalHandlerRoute('platform-security', 'read', action ?? '')
46
+ && !internalHandlerRoute('platform-security', 'dismiss', action ?? '')) {
47
+ fail('a valid operation is required');
48
+ return;
49
+ }
50
+ if (action !== 'read' && action !== 'dismiss') { fail('a valid operation is required'); return; }
51
+ const parsed = parseOptions(args.slice(1));
52
+ if (!parsed) { fail('invalid security alert options'); return; }
53
+ const kind = required(parsed.values, '--kind');
54
+ const numberRaw = required(parsed.values, '--number');
55
+ const cwd = path.resolve(required(parsed.values, '--cwd') || process.cwd());
56
+ if (!kind || !numberRaw) { fail(`${action} requires --kind and --number`); return; }
57
+ const number = Number(numberRaw);
58
+ if (!Number.isSafeInteger(number) || number <= 0) { fail('--number must be a positive integer'); return; }
59
+
60
+ if (action === 'read') {
61
+ if ([...parsed.values.keys()].some((flag) => !['--kind', '--number', '--cwd'].includes(flag))) {
62
+ fail('read does not accept dismissal options');
63
+ return;
64
+ }
65
+ finish(await readSecurityAlert({ kind: kind as 'dependabot' | 'code-scanning', number }, { cwd }));
66
+ return;
67
+ }
68
+
69
+ const reason = required(parsed.values, '--reason');
70
+ const commentFile = required(parsed.values, '--comment-file');
71
+ if (!reason || !commentFile) { fail('dismiss requires --reason and --comment-file'); return; }
72
+ const comment = readCommentFile(commentFile, cwd);
73
+ if (!comment.ok) {
74
+ finish({ status: 'failed', changed: false, error: comment.error });
75
+ return;
76
+ }
77
+ finish(await dismissSecurityAlert(
78
+ { kind: kind as 'dependabot' | 'code-scanning', number, reason, comment: comment.value },
79
+ { cwd }
80
+ ));
81
+ }
82
+
83
+ export { platformSecurity };
@@ -11,6 +11,7 @@ import {
11
11
  import type { DecisionRecord, HostResolution } from '../pr-review/evidence-grading.ts';
12
12
  import { inspectPlatformPullRequestByNumber } from '../platform/pull-requests.ts';
13
13
  import { verifyInProcess } from '../task/verification-engine.ts';
14
+ import { ensureInternalHandlerRoute, internalHandlerRoute } from './cli-route-inventory.ts';
14
15
 
15
16
  const USAGE = `Usage: agent-infra-internal pr-review-grade decide --input-file <path|-> [--cwd <path>]
16
17
  agent-infra-internal pr-review-grade resolve-host --pr <token> [--cwd <path>]
@@ -50,9 +51,14 @@ function readInput(value: string, cwd: string): string {
50
51
  }
51
52
 
52
53
  async function prReviewGrade(args: string[] = []): Promise<void> {
54
+ if (!ensureInternalHandlerRoute('pr-review-grade', args)) return;
53
55
  if (args[0] === '--help' || args[0] === '-h') { process.stdout.write(USAGE); return; }
54
56
  const operation = args[0];
55
- if (!operation || !['decide', 'resolve-host', 'verify-artifact'].includes(operation)) { fail('a valid operation is required'); return; }
57
+ if (!operation || ![
58
+ internalHandlerRoute('pr-review-grade', 'decide', operation),
59
+ internalHandlerRoute('pr-review-grade', 'resolve-host', operation),
60
+ internalHandlerRoute('pr-review-grade', 'verify-artifact', operation)
61
+ ].some(Boolean)) { fail('a valid operation is required'); return; }
56
62
  const parsed = parseFlags(args, 1);
57
63
  if (parsed.error) { fail(parsed.error); return; }
58
64
  const values = parsed.values;
@@ -65,7 +71,7 @@ async function prReviewGrade(args: string[] = []): Promise<void> {
65
71
  const unexpected = Object.keys(values).find((key) => !allowed[operation]!.includes(key));
66
72
  if (unexpected) { fail(`${operation} does not accept '--${unexpected}'`); return; }
67
73
 
68
- if (operation === 'decide') {
74
+ if (internalHandlerRoute('pr-review-grade', 'decide', operation)) {
69
75
  if (typeof values.inputFile !== 'string') { fail('decide requires --input-file'); return; }
70
76
  let input: unknown;
71
77
  try {
@@ -84,7 +90,7 @@ async function prReviewGrade(args: string[] = []): Promise<void> {
84
90
  return;
85
91
  }
86
92
 
87
- if (operation === 'resolve-host') {
93
+ if (internalHandlerRoute('pr-review-grade', 'resolve-host', operation)) {
88
94
  const pr = typeof values.pr === 'string' ? values.pr : '';
89
95
  if (!pr) { fail('resolve-host requires --pr <token>'); return; }
90
96
  const inspected = await inspectPlatformPullRequestByNumber(pr, { cwd });
@@ -109,6 +115,7 @@ async function prReviewGrade(args: string[] = []): Promise<void> {
109
115
  return;
110
116
  }
111
117
 
118
+ if (!internalHandlerRoute('pr-review-grade', 'verify-artifact', operation)) { fail('operation is not registered'); return; }
112
119
  if (typeof values.artifactFile !== 'string') { fail('verify-artifact requires --artifact-file'); return; }
113
120
  const artifactPath = path.resolve(cwd, values.artifactFile);
114
121
  let result: Record<string, unknown>;
@@ -1,6 +1,5 @@
1
1
  import fs from 'node:fs';
2
2
  import path from 'node:path';
3
- import crypto from 'node:crypto';
4
3
  import { spawnSync } from 'node:child_process';
5
4
  import semver from 'semver';
6
5
 
@@ -9,114 +8,95 @@ import { inspectPlatformRelease, reconcileReleaseMilestones } from '../platform/
9
8
  import { inspectHomebrewChannel, inspectNpmChannel } from '../release/channels.ts';
10
9
  import { releaseSnapshot } from '../release/workflow.ts';
11
10
  import type { PostReleaseFacts, ReleaseFacts } from '../release/workflow.ts';
11
+ import { collectDemoTranscript } from './demo-transcript.ts';
12
+ import { createDemoPromotion, promoteDemoAssets, recoverDemoPromotion } from './demo-promotion.ts';
13
+ import { ensureInternalHandlerRoute, internalHandlerRoute } from './cli-route-inventory.ts';
12
14
 
13
- function command(cwd: string, executable: string, args: string[]) {
14
- return spawnSync(executable, args, { cwd, encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] });
15
+ type CommandOptions = { env?: NodeJS.ProcessEnv };
16
+
17
+ function command(cwd: string, executable: string, args: string[], options: CommandOptions = {}) {
18
+ return spawnSync(executable, args, {
19
+ cwd, encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'], env: { ...process.env, ...options.env }
20
+ });
15
21
  }
16
22
 
17
23
  type CommandResult = ReturnType<typeof command>;
18
- type CommandRunner = (cwd: string, executable: string, args: string[]) => CommandResult;
24
+ type CommandRunner = (cwd: string, executable: string, args: string[], options?: CommandOptions) => CommandResult;
19
25
  type DemoResult = {
20
26
  status: 'recorded' | 'skipped' | 'failed';
21
- reasonCode: 'DEMO_INPUTS_UNCHANGED' | 'GIT_LFS_MISSING' | 'VHS_MISSING' | 'FFMPEG_MISSING'
27
+ reasonCode: 'DEMO_TRANSCRIPT_UNCHANGED' | 'DEMO_TRANSCRIPT_UNAVAILABLE' | 'DEMO_TRANSCRIPT_FAILED'
28
+ | 'DEMO_PROMOTION_FAILED' | 'DEMO_PROMOTION_RECOVERY_REQUIRED' | 'DEMO_PROMOTION_CLEANUP_FAILED'
29
+ | 'GIT_LFS_MISSING' | 'VHS_MISSING' | 'FFMPEG_MISSING'
22
30
  | 'DEMO_COMMAND_FAILED' | 'DEMO_OUTPUT_MISSING' | 'DEMO_OUTPUT_INVALID'
23
- | 'DEMO_OUTPUT_TOO_LARGE' | 'DEMO_DIGEST_FAILED' | null;
31
+ | 'DEMO_OUTPUT_TOO_LARGE' | null;
24
32
  message: string | null;
25
33
  outputPath: string | null;
26
34
  };
27
35
 
28
- // The tape is the canonical init interaction contract. Do not hash all of
29
- // lib/init.ts: non-visual config serialization changes must not re-record it.
30
- const DEMO_INPUT_PATHS = [
31
- 'assets/demo-init.tape',
32
- 'scripts/demo-regen.sh',
33
- 'scripts/normalize-gif-duration.py',
34
- 'bin/cli.ts',
35
- 'lib/log.ts',
36
- 'lib/prompt.ts',
37
- 'lib/paths.ts',
38
- 'lib/render.ts',
39
- 'lib/sandbox/engines/'
40
- ] as const;
41
- const DEMO_DIGEST_PATH = 'assets/demo-init.inputs.sha256';
36
+ const DEMO_DIGEST_PATH = 'assets/demo-init.transcript.sha256';
42
37
  const DEMO_OUTPUT_PATH = 'assets/demo-init.gif';
43
38
  const DEMO_MAX_BYTES = 4 * 1024 * 1024;
44
39
 
45
- function demoInputFiles(cwd: string): string[] {
46
- const files = new Set<string>();
47
- for (const input of DEMO_INPUT_PATHS) {
48
- if (input.endsWith('/')) {
49
- const listed = command(cwd, 'git', ['ls-files', '--', input]);
50
- if (listed.status !== 0) throw new Error(String(listed.stderr || `Unable to list ${input}`));
51
- const directoryFiles = String(listed.stdout).split('\n').filter(Boolean);
52
- if (!directoryFiles.length) throw new Error(`Canonical demo input directory is empty: ${input}`);
53
- for (const file of directoryFiles) files.add(file.replaceAll('\\', '/'));
54
- } else {
55
- files.add(input);
56
- }
57
- }
58
- const sorted = [...files].sort((left, right) => Buffer.from(left).compare(Buffer.from(right)));
59
- if (!sorted.length) throw new Error('Canonical demo input set is empty');
60
- for (const file of sorted) {
61
- const absolute = path.join(cwd, file);
62
- if (!fs.statSync(absolute).isFile()) throw new Error(`Canonical demo input is not a file: ${file}`);
63
- }
64
- return sorted;
65
- }
66
-
67
- function computeDemoInputDigest(cwd: string): string {
68
- const hash = crypto.createHash('sha256');
69
- for (const file of demoInputFiles(cwd)) {
70
- const bytes = fs.readFileSync(path.join(cwd, file));
71
- hash.update(file);
72
- hash.update('\0');
73
- hash.update(String(bytes.byteLength));
74
- hash.update('\0');
75
- hash.update(bytes);
76
- hash.update('\0');
77
- }
78
- return hash.digest('hex');
79
- }
80
-
81
40
  function validGif(filePath: string): boolean {
82
41
  if (!fs.existsSync(filePath)) return false;
83
42
  const header = fs.readFileSync(filePath).subarray(0, 6).toString('ascii');
84
43
  return header === 'GIF87a' || header === 'GIF89a';
85
44
  }
86
45
 
87
- function writeDigestAtomically(cwd: string, digest: string): void {
88
- const target = path.join(cwd, DEMO_DIGEST_PATH);
89
- const temporary = `${target}.tmp-${process.pid}`;
90
- fs.writeFileSync(temporary, `${digest}\n`);
91
- fs.renameSync(temporary, target);
92
- }
93
-
94
- function runOptionalDemo(cwd: string, run: CommandRunner = command): DemoResult {
95
- let digest: string;
96
- try {
97
- digest = computeDemoInputDigest(cwd);
98
- } catch (error) {
99
- return { status: 'failed', reasonCode: 'DEMO_DIGEST_FAILED', message: String(error), outputPath: null };
46
+ async function runOptionalDemo(
47
+ cwd: string,
48
+ run: CommandRunner = command,
49
+ collect = collectDemoTranscript
50
+ ): Promise<DemoResult> {
51
+ const recovery = recoverDemoPromotion(cwd);
52
+ if (recovery?.status === 'failed') {
53
+ return { status: 'failed', reasonCode: recovery.code === 'DEMO_PROMOTION_CLEANUP_FAILED' ? recovery.code : 'DEMO_PROMOTION_RECOVERY_REQUIRED', message: recovery.message, outputPath: null };
54
+ }
55
+ const transcript = await collect(cwd);
56
+ if (transcript.status === 'failed') {
57
+ return { status: 'failed', reasonCode: transcript.reasonCode, message: transcript.message, outputPath: null };
100
58
  }
101
59
  const digestPath = path.join(cwd, DEMO_DIGEST_PATH);
102
60
  const priorDigest = fs.existsSync(digestPath) ? fs.readFileSync(digestPath, 'utf8').trim() : '';
103
- if (/^[0-9a-f]{64}$/.test(priorDigest) && priorDigest === digest) {
104
- return { status: 'skipped', reasonCode: 'DEMO_INPUTS_UNCHANGED', message: null, outputPath: null };
61
+ if (/^[0-9a-f]{64}$/.test(priorDigest) && priorDigest === transcript.sha256) {
62
+ return { status: 'skipped', reasonCode: 'DEMO_TRANSCRIPT_UNCHANGED', message: null, outputPath: null };
105
63
  }
64
+ const promotion = createDemoPromotion(cwd, transcript.transcript);
65
+ const discardPromotion = () => fs.rmSync(promotion.directory, { recursive: true, force: true });
106
66
  const lfs = run(cwd, 'git', ['lfs', 'version']);
107
67
  if (lfs.status !== 0) {
68
+ discardPromotion();
108
69
  return { status: 'failed', reasonCode: 'GIT_LFS_MISSING', message: String(lfs.stderr || lfs.stdout), outputPath: null };
109
70
  }
110
71
  const lfsAttribute = run(cwd, 'git', ['check-attr', 'filter', '--', DEMO_OUTPUT_PATH]);
111
72
  if (lfsAttribute.status !== 0 || !String(lfsAttribute.stdout).trim().endsWith(': lfs')) {
73
+ discardPromotion();
112
74
  return { status: 'failed', reasonCode: 'GIT_LFS_MISSING', message: `${DEMO_OUTPUT_PATH} is not tracked by Git LFS`, outputPath: null };
113
75
  }
114
76
  const vhs = run(cwd, 'vhs', ['--version']);
115
- if (vhs.status !== 0) return { status: 'skipped', reasonCode: 'VHS_MISSING', message: null, outputPath: null };
77
+ if (vhs.status !== 0) {
78
+ discardPromotion();
79
+ return {
80
+ status: 'failed',
81
+ reasonCode: 'VHS_MISSING',
82
+ message: String(vhs.stderr || vhs.stdout) || 'VHS is required to record the changed demo transcript',
83
+ outputPath: null
84
+ };
85
+ }
116
86
  const ffmpeg = run(cwd, 'ffmpeg', ['-version']);
117
- if (ffmpeg.status !== 0) return { status: 'skipped', reasonCode: 'FFMPEG_MISSING', message: null, outputPath: null };
118
- const demo = run(cwd, 'npm', ['run', 'demo:regen']);
87
+ if (ffmpeg.status !== 0) {
88
+ discardPromotion();
89
+ return {
90
+ status: 'failed',
91
+ reasonCode: 'FFMPEG_MISSING',
92
+ message: String(ffmpeg.stderr || ffmpeg.stdout) || 'ffmpeg is required to record the changed demo transcript',
93
+ outputPath: null
94
+ };
95
+ }
96
+ const stagedOutputPath = path.relative(cwd, promotion.staging.gif).replaceAll(path.sep, '/');
97
+ const demo = run(cwd, 'npm', ['run', 'demo:regen'], { env: { DEMO_OUTPUT_PATH: stagedOutputPath } });
119
98
  if (demo.status !== 0) {
99
+ discardPromotion();
120
100
  return {
121
101
  status: 'failed',
122
102
  reasonCode: 'DEMO_COMMAND_FAILED',
@@ -125,7 +105,8 @@ function runOptionalDemo(cwd: string, run: CommandRunner = command): DemoResult
125
105
  };
126
106
  }
127
107
  const outputPath = DEMO_OUTPUT_PATH;
128
- if (!fs.existsSync(path.join(cwd, outputPath))) {
108
+ if (!fs.existsSync(promotion.staging.gif)) {
109
+ discardPromotion();
129
110
  return {
130
111
  status: 'failed',
131
112
  reasonCode: 'DEMO_OUTPUT_MISSING',
@@ -133,18 +114,24 @@ function runOptionalDemo(cwd: string, run: CommandRunner = command): DemoResult
133
114
  outputPath: null
134
115
  };
135
116
  }
136
- const absoluteOutput = path.join(cwd, outputPath);
117
+ const absoluteOutput = promotion.staging.gif;
137
118
  if (!validGif(absoluteOutput)) {
119
+ discardPromotion();
138
120
  return { status: 'failed', reasonCode: 'DEMO_OUTPUT_INVALID', message: `${outputPath} is not a GIF`, outputPath: null };
139
121
  }
140
122
  if (fs.statSync(absoluteOutput).size > DEMO_MAX_BYTES) {
123
+ discardPromotion();
141
124
  return { status: 'failed', reasonCode: 'DEMO_OUTPUT_TOO_LARGE', message: `${outputPath} exceeds 4 MiB`, outputPath: null };
142
125
  }
143
- const pointer = run(cwd, 'git', ['lfs', 'pointer', `--file=${outputPath}`]);
126
+ const pointer = run(cwd, 'git', ['lfs', 'pointer', `--file=${stagedOutputPath}`]);
144
127
  if (pointer.status !== 0 || !String(pointer.stdout).includes(`size ${fs.statSync(absoluteOutput).size}`)) {
128
+ discardPromotion();
145
129
  return { status: 'failed', reasonCode: 'DEMO_OUTPUT_INVALID', message: 'Git LFS could not produce a matching pointer', outputPath: null };
146
130
  }
147
- writeDigestAtomically(cwd, digest);
131
+ const promoted = promoteDemoAssets(cwd, promotion.staging, `${process.pid}-${Date.now()}`);
132
+ if (promoted.status === 'failed') {
133
+ return { status: 'failed', reasonCode: promoted.code ?? 'DEMO_PROMOTION_FAILED', message: promoted.message, outputPath: null };
134
+ }
148
135
  return { status: 'recorded', reasonCode: null, message: null, outputPath };
149
136
  }
150
137
 
@@ -206,7 +193,7 @@ function inspectPostReleaseFacts(
206
193
  remoteHead,
207
194
  newVersion,
208
195
  changedPaths,
209
- demoInputSha256: digest && /^[0-9a-f]{64}$/.test(digest) ? digest : null,
196
+ demoTranscriptSha256: digest && /^[0-9a-f]{64}$/.test(digest) ? digest : null,
210
197
  worktree: snapshot?.worktree ?? [],
211
198
  staged: snapshot?.staged ?? []
212
199
  };
@@ -312,20 +299,28 @@ async function releaseWorkflow(args: string[] = []): Promise<void> {
312
299
  const version = String(rawVersion || '').replace(/^v/, '');
313
300
  const cwdIndex = rest.indexOf('--cwd');
314
301
  const cwd = path.resolve(cwdIndex >= 0 && rest[cwdIndex + 1] ? rest[cwdIndex + 1]! : process.cwd());
315
- if (!['inspect', 'prepare', 'publish', 'post-prepare', 'post-publish'].includes(action || '') || !semver.valid(version)) {
302
+ if (![
303
+ internalHandlerRoute('release-workflow', 'inspect', action || ''),
304
+ internalHandlerRoute('release-workflow', 'prepare', action || ''),
305
+ internalHandlerRoute('release-workflow', 'publish', action || ''),
306
+ internalHandlerRoute('release-workflow', 'post-prepare', action || ''),
307
+ internalHandlerRoute('release-workflow', 'post-publish', action || '')
308
+ ].some(Boolean) || !semver.valid(version)) {
316
309
  process.stdout.write(`${JSON.stringify({ status: 'failed', changed: false, error: { code: 'RELEASE_INPUT_INVALID', message: 'Usage: release-workflow inspect|prepare|publish|post-prepare|post-publish <version>' } })}\n`);
317
310
  process.exitCode = 1; return;
318
311
  }
312
+ if (!ensureInternalHandlerRoute('release-workflow', args)) return;
319
313
  const before = releaseSnapshot(version, await inspectFacts(cwd, version));
320
- if (action === 'inspect') { process.stdout.write(`${JSON.stringify({ status: 'no-op', changed: false, snapshot: before, error: null })}\n`); return; }
314
+ if (internalHandlerRoute('release-workflow', 'inspect', action || '')) { process.stdout.write(`${JSON.stringify({ status: 'no-op', changed: false, snapshot: before, error: null })}\n`); return; }
321
315
  if (before.facts.localTagConflict) {
322
316
  process.stdout.write(`${JSON.stringify({ status: 'failed', changed: false, snapshot: before, error: { code: 'GIT_TAG_CONFLICT', message: `Tag v${version} is not reachable from HEAD` } })}\n`);
323
317
  process.exitCode = 1; return;
324
318
  }
325
- if (action === 'prepare') {
319
+ if (internalHandlerRoute('release-workflow', 'prepare', action || '')) {
326
320
  if (before.phase !== 'unprepared') {
327
321
  const milestones = await reconcileReleaseMilestones(version, { cwd });
328
- process.stdout.write(`${JSON.stringify({ ...milestones, snapshot: before })}\n`);
322
+ const snapshot = releaseSnapshot(version, await inspectFacts(cwd, version));
323
+ process.stdout.write(`${JSON.stringify({ ...milestones, snapshot })}\n`);
329
324
  process.exitCode = milestones.status === 'failed' ? 1 : milestones.status === 'blocked' ? 2 : 0;
330
325
  return;
331
326
  }
@@ -352,14 +347,14 @@ async function releaseWorkflow(args: string[] = []): Promise<void> {
352
347
  process.stdout.write(`${JSON.stringify({ status, changed: true, snapshot, operations: milestones?.operations ?? [], error: tagged.status !== 0 ? { code: 'GIT_TAG_FAILED', message: String(tagged.stderr) } : milestones?.error ?? null })}\n`);
353
348
  process.exitCode = failed ? 1 : blocked ? 2 : 0; return;
354
349
  }
355
- if (action === 'publish') {
350
+ if (internalHandlerRoute('release-workflow', 'publish', action || '')) {
356
351
  if (!before.facts.localTag || !['prepared', 'partially-published'].includes(before.phase)) { process.stdout.write(`${JSON.stringify({ status: 'failed', changed: false, snapshot: before, error: { code: 'RELEASE_PHASE_INVALID', message: `Cannot publish from ${before.phase}` } })}\n`); process.exitCode = 1; return; }
357
352
  const branch = git(cwd, ['branch', '--show-current']) || '';
358
353
  const refs = [...(!before.facts.remoteBranch ? [branch] : []), ...(!before.facts.remoteTag ? [`refs/tags/v${version}`] : [])];
359
354
  const result = pushGitRefs({ cwd, remote: 'origin', refs });
360
355
  process.stdout.write(`${JSON.stringify({ ...result, snapshot: releaseSnapshot(version, await inspectFacts(cwd, version)) })}\n`); process.exitCode = result.status === 'failed' ? 1 : result.status === 'degraded' ? 2 : 0; return;
361
356
  }
362
- if (action === 'post-publish') {
357
+ if (internalHandlerRoute('release-workflow', 'post-publish', action || '')) {
363
358
  const expectedValues = optionValues(rest, '--expected-sha256');
364
359
  if (expectedValues.length !== 1 || !/^sha256:[0-9a-f]{64}$/.test(expectedValues[0]!)) {
365
360
  process.stdout.write(`${JSON.stringify({ status: 'failed', changed: false, snapshot: before, error: { code: 'RELEASE_INPUT_INVALID', message: 'post-publish requires one --expected-sha256 sha256:<64 hex>' } })}\n`);
@@ -394,6 +389,10 @@ async function releaseWorkflow(args: string[] = []): Promise<void> {
394
389
  process.exitCode = blocked ? 2 : 1; return;
395
390
  }
396
391
 
392
+ if (!internalHandlerRoute('release-workflow', 'post-prepare', action || '')) {
393
+ process.stdout.write(`${JSON.stringify({ status: 'failed', changed: false, snapshot: before, error: { code: 'RELEASE_INPUT_INVALID', message: 'release-workflow action is not registered' } })}\n`);
394
+ process.exitCode = 1; return;
395
+ }
397
396
  if (before.phase === 'complete') {
398
397
  process.stdout.write(`${JSON.stringify({ status: 'no-op', changed: false, snapshot: before, error: null })}\n`);
399
398
  return;
@@ -408,6 +407,11 @@ async function releaseWorkflow(args: string[] = []): Promise<void> {
408
407
  if (!['published', 'post-pending'].includes(before.phase) || !channelsComplete || before.facts.smoke !== 'success') {
409
408
  process.stdout.write(`${JSON.stringify({ status: before.facts.smoke === 'failed' ? 'failed' : 'blocked', changed: false, snapshot: before, error: { code: 'RELEASE_CHANNELS_PENDING', message: 'Release channels or smoke workflow are not complete' } })}\n`); process.exitCode = before.facts.smoke === 'failed' ? 1 : 2; return;
410
409
  }
410
+ const recovery = recoverDemoPromotion(cwd);
411
+ if (recovery?.status === 'failed') {
412
+ process.stdout.write(`${JSON.stringify({ status: 'failed', changed: true, snapshot: before, error: { code: recovery.code, message: recovery.message } })}\n`);
413
+ process.exitCode = 1; return;
414
+ }
411
415
  const worktreeError = inspectPostWorktree(cwd);
412
416
  if (worktreeError) {
413
417
  process.stdout.write(`${JSON.stringify({ status: 'failed', changed: false, snapshot: before, error: worktreeError })}\n`);
@@ -418,7 +422,7 @@ async function releaseWorkflow(args: string[] = []): Promise<void> {
418
422
  process.stdout.write(`${JSON.stringify({ status: 'failed', changed: true, snapshot: before, error: { code: 'RELEASE_POST_COMMAND_FAILED', message: String(built.stderr || built.stdout) } })}\n`);
419
423
  process.exitCode = 1; return;
420
424
  }
421
- const demo = runOptionalDemo(cwd);
425
+ const demo = await runOptionalDemo(cwd);
422
426
  if (demo.status === 'failed') {
423
427
  process.stdout.write(`${JSON.stringify({ status: 'failed', changed: true, snapshot: before, demo, error: { code: 'RELEASE_POST_DEMO_FAILED', message: demo.message } })}\n`);
424
428
  process.exitCode = 1; return;
@@ -437,5 +441,5 @@ async function releaseWorkflow(args: string[] = []): Promise<void> {
437
441
  process.stdout.write(`${JSON.stringify({ status: 'applied', changed: true, demo, snapshot, error: null })}\n`);
438
442
  }
439
443
 
440
- export { changedPaths, computeDemoInputDigest, inspectFacts, inspectLocalReleaseFacts, inspectPostReleaseFacts, inspectPostWorktree, releaseSmokeStatus, releaseWorkflow, runOptionalDemo };
444
+ export { changedPaths, inspectFacts, inspectLocalReleaseFacts, inspectPostReleaseFacts, inspectPostWorktree, releaseSmokeStatus, releaseWorkflow, runOptionalDemo };
441
445
  export type { CommandRunner, DemoResult };