@fitlab-ai/agent-infra 0.9.2 → 0.9.4

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 +12 -9
  2. package/README.zh-CN.md +12 -9
  3. package/bin/cli.ts +12 -0
  4. package/dist/bin/cli.js +14 -0
  5. package/dist/bin/internal-cli.js +134 -108
  6. package/dist/lib/agent-clients/adapter.js +137 -4
  7. package/dist/lib/agent-clients/adapters/antigravity-cli.js +64 -0
  8. package/dist/lib/{sandbox/credentials.js → agent-clients/adapters/claude-code-credentials.js} +4 -14
  9. package/dist/lib/agent-clients/adapters/claude-code-refresh.js +85 -0
  10. package/dist/lib/agent-clients/adapters/claude-code-sandbox.js +247 -0
  11. package/dist/lib/agent-clients/adapters/claude-code.js +27 -4
  12. package/dist/lib/agent-clients/adapters/codex.js +9 -0
  13. package/dist/lib/agent-clients/adapters/index.js +2 -2
  14. package/dist/lib/agent-clients/adapters/opencode-sandbox.js +108 -0
  15. package/dist/lib/agent-clients/adapters/opencode.js +24 -3
  16. package/dist/lib/agent-clients/adapters/runtimes/claude-code.dockerfile +65 -0
  17. package/dist/lib/agent-clients/config.js +42 -4
  18. package/dist/lib/agent-clients/next-steps.js +11 -2
  19. package/dist/lib/agent-clients/reconcile.js +2 -1
  20. package/dist/lib/agent-clients/registry.js +16 -2
  21. package/dist/lib/agent-clients/schema.js +18 -0
  22. package/dist/lib/agent-clients/tokens.js +49 -0
  23. package/dist/lib/agent-clients/types.js +1 -1
  24. package/dist/lib/defaults.json +1 -1
  25. package/dist/lib/git/workflow.js +54 -1
  26. package/dist/lib/internal/agent-client.js +61 -4
  27. package/dist/lib/internal/git-workflow.js +6 -1
  28. package/dist/lib/internal/platform-checks.js +3 -3
  29. package/dist/lib/internal/platform-comment.js +29 -2
  30. package/dist/lib/internal/platform-issue.js +7 -0
  31. package/dist/lib/internal/platform-pr-review.js +132 -0
  32. package/dist/lib/internal/platform-pr.js +20 -2
  33. package/dist/lib/internal/platform-release-notes.js +52 -16
  34. package/dist/lib/internal/pr-review-grade.js +140 -0
  35. package/dist/lib/internal/release-workflow.js +13 -6
  36. package/dist/lib/internal/sandbox-control.js +24 -0
  37. package/dist/lib/internal/task-activity.js +105 -0
  38. package/dist/lib/internal/task-event.js +17 -1
  39. package/dist/lib/internal/task-ledger.js +12 -3
  40. package/dist/lib/internal/task-lifecycle.js +26 -1
  41. package/dist/lib/internal/task-orchestration.js +167 -18
  42. package/dist/lib/internal/task-review.js +8 -3
  43. package/dist/lib/platform/adapters.js +8 -1
  44. package/dist/lib/platform/completion-backfill.js +122 -0
  45. package/dist/lib/platform/github-client.js +19 -2
  46. package/dist/lib/platform/issue-comments.js +3 -2
  47. package/dist/lib/platform/merged-pr-equivalence.js +115 -5
  48. package/dist/lib/platform/pr-checks.js +65 -42
  49. package/dist/lib/platform/pr-review.js +166 -0
  50. package/dist/lib/platform/pull-requests.js +371 -4
  51. package/dist/lib/platform/release-notes.js +85 -3
  52. package/dist/lib/platform/releases.js +13 -5
  53. package/dist/lib/pr-review/evidence-grading.js +229 -0
  54. package/dist/lib/process-data/index.js +239 -0
  55. package/dist/lib/process-data/normalize.js +198 -0
  56. package/dist/lib/process-data/privacy.js +33 -0
  57. package/dist/lib/process-data/reconcile.js +95 -0
  58. package/dist/lib/process-data/repair.js +58 -0
  59. package/dist/lib/process-data/sources.js +345 -0
  60. package/dist/lib/process-data/store.js +282 -0
  61. package/dist/lib/process-data/types.js +2 -0
  62. package/dist/lib/run/index.js +4 -3
  63. package/dist/lib/run/tui.js +10 -13
  64. package/dist/lib/sandbox/agent-client-reconciler.js +26 -2
  65. package/dist/lib/sandbox/capture.js +7 -2
  66. package/dist/lib/sandbox/commands/create.js +109 -248
  67. package/dist/lib/sandbox/commands/enter.js +15 -47
  68. package/dist/lib/sandbox/commands/list-running.js +16 -6
  69. package/dist/lib/sandbox/commands/ls.js +10 -6
  70. package/dist/lib/sandbox/commands/rebuild.js +4 -3
  71. package/dist/lib/sandbox/commands/refresh.js +4 -84
  72. package/dist/lib/sandbox/commands/rm.js +24 -3
  73. package/dist/lib/sandbox/commands/show.js +13 -4
  74. package/dist/lib/sandbox/commands/start.js +7 -4
  75. package/dist/lib/sandbox/config.js +2 -0
  76. package/dist/lib/sandbox/constants.js +6 -0
  77. package/dist/lib/sandbox/control/client.js +44 -0
  78. package/dist/lib/sandbox/control/protocol.js +35 -0
  79. package/dist/lib/sandbox/control/server.js +109 -0
  80. package/dist/lib/sandbox/dockerfile.js +36 -13
  81. package/dist/lib/sandbox/mounts.js +17 -4
  82. package/dist/lib/sandbox/recovery.js +128 -5
  83. package/dist/lib/sandbox/redaction.js +12 -0
  84. package/dist/lib/sandbox/runtimes/base.dockerfile +24 -74
  85. package/dist/lib/sandbox/workspace-identity.js +95 -0
  86. package/dist/lib/sandbox/workspace-view.js +103 -0
  87. package/dist/lib/task/activity-intent.js +307 -0
  88. package/dist/lib/task/artifact-lifecycle.js +2 -1
  89. package/dist/lib/task/commands/log.js +15 -14
  90. package/dist/lib/task/commit-finalization.js +267 -0
  91. package/dist/lib/task/commit-intent.js +165 -0
  92. package/dist/lib/task/decision-intents.js +31 -17
  93. package/dist/lib/task/delegation-receipts.js +25 -4
  94. package/dist/lib/task/events.js +67 -28
  95. package/dist/lib/task/finalization-artifacts.js +35 -0
  96. package/dist/lib/task/implementation-inputs.js +54 -7
  97. package/dist/lib/task/ledger-intents.js +35 -1
  98. package/dist/lib/task/lifecycle-execution.js +46 -0
  99. package/dist/lib/task/orchestration.js +753 -54
  100. package/dist/lib/task/resolve-ref.js +41 -1
  101. package/dist/lib/task/review-finalization.js +29 -9
  102. package/dist/lib/task/task-execution-lock.js +231 -0
  103. package/dist/lib/task/verification-checks.js +1 -1
  104. package/dist/lib/task/verification-engine.js +218 -2
  105. package/dist/lib/task/verification.js +4 -3
  106. package/dist/lib/task/workspace-snapshot.js +17 -5
  107. package/lib/agent-clients/adapter.ts +201 -4
  108. package/lib/agent-clients/adapters/antigravity-cli.ts +65 -0
  109. package/lib/{sandbox/credentials.ts → agent-clients/adapters/claude-code-credentials.ts} +3 -18
  110. package/lib/agent-clients/adapters/claude-code-refresh.ts +128 -0
  111. package/lib/agent-clients/adapters/claude-code-sandbox.ts +295 -0
  112. package/lib/agent-clients/adapters/claude-code.ts +31 -4
  113. package/lib/agent-clients/adapters/codex.ts +9 -0
  114. package/lib/agent-clients/adapters/index.ts +2 -2
  115. package/lib/agent-clients/adapters/opencode-sandbox.ts +136 -0
  116. package/lib/agent-clients/adapters/opencode.ts +27 -3
  117. package/lib/agent-clients/adapters/runtimes/claude-code.dockerfile +65 -0
  118. package/lib/agent-clients/config.ts +49 -4
  119. package/lib/agent-clients/next-steps.ts +15 -2
  120. package/lib/agent-clients/reconcile.ts +2 -1
  121. package/lib/agent-clients/registry.ts +19 -1
  122. package/lib/agent-clients/schema.ts +18 -0
  123. package/lib/agent-clients/tokens.ts +53 -0
  124. package/lib/agent-clients/types.ts +15 -1
  125. package/lib/defaults.json +1 -1
  126. package/lib/git/workflow.ts +66 -2
  127. package/lib/internal/agent-client.ts +59 -4
  128. package/lib/internal/git-workflow.ts +5 -1
  129. package/lib/internal/platform-checks.ts +4 -4
  130. package/lib/internal/platform-comment.ts +17 -2
  131. package/lib/internal/platform-issue.ts +4 -0
  132. package/lib/internal/platform-pr-review.ts +104 -0
  133. package/lib/internal/platform-pr.ts +14 -1
  134. package/lib/internal/platform-release-notes.ts +59 -16
  135. package/lib/internal/pr-review-grade.ts +130 -0
  136. package/lib/internal/release-workflow.ts +13 -6
  137. package/lib/internal/sandbox-control.ts +24 -0
  138. package/lib/internal/task-activity.ts +91 -0
  139. package/lib/internal/task-event.ts +9 -1
  140. package/lib/internal/task-ledger.ts +12 -3
  141. package/lib/internal/task-lifecycle.ts +26 -1
  142. package/lib/internal/task-orchestration.ts +123 -14
  143. package/lib/internal/task-review.ts +8 -3
  144. package/lib/platform/adapters.ts +29 -2
  145. package/lib/platform/completion-backfill.ts +134 -0
  146. package/lib/platform/github-client.ts +20 -2
  147. package/lib/platform/issue-comments.ts +3 -2
  148. package/lib/platform/merged-pr-equivalence.ts +127 -5
  149. package/lib/platform/pr-checks.ts +73 -46
  150. package/lib/platform/pr-review.ts +201 -0
  151. package/lib/platform/pull-requests.ts +444 -3
  152. package/lib/platform/release-notes.ts +100 -4
  153. package/lib/platform/releases.ts +16 -4
  154. package/lib/pr-review/evidence-grading.ts +318 -0
  155. package/lib/process-data/index.ts +224 -0
  156. package/lib/process-data/normalize.ts +202 -0
  157. package/lib/process-data/privacy.ts +41 -0
  158. package/lib/process-data/reconcile.ts +110 -0
  159. package/lib/process-data/repair.ts +64 -0
  160. package/lib/process-data/sources.ts +354 -0
  161. package/lib/process-data/store.ts +315 -0
  162. package/lib/process-data/types.ts +135 -0
  163. package/lib/run/index.ts +5 -3
  164. package/lib/run/tui.ts +14 -12
  165. package/lib/sandbox/agent-client-reconciler.ts +44 -2
  166. package/lib/sandbox/capture.ts +9 -2
  167. package/lib/sandbox/commands/create.ts +126 -280
  168. package/lib/sandbox/commands/enter.ts +13 -59
  169. package/lib/sandbox/commands/list-running.ts +23 -6
  170. package/lib/sandbox/commands/ls.ts +22 -6
  171. package/lib/sandbox/commands/rebuild.ts +4 -3
  172. package/lib/sandbox/commands/refresh.ts +4 -126
  173. package/lib/sandbox/commands/rm.ts +24 -3
  174. package/lib/sandbox/commands/show.ts +30 -4
  175. package/lib/sandbox/commands/start.ts +6 -4
  176. package/lib/sandbox/config.ts +4 -0
  177. package/lib/sandbox/constants.ts +8 -0
  178. package/lib/sandbox/control/client.ts +55 -0
  179. package/lib/sandbox/control/protocol.ts +75 -0
  180. package/lib/sandbox/control/server.ts +117 -0
  181. package/lib/sandbox/dockerfile.ts +66 -13
  182. package/lib/sandbox/mounts.ts +24 -4
  183. package/lib/sandbox/recovery.ts +163 -6
  184. package/lib/sandbox/redaction.ts +14 -0
  185. package/lib/sandbox/runtimes/base.dockerfile +24 -74
  186. package/lib/sandbox/tool-types.ts +16 -0
  187. package/lib/sandbox/workspace-identity.ts +123 -0
  188. package/lib/sandbox/workspace-view.ts +143 -0
  189. package/lib/server/server.schema.json +2 -2
  190. package/lib/task/activity-intent.ts +417 -0
  191. package/lib/task/artifact-lifecycle.ts +2 -1
  192. package/lib/task/commands/log.ts +15 -14
  193. package/lib/task/commit-finalization.ts +356 -0
  194. package/lib/task/commit-intent.ts +230 -0
  195. package/lib/task/decision-intents.ts +32 -12
  196. package/lib/task/delegation-receipts.ts +34 -5
  197. package/lib/task/events.ts +75 -30
  198. package/lib/task/finalization-artifacts.ts +58 -0
  199. package/lib/task/implementation-inputs.ts +62 -8
  200. package/lib/task/ledger-intents.ts +43 -3
  201. package/lib/task/lifecycle-execution.ts +106 -0
  202. package/lib/task/orchestration.ts +1019 -72
  203. package/lib/task/resolve-ref.ts +46 -1
  204. package/lib/task/review-finalization.ts +40 -9
  205. package/lib/task/task-execution-lock.ts +296 -0
  206. package/lib/task/verification-checks.ts +1 -1
  207. package/lib/task/verification-engine.ts +249 -2
  208. package/lib/task/verification.ts +5 -3
  209. package/lib/task/workspace-snapshot.ts +26 -5
  210. package/package.json +3 -3
  211. package/templates/.agents/QUICKSTART.en.md +25 -8
  212. package/templates/.agents/QUICKSTART.zh-CN.md +25 -8
  213. package/templates/.agents/README.en.md +10 -7
  214. package/templates/.agents/README.zh-CN.md +10 -7
  215. package/templates/.agents/hooks/lifecycle-delegation.js +15 -4
  216. package/templates/.agents/rules/README.en.md +1 -1
  217. package/templates/.agents/rules/README.zh-CN.md +1 -1
  218. package/templates/.agents/rules/create-issue.github.en.md +4 -2
  219. package/templates/.agents/rules/create-issue.github.zh-CN.md +4 -2
  220. package/templates/.agents/rules/human-decision-context.en.md +11 -2
  221. package/templates/.agents/rules/human-decision-context.zh-CN.md +11 -2
  222. package/templates/.agents/rules/issue-fields.github.en.md +3 -1
  223. package/templates/.agents/rules/issue-fields.github.zh-CN.md +3 -1
  224. package/templates/.agents/rules/issue-pr-commands.en.md +5 -3
  225. package/templates/.agents/rules/issue-pr-commands.github.en.md +5 -3
  226. package/templates/.agents/rules/issue-pr-commands.github.zh-CN.md +5 -3
  227. package/templates/.agents/rules/issue-pr-commands.zh-CN.md +5 -3
  228. package/templates/.agents/rules/issue-sync.github.en.md +7 -3
  229. package/templates/.agents/rules/issue-sync.github.zh-CN.md +8 -4
  230. package/templates/.agents/rules/lifecycle-orchestration.en.md +5 -2
  231. package/templates/.agents/rules/lifecycle-orchestration.zh-CN.md +5 -2
  232. package/templates/.agents/rules/milestone-inference.github.en.md +4 -2
  233. package/templates/.agents/rules/milestone-inference.github.zh-CN.md +4 -2
  234. package/templates/.agents/rules/next-step-output.en.md +3 -1
  235. package/templates/.agents/rules/next-step-output.zh-CN.md +3 -1
  236. package/templates/.agents/rules/no-mid-flow-questions.en.md +17 -0
  237. package/templates/.agents/rules/no-mid-flow-questions.zh-CN.md +17 -0
  238. package/templates/.agents/rules/pr-checks-commands.en.md +3 -3
  239. package/templates/.agents/rules/pr-checks-commands.github.en.md +2 -2
  240. package/templates/.agents/rules/pr-checks-commands.github.zh-CN.md +3 -3
  241. package/templates/.agents/rules/pr-checks-commands.zh-CN.md +3 -3
  242. package/templates/.agents/rules/pr-sync.en.md +3 -1
  243. package/templates/.agents/rules/pr-sync.github.en.md +11 -1
  244. package/templates/.agents/rules/pr-sync.github.zh-CN.md +11 -1
  245. package/templates/.agents/rules/pr-sync.zh-CN.md +3 -1
  246. package/templates/.agents/rules/release-commands.en.md +1 -1
  247. package/templates/.agents/rules/release-commands.github.en.md +12 -2
  248. package/templates/.agents/rules/release-commands.github.zh-CN.md +12 -2
  249. package/templates/.agents/rules/release-commands.zh-CN.md +1 -1
  250. package/templates/.agents/rules/review-handshake.zh-CN.md +1 -1
  251. package/templates/.agents/rules/task-management.en.md +13 -0
  252. package/templates/.agents/rules/task-management.zh-CN.md +13 -0
  253. package/templates/.agents/skills/analyze-task/SKILL.en.md +10 -7
  254. package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +10 -7
  255. package/templates/.agents/skills/block-task/SKILL.en.md +7 -5
  256. package/templates/.agents/skills/block-task/SKILL.zh-CN.md +7 -5
  257. package/templates/.agents/skills/cancel-task/SKILL.en.md +6 -4
  258. package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +6 -4
  259. package/templates/.agents/skills/close-codescan/SKILL.en.md +3 -1
  260. package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +3 -1
  261. package/templates/.agents/skills/close-dependabot/SKILL.en.md +3 -1
  262. package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +3 -1
  263. package/templates/.agents/skills/code-task/SKILL.en.md +10 -7
  264. package/templates/.agents/skills/code-task/SKILL.zh-CN.md +12 -9
  265. package/templates/.agents/skills/commit/SKILL.en.md +23 -11
  266. package/templates/.agents/skills/commit/SKILL.zh-CN.md +29 -11
  267. package/templates/.agents/skills/commit/reference/commit-message.en.md +1 -1
  268. package/templates/.agents/skills/commit/reference/commit-message.zh-CN.md +1 -1
  269. package/templates/.agents/skills/commit/reference/commit-orchestration.en.md +49 -0
  270. package/templates/.agents/skills/commit/reference/commit-orchestration.zh-CN.md +55 -0
  271. package/templates/.agents/skills/commit/reference/issue-metadata-sync.en.md +1 -1
  272. package/templates/.agents/skills/commit/reference/issue-metadata-sync.zh-CN.md +1 -1
  273. package/templates/.agents/skills/commit/reference/pr-summary-sync.en.md +1 -1
  274. package/templates/.agents/skills/commit/reference/pr-summary-sync.zh-CN.md +1 -1
  275. package/templates/.agents/skills/commit/reference/task-status-update.en.md +4 -4
  276. package/templates/.agents/skills/commit/reference/task-status-update.zh-CN.md +4 -4
  277. package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +6 -4
  278. package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +6 -4
  279. package/templates/.agents/skills/complete-manual-validation/reference/summary-update.en.md +1 -1
  280. package/templates/.agents/skills/complete-manual-validation/reference/summary-update.zh-CN.md +1 -1
  281. package/templates/.agents/skills/complete-task/SKILL.en.md +22 -7
  282. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +22 -7
  283. package/templates/.agents/skills/complete-task/config/verify.en.json +1 -0
  284. package/templates/.agents/skills/complete-task/config/verify.zh-CN.json +1 -0
  285. package/templates/.agents/skills/complete-task/reference/external-delivery.en.md +17 -0
  286. package/templates/.agents/skills/complete-task/reference/external-delivery.zh-CN.md +17 -0
  287. package/templates/.agents/skills/create-pr/SKILL.en.md +7 -5
  288. package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +7 -5
  289. package/templates/.agents/skills/create-pr/reference/comment-publish.en.md +1 -1
  290. package/templates/.agents/skills/create-pr/reference/comment-publish.zh-CN.md +1 -1
  291. package/templates/.agents/skills/create-release-note/SKILL.en.md +17 -16
  292. package/templates/.agents/skills/create-release-note/SKILL.zh-CN.md +17 -16
  293. package/templates/.agents/skills/create-task/SKILL.en.md +5 -3
  294. package/templates/.agents/skills/create-task/SKILL.zh-CN.md +5 -3
  295. package/templates/.agents/skills/create-task/reference/context-capture.en.md +34 -0
  296. package/templates/.agents/skills/create-task/reference/context-capture.zh-CN.md +34 -0
  297. package/templates/.agents/skills/import-issue/SKILL.en.md +5 -3
  298. package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +5 -3
  299. package/templates/.agents/skills/plan-task/SKILL.en.md +8 -5
  300. package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +8 -5
  301. package/templates/.agents/skills/post-release/SKILL.en.md +2 -0
  302. package/templates/.agents/skills/post-release/SKILL.zh-CN.md +2 -0
  303. package/templates/.agents/skills/release/SKILL.en.md +16 -10
  304. package/templates/.agents/skills/release/SKILL.zh-CN.md +16 -10
  305. package/templates/.agents/skills/restore-task/SKILL.en.md +4 -1
  306. package/templates/.agents/skills/restore-task/SKILL.zh-CN.md +4 -1
  307. package/templates/.agents/skills/review-analysis/SKILL.en.md +7 -4
  308. package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +7 -4
  309. package/templates/.agents/skills/review-code/SKILL.en.md +11 -8
  310. package/templates/.agents/skills/review-code/SKILL.zh-CN.md +11 -8
  311. package/templates/.agents/skills/review-code/reference/output-templates.en.md +1 -1
  312. package/templates/.agents/skills/review-code/reference/output-templates.zh-CN.md +1 -1
  313. package/templates/.agents/skills/review-plan/SKILL.en.md +7 -4
  314. package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +7 -4
  315. package/templates/.agents/skills/review-pr/SKILL.en.md +186 -0
  316. package/templates/.agents/skills/review-pr/SKILL.zh-CN.md +186 -0
  317. package/templates/.agents/skills/review-pr/config/verify.en.json +37 -0
  318. package/templates/.agents/skills/review-pr/config/verify.zh-CN.json +37 -0
  319. package/templates/.agents/skills/review-pr/reference/evidence-grading.en.md +65 -0
  320. package/templates/.agents/skills/review-pr/reference/evidence-grading.zh-CN.md +65 -0
  321. package/templates/.agents/skills/review-pr/reference/output-guidance.en.md +18 -0
  322. package/templates/.agents/skills/review-pr/reference/output-guidance.zh-CN.md +18 -0
  323. package/templates/.agents/skills/review-pr/reference/report-template.en.md +85 -0
  324. package/templates/.agents/skills/review-pr/reference/report-template.zh-CN.md +85 -0
  325. package/templates/.agents/skills/run-task/SKILL.en.md +8 -8
  326. package/templates/.agents/skills/run-task/SKILL.zh-CN.md +8 -8
  327. package/templates/.agents/skills/run-task/config/verify.en.json +2 -1
  328. package/templates/.agents/skills/run-task/config/verify.zh-CN.json +2 -1
  329. package/templates/.agents/skills/run-task/reference/host-validation.en.md +20 -0
  330. package/templates/.agents/skills/run-task/reference/host-validation.zh-CN.md +20 -0
  331. package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +98 -107
  332. package/templates/.agents/skills/watch-pr/SKILL.en.md +13 -13
  333. package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +13 -13
  334. package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.en.md +27 -6
  335. package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.zh-CN.md +26 -5
  336. package/templates/.agents/templates/task.en.md +2 -2
  337. package/templates/.agents/templates/task.zh-CN.md +2 -2
  338. package/templates/.agents/workflows/bug-fix.en.yaml +3 -3
  339. package/templates/.agents/workflows/bug-fix.zh-CN.yaml +3 -3
  340. package/templates/.agents/workflows/code-review.en.yaml +1 -1
  341. package/templates/.agents/workflows/code-review.zh-CN.yaml +1 -1
  342. package/templates/.agents/workflows/feature-development.en.yaml +4 -4
  343. package/templates/.agents/workflows/feature-development.zh-CN.yaml +4 -4
  344. package/templates/.agents/workflows/refactoring.en.yaml +3 -3
  345. package/templates/.agents/workflows/refactoring.zh-CN.yaml +3 -3
  346. package/templates/.claude/commands/post-release.en.md +1 -0
  347. package/templates/.claude/commands/post-release.zh-CN.md +1 -0
  348. package/templates/.claude/commands/review-pr.en.md +8 -0
  349. package/templates/.claude/commands/review-pr.zh-CN.md +8 -0
  350. package/templates/.claude/commands/watch-pr.en.md +1 -1
  351. package/templates/.claude/commands/watch-pr.zh-CN.md +1 -1
  352. package/templates/.claude/rules/tool-preferences.en.md +20 -0
  353. package/templates/.claude/rules/tool-preferences.zh-CN.md +20 -0
  354. package/templates/.opencode/commands/post-release.en.md +2 -0
  355. package/templates/.opencode/commands/post-release.zh-CN.md +2 -0
  356. package/templates/.opencode/commands/review-pr.en.md +11 -0
  357. package/templates/.opencode/commands/review-pr.zh-CN.md +11 -0
  358. package/templates/.opencode/commands/watch-pr.en.md +1 -1
  359. package/templates/.opencode/commands/watch-pr.zh-CN.md +1 -1
  360. package/dist/lib/agent-clients/adapters/gemini-cli.js +0 -64
  361. package/lib/agent-clients/adapters/gemini-cli.ts +0 -65
  362. package/templates/.gemini/commands/_project_/analyze-task.en.toml +0 -10
  363. package/templates/.gemini/commands/_project_/analyze-task.zh-CN.toml +0 -10
  364. package/templates/.gemini/commands/_project_/archive-tasks.en.toml +0 -10
  365. package/templates/.gemini/commands/_project_/archive-tasks.zh-CN.toml +0 -10
  366. package/templates/.gemini/commands/_project_/block-task.en.toml +0 -10
  367. package/templates/.gemini/commands/_project_/block-task.zh-CN.toml +0 -10
  368. package/templates/.gemini/commands/_project_/cancel-task.en.toml +0 -10
  369. package/templates/.gemini/commands/_project_/cancel-task.zh-CN.toml +0 -10
  370. package/templates/.gemini/commands/_project_/check-task.en.toml +0 -10
  371. package/templates/.gemini/commands/_project_/check-task.zh-CN.toml +0 -10
  372. package/templates/.gemini/commands/_project_/close-codescan.en.toml +0 -8
  373. package/templates/.gemini/commands/_project_/close-codescan.zh-CN.toml +0 -8
  374. package/templates/.gemini/commands/_project_/close-dependabot.en.toml +0 -8
  375. package/templates/.gemini/commands/_project_/close-dependabot.zh-CN.toml +0 -8
  376. package/templates/.gemini/commands/_project_/code-task.en.toml +0 -10
  377. package/templates/.gemini/commands/_project_/code-task.zh-CN.toml +0 -10
  378. package/templates/.gemini/commands/_project_/commit.en.toml +0 -10
  379. package/templates/.gemini/commands/_project_/commit.zh-CN.toml +0 -10
  380. package/templates/.gemini/commands/_project_/complete-manual-validation.en.toml +0 -10
  381. package/templates/.gemini/commands/_project_/complete-manual-validation.zh-CN.toml +0 -10
  382. package/templates/.gemini/commands/_project_/complete-task.en.toml +0 -10
  383. package/templates/.gemini/commands/_project_/complete-task.zh-CN.toml +0 -10
  384. package/templates/.gemini/commands/_project_/create-pr.en.toml +0 -10
  385. package/templates/.gemini/commands/_project_/create-pr.zh-CN.toml +0 -10
  386. package/templates/.gemini/commands/_project_/create-release-note.en.toml +0 -8
  387. package/templates/.gemini/commands/_project_/create-release-note.zh-CN.toml +0 -8
  388. package/templates/.gemini/commands/_project_/create-task.en.toml +0 -8
  389. package/templates/.gemini/commands/_project_/create-task.zh-CN.toml +0 -8
  390. package/templates/.gemini/commands/_project_/import-codescan.en.toml +0 -8
  391. package/templates/.gemini/commands/_project_/import-codescan.zh-CN.toml +0 -8
  392. package/templates/.gemini/commands/_project_/import-dependabot.en.toml +0 -8
  393. package/templates/.gemini/commands/_project_/import-dependabot.zh-CN.toml +0 -8
  394. package/templates/.gemini/commands/_project_/import-issue.en.toml +0 -8
  395. package/templates/.gemini/commands/_project_/import-issue.zh-CN.toml +0 -8
  396. package/templates/.gemini/commands/_project_/init-labels.en.toml +0 -8
  397. package/templates/.gemini/commands/_project_/init-labels.zh-CN.toml +0 -8
  398. package/templates/.gemini/commands/_project_/init-milestones.en.toml +0 -10
  399. package/templates/.gemini/commands/_project_/init-milestones.zh-CN.toml +0 -10
  400. package/templates/.gemini/commands/_project_/plan-task.en.toml +0 -10
  401. package/templates/.gemini/commands/_project_/plan-task.zh-CN.toml +0 -10
  402. package/templates/.gemini/commands/_project_/post-release.en.toml +0 -6
  403. package/templates/.gemini/commands/_project_/post-release.zh-CN.toml +0 -6
  404. package/templates/.gemini/commands/_project_/refine-title.en.toml +0 -8
  405. package/templates/.gemini/commands/_project_/refine-title.zh-CN.toml +0 -8
  406. package/templates/.gemini/commands/_project_/release.en.toml +0 -8
  407. package/templates/.gemini/commands/_project_/release.zh-CN.toml +0 -8
  408. package/templates/.gemini/commands/_project_/restore-task.en.toml +0 -8
  409. package/templates/.gemini/commands/_project_/restore-task.zh-CN.toml +0 -8
  410. package/templates/.gemini/commands/_project_/review-analysis.en.toml +0 -10
  411. package/templates/.gemini/commands/_project_/review-analysis.zh-CN.toml +0 -10
  412. package/templates/.gemini/commands/_project_/review-code.en.toml +0 -10
  413. package/templates/.gemini/commands/_project_/review-code.zh-CN.toml +0 -10
  414. package/templates/.gemini/commands/_project_/review-plan.en.toml +0 -10
  415. package/templates/.gemini/commands/_project_/review-plan.zh-CN.toml +0 -10
  416. package/templates/.gemini/commands/_project_/run-task.en.toml +0 -8
  417. package/templates/.gemini/commands/_project_/run-task.zh-CN.toml +0 -8
  418. package/templates/.gemini/commands/_project_/test-integration.en.toml +0 -6
  419. package/templates/.gemini/commands/_project_/test-integration.zh-CN.toml +0 -6
  420. package/templates/.gemini/commands/_project_/test.en.toml +0 -6
  421. package/templates/.gemini/commands/_project_/test.zh-CN.toml +0 -6
  422. package/templates/.gemini/commands/_project_/update-agent-infra.en.toml +0 -6
  423. package/templates/.gemini/commands/_project_/update-agent-infra.zh-CN.toml +0 -6
  424. package/templates/.gemini/commands/_project_/upgrade-dependency.en.toml +0 -8
  425. package/templates/.gemini/commands/_project_/upgrade-dependency.zh-CN.toml +0 -8
  426. package/templates/.gemini/commands/_project_/watch-pr.en.toml +0 -10
  427. package/templates/.gemini/commands/_project_/watch-pr.zh-CN.toml +0 -10
  428. package/templates/.gemini/settings.json +0 -3
