@fitlab-ai/agent-infra 0.9.10 → 0.9.12

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 (375) hide show
  1. package/README.md +19 -9
  2. package/README.zh-CN.md +19 -9
  3. package/bin/cli.ts +17 -4
  4. package/dist/bin/cli.js +19 -4
  5. package/dist/bin/internal-cli.js +55 -4
  6. package/dist/lib/agent-client.js +0 -1
  7. package/dist/lib/agent-clients/adapters/codex-lifecycle/build-identity.js +71 -10
  8. package/dist/lib/agent-clients/adapters/opencode-sandbox.js +0 -4
  9. package/dist/lib/agent-clients/adapters/traecli.js +7 -8
  10. package/dist/lib/agent-clients/config.js +14 -99
  11. package/dist/lib/agent-clients/reconcile.js +1 -11
  12. package/dist/lib/agent-clients/schema.js +5 -4
  13. package/dist/lib/agent-clients/tokens.js +5 -15
  14. package/dist/lib/decide.js +14 -30
  15. package/dist/lib/defaults.json +8 -0
  16. package/dist/lib/git/workflow.js +43 -1
  17. package/dist/lib/init.js +3 -2
  18. package/dist/lib/internal/git-workflow.js +8 -1
  19. package/dist/lib/internal/platform-pr.js +8 -2
  20. package/dist/lib/internal/release-workflow.js +0 -1
  21. package/dist/lib/internal/task-delivery.js +59 -0
  22. package/dist/lib/internal/task-finalization.js +19 -9
  23. package/dist/lib/internal/task-ledger.js +8 -2
  24. package/dist/lib/internal/task-lifecycle.js +17 -93
  25. package/dist/lib/internal/task-orchestration.js +30 -216
  26. package/dist/lib/internal/task-validate.js +2 -1
  27. package/dist/lib/merge.js +205 -103
  28. package/dist/lib/platform/github-client.js +7 -1
  29. package/dist/lib/platform/pr-checks.js +8 -5
  30. package/dist/lib/platform/pr-summary.js +12 -8
  31. package/dist/lib/platform/pull-requests.js +393 -91
  32. package/dist/lib/platform/verification-required.js +13 -2
  33. package/dist/lib/platform/verification-sync.js +9 -4
  34. package/dist/lib/pr-review/evidence-grading.js +12 -4
  35. package/dist/lib/process-data/normalize.js +2 -9
  36. package/dist/lib/run/host.js +46 -16
  37. package/dist/lib/sandbox/agent-client-reconciler.js +4 -12
  38. package/dist/lib/sandbox/commands/create.js +3 -3
  39. package/dist/lib/sandbox/commands/enter.js +18 -4
  40. package/dist/lib/sandbox/config.js +8 -12
  41. package/dist/lib/sandbox/control/executor.js +131 -39
  42. package/dist/lib/sandbox/control/lifecycle.js +55 -24
  43. package/dist/lib/sandbox/control/server.js +18 -16
  44. package/dist/lib/sandbox/mounts.js +3 -2
  45. package/dist/lib/sandbox/recovery.js +123 -22
  46. package/dist/lib/sandbox/workspace-identity.js +14 -0
  47. package/dist/lib/sandbox/workspace-view.js +8 -9
  48. package/dist/lib/self-update.js +278 -0
  49. package/dist/lib/server/process-state.js +2 -2
  50. package/dist/lib/server/protocol.js +5 -5
  51. package/dist/lib/task/activity-log.js +4 -1
  52. package/dist/lib/task/artifact-lifecycle.js +6 -4
  53. package/dist/lib/task/codex-orchestration.js +1 -1
  54. package/dist/lib/task/command-options.js +1 -3
  55. package/dist/lib/task/commands/cat.js +0 -5
  56. package/dist/lib/task/commands/decisions.js +76 -92
  57. package/dist/lib/task/commands/files.js +6 -6
  58. package/dist/lib/task/commands/grep.js +25 -61
  59. package/dist/lib/task/commands/issue-body.js +6 -6
  60. package/dist/lib/task/commands/log.js +25 -10
  61. package/dist/lib/task/commands/show.js +5 -6
  62. package/dist/lib/task/commands/status.js +8 -8
  63. package/dist/lib/task/commit-intent.js +100 -0
  64. package/dist/lib/task/commit-operation.js +181 -91
  65. package/dist/lib/task/commit-policy.js +1 -1
  66. package/dist/lib/task/control-authority.js +431 -0
  67. package/dist/lib/task/create.js +39 -8
  68. package/dist/lib/task/current-contract.js +35 -0
  69. package/dist/lib/task/decision-details.js +218 -0
  70. package/dist/lib/task/decision-intents.js +5 -2
  71. package/dist/lib/task/delivery-target.js +97 -0
  72. package/dist/lib/task/delivery.js +153 -0
  73. package/dist/lib/task/events.js +11 -5
  74. package/dist/lib/task/human-override.js +5 -2
  75. package/dist/lib/task/index.js +19 -20
  76. package/dist/lib/task/ledger-intents.js +8 -9
  77. package/dist/lib/task/ledger.js +13 -6
  78. package/dist/lib/task/lifecycle.js +122 -9
  79. package/dist/lib/task/orchestration.js +105 -75
  80. package/dist/lib/task/pr-delivery-fact.js +182 -0
  81. package/dist/lib/task/review-artifacts.js +31 -12
  82. package/dist/lib/task/review-finalization.js +13 -4
  83. package/dist/lib/task/review-fingerprint.js +8 -0
  84. package/dist/lib/task/verification-engine.js +86 -39
  85. package/dist/lib/task/verification-review.js +2 -2
  86. package/dist/lib/task/workspace-snapshot.js +5 -11
  87. package/dist/lib/task/write.js +11 -1
  88. package/dist/lib/update.js +13 -35
  89. package/dist/lib/version.js +5 -1
  90. package/dist/lifecycle-build-manifest.json +3 -3
  91. package/lib/agent-client.ts +0 -1
  92. package/lib/agent-clients/adapters/codex-lifecycle/build-identity.ts +105 -10
  93. package/lib/agent-clients/adapters/codex-lifecycle/manifest-files.json +12 -1
  94. package/lib/agent-clients/adapters/opencode-sandbox.ts +0 -5
  95. package/lib/agent-clients/adapters/traecli.ts +7 -8
  96. package/lib/agent-clients/config.ts +14 -144
  97. package/lib/agent-clients/reconcile.ts +1 -12
  98. package/lib/agent-clients/schema.ts +5 -4
  99. package/lib/agent-clients/tokens.ts +5 -18
  100. package/lib/decide.ts +12 -22
  101. package/lib/defaults.json +8 -0
  102. package/lib/git/workflow.ts +38 -1
  103. package/lib/init.ts +5 -2
  104. package/lib/internal/git-workflow.ts +7 -1
  105. package/lib/internal/platform-pr.ts +8 -1
  106. package/lib/internal/release-workflow.ts +0 -1
  107. package/lib/internal/task-delivery.ts +53 -0
  108. package/lib/internal/task-finalization.ts +27 -13
  109. package/lib/internal/task-ledger.ts +8 -2
  110. package/lib/internal/task-lifecycle.ts +33 -76
  111. package/lib/internal/task-orchestration.ts +33 -190
  112. package/lib/internal/task-validate.ts +3 -2
  113. package/lib/merge.ts +236 -120
  114. package/lib/platform/github-client.ts +8 -1
  115. package/lib/platform/pr-checks.ts +7 -5
  116. package/lib/platform/pr-summary.ts +10 -7
  117. package/lib/platform/pull-requests.ts +425 -87
  118. package/lib/platform/verification-required.ts +12 -2
  119. package/lib/platform/verification-sync.ts +9 -4
  120. package/lib/pr-review/evidence-grading.ts +7 -4
  121. package/lib/process-data/normalize.ts +2 -8
  122. package/lib/run/host.ts +63 -14
  123. package/lib/sandbox/agent-client-reconciler.ts +5 -15
  124. package/lib/sandbox/commands/create.ts +4 -4
  125. package/lib/sandbox/commands/enter.ts +24 -4
  126. package/lib/sandbox/config.ts +9 -14
  127. package/lib/sandbox/control/container-identity.ts +2 -2
  128. package/lib/sandbox/control/executor.ts +148 -51
  129. package/lib/sandbox/control/lifecycle.ts +59 -31
  130. package/lib/sandbox/control/protocol.ts +1 -3
  131. package/lib/sandbox/control/server.ts +15 -12
  132. package/lib/sandbox/control/state.ts +7 -7
  133. package/lib/sandbox/mounts.ts +4 -3
  134. package/lib/sandbox/recovery.ts +162 -34
  135. package/lib/sandbox/tools.ts +1 -1
  136. package/lib/sandbox/workspace-identity.ts +28 -0
  137. package/lib/sandbox/workspace-view.ts +14 -10
  138. package/lib/self-update.ts +341 -0
  139. package/lib/server/process-state.ts +2 -2
  140. package/lib/server/protocol.ts +5 -5
  141. package/lib/task/activity-log.ts +6 -1
  142. package/lib/task/artifact-lifecycle.ts +7 -5
  143. package/lib/task/codex-orchestration.ts +2 -1
  144. package/lib/task/command-options.ts +1 -2
  145. package/lib/task/commands/cat.ts +0 -4
  146. package/lib/task/commands/decisions.ts +80 -75
  147. package/lib/task/commands/files.ts +6 -6
  148. package/lib/task/commands/grep.ts +23 -51
  149. package/lib/task/commands/issue-body.ts +6 -6
  150. package/lib/task/commands/log.ts +22 -10
  151. package/lib/task/commands/show.ts +5 -6
  152. package/lib/task/commands/status.ts +8 -8
  153. package/lib/task/commit-intent.ts +137 -0
  154. package/lib/task/commit-operation.ts +161 -79
  155. package/lib/task/commit-policy.ts +1 -2
  156. package/lib/task/control-authority.ts +563 -0
  157. package/lib/task/create.ts +40 -10
  158. package/lib/task/current-contract.ts +49 -0
  159. package/lib/task/decision-details.ts +280 -0
  160. package/lib/task/decision-intents.ts +7 -2
  161. package/lib/task/delivery-target.ts +130 -0
  162. package/lib/task/delivery.ts +160 -0
  163. package/lib/task/events.ts +10 -5
  164. package/lib/task/human-override.ts +5 -2
  165. package/lib/task/index.ts +19 -20
  166. package/lib/task/ledger-intents.ts +6 -8
  167. package/lib/task/ledger.ts +17 -7
  168. package/lib/task/lifecycle.ts +113 -4
  169. package/lib/task/orchestration.ts +102 -80
  170. package/lib/task/pr-delivery-fact.ts +254 -0
  171. package/lib/task/review-artifacts.ts +53 -15
  172. package/lib/task/review-finalization.ts +21 -5
  173. package/lib/task/review-fingerprint.ts +10 -0
  174. package/lib/task/verification-engine.ts +78 -46
  175. package/lib/task/verification-review.ts +3 -1
  176. package/lib/task/workspace-snapshot.ts +4 -13
  177. package/lib/task/write.ts +20 -1
  178. package/lib/update.ts +14 -38
  179. package/lib/version.ts +6 -1
  180. package/package.json +1 -1
  181. package/templates/.agents/README.en.md +5 -3
  182. package/templates/.agents/README.zh-CN.md +5 -3
  183. package/templates/.agents/rules/README.en.md +4 -2
  184. package/templates/.agents/rules/README.zh-CN.md +4 -2
  185. package/templates/.agents/rules/compatibility-policy.en.md +5 -4
  186. package/templates/.agents/rules/compatibility-policy.zh-CN.md +5 -4
  187. package/templates/.agents/rules/create-issue.en.md +11 -2
  188. package/templates/.agents/rules/create-issue.zh-CN.md +24 -2
  189. package/templates/.agents/rules/human-decision-context.en.md +9 -7
  190. package/templates/.agents/rules/human-decision-context.zh-CN.md +9 -7
  191. package/templates/.agents/rules/{issue-fields.github.en.md → issue-fields.en.md} +1 -1
  192. package/templates/.agents/rules/{issue-fields.github.zh-CN.md → issue-fields.zh-CN.md} +1 -1
  193. package/templates/.agents/rules/issue-pr-commands.en.md +5 -18
  194. package/templates/.agents/rules/issue-pr-commands.zh-CN.md +1 -1
  195. package/templates/.agents/rules/issue-sync.en.md +26 -5
  196. package/templates/.agents/rules/issue-sync.zh-CN.md +62 -5
  197. package/templates/.agents/rules/label-milestone-setup.en.md +15 -2
  198. package/templates/.agents/rules/label-milestone-setup.zh-CN.md +15 -2
  199. package/templates/.agents/rules/local-artifact-repair.en.md +46 -0
  200. package/templates/.agents/rules/local-artifact-repair.zh-CN.md +46 -0
  201. package/templates/.agents/rules/milestone-inference.en.md +12 -2
  202. package/templates/.agents/rules/milestone-inference.zh-CN.md +25 -3
  203. package/templates/.agents/rules/next-step-output.en.md +3 -3
  204. package/templates/.agents/rules/next-step-output.zh-CN.md +3 -3
  205. package/templates/.agents/rules/no-mid-flow-questions.en.md +1 -1
  206. package/templates/.agents/rules/no-mid-flow-questions.zh-CN.md +1 -1
  207. package/templates/.agents/rules/pr-sync.en.md +23 -14
  208. package/templates/.agents/rules/pr-sync.zh-CN.md +23 -2
  209. package/templates/.agents/rules/release-commands.en.md +42 -3
  210. package/templates/.agents/rules/release-commands.zh-CN.md +42 -3
  211. package/templates/.agents/rules/review-handshake.en.md +1 -1
  212. package/templates/.agents/rules/review-handshake.zh-CN.md +1 -1
  213. package/templates/.agents/rules/security-alerts.en.md +17 -2
  214. package/templates/.agents/rules/security-alerts.zh-CN.md +17 -2
  215. package/templates/.agents/rules/sync-content-generation.en.md +8 -0
  216. package/templates/.agents/rules/sync-content-generation.zh-CN.md +8 -0
  217. package/templates/.agents/rules/task-management.en.md +2 -3
  218. package/templates/.agents/rules/task-management.zh-CN.md +2 -3
  219. package/templates/.agents/scripts/security-alerts.github.sh +151 -0
  220. package/templates/.agents/scripts/security-alerts.sh +31 -0
  221. package/templates/.agents/skills/analyze-task/SKILL.en.md +4 -3
  222. package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +4 -3
  223. package/templates/.agents/skills/block-task/SKILL.en.md +2 -2
  224. package/templates/.agents/skills/block-task/SKILL.zh-CN.md +2 -2
  225. package/templates/.agents/skills/cancel-task/SKILL.en.md +2 -2
  226. package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +2 -2
  227. package/templates/.agents/skills/check-task/SKILL.en.md +2 -2
  228. package/templates/.agents/skills/check-task/SKILL.zh-CN.md +2 -2
  229. package/templates/.agents/skills/close-codescan/SKILL.en.md +3 -3
  230. package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +3 -3
  231. package/templates/.agents/skills/close-dependabot/SKILL.en.md +3 -3
  232. package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +3 -3
  233. package/templates/.agents/skills/code-task/SKILL.en.md +8 -5
  234. package/templates/.agents/skills/code-task/SKILL.zh-CN.md +9 -6
  235. package/templates/.agents/skills/commit/SKILL.en.md +30 -10
  236. package/templates/.agents/skills/commit/SKILL.zh-CN.md +6 -7
  237. package/templates/.agents/skills/commit/config/verify.json +1 -1
  238. package/templates/.agents/skills/commit/reference/commit-orchestration.en.md +3 -3
  239. package/templates/.agents/skills/commit/reference/commit-orchestration.zh-CN.md +3 -3
  240. package/templates/.agents/skills/commit/reference/pr-summary-sync.en.md +1 -1
  241. package/templates/.agents/skills/commit/reference/pr-summary-sync.zh-CN.md +1 -1
  242. package/templates/.agents/skills/commit/reference/task-status-update.en.md +2 -2
  243. package/templates/.agents/skills/commit/reference/task-status-update.zh-CN.md +2 -2
  244. package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +7 -6
  245. package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +7 -6
  246. package/templates/.agents/skills/complete-manual-validation/config/verify.en.json +1 -1
  247. package/templates/.agents/skills/complete-manual-validation/config/verify.zh-CN.json +1 -1
  248. package/templates/.agents/skills/complete-manual-validation/reference/summary-update.en.md +1 -1
  249. package/templates/.agents/skills/complete-manual-validation/reference/summary-update.zh-CN.md +1 -1
  250. package/templates/.agents/skills/complete-task/SKILL.en.md +13 -13
  251. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +13 -13
  252. package/templates/.agents/skills/complete-task/reference/external-delivery.en.md +2 -2
  253. package/templates/.agents/skills/complete-task/reference/external-delivery.zh-CN.md +2 -2
  254. package/templates/.agents/skills/create-pr/SKILL.en.md +9 -11
  255. package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +9 -11
  256. package/templates/.agents/skills/create-pr/reference/change-report.en.md +9 -4
  257. package/templates/.agents/skills/create-pr/reference/change-report.zh-CN.md +9 -4
  258. package/templates/.agents/skills/create-pr/scripts/change-report.mjs +203 -0
  259. package/templates/.agents/skills/create-release-note/SKILL.en.md +5 -6
  260. package/templates/.agents/skills/create-release-note/SKILL.zh-CN.md +5 -6
  261. package/templates/.agents/skills/create-task/SKILL.en.md +2 -1
  262. package/templates/.agents/skills/create-task/SKILL.zh-CN.md +2 -1
  263. package/templates/.agents/skills/import-codescan/SKILL.en.md +1 -1
  264. package/templates/.agents/skills/import-codescan/SKILL.zh-CN.md +1 -1
  265. package/templates/.agents/skills/import-dependabot/SKILL.en.md +1 -1
  266. package/templates/.agents/skills/import-dependabot/SKILL.zh-CN.md +1 -1
  267. package/templates/.agents/skills/import-issue/SKILL.en.md +1 -1
  268. package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +1 -1
  269. package/templates/.agents/skills/init-labels/SKILL.en.md +9 -10
  270. package/templates/.agents/skills/init-labels/SKILL.zh-CN.md +9 -10
  271. package/templates/.agents/skills/init-labels/scripts/init-labels.github.sh +57 -1
  272. package/templates/.agents/skills/init-labels/scripts/init-labels.sh +27 -1
  273. package/templates/.agents/skills/init-milestones/SKILL.en.md +3 -5
  274. package/templates/.agents/skills/init-milestones/SKILL.zh-CN.md +3 -5
  275. package/templates/.agents/skills/plan-task/SKILL.en.md +4 -3
  276. package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +4 -3
  277. package/templates/.agents/skills/restore-task/SKILL.en.md +1 -1
  278. package/templates/.agents/skills/restore-task/SKILL.zh-CN.md +1 -1
  279. package/templates/.agents/skills/review-analysis/SKILL.en.md +8 -5
  280. package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +8 -5
  281. package/templates/.agents/skills/review-analysis/config/verify.en.json +1 -0
  282. package/templates/.agents/skills/review-analysis/config/verify.zh-CN.json +1 -0
  283. package/templates/.agents/skills/review-analysis/reference/output-templates.en.md +18 -0
  284. package/templates/.agents/skills/review-analysis/reference/output-templates.zh-CN.md +18 -0
  285. package/templates/.agents/skills/review-code/SKILL.en.md +11 -8
  286. package/templates/.agents/skills/review-code/SKILL.zh-CN.md +11 -8
  287. package/templates/.agents/skills/review-code/config/verify.en.json +1 -0
  288. package/templates/.agents/skills/review-code/config/verify.zh-CN.json +1 -0
  289. package/templates/.agents/skills/review-code/reference/migration-risks.en.md +2 -1
  290. package/templates/.agents/skills/review-code/reference/migration-risks.zh-CN.md +2 -1
  291. package/templates/.agents/skills/review-code/reference/output-templates.en.md +19 -1
  292. package/templates/.agents/skills/review-code/reference/output-templates.zh-CN.md +19 -1
  293. package/templates/.agents/skills/review-code/reference/report-template.en.md +4 -2
  294. package/templates/.agents/skills/review-code/reference/report-template.zh-CN.md +4 -2
  295. package/templates/.agents/skills/review-plan/SKILL.en.md +8 -5
  296. package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +8 -5
  297. package/templates/.agents/skills/review-plan/config/verify.en.json +1 -0
  298. package/templates/.agents/skills/review-plan/config/verify.zh-CN.json +1 -0
  299. package/templates/.agents/skills/review-plan/reference/output-templates.en.md +18 -0
  300. package/templates/.agents/skills/review-plan/reference/output-templates.zh-CN.md +18 -0
  301. package/templates/.agents/skills/review-pr/SKILL.en.md +1 -0
  302. package/templates/.agents/skills/review-pr/SKILL.zh-CN.md +1 -0
  303. package/templates/.agents/skills/review-pr/reference/evidence-grading.en.md +2 -2
  304. package/templates/.agents/skills/review-pr/reference/evidence-grading.zh-CN.md +2 -2
  305. package/templates/.agents/skills/run-manual-validation/SKILL.en.md +5 -0
  306. package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +5 -0
  307. package/templates/.agents/skills/run-task/SKILL.en.md +5 -1
  308. package/templates/.agents/skills/run-task/SKILL.zh-CN.md +5 -1
  309. package/templates/.agents/skills/update-agent-infra/SKILL.en.md +2 -2
  310. package/templates/.agents/skills/update-agent-infra/SKILL.zh-CN.md +2 -2
  311. package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +144 -109
  312. package/templates/.agents/skills/watch-pr/SKILL.en.md +5 -5
  313. package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +5 -5
  314. package/templates/.agents/templates/task.en.md +6 -2
  315. package/templates/.agents/templates/task.zh-CN.md +6 -2
  316. package/templates/.agents/workflows/bug-fix.en.yaml +9 -8
  317. package/templates/.agents/workflows/bug-fix.zh-CN.yaml +9 -8
  318. package/templates/.agents/workflows/feature-development.en.yaml +9 -8
  319. package/templates/.agents/workflows/feature-development.zh-CN.yaml +9 -8
  320. package/templates/.agents/workflows/refactoring.en.yaml +9 -8
  321. package/templates/.agents/workflows/refactoring.zh-CN.yaml +9 -8
  322. package/templates/.claude/commands/analyze-task.en.md +1 -1
  323. package/templates/.claude/commands/analyze-task.zh-CN.md +1 -1
  324. package/templates/.claude/commands/block-task.en.md +1 -1
  325. package/templates/.claude/commands/block-task.zh-CN.md +1 -1
  326. package/templates/.claude/commands/cancel-task.en.md +1 -1
  327. package/templates/.claude/commands/cancel-task.zh-CN.md +1 -1
  328. package/templates/.claude/commands/check-task.en.md +1 -1
  329. package/templates/.claude/commands/check-task.zh-CN.md +1 -1
  330. package/templates/.claude/commands/code-task.en.md +1 -1
  331. package/templates/.claude/commands/code-task.zh-CN.md +1 -1
  332. package/templates/.claude/commands/commit.en.md +1 -1
  333. package/templates/.claude/commands/commit.zh-CN.md +1 -1
  334. package/templates/.claude/commands/complete-manual-validation.en.md +1 -1
  335. package/templates/.claude/commands/complete-manual-validation.zh-CN.md +1 -1
  336. package/templates/.claude/commands/complete-task.en.md +1 -1
  337. package/templates/.claude/commands/complete-task.zh-CN.md +1 -1
  338. package/templates/.claude/commands/create-pr.en.md +1 -1
  339. package/templates/.claude/commands/create-pr.zh-CN.md +1 -1
  340. package/templates/.claude/commands/plan-task.en.md +1 -1
  341. package/templates/.claude/commands/plan-task.zh-CN.md +1 -1
  342. package/templates/.claude/commands/review-analysis.en.md +1 -1
  343. package/templates/.claude/commands/review-analysis.zh-CN.md +1 -1
  344. package/templates/.claude/commands/review-code.en.md +1 -1
  345. package/templates/.claude/commands/review-code.zh-CN.md +1 -1
  346. package/templates/.claude/commands/review-plan.en.md +1 -1
  347. package/templates/.claude/commands/review-plan.zh-CN.md +1 -1
  348. package/templates/.claude/commands/run-manual-validation.en.md +1 -1
  349. package/templates/.claude/commands/run-manual-validation.zh-CN.md +1 -1
  350. package/templates/.claude/commands/run-task.en.md +1 -1
  351. package/templates/.claude/commands/run-task.zh-CN.md +1 -1
  352. package/templates/.claude/commands/watch-pr.en.md +1 -1
  353. package/templates/.claude/commands/watch-pr.zh-CN.md +1 -1
  354. package/templates/{.trae/skills → .traecli/commands}/update-agent-infra.en.md +0 -1
  355. package/templates/{.trae/skills → .traecli/commands}/update-agent-infra.zh-CN.md +0 -1
  356. package/dist/lib/builtin-tuis.js +0 -40
  357. package/lib/builtin-tuis.ts +0 -56
  358. package/templates/.agents/rules/create-issue.github.en.md +0 -14
  359. package/templates/.agents/rules/create-issue.github.zh-CN.md +0 -27
  360. package/templates/.agents/rules/issue-pr-commands.github.en.md +0 -34
  361. package/templates/.agents/rules/issue-pr-commands.github.zh-CN.md +0 -49
  362. package/templates/.agents/rules/issue-sync.github.en.md +0 -40
  363. package/templates/.agents/rules/issue-sync.github.zh-CN.md +0 -76
  364. package/templates/.agents/rules/label-milestone-setup.github.en.md +0 -60
  365. package/templates/.agents/rules/label-milestone-setup.github.zh-CN.md +0 -60
  366. package/templates/.agents/rules/milestone-inference.github.en.md +0 -15
  367. package/templates/.agents/rules/milestone-inference.github.zh-CN.md +0 -27
  368. package/templates/.agents/rules/pr-checks-commands.github.en.md +0 -18
  369. package/templates/.agents/rules/pr-checks-commands.github.zh-CN.md +0 -34
  370. package/templates/.agents/rules/pr-sync.github.en.md +0 -43
  371. package/templates/.agents/rules/pr-sync.github.zh-CN.md +0 -69
  372. package/templates/.agents/rules/release-commands.github.en.md +0 -38
  373. package/templates/.agents/rules/release-commands.github.zh-CN.md +0 -38
  374. package/templates/.agents/rules/security-alerts.github.en.md +0 -43
  375. package/templates/.agents/rules/security-alerts.github.zh-CN.md +0 -43
