@fitlab-ai/agent-infra 0.8.4 → 0.9.0

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 (443) hide show
  1. package/bin/cli.ts +2 -2
  2. package/dist/bin/cli.js +2 -2
  3. package/dist/bin/internal-cli.js +98 -0
  4. package/dist/lib/decide.js +81 -90
  5. package/dist/lib/defaults.json +9 -0
  6. package/dist/lib/git/review-snapshot.js +79 -0
  7. package/dist/lib/git/workflow.js +101 -0
  8. package/dist/lib/init.js +5 -0
  9. package/dist/lib/internal/git-workflow.js +42 -0
  10. package/dist/lib/internal/platform-checks.js +119 -0
  11. package/dist/lib/internal/platform-comment.js +129 -0
  12. package/dist/lib/internal/platform-context.js +46 -0
  13. package/dist/lib/internal/platform-issue.js +142 -0
  14. package/dist/lib/internal/platform-pr.js +146 -0
  15. package/dist/lib/internal/release-workflow.js +217 -0
  16. package/dist/lib/internal/task-artifact.js +67 -0
  17. package/dist/lib/internal/task-context.js +43 -0
  18. package/dist/lib/internal/task-event.js +70 -0
  19. package/dist/lib/internal/task-ledger.js +114 -0
  20. package/dist/lib/internal/task-lifecycle.js +58 -0
  21. package/dist/lib/internal/task-short-id.js +83 -0
  22. package/dist/lib/internal/task-snapshot.js +47 -0
  23. package/dist/lib/internal/task-verify.js +50 -0
  24. package/dist/lib/internal/task-warning.js +74 -0
  25. package/dist/lib/platform/adapters.js +14 -0
  26. package/dist/lib/platform/context.js +146 -0
  27. package/dist/lib/platform/github-client.js +124 -0
  28. package/dist/lib/platform/issue-comments.js +409 -0
  29. package/dist/lib/platform/issue-metadata.js +183 -0
  30. package/dist/lib/platform/issues.js +617 -0
  31. package/dist/lib/platform/metadata-labels.js +28 -0
  32. package/dist/lib/platform/pr-checks.js +216 -0
  33. package/dist/lib/platform/pr-summary.js +88 -0
  34. package/dist/lib/platform/pull-request-metadata.js +38 -0
  35. package/dist/lib/platform/pull-requests.js +338 -0
  36. package/dist/lib/platform/releases.js +82 -0
  37. package/dist/lib/platform/types.js +22 -0
  38. package/dist/lib/platform/verification-required.js +78 -0
  39. package/dist/lib/platform/verification-sync.js +1014 -0
  40. package/dist/lib/platform/verification.js +6 -0
  41. package/dist/lib/release/channels.js +31 -0
  42. package/dist/lib/release/workflow.js +32 -0
  43. package/dist/lib/render.js +3 -2
  44. package/dist/lib/run/index.js +24 -7
  45. package/dist/lib/sandbox/capture.js +25 -3
  46. package/dist/lib/sandbox/commands/create.js +66 -26
  47. package/dist/lib/sandbox/commands/enter.js +34 -11
  48. package/dist/lib/sandbox/commands/list-running.js +10 -8
  49. package/dist/lib/sandbox/commands/show.js +2 -2
  50. package/dist/lib/sandbox/commands/start.js +53 -15
  51. package/dist/lib/sandbox/engine.js +33 -3
  52. package/dist/lib/sandbox/engines/colima.js +2 -2
  53. package/dist/lib/sandbox/index.js +6 -6
  54. package/dist/lib/sandbox/mounts.js +36 -0
  55. package/dist/lib/sandbox/recovery.js +562 -0
  56. package/dist/lib/sandbox/runtimes/base.dockerfile +4 -0
  57. package/dist/lib/sandbox/task-resolver.js +5 -1
  58. package/dist/lib/sandbox/tools.js +19 -0
  59. package/dist/lib/server/daemon.js +9 -0
  60. package/dist/lib/server/process-control.js +66 -73
  61. package/dist/lib/server/process-state.js +168 -0
  62. package/dist/lib/server/protocol.js +5 -5
  63. package/dist/lib/task/activity-log.js +61 -0
  64. package/dist/lib/task/artifact-lifecycle.js +427 -0
  65. package/dist/lib/task/command-options.js +30 -0
  66. package/dist/lib/task/commands/cat.js +36 -6
  67. package/dist/lib/task/commands/decisions.js +80 -46
  68. package/dist/lib/task/commands/files.js +21 -7
  69. package/dist/lib/task/commands/grep.js +55 -7
  70. package/dist/lib/task/commands/issue-body.js +23 -14
  71. package/dist/lib/task/commands/log.js +27 -70
  72. package/dist/lib/task/commands/show.js +20 -7
  73. package/dist/lib/task/commands/status.js +18 -8
  74. package/dist/lib/task/decision-intents.js +154 -0
  75. package/dist/lib/task/decision-items.js +53 -0
  76. package/dist/lib/task/events.js +318 -0
  77. package/dist/lib/task/frontmatter.js +159 -1
  78. package/dist/lib/task/implementation-inputs.js +117 -0
  79. package/dist/lib/task/index.js +12 -9
  80. package/dist/lib/task/issue-form.js +19 -6
  81. package/dist/lib/task/ledger-intents.js +185 -0
  82. package/dist/lib/task/ledger.js +65 -35
  83. package/dist/lib/task/lifecycle.js +484 -0
  84. package/dist/lib/task/resolve-ref.js +152 -32
  85. package/dist/lib/task/review-artifacts.js +101 -0
  86. package/dist/lib/task/review-fingerprint.js +46 -0
  87. package/dist/lib/task/sections.js +358 -4
  88. package/dist/lib/task/short-id.js +292 -7
  89. package/dist/lib/task/snapshot.js +95 -0
  90. package/dist/lib/task/verification-checks.js +8 -0
  91. package/dist/lib/task/verification-config.js +14 -0
  92. package/dist/lib/task/verification-engine.js +906 -0
  93. package/dist/lib/task/verification-review.js +3 -0
  94. package/dist/lib/task/verification-types.js +2 -0
  95. package/dist/lib/task/verification.js +113 -0
  96. package/dist/lib/task/workflow-warning-intents.js +131 -0
  97. package/dist/lib/task/workflow-warnings.js +10 -78
  98. package/dist/lib/task/write.js +241 -0
  99. package/dist/lib/update.js +2 -0
  100. package/lib/decide.ts +57 -97
  101. package/lib/defaults.json +9 -0
  102. package/lib/git/review-snapshot.ts +73 -0
  103. package/lib/git/workflow.ts +101 -0
  104. package/lib/init.ts +9 -0
  105. package/lib/internal/git-workflow.ts +37 -0
  106. package/lib/internal/platform-checks.ts +101 -0
  107. package/lib/internal/platform-comment.ts +99 -0
  108. package/lib/internal/platform-context.ts +33 -0
  109. package/lib/internal/platform-issue.ts +116 -0
  110. package/lib/internal/platform-pr.ts +119 -0
  111. package/lib/internal/release-workflow.ts +186 -0
  112. package/lib/internal/task-artifact.ts +47 -0
  113. package/lib/internal/task-context.ts +35 -0
  114. package/lib/internal/task-event.ts +51 -0
  115. package/lib/internal/task-ledger.ts +86 -0
  116. package/lib/internal/task-lifecycle.ts +41 -0
  117. package/lib/internal/task-short-id.ts +55 -0
  118. package/lib/internal/task-snapshot.ts +32 -0
  119. package/lib/internal/task-verify.ts +33 -0
  120. package/lib/internal/task-warning.ts +50 -0
  121. package/lib/platform/adapters.ts +30 -0
  122. package/lib/platform/context.ts +164 -0
  123. package/lib/platform/github-client.ts +146 -0
  124. package/lib/platform/issue-comments.ts +479 -0
  125. package/lib/platform/issue-metadata.ts +250 -0
  126. package/lib/platform/issues.ts +691 -0
  127. package/lib/platform/metadata-labels.ts +33 -0
  128. package/lib/platform/pr-checks.ts +246 -0
  129. package/lib/platform/pr-summary.ts +90 -0
  130. package/lib/platform/pull-request-metadata.ts +69 -0
  131. package/lib/platform/pull-requests.ts +380 -0
  132. package/lib/platform/releases.ts +71 -0
  133. package/lib/platform/types.ts +74 -0
  134. package/lib/platform/verification-required.ts +83 -0
  135. package/{templates/.agents/scripts/platform-adapters/platform-sync.github.js → lib/platform/verification-sync.ts} +221 -363
  136. package/lib/platform/verification.ts +11 -0
  137. package/lib/release/channels.ts +30 -0
  138. package/lib/release/workflow.ts +31 -0
  139. package/lib/render.ts +3 -2
  140. package/lib/run/index.ts +25 -7
  141. package/lib/sandbox/capture.ts +29 -3
  142. package/lib/sandbox/commands/create.ts +75 -28
  143. package/lib/sandbox/commands/enter.ts +38 -11
  144. package/lib/sandbox/commands/list-running.ts +10 -8
  145. package/lib/sandbox/commands/show.ts +2 -2
  146. package/lib/sandbox/commands/start.ts +50 -16
  147. package/lib/sandbox/engine.ts +37 -3
  148. package/lib/sandbox/engines/colima.ts +2 -2
  149. package/lib/sandbox/index.ts +6 -6
  150. package/lib/sandbox/mounts.ts +55 -0
  151. package/lib/sandbox/recovery.ts +828 -0
  152. package/lib/sandbox/runtimes/base.dockerfile +4 -0
  153. package/lib/sandbox/task-resolver.ts +5 -1
  154. package/lib/sandbox/tools.ts +31 -0
  155. package/lib/server/daemon.ts +9 -0
  156. package/lib/server/process-control.ts +70 -71
  157. package/lib/server/process-state.ts +183 -0
  158. package/lib/server/protocol.ts +5 -5
  159. package/lib/task/activity-log.ts +62 -0
  160. package/lib/task/artifact-lifecycle.ts +484 -0
  161. package/lib/task/command-options.ts +32 -0
  162. package/lib/task/commands/cat.ts +22 -9
  163. package/lib/task/commands/decisions.ts +49 -47
  164. package/lib/task/commands/files.ts +14 -6
  165. package/lib/task/commands/grep.ts +34 -7
  166. package/lib/task/commands/issue-body.ts +16 -16
  167. package/lib/task/commands/log.ts +17 -70
  168. package/lib/task/commands/show.ts +14 -6
  169. package/lib/task/commands/status.ts +17 -7
  170. package/lib/task/decision-intents.ts +163 -0
  171. package/lib/task/decision-items.ts +64 -0
  172. package/lib/task/events.ts +320 -0
  173. package/lib/task/frontmatter.ts +202 -2
  174. package/lib/task/implementation-inputs.ts +137 -0
  175. package/lib/task/index.ts +12 -9
  176. package/lib/task/issue-form.ts +20 -6
  177. package/lib/task/ledger-intents.ts +201 -0
  178. package/lib/task/ledger.ts +77 -31
  179. package/lib/task/lifecycle.ts +458 -0
  180. package/lib/task/resolve-ref.ts +197 -36
  181. package/{templates/.agents/scripts/lib/review-artifacts.js → lib/task/review-artifacts.ts} +11 -12
  182. package/{templates/.agents/scripts/lib/post-review-commit.js → lib/task/review-fingerprint.ts} +20 -10
  183. package/lib/task/sections.ts +486 -4
  184. package/lib/task/short-id.ts +403 -9
  185. package/lib/task/snapshot.ts +143 -0
  186. package/lib/task/verification-checks.ts +9 -0
  187. package/lib/task/verification-config.ts +20 -0
  188. package/{templates/.agents/scripts/validate-artifact.js → lib/task/verification-engine.ts} +257 -451
  189. package/lib/task/verification-review.ts +9 -0
  190. package/lib/task/verification-types.ts +29 -0
  191. package/lib/task/verification.ts +156 -0
  192. package/lib/task/workflow-warning-intents.ts +142 -0
  193. package/lib/task/workflow-warnings.ts +10 -78
  194. package/lib/task/write.ts +468 -0
  195. package/lib/update.ts +6 -0
  196. package/package.json +5 -2
  197. package/templates/.agents/README.en.md +6 -3
  198. package/templates/.agents/README.zh-CN.md +6 -3
  199. package/templates/.agents/rules/README.en.md +6 -5
  200. package/templates/.agents/rules/README.zh-CN.md +6 -5
  201. package/templates/.agents/rules/create-issue.en.md +1 -1
  202. package/templates/.agents/rules/create-issue.github.en.md +5 -184
  203. package/templates/.agents/rules/create-issue.github.zh-CN.md +12 -178
  204. package/templates/.agents/rules/create-issue.zh-CN.md +1 -1
  205. package/templates/.agents/rules/human-decision-context.en.md +36 -0
  206. package/templates/.agents/rules/human-decision-context.zh-CN.md +36 -0
  207. package/templates/.agents/rules/issue-fields.github.en.md +4 -148
  208. package/templates/.agents/rules/issue-fields.github.zh-CN.md +9 -144
  209. package/templates/.agents/rules/issue-pr-commands.en.md +43 -3
  210. package/templates/.agents/rules/issue-pr-commands.github.en.md +15 -220
  211. package/templates/.agents/rules/issue-pr-commands.github.zh-CN.md +23 -213
  212. package/templates/.agents/rules/issue-pr-commands.zh-CN.md +45 -3
  213. package/templates/.agents/rules/issue-sync.en.md +1 -1
  214. package/templates/.agents/rules/issue-sync.github.en.md +14 -301
  215. package/templates/.agents/rules/issue-sync.github.zh-CN.md +36 -287
  216. package/templates/.agents/rules/issue-sync.zh-CN.md +1 -1
  217. package/templates/.agents/rules/milestone-inference.en.md +1 -1
  218. package/templates/.agents/rules/milestone-inference.github.en.md +7 -119
  219. package/templates/.agents/rules/milestone-inference.github.zh-CN.md +8 -108
  220. package/templates/.agents/rules/milestone-inference.zh-CN.md +1 -1
  221. package/templates/.agents/rules/next-step-output.en.md +12 -11
  222. package/templates/.agents/rules/next-step-output.zh-CN.md +12 -11
  223. package/templates/.agents/rules/no-mid-flow-questions.en.md +3 -3
  224. package/templates/.agents/rules/no-mid-flow-questions.zh-CN.md +3 -3
  225. package/templates/.agents/rules/pr-checks-commands.en.md +16 -3
  226. package/templates/.agents/rules/pr-checks-commands.github.en.md +10 -54
  227. package/templates/.agents/rules/pr-checks-commands.github.zh-CN.md +19 -47
  228. package/templates/.agents/rules/pr-checks-commands.zh-CN.md +30 -3
  229. package/templates/.agents/rules/pr-sync.en.md +30 -3
  230. package/templates/.agents/rules/pr-sync.github.en.md +12 -115
  231. package/templates/.agents/rules/pr-sync.github.zh-CN.md +26 -104
  232. package/templates/.agents/rules/pr-sync.zh-CN.md +43 -3
  233. package/templates/.agents/rules/review-handshake.en.md +13 -9
  234. package/templates/.agents/rules/review-handshake.zh-CN.md +13 -9
  235. package/templates/.agents/rules/task-management.en.md +11 -2
  236. package/templates/.agents/rules/task-management.zh-CN.md +11 -3
  237. package/templates/.agents/rules/task-short-id.en.md +25 -29
  238. package/templates/.agents/rules/task-short-id.zh-CN.md +20 -23
  239. package/templates/.agents/scripts/lib/agent-infra-package.js +141 -0
  240. package/templates/.agents/scripts/task-short-id.js +71 -533
  241. package/templates/.agents/skills/analyze-task/SKILL.en.md +22 -55
  242. package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +26 -59
  243. package/templates/.agents/skills/analyze-task/config/verify.en.json +1 -3
  244. package/templates/.agents/skills/analyze-task/config/verify.zh-CN.json +1 -3
  245. package/templates/.agents/skills/block-task/SKILL.en.md +21 -47
  246. package/templates/.agents/skills/block-task/SKILL.zh-CN.md +23 -47
  247. package/templates/.agents/skills/block-task/config/verify.json +1 -2
  248. package/templates/.agents/skills/cancel-task/SKILL.en.md +16 -45
  249. package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +16 -45
  250. package/templates/.agents/skills/cancel-task/config/verify.json +1 -2
  251. package/templates/.agents/skills/check-task/SKILL.en.md +6 -3
  252. package/templates/.agents/skills/check-task/SKILL.zh-CN.md +6 -3
  253. package/templates/.agents/skills/close-codescan/SKILL.en.md +6 -24
  254. package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +6 -24
  255. package/templates/.agents/skills/close-dependabot/SKILL.en.md +6 -24
  256. package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +6 -24
  257. package/templates/.agents/skills/code-task/SKILL.en.md +22 -35
  258. package/templates/.agents/skills/code-task/SKILL.zh-CN.md +28 -44
  259. package/templates/.agents/skills/code-task/config/verify.en.json +3 -3
  260. package/templates/.agents/skills/code-task/config/verify.zh-CN.json +3 -3
  261. package/templates/.agents/skills/code-task/reference/dual-mode.en.md +13 -9
  262. package/templates/.agents/skills/code-task/reference/dual-mode.zh-CN.md +13 -9
  263. package/templates/.agents/skills/code-task/reference/fix-mode.en.md +1 -1
  264. package/templates/.agents/skills/code-task/reference/fix-mode.zh-CN.md +1 -1
  265. package/templates/.agents/skills/code-task/reference/report-template.en.md +11 -1
  266. package/templates/.agents/skills/code-task/reference/report-template.zh-CN.md +11 -1
  267. package/templates/.agents/skills/commit/SKILL.en.md +18 -14
  268. package/templates/.agents/skills/commit/SKILL.zh-CN.md +18 -14
  269. package/templates/.agents/skills/commit/config/verify.json +1 -2
  270. package/templates/.agents/skills/commit/reference/commit-message.en.md +1 -11
  271. package/templates/.agents/skills/commit/reference/commit-message.zh-CN.md +1 -11
  272. package/templates/.agents/skills/commit/reference/issue-metadata-sync.en.md +4 -8
  273. package/templates/.agents/skills/commit/reference/issue-metadata-sync.zh-CN.md +4 -8
  274. package/templates/.agents/skills/commit/reference/pr-summary-sync.en.md +6 -18
  275. package/templates/.agents/skills/commit/reference/pr-summary-sync.zh-CN.md +5 -17
  276. package/templates/.agents/skills/commit/reference/task-status-update.en.md +42 -2
  277. package/templates/.agents/skills/commit/reference/task-status-update.zh-CN.md +42 -2
  278. package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +13 -36
  279. package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +13 -36
  280. package/templates/.agents/skills/complete-manual-validation/config/verify.en.json +1 -3
  281. package/templates/.agents/skills/complete-manual-validation/config/verify.zh-CN.json +1 -3
  282. package/templates/.agents/skills/complete-manual-validation/reference/summary-update.en.md +10 -54
  283. package/templates/.agents/skills/complete-manual-validation/reference/summary-update.zh-CN.md +10 -79
  284. package/templates/.agents/skills/complete-task/SKILL.en.md +59 -63
  285. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +59 -63
  286. package/templates/.agents/skills/complete-task/config/verify.en.json +11 -6
  287. package/templates/.agents/skills/complete-task/config/verify.zh-CN.json +11 -6
  288. package/templates/.agents/skills/create-pr/SKILL.en.md +19 -44
  289. package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +19 -43
  290. package/templates/.agents/skills/create-pr/config/verify.json +1 -2
  291. package/templates/.agents/skills/create-pr/reference/comment-publish.en.md +8 -12
  292. package/templates/.agents/skills/create-pr/reference/comment-publish.zh-CN.md +7 -11
  293. package/templates/.agents/skills/create-task/SKILL.en.md +12 -13
  294. package/templates/.agents/skills/create-task/SKILL.zh-CN.md +12 -13
  295. package/templates/.agents/skills/create-task/config/verify.json +1 -2
  296. package/templates/.agents/skills/import-codescan/SKILL.en.md +2 -2
  297. package/templates/.agents/skills/import-codescan/SKILL.zh-CN.md +2 -2
  298. package/templates/.agents/skills/import-codescan/config/verify.json +1 -2
  299. package/templates/.agents/skills/import-dependabot/SKILL.en.md +2 -2
  300. package/templates/.agents/skills/import-dependabot/SKILL.zh-CN.md +2 -2
  301. package/templates/.agents/skills/import-dependabot/config/verify.json +1 -2
  302. package/templates/.agents/skills/import-issue/SKILL.en.md +9 -13
  303. package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +10 -14
  304. package/templates/.agents/skills/import-issue/config/verify.json +1 -2
  305. package/templates/.agents/skills/init-milestones/SKILL.en.md +2 -2
  306. package/templates/.agents/skills/init-milestones/SKILL.zh-CN.md +2 -2
  307. package/templates/.agents/skills/init-milestones/scripts/init-milestones.github.sh +5 -4
  308. package/templates/.agents/skills/plan-task/SKILL.en.md +17 -42
  309. package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +22 -49
  310. package/templates/.agents/skills/plan-task/config/verify.en.json +1 -3
  311. package/templates/.agents/skills/plan-task/config/verify.zh-CN.json +1 -3
  312. package/templates/.agents/skills/post-release/SKILL.en.md +11 -81
  313. package/templates/.agents/skills/post-release/SKILL.zh-CN.md +8 -78
  314. package/templates/.agents/skills/refine-title/SKILL.en.md +1 -1
  315. package/templates/.agents/skills/refine-title/SKILL.zh-CN.md +1 -1
  316. package/templates/.agents/skills/release/SKILL.en.md +16 -163
  317. package/templates/.agents/skills/release/SKILL.zh-CN.md +15 -162
  318. package/templates/.agents/skills/restore-task/SKILL.en.md +11 -29
  319. package/templates/.agents/skills/restore-task/SKILL.zh-CN.md +11 -29
  320. package/templates/.agents/skills/restore-task/config/verify.json +1 -2
  321. package/templates/.agents/skills/review-analysis/SKILL.en.md +13 -25
  322. package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +13 -33
  323. package/templates/.agents/skills/review-analysis/config/verify.en.json +3 -4
  324. package/templates/.agents/skills/review-analysis/config/verify.zh-CN.json +3 -4
  325. package/templates/.agents/skills/review-analysis/reference/output-templates.en.md +9 -12
  326. package/templates/.agents/skills/review-analysis/reference/output-templates.zh-CN.md +9 -12
  327. package/templates/.agents/skills/review-analysis/reference/report-template.en.md +9 -2
  328. package/templates/.agents/skills/review-analysis/reference/report-template.zh-CN.md +9 -2
  329. package/templates/.agents/skills/review-analysis/reference/review-criteria.en.md +7 -0
  330. package/templates/.agents/skills/review-analysis/reference/review-criteria.zh-CN.md +7 -0
  331. package/templates/.agents/skills/review-code/SKILL.en.md +22 -36
  332. package/templates/.agents/skills/review-code/SKILL.zh-CN.md +23 -41
  333. package/templates/.agents/skills/review-code/config/verify.en.json +5 -5
  334. package/templates/.agents/skills/review-code/config/verify.zh-CN.json +5 -5
  335. package/templates/.agents/skills/review-code/reference/output-templates.en.md +56 -18
  336. package/templates/.agents/skills/review-code/reference/output-templates.zh-CN.md +56 -18
  337. package/templates/.agents/skills/review-code/reference/report-template.en.md +12 -4
  338. package/templates/.agents/skills/review-code/reference/report-template.zh-CN.md +12 -4
  339. package/templates/.agents/skills/review-code/reference/review-criteria.en.md +7 -0
  340. package/templates/.agents/skills/review-code/reference/review-criteria.zh-CN.md +7 -0
  341. package/templates/.agents/skills/review-plan/SKILL.en.md +13 -25
  342. package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +13 -33
  343. package/templates/.agents/skills/review-plan/config/verify.en.json +3 -4
  344. package/templates/.agents/skills/review-plan/config/verify.zh-CN.json +3 -4
  345. package/templates/.agents/skills/review-plan/reference/output-templates.en.md +9 -12
  346. package/templates/.agents/skills/review-plan/reference/output-templates.zh-CN.md +9 -12
  347. package/templates/.agents/skills/review-plan/reference/report-template.en.md +9 -2
  348. package/templates/.agents/skills/review-plan/reference/report-template.zh-CN.md +9 -2
  349. package/templates/.agents/skills/review-plan/reference/review-criteria.en.md +7 -0
  350. package/templates/.agents/skills/review-plan/reference/review-criteria.zh-CN.md +7 -0
  351. package/templates/.agents/skills/update-agent-infra/SKILL.en.md +6 -3
  352. package/templates/.agents/skills/update-agent-infra/SKILL.zh-CN.md +6 -3
  353. package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +177 -151
  354. package/templates/.agents/skills/watch-pr/SKILL.en.md +24 -14
  355. package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +25 -15
  356. package/templates/.agents/skills/watch-pr/config/verify.json +1 -2
  357. package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.en.md +2 -2
  358. package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.zh-CN.md +2 -2
  359. package/templates/.agents/templates/task.en.md +6 -1
  360. package/templates/.agents/templates/task.zh-CN.md +6 -1
  361. package/templates/.claude/commands/analyze-task.en.md +1 -1
  362. package/templates/.claude/commands/analyze-task.zh-CN.md +1 -1
  363. package/templates/.claude/commands/block-task.en.md +1 -1
  364. package/templates/.claude/commands/block-task.zh-CN.md +1 -1
  365. package/templates/.claude/commands/cancel-task.en.md +1 -1
  366. package/templates/.claude/commands/cancel-task.zh-CN.md +1 -1
  367. package/templates/.claude/commands/check-task.en.md +1 -1
  368. package/templates/.claude/commands/check-task.zh-CN.md +1 -1
  369. package/templates/.claude/commands/code-task.en.md +1 -1
  370. package/templates/.claude/commands/code-task.zh-CN.md +1 -1
  371. package/templates/.claude/commands/commit.en.md +1 -0
  372. package/templates/.claude/commands/commit.zh-CN.md +1 -0
  373. package/templates/.claude/commands/complete-manual-validation.en.md +1 -1
  374. package/templates/.claude/commands/complete-manual-validation.zh-CN.md +1 -1
  375. package/templates/.claude/commands/complete-task.en.md +1 -1
  376. package/templates/.claude/commands/complete-task.zh-CN.md +1 -1
  377. package/templates/.claude/commands/create-pr.en.md +1 -1
  378. package/templates/.claude/commands/create-pr.zh-CN.md +1 -1
  379. package/templates/.claude/commands/plan-task.en.md +1 -1
  380. package/templates/.claude/commands/plan-task.zh-CN.md +1 -1
  381. package/templates/.claude/commands/review-analysis.en.md +1 -1
  382. package/templates/.claude/commands/review-analysis.zh-CN.md +1 -1
  383. package/templates/.claude/commands/review-code.en.md +1 -1
  384. package/templates/.claude/commands/review-code.zh-CN.md +1 -1
  385. package/templates/.claude/commands/review-plan.en.md +1 -1
  386. package/templates/.claude/commands/review-plan.zh-CN.md +1 -1
  387. package/templates/.claude/commands/watch-pr.en.md +1 -1
  388. package/templates/.claude/commands/watch-pr.zh-CN.md +1 -1
  389. package/templates/.gemini/commands/_project_/analyze-task.en.toml +2 -0
  390. package/templates/.gemini/commands/_project_/analyze-task.zh-CN.toml +2 -0
  391. package/templates/.gemini/commands/_project_/block-task.en.toml +2 -0
  392. package/templates/.gemini/commands/_project_/block-task.zh-CN.toml +2 -0
  393. package/templates/.gemini/commands/_project_/cancel-task.en.toml +2 -0
  394. package/templates/.gemini/commands/_project_/cancel-task.zh-CN.toml +2 -0
  395. package/templates/.gemini/commands/_project_/check-task.en.toml +2 -0
  396. package/templates/.gemini/commands/_project_/check-task.zh-CN.toml +2 -0
  397. package/templates/.gemini/commands/_project_/code-task.en.toml +2 -0
  398. package/templates/.gemini/commands/_project_/code-task.zh-CN.toml +2 -0
  399. package/templates/.gemini/commands/_project_/commit.en.toml +4 -0
  400. package/templates/.gemini/commands/_project_/commit.zh-CN.toml +4 -0
  401. package/templates/.gemini/commands/_project_/complete-manual-validation.en.toml +2 -0
  402. package/templates/.gemini/commands/_project_/complete-manual-validation.zh-CN.toml +2 -0
  403. package/templates/.gemini/commands/_project_/complete-task.en.toml +2 -0
  404. package/templates/.gemini/commands/_project_/complete-task.zh-CN.toml +2 -0
  405. package/templates/.gemini/commands/_project_/create-pr.en.toml +2 -0
  406. package/templates/.gemini/commands/_project_/create-pr.zh-CN.toml +2 -0
  407. package/templates/.gemini/commands/_project_/plan-task.en.toml +2 -0
  408. package/templates/.gemini/commands/_project_/plan-task.zh-CN.toml +2 -0
  409. package/templates/.gemini/commands/_project_/review-analysis.en.toml +2 -0
  410. package/templates/.gemini/commands/_project_/review-analysis.zh-CN.toml +2 -0
  411. package/templates/.gemini/commands/_project_/review-code.en.toml +2 -0
  412. package/templates/.gemini/commands/_project_/review-code.zh-CN.toml +2 -0
  413. package/templates/.gemini/commands/_project_/review-plan.en.toml +2 -0
  414. package/templates/.gemini/commands/_project_/review-plan.zh-CN.toml +2 -0
  415. package/templates/.gemini/commands/_project_/watch-pr.en.toml +2 -0
  416. package/templates/.gemini/commands/_project_/watch-pr.zh-CN.toml +2 -0
  417. package/templates/.git-hooks/check-large-files.cjs +92 -0
  418. package/templates/.git-hooks/check-version-format.sh +5 -1
  419. package/templates/.git-hooks/pre-commit +1 -0
  420. package/templates/.opencode/commands/analyze-task.en.md +1 -1
  421. package/templates/.opencode/commands/analyze-task.zh-CN.md +1 -1
  422. package/templates/.opencode/commands/check-task.en.md +1 -1
  423. package/templates/.opencode/commands/check-task.zh-CN.md +1 -1
  424. package/templates/.opencode/commands/code-task.en.md +1 -1
  425. package/templates/.opencode/commands/code-task.zh-CN.md +1 -1
  426. package/templates/.opencode/commands/commit.en.md +2 -0
  427. package/templates/.opencode/commands/commit.zh-CN.md +2 -0
  428. package/templates/.opencode/commands/complete-task.en.md +1 -1
  429. package/templates/.opencode/commands/complete-task.zh-CN.md +1 -1
  430. package/templates/.opencode/commands/plan-task.en.md +1 -1
  431. package/templates/.opencode/commands/plan-task.zh-CN.md +1 -1
  432. package/templates/.opencode/commands/review-analysis.en.md +1 -1
  433. package/templates/.opencode/commands/review-analysis.zh-CN.md +1 -1
  434. package/templates/.opencode/commands/review-code.en.md +1 -1
  435. package/templates/.opencode/commands/review-code.zh-CN.md +1 -1
  436. package/templates/.opencode/commands/review-plan.en.md +1 -1
  437. package/templates/.opencode/commands/review-plan.zh-CN.md +1 -1
  438. package/templates/.agents/scripts/platform-adapters/platform-sync.js +0 -13
  439. package/templates/.agents/scripts/review-diff-fingerprint.js +0 -99
  440. package/templates/.agents/scripts/workflow-warnings.js +0 -290
  441. package/templates/.agents/skills/code-task/scripts/detect-mode.js +0 -257
  442. package/templates/.agents/skills/release/scripts/manage-milestones.github.sh +0 -80
  443. package/templates/.agents/skills/release/scripts/manage-milestones.sh +0 -6