@@ -2,6 +2,14 @@ import process from 'node:process';
2
2
  import spawn from 'cross-spawn';
3
3
  import semver from 'semver';
4
4
  const MINIMUM_GITHUB_CLI_VERSION = '2.16.0';
5
+ const GITHUB_CLI_MAX_BUFFER = 64 * 1024 * 1024;
6
+ const ERROR_DETAIL_LIMIT = 4096;
7
+ function boundedFailureDetail(result) {
8
+ const diagnostic = `${result.stderr}\n${result.error?.message || ''}`.trim() || result.stdout.trim();
9
+ return diagnostic.length <= ERROR_DETAIL_LIMIT
10
+ ? diagnostic
11
+ : `${diagnostic.slice(0, ERROR_DETAIL_LIMIT)}… [truncated]`;
12
+ }
5
13
  function defaultRunner(args, options) {
6
14
  const command = process.env.AGENT_INFRA_GH_BIN || 'gh';
7
15
  let prefix = [];
@@ -16,6 +24,7 @@ function defaultRunner(args, options) {
16
24
  encoding: 'utf8',
17
25
  env: process.env,
18
26
  input: options.input,
27
+ maxBuffer: GITHUB_CLI_MAX_BUFFER,
19
28
  shell: false
20
29
  });
21
30
  return {
@@ -37,7 +46,15 @@ function defaultSleep(delayMs) {
37
46
  Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, delayMs);
38
47
  }
