@fitlab-ai/agent-infra 0.9.0 → 0.9.2

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 (327) hide show
  1. package/README.md +22 -5
  2. package/README.zh-CN.md +21 -5
  3. package/bin/cli.ts +12 -0
  4. package/dist/bin/cli.js +14 -0
  5. package/dist/bin/internal-cli.js +20 -0
  6. package/dist/lib/agent-client.js +163 -0
  7. package/dist/lib/agent-clients/adapter.js +294 -0
  8. package/dist/lib/agent-clients/adapters/claude-code.js +62 -0
  9. package/dist/lib/agent-clients/adapters/codex-sandbox.js +210 -0
  10. package/dist/lib/agent-clients/adapters/codex.js +54 -0
  11. package/dist/lib/agent-clients/adapters/gemini-cli.js +64 -0
  12. package/dist/lib/agent-clients/adapters/index.js +20 -0
  13. package/dist/lib/agent-clients/adapters/opencode.js +64 -0
  14. package/dist/lib/agent-clients/config.js +177 -0
  15. package/dist/lib/agent-clients/custom-tuis.js +99 -0
  16. package/dist/lib/agent-clients/invocation.js +9 -0
  17. package/dist/lib/agent-clients/next-steps.js +38 -0
  18. package/dist/lib/agent-clients/project-assets.js +45 -0
  19. package/dist/lib/agent-clients/reconcile.js +235 -0
  20. package/dist/lib/agent-clients/registry.js +72 -0
  21. package/dist/lib/agent-clients/schema.js +57 -0
  22. package/dist/lib/agent-clients/types.js +29 -0
  23. package/dist/lib/builtin-tuis.js +9 -14
  24. package/dist/lib/defaults.json +85 -73
  25. package/dist/lib/git/review-snapshot.js +25 -10
  26. package/dist/lib/git/reviewed-commit-equivalence.js +110 -0
  27. package/dist/lib/init.js +47 -91
  28. package/dist/lib/internal/agent-client.js +117 -0
  29. package/dist/lib/internal/git-workflow.js +1 -1
  30. package/dist/lib/internal/platform-release-notes.js +53 -0
  31. package/dist/lib/internal/release-workflow.js +175 -17
  32. package/dist/lib/internal/task-orchestration.js +151 -0
  33. package/dist/lib/internal/task-review.js +65 -0
  34. package/dist/lib/merge.js +2 -1
  35. package/dist/lib/platform/adapters.js +38 -1
  36. package/dist/lib/platform/change-request-git-evidence.js +113 -0
  37. package/dist/lib/platform/github-release-notes.js +96 -0
  38. package/dist/lib/platform/issues.js +4 -2
  39. package/dist/lib/platform/merged-pr-equivalence.js +84 -0
  40. package/dist/lib/platform/pr-checks.js +43 -15
  41. package/dist/lib/platform/pull-requests.js +113 -7
  42. package/dist/lib/platform/release-notes.js +143 -0
  43. package/dist/lib/platform/releases.js +1 -1
  44. package/dist/lib/platform/verification-required.js +31 -9
  45. package/dist/lib/prompt.js +15 -3
  46. package/dist/lib/release/channels.js +4 -1
  47. package/dist/lib/release/workflow.js +4 -2
  48. package/dist/lib/remove-dir.js +25 -0
  49. package/dist/lib/run/tui.js +8 -2
  50. package/dist/lib/sandbox/agent-client-reconciler.js +234 -0
  51. package/dist/lib/sandbox/build-proxy.js +63 -0
  52. package/dist/lib/sandbox/clipboard/linux.js +2 -1
  53. package/dist/lib/sandbox/commands/create.js +133 -189
  54. package/dist/lib/sandbox/commands/enter.js +17 -1
  55. package/dist/lib/sandbox/commands/prune.js +2 -2
  56. package/dist/lib/sandbox/commands/rebuild.js +24 -7
  57. package/dist/lib/sandbox/commands/rm.js +3 -2
  58. package/dist/lib/sandbox/config.js +21 -4
  59. package/dist/lib/sandbox/constants.js +3 -0
  60. package/dist/lib/sandbox/dockerfile.js +2 -1
  61. package/dist/lib/sandbox/image-build.js +2 -1
  62. package/dist/lib/sandbox/managed-fs.js +10 -5
  63. package/dist/lib/sandbox/recovery.js +52 -36
  64. package/dist/lib/sandbox/runtimes/ai-tools.dockerfile +24 -3
  65. package/dist/lib/sandbox/runtimes/base.dockerfile +8 -1
  66. package/dist/lib/sandbox/runtimes/java17.dockerfile +8 -1
  67. package/dist/lib/sandbox/runtimes/java21.dockerfile +8 -1
  68. package/dist/lib/sandbox/runtimes/node20.dockerfile +8 -1
  69. package/dist/lib/sandbox/runtimes/node22.dockerfile +8 -1
  70. package/dist/lib/sandbox/runtimes/python3.dockerfile +8 -1
  71. package/dist/lib/sandbox/shell.js +3 -2
  72. package/dist/lib/sandbox/tool-types.js +8 -0
  73. package/dist/lib/sandbox/tools.js +2 -126
  74. package/dist/lib/task/artifact-lifecycle.js +1 -49
  75. package/dist/lib/task/commands/decisions.js +50 -20
  76. package/dist/lib/task/commands/issue-body.js +10 -5
  77. package/dist/lib/task/delegation-receipts.js +107 -0
  78. package/dist/lib/task/events.js +101 -2
  79. package/dist/lib/task/issue-form.js +2 -0
  80. package/dist/lib/task/orchestration.js +420 -0
  81. package/dist/lib/task/review-artifacts.js +143 -59
  82. package/dist/lib/task/review-finalization.js +166 -0
  83. package/dist/lib/task/review-fingerprint.js +13 -0
  84. package/dist/lib/task/verification-checks.js +2 -1
  85. package/dist/lib/task/verification-engine.js +168 -23
  86. package/dist/lib/task/verification-review.js +1 -1
  87. package/dist/lib/task/verification.js +4 -2
  88. package/dist/lib/task/workspace-snapshot.js +34 -0
  89. package/dist/lib/update.js +42 -161
  90. package/lib/agent-client.ts +214 -0
  91. package/lib/agent-clients/adapter.ts +461 -0
  92. package/lib/agent-clients/adapters/claude-code.ts +70 -0
  93. package/lib/agent-clients/adapters/codex-sandbox.ts +245 -0
  94. package/lib/agent-clients/adapters/codex.ts +58 -0
  95. package/lib/agent-clients/adapters/gemini-cli.ts +65 -0
  96. package/lib/agent-clients/adapters/index.ts +27 -0
  97. package/lib/agent-clients/adapters/opencode.ts +66 -0
  98. package/lib/agent-clients/config.ts +278 -0
  99. package/lib/agent-clients/custom-tuis.ts +149 -0
  100. package/lib/agent-clients/invocation.ts +19 -0
  101. package/lib/agent-clients/next-steps.ts +72 -0
  102. package/lib/agent-clients/project-assets.ts +96 -0
  103. package/lib/agent-clients/reconcile.ts +324 -0
  104. package/lib/agent-clients/registry.ts +144 -0
  105. package/lib/agent-clients/schema.ts +62 -0
  106. package/lib/agent-clients/types.ts +78 -0
  107. package/lib/benchmark/schemas/v1.0.0/case-manifest.schema.json +120 -0
  108. package/lib/benchmark/schemas/v1.0.0/common.schema.json +109 -0
  109. package/lib/benchmark/schemas/v1.0.0/grader-result.schema.json +160 -0
  110. package/lib/benchmark/schemas/v1.0.0/run-manifest.schema.json +122 -0
  111. package/lib/benchmark/schemas/v1.0.0/subject.schema.json +227 -0
  112. package/lib/builtin-tuis.ts +16 -15
  113. package/lib/defaults.json +24 -12
  114. package/lib/git/review-snapshot.ts +39 -9
  115. package/lib/git/reviewed-commit-equivalence.ts +135 -0
  116. package/lib/init.ts +53 -121
  117. package/lib/internal/agent-client.ts +131 -0
  118. package/lib/internal/git-workflow.ts +1 -1
  119. package/lib/internal/platform-release-notes.ts +55 -0
  120. package/lib/internal/release-workflow.ts +172 -18
  121. package/lib/internal/task-orchestration.ts +132 -0
  122. package/lib/internal/task-review.ts +65 -0
  123. package/lib/merge.ts +2 -1
  124. package/lib/platform/adapters.ts +142 -3
  125. package/lib/platform/change-request-git-evidence.ts +159 -0
  126. package/lib/platform/github-release-notes.ts +132 -0
  127. package/lib/platform/issues.ts +6 -4
  128. package/lib/platform/merged-pr-equivalence.ts +107 -0
  129. package/lib/platform/pr-checks.ts +57 -28
  130. package/lib/platform/pull-requests.ts +140 -25
  131. package/lib/platform/release-notes.ts +167 -0
  132. package/lib/platform/releases.ts +1 -1
  133. package/lib/platform/verification-required.ts +37 -9
  134. package/lib/prompt.ts +16 -3
  135. package/lib/release/channels.ts +4 -1
  136. package/lib/release/workflow.ts +4 -3
  137. package/lib/remove-dir.ts +28 -0
  138. package/lib/run/tui.ts +9 -1
  139. package/lib/sandbox/agent-client-reconciler.ts +378 -0
  140. package/lib/sandbox/build-proxy.ts +94 -0
  141. package/lib/sandbox/clipboard/linux.ts +2 -1
  142. package/lib/sandbox/commands/create.ts +188 -203
  143. package/lib/sandbox/commands/enter.ts +23 -1
  144. package/lib/sandbox/commands/prune.ts +2 -2
  145. package/lib/sandbox/commands/rebuild.ts +29 -7
  146. package/lib/sandbox/commands/rm.ts +3 -2
  147. package/lib/sandbox/config.ts +31 -4
  148. package/lib/sandbox/constants.ts +6 -0
  149. package/lib/sandbox/dockerfile.ts +2 -1
  150. package/lib/sandbox/image-build.ts +4 -1
  151. package/lib/sandbox/managed-fs.ts +18 -5
  152. package/lib/sandbox/recovery.ts +98 -67
  153. package/lib/sandbox/runtimes/ai-tools.dockerfile +24 -3
  154. package/lib/sandbox/runtimes/base.dockerfile +8 -1
  155. package/lib/sandbox/runtimes/java17.dockerfile +8 -1
  156. package/lib/sandbox/runtimes/java21.dockerfile +8 -1
  157. package/lib/sandbox/runtimes/node20.dockerfile +8 -1
  158. package/lib/sandbox/runtimes/node22.dockerfile +8 -1
  159. package/lib/sandbox/runtimes/python3.dockerfile +8 -1
  160. package/lib/sandbox/shell.ts +4 -2
  161. package/lib/sandbox/tool-types.ts +143 -0
  162. package/lib/sandbox/tools.ts +5 -157
  163. package/lib/task/artifact-lifecycle.ts +1 -38
  164. package/lib/task/commands/decisions.ts +53 -20
  165. package/lib/task/commands/issue-body.ts +10 -5
  166. package/lib/task/delegation-receipts.ts +170 -0
  167. package/lib/task/events.ts +111 -3
  168. package/lib/task/issue-form.ts +4 -0
  169. package/lib/task/orchestration.ts +543 -0
  170. package/lib/task/review-artifacts.ts +201 -69
  171. package/lib/task/review-finalization.ts +275 -0
  172. package/lib/task/review-fingerprint.ts +15 -1
  173. package/lib/task/verification-checks.ts +2 -1
  174. package/lib/task/verification-engine.ts +190 -25
  175. package/lib/task/verification-review.ts +2 -0
  176. package/lib/task/verification.ts +6 -3
  177. package/lib/task/workspace-snapshot.ts +36 -0
  178. package/lib/update.ts +48 -184
  179. package/package.json +2 -2
  180. package/templates/.agents/README.en.md +21 -20
  181. package/templates/.agents/README.zh-CN.md +21 -20
  182. package/templates/.agents/hooks/lifecycle-delegation.js +64 -0
  183. package/templates/.agents/rules/README.en.md +2 -0
  184. package/templates/.agents/rules/README.zh-CN.md +2 -0
  185. package/templates/.agents/rules/commit-and-pr.en.md +1 -0
  186. package/templates/.agents/rules/commit-and-pr.zh-CN.md +1 -0
  187. package/templates/.agents/rules/human-decision-context.en.md +14 -12
  188. package/templates/.agents/rules/human-decision-context.zh-CN.md +14 -12
  189. package/templates/.agents/rules/lifecycle-orchestration.en.md +21 -0
  190. package/templates/.agents/rules/lifecycle-orchestration.zh-CN.md +21 -0
  191. package/templates/.agents/rules/next-step-output.en.md +16 -0
  192. package/templates/.agents/rules/next-step-output.zh-CN.md +16 -0
  193. package/templates/.agents/rules/no-mid-flow-questions.en.md +2 -0
  194. package/templates/.agents/rules/no-mid-flow-questions.zh-CN.md +2 -0
  195. package/templates/.agents/rules/release-commands.en.md +2 -2
  196. package/templates/.agents/rules/release-commands.github.en.md +15 -39
  197. package/templates/.agents/rules/release-commands.github.zh-CN.md +14 -38
  198. package/templates/.agents/rules/release-commands.zh-CN.md +2 -2
  199. package/templates/.agents/rules/review-handshake.en.md +4 -3
  200. package/templates/.agents/rules/review-handshake.zh-CN.md +4 -3
  201. package/templates/.agents/rules/review-method.en.md +113 -0
  202. package/templates/.agents/rules/review-method.zh-CN.md +98 -0
  203. package/templates/.agents/rules/testing-discipline.en.md +20 -2
  204. package/templates/.agents/rules/testing-discipline.zh-CN.md +20 -2
  205. package/templates/.agents/skills/analyze-task/SKILL.en.md +9 -6
  206. package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +9 -6
  207. package/templates/.agents/skills/block-task/SKILL.en.md +4 -4
  208. package/templates/.agents/skills/block-task/SKILL.zh-CN.md +4 -4
  209. package/templates/.agents/skills/cancel-task/SKILL.en.md +4 -4
  210. package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +5 -5
  211. package/templates/.agents/skills/check-task/SKILL.en.md +16 -16
  212. package/templates/.agents/skills/check-task/SKILL.zh-CN.md +16 -16
  213. package/templates/.agents/skills/close-codescan/SKILL.en.md +4 -4
  214. package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +4 -4
  215. package/templates/.agents/skills/close-dependabot/SKILL.en.md +4 -4
  216. package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +4 -4
  217. package/templates/.agents/skills/code-task/SKILL.en.md +1 -1
  218. package/templates/.agents/skills/code-task/SKILL.zh-CN.md +2 -2
  219. package/templates/.agents/skills/code-task/reference/fix-mode.en.md +14 -16
  220. package/templates/.agents/skills/code-task/reference/fix-mode.zh-CN.md +14 -16
  221. package/templates/.agents/skills/code-task/reference/output-template.en.md +3 -3
  222. package/templates/.agents/skills/code-task/reference/output-template.zh-CN.md +3 -3
  223. package/templates/.agents/skills/commit/SKILL.en.md +11 -1
  224. package/templates/.agents/skills/commit/SKILL.zh-CN.md +11 -1
  225. package/templates/.agents/skills/commit/reference/task-status-update.en.md +24 -28
  226. package/templates/.agents/skills/commit/reference/task-status-update.zh-CN.md +24 -28
  227. package/templates/.agents/skills/complete-task/SKILL.en.md +3 -3
  228. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +3 -3
  229. package/templates/.agents/skills/complete-task/config/verify.en.json +0 -1
  230. package/templates/.agents/skills/complete-task/config/verify.zh-CN.json +0 -1
  231. package/templates/.agents/skills/create-pr/SKILL.en.md +10 -10
  232. package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +10 -10
  233. package/templates/.agents/skills/create-pr/reference/pr-body-template.en.md +3 -3
  234. package/templates/.agents/skills/create-pr/reference/pr-body-template.zh-CN.md +3 -3
  235. package/templates/.agents/skills/create-release-note/SKILL.en.md +17 -38
  236. package/templates/.agents/skills/create-release-note/SKILL.zh-CN.md +16 -37
  237. package/templates/.agents/skills/create-task/SKILL.en.md +15 -11
  238. package/templates/.agents/skills/create-task/SKILL.zh-CN.md +15 -11
  239. package/templates/.agents/skills/create-task/reference/context-capture.en.md +29 -0
  240. package/templates/.agents/skills/create-task/reference/context-capture.zh-CN.md +29 -0
  241. package/templates/.agents/skills/import-codescan/SKILL.en.md +5 -5
  242. package/templates/.agents/skills/import-codescan/SKILL.zh-CN.md +5 -5
  243. package/templates/.agents/skills/import-dependabot/SKILL.en.md +5 -5
  244. package/templates/.agents/skills/import-dependabot/SKILL.zh-CN.md +5 -5
  245. package/templates/.agents/skills/import-issue/SKILL.en.md +5 -5
  246. package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +5 -5
  247. package/templates/.agents/skills/init-labels/SKILL.en.md +4 -4
  248. package/templates/.agents/skills/init-labels/SKILL.zh-CN.md +4 -4
  249. package/templates/.agents/skills/init-milestones/SKILL.en.md +4 -4
  250. package/templates/.agents/skills/init-milestones/SKILL.zh-CN.md +4 -4
  251. package/templates/.agents/skills/plan-task/SKILL.en.md +5 -5
  252. package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +5 -5
  253. package/templates/.agents/skills/refine-title/SKILL.en.md +1 -1
  254. package/templates/.agents/skills/refine-title/SKILL.zh-CN.md +1 -1
  255. package/templates/.agents/skills/release/SKILL.en.md +1 -1
  256. package/templates/.agents/skills/review-analysis/SKILL.en.md +13 -2
  257. package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +14 -3
  258. package/templates/.agents/skills/review-analysis/config/verify.en.json +6 -0
  259. package/templates/.agents/skills/review-analysis/config/verify.zh-CN.json +6 -0
  260. package/templates/.agents/skills/review-analysis/reference/output-templates.en.md +13 -15
  261. package/templates/.agents/skills/review-analysis/reference/output-templates.zh-CN.md +13 -15
  262. package/templates/.agents/skills/review-analysis/reference/report-template.en.md +51 -5
  263. package/templates/.agents/skills/review-analysis/reference/report-template.zh-CN.md +52 -6
  264. package/templates/.agents/skills/review-analysis/reference/review-criteria.en.md +25 -22
  265. package/templates/.agents/skills/review-analysis/reference/review-criteria.zh-CN.md +25 -22
  266. package/templates/.agents/skills/review-code/SKILL.en.md +28 -8
  267. package/templates/.agents/skills/review-code/SKILL.zh-CN.md +28 -8
  268. package/templates/.agents/skills/review-code/config/verify.en.json +7 -0
  269. package/templates/.agents/skills/review-code/config/verify.zh-CN.json +7 -0
  270. package/templates/.agents/skills/review-code/reference/concurrency-risks.en.md +25 -0
  271. package/templates/.agents/skills/review-code/reference/concurrency-risks.zh-CN.md +25 -0
  272. package/templates/.agents/skills/review-code/reference/cross-platform-risks.en.md +27 -0
  273. package/templates/.agents/skills/review-code/reference/cross-platform-risks.zh-CN.md +27 -0
  274. package/templates/.agents/skills/review-code/reference/documentation-antipatterns.en.md +13 -0
  275. package/templates/.agents/skills/review-code/reference/documentation-antipatterns.zh-CN.md +13 -0
  276. package/templates/.agents/skills/review-code/reference/migration-risks.en.md +25 -0
  277. package/templates/.agents/skills/review-code/reference/migration-risks.zh-CN.md +25 -0
  278. package/templates/.agents/skills/review-code/reference/output-templates.en.md +22 -24
  279. package/templates/.agents/skills/review-code/reference/output-templates.zh-CN.md +22 -24
  280. package/templates/.agents/skills/review-code/reference/report-template.en.md +54 -7
  281. package/templates/.agents/skills/review-code/reference/report-template.zh-CN.md +54 -7
  282. package/templates/.agents/skills/review-code/reference/review-criteria.en.md +29 -24
  283. package/templates/.agents/skills/review-code/reference/review-criteria.zh-CN.md +29 -24
  284. package/templates/.agents/skills/review-code/reference/security-risks.en.md +25 -0
  285. package/templates/.agents/skills/review-code/reference/security-risks.zh-CN.md +25 -0
  286. package/templates/.agents/skills/review-plan/SKILL.en.md +13 -2
  287. package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +14 -3
  288. package/templates/.agents/skills/review-plan/config/verify.en.json +6 -0
  289. package/templates/.agents/skills/review-plan/config/verify.zh-CN.json +6 -0
  290. package/templates/.agents/skills/review-plan/reference/output-templates.en.md +13 -15
  291. package/templates/.agents/skills/review-plan/reference/output-templates.zh-CN.md +13 -15
  292. package/templates/.agents/skills/review-plan/reference/report-template.en.md +61 -5
  293. package/templates/.agents/skills/review-plan/reference/report-template.zh-CN.md +61 -5
  294. package/templates/.agents/skills/review-plan/reference/review-criteria.en.md +21 -24
  295. package/templates/.agents/skills/review-plan/reference/review-criteria.zh-CN.md +21 -24
  296. package/templates/.agents/skills/run-task/SKILL.en.md +22 -0
  297. package/templates/.agents/skills/run-task/SKILL.zh-CN.md +22 -0
  298. package/templates/.agents/skills/run-task/config/verify.en.json +9 -0
  299. package/templates/.agents/skills/run-task/config/verify.zh-CN.json +9 -0
  300. package/templates/.agents/skills/test/SKILL.en.md +4 -4
  301. package/templates/.agents/skills/test/SKILL.zh-CN.md +4 -4
  302. package/templates/.agents/skills/test-integration/SKILL.en.md +4 -4
  303. package/templates/.agents/skills/test-integration/SKILL.zh-CN.md +4 -4
  304. package/templates/.agents/skills/update-agent-infra/SKILL.en.md +5 -5
  305. package/templates/.agents/skills/update-agent-infra/SKILL.zh-CN.md +5 -5
  306. package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +647 -203
  307. package/templates/.agents/skills/upgrade-dependency/SKILL.en.md +4 -4
  308. package/templates/.agents/skills/upgrade-dependency/SKILL.zh-CN.md +4 -4
  309. package/templates/.agents/skills/watch-pr/SKILL.en.md +8 -8
  310. package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +8 -8
  311. package/templates/.agents/templates/task.en.md +18 -0
  312. package/templates/.agents/templates/task.zh-CN.md +18 -0
  313. package/templates/.claude/agents/agent-infra-lifecycle-executor.en.md +6 -0
  314. package/templates/.claude/agents/agent-infra-lifecycle-executor.zh-CN.md +6 -0
  315. package/templates/.claude/agents/agent-infra-lifecycle-reviewer.en.md +6 -0
  316. package/templates/.claude/agents/agent-infra-lifecycle-reviewer.zh-CN.md +6 -0
  317. package/templates/.claude/commands/run-task.en.md +8 -0
  318. package/templates/.claude/commands/run-task.zh-CN.md +8 -0
  319. package/templates/.claude/settings.json +24 -0
  320. package/templates/.codex/agents/agent-infra-lifecycle-executor.en.toml +3 -0
  321. package/templates/.codex/agents/agent-infra-lifecycle-executor.zh-CN.toml +3 -0
  322. package/templates/.codex/agents/agent-infra-lifecycle-reviewer.en.toml +3 -0
  323. package/templates/.codex/agents/agent-infra-lifecycle-reviewer.zh-CN.toml +3 -0
  324. package/templates/.gemini/commands/_project_/run-task.en.toml +8 -0
  325. package/templates/.gemini/commands/_project_/run-task.zh-CN.toml +8 -0
  326. package/templates/.opencode/commands/run-task.en.md +11 -0
  327. package/templates/.opencode/commands/run-task.zh-CN.md +11 -0
