@fitlab-ai/agent-infra 0.9.2 → 0.9.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (318) hide show
  1. package/README.md +10 -9
  2. package/README.zh-CN.md +10 -9
  3. package/dist/bin/internal-cli.js +15 -0
  4. package/dist/lib/agent-clients/adapter.js +45 -2
  5. package/dist/lib/agent-clients/adapters/antigravity-cli.js +64 -0
  6. package/dist/lib/agent-clients/adapters/claude-code.js +10 -1
  7. package/dist/lib/agent-clients/adapters/codex.js +9 -0
  8. package/dist/lib/agent-clients/adapters/index.js +2 -2
  9. package/dist/lib/agent-clients/adapters/opencode.js +9 -0
  10. package/dist/lib/agent-clients/config.js +42 -4
  11. package/dist/lib/agent-clients/next-steps.js +11 -2
  12. package/dist/lib/agent-clients/reconcile.js +2 -1
  13. package/dist/lib/agent-clients/registry.js +7 -1
  14. package/dist/lib/agent-clients/schema.js +18 -0
  15. package/dist/lib/agent-clients/tokens.js +49 -0
  16. package/dist/lib/agent-clients/types.js +1 -1
  17. package/dist/lib/defaults.json +1 -1
  18. package/dist/lib/git/workflow.js +54 -1
  19. package/dist/lib/internal/agent-client.js +61 -4
  20. package/dist/lib/internal/git-workflow.js +6 -1
  21. package/dist/lib/internal/platform-checks.js +3 -3
  22. package/dist/lib/internal/platform-comment.js +29 -2
  23. package/dist/lib/internal/platform-issue.js +7 -0
  24. package/dist/lib/internal/platform-pr-review.js +132 -0
  25. package/dist/lib/internal/platform-pr.js +20 -2
  26. package/dist/lib/internal/platform-release-notes.js +52 -16
  27. package/dist/lib/internal/pr-review-grade.js +140 -0
  28. package/dist/lib/internal/release-workflow.js +13 -6
  29. package/dist/lib/internal/task-activity.js +73 -0
  30. package/dist/lib/internal/task-event.js +17 -1
  31. package/dist/lib/internal/task-lifecycle.js +7 -0
  32. package/dist/lib/internal/task-orchestration.js +51 -6
  33. package/dist/lib/internal/task-review.js +8 -3
  34. package/dist/lib/platform/adapters.js +8 -1
  35. package/dist/lib/platform/completion-backfill.js +122 -0
  36. package/dist/lib/platform/issue-comments.js +3 -2
  37. package/dist/lib/platform/merged-pr-equivalence.js +115 -5
  38. package/dist/lib/platform/pr-checks.js +64 -41
  39. package/dist/lib/platform/pr-review.js +166 -0
  40. package/dist/lib/platform/pull-requests.js +324 -3
  41. package/dist/lib/platform/release-notes.js +85 -3
  42. package/dist/lib/pr-review/evidence-grading.js +229 -0
  43. package/dist/lib/run/tui.js +3 -5
  44. package/dist/lib/sandbox/commands/create.js +0 -20
  45. package/dist/lib/sandbox/commands/show.js +1 -1
  46. package/dist/lib/task/activity-intent.js +101 -0
  47. package/dist/lib/task/artifact-lifecycle.js +2 -1
  48. package/dist/lib/task/commands/log.js +15 -14
  49. package/dist/lib/task/delegation-receipts.js +23 -2
  50. package/dist/lib/task/events.js +67 -28
  51. package/dist/lib/task/finalization-artifacts.js +35 -0
  52. package/dist/lib/task/lifecycle-execution.js +46 -0
  53. package/dist/lib/task/orchestration.js +408 -44
  54. package/dist/lib/task/review-finalization.js +29 -9
  55. package/dist/lib/task/task-execution-lock.js +231 -0
  56. package/dist/lib/task/verification-checks.js +1 -1
  57. package/dist/lib/task/verification-engine.js +218 -2
  58. package/dist/lib/task/verification.js +4 -3
  59. package/dist/lib/task/workspace-snapshot.js +17 -5
  60. package/lib/agent-clients/adapter.ts +75 -2
  61. package/lib/agent-clients/adapters/antigravity-cli.ts +65 -0
  62. package/lib/agent-clients/adapters/claude-code.ts +10 -1
  63. package/lib/agent-clients/adapters/codex.ts +9 -0
  64. package/lib/agent-clients/adapters/index.ts +2 -2
  65. package/lib/agent-clients/adapters/opencode.ts +9 -0
  66. package/lib/agent-clients/config.ts +49 -4
  67. package/lib/agent-clients/next-steps.ts +15 -2
  68. package/lib/agent-clients/reconcile.ts +2 -1
  69. package/lib/agent-clients/registry.ts +10 -0
  70. package/lib/agent-clients/schema.ts +18 -0
  71. package/lib/agent-clients/tokens.ts +53 -0
  72. package/lib/agent-clients/types.ts +15 -1
  73. package/lib/defaults.json +1 -1
  74. package/lib/git/workflow.ts +66 -2
  75. package/lib/internal/agent-client.ts +59 -4
  76. package/lib/internal/git-workflow.ts +5 -1
  77. package/lib/internal/platform-checks.ts +4 -4
  78. package/lib/internal/platform-comment.ts +17 -2
  79. package/lib/internal/platform-issue.ts +4 -0
  80. package/lib/internal/platform-pr-review.ts +104 -0
  81. package/lib/internal/platform-pr.ts +14 -1
  82. package/lib/internal/platform-release-notes.ts +59 -16
  83. package/lib/internal/pr-review-grade.ts +130 -0
  84. package/lib/internal/release-workflow.ts +13 -6
  85. package/lib/internal/task-activity.ts +55 -0
  86. package/lib/internal/task-event.ts +9 -1
  87. package/lib/internal/task-lifecycle.ts +4 -0
  88. package/lib/internal/task-orchestration.ts +45 -6
  89. package/lib/internal/task-review.ts +8 -3
  90. package/lib/platform/adapters.ts +29 -2
  91. package/lib/platform/completion-backfill.ts +134 -0
  92. package/lib/platform/issue-comments.ts +3 -2
  93. package/lib/platform/merged-pr-equivalence.ts +127 -5
  94. package/lib/platform/pr-checks.ts +72 -45
  95. package/lib/platform/pr-review.ts +201 -0
  96. package/lib/platform/pull-requests.ts +395 -2
  97. package/lib/platform/release-notes.ts +100 -4
  98. package/lib/pr-review/evidence-grading.ts +318 -0
  99. package/lib/run/tui.ts +3 -4
  100. package/lib/sandbox/commands/create.ts +0 -20
  101. package/lib/sandbox/commands/show.ts +1 -1
  102. package/lib/server/server.schema.json +2 -2
  103. package/lib/task/activity-intent.ts +128 -0
  104. package/lib/task/artifact-lifecycle.ts +2 -1
  105. package/lib/task/commands/log.ts +15 -14
  106. package/lib/task/delegation-receipts.ts +32 -3
  107. package/lib/task/events.ts +75 -30
  108. package/lib/task/finalization-artifacts.ts +58 -0
  109. package/lib/task/lifecycle-execution.ts +106 -0
  110. package/lib/task/orchestration.ts +543 -64
  111. package/lib/task/review-finalization.ts +40 -9
  112. package/lib/task/task-execution-lock.ts +296 -0
  113. package/lib/task/verification-checks.ts +1 -1
  114. package/lib/task/verification-engine.ts +249 -2
  115. package/lib/task/verification.ts +5 -3
  116. package/lib/task/workspace-snapshot.ts +26 -5
  117. package/package.json +3 -3
  118. package/templates/.agents/QUICKSTART.en.md +25 -8
  119. package/templates/.agents/QUICKSTART.zh-CN.md +25 -8
  120. package/templates/.agents/README.en.md +10 -7
  121. package/templates/.agents/README.zh-CN.md +10 -7
  122. package/templates/.agents/hooks/lifecycle-delegation.js +15 -4
  123. package/templates/.agents/rules/create-issue.github.en.md +4 -2
  124. package/templates/.agents/rules/create-issue.github.zh-CN.md +4 -2
  125. package/templates/.agents/rules/issue-fields.github.en.md +3 -1
  126. package/templates/.agents/rules/issue-fields.github.zh-CN.md +3 -1
  127. package/templates/.agents/rules/issue-pr-commands.en.md +5 -3
  128. package/templates/.agents/rules/issue-pr-commands.github.en.md +5 -3
  129. package/templates/.agents/rules/issue-pr-commands.github.zh-CN.md +5 -3
  130. package/templates/.agents/rules/issue-pr-commands.zh-CN.md +5 -3
  131. package/templates/.agents/rules/issue-sync.github.en.md +7 -3
  132. package/templates/.agents/rules/issue-sync.github.zh-CN.md +8 -4
  133. package/templates/.agents/rules/lifecycle-orchestration.en.md +5 -2
  134. package/templates/.agents/rules/lifecycle-orchestration.zh-CN.md +5 -2
  135. package/templates/.agents/rules/milestone-inference.github.en.md +4 -2
  136. package/templates/.agents/rules/milestone-inference.github.zh-CN.md +4 -2
  137. package/templates/.agents/rules/next-step-output.en.md +3 -1
  138. package/templates/.agents/rules/next-step-output.zh-CN.md +3 -1
  139. package/templates/.agents/rules/no-mid-flow-questions.en.md +17 -0
  140. package/templates/.agents/rules/no-mid-flow-questions.zh-CN.md +17 -0
  141. package/templates/.agents/rules/pr-checks-commands.en.md +3 -3
  142. package/templates/.agents/rules/pr-checks-commands.github.en.md +2 -2
  143. package/templates/.agents/rules/pr-checks-commands.github.zh-CN.md +2 -2
  144. package/templates/.agents/rules/pr-checks-commands.zh-CN.md +3 -3
  145. package/templates/.agents/rules/pr-sync.en.md +3 -1
  146. package/templates/.agents/rules/pr-sync.github.en.md +11 -1
  147. package/templates/.agents/rules/pr-sync.github.zh-CN.md +11 -1
  148. package/templates/.agents/rules/pr-sync.zh-CN.md +3 -1
  149. package/templates/.agents/rules/release-commands.en.md +1 -1
  150. package/templates/.agents/rules/release-commands.github.en.md +12 -2
  151. package/templates/.agents/rules/release-commands.github.zh-CN.md +12 -2
  152. package/templates/.agents/rules/release-commands.zh-CN.md +1 -1
  153. package/templates/.agents/rules/task-management.en.md +11 -0
  154. package/templates/.agents/rules/task-management.zh-CN.md +11 -0
  155. package/templates/.agents/skills/analyze-task/SKILL.en.md +10 -7
  156. package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +10 -7
  157. package/templates/.agents/skills/block-task/SKILL.en.md +7 -5
  158. package/templates/.agents/skills/block-task/SKILL.zh-CN.md +7 -5
  159. package/templates/.agents/skills/cancel-task/SKILL.en.md +6 -4
  160. package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +6 -4
  161. package/templates/.agents/skills/close-codescan/SKILL.en.md +3 -1
  162. package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +3 -1
  163. package/templates/.agents/skills/close-dependabot/SKILL.en.md +3 -1
  164. package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +3 -1
  165. package/templates/.agents/skills/code-task/SKILL.en.md +9 -6
  166. package/templates/.agents/skills/code-task/SKILL.zh-CN.md +11 -8
  167. package/templates/.agents/skills/commit/SKILL.en.md +3 -1
  168. package/templates/.agents/skills/commit/SKILL.zh-CN.md +3 -1
  169. package/templates/.agents/skills/commit/reference/commit-message.en.md +1 -1
  170. package/templates/.agents/skills/commit/reference/commit-message.zh-CN.md +1 -1
  171. package/templates/.agents/skills/commit/reference/issue-metadata-sync.en.md +1 -1
  172. package/templates/.agents/skills/commit/reference/issue-metadata-sync.zh-CN.md +1 -1
  173. package/templates/.agents/skills/commit/reference/pr-summary-sync.en.md +1 -1
  174. package/templates/.agents/skills/commit/reference/pr-summary-sync.zh-CN.md +1 -1
  175. package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +6 -4
  176. package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +6 -4
  177. package/templates/.agents/skills/complete-manual-validation/reference/summary-update.en.md +1 -1
  178. package/templates/.agents/skills/complete-manual-validation/reference/summary-update.zh-CN.md +1 -1
  179. package/templates/.agents/skills/complete-task/SKILL.en.md +21 -6
  180. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +21 -6
  181. package/templates/.agents/skills/complete-task/config/verify.en.json +1 -0
  182. package/templates/.agents/skills/complete-task/config/verify.zh-CN.json +1 -0
  183. package/templates/.agents/skills/complete-task/reference/external-delivery.en.md +17 -0
  184. package/templates/.agents/skills/complete-task/reference/external-delivery.zh-CN.md +17 -0
  185. package/templates/.agents/skills/create-pr/SKILL.en.md +3 -1
  186. package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +3 -1
  187. package/templates/.agents/skills/create-pr/reference/comment-publish.en.md +1 -1
  188. package/templates/.agents/skills/create-pr/reference/comment-publish.zh-CN.md +1 -1
  189. package/templates/.agents/skills/create-release-note/SKILL.en.md +17 -16
  190. package/templates/.agents/skills/create-release-note/SKILL.zh-CN.md +17 -16
  191. package/templates/.agents/skills/create-task/SKILL.en.md +5 -3
  192. package/templates/.agents/skills/create-task/SKILL.zh-CN.md +5 -3
  193. package/templates/.agents/skills/import-issue/SKILL.en.md +5 -3
  194. package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +5 -3
  195. package/templates/.agents/skills/plan-task/SKILL.en.md +8 -5
  196. package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +8 -5
  197. package/templates/.agents/skills/post-release/SKILL.en.md +2 -0
  198. package/templates/.agents/skills/post-release/SKILL.zh-CN.md +2 -0
  199. package/templates/.agents/skills/release/SKILL.en.md +16 -10
  200. package/templates/.agents/skills/release/SKILL.zh-CN.md +16 -10
  201. package/templates/.agents/skills/restore-task/SKILL.en.md +4 -1
  202. package/templates/.agents/skills/restore-task/SKILL.zh-CN.md +4 -1
  203. package/templates/.agents/skills/review-analysis/SKILL.en.md +7 -4
  204. package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +7 -4
  205. package/templates/.agents/skills/review-code/SKILL.en.md +9 -6
  206. package/templates/.agents/skills/review-code/SKILL.zh-CN.md +9 -6
  207. package/templates/.agents/skills/review-plan/SKILL.en.md +7 -4
  208. package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +7 -4
  209. package/templates/.agents/skills/review-pr/SKILL.en.md +158 -0
  210. package/templates/.agents/skills/review-pr/SKILL.zh-CN.md +158 -0
  211. package/templates/.agents/skills/review-pr/config/verify.en.json +37 -0
  212. package/templates/.agents/skills/review-pr/config/verify.zh-CN.json +37 -0
  213. package/templates/.agents/skills/review-pr/reference/evidence-grading.en.md +65 -0
  214. package/templates/.agents/skills/review-pr/reference/evidence-grading.zh-CN.md +65 -0
  215. package/templates/.agents/skills/review-pr/reference/output-guidance.en.md +18 -0
  216. package/templates/.agents/skills/review-pr/reference/output-guidance.zh-CN.md +18 -0
  217. package/templates/.agents/skills/review-pr/reference/report-template.en.md +85 -0
  218. package/templates/.agents/skills/review-pr/reference/report-template.zh-CN.md +85 -0
  219. package/templates/.agents/skills/run-task/SKILL.en.md +8 -8
  220. package/templates/.agents/skills/run-task/SKILL.zh-CN.md +8 -8
  221. package/templates/.agents/skills/run-task/config/verify.en.json +2 -1
  222. package/templates/.agents/skills/run-task/config/verify.zh-CN.json +2 -1
  223. package/templates/.agents/skills/run-task/reference/host-validation.en.md +20 -0
  224. package/templates/.agents/skills/run-task/reference/host-validation.zh-CN.md +20 -0
  225. package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +35 -61
  226. package/templates/.agents/skills/watch-pr/SKILL.en.md +13 -13
  227. package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +13 -13
  228. package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.en.md +27 -6
  229. package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.zh-CN.md +26 -5
  230. package/templates/.agents/templates/task.en.md +1 -1
  231. package/templates/.agents/templates/task.zh-CN.md +1 -1
  232. package/templates/.agents/workflows/bug-fix.en.yaml +3 -3
  233. package/templates/.agents/workflows/bug-fix.zh-CN.yaml +3 -3
  234. package/templates/.agents/workflows/code-review.en.yaml +1 -1
  235. package/templates/.agents/workflows/code-review.zh-CN.yaml +1 -1
  236. package/templates/.agents/workflows/feature-development.en.yaml +4 -4
  237. package/templates/.agents/workflows/feature-development.zh-CN.yaml +4 -4
  238. package/templates/.agents/workflows/refactoring.en.yaml +3 -3
  239. package/templates/.agents/workflows/refactoring.zh-CN.yaml +3 -3
  240. package/templates/.claude/commands/post-release.en.md +1 -0
  241. package/templates/.claude/commands/post-release.zh-CN.md +1 -0
  242. package/templates/.claude/commands/review-pr.en.md +8 -0
  243. package/templates/.claude/commands/review-pr.zh-CN.md +8 -0
  244. package/templates/.gemini/commands/_project_/review-pr.en.toml +10 -0
  245. package/templates/.gemini/commands/_project_/review-pr.zh-CN.toml +10 -0
  246. package/templates/.opencode/commands/post-release.en.md +2 -0
  247. package/templates/.opencode/commands/post-release.zh-CN.md +2 -0
  248. package/templates/.opencode/commands/review-pr.en.md +11 -0
  249. package/templates/.opencode/commands/review-pr.zh-CN.md +11 -0
  250. package/dist/lib/agent-clients/adapters/gemini-cli.js +0 -64
  251. package/lib/agent-clients/adapters/gemini-cli.ts +0 -65
  252. package/templates/.gemini/commands/_project_/analyze-task.en.toml +0 -10
  253. package/templates/.gemini/commands/_project_/analyze-task.zh-CN.toml +0 -10
  254. package/templates/.gemini/commands/_project_/archive-tasks.en.toml +0 -10
  255. package/templates/.gemini/commands/_project_/archive-tasks.zh-CN.toml +0 -10
  256. package/templates/.gemini/commands/_project_/block-task.en.toml +0 -10
  257. package/templates/.gemini/commands/_project_/block-task.zh-CN.toml +0 -10
  258. package/templates/.gemini/commands/_project_/cancel-task.en.toml +0 -10
  259. package/templates/.gemini/commands/_project_/cancel-task.zh-CN.toml +0 -10
  260. package/templates/.gemini/commands/_project_/check-task.en.toml +0 -10
  261. package/templates/.gemini/commands/_project_/check-task.zh-CN.toml +0 -10
  262. package/templates/.gemini/commands/_project_/close-codescan.en.toml +0 -8
  263. package/templates/.gemini/commands/_project_/close-codescan.zh-CN.toml +0 -8
  264. package/templates/.gemini/commands/_project_/close-dependabot.en.toml +0 -8
  265. package/templates/.gemini/commands/_project_/close-dependabot.zh-CN.toml +0 -8
  266. package/templates/.gemini/commands/_project_/code-task.en.toml +0 -10
  267. package/templates/.gemini/commands/_project_/code-task.zh-CN.toml +0 -10
  268. package/templates/.gemini/commands/_project_/commit.en.toml +0 -10
  269. package/templates/.gemini/commands/_project_/commit.zh-CN.toml +0 -10
  270. package/templates/.gemini/commands/_project_/complete-manual-validation.en.toml +0 -10
  271. package/templates/.gemini/commands/_project_/complete-manual-validation.zh-CN.toml +0 -10
  272. package/templates/.gemini/commands/_project_/complete-task.en.toml +0 -10
  273. package/templates/.gemini/commands/_project_/complete-task.zh-CN.toml +0 -10
  274. package/templates/.gemini/commands/_project_/create-pr.en.toml +0 -10
  275. package/templates/.gemini/commands/_project_/create-pr.zh-CN.toml +0 -10
  276. package/templates/.gemini/commands/_project_/create-release-note.en.toml +0 -8
  277. package/templates/.gemini/commands/_project_/create-release-note.zh-CN.toml +0 -8
  278. package/templates/.gemini/commands/_project_/create-task.en.toml +0 -8
  279. package/templates/.gemini/commands/_project_/create-task.zh-CN.toml +0 -8
  280. package/templates/.gemini/commands/_project_/import-codescan.en.toml +0 -8
  281. package/templates/.gemini/commands/_project_/import-codescan.zh-CN.toml +0 -8
  282. package/templates/.gemini/commands/_project_/import-dependabot.en.toml +0 -8
  283. package/templates/.gemini/commands/_project_/import-dependabot.zh-CN.toml +0 -8
  284. package/templates/.gemini/commands/_project_/import-issue.en.toml +0 -8
  285. package/templates/.gemini/commands/_project_/import-issue.zh-CN.toml +0 -8
  286. package/templates/.gemini/commands/_project_/init-labels.en.toml +0 -8
  287. package/templates/.gemini/commands/_project_/init-labels.zh-CN.toml +0 -8
  288. package/templates/.gemini/commands/_project_/init-milestones.en.toml +0 -10
  289. package/templates/.gemini/commands/_project_/init-milestones.zh-CN.toml +0 -10
  290. package/templates/.gemini/commands/_project_/plan-task.en.toml +0 -10
  291. package/templates/.gemini/commands/_project_/plan-task.zh-CN.toml +0 -10
  292. package/templates/.gemini/commands/_project_/post-release.en.toml +0 -6
  293. package/templates/.gemini/commands/_project_/post-release.zh-CN.toml +0 -6
  294. package/templates/.gemini/commands/_project_/refine-title.en.toml +0 -8
  295. package/templates/.gemini/commands/_project_/refine-title.zh-CN.toml +0 -8
  296. package/templates/.gemini/commands/_project_/release.en.toml +0 -8
  297. package/templates/.gemini/commands/_project_/release.zh-CN.toml +0 -8
  298. package/templates/.gemini/commands/_project_/restore-task.en.toml +0 -8
  299. package/templates/.gemini/commands/_project_/restore-task.zh-CN.toml +0 -8
  300. package/templates/.gemini/commands/_project_/review-analysis.en.toml +0 -10
  301. package/templates/.gemini/commands/_project_/review-analysis.zh-CN.toml +0 -10
  302. package/templates/.gemini/commands/_project_/review-code.en.toml +0 -10
  303. package/templates/.gemini/commands/_project_/review-code.zh-CN.toml +0 -10
  304. package/templates/.gemini/commands/_project_/review-plan.en.toml +0 -10
  305. package/templates/.gemini/commands/_project_/review-plan.zh-CN.toml +0 -10
  306. package/templates/.gemini/commands/_project_/run-task.en.toml +0 -8
  307. package/templates/.gemini/commands/_project_/run-task.zh-CN.toml +0 -8
  308. package/templates/.gemini/commands/_project_/test-integration.en.toml +0 -6
  309. package/templates/.gemini/commands/_project_/test-integration.zh-CN.toml +0 -6
  310. package/templates/.gemini/commands/_project_/test.en.toml +0 -6
  311. package/templates/.gemini/commands/_project_/test.zh-CN.toml +0 -6
  312. package/templates/.gemini/commands/_project_/update-agent-infra.en.toml +0 -6
  313. package/templates/.gemini/commands/_project_/update-agent-infra.zh-CN.toml +0 -6
  314. package/templates/.gemini/commands/_project_/upgrade-dependency.en.toml +0 -8
  315. package/templates/.gemini/commands/_project_/upgrade-dependency.zh-CN.toml +0 -8
  316. package/templates/.gemini/commands/_project_/watch-pr.en.toml +0 -10
  317. package/templates/.gemini/commands/_project_/watch-pr.zh-CN.toml +0 -10
  318. package/templates/.gemini/settings.json +0 -3
