@fitlab-ai/agent-infra 0.9.13 → 0.9.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (280) hide show
  1. package/bin/cli.ts +26 -9
  2. package/dist/bin/cli.js +173 -157
  3. package/dist/bin/internal-cli.js +42 -21
  4. package/dist/lib/agent-client.js +5 -0
  5. package/dist/lib/internal/agent-client.js +5 -2
  6. package/dist/lib/internal/cli-route-inventory.js +152 -0
  7. package/dist/lib/internal/codex-lifecycle.js +9 -6
  8. package/dist/lib/internal/codex-sandbox-controller.js +8 -2
  9. package/dist/lib/internal/git-workflow.js +10 -6
  10. package/dist/lib/internal/platform-checks.js +16 -4
  11. package/dist/lib/internal/platform-comment.js +16 -4
  12. package/dist/lib/internal/platform-context.js +4 -1
  13. package/dist/lib/internal/platform-issue.js +12 -4
  14. package/dist/lib/internal/platform-metadata.js +88 -0
  15. package/dist/lib/internal/platform-pr-review.js +16 -3
  16. package/dist/lib/internal/platform-pr.js +24 -10
  17. package/dist/lib/internal/platform-release-notes.js +6 -3
  18. package/dist/lib/internal/platform-security.js +93 -0
  19. package/dist/lib/internal/pr-review-grade.js +14 -3
  20. package/dist/lib/internal/release-workflow.js +19 -5
  21. package/dist/lib/internal/sandbox-control.js +22 -5
  22. package/dist/lib/internal/task-activity.js +13 -4
  23. package/dist/lib/internal/task-artifact.js +186 -27
  24. package/dist/lib/internal/task-context.js +4 -1
  25. package/dist/lib/internal/task-create.js +48 -15
  26. package/dist/lib/internal/task-delivery.js +4 -1
  27. package/dist/lib/internal/task-event.js +4 -1
  28. package/dist/lib/internal/task-finalization.js +4 -1
  29. package/dist/lib/internal/task-invalidation.js +4 -1
  30. package/dist/lib/internal/task-ledger.js +12 -1
  31. package/dist/lib/internal/task-lifecycle.js +4 -1
  32. package/dist/lib/internal/task-operation-registry.js +421 -0
  33. package/dist/lib/internal/task-orchestration.js +12 -0
  34. package/dist/lib/internal/task-override.js +10 -3
  35. package/dist/lib/internal/task-qualification.js +90 -0
  36. package/dist/lib/internal/task-review.js +4 -1
  37. package/dist/lib/internal/task-short-id.js +11 -1
  38. package/dist/lib/internal/task-snapshot.js +4 -1
  39. package/dist/lib/internal/task-validate.js +7 -2
  40. package/dist/lib/internal/task-verify.js +5 -1
  41. package/dist/lib/internal/task-warning.js +8 -1
  42. package/dist/lib/platform/comment-safety.js +226 -0
  43. package/dist/lib/platform/github-provider.js +233 -6
  44. package/dist/lib/platform/issue-comments.js +146 -34
  45. package/dist/lib/platform/pr-change-report.js +6 -12
  46. package/dist/lib/platform/pr-summary.js +28 -7
  47. package/dist/lib/platform/provider-contract.d.ts +67 -1
  48. package/dist/lib/platform/provider-contract.js +6 -2
  49. package/dist/lib/platform/provider-validation.js +76 -3
  50. package/dist/lib/platform/pull-requests.js +115 -170
  51. package/dist/lib/platform/repository-metadata.js +377 -0
  52. package/dist/lib/platform/security-alerts.js +155 -0
  53. package/dist/lib/platform/verification-sync.js +33 -26
  54. package/dist/lib/process-data/index.js +7 -0
  55. package/dist/lib/run/index.js +5 -0
  56. package/dist/lib/sandbox/commands/control-status.js +22 -0
  57. package/dist/lib/sandbox/commands/create.js +32 -6
  58. package/dist/lib/sandbox/commands/ls.js +5 -0
  59. package/dist/lib/sandbox/commands/rm.js +827 -120
  60. package/dist/lib/sandbox/commands/show.js +16 -0
  61. package/dist/lib/sandbox/control/client.js +83 -9
  62. package/dist/lib/sandbox/control/completed-reentry.js +85 -0
  63. package/dist/lib/sandbox/control/container-identity.js +28 -31
  64. package/dist/lib/sandbox/control/executor.js +149 -14
  65. package/dist/lib/sandbox/control/lifecycle.js +621 -127
  66. package/dist/lib/sandbox/control/native-file-lock.js +177 -0
  67. package/dist/lib/sandbox/control/server.js +305 -35
  68. package/dist/lib/sandbox/control/state.js +5 -3
  69. package/dist/lib/sandbox/control/task-view.js +183 -0
  70. package/dist/lib/sandbox/engines/authority.js +289 -0
  71. package/dist/lib/sandbox/engines/docker-exact-discovery.js +97 -0
  72. package/dist/lib/sandbox/index.js +5 -0
  73. package/dist/lib/sandbox/recovery.js +21 -1
  74. package/dist/lib/sandbox/workspace-view.js +2 -1
  75. package/dist/lib/sandbox/worktree-safety.js +4 -1
  76. package/dist/lib/server/index.js +8 -0
  77. package/dist/lib/task/artifact-lifecycle.js +42 -13
  78. package/dist/lib/task/artifact-operations.js +520 -0
  79. package/dist/lib/task/artifact-repair-intent.js +59 -0
  80. package/dist/lib/task/artifact-schema.js +135 -0
  81. package/dist/lib/task/capabilities.js +72 -1
  82. package/dist/lib/task/control-authority.js +4 -0
  83. package/dist/lib/task/create-service.js +103 -4
  84. package/dist/lib/task/create.js +25 -0
  85. package/dist/lib/task/events.js +186 -67
  86. package/dist/lib/task/index.js +11 -3
  87. package/dist/lib/task/local-artifact-finalization.js +75 -150
  88. package/dist/lib/task/orchestration.js +192 -52
  89. package/dist/lib/task/qualification-audit.js +490 -0
  90. package/dist/lib/task/qualification-confirmation.js +109 -0
  91. package/dist/lib/task/qualification-intents.js +148 -0
  92. package/dist/lib/task/review-artifacts.js +22 -23
  93. package/dist/lib/task/review-finalization.js +121 -4
  94. package/dist/lib/task/sections.js +6 -4
  95. package/dist/lib/task/verification-config.js +11 -0
  96. package/dist/lib/task/verification-engine.js +54 -4
  97. package/dist/lifecycle-build-manifest.json +3 -3
  98. package/lib/agent-client.ts +9 -0
  99. package/lib/internal/agent-client.ts +4 -2
  100. package/lib/internal/cli-route-inventory.ts +140 -0
  101. package/lib/internal/codex-lifecycle.ts +8 -6
  102. package/lib/internal/codex-sandbox-controller.ts +7 -2
  103. package/lib/internal/git-workflow.ts +9 -6
  104. package/lib/internal/platform-checks.ts +12 -4
  105. package/lib/internal/platform-comment.ts +12 -4
  106. package/lib/internal/platform-context.ts +3 -1
  107. package/lib/internal/platform-issue.ts +11 -4
  108. package/lib/internal/platform-metadata.ts +70 -0
  109. package/lib/internal/platform-pr-review.ts +14 -3
  110. package/lib/internal/platform-pr.ts +23 -10
  111. package/lib/internal/platform-release-notes.ts +5 -3
  112. package/lib/internal/platform-security.ts +83 -0
  113. package/lib/internal/pr-review-grade.ts +10 -3
  114. package/lib/internal/release-workflow.ts +17 -5
  115. package/lib/internal/sandbox-control.ts +19 -5
  116. package/lib/internal/task-activity.ts +12 -4
  117. package/lib/internal/task-artifact.ts +164 -25
  118. package/lib/internal/task-context.ts +3 -1
  119. package/lib/internal/task-create.ts +76 -15
  120. package/lib/internal/task-delivery.ts +3 -1
  121. package/lib/internal/task-event.ts +3 -1
  122. package/lib/internal/task-finalization.ts +3 -1
  123. package/lib/internal/task-invalidation.ts +3 -1
  124. package/lib/internal/task-ledger.ts +11 -1
  125. package/lib/internal/task-lifecycle.ts +3 -1
  126. package/lib/internal/task-operation-registry.ts +482 -0
  127. package/lib/internal/task-orchestration.ts +12 -0
  128. package/lib/internal/task-override.ts +9 -3
  129. package/lib/internal/task-qualification.ts +61 -0
  130. package/lib/internal/task-review.ts +3 -1
  131. package/lib/internal/task-short-id.ts +10 -1
  132. package/lib/internal/task-snapshot.ts +3 -1
  133. package/lib/internal/task-validate.ts +6 -2
  134. package/lib/internal/task-verify.ts +4 -1
  135. package/lib/internal/task-warning.ts +7 -1
  136. package/lib/platform/comment-safety.ts +250 -0
  137. package/lib/platform/github-provider.ts +229 -7
  138. package/lib/platform/issue-comments.ts +160 -35
  139. package/lib/platform/pr-change-report.ts +6 -12
  140. package/lib/platform/pr-summary.ts +27 -6
  141. package/lib/platform/provider-contract.ts +78 -3
  142. package/lib/platform/provider-validation.ts +85 -3
  143. package/lib/platform/pull-requests.ts +126 -178
  144. package/lib/platform/repository-metadata.ts +453 -0
  145. package/lib/platform/security-alerts.ts +191 -0
  146. package/lib/platform/verification-sync.ts +32 -28
  147. package/lib/process-data/index.ts +7 -0
  148. package/lib/run/index.ts +5 -0
  149. package/lib/sandbox/commands/control-status.ts +26 -0
  150. package/lib/sandbox/commands/create.ts +46 -6
  151. package/lib/sandbox/commands/ls.ts +7 -0
  152. package/lib/sandbox/commands/rm.ts +1000 -87
  153. package/lib/sandbox/commands/show.ts +16 -0
  154. package/lib/sandbox/control/client.ts +103 -9
  155. package/lib/sandbox/control/completed-reentry.ts +98 -0
  156. package/lib/sandbox/control/container-identity.ts +27 -43
  157. package/lib/sandbox/control/executor.ts +177 -12
  158. package/lib/sandbox/control/lifecycle.ts +629 -17
  159. package/lib/sandbox/control/native-file-lock.ts +209 -0
  160. package/lib/sandbox/control/protocol.ts +5 -2
  161. package/lib/sandbox/control/server.ts +319 -37
  162. package/lib/sandbox/control/state.ts +6 -3
  163. package/lib/sandbox/control/task-view.ts +240 -0
  164. package/lib/sandbox/engines/authority.ts +365 -0
  165. package/lib/sandbox/engines/docker-exact-discovery.ts +132 -0
  166. package/lib/sandbox/index.ts +7 -0
  167. package/lib/sandbox/recovery.ts +21 -1
  168. package/lib/sandbox/workspace-view.ts +9 -3
  169. package/lib/sandbox/worktree-safety.ts +4 -1
  170. package/lib/server/index.ts +10 -0
  171. package/lib/task/artifact-lifecycle.ts +38 -16
  172. package/lib/task/artifact-operations.ts +626 -0
  173. package/lib/task/artifact-repair-intent.ts +71 -0
  174. package/lib/task/artifact-schema.ts +192 -0
  175. package/lib/task/capabilities.ts +70 -1
  176. package/lib/task/control-authority.ts +11 -1
  177. package/lib/task/create-service.ts +131 -6
  178. package/lib/task/create.ts +27 -0
  179. package/lib/task/events.ts +161 -65
  180. package/lib/task/index.ts +16 -3
  181. package/lib/task/local-artifact-finalization.ts +87 -218
  182. package/lib/task/orchestration.ts +172 -49
  183. package/lib/task/qualification-audit.ts +599 -0
  184. package/lib/task/qualification-confirmation.ts +117 -0
  185. package/lib/task/qualification-intents.ts +150 -0
  186. package/lib/task/review-artifacts.ts +21 -22
  187. package/lib/task/review-finalization.ts +133 -4
  188. package/lib/task/sections.ts +6 -4
  189. package/lib/task/verification-config.ts +12 -0
  190. package/lib/task/verification-engine.ts +55 -4
  191. package/package.json +6 -1
  192. package/templates/.agents/rules/README.en.md +2 -0
  193. package/templates/.agents/rules/README.zh-CN.md +2 -0
  194. package/templates/.agents/rules/decision-qualification.en.md +22 -0
  195. package/templates/.agents/rules/decision-qualification.zh-CN.md +22 -0
  196. package/templates/.agents/rules/evidence-reporting.en.md +28 -0
  197. package/templates/.agents/rules/evidence-reporting.zh-CN.md +28 -0
  198. package/templates/.agents/rules/label-milestone-setup.en.md +7 -7
  199. package/templates/.agents/rules/label-milestone-setup.zh-CN.md +7 -7
  200. package/templates/.agents/rules/local-artifact-repair.en.md +5 -1
  201. package/templates/.agents/rules/local-artifact-repair.zh-CN.md +5 -1
  202. package/templates/.agents/rules/security-alerts.en.md +8 -8
  203. package/templates/.agents/rules/security-alerts.zh-CN.md +8 -8
  204. package/templates/.agents/skills/analyze-task/SKILL.en.md +15 -2
  205. package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +19 -2
  206. package/templates/.agents/skills/analyze-task/config/verify.en.json +1 -12
  207. package/templates/.agents/skills/analyze-task/config/verify.zh-CN.json +1 -12
  208. package/templates/.agents/skills/close-codescan/SKILL.en.md +2 -2
  209. package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +2 -2
  210. package/templates/.agents/skills/close-dependabot/SKILL.en.md +2 -2
  211. package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +2 -2
  212. package/templates/.agents/skills/code-task/SKILL.en.md +14 -1
  213. package/templates/.agents/skills/code-task/SKILL.zh-CN.md +14 -1
  214. package/templates/.agents/skills/code-task/config/verify.en.json +5 -14
  215. package/templates/.agents/skills/code-task/config/verify.zh-CN.json +5 -14
  216. package/templates/.agents/skills/code-task/reference/code-rules.en.md +5 -4
  217. package/templates/.agents/skills/code-task/reference/code-rules.zh-CN.md +5 -4
  218. package/templates/.agents/skills/code-task/reference/dual-mode.en.md +1 -1
  219. package/templates/.agents/skills/code-task/reference/dual-mode.zh-CN.md +1 -1
  220. package/templates/.agents/skills/code-task/reference/fix-mode.en.md +3 -1
  221. package/templates/.agents/skills/code-task/reference/fix-mode.zh-CN.md +3 -1
  222. package/templates/.agents/skills/code-task/reference/report-template.en.md +42 -15
  223. package/templates/.agents/skills/code-task/reference/report-template.zh-CN.md +39 -12
  224. package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +5 -1
  225. package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +5 -1
  226. package/templates/.agents/skills/complete-manual-validation/reference/report-template.en.md +2 -0
  227. package/templates/.agents/skills/complete-manual-validation/reference/report-template.zh-CN.md +2 -0
  228. package/templates/.agents/skills/complete-task/SKILL.en.md +9 -22
  229. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +9 -22
  230. package/templates/.agents/skills/create-task/SKILL.en.md +8 -0
  231. package/templates/.agents/skills/create-task/SKILL.zh-CN.md +7 -0
  232. package/templates/.agents/skills/import-codescan/SKILL.en.md +1 -1
  233. package/templates/.agents/skills/import-codescan/SKILL.zh-CN.md +1 -1
  234. package/templates/.agents/skills/import-dependabot/SKILL.en.md +1 -1
  235. package/templates/.agents/skills/import-dependabot/SKILL.zh-CN.md +1 -1
  236. package/templates/.agents/skills/init-labels/SKILL.en.md +8 -8
  237. package/templates/.agents/skills/init-labels/SKILL.zh-CN.md +8 -8
  238. package/templates/.agents/skills/init-milestones/SKILL.en.md +9 -11
  239. package/templates/.agents/skills/init-milestones/SKILL.zh-CN.md +9 -11
  240. package/templates/.agents/skills/plan-task/SKILL.en.md +14 -1
  241. package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +16 -1
  242. package/templates/.agents/skills/plan-task/config/verify.en.json +4 -14
  243. package/templates/.agents/skills/plan-task/config/verify.zh-CN.json +4 -14
  244. package/templates/.agents/skills/review-analysis/SKILL.en.md +14 -1
  245. package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +14 -1
  246. package/templates/.agents/skills/review-analysis/config/verify.en.json +1 -18
  247. package/templates/.agents/skills/review-analysis/config/verify.zh-CN.json +1 -18
  248. package/templates/.agents/skills/review-analysis/reference/report-template.en.md +50 -23
  249. package/templates/.agents/skills/review-analysis/reference/report-template.zh-CN.md +45 -18
  250. package/templates/.agents/skills/review-code/SKILL.en.md +14 -1
  251. package/templates/.agents/skills/review-code/SKILL.zh-CN.md +14 -1
  252. package/templates/.agents/skills/review-code/config/verify.en.json +1 -23
  253. package/templates/.agents/skills/review-code/config/verify.zh-CN.json +1 -23
  254. package/templates/.agents/skills/review-code/reference/report-template.en.md +49 -22
  255. package/templates/.agents/skills/review-code/reference/report-template.zh-CN.md +45 -18
  256. package/templates/.agents/skills/review-plan/SKILL.en.md +14 -1
  257. package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +14 -1
  258. package/templates/.agents/skills/review-plan/config/verify.en.json +1 -18
  259. package/templates/.agents/skills/review-plan/config/verify.zh-CN.json +1 -18
  260. package/templates/.agents/skills/review-plan/reference/report-template.en.md +49 -22
  261. package/templates/.agents/skills/review-plan/reference/report-template.zh-CN.md +45 -18
  262. package/templates/.agents/skills/review-pr/SKILL.en.md +5 -1
  263. package/templates/.agents/skills/review-pr/SKILL.zh-CN.md +5 -1
  264. package/templates/.agents/skills/review-pr/reference/report-template.en.md +4 -3
  265. package/templates/.agents/skills/review-pr/reference/report-template.zh-CN.md +4 -3
  266. package/templates/.agents/skills/run-manual-validation/SKILL.en.md +5 -1
  267. package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +5 -1
  268. package/templates/.agents/skills/run-manual-validation/reference/report-template.en.md +3 -1
  269. package/templates/.agents/skills/run-manual-validation/reference/report-template.zh-CN.md +3 -1
  270. package/templates/.agents/skills/test/SKILL.en.md +13 -2
  271. package/templates/.agents/skills/test/SKILL.zh-CN.md +13 -2
  272. package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +14 -0
  273. package/templates/.agents/templates/task.en.md +6 -0
  274. package/templates/.agents/templates/task.zh-CN.md +6 -0
  275. package/templates/.agents/scripts/security-alerts.github.sh +0 -151
  276. package/templates/.agents/scripts/security-alerts.sh +0 -31
  277. package/templates/.agents/skills/init-labels/scripts/init-labels.github.sh +0 -146
  278. package/templates/.agents/skills/init-labels/scripts/init-labels.sh +0 -32
  279. package/templates/.agents/skills/init-milestones/scripts/init-milestones.github.sh +0 -294
  280. package/templates/.agents/skills/init-milestones/scripts/init-milestones.sh +0 -6
