@fitlab-ai/agent-infra 0.9.12 → 0.9.14

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 (402) hide show
  1. package/README.md +11 -11
  2. package/README.zh-CN.md +11 -11
  3. package/bin/cli.ts +26 -9
  4. package/dist/bin/cli.js +173 -157
  5. package/dist/bin/internal-cli.js +66 -40
  6. package/dist/lib/agent-client.js +5 -0
  7. package/dist/lib/agent-clients/next-steps.js +6 -1
  8. package/dist/lib/decide.js +3 -3
  9. package/dist/lib/internal/agent-client.js +5 -2
  10. package/dist/lib/internal/cli-route-inventory.js +152 -0
  11. package/dist/lib/internal/codex-lifecycle.js +9 -6
  12. package/dist/lib/internal/codex-sandbox-controller.js +8 -2
  13. package/dist/lib/internal/git-workflow.js +10 -6
  14. package/dist/lib/internal/platform-checks.js +19 -7
  15. package/dist/lib/internal/platform-comment.js +25 -13
  16. package/dist/lib/internal/platform-context.js +6 -3
  17. package/dist/lib/internal/platform-issue.js +20 -13
  18. package/dist/lib/internal/platform-metadata.js +88 -0
  19. package/dist/lib/internal/platform-pr-review.js +26 -13
  20. package/dist/lib/internal/platform-pr.js +84 -31
  21. package/dist/lib/internal/platform-release-notes.js +9 -6
  22. package/dist/lib/internal/platform-security.js +93 -0
  23. package/dist/lib/internal/pr-review-grade.js +25 -12
  24. package/dist/lib/internal/release-workflow.js +23 -9
  25. package/dist/lib/internal/sandbox-control.js +50 -7
  26. package/dist/lib/internal/task-activity.js +16 -7
  27. package/dist/lib/internal/task-artifact.js +235 -24
  28. package/dist/lib/internal/task-context.js +4 -1
  29. package/dist/lib/internal/task-create.js +50 -17
  30. package/dist/lib/internal/task-delivery.js +4 -1
  31. package/dist/lib/internal/task-event.js +10 -4
  32. package/dist/lib/internal/task-finalization.js +6 -3
  33. package/dist/lib/internal/task-invalidation.js +63 -0
  34. package/dist/lib/internal/task-ledger.js +20 -6
  35. package/dist/lib/internal/task-lifecycle.js +6 -3
  36. package/dist/lib/internal/task-operation-registry.js +421 -0
  37. package/dist/lib/internal/task-orchestration.js +12 -0
  38. package/dist/lib/internal/task-override.js +14 -7
  39. package/dist/lib/internal/task-qualification.js +90 -0
  40. package/dist/lib/internal/task-review.js +7 -4
  41. package/dist/lib/internal/task-short-id.js +11 -1
  42. package/dist/lib/internal/task-snapshot.js +4 -1
  43. package/dist/lib/internal/task-validate.js +7 -2
  44. package/dist/lib/internal/task-verify.js +11 -7
  45. package/dist/lib/internal/task-warning.js +11 -4
  46. package/dist/lib/platform/adapters.js +1 -3
  47. package/dist/lib/platform/change-request-git-evidence.js +27 -7
  48. package/dist/lib/platform/comment-safety.js +226 -0
  49. package/dist/lib/platform/completion-backfill.js +6 -5
  50. package/dist/lib/platform/context.js +70 -130
  51. package/dist/lib/platform/github-client.js +1 -1
  52. package/dist/lib/platform/github-provider.js +1130 -0
  53. package/dist/lib/platform/in-label-sync.js +143 -0
  54. package/dist/lib/platform/issue-comments.js +237 -72
  55. package/dist/lib/platform/issues.js +335 -189
  56. package/dist/lib/platform/metadata-labels.js +6 -2
  57. package/dist/lib/platform/none-provider.js +28 -0
  58. package/dist/lib/platform/pr-change-report.js +545 -0
  59. package/dist/lib/platform/pr-checks.js +119 -104
  60. package/dist/lib/platform/pr-review.js +88 -72
  61. package/dist/lib/platform/pr-summary.js +541 -50
  62. package/dist/lib/platform/provider-bridge.js +45 -0
  63. package/dist/lib/platform/provider-contract.d.ts +553 -0
  64. package/dist/lib/platform/provider-contract.js +99 -0
  65. package/dist/lib/platform/provider-loader.js +251 -0
  66. package/dist/lib/platform/provider-validation.js +577 -0
  67. package/dist/lib/platform/pull-request-metadata.js +8 -6
  68. package/dist/lib/platform/pull-requests.js +734 -543
  69. package/dist/lib/platform/release-notes.js +78 -55
  70. package/dist/lib/platform/releases.js +79 -50
  71. package/dist/lib/platform/repository-metadata.js +377 -0
  72. package/dist/lib/platform/resource-identity.d.ts +26 -0
  73. package/dist/lib/platform/resource-identity.js +92 -0
  74. package/dist/lib/platform/security-alerts.js +155 -0
  75. package/dist/lib/platform/task-identities.js +31 -0
  76. package/dist/lib/platform/verification-required.js +11 -12
  77. package/dist/lib/platform/verification-sync.js +129 -248
  78. package/dist/lib/pr-review/evidence-grading.js +19 -6
  79. package/dist/lib/process-data/index.js +7 -0
  80. package/dist/lib/run/index.js +68 -23
  81. package/dist/lib/sandbox/commands/control-status.js +22 -0
  82. package/dist/lib/sandbox/commands/create.js +32 -6
  83. package/dist/lib/sandbox/commands/ls.js +5 -0
  84. package/dist/lib/sandbox/commands/rm.js +827 -120
  85. package/dist/lib/sandbox/commands/show.js +16 -0
  86. package/dist/lib/sandbox/control/client.js +174 -19
  87. package/dist/lib/sandbox/control/completed-reentry.js +85 -0
  88. package/dist/lib/sandbox/control/container-identity.js +28 -31
  89. package/dist/lib/sandbox/control/executor.js +151 -16
  90. package/dist/lib/sandbox/control/lifecycle.js +621 -127
  91. package/dist/lib/sandbox/control/native-file-lock.js +177 -0
  92. package/dist/lib/sandbox/control/protocol.js +77 -0
  93. package/dist/lib/sandbox/control/server.js +677 -34
  94. package/dist/lib/sandbox/control/state.js +209 -4
  95. package/dist/lib/sandbox/control/task-view.js +183 -0
  96. package/dist/lib/sandbox/engines/authority.js +289 -0
  97. package/dist/lib/sandbox/engines/docker-exact-discovery.js +97 -0
  98. package/dist/lib/sandbox/index.js +5 -0
  99. package/dist/lib/sandbox/recovery.js +21 -1
  100. package/dist/lib/sandbox/workspace-view.js +2 -1
  101. package/dist/lib/sandbox/worktree-safety.js +4 -1
  102. package/dist/lib/server/index.js +8 -0
  103. package/dist/lib/server/protocol.js +38 -1
  104. package/dist/lib/task/activity-log.js +8 -1
  105. package/dist/lib/task/artifact-lifecycle.js +75 -18
  106. package/dist/lib/task/artifact-operations.js +520 -0
  107. package/dist/lib/task/artifact-repair-intent.js +59 -0
  108. package/dist/lib/task/artifact-schema.js +135 -0
  109. package/dist/lib/task/capabilities.js +310 -0
  110. package/dist/lib/task/commands/status.js +32 -0
  111. package/dist/lib/task/commit-operation.js +12 -1
  112. package/dist/lib/task/control-authority.js +12 -5
  113. package/dist/lib/task/create-service.js +111 -12
  114. package/dist/lib/task/create.js +25 -0
  115. package/dist/lib/task/current-contract.js +5 -1
  116. package/dist/lib/task/decision-details.js +1 -1
  117. package/dist/lib/task/events.js +416 -17
  118. package/dist/lib/task/finalization.js +37 -13
  119. package/dist/lib/task/human-override.js +58 -36
  120. package/dist/lib/task/index.js +11 -3
  121. package/dist/lib/task/invalidation-command.js +86 -0
  122. package/dist/lib/task/invalidation.js +260 -0
  123. package/dist/lib/task/ledger-intents.js +37 -0
  124. package/dist/lib/task/local-artifact-finalization.js +299 -0
  125. package/dist/lib/task/orchestration.js +240 -144
  126. package/dist/lib/task/pr-delivery-fact.js +102 -101
  127. package/dist/lib/task/qualification-audit.js +490 -0
  128. package/dist/lib/task/qualification-confirmation.js +109 -0
  129. package/dist/lib/task/qualification-intents.js +148 -0
  130. package/dist/lib/task/review-artifacts.js +22 -23
  131. package/dist/lib/task/review-finalization.js +121 -4
  132. package/dist/lib/task/review-fingerprint.js +24 -2
  133. package/dist/lib/task/rework-intent.js +138 -0
  134. package/dist/lib/task/sections.js +6 -4
  135. package/dist/lib/task/skill-scope.js +23 -0
  136. package/dist/lib/task/task-execution-lock.js +27 -17
  137. package/dist/lib/task/verification-config.js +11 -0
  138. package/dist/lib/task/verification-engine.js +93 -16
  139. package/dist/lib/task/verification.js +3 -3
  140. package/dist/lib/task/workflow-warning-intents.js +1 -1
  141. package/dist/lib/task/write.js +11 -0
  142. package/dist/lib/version.js +11 -1
  143. package/dist/lifecycle-build-manifest.json +3 -3
  144. package/lib/agent-client.ts +9 -0
  145. package/lib/agent-clients/next-steps.ts +6 -1
  146. package/lib/decide.ts +3 -3
  147. package/lib/init.ts +1 -1
  148. package/lib/internal/agent-client.ts +4 -2
  149. package/lib/internal/cli-route-inventory.ts +140 -0
  150. package/lib/internal/codex-lifecycle.ts +8 -6
  151. package/lib/internal/codex-sandbox-controller.ts +7 -2
  152. package/lib/internal/git-workflow.ts +9 -6
  153. package/lib/internal/platform-checks.ts +14 -6
  154. package/lib/internal/platform-comment.ts +20 -12
  155. package/lib/internal/platform-context.ts +5 -3
  156. package/lib/internal/platform-issue.ts +17 -11
  157. package/lib/internal/platform-metadata.ts +70 -0
  158. package/lib/internal/platform-pr-review.ts +23 -12
  159. package/lib/internal/platform-pr.ts +65 -27
  160. package/lib/internal/platform-release-notes.ts +8 -6
  161. package/lib/internal/platform-security.ts +83 -0
  162. package/lib/internal/pr-review-grade.ts +20 -11
  163. package/lib/internal/release-workflow.ts +21 -9
  164. package/lib/internal/sandbox-control.ts +44 -6
  165. package/lib/internal/task-activity.ts +15 -7
  166. package/lib/internal/task-artifact.ts +204 -22
  167. package/lib/internal/task-context.ts +3 -1
  168. package/lib/internal/task-create.ts +78 -17
  169. package/lib/internal/task-delivery.ts +3 -1
  170. package/lib/internal/task-event.ts +9 -4
  171. package/lib/internal/task-finalization.ts +6 -4
  172. package/lib/internal/task-invalidation.ts +41 -0
  173. package/lib/internal/task-ledger.ts +21 -8
  174. package/lib/internal/task-lifecycle.ts +5 -3
  175. package/lib/internal/task-operation-registry.ts +482 -0
  176. package/lib/internal/task-orchestration.ts +12 -0
  177. package/lib/internal/task-override.ts +15 -9
  178. package/lib/internal/task-qualification.ts +61 -0
  179. package/lib/internal/task-review.ts +6 -4
  180. package/lib/internal/task-short-id.ts +10 -1
  181. package/lib/internal/task-snapshot.ts +3 -1
  182. package/lib/internal/task-validate.ts +6 -2
  183. package/lib/internal/task-verify.ts +10 -7
  184. package/lib/internal/task-warning.ts +10 -4
  185. package/lib/platform/adapters.ts +3 -2
  186. package/lib/platform/change-request-git-evidence.ts +36 -9
  187. package/lib/platform/comment-safety.ts +250 -0
  188. package/lib/platform/completion-backfill.ts +9 -8
  189. package/lib/platform/context.ts +97 -134
  190. package/lib/platform/github-client.ts +2 -2
  191. package/lib/platform/github-provider.ts +1132 -0
  192. package/lib/platform/in-label-sync.ts +208 -0
  193. package/lib/platform/issue-comments.ts +255 -80
  194. package/lib/platform/issues.ts +344 -174
  195. package/lib/platform/metadata-labels.ts +5 -2
  196. package/lib/platform/none-provider.ts +35 -0
  197. package/lib/platform/pr-change-report.ts +639 -0
  198. package/lib/platform/pr-checks.ts +126 -106
  199. package/lib/platform/pr-review.ts +88 -85
  200. package/lib/platform/pr-summary.ts +581 -53
  201. package/lib/platform/provider-bridge.ts +82 -0
  202. package/lib/platform/provider-contract.ts +611 -0
  203. package/lib/platform/provider-loader.ts +307 -0
  204. package/lib/platform/provider-validation.ts +611 -0
  205. package/lib/platform/pull-request-metadata.ts +10 -7
  206. package/lib/platform/pull-requests.ts +781 -550
  207. package/lib/platform/release-notes.ts +76 -59
  208. package/lib/platform/releases.ts +77 -43
  209. package/lib/platform/repository-metadata.ts +453 -0
  210. package/lib/platform/resource-identity.ts +121 -0
  211. package/lib/platform/security-alerts.ts +191 -0
  212. package/lib/platform/task-identities.ts +40 -0
  213. package/lib/platform/types.ts +4 -2
  214. package/lib/platform/verification-required.ts +11 -13
  215. package/lib/platform/verification-sync.ts +128 -304
  216. package/lib/pr-review/evidence-grading.ts +25 -8
  217. package/lib/process-data/index.ts +7 -0
  218. package/lib/run/index.ts +62 -22
  219. package/lib/sandbox/commands/control-status.ts +26 -0
  220. package/lib/sandbox/commands/create.ts +46 -6
  221. package/lib/sandbox/commands/ls.ts +7 -0
  222. package/lib/sandbox/commands/rm.ts +1000 -87
  223. package/lib/sandbox/commands/show.ts +16 -0
  224. package/lib/sandbox/control/client.ts +203 -19
  225. package/lib/sandbox/control/completed-reentry.ts +98 -0
  226. package/lib/sandbox/control/container-identity.ts +27 -43
  227. package/lib/sandbox/control/executor.ts +180 -15
  228. package/lib/sandbox/control/lifecycle.ts +629 -17
  229. package/lib/sandbox/control/native-file-lock.ts +209 -0
  230. package/lib/sandbox/control/protocol.ts +118 -2
  231. package/lib/sandbox/control/server.ts +724 -34
  232. package/lib/sandbox/control/state.ts +257 -4
  233. package/lib/sandbox/control/task-view.ts +240 -0
  234. package/lib/sandbox/engines/authority.ts +365 -0
  235. package/lib/sandbox/engines/docker-exact-discovery.ts +132 -0
  236. package/lib/sandbox/index.ts +7 -0
  237. package/lib/sandbox/recovery.ts +21 -1
  238. package/lib/sandbox/workspace-view.ts +9 -3
  239. package/lib/sandbox/worktree-safety.ts +4 -1
  240. package/lib/server/index.ts +10 -0
  241. package/lib/server/protocol.ts +36 -1
  242. package/lib/task/activity-log.ts +8 -0
  243. package/lib/task/artifact-lifecycle.ts +70 -22
  244. package/lib/task/artifact-operations.ts +626 -0
  245. package/lib/task/artifact-repair-intent.ts +71 -0
  246. package/lib/task/artifact-schema.ts +192 -0
  247. package/lib/task/capabilities.ts +331 -0
  248. package/lib/task/commands/status.ts +38 -0
  249. package/lib/task/commit-operation.ts +12 -1
  250. package/lib/task/control-authority.ts +22 -9
  251. package/lib/task/create-service.ts +139 -14
  252. package/lib/task/create.ts +27 -0
  253. package/lib/task/current-contract.ts +6 -1
  254. package/lib/task/decision-details.ts +6 -2
  255. package/lib/task/events.ts +392 -16
  256. package/lib/task/finalization.ts +50 -14
  257. package/lib/task/human-override.ts +70 -46
  258. package/lib/task/index.ts +16 -3
  259. package/lib/task/invalidation-command.ts +86 -0
  260. package/lib/task/invalidation.ts +332 -0
  261. package/lib/task/ledger-intents.ts +35 -1
  262. package/lib/task/local-artifact-finalization.ts +432 -0
  263. package/lib/task/orchestration.ts +223 -133
  264. package/lib/task/pr-delivery-fact.ts +131 -148
  265. package/lib/task/qualification-audit.ts +599 -0
  266. package/lib/task/qualification-confirmation.ts +117 -0
  267. package/lib/task/qualification-intents.ts +150 -0
  268. package/lib/task/review-artifacts.ts +21 -22
  269. package/lib/task/review-finalization.ts +133 -4
  270. package/lib/task/review-fingerprint.ts +23 -2
  271. package/lib/task/rework-intent.ts +165 -0
  272. package/lib/task/sections.ts +6 -4
  273. package/lib/task/skill-scope.ts +24 -0
  274. package/lib/task/task-execution-lock.ts +23 -14
  275. package/lib/task/verification-config.ts +12 -0
  276. package/lib/task/verification-engine.ts +103 -17
  277. package/lib/task/verification.ts +4 -4
  278. package/lib/task/workflow-warning-intents.ts +1 -1
  279. package/lib/task/write.ts +19 -0
  280. package/lib/update.ts +1 -1
  281. package/lib/version.ts +15 -1
  282. package/package.json +12 -1
  283. package/templates/.agents/rules/README.en.md +3 -1
  284. package/templates/.agents/rules/README.zh-CN.md +3 -1
  285. package/templates/.agents/rules/decision-qualification.en.md +22 -0
  286. package/templates/.agents/rules/decision-qualification.zh-CN.md +22 -0
  287. package/templates/.agents/rules/evidence-reporting.en.md +28 -0
  288. package/templates/.agents/rules/evidence-reporting.zh-CN.md +28 -0
  289. package/templates/.agents/rules/issue-pr-commands.en.md +1 -1
  290. package/templates/.agents/rules/issue-pr-commands.zh-CN.md +1 -1
  291. package/templates/.agents/rules/issue-sync.en.md +2 -0
  292. package/templates/.agents/rules/issue-sync.zh-CN.md +2 -0
  293. package/templates/.agents/rules/label-milestone-setup.en.md +7 -7
  294. package/templates/.agents/rules/label-milestone-setup.zh-CN.md +7 -7
  295. package/templates/.agents/rules/local-artifact-repair.en.md +13 -2
  296. package/templates/.agents/rules/local-artifact-repair.zh-CN.md +13 -2
  297. package/templates/.agents/rules/pr-sync.en.md +10 -4
  298. package/templates/.agents/rules/pr-sync.zh-CN.md +17 -4
  299. package/templates/.agents/rules/security-alerts.en.md +8 -8
  300. package/templates/.agents/rules/security-alerts.zh-CN.md +8 -8
  301. package/templates/.agents/rules/task-short-id.en.md +1 -4
  302. package/templates/.agents/rules/task-short-id.zh-CN.md +1 -3
  303. package/templates/.agents/skills/analyze-task/SKILL.en.md +28 -5
  304. package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +29 -5
  305. package/templates/.agents/skills/analyze-task/config/verify.en.json +1 -12
  306. package/templates/.agents/skills/analyze-task/config/verify.zh-CN.json +1 -12
  307. package/templates/.agents/skills/check-task/SKILL.en.md +16 -37
  308. package/templates/.agents/skills/check-task/SKILL.zh-CN.md +16 -37
  309. package/templates/.agents/skills/close-codescan/SKILL.en.md +2 -2
  310. package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +2 -2
  311. package/templates/.agents/skills/close-dependabot/SKILL.en.md +2 -2
  312. package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +2 -2
  313. package/templates/.agents/skills/code-task/SKILL.en.md +41 -9
  314. package/templates/.agents/skills/code-task/SKILL.zh-CN.md +43 -11
  315. package/templates/.agents/skills/code-task/config/verify.en.json +6 -14
  316. package/templates/.agents/skills/code-task/config/verify.zh-CN.json +6 -14
  317. package/templates/.agents/skills/code-task/reference/code-rules.en.md +5 -4
  318. package/templates/.agents/skills/code-task/reference/code-rules.zh-CN.md +5 -4
  319. package/templates/.agents/skills/code-task/reference/dual-mode.en.md +1 -1
  320. package/templates/.agents/skills/code-task/reference/dual-mode.zh-CN.md +1 -1
  321. package/templates/.agents/skills/code-task/reference/fix-mode.en.md +3 -1
  322. package/templates/.agents/skills/code-task/reference/fix-mode.zh-CN.md +3 -1
  323. package/templates/.agents/skills/code-task/reference/report-template.en.md +42 -15
  324. package/templates/.agents/skills/code-task/reference/report-template.zh-CN.md +39 -12
  325. package/templates/.agents/skills/commit/reference/issue-metadata-sync.en.md +2 -0
  326. package/templates/.agents/skills/commit/reference/issue-metadata-sync.zh-CN.md +2 -0
  327. package/templates/.agents/skills/commit/reference/pr-summary-sync.en.md +3 -3
  328. package/templates/.agents/skills/commit/reference/pr-summary-sync.zh-CN.md +3 -3
  329. package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +10 -4
  330. package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +10 -4
  331. package/templates/.agents/skills/complete-manual-validation/reference/report-template.en.md +2 -0
  332. package/templates/.agents/skills/complete-manual-validation/reference/report-template.zh-CN.md +2 -0
  333. package/templates/.agents/skills/complete-manual-validation/reference/summary-update.en.md +4 -3
  334. package/templates/.agents/skills/complete-manual-validation/reference/summary-update.zh-CN.md +4 -3
  335. package/templates/.agents/skills/complete-task/SKILL.en.md +26 -21
  336. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +25 -21
  337. package/templates/.agents/skills/create-pr/SKILL.en.md +7 -6
  338. package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +7 -7
  339. package/templates/.agents/skills/create-pr/config/verify.json +1 -0
  340. package/templates/.agents/skills/create-pr/reference/change-report.en.md +16 -4
  341. package/templates/.agents/skills/create-pr/reference/change-report.zh-CN.md +16 -4
  342. package/templates/.agents/skills/create-pr/reference/comment-publish.en.md +5 -3
  343. package/templates/.agents/skills/create-pr/reference/comment-publish.zh-CN.md +5 -3
  344. package/templates/.agents/skills/create-pr/reference/pr-body-template.en.md +1 -1
  345. package/templates/.agents/skills/create-pr/reference/pr-body-template.zh-CN.md +1 -1
  346. package/templates/.agents/skills/create-pr/scripts/change-report.mjs +3 -0
  347. package/templates/.agents/skills/create-task/SKILL.en.md +8 -0
  348. package/templates/.agents/skills/create-task/SKILL.zh-CN.md +7 -0
  349. package/templates/.agents/skills/import-codescan/SKILL.en.md +1 -1
  350. package/templates/.agents/skills/import-codescan/SKILL.zh-CN.md +1 -1
  351. package/templates/.agents/skills/import-dependabot/SKILL.en.md +1 -1
  352. package/templates/.agents/skills/import-dependabot/SKILL.zh-CN.md +1 -1
  353. package/templates/.agents/skills/init-labels/SKILL.en.md +8 -8
  354. package/templates/.agents/skills/init-labels/SKILL.zh-CN.md +8 -8
  355. package/templates/.agents/skills/init-milestones/SKILL.en.md +9 -11
  356. package/templates/.agents/skills/init-milestones/SKILL.zh-CN.md +9 -11
  357. package/templates/.agents/skills/plan-task/SKILL.en.md +27 -4
  358. package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +26 -4
  359. package/templates/.agents/skills/plan-task/config/verify.en.json +4 -14
  360. package/templates/.agents/skills/plan-task/config/verify.zh-CN.json +4 -14
  361. package/templates/.agents/skills/review-analysis/SKILL.en.md +17 -4
  362. package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +17 -4
  363. package/templates/.agents/skills/review-analysis/config/verify.en.json +1 -18
  364. package/templates/.agents/skills/review-analysis/config/verify.zh-CN.json +1 -18
  365. package/templates/.agents/skills/review-analysis/reference/report-template.en.md +50 -23
  366. package/templates/.agents/skills/review-analysis/reference/report-template.zh-CN.md +45 -18
  367. package/templates/.agents/skills/review-code/SKILL.en.md +18 -5
  368. package/templates/.agents/skills/review-code/SKILL.zh-CN.md +18 -5
  369. package/templates/.agents/skills/review-code/config/verify.en.json +1 -23
  370. package/templates/.agents/skills/review-code/config/verify.zh-CN.json +1 -23
  371. package/templates/.agents/skills/review-code/reference/report-template.en.md +49 -22
  372. package/templates/.agents/skills/review-code/reference/report-template.zh-CN.md +45 -18
  373. package/templates/.agents/skills/review-plan/SKILL.en.md +17 -4
  374. package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +17 -4
  375. package/templates/.agents/skills/review-plan/config/verify.en.json +1 -18
  376. package/templates/.agents/skills/review-plan/config/verify.zh-CN.json +1 -18
  377. package/templates/.agents/skills/review-plan/reference/report-template.en.md +49 -22
  378. package/templates/.agents/skills/review-plan/reference/report-template.zh-CN.md +45 -18
  379. package/templates/.agents/skills/review-pr/SKILL.en.md +5 -1
  380. package/templates/.agents/skills/review-pr/SKILL.zh-CN.md +5 -1
  381. package/templates/.agents/skills/review-pr/reference/report-template.en.md +4 -3
  382. package/templates/.agents/skills/review-pr/reference/report-template.zh-CN.md +4 -3
  383. package/templates/.agents/skills/run-manual-validation/SKILL.en.md +9 -3
  384. package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +9 -3
  385. package/templates/.agents/skills/run-manual-validation/reference/report-template.en.md +3 -1
  386. package/templates/.agents/skills/run-manual-validation/reference/report-template.zh-CN.md +3 -1
  387. package/templates/.agents/skills/test/SKILL.en.md +13 -2
  388. package/templates/.agents/skills/test/SKILL.zh-CN.md +13 -2
  389. package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +14 -0
  390. package/templates/.agents/skills/watch-pr/SKILL.en.md +2 -2
  391. package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +2 -2
  392. package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.en.md +6 -0
  393. package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.zh-CN.md +6 -0
  394. package/templates/.agents/templates/task.en.md +24 -1
  395. package/templates/.agents/templates/task.zh-CN.md +24 -1
  396. package/templates/.github/workflows/pr-label.yml +7 -26
  397. package/templates/.agents/scripts/security-alerts.github.sh +0 -151
  398. package/templates/.agents/scripts/security-alerts.sh +0 -31
  399. package/templates/.agents/skills/init-labels/scripts/init-labels.github.sh +0 -146
  400. package/templates/.agents/skills/init-labels/scripts/init-labels.sh +0 -32
  401. package/templates/.agents/skills/init-milestones/scripts/init-milestones.github.sh +0 -294
  402. package/templates/.agents/skills/init-milestones/scripts/init-milestones.sh +0 -6