@@ -0,0 +1,318 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+
4
+ import { parseTaskFrontmatter } from '../task/frontmatter.ts';
5
+
6
+ // ---------------------------------------------------------------------------
7
+ // Host resolution (PL-4): PR -> unique / ambiguous / none.
8
+ // ---------------------------------------------------------------------------
9
+
10
+ export type HostCandidate = {
11
+ taskId: string;
12
+ taskDir: string;
13
+ issueNumber: number | null;
14
+ prNumber: number | null;
15
+ };
16
+
17
+ export type HostResolution =
18
+ | { kind: 'unique'; taskId: string; taskDir: string; issueNumber: number; prNumber: number }
19
+ | { kind: 'ambiguous'; candidates: Array<{ taskId: string; issueNumber: number }> }
20
+ | { kind: 'none' };
21
+
22
+ export type HostResolutionErrorCode = 'HOST_AMBIGUOUS' | 'HOST_RESOLUTION_INVALID';
23
+
24
+ export class HostResolutionError extends Error {
25
+ code: HostResolutionErrorCode;
26
+
27
+ constructor(code: HostResolutionErrorCode, message: string) {
28
+ super(message);
29
+ this.name = 'HostResolutionError';
30
+ this.code = code;
31
+ }
32
+ }
33
+
34
+ function toPositiveNumber(value: unknown): number | null {
35
+ const n = Number(value);
36
+ return Number.isInteger(n) && n > 0 ? n : null;
37
+ }
38
+
39
+ /**
40
+ * Parse the PR body for closing-issue references. Handles the conventional
41
+ * `Closes #1` / `Fixes: #2, #3` shapes (case-insensitive, comma/space
42
+ * separated, optional keyword list on the same logical line).
43
+ */
44
+ export function extractClosingIssueNumbers(body: string): number[] {
45
+ const result: number[] = [];
46
+ const seen = new Set<number>();
47
+ const linePattern = /(?:^|\n)[^\n]*\b(?:close|closes|closed|fix|fixes|fixed|resolve|resolves|resolved)\b[^\n]*/gi;
48
+ let lineMatch: RegExpExecArray | null;
49
+ while ((lineMatch = linePattern.exec(body)) !== null) {
50
+ const line = lineMatch[0]!;
51
+ for (const numMatch of line.matchAll(/#(\d+)/g)) {
52
+ const n = Number(numMatch[1]!);
53
+ if (Number.isInteger(n) && n > 0 && !seen.has(n)) {
54
+ seen.add(n);
55
+ result.push(n);
56
+ }
57
+ }
58
+ }
59
+ return result;
60
+ }
61
+
62
+ /**
63
+ * Scan `.agents/workspace/active/{task-id}/task.md` files for tasks bound to the
64
+ * PR number or to one of the PR's closing issues. A task is emitted at most once;
65
+ * a direct `pr_number` hit takes priority over the issue-number reverse lookup.
66
+ */
67
+ export function collectHostCandidates(input: {
68
+ prNumber: number;
69
+ closingIssues: number[];
70
+ workspaceRoot: string;
71
+ }): HostCandidate[] {
72
+ const activeRoot = path.join(input.workspaceRoot, '.agents', 'workspace', 'active');
73
+ const candidates: HostCandidate[] = [];
74
+ let entries: fs.Dirent[];
75
+ try {
76
+ entries = fs.readdirSync(activeRoot, { withFileTypes: true });
77
+ } catch {
78
+ return [];
79
+ }
80
+ for (const entry of entries) {
81
+ if (!entry.isDirectory()) continue;
82
+ const taskDir = path.join(activeRoot, entry.name);
83
+ const taskMdPath = path.join(taskDir, 'task.md');
84
+ let content: string;
85
+ try {
86
+ content = fs.readFileSync(taskMdPath, 'utf8');
87
+ } catch {
88
+ continue;
89
+ }
90
+ const frontmatter = parseTaskFrontmatter(content);
91
+ const issueNumber = toPositiveNumber(frontmatter.issue_number);
92
+ const prNumber = toPositiveNumber(frontmatter.pr_number);
93
+ if (prNumber === input.prNumber) {
94
+ candidates.push({ taskId: entry.name, taskDir, issueNumber, prNumber });
95
+ } else if (issueNumber !== null && input.closingIssues.includes(issueNumber)) {
96
+ candidates.push({ taskId: entry.name, taskDir, issueNumber, prNumber });
97
+ }
98
+ }
99
+ return candidates;
100
+ }
101
+
102
+ /**
103
+ * Collapse a candidate list into a typed HostResolution. Zero candidates ->
104
+ * none; exactly one -> unique; more than one (no stable selection rule) ->
105
+ * ambiguous (fail closed, AN-6).
106
+ */
107
+ export function resolveHostFromCandidates(candidates: HostCandidate[]): HostResolution {
108
+ if (candidates.length === 0) return { kind: 'none' };
109
+ if (candidates.length === 1) {
110
+ const candidate = candidates[0]!;
111
+ return {
112
+ kind: 'unique',
113
+ taskId: candidate.taskId,
114
+ taskDir: candidate.taskDir,
115
+ issueNumber: candidate.issueNumber ?? 0,
116
+ prNumber: candidate.prNumber ?? 0
117
+ };
118
+ }
119
+ return {
120
+ kind: 'ambiguous',
121
+ candidates: candidates.map((candidate) => ({ taskId: candidate.taskId, issueNumber: candidate.issueNumber ?? 0 }))
122
+ };
123
+ }
124
+
125
+ // ---------------------------------------------------------------------------
126
+ // Evidence presence, head state, scenarios, risk and mode (AN-5/6/7, PL-3/5/9).
127
+ // ---------------------------------------------------------------------------
128
+
129
+ export type ArtifactPresence = {
130
+ hasAnalysis: boolean;
131
+ hasPlan: boolean;
132
+ hasCode: boolean;
133
+ hasReviewAnalysis: boolean;
134
+ hasReviewPlan: boolean;
135
+ hasReviewCode: boolean;
136
+ hasPriorPrReview: boolean;
137
+ };
138
+
139
+ export type HeadState = {
140
+ currentHeadSha: string;
141
+ priorHeadSha: string | null;
142
+ issueNumberBound: boolean;
143
+ taskIssueMatches: boolean;
144
+ prNumberBound: boolean;
145
+ taskPrMatches: boolean;
146
+ };
147
+
148
+ export type EvidenceScenario = 'S1' | 'S2' | 'S3';
149
+ export type Freshness = 'fresh' | 'stale' | 'n/a';
150
+ export type Alignment = 'aligned' | 'misaligned' | 'n/a';
151
+ export type RiskLevel = 'LOW' | 'MEDIUM' | 'HIGH';
152
+ export type ReviewMode = 'verify' | 'audit' | 'reconstruct';
153
+
154
+ export type RiskFactors = {
155
+ changeSize: 'LOW' | 'HIGH';
156
+ sensitivity: 'LOW' | 'HIGH';
157
+ structural: 'LOW' | 'HIGH';
158
+ testCoverage: 'LOW' | 'HIGH';
159
+ sourceCredibility: 'LOW' | 'HIGH';
160
+ recoveryImpact: 'LOW' | 'HIGH';
161
+ };
162
+
163
+ export type DecisionRecord = {
164
+ host: HostResolution;
165
+ scenario: EvidenceScenario;
166
+ freshness: Freshness;
167
+ alignment: Alignment;
168
+ risk: RiskLevel;
169
+ mode: ReviewMode;
170
+ firstReview: boolean;
171
+ reason: string;
172
+ };
173
+
174
+ function hasNoLifecycleArtifacts(presence: ArtifactPresence): boolean {
175
+ return !presence.hasAnalysis && !presence.hasPlan && !presence.hasCode &&
176
+ !presence.hasReviewAnalysis && !presence.hasReviewPlan && !presence.hasReviewCode &&
177
+ !presence.hasPriorPrReview;
178
+ }
179
+
180
+ function hasFullArtifacts(presence: ArtifactPresence): boolean {
181
+ return presence.hasAnalysis && presence.hasPlan && presence.hasCode &&
182
+ presence.hasReviewAnalysis && presence.hasReviewPlan && presence.hasReviewCode;
183
+ }
184
+
185
+ function isTrusted(head: HeadState): boolean {
186
+ return head.issueNumberBound && head.taskIssueMatches && (!head.prNumberBound || head.taskPrMatches);
187
+ }
188
+
189
+ /**
190
+ * Derive the S1(a) trust factor `taskIssueMatches` (PL-3) mechanically from
191
+ * resolved data: a unique host is trusted on the issue dimension only when the
192
+ * task's bound issue_number is among the PR's closing issues. Ambiguous/none
193
+ * hosts and tasks without a positive issue_number cannot be confirmed -> false
194
+ * (fail closed to S2/audit). `resolve-host` emits this value so the prompt layer
195
+ * does not derive the trust factor ad hoc (CD-2).
196
+ */
197
+ export function deriveTaskIssueMatches(host: HostResolution, closingIssues: number[]): boolean {
198
+ return host.kind === 'unique' && host.issueNumber > 0 && closingIssues.includes(host.issueNumber);
199
+ }
200
+
201
+ /**
202
+ * S1 -> S2 -> S3 classification (analysis §6.1). The ambiguous host is rejected
203
+ * before classification (AN-6 fail closed). S3(a) is host.kind === 'none';
204
+ * S3(b) is a unique task with no lifecycle artifacts and no prior pr-review
205
+ * (PL-5/PL-9 symmetric: any review-only task falls to S2).
206
+ */
207
+ export function classifyScenario(host: HostResolution, presence: ArtifactPresence, head: HeadState): EvidenceScenario {
208
+ if (host.kind === 'ambiguous') {
209
+ throw new HostResolutionError('HOST_AMBIGUOUS', 'multiple candidate hosts; resolve before evidence classification');
210
+ }
211
+ if (host.kind === 'none') return 'S3';
212
+ if (hasNoLifecycleArtifacts(presence)) return 'S3';
213
+ const priorHeadMatches = presence.hasPriorPrReview && head.priorHeadSha !== null && head.priorHeadSha === head.currentHeadSha;
214
+ if (hasFullArtifacts(presence) && priorHeadMatches && isTrusted(head)) return 'S1';
215
+ return 'S2';
216
+ }
217
+
218
+ /**
219
+ * Freshness is benchmarked against the prior `pr-review*` reviewed head SHA
220
+ * (analysis §6.2, AN-7). No prior review -> n/a/n/a (first-review special case).
221
+ */
222
+ export function evaluateHead(head: HeadState): { freshness: Freshness; alignment: Alignment } {
223
+ if (head.priorHeadSha === null) return { freshness: 'n/a', alignment: 'n/a' };
224
+ const freshness: Freshness = head.priorHeadSha === head.currentHeadSha ? 'fresh' : 'stale';
225
+ const aligned = freshness === 'fresh' && isTrusted(head);
226
+ return { freshness, alignment: aligned ? 'aligned' : 'misaligned' };
227
+ }
228
+
229
+ /**
230
+ * Pure-evidence risk aggregation (analysis §6.3, AN-5): any priority factor
231
+ * (sensitivity / sourceCredibility) HIGH -> HIGH; otherwise any HIGH -> MEDIUM;
232
+ * all LOW -> LOW. Identity factors are structurally absent from the input type.
233
+ */
234
+ export function gradeRisk(factors: RiskFactors): RiskLevel {
235
+ const priority = [factors.sensitivity, factors.sourceCredibility] as const;
236
+ if (priority.includes('HIGH')) return 'HIGH';
237
+ const values = Object.values(factors) as Array<'LOW' | 'HIGH'>;
238
+ if (values.includes('HIGH')) return 'MEDIUM';
239
+ return 'LOW';
240
+ }
241
+
242
+ /**
243
+ * verify / audit / reconstruct selection matrix (analysis §6.4). S3 ->
244
+ * reconstruct; S2 -> audit; S1 fresh+aligned -> verify (LOW/MEDIUM risk) or
245
+ * audit (HIGH risk); any other S1 combination falls back to audit.
246
+ */
247
+ export function selectMode(scenario: EvidenceScenario, freshness: Freshness, alignment: Alignment, risk: RiskLevel): ReviewMode {
248
+ if (scenario === 'S3') return 'reconstruct';
249
+ if (scenario === 'S2') return 'audit';
250
+ if (freshness === 'fresh' && alignment === 'aligned') {
251
+ return risk === 'HIGH' ? 'audit' : 'verify';
252
+ }
253
+ return 'audit';
254
+ }
255
+
256
+ function buildReason(input: {
257
+ scenario: EvidenceScenario;
258
+ freshness: Freshness;
259
+ alignment: Alignment;
260
+ risk: RiskLevel;
261
+ mode: ReviewMode;
262
+ firstReview: boolean;
263
+ hasPriorPrReview: boolean;
264
+ }): string {
265
+ const pieces: string[] = [];
266
+ if (input.firstReview) pieces.push('first review (no prior pr-review head)');
267
+ if (input.hasPriorPrReview) pieces.push(`prior head ${input.freshness}`);
268
+ if (input.freshness === 'fresh') pieces.push(`alignment ${input.alignment}`);
269
+ pieces.push(`evidence scenario ${input.scenario}`);
270
+ pieces.push(`risk ${input.risk}`);
271
+ pieces.push(`mode ${input.mode}`);
272
+ return pieces.join('; ');
273
+ }
274
+
275
+ /**
276
+ * Full decision pipeline: classify -> freshness/alignment -> risk -> mode,
277
+ * producing a traceable DecisionRecord (AC3). Ambiguous hosts are rejected
278
+ * before any classification runs (AN-6).
279
+ */
280
+ export function decide(input: {
281
+ host: HostResolution;
282
+ presence: ArtifactPresence;
283
+ head: HeadState;
284
+ riskFactors: RiskFactors;
285
+ }): DecisionRecord {
286
+ if (input.host.kind === 'ambiguous') {
287
+ throw new HostResolutionError('HOST_AMBIGUOUS', 'ambiguous host; resolve before evidence classification');
288
+ }
289
+ const scenario = classifyScenario(input.host, input.presence, input.head);
290
+ const { freshness, alignment } = evaluateHead(input.head);
291
+ const risk = gradeRisk(input.riskFactors);
292
+ const mode = selectMode(scenario, freshness, alignment, risk);
293
+ const firstReview = !input.presence.hasPriorPrReview;
294
+ return {
295
+ host: input.host,
296
+ scenario,
297
+ freshness,
298
+ alignment,
299
+ risk,
300
+ mode,
301
+ firstReview,
302
+ reason: buildReason({
303
+ scenario,
304
+ freshness,
305
+ alignment,
306
+ risk,
307
+ mode,
308
+ firstReview,
309
+ hasPriorPrReview: input.presence.hasPriorPrReview
310
+ })
311
+ };
312
+ }
313
+
314
+ export {
315
+ hasFullArtifacts,
316
+ hasNoLifecycleArtifacts,
317
+ isTrusted
318
+ };
package/lib/run/tui.ts CHANGED
@@ -1,9 +1,9 @@
1
1
  import { renderAgentClientInvocation } from '../agent-clients/invocation.ts';
2
2
  import { getAgentClientAdapter } from '../agent-clients/registry.ts';
3
3
 
4
- export type TuiName = 'claude' | 'codex' | 'gemini' | 'opencode';
4
+ export type TuiName = 'claude' | 'codex' | 'antigravity' | 'opencode';
5
5
 
6
- const TUI_NAMES = new Set(['claude', 'codex', 'gemini', 'opencode']);
6
+ const TUI_NAMES = new Set(['claude', 'codex', 'antigravity', 'opencode']);
7
7
 
8
8
  export type CommandConfig = {
9
9
  defaultTui?: unknown;
@@ -36,13 +36,12 @@ export function renderPrompt(params: { tui: TuiName; skill: string; args: string
36
36
  args: params.args
37
37
  });
38
38
  }
39
- if (params.tui === 'gemini') return `/agent-infra:${suffix}`;
40
39
  return `/${suffix}`;
41
40
  }
