@fitlab-ai/agent-infra 0.8.5 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (425) hide show
  1. package/bin/cli.ts +2 -2
  2. package/dist/bin/cli.js +2 -2
  3. package/dist/bin/internal-cli.js +98 -0
  4. package/dist/lib/decide.js +81 -90
  5. package/dist/lib/defaults.json +1 -0
  6. package/dist/lib/git/review-snapshot.js +79 -0
  7. package/dist/lib/git/workflow.js +101 -0
  8. package/dist/lib/internal/git-workflow.js +42 -0
  9. package/dist/lib/internal/platform-checks.js +119 -0
  10. package/dist/lib/internal/platform-comment.js +129 -0
  11. package/dist/lib/internal/platform-context.js +46 -0
  12. package/dist/lib/internal/platform-issue.js +142 -0
  13. package/dist/lib/internal/platform-pr.js +146 -0
  14. package/dist/lib/internal/release-workflow.js +217 -0
  15. package/dist/lib/internal/task-artifact.js +67 -0
  16. package/dist/lib/internal/task-context.js +43 -0
  17. package/dist/lib/internal/task-event.js +70 -0
  18. package/dist/lib/internal/task-ledger.js +114 -0
  19. package/dist/lib/internal/task-lifecycle.js +58 -0
  20. package/dist/lib/internal/task-short-id.js +83 -0
  21. package/dist/lib/internal/task-snapshot.js +47 -0
  22. package/dist/lib/internal/task-verify.js +50 -0
  23. package/dist/lib/internal/task-warning.js +74 -0
  24. package/dist/lib/platform/adapters.js +14 -0
  25. package/dist/lib/platform/context.js +146 -0
  26. package/dist/lib/platform/github-client.js +124 -0
  27. package/dist/lib/platform/issue-comments.js +409 -0
  28. package/dist/lib/platform/issue-metadata.js +183 -0
  29. package/dist/lib/platform/issues.js +617 -0
  30. package/dist/lib/platform/metadata-labels.js +28 -0
  31. package/dist/lib/platform/pr-checks.js +216 -0
  32. package/dist/lib/platform/pr-summary.js +88 -0
  33. package/dist/lib/platform/pull-request-metadata.js +38 -0
  34. package/dist/lib/platform/pull-requests.js +338 -0
  35. package/dist/lib/platform/releases.js +82 -0
  36. package/dist/lib/platform/types.js +22 -0
  37. package/dist/lib/platform/verification-required.js +78 -0
  38. package/dist/lib/platform/verification-sync.js +1014 -0
  39. package/dist/lib/platform/verification.js +6 -0
  40. package/dist/lib/release/channels.js +31 -0
  41. package/dist/lib/release/workflow.js +32 -0
  42. package/dist/lib/render.js +3 -2
  43. package/dist/lib/run/index.js +24 -7
  44. package/dist/lib/sandbox/capture.js +25 -3
  45. package/dist/lib/sandbox/commands/create.js +36 -22
  46. package/dist/lib/sandbox/commands/enter.js +34 -11
  47. package/dist/lib/sandbox/commands/list-running.js +10 -8
  48. package/dist/lib/sandbox/commands/show.js +2 -2
  49. package/dist/lib/sandbox/commands/start.js +53 -15
  50. package/dist/lib/sandbox/index.js +6 -6
  51. package/dist/lib/sandbox/mounts.js +36 -0
  52. package/dist/lib/sandbox/recovery.js +562 -0
  53. package/dist/lib/sandbox/task-resolver.js +5 -1
  54. package/dist/lib/sandbox/tools.js +19 -0
  55. package/dist/lib/server/protocol.js +5 -5
  56. package/dist/lib/task/activity-log.js +61 -0
  57. package/dist/lib/task/artifact-lifecycle.js +427 -0
  58. package/dist/lib/task/command-options.js +30 -0
  59. package/dist/lib/task/commands/cat.js +36 -6
  60. package/dist/lib/task/commands/decisions.js +80 -46
  61. package/dist/lib/task/commands/files.js +21 -7
  62. package/dist/lib/task/commands/grep.js +55 -7
  63. package/dist/lib/task/commands/issue-body.js +23 -14
  64. package/dist/lib/task/commands/log.js +27 -70
  65. package/dist/lib/task/commands/show.js +20 -7
  66. package/dist/lib/task/commands/status.js +18 -8
  67. package/dist/lib/task/decision-intents.js +154 -0
  68. package/dist/lib/task/decision-items.js +53 -0
  69. package/dist/lib/task/events.js +318 -0
  70. package/dist/lib/task/frontmatter.js +159 -1
  71. package/dist/lib/task/implementation-inputs.js +117 -0
  72. package/dist/lib/task/index.js +12 -9
  73. package/dist/lib/task/issue-form.js +19 -6
  74. package/dist/lib/task/ledger-intents.js +185 -0
  75. package/dist/lib/task/ledger.js +65 -35
  76. package/dist/lib/task/lifecycle.js +484 -0
  77. package/dist/lib/task/resolve-ref.js +152 -32
  78. package/dist/lib/task/review-artifacts.js +101 -0
  79. package/dist/lib/task/review-fingerprint.js +46 -0
  80. package/dist/lib/task/sections.js +358 -4
  81. package/dist/lib/task/short-id.js +292 -7
  82. package/dist/lib/task/snapshot.js +95 -0
  83. package/dist/lib/task/verification-checks.js +8 -0
  84. package/dist/lib/task/verification-config.js +14 -0
  85. package/dist/lib/task/verification-engine.js +906 -0
  86. package/dist/lib/task/verification-review.js +3 -0
  87. package/dist/lib/task/verification-types.js +2 -0
  88. package/dist/lib/task/verification.js +113 -0
  89. package/dist/lib/task/workflow-warning-intents.js +131 -0
  90. package/dist/lib/task/workflow-warnings.js +10 -78
  91. package/dist/lib/task/write.js +241 -0
  92. package/lib/decide.ts +57 -97
  93. package/lib/defaults.json +1 -0
  94. package/lib/git/review-snapshot.ts +73 -0
  95. package/lib/git/workflow.ts +101 -0
  96. package/lib/internal/git-workflow.ts +37 -0
  97. package/lib/internal/platform-checks.ts +101 -0
  98. package/lib/internal/platform-comment.ts +99 -0
  99. package/lib/internal/platform-context.ts +33 -0
  100. package/lib/internal/platform-issue.ts +116 -0
  101. package/lib/internal/platform-pr.ts +119 -0
  102. package/lib/internal/release-workflow.ts +186 -0
  103. package/lib/internal/task-artifact.ts +47 -0
  104. package/lib/internal/task-context.ts +35 -0
  105. package/lib/internal/task-event.ts +51 -0
  106. package/lib/internal/task-ledger.ts +86 -0
  107. package/lib/internal/task-lifecycle.ts +41 -0
  108. package/lib/internal/task-short-id.ts +55 -0
  109. package/lib/internal/task-snapshot.ts +32 -0
  110. package/lib/internal/task-verify.ts +33 -0
  111. package/lib/internal/task-warning.ts +50 -0
  112. package/lib/platform/adapters.ts +30 -0
  113. package/lib/platform/context.ts +164 -0
  114. package/lib/platform/github-client.ts +146 -0
  115. package/lib/platform/issue-comments.ts +479 -0
  116. package/lib/platform/issue-metadata.ts +250 -0
  117. package/lib/platform/issues.ts +691 -0
  118. package/lib/platform/metadata-labels.ts +33 -0
  119. package/lib/platform/pr-checks.ts +246 -0
  120. package/lib/platform/pr-summary.ts +90 -0
  121. package/lib/platform/pull-request-metadata.ts +69 -0
  122. package/lib/platform/pull-requests.ts +380 -0
  123. package/lib/platform/releases.ts +71 -0
  124. package/lib/platform/types.ts +74 -0
  125. package/lib/platform/verification-required.ts +83 -0
  126. package/{runtime/platform-adapters/platform-sync.github.js → lib/platform/verification-sync.ts} +198 -367
  127. package/lib/platform/verification.ts +11 -0
  128. package/lib/release/channels.ts +30 -0
  129. package/lib/release/workflow.ts +31 -0
  130. package/lib/render.ts +3 -2
  131. package/lib/run/index.ts +25 -7
  132. package/lib/sandbox/capture.ts +29 -3
  133. package/lib/sandbox/commands/create.ts +43 -25
  134. package/lib/sandbox/commands/enter.ts +38 -11
  135. package/lib/sandbox/commands/list-running.ts +10 -8
  136. package/lib/sandbox/commands/show.ts +2 -2
  137. package/lib/sandbox/commands/start.ts +50 -16
  138. package/lib/sandbox/index.ts +6 -6
  139. package/lib/sandbox/mounts.ts +55 -0
  140. package/lib/sandbox/recovery.ts +828 -0
  141. package/lib/sandbox/task-resolver.ts +5 -1
  142. package/lib/sandbox/tools.ts +31 -0
  143. package/lib/server/protocol.ts +5 -5
  144. package/lib/task/activity-log.ts +62 -0
  145. package/lib/task/artifact-lifecycle.ts +484 -0
  146. package/lib/task/command-options.ts +32 -0
  147. package/lib/task/commands/cat.ts +22 -9
  148. package/lib/task/commands/decisions.ts +49 -47
  149. package/lib/task/commands/files.ts +14 -6
  150. package/lib/task/commands/grep.ts +34 -7
  151. package/lib/task/commands/issue-body.ts +16 -16
  152. package/lib/task/commands/log.ts +17 -70
  153. package/lib/task/commands/show.ts +14 -6
  154. package/lib/task/commands/status.ts +17 -7
  155. package/lib/task/decision-intents.ts +163 -0
  156. package/lib/task/decision-items.ts +64 -0
  157. package/lib/task/events.ts +320 -0
  158. package/lib/task/frontmatter.ts +202 -2
  159. package/lib/task/implementation-inputs.ts +137 -0
  160. package/lib/task/index.ts +12 -9
  161. package/lib/task/issue-form.ts +20 -6
  162. package/lib/task/ledger-intents.ts +201 -0
  163. package/lib/task/ledger.ts +77 -31
  164. package/lib/task/lifecycle.ts +458 -0
  165. package/lib/task/resolve-ref.ts +197 -36
  166. package/{templates/.agents/scripts/lib/review-artifacts.js → lib/task/review-artifacts.ts} +11 -12
  167. package/{templates/.agents/scripts/lib/post-review-commit.js → lib/task/review-fingerprint.ts} +17 -12
  168. package/lib/task/sections.ts +486 -4
  169. package/lib/task/short-id.ts +403 -9
  170. package/lib/task/snapshot.ts +143 -0
  171. package/lib/task/verification-checks.ts +9 -0
  172. package/lib/task/verification-config.ts +20 -0
  173. package/{templates/.agents/scripts/validate-artifact.js → lib/task/verification-engine.ts} +199 -406
  174. package/lib/task/verification-review.ts +9 -0
  175. package/lib/task/verification-types.ts +29 -0
  176. package/lib/task/verification.ts +156 -0
  177. package/lib/task/workflow-warning-intents.ts +142 -0
  178. package/lib/task/workflow-warnings.ts +10 -78
  179. package/lib/task/write.ts +468 -0
  180. package/package.json +4 -2
  181. package/templates/.agents/README.en.md +4 -3
  182. package/templates/.agents/README.zh-CN.md +4 -3
  183. package/templates/.agents/rules/README.en.md +6 -5
  184. package/templates/.agents/rules/README.zh-CN.md +6 -5
  185. package/templates/.agents/rules/create-issue.en.md +1 -1
  186. package/templates/.agents/rules/create-issue.github.en.md +5 -184
  187. package/templates/.agents/rules/create-issue.github.zh-CN.md +12 -178
  188. package/templates/.agents/rules/create-issue.zh-CN.md +1 -1
  189. package/templates/.agents/rules/human-decision-context.en.md +36 -0
  190. package/templates/.agents/rules/human-decision-context.zh-CN.md +36 -0
  191. package/templates/.agents/rules/issue-fields.github.en.md +4 -148
  192. package/templates/.agents/rules/issue-fields.github.zh-CN.md +9 -144
  193. package/templates/.agents/rules/issue-pr-commands.en.md +43 -3
  194. package/templates/.agents/rules/issue-pr-commands.github.en.md +15 -220
  195. package/templates/.agents/rules/issue-pr-commands.github.zh-CN.md +23 -213
  196. package/templates/.agents/rules/issue-pr-commands.zh-CN.md +45 -3
  197. package/templates/.agents/rules/issue-sync.en.md +1 -1
  198. package/templates/.agents/rules/issue-sync.github.en.md +14 -301
  199. package/templates/.agents/rules/issue-sync.github.zh-CN.md +36 -287
  200. package/templates/.agents/rules/issue-sync.zh-CN.md +1 -1
  201. package/templates/.agents/rules/milestone-inference.en.md +1 -1
  202. package/templates/.agents/rules/milestone-inference.github.en.md +7 -119
  203. package/templates/.agents/rules/milestone-inference.github.zh-CN.md +8 -108
  204. package/templates/.agents/rules/milestone-inference.zh-CN.md +1 -1
  205. package/templates/.agents/rules/next-step-output.en.md +12 -11
  206. package/templates/.agents/rules/next-step-output.zh-CN.md +12 -11
  207. package/templates/.agents/rules/no-mid-flow-questions.en.md +3 -3
  208. package/templates/.agents/rules/no-mid-flow-questions.zh-CN.md +3 -3
  209. package/templates/.agents/rules/pr-checks-commands.en.md +16 -3
  210. package/templates/.agents/rules/pr-checks-commands.github.en.md +10 -54
  211. package/templates/.agents/rules/pr-checks-commands.github.zh-CN.md +19 -47
  212. package/templates/.agents/rules/pr-checks-commands.zh-CN.md +30 -3
  213. package/templates/.agents/rules/pr-sync.en.md +30 -3
  214. package/templates/.agents/rules/pr-sync.github.en.md +12 -115
  215. package/templates/.agents/rules/pr-sync.github.zh-CN.md +26 -104
  216. package/templates/.agents/rules/pr-sync.zh-CN.md +43 -3
  217. package/templates/.agents/rules/review-handshake.en.md +8 -5
  218. package/templates/.agents/rules/review-handshake.zh-CN.md +8 -5
  219. package/templates/.agents/rules/task-management.en.md +11 -2
  220. package/templates/.agents/rules/task-management.zh-CN.md +11 -3
  221. package/templates/.agents/rules/task-short-id.en.md +25 -29
  222. package/templates/.agents/rules/task-short-id.zh-CN.md +20 -23
  223. package/templates/.agents/scripts/lib/agent-infra-package.js +1 -9
  224. package/templates/.agents/scripts/task-short-id.js +71 -533
  225. package/templates/.agents/skills/analyze-task/SKILL.en.md +22 -55
  226. package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +26 -59
  227. package/templates/.agents/skills/analyze-task/config/verify.en.json +1 -3
  228. package/templates/.agents/skills/analyze-task/config/verify.zh-CN.json +1 -3
  229. package/templates/.agents/skills/block-task/SKILL.en.md +21 -47
  230. package/templates/.agents/skills/block-task/SKILL.zh-CN.md +23 -47
  231. package/templates/.agents/skills/block-task/config/verify.json +1 -2
  232. package/templates/.agents/skills/cancel-task/SKILL.en.md +16 -45
  233. package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +16 -45
  234. package/templates/.agents/skills/cancel-task/config/verify.json +1 -2
  235. package/templates/.agents/skills/check-task/SKILL.en.md +6 -3
  236. package/templates/.agents/skills/check-task/SKILL.zh-CN.md +6 -3
  237. package/templates/.agents/skills/close-codescan/SKILL.en.md +6 -24
  238. package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +6 -24
  239. package/templates/.agents/skills/close-dependabot/SKILL.en.md +6 -24
  240. package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +6 -24
  241. package/templates/.agents/skills/code-task/SKILL.en.md +22 -35
  242. package/templates/.agents/skills/code-task/SKILL.zh-CN.md +28 -44
  243. package/templates/.agents/skills/code-task/config/verify.en.json +3 -3
  244. package/templates/.agents/skills/code-task/config/verify.zh-CN.json +3 -3
  245. package/templates/.agents/skills/code-task/reference/dual-mode.en.md +13 -9
  246. package/templates/.agents/skills/code-task/reference/dual-mode.zh-CN.md +13 -9
  247. package/templates/.agents/skills/code-task/reference/fix-mode.en.md +1 -1
  248. package/templates/.agents/skills/code-task/reference/fix-mode.zh-CN.md +1 -1
  249. package/templates/.agents/skills/code-task/reference/report-template.en.md +11 -1
  250. package/templates/.agents/skills/code-task/reference/report-template.zh-CN.md +11 -1
  251. package/templates/.agents/skills/commit/SKILL.en.md +13 -11
  252. package/templates/.agents/skills/commit/SKILL.zh-CN.md +13 -11
  253. package/templates/.agents/skills/commit/config/verify.json +1 -2
  254. package/templates/.agents/skills/commit/reference/commit-message.en.md +1 -11
  255. package/templates/.agents/skills/commit/reference/commit-message.zh-CN.md +1 -11
  256. package/templates/.agents/skills/commit/reference/issue-metadata-sync.en.md +4 -8
  257. package/templates/.agents/skills/commit/reference/issue-metadata-sync.zh-CN.md +4 -8
  258. package/templates/.agents/skills/commit/reference/pr-summary-sync.en.md +6 -18
  259. package/templates/.agents/skills/commit/reference/pr-summary-sync.zh-CN.md +5 -17
  260. package/templates/.agents/skills/commit/reference/task-status-update.en.md +13 -0
  261. package/templates/.agents/skills/commit/reference/task-status-update.zh-CN.md +13 -0
  262. package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +13 -36
  263. package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +13 -36
  264. package/templates/.agents/skills/complete-manual-validation/config/verify.en.json +1 -3
  265. package/templates/.agents/skills/complete-manual-validation/config/verify.zh-CN.json +1 -3
  266. package/templates/.agents/skills/complete-manual-validation/reference/summary-update.en.md +10 -54
  267. package/templates/.agents/skills/complete-manual-validation/reference/summary-update.zh-CN.md +10 -79
  268. package/templates/.agents/skills/complete-task/SKILL.en.md +59 -63
  269. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +59 -63
  270. package/templates/.agents/skills/complete-task/config/verify.en.json +11 -7
  271. package/templates/.agents/skills/complete-task/config/verify.zh-CN.json +11 -7
  272. package/templates/.agents/skills/create-pr/SKILL.en.md +19 -44
  273. package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +19 -43
  274. package/templates/.agents/skills/create-pr/config/verify.json +1 -2
  275. package/templates/.agents/skills/create-pr/reference/comment-publish.en.md +8 -12
  276. package/templates/.agents/skills/create-pr/reference/comment-publish.zh-CN.md +7 -11
  277. package/templates/.agents/skills/create-task/SKILL.en.md +12 -13
  278. package/templates/.agents/skills/create-task/SKILL.zh-CN.md +12 -13
  279. package/templates/.agents/skills/create-task/config/verify.json +1 -2
  280. package/templates/.agents/skills/import-codescan/SKILL.en.md +2 -2
  281. package/templates/.agents/skills/import-codescan/SKILL.zh-CN.md +2 -2
  282. package/templates/.agents/skills/import-codescan/config/verify.json +1 -2
  283. package/templates/.agents/skills/import-dependabot/SKILL.en.md +2 -2
  284. package/templates/.agents/skills/import-dependabot/SKILL.zh-CN.md +2 -2
  285. package/templates/.agents/skills/import-dependabot/config/verify.json +1 -2
  286. package/templates/.agents/skills/import-issue/SKILL.en.md +9 -13
  287. package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +10 -14
  288. package/templates/.agents/skills/import-issue/config/verify.json +1 -2
  289. package/templates/.agents/skills/plan-task/SKILL.en.md +17 -42
  290. package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +22 -49
  291. package/templates/.agents/skills/plan-task/config/verify.en.json +1 -3
  292. package/templates/.agents/skills/plan-task/config/verify.zh-CN.json +1 -3
  293. package/templates/.agents/skills/post-release/SKILL.en.md +11 -81
  294. package/templates/.agents/skills/post-release/SKILL.zh-CN.md +8 -78
  295. package/templates/.agents/skills/refine-title/SKILL.en.md +1 -1
  296. package/templates/.agents/skills/refine-title/SKILL.zh-CN.md +1 -1
  297. package/templates/.agents/skills/release/SKILL.en.md +16 -163
  298. package/templates/.agents/skills/release/SKILL.zh-CN.md +15 -162
  299. package/templates/.agents/skills/restore-task/SKILL.en.md +11 -29
  300. package/templates/.agents/skills/restore-task/SKILL.zh-CN.md +11 -29
  301. package/templates/.agents/skills/restore-task/config/verify.json +1 -2
  302. package/templates/.agents/skills/review-analysis/SKILL.en.md +13 -25
  303. package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +13 -33
  304. package/templates/.agents/skills/review-analysis/config/verify.en.json +3 -4
  305. package/templates/.agents/skills/review-analysis/config/verify.zh-CN.json +3 -4
  306. package/templates/.agents/skills/review-analysis/reference/output-templates.en.md +9 -12
  307. package/templates/.agents/skills/review-analysis/reference/output-templates.zh-CN.md +9 -12
  308. package/templates/.agents/skills/review-analysis/reference/report-template.en.md +9 -2
  309. package/templates/.agents/skills/review-analysis/reference/report-template.zh-CN.md +9 -2
  310. package/templates/.agents/skills/review-analysis/reference/review-criteria.en.md +7 -0
  311. package/templates/.agents/skills/review-analysis/reference/review-criteria.zh-CN.md +7 -0
  312. package/templates/.agents/skills/review-code/SKILL.en.md +22 -36
  313. package/templates/.agents/skills/review-code/SKILL.zh-CN.md +23 -41
  314. package/templates/.agents/skills/review-code/config/verify.en.json +3 -4
  315. package/templates/.agents/skills/review-code/config/verify.zh-CN.json +3 -4
  316. package/templates/.agents/skills/review-code/reference/output-templates.en.md +56 -18
  317. package/templates/.agents/skills/review-code/reference/output-templates.zh-CN.md +56 -18
  318. package/templates/.agents/skills/review-code/reference/report-template.en.md +11 -4
  319. package/templates/.agents/skills/review-code/reference/report-template.zh-CN.md +11 -4
  320. package/templates/.agents/skills/review-code/reference/review-criteria.en.md +7 -0
  321. package/templates/.agents/skills/review-code/reference/review-criteria.zh-CN.md +7 -0
  322. package/templates/.agents/skills/review-plan/SKILL.en.md +13 -25
  323. package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +13 -33
  324. package/templates/.agents/skills/review-plan/config/verify.en.json +3 -4
  325. package/templates/.agents/skills/review-plan/config/verify.zh-CN.json +3 -4
  326. package/templates/.agents/skills/review-plan/reference/output-templates.en.md +9 -12
  327. package/templates/.agents/skills/review-plan/reference/output-templates.zh-CN.md +9 -12
  328. package/templates/.agents/skills/review-plan/reference/report-template.en.md +9 -2
  329. package/templates/.agents/skills/review-plan/reference/report-template.zh-CN.md +9 -2
  330. package/templates/.agents/skills/review-plan/reference/review-criteria.en.md +7 -0
  331. package/templates/.agents/skills/review-plan/reference/review-criteria.zh-CN.md +7 -0
  332. package/templates/.agents/skills/update-agent-infra/SKILL.en.md +1 -2
  333. package/templates/.agents/skills/update-agent-infra/SKILL.zh-CN.md +1 -2
  334. package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +3 -15
  335. package/templates/.agents/skills/watch-pr/SKILL.en.md +24 -14
  336. package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +25 -15
  337. package/templates/.agents/skills/watch-pr/config/verify.json +1 -2
  338. package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.en.md +2 -2
  339. package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.zh-CN.md +2 -2
  340. package/templates/.agents/templates/task.en.md +6 -1
  341. package/templates/.agents/templates/task.zh-CN.md +6 -1
  342. package/templates/.claude/commands/analyze-task.en.md +1 -1
  343. package/templates/.claude/commands/analyze-task.zh-CN.md +1 -1
  344. package/templates/.claude/commands/block-task.en.md +1 -1
  345. package/templates/.claude/commands/block-task.zh-CN.md +1 -1
  346. package/templates/.claude/commands/cancel-task.en.md +1 -1
  347. package/templates/.claude/commands/cancel-task.zh-CN.md +1 -1
  348. package/templates/.claude/commands/check-task.en.md +1 -1
  349. package/templates/.claude/commands/check-task.zh-CN.md +1 -1
  350. package/templates/.claude/commands/code-task.en.md +1 -1
  351. package/templates/.claude/commands/code-task.zh-CN.md +1 -1
  352. package/templates/.claude/commands/commit.en.md +1 -0
  353. package/templates/.claude/commands/commit.zh-CN.md +1 -0
  354. package/templates/.claude/commands/complete-manual-validation.en.md +1 -1
  355. package/templates/.claude/commands/complete-manual-validation.zh-CN.md +1 -1
  356. package/templates/.claude/commands/complete-task.en.md +1 -1
  357. package/templates/.claude/commands/complete-task.zh-CN.md +1 -1
  358. package/templates/.claude/commands/create-pr.en.md +1 -1
  359. package/templates/.claude/commands/create-pr.zh-CN.md +1 -1
  360. package/templates/.claude/commands/plan-task.en.md +1 -1
  361. package/templates/.claude/commands/plan-task.zh-CN.md +1 -1
  362. package/templates/.claude/commands/review-analysis.en.md +1 -1
  363. package/templates/.claude/commands/review-analysis.zh-CN.md +1 -1
  364. package/templates/.claude/commands/review-code.en.md +1 -1
  365. package/templates/.claude/commands/review-code.zh-CN.md +1 -1
  366. package/templates/.claude/commands/review-plan.en.md +1 -1
  367. package/templates/.claude/commands/review-plan.zh-CN.md +1 -1
  368. package/templates/.claude/commands/watch-pr.en.md +1 -1
  369. package/templates/.claude/commands/watch-pr.zh-CN.md +1 -1
  370. package/templates/.gemini/commands/_project_/analyze-task.en.toml +2 -0
  371. package/templates/.gemini/commands/_project_/analyze-task.zh-CN.toml +2 -0
  372. package/templates/.gemini/commands/_project_/block-task.en.toml +2 -0
  373. package/templates/.gemini/commands/_project_/block-task.zh-CN.toml +2 -0
  374. package/templates/.gemini/commands/_project_/cancel-task.en.toml +2 -0
  375. package/templates/.gemini/commands/_project_/cancel-task.zh-CN.toml +2 -0
  376. package/templates/.gemini/commands/_project_/check-task.en.toml +2 -0
  377. package/templates/.gemini/commands/_project_/check-task.zh-CN.toml +2 -0
  378. package/templates/.gemini/commands/_project_/code-task.en.toml +2 -0
  379. package/templates/.gemini/commands/_project_/code-task.zh-CN.toml +2 -0
  380. package/templates/.gemini/commands/_project_/commit.en.toml +4 -0
  381. package/templates/.gemini/commands/_project_/commit.zh-CN.toml +4 -0
  382. package/templates/.gemini/commands/_project_/complete-manual-validation.en.toml +2 -0
  383. package/templates/.gemini/commands/_project_/complete-manual-validation.zh-CN.toml +2 -0
  384. package/templates/.gemini/commands/_project_/complete-task.en.toml +2 -0
  385. package/templates/.gemini/commands/_project_/complete-task.zh-CN.toml +2 -0
  386. package/templates/.gemini/commands/_project_/create-pr.en.toml +2 -0
  387. package/templates/.gemini/commands/_project_/create-pr.zh-CN.toml +2 -0
  388. package/templates/.gemini/commands/_project_/plan-task.en.toml +2 -0
  389. package/templates/.gemini/commands/_project_/plan-task.zh-CN.toml +2 -0
  390. package/templates/.gemini/commands/_project_/review-analysis.en.toml +2 -0
  391. package/templates/.gemini/commands/_project_/review-analysis.zh-CN.toml +2 -0
  392. package/templates/.gemini/commands/_project_/review-code.en.toml +2 -0
  393. package/templates/.gemini/commands/_project_/review-code.zh-CN.toml +2 -0
  394. package/templates/.gemini/commands/_project_/review-plan.en.toml +2 -0
  395. package/templates/.gemini/commands/_project_/review-plan.zh-CN.toml +2 -0
  396. package/templates/.gemini/commands/_project_/watch-pr.en.toml +2 -0
  397. package/templates/.gemini/commands/_project_/watch-pr.zh-CN.toml +2 -0
  398. package/templates/.git-hooks/check-large-files.cjs +92 -0
  399. package/templates/.git-hooks/check-version-format.sh +5 -1
  400. package/templates/.git-hooks/pre-commit +1 -0
  401. package/templates/.opencode/commands/analyze-task.en.md +1 -1
  402. package/templates/.opencode/commands/analyze-task.zh-CN.md +1 -1
  403. package/templates/.opencode/commands/check-task.en.md +1 -1
  404. package/templates/.opencode/commands/check-task.zh-CN.md +1 -1
  405. package/templates/.opencode/commands/code-task.en.md +1 -1
  406. package/templates/.opencode/commands/code-task.zh-CN.md +1 -1
  407. package/templates/.opencode/commands/commit.en.md +2 -0
  408. package/templates/.opencode/commands/commit.zh-CN.md +2 -0
  409. package/templates/.opencode/commands/complete-task.en.md +1 -1
  410. package/templates/.opencode/commands/complete-task.zh-CN.md +1 -1
  411. package/templates/.opencode/commands/plan-task.en.md +1 -1
  412. package/templates/.opencode/commands/plan-task.zh-CN.md +1 -1
  413. package/templates/.opencode/commands/review-analysis.en.md +1 -1
  414. package/templates/.opencode/commands/review-analysis.zh-CN.md +1 -1
  415. package/templates/.opencode/commands/review-code.en.md +1 -1
  416. package/templates/.opencode/commands/review-code.zh-CN.md +1 -1
  417. package/templates/.opencode/commands/review-plan.en.md +1 -1
  418. package/templates/.opencode/commands/review-plan.zh-CN.md +1 -1
  419. package/templates/.agents/scripts/platform-adapters/platform-sync.github.js +0 -40
  420. package/templates/.agents/scripts/platform-adapters/platform-sync.js +0 -13
  421. package/templates/.agents/scripts/review-diff-fingerprint.js +0 -170
  422. package/templates/.agents/scripts/workflow-warnings.js +0 -290
  423. package/templates/.agents/skills/code-task/scripts/detect-mode.js +0 -257
  424. package/templates/.agents/skills/release/scripts/manage-milestones.github.sh +0 -80
  425. package/templates/.agents/skills/release/scripts/manage-milestones.sh +0 -6