@@ -3,13 +3,29 @@ import path from 'node:path';
3
3
 
4
4
  import { parseTaskFrontmatter } from '../task/frontmatter.ts';
5
5
  import { resolveTaskRef } from '../task/resolve-ref.ts';
6
- import { createGitHubClient } from './github-client.ts';
7
- import type { GitHubClient } from './github-client.ts';
8
- import { resolvePlatformContext } from './context.ts';
6
+ import { resolvePlatformProviderContext } from './context.ts';
7
+ import type { PlatformClient } from './context.ts';
9
8
  import { platformResult } from './types.ts';
10
9
  import type { PlatformOperation, PlatformResult } from './types.ts';
11
-
12
- type RemoteComment = { id: number; body: string; user?: { login?: string } };
10
+ import {
11
+ providerError,
12
+ providerOperationContext,
13
+ providerStatus,
14
+ providerResourceToken,
15
+ unsupportedProviderOperation
16
+ } from './provider-bridge.ts';
17
+ import { resourceIdentityNumber } from './resource-identity.ts';
18
+ import { taskIssueIdentity, taskIssueIdentityError } from './task-identities.ts';
19
+ import {
20
+ CONTROL_MARKER_PATTERN,
21
+ escapeHtmlText,
22
+ fenceRanges,
23
+ renderSafeCodeFence,
24
+ sanitizeMarkdownDocument
25
+ } from './comment-safety.ts';
26
+ import type { FenceRange } from './comment-safety.ts';
27
+
28
+ type RemoteComment = { id: number | string; body: string; user?: { login?: string } };
13
29
  type RenderedChunk = { marker: string; body: string; content: string; part: number; total: number };