42
41
 
43
42
  export function buildTuiCommand(tui: TuiName, prompt: string): [string, string[]] {
44
43
  if (tui === 'claude') return ['claude', ['--dangerously-skip-permissions', '--print', prompt]];
45
- if (tui === 'gemini') return ['gemini', ['--approval-mode', 'yolo', '--prompt', prompt]];
44
+ if (tui === 'antigravity') return ['agy', ['--dangerously-skip-permissions', '--print', prompt]];
46
45
  if (tui === 'opencode') return ['opencode', ['run', '--dangerously-skip-permissions', prompt]];
47
46
  return ['codex', ['exec', '--dangerously-bypass-approvals-and-sandbox', prompt]];
48
47
  }
@@ -1010,22 +1010,6 @@ export function ensureOpenCodeModelInheritance(toolDir: string, hostHomeDir?: st
1010
1010
  }
1011
1011
  }
1012
1012
 
1013
- export function ensureGeminiWorkspaceTrust(toolDir: string): void {
1014
- const trustPath = path.join(toolDir, 'trustedFolders.json');
1015
- let data: Record<string, string> = {};
1016
- if (fs.existsSync(trustPath)) {
1017
- try {
1018
- data = JSON.parse(fs.readFileSync(trustPath, 'utf8')) as Record<string, string>;
1019
- } catch {
1020
- // malformed JSON, start fresh
1021
- }
1022
- }
1023
- if (data['/workspace'] !== 'TRUST_FOLDER') {
1024
- data['/workspace'] = 'TRUST_FOLDER';
1025
- fs.writeFileSync(trustPath, JSON.stringify(data, null, 2), 'utf8');
1026
- }
1027
- }
1028
-
1029
1013
  export function sandboxAliasesPath(home: string): string {
1030
1014
  return hostJoin(home, '.agent-infra', 'aliases', 'sandbox.sh');
1031
1015
  }