package/lib/decide.ts CHANGED
@@ -1,10 +1,7 @@
1
- import fs from 'node:fs';
2
- import path from 'node:path';
3
- import { execFileSync } from 'node:child_process';
4
1
  import { VERSION } from './version.ts';
5
- import { resolveTaskRef } from './task/resolve-ref.ts';
6
-
7
- const TASK_ID_RE = /^TASK-\d{8}-\d{6}$/;
2
+ import { parseTaskScope } from './task/command-options.ts';
3
+ import { applyHumanDecision } from './task/decision-intents.ts';
4
+ import { canonicalTimestamp } from './task/write.ts';
8
5
 
9
6
  type DecideOptions = {
10
7
  repoRoot?: string;
@@ -12,106 +9,69 @@ type DecideOptions = {
12
9
  version?: string;
13
10
  };
14
11
 
15
- function detectRepoRoot(): string {
16
- return execFileSync('git', ['rev-parse', '--show-toplevel'], {
17
- encoding: 'utf8',
18
- stdio: ['pipe', 'pipe', 'pipe']
19
- }).trim();
20
- }
21
-
22
- function defaultNow(): string {
23
- return new Intl.DateTimeFormat('sv-SE', {
24
- timeZoneName: 'longOffset',
25
- year: 'numeric',
26
- month: '2-digit',
27
- day: '2-digit',
28
- hour: '2-digit',
29
- minute: '2-digit',
30
- second: '2-digit',
31
- hour12: false
32
- })
33
- .format(new Date())
34
- .replace(' GMT', '');
35
- }
36
-
37
- function taskPath(repoRoot: string, ref: string): string {
38
- if (!TASK_ID_RE.test(ref)) {
39
- const resolved = resolveTaskRef(ref);
40
- if (!resolved.ok) throw new Error(resolved.message);
41
- if (!resolved.taskDir.includes(`${path.join('.agents', 'workspace', 'active')}${path.sep}`)) {
42
- throw new Error(`task ${resolved.taskId} is not active`);
12
+ function parseDecisionParts(parts: string[]): { decision: string; needsImplementation: boolean | undefined } {
13
+ const decision: string[] = [];
14
+ let needsImplementation: boolean | undefined;
15
+ for (let index = 0; index < parts.length; index += 1) {
16
+ if (parts[index] !== '--needs-implementation') {
17
+ decision.push(parts[index]!);
18
+ continue;
19
+ }
20
+ if (needsImplementation !== undefined) throw new Error("duplicate option '--needs-implementation'");
21
+ const value = parts[++index];
22
+ if (value !== 'true' && value !== 'false') {
23
+ throw new Error("--needs-implementation must be 'true' or 'false'");
43
24
  }
44
- return resolved.taskMdPath;
25
+ needsImplementation = value === 'true';
45
26
  }
46
- const candidate = path.join(repoRoot, '.agents', 'workspace', 'active', ref, 'task.md');
47
- if (!fs.existsSync(candidate)) throw new Error(`active task not found: ${ref}`);
48
- return candidate;
49
- }
50
-
51
- function replaceFrontmatterField(content: string, field: string, value: string): string {
52
- const re = new RegExp(`^${field}:.*$`, 'm');
53
- if (re.test(content)) return content.replace(re, `${field}: ${value}`);
54
- return content.replace(/^---\n/, `---\n${field}: ${value}\n`);
27
+ if (decision.length === 0) throw new Error('decision content is required');
28
+ return { decision: decision.join(' '), needsImplementation };
55
29
  }
56
30
 
57
- function replaceLedgerRow(content: string, hdId: string): { content: string; found: boolean; pending: boolean } {
58
- const lines = content.split('\n');
59
- let found = false;
60
- let pending = false;
61
- for (let i = 0; i < lines.length; i += 1) {
62
- const line = lines[i] as string;
63
- if (!line.trim().startsWith(`| ${hdId} |`)) continue;
64
- found = true;
65
- const cells = line.split('|').slice(1, -1).map((cell) => cell.trim());
66
- if (cells[4] !== 'needs-human-decision') break;
67
- pending = true;
68
- cells[4] = 'human-decided';
69
- cells[5] = `task.md#${hdId}`;
70
- lines[i] = `| ${cells.join(' | ')} |`;
71
- break;
31
+ export async function decide(args: string[], options: DecideOptions = {}): Promise<number> {
32
+ let scope;
33
+ try { scope = parseTaskScope(args); } catch (error) {
34
+ process.stderr.write(`Error: ${error instanceof Error ? error.message : String(error)}\n`); return 1;
72
35
  }
73
- return { content: lines.join('\n'), found, pending };
74
- }
75
-
76
- function appendUnderHeading(content: string, heading: string, block: string): string {
77
- if (!content.includes(`${heading}\n`)) {
78
- return `${content.trimEnd()}\n\n${heading}\n\n${block}\n`;
36
+ let item: string | undefined;
37
+ const operands: string[] = [];
38
+ for (let index = 0; index < scope.positionals.length; index += 1) {
39
+ const arg = scope.positionals[index]!;
40
+ if (arg === '--item' || arg === '-i') {
41
+ if (item !== undefined) { process.stderr.write("Error: duplicate option '--item'\n"); return 1; }
42
+ item = scope.positionals[++index];
43
+ if (!item) { process.stderr.write(`Error: ${arg} requires a value\n`); return 1; }
44
+ } else if (arg.startsWith('--item=')) {
45
+ if (item !== undefined) { process.stderr.write("Error: duplicate option '--item'\n"); return 1; }
46
+ item = arg.slice('--item='.length);
47
+ if (item === '') { process.stderr.write('Error: --item requires a value\n'); return 1; }
48
+ } else operands.push(arg);
79
49
  }
80
- const idx = content.indexOf(`${heading}\n`) + heading.length + 1;
81
- const before = content.slice(0, idx);
82
- const after = content.slice(idx);
83
- return `${before}\n${block}\n${after.replace(/^\n/, '')}`;
84
- }
85
-
86
- export async function decide(args: string[], options: DecideOptions = {}): Promise<number> {
87
- const [taskRef, hdId, ...decisionParts] = args;
88
- if (!taskRef || !hdId || decisionParts.length === 0) {
89
- process.stderr.write('Usage: ai decide <task-ref> <HD-id> <decision>\n');
50
+ let taskRef = scope.taskRef;
51
+ let selector = item;
52
+ let decisionParts: string[];
53
+ if (item !== undefined) {
54
+ decisionParts = operands;
55
+ } else if (!scope.explicit) {
56
+ [taskRef, selector, ...decisionParts] = operands;
57
+ } else {
58
+ decisionParts = [];
59
+ }
60
+ if (!selector || decisionParts.length === 0) {
61
+ process.stderr.write('Usage: ai decide [--task <ref>] --item <ordinal|ledger-id> [--needs-implementation true|false] <decision>\n ai decide <task-ref> <ordinal|ledger-id> [--needs-implementation true|false] <decision>\n');
90
62
  return 1;
91
63
  }
92
64
  try {
93
- const repoRoot = options.repoRoot ?? detectRepoRoot();
94
- const file = taskPath(repoRoot, taskRef);
95
- let content = fs.readFileSync(file, 'utf8');
96
- const replaced = replaceLedgerRow(content, hdId);
97
- if (!replaced.found) throw new Error(`${hdId} not found in review ledger`);
98
- if (!replaced.pending) throw new Error(`${hdId} is not needs-human-decision`);
99
- content = replaced.content;
100
- const now = (options.now ?? defaultNow)();
101
- content = replaceFrontmatterField(content, 'updated_at', now);
102
- content = replaceFrontmatterField(content, 'agent_infra_version', options.version ?? VERSION);
103
- const decision = decisionParts.join(' ');
104
- content = appendUnderHeading(
105
- content,
106
- '## 人工裁决',
107
- `### ${hdId}\n\n- **裁决时间**:${now}\n- **裁决结果**:${decision}`
108
- );
109
- content = appendUnderHeading(
110
- content,
111
- '## 活动日志',
112
- `- ${now} — **Human Decision** by human — ${hdId} decided`
113
- );
114
- fs.writeFileSync(file, content);
65
+ const parsedDecision = parseDecisionParts(decisionParts);
66
+ const now = (options.now ?? canonicalTimestamp)();
67
+ const result = applyHumanDecision({
68
+ taskRef, selector, decision: parsedDecision.decision,
69
+ needsImplementation: parsedDecision.needsImplementation
70
+ }, {
71
+ repoRoot: options.repoRoot,
72
+ metadataProvider: () => ({ timestamp: now, agentInfraVersion: options.version ?? VERSION })
73
+ });
74
+ if (result.error) throw new Error(result.error.message);
115
75
  return 0;
116
76
  } catch (error) {
117
77
  process.stderr.write(`Error: ${error instanceof Error ? error.message : String(error)}\n`);
package/lib/defaults.json CHANGED
@@ -42,6 +42,7 @@
42
42
  ".claude/commands/",
43
43
  ".codex/hooks.json",
44
44
  ".gemini/commands/",
45
+ ".git-hooks/check-large-files.cjs",
45
46
  ".git-hooks/check-version-format.sh",
46
47
  ".github/scripts/",
47
48
  ".github/workflows/metadata-sync.yml",
@@ -0,0 +1,73 @@
1
+ import crypto from 'node:crypto';
2
+ import fs from 'node:fs';
3
+ import os from 'node:os';
4
+ import path from 'node:path';
5
+ import { execFileSync } from 'node:child_process';
6
+
7
+ function git(repoRoot: string, args: string[], options: { env?: NodeJS.ProcessEnv; encoding?: BufferEncoding | 'buffer' } = {}) {
8
+ return execFileSync('git', args, { cwd: repoRoot, encoding: options.encoding ?? 'utf8', env: options.env ?? process.env });
9
+ }
10
+
11
+ function splitNull(output: string): string[] { return output.split('\0').filter(Boolean); }
12
+ function exists(filePath: string): boolean { try { fs.lstatSync(filePath); return true; } catch { return false; } }
13
+
14
+ function withTemporaryIndex<T>(repoRoot: string, baseline: string, callback: (env: NodeJS.ProcessEnv) => T): T {
15
+ const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'agent-infra-review-index-'));
16
+ const env = { ...process.env, GIT_INDEX_FILE: path.join(tempDir, 'index') };
17
+ try {
18
+ execFileSync('git', ['read-tree', baseline], { cwd: repoRoot, env });
19
+ return callback(env);
20
+ } finally {
21
+ fs.rmSync(tempDir, { recursive: true, force: true });
22
+ }
23
+ }
24
+
25
+ function projectWorktree(repoRoot: string, baseline: string, globs: string[], env: NodeJS.ProcessEnv) {
26
+ const tracked = splitNull(String(git(repoRoot, ['diff', '--name-only', '-z', baseline, '--', ...globs])));
27
+ const untracked = splitNull(String(git(repoRoot, ['ls-files', '-o', '--exclude-standard', '-z', '--', ...globs])));
28
+ for (const filePath of new Set([...tracked, ...untracked])) {
29
+ const args = exists(path.join(repoRoot, filePath)) ? ['update-index', '--add', '--', filePath] : ['update-index', '--remove', '--', filePath];
30
+ execFileSync('git', args, { cwd: repoRoot, env });
31
+ }
32
+ }
33
+
34
+ function projectStaged(repoRoot: string, baseline: string, globs: string[], env: NodeJS.ProcessEnv) {
35
+ for (const filePath of splitNull(String(git(repoRoot, ['diff', '--cached', '--name-only', '-z', baseline, '--', ...globs])))) {
36
+ const entry = execFileSync('git', ['--literal-pathspecs', 'ls-files', '--stage', '-z', '--', filePath], { cwd: repoRoot, encoding: 'buffer' });
37
+ execFileSync('git', ['update-index', '-z', '--index-info'], {
38
+ cwd: repoRoot, env, input: entry.length > 0 ? entry : Buffer.from(`0 ${'0'.repeat(40)}\t${filePath}\0`)
39
+ });
40
+ }
41
+ }
42
+
43
+ function snapshotReview(input: { cwd: string; mode: 'worktree' | 'staged'; baseline: string; globs: string[] }) {
44
+ const repoRoot = String(git(input.cwd, ['rev-parse', '--show-toplevel'])).trim();
45
+ const resolvedBaseline = String(git(repoRoot, ['rev-parse', '--verify', `${input.baseline}^{commit}`])).trim();
46
+ return withTemporaryIndex(repoRoot, resolvedBaseline, (env) => {
47
+ if (input.mode === 'worktree') projectWorktree(repoRoot, resolvedBaseline, input.globs, env);
48
+ else projectStaged(repoRoot, resolvedBaseline, input.globs, env);
49
+ const diff = execFileSync('git', ['diff', '--cached', '--binary', resolvedBaseline, '--', ...input.globs], { cwd: repoRoot, encoding: 'buffer', env });
50
+ return {
51
+ baseline: resolvedBaseline,
52
+ fingerprint: `sha256:${crypto.createHash('sha256').update(diff).digest('hex')}`,
53
+ tree: String(git(repoRoot, ['write-tree'], { env })).trim()
54
+ };
55
+ });
56
+ }
57
+
58
+ function compareReviewTrees(input: { cwd: string; expected: string; actual: string }) {
59
+ const repoRoot = String(git(input.cwd, ['rev-parse', '--show-toplevel'])).trim();
60
+ git(repoRoot, ['rev-parse', '--verify', `${input.expected}^{tree}`]);
61
+ git(repoRoot, ['rev-parse', '--verify', `${input.actual}^{tree}`]);
62
+ const fields = splitNull(String(git(repoRoot, ['diff', '--name-status', '-z', '--no-renames', input.expected, input.actual])));
63
+ const result = { equal: fields.length === 0, added: [] as string[], missing: [] as string[], different: [] as string[] };
64
+ for (let index = 0; index < fields.length; index += 2) {
65
+ const status = fields[index]; const filePath = fields[index + 1]!;
66
+ if (status === 'A') result.added.push(filePath);
67
+ else if (status === 'D') result.missing.push(filePath);
68
+ else result.different.push(filePath);
69
+ }
70
+ return result;
71
+ }
72
+
73
+ export { compareReviewTrees, snapshotReview };
@@ -0,0 +1,101 @@
1
+ import { spawnSync } from 'node:child_process';
2
+
3
+ type CommandResult = { status: number | null; stdout: string; stderr: string };
4
+ type GitRunner = (args: readonly string[], cwd: string) => CommandResult;
5
+ type GitOperation = { name: string; status: 'applied' | 'no-op' | 'failed'; ref?: string; message?: string };
6
+
7
+ const defaultRunner: GitRunner = (args, cwd) => {
8
+ const result = spawnSync('git', [...args], { cwd, encoding: 'utf8' });
9
+ return { status: result.status, stdout: result.stdout ?? '', stderr: result.stderr ?? '' };
10
+ };
11
+
12
+ function run(runner: GitRunner, cwd: string, args: readonly string[]): CommandResult {
13
+ return runner(args, cwd);
14
+ }
15
+
16
+ function value(runner: GitRunner, cwd: string, args: readonly string[], trim = true): string | null {
17
+ const result = run(runner, cwd, args);
18
+ return result.status === 0 ? (trim ? result.stdout.trim() : result.stdout.replace(/\n$/, '')) : null;
19
+ }
20
+
21
+ function inspectGitWorkflow(cwd: string, runner: GitRunner = defaultRunner, remoteInput?: { remote: string; refs: readonly string[] }) {
22
+ const head = value(runner, cwd, ['rev-parse', 'HEAD']);
23
+ const branch = value(runner, cwd, ['branch', '--show-current']);
24
+ const upstream = value(runner, cwd, ['rev-parse', '--abbrev-ref', '--symbolic-full-name', '@{upstream}']);
25
+ const porcelain = value(runner, cwd, ['status', '--porcelain=v1'], false);
26
+ if (!head || branch === null || porcelain === null) {
27
+ return { status: 'failed' as const, changed: false, snapshot: null, operations: [], error: { code: 'GIT_INSPECT_FAILED', message: 'Unable to inspect Git repository' } };
28
+ }
29
+ const lines = porcelain ? porcelain.split('\n') : [];
30
+ const remoteRefs = remoteInput ? Object.fromEntries(remoteInput.refs.map((ref) => {
31
+ const canonical = ref.startsWith('refs/') ? ref : `refs/heads/${ref}`;
32
+ const remote = run(runner, cwd, ['ls-remote', '--exit-code', remoteInput.remote, canonical]);
33
+ return [ref, remote.status === 0 ? remote.stdout.trim().split(/\s+/)[0] ?? null : null];
34
+ })) : {};
35
+ return {
36
+ status: 'no-op' as const, changed: false,
37
+ snapshot: {
38
+ head, branch, upstream,
39
+ worktree: lines.filter((line) => line.startsWith('??') || line[1] !== ' ').map((line) => line.slice(3)),
40
+ staged: lines.filter((line) => !line.startsWith('??') && line[0] !== ' ').map((line) => line.slice(3)),
41
+ remoteRefs
42
+ },
43
+ operations: [], error: null
44
+ };
45
+ }
46
+
47
+ function validatePaths(paths: readonly string[]): string | null {
48
+ if (paths.length === 0) return 'At least one explicit path is required';
49
+ for (const candidate of paths) {
50
+ if (!candidate || candidate.startsWith('/') || candidate.split(/[\\/]/).includes('..')) return `Path escapes repository: ${candidate}`;
51
+ if (/(^|\/)(?:\.env|credentials\.json|id_rsa)(?:$|\/)/i.test(candidate)) return `Sensitive path is not allowed: ${candidate}`;
52
+ }
53
+ return null;
54
+ }
55
+
56
+ function commitExplicitPaths(input: { cwd: string; paths: readonly string[]; message: string; expectedHead?: string; expectedTree?: string }, runner: GitRunner = defaultRunner) {
57
+ const invalid = validatePaths(input.paths);
58
+ if (invalid || !input.message.trim()) return { status: 'failed' as const, changed: false, snapshot: inspectGitWorkflow(input.cwd, runner).snapshot, operations: [], error: { code: 'GIT_COMMIT_INPUT_INVALID', message: invalid ?? 'Commit message is required' } };
59
+ const before = inspectGitWorkflow(input.cwd, runner);
60
+ if (!before.snapshot) return before;
61
+ if (input.expectedHead && before.snapshot.head !== input.expectedHead) return { status: 'failed' as const, changed: false, snapshot: before.snapshot, operations: [], error: { code: 'GIT_HEAD_MISMATCH', message: `Expected HEAD ${input.expectedHead}, received ${before.snapshot.head}` } };
62
+ const selected = new Set(input.paths);
63
+ const unrelatedStaged = before.snapshot.staged.filter((candidate) => !selected.has(candidate));
64
+ if (unrelatedStaged.length > 0) return { status: 'failed' as const, changed: false, snapshot: before.snapshot, operations: [], error: { code: 'GIT_STAGED_SCOPE_MISMATCH', message: `Unrelated staged paths: ${unrelatedStaged.join(', ')}` } };
65
+ const add = run(runner, input.cwd, ['add', '--', ...input.paths]);
66
+ if (add.status !== 0) return { status: 'failed' as const, changed: false, snapshot: inspectGitWorkflow(input.cwd, runner).snapshot, operations: [{ name: 'stage', status: 'failed' as const, message: add.stderr.trim() }], error: { code: 'GIT_STAGE_FAILED', message: add.stderr.trim() } };
67
+ const staged = run(runner, input.cwd, ['diff', '--cached', '--quiet', '--']);
68
+ if (staged.status === 0) return { status: 'no-op' as const, changed: false, snapshot: inspectGitWorkflow(input.cwd, runner).snapshot, operations: [{ name: 'stage', status: 'no-op' as const }], error: null };
69
+ if (input.expectedTree) {
70
+ const tree = value(runner, input.cwd, ['write-tree']);
71
+ if (tree !== input.expectedTree) return { status: 'failed' as const, changed: false, snapshot: inspectGitWorkflow(input.cwd, runner).snapshot, operations: [{ name: 'stage', status: 'applied' as const }], error: { code: 'GIT_TREE_MISMATCH', message: `Expected staged tree ${input.expectedTree}, received ${tree ?? 'unavailable'}` } };
72
+ }
73
+ const commit = run(runner, input.cwd, ['commit', '-m', input.message]);
74
+ const after = inspectGitWorkflow(input.cwd, runner);
75
+ if (commit.status !== 0) return { status: 'failed' as const, changed: false, snapshot: after.snapshot, operations: [{ name: 'commit', status: 'failed' as const, message: commit.stderr.trim() }], error: { code: 'GIT_COMMIT_FAILED', message: commit.stderr.trim() } };
76
+ return { status: 'applied' as const, changed: true, snapshot: after.snapshot, operations: [{ name: 'stage', status: 'applied' as const }, { name: 'commit', status: 'applied' as const }], error: null };
77
+ }
78
+
79
+ function pushGitRefs(input: { cwd: string; remote: string; refs: readonly string[] }, runner: GitRunner = defaultRunner) {
80
+ if (!input.remote || input.refs.length === 0) return { status: 'failed' as const, changed: false, snapshot: inspectGitWorkflow(input.cwd, runner).snapshot, operations: [], error: { code: 'GIT_PUSH_INPUT_INVALID', message: 'Remote and refs are required' } };
81
+ const operations: GitOperation[] = [];
82
+ for (const ref of input.refs) {
83
+ if (!/^(?:refs\/(?:heads|tags)\/)?[A-Za-z0-9._/-]+$/.test(ref) || ref.startsWith('-')) {
84
+ operations.push({ name: 'push', ref, status: 'failed', message: 'Invalid ref' });
85
+ continue;
86
+ }
87
+ const pushed = run(runner, input.cwd, ['push', input.remote, ref]);
88
+ if (pushed.status !== 0) {
89
+ operations.push({ name: 'push', ref, status: 'failed', message: pushed.stderr.trim() });
90
+ continue;
91
+ }
92
+ const canonical = ref.startsWith('refs/') ? ref : `refs/heads/${ref}`;
93
+ const remoteFact = run(runner, input.cwd, ['ls-remote', '--exit-code', input.remote, canonical]);
94
+ operations.push({ name: 'push', ref, status: remoteFact.status === 0 ? 'applied' : 'failed', message: remoteFact.status === 0 ? undefined : 'Remote ref verification failed' });
95
+ }
96
+ const failures = operations.filter((item) => item.status === 'failed').length;
97
+ return { status: failures === 0 ? 'applied' as const : failures === operations.length ? 'failed' as const : 'degraded' as const, changed: failures < operations.length, snapshot: inspectGitWorkflow(input.cwd, runner).snapshot, operations, error: failures ? { code: 'GIT_PUSH_PARTIAL', message: `${failures} ref push operation(s) failed` } : null };
98
+ }
99
+
100
+ export { commitExplicitPaths, inspectGitWorkflow, pushGitRefs };
101
+ export type { CommandResult, GitRunner };
@@ -0,0 +1,37 @@
1
+ import fs from 'node:fs';
2
+ import { commitExplicitPaths, inspectGitWorkflow, pushGitRefs } from '../git/workflow.ts';
3
+ import { compareReviewTrees, snapshotReview } from '../git/review-snapshot.ts';
4
+ import { resolvePostReviewGlobs } from '../task/review-fingerprint.ts';
5
+
6
+ function output(value: unknown): void { process.stdout.write(`${JSON.stringify(value)}\n`); }
7
+
8
+ function gitWorkflow(args: string[] = []): void {
9
+ const [action, ...rest] = args;
10
+ const cwdIndex = rest.indexOf('--cwd');
11
+ const cwd = cwdIndex >= 0 ? rest[cwdIndex + 1] : process.cwd();
12
+ let result;
13
+ if (action === 'inspect') {
14
+ const inputIndex = rest.indexOf('--input');
15
+ const input = inputIndex >= 0 && rest[inputIndex + 1] ? JSON.parse(fs.readFileSync(rest[inputIndex + 1]!, 'utf8')) as { remote?: string; refs?: string[] } : null;
16
+ result = inspectGitWorkflow(cwd ?? process.cwd(), undefined, input?.remote && input.refs ? { remote: input.remote, refs: input.refs } : undefined);
17
+ }
18
+ else if (action === 'commit') {
19
+ const inputIndex = rest.indexOf('--input');
20
+ const input = inputIndex >= 0 && rest[inputIndex + 1] ? JSON.parse(fs.readFileSync(rest[inputIndex + 1]!, 'utf8')) : null;
21
+ result = input ? commitExplicitPaths({ cwd: cwd ?? process.cwd(), ...input }) : { status: 'failed', changed: false, error: { code: 'GIT_INPUT_REQUIRED', message: '--input JSON file is required' } };
22
+ } else if (action === 'push') {
23
+ const inputIndex = rest.indexOf('--input');
24
+ const input = inputIndex >= 0 && rest[inputIndex + 1] ? JSON.parse(fs.readFileSync(rest[inputIndex + 1]!, 'utf8')) : null;
25
+ result = input ? pushGitRefs({ cwd: cwd ?? process.cwd(), ...input }) : { status: 'failed', changed: false, error: { code: 'GIT_INPUT_REQUIRED', message: '--input JSON file is required' } };
26
+ } else if (action === 'snapshot' || action === 'compare-trees') {
27
+ const inputIndex = rest.indexOf('--input');
28
+ const input = inputIndex >= 0 && rest[inputIndex + 1] ? JSON.parse(fs.readFileSync(rest[inputIndex + 1]!, 'utf8')) : null;
29
+ if (!input) result = { status: 'failed', changed: false, error: { code: 'GIT_INPUT_REQUIRED', message: '--input JSON file is required' } };
30
+ else if (action === 'snapshot') result = { status: 'no-op', changed: false, snapshot: snapshotReview({ cwd: cwd ?? process.cwd(), mode: input.mode, baseline: input.baseline, globs: input.globs ?? resolvePostReviewGlobs({}, {}) }), error: null };
31
+ else result = { status: 'no-op', changed: false, comparison: compareReviewTrees({ cwd: cwd ?? process.cwd(), expected: input.expected, actual: input.actual }), error: null };
32
+ } else result = { status: 'failed', changed: false, error: { code: 'GIT_ACTION_INVALID', message: `Unknown git-workflow action '${action ?? ''}'` } };
33
+ output(result);
34
+ process.exitCode = result.status === 'failed' ? 1 : result.status === 'blocked' ? 2 : 0;
35
+ }
36
+
37
+ export { gitWorkflow };
@@ -0,0 +1,101 @@
1
+ import path from 'node:path';
2
+
3
+ import {
4
+ fetchPlatformCheckLogs,
5
+ inspectRequiredChecks,
6
+ resolvePlatformCheckRun,
7
+ watchPlatformChecks
8
+ } from '../platform/pr-checks.ts';
9
+ import type { ChecksResult } from '../platform/pr-checks.ts';
10
+
11
+ const USAGE = `Usage: agent-infra-internal platform-checks inspect <task-ref> [--cwd <path>]
12
+ agent-infra-internal platform-checks watch <task-ref> --interval-seconds <N> --deadline-seconds <N> [--cwd <path>]
13
+ agent-infra-internal platform-checks resolve-run <task-ref> --check-name <name> [--details-url <url>] [--cwd <path>]
14
+ agent-infra-internal platform-checks logs <task-ref> --run <id> [--job <id>] [--cwd <path>]
15
+ `;
16
+
17
+ const VALUE_FLAGS = new Set(['--cwd', '--interval-seconds', '--deadline-seconds', '--check-name', '--details-url', '--run', '--job']);
18
+
19
+ function key(flag: string): string {
20
+ return flag.slice(2).replace(/-([a-z])/g, (_, letter: string) => letter.toUpperCase());
21
+ }
22
+
23
+ function fail(message: string): void {
24
+ process.stdout.write(`${JSON.stringify({ status: 'failed', changed: false, error: { code: 'CHECKS_PAYLOAD_INVALID', message } })}\n`);
25
+ process.stderr.write(USAGE);
26
+ process.exitCode = 1;
27
+ }
28
+
29
+ function finish(output: ChecksResult): void {
30
+ process.stdout.write(`${JSON.stringify(output)}\n`);
31
+ if (output.status === 'failed') process.exitCode = 1;
32
+ if (output.status === 'blocked') process.exitCode = 2;
33
+ }
34
+
35
+ function parse(args: string[], start: number) {
36
+ const values: Record<string, string> = {};
37
+ const seen = new Set<string>();
38
+ for (let index = start; index < args.length; index += 1) {
39
+ const flag = args[index]!;
40
+ if (!VALUE_FLAGS.has(flag)) return { values, error: `unknown option '${flag}'` };
41
+ if (seen.has(flag)) return { values, error: `duplicate option '${flag}'` };
42
+ seen.add(flag);
43
+ const value = args[++index];
44
+ if (value === undefined || value.startsWith('--')) return { values, error: `option '${flag}' requires a value` };
45
+ values[key(flag)] = value;
46
+ }
47
+ return { values };
48
+ }
49
+
50
+ function positive(value: string | undefined): number | null {
51
+ const number = Number(value);
52
+ return Number.isFinite(number) && number > 0 ? number : null;
53
+ }
54
+
55
+ async function platformChecks(args: string[] = []): Promise<void> {
56
+ if (args[0] === '--help' || args[0] === '-h') { process.stdout.write(USAGE); return; }
57
+ const operation = args[0];
58
+ if (!operation || !['inspect', 'watch', 'resolve-run', 'logs'].includes(operation)) { fail('a valid operation is required'); return; }
59
+ const taskRef = args[1];
60
+ if (!taskRef || taskRef.startsWith('--')) { fail(`${operation} requires a task ref`); return; }
61
+ const parsed = parse(args, 2);
62
+ if (parsed.error) { fail(parsed.error); return; }
63
+ const values = parsed.values;
64
+ const cwd = path.resolve(values.cwd || process.cwd());
65
+ const allowed: Record<string, string[]> = {
66
+ inspect: ['cwd'],
67
+ watch: ['cwd', 'intervalSeconds', 'deadlineSeconds'],
68
+ 'resolve-run': ['cwd', 'checkName', 'detailsUrl'],
69
+ logs: ['cwd', 'run', 'job']
70
+ };
71
+ const unexpected = Object.keys(values).find((name) => !allowed[operation]!.includes(name));
72
+ if (unexpected) { fail(`${operation} does not accept --${unexpected}`); return; }
73
+ if (operation === 'inspect') { finish(inspectRequiredChecks(taskRef, { cwd })); return; }
74
+ if (operation === 'resolve-run') {
75
+ if (!values.checkName) { fail('resolve-run requires --check-name'); return; }
76
+ finish(resolvePlatformCheckRun(taskRef, { cwd, checkName: values.checkName, detailsUrl: values.detailsUrl }));
77
+ return;
78
+ }
79
+ if (operation === 'logs') {
80
+ const run = positive(values.run);
81
+ const job = values.job === undefined ? undefined : positive(values.job);
82
+ if (!run || values.job !== undefined && !job) { fail('logs requires a positive --run and optional positive --job'); return; }
83
+ finish(fetchPlatformCheckLogs(taskRef, { cwd, run, job: job || undefined }));
84
+ return;
85
+ }
86
+ const intervalSeconds = positive(values.intervalSeconds);
87
+ const deadlineSeconds = positive(values.deadlineSeconds);
88
+ if (!intervalSeconds || !deadlineSeconds) { fail('watch requires positive --interval-seconds and --deadline-seconds'); return; }
89
+ const controller = new AbortController();
90
+ const abort = () => controller.abort();
91
+ process.once('SIGINT', abort);
92
+ process.once('SIGTERM', abort);
93
+ try {
94
+ finish(await watchPlatformChecks(taskRef, { cwd, intervalSeconds, deadlineSeconds, signal: controller.signal }));
95
+ } finally {
96
+ process.removeListener('SIGINT', abort);
97
+ process.removeListener('SIGTERM', abort);
98
+ }
99
+ }
100
+
101
+ export { platformChecks };
@@ -0,0 +1,99 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+
4
+ import {
5
+ checkPlatformCommentOwner,
6
+ listPlatformComments,
7
+ syncPlatformComment
8
+ } from '../platform/issue-comments.ts';
9
+ import type { CommentKind } from '../platform/issue-comments.ts';
10
+ import type { PlatformResult } from '../platform/types.ts';
11
+
12
+ const USAGE = `Usage: agent-infra-internal platform-comment list --issue <N> [--cwd <path>]
13
+ agent-infra-internal platform-comment owner <task-ref> [--cwd <path>]
14
+ agent-infra-internal platform-comment sync <task-ref> --kind <kind> --agent <agent> [--artifact <file>] [--body-file <path|->] [--status-label <label>] [--backfill] [--cwd <path>]
15
+ `;
16
+
17
+ function fail(message: string): void {
18
+ process.stdout.write(`${JSON.stringify({ status: 'failed', changed: false, error: { code: 'COMMENT_PAYLOAD_INVALID', message } })}\n`);
19
+ process.stderr.write(USAGE);
20
+ process.exitCode = 1;
21
+ }
22
+
23
+ function finish(result: PlatformResult): void {
24
+ process.stdout.write(`${JSON.stringify(result)}\n`);
25
+ if (result.status === 'failed') process.exitCode = 1;
26
+ if (result.status === 'blocked') process.exitCode = 2;
27
+ }
28
+
29
+ function parseFlags(args: string[], start: number): { values: Record<string, string | boolean>; error?: string } {
30
+ const values: Record<string, string | boolean> = {};
31
+ const seen = new Set<string>();
32
+ for (let index = start; index < args.length; index += 1) {
33
+ const flag = args[index]!;
34
+ if (!['--issue', '--cwd', '--kind', '--agent', '--artifact', '--body-file', '--status-label', '--backfill'].includes(flag)) {
35
+ return { values, error: `unknown option '${flag}'` };
36
+ }
37
+ if (seen.has(flag)) return { values, error: `duplicate option '${flag}'` };
38
+ seen.add(flag);
39
+ if (flag === '--backfill') { values.backfill = true; continue; }
40
+ const value = args[++index];
41
+ if (value === undefined || value.startsWith('--')) return { values, error: `option '${flag}' requires a value` };
42
+ values[flag.slice(2).replace(/-([a-z])/g, (_, letter: string) => letter.toUpperCase())] = value;
43
+ }
44
+ return { values };
45
+ }
46
+
47
+ function readBodyFile(value: string, cwd: string): string {
48
+ if (value === '-') return fs.readFileSync(0, 'utf8');
49
+ return fs.readFileSync(path.resolve(cwd, value), 'utf8');
50
+ }
51
+
52
+ function platformComment(args: string[] = []): void {
53
+ if (args[0] === '--help' || args[0] === '-h') { process.stdout.write(USAGE); return; }
54
+ const operation = args[0];
55
+ if (!operation || !['list', 'owner', 'sync'].includes(operation)) { fail('a valid operation is required'); return; }
56
+ const hasTaskRef = operation !== 'list';
57
+ const taskRef = hasTaskRef ? args[1] : undefined;
58
+ if (hasTaskRef && (!taskRef || taskRef.startsWith('--'))) { fail(`${operation} requires a task ref`); return; }
59
+ const parsed = parseFlags(args, hasTaskRef ? 2 : 1);
60
+ if (parsed.error) { fail(parsed.error); return; }
61
+ const cwd = path.resolve(typeof parsed.values.cwd === 'string' ? parsed.values.cwd : process.cwd());
62
+ if (operation === 'list') {
63
+ const issue = Number(parsed.values.issue);
64
+ if (!Number.isInteger(issue) || issue <= 0) { fail('list requires a positive --issue'); return; }
65
+ const unexpected = Object.keys(parsed.values).find((key) => !['issue', 'cwd'].includes(key));
66
+ if (unexpected) { fail(`list does not accept '--${unexpected}'`); return; }
67
+ finish(listPlatformComments(issue, cwd));
68
+ return;
69
+ }
70
+ if (operation === 'owner') {
71
+ const unexpected = Object.keys(parsed.values).find((key) => key !== 'cwd');
72
+ if (unexpected) { fail(`owner does not accept '--${unexpected}'`); return; }
73
+ finish(checkPlatformCommentOwner(taskRef!, { cwd }));
74
+ return;
75
+ }
76
+ const kind = parsed.values.kind;
77
+ const agent = parsed.values.agent;
78
+ if (!['task', 'artifact', 'summary', 'cancel'].includes(String(kind))) { fail('sync requires a valid --kind'); return; }
79
+ if (typeof agent !== 'string' || !agent) { fail('sync requires --agent'); return; }
80
+ if (kind === 'artifact' && typeof parsed.values.artifact !== 'string') { fail('artifact sync requires --artifact'); return; }
81
+ if ((kind === 'summary' || kind === 'cancel') && typeof parsed.values.bodyFile !== 'string') { fail(`${kind} sync requires --body-file`); return; }
82
+ let body: string | undefined;
83
+ try {
84
+ if (typeof parsed.values.bodyFile === 'string') body = readBodyFile(parsed.values.bodyFile, cwd);
85
+ } catch (error) {
86
+ fail(`unable to read body file: ${error instanceof Error ? error.message : String(error)}`);
87
+ return;
88
+ }
89
+ finish(syncPlatformComment(taskRef!, {
90
+ kind: kind as CommentKind,
91
+ agent,
92
+ artifact: typeof parsed.values.artifact === 'string' ? parsed.values.artifact : undefined,
93
+ body,
94
+ backfill: parsed.values.backfill === true,
95
+ cwd
96
+ }));
97
+ }
98
+
99
+ export { platformComment };
@@ -0,0 +1,33 @@
1
+ import path from 'node:path';
2
+
3
+ import { resolvePlatformContext } from '../platform/context.ts';
4
+
5
+ const USAGE = 'Usage: agent-infra-internal platform-context resolve [--cwd <path>]\n';
6
+
7
+ function fail(message: string): void {
8
+ process.stdout.write(`${JSON.stringify({ status: 'failed', changed: false, error: { code: 'PLATFORM_PAYLOAD_INVALID', message } })}\n`);
9
+ process.stderr.write(USAGE);
10
+ process.exitCode = 1;
11
+ }
12
+
13
+ function platformContext(args: string[] = []): void {
14
+ if (args[0] === '--help' || args[0] === '-h') { process.stdout.write(USAGE); return; }
15
+ if (args[0] !== 'resolve') { fail("operation must be 'resolve'"); return; }
16
+ let cwd = process.cwd();
17
+ const seen = new Set<string>();
18
+ for (let index = 1; index < args.length; index += 1) {
19
+ const flag = args[index]!;
20
+ if (flag !== '--cwd') { fail(`unknown option '${flag}'`); return; }
21
+ if (seen.has(flag)) { fail(`duplicate option '${flag}'`); return; }
22
+ const value = args[++index];
23
+ if (!value || value.startsWith('--')) { fail(`option '${flag}' requires a value`); return; }
24
+ seen.add(flag);
25
+ cwd = path.resolve(value);
26
+ }
27
+ const result = resolvePlatformContext({ cwd });
28
+ process.stdout.write(`${JSON.stringify(result)}\n`);
29
+ if (result.status === 'failed') process.exitCode = 1;
30
+ if (result.status === 'blocked') process.exitCode = 2;
31
+ }
32
+
33
+ export { platformContext };