package/lib/merge.ts CHANGED
@@ -1,19 +1,18 @@
1
1
  import fs from 'node:fs';
2
2
  import path from 'node:path';
3
- import { info, ok } from './log.ts';
4
3
  import { removeDirRecursive } from './remove-dir.ts';
4
+ import { validateCurrentTaskContract } from './task/current-contract.ts';
5
5
 
6
6
  const TASK_ID_RE = /^TASK-\d{8}-\d{6}$/;
7
7
  const FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/;
8
8
  const TITLE_RE = /^# (.+)$/m;
9
- const DATE_FROM_PATH_RE = /(?:^|[/\\])(\d{4})[/\\](\d{2})[/\\](\d{2})(?:[/\\]|$)/;
10
9
  const MUTABLE_SECTIONS = ['active', 'blocked', 'completed'] as const;
11
10
  const ALL_SECTIONS = [...MUTABLE_SECTIONS, 'archive'] as const;
12
11
  type MutableSection = typeof MUTABLE_SECTIONS[number];
13
12
  type Section = typeof ALL_SECTIONS[number];
14
13
  type MutableAction = 'copied' | 'updated' | 'moved' | 'skipped';
15
14
  type ArchiveAction = 'copied' | 'skipped';
16
- type SourceMode = 'workspace' | 'legacy-archive';
15
+ type SourceMode = 'workspace';
17
16
  type DateParts = {
18
17
  year: string;
19
18
  month: string;
@@ -129,40 +128,6 @@ function normalizeTaskRecord(taskDir: string, taskFile: string, dateParts: DateP
129
128
  };
130
129
  }
