@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
@@ -0,0 +1,417 @@
1
+ import fs from 'node:fs';
2
+
3
+ import { normalizeAgentToken } from '../agent-clients/tokens.ts';
4
+ import { resolveTaskRef } from './resolve-ref.ts';
5
+ import { locateActivityLog, appendActivityEntry } from './activity-log.ts';
6
+ import {
7
+ assertWritableInventory,
8
+ buildArtifactLinkSection,
9
+ inspectTaskArtifacts,
10
+ parseArtifactName
11
+ } from './artifact-lifecycle.ts';
12
+ import type { ArtifactIdentity } from './artifact-lifecycle.ts';
13
+ import { TaskExecutionLockError, withTaskExecutionLock } from './task-execution-lock.ts';
14
+ import type { TaskExecutionLockOptions } from './task-execution-lock.ts';
15
+ import { captureTaskWriteMetadata, writeTask } from './write.ts';
16
+ import type { TaskMutation, TaskOperationSummary, TaskWriteOptions } from './write.ts';
17
+
18
+ type PrReviewVerdict = 'approved' | 'changes-requested' | 'commented';
19
+ type PrReviewOutcome = 'aborted' | 'superseded';
20
+ type PrReviewArtifactStatus = 'pending' | 'applied' | 'no-op' | 'blocked' | 'failed' | 'aborted' | 'superseded';
21
+
22
+ export type PrReviewInspectIntent = {
23
+ kind: 'pr-review-inspect';
24
+ taskRef: string;
25
+ };
26
+
27
+ export type PrReviewStartIntent = {
28
+ kind: 'pr-review-start';
29
+ taskRef: string;
30
+ agent: string;
31
+ artifact: string;
32
+ head: string;
33
+ dryRun?: boolean;
34
+ };
35
+
36
+ export type PrReviewCompleteIntent = {
37
+ kind: 'pr-review-complete';
38
+ taskRef: string;
39
+ agent: string;
40
+ artifact: string;
41
+ head: string;
42
+ verdict: PrReviewVerdict;
43
+ blockers: number;
44
+ major: number;
45
+ minor: number;
46
+ dryRun?: boolean;
47
+ };
48
+
49
+ export type PrReviewTerminateIntent = {
50
+ kind: 'pr-review-terminate';
51
+ taskRef: string;
52
+ agent: string;
53
+ artifact: string;
54
+ head: string;
55
+ outcome: PrReviewOutcome;
56
+ reason: string;
57
+ dryRun?: boolean;
58
+ };
59
+
60
+ export type PrReviewActivityIntent = PrReviewStartIntent | PrReviewCompleteIntent | PrReviewTerminateIntent;
61
+
62
+ type PrReviewIdentity = {
63
+ round: number;
64
+ artifact: string;
65
+ head: string;
66
+ agent: string | null;
67
+ artifactStatus: PrReviewArtifactStatus;
68
+ note: string | null;
69
+ };
70
+
71
+ export type PrReviewActivitySnapshot = {
72
+ nextRound: number;
73
+ prepared: PrReviewIdentity | null;
74
+ open: PrReviewIdentity | null;
75
+ latestTerminal: PrReviewIdentity | null;
76
+ latestSuccessful: PrReviewIdentity | null;
77
+ };
78
+
79
+ export type PrReviewActivityResult = {
80
+ status: 'planned' | 'applied' | 'no-op' | 'failed';
81
+ changed: boolean;
82
+ intent: PrReviewActivityIntent['kind'];
83
+ taskId: string | null;
84
+ artifact: string | null;
85
+ operations: readonly TaskOperationSummary[];
86
+ error: { code: string; message: string } | null;
87
+ };
88
+
89
+ export type PrReviewInspectionResult = {
90
+ status: 'ready' | 'failed';
91
+ changed: false;
92
+ intent: 'pr-review-inspect';
93
+ taskId: string | null;
94
+ snapshot: PrReviewActivitySnapshot | null;
95
+ error: { code: string; message: string } | null;
96
+ };
97
+
98
+ type ActivityIntentOptions = TaskWriteOptions & { lockOptions?: TaskExecutionLockOptions };
99
+ type ArtifactRecord = { identity: ArtifactIdentity; head: string; status: PrReviewArtifactStatus };
100
+ type RoundState = ArtifactRecord & {
101
+ startedAgent: string | null;
102
+ terminalAgent: string | null;
103
+ terminalNote: string | null;
104
+ };
105
+
106
+ const HEAD_RE = /^[0-9a-f]{40}$/;
107
+ const REVIEW_ACTION_RE = /^Review PR \(Round ([1-9]\d*)\)( \[started\])?$/;
108
+ const VERDICTS: ReadonlySet<string> = new Set(['approved', 'changes-requested', 'commented']);
109
+ const OUTCOMES: ReadonlySet<string> = new Set(['aborted', 'superseded']);
110
+ const SUCCESS_STATUSES: ReadonlySet<PrReviewArtifactStatus> = new Set(['applied', 'no-op']);
111
+ const ARTIFACT_STATUSES: ReadonlySet<string> = new Set([
112
+ 'pending', 'applied', 'no-op', 'blocked', 'failed', 'aborted', 'superseded'
113
+ ]);
114
+
115
+ function failed(
116
+ intent: PrReviewActivityIntent,
117
+ code: string,
118
+ message: string,
119
+ taskId: string | null = null
120
+ ): PrReviewActivityResult {
121
+ return {
122
+ status: 'failed', changed: false, intent: intent.kind, taskId,
123
+ artifact: intent.artifact, operations: [], error: { code, message }
124
+ };
125
+ }
126
+
127
+ function inspectionFailed(code: string, message: string, taskId: string | null = null): PrReviewInspectionResult {
128
+ return {
129
+ status: 'failed', changed: false, intent: 'pr-review-inspect', taskId,
130
+ snapshot: null, error: { code, message }
131
+ };
132
+ }
133
+
134
+ function parseArtifactRecord(identity: ArtifactIdentity): ArtifactRecord | { code: string; message: string } {
135
+ let content: string;
136
+ try {
137
+ content = fs.readFileSync(identity.path, 'utf8');
138
+ } catch (error) {
139
+ return { code: 'ACTIVITY_ARTIFACT_READ_FAILED', message: error instanceof Error ? error.message : String(error) };
140
+ }
141
+ const heads = [...content.matchAll(/^- \*\*(?:被审 head SHA|Reviewed Head SHA)\*\*[::]\s*([0-9a-f]{40})\s*$/gm)];
142
+ if (heads.length !== 1) {
143
+ return { code: 'ACTIVITY_ARTIFACT_INVALID', message: `${identity.name} must contain exactly one reviewed head SHA` };
144
+ }
145
+ const statuses = [...content.matchAll(/^- \*\*(?:正式 Review 状态|Formal Review Status)\*\*[::][ \t]*([a-z-]+)[^\r\n]*$/gm)];
146
+ const status = statuses[0]?.[1];
147
+ if (statuses.length !== 1 || !status || !ARTIFACT_STATUSES.has(status)) {
148
+ return { code: 'ACTIVITY_ARTIFACT_INVALID', message: `${identity.name} must contain exactly one supported formal review status` };
149
+ }
150
+ return { identity, head: heads[0]![1]!, status: status as PrReviewArtifactStatus };
151
+ }
152
+
153
+ function toIdentity(state: RoundState, terminal = false): PrReviewIdentity {
154
+ return {
155
+ round: state.identity.round,
156
+ artifact: state.identity.name,
157
+ head: state.head,
158
+ agent: terminal ? state.terminalAgent : state.startedAgent,
159
+ artifactStatus: state.status,
160
+ note: terminal ? state.terminalNote : null
161
+ };
162
+ }
163
+
164
+ function readSnapshot(taskRef: string, repoRoot?: string):
165
+ | { ok: true; taskId: string; taskDir: string; taskMdPath: string; repoRoot: string; content: string; states: RoundState[]; snapshot: PrReviewActivitySnapshot }
166
+ | { ok: false; code: string; message: string; taskId: string | null } {
167
+ const resolved = resolveTaskRef(taskRef, { repoRoot });
168
+ if (!resolved.ok) return { ok: false, code: resolved.code, message: resolved.message, taskId: resolved.taskId };
169
+ if (resolved.state !== 'active') {
170
+ return { ok: false, code: 'TASK_STATE_MISMATCH', message: `task ${resolved.taskId} is ${resolved.state}, expected active`, taskId: resolved.taskId };
171
+ }
172
+ const inventory = inspectTaskArtifacts(taskRef, 'pr-review', { repoRoot: resolved.repoRoot });
173
+ if (inventory.status === 'failed') {
174
+ return { ok: false, code: inventory.error?.code ?? 'ACTIVITY_ARTIFACT_INVALID', message: inventory.error?.message ?? 'artifact inspection failed', taskId: resolved.taskId };
175
+ }
176
+ const topology = assertWritableInventory(inventory);
177
+ if (topology) return { ok: false, code: topology.code, message: topology.message, taskId: resolved.taskId };
178
+
179
+ let content: string;
180
+ try {
181
+ content = fs.readFileSync(resolved.taskMdPath, 'utf8');
182
+ } catch (error) {
183
+ return { ok: false, code: 'TASK_READ_FAILED', message: error instanceof Error ? error.message : String(error), taskId: resolved.taskId };
184
+ }
185
+ const activity = locateActivityLog(content);
186
+ if (!activity) return { ok: false, code: 'ACTIVITY_SECTION_MISSING', message: 'task has no unique Activity Log section', taskId: resolved.taskId };
187
+
188
+ const knownRounds = new Set(inventory.artifacts.map((artifact) => artifact.round));
189
+ for (const entry of activity.entries) {
190
+ const match = REVIEW_ACTION_RE.exec(entry.step);
191
+ if (match && !knownRounds.has(Number(match[1]))) {
192
+ return { ok: false, code: 'ACTIVITY_STATE_CONFLICT', message: `Activity Log round ${match[1]} has no canonical pr-review artifact`, taskId: resolved.taskId };
193
+ }
194
+ }
195
+
196
+ const states: RoundState[] = [];
197
+ for (const identity of inventory.artifacts) {
198
+ const parsed = parseArtifactRecord(identity);
199
+ if ('code' in parsed) return { ok: false, ...parsed, taskId: resolved.taskId };
200
+ const base = `Review PR (Round ${identity.round})`;
201
+ const started = activity.entries.filter((entry) => entry.step === `${base} [started]`);
202
+ const terminal = activity.entries.filter((entry) => entry.step === base);
203
+ if (started.length > 1 || terminal.length > 1) {
204
+ return { ok: false, code: 'ACTIVITY_STATE_CONFLICT', message: `${base} has duplicate started or terminal rows`, taskId: resolved.taskId };
205
+ }
206
+ if (terminal.length === 1 && started.length === 0 && !terminal[0]!.note.startsWith('receipt ')) {
207
+ return { ok: false, code: 'ACTIVITY_STATE_CONFLICT', message: `${base} has a terminal row without a matching started row`, taskId: resolved.taskId };
208
+ }
209
+ if (terminal.length === 1) {
210
+ const note = terminal[0]!.note;
211
+ const terminalMatchesStatus =
212
+ (note.startsWith('Verdict: ') && SUCCESS_STATUSES.has(parsed.status))
213
+ || (note.startsWith('Outcome: Aborted, ') && parsed.status === 'aborted')
214
+ || (note.startsWith('Outcome: Superseded, ') && parsed.status === 'superseded')
215
+ || (note.startsWith('receipt ') && SUCCESS_STATUSES.has(parsed.status));
216
+ if (!terminalMatchesStatus) {
217
+ return { ok: false, code: 'ACTIVITY_STATE_CONFLICT', message: `${base} terminal note conflicts with artifact status ${parsed.status}`, taskId: resolved.taskId };
218
+ }
219
+ }
220
+ states.push({
221
+ ...parsed,
222
+ startedAgent: started[0]?.agent ?? null,
223
+ terminalAgent: terminal[0]?.agent ?? null,
224
+ terminalNote: terminal[0]?.note ?? null
225
+ });
226
+ }
227
+
228
+ const openStates = states.filter((state) => state.startedAgent !== null && state.terminalNote === null);
229
+ const preparedStates = states.filter((state) => state.status === 'pending' && state.startedAgent === null && state.terminalNote === null);
230
+ if (openStates.length > 1 || preparedStates.length > 1) {
231
+ return { ok: false, code: 'ACTIVITY_STATE_CONFLICT', message: 'multiple prepared or open PR review rounds exist', taskId: resolved.taskId };
232
+ }
233
+ const terminalStates = states.filter((state) => state.terminalNote !== null);
234
+ const successfulStates = states.filter((state) => SUCCESS_STATUSES.has(state.status));
235
+ const latestTerminal = terminalStates.at(-1) ?? null;
236
+ const latestSuccessful = successfulStates.at(-1) ?? null;
237
+ return {
238
+ ok: true,
239
+ taskId: resolved.taskId,
240
+ taskDir: resolved.taskDir,
241
+ taskMdPath: resolved.taskMdPath,
242
+ repoRoot: resolved.repoRoot,
243
+ content,
244
+ states,
245
+ snapshot: {
246
+ nextRound: inventory.next?.round ?? 1,
247
+ prepared: preparedStates[0] ? toIdentity(preparedStates[0]) : null,
248
+ open: openStates[0] ? toIdentity(openStates[0]) : null,
249
+ latestTerminal: latestTerminal ? toIdentity(latestTerminal, true) : null,
250
+ latestSuccessful: latestSuccessful ? toIdentity(latestSuccessful, true) : null
251
+ }
252
+ };
253
+ }
254
+
255
+ export function inspectPrReviewActivity(intent: PrReviewInspectIntent, options: Pick<ActivityIntentOptions, 'repoRoot'> = {}): PrReviewInspectionResult {
256
+ const state = readSnapshot(intent.taskRef, options.repoRoot);
257
+ if (!state.ok) return inspectionFailed(state.code, state.message, state.taskId);
258
+ return { status: 'ready', changed: false, intent: intent.kind, taskId: state.taskId, snapshot: state.snapshot, error: null };
259
+ }
260
+
261
+ function validateIntent(intent: PrReviewActivityIntent): { agent: string; artifact: { round: number; name: string } } | { code: string; message: string } {
262
+ const agent = normalizeAgentToken(intent.agent);
263
+ if (!agent) return { code: 'ACTIVITY_INTENT_INVALID', message: 'agent must be a supported collaborator token' };
264
+ const artifact = parseArtifactName(intent.artifact);
265
+ if (!artifact || artifact.family !== 'pr-review') {
266
+ return { code: 'ACTIVITY_ARTIFACT_INVALID', message: `artifact '${intent.artifact}' is not a canonical pr-review artifact` };
267
+ }
268
+ if (!HEAD_RE.test(intent.head)) return { code: 'ACTIVITY_INTENT_INVALID', message: 'head must be a lowercase 40-character SHA' };
269
+ if (intent.kind === 'pr-review-complete') {
270
+ if (!VERDICTS.has(intent.verdict)) return { code: 'ACTIVITY_INTENT_INVALID', message: 'verdict is invalid' };
271
+ if (![intent.blockers, intent.major, intent.minor].every((value) => Number.isSafeInteger(value) && value >= 0)) {
272
+ return { code: 'ACTIVITY_INTENT_INVALID', message: 'finding counts must be non-negative safe integers' };
273
+ }
274
+ }
275
+ if (intent.kind === 'pr-review-terminate') {
276
+ if (!OUTCOMES.has(intent.outcome)) return { code: 'ACTIVITY_INTENT_INVALID', message: 'outcome is invalid' };
277
+ if (!intent.reason.trim() || /[\r\n]/.test(intent.reason) || intent.reason !== intent.reason.trim()) {
278
+ return { code: 'ACTIVITY_INTENT_INVALID', message: 'reason must be a non-empty trimmed single-line value' };
279
+ }
280
+ }
281
+ return { agent, artifact };
282
+ }
283
+
284
+ function verdictLabel(verdict: PrReviewVerdict): string {
285
+ if (verdict === 'approved') return 'Approved';
286
+ if (verdict === 'changes-requested') return 'Changes Requested';
287
+ return 'Commented';
288
+ }
289
+
290
+ function outcomeLabel(outcome: PrReviewOutcome): string {
291
+ return outcome === 'aborted' ? 'Aborted' : 'Superseded';
292
+ }
293
+
294
+ function terminalNote(intent: PrReviewCompleteIntent | PrReviewTerminateIntent): string {
295
+ if (intent.kind === 'pr-review-complete') {
296
+ return `Verdict: ${verdictLabel(intent.verdict)}, blockers: ${intent.blockers}, major: ${intent.major}, minor: ${intent.minor} → ${intent.artifact}`;
297
+ }
298
+ return `Outcome: ${outcomeLabel(intent.outcome)}, reason: ${intent.reason} → ${intent.artifact}`;
299
+ }
300
+
301
+ function applyLocked(intent: PrReviewActivityIntent, normalizedAgent: string, options: ActivityIntentOptions): PrReviewActivityResult {
302
+ const state = readSnapshot(intent.taskRef, options.repoRoot);
303
+ if (!state.ok) return failed(intent, state.code, state.message, state.taskId);
304
+ const parsed = parseArtifactName(intent.artifact)!;
305
+ const roundState = state.states.find((item) => item.identity.name === parsed.name);
306
+ if (!roundState) return failed(intent, 'ACTIVITY_ARTIFACT_INVALID', `artifact '${intent.artifact}' is not landed`, state.taskId);
307
+ if (roundState.head !== intent.head) {
308
+ return failed(intent, 'ACTIVITY_IDENTITY_MISMATCH', `head does not match ${intent.artifact}`, state.taskId);
309
+ }
310
+ const base = `Review PR (Round ${parsed.round})`;
311
+ let step: string;
312
+ let note: string;
313
+ let linkArtifact = false;
314
+
315
+ if (intent.kind === 'pr-review-start') {
316
+ if (roundState.terminalNote !== null) {
317
+ return failed(intent, 'ACTIVITY_STATE_CONFLICT', `${base} is already terminal`, state.taskId);
318
+ }
319
+ if (roundState.startedAgent !== null) {
320
+ return { status: 'no-op', changed: false, intent: intent.kind, taskId: state.taskId, artifact: intent.artifact, operations: [], error: null };
321
+ }
322
+ if (roundState.status !== 'pending') {
323
+ return failed(intent, 'ACTIVITY_STATE_CONFLICT', `${intent.artifact} must be pending before start`, state.taskId);
324
+ }
325
+ if (state.snapshot.open && state.snapshot.open.artifact !== intent.artifact) {
326
+ return failed(intent, 'ACTIVITY_STATE_CONFLICT', `another PR review round is open: ${state.snapshot.open.artifact}`, state.taskId);
327
+ }
328
+ step = `${base} [started]`;
329
+ note = 'started';
330
+ } else {
331
+ note = terminalNote(intent);
332
+ if (roundState.terminalNote !== null) {
333
+ if (roundState.terminalNote === note) {
334
+ return { status: 'no-op', changed: false, intent: intent.kind, taskId: state.taskId, artifact: intent.artifact, operations: [], error: null };
335
+ }
336
+ return failed(intent, 'ACTIVITY_STATE_CONFLICT', `${base} has a different terminal payload`, state.taskId);
337
+ }
338
+ if (roundState.startedAgent === null) {
339
+ return failed(intent, 'ACTIVITY_STATE_CONFLICT', `${base} has no matching started row`, state.taskId);
340
+ }
341
+ if (intent.kind === 'pr-review-complete' && !SUCCESS_STATUSES.has(roundState.status)) {
342
+ return failed(intent, 'ACTIVITY_STATE_CONFLICT', `${intent.artifact} must be applied or no-op before completion`, state.taskId);
343
+ }
344
+ if (intent.kind === 'pr-review-terminate' && roundState.status !== intent.outcome) {
345
+ return failed(intent, 'ACTIVITY_STATE_CONFLICT', `${intent.artifact} status must match outcome ${intent.outcome}`, state.taskId);
346
+ }
347
+ step = base;
348
+ linkArtifact = true;
349
+ }
350
+
351
+ let metadata;
352
+ try {
353
+ metadata = (options.metadataProvider ?? captureTaskWriteMetadata)();
354
+ } catch (error) {
355
+ return failed(intent, 'METADATA_CAPTURE_FAILED', error instanceof Error ? error.message : String(error), state.taskId);
356
+ }
357
+ const section = locateActivityLog(state.content)!;
358
+ const mutations: TaskMutation[] = [{
359
+ kind: 'section', aliases: ['活动日志', 'Activity Log'], heading: section.heading,
360
+ body: appendActivityEntry(section, { time: metadata.timestamp, step, agent: normalizedAgent, note })
361
+ }];
362
+ if (linkArtifact) {
363
+ const link = buildArtifactLinkSection(state.content, roundState.identity);
364
+ mutations.push({ kind: 'section', aliases: link.aliases, heading: link.heading, body: link.body });
365
+ }
366
+ const writeResult = writeTask({
367
+ taskRef: intent.taskRef,
368
+ expectedState: 'active',
369
+ mutations,
370
+ dryRun: intent.dryRun
371
+ }, {
372
+ ...options,
373
+ taskLocation: {
374
+ repoRoot: state.repoRoot,
375
+ taskId: state.taskId,
376
+ taskMdPath: state.taskMdPath,
377
+ state: 'active'
378
+ },
379
+ metadataProvider: () => metadata
380
+ });
381
+ if (writeResult.status === 'failed') {
382
+ return failed(intent, writeResult.error.code, writeResult.error.message, writeResult.taskId);
383
+ }
384
+ return {
385
+ status: writeResult.status,
386
+ changed: writeResult.changed,
387
+ intent: intent.kind,
388
+ taskId: writeResult.taskId,
389
+ artifact: intent.artifact,
390
+ operations: writeResult.operations,
391
+ error: null
392
+ };
393
+ }
394
+
395
+ export function applyPrReviewActivityIntent(intent: PrReviewActivityIntent, options: ActivityIntentOptions = {}): PrReviewActivityResult {
396
+ const validated = validateIntent(intent);
397
+ if ('code' in validated) return failed(intent, validated.code, validated.message);
398
+ const resolved = resolveTaskRef(intent.taskRef, { repoRoot: options.repoRoot });
399
+ if (!resolved.ok) return failed(intent, resolved.code, resolved.message, resolved.taskId);
400
+ if (resolved.state !== 'active') {
401
+ return failed(intent, 'TASK_STATE_MISMATCH', `task ${resolved.taskId} is ${resolved.state}, expected active`, resolved.taskId);
402
+ }
403
+ try {
404
+ return withTaskExecutionLock(
405
+ resolved.repoRoot,
406
+ resolved.taskId,
407
+ `task-activity:${intent.kind}`,
408
+ () => applyLocked(intent, validated.agent, { ...options, repoRoot: resolved.repoRoot }),
409
+ options.lockOptions
410
+ );
411
+ } catch (error) {
412
+ if (error instanceof TaskExecutionLockError) return failed(intent, error.code, error.message, resolved.taskId);
413
+ return failed(intent, 'ACTIVITY_LOCK_FAILED', error instanceof Error ? error.message : String(error), resolved.taskId);
414
+ }
415
+ }
416
+
417
+ export type { ActivityIntentOptions };
@@ -15,7 +15,8 @@ const artifactFamilyCatalog = [
15
15
  { family: 'review-plan', sectionAliases: ['审查反馈', 'Review Feedback'], heading: '审查反馈', labels: ['技术方案审查', 'Plan Review'] },
16
16
  { family: 'code', sectionAliases: ['实现备注', 'Implementation Notes'], heading: '实现备注', labels: ['实现报告', 'Implementation Report'] },
17
17
  { family: 'review-code', sectionAliases: ['审查反馈', 'Review Feedback'], heading: '审查反馈', labels: ['代码审查', 'Code Review'] },
18
- { family: 'manual-validation', sectionAliases: ['实现备注', 'Implementation Notes'], heading: '实现备注', labels: ['人工验证', 'Manual Validation'] }
18
+ { family: 'manual-validation', sectionAliases: ['实现备注', 'Implementation Notes'], heading: '实现备注', labels: ['人工验证', 'Manual Validation'] },
19
+ { family: 'pr-review', sectionAliases: ['审查反馈', 'Review Feedback'], heading: '审查反馈', labels: ['PR 审查报告', 'PR Review Report'] }
19
20
  ] as const;