@@ -0,0 +1,1014 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import process from "node:process";
4
+ import { execFileSync } from "node:child_process";
5
+ import { createGitHubClient } from "./github-client.js";
6
+ import { resolvePlatformContext } from "./context.js";
7
+ import { hasCheckedRequirement, resolveRequirementSection } from "./issue-metadata.js";
8
+ import { inspectGitHubIssueMetadata, requirementSectionAnchors } from "./issues.js";
9
+ import { listRemoteComments } from "./issue-comments.js";
10
+ import { taskTypeLabel } from "./metadata-labels.js";
11
+ import { inspectGitHubPullRequest } from "./pull-requests.js";
12
+ const CHECK_TYPE = "platform-sync";
13
+ const VERSION_LINE_REGEX = /^[0-9]+\.[0-9]+\.x$/;
14
+ const FRONTMATTER_FIELD_MAP = {
15
+ priority: "Priority",
16
+ effort: "Effort",
17
+ start_date: "Start date",
18
+ target_date: "Target date"
19
+ };
20
+ const OPTION_LOCALIZATION = {
21
+ "紧急": "Urgent",
22
+ "高": "High",
23
+ "中": "Medium",
24
+ "低": "Low"
25
+ };
26
+ let activeShared = null;
27
+ let repoRoot = "";
28
+ const githubClient = createGitHubClient();
29
+ export function getDefaults() {
30
+ return {
31
+ statusLabels: {
32
+ pendingDesignWork: "status: pending-design-work",
33
+ inProgress: "status: in-progress",
34
+ blocked: "status: blocked",
35
+ completed: "status: completed",
36
+ waitingForTriage: "status: waiting-for-triage"
37
+ },
38
+ markers: {
39
+ task: "<!-- sync-issue:{task-id}:task -->",
40
+ artifact: "<!-- sync-issue:{task-id}:{artifact-stem} -->",
41
+ artifactChunk: "<!-- sync-issue:{task-id}:{artifact-stem}:{part}/{total} -->",
42
+ summary: "<!-- sync-issue:{task-id}:summary -->",
43
+ cancel: "<!-- sync-issue:{task-id}:cancel -->",
44
+ prSummary: "<!-- sync-pr:{task-id}:summary -->"
45
+ }
46
+ };
47
+ }
48
+ function getShared() {
49
+ if (!activeShared) {
50
+ throw new Error("platform-sync adapter shared utilities are unavailable");
51
+ }
52
+ return activeShared;
53
+ }
54
+ function loadTask(...args) {
55
+ return getShared().loadTask(...args);
56
+ }
57
+ function getCheckedRequirements(...args) {
58
+ return getShared().getCheckedRequirements(...args);
59
+ }
60
+ function normalizeContent(...args) {
61
+ return getShared().normalizeContent(...args);
62
+ }
63
+ function isBlank(...args) {
64
+ return getShared().isBlank(...args);
65
+ }
66
+ function escapeRegExp(...args) {
67
+ return getShared().escapeRegExp(...args);
68
+ }
69
+ function passResult(...args) {
70
+ return getShared().passResult(...args);
71
+ }
72
+ function failResult(...args) {
73
+ return getShared().failResult(...args);
74
+ }
75
+ function blockedResult(...args) {
76
+ return getShared().blockedResult(...args);
77
+ }
78
+ function safeStat(...args) {
79
+ return getShared().safeStat(...args);
80
+ }
81
+ function parseIssueNumber(...args) {
82
+ return getShared().parseIssueNumber(...args);
83
+ }
84
+ function parsePrNumber(...args) {
85
+ return getShared().parsePrNumber(...args);
86
+ }
87
+ export function check({ taskDir, config, artifactFile }, shared) {
88
+ activeShared = shared;
89
+ repoRoot = shared.repoRoot;
90
+ const context = buildSyncContext({ taskDir, config, artifactFile });
91
+ if (context.earlyReturn) {
92
+ return context.earlyReturn;
93
+ }
94
+ const remoteData = fetchRemoteData(context);
95
+ if (remoteData.earlyReturn) {
96
+ return remoteData.earlyReturn;
97
+ }
98
+ const subChecks = [
99
+ checkClosedIssueStatusLabels,
100
+ checkStatusLabel,
101
+ checkCommentMarker,
102
+ checkPrCommentMarker,
103
+ checkPrCommentLastCommit,
104
+ checkPrCommentRequiredPatterns,
105
+ checkCommentContent,
106
+ checkTaskCommentContent,
107
+ checkInLabelsComputed,
108
+ checkPrTypeLabel,
109
+ checkInLabelsMatchPr,
110
+ checkPrAssignee,
111
+ checkSyncedRequirements,
112
+ checkIssueType,
113
+ checkIssueFields,
114
+ checkMilestone
115
+ ];
116
+ for (const subCheck of subChecks) {
117
+ const result = subCheck(context, remoteData);
118
+ if (result) {
119
+ return result;
120
+ }
121
+ }
122
+ return passResult(CHECK_TYPE, `GitHub sync checks passed for Issue #${context.issueNumber}`);
123
+ }
124
+ function buildSyncContext({ taskDir, config, artifactFile }) {
125
+ const task = loadTask(taskDir);
126
+ if (!task.ok) {
127
+ return { earlyReturn: failResult(CHECK_TYPE, task.message) };
128
+ }
129
+ const issueNumber = parseIssueNumber(task.metadata.issue_number);
130
+ const prNumber = parsePrNumber(task.metadata.pr_number);
131
+ if (config.when === "issue_number_exists" && !issueNumber) {
132
+ return { earlyReturn: passResult(CHECK_TYPE, "Skipped: task has no issue_number") };
133
+ }
134
+ if (config.when === "pr_number_exists" && !prNumber) {
135
+ return { earlyReturn: passResult(CHECK_TYPE, "Skipped: task has no pr_number") };
136
+ }
137
+ if (!issueNumber) {
138
+ return { earlyReturn: passResult(CHECK_TYPE, "Skipped: platform-sync not required for this task") };
139
+ }
140
+ const platformContext = resolvePlatformContext({ cwd: repoRoot, client: githubClient });
141
+ if (platformContext.status === "failed") {
142
+ return { earlyReturn: failResult(CHECK_TYPE, platformContext.error?.message || "Platform context failed", "check_failed") };
143
+ }
144
+ if (platformContext.status === "blocked") {
145
+ return { earlyReturn: blockedResult(CHECK_TYPE, platformContext.error?.message || "Platform context blocked", "network_error") };
146
+ }
147
+ if (!platformContext.platform.repository) {
148
+ if (platformContext.error?.code === "REMOTE_MISSING" || platformContext.error?.code === "REMOTE_INVALID") {
149
+ return { earlyReturn: blockedResult(CHECK_TYPE, platformContext.error.message, "network_error") };
150
+ }
151
+ return { earlyReturn: passResult(CHECK_TYPE, `Skipped: ${platformContext.error?.message || "platform unavailable"}`) };
152
+ }
153
+ const repoOwnerType = detectRepoOwnerType(platformContext.platform.repository, taskDir);
154
+ const expectedValues = resolveExpectedValues(config);
155
+ if (!expectedValues.ok) {
156
+ return { earlyReturn: failResult(CHECK_TYPE, expectedValues.message, "check_failed") };
157
+ }
158
+ const marker = expectedValues.commentMarker
159
+ ? interpolate(expectedValues.commentMarker, taskDir, artifactFile)
160
+ : null;
161
+ const prMarker = expectedValues.prCommentMarker
162
+ ? interpolate(expectedValues.prCommentMarker, taskDir, artifactFile)
163
+ : null;
164
+ const artifactPath = artifactFile ? path.join(taskDir, artifactFile) : null;
165
+ return {
166
+ task,
167
+ taskDir,
168
+ config,
169
+ artifactFile,
170
+ artifactPath,
171
+ issueNumber,
172
+ prNumber,
173
+ upstreamRepo: platformContext.platform.repository,
174
+ repoOwnerType,
175
+ hasTriage: platformContext.capabilities.triage,
176
+ hasPush: platformContext.capabilities.push,
177
+ expectedStatusLabel: expectedValues.statusLabel,
178
+ marker,
179
+ prMarker
180
+ };
181
+ }
182
+ function resolveExpectedValues(config) {
183
+ const defaults = getDefaults();
184
+ const statusLabel = resolveDefaultValue({
185
+ collection: defaults.statusLabels,
186
+ key: config.expected_status_label_key,
187
+ value: config.expected_status_label,
188
+ configKey: "expected_status_label_key"
189
+ });
190
+ if (!statusLabel.ok) {
191
+ return statusLabel;
192
+ }
193
+ const commentMarker = resolveDefaultValue({
194
+ collection: defaults.markers,
195
+ key: config.expected_comment_marker_key,
196
+ value: config.expected_comment_marker,
197
+ configKey: "expected_comment_marker_key"
198
+ });
199
+ if (!commentMarker.ok) {
200
+ return commentMarker;
201
+ }
202
+ const prCommentMarker = resolveDefaultValue({
203
+ collection: defaults.markers,
204
+ key: config.expected_pr_comment_marker_key,
205
+ value: config.expected_pr_comment_marker,
206
+ configKey: "expected_pr_comment_marker_key"
207
+ });
208
+ if (!prCommentMarker.ok) {
209
+ return prCommentMarker;
210
+ }
211
+ return {
212
+ ok: true,
213
+ statusLabel: statusLabel.value,
214
+ commentMarker: commentMarker.value,
215
+ prCommentMarker: prCommentMarker.value
216
+ };
217
+ }
218
+ function resolveDefaultValue({ collection, key, value, configKey }) {
219
+ if (!key) {
220
+ return { ok: true, value: value || null };
221
+ }
222
+ const resolvedValue = collection[key];
223
+ if (!resolvedValue) {
224
+ return { ok: false, message: `Unknown ${configKey}: ${key}` };
225
+ }
226
+ return { ok: true, value: resolvedValue };
227
+ }
228
+ function fetchRemoteData(context) {
229
+ const sharedIssue = inspectGitHubIssueMetadata(githubClient, context.upstreamRepo, context.issueNumber, context.taskDir);
230
+ const issueResult = sharedIssue.ok
231
+ ? {
232
+ ok: true,
233
+ value: {
234
+ state: sharedIssue.value.state.toUpperCase(),
235
+ labels: sharedIssue.value.labels.map((name) => ({ name })),
236
+ body: sharedIssue.value.body,
237
+ milestone: sharedIssue.value.milestone ? { title: sharedIssue.value.milestone } : null
238
+ }
239
+ }
240
+ : {
241
+ ok: false,
242
+ type: sharedIssue.error.retryable ? "network_error" : "check_failed",
243
+ message: sharedIssue.error.message
244
+ };
245
+ if (!issueResult.ok) {
246
+ return {
247
+ earlyReturn: issueResult.type === "check_failed"
248
+ ? failResult(CHECK_TYPE, issueResult.message, issueResult.type)
249
+ : blockedResult(CHECK_TYPE, issueResult.message, issueResult.type)
250
+ };
251
+ }
252
+ const issue = issueResult.value;
253
+ if (context.config.issue_must_exist !== false && !issue) {
254
+ return {
255
+ earlyReturn: failResult(CHECK_TYPE, `Issue #${context.issueNumber} not found`, "check_failed")
256
+ };
257
+ }
258
+ let comments = null;
259
+ if (shouldFetchComments(context.config)) {
260
+ const commentsResult = withRetry(() => ghPaginatedJson([
261
+ "api",
262
+ "--paginate",
263
+ "--slurp",
264
+ `repos/${context.upstreamRepo}/issues/${context.issueNumber}/comments?per_page=100`
265
+ ], context.taskDir));
266
+ if (!commentsResult.ok) {
267
+ return {
268
+ earlyReturn: commentsResult.type === "check_failed"
269
+ ? failResult(CHECK_TYPE, commentsResult.message, commentsResult.type)
270
+ : blockedResult(CHECK_TYPE, commentsResult.message, commentsResult.type)
271
+ };
272
+ }
273
+ comments = flattenComments(commentsResult.value);
274
+ }
275
+ let prComments = null;
276
+ if (context.prMarker) {
277
+ if (!context.prNumber) {
278
+ return {
279
+ earlyReturn: failResult(CHECK_TYPE, "Expected a valid pr_number for PR comment verification", "check_failed")
280
+ };
281
+ }
282
+ const prCommentsResult = listRemoteComments(githubClient, context.upstreamRepo, context.prNumber, context.taskDir);
283
+ if (!prCommentsResult.ok) {
284
+ return {
285
+ earlyReturn: prCommentsResult.error.retryable
286
+ ? blockedResult(CHECK_TYPE, prCommentsResult.error.message, "network_error")
287
+ : failResult(CHECK_TYPE, prCommentsResult.error.message, "check_failed")
288
+ };
289
+ }
290
+ prComments = prCommentsResult.value;
291
+ }
292
+ let issueType;
293
+ if (context.config.verify_issue_type && context.hasPush) {
294
+ const issueTypeResult = withRetry(() => ghText([
295
+ "api",
296
+ `repos/${context.upstreamRepo}/issues/${context.issueNumber}`,
297
+ "--jq",
298
+ ".type.name // empty"
299
+ ], context.taskDir));
300
+ if (issueTypeResult.ok) {
301
+ issueType = issueTypeResult.value || null;
302
+ }
303
+ }
304
+ let issueFields;
305
+ if (context.config.verify_issue_fields && context.hasPush) {
306
+ const [owner, name] = context.upstreamRepo.split("/");
307
+ const issueFieldsResult = withRetry(() => ghJson([
308
+ "api",
309
+ "graphql",
310
+ "-f",
311
+ `query=${ISSUE_FIELDS_QUERY}`,
312
+ "-F",
313
+ `owner=${owner}`,
314
+ "-F",
315
+ `name=${name}`,
316
+ "-F",
317
+ `number=${context.issueNumber}`
318
+ ], context.taskDir));
319
+ if (issueFieldsResult.ok) {
320
+ issueFields = normalizeIssueFields(issueFieldsResult.value);
321
+ }
322
+ }
323
+ let prLabels = null;
324
+ let prMilestone;
325
+ let prAssignees;
326
+ let prHeadSha;
327
+ if (((context.config.verify_in_labels_match_pr && context.hasTriage)
328
+ || (context.config.verify_pr_type_label && context.hasTriage)
329
+ || (context.config.verify_milestone && context.hasTriage)
330
+ || (context.config.verify_pr_assignee && context.hasPush)
331
+ || context.config.verify_pr_comment_last_commit_matches_head) && context.prNumber) {
332
+ const prResult = inspectGitHubPullRequest(githubClient, context.upstreamRepo, context.prNumber, context.taskDir);
333
+ if (!prResult.ok) {
334
+ return {
335
+ earlyReturn: prResult.error.retryable
336
+ ? blockedResult(CHECK_TYPE, prResult.error.message, "network_error")
337
+ : failResult(CHECK_TYPE, prResult.error.message, "check_failed")
338
+ };
339
+ }
340
+ prLabels = (context.config.verify_in_labels_match_pr || context.config.verify_pr_type_label)
341
+ ? prResult.value.labels
342
+ : null;
343
+ prMilestone = context.config.verify_milestone
344
+ ? prResult.value.milestone ? { title: prResult.value.milestone } : null
345
+ : undefined;
346
+ prAssignees = context.config.verify_pr_assignee
347
+ ? prResult.value.assignees
348
+ : undefined;
349
+ prHeadSha = prResult.value.head.sha;
350
+ }
351
+ return {
352
+ issue,
353
+ comments,
354
+ prComments,
355
+ prLabels,
356
+ issueType,
357
+ issueFields,
358
+ prMilestone,
359
+ prAssignees,
360
+ prHeadSha
361
+ };
362
+ }
363
+ function mapTaskTypeToLabel(taskType) {
364
+ return taskTypeLabel(taskType);
365
+ }
366
+ function shouldFetchComments(config) {
367
+ return Boolean(config.expected_comment_marker
368
+ || config.expected_comment_marker_key
369
+ || config.expected_pr_comment_marker
370
+ || config.expected_pr_comment_marker_key
371
+ || config.verify_pr_comment_last_commit_matches_head
372
+ || config.verify_comment_content
373
+ || config.verify_task_comment_content);
374
+ }
375
+ function flattenComments(value) {
376
+ if (!Array.isArray(value)) {
377
+ return [];
378
+ }
379
+ return value.flatMap((page) => Array.isArray(page) ? page : []);
380
+ }
381
+ function checkStatusLabel(context, remoteData) {
382
+ if (!context.expectedStatusLabel || !context.hasTriage) {
383
+ return null;
384
+ }
385
+ if (String(remoteData.issue.state || "").toUpperCase() !== "OPEN") {
386
+ return null;
387
+ }
388
+ const labels = extractLabelNames(remoteData.issue.labels);
389
+ if (labels.includes(context.expectedStatusLabel)) {
390
+ return null;
391
+ }
392
+ return failResult(CHECK_TYPE, `Expected label '${context.expectedStatusLabel}' not found on Issue #${context.issueNumber}`, "check_failed");
393
+ }
394
+ function checkClosedIssueStatusLabels(context, remoteData) {
395
+ if (!context.config.verify_closed_issue_has_no_status_labels) {
396
+ return null;
397
+ }
398
+ if (String(remoteData.issue.state || "").toUpperCase() !== "CLOSED") {
399
+ return null;
400
+ }
401
+ const statusLabels = extractLabelNames(remoteData.issue.labels)
402
+ .filter((label) => label.startsWith("status:"));
403
+ if (statusLabels.length === 0) {
404
+ return null;
405
+ }
406
+ return failResult(CHECK_TYPE, `Closed Issue #${context.issueNumber} retains status labels: ${statusLabels.join(", ")}`, "check_failed");
407
+ }
408
+ function checkCommentMarker(context, remoteData) {
409
+ if (!context.marker) {
410
+ return null;
411
+ }
412
+ const comment = findCommentByMarker(remoteData.comments, context.marker);
413
+ if (comment) {
414
+ return null;
415
+ }
416
+ return failResult(CHECK_TYPE, `Expected comment marker '${context.marker}' not found on Issue #${context.issueNumber}`, "check_failed");
417
+ }
418
+ function checkPrCommentMarker(context, remoteData) {
419
+ if (!context.prMarker) {
420
+ return null;
421
+ }
422
+ const comment = findCommentByMarker(remoteData.prComments, context.prMarker);
423
+ if (comment) {
424
+ return null;
425
+ }
426
+ return failResult(CHECK_TYPE, `Expected PR comment marker '${context.prMarker}' not found on PR #${context.prNumber}`, "check_failed");
427
+ }
428
+ function checkPrCommentLastCommit(context, remoteData) {
429
+ if (!context.config.verify_pr_comment_last_commit_matches_head) {
430
+ return null;
431
+ }
432
+ if (!context.prMarker) {
433
+ return failResult(CHECK_TYPE, "verify_pr_comment_last_commit_matches_head requires expected_pr_comment_marker", "check_failed");
434
+ }
435
+ const comment = findCommentByMarker(remoteData.prComments, context.prMarker);
436
+ if (!comment) {
437
+ return failResult(CHECK_TYPE, `Expected PR comment marker '${context.prMarker}' not found on PR #${context.prNumber}`, "check_failed");
438
+ }
439
+ const match = String(comment.body || "").match(/<!--\s*last-commit:\s*([0-9a-f]{7,40})\s*-->/i);
440
+ if (!match) {
441
+ return failResult(CHECK_TYPE, `PR #${context.prNumber} summary comment is missing '<!-- last-commit: <sha> -->' metadata`, "check_failed");
442
+ }
443
+ const expectedHead = String(remoteData.prHeadSha || "").trim();
444
+ if (!expectedHead)
445
+ return blockedResult(CHECK_TYPE, "Unable to resolve the PR head SHA", "network_error");
446
+ const actualHead = match[1].trim();
447
+ if (expectedHead === actualHead) {
448
+ return null;
449
+ }
450
+ return failResult(CHECK_TYPE, `PR #${context.prNumber} summary comment last-commit metadata mismatch: expected ${expectedHead}, got ${actualHead}`, "check_failed");
451
+ }
452
+ function checkPrCommentRequiredPatterns(context, remoteData) {
453
+ const patterns = context.config.expected_pr_comment_required_patterns || [];
454
+ if (!Array.isArray(patterns) || patterns.length === 0) {
455
+ return null;
456
+ }
457
+ if (!context.prMarker) {
458
+ return failResult(CHECK_TYPE, "expected_pr_comment_required_patterns requires expected_pr_comment_marker", "check_failed");
459
+ }
460
+ const comment = findCommentByMarker(remoteData.prComments, context.prMarker);
461
+ if (!comment) {
462
+ return failResult(CHECK_TYPE, `Expected PR comment marker '${context.prMarker}' not found on PR #${context.prNumber}`, "check_failed");
463
+ }
464
+ const body = String(comment.body || "");
465
+ for (const pattern of patterns) {
466
+ const regex = new RegExp(pattern, "m");
467
+ if (!regex.test(body)) {
468
+ return failResult(CHECK_TYPE, `PR #${context.prNumber} summary comment is missing required pattern: ${pattern}`, "check_failed");
469
+ }
470
+ }
471
+ return null;
472
+ }
473
+ function checkCommentContent(context, remoteData) {
474
+ if (!context.config.verify_comment_content) {
475
+ return null;
476
+ }
477
+ if (!context.marker) {
478
+ return failResult(CHECK_TYPE, "verify_comment_content requires expected_comment_marker", "check_failed");
479
+ }
480
+ if (!context.artifactPath || !safeStat(context.artifactPath)) {
481
+ return failResult(CHECK_TYPE, `Artifact not found for comment verification: ${context.artifactFile || "(missing artifactFile)"}`, "check_failed");
482
+ }
483
+ const comment = findCommentByMarker(remoteData.comments, context.marker);
484
+ const localContent = normalizeContent(fs.readFileSync(context.artifactPath, "utf8"));
485
+ const commentContent = normalizeContent(extractCommentBody(comment?.body || ""));
486
+ if (localContent === commentContent) {
487
+ return null;
488
+ }
489
+ return failResult(CHECK_TYPE, buildCommentContentMismatchMessage(path.basename(context.artifactPath, path.extname(context.artifactPath)), context.issueNumber, localContent, commentContent), "check_failed");
490
+ }
491
+ function checkTaskCommentContent(context, remoteData) {
492
+ if (!context.config.verify_task_comment_content) {
493
+ return null;
494
+ }
495
+ const taskMarker = `<!-- sync-issue:${context.task.metadata.id}:task -->`;
496
+ const comment = findCommentByMarker(remoteData.comments, taskMarker);
497
+ if (!comment) {
498
+ return failResult(CHECK_TYPE, `Expected comment marker '${taskMarker}' not found on Issue #${context.issueNumber}`, "check_failed");
499
+ }
500
+ const expectedBody = normalizeContent(buildExpectedTaskBody(context.task.content));
501
+ const commentBody = normalizeContent(extractCommentBody(comment.body || ""));
502
+ if (expectedBody === commentBody) {
503
+ return null;
504
+ }
505
+ return failResult(CHECK_TYPE, buildCommentContentMismatchMessage("task", context.issueNumber, expectedBody, commentBody), "check_failed");
506
+ }
507
+ function checkPrTypeLabel(context, remoteData) {
508
+ if (!context.config.verify_pr_type_label || !context.hasTriage || !context.prNumber || !remoteData.prLabels) {
509
+ return null;
510
+ }
511
+ const expectedLabel = mapTaskTypeToLabel(context.task.metadata.type);
512
+ if (!expectedLabel) {
513
+ return null;
514
+ }
515
+ if (remoteData.prLabels.includes(expectedLabel)) {
516
+ return null;
517
+ }
518
+ return failResult(CHECK_TYPE, `Expected type label '${expectedLabel}' not found on PR #${context.prNumber}`, "check_failed");
519
+ }
520
+ function checkInLabelsMatchPr(context, remoteData) {
521
+ if (!context.config.verify_in_labels_match_pr || !context.hasTriage || !context.prNumber || !remoteData.prLabels) {
522
+ return null;
523
+ }
524
+ const issueInLabels = extractLabelNames(remoteData.issue.labels)
525
+ .filter((label) => label.startsWith("in:"))
526
+ .sort();
527
+ const prInLabels = remoteData.prLabels
528
+ .filter((label) => label.startsWith("in:"))
529
+ .sort();
530
+ if (arraysEqual(issueInLabels, prInLabels)) {
531
+ return null;
532
+ }
533
+ return failResult(CHECK_TYPE, `in: labels mismatch — PR #${context.prNumber} has [${formatLabelList(prInLabels)}], Issue #${context.issueNumber} has [${formatLabelList(issueInLabels)}]`, "check_failed");
534
+ }
535
+ function checkInLabelsComputed(context, remoteData) {
536
+ if (!context.config.verify_in_labels_computed || !context.hasTriage) {
537
+ return null;
538
+ }
539
+ const expectedInLabels = computeExpectedInLabels(context.taskDir);
540
+ if (!expectedInLabels.ok) {
541
+ return expectedInLabels.type === "check_failed"
542
+ ? failResult(CHECK_TYPE, expectedInLabels.message, expectedInLabels.type)
543
+ : blockedResult(CHECK_TYPE, expectedInLabels.message, expectedInLabels.type);
544
+ }
545
+ if (expectedInLabels.mode === "skipped") {
546
+ return null;
547
+ }
548
+ const actualInLabels = extractLabelNames(remoteData.issue.labels)
549
+ .filter((label) => label.startsWith("in:"))
550
+ .sort();
551
+ if (arraysEqual(expectedInLabels.labels, actualInLabels)) {
552
+ return null;
553
+ }
554
+ return failResult(CHECK_TYPE, `Issue #${context.issueNumber} in: labels do not match committed changes: expected [${formatLabelList(expectedInLabels.labels)}], got [${formatLabelList(actualInLabels)}]`, "check_failed");
555
+ }
556
+ function checkSyncedRequirements(context, remoteData) {
557
+ if (!context.config.sync_checked_requirements || !context.hasTriage) {
558
+ return null;
559
+ }
560
+ const checkedRequirements = getCheckedRequirements(context.task.content);
561
+ if (checkedRequirements.length === 0) {
562
+ return null;
563
+ }
564
+ const issueBody = remoteData.issue.body || "";
565
+ const resolution = resolveRequirementSection(issueBody, requirementSectionAnchors(repoRoot, context.task.metadata.type || "task"));
566
+ if (resolution.status === "missing") {
567
+ return null;
568
+ }
569
+ if (resolution.status === "ambiguous") {
570
+ return failResult(CHECK_TYPE, `Issue #${context.issueNumber} requirements section is ambiguous`, "check_failed");
571
+ }
572
+ const requirementBody = issueBody.slice(resolution.bodyStart, resolution.bodyEnd);
573
+ const missingRequirements = checkedRequirements.filter((item) => !hasCheckedRequirement(requirementBody, item));
574
+ if (missingRequirements.length === 0) {
575
+ return null;
576
+ }
577
+ return failResult(CHECK_TYPE, `Issue body is missing checked requirements: ${missingRequirements.join(", ")}`, "check_failed");
578
+ }
579
+ function checkIssueType(context, remoteData) {
580
+ if (!context.config.verify_issue_type || !context.hasPush) {
581
+ return null;
582
+ }
583
+ if (remoteData.issueType === undefined) {
584
+ return null;
585
+ }
586
+ if (!remoteData.issueType) {
587
+ if (context.repoOwnerType === "User") {
588
+ return null;
589
+ }
590
+ return failResult(CHECK_TYPE, `Issue #${context.issueNumber} has no Issue Type set`, "check_failed");
591
+ }
592
+ const expectedType = mapTaskTypeToIssueType(context.task.metadata.type);
593
+ if (expectedType && remoteData.issueType !== expectedType) {
594
+ return failResult(CHECK_TYPE, `Issue #${context.issueNumber} has type '${remoteData.issueType}', expected '${expectedType}' (from task type '${context.task.metadata.type}')`, "check_failed");
595
+ }
596
+ return null;
597
+ }
598
+ function checkIssueFields(context, remoteData) {
599
+ if (!context.config.verify_issue_fields || !context.hasPush) {
600
+ return null;
601
+ }
602
+ if (remoteData.issueFields === undefined) {
603
+ return null;
604
+ }
605
+ for (const [metadataKey, fieldName] of Object.entries(FRONTMATTER_FIELD_MAP)) {
606
+ const expectedRaw = context.task.metadata[metadataKey];
607
+ if (isBlank(expectedRaw) || !remoteData.issueFields.pinnedNames.has(fieldName)) {
608
+ continue;
609
+ }
610
+ const actual = remoteData.issueFields.values.get(fieldName);
611
+ const expected = normalizeExpectedIssueField(metadataKey, expectedRaw);
612
+ if (!expected) {
613
+ continue;
614
+ }
615
+ if (!actual) {
616
+ return failResult(CHECK_TYPE, `Issue #${context.issueNumber} field '${fieldName}' is missing, expected '${expected.value}'`, "check_failed");
617
+ }
618
+ if (actual.kind !== expected.kind || actual.value !== expected.value) {
619
+ return failResult(CHECK_TYPE, `Issue #${context.issueNumber} field '${fieldName}' is '${actual.value}', expected '${expected.value}'`, "check_failed");
620
+ }
621
+ }
622
+ return null;
623
+ }
624
+ function checkPrAssignee(context, remoteData) {
625
+ if (!context.config.verify_pr_assignee || !context.hasPush || !context.prNumber) {
626
+ return null;
627
+ }
628
+ if (!remoteData.prAssignees || remoteData.prAssignees.length === 0) {
629
+ return failResult(CHECK_TYPE, `PR #${context.prNumber} has no assignee`, "check_failed");
630
+ }
631
+ return null;
632
+ }
633
+ function checkMilestone(context, remoteData) {
634
+ if (!context.config.verify_milestone || !context.hasTriage) {
635
+ return null;
636
+ }
637
+ if (!remoteData.issue?.milestone?.title) {
638
+ return failResult(CHECK_TYPE, `Issue #${context.issueNumber} has no milestone set`, "check_failed");
639
+ }
640
+ if (context.prNumber && remoteData.prMilestone !== undefined && !remoteData.prMilestone?.title) {
641
+ return failResult(CHECK_TYPE, `PR #${context.prNumber} has no milestone set`, "check_failed");
642
+ }
643
+ if (context.config.verify_milestone_specific) {
644
+ const issueTitle = remoteData.issue.milestone.title;
645
+ if (VERSION_LINE_REGEX.test(issueTitle)) {
646
+ return failResult(CHECK_TYPE, `Issue #${context.issueNumber} milestone '${issueTitle}' is a release line; narrow to a specific version (e.g. ${issueTitle.replace(/\.x$/, ".N")}) before continuing`, "check_failed");
647
+ }
648
+ if (context.prNumber && remoteData.prMilestone?.title && VERSION_LINE_REGEX.test(remoteData.prMilestone.title)) {
649
+ return failResult(CHECK_TYPE, `PR #${context.prNumber} milestone '${remoteData.prMilestone.title}' is a release line; narrow to a specific version before continuing`, "check_failed");
650
+ }
651
+ }
652
+ return null;
653
+ }
654
+ function findCommentByMarker(comments, marker) {
655
+ return (comments || []).find((comment) => typeof comment.body === "string" && comment.body.includes(marker)) || null;
656
+ }
657
+ function isGeneratedMarkerLine(line) {
658
+ return line.startsWith("<!--") && line.endsWith("-->");
659
+ }
660
+ function extractCommentBody(commentBody) {
661
+ const lines = String(commentBody || "").split(/\r?\n/);
662
+ let start = 0;
663
+ while (start < lines.length && (lines[start].trim() === "" || isGeneratedMarkerLine(lines[start].trim()))) {
664
+ start += 1;
665
+ }
666
+ if (start < lines.length && lines[start].startsWith("## ")) {
667
+ start += 1;
668
+ }
669
+ while (start < lines.length && lines[start].trim() === "") {
670
+ start += 1;
671
+ }
672
+ if (start < lines.length && /^> \*\*.+\*\* · .+$/.test(lines[start].trim())) {
673
+ start += 1;
674
+ }
675
+ while (start < lines.length && lines[start].trim() === "") {
676
+ start += 1;
677
+ }
678
+ let end = lines.length;
679
+ for (let index = lines.length - 1; index >= start; index -= 1) {
680
+ const trimmed = lines[index].trim();
681
+ if (trimmed === "") {
682
+ continue;
683
+ }
684
+ if (/^\*.*\*$/.test(trimmed)) {
685
+ end = index;
686
+ if (end > start && lines[end - 1].trim() === "---") {
687
+ end -= 1;
688
+ }
689
+ }
690
+ break;
691
+ }
692
+ return lines.slice(start, end).join("\n");
693
+ }
694
+ function buildExpectedTaskBody(taskContent) {
695
+ const frontmatterMatch = taskContent.match(/^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/);
696
+ if (!frontmatterMatch) {
697
+ return taskContent.trim();
698
+ }
699
+ const body = taskContent.slice(frontmatterMatch[0].length).trim();
700
+ return [
701
+ buildTaskFrontmatterSummary(),
702
+ "",
703
+ "```yaml",
704
+ frontmatterMatch[0].trim(),
705
+ "```",
706
+ "",
707
+ "</details>",
708
+ "",
709
+ body
710
+ ].join("\n").trim();
711
+ }
712
+ function buildTaskFrontmatterSummary() {
713
+ const language = loadProjectLanguage();
714
+ if (language === "en" || language === "en-US") {
715
+ return "<details><summary>Metadata (frontmatter)</summary>";
716
+ }
717
+ return "<details><summary>元数据 (frontmatter)</summary>";
718
+ }
719
+ function loadProjectLanguage() {
720
+ const override = process.env.VALIDATE_ARTIFACT_LANGUAGE;
721
+ if (!isBlank(override)) {
722
+ return String(override).trim();
723
+ }
724
+ const configPath = path.join(repoRoot, ".agents", ".airc.json");
725
+ if (!fs.existsSync(configPath)) {
726
+ return "";
727
+ }
728
+ try {
729
+ const config = JSON.parse(fs.readFileSync(configPath, "utf8"));
730
+ return String(config.language || "").trim();
731
+ }
732
+ catch {
733
+ return "";
734
+ }
735
+ }
736
+ function buildCommentContentMismatchMessage(fileStem, issueNumber, localContent, commentContent) {
737
+ const diffIndex = firstDifferenceIndex(localContent, commentContent);
738
+ const position = indexToLineColumn(localContent, diffIndex);
739
+ return `Comment content mismatch for '${fileStem}' on Issue #${issueNumber}: local file has ${localContent.length} chars, comment body has ${commentContent.length} chars (first difference near char ${diffIndex + 1}, line ${position.line}, column ${position.column})`;
740
+ }
741
+ function firstDifferenceIndex(left, right) {
742
+ const limit = Math.max(left.length, right.length);
743
+ for (let index = 0; index < limit; index += 1) {
744
+ if (left[index] !== right[index]) {
745
+ return index;
746
+ }
747
+ }
748
+ return limit;
749
+ }
750
+ function indexToLineColumn(text, index) {
751
+ const prefix = text.slice(0, Math.min(index, text.length));
752
+ const lines = prefix.split("\n");
753
+ return {
754
+ line: lines.length,
755
+ column: (lines.at(-1) || "").length + 1
756
+ };
757
+ }
758
+ function extractLabelNames(labels) {
759
+ return (labels || [])
760
+ .map((label) => typeof label === "string" ? label : label?.name)
761
+ .filter((label) => typeof label === "string" && label.length > 0);
762
+ }
763
+ function mapTaskTypeToIssueType(taskType) {
764
+ const mapping = {
765
+ bug: "Bug",
766
+ bugfix: "Bug",
767
+ enhancement: "Feature",
768
+ feature: "Feature",
769
+ task: "Task",
770
+ documentation: "Task",
771
+ "dependency-upgrade": "Task",
772
+ chore: "Task",
773
+ docs: "Task",
774
+ refactor: "Task",
775
+ refactoring: "Task"
776
+ };
777
+ return mapping[taskType] || "Task";
778
+ }
779
+ const ISSUE_FIELDS_QUERY = `query($owner:String!,$name:String!,$number:Int!){repository(owner:$owner,name:$name){issue(number:$number){issueType{name pinnedFields{__typename ... on IssueFieldSingleSelect{id name} ... on IssueFieldDate{id name} ... on IssueFieldText{id name} ... on IssueFieldNumber{id name}}} issueFieldValues(first:50){nodes{__typename ... on IssueFieldSingleSelectValue{name optionId field{... on IssueFieldSingleSelect{name}}} ... on IssueFieldDateValue{value field{... on IssueFieldDate{name}}} ... on IssueFieldTextValue{value field{... on IssueFieldText{name}}} ... on IssueFieldNumberValue{value field{... on IssueFieldNumber{name}}}}}}}}`;
780
+ function normalizeIssueFields(payload) {
781
+ const issue = payload?.data?.repository?.issue;
782
+ const pinnedFields = Array.isArray(issue?.issueType?.pinnedFields)
783
+ ? issue.issueType.pinnedFields
784
+ : [];
785
+ const values = Array.isArray(issue?.issueFieldValues?.nodes)
786
+ ? issue.issueFieldValues.nodes
787
+ : [];
788
+ const pinnedNames = new Set(pinnedFields
789
+ .map((field) => typeof field?.name === "string" ? field.name : "")
790
+ .filter(Boolean));
791
+ const normalizedValues = new Map();
792
+ for (const value of values) {
793
+ const fieldName = value?.field?.name;
794
+ if (!fieldName) {
795
+ continue;
796
+ }
797
+ if (value.__typename === "IssueFieldSingleSelectValue") {
798
+ normalizedValues.set(fieldName, {
799
+ kind: "single-select",
800
+ value: normalizeOptionName(value.name)
801
+ });
802
+ }
803
+ else if (value.__typename === "IssueFieldDateValue") {
804
+ normalizedValues.set(fieldName, {
805
+ kind: "date",
806
+ value: normalizeDateValue(value.value)
807
+ });
808
+ }
809
+ }
810
+ return { pinnedNames, values: normalizedValues };
811
+ }
812
+ function normalizeExpectedIssueField(metadataKey, rawValue) {
813
+ const value = String(rawValue || "").trim();
814
+ if (!value) {
815
+ return null;
816
+ }
817
+ if (metadataKey === "start_date" || metadataKey === "target_date") {
818
+ return { kind: "date", value: normalizeDateValue(value) };
819
+ }
820
+ return { kind: "single-select", value: normalizeOptionName(value) };
821
+ }
822
+ function normalizeOptionName(value) {
823
+ const normalized = String(value || "").trim();
824
+ return OPTION_LOCALIZATION[normalized] || normalized;
825
+ }
826
+ function normalizeDateValue(value) {
827
+ const normalized = String(value || "").trim();
828
+ const match = normalized.match(/^\d{4}-\d{2}-\d{2}/);
829
+ return match ? match[0] : normalized;
830
+ }
831
+ function arraysEqual(left, right) {
832
+ if (left.length !== right.length) {
833
+ return false;
834
+ }
835
+ return left.every((value, index) => value === right[index]);
836
+ }
837
+ function formatLabelList(labels) {
838
+ return labels.length > 0 ? labels.join(", ") : "none";
839
+ }
840
+ function computeExpectedInLabels(taskDir) {
841
+ const changedFilesResult = gitText(["diff", "main...HEAD", "--name-only"], taskDir);
842
+ if (!changedFilesResult.ok) {
843
+ return changedFilesResult;
844
+ }
845
+ const changedFiles = String(changedFilesResult.value || "")
846
+ .split(/\r?\n/)
847
+ .map((value) => value.trim())
848
+ .filter(Boolean);
849
+ const mapping = loadInLabelMapping();
850
+ if (!mapping.ok) {
851
+ return mapping;
852
+ }
853
+ if (Object.keys(mapping.value ?? {}).length > 0) {
854
+ const labels = new Set();
855
+ for (const file of changedFiles) {
856
+ for (const [label, prefixes] of Object.entries(mapping.value ?? {})) {
857
+ if (prefixes.some((prefix) => file.startsWith(prefix))) {
858
+ labels.add(`in: ${label}`);
859
+ }
860
+ }
861
+ }
862
+ return { ok: true, labels: Array.from(labels).sort(), mode: "mapped" };
863
+ }
864
+ const repoLabelsResult = withRetry(() => ghJson([
865
+ "label",
866
+ "list",
867
+ "--limit",
868
+ "200",
869
+ "--json",
870
+ "name"
871
+ ], taskDir));
872
+ if (!repoLabelsResult.ok) {
873
+ return repoLabelsResult;
874
+ }
875
+ const repoInLabels = new Set(extractLabelNames(repoLabelsResult.value)
876
+ .filter((label) => label.startsWith("in:")));
877
+ if (repoInLabels.size === 0) {
878
+ return { ok: true, labels: [], mode: "fallback" };
879
+ }
880
+ const labels = new Set();
881
+ for (const file of changedFiles) {
882
+ const topLevel = file.split("/")[0];
883
+ if (!topLevel) {
884
+ continue;
885
+ }
886
+ const candidate = `in: ${topLevel}`;
887
+ if (repoInLabels.has(candidate)) {
888
+ labels.add(candidate);
889
+ }
890
+ }
891
+ return { ok: true, labels: Array.from(labels).sort(), mode: "fallback" };
892
+ }
893
+ function loadInLabelMapping() {
894
+ const configPath = path.join(repoRoot, ".agents", ".airc.json");
895
+ if (!fs.existsSync(configPath)) {
896
+ return { ok: true, value: {} };
897
+ }
898
+ try {
899
+ const config = JSON.parse(fs.readFileSync(configPath, "utf8"));
900
+ const mapping = config?.labels?.in;
901
+ if (!mapping || typeof mapping !== "object" || Array.isArray(mapping)) {
902
+ return { ok: true, value: {} };
903
+ }
904
+ const normalized = {};
905
+ for (const [label, prefixes] of Object.entries(mapping)) {
906
+ if (!Array.isArray(prefixes)) {
907
+ continue;
908
+ }
909
+ const cleaned = prefixes
910
+ .map((value) => String(value || "").trim())
911
+ .filter(Boolean);
912
+ if (cleaned.length > 0) {
913
+ normalized[label] = cleaned;
914
+ }
915
+ }
916
+ return { ok: true, value: normalized };
917
+ }
918
+ catch (error) {
919
+ return { ok: false, type: "check_failed", message: `Unable to parse .agents/.airc.json: ${error.message}` };
920
+ }
921
+ }
922
+ // === GitHub API ===
923
+ function detectRepoOwnerType(upstreamRepo, taskDir) {
924
+ const ownerTypeResult = withRetry(() => ghText([
925
+ "api",
926
+ `repos/${upstreamRepo}`,
927
+ "--jq",
928
+ ".owner.type // empty"
929
+ ], taskDir));
930
+ if (!ownerTypeResult.ok) {
931
+ return "unknown";
932
+ }
933
+ return ownerTypeResult.value || "unknown";
934
+ }
935
+ function ghJson(args, cwd) {
936
+ return mapClientResult(githubClient.json(args, { cwd }));
937
+ }
938
+ function ghText(args, cwd) {
939
+ return mapClientResult(githubClient.text(args, { cwd }));
940
+ }
941
+ function ghPaginatedJson(args, cwd) {
942
+ return ghJson(args, cwd);
943
+ }
944
+ function mapClientResult(result) {
945
+ if (result.ok)
946
+ return result;
947
+ const checkFailed = ["RESOURCE_NOT_FOUND", "PERMISSION_DENIED", "PLATFORM_REQUEST_INVALID"].includes(result.error.code);
948
+ return {
949
+ ok: false,
950
+ type: checkFailed ? "check_failed" : "network_error",
951
+ message: result.error.message
952
+ };
953
+ }
954
+ function gitText(args, cwd) {
955
+ try {
956
+ const value = execFileSync("git", args, { cwd, encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] });
957
+ return { ok: true, value: String(value || "").trim() };
958
+ }
959
+ catch (error) {
960
+ const stderr = `${error?.stderr || ""}${error?.stdout || ""}`.trim();
961
+ return {
962
+ ok: false,
963
+ type: "check_failed",
964
+ message: stderr || `git ${args.join(" ")} failed`
965
+ };
966
+ }
967
+ }
968
+ function resolvePrHeadSha(context) {
969
+ const fallback = () => withRetry(() => gitText(["rev-parse", "HEAD"], context.taskDir));
970
+ const branch = String(context.task?.metadata?.branch || "").trim();
971
+ if (!branch) {
972
+ return fallback();
973
+ }
974
+ const worktreeList = withRetry(() => gitText(["worktree", "list", "--porcelain"], context.taskDir));
975
+ if (!worktreeList.ok) {
976
+ return fallback();
977
+ }
978
+ const matchedWorktree = findWorktreeForBranch(worktreeList.value, branch);
979
+ if (!matchedWorktree) {
980
+ return fallback();
981
+ }
982
+ const headInWorktree = withRetry(() => gitText(["rev-parse", "HEAD"], matchedWorktree));
983
+ if (!headInWorktree.ok) {
984
+ return fallback();
985
+ }
986
+ return headInWorktree;
987
+ }
988
+ function findWorktreeForBranch(porcelainOutput, branch) {
989
+ let currentWorktree = "";
990
+ for (const rawLine of String(porcelainOutput || "").split("\n")) {
991
+ const line = rawLine.trimEnd();
992
+ if (line.startsWith("worktree ")) {
993
+ currentWorktree = line.slice("worktree ".length).trim();
994
+ continue;
995
+ }
996
+ if (line.startsWith("branch refs/heads/")) {
997
+ const usedBranch = line.slice("branch refs/heads/".length).trim();
998
+ if (usedBranch === branch && currentWorktree) {
999
+ return currentWorktree;
1000
+ }
1001
+ }
1002
+ }
1003
+ return null;
1004
+ }
1005
+ function withRetry(operation) {
1006
+ return operation();
1007
+ }
1008
+ function interpolate(template, taskDir, artifactFile) {
1009
+ const artifactStem = artifactFile ? path.basename(artifactFile, path.extname(artifactFile)) : "";
1010
+ return template
1011
+ .replace(/\{task-id\}/g, path.basename(taskDir))
1012
+ .replace(/\{artifact-stem\}/g, artifactStem);
1013
+ }
1014
+ //# sourceMappingURL=verification-sync.js.map