39
48
  function classifyGitHubFailure(result) {
40
- const detail = `${result.stderr}\n${result.stdout}\n${result.error?.message || ''}`.trim();
49
+ const errorCode = result.error && 'code' in result.error ? result.error.code : undefined;
50
+ if (errorCode === 'ENOBUFS') {
51
+ return {
52
+ code: 'PLATFORM_OUTPUT_TOO_LARGE',
53
+ message: 'GitHub CLI output exceeded the configured limit',
54
+ retryable: false
55
+ };
56
+ }
57
+ const detail = boundedFailureDetail(result);
41
58
  const lower = detail.toLowerCase();
42
59
  if (/\b401\b|bad credentials|authentication required|not logged into/.test(lower)) {
43
60
  return { code: 'AUTH_REQUIRED', message: detail || 'GitHub authentication is required', retryable: false };
@@ -54,7 +71,7 @@ function classifyGitHubFailure(result) {
54
71
  if (/\b422\b|validation failed/.test(lower)) {
55
72
  return { code: 'PLATFORM_REQUEST_INVALID', message: detail || 'GitHub rejected the request', retryable: false };
56
73
  }
57
- if (result.error && 'code' in result.error && result.error.code === 'ENOENT') {
74
+ if (errorCode === 'ENOENT') {
58
75
  return { code: 'PLATFORM_DEPENDENCY_MISSING', message: detail || 'GitHub CLI is unavailable', retryable: false };
59
76
  }
60
77
  return { code: 'PLATFORM_REQUEST_FAILED', message: detail || 'GitHub request failed', retryable: false };
@@ -19,7 +19,8 @@ const ARTIFACT_TITLES = {
19
19
  'review-plan': '技术方案审查',
20
20
  code: '实现报告',
21
21
  'review-code': '代码审查',
22
- 'manual-validation': '人工验证报告'
22
+ 'manual-validation': '人工验证报告',
23
+ 'pr-review': 'PR 审查报告'
23
24
  };
24
25
  function normalizeCommentContent(content) {
25
26
  return content.replace(/\r\n/g, '\n').replace(/\n+$/, '\n');
@@ -51,7 +52,7 @@ function renderTaskComment(content, taskId, agent) {
51
52
  }
52
53
  function artifactIdentity(artifact) {
53
54
  const stem = path.basename(artifact, '.md');
54
- const match = stem.match(/^(analysis|review-analysis|plan|review-plan|code|review-code|manual-validation)(?:-r(\d+))?$/);
55
+ const match = stem.match(/^(analysis|review-analysis|plan|review-plan|code|review-code|manual-validation|pr-review)(?:-r(\d+))?$/);
55
56
  if (!match)
56
57
  throw new Error(`unsupported artifact '${artifact}'`);
57
58
  const base = ARTIFACT_TITLES[match[1]];
@@ -1,4 +1,7 @@
1
1
  import { execFileSync, spawnSync } from "node:child_process";
2
+ import fs from "node:fs";
3
+ import os from "node:os";
4
+ import path from "node:path";
2
5
  const SHA_PATTERN = /^[0-9a-f]{40}$/i;
3
6
  function git(cwd, args) {
4
7
  return execFileSync("git", args, {
@@ -29,6 +32,103 @@ function isAncestor(gitRoot, ancestor, descendant) {
29
32
  return { status: "no" };
30
33
  return { status: "error" };
31
34
  }
35
+ function blocked(message) {
36
+ return { status: "blocked", code: "PR_MERGE_GIT_EVIDENCE_UNAVAILABLE", message };
37
+ }
38
+ function resolveUniqueMergeBase(gitRoot, reviewedHead, squashParent) {
39
+ const result = spawnSync("git", ["merge-base", "--all", reviewedHead, squashParent], {
40
+ cwd: gitRoot,
41
+ encoding: "utf8",
42
+ stdio: ["ignore", "pipe", "pipe"]
43
+ });
44
+ if (result.error || result.status === null || result.status > 1) {
45
+ return { status: "blocked", relation: blocked("Unable to compute the reviewed PR merge base") };
46
+ }
47
+ const candidates = result.stdout.trim() === "" ? [] : result.stdout.trim().split(/\s+/);
48
+ if (result.status === 1 || candidates.length !== 1) {
49
+ return {
50
+ status: "failed",
51
+ relation: {
52
+ status: "failed",
53
+ code: "PR_MERGE_TOPOLOGY_INVALID",
54
+ message: "Reviewed head and squash parent do not have a unique merge base"
55
+ }
56
+ };
57
+ }
58
+ const [candidate] = candidates;
59
+ if (!candidate || !SHA_PATTERN.test(candidate)) {
60
+ return { status: "blocked", relation: blocked("Git returned an invalid reviewed PR merge base") };
61
+ }
62
+ return { status: "ok", mergeBase: candidate };
63
+ }
64
+ function compareReplayedTree(input) {
65
+ const { gitRoot, mergeBase, reviewedHead, squashParent, squashCommit } = input;
66
+ let tempDir;
67
+ let relation = null;
68
+ try {
69
+ tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "agent-infra-merge-equivalence-"));
70
+ const env = { ...process.env, GIT_INDEX_FILE: path.join(tempDir, "index") };
71
+ const readTree = spawnSync("git", ["read-tree", squashParent], {
72
+ cwd: gitRoot, env, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"]
73
+ });
74
+ if (readTree.error || readTree.status !== 0) {
75
+ relation = blocked("Unable to initialize isolated squash merge evidence");
76
+ }
77
+ else {
78
+ const patchResult = spawnSync("git", ["diff", "--no-renames", "--binary", "--full-index", mergeBase, reviewedHead], { cwd: gitRoot, env, encoding: "utf8", maxBuffer: 32 * 1024 * 1024, stdio: ["ignore", "pipe", "pipe"] });
79
+ if (patchResult.error || patchResult.status !== 0) {
80
+ relation = blocked("Unable to generate the reviewed PR patch");
81
+ }
82
+ else if (patchResult.stdout !== "") {
83
+ const applyResult = spawnSync("git", ["apply", "--cached", "--3way", "--binary", "-q"], {
84
+ cwd: gitRoot,
85
+ env,
86
+ encoding: "utf8",
87
+ input: patchResult.stdout,
88
+ maxBuffer: 32 * 1024 * 1024,
89
+ stdio: ["pipe", "pipe", "pipe"]
90
+ });
91
+ if (applyResult.status === 1) {
92
+ relation = {
93
+ status: "failed",
94
+ code: "PR_MERGE_CONTENT_MISMATCH",
95
+ message: "Reviewed PR patch cannot be replayed without conflicts, so content equivalence cannot be established"
96
+ };
97
+ }
98
+ else if (applyResult.error || applyResult.status !== 0) {
99
+ relation = blocked("Unable to replay the reviewed PR patch");
100
+ }
101
+ }
102
+ if (relation === null) {
103
+ const compareResult = spawnSync("git", ["diff", "--cached", "--quiet", "--no-ext-diff", squashCommit, "--"], { cwd: gitRoot, env, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] });
104
+ if (compareResult.status === 1) {
105
+ relation = {
106
+ status: "failed",
107
+ code: "PR_MERGE_CONTENT_MISMATCH",
108
+ message: "Replayed reviewed PR content differs from the squash merge tree"
109
+ };
110
+ }
111
+ else if (compareResult.error || compareResult.status !== 0) {
112
+ relation = blocked("Unable to compare replayed PR content with the squash merge tree");
113
+ }
114
+ }
115
+ }
116
+ }
117
+ catch {
118
+ relation = blocked("Unable to create isolated squash merge evidence");
119
+ }
120
+ finally {
121
+ if (tempDir) {
122
+ try {
123
+ fs.rmSync(tempDir, { recursive: true, force: true });
124
+ }
125
+ catch {
126
+ relation = blocked("Unable to clean up isolated squash merge evidence");
127
+ }
128
+ }
129
+ }
130
+ return relation;
131
+ }
32
132
  export function resolveReviewedHeadRelation(input) {
33
133
  const { gitRoot, comparisonHead, lastReviewedCommit, pullRequest } = input;
34
134
  if (comparisonHead === lastReviewedCommit &&
@@ -64,12 +164,22 @@ export function resolveReviewedHeadRelation(input) {
64
164
  if (parents.length !== 2) {
65
165
  return { status: "failed", code: "PR_MERGE_TOPOLOGY_INVALID", message: "PR merge commit is not a single-parent squash commit" };
66
166
  }
67
- const diffArgs = ["diff", "--no-renames", "--binary", "--full-index"];
68
- const reviewedDiff = git(gitRoot, [...diffArgs, pullRequest.base.sha, pullRequest.head.sha]);
69
- const mergedDiff = git(gitRoot, [...diffArgs, `${pullRequest.mergeCommitSha}^`, pullRequest.mergeCommitSha]);
70
- if (reviewedDiff !== mergedDiff) {
71
- return { status: "failed", code: "PR_MERGE_CONTENT_MISMATCH", message: "Squash merge content differs from the reviewed PR changes" };
167
+ const squashParent = parents[1];
168
+ if (!squashParent) {
169
+ return { status: "failed", code: "PR_MERGE_TOPOLOGY_INVALID", message: "PR merge commit is not a single-parent squash commit" };
72
170
  }
171
+ const mergeBase = resolveUniqueMergeBase(gitRoot, pullRequest.head.sha, squashParent);
172
+ if (mergeBase.status !== "ok")
173
+ return mergeBase.relation;
174
+ const replayFailure = compareReplayedTree({
175
+ gitRoot,
176
+ mergeBase: mergeBase.mergeBase,
177
+ reviewedHead: pullRequest.head.sha,
178
+ squashParent,
179
+ squashCommit: pullRequest.mergeCommitSha
180
+ });
181
+ if (replayFailure)
182
+ return replayFailure;
73
183
  }
74
184
  catch {
75
185
  return { status: "blocked", code: "PR_MERGE_GIT_EVIDENCE_UNAVAILABLE", message: "Unable to verify PR merge topology or content" };
@@ -17,21 +17,14 @@ function classifyRequiredChecks(required) {
17
17
  return { state: 'pending', required };
18
18
  return { state: 'passed', required };
19
19
  }
20
- async function watchRequiredChecks(options) {
21
- const now = options.now || (() => performance.now());
22
- const sleep = options.sleep || ((delay) => new Promise((resolve) => setTimeout(resolve, delay)));
23
- const started = now();
24
- while (true) {
25
- if (options.signal?.aborted)
26
- return { state: 'cancelled', required: [] };
27
- const snapshot = await options.inspect();
28
- if (snapshot.state !== 'pending')
29
- return snapshot;
30
- const elapsed = now() - started;
31
- if (elapsed >= options.deadlineMs)
32
- return { state: 'timed-out', required: snapshot.required };
33
- await sleep(Math.min(options.intervalMs, options.deadlineMs - elapsed));
34
- }
20
+ function classifyPullRequestReadiness(input) {
21
+ if (input.mergeability === 'conflicting')
22
+ return { state: 'conflicting', headSha: input.headSha };
23
+ if (input.checks.state === 'failed' || input.checks.state === 'cancelled')
24
+ return { state: 'checks-failed', headSha: input.headSha };
25
+ if (input.mergeability === 'unknown' || input.checks.state === 'pending')
26
+ return { state: 'pending', headSha: input.headSha };
27
+ return { state: 'ready', headSha: input.headSha };
35
28
  }
36
29
  function parseRunJobIdentity(detailsUrl) {
37
30
  try {
@@ -92,7 +85,7 @@ registerPlatformCapabilities('github', {
92
85
  inspectRequiredChecks({ client, repository, number, cwd }) {
93
86
  const github = client || createGitHubClient();
94
87
  const inspected = github.json([
95
- 'pr', 'checks', String(number), '--repo', repository, '--required',
88
+ 'pr', 'checks', String(number), '--repo', repository,
96
89
  '--json', 'name,state,bucket,link,workflow,startedAt,completedAt'
97
90
  ], { cwd });
98
91
  return inspected.ok
@@ -122,6 +115,9 @@ function inspectRequiredChecks(taskRef, options = {}) {
122
115
  const base = resolvedTask(taskRef, options);
123
116
  if (!base.ok)
124
117
  return base.output;
118
+ return inspectChecksForResolvedTask(base, options);
119
+ }
120
+ function inspectChecksForResolvedTask(base, options) {
125
121
  const repository = base.context.platform.repository;
126
122
  const inspected = inspectPlatformRequiredChecks(base.context.platform.type, {
127
123
  cwd: base.resolved.repoRoot,
@@ -157,33 +153,60 @@ function inspectRequiredChecks(taskRef, options = {}) {
157
153
  }
158
154
  });
159
155
  }
160
- async function watchPlatformChecks(taskRef, options) {
161
- let last = null;
162
- const watched = await watchRequiredChecks({
163
- intervalMs: options.intervalSeconds * 1000,
164
- deadlineMs: options.deadlineSeconds * 1000,
165
- signal: options.signal,
166
- now: options.now,
167
- sleep: options.sleep,
168
- inspect: async () => {
169
- last = inspectRequiredChecks(taskRef, options);
170
- if (last.checks.state === 'pending' && last.error?.code === 'REQUIRED_CHECKS_PENDING')
171
- return last.checks;
172
- return last.checks;
156
+ function inspectPullRequestReadiness(taskRef, options = {}) {
157
+ const base = resolvedTask(taskRef, options);
158
+ if (!base.ok)
159
+ return base.output;
160
+ const checked = inspectChecksForResolvedTask(base, options);
161
+ const classifiedCodes = new Set(['REQUIRED_CHECKS_PENDING', 'REQUIRED_CHECKS_FAILED', 'REQUIRED_CHECKS_CANCELLED']);
162
+ if (checked.error && !classifiedCodes.has(checked.error.code))
163
+ return checked;
164
+ const readiness = classifyPullRequestReadiness({
165
+ headSha: base.pullRequest.head.sha,
166
+ mergeability: base.pullRequest.mergeability?.state ?? 'unknown',
167
+ checks: checked.checks
168
+ });
169
+ const status = readiness.state === 'ready' ? 'no-op'
170
+ : readiness.state === 'conflicting' || readiness.state === 'checks-failed' ? 'failed' : 'blocked';
171
+ return checksResult(status, {
172
+ ...checked,
173
+ status,
174
+ changed: false,
175
+ readiness,
176
+ error: status === 'no-op' ? null : {
177
+ code: readiness.state === 'conflicting' ? 'PR_MERGE_CONFLICT'
178
+ : readiness.state === 'checks-failed' ? 'REQUIRED_CHECKS_FAILED' : 'PR_READINESS_PENDING',
179
+ message: `Pull request readiness is ${readiness.state}`,
180
+ retryable: readiness.state === 'pending'
173
181
  }
174
182
  });
175
- if (!last)
176
- return checksResult('blocked', { checks: watched, error: { code: 'REQUIRED_CHECKS_UNAVAILABLE', message: 'No checks snapshot was produced', retryable: true } });
177
- const snapshot = last;
178
- if (watched.state === 'timed-out' || watched.state === 'cancelled')
179
- return checksResult('blocked', {
180
- ...snapshot,
181
- status: 'blocked',
182
- changed: false,
183
- checks: watched,
184
- error: { code: watched.state === 'timed-out' ? 'REQUIRED_CHECKS_TIMEOUT' : 'REQUIRED_CHECKS_CANCELLED', message: `Required checks ${watched.state}`, retryable: watched.state === 'timed-out' }
185
- });
186
- return { ...snapshot, checks: watched };
183
+ }
184
+ async function watchPullRequestReadiness(taskRef, options) {
185
+ const now = options.now || (() => performance.now());
186
+ const sleep = options.sleep || ((delay) => new Promise((resolve) => setTimeout(resolve, delay)));
187
+ const started = now();
188
+ let last = null;
189
+ while (true) {
190
+ if (options.signal?.aborted)
191
+ return checksResult('blocked', {
192
+ ...last,
193
+ status: 'blocked', changed: false,
194
+ readiness: { state: 'cancelled', headSha: last?.pullRequest?.head.sha ?? '' },
195
+ error: { code: 'PR_READINESS_CANCELLED', message: 'Pull request readiness watch was cancelled', retryable: false }
196
+ });
197
+ last = inspectPullRequestReadiness(taskRef, options);
198
+ if (last.readiness?.state !== 'pending' || last.error?.code !== 'PR_READINESS_PENDING')
199
+ return last;
200
+ const elapsed = now() - started;
201
+ if (elapsed >= options.deadlineSeconds * 1000)
202
+ return checksResult('blocked', {
203
+ ...last,
204
+ status: 'blocked', changed: false,
205
+ readiness: { state: 'timed-out', headSha: last.pullRequest?.head.sha ?? '' },
206
+ error: { code: 'PR_READINESS_TIMEOUT', message: 'Pull request readiness watch timed out', retryable: true }
207
+ });
208
+ await sleep(Math.min(options.intervalSeconds * 1000, options.deadlineSeconds * 1000 - elapsed));
209
+ }
187
210
  }
188
211
  function resolvePlatformCheckRun(taskRef, options) {
189
212
  const base = resolvedTask(taskRef, options);
@@ -240,5 +263,5 @@ function fetchPlatformCheckLogs(taskRef, options) {
240
263
  logs: { runId: options.run, ...(options.job ? { jobId: options.job } : {}), text: fetched.value }, error: null
241
264
  });
242
265
  }
243
- export { classifyRequiredChecks, fetchCheckLogText, fetchPlatformCheckLogs, inspectRequiredChecks, parseRunJobIdentity, resolvePlatformCheckRun, resolveRunCandidate, watchPlatformChecks, watchRequiredChecks };
266
+ export { classifyPullRequestReadiness, classifyRequiredChecks, fetchCheckLogText, fetchPlatformCheckLogs, inspectRequiredChecks, inspectPullRequestReadiness, parseRunJobIdentity, resolvePlatformCheckRun, resolveRunCandidate, watchPullRequestReadiness };
244
267
  //# sourceMappingURL=pr-checks.js.map
@@ -0,0 +1,166 @@
1
+ import { createGitHubClient } from './github-client.js';
2
+ import { resolvePlatformContext } from './context.js';
3
+ import { platformResult } from './types.js';
4
+ const REVIEW_EVENTS = ['COMMENT', 'APPROVE', 'REQUEST_CHANGES'];
5
+ // Marker-safe scope: a task id (`TASK-YYYYMMDD-HHMMSS`) or a bare PR number
6
+ // (`pr{N}`). Any other value (e.g. one containing `\r\n` or `-->`) would break
7
+ // the first-line marker idempotency contract of `reviewMarker` (PL-6).
8
+ const REVIEW_SCOPE_PATTERN = /^(?:pr\d+|TASK-\d{8}-\d{6})$/;
9
+ export function reviewMarker(identity) {
10
+ return `<!-- review-pr:${identity.scope}:r${identity.round} -->`;
11
+ }
12
+ export function reviewedCommitMarker(sha) {
13
+ return `<!-- reviewed-commit: ${sha} -->`;
14
+ }
15
+ function normalizeBody(body) {
16
+ return String(body || '').replace(/\r\n/g, '\n').replace(/\n+$/, '\n');
17
+ }
18
+ function firstLine(body) {
19
+ return normalizeBody(String(body || '')).split('\n', 1)[0] || '';
20
+ }
21
+ function hasUsableContext(context) {
22
+ return (context.status === 'no-op' || context.status === 'degraded') && context.platform.repository !== null;
23
+ }
24
+ function invalidPrNumber(prNumber) {
25
+ return !Number.isInteger(prNumber) || prNumber <= 0;
26
+ }
27
+ export function listPrReviews(prNumber, options = {}) {
28
+ const client = options.client || createGitHubClient();
29
+ const context = resolvePlatformContext({ cwd: options.cwd || process.cwd(), client });
30
+ if (!hasUsableContext(context)) {
31
+ return { ...platformResult(context.status, { platform: context.platform, capabilities: context.capabilities, error: context.error }), reviews: [] };
32
+ }
33
+ const repository = context.platform.repository;
34
+ if (invalidPrNumber(prNumber)) {
35
+ return {
36
+ ...platformResult('failed', {
37
+ platform: context.platform, capabilities: context.capabilities,
38
+ error: { code: 'PR_NUMBER_INVALID', message: 'PR number must be positive', retryable: false }
39
+ }),
40
+ reviews: []
41
+ };
42
+ }
43
+ const fetched = client.json(['api', '--paginate', '--slurp', `repos/${repository}/pulls/${prNumber}/reviews?per_page=100`], { cwd: options.cwd || process.cwd() });
44
+ if (!fetched.ok) {
45
+ return {
46
+ ...platformResult(fetched.error.retryable ? 'blocked' : 'failed', {
47
+ platform: context.platform, capabilities: context.capabilities,
48
+ resource: { kind: 'pull-request', number: prNumber }, error: fetched.error
49
+ }),
50
+ reviews: []
51
+ };
52
+ }
53
+ const flat = (fetched.value || []).flatMap((entry) => Array.isArray(entry) ? entry : [entry]);
54
+ const reviews = flat
55
+ .map((review) => ({
56
+ id: review.id ?? 0,
57
+ commitId: review.commit_id ?? '',
58
+ body: review.body ?? '',
59
+ url: review.html_url ?? ''
60
+ }))
61
+ .filter((review) => review.body !== '');
62
+ return {
63
+ ...platformResult('no-op', {
64
+ platform: context.platform, capabilities: context.capabilities,
65
+ resource: { kind: 'pull-request', number: prNumber }, error: null
66
+ }),
67
+ reviews
68
+ };
69
+ }
70
+ export function publishPrReview(options) {
71
+ const client = options.client || createGitHubClient();
72
+ const context = resolvePlatformContext({ cwd: options.cwd || process.cwd(), client });
73
+ if (!hasUsableContext(context)) {
74
+ return platformResult(context.status, { platform: context.platform, capabilities: context.capabilities, error: context.error });
75
+ }
76
+ const repository = context.platform.repository;
77
+ if (invalidPrNumber(options.prNumber)) {
78
+ return platformResult('failed', {
79
+ platform: context.platform, capabilities: context.capabilities,
80
+ error: { code: 'PR_NUMBER_INVALID', message: 'PR number must be positive', retryable: false }
81
+ });
82
+ }
83
+ if (!REVIEW_EVENTS.includes(options.event)) {
84
+ return platformResult('failed', {
85
+ platform: context.platform, capabilities: context.capabilities,
86
+ resource: { kind: 'pull-request', number: options.prNumber },
87
+ error: { code: 'REVIEW_EVENT_INVALID', message: `review event must be one of ${REVIEW_EVENTS.join('|')}`, retryable: false }
88
+ });
89
+ }
90
+ if (!REVIEW_SCOPE_PATTERN.test(options.identity.scope) || !/^[0-9a-f]{7,40}$/i.test(options.identity.commitSha)) {
91
+ return platformResult('failed', {
92
+ platform: context.platform, capabilities: context.capabilities,
93
+ resource: { kind: 'pull-request', number: options.prNumber },
94
+ error: { code: 'REVIEW_IDENTITY_INVALID', message: 'review scope must be a task id or pr{N}; commitSha is required', retryable: false }
95
+ });
96
+ }
97
+ const marker = reviewMarker(options.identity);
98
+ const listed = listPrReviews(options.prNumber, { cwd: options.cwd, client });
99
+ if (listed.status === 'failed' || listed.status === 'blocked')
100
+ return listed;
101
+ const existing = listed.reviews.find((review) => firstLine(review.body) === marker) ?? null;
102
+ if (existing) {
103
+ if (existing.commitId === options.identity.commitSha) {
104
+ return platformResult('no-op', {
105
+ platform: context.platform, capabilities: context.capabilities,
106
+ resource: { kind: 'pull-request', number: options.prNumber },
107
+ operations: [{ name: 'review:publish', status: 'no-op', reasonCode: null }], error: null
108
+ });
109
+ }
110
+ return platformResult('failed', {
111
+ platform: context.platform, capabilities: context.capabilities,
112
+ resource: { kind: 'pull-request', number: options.prNumber },
113
+ operations: [{ name: 'review:publish', status: 'failed', reasonCode: 'REVIEW_MARKER_CONFLICT' }],
114
+ error: { code: 'REVIEW_MARKER_CONFLICT', message: 'A review with the same marker targets a different commit; start a new round', retryable: false }
115
+ });
116
+ }
117
+ const wrappedBody = [
118
+ marker,
119
+ reviewedCommitMarker(options.identity.commitSha),
120
+ '',
121
+ String(options.body || '').replace(/\r?\n/g, '\n').replace(/\n+$/, ''),
122
+ ''
123
+ ].join('\n');
124
+ if (options.dryRun) {
125
+ return platformResult('planned', {
126
+ platform: context.platform, capabilities: context.capabilities,
127
+ resource: { kind: 'pull-request', number: options.prNumber },
128
+ operations: [{ name: 'review:publish', status: 'planned', reasonCode: null }], error: null
129
+ });
130
+ }
131
+ const posted = client.json(['api', `repos/${repository}/pulls/${options.prNumber}/reviews`, '-X', 'POST', '--input', '-'], {
132
+ cwd: options.cwd || process.cwd(),
133
+ method: 'POST',
134
+ input: JSON.stringify({ commit_id: options.identity.commitSha, body: wrappedBody, event: options.event })
135
+ });
136
+ if (!posted.ok) {
137
+ if (posted.error.retryable) {
138
+ const reconciled = listPrReviews(options.prNumber, { cwd: options.cwd, client });
139
+ const found = reconciled.reviews.find((review) => firstLine(review.body) === marker);
140
+ if (found) {
141
+ return platformResult('applied', {
142
+ changed: true, platform: context.platform, capabilities: context.capabilities,
143
+ resource: { kind: 'pull-request', number: options.prNumber },
144
+ operations: [{ name: 'review:publish', status: 'applied', reasonCode: 'CREATE_RECONCILED' }], error: null
145
+ });
146
+ }
147
+ return platformResult('blocked', {
148
+ platform: context.platform, capabilities: context.capabilities,
149
+ resource: { kind: 'pull-request', number: options.prNumber },
150
+ operations: [{ name: 'review:publish', status: 'failed', reasonCode: 'REVIEW_CREATE_OUTCOME_UNKNOWN' }],
151
+ error: { code: 'REVIEW_CREATE_OUTCOME_UNKNOWN', message: posted.error.message, retryable: true }
152
+ });
153
+ }
154
+ return platformResult('failed', {
155
+ platform: context.platform, capabilities: context.capabilities,
156
+ resource: { kind: 'pull-request', number: options.prNumber },
157
+ operations: [{ name: 'review:publish', status: 'failed', reasonCode: posted.error.code }], error: posted.error
158
+ });
159
+ }
160
+ return platformResult('applied', {
161
+ changed: true, platform: context.platform, capabilities: context.capabilities,
162
+ resource: { kind: 'pull-request', number: options.prNumber },
163
+ operations: [{ name: 'review:publish', status: 'applied', reasonCode: null }], error: null
164
+ });
165
+ }
166
+ //# sourceMappingURL=pr-review.js.map