@@ -1523,10 +1507,6 @@ export async function create(args: string[]): Promise<void> {
1523
1507
  // provider/API settings are enough for Claude Code. If no usable
1524
1508
  // auth was present, the claude-code entry was removed above.
1525
1509
  }
1526
- const geminiEntry = effectiveResolvedTools.find(({ tool }) => tool.id === 'gemini-cli');
1527
- if (geminiEntry) {
1528
- ensureGeminiWorkspaceTrust(geminiEntry.dir);
1529
- }
1530
1510
  const opencodeEntry = effectiveResolvedTools.find(({ tool }) => tool.id === 'opencode');
1531
1511
  if (opencodeEntry) {
1532
1512
  // The TUI reads <toolDir>/opencode.json via OPENCODE_CONFIG pinned in tools.js.
@@ -10,7 +10,7 @@ import { resolveTools, toolConfigDirCandidates } from '../tools.ts';
10
10
  const USAGE = `Usage: ai sandbox show <branch | TASK-id | N>
11
11
 
12
12
  Shows one sandbox's worktree path and per-tool state paths (Claude Code,
13
- Codex, Gemini CLI, OpenCode). The argument follows the same contract as
13
+ Codex, Antigravity CLI, OpenCode). The argument follows the same contract as
14
14
  'ai sandbox exec' and 'ai sandbox start': N (bare) is the recommended form
15
15
  for task short ids (e.g. 'ai sandbox show 11'); a TASK-id or a plain
16
16
  branch name are also accepted. Use 'ai sandbox ls' for the container list.`;
@@ -75,14 +75,14 @@
75
75
  "properties": {
76
76
  "defaultTui": {
77
77
  "type": "string",
78
- "enum": ["claude", "codex", "gemini", "opencode"],
78
+ "enum": ["claude", "codex", "antigravity", "opencode"],
79
79
  "default": "codex"
80
80
  },
81
81
  "skillTuiDefaults": {
82
82
  "type": "object",
83
83
  "additionalProperties": {
84
84
  "type": "string",
85
- "enum": ["claude", "codex", "gemini", "opencode"]
85
+ "enum": ["claude", "codex", "antigravity", "opencode"]
86
86
  }
87
87
  },
88
88
  "allowedSkills": {
@@ -0,0 +1,128 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+
4
+ import { resolveTaskRef } from './resolve-ref.ts';
5
+ import { locateActivityLog, appendActivityEntry } from './activity-log.ts';
6
+ import { buildArtifactLinkSection, parseArtifactName } from './artifact-lifecycle.ts';
7
+ import { captureTaskWriteMetadata, writeTask } from './write.ts';
8
+ import type { TaskMutation, TaskOperationSummary, TaskWriteOptions } from './write.ts';
9
+
10
+ export type ActivityAppendIntent = {
11
+ kind: 'append';
12
+ taskRef: string;
13
+ step: string;
14
+ agent: string;
15
+ note: string;
16
+ artifact?: string;
17
+ dryRun?: boolean;
18
+ };
19
+
20
+ export type ActivityIntentResult = {
21
+ status: 'planned' | 'applied' | 'no-op' | 'failed';
22
+ changed: boolean;
23
+ intent: 'append';
24
+ taskId: string | null;
25
+ artifact: string | null;
26
+ operations: readonly TaskOperationSummary[];
27
+ error: { code: string; message: string } | null;
28
+ };
29
+
30
+ function failed(intent: ActivityAppendIntent, code: string, message: string, taskId: string | null = null): ActivityIntentResult {
31
+ return {
32
+ status: 'failed', changed: false, intent: 'append', taskId,
33
+ artifact: intent.artifact ?? null, operations: [], error: { code, message }
34
+ };
35
+ }
36
+
37
+ function oneLine(value: string): string {
38
+ return value.replace(/\s*\r?\n\s*/g, ' ').trim();
39
+ }
40
+
41
+ /**
42
+ * Append an Activity Log entry (and, optionally, a `## 审查反馈` artifact link)
43
+ * to an active task via the atomic `writeTask` path. `current_step` is never
44
+ * touched (PL-2): review-pr does not join the standard stage chain.
45
+ */
46
+ export function applyActivityAppendIntent(intent: ActivityAppendIntent, options: TaskWriteOptions = {}): ActivityIntentResult {
47
+ if (intent.kind !== 'append') return failed(intent, 'ACTIVITY_INTENT_INVALID', 'intent kind must be append');
48
+ const resolved = resolveTaskRef(intent.taskRef, { repoRoot: options.repoRoot });
49
+ if (!resolved.ok) return failed(intent, resolved.code, resolved.message, resolved.taskId);
50
+ if (resolved.state !== 'active') {
51
+ return failed(intent, 'TASK_STATE_MISMATCH', `task ${resolved.taskId} is ${resolved.state}, expected active`, resolved.taskId);
52
+ }
53
+ const step = oneLine(intent.step);
54
+ const agent = oneLine(intent.agent);
55
+ const note = oneLine(intent.note);
56
+ if (!step || !agent || !note || /[\r\n]/.test(intent.step) || /[\r\n]/.test(intent.agent) || /[\r\n]/.test(intent.note)) {
57
+ return failed(intent, 'ACTIVITY_INTENT_INVALID', 'step, agent and note must be non-empty single-line values', resolved.taskId);
58
+ }
59
+
60
+ let artifact: ReturnType<typeof parseArtifactName> | null = null;
61
+ if (intent.artifact !== undefined) {
62
+ artifact = parseArtifactName(intent.artifact);
63
+ if (!artifact || artifact.family !== 'pr-review') {
64
+ return failed(intent, 'ACTIVITY_ARTIFACT_INVALID', `artifact '${intent.artifact}' is not a canonical pr-review artifact`, resolved.taskId);
65
+ }
66
+ }
67
+
68
+ let content: string;
69
+ try {
70
+ content = fs.readFileSync(resolved.taskMdPath, 'utf8');
71
+ } catch (error) {
72
+ return failed(intent, 'TASK_READ_FAILED', error instanceof Error ? error.message : String(error), resolved.taskId);
73
+ }
74
+ const section = locateActivityLog(content);
75
+ if (!section) {
76
+ return failed(intent, 'ACTIVITY_SECTION_MISSING', 'task has no unique Activity Log section', resolved.taskId);
77
+ }
78
+
79
+ let metadata;
80
+ try {
81
+ metadata = (options.metadataProvider ?? captureTaskWriteMetadata)();
82
+ } catch (error) {
83
+ return failed(intent, 'METADATA_CAPTURE_FAILED', error instanceof Error ? error.message : String(error), resolved.taskId);
84
+ }
85
+
86
+ const mutations: TaskMutation[] = [];
87
+ const activityBody = appendActivityEntry(section, { time: metadata.timestamp, step, agent, note });
88
+ mutations.push({ kind: 'section', aliases: ['活动日志', 'Activity Log'], heading: section.heading, body: activityBody });
89
+ if (artifact) {
90
+ const link = buildArtifactLinkSection(content, {
91
+ ...artifact,
92
+ path: path.join(resolved.taskDir, artifact.name),
93
+ size: 0,
94
+ mtimeMs: 0
95
+ });
96
+ mutations.push({ kind: 'section', aliases: link.aliases, heading: link.heading, body: link.body });
97
+ }
98
+
99
+ const writeResult = writeTask({
100
+ taskRef: intent.taskRef,
101
+ expectedState: 'active',
102
+ mutations,
103
+ dryRun: intent.dryRun
104
+ }, {
105
+ ...options,
106
+ taskLocation: {
107
+ repoRoot: resolved.repoRoot,
108
+ taskId: resolved.taskId,
109
+ taskMdPath: resolved.taskMdPath,
110
+ state: resolved.state
111
+ },
112
+ metadataProvider: () => metadata
113
+ });
114
+ if (writeResult.status === 'failed') {
115
+ return failed(intent, writeResult.error.code, writeResult.error.message, writeResult.taskId);
116
+ }
117
+ return {
118
+ status: writeResult.status,
119
+ changed: writeResult.changed,
120
+ intent: 'append',
121
+ taskId: writeResult.taskId,
122
+ artifact: intent.artifact ?? null,
123
+ operations: writeResult.operations,
124
+ error: null
125
+ };
126
+ }
127
+
128
+ export type { TaskWriteOptions } from './write.ts';
@@ -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 };