131
130
 
132
- function fallbackDateParts(taskDir: string, content: string): DateParts | null {
133
- const pathMatch = taskDir.match(DATE_FROM_PATH_RE);
134
- if (pathMatch) {
135
- const [, year, month, day] = pathMatch;
136
- if (!year || !month || !day) {
137
- return null;
138
- }
139
- return {
140
- year,
141
- month,
142
- day
143
- };
144
- }
145
-
146
- const completedAt = extractField(content, 'completed_at');
147
- const updatedAt = extractField(content, 'updated_at');
148
- const source = completedAt || updatedAt;
149
- const dateMatch = source?.match(/^(\d{4})-(\d{2})-(\d{2})/);
150
-
151
- if (dateMatch) {
152
- const [, year, month, day] = dateMatch;
153
- if (!year || !month || !day) {
154
- return null;
155
- }
156
- return {
157
- year,
158
- month,
159
- day
160
- };
161
- }
162
-
163
- return null;
164
- }
165
-
166
131
  function scanSourceTasks(sourceDir: string): TaskRecord[] {
167
132
  const tasks: TaskRecord[] = [];
168
133
  const years = fs.existsSync(sourceDir) ? fs.readdirSync(sourceDir, { withFileTypes: true }) : [];
@@ -206,44 +171,6 @@ function scanSourceTasks(sourceDir: string): TaskRecord[] {
206
171
  }
207
172
  }
