@fitlab-ai/agent-infra 0.9.11 → 0.9.13

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 (356) hide show
  1. package/README.md +13 -13
  2. package/README.zh-CN.md +13 -13
  3. package/dist/bin/internal-cli.js +29 -19
  4. package/dist/lib/agent-clients/adapters/codex-lifecycle/build-identity.js +71 -10
  5. package/dist/lib/agent-clients/next-steps.js +6 -1
  6. package/dist/lib/decide.js +3 -3
  7. package/dist/lib/defaults.json +4 -0
  8. package/dist/lib/git/workflow.js +43 -1
  9. package/dist/lib/init.js +1 -0
  10. package/dist/lib/internal/git-workflow.js +8 -1
  11. package/dist/lib/internal/platform-checks.js +3 -3
  12. package/dist/lib/internal/platform-comment.js +9 -9
  13. package/dist/lib/internal/platform-context.js +2 -2
  14. package/dist/lib/internal/platform-issue.js +8 -9
  15. package/dist/lib/internal/platform-pr-review.js +10 -10
  16. package/dist/lib/internal/platform-pr.js +68 -23
  17. package/dist/lib/internal/platform-release-notes.js +3 -3
  18. package/dist/lib/internal/pr-review-grade.js +11 -9
  19. package/dist/lib/internal/release-workflow.js +4 -4
  20. package/dist/lib/internal/sandbox-control.js +30 -4
  21. package/dist/lib/internal/task-activity.js +3 -3
  22. package/dist/lib/internal/task-artifact.js +76 -24
  23. package/dist/lib/internal/task-create.js +2 -2
  24. package/dist/lib/internal/task-delivery.js +59 -0
  25. package/dist/lib/internal/task-event.js +6 -3
  26. package/dist/lib/internal/task-finalization.js +2 -2
  27. package/dist/lib/internal/task-invalidation.js +60 -0
  28. package/dist/lib/internal/task-ledger.js +9 -6
  29. package/dist/lib/internal/task-lifecycle.js +2 -2
  30. package/dist/lib/internal/task-override.js +4 -4
  31. package/dist/lib/internal/task-review.js +3 -3
  32. package/dist/lib/internal/task-verify.js +6 -6
  33. package/dist/lib/internal/task-warning.js +3 -3
  34. package/dist/lib/platform/adapters.js +1 -3
  35. package/dist/lib/platform/change-request-git-evidence.js +27 -7
  36. package/dist/lib/platform/completion-backfill.js +6 -5
  37. package/dist/lib/platform/context.js +70 -130
  38. package/dist/lib/platform/github-client.js +8 -2
  39. package/dist/lib/platform/github-provider.js +903 -0
  40. package/dist/lib/platform/in-label-sync.js +143 -0
  41. package/dist/lib/platform/issue-comments.js +94 -41
  42. package/dist/lib/platform/issues.js +335 -189
  43. package/dist/lib/platform/metadata-labels.js +6 -2
  44. package/dist/lib/platform/none-provider.js +28 -0
  45. package/dist/lib/platform/pr-change-report.js +551 -0
  46. package/dist/lib/platform/pr-checks.js +124 -106
  47. package/dist/lib/platform/pr-review.js +88 -72
  48. package/dist/lib/platform/pr-summary.js +523 -49
  49. package/dist/lib/platform/provider-bridge.js +45 -0
  50. package/dist/lib/platform/provider-contract.d.ts +487 -0
  51. package/dist/lib/platform/provider-contract.js +95 -0
  52. package/dist/lib/platform/provider-loader.js +251 -0
  53. package/dist/lib/platform/provider-validation.js +504 -0
  54. package/dist/lib/platform/pull-request-metadata.js +8 -6
  55. package/dist/lib/platform/pull-requests.js +1050 -502
  56. package/dist/lib/platform/release-notes.js +78 -55
  57. package/dist/lib/platform/releases.js +79 -50
  58. package/dist/lib/platform/resource-identity.d.ts +26 -0
  59. package/dist/lib/platform/resource-identity.js +92 -0
  60. package/dist/lib/platform/task-identities.js +31 -0
  61. package/dist/lib/platform/verification-required.js +22 -12
  62. package/dist/lib/platform/verification-sync.js +118 -239
  63. package/dist/lib/pr-review/evidence-grading.js +30 -9
  64. package/dist/lib/run/index.js +63 -23
  65. package/dist/lib/sandbox/config.js +5 -0
  66. package/dist/lib/sandbox/control/client.js +96 -15
  67. package/dist/lib/sandbox/control/executor.js +3 -3
  68. package/dist/lib/sandbox/control/lifecycle.js +29 -7
  69. package/dist/lib/sandbox/control/protocol.js +77 -0
  70. package/dist/lib/sandbox/control/server.js +398 -25
  71. package/dist/lib/sandbox/control/state.js +204 -1
  72. package/dist/lib/server/process-state.js +2 -2
  73. package/dist/lib/server/protocol.js +38 -1
  74. package/dist/lib/task/activity-log.js +8 -1
  75. package/dist/lib/task/artifact-lifecycle.js +35 -7
  76. package/dist/lib/task/capabilities.js +239 -0
  77. package/dist/lib/task/commands/status.js +32 -0
  78. package/dist/lib/task/commit-intent.js +100 -0
  79. package/dist/lib/task/commit-operation.js +207 -102
  80. package/dist/lib/task/control-authority.js +8 -5
  81. package/dist/lib/task/create-service.js +8 -8
  82. package/dist/lib/task/create.js +33 -7
  83. package/dist/lib/task/current-contract.js +5 -1
  84. package/dist/lib/task/decision-details.js +3 -48
  85. package/dist/lib/task/delivery-target.js +97 -0
  86. package/dist/lib/task/delivery.js +153 -0
  87. package/dist/lib/task/events.js +297 -17
  88. package/dist/lib/task/finalization.js +37 -13
  89. package/dist/lib/task/human-override.js +61 -36
  90. package/dist/lib/task/invalidation-command.js +86 -0
  91. package/dist/lib/task/invalidation.js +260 -0
  92. package/dist/lib/task/ledger-intents.js +37 -0
  93. package/dist/lib/task/lifecycle.js +106 -9
  94. package/dist/lib/task/local-artifact-finalization.js +374 -0
  95. package/dist/lib/task/orchestration.js +100 -151
  96. package/dist/lib/task/pr-delivery-fact.js +183 -0
  97. package/dist/lib/task/review-finalization.js +4 -8
  98. package/dist/lib/task/review-fingerprint.js +32 -2
  99. package/dist/lib/task/rework-intent.js +138 -0
  100. package/dist/lib/task/skill-scope.js +23 -0
  101. package/dist/lib/task/task-execution-lock.js +27 -17
  102. package/dist/lib/task/verification-engine.js +89 -38
  103. package/dist/lib/task/verification-review.js +1 -1
  104. package/dist/lib/task/verification.js +3 -3
  105. package/dist/lib/task/workflow-warning-intents.js +1 -1
  106. package/dist/lib/task/write.js +11 -0
  107. package/dist/lib/update.js +9 -0
  108. package/dist/lib/version.js +11 -1
  109. package/dist/lifecycle-build-manifest.json +3 -3
  110. package/lib/agent-clients/adapters/codex-lifecycle/build-identity.ts +105 -10
  111. package/lib/agent-clients/adapters/codex-lifecycle/manifest-files.json +12 -1
  112. package/lib/agent-clients/next-steps.ts +6 -1
  113. package/lib/decide.ts +3 -3
  114. package/lib/defaults.json +4 -0
  115. package/lib/git/workflow.ts +38 -1
  116. package/lib/init.ts +4 -1
  117. package/lib/internal/git-workflow.ts +7 -1
  118. package/lib/internal/platform-checks.ts +3 -3
  119. package/lib/internal/platform-comment.ts +8 -8
  120. package/lib/internal/platform-context.ts +2 -2
  121. package/lib/internal/platform-issue.ts +7 -8
  122. package/lib/internal/platform-pr-review.ts +9 -9
  123. package/lib/internal/platform-pr.ts +53 -21
  124. package/lib/internal/platform-release-notes.ts +3 -3
  125. package/lib/internal/pr-review-grade.ts +10 -8
  126. package/lib/internal/release-workflow.ts +4 -4
  127. package/lib/internal/sandbox-control.ts +27 -3
  128. package/lib/internal/task-activity.ts +3 -3
  129. package/lib/internal/task-artifact.ts +65 -22
  130. package/lib/internal/task-create.ts +2 -2
  131. package/lib/internal/task-delivery.ts +53 -0
  132. package/lib/internal/task-event.ts +6 -3
  133. package/lib/internal/task-finalization.ts +3 -3
  134. package/lib/internal/task-invalidation.ts +39 -0
  135. package/lib/internal/task-ledger.ts +11 -8
  136. package/lib/internal/task-lifecycle.ts +2 -2
  137. package/lib/internal/task-override.ts +6 -6
  138. package/lib/internal/task-review.ts +3 -3
  139. package/lib/internal/task-verify.ts +6 -6
  140. package/lib/internal/task-warning.ts +3 -3
  141. package/lib/platform/adapters.ts +3 -2
  142. package/lib/platform/change-request-git-evidence.ts +36 -9
  143. package/lib/platform/completion-backfill.ts +9 -8
  144. package/lib/platform/context.ts +97 -134
  145. package/lib/platform/github-client.ts +10 -3
  146. package/lib/platform/github-provider.ts +910 -0
  147. package/lib/platform/in-label-sync.ts +208 -0
  148. package/lib/platform/issue-comments.ts +97 -47
  149. package/lib/platform/issues.ts +344 -174
  150. package/lib/platform/metadata-labels.ts +5 -2
  151. package/lib/platform/none-provider.ts +35 -0
  152. package/lib/platform/pr-change-report.ts +645 -0
  153. package/lib/platform/pr-checks.ts +129 -107
  154. package/lib/platform/pr-review.ts +88 -85
  155. package/lib/platform/pr-summary.ts +562 -52
  156. package/lib/platform/provider-bridge.ts +82 -0
  157. package/lib/platform/provider-contract.ts +536 -0
  158. package/lib/platform/provider-loader.ts +307 -0
  159. package/lib/platform/provider-validation.ts +529 -0
  160. package/lib/platform/pull-request-metadata.ts +10 -7
  161. package/lib/platform/pull-requests.ts +1126 -505
  162. package/lib/platform/release-notes.ts +76 -59
  163. package/lib/platform/releases.ts +77 -43
  164. package/lib/platform/resource-identity.ts +121 -0
  165. package/lib/platform/task-identities.ts +40 -0
  166. package/lib/platform/types.ts +4 -2
  167. package/lib/platform/verification-required.ts +21 -13
  168. package/lib/platform/verification-sync.ts +118 -293
  169. package/lib/pr-review/evidence-grading.ts +31 -11
  170. package/lib/run/index.ts +57 -22
  171. package/lib/sandbox/config.ts +7 -0
  172. package/lib/sandbox/control/client.ts +105 -15
  173. package/lib/sandbox/control/executor.ts +4 -4
  174. package/lib/sandbox/control/lifecycle.ts +30 -8
  175. package/lib/sandbox/control/protocol.ts +113 -0
  176. package/lib/sandbox/control/server.ts +431 -23
  177. package/lib/sandbox/control/state.ts +251 -1
  178. package/lib/server/process-state.ts +2 -2
  179. package/lib/server/protocol.ts +36 -1
  180. package/lib/task/activity-log.ts +8 -0
  181. package/lib/task/artifact-lifecycle.ts +34 -8
  182. package/lib/task/capabilities.ts +262 -0
  183. package/lib/task/commands/status.ts +38 -0
  184. package/lib/task/commit-intent.ts +137 -0
  185. package/lib/task/commit-operation.ts +186 -88
  186. package/lib/task/control-authority.ts +11 -8
  187. package/lib/task/create-service.ts +8 -8
  188. package/lib/task/create.ts +34 -9
  189. package/lib/task/current-contract.ts +6 -1
  190. package/lib/task/decision-details.ts +8 -64
  191. package/lib/task/delivery-target.ts +130 -0
  192. package/lib/task/delivery.ts +160 -0
  193. package/lib/task/events.ts +296 -16
  194. package/lib/task/finalization.ts +50 -14
  195. package/lib/task/human-override.ts +73 -46
  196. package/lib/task/invalidation-command.ts +86 -0
  197. package/lib/task/invalidation.ts +332 -0
  198. package/lib/task/ledger-intents.ts +35 -1
  199. package/lib/task/lifecycle.ts +93 -4
  200. package/lib/task/local-artifact-finalization.ts +563 -0
  201. package/lib/task/orchestration.ts +110 -146
  202. package/lib/task/pr-delivery-fact.ts +237 -0
  203. package/lib/task/review-finalization.ts +4 -14
  204. package/lib/task/review-fingerprint.ts +33 -2
  205. package/lib/task/rework-intent.ts +165 -0
  206. package/lib/task/skill-scope.ts +24 -0
  207. package/lib/task/task-execution-lock.ts +23 -14
  208. package/lib/task/verification-engine.ts +95 -43
  209. package/lib/task/verification-review.ts +2 -0
  210. package/lib/task/verification.ts +4 -4
  211. package/lib/task/workflow-warning-intents.ts +1 -1
  212. package/lib/task/write.ts +19 -0
  213. package/lib/update.ts +11 -1
  214. package/lib/version.ts +15 -1
  215. package/package.json +7 -1
  216. package/templates/.agents/rules/README.en.md +4 -2
  217. package/templates/.agents/rules/README.zh-CN.md +4 -2
  218. package/templates/.agents/rules/create-issue.en.md +11 -2
  219. package/templates/.agents/rules/create-issue.zh-CN.md +24 -2
  220. package/templates/.agents/rules/issue-pr-commands.en.md +4 -17
  221. package/templates/.agents/rules/issue-pr-commands.zh-CN.md +1 -1
  222. package/templates/.agents/rules/issue-sync.en.md +28 -5
  223. package/templates/.agents/rules/issue-sync.zh-CN.md +64 -5
  224. package/templates/.agents/rules/label-milestone-setup.en.md +15 -2
  225. package/templates/.agents/rules/label-milestone-setup.zh-CN.md +15 -2
  226. package/templates/.agents/rules/local-artifact-repair.en.md +53 -0
  227. package/templates/.agents/rules/local-artifact-repair.zh-CN.md +53 -0
  228. package/templates/.agents/rules/milestone-inference.en.md +12 -2
  229. package/templates/.agents/rules/milestone-inference.zh-CN.md +25 -3
  230. package/templates/.agents/rules/pr-sync.en.md +28 -13
  231. package/templates/.agents/rules/pr-sync.zh-CN.md +39 -5
  232. package/templates/.agents/rules/release-commands.en.md +42 -3
  233. package/templates/.agents/rules/release-commands.zh-CN.md +42 -3
  234. package/templates/.agents/rules/security-alerts.en.md +17 -2
  235. package/templates/.agents/rules/security-alerts.zh-CN.md +17 -2
  236. package/templates/.agents/rules/sync-content-generation.en.md +8 -0
  237. package/templates/.agents/rules/sync-content-generation.zh-CN.md +8 -0
  238. package/templates/.agents/rules/task-management.en.md +1 -1
  239. package/templates/.agents/rules/task-management.zh-CN.md +1 -1
  240. package/templates/.agents/rules/task-short-id.en.md +1 -4
  241. package/templates/.agents/rules/task-short-id.zh-CN.md +1 -3
  242. package/templates/.agents/scripts/security-alerts.github.sh +151 -0
  243. package/templates/.agents/scripts/security-alerts.sh +31 -0
  244. package/templates/.agents/skills/analyze-task/SKILL.en.md +14 -3
  245. package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +11 -3
  246. package/templates/.agents/skills/check-task/SKILL.en.md +16 -37
  247. package/templates/.agents/skills/check-task/SKILL.zh-CN.md +16 -37
  248. package/templates/.agents/skills/close-codescan/SKILL.en.md +2 -2
  249. package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +2 -2
  250. package/templates/.agents/skills/close-dependabot/SKILL.en.md +2 -2
  251. package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +2 -2
  252. package/templates/.agents/skills/code-task/SKILL.en.md +32 -10
  253. package/templates/.agents/skills/code-task/SKILL.zh-CN.md +35 -13
  254. package/templates/.agents/skills/code-task/config/verify.en.json +1 -0
  255. package/templates/.agents/skills/code-task/config/verify.zh-CN.json +1 -0
  256. package/templates/.agents/skills/commit/SKILL.en.md +3 -3
  257. package/templates/.agents/skills/commit/SKILL.zh-CN.md +3 -3
  258. package/templates/.agents/skills/commit/config/verify.json +1 -1
  259. package/templates/.agents/skills/commit/reference/commit-orchestration.en.md +3 -3
  260. package/templates/.agents/skills/commit/reference/commit-orchestration.zh-CN.md +3 -3
  261. package/templates/.agents/skills/commit/reference/issue-metadata-sync.en.md +2 -0
  262. package/templates/.agents/skills/commit/reference/issue-metadata-sync.zh-CN.md +2 -0
  263. package/templates/.agents/skills/commit/reference/pr-summary-sync.en.md +4 -4
  264. package/templates/.agents/skills/commit/reference/pr-summary-sync.zh-CN.md +4 -4
  265. package/templates/.agents/skills/commit/reference/task-status-update.en.md +2 -2
  266. package/templates/.agents/skills/commit/reference/task-status-update.zh-CN.md +2 -2
  267. package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +7 -4
  268. package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +7 -4
  269. package/templates/.agents/skills/complete-manual-validation/config/verify.en.json +1 -1
  270. package/templates/.agents/skills/complete-manual-validation/config/verify.zh-CN.json +1 -1
  271. package/templates/.agents/skills/complete-manual-validation/reference/summary-update.en.md +5 -4
  272. package/templates/.agents/skills/complete-manual-validation/reference/summary-update.zh-CN.md +5 -4
  273. package/templates/.agents/skills/complete-task/SKILL.en.md +31 -13
  274. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +30 -13
  275. package/templates/.agents/skills/complete-task/reference/external-delivery.en.md +2 -2
  276. package/templates/.agents/skills/complete-task/reference/external-delivery.zh-CN.md +2 -2
  277. package/templates/.agents/skills/create-pr/SKILL.en.md +11 -10
  278. package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +11 -11
  279. package/templates/.agents/skills/create-pr/config/verify.json +1 -0
  280. package/templates/.agents/skills/create-pr/reference/change-report.en.md +16 -4
  281. package/templates/.agents/skills/create-pr/reference/change-report.zh-CN.md +16 -4
  282. package/templates/.agents/skills/create-pr/reference/comment-publish.en.md +5 -3
  283. package/templates/.agents/skills/create-pr/reference/comment-publish.zh-CN.md +5 -3
  284. package/templates/.agents/skills/create-pr/reference/pr-body-template.en.md +1 -1
  285. package/templates/.agents/skills/create-pr/reference/pr-body-template.zh-CN.md +1 -1
  286. package/templates/.agents/skills/create-pr/scripts/change-report.mjs +3 -0
  287. package/templates/.agents/skills/create-task/SKILL.en.md +1 -0
  288. package/templates/.agents/skills/create-task/SKILL.zh-CN.md +1 -0
  289. package/templates/.agents/skills/import-codescan/SKILL.en.md +1 -1
  290. package/templates/.agents/skills/import-codescan/SKILL.zh-CN.md +1 -1
  291. package/templates/.agents/skills/import-dependabot/SKILL.en.md +1 -1
  292. package/templates/.agents/skills/import-dependabot/SKILL.zh-CN.md +1 -1
  293. package/templates/.agents/skills/init-labels/SKILL.en.md +9 -10
  294. package/templates/.agents/skills/init-labels/SKILL.zh-CN.md +9 -10
  295. package/templates/.agents/skills/init-labels/scripts/init-labels.github.sh +57 -1
  296. package/templates/.agents/skills/init-labels/scripts/init-labels.sh +27 -1
  297. package/templates/.agents/skills/init-milestones/SKILL.en.md +3 -5
  298. package/templates/.agents/skills/init-milestones/SKILL.zh-CN.md +3 -5
  299. package/templates/.agents/skills/plan-task/SKILL.en.md +14 -3
  300. package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +11 -3
  301. package/templates/.agents/skills/review-analysis/SKILL.en.md +6 -5
  302. package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +6 -5
  303. package/templates/.agents/skills/review-analysis/reference/output-templates.en.md +18 -0
  304. package/templates/.agents/skills/review-analysis/reference/output-templates.zh-CN.md +18 -0
  305. package/templates/.agents/skills/review-code/SKILL.en.md +10 -9
  306. package/templates/.agents/skills/review-code/SKILL.zh-CN.md +10 -9
  307. package/templates/.agents/skills/review-code/reference/output-templates.en.md +19 -1
  308. package/templates/.agents/skills/review-code/reference/output-templates.zh-CN.md +19 -1
  309. package/templates/.agents/skills/review-code/reference/report-template.en.md +4 -2
  310. package/templates/.agents/skills/review-code/reference/report-template.zh-CN.md +4 -2
  311. package/templates/.agents/skills/review-plan/SKILL.en.md +6 -5
  312. package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +6 -5
  313. package/templates/.agents/skills/review-plan/reference/output-templates.en.md +18 -0
  314. package/templates/.agents/skills/review-plan/reference/output-templates.zh-CN.md +18 -0
  315. package/templates/.agents/skills/review-pr/SKILL.en.md +1 -0
  316. package/templates/.agents/skills/review-pr/SKILL.zh-CN.md +1 -0
  317. package/templates/.agents/skills/review-pr/reference/evidence-grading.en.md +2 -2
  318. package/templates/.agents/skills/review-pr/reference/evidence-grading.zh-CN.md +2 -2
  319. package/templates/.agents/skills/run-manual-validation/SKILL.en.md +5 -2
  320. package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +5 -2
  321. package/templates/.agents/skills/run-task/SKILL.en.md +1 -1
  322. package/templates/.agents/skills/run-task/SKILL.zh-CN.md +1 -1
  323. package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +4 -0
  324. package/templates/.agents/skills/watch-pr/SKILL.en.md +6 -6
  325. package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +6 -6
  326. package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.en.md +6 -0
  327. package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.zh-CN.md +6 -0
  328. package/templates/.agents/templates/task.en.md +23 -2
  329. package/templates/.agents/templates/task.zh-CN.md +23 -2
  330. package/templates/.agents/workflows/bug-fix.en.yaml +9 -8
  331. package/templates/.agents/workflows/bug-fix.zh-CN.yaml +9 -8
  332. package/templates/.agents/workflows/feature-development.en.yaml +9 -8
  333. package/templates/.agents/workflows/feature-development.zh-CN.yaml +9 -8
  334. package/templates/.agents/workflows/refactoring.en.yaml +9 -8
  335. package/templates/.agents/workflows/refactoring.zh-CN.yaml +9 -8
  336. package/templates/.github/workflows/pr-label.yml +7 -26
  337. package/templates/.agents/rules/create-issue.github.en.md +0 -14
  338. package/templates/.agents/rules/create-issue.github.zh-CN.md +0 -27
  339. package/templates/.agents/rules/issue-pr-commands.github.en.md +0 -34
  340. package/templates/.agents/rules/issue-pr-commands.github.zh-CN.md +0 -49
  341. package/templates/.agents/rules/issue-sync.github.en.md +0 -40
  342. package/templates/.agents/rules/issue-sync.github.zh-CN.md +0 -76
  343. package/templates/.agents/rules/label-milestone-setup.github.en.md +0 -60
  344. package/templates/.agents/rules/label-milestone-setup.github.zh-CN.md +0 -60
  345. package/templates/.agents/rules/milestone-inference.github.en.md +0 -15
  346. package/templates/.agents/rules/milestone-inference.github.zh-CN.md +0 -27
  347. package/templates/.agents/rules/pr-checks-commands.github.en.md +0 -18
  348. package/templates/.agents/rules/pr-checks-commands.github.zh-CN.md +0 -34
  349. package/templates/.agents/rules/pr-sync.github.en.md +0 -43
  350. package/templates/.agents/rules/pr-sync.github.zh-CN.md +0 -69
  351. package/templates/.agents/rules/release-commands.github.en.md +0 -44
  352. package/templates/.agents/rules/release-commands.github.zh-CN.md +0 -44
  353. package/templates/.agents/rules/security-alerts.github.en.md +0 -43
  354. package/templates/.agents/rules/security-alerts.github.zh-CN.md +0 -43
  355. /package/templates/.agents/rules/{issue-fields.github.en.md → issue-fields.en.md} +0 -0
  356. /package/templates/.agents/rules/{issue-fields.github.zh-CN.md → issue-fields.zh-CN.md} +0 -0