20
21
 
21
22
  type ArtifactFamily = (typeof artifactFamilyCatalog)[number]['family'];
@@ -1,4 +1,5 @@
1
1
  import fs from 'node:fs';
2
+ import { classifyAgent } from '../../agent-clients/tokens.ts';
2
3
  import { formatTable } from '../../table.ts';
3
4
  import { parseTaskScope } from '../command-options.ts';
4
5
  import { resolveTaskContext } from '../resolve-ref.ts';
@@ -34,11 +35,11 @@ const TABLE_HEADERS = ['#', 'STEP', 'AGENT', 'STARTED', 'DONE', 'NOTE'] as const
34
35
  // with ` [started]`; the matching done entry carries the identical base action
35
36
  // without the suffix. Pairing therefore keys on the base action (including any
36
37
  // `(Round N)`), so every round and every repeated execution pairs on its own.
37
- // Short agent tokens that actually appear in activity logs for AI executors
38
- // (workflow recommended_agents claude/codex/gemini/cursor + enabled TUI opencode;
39
- // note these differ from the `.airc.json` long names claude-code/gemini-cli).
40
- // Any other executor token (a human name, possibly CJK) is treated as human.
41
- const KNOWN_AI_AGENTS = new Set(['claude', 'codex', 'gemini', 'opencode', 'cursor']);
38
+ // Known AI tokens, long-name mapping, and loose rendering classification live
39
+ // in lib/agent-clients/tokens.ts (single source of truth shared with the
40
+ // write-side internal commands). Any other executor token (a human name,
41
+ // possibly CJK, or an unknown value) is rendered as human with a visible
42
+ // `(unknown)` marker.
42
43
  const REVIEW_STAGE_PREFIXES: { prefix: string; stage: ReviewStage }[] = [
43
44
  { prefix: 'Review Analysis', stage: 'analysis' },
44
45
  { prefix: 'Review Plan', stage: 'plan' },
@@ -90,12 +91,11 @@ function isHumanCountField(field: string): boolean {
90
91
  return fieldNumber(field, 'manual-validation') !== undefined || fieldNumber(field, 'human-decision') !== undefined;
91
92
  }
92
93
 
93
- // A step is human-executed when its agent token is not a known AI token. Take
94
- // the first whitespace-delimited token and drop any trailing parenthetical
95
- // annotation (e.g. `张三 (executed on host)` -> `张三`) before the lookup.
96
- function isHumanAgent(agent: string): boolean {
97
- const token = agent.trim().split(/\s+/)[0]?.replace(/\(.*$/, '') ?? '';
98
- return token !== '' && !KNOWN_AI_AGENTS.has(token);
94
+ // A step is human-executed when its agent token is not a known AI token.
95
+ // Known long names (claude-code -> claude, gemini-cli -> gemini) classify as
96
+ // AI; empty or unknown tokens classify as non-AI (human).
97
+ export function isHumanAgent(agent: string): boolean {
98
+ return classifyAgent(agent).status !== 'ai';
99
99
  }
100
100
 
101
101
  // Fold the two human counts into a review row's verdict NOTE: comma-joined, right
@@ -152,8 +152,9 @@ function log(args: string[] = []): void {
152
152
  const note = stage
153
153
  ? foldHumanCounts(s.note, humanDecisionCounts.get(stage) ?? 0, humanValidationCount(s.note))
154
154
  : s.note;
155
- const human = isHumanAgent(s.agent);
156
- const agent = human ? 'human' : s.agent;
155
+ const { status, display } = classifyAgent(s.agent);
156
+ const human = status !== 'ai';
157
+ const agent = status === 'unknown' ? `${display} (unknown)` : display;
157
158
  const started = s.started || (human ? '-' : '');
158
159
  return [String(idx + 1), s.step, agent, started, s.done || (s.started ? '(in progress)' : ''), note];
159
160
  });
@@ -163,4 +164,4 @@ function log(args: string[] = []): void {
163
164
  process.stdout.write(`Total: ${steps.length} steps\n`);
164
165
  }
165
166
 
166
- export { log, isHumanAgent };
167
+ export { log };