@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
@@ -0,0 +1,626 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { createHash } from 'node:crypto';
4
+
5
+ import { scanVisibleMarkdown } from './decision-details.ts';
6
+ import { locateActivityLog, pairEntries, startedBackedRows } from './activity-log.ts';
7
+ import { readArtifactRepairIntent } from './artifact-repair-intent.ts';
8
+ import {
9
+ getArtifactSchema,
10
+ renderArtifactSkeleton
11
+ } from './artifact-schema.ts';
12
+ import type {
13
+ ArtifactSchema,
14
+ ArtifactSchemaFamily,
15
+ ArtifactSection
16
+ } from './artifact-schema.ts';
17
+ import { withTaskExecutionLock } from './task-execution-lock.ts';
18
+
19
+ type ArtifactRepairOperation = Readonly<{
20
+ kind: 'replace-line' | 'insert-section';
21
+ sectionId: string;
22
+ line: number;
23
+ from: string;
24
+ to: string;
25
+ start: number;
26
+ end: number;
27
+ }>;
28
+
29
+ type ArtifactStructuralDiagnosticCode =
30
+ | 'ARTIFACT_EMPTY'
31
+ | 'ARTIFACT_MISSING_SECTION'
32
+ | 'ARTIFACT_DUPLICATE_SECTION'
33
+ | 'ARTIFACT_UNCLOSED_FENCE'
34
+ | 'ARTIFACT_MARKER_MISSING'
35
+ | 'ARTIFACT_MARKER_DUPLICATE'
36
+ | 'ARTIFACT_MARKER_MISMATCH'
37
+ | 'ARTIFACT_EMPTY_SECTION'
38
+ | 'ARTIFACT_SECTION_ORDER_INVALID'
39
+ | 'ARTIFACT_HEADING_TRAILING_PUNCTUATION'
40
+ | 'ARTIFACT_REQUIRED_PATTERN_MISSING';
41
+
42
+ type ArtifactStructuralDiagnostic = Readonly<{
43
+ code: ArtifactStructuralDiagnosticCode;
44
+ message: string;
45
+ sectionId: string | null;
46
+ line: number | null;
47
+ repairable: boolean;
48
+ operation?: ArtifactRepairOperation;
49
+ }>;
50
+
51
+ type ArtifactStructureResult = Readonly<{
52
+ ok: boolean;
53
+ family: ArtifactSchemaFamily;
54
+ semanticDigest: string;
55
+ diagnostics: readonly ArtifactStructuralDiagnostic[];
56
+ repair: ArtifactRepairOperation | null;
57
+ }>;
58
+
59
+ type ArtifactFileResult = Readonly<{
60
+ status: 'applied' | 'no-op' | 'failed';
61
+ changed: boolean;
62
+ artifactSha256: string | null;
63
+ semanticDigest: string | null;
64
+ operation: ArtifactRepairOperation | null;
65
+ error: { code: string; message: string } | null;
66
+ }>;
67
+
68
+ type ArtifactRepairRequest = Readonly<{
69
+ repoRoot: string;
70
+ taskId: string;
71
+ taskDir: string;
72
+ family: ArtifactSchemaFamily;
73
+ artifact: string;
74
+ expectedSha256: string;
75
+ expectedSemanticDigest: string;
76
+ operation: ArtifactRepairOperation;
77
+ }>;
78
+
79
+ type ArtifactInitRequest = Readonly<{
80
+ repoRoot: string;
81
+ taskId: string;
82
+ taskDir: string;
83
+ family: ArtifactSchemaFamily;
84
+ artifact: string;
85
+ locale?: 'zh-CN' | 'en';
86
+ }>;
87
+
88
+ function sha256Content(content: string): string {
89
+ return createHash('sha256').update(content, 'utf8').digest('hex');
90
+ }
91
+
92
+ function sourceLines(content: string): Array<{ start: number; end: number; text: string }> {
93
+ const lines: Array<{ start: number; end: number; text: string }> = [];
94
+ let start = 0;
95
+ while (start < content.length) {
96
+ const newline = content.indexOf('\n', start);
97
+ const end = newline === -1 ? content.length : newline;
98
+ lines.push({ start, end, text: content.slice(start, end).replace(/\r$/, '') });
99
+ start = newline === -1 ? content.length : newline + 1;
100
+ }
101
+ if (content.length === 0) lines.push({ start: 0, end: 0, text: '' });
102
+ return lines;
103
+ }
104
+
105
+ function hasUnclosedFence(content: string): boolean {
106
+ let fence: { character: '`' | '~'; length: number } | null = null;
107
+ for (const line of sourceLines(content)) {
108
+ if (fence) {
109
+ const close = line.text.match(/^ {0,3}(`{3,}|~{3,})\s*$/);
110
+ if (close && close[1]![0] === fence.character && close[1]!.length >= fence.length) fence = null;
111
+ continue;
112
+ }
113
+ const open = line.text.match(/^ {0,3}(`{3,}|~{3,})(.*)$/);
114
+ if (open && !(open[1]![0] === '`' && open[2]!.includes('`'))) {
115
+ fence = { character: open[1]![0] as '`' | '~', length: open[1]!.length };
116
+ }
117
+ }
118
+ return fence !== null;
119
+ }
120
+
121
+ function lineNumber(content: string, offset: number): number {
122
+ return content.slice(0, offset).split('\n').length;
123
+ }
124
+
125
+ function markerPattern(marker: string): RegExp {
126
+ return new RegExp(`<!--\\s*${escapeRegExp(marker)}\\s*-->`);
127
+ }
128
+
129
+ function escapeRegExp(value: string): string {
130
+ return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
131
+ }
132
+
133
+ function markerEntries(content: string): Array<{ marker: string; start: number; end: number; line: number; standalone: boolean }> {
134
+ const entries: Array<{ marker: string; start: number; end: number; line: number; standalone: boolean }> = [];
135
+ for (const line of scanVisibleMarkdown(content).lines) {
136
+ const match = line.text.match(/<!--\s*(artifact-section:[^\s>]+)\s*-->/g);
137
+ if (!match) continue;
138
+ for (const raw of match) {
139
+ const marker = raw.match(/artifact-section:[^\s>]+/)![0]!;
140
+ const offset = line.text.indexOf(raw);
141
+ entries.push({
142
+ marker,
143
+ start: line.start + offset,
144
+ end: line.start + offset + raw.length,
145
+ line: lineNumber(content, line.start),
146
+ standalone: line.text.trim() === raw
147
+ });
148
+ }
149
+ }
150
+ return entries;
151
+ }
152
+
153
+ function sectionBodyBounds(content: string, heading: { start: number }): { start: number; end: number } {
154
+ const next = scanVisibleMarkdown(content).headings.find((candidate) => (
155
+ candidate.start > heading.start && candidate.level <= 2
156
+ ));
157
+ const headingEnd = scanVisibleMarkdown(content).headings.find((candidate) => candidate.start === heading.start)?.end ?? heading.start;
158
+ return { start: headingEnd, end: next?.start ?? content.length };
159
+ }
160
+
161
+ function stripHtmlComments(content: string): string {
162
+ let result = '';
163
+ let cursor = 0;
164
+ while (cursor < content.length) {
165
+ const start = content.indexOf('<!--', cursor);
166
+ if (start < 0) return result + content.slice(cursor);
167
+ result += content.slice(cursor, start);
168
+ const end = content.indexOf('-->', start + 4);
169
+ if (end < 0) return result;
170
+ cursor = end + 3;
171
+ }
172
+ return result;
173
+ }
174
+
175
+ function diagnostic(
176
+ code: ArtifactStructuralDiagnosticCode,
177
+ message: string,
178
+ sectionId: string | null,
179
+ line: number | null,
180
+ repairable = false,
181
+ operation?: ArtifactRepairOperation
182
+ ): ArtifactStructuralDiagnostic {
183
+ return {
184
+ code, message, sectionId, line, repairable,
185
+ ...(operation ? { operation } : {})
186
+ };
187
+ }
188
+
189
+ function sectionHeadings(schema: ArtifactSchema, section: ArtifactSection): readonly string[] {
190
+ return [section.headings.zh, section.headings.en];
191
+ }
192
+
193
+ function createHeadingRepair(
194
+ content: string,
195
+ section: ArtifactSection,
196
+ heading: { start: number; end: number; text: string }
197
+ ): ArtifactRepairOperation {
198
+ const to = heading.text.slice(0, -1);
199
+ return {
200
+ kind: 'replace-line',
201
+ sectionId: section.id,
202
+ line: lineNumber(content, heading.start),
203
+ from: heading.text,
204
+ to,
205
+ start: heading.start,
206
+ end: heading.end
207
+ };
208
+ }
209
+
210
+ function createSectionInsertionRepair(
211
+ content: string,
212
+ section: ArtifactSection,
213
+ marker: { start: number; line: number },
214
+ locale: 'zh-CN' | 'en'
215
+ ): ArtifactRepairOperation {
216
+ return {
217
+ kind: 'insert-section',
218
+ sectionId: section.id,
219
+ line: marker.line,
220
+ from: '',
221
+ to: `## ${section.headings[locale === 'en' ? 'en' : 'zh']}\n`,
222
+ start: marker.start,
223
+ end: marker.start
224
+ };
225
+ }
226
+
227
+ function applyRepairOperation(content: string, operation: ArtifactRepairOperation): string | null {
228
+ if (operation.start < 0 || operation.end < operation.start || operation.end > content.length) return null;
229
+ if (operation.kind === 'insert-section') {
230
+ if (operation.from !== '' || operation.start !== operation.end || !operation.to) return null;
231
+ return `${content.slice(0, operation.start)}${operation.to}${content.slice(operation.end)}`;
232
+ }
233
+ const raw = content.slice(operation.start, operation.end);
234
+ const offset = raw.indexOf(operation.from);
235
+ if (!operation.from || offset < 0 || raw.indexOf(operation.from, offset + operation.from.length) >= 0) return null;
236
+ return `${content.slice(0, operation.start)}${raw.slice(0, offset)}${operation.to}${raw.slice(offset + operation.from.length)}${content.slice(operation.end)}`;
237
+ }
238
+
239
+ function canonicalContent(content: string, operation: ArtifactRepairOperation | null = null): string {
240
+ let normalized = content;
241
+ if (operation) {
242
+ normalized = applyRepairOperation(normalized, operation) ?? normalized;
243
+ }
244
+ normalized = normalized.replace(/\r\n/g, '\n');
245
+ normalized = normalized.replace(/^<!--\s*artifact-context:[^\n]+-->\s*\n?/gm, '');
246
+ normalized = normalized.replace(/^\s*<!--\s*artifact-section:[^\n]+-->\s*\n?/gm, '');
247
+ normalized = normalized.replace(/^\s*<!--\s*artifact-slot:empty\s*-->\s*\n?/gm, '');
248
+ return normalized;
249
+ }
250
+
251
+ function canonicalSemanticDigest(content: string, operation: ArtifactRepairOperation | null = null): string {
252
+ return sha256Content(canonicalContent(content, operation));
253
+ }
254
+
255
+ function inspectArtifactStructure(
256
+ content: string,
257
+ schema: ArtifactSchema
258
+ ): ArtifactStructureResult {
259
+ const diagnostics: ArtifactStructuralDiagnostic[] = [];
260
+ const scanned = scanVisibleMarkdown(content);
261
+ const repairCandidates: ArtifactRepairOperation[] = [];
262
+ if (!content.trim()) diagnostics.push(diagnostic('ARTIFACT_EMPTY', 'artifact is empty', null, null));
263
+ if (hasUnclosedFence(content)) diagnostics.push(diagnostic('ARTIFACT_UNCLOSED_FENCE', 'artifact contains an unclosed Markdown fence', null, null));
264
+
265
+ const headingsBySection = new Map<string, typeof scanned.headings>();
266
+ for (const section of schema.sections) {
267
+ const aliases = sectionHeadings(schema, section);
268
+ const exact = scanned.headings.filter((heading) => heading.level === 2 && aliases.includes(heading.text));
269
+ const punctuated = scanned.headings.filter((heading) => heading.level === 2 && aliases.some((alias) => heading.text === `${alias}:` || heading.text === `${alias}:`));
270
+ headingsBySection.set(section.id, exact.length === 0 && punctuated.length === 1 ? punctuated : exact);
271
+ if (exact.length > 1 || punctuated.length > 0 && exact.length > 0 || punctuated.length > 1) {
272
+ const first = exact[1] ?? punctuated[0] ?? exact[0];
273
+ diagnostics.push(diagnostic(
274
+ 'ARTIFACT_DUPLICATE_SECTION',
275
+ `required section '${aliases[0]}' is duplicated or has an ambiguous punctuation variant`,
276
+ section.id,
277
+ first ? lineNumber(content, first.start) : null
278
+ ));
279
+ } else if (exact.length === 0 && punctuated.length === 1) {
280
+ repairCandidates.push(createHeadingRepair(content, section, punctuated[0]!));
281
+ } else if (exact.length === 0) {
282
+ diagnostics.push(diagnostic('ARTIFACT_MISSING_SECTION', `required section '${aliases[0]}' is missing`, section.id, null));
283
+ }
284
+ }
285
+
286
+ const markers = markerEntries(content);
287
+ const englishHeadings = scanned.headings.filter((heading) => heading.level === 2 && schema.sections.some((section) => section.headings.en === heading.text)).length;
288
+ const chineseHeadings = scanned.headings.filter((heading) => heading.level === 2 && schema.sections.some((section) => section.headings.zh === heading.text)).length;
289
+ const locale = englishHeadings > chineseHeadings ? 'en' : 'zh-CN';
290
+ for (const section of schema.sections) {
291
+ const matches = markers.filter((entry) => entry.marker === section.marker);
292
+ if (matches.length === 0) {
293
+ diagnostics.push(diagnostic('ARTIFACT_MARKER_MISSING', `section marker '${section.marker}' is missing`, section.id, null));
294
+ } else if (matches.length > 1) {
295
+ diagnostics.push(diagnostic('ARTIFACT_MARKER_DUPLICATE', `section marker '${section.marker}' is duplicated`, section.id, matches[1]!.line));
296
+ }
297
+ const marker = matches[0];
298
+ const heading = headingsBySection.get(section.id)?.[0];
299
+ if (marker && !marker.standalone) {
300
+ diagnostics.push(diagnostic('ARTIFACT_MARKER_MISMATCH', `section marker '${section.marker}' must occupy its own visible line`, section.id, marker.line));
301
+ }
302
+ if (marker && heading) {
303
+ const bounds = sectionBodyBounds(content, heading);
304
+ if (marker.start < bounds.start || marker.start >= bounds.end) {
305
+ diagnostics.push(diagnostic('ARTIFACT_MARKER_MISMATCH', `section marker '${section.marker}' is outside its section`, section.id, marker.line));
306
+ }
307
+ const body = stripHtmlComments(content.slice(bounds.start, bounds.end)).trim();
308
+ if (!body) diagnostics.push(diagnostic('ARTIFACT_EMPTY_SECTION', `section '${section.headings.zh}' has no semantic body`, section.id, lineNumber(content, heading.start)));
309
+ }
310
+ if (marker && !heading && marker.standalone) {
311
+ const nextHeading = scanned.headings.find((candidate) => candidate.start > marker.start && candidate.level <= 2);
312
+ const body = stripHtmlComments(content.slice(marker.end, nextHeading?.start ?? content.length)).trim();
313
+ if (body && repairCandidates.length === 0) {
314
+ repairCandidates.push(createSectionInsertionRepair(content, section, marker, locale));
315
+ } else if (!body) {
316
+ diagnostics.push(diagnostic('ARTIFACT_EMPTY_SECTION', `section '${section.headings.zh}' has no semantic body`, section.id, marker.line));
317
+ }
318
+ }
319
+ }
320
+ for (const marker of markers) {
321
+ if (!schema.sections.some((section) => markerPattern(section.marker).test(`<!-- ${marker.marker} -->`))) {
322
+ diagnostics.push(diagnostic('ARTIFACT_MARKER_MISMATCH', `unknown section marker '${marker.marker}'`, null, marker.line));
323
+ }
324
+ }
325
+
326
+ for (const candidate of repairCandidates) {
327
+ if (candidate.kind === 'replace-line') {
328
+ diagnostics.push(diagnostic(
329
+ 'ARTIFACT_HEADING_TRAILING_PUNCTUATION',
330
+ `visible required H2 '${candidate.from}' may be normalized to '${candidate.to}'`,
331
+ candidate.sectionId,
332
+ candidate.line,
333
+ true,
334
+ candidate
335
+ ));
336
+ continue;
337
+ }
338
+ const missing = diagnostics.findIndex((item) => item.code === 'ARTIFACT_MISSING_SECTION' && item.sectionId === candidate.sectionId);
339
+ if (missing >= 0) {
340
+ const section = schema.sections.find((item) => item.id === candidate.sectionId)!;
341
+ diagnostics.splice(missing, 1, diagnostic(
342
+ 'ARTIFACT_MISSING_SECTION',
343
+ `required section '${section.headings.zh}' is missing and can be inserted at its unique marker`,
344
+ candidate.sectionId,
345
+ candidate.line,
346
+ true,
347
+ candidate
348
+ ));
349
+ }
350
+ }
351
+
352
+ const orderedHeadings = schema.sections.flatMap((section) => {
353
+ const heading = headingsBySection.get(section.id)?.[0];
354
+ return heading ? [{ section, start: heading.start }] : [];
355
+ });
356
+ if (orderedHeadings.some((item, index) => index > 0 && item.start <= orderedHeadings[index - 1]!.start)) {
357
+ diagnostics.push(diagnostic('ARTIFACT_SECTION_ORDER_INVALID', 'required sections are not in schema order', null, null));
358
+ }
359
+ const orderedMarkers = schema.sections.flatMap((section) => {
360
+ const marker = markers.find((entry) => entry.marker === section.marker);
361
+ return marker ? [{ section, start: marker.start }] : [];
362
+ });
363
+ if (orderedMarkers.some((item, index) => index > 0 && item.start <= orderedMarkers[index - 1]!.start)) {
364
+ diagnostics.push(diagnostic('ARTIFACT_SECTION_ORDER_INVALID', 'section markers are not in schema order', null, null));
365
+ }
366
+
367
+ const repair = repairCandidates.length === 1 && (
368
+ diagnostics.length === 0 ||
369
+ (diagnostics.length === 1 && diagnostics[0]?.operation?.kind === repairCandidates[0]?.kind)
370
+ ) ? repairCandidates[0]! : null;
371
+ return {
372
+ ok: diagnostics.length === 0,
373
+ family: schema.family,
374
+ semanticDigest: canonicalSemanticDigest(content, repair),
375
+ diagnostics,
376
+ repair
377
+ };
378
+ }
379
+
380
+ function inspectArtifactPatterns(
381
+ content: string,
382
+ schema: ArtifactSchema
383
+ ): ArtifactStructureResult {
384
+ const diagnostics = schema.requiredPatterns
385
+ .filter((pattern) => !new RegExp(pattern, 'm').test(content))
386
+ .map((pattern) => diagnostic(
387
+ 'ARTIFACT_REQUIRED_PATTERN_MISSING',
388
+ `artifact is missing required pattern '${pattern}'`,
389
+ null,
390
+ null
391
+ ));
392
+ return {
393
+ ok: diagnostics.length === 0,
394
+ family: schema.family,
395
+ semanticDigest: canonicalSemanticDigest(content),
396
+ diagnostics,
397
+ repair: null
398
+ };
399
+ }
400
+
401
+ function inspectArtifactContract(
402
+ content: string,
403
+ schema: ArtifactSchema
404
+ ): ArtifactStructureResult {
405
+ const structure = inspectArtifactStructure(content, schema);
406
+ const patterns = inspectArtifactPatterns(content, schema);
407
+ const diagnostics = [...structure.diagnostics, ...patterns.diagnostics];
408
+ const repair = patterns.diagnostics.length === 0 ? structure.repair : null;
409
+ return {
410
+ ok: diagnostics.length === 0,
411
+ family: schema.family,
412
+ semanticDigest: canonicalSemanticDigest(content, repair),
413
+ diagnostics,
414
+ repair
415
+ };
416
+ }
417
+
418
+ function resultFailure(code: string, message: string): ArtifactFileResult {
419
+ return { status: 'failed', changed: false, artifactSha256: null, semanticDigest: null, operation: null, error: { code, message } };
420
+ }
421
+
422
+ function resultNoOp(content: string): ArtifactFileResult {
423
+ return {
424
+ status: 'no-op',
425
+ changed: false,
426
+ artifactSha256: sha256Content(content),
427
+ semanticDigest: canonicalSemanticDigest(content),
428
+ operation: null,
429
+ error: null
430
+ };
431
+ }
432
+
433
+ function validateRepairContext(request: ArtifactRepairRequest, content: string): { ok: true } | { ok: false; code: string; message: string } {
434
+ const round = artifactRound(request.family, request.artifact);
435
+ if (round === null) return { ok: false, code: 'ARTIFACT_REPAIR_CONTEXT_INVALID', message: 'artifact round is not canonical' };
436
+ const contextPattern = /^<!--\s*artifact-context:([^:\s]+):([^:\s]+):(\d+)\s*-->\s*$/;
437
+ const contexts = scanVisibleMarkdown(content).lines
438
+ .map((line) => line.text.trim().match(contextPattern))
439
+ .filter((match): match is RegExpMatchArray => match !== null);
440
+ if (contexts.length !== 1
441
+ || contexts[0]![1] !== request.taskId
442
+ || contexts[0]![2] !== request.family
443
+ || Number(contexts[0]![3]) !== round) {
444
+ return { ok: false, code: 'ARTIFACT_REPAIR_CONTEXT_INVALID', message: 'artifact context marker does not match the requested task, family, and round' };
445
+ }
446
+ let taskContent: string;
447
+ try { taskContent = fs.readFileSync(path.join(request.taskDir, 'task.md'), 'utf8'); }
448
+ catch (error) { return { ok: false, code: 'ARTIFACT_REPAIR_CONTEXT_INVALID', message: `cannot read task lifecycle context: ${String(error)}` }; }
449
+ const activity = locateActivityLog(taskContent);
450
+ if (!activity) return { ok: false, code: 'ARTIFACT_REPAIR_CONTEXT_INVALID', message: 'task has no unique Activity Log section' };
451
+ const label: Record<ArtifactSchemaFamily, string> = {
452
+ analysis: 'Analyze Task',
453
+ 'review-analysis': 'Review Analysis',
454
+ plan: 'Plan Task',
455
+ 'review-plan': 'Review Plan',
456
+ code: 'Code Task',
457
+ 'review-code': 'Review Code'
458
+ };
459
+ const escaped = escapeRegExp(label[request.family]);
460
+ const qualifier = request.family === 'code'
461
+ ? '(?:, (?:fix for review-code(?:-r(?:[2-9]|[1-9]\\d+))?\\.md|decision II-[1-9]\\d+))?'
462
+ : '';
463
+ const expected = new RegExp(`^${escaped} \\(Round ${round}${qualifier}\\)$`);
464
+ const open = startedBackedRows(pairEntries(activity.entries)).filter((row) => expected.test(row.step) && !row.done);
465
+ if (open.length !== 1) {
466
+ return { ok: false, code: 'ARTIFACT_REPAIR_CONTEXT_INVALID', message: `artifact '${request.artifact}' does not have exactly one matching open started lifecycle event` };
467
+ }
468
+ let intent;
469
+ try { intent = readArtifactRepairIntent(request.repoRoot, request.taskId, request.family, request.artifact); }
470
+ catch (error) { return { ok: false, code: 'ARTIFACT_REPAIR_PROVENANCE_INVALID', message: String(error) }; }
471
+ if (!intent || intent.state !== 'awaiting-repair') {
472
+ return { ok: false, code: 'ARTIFACT_REPAIR_PROVENANCE_INVALID', message: 'artifact has no awaiting-repair finalization provenance' };
473
+ }
474
+ if (intent.artifactSha256 !== request.expectedSha256 || intent.semanticDigest !== request.expectedSemanticDigest) {
475
+ return { ok: false, code: 'ARTIFACT_REPAIR_PROVENANCE_INVALID', message: 'repair request does not match the current finalization provenance' };
476
+ }
477
+ return { ok: true };
478
+ }
479
+
480
+ function artifactRound(family: ArtifactSchemaFamily, artifact: string): number | null {
481
+ if (artifact === `${family}.md`) return 1;
482
+ const match = artifact.match(new RegExp(`^${escapeRegExp(family)}-r([2-9]|[1-9]\\d+)\\.md$`));
483
+ if (!match) return null;
484
+ const round = Number(match[1]);
485
+ return Number.isSafeInteger(round) ? round : null;
486
+ }
487
+
488
+ function validateTarget(taskDir: string, family: ArtifactSchemaFamily, artifact: string): { path: string } | ArtifactFileResult {
489
+ if (path.basename(artifact) !== artifact || artifactRound(family, artifact) === null || !artifact.endsWith('.md')) return resultFailure('ARTIFACT_REPAIR_TARGET_INVALID', 'artifact must be a canonical top-level Markdown file');
490
+ if (!/^TASK-\d{8}-\d{6}$/.test(path.basename(path.resolve(taskDir)))) return resultFailure('ARTIFACT_REPAIR_TARGET_INVALID', 'target directory is not a task directory');
491
+ const target = path.join(taskDir, artifact);
492
+ if (path.dirname(path.resolve(target)) !== path.resolve(taskDir)) return resultFailure('ARTIFACT_REPAIR_TARGET_INVALID', 'artifact must be inside the current task directory');
493
+ let stat: fs.Stats;
494
+ try { stat = fs.lstatSync(target); }
495
+ catch (error) { return resultFailure('ARTIFACT_REPAIR_TARGET_INVALID', `artifact cannot be read: ${String(error)}`); }
496
+ if (stat.isSymbolicLink() || !stat.isFile()) return resultFailure('ARTIFACT_REPAIR_TARGET_INVALID', 'artifact is not a regular file');
497
+ try { fs.accessSync(target, fs.constants.R_OK | fs.constants.W_OK); }
498
+ catch (error) { return resultFailure('ARTIFACT_REPAIR_TARGET_INVALID', `artifact is not readable and writable: ${String(error)}`); }
499
+ return { path: target };
500
+ }
501
+
502
+ function applyRepairUnlocked(request: ArtifactRepairRequest): ArtifactFileResult {
503
+ const schema = getArtifactSchema(request.family);
504
+ if (!schema) return resultFailure('ARTIFACT_FAMILY_UNKNOWN', `unknown artifact family '${request.family}'`);
505
+ const target = validateTarget(request.taskDir, request.family, request.artifact);
506
+ if ('status' in target) return target;
507
+ let content: string;
508
+ try { content = fs.readFileSync(target.path, 'utf8'); }
509
+ catch (error) { return resultFailure('ARTIFACT_REPAIR_TARGET_INVALID', String(error)); }
510
+ const actualSha256 = sha256Content(content);
511
+ if (actualSha256 !== request.expectedSha256) return resultFailure('ARTIFACT_REPAIR_BASELINE_MISMATCH', 'artifact SHA-256 does not match the expected repair baseline');
512
+ const context = validateRepairContext(request, content);
513
+ if (!context.ok) return resultFailure(context.code, context.message);
514
+ const inspection = inspectArtifactStructure(content, schema);
515
+ if (!inspection.repair || inspection.diagnostics.length !== 1) {
516
+ return resultFailure('ARTIFACT_REPAIR_UNSAFE', inspection.diagnostics.map((item) => `${item.code}: ${item.message}`).join('; ') || 'no deterministic structural repair is available');
517
+ }
518
+ if (inspection.repair.kind !== request.operation.kind
519
+ || inspection.repair.sectionId !== request.operation.sectionId
520
+ || inspection.repair.from !== request.operation.from
521
+ || inspection.repair.to !== request.operation.to
522
+ || inspection.repair.start !== request.operation.start
523
+ || inspection.repair.end !== request.operation.end) {
524
+ return resultFailure('ARTIFACT_REPAIR_OPERATION_MISMATCH', 'repair operation does not match the current artifact structure');
525
+ }
526
+ if (inspection.semanticDigest !== request.expectedSemanticDigest) return resultFailure('ARTIFACT_REPAIR_BASELINE_MISMATCH', 'artifact semantic digest does not match the expected repair baseline');
527
+ const transformed = applyRepairOperation(content, inspection.repair);
528
+ if (transformed === null) return resultFailure('ARTIFACT_REPAIR_OPERATION_MISMATCH', 'repair operation does not match the current artifact bytes');
529
+ if (transformed === content) return resultFailure('ARTIFACT_REPAIR_NO_PROGRESS', 'repair operation produced no byte change');
530
+ const tempPath = path.join(request.taskDir, `.${request.artifact}.repair-${process.pid}-${Date.now()}.tmp`);
531
+ let mode = 0o600;
532
+ try {
533
+ mode = fs.statSync(target.path).mode & 0o777;
534
+ fs.writeFileSync(tempPath, transformed, { encoding: 'utf8', mode, flag: 'wx' });
535
+ const currentStat = fs.lstatSync(target.path);
536
+ if (currentStat.isSymbolicLink() || !currentStat.isFile() || sha256Content(fs.readFileSync(target.path, 'utf8')) !== actualSha256) {
537
+ fs.unlinkSync(tempPath);
538
+ return resultFailure('ARTIFACT_REPAIR_CONFLICT', 'artifact changed or was replaced during repair');
539
+ }
540
+ fs.renameSync(tempPath, target.path);
541
+ } catch (error) {
542
+ try { fs.unlinkSync(tempPath); } catch { /* preserve primary error */ }
543
+ return resultFailure('ARTIFACT_REPAIR_WRITE_FAILED', String(error));
544
+ }
545
+ return {
546
+ status: 'applied',
547
+ changed: true,
548
+ artifactSha256: sha256Content(transformed),
549
+ semanticDigest: canonicalSemanticDigest(transformed),
550
+ operation: inspection.repair,
551
+ error: null
552
+ };
553
+ }
554
+
555
+ function applyArtifactRepair(request: ArtifactRepairRequest): ArtifactFileResult {
556
+ try {
557
+ return withTaskExecutionLock(request.repoRoot, request.taskId, 'task-artifact.repair', () => applyRepairUnlocked(request));
558
+ } catch (error) {
559
+ return resultFailure('ARTIFACT_REPAIR_LOCK_FAILED', String(error));
560
+ }
561
+ }
562
+
563
+ function initializeArtifactSkeleton(request: ArtifactInitRequest): ArtifactFileResult {
564
+ const schema = getArtifactSchema(request.family);
565
+ if (!schema) return resultFailure('ARTIFACT_FAMILY_UNKNOWN', `unknown artifact family '${request.family}'`);
566
+ if (artifactRound(request.family, request.artifact) === null) return resultFailure('ARTIFACT_INIT_TARGET_INVALID', 'artifact must be a canonical top-level Markdown file');
567
+ if (!/^TASK-\d{8}-\d{6}$/.test(path.basename(path.resolve(request.taskDir))) || path.basename(path.resolve(request.taskDir)) !== request.taskId) return resultFailure('ARTIFACT_INIT_TARGET_INVALID', 'target directory is not the requested task directory');
568
+ const target = path.join(request.taskDir, request.artifact);
569
+ if (path.dirname(path.resolve(target)) !== path.resolve(request.taskDir)) return resultFailure('ARTIFACT_INIT_TARGET_INVALID', 'artifact must be inside the current task directory');
570
+ try {
571
+ return withTaskExecutionLock(request.repoRoot, request.taskId, 'task-artifact.init', () => {
572
+ try {
573
+ const existing = fs.lstatSync(target);
574
+ if (existing.isSymbolicLink() || !existing.isFile()) return resultFailure('ARTIFACT_INIT_TARGET_INVALID', 'artifact target is not a regular file');
575
+ return resultNoOp(fs.readFileSync(target, 'utf8'));
576
+ } catch (error) {
577
+ if ((error as NodeJS.ErrnoException).code !== 'ENOENT') return resultFailure('ARTIFACT_INIT_TARGET_INVALID', String(error));
578
+ }
579
+ const content = renderArtifactSkeleton({
580
+ taskId: request.taskId,
581
+ family: request.family,
582
+ artifact: request.artifact,
583
+ ...(request.locale ? { locale: request.locale } : {})
584
+ });
585
+ const tempPath = path.join(request.taskDir, `.${request.artifact}.init-${process.pid}-${Date.now()}.tmp`);
586
+ try {
587
+ fs.writeFileSync(tempPath, content, { encoding: 'utf8', mode: 0o600, flag: 'wx' });
588
+ try { fs.lstatSync(target); fs.unlinkSync(tempPath); return resultFailure('ARTIFACT_INIT_TARGET_EXISTS', 'artifact was created concurrently'); }
589
+ catch (error) { if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error; }
590
+ fs.renameSync(tempPath, target);
591
+ } catch (error) {
592
+ try { fs.unlinkSync(tempPath); } catch { /* preserve primary error */ }
593
+ return resultFailure('ARTIFACT_INIT_WRITE_FAILED', String(error));
594
+ }
595
+ return {
596
+ status: 'applied',
597
+ changed: true,
598
+ artifactSha256: sha256Content(content),
599
+ semanticDigest: canonicalSemanticDigest(content),
600
+ operation: null,
601
+ error: null
602
+ };
603
+ });
604
+ } catch (error) {
605
+ return resultFailure('ARTIFACT_INIT_LOCK_FAILED', String(error));
606
+ }
607
+ }
608
+
609
+ export {
610
+ applyArtifactRepair,
611
+ canonicalSemanticDigest,
612
+ initializeArtifactSkeleton,
613
+ inspectArtifactContract,
614
+ inspectArtifactPatterns,
615
+ inspectArtifactStructure,
616
+ sha256Content
617
+ };
618
+ export type {
619
+ ArtifactFileResult,
620
+ ArtifactInitRequest,
621
+ ArtifactRepairOperation,
622
+ ArtifactRepairRequest,
623
+ ArtifactStructuralDiagnostic,
624
+ ArtifactStructuralDiagnosticCode,
625
+ ArtifactStructureResult
626
+ };