@@ -0,0 +1,377 @@
1
+ import { execFileSync } from 'node:child_process';
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
4
+ import { resolvePlatformProviderContext } from './context.js';
5
+ import { providerError, providerOperationContext, providerStatus } from './provider-bridge.js';
6
+ import { platformResult } from './types.js';
7
+ const COMMON_LABELS = [
8
+ { name: 'type: bug', color: 'DED6F9', description: 'A general bug' },
9
+ { name: 'type: enhancement', color: 'DED6F9', description: 'A general enhancement' },
10
+ { name: 'type: feature', color: 'DED6F9', description: 'A general feature' },
11
+ { name: 'type: documentation', color: 'DED6F9', description: 'A documentation task' },
12
+ { name: 'type: dependency-upgrade', color: 'DED6F9', description: 'A dependency upgrade' },
13
+ { name: 'type: task', color: 'DED6F9', description: 'A general task' },
14
+ { name: 'status: waiting-for-triage', color: 'FCF1C4', description: "An issue we've not yet triaged or decided on" },
15
+ { name: 'status: waiting-for-feedback', color: 'FCF1C4', description: 'We need additional information before we can continue' },
16
+ { name: 'status: feedback-provided', color: 'FCF1C4', description: 'Feedback has been provided' },
17
+ { name: 'status: feedback-reminder', color: 'FCF1C4', description: "We've sent a reminder that we need additional information before we can continue" },
18
+ { name: 'status: pending-design-work', color: 'FCF1C4', description: 'Needs design work before any code can be developed' },
19
+ { name: 'status: in-progress', color: 'FCF1C4', description: 'Work is actively being developed' },
20
+ { name: 'status: on-hold', color: 'FCF1C4', description: "We can't start working on this issue yet" },
21
+ { name: 'status: blocked', color: 'FCF1C4', description: "An issue that's blocked on an external project change" },
22
+ { name: 'status: declined', color: 'FCF1C4', description: "A suggestion or change that we don't feel we should currently apply" },
23
+ { name: 'status: duplicate', color: 'FCF1C4', description: 'A duplicate of another issue' },
24
+ { name: 'status: invalid', color: 'FCF1C4', description: "An issue that we don't feel is valid" },
25
+ { name: 'status: superseded', color: 'FCF1C4', description: 'An issue that has been superseded by another' },
26
+ { name: 'status: bulk-closed', color: 'FCF1C4', description: "An outdated, unresolved issue that's closed in bulk as part of a cleaning process" },
27
+ { name: 'status: ideal-for-contribution', color: 'FCF1C4', description: 'An issue that a contributor can help us with' },
28
+ { name: 'status: backported', color: 'FCF1C4', description: 'An issue that has been backported to maintenance branches' },
29
+ { name: 'status: waiting-for-internal-feedback', color: 'FCF1C4', description: 'An issue that needs input from a member or another team' },
30
+ { name: 'good first issue', color: 'F9D9E6', description: 'Good for newcomers' },
31
+ { name: 'help wanted', color: '008672', description: 'Extra attention is needed' },
32
+ { name: 'dependencies', color: '0366D6', description: 'Pull requests that update a dependency file' }
33
+ ];
34
+ function validIdentifiers(value, prerelease) {
35
+ if (!value)
36
+ return false;
37
+ return value.split('.').every((identifier) => /^[0-9A-Za-z-]+$/.test(identifier)
38
+ && (!prerelease || !/^\d+$/.test(identifier) || /^(0|[1-9]\d*)$/.test(identifier)));
39
+ }
40
+ function parseVersionTag(tag) {
41
+ if (!tag.startsWith('v'))
42
+ return null;
43
+ let version = tag.slice(1);
44
+ const plusAt = version.indexOf('+');
45
+ if (plusAt >= 0) {
46
+ const build = version.slice(plusAt + 1);
47
+ if (!validIdentifiers(build, false))
48
+ return null;
49
+ version = version.slice(0, plusAt);
50
+ }
51
+ let prerelease = '';
52
+ const dashAt = version.indexOf('-');
53
+ if (dashAt >= 0) {
54
+ prerelease = version.slice(dashAt + 1);
55
+ if (!validIdentifiers(prerelease, true))
56
+ return null;
57
+ version = version.slice(0, dashAt);
58
+ }
59
+ const core = version.split('.');
60
+ if (core.length !== 3 || !core.every((part) => /^(0|[1-9]\d*)$/.test(part)))
61
+ return null;
62
+ return { tag, major: core[0], minor: core[1], patch: core[2], prerelease };
63
+ }
64
+ function compareDecimal(left, right) {
65
+ if (left.length !== right.length)
66
+ return left.length > right.length ? 1 : -1;
67
+ if (left === right)
68
+ return 0;
69
+ return left > right ? 1 : -1;
70
+ }
71
+ function compareAscii(left, right) {
72
+ const alphabet = '+-.0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
73
+ const limit = Math.min(left.length, right.length);
74
+ for (let index = 0; index < limit; index += 1) {
75
+ const leftRank = alphabet.indexOf(left[index]);
76
+ const rightRank = alphabet.indexOf(right[index]);
77
+ if (leftRank !== rightRank)
78
+ return leftRank > rightRank ? 1 : -1;
79
+ }
80
+ if (left.length === right.length)
81
+ return 0;
82
+ return left.length > right.length ? 1 : -1;
83
+ }
84
+ function comparePrerelease(left, right) {
85
+ if (!left || !right) {
86
+ if (left === right)
87
+ return 0;
88
+ return left ? -1 : 1;
89
+ }
90
+ const leftIds = left.split('.');
91
+ const rightIds = right.split('.');
92
+ const limit = Math.min(leftIds.length, rightIds.length);
93
+ for (let index = 0; index < limit; index += 1) {
94
+ const leftId = leftIds[index];
95
+ const rightId = rightIds[index];
96
+ const leftNumeric = /^\d+$/.test(leftId);
97
+ const rightNumeric = /^\d+$/.test(rightId);
98
+ const comparison = leftNumeric && rightNumeric
99
+ ? compareDecimal(leftId, rightId)
100
+ : leftNumeric !== rightNumeric
101
+ ? leftNumeric ? -1 : 1
102
+ : compareAscii(leftId, rightId);
103
+ if (comparison !== 0)
104
+ return comparison;
105
+ }
106
+ if (leftIds.length === rightIds.length)
107
+ return 0;
108
+ return leftIds.length > rightIds.length ? 1 : -1;
109
+ }
110
+ function compareVersions(left, right) {
111
+ for (const [leftPart, rightPart] of [
112
+ [left.major, right.major],
113
+ [left.minor, right.minor],
114
+ [left.patch, right.patch]
115
+ ]) {
116
+ const comparison = compareDecimal(leftPart, rightPart);
117
+ if (comparison !== 0)
118
+ return comparison;
119
+ }
120
+ const prerelease = comparePrerelease(left.prerelease, right.prerelease);
121
+ return prerelease !== 0 ? prerelease : compareAscii(left.tag, right.tag);
122
+ }
123
+ function incrementDecimal(value) {
124
+ const digits = value.split('');
125
+ let carry = 1;
126
+ for (let index = digits.length - 1; index >= 0 && carry; index -= 1) {
127
+ const next = Number(digits[index]) + carry;
128
+ digits[index] = String(next % 10);
129
+ carry = next === 10 ? 1 : 0;
130
+ }
131
+ return carry ? `1${digits.join('')}` : digits.join('');
132
+ }
133
+ function buildMilestonePlan(tags, history) {
134
+ const parsed = tags.flatMap((tag) => {
135
+ const version = parseVersionTag(tag);
136
+ return version ? [version] : [];
137
+ });
138
+ const selected = parsed.reduce((best, candidate) => !best || compareVersions(candidate, best) > 0 ? candidate : best, null);
139
+ const baseline = selected
140
+ ? { version: `${selected.major}.${selected.minor}.${selected.patch}`, source: `git tag ${selected.tag}` }
141
+ : { version: '0.1.0', source: 'compatibility default' };
142
+ const [major, minor, patch] = baseline.version.split('.');
143
+ const desired = [];
144
+ const titles = new Set();
145
+ const add = (title, description, state) => {
146
+ if (titles.has(title))
147
+ return;
148
+ titles.add(title);
149
+ desired.push({ title, description, state });
150
+ };
151
+ const line = `${major}.${minor}.x`;
152
+ const version = selected
153
+ ? `${major}.${minor}.${incrementDecimal(patch)}`
154
+ : baseline.version;
155
+ add('General Backlog', 'All unsorted backlogged tasks may be completed in a future version.', 'open');
156
+ add(line, `Issues that we want to resolve in ${major}.${minor} line.`, 'open');
157
+ add(version, `Issues that we want to release in v${version}.`, 'open');
158
+ if (patch === '0') {
159
+ const nextMinor = `${major}.${incrementDecimal(minor)}.0`;
160
+ const nextLine = `${major}.${incrementDecimal(minor)}.x`;
161
+ add(nextMinor, `Issues that we want to release in v${nextMinor}.`, 'open');
162
+ add(nextLine, `Issues that we want to resolve in ${major}.${incrementDecimal(minor)} line.`, 'open');
163
+ }
164
+ if (history) {
165
+ for (const historical of parsed) {
166
+ const historicalVersion = `${historical.major}.${historical.minor}.${historical.patch}`;
167
+ add(`${historical.major}.${historical.minor}.x`, `Issues that we want to resolve in ${historical.major}.${historical.minor} line.`, 'open');
168
+ add(historicalVersion, `Issues that we want to release in v${historicalVersion}.`, 'closed');
169
+ }
170
+ }
171
+ return { baseline, history, desired };
172
+ }
173
+ function buildLabelDefinitions(config) {
174
+ const labels = config && typeof config === 'object' && !Array.isArray(config)
175
+ ? config.labels
176
+ : undefined;
177
+ const mapping = labels && typeof labels === 'object' && !Array.isArray(labels)
178
+ ? labels.in
179
+ : undefined;
180
+ if (mapping === undefined)
181
+ return [...COMMON_LABELS];
182
+ if (!mapping || typeof mapping !== 'object' || Array.isArray(mapping))
183
+ throw new Error('labels.in must be an object');
184
+ const dynamic = Object.keys(mapping).sort().map((key) => {
185
+ if (!key || /[\r\n\t]/.test(key))
186
+ throw new Error('labels.in keys must be non-empty single-line values');
187
+ return { name: `in: ${key}`, color: 'BFD4F2', description: `Module label for ${key}` };
188
+ });
189
+ return [...COMMON_LABELS, ...dynamic];
190
+ }
191
+ function configAt(root) {
192
+ try {
193
+ return JSON.parse(fs.readFileSync(path.join(root, '.agents', '.airc.json'), 'utf8'));
194
+ }
195
+ catch {
196
+ return {};
197
+ }
198
+ }
199
+ function baseResult(status, context, extra = {}) {
200
+ return {
201
+ ...platformResult(status, {
202
+ platform: context.platform,
203
+ capabilities: context.capabilities,
204
+ operations: context.operations,
205
+ error: context.error
206
+ }),
207
+ ...extra
208
+ };
209
+ }
210
+ function unsupported(providerType, operation) {
211
+ return {
212
+ code: 'PLATFORM_CAPABILITY_UNSUPPORTED',
213
+ message: `Platform '${providerType}' does not provide ${operation}`,
214
+ retryable: false
215
+ };
216
+ }
217
+ function operationStatus(changed) {
218
+ return changed ? 'applied' : 'no-op';
219
+ }
220
+ function errorStatus(error) {
221
+ return error.code === 'PLATFORM_CAPABILITY_UNSUPPORTED' ? 'degraded' : providerStatus(error);
222
+ }
223
+ function initialLabelsResult(context, labels) {
224
+ return baseResult('degraded', context, {
225
+ operation: 'init-labels',
226
+ labels: {
227
+ commonCount: COMMON_LABELS.length,
228
+ inCount: labels.filter((item) => item.name.startsWith('in:')).length,
229
+ created: [], updated: [], removed: [], skipped: []
230
+ },
231
+ error: context.error || { code: 'PLATFORM_CAPABILITY_UNSUPPORTED', message: 'Label initialization is unavailable', retryable: false }
232
+ });
233
+ }
234
+ function initialMilestonesResult(context, history) {
235
+ return baseResult('degraded', context, {
236
+ operation: 'init-milestones',
237
+ milestones: { baseline: null, history, desired: [], created: [], skipped: [] },
238
+ error: context.error || { code: 'PLATFORM_CAPABILITY_UNSUPPORTED', message: 'Milestone initialization is unavailable', retryable: false }
239
+ });
240
+ }
241
+ function labelsFromReconciliation(initial, value) {
242
+ if (!value)
243
+ return initial.labels;
244
+ return {
245
+ ...initial.labels,
246
+ created: value.created,
247
+ updated: value.updated,
248
+ removed: value.removed,
249
+ skipped: value.skipped
250
+ };
251
+ }
252
+ function milestonesFromInitialization(initial, value) {
253
+ if (!value)
254
+ return initial.milestones;
255
+ return {
256
+ ...initial.milestones,
257
+ created: value.created,
258
+ skipped: value.skipped
259
+ };
260
+ }
261
+ function usableContext(loaded) {
262
+ return loaded.ok && Boolean(loaded.value.context.platform.repository);
263
+ }
264
+ async function initializeLabels(input = {}, options = {}) {
265
+ const loaded = await resolvePlatformProviderContext({ cwd: options.cwd, client: options.client, platformType: options.platformType });
266
+ const context = loaded.ok ? loaded.value.context : loaded.context;
267
+ let definitions;
268
+ try {
269
+ definitions = buildLabelDefinitions(loaded.ok ? configAt(loaded.value.repositoryRoot) : {});
270
+ }
271
+ catch (error) {
272
+ return {
273
+ ...initialLabelsResult(context, []),
274
+ status: 'failed',
275
+ error: { code: 'LABEL_CONFIG_INVALID', message: error instanceof Error ? error.message : String(error), retryable: false }
276
+ };
277
+ }
278
+ const initial = initialLabelsResult(context, definitions);
279
+ if (!usableContext(loaded))
280
+ return { ...initial, error: initial.error || context.error };
281
+ const operation = loaded.value.provider.repositoryMetadata?.reconcileLabels;
282
+ if (!operation)
283
+ return { ...initial, error: unsupported(loaded.value.provider.type, 'repositoryMetadata.reconcileLabels') };
284
+ const reconciled = await operation({
285
+ context: providerOperationContext(loaded.value),
286
+ desired: definitions,
287
+ cleanupStaleIn: input.cleanupStaleIn === true,
288
+ mutation: { idempotencyKey: 'labels:init' }
289
+ });
290
+ if (!reconciled.ok)
291
+ return {
292
+ ...initial,
293
+ status: errorStatus(reconciled.error),
294
+ changed: reconciled.value?.changed || false,
295
+ operations: [{ name: 'reconcile-labels', status: 'failed', reasonCode: reconciled.error.code }],
296
+ labels: labelsFromReconciliation(initial, reconciled.value),
297
+ error: providerError(reconciled.error, 'LABEL_INITIALIZATION_FAILED')
298
+ };
299
+ return {
300
+ ...initial,
301
+ status: operationStatus(reconciled.value.changed),
302
+ changed: reconciled.value.changed,
303
+ operations: [{ name: 'reconcile-labels', status: operationStatus(reconciled.value.changed), reasonCode: null }],
304
+ labels: {
305
+ commonCount: COMMON_LABELS.length,
306
+ inCount: definitions.length - COMMON_LABELS.length,
307
+ created: reconciled.value.created,
308
+ updated: reconciled.value.updated,
309
+ removed: reconciled.value.removed,
310
+ skipped: reconciled.value.skipped
311
+ },
312
+ error: null
313
+ };
314
+ }
315
+ function gitTags(cwd) {
316
+ const output = execFileSync('git', ['tag', '--list', 'v*'], { cwd, encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] }).trim();
317
+ return output ? output.split(/\r?\n/).filter(Boolean) : [];
318
+ }
319
+ async function initializeMilestones(input = {}, options = {}) {
320
+ const history = input.history === true;
321
+ const loaded = await resolvePlatformProviderContext({ cwd: options.cwd, client: options.client, platformType: options.platformType });
322
+ const context = loaded.ok ? loaded.value.context : loaded.context;
323
+ const initial = initialMilestonesResult(context, history);
324
+ if (!usableContext(loaded))
325
+ return initial;
326
+ let plan;
327
+ try {
328
+ plan = buildMilestonePlan(gitTags(loaded.value.workingDirectory), history);
329
+ }
330
+ catch (error) {
331
+ return {
332
+ ...initial,
333
+ status: 'failed',
334
+ error: { code: 'MILESTONE_TAG_DISCOVERY_FAILED', message: error instanceof Error ? error.message : String(error), retryable: false }
335
+ };
336
+ }
337
+ const operation = loaded.value.provider.repositoryMetadata?.reconcileMilestones;
338
+ if (!operation)
339
+ return {
340
+ ...initial,
341
+ milestones: { ...initial.milestones, baseline: plan.baseline, history, desired: plan.desired.map((item) => item.title) },
342
+ error: unsupported(loaded.value.provider.type, 'repositoryMetadata.reconcileMilestones')
343
+ };
344
+ const reconciled = await operation({
345
+ context: providerOperationContext(loaded.value),
346
+ desired: plan.desired,
347
+ mutation: { idempotencyKey: `milestones:init:${plan.baseline.version}:${history}` }
348
+ });
349
+ if (!reconciled.ok)
350
+ return {
351
+ ...initial,
352
+ status: errorStatus(reconciled.error),
353
+ changed: reconciled.value?.changed || false,
354
+ operations: [{ name: 'reconcile-milestones', status: 'failed', reasonCode: reconciled.error.code }],
355
+ milestones: milestonesFromInitialization({
356
+ ...initial,
357
+ milestones: { ...initial.milestones, baseline: plan.baseline, history, desired: plan.desired.map((item) => item.title) }
358
+ }, reconciled.value),
359
+ error: providerError(reconciled.error, 'MILESTONE_INITIALIZATION_FAILED')
360
+ };
361
+ return {
362
+ ...initial,
363
+ status: operationStatus(reconciled.value.changed),
364
+ changed: reconciled.value.changed,
365
+ operations: [{ name: 'reconcile-milestones', status: operationStatus(reconciled.value.changed), reasonCode: null }],
366
+ milestones: {
367
+ baseline: plan.baseline,
368
+ history,
369
+ desired: plan.desired.map((item) => item.title),
370
+ created: reconciled.value.created,
371
+ skipped: reconciled.value.skipped
372
+ },
373
+ error: null
374
+ };
375
+ }
376
+ export { buildLabelDefinitions, buildMilestonePlan, initializeLabels, initializeMilestones };
377
+ //# sourceMappingURL=repository-metadata.js.map
@@ -0,0 +1,155 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { resolvePlatformProviderContext } from './context.js';
4
+ import { providerError, providerOperationContext, providerStatus } from './provider-bridge.js';
5
+ import { platformResult } from './types.js';
6
+ function baseResult(operation, status, context, overrides = {}) {
7
+ return {
8
+ ...platformResult(status, {
9
+ platform: context.platform,
10
+ capabilities: context.capabilities,
11
+ operations: context.operations,
12
+ error: context.error,
13
+ changed: false
14
+ }),
15
+ operation,
16
+ alert: null,
17
+ data: null,
18
+ ...overrides
19
+ };
20
+ }
21
+ function unsupported(providerType) {
22
+ return {
23
+ code: 'PLATFORM_CAPABILITY_UNSUPPORTED',
24
+ message: `Platform '${providerType}' does not provide securityAlerts`,
25
+ retryable: false
26
+ };
27
+ }
28
+ function resultStatus(error) {
29
+ return error.code === 'PLATFORM_CAPABILITY_UNSUPPORTED' ? 'degraded' : providerStatus(error);
30
+ }
31
+ function validKind(value) {
32
+ return value === 'dependabot' || value === 'code-scanning';
33
+ }
34
+ function validNumber(value) {
35
+ return Number.isSafeInteger(value) && value > 0;
36
+ }
37
+ function unavailable(operation, context, error) {
38
+ const status = context.status === 'blocked' || context.status === 'failed' ? context.status : 'degraded';
39
+ return baseResult(operation, status, context, {
40
+ error: error || context.error || { code: 'PLATFORM_CAPABILITY_UNSUPPORTED', message: 'Security alert operations are unavailable', retryable: false }
41
+ });
42
+ }
43
+ async function readSecurityAlert(input, options = {}) {
44
+ if (!validKind(input.kind))
45
+ return baseResult('read', 'failed', platformResult('failed'), {
46
+ error: { code: 'SECURITY_KIND_INVALID', message: 'Alert kind must be dependabot or code-scanning', retryable: false }
47
+ });
48
+ if (!validNumber(input.number))
49
+ return baseResult('read', 'failed', platformResult('failed'), {
50
+ error: { code: 'SECURITY_NUMBER_INVALID', message: 'Alert number must be a positive integer', retryable: false }
51
+ });
52
+ const loaded = await resolvePlatformProviderContext({ cwd: options.cwd, client: options.client, platformType: options.platformType });
53
+ const context = loaded.ok ? loaded.value.context : loaded.context;
54
+ if (!loaded.ok || !context.platform.repository)
55
+ return unavailable('read', context);
56
+ const inspect = loaded.value.provider.securityAlerts?.inspect;
57
+ if (!inspect)
58
+ return unavailable('read', context, unsupported(loaded.value.provider.type));
59
+ const result = await inspect({ context: providerOperationContext(loaded.value), kind: input.kind, number: input.number });
60
+ if (!result.ok)
61
+ return {
62
+ ...baseResult('read', resultStatus(result.error), context),
63
+ error: providerError(result.error, 'SECURITY_ALERT_READ_FAILED')
64
+ };
65
+ return {
66
+ ...baseResult('read', 'applied', context, {
67
+ alert: result.value,
68
+ data: result.value.data,
69
+ operations: [{ name: 'inspect-alert', status: 'applied', reasonCode: null }]
70
+ }),
71
+ changed: false,
72
+ error: null
73
+ };
74
+ }
75
+ async function dismissSecurityAlert(input, options = {}) {
76
+ if (!validKind(input.kind))
77
+ return baseResult('dismiss', 'failed', platformResult('failed'), {
78
+ error: { code: 'SECURITY_KIND_INVALID', message: 'Alert kind must be dependabot or code-scanning', retryable: false }
79
+ });
80
+ if (!validNumber(input.number))
81
+ return baseResult('dismiss', 'failed', platformResult('failed'), {
82
+ error: { code: 'SECURITY_NUMBER_INVALID', message: 'Alert number must be a positive integer', retryable: false }
83
+ });
84
+ if (!input.reason || typeof input.comment !== 'string')
85
+ return baseResult('dismiss', 'failed', platformResult('failed'), {
86
+ error: { code: 'SECURITY_DISMISS_INPUT_INVALID', message: 'Dismissal reason and comment are required', retryable: false }
87
+ });
88
+ const loaded = await resolvePlatformProviderContext({ cwd: options.cwd, client: options.client, platformType: options.platformType });
89
+ const context = loaded.ok ? loaded.value.context : loaded.context;
90
+ if (!loaded.ok || !context.platform.repository)
91
+ return unavailable('dismiss', context);
92
+ const inspect = loaded.value.provider.securityAlerts?.inspect;
93
+ const dismiss = loaded.value.provider.securityAlerts?.dismiss;
94
+ if (!inspect || !dismiss)
95
+ return unavailable('dismiss', context, unsupported(loaded.value.provider.type));
96
+ const current = await inspect({ context: providerOperationContext(loaded.value), kind: input.kind, number: input.number });
97
+ if (!current.ok)
98
+ return {
99
+ ...baseResult('dismiss', resultStatus(current.error), context),
100
+ error: providerError(current.error, 'SECURITY_ALERT_READ_FAILED')
101
+ };
102
+ if (current.value.state === 'dismissed' || current.value.state === 'fixed')
103
+ return {
104
+ ...baseResult('dismiss', 'no-op', context, {
105
+ alert: current.value,
106
+ data: current.value.data,
107
+ operations: [{ name: 'inspect-alert', status: 'no-op', reasonCode: 'ALREADY_CLOSED' }]
108
+ }),
109
+ error: null
110
+ };
111
+ if (current.value.state !== 'open')
112
+ return {
113
+ ...baseResult('dismiss', 'failed', context, {
114
+ alert: current.value,
115
+ data: current.value.data,
116
+ operations: [{ name: 'inspect-alert', status: 'failed', reasonCode: 'INVALID_STATE' }]
117
+ }),
118
+ error: { code: 'SECURITY_RESPONSE_INVALID', message: 'The platform returned an alert without a valid state', retryable: false }
119
+ };
120
+ const changed = await dismiss({
121
+ context: providerOperationContext(loaded.value),
122
+ kind: input.kind,
123
+ number: input.number,
124
+ reason: input.reason,
125
+ comment: input.comment,
126
+ mutation: { idempotencyKey: `security-alert:dismiss:${input.kind}:${input.number}` }
127
+ });
128
+ if (!changed.ok)
129
+ return {
130
+ ...baseResult('dismiss', resultStatus(changed.error), context, {
131
+ alert: current.value,
132
+ data: current.value.data,
133
+ operations: [{ name: 'dismiss-alert', status: 'failed', reasonCode: null }]
134
+ }),
135
+ error: providerError(changed.error, 'SECURITY_ALERT_DISMISS_FAILED')
136
+ };
137
+ return {
138
+ ...baseResult('dismiss', changed.value.changed ? 'applied' : 'no-op', context, {
139
+ operations: [{ name: 'dismiss-alert', status: changed.value.changed ? 'applied' : 'no-op', reasonCode: null }]
140
+ }),
141
+ changed: changed.value.changed,
142
+ error: null
143
+ };
144
+ }
145
+ function readCommentFile(commentFile, cwd = process.cwd()) {
146
+ try {
147
+ const requested = path.resolve(cwd, commentFile);
148
+ return { ok: true, value: fs.readFileSync(requested, 'utf8') };
149
+ }
150
+ catch {
151
+ return { ok: false, error: { code: 'SECURITY_DISMISS_INPUT_INVALID', message: 'Dismissal comment file could not be read', retryable: false } };
152
+ }
153
+ }
154
+ export { dismissSecurityAlert, readCommentFile, readSecurityAlert };
155
+ //# sourceMappingURL=security-alerts.js.map
@@ -6,9 +6,7 @@ import { resolvePlatformProviderContext } from "./context.js";
6
6
  import { hasCheckedRequirement, resolveRequirementSection } from "./issue-metadata.js";