@@ -1,3 +1,10 @@
1
- RUN bash -o pipefail -c 'curl -fsSL --retry 5 --retry-delay 2 --retry-all-errors https://deb.nodesource.com/setup_20.x | bash -' && \
1
+ RUN --mount=type=secret,id=HTTP_PROXY \
2
+ --mount=type=secret,id=HTTPS_PROXY \
3
+ --mount=type=secret,id=NO_PROXY \
4
+ export HTTP_PROXY="$(cat /run/secrets/HTTP_PROXY 2>/dev/null || true)" && \
5
+ export HTTPS_PROXY="$(cat /run/secrets/HTTPS_PROXY 2>/dev/null || true)" && \
6
+ export NO_PROXY="$(cat /run/secrets/NO_PROXY 2>/dev/null || true)" && \
7
+ export http_proxy="${HTTP_PROXY}" https_proxy="${HTTPS_PROXY}" no_proxy="${NO_PROXY}" && \
8
+ bash -o pipefail -c 'curl -fsSL --retry 5 --retry-delay 2 --retry-all-errors https://deb.nodesource.com/setup_20.x | bash -' && \
2
9
  apt-get install -y nodejs && \
3
10
  rm -rf /var/lib/apt/lists/*
@@ -1,3 +1,10 @@
1
- RUN bash -o pipefail -c 'curl -fsSL --retry 5 --retry-delay 2 --retry-all-errors https://deb.nodesource.com/setup_22.x | bash -' && \
1
+ RUN --mount=type=secret,id=HTTP_PROXY \
2
+ --mount=type=secret,id=HTTPS_PROXY \
3
+ --mount=type=secret,id=NO_PROXY \
4
+ export HTTP_PROXY="$(cat /run/secrets/HTTP_PROXY 2>/dev/null || true)" && \
5
+ export HTTPS_PROXY="$(cat /run/secrets/HTTPS_PROXY 2>/dev/null || true)" && \
6
+ export NO_PROXY="$(cat /run/secrets/NO_PROXY 2>/dev/null || true)" && \
7
+ export http_proxy="${HTTP_PROXY}" https_proxy="${HTTPS_PROXY}" no_proxy="${NO_PROXY}" && \
8
+ bash -o pipefail -c 'curl -fsSL --retry 5 --retry-delay 2 --retry-all-errors https://deb.nodesource.com/setup_22.x | bash -' && \
2
9
  apt-get install -y nodejs && \
3
10
  rm -rf /var/lib/apt/lists/*
@@ -1,3 +1,10 @@
1
- RUN apt-get update && apt-get install -y \
1
+ RUN --mount=type=secret,id=HTTP_PROXY \
2
+ --mount=type=secret,id=HTTPS_PROXY \
3
+ --mount=type=secret,id=NO_PROXY \
4
+ export HTTP_PROXY="$(cat /run/secrets/HTTP_PROXY 2>/dev/null || true)" && \
5
+ export HTTPS_PROXY="$(cat /run/secrets/HTTPS_PROXY 2>/dev/null || true)" && \
6
+ export NO_PROXY="$(cat /run/secrets/NO_PROXY 2>/dev/null || true)" && \
7
+ export http_proxy="${HTTP_PROXY}" https_proxy="${HTTPS_PROXY}" no_proxy="${NO_PROXY}" && \
8
+ apt-get update && apt-get install -y \
2
9
  python3 python3-pip python3-venv \
3
10
  && rm -rf /var/lib/apt/lists/*
@@ -12,6 +12,7 @@ type CommandOptions = {
12
12
  stdio?: StdioOptions;
13
13
  timeout?: number;
14
14
  shell?: boolean;
15
+ env?: NodeJS.ProcessEnv;
15
16
  };
16
17
 
17
18
  type RunProbeOptions = CommandOptions & {
@@ -23,7 +24,8 @@ function normalizeOptions(opts: CommandOptions = {}, stdio: StdioOptions): Comma
23
24
  cwd: opts.cwd,
24
25
  encoding: opts.encoding,
25
26
  stdio,
26
- timeout: opts.timeout ?? DEFAULT_TIMEOUT_MS
27
+ timeout: opts.timeout ?? DEFAULT_TIMEOUT_MS,
28
+ env: opts.env
27
29
  };
28
30
  }
29
31
 
@@ -141,7 +143,7 @@ export function runSafe(cmd: string, args: string[], opts: CommandOptions = {}):
141
143
  export function commandForEngine(engine: string, cmd: string, args: string[] = []): { cmd: string; args: string[] } {
142
144
  if (engine === 'wsl2') {
143
145
  const resolvedWrapper = resolveCommand('wsl.exe');
144
- return { cmd: resolvedWrapper, args: ['--', cmd, ...args] };
146
+ return { cmd: resolvedWrapper, args: ['--exec', cmd, ...args] };
145
147
  }
146
148
 
147
149
  if (cmd === 'docker') {
@@ -0,0 +1,143 @@
1
+ type SandboxToolInstall =
2
+ | Readonly<{ type: 'npm'; cmd: string }>
3
+ | Readonly<{ type: 'shell'; cmd: string }>;
4
+
5
+ type SandboxTool = {
6
+ id: string;
7
+ name: string;
8
+ install: SandboxToolInstall;
9
+ sandboxBase: string;
10
+ containerMount: string;
11
+ versionCmd: string;
12
+ setupHint: string;
13
+ envVars?: Record<string, string>;
14
+ hostPreSeedFiles?: Array<{ hostPath: string; sandboxName: string }>;
15
+ hostPreSeedDirs?: Array<{ hostDir: string; sandboxSubdir: string }>;
16
+ pathRewriteFiles?: string[];
17
+ hostLiveMounts?: Array<{ hostPath: string; containerSubpath: string }>;
18
+ postSetupCmds?: string[];
19
+ tmpfs?: { size?: string; seed?: string[] };
20
+ };
21
+
22
+ type SandboxAlias = Readonly<{
23
+ name: string;
24
+ command: string;
25
+ }>;
26
+
27
+ const SANDBOX_HOOK_PHASES = [
28
+ 'prepare',
29
+ 'before-container-create',
30
+ 'after-container-start',
31
+ 'before-enter'
32
+ ] as const;
33
+
34
+ type SandboxHookPhase = (typeof SANDBOX_HOOK_PHASES)[number];
35
+
36
+ type SandboxHookStatus =
37
+ | 'ready'
38
+ | 'skip-client-runtime'
39
+ | 'warning'
40
+ | 'fatal'
41
+ | 'healthy'
42
+ | 'hard-failure';
43
+
44
+ type SandboxHookCommand = Readonly<{
45
+ command: string;
46
+ args?: readonly string[];
47
+ cwd?: string;
48
+ env?: NodeJS.ProcessEnv;
49
+ }>;
50
+
51
+ type SandboxHookCommandResult = Readonly<{
52
+ stdout: string;
53
+ }>;
54
+
55
+ type AgentClientSandboxHookResult = Readonly<{
56
+ status: SandboxHookStatus;
57
+ message?: string;
58
+ }>;
59
+
60
+ type SandboxResolvedToolState = Readonly<{
61
+ tool: SandboxTool;
62
+ dir: string;
63
+ }>;
64
+
65
+ type SandboxHookCreateContext = Readonly<{
66
+ hostHome: string;
67
+ resolvedTools: readonly SandboxResolvedToolState[];
68
+ }>;
69
+
70
+ type AgentClientSandboxHookContext = Readonly<{
71
+ signal: AbortSignal;
72
+ runCommand(command: SandboxHookCommand): Promise<SandboxHookCommandResult>;
73
+ config?: Readonly<Record<string, unknown>>;
74
+ plan?: Readonly<Record<string, unknown>>;
75
+ create?: SandboxHookCreateContext;
76
+ }>;
77
+
78
+ type AgentClientSandboxHook = Readonly<{
79
+ id: string;
80
+ phase: SandboxHookPhase;
81
+ timeoutMs?: number;
82
+ run(context: AgentClientSandboxHookContext): Promise<AgentClientSandboxHookResult>;
83
+ }>;
84
+
85
+ type SandboxToolContext = Readonly<{
86
+ home: string;
87
+ project: string;
88
+ }>;
89
+
90
+ type SandboxRecoveryProbe = Readonly<{
91
+ script: string;
92
+ args: readonly string[];
93
+ user?: string;
94
+ }>;
95
+
96
+ type SandboxRecoveryFindingDescriptor = Readonly<{
97
+ repairKind: 'permissions' | 'builtin-link' | 'hard-failure';
98
+ message: string;
99
+ path?: string;
100
+ }>;
101
+
102
+ type SandboxRecoveryRepair = Readonly<{
103
+ user: string;
104
+ command: string;
105
+ args: readonly string[];
106
+ }>;
107
+
108
+ type AgentClientSandboxRecoveryCheck = Readonly<{
109
+ id: string;
110
+ when?: SandboxRecoveryProbe;
111
+ probe: SandboxRecoveryProbe;
112
+ finding: SandboxRecoveryFindingDescriptor;
113
+ repair?: SandboxRecoveryRepair;
114
+ }>;
115
+
116
+ type AgentClientSandboxDescriptor = Readonly<{
117
+ createTool(context: SandboxToolContext): SandboxTool;
118
+ aliases: readonly SandboxAlias[];
119
+ hooks: readonly AgentClientSandboxHook[];
120
+ recoveryChecks?: readonly AgentClientSandboxRecoveryCheck[];
121
+ }>;
122
+
123
+ export { SANDBOX_HOOK_PHASES };
124
+ export type {
125
+ AgentClientSandboxDescriptor,
126
+ AgentClientSandboxHook,
127
+ AgentClientSandboxHookContext,
128
+ AgentClientSandboxHookResult,
129
+ AgentClientSandboxRecoveryCheck,
130
+ SandboxAlias,
131
+ SandboxHookCommand,
132
+ SandboxHookCommandResult,
133
+ SandboxHookPhase,
134
+ SandboxHookStatus,
135
+ SandboxHookCreateContext,
136
+ SandboxRecoveryFindingDescriptor,
137
+ SandboxRecoveryProbe,
138
+ SandboxRecoveryRepair,
139
+ SandboxResolvedToolState,
140
+ SandboxTool,
141
+ SandboxToolContext,
142
+ SandboxToolInstall
143
+ };
@@ -1,33 +1,10 @@
1
1
  import path from 'node:path';
2
+ import { createSandboxCapabilityPlan } from './agent-client-reconciler.ts';
2
3
  import { safeNameCandidates, sanitizeBranchName } from './constants.ts';
3
4
  import { hostJoin } from './engines/wsl2-paths.ts';
5
+ import type { SandboxTool, SandboxToolInstall } from './tool-types.ts';
4
6
 
5
- export type SandboxToolInstall =
6
- | { type: 'npm'; cmd: string }
7
- | { type: 'shell'; cmd: string };
8
-
9
- export type SandboxTool = {
10
- id: string;
11
- name: string;
12
- install: SandboxToolInstall;
13
- sandboxBase: string;
14
- containerMount: string;
15
- versionCmd: string;
16
- setupHint: string;
17
- envVars?: Record<string, string>;
18
- hostPreSeedFiles?: Array<{ hostPath: string; sandboxName: string }>;
19
- hostPreSeedDirs?: Array<{ hostDir: string; sandboxSubdir: string }>;
20
- pathRewriteFiles?: string[];
21
- hostLiveMounts?: Array<{ hostPath: string; containerSubpath: string }>;
22
- postSetupCmds?: string[];
23
- // When set, containerMount is mounted as an in-container tmpfs (RAM) instead
24
- // of bind-mounting the host config dir, keeping high-churn tool logs off the
25
- // host disk. `seed` lists the host-dir entries (relative to the tool's config
26
- // dir) to copy into the tmpfs when the container starts. It is an explicit
27
- // allowlist so runtime files (e.g. logs_2.sqlite, sessions) left in the host
28
- // dir stay out of the container and seeded config cannot write back.
29
- tmpfs?: { size?: string; seed?: string[] };
30
- };
7
+ export type { SandboxTool, SandboxToolInstall } from './tool-types.ts';
31
8
 
32
9
  export type TmpfsSeedEntry = {
33
10
  toolId: string;
@@ -64,114 +41,11 @@ type ToolsConfig = {
64
41
  project: string;
65
42
  tools: string[];
66
43
  customTools?: SandboxTool[];
44
+ agentClientState?: import('../agent-clients/types.ts').AgentClientState;
67
45
  };
68
46
 
69
47
  const TOOL_ID_PATTERN = /^[a-z0-9][a-z0-9-]*$/;
70
48
 
71
- function createBuiltinTools(home: string, project: string): Record<string, SandboxTool> {
72
- return {
73
- 'claude-code': {
74
- id: 'claude-code',
75
- name: 'Claude Code',
76
- install: { type: 'npm', cmd: '@anthropic-ai/claude-code@latest' },
77
- sandboxBase: hostJoin(home, '.agent-infra', 'sandboxes', 'claude-code'),
78
- containerMount: '/home/devuser/.claude',
79
- versionCmd: 'claude --version',
80
- setupHint: 'Authenticates via host credentials live-mounted at ~/.claude/.credentials.json',
81
- // Claude Code stores user data (.claude.json — onboarding state, theme,
82
- // workspace trust) at $HOME/.claude.json by default, which sits OUTSIDE
83
- // the bind-mounted /home/devuser/.claude tree, so our preseeded
84
- // .claude.json never gets read and the theme picker re-runs on every
85
- // container start. Pinning CLAUDE_CONFIG_DIR to the tool mount relocates
86
- // .claude.json into the same directory as .credentials.json/settings.json,
87
- // letting ensureClaudeOnboarding actually take effect.
88
- envVars: { CLAUDE_CONFIG_DIR: '/home/devuser/.claude' },
89
- hostPreSeedDirs: [
90
- { hostDir: hostJoin(home, '.claude', 'plugins'), sandboxSubdir: 'plugins' }
91
- ],
92
- pathRewriteFiles: [
93
- 'plugins/installed_plugins.json',
94
- 'plugins/known_marketplaces.json'
95
- ],
96
- hostLiveMounts: [
97
- {
98
- hostPath: hostJoin(home, '.agent-infra', 'credentials', project, 'claude-code', '.credentials.json'),
99
- containerSubpath: '.credentials.json'
100
- }
101
- ]
102
- },
103
- codex: {
104
- id: 'codex',
105
- name: 'Codex',
106
- install: { type: 'npm', cmd: '@openai/codex' },
107
- sandboxBase: hostJoin(home, '.agent-infra', 'sandboxes', 'codex'),
108
- containerMount: '/home/devuser/.codex',
109
- versionCmd: 'codex --version',
110
- setupHint: 'Run codex once inside the container and choose Device Code login if needed.',
111
- // codex churns ~/.codex/logs_2.sqlite heavily (upstream openai/codex#24275);
112
- // a bind-mount would write-amplify onto the host SSD via virtiofs. Mount the
113
- // codex home as tmpfs so those logs stay in RAM and die with the container.
114
- // Seeded config (config.toml, model-catalogs) is copied into the tmpfs at
115
- // startup; runtime files like logs_2.sqlite stay in RAM.
116
- tmpfs: { size: '512m', seed: ['config.toml', 'model-catalogs'] },
117
- hostLiveMounts: [
118
- { hostPath: hostJoin(home, '.codex', 'auth.json'), containerSubpath: 'auth.json' }
119
- ],
120
- postSetupCmds: [
121
- 'test -d /workspace/.codex/commands && ln -sfn /workspace/.codex/commands /home/devuser/.codex/prompts || true'
122
- ]
123
- },
124
- opencode: {
125
- id: 'opencode',
126
- name: 'OpenCode',
127
- install: { type: 'npm', cmd: 'opencode-ai' },
128
- sandboxBase: hostJoin(home, '.agent-infra', 'sandboxes', 'opencode'),
129
- containerMount: '/home/devuser/.local/share/opencode',
130
- versionCmd: 'opencode version',
131
- setupHint: 'Configure OpenCode credentials inside the container before first use.',
132
- // OpenCode reads opencode.json from $XDG_CONFIG_HOME/opencode by default,
133
- // outside this tool mount. Pin the config file path so the inherited
134
- // sandbox opencode.json is the one the TUI actually reads.
135
- envVars: { OPENCODE_CONFIG: '/home/devuser/.local/share/opencode/opencode.json' },
136
- hostLiveMounts: [
137
- {
138
- hostPath: hostJoin(home, '.local', 'share', 'opencode', 'auth.json'),
139
- containerSubpath: 'auth.json'
140
- }
141
- ]
142
- },
143
- 'gemini-cli': {
144
- id: 'gemini-cli',
145
- name: 'Gemini CLI',
146
- install: { type: 'npm', cmd: '@google/gemini-cli' },
147
- sandboxBase: hostJoin(home, '.agent-infra', 'sandboxes', 'gemini-cli'),
148
- containerMount: '/home/devuser/.gemini',
149
- versionCmd: 'gemini --version',
150
- setupHint: 'Run gemini inside the container to finish authentication.',
151
- hostLiveMounts: [
152
- { hostPath: hostJoin(home, '.gemini', 'oauth_creds.json'), containerSubpath: 'oauth_creds.json' }
153
- ],
154
- hostPreSeedFiles: [
155
- { hostPath: hostJoin(home, '.gemini', 'settings.json'), sandboxName: 'settings.json' },
156
- { hostPath: hostJoin(home, '.gemini', 'google_accounts.json'), sandboxName: 'google_accounts.json' }
157
- ]
158
- },
159
- 'agent-infra': {
160
- id: 'agent-infra',
161
- name: 'agent-infra CLI',
162
- install: { type: 'npm', cmd: '@fitlab-ai/agent-infra@latest' },
163
- sandboxBase: hostJoin(home, '.agent-infra', 'sandboxes', 'agent-infra'),
164
- containerMount: '/home/devuser/.agent-infra-cli',
165
- versionCmd: 'ai version --raw',
166
- setupHint: 'Provides the ai and agent-infra CLI commands inside the sandbox.'
167
- }
168
- };
169
- }
170
-
171
- export function builtinToolIds(): string[] {
172
- return Object.keys(createBuiltinTools('', ''));
173
- }
174
-
175
49
  function validateTool(tool: SandboxTool): void {
176
50
  if (!tool.id || !TOOL_ID_PATTERN.test(tool.id)) {
177
51
  throw new Error(`Invalid sandbox tool id: ${String(tool.id)}`);
@@ -379,33 +253,7 @@ export function parseCustomTools(value: unknown, options: { home: string }): San
379
253
  }
380
254
 
381
255
  export function resolveTools(config: ToolsConfig): SandboxTool[] {
382
- const builtins = createBuiltinTools(config.home, config.project);
383
- const customs = config.customTools ?? [];
384
-
385
- const seen = new Set<string>();
386
- for (const tool of customs) {
387
- if (builtins[tool.id]) {
388
- throw new Error(`Custom sandbox tool id "${tool.id}" collides with a built-in tool`);
389
- }
390
- if (seen.has(tool.id)) {
391
- throw new Error(`Duplicate sandbox tool id "${tool.id}" in customTools`);
392
- }
393
- seen.add(tool.id);
394
- }
395
-
396
- const merged: Record<string, SandboxTool> = { ...builtins };
397
- for (const tool of customs) {
398
- merged[tool.id] = tool;
399
- }
400
-
401
- return config.tools.map((id) => {
402
- const tool = merged[id];
403
- if (!tool) {
404
- throw new Error(`Unknown sandbox tool: ${id}`);
405
- }
406
- validateTool(tool);
407
- return tool;
408
- });
256
+ return [...createSandboxCapabilityPlan(config).tools];
409
257
  }
410
258
 
411
259
  export function toolConfigDir(tool: SandboxTool, project: string, branch: string): string {
@@ -5,6 +5,7 @@ import { resolveTaskRef } from './resolve-ref.ts';
5
5
  import type { ResolveTaskRefErrorCode, TaskWorkspaceState } from './resolve-ref.ts';
6
6
  import { locateActivityLog } from './activity-log.ts';
7
7
  import { parseImplementationInputs, selectPendingImplementationInput } from './implementation-inputs.ts';
8
+ import { parseVerdict } from './review-artifacts.ts';
8
9
  import { extractSection, findSectionHeading } from './sections.ts';
9
10
 
10
11
  const artifactFamilyCatalog = [
@@ -239,21 +240,6 @@ function resolveReviewedInput(
239
240
  }
240
241
  }
241
242
 
242
- function extractMarkdownSection(content: string, names: readonly string[]): string {
243
- const lines = content.split(/\r?\n/);
244
- const allowed = new Set(names);
245
- const start = lines.findIndex((line) => {
246
- const match = /^##\s+(.+?)\s*$/.exec(line.trim());
247
- return Boolean(match?.[1] && allowed.has(match[1]));
248
- });
249
- if (start === -1) return '';
250
- let end = lines.length;
251
- for (let index = start + 1; index < lines.length; index += 1) {
252
- if (/^##\s+/.test(lines[index]!)) { end = index; break; }
253
- }
254
- return lines.slice(start + 1, end).join('\n');
255
- }
256
-
257
243
  function assertWritableInventory(inventory: ArtifactInventoryResult): ArtifactError | null {
258
244
  if (inventory.status === 'failed') return inventory.error;
259
245
  const blockers = inventory.diagnostics.filter((item) => BLOCKING_DIAGNOSTICS.has(item.code));
@@ -398,29 +384,6 @@ function resolveDecisionImplementationInput(
398
384
  catch (error) { return { error: error instanceof Error ? error.message : String(error) }; }
399
385
  }
400
386
 
401
- function normalizeVerdict(value: string): 'Approved' | 'Changes Requested' | 'Rejected' | null {
402
- const normalized = value.trim().toLowerCase();
403
- if (normalized === '通过' || normalized === 'approved') return 'Approved';
404
- if (normalized === '需要修改' || normalized === 'changes requested') return 'Changes Requested';
405
- if (normalized === '拒绝' || normalized === 'rejected') return 'Rejected';
406
- return null;
407
- }
408
-
409
- function parseVerdict(file: string): { ok: true; verdict: string } | { ok: false; message: string } {
410
- let content: string;
411
- try { content = fs.readFileSync(file, 'utf8'); }
412
- catch (error) { return { ok: false, message: String(error) }; }
413
- const summary = extractMarkdownSection(content, ['审查摘要', 'Review Summary']);
414
- const verdictMatch = /^[-*]?\s*\*\*(?:总体结论|Overall Verdict)\*\*[::]\s*(.+?)\s*$/im.exec(summary);
415
- const verdict = verdictMatch?.[1] ? normalizeVerdict(verdictMatch[1]) : null;
416
- if (!verdict) return { ok: false, message: `cannot parse verdict in ${path.basename(file)}` };
417
- if (verdict !== 'Approved') return { ok: true, verdict };
418
- const findings = /^[-*]?\s*\*\*(?:发现(AI 可处理)|Findings \(AI-actionable\))\*\*[::]\s*(.+?)\s*$/im.exec(summary)?.[1];
419
- const counts = findings?.match(/(\d+)\s*(?:阻塞项|blockers?).*?(\d+)\s*(?:主要|majors?).*?(\d+)\s*(?:次要|minors?)/i);
420
- if (!counts) return { ok: false, message: `cannot parse findings count in ${path.basename(file)}` };
421
- return { ok: true, verdict: counts.slice(1).some((value) => Number(value) > 0) ? 'Approved-with-issues' : 'Approved' };
422
- }
423
-
424
387
  function validateCompletedArtifact(taskDir: string, family: ArtifactFamily, name: string, round?: number): { ok: true; artifact: ArtifactIdentity } | { ok: false; error: ArtifactError } {
425
388
  if (path.basename(name) !== name) return { ok: false, error: { code: 'ARTIFACT_PATH_INVALID', message: 'artifact must be a top-level basename' } };
426
389
  const parsed = parseArtifactName(name);
@@ -10,8 +10,9 @@ import { extractSubSection } from '../sections.ts';
10
10
  const USAGE = `Usage: ai task decisions [--task <ref> | -t <ref>] [--item <selector> | -i <selector>] [options]
11
11
  ai task decisions <ref> [selector] [options]
12
12
 
13
- Lists decision items recorded in a task's review disagreement
14
- ledger, or prints the full detail block for a single item. Read-only.
13
+ Shows what still needs a maintainer's decision. Without a selector, it lists
14
+ the choices. With a selector, it shows the explanation and how to respond.
15
+ Read-only.
15
16
 
16
17
  Omit <ref> Resolve the unique active task for the current branch.
17
18
  <ref> Legacy positional task ref.
@@ -150,29 +151,31 @@ function findDecisionRecord(id: string, content: string): string[] {
150
151
  function titleOf(row: LedgerRow, taskDir: string): string {
151
152
  const block = findDetailBlock(row, taskDir);
152
153
  if (block) {
153
- return block
154
+ const title = block
154
155
  .split('\n')[0]!
155
156
  .replace(/^###\s+/, '')
156
157
  .replace(/\s*\[needs-human-decision\]\s*$/, '')
157
158
  .trim();
159
+ return title.replace(new RegExp(`^${row.id}\\s*[::]\\s*`, 'i'), '').trim();
158
160
  }
159
- return row.evidence || '(无详情)';
161
+ return '(explanation unavailable)';
162
+ }
163
+
164
+ function trackingOf(row: LedgerRow): string {
165
+ return `stage=${row.stage} · severity=${row.severity} · status=${row.status} · evidence=${row.evidence}`;
160
166
  }
161
167
 
162
168
  function renderList(rows: LedgerRow[], format: string, taskDir: string): void {
163
169
  if (rows.length === 0) {
164
- process.stdout.write('无待裁决项。\n');
170
+ process.stdout.write('No pending decisions.\n');
165
171
  return;
166
172
  }
167
- const headers = ['#', 'ID', 'STAGE', 'SEVERITY', 'STATUS', 'EVIDENCE', 'TITLE'];
173
+ const headers = ['#', 'CHOICE', 'WHAT NEEDS A DECISION', 'TRACKING'];
168
174
  const data = rows.map((r, i) => [
169
175
  String(i + 1),
170
176
  r.id,
171
- r.stage,
172
- r.severity,
173
- r.status,
174
- r.evidence,
175
- titleOf(r, taskDir)
177
+ titleOf(r, taskDir),
178
+ trackingOf(r)
176
179
  ]);
177
180
  if (format === 'markdown') {
178
181
  const sep = headers.map(() => '---');
@@ -181,10 +184,29 @@ function renderList(rows: LedgerRow[], format: string, taskDir: string): void {
181
184
  `| ${sep.join(' | ')} |`,
182
185
  ...data.map((row) => `| ${row.join(' | ')} |`)
183
186
  ];
184
- process.stdout.write(`${md.join('\n')}\n`);
187
+ process.stdout.write(`${md.join('\n')}\n\nView one item: ai task decisions <task-ref> <ordinal|ledger-id>\n`);
185
188
  return;
186
189
  }
187
- process.stdout.write(`${formatTable(headers, data).join('\n')}\n`);
190
+ process.stdout.write(`${formatTable(headers, data).join('\n')}\n\nView one item: ai task decisions <task-ref> <ordinal|ledger-id>\n`);
191
+ }
192
+
193
+ function renderTracking(row: LedgerRow, format: string): string[] {
194
+ if (format === 'markdown') {
195
+ return [
196
+ '**Tracking**',
197
+ `- stage: \`${row.stage}\``,
198
+ `- severity: \`${row.severity}\``,
199
+ `- status: \`${row.status}\``,
200
+ `- evidence: \`${row.evidence}\``
201
+ ];
202
+ }
203
+ return [
204
+ 'Tracking:',
205
+ `stage: ${row.stage}`,
206
+ `severity: ${row.severity}`,
207
+ `status: ${row.status}`,
208
+ `evidence: ${row.evidence}`
209
+ ];
188
210
  }
189
211
 
190
212
  function renderDetail(
@@ -201,25 +223,36 @@ function renderDetail(
201
223
  }
202
224
  const r = selected.row;
203
225
  const block = findDetailBlock(r, taskDir);
226
+ const title = titleOf(r, taskDir);
204
227
  const lines: string[] = [];
228
+ const decided = r.status === 'human-decided';
205
229
  if (format === 'markdown') {
206
- lines.push(`**${r.id}** (${r.stage}/${r.severity}) · status=\`${r.status}\` · evidence: \`${r.evidence}\``, '');
230
+ lines.push(`## ${decided ? 'Decision already recorded' : 'Decision needed'}: ${title}`, '');
231
+ if (!decided) {
232
+ lines.push('**How to record your choice**', `\`ai decide <task-ref> ${r.id} <your choice and rationale>\``, '');
233
+ }
234
+ lines.push('**Original context**', '');
207
235
  } else {
208
- lines.push(`${r.id} (${r.stage}/${r.severity}) status=${r.status}`, `evidence: ${r.evidence}`, '');
236
+ lines.push(`${decided ? 'Decision already recorded' : 'Decision needed'}: ${title}`, '');
237
+ if (!decided) {
238
+ lines.push('How to record your choice:', `ai decide <task-ref> ${r.id} <your choice and rationale>`, '');
239
+ }
240
+ lines.push('Original context:');
209
241
  }
210
242
  if (block) {
211
243
  lines.push(block);
212
244
  } else {
213
245
  lines.push(
214
- `(详情块未找到:未在任务产物中定位到 \`### ${r.id}\` 锚点,可能为历史产物或尚未写入;evidence 指向 ${r.evidence})`
246
+ `This older task does not include a full explanation for ${r.id}.`,
247
+ 'Use the tracking reference below to find the original context.'
215
248
  );
216
249
  }
217
- if (r.status === 'human-decided') {
250
+ if (decided) {
218
251
  const record = findDecisionRecord(r.id, content);
219
- if (record.length) {
220
- lines.push('', '人工裁定:', ...record);
221
- }
252
+ lines.push('', format === 'markdown' ? '**Recorded choice**' : 'Recorded choice:');
253
+ lines.push(...(record.length ? record : ['No separate ruling record was found.']));
222
254
  }
255
+ lines.push('', ...renderTracking(r, format));
223
256
  process.stdout.write(`${lines.join('\n')}\n`);
224
257
  }
225
258
 
@@ -12,12 +12,13 @@ Print a deterministic Issue body extracted from a task's task.md.
12
12
  Omit <ref> Resolve the unique active task for the current branch.
13
13
  <ref> Bare numeric short id, or a full TASK-YYYYMMDD-HHMMSS id.
14
14
  --template <path> Render the final body for the given GitHub Issue Form (scenario A);
15
- without it, print the default '描述 + 需求' body (scenario B).
15
+ without it, print the default '任务输入 + 描述 + 需求' body (scenario B).
16
16
 
17
- Only the task title, '## 描述' and '## 需求' sections are ever emitted; the rest of
18
- task.md (scaffolding sections, placeholders) is never written to the body.
17
+ Only the task title, '## 任务输入', '## 描述' and '## 需求' sections are ever
18
+ emitted; the rest of task.md is never written to the body.
19
19
  `;
20
20
 
21
+ const TASK_INPUT_ALIASES = ['任务输入', 'Task Input'];
21
22
  const DESCRIPTION_ALIASES = ['描述', 'Description'];
22
23
  const REQUIREMENTS_ALIASES = ['需求', 'Requirements'];
23
24
 
@@ -26,18 +27,22 @@ const REQUIREMENTS_ALIASES = ['需求', 'Requirements'];
26
27
  * task.md actually uses, with empty sections falling back to `N/A`.
27
28
  */