@@ -0,0 +1,903 @@
1
+ import { execFileSync } from 'node:child_process';
2
+ import fs from 'node:fs';
3
+ import os from 'node:os';
4
+ import path from 'node:path';
5
+ import semver from 'semver';
6
+ import { createGitHubClient, MINIMUM_GITHUB_CLI_VERSION } from './github-client.js';
7
+ import { resourceIdentityNumber, resourceIdentityString } from './resource-identity.js';
8
+ import { syncLabelDelta } from './in-label-sync.js';
9
+ const CURRENT_USER_QUERY = 'query { viewer { login } }';
10
+ const ISSUE_TYPES_QUERY = `query($owner:String!){organization(login:$owner){issueTypes(first:20){nodes{id name pinnedFields{__typename ... on IssueFieldSingleSelect{id name options{id name}} ... on IssueFieldDate{id name} ... on IssueFieldText{id name} ... on IssueFieldNumber{id name}}}}}}`;
11
+ const ISSUE_FIELDS_QUERY = `query($owner:String!,$name:String!,$number:Int!){repository(owner:$owner,name:$name){issue(number:$number){id issueType{id name pinnedFields{__typename ... on IssueFieldSingleSelect{id name options{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{id name}}} ... on IssueFieldDateValue{value field{... on IssueFieldDate{id name}}} ... on IssueFieldTextValue{value field{... on IssueFieldText{id name}}} ... on IssueFieldNumberValue{value field{... on IssueFieldNumber{id name}}}}}}}}`;
12
+ function parseGitHubRemote(remote) {
13
+ const trimmed = remote.trim().replace(/\.git$/, '');
14
+ const match = trimmed.match(/^(?:https?:\/\/github\.com\/|ssh:\/\/(?:git@)?github\.com\/|git@github\.com:)([^/\s]+\/[^/\s]+)$/i);
15
+ return match?.[1] || null;
16
+ }
17
+ function defaultGitRemote(cwd) {
18
+ try {
19
+ return execFileSync('git', ['remote', 'get-url', 'origin'], {
20
+ cwd, encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe']
21
+ }).trim();
22
+ }
23
+ catch {
24
+ return null;
25
+ }
26
+ }
27
+ function configuredGitRemotes(cwd) {
28
+ try {
29
+ return execFileSync('git', ['remote'], {
30
+ cwd, encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe']
31
+ }).split(/\r?\n/).filter(Boolean).flatMap((name) => {
32
+ try {
33
+ const url = execFileSync('git', ['config', '--get', `remote.${name}.url`], {
34
+ cwd, encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe']
35
+ }).trim();
36
+ const repository = parseGitHubRemote(url);
37
+ return repository ? [{ name, url, repository }] : [];
38
+ }
39
+ catch {
40
+ return [];
41
+ }
42
+ });
43
+ }
44
+ catch {
45
+ return [];
46
+ }
47
+ }
48
+ function rewriteGitHubRemote(remote, repository) {
49
+ const match = remote.trim().match(/^(https?:\/\/github\.com\/|ssh:\/\/(?:git@)?github\.com\/|git@github\.com:)[^\/\s]+\/[^\/\s]+(?:\.git)?$/i);
50
+ return match ? `${match[1]}${repository}.git` : null;
51
+ }
52
+ function resolveGitHubChangeRequestGitEvidence({ cwd, repository, number, baseRepository, baseRef }) {
53
+ if (baseRepository !== repository || number <= 0) {
54
+ return {
55
+ ok: false,
56
+ error: {
57
+ code: 'PR_MERGE_EVIDENCE_SOURCE_UNAVAILABLE',
58
+ message: 'Pull request repository identity is inconsistent',
59
+ retryable: false
60
+ }
61
+ };
62
+ }
63
+ const reviewedHeadRef = `refs/pull/${number}/head`;
64
+ const targetHeadRef = `refs/heads/${baseRef}`;
65
+ const refValid = (ref) => {
66
+ try {
67
+ execFileSync('git', ['check-ref-format', ref], {
68
+ cwd, encoding: 'utf8', stdio: ['ignore', 'ignore', 'ignore']
69
+ });
70
+ return true;
71
+ }
72
+ catch {
73
+ return false;
74
+ }
75
+ };
76
+ if (!refValid(reviewedHeadRef) || !refValid(targetHeadRef)) {
77
+ return {
78
+ ok: false,
79
+ error: {
80
+ code: 'PR_MERGE_EVIDENCE_SOURCE_UNAVAILABLE',
81
+ message: 'Pull request Git refs are invalid',
82
+ retryable: false
83
+ }
84
+ };
85
+ }
86
+ const remotes = configuredGitRemotes(cwd);
87
+ const exact = remotes
88
+ .filter((remote) => remote.repository === repository)
89
+ .sort((left, right) => Number(right.name === 'origin') - Number(left.name === 'origin') ||
90
+ left.name.localeCompare(right.name))[0];
91
+ const origin = remotes.find((remote) => remote.name === 'origin');
92
+ const remoteUrl = exact?.url || (origin ? rewriteGitHubRemote(origin.url, repository) : null);
93
+ if (!remoteUrl) {
94
+ return {
95
+ ok: false,
96
+ error: {
97
+ code: 'PR_MERGE_EVIDENCE_SOURCE_UNAVAILABLE',
98
+ message: `No GitHub remote can provide evidence for ${repository}`,
99
+ retryable: false
100
+ }
101
+ };
102
+ }
103
+ return {
104
+ ok: true,
105
+ value: { remoteUrl, reviewedHeadRef, targetHeadRef }
106
+ };
107
+ }
108
+ function failure(error) {
109
+ return { ok: false, error };
110
+ }
111
+ function resolveContext(client, input) {
112
+ const cwd = input.workingDirectory;
113
+ const version = client.version({ cwd });
114
+ if (!version.ok)
115
+ return failure(version.error);
116
+ if (!semver.gte(version.value, MINIMUM_GITHUB_CLI_VERSION)) {
117
+ return failure({
118
+ code: 'GH_CLI_VERSION_UNSUPPORTED',
119
+ message: 'GitHub CLI ' + version.value + ' is unsupported; install gh >= ' + MINIMUM_GITHUB_CLI_VERSION,
120
+ retryable: false
121
+ });
122
+ }
123
+ const remote = input.gitRemote || defaultGitRemote(cwd);
124
+ if (!remote) {
125
+ return failure({ code: 'REMOTE_MISSING', message: 'Git origin remote is not configured', retryable: false });
126
+ }
127
+ const ownerRepo = parseGitHubRemote(remote);
128
+ if (!ownerRepo) {
129
+ return failure({
130
+ code: /github\.com/i.test(remote) ? 'REMOTE_INVALID' : 'PLATFORM_UNSUPPORTED',
131
+ message: 'Unable to parse GitHub owner/repo from configured remote',
132
+ retryable: false
133
+ });
134
+ }
135
+ const repository = client.json(['api', 'repos/' + ownerRepo], { cwd });
136
+ if (!repository.ok)
137
+ return failure(repository.error);
138
+ const repositoryValue = repository.value;
139
+ const upstream = repositoryValue?.fork ? repositoryValue.parent?.full_name : repositoryValue?.full_name;
140
+ if (!upstream)
141
+ return failure({
142
+ code: 'UPSTREAM_UNRESOLVED',
143
+ message: 'Unable to resolve the upstream repository',
144
+ retryable: false
145
+ });
146
+ const user = client.json(['api', 'graphql', '-f', 'query=' + CURRENT_USER_QUERY], { cwd });
147
+ if (!user.ok)
148
+ return failure(user.error);
149
+ const userValue = user.value;
150
+ const currentUser = userValue?.data?.viewer?.login || null;
151
+ const permissions = client.json(['api', 'repos/' + upstream], { cwd });
152
+ if (!permissions.ok)
153
+ return failure(permissions.error);
154
+ const permissionValue = permissions.value;
155
+ const values = permissionValue?.permissions || {};
156
+ const capabilities = {
157
+ authenticated: Boolean(currentUser),
158
+ comment: Boolean(currentUser),
159
+ triage: Boolean(values.triage || values.push || values.admin),
160
+ push: Boolean(values.push || values.admin),
161
+ admin: Boolean(values.admin)
162
+ };
163
+ return {
164
+ ok: true,
165
+ value: {
166
+ type: 'github',
167
+ scope: { id: upstream, label: upstream },
168
+ currentUser: currentUser ? { id: currentUser, name: currentUser } : null,
169
+ capabilities,
170
+ authenticated: capabilities.authenticated,
171
+ ...(repositoryValue?.owner?.type ? { metadata: { ownerType: repositoryValue.owner.type } } : {})
172
+ }
173
+ };
174
+ }
175
+ function repository(context) {
176
+ return context.scopeId;
177
+ }
178
+ function expectedHeadRepository(repositoryName, head) {
179
+ const colon = head.indexOf(':');
180
+ if (colon === -1)
181
+ return { repository: repositoryName, ref: head };
182
+ const repoName = repositoryName.split('/')[1] || '';
183
+ return { repository: `${head.slice(0, colon)}/${repoName}`, ref: head.slice(colon + 1) };
184
+ }
185
+ function repositoryHead(cwd) {
186
+ return execFileSync('git', ['rev-parse', 'HEAD'], {
187
+ cwd, encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe']
188
+ }).trim();
189
+ }
190
+ function verifyRemoteBranch(cwd, repositoryName, head) {
191
+ const wanted = expectedHeadRepository(repositoryName, head);
192
+ if (!wanted.ref || !/^[A-Za-z0-9._/-]+$/.test(wanted.ref))
193
+ return {
194
+ ok: false,
195
+ error: { code: 'PR_HEAD_INVALID', message: 'Pull request head ref is invalid', retryable: false }
196
+ };
197
+ const remotes = configuredGitRemotes(cwd);
198
+ const remote = remotes.find((item) => item.repository.toLowerCase() === wanted.repository.toLowerCase())
199
+ ?? remotes.find((item) => item.name === 'origin');
200
+ if (!remote)
201
+ return {
202
+ ok: false,
203
+ error: { code: 'PR_REMOTE_BRANCH_MISSING', message: `No configured remote can verify ${wanted.repository}:${wanted.ref}`, retryable: false }
204
+ };
205
+ try {
206
+ const output = execFileSync('git', ['ls-remote', '--refs', remote.name, `refs/heads/${wanted.ref}`], {
207
+ cwd, encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe']
208
+ }).trim();
209
+ const match = output.split(/\r?\n/).map((line) => line.trim()).find((line) => line.endsWith(`refs/heads/${wanted.ref}`));
210
+ const sha = match?.split(/\s+/)[0] || null;
211
+ if (!sha || !/^[a-f0-9]{40}$/i.test(sha))
212
+ return {
213
+ ok: false,
214
+ error: { code: 'PR_REMOTE_BRANCH_MISSING', message: `Remote branch ${wanted.repository}:${wanted.ref} does not exist`, retryable: false }
215
+ };
216
+ return { ok: true, value: sha };
217
+ }
218
+ catch (error) {
219
+ return {
220
+ ok: false,
221
+ error: { code: 'PR_REMOTE_BRANCH_UNAVAILABLE', message: error instanceof Error ? error.message : String(error), retryable: true }
222
+ };
223
+ }
224
+ }
225
+ function invalid(code, message) {
226
+ return { ok: false, error: { code, message, retryable: false } };
227
+ }
228
+ function changeRequestSnapshot(value) {
229
+ return {
230
+ id: value.nodeId,
231
+ identity: { kind: 'number', value: value.number },
232
+ number: value.number,
233
+ state: value.state,
234
+ title: value.title,
235
+ body: value.body,
236
+ baseSha: value.base.sha,
237
+ headSha: value.head.sha,
238
+ mergedAt: value.mergedAt,
239
+ displayUrl: value.url,
240
+ draft: value.draft,
241
+ labels: value.labels,
242
+ assignees: value.assignees,
243
+ milestone: value.milestone,
244
+ mergeCommitSha: value.mergeCommitSha,
245
+ mergeability: value.mergeability,
246
+ head: value.head,
247
+ base: value.base
248
+ };
249
+ }
250
+ function issueSnapshot(value, graph) {
251
+ const issueType = graph?.type ? {
252
+ identity: { kind: 'id', value: String(graph.type.id) },
253
+ name: String(graph.type.name),
254
+ fields: (graph.type.fields || []).map((field) => ({
255
+ identity: { kind: 'id', value: String(field.id) },
256
+ name: String(field.name),
257
+ kind: field.kind,
258
+ options: (field.options || []).map((option) => ({ identity: { kind: 'id', value: String(option.id) }, name: String(option.name) }))
259
+ }))
260
+ } : value.issueType ? {
261
+ identity: { kind: 'key', value: String(value.issueType) },
262
+ name: String(value.issueType),
263
+ fields: []
264
+ } : null;
265
+ return {
266
+ id: value.nodeId,
267
+ identity: { kind: 'number', value: value.number },
268
+ number: value.number,
269
+ state: value.state,
270
+ title: value.title,
271
+ body: value.body,
272
+ labels: value.labels,
273
+ assignees: value.assignees,
274
+ milestone: value.milestone,
275
+ fields: graph ? Object.fromEntries(graph.values.map((field) => [field.name, field.value])) : value.fields,
276
+ issueType,
277
+ displayUrl: value.url
278
+ };
279
+ }
280
+ function commentSnapshot(value) {
281
+ return {
282
+ id: String(value.id),
283
+ author: value.user?.login ? { id: String(value.user.login), name: String(value.user.login) } : null,
284
+ body: String(value.body || ''),
285
+ createdAt: String(value.created_at || ''),
286
+ updatedAt: String(value.updated_at || value.created_at || '')
287
+ };
288
+ }
289
+ function createReceipt(remoteId) {
290
+ return { ok: true, value: { changed: true, remoteId } };
291
+ }
292
+ function githubResourceToken(identity) {
293
+ return resourceIdentityString(identity) || (identity?.kind === 'number' ? String(identity.value) : null);
294
+ }
295
+ function syncLabels(client, repositoryName, number, cwd, current, target, prefixes) {
296
+ let changed = false;
297
+ for (const prefix of prefixes) {
298
+ const synced = syncLabelDelta(client, repositoryName, number, cwd, current, target, prefix);
299
+ if (synced.status === 'failed' || synced.status === 'blocked') {
300
+ if (changed || synced.changed)
301
+ return {
302
+ status: 'blocked',
303
+ changed: true,
304
+ error: synced.error
305
+ ? { ...synced.error, code: 'IN_LABEL_SYNC_PARTIAL', message: `In-label synchronization is partial or unknown: ${synced.error.message}` }
306
+ : { code: 'IN_LABEL_SYNC_PARTIAL', message: 'In-label synchronization is partial or unknown', retryable: true }
307
+ };
308
+ return synced;
309
+ }
310
+ changed ||= synced.changed;
311
+ }
312
+ return { status: changed ? 'applied' : 'no-op', changed, error: null };
313
+ }
314
+ function createGitHubOperations(client) {
315
+ const issues = {
316
+ async listLabels({ context }) {
317
+ const response = client.json(['api', '--paginate', '--slurp', `repos/${repository(context)}/labels?per_page=100`], { cwd: context.workingDirectory });
318
+ if (!response.ok)
319
+ return response;
320
+ const values = Array.isArray(response.value)
321
+ ? response.value.flatMap((entry) => Array.isArray(entry) ? entry : [entry])
322
+ : [];
323
+ if (values.some((entry) => !entry || typeof entry.name !== 'string' || !entry.name.trim())) {
324
+ return invalid('IN_LABEL_SYNC_LABELS_INVALID', 'Repository labels response contains an entry without a valid name');
325
+ }
326
+ return { ok: true, value: [...new Set(values.map((entry) => String(entry.name)))].sort() };
327
+ },
328
+ async listMilestones({ context }) {
329
+ const response = client.json(['api', '--paginate', '--slurp', `repos/${repository(context)}/milestones?state=open&per_page=100`], { cwd: context.workingDirectory });
330
+ if (!response.ok)
331
+ return response;
332
+ const values = Array.isArray(response.value)
333
+ ? response.value.flatMap((entry) => Array.isArray(entry) ? entry : [entry])
334
+ : [];
335
+ if (values.some((entry) => !entry || typeof entry.title !== 'string' || !entry.title.trim())) {
336
+ return invalid('MILESTONE_IDENTITY_INVALID', 'Milestones response contains an entry without a valid title');
337
+ }
338
+ return { ok: true, value: [...new Set(values.map((entry) => String(entry.title)))].sort() };
339
+ },
340
+ async describeRepository({ context }) {
341
+ const repositoryName = repository(context);
342
+ const info = client.json(['api', `repos/${repositoryName}`], { cwd: context.workingDirectory });
343
+ if (!info.ok)
344
+ return info;
345
+ const labels = client.json(['api', '--paginate', '--slurp', `repos/${repositoryName}/labels?per_page=100`], { cwd: context.workingDirectory });
346
+ if (!labels.ok)
347
+ return labels;
348
+ const milestones = client.json(['api', '--paginate', '--slurp', `repos/${repositoryName}/milestones?state=open&per_page=100`], { cwd: context.workingDirectory });
349
+ if (!milestones.ok)
350
+ return milestones;
351
+ const [owner] = repositoryName.split('/');
352
+ const issueTypes = client.json(['api', 'graphql', '-f', `query=${ISSUE_TYPES_QUERY}`, '-F', `owner=${owner}`], { cwd: context.workingDirectory });
353
+ if (!issueTypes.ok)
354
+ return issueTypes;
355
+ const flatten = (value) => Array.isArray(value) ? value.flatMap((entry) => Array.isArray(entry) ? entry : [entry]) : [];
356
+ const fields = (raw) => (raw || []).flatMap((field) => {
357
+ const kind = field.__typename === 'IssueFieldSingleSelect' ? 'single-select'
358
+ : field.__typename === 'IssueFieldDate' ? 'date'
359
+ : field.__typename === 'IssueFieldText' ? 'text'
360
+ : field.__typename === 'IssueFieldNumber' ? 'number' : null;
361
+ return field.id && field.name && kind ? [{
362
+ identity: { kind: 'id', value: String(field.id) }, name: String(field.name), kind,
363
+ options: (field.options || []).filter((option) => option?.id && option?.name).map((option) => ({ identity: { kind: 'id', value: String(option.id) }, name: String(option.name) }))
364
+ }] : [];
365
+ });
366
+ const typeNodes = issueTypes.value?.data?.organization?.issueTypes?.nodes || [];
367
+ return {
368
+ ok: true,
369
+ value: {
370
+ repository: {
371
+ identity: { kind: 'key', value: repositoryName },
372
+ name: String(info.value?.full_name || repositoryName),
373
+ url: info.value?.html_url ? String(info.value.html_url) : null
374
+ },
375
+ labels: flatten(labels.value).filter((item) => item?.name).map((item) => ({ identity: { kind: 'key', value: String(item.name) }, name: String(item.name) })),
376
+ milestones: flatten(milestones.value).filter((item) => item?.title).map((item) => ({ identity: { kind: 'key', value: String(item.title) }, title: String(item.title), state: item.state === 'closed' ? 'closed' : 'open' })),
377
+ issueTypes: typeNodes.filter((item) => item?.id && item?.name).map((item) => ({ identity: { kind: 'id', value: String(item.id) }, name: String(item.name), fields: fields(item.pinnedFields) })),
378
+ fields: []
379
+ }
380
+ };
381
+ },
382
+ async inspect({ context, target }) {
383
+ const number = resourceIdentityNumber(target);
384
+ if (!number)
385
+ return invalid('ISSUE_NUMBER_INVALID', 'Issue number must be positive');
386
+ const module = await import('./issues.js');
387
+ const fetched = module.inspectGitHubIssue(client, repository(context), number, context.workingDirectory);
388
+ if (!fetched.ok)
389
+ return fetched;
390
+ const graph = module.graphState(client, repository(context), number, context.workingDirectory);
391
+ return { ok: true, value: issueSnapshot(fetched.value, graph || undefined) };
392
+ },
393
+ async create({ context, desired }) {
394
+ const response = client.json(['api', `repos/${repository(context)}/issues`, '-X', 'POST', '--input', '-'], {
395
+ cwd: context.workingDirectory,
396
+ method: 'POST',
397
+ input: JSON.stringify({
398
+ title: desired.title,
399
+ body: desired.body,
400
+ labels: desired.labels,
401
+ assignees: desired.assignees,
402
+ ...(desired.milestone ? { milestone: desired.milestone } : {})
403
+ })
404
+ });
405
+ if (!response.ok)
406
+ return response;
407
+ const number = Number(response.value?.number);
408
+ if (!Number.isInteger(number) || number <= 0)
409
+ return invalid('ISSUE_CREATE_RESPONSE_INVALID', 'Issue create response lacks issue number');
410
+ return createReceipt(String(number));
411
+ },
412
+ async update({ context, target, currentLabels, patch }) {
413
+ const number = resourceIdentityNumber(target);
414
+ if (!number)
415
+ return invalid('ISSUE_NUMBER_INVALID', 'Issue number must be positive');
416
+ const { issueType, fields, labels, ...restPatch } = patch;
417
+ let requestPatch = restPatch;
418
+ let changed = false;
419
+ if (labels !== undefined) {
420
+ let before = currentLabels;
421
+ if (!before) {
422
+ const current = client.json(['api', `repos/${repository(context)}/issues/${number}`], { cwd: context.workingDirectory });
423
+ if (!current.ok)
424
+ return current;
425
+ before = Array.isArray(current.value?.labels)
426
+ ? current.value.labels.map((label) => typeof label === 'string' ? label : label?.name).filter((label) => typeof label === 'string')
427
+ : [];
428
+ }
429
+ const synced = syncLabels(client, repository(context), number, context.workingDirectory, before || [], labels, ['status:', 'in:']);
430
+ if (synced.status === 'failed' || synced.status === 'blocked')
431
+ return synced.error
432
+ ? { ok: false, error: synced.error }
433
+ : invalid('IN_LABEL_SYNC_FAILED', 'Issue label synchronization failed');
434
+ changed ||= synced.changed;
435
+ }
436
+ if (typeof patch.milestone === 'string') {
437
+ const milestones = client.json(['api', '--paginate', '--slurp', `repos/${repository(context)}/milestones?state=open&per_page=100`], { cwd: context.workingDirectory });
438
+ if (!milestones.ok)
439
+ return milestones;
440
+ const values = Array.isArray(milestones.value)
441
+ ? milestones.value.flatMap((entry) => Array.isArray(entry) ? entry : [entry])
442
+ : [];
443
+ const selected = values.find((item) => item?.title === patch.milestone);
444
+ if (!Number.isSafeInteger(selected?.number) || selected.number <= 0) {
445
+ return invalid('MILESTONE_IDENTITY_INVALID', 'Milestone title does not resolve to a positive number');
446
+ }
447
+ requestPatch = { ...restPatch, milestone: selected.number };
448
+ }
449
+ if (issueType !== undefined || fields !== undefined) {
450
+ const [owner, name] = repository(context).split('/');
451
+ const current = client.json(['api', 'graphql', '-f', `query=${ISSUE_FIELDS_QUERY}`, '-F', `owner=${owner}`, '-F', `name=${name}`, '-F', `number=${number}`], { cwd: context.workingDirectory });
452
+ if (!current.ok)
453
+ return current;
454
+ const currentIssue = current.value?.data?.repository?.issue;
455
+ const issueId = currentIssue?.id;
456
+ if (!issueId)
457
+ return invalid('ISSUE_GRAPH_IDENTITY_INVALID', 'Issue GraphQL response lacks issue identity');
458
+ let targetType = currentIssue.issueType;
459
+ if (issueType !== undefined) {
460
+ const types = client.json(['api', 'graphql', '-f', `query=${ISSUE_TYPES_QUERY}`, '-F', `owner=${owner}`], { cwd: context.workingDirectory });
461
+ if (!types.ok)
462
+ return types;
463
+ targetType = (types.value?.data?.organization?.issueTypes?.nodes || []).find((item) => item?.name === issueType);
464
+ if (!targetType)
465
+ return invalid('ISSUE_TYPE_NOT_FOUND', 'Issue type was not found');
466
+ if (targetType.id !== currentIssue.issueType?.id) {
467
+ const updatedType = client.json(['api', 'graphql', '--input', '-'], {
468
+ cwd: context.workingDirectory,
469
+ method: 'POST',
470
+ input: JSON.stringify({
471
+ query: 'mutation($issueId:ID!,$issueTypeId:ID){updateIssueIssueType(input:{issueId:$issueId,issueTypeId:$issueTypeId}){issue{id}}}',
472
+ variables: { issueId, issueTypeId: targetType.id }
473
+ })
474
+ });
475
+ if (!updatedType.ok)
476
+ return updatedType;
477
+ changed = true;
478
+ }
479
+ }
480
+ if (fields !== undefined) {
481
+ const values = Object.entries(fields).flatMap(([name, value]) => {
482
+ const field = (targetType?.pinnedFields || []).find((candidate) => candidate?.name === name);
483
+ if (!field?.id)
484
+ return [];
485
+ if (field.__typename === 'IssueFieldSingleSelect') {
486
+ const option = (field.options || []).find((candidate) => candidate?.name === value);
487
+ return option?.id ? [{ fieldId: field.id, singleSelectOptionId: option.id }] : [];
488
+ }
489
+ const key = field.__typename === 'IssueFieldDate' ? 'dateValue' : field.__typename === 'IssueFieldNumber' ? 'numberValue' : 'textValue';
490
+ return [{ fieldId: field.id, [key]: value }];
491
+ });
492
+ if (values.length > 0) {
493
+ const updatedFields = client.json(['api', 'graphql', '--input', '-'], {
494
+ cwd: context.workingDirectory,
495
+ method: 'POST',
496
+ input: JSON.stringify({
497
+ query: 'mutation($issueId:ID!,$issueFields:[IssueFieldCreateOrUpdateInput!]!){setIssueFieldValue(input:{issueId:$issueId,issueFields:$issueFields}){issue{id}}}',
498
+ variables: { issueId, issueFields: values }
499
+ })
500
+ });
501
+ if (!updatedFields.ok)
502
+ return updatedFields;
503
+ changed = true;
504
+ }
505
+ }
506
+ }
507
+ if (Object.keys(requestPatch).length > 0) {
508
+ const response = client.json(['api', `repos/${repository(context)}/issues/${number}`, '-X', 'PATCH', '--input', '-'], {
509
+ cwd: context.workingDirectory,
510
+ method: 'PATCH',
511
+ input: JSON.stringify(requestPatch)
512
+ });
513
+ if (!response.ok)
514
+ return response;
515
+ changed = true;
516
+ }
517
+ return { ok: true, value: { changed, remoteId: String(number) } };
518
+ }
519
+ };
520
+ const comments = {
521
+ async list({ context, parent }) {
522
+ const number = resourceIdentityNumber(parent);
523
+ if (!number)
524
+ return invalid('ISSUE_NUMBER_INVALID', 'Issue number must be positive');
525
+ const response = client.json(['api', '--paginate', '--slurp', `repos/${repository(context)}/issues/${number}/comments?per_page=100`], { cwd: context.workingDirectory });
526
+ if (!response.ok)
527
+ return response;
528
+ const values = Array.isArray(response.value) ? response.value.flatMap((entry) => Array.isArray(entry) ? entry : [entry]) : [];
529
+ return { ok: true, value: values.filter((entry) => entry && entry.id !== undefined).map(commentSnapshot) };
530
+ },
531
+ async write({ context, parent, body, existingComment }) {
532
+ const number = resourceIdentityNumber(parent);
533
+ if (!number)
534
+ return invalid('ISSUE_NUMBER_INVALID', 'Issue number must be positive');
535
+ const commentId = githubResourceToken(existingComment);
536
+ const endpoint = commentId ? `repos/${repository(context)}/issues/comments/${commentId}` : `repos/${repository(context)}/issues/${number}/comments`;
537
+ const response = client.json(['api', endpoint, '-X', commentId ? 'PATCH' : 'POST', '--input', '-'], {
538
+ cwd: context.workingDirectory,
539
+ method: commentId ? 'PATCH' : 'POST',
540
+ input: JSON.stringify({ body })
541
+ });
542
+ if (!response.ok)
543
+ return response;
544
+ return createReceipt(String(response.value?.id || commentId || ''));
545
+ },
546
+ async delete({ context, comment }) {
547
+ const commentId = githubResourceToken(comment);
548
+ if (!commentId)
549
+ return invalid('COMMENT_ID_INVALID', 'Comment id is required');
550
+ const response = client.text(['api', `repos/${repository(context)}/issues/comments/${commentId}`, '-X', 'DELETE'], { cwd: context.workingDirectory, method: 'DELETE' });
551
+ if (!response.ok)
552
+ return response;
553
+ return createReceipt(commentId);
554
+ }
555
+ };
556
+ const changeRequests = {
557
+ async verifyHead({ context, head }) {
558
+ let localHead;
559
+ try {
560
+ localHead = repositoryHead(context.workingDirectory);
561
+ }
562
+ catch (error) {
563
+ return { ok: false, error: { code: 'PR_LOCAL_HEAD_UNAVAILABLE', message: error instanceof Error ? error.message : String(error), retryable: false } };
564
+ }
565
+ const remote = verifyRemoteBranch(context.workingDirectory, repository(context), head);
566
+ if (!remote.ok)
567
+ return remote;
568
+ if (remote.value !== localHead)
569
+ return {
570
+ ok: false,
571
+ error: { code: 'PR_REMOTE_HEAD_MISMATCH', message: `Remote head ${remote.value} does not match local HEAD ${localHead}`, retryable: false }
572
+ };
573
+ return { ok: true, value: { sha: localHead } };
574
+ },
575
+ async inspect({ context, target }) {
576
+ const number = resourceIdentityNumber(target);
577
+ if (!number)
578
+ return invalid('PR_NUMBER_INVALID', 'Pull request number must be positive');
579
+ const module = await import('./pull-requests.js');
580
+ const fetched = module.inspectGitHubPullRequest(client, repository(context), number, context.workingDirectory);
581
+ if (!fetched.ok)
582
+ return fetched;
583
+ return { ok: true, value: changeRequestSnapshot(fetched.value) };
584
+ },
585
+ async listClosing({ context, issue }) {
586
+ const number = resourceIdentityNumber(issue);
587
+ if (!number)
588
+ return invalid('ISSUE_NUMBER_INVALID', 'Issue number must be positive');
589
+ const module = await import('./pull-requests.js');
590
+ const fetched = module.inspectGitHubIssueClosingChangeRequests(client, repository(context), number, context.workingDirectory);
591
+ if (!fetched.ok)
592
+ return fetched;
593
+ return { ok: true, value: fetched.value.map(changeRequestSnapshot) };
594
+ },
595
+ async create({ context, base, head, title, body, draft }) {
596
+ const response = client.json(['api', `repos/${repository(context)}/pulls`, '-X', 'POST', '--input', '-'], {
597
+ cwd: context.workingDirectory,
598
+ method: 'POST',
599
+ input: JSON.stringify({ title, body, head, base, draft })
600
+ });
601
+ if (!response.ok)
602
+ return response;
603
+ const number = Number(response.value?.number);
604
+ if (!Number.isInteger(number) || number <= 0)
605
+ return invalid('PR_CREATE_RESPONSE_INVALID', 'Pull request create response lacks number');
606
+ return createReceipt(String(number));
607
+ },
608
+ async update({ context, target, currentLabels, patch }) {
609
+ const number = resourceIdentityNumber(target);
610
+ if (!number)
611
+ return invalid('PR_NUMBER_INVALID', 'Pull request number must be positive');
612
+ const { labels, assignees, milestone, ...pullRequestPatch } = patch;
613
+ let changed = false;
614
+ if (labels !== undefined) {
615
+ let before = currentLabels;
616
+ if (!before) {
617
+ const current = client.json(['api', `repos/${repository(context)}/pulls/${number}`], { cwd: context.workingDirectory });
618
+ if (!current.ok)
619
+ return current;
620
+ before = Array.isArray(current.value?.labels)
621
+ ? current.value.labels.map((label) => typeof label === 'string' ? label : label?.name).filter((label) => typeof label === 'string')
622
+ : [];
623
+ }
624
+ const synced = syncLabels(client, repository(context), number, context.workingDirectory, before || [], labels, ['in:', 'type:']);
625
+ if (synced.status === 'failed' || synced.status === 'blocked')
626
+ return synced.error
627
+ ? { ok: false, error: synced.error }
628
+ : invalid('IN_LABEL_SYNC_FAILED', 'Pull request label synchronization failed');
629
+ changed ||= synced.changed;
630
+ }
631
+ if (Object.keys(pullRequestPatch).length > 0) {
632
+ const response = client.json(['api', `repos/${repository(context)}/pulls/${number}`, '-X', 'PATCH', '--input', '-'], {
633
+ cwd: context.workingDirectory,
634
+ method: 'PATCH',
635
+ input: JSON.stringify(pullRequestPatch)
636
+ });
637
+ if (!response.ok)
638
+ return response;
639
+ changed = true;
640
+ }
641
+ const issuePatch = {};
642
+ if (assignees !== undefined)
643
+ issuePatch.assignees = assignees;
644
+ if (milestone !== undefined) {
645
+ if (milestone === null)
646
+ issuePatch.milestone = null;
647
+ else {
648
+ const milestones = client.json(['api', '--paginate', '--slurp', `repos/${repository(context)}/milestones?state=open&per_page=100`], { cwd: context.workingDirectory });
649
+ if (!milestones.ok)
650
+ return milestones;
651
+ const values = Array.isArray(milestones.value) ? milestones.value.flatMap((entry) => Array.isArray(entry) ? entry : [entry]) : [];
652
+ const selected = values.find((entry) => entry?.title === milestone);
653
+ if (!Number.isSafeInteger(selected?.number) || selected.number <= 0)
654
+ return invalid('MILESTONE_IDENTITY_INVALID', 'Milestone title does not resolve to a positive number');
655
+ issuePatch.milestone = selected.number;
656
+ }
657
+ }
658
+ if (Object.keys(issuePatch).length > 0) {
659
+ const response = client.json(['api', `repos/${repository(context)}/issues/${number}`, '-X', 'PATCH', '--input', '-'], {
660
+ cwd: context.workingDirectory,
661
+ method: 'PATCH',
662
+ input: JSON.stringify(issuePatch)
663
+ });
664
+ if (!response.ok)
665
+ return response;
666
+ changed = true;
667
+ }
668
+ return { ok: true, value: { changed, remoteId: String(number) } };
669
+ },
670
+ async resolveGitEvidence({ context, target, expected }) {
671
+ const number = resourceIdentityNumber(target);
672
+ if (!number)
673
+ return invalid('PR_NUMBER_INVALID', 'Pull request number must be positive');
674
+ const module = await import('./github-provider.js');
675
+ return module.resolveGitHubChangeRequestGitEvidence({
676
+ cwd: context.workingDirectory,
677
+ repository: repository(context),
678
+ number,
679
+ baseRepository: repository(context),
680
+ baseRef: expected.targetBranch || 'main'
681
+ });
682
+ }
683
+ };
684
+ const checks = {
685
+ async inspectRequired({ context, changeRequest }) {
686
+ const number = resourceIdentityNumber(changeRequest);
687
+ if (!number)
688
+ return invalid('PR_NUMBER_INVALID', 'Pull request number must be positive');
689
+ const module = await import('./pr-checks.js');
690
+ const fetched = module.inspectGitHubRequiredChecks(client, repository(context), number, context.workingDirectory);
691
+ if (!fetched.ok)
692
+ return fetched;
693
+ return { ok: true, value: fetched.value.map((check) => ({ name: check.name, status: check.bucket, conclusion: check.conclusion, detailsUrl: check.detailsUrl })) };
694
+ },
695
+ async resolveRun({ context, changeRequest, checkName, detailsUrl }) {
696
+ const number = resourceIdentityNumber(changeRequest);
697
+ if (!number)
698
+ return invalid('PR_NUMBER_INVALID', 'Pull request number must be positive');
699
+ const module = await import('./pr-checks.js');
700
+ const direct = detailsUrl ? module.parseRunJobIdentity(detailsUrl) : null;
701
+ if (direct) {
702
+ const run = client.json(['api', `repos/${repository(context)}/actions/runs/${direct.runId}`], { cwd: context.workingDirectory });
703
+ if (!run.ok)
704
+ return run;
705
+ return { ok: true, value: { runId: String(direct.runId), jobId: direct.jobId ? String(direct.jobId) : undefined } };
706
+ }
707
+ const listed = client.json(['api', `repos/${repository(context)}/commits/${number}/check-runs`], { cwd: context.workingDirectory });
708
+ if (!listed.ok)
709
+ return listed;
710
+ const candidate = (listed.value?.check_runs || []).find((run) => run.name === checkName && run.id);
711
+ return candidate
712
+ ? { ok: true, value: { runId: String(candidate.id), jobId: candidate.job_id ? String(candidate.job_id) : undefined, name: checkName, status: String(candidate.status || ''), conclusion: candidate.conclusion, detailsUrl: candidate.details_url } }
713
+ : invalid('CHECK_RUN_NOT_FOUND', 'Check run was not found');
714
+ },
715
+ async fetchLogs({ context, runId, jobId }) {
716
+ const module = await import('./pr-checks.js');
717
+ const args = jobId
718
+ ? ['api', `repos/${repository(context)}/actions/jobs/${jobId}/logs`]
719
+ : ['run', 'view', runId, '--repo', repository(context), '--log-failed'];
720
+ const fetched = module.fetchCheckLogText(client, args, context.workingDirectory);
721
+ if (!fetched.ok)
722
+ return fetched;
723
+ return { ok: true, value: { runId, jobId, text: fetched.value || '' } };
724
+ }
725
+ };
726
+ const reviews = {
727
+ async list({ context, changeRequest }) {
728
+ const number = resourceIdentityNumber(changeRequest);
729
+ if (!number)
730
+ return invalid('PR_NUMBER_INVALID', 'Pull request number must be positive');
731
+ const response = client.json(['api', '--paginate', '--slurp', `repos/${repository(context)}/pulls/${number}/reviews?per_page=100`], { cwd: context.workingDirectory });
732
+ if (!response.ok)
733
+ return response;
734
+ const values = Array.isArray(response.value) ? response.value.flatMap((entry) => Array.isArray(entry) ? entry : [entry]) : [];
735
+ return { ok: true, value: values.filter((review) => review?.body).map((review) => ({ id: String(review.id || review.node_id || ''), author: review.user?.login ? { id: String(review.user.login), name: String(review.user.login) } : null, commitSha: String(review.commit_id || ''), body: String(review.body), state: String(review.state || ''), submittedAt: String(review.submitted_at || ''), displayUrl: review.html_url })) };
736
+ },
737
+ async publish({ context, changeRequest, identity, event, body }) {
738
+ const number = resourceIdentityNumber(changeRequest);
739
+ if (!number)
740
+ return invalid('PR_NUMBER_INVALID', 'Pull request number must be positive');
741
+ const response = client.json(['api', `repos/${repository(context)}/pulls/${number}/reviews`, '-X', 'POST', '--input', '-'], {
742
+ cwd: context.workingDirectory,
743
+ method: 'POST',
744
+ input: JSON.stringify({ commit_id: identity.commitSha, body, event })
745
+ });
746
+ if (!response.ok)
747
+ return response;
748
+ return createReceipt(String(response.value?.id || ''));
749
+ }
750
+ };
751
+ const releases = {
752
+ async inspect({ context, tag }) {
753
+ const response = client.json(['release', 'view', tag, '--repo', repository(context), '--json', 'tagName,isDraft,url'], { cwd: context.workingDirectory });
754
+ if (!response.ok)
755
+ return response;
756
+ const runs = client.json(['run', 'list', '--repo', repository(context), '--limit', '100', '--json', 'name,workflowName,displayTitle,event,headBranch,headSha,status,conclusion,createdAt,databaseId,attempt,url'], { cwd: context.workingDirectory });
757
+ if (!runs.ok)
758
+ return runs;
759
+ const value = { id: String(response.value?.id || tag), tag: String(response.value?.tagName || tag), title: String(response.value?.name || tag), body: String(response.value?.body || ''), draft: Boolean(response.value?.isDraft), prerelease: Boolean(response.value?.isPrerelease), publishedAt: response.value?.publishedAt ? new Date(String(response.value.publishedAt)).toISOString() : null, displayUrl: response.value?.url ? String(response.value.url) : undefined, workflows: Array.isArray(runs.value) ? runs.value : [] };
760
+ return { ok: true, value };
761
+ },
762
+ async create({ context, tag, title, notes }) {
763
+ const args = ['release', 'create', tag, '--repo', repository(context), '--title', title];
764
+ if (notes)
765
+ args.push('--notes', notes.text);
766
+ const response = client.text(args, { cwd: context.workingDirectory, method: 'POST' });
767
+ if (!response.ok)
768
+ return response;
769
+ return createReceipt(tag);
770
+ },
771
+ async update({ context, release, patch }) {
772
+ const tag = resourceIdentityString(release) || '';
773
+ const args = ['release', 'edit', tag, '--repo', repository(context)];
774
+ if (patch.title)
775
+ args.push('--title', patch.title);
776
+ if (patch.body)
777
+ args.push('--notes', patch.body);
778
+ const response = client.text(args, { cwd: context.workingDirectory, method: 'PATCH' });
779
+ if (!response.ok)
780
+ return response;
781
+ return createReceipt(tag);
782
+ },
783
+ async reconcileMilestones({ context, desired }) {
784
+ const listed = client.json(['api', '--paginate', '--slurp', `repos/${repository(context)}/milestones?state=all&per_page=100`], { cwd: context.workingDirectory });
785
+ if (!listed.ok)
786
+ return listed;
787
+ const values = Array.isArray(listed.value) ? listed.value.flatMap((entry) => Array.isArray(entry) ? entry : [entry]) : [];
788
+ const created = [];
789
+ const closed = [];
790
+ for (const item of desired) {
791
+ const current = values.find((candidate) => candidate.title === item.title);
792
+ if (current) {
793
+ if (item.state === 'closed' && current.state !== 'closed') {
794
+ const response = client.json(['api', '--method', 'PATCH', `repos/${repository(context)}/milestones/${current.number}`, '-f', 'state=closed'], { cwd: context.workingDirectory });
795
+ if (!response.ok)
796
+ return response;
797
+ closed.push(item.title);
798
+ }
799
+ continue;
800
+ }
801
+ const response = client.json(['api', '--method', 'POST', `repos/${repository(context)}/milestones`, '-f', `title=${item.title}`, '-f', `description=${item.description}`], { cwd: context.workingDirectory });
802
+ if (!response.ok)
803
+ return response;
804
+ created.push(item.title);
805
+ }
806
+ return { ok: true, value: { changed: created.length > 0 || closed.length > 0, created, closed } };
807
+ },
808
+ async publishNotes({ context, release, title, notes }) {
809
+ const module = await import('./github-release-notes.js');
810
+ const temporaryRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'agent-infra-release-notes-'));
811
+ const notesFile = path.join(temporaryRoot, 'notes.md');
812
+ fs.writeFileSync(notesFile, notes.text);
813
+ try {
814
+ const tag = resourceIdentityString(release) || '';
815
+ const result = module.publishGitHubReleaseNotes({ repository: repository(context), tag, title, notesFile }, { cwd: context.workingDirectory, client });
816
+ if (result.status === 'failed' || result.status === 'blocked')
817
+ return { ok: false, error: result.error || { code: 'RELEASE_NOTES_PUBLISH_FAILED', message: 'Release notes publish failed', retryable: false } };
818
+ return { ok: true, value: { changed: result.changed, remoteId: tag } };
819
+ }
820
+ finally {
821
+ fs.rmSync(temporaryRoot, { recursive: true, force: true });
822
+ }
823
+ },
824
+ async collectNotes({ context, fromTime, toTime, commitOids, branch, historyLimit }) {
825
+ const module = await import('./github-release-notes.js');
826
+ const collected = module.fetchGitHubReleaseNoteData({
827
+ repository: repository(context), commitOids, branch, historyLimit, fromTime, toTime
828
+ }, { cwd: context.workingDirectory, client });
829
+ if (collected.status === 'failed' || collected.status === 'blocked' || !('pullRequests' in collected)) {
830
+ return { ok: false, error: collected.error || { code: 'RELEASE_NOTES_COLLECTION_FAILED', message: 'Release notes could not be collected', retryable: false } };
831
+ }
832
+ const mergedPullRequests = collected.pullRequests.map((item) => ({
833
+ id: String(item.number), identity: { kind: 'number', value: Number(item.number) }, number: Number(item.number),
834
+ state: 'closed', title: String(item.title || ''), body: String(item.body || ''),
835
+ mergedAt: new Date(String(item.mergedAt)).toISOString(), displayUrl: String(item.url || ''),
836
+ labels: Array.isArray(item.labels) ? item.labels.map((label) => String(label.name || label)) : [],
837
+ assignees: [], mergeCommitSha: null
838
+ }));
839
+ const closingIssues = collected.pullRequests.flatMap((item) => Array.isArray(item.closingIssuesReferences) ? item.closingIssuesReferences.map((issue) => ({
840
+ id: String(issue.number), identity: { kind: 'number', value: Number(issue.number) }, number: Number(issue.number),
841
+ state: 'closed', title: String(issue.title || ''), body: '', labels: Array.isArray(issue.labels) ? issue.labels.map((label) => String(label.name || label)) : [],
842
+ assignees: [], milestone: null, fields: {}, displayUrl: String(issue.url || '')
843
+ })) : []);
844
+ const actors = [...collected.authors.values()].flatMap((items) => items.map((item) => ({
845
+ ...(item.login ? { id: String(item.login) } : {}), ...(item.name ? { name: String(item.name) } : {})
846
+ })));
847
+ return {
848
+ ok: true,
849
+ value: {
850
+ history: commitOids.map((sha) => ({ sha, message: '', authoredAt: toTime, author: actors.find((actor) => actor.id) || null })),
851
+ mergedPullRequests,
852
+ closingIssues,
853
+ actors
854
+ }
855
+ };
856
+ }
857
+ };
858
+ const verification = {
859
+ async fetchRemoteFacts(input) {
860
+ const issue = input.issue && issues.inspect
861
+ ? await issues.inspect({ context: input.context, target: input.issue })
862
+ : { ok: true, value: null };
863
+ if (!issue.ok)
864
+ return issue;
865
+ const commentFacts = input.includeComments && input.issue && comments.list
866
+ ? await comments.list({ context: input.context, parent: input.issue })
867
+ : { ok: true, value: [] };
868
+ if (!commentFacts.ok)
869
+ return commentFacts;
870
+ const changeRequest = input.changeRequest && changeRequests.inspect
871
+ ? await changeRequests.inspect({ context: input.context, target: input.changeRequest })
872
+ : { ok: true, value: null };
873
+ if (!changeRequest.ok)
874
+ return changeRequest;
875
+ return {
876
+ ok: true,
877
+ value: {
878
+ issue: issue.value,
879
+ comments: commentFacts.value,
880
+ changeRequest: changeRequest.value,
881
+ commit: null,
882
+ fields: input.includeFields ? issue.value?.fields || {} : {}
883
+ }
884
+ };
885
+ }
886
+ };
887
+ return { issues, comments, changeRequests, checks, reviews, releases, verification };
888
+ }
889
+ function createGitHubProvider(input, client = createGitHubClient()) {
890
+ return {
891
+ type: input.providerType,
892
+ contractVersion: 1,
893
+ identity: { issue: 'number', 'pull-request': 'number', comment: 'number', release: 'key' },
894
+ ...createGitHubOperations(client),
895
+ context: {
896
+ async resolve(contextInput) {
897
+ return resolveContext(client, contextInput);
898
+ }
899
+ }
900
+ };
901
+ }
902
+ export { configuredGitRemotes, createGitHubProvider, defaultGitRemote, parseGitHubRemote, resolveGitHubChangeRequestGitEvidence };
903
+ //# sourceMappingURL=github-provider.js.map