7
7
  import { requirementSectionAnchors } from "./issues.js";
8
8
  import { taskTypeLabel } from "./metadata-labels.js";
9
- import { planInLabelUpdate, validateInLabelMapping, validateRepositoryLabelPayload } from "./in-label-sync.js";
10
- import { inspectGitHubPullRequest } from "./pull-requests.js";
11
- import { createGitHubClient } from "./github-client.js";
9
+ import { planInLabelUpdate, validateInLabelMapping } from "./in-label-sync.js";
12
10
  import { readPrDeliveryFact } from "../task/pr-delivery-fact.js";
13
11
  import { providerError, providerOperationContext, resourceIdentityNumber, unsupportedProviderOperation } from "./provider-bridge.js";
14
12
  import { taskIssueIdentity } from "./task-identities.js";
@@ -174,7 +172,6 @@ async function buildSyncContext({ taskDir, config, artifactFile }) {
174
172
  prIdentity,
175
173
  issueNumber,
176
174
  prNumber,
177
- upstreamRepo: platformContext.platform.repository,
178
175
  repoOwnerType: String(loaded.ok ? loaded.value.snapshot.metadata?.ownerType || "unknown" : "unknown"),
179
176
  hasTriage: platformContext.capabilities.triage,
180
177
  hasPush: platformContext.capabilities.push,
@@ -281,6 +278,28 @@ async function fetchRemoteData(context) {
281
278
  prComments = listed.value.map((comment) => ({ id: comment.id, body: comment.body }));
282
279
  }
283
280
  const changeRequest = facts.value.changeRequest;
281
+ let inLabelMapping = {};
282
+ let repositoryLabels = [];
283
+ if (context.config.verify_in_labels_computed && context.hasTriage) {
284
+ const mapping = loadInLabelMapping();
285
+ if (!mapping.ok)
286
+ return { earlyReturn: failResult(CHECK_TYPE, mapping.message, "check_failed") };
287
+ inLabelMapping = mapping.value;
288
+ if (Object.keys(inLabelMapping).length > 0) {
289
+ const labels = provider?.issues?.listLabels
290
+ ? await provider.issues.listLabels({ context: operationContext })
291
+ : unsupportedProviderOperation(provider, "issues.listLabels");
292
+ if (!labels.ok) {
293
+ const error = providerError(labels.error, "PLATFORM_PROVIDER_OPERATION_FAILED");
294
+ return {
295
+ earlyReturn: error.retryable
296
+ ? blockedResult(CHECK_TYPE, `${error.code}: ${error.message}`, "network_error")
297
+ : failResult(CHECK_TYPE, `${error.code}: ${error.message}`, "check_failed")
298
+ };
299
+ }
300
+ repositoryLabels = labels.value;
301
+ }
302
+ }
284
303
  return {
285
304
  issue,
286
305
  comments: facts.value.comments.map((comment) => ({ id: comment.id, body: comment.body })),
@@ -294,7 +313,9 @@ async function fetchRemoteData(context) {
294
313
  ? (changeRequest.milestone ? { title: changeRequest.milestone } : null)
295
314
  : undefined,
296
315
  prAssignees: changeRequest?.assignees,
297
- prHeadSha: changeRequest?.headSha
316
+ prHeadSha: changeRequest?.headSha,
317
+ inLabelMapping,
318
+ repositoryLabels
298
319
  };
299
320
  }
300
321
  function mapTaskTypeToLabel(taskType) {
@@ -467,7 +488,7 @@ function checkInLabelsComputed(context, remoteData) {
467
488
  if (!context.config.verify_in_labels_computed || !context.hasTriage) {
468
489
  return null;
469
490
  }
470
- const expectedInLabels = computeExpectedInLabels(context.taskDir, context.upstreamRepo);
491
+ const expectedInLabels = computeExpectedInLabels(context.taskDir, remoteData.repositoryLabels, remoteData.inLabelMapping);
471
492
  if (!expectedInLabels.ok) {
472
493
  return expectedInLabels.type === "check_failed"
473
494
  ? failResult(CHECK_TYPE, expectedInLabels.message, expectedInLabels.type)
@@ -767,7 +788,7 @@ function arraysEqual(left, right) {
767
788
  function formatLabelList(labels) {
768
789
  return labels.length > 0 ? labels.join(", ") : "none";
769
790
  }
770
- function computeExpectedInLabels(taskDir, repository) {
791
+ function computeExpectedInLabels(taskDir, repositoryLabels = [], mappingOverride) {
771
792
  const task = loadTask(taskDir);
772
793
  if (!task.ok) {
773
794
  return task;
@@ -784,29 +805,15 @@ function computeExpectedInLabels(taskDir, repository) {
784
805
  .split(/\r?\n/)
785
806
  .map((value) => value.trim())
786
807
  .filter(Boolean);
787
- const mapping = loadInLabelMapping();
788
- if (!mapping.ok) {
789
- return mapping;
790
- }
791
- if (Object.keys(mapping.value ?? {}).length === 0) {
808
+ const mapping = mappingOverride ?? {};
809
+ if (Object.keys(mapping).length === 0) {
792
810
  return { ok: true, labels: [], mode: "mapped" };
793
811
  }
794
- const client = createGitHubClient();
795
- const repoLabelsResult = withRetry(() => client.json([
796
- "api", "--paginate", "--slurp", `repos/${repository}/labels?per_page=100`
797
- ], { cwd: taskDir }));
798
- if (!repoLabelsResult.ok) {
799
- return repoLabelsResult;
800
- }
801
- const repositoryLabels = validateRepositoryLabelPayload(repoLabelsResult.value);
802
- if (!repositoryLabels.ok) {
803
- return { ok: false, type: "check_failed", message: repositoryLabels.error.message };
804
- }
805
812
  const planned = planInLabelUpdate({
806
813
  changedFiles,
807
814
  currentLabels: [],
808
- mapping: mapping.value ?? {},
809
- repositoryLabels: new Set(repositoryLabels.value)
815
+ mapping,
816
+ repositoryLabels: new Set(repositoryLabels)
810
817
  });
811
818
  if (planned.error) {
812
819
  return { ok: false, type: "check_failed", message: planned.error.message };
@@ -829,7 +836,7 @@ function loadInLabelMapping() {
829
836
  return { ok: false, type: "check_failed", message: `Unable to parse .agents/.airc.json: ${error.message}` };
830
837
  }
831
838
  }
832
- // === GitHub API ===
839
+ // === Git working tree ===
833
840
  function gitText(args, cwd) {
834
841
  try {
835
842
  const value = execFileSync("git", args, { cwd, encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] });
@@ -9,6 +9,7 @@ import { readAppliedOverlays, repairSnapshot } from './repair.js';
9
9
  import { collectGitHubBoundary, resolveGitHubRepository } from './sources.js';
10
10
  import { canonicalJsonBytes, createObjectStore, findSnapshot, publishSnapshotV2, verifySnapshot } from './store.js';
11
11
  import { createGitHubClient } from '../platform/github-client.js';
12
+ import { PUBLIC_CLI_ROUTE_SELECTORS } from '../internal/cli-route-inventory.js';
12
13
  const USAGE = `Usage: ai data <command> [options]
13
14
 
14
15
  Commands:
@@ -18,6 +19,7 @@ Commands:
18
19
  repair <snapshot-id> [--root <dir>] [--apply]
19
20
  export <snapshot-id> [--root <dir>] [--repairs none|applied] [--as-of <ISO>] [--output <file|->]
20
21
  `;
22
+ const DATA_OPERATIONS = PUBLIC_CLI_ROUTE_SELECTORS.data;
21
23
  function parseArgs(args, valueOptions, booleanOptions) {
22
24
  const positionals = [];
23
25
  const options = new Map();
@@ -286,6 +288,11 @@ async function cmdData(args) {
286
288
  return command ? 0 : 1;
287
289
  }
288
290
  try {
291
+ if (!DATA_OPERATIONS.includes(command)) {
292
+ process.stderr.write(`Unknown data command: ${command}\n`);
293
+ process.stdout.write(USAGE);
294
+ return 1;
295
+ }
289
296
  switch (command) {
290
297
  case 'capture': return capture(rest);
291
298
  case 'verify': return verify(rest);