208
173
 
209
- if (tasks.length > 0) {
210
- return tasks;
211
- }
212
-
213
- // Fall back to a deeper scan if the source layout is unusual but still contains archived tasks.
214
- const stack = [sourceDir];
215
- while (stack.length > 0) {
216
- const currentDir = stack.pop();
217
- if (!currentDir || !fs.existsSync(currentDir)) {
218
- continue;
219
- }
220
-
221
- for (const entry of fs.readdirSync(currentDir, { withFileTypes: true })) {
222
- const entryPath = path.join(currentDir, entry.name);
223
- if (!entry.isDirectory()) {
224
- continue;
225
- }
226
-
227
- if (TASK_ID_RE.test(entry.name)) {
228
- const taskFile = path.join(entryPath, 'task.md');
229
- if (!fs.existsSync(taskFile)) {
230
- continue;
231
- }
232
-
233
- const content = fs.readFileSync(taskFile, 'utf8');
234
- const dateParts = fallbackDateParts(entryPath, content);
235
- if (!dateParts) {
236
- continue;
237
- }
238
-
239
- tasks.push(normalizeTaskRecord(entryPath, taskFile, dateParts));
240
- continue;
241
- }
242
-
243
- stack.push(entryPath);
244
- }
245
- }
246
-
247
174
  return tasks.sort((left, right) => left.relativePath.localeCompare(right.relativePath));