28
29
  function buildDefaultBody(content: string): string {
30
+ const taskInputHeading = findSectionHeading(content, TASK_INPUT_ALIASES);
29
31
  const descHeading = findSectionHeading(content, DESCRIPTION_ALIASES);
30
32
  const reqHeading = findSectionHeading(content, REQUIREMENTS_ALIASES);
33
+ const taskInput = extractSection(content, TASK_INPUT_ALIASES) || PLACEHOLDER;
31
34
  const description = extractSection(content, DESCRIPTION_ALIASES) || PLACEHOLDER;
32
35
  const requirements = extractSection(content, REQUIREMENTS_ALIASES) || PLACEHOLDER;
33
- return `## ${descHeading}\n\n${description}\n\n## ${reqHeading}\n\n${requirements}\n`;
36
+ return `## ${taskInputHeading}\n\n${taskInput}\n\n## ${descHeading}\n\n${description}\n\n## ${reqHeading}\n\n${requirements}\n`;
34
37
  }
35
38
 
36
39
  function readTaskFields(content: string): TaskFields {
37
40
  return {
38
41
  title: extractTitle(content),
39
42
  description: extractSection(content, DESCRIPTION_ALIASES),
40
- requirements: extractSection(content, REQUIREMENTS_ALIASES)
43
+ requirements: extractSection(content, REQUIREMENTS_ALIASES),
44
+ taskInput: extractSection(content, TASK_INPUT_ALIASES),
45
+ taskInputHeading: findSectionHeading(content, TASK_INPUT_ALIASES)
41
46
  };
42
47
  }
43
48