14
30
  type CommentKind = 'task' | 'artifact' | 'summary' | 'cancel';
15
31
  type SyncOptions = {
@@ -19,9 +35,14 @@ type SyncOptions = {
19
35
  body?: string;
20
36
  cwd?: string;
21
37
  backfill?: boolean;
22
- client?: GitHubClient;
38
+ client?: PlatformClient;
39
+ runtimeVersion?: string;
23
40
  };
24
41
 
42
+ function providerCommentId(id: string, provider: { identity?: { comment?: string } }): number | string {
43
+ return provider.identity?.comment === 'number' && /^\d+$/.test(id) ? Number(id) : id;
44
+ }
45
+
25
46
  const MARKERS = {
26
47
  task: (taskId: string) => `<!-- sync-issue:${taskId}:task -->`,
27
48
  artifact: (taskId: string, stem: string) => `<!-- sync-issue:${taskId}:${stem} -->`,
@@ -53,15 +74,22 @@ function splitFrontmatter(content: string): { frontmatter: string | null; body:
53
74
  return { frontmatter: match[1]!, body: content.slice(match[0].length).replace(/^\r?\n/, '') };
54
75
  }
55
76
 
77
+ function sanitizeCommentBody(value: string, label: string): string {
78
+ const result = sanitizeMarkdownDocument(value, { reservedMarkers: [CONTROL_MARKER_PATTERN] });
79
+ if (!result.ok) throw new Error(`${result.error.code}: ${label}: ${result.error.message} at offset ${result.error.offset}`);
80
+ return result.value;
81
+ }
82
+
56
83
  function footer(agent: string, taskId: string): string {
57
84
  return `---\n*由 ${agent} 自动生成 · 内部追踪:${taskId}*`;
58
85
  }
59
86
 
60
87
  function renderTaskComment(content: string, taskId: string, agent: string): string {
61
88
  const split = splitFrontmatter(content);
89
+ const safeBody = sanitizeCommentBody(split.body, 'task body');
62
90
  const taskBody = split.frontmatter === null
63
- ? split.body
64
- : `<details><summary>元数据 (frontmatter)</summary>\n\n\`\`\`yaml\n---\n${split.frontmatter}\n---\n\`\`\`\n\n</details>\n\n${split.body}`;
91
+ ? safeBody
92
+ : `<details><summary>元数据 (frontmatter)</summary>\n\n${renderSafeCodeFence(`---\n${split.frontmatter}\n---`, 'yaml')}\n\n</details>\n\n${safeBody}`;
65
93
  return normalizeCommentContent([
66
94
  MARKERS.task(taskId),
67
95
  '## 任务文件',
@@ -83,13 +111,16 @@ function artifactIdentity(artifact: string): { stem: string; title: string } {
83
111
  return { stem, title: `${base}(Round ${round})` };
84
112
  }
85
113
 
86
- function chunkByUtf8(content: string, maxBytes: number): string[] {
114
+ type SourceChunk = { content: string; start: number; end: number };
115
+
116
+ function chunkByUtf8(content: string, maxBytes: number): SourceChunk[] {
87
117
  if (maxBytes <= 0) throw new Error('comment byte limit is too small');
88
- const chunks: string[] = [];
89
- let remaining = content;
90
- while (remaining) {
118
+ const chunks: SourceChunk[] = [];
119
+ let start = 0;
120
+ while (start < content.length) {
121
+ const remaining = content.slice(start);
91
122
  if (Buffer.byteLength(remaining, 'utf8') <= maxBytes) {
92
- chunks.push(remaining);
123
+ chunks.push({ content: remaining, start, end: content.length });
93
124
  break;
94
125
  }
95
126
  let bytes = 0;
@@ -104,10 +135,102 @@ function chunkByUtf8(content: string, maxBytes: number): string[] {
104
135
  }
105
136
  const cut = newlineIndex > 0 ? newlineIndex : index;
106
137
  if (cut === 0) throw new Error('comment byte limit cannot fit one Unicode code point');
107
- chunks.push(remaining.slice(0, cut));
108
- remaining = remaining.slice(cut);
138
+ chunks.push({ content: remaining.slice(0, cut), start, end: start + cut });
139
+ start += cut;
140
+ }
141
+ return chunks.length > 0 ? chunks : [{ content: '', start: 0, end: 0 }];
142
+ }
143
+
144
+ function longestFenceLineRun(content: string, character: '`' | '~'): number {
145
+ const pattern = new RegExp(`^ {0,3}(${character}+)[ \\t]*(?:\\n|$)`, 'gm');
146
+ let longest = 0;
147
+ for (const match of content.matchAll(pattern)) longest = Math.max(longest, match[1]!.length);
148
+ return longest;
149
+ }
150
+
151
+ function boundedFence(content: string, sourceCharacter: '`' | '~', maxBytes: number): { opening: string; closing: string } | null {
152
+ const candidates: Array<'`' | '~'> = sourceCharacter === '`' ? ['~', '`'] : ['`', '~'];
153
+ for (const character of candidates) {
154
+ const length = Math.max(3, longestFenceLineRun(content, character) + 1);
155
+ const delimiter = character.repeat(length);
156
+ if (Buffer.byteLength(delimiter, 'utf8') * 2 + 2 <= maxBytes) return { opening: `${delimiter}\n`, closing: `${delimiter}\n` };
157
+ }
158
+ return null;
159
+ }
160
+
161
+ function sourceSlice(piece: SourceChunk, start: number, end: number): string {
162
+ return piece.content.slice(Math.max(0, start - piece.start), Math.max(0, end - piece.start));
163
+ }
164
+
165
+ function renderOversizedFenceChunk(piece: SourceChunk, fences: readonly FenceRange[], maxBytes: number): string {
166
+ const insertions = new Map<number, Array<{ value: string; ensureLineStart: boolean }>>();
167
+ const omitted: Array<{ start: number; end: number }> = [];
168
+ const insert = (position: number, value: string, ensureLineStart = false) => {
169
+ const values = insertions.get(position) || [];
170
+ values.push({ value, ensureLineStart });
171
+ insertions.set(position, values);
172
+ };
173
+
174
+ for (const fence of fences) {
175
+ if (piece.end <= fence.start || piece.start >= fence.end) continue;
176
+ const oversized = Buffer.byteLength(fence.opening, 'utf8') > maxBytes
177
+ || Buffer.byteLength(fence.closing, 'utf8') > maxBytes;
178
+ if (!oversized) {
179
+ if (piece.start > fence.start && piece.start < fence.end) insert(piece.start, fence.opening);
180
+ if (piece.end > fence.start && piece.end < fence.end) insert(piece.end, fence.closing, true);
181
+ continue;
182
+ }
183
+
184
+ const code = sourceSlice(piece, Math.max(piece.start, fence.openingEnd), Math.min(piece.end, fence.closingStart));
185
+ const delimiter = boundedFence(code, fence.character, maxBytes);
186
+ if (!delimiter) return escapeHtmlText(piece.content);
187
+ insert(Math.max(piece.start, fence.start), delimiter.opening);
188
+ insert(Math.min(piece.end, fence.end), delimiter.closing, true);
189
+ const openingStart = Math.max(piece.start, fence.start);
190
+ const openingEnd = Math.min(piece.end, fence.openingEnd);
191
+ if (openingStart < openingEnd) omitted.push({ start: openingStart, end: openingEnd });
192
+ const closingStart = Math.max(piece.start, fence.closingStart);
193
+ const closingEnd = Math.min(piece.end, fence.end);
194
+ if (closingStart < closingEnd) omitted.push({ start: closingStart, end: closingEnd });
195
+ }
196
+
197
+ const boundaries = new Set<number>([piece.start, piece.end]);
198
+ for (const position of insertions.keys()) boundaries.add(position);
199
+ for (const range of omitted) {
200
+ boundaries.add(range.start);
201
+ boundaries.add(range.end);
109
202
  }
110
- return chunks.length > 0 ? chunks : [''];
203
+ const points = [...boundaries].sort((left, right) => left - right);
204
+ let content = '';
205
+ for (let index = 0; index < points.length; index += 1) {
206
+ const point = points[index]!;
207
+ for (const insertion of insertions.get(point) || []) {
208
+ if (insertion.ensureLineStart && content.length > 0 && !content.endsWith('\n')) content += '\n';
209
+ content += insertion.value;
210
+ }
211
+ const next = points[index + 1];
212
+ if (next === undefined) continue;
213
+ const skipped = omitted.some((range) => range.start <= point && next <= range.end);
214
+ if (!skipped) content += sourceSlice(piece, point, next);
215
+ }
216
+ return content;
217
+ }
218
+
219
+ function independentChunkContent(piece: SourceChunk, fences: readonly FenceRange[], maxBytes: number): string {
220
+ const oversizedFence = fences.some((fence) =>
221
+ piece.end > fence.start && piece.start < fence.end
222
+ && (Buffer.byteLength(fence.opening, 'utf8') > maxBytes || Buffer.byteLength(fence.closing, 'utf8') > maxBytes)
223
+ );
224
+ if (oversizedFence) return renderOversizedFenceChunk(piece, fences, maxBytes);
225
+ const openingFence = fences.find((fence) => piece.start > fence.start && piece.start < fence.end);
226
+ const closingFence = fences.find((fence) => piece.end > fence.start && piece.end < fence.end);
227
+ let content = piece.content;
228
+ if (openingFence) content = openingFence.opening + content;
229
+ if (closingFence) {
230
+ if (!content.endsWith('\n')) content += '\n';
231
+ content += closingFence.closing;
232
+ }
233
+ return content;
111
234
  }
112
235
 
113
236
  function buildArtifactChunk(
@@ -119,7 +242,8 @@ function buildArtifactChunk(
119
242
  part: number,
120
243
  total: number,
121
244
  chunked: boolean,
122
- backfill: boolean
245
+ backfill: boolean,
246
+ renderedContent = content
123
247
  ): RenderedChunk {
124
248
  const marker = chunked ? MARKERS.artifactChunk(taskId, stem, part, total) : MARKERS.artifact(taskId, stem);
125
249
  const heading = chunked ? `## ${title}(${part}/${total})` : `## ${title}`;
@@ -131,7 +255,7 @@ function buildArtifactChunk(
131
255
  '',
132
256
  `> **${agent}** · ${taskId}`,
133
257
  '',
134
- content,
258
+ renderedContent,
135
259
  '',
136
260
  footer(agent, taskId)
137
261
  ].join('\n'));
@@ -148,29 +272,44 @@ function chunkArtifactComment(input: {
148
272
  }): RenderedChunk[] {
149
273
  const byteLimit = input.byteLimit || 60_000;
150
274
  const identity = artifactIdentity(input.artifact);
275
+ const safeBody = sanitizeCommentBody(input.body, 'artifact body');
151
276
  const single = buildArtifactChunk(
152
- input.taskId, identity.stem, identity.title, input.agent, input.body, 1, 1, false, Boolean(input.backfill)
277
+ input.taskId, identity.stem, identity.title, input.agent, safeBody, 1, 1, false, Boolean(input.backfill)
153
278
  );
154
279
  if (Buffer.byteLength(single.body, 'utf8') <= byteLimit) return [single];
155
280
 
156
281
  let total = 2;
157
- let pieces: string[] = [];
282
+ let payloadLimit = Number.POSITIVE_INFINITY;
283
+ const ranges = fenceRanges(safeBody);
284
+ if (!ranges.ok) throw new Error(`${ranges.error.code}: artifact body: ${ranges.error.message} at offset ${ranges.error.offset}`);
158
285
  for (;;) {
159
286
  const probe = buildArtifactChunk(
160
287
  input.taskId, identity.stem, identity.title, input.agent, '', total, total, true, Boolean(input.backfill)
161
288
  );
162
289
  const available = byteLimit - Buffer.byteLength(probe.body, 'utf8');
163
- pieces = chunkByUtf8(input.body, available);
164
- if (pieces.length === total) break;
165
- total = pieces.length;
290
+ const sourceLimit = Math.min(available, payloadLimit);
291
+ if (sourceLimit <= 0) throw new Error('comment byte limit is too small for an artifact chunk');
292
+ const pieces = chunkByUtf8(safeBody, sourceLimit);
293
+ if (pieces.length !== total) {
294
+ total = pieces.length;
295
+ continue;
296
+ }
297
+ const chunks = pieces.map((piece, index) => buildArtifactChunk(
298
+ input.taskId,
299
+ identity.stem,
300
+ identity.title,
301
+ input.agent,
302
+ piece.content,
303
+ index + 1,
304
+ total,
305
+ true,
306
+ Boolean(input.backfill),
307
+ independentChunkContent(piece, ranges.value, sourceLimit)
308
+ ));
309
+ const overflow = Math.max(...chunks.map((chunk) => Buffer.byteLength(chunk.body, 'utf8') - byteLimit));
310
+ if (overflow <= 0) return chunks;
311
+ payloadLimit = sourceLimit - overflow;
166
312
  }
167
- return pieces.map((content, index) => {
168
- const chunk = buildArtifactChunk(
169
- input.taskId, identity.stem, identity.title, input.agent, content, index + 1, total, true, Boolean(input.backfill)
170
- );
171
- if (Buffer.byteLength(chunk.body, 'utf8') > byteLimit) throw new Error('rendered comment exceeds byte limit');
172
- return chunk;
173
- });
174
313
  }
175
314
 
176
315
  function findMarkerComments(comments: RemoteComment[], marker: string): RemoteComment[] {
@@ -181,7 +320,7 @@ function flattenComments(value: unknown): RemoteComment[] {
181
320
  if (!Array.isArray(value)) return [];
182
321
  const flattened = value.flatMap((entry) => Array.isArray(entry) ? entry : [entry]);
183
322
  return flattened.filter((entry): entry is RemoteComment =>
184
- Boolean(entry && typeof entry === 'object' && typeof entry.id === 'number' && typeof entry.body === 'string')
323
+ Boolean(entry && typeof entry === 'object' && (typeof entry.id === 'number' || typeof entry.id === 'string') && typeof entry.body === 'string')
185
324
  );
186
325
  }
187
326
 
@@ -191,7 +330,7 @@ function issueNumberFromTask(content: string): number | null {
191
330
  return Number.isInteger(number) && number > 0 ? number : null;
192
331
  }
193
332
 
194
- function listRemoteComments(client: GitHubClient, repo: string, issue: number, cwd: string) {
333
+ function listRemoteComments(client: PlatformClient, repo: string, issue: number, cwd: string) {
195
334
  const result = client.json<unknown>([
196
335
  'api', '--paginate', '--slurp', `repos/${repo}/issues/${issue}/comments?per_page=100`
197
336
  ], { cwd });
@@ -211,7 +350,7 @@ function hasResolvedPlatformContext(context: PlatformResult): boolean {
211
350
 
212
351
  function bodyEnvelope(marker: string, title: string, taskId: string, agent: string, body: string): string {
213
352
  return normalizeCommentContent([
214
- marker, `## ${title}`, '', `> **${agent}** · ${taskId}`, '', body.replace(/\n+$/, ''), '', footer(agent, taskId)
353
+ marker, `## ${title}`, '', `> **${agent}** · ${taskId}`, '', sanitizeCommentBody(body, `${title} body`).replace(/\n+$/, ''), '', footer(agent, taskId)
215
354
  ].join('\n'));
216
355
  }
217
356
 
@@ -291,7 +430,7 @@ function validateRelatedMarkerSet(comments: RemoteComment[], prefix: string): {
291
430
  }
292
431
 
293
432
  function writeComment(
294
- client: GitHubClient,
433
+ client: PlatformClient,
295
434
  repo: string,
296
435
  issue: number,
297
436
  cwd: string,
@@ -305,7 +444,7 @@ function writeComment(
305
444
  );
306
445
  }
307
446
 
308
- function syncPlatformComment(taskRef: string, options: SyncOptions): PlatformResult {
447
+ async function syncPlatformComment(taskRef: string, options: SyncOptions): Promise<PlatformResult> {
309
448
  const resolved = resolveTaskRef(taskRef, options.cwd ? { repoRoot: options.cwd } : {});
310
449
  if (!resolved.ok) {
311
450
  return platformResult('failed', {
@@ -313,32 +452,45 @@ function syncPlatformComment(taskRef: string, options: SyncOptions): PlatformRes
313
452
  });
314
453
  }
315
454
  const taskContent = fs.readFileSync(resolved.taskMdPath, 'utf8');
316
- const issue = issueNumberFromTask(taskContent);
317
- if (!issue) {
455
+ let issueIdentityFromTask: ReturnType<typeof taskIssueIdentity>;
456
+ try { issueIdentityFromTask = taskIssueIdentity(parseTaskFrontmatter(taskContent), undefined, options.runtimeVersion); }
457
+ catch (error) { return platformResult('failed', { error: { ...taskIssueIdentityError(error), retryable: false } }); }
458
+ if (!issueIdentityFromTask) {
318
459
  return platformResult('no-op', {
319
460
  error: { code: 'ISSUE_NOT_LINKED', message: 'Task has no valid issue_number', retryable: false }
320
461
  });
321
462
  }
322
- const context = resolvePlatformContext({ cwd: resolved.repoRoot, client: options.client });
323
- if (!hasResolvedPlatformContext(context)) return context;
324
- const client = options.client || createGitHubClient();
325
- const listed = listRemoteComments(client, context.platform.repository!, issue, resolved.repoRoot);
326
- if (!listed.ok) {
327
- return platformResult(listed.error.retryable ? 'blocked' : 'failed', {
328
- ...contextFields(context), resource: { kind: 'issue', number: issue }, error: listed.error
329
- });
330
- }
331
-
332
463
  let desired: RenderedChunk[];
333
464
  try {
334
465
  desired = expectedComments(resolved.taskId, taskContent, resolved.taskDir, options);
335
466
  } catch (error) {
336
467
  return platformResult('failed', {
337
- ...contextFields(context),
338
- resource: { kind: 'issue', number: issue },
468
+ resource: { kind: 'issue', number: resourceIdentityNumber(issueIdentityFromTask) },
339
469
  error: { code: 'COMMENT_PAYLOAD_INVALID', message: error instanceof Error ? error.message : String(error), retryable: false }
340
470
  });
341
471
  }
472
+ const loaded = await resolvePlatformProviderContext({ cwd: resolved.repoRoot, client: options.client });
473
+ const context = loaded.ok ? loaded.value.context : loaded.context;
474
+ if (!hasResolvedPlatformContext(context) || !loaded.ok) return context;
475
+ const issue = resourceIdentityNumber(issueIdentityFromTask);
476
+ const listed = loaded.value.provider.comments?.list
477
+ ? await loaded.value.provider.comments.list({
478
+ context: providerOperationContext(loaded.value),
479
+ parent: issueIdentityFromTask
480
+ }).then((response) => response.ok
481
+ ? { ok: true as const, value: response.value.map((comment) => ({
482
+ id: providerCommentId(comment.id, loaded.value.provider),
483
+ body: comment.body,
484
+ user: comment.author?.name ? { login: comment.author.name } : undefined
485
+ })) }
486
+ : response)
487
+ : unsupportedProviderOperation(loaded.value.provider, 'comments.list');
488
+ if (!listed.ok) {
489
+ return platformResult(listed.error.retryable ? 'blocked' : 'failed', {
490
+ ...contextFields(context), resource: { kind: 'issue', number: issue }, error: listed.error
491
+ });
492
+ }
493
+
342
494
  const existing = relatedComments(listed.value, markerPrefix(resolved.taskId, options));
343
495
  if (!validateRelatedMarkerSet(existing, markerPrefix(resolved.taskId, options)).ok) {
344
496
  return platformResult('failed', {
@@ -370,9 +522,8 @@ function syncPlatformComment(taskRef: string, options: SyncOptions): PlatformRes
370
522
  });
371
523
  }
372
524
 
373
- const repo = context.platform.repository!;
374
525
  const operations: PlatformOperation[] = [];
375
- const ids: number[] = [];
526
+ const ids: Array<number | string> = [];
376
527
  for (const item of desired) {
377
528
  const current = existing.find((comment) => normalizeCommentContent(comment.body).split('\n', 1)[0] === item.marker);
378
529
  if (current && normalizeCommentContent(current.body) === normalizeCommentContent(item.body)) {
@@ -380,16 +531,18 @@ function syncPlatformComment(taskRef: string, options: SyncOptions): PlatformRes
380
531
  operations.push({ name: `comment:${item.marker}`, status: 'no-op', reasonCode: null });
381
532
  continue;
382
533
  }
383
- const written = writeComment(client, repo, issue, resolved.repoRoot, item.body, current?.id);
534
+ const written = loaded.value.provider.comments?.write
535
+ ? await loaded.value.provider.comments.write({
536
+ context: providerOperationContext(loaded.value),
537
+ parent: issueIdentityFromTask,
538
+ body: item.body,
539
+ ...(current ? { existingComment: { kind: 'id' as const, value: String(current.id) } } : {}),
540
+ mutation: { idempotencyKey: `comment:${resolved.taskId}:${item.marker}` }
541
+ })
542
+ : unsupportedProviderOperation(loaded.value.provider, 'comments.write');
384
543
  if (!written.ok) {
385
544
  if (!current && written.error.retryable) {
386
- const reconciled = listRemoteComments(client, repo, issue, resolved.repoRoot);
387
- const found = reconciled.ok ? findMarkerComments(reconciled.value, item.marker) : [];
388
- if (found.length === 1) {
389
- ids.push(found[0]!.id);
390
- operations.push({ name: `comment:${item.marker}`, status: 'applied', reasonCode: 'CREATE_RECONCILED' });
391
- continue;
392
- }
545
+ // A provider owns reconciliation because only it knows how to address the resource.
393
546
  }
394
547
  return platformResult(written.error.retryable ? 'blocked' : 'failed', {
395
548
  ...contextFields(context),
@@ -398,16 +551,21 @@ function syncPlatformComment(taskRef: string, options: SyncOptions): PlatformRes
398
551
  error: written.error
399
552
  });
400
553
  }
401
- ids.push(current?.id || written.value.id || 0);
554
+ ids.push(providerCommentId((written.value as { remoteId: string }).remoteId, loaded.value.provider));
402
555
  operations.push({ name: `comment:${item.marker}`, status: 'applied', reasonCode: null });
403
556
  }
404
557
 
405
558
  const desiredMarkers = new Set(desired.map((item) => item.marker));
406
559
  const stale = existing.filter((comment) => !desiredMarkers.has(normalizeCommentContent(comment.body).split('\n', 1)[0]!));
407
560
  for (const comment of stale) {
408
- const deleted = client.text(['api', `repos/${repo}/issues/comments/${comment.id}`, '-X', 'DELETE'], {
409
- cwd: resolved.repoRoot, method: 'DELETE'
410
- });
561
+ const deleted = loaded.value.provider.comments?.delete
562
+ ? await loaded.value.provider.comments.delete({
563
+ context: providerOperationContext(loaded.value),
564
+ parent: issueIdentityFromTask,
565
+ comment: { kind: 'id', value: String(comment.id) },
566
+ mutation: { idempotencyKey: `comment-delete:${resolved.taskId}:${comment.id}` }
567
+ })
568
+ : unsupportedProviderOperation(loaded.value.provider, 'comments.delete');
411
569
  if (!deleted.ok) {
412
570
  return platformResult(deleted.error.retryable ? 'blocked' : 'failed', {
413
571
  ...contextFields(context),
@@ -429,28 +587,45 @@ function syncPlatformComment(taskRef: string, options: SyncOptions): PlatformRes
429
587
  });
430
588
  }
431
589
 
432
- function listPlatformComments(issue: number, cwd = process.cwd(), client: GitHubClient = createGitHubClient()): PlatformResult & { comments?: RemoteComment[] } {
433
- const context = resolvePlatformContext({ cwd, client });
434
- if (!hasResolvedPlatformContext(context)) return context;
435
- const result = listRemoteComments(client, context.platform.repository!, issue, cwd);
436
- if (!result.ok) {
437
- return platformResult(result.error.retryable ? 'blocked' : 'failed', {
438
- ...contextFields(context), resource: { kind: 'issue', number: issue }, error: result.error
590
+ async function listPlatformComments(issue: string | number, cwd = process.cwd(), client?: PlatformClient): Promise<PlatformResult & { comments?: RemoteComment[] }> {
591
+ const loaded = await resolvePlatformProviderContext({ cwd, client });
592
+ const context = loaded.ok ? loaded.value.context : loaded.context;
593
+ if (!hasResolvedPlatformContext(context) || !loaded.ok) return context;
594
+ let identity;
595
+ try { identity = loaded.ok ? providerResourceToken(loaded.value.provider, 'issue', String(issue)) : null; }
596
+ catch (error) {
597
+ return platformResult('failed', { ...contextFields(context), error: { code: 'PLATFORM_IDENTITY_TOKEN_INVALID', message: error instanceof Error ? error.message : String(error), retryable: false } });
598
+ }
599
+ if (!loaded.ok || !identity) return context;
600
+ const listed = loaded.value.provider.comments?.list
601
+ ? await loaded.value.provider.comments.list({ context: providerOperationContext(loaded.value), parent: identity }).then((response) => response.ok
602
+ ? { ok: true as const, value: response.value.map((comment) => ({ id: comment.id, body: comment.body, user: comment.author?.name ? { login: comment.author.name } : undefined })) }
603
+ : response)
604
+ : unsupportedProviderOperation(loaded.value.provider, 'comments.list');
605
+ if (!listed.ok) {
606
+ return platformResult(providerStatus(listed.error), {
607
+ ...contextFields(context), resource: { kind: 'issue', number: resourceIdentityNumber(identity), identity }, error: providerError(listed.error, 'PLATFORM_PROVIDER_OPERATION_FAILED')
439
608
  });
440
609
  }
441
- return { ...platformResult('no-op', { ...contextFields(context), resource: { kind: 'issue', number: issue }, error: null }), comments: result.value };
610
+ return { ...platformResult('no-op', { ...contextFields(context), resource: { kind: 'issue', number: resourceIdentityNumber(identity), identity }, error: null }), comments: listed.value };
442
611
  }
443
612
 
444
- function checkPlatformCommentOwner(taskRef: string, options: { cwd?: string; client?: GitHubClient } = {}): PlatformResult {
613
+ async function checkPlatformCommentOwner(taskRef: string, options: { cwd?: string; client?: PlatformClient; runtimeVersion?: string } = {}): Promise<PlatformResult> {
445
614
  const resolved = resolveTaskRef(taskRef, options.cwd ? { repoRoot: options.cwd } : {});
446
615
  if (!resolved.ok) return platformResult('failed', { error: { code: resolved.code, message: resolved.message, retryable: false } });
447
616
  const content = fs.readFileSync(resolved.taskMdPath, 'utf8');
448
- const issue = issueNumberFromTask(content);
449
- if (!issue) return platformResult('no-op', { error: { code: 'ISSUE_NOT_LINKED', message: 'Task has no valid issue_number', retryable: false } });
450
- const client = options.client || createGitHubClient();
451
- const context = resolvePlatformContext({ cwd: resolved.repoRoot, client });
452
- if (!hasResolvedPlatformContext(context)) return context;
453
- const listed = listRemoteComments(client, context.platform.repository!, issue, resolved.repoRoot);
617
+ let issueIdentity: ReturnType<typeof taskIssueIdentity>;
618
+ try { issueIdentity = taskIssueIdentity(parseTaskFrontmatter(content), undefined, options.runtimeVersion); }
619
+ catch (error) { return platformResult('failed', { error: { ...taskIssueIdentityError(error), retryable: false } }); }
620
+ if (!issueIdentity) return platformResult('no-op', { error: { code: 'ISSUE_NOT_LINKED', message: 'Task has no valid platform issue identity', retryable: false } });
621
+ const loaded = await resolvePlatformProviderContext({ cwd: resolved.repoRoot, client: options.client });
622
+ const context = loaded.ok ? loaded.value.context : loaded.context;
623
+ if (!hasResolvedPlatformContext(context) || !loaded.ok) return context;
624
+ const listed = loaded.value.provider.comments?.list
625
+ ? await loaded.value.provider.comments.list({ context: providerOperationContext(loaded.value), parent: issueIdentity }).then((response) => response.ok
626
+ ? { ok: true as const, value: response.value.map((comment) => ({ id: comment.id, body: comment.body, user: comment.author?.name ? { login: comment.author.name } : undefined })) }
627
+ : response)
628
+ : unsupportedProviderOperation(loaded.value.provider, 'comments.list');
454
629
  if (!listed.ok) return platformResult(listed.error.retryable ? 'blocked' : 'failed', { ...contextFields(context), error: listed.error });
455
630
  const matches = findMarkerComments(listed.value, MARKERS.task(resolved.taskId));
456
631
  if (matches.length > 1) return platformResult('failed', { ...contextFields(context), error: { code: 'COMMENT_MARKER_CONFLICT', message: 'Multiple task comments use the registered marker', retryable: false } });
@@ -458,11 +633,11 @@ function checkPlatformCommentOwner(taskRef: string, options: { cwd?: string; cli
458
633
  if (owner && owner !== context.platform.currentUser && !context.capabilities.triage) {
459
634
  return platformResult('blocked', {
460
635
  ...contextFields(context),
461
- resource: { kind: 'issue', number: issue },
636
+ resource: { kind: 'issue', number: resourceIdentityNumber(issueIdentity), identity: issueIdentity },
462
637
  error: { code: 'COMMENT_OWNER_CONFLICT', message: `Task comment is owned by '${owner}'`, retryable: false }
463
638
  });
464
639
  }
465
- return platformResult('no-op', { ...contextFields(context), resource: { kind: 'issue', number: issue }, error: null });
640
+ return platformResult('no-op', { ...contextFields(context), resource: { kind: 'issue', number: resourceIdentityNumber(issueIdentity), identity: issueIdentity }, error: null });
466
641
  }
467
642
 
468
643
  export {