248
175
  }
249
176
 
@@ -624,15 +551,230 @@ function copyTaskToSection(sourceTask: WorkspaceRecord, workspaceDir: string): s
624
551
  return destinationDir;
625
552
  }
626
553
 
554
+ function invalidSource(message: string): never {
555
+ throw new Error(`Invalid merge source: ${message}`);
556
+ }
557
+
558
+ function sourceRelativePath(sourceRoot: string, targetPath: string): string {
559
+ return path.relative(sourceRoot, targetPath).split(path.sep).join('/') || '.';
560
+ }
561
+
562
+ function isMissingPathError(error: unknown): boolean {
563
+ return Boolean(error && typeof error === 'object' && 'code' in error && error.code === 'ENOENT');
564
+ }
565
+
566
+ function assertSourcePathContained(sourceRoot: string, targetPath: string): void {
567
+ let sourceRealPath: string;
568
+ let targetRealPath: string;
569
+ try {
570
+ sourceRealPath = fs.realpathSync(sourceRoot);
571
+ targetRealPath = fs.realpathSync(targetPath);
572
+ } catch (error) {
573
+ invalidSource(`${sourceRelativePath(sourceRoot, targetPath)} cannot be resolved: ${error instanceof Error ? error.message : String(error)}`);
574
+ }
575
+
576
+ const relativePath = path.relative(sourceRealPath, targetRealPath);
577
+ if (relativePath === '..' || relativePath.startsWith(`..${path.sep}`) || path.isAbsolute(relativePath)) {
578
+ invalidSource(`${sourceRelativePath(sourceRoot, targetPath)} resolves outside the source workspace`);
579
+ }
580
+ }
581
+
582
+ function sourcePathStat(sourceRoot: string, targetPath: string): fs.Stats | null {
583
+ let stat: fs.Stats;
584
+ try {
585
+ stat = fs.lstatSync(targetPath);
586
+ } catch (error) {
587
+ if (isMissingPathError(error)) {
588
+ return null;
589
+ }
590
+ throw error;
591
+ }
592
+
593
+ if (stat.isSymbolicLink()) {
594
+ invalidSource(`${sourceRelativePath(sourceRoot, targetPath)} must not be a symbolic link`);
595
+ }
596
+ assertSourcePathContained(sourceRoot, targetPath);
597
+ return stat;
598
+ }
599
+
600
+ function validateTaskTree(sourceRoot: string, taskDir: string): void {
601
+ for (const entry of fs.readdirSync(taskDir, { withFileTypes: true })) {
602
+ const entryPath = path.join(taskDir, entry.name);
603
+ const entryStat = sourcePathStat(sourceRoot, entryPath);
604
+ if (!entryStat) {
605
+ invalidSource(`${sourceRelativePath(sourceRoot, entryPath)} disappeared during validation`);
606
+ }
607
+ if (entryStat.isDirectory()) {
608
+ validateTaskTree(sourceRoot, entryPath);
609
+ continue;
610
+ }
611
+ if (!entryStat.isFile()) {
612
+ invalidSource(`${sourceRelativePath(sourceRoot, entryPath)} must be a regular file or directory`);
613
+ }
614
+ }
615
+ }
616
+
617
+ function validateSourceRoot(sourceRoot: string): void {
618
+ const stat = sourcePathStat(sourceRoot, sourceRoot);
619
+ if (!stat || !stat.isDirectory()) {
620
+ invalidSource('source path must be a real directory');
621
+ }
622
+ }
623
+
624
+ function validateTaskDirectory(
625
+ sourceRoot: string,
626
+ taskDir: string,
627
+ seenTaskIds: Map<string, string>
628
+ ): void {
629
+ const taskId = path.basename(taskDir);
630
+ const taskFile = path.join(taskDir, 'task.md');
631
+ const taskDirStat = sourcePathStat(sourceRoot, taskDir);
632
+ if (!taskDirStat || !taskDirStat.isDirectory()) {
633
+ invalidSource(`${sourceRelativePath(sourceRoot, taskDir)} must be a directory`);
634
+ }
635
+ const taskFileStat = sourcePathStat(sourceRoot, taskFile);
636
+ if (!taskFileStat || !taskFileStat.isFile()) {
637
+ invalidSource(`${sourceRelativePath(sourceRoot, taskDir)} requires a regular task.md file`);
638
+ }
639
+
640
+ const content = fs.readFileSync(taskFile, 'utf8');
641
+ const contract = validateCurrentTaskContract(content);
642
+ if (!contract.ok) {
643
+ invalidSource(`${sourceRelativePath(sourceRoot, taskFile)} is not a valid current task: ${contract.message}`);
644
+ }
645
+ if (contract.metadata.id !== taskId) {
646
+ invalidSource(`${sourceRelativePath(sourceRoot, taskFile)} id '${String(contract.metadata.id ?? 'missing')}' does not match directory '${taskId}'`);
647
+ }
648
+ validateTaskTree(sourceRoot, taskDir);
649
+
650
+ const previousPath = seenTaskIds.get(taskId);
651
+ if (previousPath) {
652
+ invalidSource(`duplicate task ID ${taskId} at ${previousPath} and ${sourceRelativePath(sourceRoot, taskDir)}`);
653
+ }
654
+ seenTaskIds.set(taskId, sourceRelativePath(sourceRoot, taskDir));
655
+ }
656
+
657
+ function validateMutableSection(
658
+ sourceRoot: string,
659
+ section: MutableSection,
660
+ seenTaskIds: Map<string, string>
661
+ ): void {
662
+ const sectionDir = path.join(sourceRoot, section);
663
+ const sectionStat = sourcePathStat(sourceRoot, sectionDir);
664
+ if (!sectionStat) {
665
+ return;
666
+ }
667
+ if (!sectionStat.isDirectory()) {
668
+ invalidSource(`${section} must be a directory`);
669
+ }
670
+
671
+ for (const entry of fs.readdirSync(sectionDir, { withFileTypes: true })) {
672
+ const entryPath = path.join(sectionDir, entry.name);
673
+ const entryStat = sourcePathStat(sourceRoot, entryPath);
674
+ if (!entryStat) {
675
+ continue;
676
+ }
677
+ if (!entryStat.isDirectory()) {
678
+ if (TASK_ID_RE.test(entry.name)) {
679
+ invalidSource(`${sourceRelativePath(sourceRoot, entryPath)} is not a directory`);
680
+ }
681
+ continue;
682
+ }
683
+ if (!TASK_ID_RE.test(entry.name)) {
684
+ invalidSource(`${sourceRelativePath(sourceRoot, entryPath)} is not a direct TASK-YYYYMMDD-HHMMSS directory`);
685
+ }
686
+ validateTaskDirectory(sourceRoot, entryPath, seenTaskIds);
687
+ }
688
+ }
689
+
690
+ function validateArchiveSection(
691
+ sourceRoot: string,
692
+ seenTaskIds: Map<string, string>
693
+ ): void {
694
+ const archiveDir = path.join(sourceRoot, 'archive');
695
+ const archiveStat = sourcePathStat(sourceRoot, archiveDir);
696
+ if (!archiveStat) {
697
+ return;
698
+ }
699
+ if (!archiveStat.isDirectory()) {
700
+ invalidSource('archive must be a directory');
701
+ }
702
+
703
+ const allowManifest = (directory: string, entry: fs.Dirent): boolean => {
704
+ const entryPath = path.join(directory, entry.name);
705
+ const entryStat = sourcePathStat(sourceRoot, entryPath);
706
+ return Boolean(entryStat?.isFile() && entry.name === 'manifest.md');
707
+ };
708
+ const years = fs.readdirSync(archiveDir, { withFileTypes: true });
709
+ for (const yearEntry of years) {
710
+ const yearPath = path.join(archiveDir, yearEntry.name);
711
+ if (allowManifest(archiveDir, yearEntry)) {
712
+ continue;
713
+ }
714
+ const yearStat = sourcePathStat(sourceRoot, yearPath);
715
+ if (!yearStat?.isDirectory() || !/^\d{4}$/.test(yearEntry.name)) {
716
+ invalidSource(`${sourceRelativePath(sourceRoot, yearPath)} is not a YYYY directory or manifest.md`);
717
+ }
718
+
719
+ const yearDir = yearPath;
720
+ for (const monthEntry of fs.readdirSync(yearDir, { withFileTypes: true })) {
721
+ const monthPath = path.join(yearDir, monthEntry.name);
722
+ if (allowManifest(yearDir, monthEntry)) {
723
+ continue;
724
+ }
725
+ const monthStat = sourcePathStat(sourceRoot, monthPath);
726
+ if (!monthStat?.isDirectory() || !/^\d{2}$/.test(monthEntry.name)) {
727
+ invalidSource(`${sourceRelativePath(sourceRoot, monthPath)} is not a MM directory or manifest.md`);
728
+ }
729
+
730
+ const monthDir = monthPath;
731
+ for (const dayEntry of fs.readdirSync(monthDir, { withFileTypes: true })) {
732
+ const dayPath = path.join(monthDir, dayEntry.name);
733
+ if (allowManifest(monthDir, dayEntry)) {
734
+ continue;
735
+ }
736
+ const dayStat = sourcePathStat(sourceRoot, dayPath);
737
+ if (!dayStat?.isDirectory() || !/^\d{2}$/.test(dayEntry.name)) {
738
+ invalidSource(`${sourceRelativePath(sourceRoot, dayPath)} is not a DD directory or manifest.md`);
739
+ }
740
+
741
+ const dayDir = dayPath;
742
+ for (const taskEntry of fs.readdirSync(dayDir, { withFileTypes: true })) {
743
+ const taskPath = path.join(dayDir, taskEntry.name);
744
+ const taskStat = sourcePathStat(sourceRoot, taskPath);
745
+ if (!taskStat?.isDirectory() || !TASK_ID_RE.test(taskEntry.name)) {
746
+ invalidSource(`${sourceRelativePath(sourceRoot, taskPath)} is not a TASK-YYYYMMDD-HHMMSS directory`);
747
+ }
748
+ validateTaskDirectory(sourceRoot, taskPath, seenTaskIds);
749
+ }
750
+ }
751
+ }
752
+ }
753
+ }
754
+
755
+ function validateSourceWorkspace(sourcePath: string): void {
756
+ validateSourceRoot(sourcePath);
757
+ const seenTaskIds = new Map<string, string>();
758
+ for (const section of MUTABLE_SECTIONS) {
759
+ validateMutableSection(sourcePath, section, seenTaskIds);
760
+ }
761
+ validateArchiveSection(sourcePath, seenTaskIds);
762
+ }
763
+
627
764
  function detectSourceMode(sourcePath: string): SourceMode {
765
+ validateSourceRoot(sourcePath);
628
766
  for (const section of ALL_SECTIONS) {
629
767
  const sectionDir = path.join(sourcePath, section);
630
- if (fs.existsSync(sectionDir) && fs.statSync(sectionDir).isDirectory()) {
768
+ const sectionStat = sourcePathStat(sourcePath, sectionDir);
769
+ if (sectionStat?.isDirectory()) {
631
770
  return 'workspace';
632
771
  }
772
+ if (sectionStat) {
773
+ invalidSource(`${section} must be a directory`);
774
+ }
633
775
  }
634
776
 
635
- return 'legacy-archive';
777
+ invalidSource('expected a current workspace containing active, blocked, completed, or archive sections');
636
778
  }
637
779
 
638
780
  function createReport(sourcePath: string, backupRoot: string): MergeReport {
@@ -778,29 +920,6 @@ function mergeArchiveSection(sourceArchive: string, localArchive: string, report
778
920
  return sourceTasks.length;
779
921
  }
780
922
 
781
- function printLegacyArchiveMessages(report: MergeReport, sourcePath: string): void {
782
- const merged = report.sections.archive.copied;
783
- const skipped = report.sections.archive.skipped;
784
-
785
- if (merged.length === 0 && skipped.length === 0) {
786
- info(`No archived tasks found in ${sourcePath}`);
787
- }
788
-
789
- for (const task of merged) {
790
- ok(`Merged ${task.taskId} -> ${task.relativePath}`);
791
- }
792
-
793
- for (const task of skipped) {
794
- info(`Skipped ${task.taskId} (already exists at ${task.relativePath})`);
795
- }
796
-
797
- process.stdout.write('\n');
798
- info('Merge summary');
799
- info(`- Merged: ${merged.length}`);
800
- info(`- Skipped: ${skipped.length}`);
801
- process.stdout.write('\n');
802
- }
803
-
804
923
  function printSection(lines: string[], name: MutableSection, counts: MutableCounts): void {
805
924
  const title = `${SECTION_LABELS[name].padEnd(9, ' ')} (.agents/workspace/${name}/):`;
806
925
  lines.push(title);
@@ -910,7 +1029,11 @@ async function cmdMerge(args: string[]): Promise<void> {
910
1029
  throw new Error(`Source path does not exist: ${sourcePath}`);
911
1030
  }
912
1031
 
913
- if (!fs.statSync(resolvedSource).isDirectory()) {
1032
+ if (fs.lstatSync(resolvedSource).isSymbolicLink()) {
1033
+ throw new Error(`Invalid merge source: source path must be a real directory: ${sourcePath}`);
1034
+ }
1035
+
1036
+ if (!fs.lstatSync(resolvedSource).isDirectory()) {
914
1037
  throw new Error(`Source path is not a directory: ${sourcePath}`);
915
1038
  }
916
1039
 
@@ -920,35 +1043,28 @@ async function cmdMerge(args: string[]): Promise<void> {
920
1043
  const backupRootRelative = `.agents/workspace/.merge-backup/${backupStamp}/`;
921
1044
  const backupRoot = path.join(workspaceDir, '.merge-backup', backupStamp);
922
1045
  const report = createReport(resolvedSource, backupRootRelative);
923
- const mode = detectSourceMode(resolvedSource);
1046
+ detectSourceMode(resolvedSource);
1047
+ validateSourceWorkspace(resolvedSource);
924
1048
 
925
1049
  for (const section of ALL_SECTIONS) {
926
1050
  fs.mkdirSync(path.join(workspaceDir, section), { recursive: true });
927
1051
  }
928
1052
 
929
- if (mode === 'legacy-archive') {
930
- info('Detected legacy archive source; treating the input as archive-only for backward compatibility.');
931
- mergeArchiveSection(resolvedSource, archiveDir, report);
932
- } else {
933
- mergeMutableSections({
934
- sourceWorkspace: resolvedSource,
935
- localWorkspace: workspaceDir,
936
- backupRoot,
937
- report
938
- });
1053
+ mergeMutableSections({
1054
+ sourceWorkspace: resolvedSource,
1055
+ localWorkspace: workspaceDir,
1056
+ backupRoot,
1057
+ report
1058
+ });
939
1059
 
940
- const sourceArchive = path.join(resolvedSource, 'archive');
941
- if (fs.existsSync(sourceArchive) && fs.statSync(sourceArchive).isDirectory()) {
942
- mergeArchiveSection(sourceArchive, archiveDir, report);
943
- }
1060
+ const sourceArchive = path.join(resolvedSource, 'archive');
1061
+ const sourceArchiveStat = sourcePathStat(resolvedSource, sourceArchive);
1062
+ if (sourceArchiveStat?.isDirectory()) {
1063
+ mergeArchiveSection(sourceArchive, archiveDir, report);
944
1064
  }
945
1065
 
946
1066
  rebuildManifests(archiveDir);
947
1067
 
948
- if (mode === 'legacy-archive') {
949
- printLegacyArchiveMessages(report, sourcePath);
950
- }
951
-
952
1068
  printReport(report);
953
1069
  }
954
1070
 
@@ -37,8 +37,15 @@ const MINIMUM_GITHUB_CLI_VERSION = '2.72.0';
37
37
  const GITHUB_CLI_MAX_BUFFER = 64 * 1024 * 1024;
38
38
  const ERROR_DETAIL_LIMIT = 4096;
39
39
 
40
+ function redactDiagnostic(value: string): string {
41
+ return value
42
+ .replace(/\b(?:gh[pousr]_[A-Za-z0-9_]+|github_pat_[A-Za-z0-9_]+)\b/g, '[REDACTED_TOKEN]')
43
+ .replace(/\b(bearer|token)\s+[A-Za-z0-9._~+\/-]+/gi, '$1 [REDACTED_TOKEN]')
44
+ .replace(/([?&](?:access_token|token|client_secret|password)=)[^&\s]+/gi, '$1[REDACTED]');
45
+ }
46
+
40
47
  function boundedFailureDetail(result: RunResult): string {
41
- const diagnostic = `${result.stderr}\n${result.error?.message || ''}`.trim() || result.stdout.trim();
48
+ const diagnostic = redactDiagnostic(`${result.stderr}\n${result.error?.message || ''}`).trim() || redactDiagnostic(result.stdout.trim());
42
49
  return diagnostic.length <= ERROR_DETAIL_LIMIT
43
50
  ? diagnostic
44
51
  : `${diagnostic.slice(0, ERROR_DETAIL_LIMIT)}… [truncated]`;
@@ -1,6 +1,6 @@
1
1
  import fs from 'node:fs';
2
2
 
3
- import { parseTaskFrontmatter } from '../task/frontmatter.ts';
3
+ import { parseTypedTaskFrontmatter } from '../task/frontmatter.ts';
4
4
  import { resolveTaskRef } from '../task/resolve-ref.ts';
5
5
  import {
6
6
  inspectPlatformRequiredChecks,
@@ -14,6 +14,7 @@ import { inspectPlatformPullRequest } from './pull-requests.ts';
14
14
  import { platformResult } from './types.ts';
15
15
  import type { PlatformResult } from './types.ts';
16
16
  import type { PullRequestSnapshot } from './pull-requests.ts';
17
+ import { readPrDeliveryFact } from '../task/pr-delivery-fact.ts';
17
18
 
18
19
  type CheckBucket = 'pass' | 'fail' | 'pending' | 'cancel';
19
20
  type CheckState = 'passed' | 'failed' | 'pending' | 'timed-out' | 'cancelled' | 'no-required';
@@ -121,10 +122,11 @@ registerPlatformCapabilities('github', {
121
122
  function resolvedTask(taskRef: string, options: InspectionOptions) {
122
123
  const resolved = resolveTaskRef(taskRef, options.cwd ? { repoRoot: options.cwd } : {});
123
124
  if (!resolved.ok) return { ok: false as const, output: checksResult('failed', { error: { code: resolved.code, message: resolved.message, retryable: false } }) };
124
- const frontmatter = parseTaskFrontmatter(fs.readFileSync(resolved.taskMdPath, 'utf8'));
125
- const pr = Number(frontmatter.pr_number);
126
- const prNumber = Number.isInteger(pr) && pr > 0 ? pr : null;
127
- if (!prNumber) return { ok: false as const, output: checksResult('failed', { error: { code: 'PR_NOT_LINKED', message: 'Task has no valid pr_number', retryable: false } }) };
125
+ const frontmatter = parseTypedTaskFrontmatter(fs.readFileSync(resolved.taskMdPath, 'utf8'));
126
+ const fact = readPrDeliveryFact(frontmatter);
127
+ if (fact.status === 'invalid') return { ok: false as const, output: checksResult('failed', { error: { code: 'PR_DELIVERY_FACT_INVALID', message: fact.error.message, retryable: false } }) };
128
+ const prNumber = fact.status === 'valid' && fact.fact.state === 'bound' ? fact.fact.identity.number : null;
129
+ if (!prNumber) return { ok: false as const, output: checksResult('failed', { error: { code: fact.status === 'missing' ? 'PR_DELIVERY_FACT_MISSING' : 'PR_NOT_LINKED', message: 'Task has no verified bound pull request', retryable: false } }) };
128
130
  const client = (options.client as GitHubClient | undefined) || createGitHubClient();
129
131
  const context = resolvePlatformContext({ cwd: resolved.repoRoot, client });
130
132
  if (!context.platform.repository || !['no-op', 'degraded'].includes(context.status)) return { ok: false as const, output: checksResult(context.status, { platform: context.platform, capabilities: context.capabilities, error: context.error }) };
@@ -2,7 +2,7 @@ import fs from 'node:fs';
2
2
  import { execFileSync } from 'node:child_process';
3
3
 
4
4
  import { enumerateArtifacts } from '../task/artifacts.ts';
5
- import { parseTaskFrontmatter } from '../task/frontmatter.ts';
5
+ import { parseTypedTaskFrontmatter } from '../task/frontmatter.ts';
6
6
  import { renderHumanOverrideAudit } from '../task/human-override.ts';
7
7
  import { resolveTaskRef } from '../task/resolve-ref.ts';
8
8
  import { resolvePlatformContext } from './context.ts';
@@ -12,6 +12,7 @@ import { listRemoteComments, normalizeCommentContent, writeComment } from './iss
12
12
  import { platformResult } from './types.ts';
13
13
  import type { PlatformResult } from './types.ts';
14
14
  import type { OperationWarning } from '../task/operation-outcome.ts';
15
+ import { readPrDeliveryFact } from '../task/pr-delivery-fact.ts';
15
16
 
16
17
  type SummaryComment = { id: number; body: string };
17
18
  type SummaryContextResult = PlatformResult & {
@@ -67,9 +68,9 @@ function canonicalArtifacts(taskDir: string) {
67
68
  function summaryContext(taskRef: string, options: { cwd?: string; client?: GitHubClient } = {}): SummaryContextResult {
68
69
  const resolved = resolveTaskRef(taskRef, options.cwd ? { repoRoot: options.cwd } : {});
69
70
  if (!resolved.ok) return { ...platformResult('failed', { error: { code: resolved.code, message: resolved.message, retryable: false } }), task: { id: resolved.taskId, prNumber: null }, artifacts: [] };
70
- const frontmatter = parseTaskFrontmatter(fs.readFileSync(resolved.taskMdPath, 'utf8'));
71
- const value = Number(frontmatter.pr_number);
72
- const prNumber = Number.isInteger(value) && value > 0 ? value : null;
71
+ const frontmatter = parseTypedTaskFrontmatter(fs.readFileSync(resolved.taskMdPath, 'utf8'));
72
+ const fact = readPrDeliveryFact(frontmatter);
73
+ const prNumber = fact.status === 'valid' && fact.fact.state === 'bound' ? fact.fact.identity.number : null;
73
74
  return { ...platformResult('no-op'), task: { id: resolved.taskId, prNumber }, artifacts: canonicalArtifacts(resolved.taskDir) };
74
75
  }
75
76
 
@@ -105,9 +106,11 @@ function syncPullRequestSummary(taskRef: string, options: { agent: string; body:
105
106
  };
106
107
  const resolved = resolveTaskRef(taskRef, options.cwd ? { repoRoot: options.cwd } : {});
107
108
  if (!resolved.ok) return softenFailure(platformResult('failed', { error: { code: resolved.code, message: resolved.message, retryable: false } }));
108
- const frontmatter = parseTaskFrontmatter(fs.readFileSync(resolved.taskMdPath, 'utf8'));
109
- const prNumber = Number(frontmatter.pr_number);
110
- if (!Number.isInteger(prNumber) || prNumber <= 0) return softenFailure(platformResult('failed', { error: { code: 'PR_NOT_LINKED', message: 'Task has no valid pr_number', retryable: false } }));
109
+ const frontmatter = parseTypedTaskFrontmatter(fs.readFileSync(resolved.taskMdPath, 'utf8'));
110
+ const fact = readPrDeliveryFact(frontmatter);
111
+ if (fact.status === 'invalid') return softenFailure(platformResult('failed', { error: { code: 'PR_DELIVERY_FACT_INVALID', message: fact.error.message, retryable: false } }));
112
+ const prNumber = fact.status === 'valid' && fact.fact.state === 'bound' ? fact.fact.identity.number : null;
113
+ if (!prNumber) return softenFailure(platformResult('failed', { error: { code: fact.status === 'missing' ? 'PR_DELIVERY_FACT_MISSING' : 'PR_NOT_LINKED', message: 'Task has no verified bound pull request', retryable: false } }));
111
114
  knownPrNumber = prNumber;
112
115
  const client = options.client || createGitHubClient();
113
116
  const context = resolvePlatformContext({ cwd: resolved.repoRoot, client });