infraweaver 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (524) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +244 -0
  3. package/dist/agents/claude.d.ts +113 -0
  4. package/dist/agents/claudePretoolGate.d.ts +137 -0
  5. package/dist/agents/dispatchMetrics.d.ts +77 -0
  6. package/dist/agents/gateServer.d.ts +7 -0
  7. package/dist/agents/index.d.ts +6 -0
  8. package/dist/agents/nativeFsDenies.d.ts +46 -0
  9. package/dist/agents/opencode.d.ts +284 -0
  10. package/dist/agents/opencodePlugin.d.ts +96 -0
  11. package/dist/agents/opencodeShared.d.ts +40 -0
  12. package/dist/agents/postRun.d.ts +126 -0
  13. package/dist/agents/reviewer.d.ts +41 -0
  14. package/dist/agents/sessionLabeler.d.ts +97 -0
  15. package/dist/agents/shared.d.ts +246 -0
  16. package/dist/agents/subagentModels.d.ts +19 -0
  17. package/dist/agents/tokenQuota.d.ts +118 -0
  18. package/dist/agents/writeGateSource.d.ts +22 -0
  19. package/dist/agents/writePolicy.d.ts +79 -0
  20. package/dist/brand.d.ts +67 -0
  21. package/dist/cli.mjs +247846 -0
  22. package/dist/external.d.ts +227 -0
  23. package/dist/i18n/scaffolding.d.ts +59 -0
  24. package/dist/index.d.ts +6 -0
  25. package/dist/index.js +247061 -0
  26. package/dist/internal/index.d.ts +18 -0
  27. package/dist/internal.js +2398 -0
  28. package/dist/lifecycle.d.ts +2 -0
  29. package/dist/main.d.ts +8 -0
  30. package/dist/mcp/arkConfig.d.ts +1 -0
  31. package/dist/mcp/assess.d.ts +157 -0
  32. package/dist/mcp/capabilityContext.d.ts +71 -0
  33. package/dist/mcp/changeSummary.d.ts +52 -0
  34. package/dist/mcp/checkSuite.d.ts +27 -0
  35. package/dist/mcp/checkout.d.ts +92 -0
  36. package/dist/mcp/comment.d.ts +127 -0
  37. package/dist/mcp/commitInfo.d.ts +11 -0
  38. package/dist/mcp/crosswalk.d.ts +178 -0
  39. package/dist/mcp/crosswalkDigest.d.ts +1 -0
  40. package/dist/mcp/cyberEssentials.d.ts +24 -0
  41. package/dist/mcp/dashboard.d.ts +125 -0
  42. package/dist/mcp/dependencies.d.ts +12 -0
  43. package/dist/mcp/frameworks.d.ts +74 -0
  44. package/dist/mcp/geminiSanitizer.d.ts +28 -0
  45. package/dist/mcp/git.d.ts +60 -0
  46. package/dist/mcp/guardrails.d.ts +222 -0
  47. package/dist/mcp/issue.d.ts +20 -0
  48. package/dist/mcp/issueComments.d.ts +11 -0
  49. package/dist/mcp/issueEvents.d.ts +11 -0
  50. package/dist/mcp/issueInfo.d.ts +11 -0
  51. package/dist/mcp/labels.d.ts +14 -0
  52. package/dist/mcp/localContext.d.ts +26 -0
  53. package/dist/mcp/moduleExtraction.d.ts +79 -0
  54. package/dist/mcp/moduleTests.d.ts +106 -0
  55. package/dist/mcp/modules.d.ts +198 -0
  56. package/dist/mcp/output.d.ts +16 -0
  57. package/dist/mcp/pathSafety.d.ts +14 -0
  58. package/dist/mcp/policy.d.ts +50 -0
  59. package/dist/mcp/pr.d.ts +64 -0
  60. package/dist/mcp/prInfo.d.ts +11 -0
  61. package/dist/mcp/providerSchema.d.ts +52 -0
  62. package/dist/mcp/review.d.ts +212 -0
  63. package/dist/mcp/reviewComments.d.ts +245 -0
  64. package/dist/mcp/roots.d.ts +60 -0
  65. package/dist/mcp/scope.d.ts +26 -0
  66. package/dist/mcp/selectMode.d.ts +20 -0
  67. package/dist/mcp/server.d.ts +61 -0
  68. package/dist/mcp/shared.d.ts +62 -0
  69. package/dist/mcp/shell.d.ts +58 -0
  70. package/dist/mcp/staleFix.d.ts +138 -0
  71. package/dist/mcp/terraform/azurePrices.d.ts +8 -0
  72. package/dist/mcp/terraform/baseline.d.ts +57 -0
  73. package/dist/mcp/terraform/concernResult.d.ts +38 -0
  74. package/dist/mcp/terraform/cost.d.ts +55 -0
  75. package/dist/mcp/terraform/cspm.d.ts +80 -0
  76. package/dist/mcp/terraform/currency.d.ts +110 -0
  77. package/dist/mcp/terraform/decisions.d.ts +178 -0
  78. package/dist/mcp/terraform/eidas.d.ts +100 -0
  79. package/dist/mcp/terraform/evidence.d.ts +343 -0
  80. package/dist/mcp/terraform/findings.d.ts +76 -0
  81. package/dist/mcp/terraform/fixMemory.d.ts +81 -0
  82. package/dist/mcp/terraform/governance.d.ts +56 -0
  83. package/dist/mcp/terraform/hcl.d.ts +115 -0
  84. package/dist/mcp/terraform/iacLanguages.d.ts +120 -0
  85. package/dist/mcp/terraform/idleFloor.d.ts +78 -0
  86. package/dist/mcp/terraform/keyless.d.ts +68 -0
  87. package/dist/mcp/terraform/moduleDocs.d.ts +56 -0
  88. package/dist/mcp/terraform/nativeRules.d.ts +38 -0
  89. package/dist/mcp/terraform/nativeScan.d.ts +11 -0
  90. package/dist/mcp/terraform/noiseProfile.d.ts +100 -0
  91. package/dist/mcp/terraform/normalization.d.ts +52 -0
  92. package/dist/mcp/terraform/oscal.d.ts +103 -0
  93. package/dist/mcp/terraform/packs/azurerm-3-4.d.ts +2 -0
  94. package/dist/mcp/terraform/packs/cloudflare-4-5.d.ts +2 -0
  95. package/dist/mcp/terraform/paths.d.ts +28 -0
  96. package/dist/mcp/terraform/plan.d.ts +157 -0
  97. package/dist/mcp/terraform/planDrift.d.ts +199 -0
  98. package/dist/mcp/terraform/policyAuthor.d.ts +191 -0
  99. package/dist/mcp/terraform/prowlerOcsf.d.ts +136 -0
  100. package/dist/mcp/terraform/refactor.d.ts +183 -0
  101. package/dist/mcp/terraform/registry.d.ts +108 -0
  102. package/dist/mcp/terraform/risk.d.ts +41 -0
  103. package/dist/mcp/terraform/scanSession.d.ts +116 -0
  104. package/dist/mcp/terraform/scannerCache.d.ts +15 -0
  105. package/dist/mcp/terraform/scannerEgress.d.ts +72 -0
  106. package/dist/mcp/terraform/scannerJson.d.ts +13 -0
  107. package/dist/mcp/terraform/scanners.d.ts +205 -0
  108. package/dist/mcp/terraform/signing.d.ts +61 -0
  109. package/dist/mcp/terraform/stateLocking.d.ts +18 -0
  110. package/dist/mcp/terraform/subprocess.d.ts +43 -0
  111. package/dist/mcp/terraform/suppressions.d.ts +90 -0
  112. package/dist/mcp/terraform/taxonomy.d.ts +39 -0
  113. package/dist/mcp/terraform/tfquery.d.ts +47 -0
  114. package/dist/mcp/terraform/toolchainIntegrity.d.ts +82 -0
  115. package/dist/mcp/terraform/tools/authorPolicy.d.ts +59 -0
  116. package/dist/mcp/terraform/tools/detectPlanDrift.d.ts +20 -0
  117. package/dist/mcp/terraform/tools/emitCost.d.ts +11 -0
  118. package/dist/mcp/terraform/tools/emitSarif.d.ts +14 -0
  119. package/dist/mcp/terraform/tools/fixMemory.d.ts +19 -0
  120. package/dist/mcp/terraform/tools/infracostDiff.d.ts +22 -0
  121. package/dist/mcp/terraform/tools/ingestExternalFindings.d.ts +21 -0
  122. package/dist/mcp/terraform/tools/moduleLookup.d.ts +14 -0
  123. package/dist/mcp/terraform/tools/moduleSearch.d.ts +23 -0
  124. package/dist/mcp/terraform/tools/plan.d.ts +59 -0
  125. package/dist/mcp/terraform/tools/providerUpgrade.d.ts +55 -0
  126. package/dist/mcp/terraform/tools/readFindings.d.ts +17 -0
  127. package/dist/mcp/terraform/tools/scan.d.ts +114 -0
  128. package/dist/mcp/terraform/tools/validate.d.ts +31 -0
  129. package/dist/mcp/terraform/tools/verifyRemediation.d.ts +31 -0
  130. package/dist/mcp/terraform/tools/versionCurrency.d.ts +5 -0
  131. package/dist/mcp/terraform/tools.d.ts +16 -0
  132. package/dist/mcp/terraform/transformPacks.d.ts +267 -0
  133. package/dist/mcp/terraform/types.d.ts +226 -0
  134. package/dist/mcp/terraform/verification.d.ts +81 -0
  135. package/dist/mcp/terraform/vex.d.ts +115 -0
  136. package/dist/mcp/terraform/writeOnly.d.ts +67 -0
  137. package/dist/mcp/terraform.d.ts +51 -0
  138. package/dist/mcp/terratest.d.ts +85 -0
  139. package/dist/mcp/untrustedContent.d.ts +76 -0
  140. package/dist/mcp/upload.d.ts +8 -0
  141. package/dist/models.d.ts +181 -0
  142. package/dist/modes/address-reviews.d.ts +2 -0
  143. package/dist/modes/assess.d.ts +2 -0
  144. package/dist/modes/build.d.ts +2 -0
  145. package/dist/modes/compliance-audit.d.ts +2 -0
  146. package/dist/modes/cost-optimization.d.ts +2 -0
  147. package/dist/modes/drift-detect.d.ts +2 -0
  148. package/dist/modes/fix.d.ts +2 -0
  149. package/dist/modes/incremental-review.d.ts +2 -0
  150. package/dist/modes/index.d.ts +24 -0
  151. package/dist/modes/modernize-deprecated.d.ts +2 -0
  152. package/dist/modes/plan.d.ts +2 -0
  153. package/dist/modes/policy-gate.d.ts +2 -0
  154. package/dist/modes/prFormats.d.ts +3 -0
  155. package/dist/modes/refactor.d.ts +2 -0
  156. package/dist/modes/refresh-remediation.d.ts +2 -0
  157. package/dist/modes/remediate-and-refactor.d.ts +2 -0
  158. package/dist/modes/remediate.d.ts +2 -0
  159. package/dist/modes/resolve-conflicts.d.ts +2 -0
  160. package/dist/modes/review.d.ts +2 -0
  161. package/dist/modes/summarize-pr.d.ts +2 -0
  162. package/dist/modes/task.d.ts +2 -0
  163. package/dist/modes/terraform-code-review.d.ts +2 -0
  164. package/dist/modes/types.d.ts +9 -0
  165. package/dist/modes/update-dependencies.d.ts +2 -0
  166. package/dist/prep/index.d.ts +7 -0
  167. package/dist/prep/installNodeDependencies.d.ts +2 -0
  168. package/dist/prep/installPythonDependencies.d.ts +2 -0
  169. package/dist/prep/types.d.ts +31 -0
  170. package/dist/reviewQuality.d.ts +107 -0
  171. package/dist/skills/terraform-best-practices/SKILL.md +379 -0
  172. package/dist/toolState.d.ts +123 -0
  173. package/dist/utils/activity.d.ts +40 -0
  174. package/dist/utils/agent.d.ts +47 -0
  175. package/dist/utils/agentHangReport.d.ts +38 -0
  176. package/dist/utils/annotations.d.ts +17 -0
  177. package/dist/utils/apiFetch.d.ts +11 -0
  178. package/dist/utils/apiKeys.d.ts +48 -0
  179. package/dist/utils/apiUrl.d.ts +28 -0
  180. package/dist/utils/assets.d.ts +8 -0
  181. package/dist/utils/baseRefConfig.d.ts +121 -0
  182. package/dist/utils/billingErrors.d.ts +85 -0
  183. package/dist/utils/body.d.ts +34 -0
  184. package/dist/utils/buildInfraweaverFooter.d.ts +29 -0
  185. package/dist/utils/byokFallback.d.ts +85 -0
  186. package/dist/utils/changeImpact.d.ts +101 -0
  187. package/dist/utils/claudeSubscription.d.ts +30 -0
  188. package/dist/utils/cli.d.ts +10 -0
  189. package/dist/utils/cloudClient.d.ts +33 -0
  190. package/dist/utils/cloudFetchCore.d.ts +42 -0
  191. package/dist/utils/cloudReport.d.ts +114 -0
  192. package/dist/utils/codexHome.d.ts +29 -0
  193. package/dist/utils/codexOAuth.d.ts +60 -0
  194. package/dist/utils/diffCoverage.d.ts +63 -0
  195. package/dist/utils/env.d.ts +46 -0
  196. package/dist/utils/errorReport.d.ts +17 -0
  197. package/dist/utils/exitHandler.d.ts +8 -0
  198. package/dist/utils/fixDoubleEscapedString.d.ts +1 -0
  199. package/dist/utils/gitAuth.d.ts +84 -0
  200. package/dist/utils/gitAuthServer.d.ts +24 -0
  201. package/dist/utils/github.d.ts +104 -0
  202. package/dist/utils/globals.d.ts +3 -0
  203. package/dist/utils/infraweaverConfig.d.ts +56 -0
  204. package/dist/utils/install.d.ts +37 -0
  205. package/dist/utils/instructions.d.ts +48 -0
  206. package/dist/utils/keylessOidc.d.ts +28 -0
  207. package/dist/utils/leapingComment.d.ts +11 -0
  208. package/dist/utils/learnings.d.ts +62 -0
  209. package/dist/utils/learningsTruncate.d.ts +25 -0
  210. package/dist/utils/lifecycle.d.ts +57 -0
  211. package/dist/utils/log.d.ts +111 -0
  212. package/dist/utils/modelResolver.d.ts +63 -0
  213. package/dist/utils/moduleFetch.d.ts +39 -0
  214. package/dist/utils/normalizeEnv.d.ts +30 -0
  215. package/dist/utils/openCodeModels.d.ts +11 -0
  216. package/dist/utils/openaiCompatible.d.ts +26 -0
  217. package/dist/utils/opentofuMcp.d.ts +50 -0
  218. package/dist/utils/overrides.d.ts +40 -0
  219. package/dist/utils/packageManager.d.ts +49 -0
  220. package/dist/utils/patchWorkflowRunFields.d.ts +29 -0
  221. package/dist/utils/payload.d.ts +249 -0
  222. package/dist/utils/prSummary.d.ts +61 -0
  223. package/dist/utils/presets.d.ts +36 -0
  224. package/dist/utils/progressComment.d.ts +146 -0
  225. package/dist/utils/providerErrors.d.ts +31 -0
  226. package/dist/utils/proxyModel.d.ts +76 -0
  227. package/dist/utils/rangeDiff.d.ts +51 -0
  228. package/dist/utils/redact.d.ts +33 -0
  229. package/dist/utils/registryAuth.d.ts +52 -0
  230. package/dist/utils/remediationCommand.d.ts +58 -0
  231. package/dist/utils/retry.d.ts +38 -0
  232. package/dist/utils/reviewCleanup.d.ts +14 -0
  233. package/dist/utils/run.d.ts +9 -0
  234. package/dist/utils/runContext.d.ts +112 -0
  235. package/dist/utils/runContextData.d.ts +42 -0
  236. package/dist/utils/runErrorRenderer.d.ts +64 -0
  237. package/dist/utils/runLifecycle.d.ts +89 -0
  238. package/dist/utils/runStartupLog.d.ts +15 -0
  239. package/dist/utils/secrets.d.ts +34 -0
  240. package/dist/utils/setup.d.ts +90 -0
  241. package/dist/utils/shell.d.ts +44 -0
  242. package/dist/utils/skills.d.ts +10 -0
  243. package/dist/utils/subprocess.d.ts +81 -0
  244. package/dist/utils/terraformMcp.d.ts +46 -0
  245. package/dist/utils/time.d.ts +15 -0
  246. package/dist/utils/timer.d.ts +23 -0
  247. package/dist/utils/todoTracking.d.ts +16 -0
  248. package/dist/utils/token.d.ts +52 -0
  249. package/dist/utils/toolLicensing.d.ts +56 -0
  250. package/dist/utils/toolSelection.d.ts +73 -0
  251. package/dist/utils/toon.d.ts +16 -0
  252. package/dist/utils/version.d.ts +2 -0
  253. package/dist/utils/versioning.d.ts +7 -0
  254. package/dist/utils/vertex.d.ts +16 -0
  255. package/dist/utils/workflow.d.ts +13 -0
  256. package/package.json +132 -0
  257. package/src/agents/claude.ts +1588 -0
  258. package/src/agents/claudePretoolGate.ts +286 -0
  259. package/src/agents/dispatchMetrics.ts +162 -0
  260. package/src/agents/gateServer.ts +178 -0
  261. package/src/agents/index.ts +10 -0
  262. package/src/agents/nativeFsDenies.ts +164 -0
  263. package/src/agents/opencode.ts +1600 -0
  264. package/src/agents/opencodePlugin.ts +304 -0
  265. package/src/agents/opencodeShared.ts +134 -0
  266. package/src/agents/postRun.ts +615 -0
  267. package/src/agents/reviewer.ts +134 -0
  268. package/src/agents/sessionLabeler.ts +185 -0
  269. package/src/agents/shared.ts +377 -0
  270. package/src/agents/subagentModels.ts +40 -0
  271. package/src/agents/tokenQuota.ts +203 -0
  272. package/src/agents/writeGateSource.ts +148 -0
  273. package/src/agents/writePolicy.ts +132 -0
  274. package/src/brand.ts +84 -0
  275. package/src/cli.ts +117 -0
  276. package/src/commands/gha.ts +188 -0
  277. package/src/commands/mcp.ts +126 -0
  278. package/src/commands/verifyEvidence.ts +100 -0
  279. package/src/entry.ts +7 -0
  280. package/src/entryPost.ts +109 -0
  281. package/src/external.ts +308 -0
  282. package/src/i18n/scaffolding.ts +191 -0
  283. package/src/index.ts +7 -0
  284. package/src/internal/index.ts +74 -0
  285. package/src/lifecycle.ts +2 -0
  286. package/src/main.ts +815 -0
  287. package/src/mcp/__fixtures__/infraweaver-scratch-pr-49-review-3485940013.json +110 -0
  288. package/src/mcp/__fixtures__/infraweaver-scratch-pr-64-review-3531000326.json +14 -0
  289. package/src/mcp/__fixtures__/infraweaver-test-repo-pr-1.diff.json +67 -0
  290. package/src/mcp/__snapshots__/checkout.test.ts.snap +109 -0
  291. package/src/mcp/__snapshots__/reviewComments.test.ts.snap +71 -0
  292. package/src/mcp/arkConfig.ts +7 -0
  293. package/src/mcp/assess.ts +723 -0
  294. package/src/mcp/capabilityContext.ts +80 -0
  295. package/src/mcp/changeSummary.ts +145 -0
  296. package/src/mcp/checkSuite.ts +261 -0
  297. package/src/mcp/checkout.ts +1015 -0
  298. package/src/mcp/comment.ts +683 -0
  299. package/src/mcp/commitInfo.ts +57 -0
  300. package/src/mcp/crosswalk.ts +1253 -0
  301. package/src/mcp/crosswalkDigest.ts +5 -0
  302. package/src/mcp/cyberEssentials.ts +60 -0
  303. package/src/mcp/dashboard.ts +232 -0
  304. package/src/mcp/dependencies.ts +190 -0
  305. package/src/mcp/frameworks.ts +236 -0
  306. package/src/mcp/geminiSanitizer.ts +212 -0
  307. package/src/mcp/git.ts +1116 -0
  308. package/src/mcp/guardrails.ts +771 -0
  309. package/src/mcp/issue.ts +74 -0
  310. package/src/mcp/issueComments.ts +48 -0
  311. package/src/mcp/issueEvents.ts +100 -0
  312. package/src/mcp/issueInfo.ts +72 -0
  313. package/src/mcp/labels.ts +35 -0
  314. package/src/mcp/localContext.ts +65 -0
  315. package/src/mcp/localServer.ts +217 -0
  316. package/src/mcp/moduleExtraction.ts +368 -0
  317. package/src/mcp/moduleTests.ts +421 -0
  318. package/src/mcp/modules.ts +752 -0
  319. package/src/mcp/output.ts +71 -0
  320. package/src/mcp/pathSafety.ts +28 -0
  321. package/src/mcp/policy.ts +226 -0
  322. package/src/mcp/pr.ts +238 -0
  323. package/src/mcp/prInfo.ts +91 -0
  324. package/src/mcp/providerSchema.ts +175 -0
  325. package/src/mcp/review.ts +1081 -0
  326. package/src/mcp/reviewComments.ts +1137 -0
  327. package/src/mcp/roots.ts +217 -0
  328. package/src/mcp/scope.ts +82 -0
  329. package/src/mcp/selectMode.ts +207 -0
  330. package/src/mcp/server.ts +498 -0
  331. package/src/mcp/shared.ts +120 -0
  332. package/src/mcp/shell.ts +631 -0
  333. package/src/mcp/staleFix.ts +557 -0
  334. package/src/mcp/terraform/__snapshots__/moduleDocs.test.ts.snap +40 -0
  335. package/src/mcp/terraform/azurePrices.ts +38 -0
  336. package/src/mcp/terraform/baseline.ts +172 -0
  337. package/src/mcp/terraform/concernResult.ts +111 -0
  338. package/src/mcp/terraform/cost.ts +175 -0
  339. package/src/mcp/terraform/cspm.ts +326 -0
  340. package/src/mcp/terraform/currency.ts +354 -0
  341. package/src/mcp/terraform/decisions.ts +590 -0
  342. package/src/mcp/terraform/eidas.ts +134 -0
  343. package/src/mcp/terraform/evidence.ts +771 -0
  344. package/src/mcp/terraform/findings.ts +383 -0
  345. package/src/mcp/terraform/fixMemory.ts +253 -0
  346. package/src/mcp/terraform/governance.ts +231 -0
  347. package/src/mcp/terraform/hcl.ts +273 -0
  348. package/src/mcp/terraform/iacLanguages.ts +431 -0
  349. package/src/mcp/terraform/idleFloor.ts +304 -0
  350. package/src/mcp/terraform/keyless.ts +123 -0
  351. package/src/mcp/terraform/moduleDocs.ts +303 -0
  352. package/src/mcp/terraform/nativeRules.ts +152 -0
  353. package/src/mcp/terraform/nativeScan.ts +67 -0
  354. package/src/mcp/terraform/noiseProfile.ts +183 -0
  355. package/src/mcp/terraform/normalization.ts +265 -0
  356. package/src/mcp/terraform/oscal.ts +283 -0
  357. package/src/mcp/terraform/packs/azurerm-3-4.ts +382 -0
  358. package/src/mcp/terraform/packs/cloudflare-4-5.ts +114 -0
  359. package/src/mcp/terraform/paths.ts +59 -0
  360. package/src/mcp/terraform/plan.ts +377 -0
  361. package/src/mcp/terraform/planDrift.ts +520 -0
  362. package/src/mcp/terraform/policyAuthor.ts +886 -0
  363. package/src/mcp/terraform/prowlerOcsf.ts +380 -0
  364. package/src/mcp/terraform/refactor.ts +879 -0
  365. package/src/mcp/terraform/registry.ts +317 -0
  366. package/src/mcp/terraform/risk.ts +99 -0
  367. package/src/mcp/terraform/scanSession.ts +242 -0
  368. package/src/mcp/terraform/scannerCache.ts +86 -0
  369. package/src/mcp/terraform/scannerEgress.ts +141 -0
  370. package/src/mcp/terraform/scannerJson.ts +22 -0
  371. package/src/mcp/terraform/scanners.ts +1134 -0
  372. package/src/mcp/terraform/signing.ts +151 -0
  373. package/src/mcp/terraform/stateLocking.ts +106 -0
  374. package/src/mcp/terraform/subprocess.ts +102 -0
  375. package/src/mcp/terraform/suppressions.ts +551 -0
  376. package/src/mcp/terraform/taxonomy.ts +0 -0
  377. package/src/mcp/terraform/tfquery.ts +159 -0
  378. package/src/mcp/terraform/toolchainIntegrity.ts +151 -0
  379. package/src/mcp/terraform/tools/authorPolicy.ts +185 -0
  380. package/src/mcp/terraform/tools/detectPlanDrift.ts +279 -0
  381. package/src/mcp/terraform/tools/emitCost.ts +130 -0
  382. package/src/mcp/terraform/tools/emitSarif.ts +108 -0
  383. package/src/mcp/terraform/tools/fixMemory.ts +82 -0
  384. package/src/mcp/terraform/tools/infracostDiff.ts +110 -0
  385. package/src/mcp/terraform/tools/ingestExternalFindings.ts +259 -0
  386. package/src/mcp/terraform/tools/moduleLookup.ts +86 -0
  387. package/src/mcp/terraform/tools/moduleSearch.ts +46 -0
  388. package/src/mcp/terraform/tools/plan.ts +384 -0
  389. package/src/mcp/terraform/tools/providerUpgrade.ts +217 -0
  390. package/src/mcp/terraform/tools/readFindings.ts +138 -0
  391. package/src/mcp/terraform/tools/scan.ts +303 -0
  392. package/src/mcp/terraform/tools/validate.ts +160 -0
  393. package/src/mcp/terraform/tools/verifyRemediation.ts +190 -0
  394. package/src/mcp/terraform/tools/versionCurrency.ts +64 -0
  395. package/src/mcp/terraform/tools.ts +62 -0
  396. package/src/mcp/terraform/transformPacks.ts +594 -0
  397. package/src/mcp/terraform/types.ts +382 -0
  398. package/src/mcp/terraform/verification.ts +150 -0
  399. package/src/mcp/terraform/vex.ts +367 -0
  400. package/src/mcp/terraform/writeOnly.ts +252 -0
  401. package/src/mcp/terraform.ts +52 -0
  402. package/src/mcp/terratest.ts +215 -0
  403. package/src/mcp/untrustedContent.ts +126 -0
  404. package/src/mcp/upload.ts +123 -0
  405. package/src/models.ts +753 -0
  406. package/src/modes/address-reviews.ts +35 -0
  407. package/src/modes/assess.ts +27 -0
  408. package/src/modes/build.ts +86 -0
  409. package/src/modes/compliance-audit.ts +25 -0
  410. package/src/modes/cost-optimization.ts +36 -0
  411. package/src/modes/drift-detect.ts +31 -0
  412. package/src/modes/fix.ts +29 -0
  413. package/src/modes/incremental-review.ts +103 -0
  414. package/src/modes/index.ts +94 -0
  415. package/src/modes/modernize-deprecated.ts +36 -0
  416. package/src/modes/plan.ts +21 -0
  417. package/src/modes/policy-gate.ts +27 -0
  418. package/src/modes/prFormats.ts +325 -0
  419. package/src/modes/refactor.ts +74 -0
  420. package/src/modes/refresh-remediation.ts +40 -0
  421. package/src/modes/remediate-and-refactor.ts +42 -0
  422. package/src/modes/remediate.ts +78 -0
  423. package/src/modes/resolve-conflicts.ts +32 -0
  424. package/src/modes/review.ts +135 -0
  425. package/src/modes/summarize-pr.ts +42 -0
  426. package/src/modes/task.ts +26 -0
  427. package/src/modes/terraform-code-review.ts +68 -0
  428. package/src/modes/types.ts +17 -0
  429. package/src/modes/update-dependencies.ts +44 -0
  430. package/src/prep/index.ts +96 -0
  431. package/src/prep/installNodeDependencies.ts +251 -0
  432. package/src/prep/installPythonDependencies.ts +235 -0
  433. package/src/prep/types.ts +38 -0
  434. package/src/reviewQuality.ts +237 -0
  435. package/src/runCli.ts +335 -0
  436. package/src/skills/terraform-best-practices/SKILL.md +379 -0
  437. package/src/toolState.ts +241 -0
  438. package/src/utils/activity.ts +210 -0
  439. package/src/utils/agent.ts +245 -0
  440. package/src/utils/agentHangReport.ts +180 -0
  441. package/src/utils/annotations.ts +56 -0
  442. package/src/utils/apiFetch.ts +19 -0
  443. package/src/utils/apiKeys.ts +305 -0
  444. package/src/utils/apiUrl.ts +42 -0
  445. package/src/utils/assets.ts +123 -0
  446. package/src/utils/baseRefConfig.ts +254 -0
  447. package/src/utils/billingErrors.ts +210 -0
  448. package/src/utils/body.ts +168 -0
  449. package/src/utils/buildInfraweaverFooter.ts +104 -0
  450. package/src/utils/byokFallback.ts +137 -0
  451. package/src/utils/changeImpact.ts +330 -0
  452. package/src/utils/claudeSubscription.ts +93 -0
  453. package/src/utils/cli.ts +36 -0
  454. package/src/utils/cloudClient.ts +57 -0
  455. package/src/utils/cloudFetchCore.ts +139 -0
  456. package/src/utils/cloudReport.ts +316 -0
  457. package/src/utils/codexHome.ts +204 -0
  458. package/src/utils/codexOAuth.ts +154 -0
  459. package/src/utils/codexRefreshDetect.ts +36 -0
  460. package/src/utils/diffCoverage.ts +404 -0
  461. package/src/utils/env.ts +84 -0
  462. package/src/utils/errorReport.ts +94 -0
  463. package/src/utils/exitHandler.ts +35 -0
  464. package/src/utils/fixDoubleEscapedString.ts +9 -0
  465. package/src/utils/ghaCore.ts +13 -0
  466. package/src/utils/gitAuth.ts +268 -0
  467. package/src/utils/gitAuthServer.ts +186 -0
  468. package/src/utils/github.ts +676 -0
  469. package/src/utils/globals.ts +9 -0
  470. package/src/utils/humanEditCapture.ts +198 -0
  471. package/src/utils/infraweaverConfig.ts +202 -0
  472. package/src/utils/install.ts +279 -0
  473. package/src/utils/instructions.ts +640 -0
  474. package/src/utils/keylessOidc.ts +47 -0
  475. package/src/utils/leapingComment.ts +20 -0
  476. package/src/utils/learnings.ts +145 -0
  477. package/src/utils/learningsTruncate.ts +42 -0
  478. package/src/utils/lifecycle.ts +198 -0
  479. package/src/utils/log.ts +452 -0
  480. package/src/utils/modelResolver.ts +105 -0
  481. package/src/utils/moduleFetch.ts +138 -0
  482. package/src/utils/normalizeEnv.ts +106 -0
  483. package/src/utils/openCodeModels.ts +86 -0
  484. package/src/utils/openaiCompatible.ts +52 -0
  485. package/src/utils/opentofuMcp.ts +61 -0
  486. package/src/utils/overrides.ts +100 -0
  487. package/src/utils/packageManager.ts +257 -0
  488. package/src/utils/patchWorkflowRunFields.ts +173 -0
  489. package/src/utils/payload.ts +1206 -0
  490. package/src/utils/prSummary.ts +148 -0
  491. package/src/utils/presets.ts +105 -0
  492. package/src/utils/progressComment.ts +266 -0
  493. package/src/utils/providerErrors.ts +189 -0
  494. package/src/utils/proxyModel.ts +239 -0
  495. package/src/utils/rangeDiff.ts +182 -0
  496. package/src/utils/redact.ts +86 -0
  497. package/src/utils/registryAuth.ts +115 -0
  498. package/src/utils/remediationCommand.ts +144 -0
  499. package/src/utils/retry.ts +180 -0
  500. package/src/utils/reviewCleanup.ts +116 -0
  501. package/src/utils/run.ts +100 -0
  502. package/src/utils/runContext.ts +276 -0
  503. package/src/utils/runContextData.ts +121 -0
  504. package/src/utils/runErrorRenderer.ts +282 -0
  505. package/src/utils/runFixture.ts +91 -0
  506. package/src/utils/runLifecycle.ts +360 -0
  507. package/src/utils/runStartupLog.ts +67 -0
  508. package/src/utils/secrets.ts +208 -0
  509. package/src/utils/setup.ts +368 -0
  510. package/src/utils/shell.ts +132 -0
  511. package/src/utils/skills.ts +67 -0
  512. package/src/utils/subprocess.ts +474 -0
  513. package/src/utils/terraformMcp.ts +99 -0
  514. package/src/utils/time.ts +59 -0
  515. package/src/utils/timer.ts +72 -0
  516. package/src/utils/todoTracking.ts +168 -0
  517. package/src/utils/token.ts +294 -0
  518. package/src/utils/toolLicensing.ts +152 -0
  519. package/src/utils/toolSelection.ts +239 -0
  520. package/src/utils/toon.ts +76 -0
  521. package/src/utils/version.ts +10 -0
  522. package/src/utils/versioning.ts +44 -0
  523. package/src/utils/vertex.ts +94 -0
  524. package/src/utils/workflow.ts +25 -0
@@ -0,0 +1,104 @@
1
+ import { PRODUCT_NAME } from "#app/brand";
2
+ import { chromeStrings } from "#app/i18n/scaffolding";
3
+ import {
4
+ getModelProvider,
5
+ modelAliases,
6
+ providers,
7
+ resolveDisplayAlias,
8
+ } from "#app/models";
9
+ import type { UserFacingLanguage } from "#app/utils/payload";
10
+
11
+ export const INFRAWEAVER_DIVIDER =
12
+ "<!-- INFRAWEAVER_DIVIDER_DO_NOT_REMOVE_PLZ -->";
13
+
14
+ export interface BuildInfraweaverFooterParams {
15
+ /** add "via Infraweaver" link */
16
+ triggeredBy?: boolean;
17
+ /** arbitrary custom parts (e.g., action links) */
18
+ customParts?: string[] | undefined;
19
+ /** model slug from payload (e.g., "anthropic/claude-opus"). shown in footer as "Using `Model Name`" */
20
+ model?: string | undefined;
21
+ /**
22
+ * When the action engaged the BYOK fallback, this is the slug the user
23
+ * had configured (e.g. "anthropic/claude-opus") — the footer renders
24
+ * `Using <free model> (credentials for <configured> not configured)`
25
+ * so the substitution is visible in PR comments + reviews.
26
+ */
27
+ fallbackFrom?: string | undefined;
28
+ /** user-facing language for the fixed "Using" verb (default English); the
29
+ * model name + product attribution stay verbatim regardless. */
30
+ language?: UserFacingLanguage | undefined;
31
+ }
32
+
33
+ /** Provider display name (e.g. "Anthropic") for the slug, or the raw provider segment as a fallback. */
34
+ function providerDisplayName(slug: string): string {
35
+ try {
36
+ const key = getModelProvider(slug);
37
+ const meta = providers[key as keyof typeof providers];
38
+ return meta?.displayName ?? key;
39
+ } catch {
40
+ // raw IDs without a `/` (Bedrock model IDs) — never reach this function
41
+ // in practice because the BYOK fallback skips Bedrock, but defensively
42
+ // return the slug itself rather than throw if it ever does.
43
+ return slug;
44
+ }
45
+ }
46
+
47
+ function formatModelLabel(params: {
48
+ model: string;
49
+ fallbackFrom?: string | undefined;
50
+ }): string {
51
+ const alias =
52
+ resolveDisplayAlias(params.model) ??
53
+ // reverse-lookup: when the caller passes an effective model (proxy or
54
+ // resolved target like "openrouter/anthropic/claude-opus-4.7") instead of
55
+ // a stored alias slug, find the alias whose resolve target matches so we
56
+ // still render a friendly display name.
57
+ modelAliases.find(
58
+ (a) => a.resolve === params.model || a.openRouterResolve === params.model,
59
+ );
60
+ const displayName = alias?.displayName ?? params.model;
61
+ const base = alias?.isFree
62
+ ? `\`${displayName}\` (free)`
63
+ : `\`${displayName}\``;
64
+ if (!params.fallbackFrom) return base;
65
+ return `${base} (credentials for ${providerDisplayName(params.fallbackFrom)} not configured)`;
66
+ }
67
+
68
+ /**
69
+ * build a infraweaver footer with configurable parts
70
+ * order: action links (customParts) > model > attribution
71
+ */
72
+ export function buildInfraweaverFooter(
73
+ params: BuildInfraweaverFooterParams,
74
+ ): string {
75
+ const parts: string[] = [];
76
+
77
+ if (params.customParts) {
78
+ parts.push(...params.customParts);
79
+ }
80
+
81
+ if (params.triggeredBy) {
82
+ parts.push(`via ${PRODUCT_NAME}`);
83
+ }
84
+
85
+ if (params.model) {
86
+ const using = chromeStrings(params.language).using;
87
+ parts.push(
88
+ `${using} ${formatModelLabel({ model: params.model, fallbackFrom: params.fallbackFrom })}`,
89
+ );
90
+ }
91
+
92
+ return `\n\n${INFRAWEAVER_DIVIDER}\n<sup>${parts.join(" | ")}</sup>`;
93
+ }
94
+
95
+ /**
96
+ * strip any existing infraweaver footer from a comment body
97
+ */
98
+ export function stripExistingFooter(body: string): string {
99
+ const dividerIndex = body.indexOf(INFRAWEAVER_DIVIDER);
100
+ if (dividerIndex === -1) {
101
+ return body;
102
+ }
103
+ return body.substring(0, dividerIndex).trimEnd();
104
+ }
@@ -0,0 +1,137 @@
1
+ import { PRODUCT_NAME } from "#app/brand";
2
+ import type { AgentId } from "#app/external";
3
+ import { getModelEnvVars, getProviderDisplayName } from "#app/models";
4
+ import { hasEnvVar } from "#app/utils/env";
5
+ import { isOpenAiCompatibleRoute } from "#app/utils/openaiCompatible";
6
+
7
+ /**
8
+ * Slug we fall back to when a BYOK-required model is configured but the
9
+ * runner has no provider key in env. Picked because it's free, stable, and
10
+ * currently served by OpenCode Zen without a key.
11
+ *
12
+ * The slug is intentionally hard-coded and not a config knob — the
13
+ * fallback is a safety net, not a user-facing preference, and adding a
14
+ * config surface here would just push the same "what to fall back to"
15
+ * decision into another setting that goes stale the same way.
16
+ */
17
+ export const FREE_FALLBACK_SLUG = "opencode/big-pickle";
18
+
19
+ /**
20
+ * Outcome of the BYOK model gate.
21
+ *
22
+ * - `use-resolved`: run the configured model as-is.
23
+ * - `fallback`: the runner has NO provider key for this model's provider,
24
+ * so swap to the free OpenCode slug — a genuine no-key safety net.
25
+ * - `unavailable`: a provider key IS present but the configured model is
26
+ * not one OpenCode can route with it. This is almost always a wrong or
27
+ * mistyped model id (or a key scoped to other models). We must NOT
28
+ * silently downgrade to the free model — that hides the misconfiguration
29
+ * and produces a free run the user didn't ask for (see PR #2). The caller
30
+ * fails loudly with the authorized-model list instead.
31
+ */
32
+ export type FallbackDecision =
33
+ | { kind: "use-resolved" }
34
+ | { kind: "fallback"; from: string; to: string }
35
+ | { kind: "unavailable"; model: string };
36
+
37
+ /**
38
+ * Does the runner have a provider credential for `resolvedModel`'s provider?
39
+ *
40
+ * Uses the provider→envVars map in `models.ts` (provider-level, since a
41
+ * resolved specifier like `google/gemini-3.5-flash-lite` won't match a catalog
42
+ * model key and correctly falls through to the provider's env vars). A present
43
+ * key is what distinguishes the two not-authorized situations: key present =
44
+ * wrong/unavailable model id (fail loudly); no key = genuine BYOK gap (free
45
+ * fallback).
46
+ */
47
+ export function hasProviderKeyForModel(resolvedModel: string): boolean {
48
+ return getModelEnvVars(resolvedModel).some(hasEnvVar);
49
+ }
50
+
51
+ /**
52
+ * Decide whether to run the configured model, fall back to the free model, or
53
+ * fail because the model is unavailable to the present key.
54
+ *
55
+ * `authorized` is OpenCode's authoritative "what can I route right now"
56
+ * snapshot, captured after Codex auth.json is in place.
57
+ *
58
+ * Skip cases (always `use-resolved`, without consulting `authorized`):
59
+ * - No resolved model: auto-select handles it downstream.
60
+ * - Resolved model is the free fallback already.
61
+ * - Resolved model is a raw Bedrock / Vertex ID (no `/`): the routing
62
+ * validators (`validateBedrockSetup` / `validateVertexSetup`) cover
63
+ * auth + region/location/model-id; `opencode models` does not.
64
+ * - The selected agent is `claude`: the Claude Code harness brings its own
65
+ * auth and `resolveAgent` only returns it when that auth is present.
66
+ * `opencode models` can't see `CLAUDE_CODE_OAUTH_TOKEN`, so without this
67
+ * an OAuth-subscription run on an Anthropic model would land in
68
+ * `unavailable` (the token counts as a present provider key) and fail a
69
+ * run the claude harness serves fine. `validateAgentApiKey` still covers
70
+ * the claude path with its own Anthropic auth check.
71
+ */
72
+ export function selectFallbackModelIfNeeded(input: {
73
+ resolvedModel: string | undefined;
74
+ authorized: Set<string>;
75
+ /** whether a provider key for the resolved model's provider is present in env */
76
+ providerKeyPresent: boolean;
77
+ /** which agent harness `resolveAgent` picks for the resolved model */
78
+ agentName: AgentId;
79
+ }): FallbackDecision {
80
+ if (!input.resolvedModel) return { kind: "use-resolved" };
81
+ if (input.resolvedModel === FREE_FALLBACK_SLUG)
82
+ return { kind: "use-resolved" };
83
+ if (!input.resolvedModel.includes("/")) return { kind: "use-resolved" };
84
+ // OpenAI-compatible BYOK ids can contain a `/` (e.g. a HuggingFace `org/model`),
85
+ // so the no-slash skip above misses them. `validateOpenAiCompatibleSetup` covers
86
+ // base-URL/model-id presence; `opencode models` can't see a custom endpoint.
87
+ if (isOpenAiCompatibleRoute(input.resolvedModel))
88
+ return { kind: "use-resolved" };
89
+ if (input.agentName === "claude") return { kind: "use-resolved" };
90
+ if (input.authorized.has(input.resolvedModel))
91
+ return { kind: "use-resolved" };
92
+
93
+ // resolved model is NOT in OpenCode's authorized set. split the two cases:
94
+ if (input.providerKeyPresent) {
95
+ // a key for this provider is configured — the model id is wrong or not
96
+ // available to this key. surface it; do not silently serve a free model.
97
+ return { kind: "unavailable", model: input.resolvedModel };
98
+ }
99
+ // no key at all → genuine BYOK gap → free safety net so the run still works.
100
+ return {
101
+ kind: "fallback",
102
+ from: input.resolvedModel,
103
+ to: FREE_FALLBACK_SLUG,
104
+ };
105
+ }
106
+
107
+ /**
108
+ * Loud, actionable error for the `unavailable` decision: a provider key is
109
+ * present but the configured model isn't one the key can serve. Lists the
110
+ * models OpenCode CAN route (same-provider first) so the user can copy a valid
111
+ * slug instead of getting a silent free downgrade.
112
+ */
113
+ export function buildUnavailableModelError(input: {
114
+ model: string;
115
+ authorized: Set<string>;
116
+ }): string {
117
+ const provider = input.model.slice(0, input.model.indexOf("/"));
118
+ const providerLabel = getProviderDisplayName(input.model) ?? provider;
119
+ const all = [...input.authorized].sort();
120
+ const sameProvider = all.filter((m) => m.startsWith(`${provider}/`));
121
+ const shown = sameProvider.length > 0 ? sameProvider : all;
122
+ const list =
123
+ shown.length > 0
124
+ ? shown.map((m) => ` - ${m}`).join("\n")
125
+ : " (none — your key does not authorize any model OpenCode can route)";
126
+
127
+ return [
128
+ `model "${input.model}" is not available to your ${providerLabel} key.`,
129
+ ``,
130
+ `A provider credential is present, so ${PRODUCT_NAME} did not fall back to the free model —`,
131
+ `it surfaces this instead so you can pick a valid id. Models your key can serve:`,
132
+ ``,
133
+ list,
134
+ ``,
135
+ `Set the model (the \`model\` input, or \`INFRAWEAVER_MODEL\`) to one of the slugs above.`,
136
+ ].join("\n");
137
+ }
@@ -0,0 +1,330 @@
1
+ import { writeFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { log } from "#app/utils/cli";
4
+ import { $ } from "#app/utils/shell";
5
+
6
+ /**
7
+ * Deterministic change-impact leads for Terraform review.
8
+ *
9
+ * On `checkout_pr` we hand the reviewer a supplemental, EXPLICITLY INCOMPLETE
10
+ * list of where the PR's changed HCL declarations are referenced elsewhere in
11
+ * the repo — the blast radius the raw diff alone doesn't show. It is a lead
12
+ * list, never a coverage source: an empty artifact is not evidence of no wider
13
+ * impact, and the authoritative diff read still governs the review.
14
+ *
15
+ * IaC-specific (vs. pullfrog's generic symbol grep): we extract only unambiguous
16
+ * HCL declarations from the diff and grep their canonical REFERENCE form, so the
17
+ * hits are real dependency edges rather than incidental identifier collisions:
18
+ * variable "x" → `var.x`
19
+ * output "x" → `module.<caller>.x` (regex; same-repo callers only)
20
+ * resource "t" "n" → `t.n`
21
+ * data "t" "n" → `data.t.n`
22
+ * module "x" → `module.x`
23
+ *
24
+ * All greps are word-bounded (`git grep -w`) so `var.env` doesn't match
25
+ * `var.environment` and `aws_s3_bucket.this` doesn't match `..._this_backup`.
26
+ *
27
+ * The whole thing is best-effort: any failure degrades to "no artifact", never
28
+ * a failed checkout.
29
+ */
30
+
31
+ export type ChangeKind = "added" | "removed";
32
+
33
+ export interface ChangeLocation {
34
+ path: string;
35
+ line: number;
36
+ kind: ChangeKind;
37
+ }
38
+
39
+ export interface ChangeImpactAtom {
40
+ /** the declaration kind, for the artifact heading. */
41
+ declaration: "variable" | "output" | "resource" | "data" | "module";
42
+ /** human label, e.g. `var.instance_type` or `aws_s3_bucket.this`. */
43
+ label: string;
44
+ /** the pattern grepped (word-bounded) across `*.tf` / `*.tfvars`; also the dedup key. */
45
+ ref: string;
46
+ /** when true, `ref` is a POSIX regex (`git grep -E`); otherwise a fixed string (`-F`). */
47
+ regex?: boolean;
48
+ /** where in the diff this declaration was touched (first added or removed). */
49
+ where: ChangeLocation;
50
+ }
51
+
52
+ export interface GrepHit {
53
+ path: string;
54
+ line: number;
55
+ text: string;
56
+ }
57
+
58
+ /** a changed file, as returned by the GitHub pulls.listFiles API. */
59
+ export interface ChangedFile {
60
+ filename: string;
61
+ patch?: string | undefined;
62
+ previous_filename?: string | undefined;
63
+ }
64
+
65
+ // bounds — a supplemental artifact must stay small enough to skim.
66
+ const MAX_ATOMS = 20;
67
+ const MAX_REFS_PER_ATOM = 6;
68
+ const MAX_EXCERPT = 160;
69
+ const MAX_BYTES = 16_000;
70
+
71
+ const TF_DECL_PATTERNS: Array<{
72
+ declaration: ChangeImpactAtom["declaration"];
73
+ re: RegExp;
74
+ build: (m: RegExpMatchArray) => { label: string; ref: string; regex?: boolean };
75
+ }> = [
76
+ {
77
+ declaration: "resource",
78
+ re: /^resource\s+"([A-Za-z0-9_]+)"\s+"([A-Za-z0-9_-]+)"/,
79
+ build: (m) => ({ label: `${m[1]}.${m[2]}`, ref: `${m[1]}.${m[2]}` }),
80
+ },
81
+ {
82
+ declaration: "data",
83
+ re: /^data\s+"([A-Za-z0-9_]+)"\s+"([A-Za-z0-9_-]+)"/,
84
+ build: (m) => ({ label: `data.${m[1]}.${m[2]}`, ref: `data.${m[1]}.${m[2]}` }),
85
+ },
86
+ {
87
+ declaration: "variable",
88
+ re: /^variable\s+"([A-Za-z0-9_-]+)"/,
89
+ build: (m) => ({ label: `var.${m[1]}`, ref: `var.${m[1]}` }),
90
+ },
91
+ {
92
+ declaration: "module",
93
+ re: /^module\s+"([A-Za-z0-9_-]+)"/,
94
+ build: (m) => ({ label: `module.${m[1]}`, ref: `module.${m[1]}` }),
95
+ },
96
+ {
97
+ declaration: "output",
98
+ re: /^output\s+"([A-Za-z0-9_-]+)"/,
99
+ // an output is consumed as `module.<caller>.<name>`; grep that access form as
100
+ // a regex rather than the bare `.<name>`, which would match every attribute
101
+ // access named `<name>` in the tree (for common names like `id`/`arn` that is
102
+ // pure noise). Precise within a monorepo whose callers live in the same
103
+ // checkout; cross-repo consumers are invisible to a single-repo grep either way.
104
+ build: (m) => ({
105
+ label: `output.${m[1]}`,
106
+ ref: `module\\.[A-Za-z0-9_-]+\\.${m[1]}`,
107
+ regex: true,
108
+ }),
109
+ },
110
+ ];
111
+
112
+ /** parse one declaration out of a source line, or null. */
113
+ function declarationOf(
114
+ line: string,
115
+ ): { declaration: ChangeImpactAtom["declaration"]; label: string; ref: string; regex?: boolean } | null {
116
+ const trimmed = line.trimStart();
117
+ for (const p of TF_DECL_PATTERNS) {
118
+ const m = trimmed.match(p.re);
119
+ if (m) return { declaration: p.declaration, ...p.build(m) };
120
+ }
121
+ return null;
122
+ }
123
+
124
+ const locationKey = (path: string, line: number): string => `${path} ${line}`;
125
+
126
+ /**
127
+ * Walk each changed file's unified-diff patch, pulling out the HCL declarations
128
+ * that were added or removed and the set of added-line keys (so grep hits that
129
+ * land on the PR's own new lines can be excluded — impact means EXISTING code
130
+ * that references the changed thing).
131
+ */
132
+ export function extractTerraformAtoms(files: ChangedFile[]): {
133
+ atoms: ChangeImpactAtom[];
134
+ addedLines: Set<string>;
135
+ } {
136
+ const byRef = new Map<string, ChangeImpactAtom>();
137
+ const addedLines = new Set<string>();
138
+
139
+ for (const file of files) {
140
+ if (!file.patch) continue;
141
+ // only Terraform declarations matter here.
142
+ if (!/\.(tf|tfvars)$/.test(file.filename)) continue;
143
+ let oldLine = 0;
144
+ let newLine = 0;
145
+ for (const patchLine of file.patch.split("\n")) {
146
+ const hunk = patchLine.match(/^@@ -(\d+)(?:,\d+)? \+(\d+)(?:,\d+)? @@/);
147
+ if (hunk) {
148
+ oldLine = Number.parseInt(hunk[1] ?? "0", 10);
149
+ newLine = Number.parseInt(hunk[2] ?? "0", 10);
150
+ continue;
151
+ }
152
+ if (patchLine.startsWith("+")) {
153
+ const decl = declarationOf(patchLine.slice(1));
154
+ const where: ChangeLocation = { path: file.filename, line: newLine, kind: "added" };
155
+ addedLines.add(locationKey(file.filename, newLine));
156
+ if (decl && !byRef.has(decl.ref)) byRef.set(decl.ref, { ...decl, where });
157
+ newLine++;
158
+ } else if (patchLine.startsWith("-")) {
159
+ const decl = declarationOf(patchLine.slice(1));
160
+ const oldPath = file.previous_filename ?? file.filename;
161
+ if (decl && !byRef.has(decl.ref)) {
162
+ byRef.set(decl.ref, { ...decl, where: { path: oldPath, line: oldLine, kind: "removed" } });
163
+ }
164
+ oldLine++;
165
+ } else if (!patchLine.startsWith("\\")) {
166
+ oldLine++;
167
+ newLine++;
168
+ }
169
+ }
170
+ }
171
+
172
+ return { atoms: [...byRef.values()].slice(0, MAX_ATOMS), addedLines };
173
+ }
174
+
175
+ function excerpt(text: string): string {
176
+ const t = text.trim();
177
+ return t.length > MAX_EXCERPT ? `${t.slice(0, MAX_EXCERPT - 1)}…` : t;
178
+ }
179
+
180
+ /**
181
+ * Render the artifact from already-collected grep hits. Pure: the caller
182
+ * supplies the reference hits per atom (via a real `git grep` at runtime, or a
183
+ * stub in tests). Bounded by MAX_REFS_PER_ATOM and MAX_BYTES.
184
+ */
185
+ export function renderChangeImpactArtifact(params: {
186
+ atoms: ChangeImpactAtom[];
187
+ hitsByRef: Map<string, GrepHit[]>;
188
+ addedLines: Set<string>;
189
+ baseSha: string;
190
+ headSha: string;
191
+ fileCount: number;
192
+ lookupError?: string | undefined;
193
+ }): { content: string; atomCount: number; referenceCount: number } {
194
+ const header = [
195
+ "# Change impact leads",
196
+ "",
197
+ "> Supplemental and explicitly incomplete. Read the authoritative `diffPath` TOC and raw lines first. Absence here is not evidence of no impact.",
198
+ "",
199
+ `Scope: base ${params.baseSha.slice(0, 7)}, head ${params.headSha.slice(0, 7)}; ${params.fileCount} changed file(s).`,
200
+ "Each entry is a Terraform declaration this PR changed, followed by pre-existing references to it elsewhere in the tree (the PR's own new lines are excluded).",
201
+ "",
202
+ ];
203
+ if (params.lookupError) header.push(`Reference lookup unavailable: ${params.lookupError}.`, "");
204
+
205
+ let content = `${header.join("\n")}`;
206
+ let atomCount = 0;
207
+ let referenceCount = 0;
208
+
209
+ for (const atom of params.atoms) {
210
+ const hits = (params.hitsByRef.get(atom.ref) ?? []).filter(
211
+ (h) => !params.addedLines.has(locationKey(h.path, h.line)),
212
+ );
213
+ if (hits.length === 0) continue;
214
+ const shown = hits.slice(0, MAX_REFS_PER_ATOM);
215
+ const note =
216
+ hits.length > shown.length
217
+ ? `${hits.length} reference(s); first ${shown.length} shown.`
218
+ : `${hits.length} reference(s).`;
219
+ const block = [
220
+ "",
221
+ `## \`${atom.label}\` (${atom.declaration})`,
222
+ "",
223
+ `Changed at: ${atom.where.kind} \`${atom.where.path}\`:${atom.where.line}`,
224
+ `References: ${note}`,
225
+ "",
226
+ ...shown.map((h) => `- \`${h.path}\`:${h.line} — ${excerpt(h.text)}`),
227
+ "",
228
+ ].join("\n");
229
+ if (Buffer.byteLength(content + block) > MAX_BYTES) break;
230
+ content += block;
231
+ atomCount++;
232
+ referenceCount += hits.length;
233
+ }
234
+
235
+ if (atomCount === 0) {
236
+ const empty = "\nNo pre-existing references found for the changed declarations. This is not evidence of no wider impact.\n";
237
+ if (Buffer.byteLength(content + empty) <= MAX_BYTES) content += empty;
238
+ }
239
+
240
+ return { content: content.endsWith("\n") ? content : `${content}\n`, atomCount, referenceCount };
241
+ }
242
+
243
+ // bound the per-atom grep parse so a heavily-referenced module can't balloon
244
+ // memory; the renderer only shows MAX_REFS_PER_ATOM anyway but reports the count.
245
+ const MAX_HITS_PER_ATOM = 200;
246
+
247
+ /** parse `git grep -n` porcelain (`path:line:text`) into hits. */
248
+ export function parseGrepHits(output: string): GrepHit[] {
249
+ const hits: GrepHit[] = [];
250
+ for (const raw of output.split("\n")) {
251
+ if (!raw) continue;
252
+ const m = raw.match(/^(.*?):(\d+):(.*)$/);
253
+ if (!m) continue;
254
+ const [, path, lineStr, text] = m;
255
+ if (path === undefined || lineStr === undefined) continue;
256
+ hits.push({ path, line: Number.parseInt(lineStr, 10), text: text ?? "" });
257
+ if (hits.length >= MAX_HITS_PER_ATOM) break;
258
+ }
259
+ return hits;
260
+ }
261
+
262
+ /**
263
+ * Runtime entry point: extract the changed declarations, grep the checked-out
264
+ * head for pre-existing references to each, and write the artifact to tmpdir.
265
+ * Returns null when there's nothing worth writing (no TF declarations changed).
266
+ * Callers treat a thrown error as "no artifact" — this must never fail checkout.
267
+ */
268
+ export function createChangeImpactArtifact(params: {
269
+ files: ChangedFile[];
270
+ tmpdir: string;
271
+ pullNumber: number;
272
+ baseSha: string;
273
+ headSha: string;
274
+ }): { path: string; atomCount: number; referenceCount: number; bytes: number } | null {
275
+ const { atoms, addedLines } = extractTerraformAtoms(params.files);
276
+ if (atoms.length === 0) return null;
277
+
278
+ const hitsByRef = new Map<string, GrepHit[]>();
279
+ let lookupError: string | undefined;
280
+ for (const atom of atoms) {
281
+ let status = 0;
282
+ // word-bounded, tracked-files-only, skip-binary grep of the checked-out head.
283
+ // `-w` keeps `var.env` from matching `var.environment`; `-E`/`-F` per atom
284
+ // (outputs grep a `module.<caller>.<name>` regex, the rest fixed strings).
285
+ // exit 1 = no matches (onError swallows it → ""); exit >1 = a real failure.
286
+ const out = $(
287
+ "git",
288
+ [
289
+ "grep",
290
+ "-n",
291
+ atom.regex ? "-E" : "-F",
292
+ "-w",
293
+ "-I",
294
+ "--no-color",
295
+ "-e",
296
+ atom.ref,
297
+ "--",
298
+ "*.tf",
299
+ "*.tfvars",
300
+ ],
301
+ { log: false, onError: (r) => (status = r.status) },
302
+ );
303
+ if (status > 1) {
304
+ lookupError = `git grep exit ${status}`;
305
+ continue;
306
+ }
307
+ hitsByRef.set(atom.ref, parseGrepHits(out));
308
+ }
309
+
310
+ const artifact = renderChangeImpactArtifact({
311
+ atoms,
312
+ hitsByRef,
313
+ addedLines,
314
+ baseSha: params.baseSha,
315
+ headSha: params.headSha,
316
+ fileCount: params.files.length,
317
+ lookupError,
318
+ });
319
+ const path = join(params.tmpdir, `pr-${params.pullNumber}-${params.headSha.slice(0, 7)}-impact.md`);
320
+ writeFileSync(path, artifact.content);
321
+ log.debug(
322
+ `» change impact: ${artifact.atomCount} atom(s), ${artifact.referenceCount} reference(s) → ${path}`,
323
+ );
324
+ return {
325
+ path,
326
+ atomCount: artifact.atomCount,
327
+ referenceCount: artifact.referenceCount,
328
+ bytes: Buffer.byteLength(artifact.content),
329
+ };
330
+ }
@@ -0,0 +1,93 @@
1
+ import { resolveModelSlug } from "#app/models";
2
+
3
+ /**
4
+ * identity block Anthropic's OAuth gate validates on `/v1/messages` calls
5
+ * authenticated with a Claude Code OAuth token. haiku is currently exempt
6
+ * but sending it costs nothing and survives the exemption being removed.
7
+ */
8
+ const CLAUDE_CODE_IDENTITY = "You are Claude Code, Anthropic's official CLI for Claude.";
9
+
10
+ // fallback probe model for runs with no explicit model (claude-code picks its
11
+ // own default there, so per-model accuracy is moot — only whether the
12
+ // subscription answers at all). registry-resolved so a catalog bump keeps
13
+ // the id fresh.
14
+ const fallbackResolve = resolveModelSlug("anthropic/claude-haiku");
15
+ if (!fallbackResolve) {
16
+ throw new Error("claudeSubscription preflight: anthropic/claude-haiku missing from registry");
17
+ }
18
+ const FALLBACK_PROBE_MODEL = fallbackResolve.slice(fallbackResolve.indexOf("/") + 1);
19
+
20
+ export type SubscriptionPreflight = { usable: true } | { usable: false; reason: string };
21
+
22
+ /**
23
+ * preflight a Claude subscription OAuth token (`CLAUDE_CODE_OAUTH_TOKEN`)
24
+ * with a 1-token Messages call, so the agent can fall back to
25
+ * `ANTHROPIC_API_KEY` when the subscription is exhausted or revoked instead
26
+ * of failing the whole run at its first model call. rides the same de-facto
27
+ * OAuth surface Claude Code itself uses: Bearer auth + the
28
+ * `claude-code-20250219,oauth-2025-04-20` betas + the identity system prompt.
29
+ *
30
+ * probes the run's own model when known — subscription limits can be
31
+ * per-model ("You've hit your Opus limit"), so a cheaper stand-in could pass
32
+ * preflight and still leave the run dead on arrival.
33
+ *
34
+ * fail-open by design: only 401 (revoked/expired token) and 429
35
+ * (session/weekly/per-model limit) mark the token unusable. network errors,
36
+ * 5xx, and request-shape drift (400) all keep today's subscription-first
37
+ * behavior, so the preflight can never fail a run that would have worked —
38
+ * the worst wrong answer is a run that bills the API key instead of the
39
+ * subscription.
40
+ */
41
+ export async function preflightClaudeSubscription(params: {
42
+ token: string;
43
+ /** bare Anthropic model id the run will use (e.g. "claude-fable-5") */
44
+ model: string | undefined;
45
+ }): Promise<SubscriptionPreflight> {
46
+ let res: Response;
47
+ try {
48
+ res = await fetch("https://api.anthropic.com/v1/messages", {
49
+ method: "POST",
50
+ headers: {
51
+ authorization: `Bearer ${params.token}`,
52
+ "anthropic-beta": "claude-code-20250219,oauth-2025-04-20",
53
+ "anthropic-version": "2023-06-01",
54
+ "content-type": "application/json",
55
+ "x-app": "cli",
56
+ },
57
+ body: JSON.stringify({
58
+ model: params.model ?? FALLBACK_PROBE_MODEL,
59
+ max_tokens: 1,
60
+ system: CLAUDE_CODE_IDENTITY,
61
+ messages: [{ role: "user", content: "ok" }],
62
+ }),
63
+ signal: AbortSignal.timeout(10_000),
64
+ });
65
+ } catch {
66
+ // network failure / timeout says nothing about the credential
67
+ return { usable: true };
68
+ }
69
+ if (res.status !== 401 && res.status !== 429) return { usable: true };
70
+ const body = await res.text().catch(() => "");
71
+ return { usable: false, reason: `${res.status}: ${extractApiErrorMessage(body)}` };
72
+ }
73
+
74
+ /** pull `error.message` out of an Anthropic error payload, else a raw excerpt */
75
+ function extractApiErrorMessage(body: string): string {
76
+ try {
77
+ const parsed: unknown = JSON.parse(body);
78
+ if (
79
+ typeof parsed === "object" &&
80
+ parsed !== null &&
81
+ "error" in parsed &&
82
+ typeof parsed.error === "object" &&
83
+ parsed.error !== null &&
84
+ "message" in parsed.error &&
85
+ typeof parsed.error.message === "string"
86
+ ) {
87
+ return parsed.error.message;
88
+ }
89
+ } catch {
90
+ // not json — fall through to the raw excerpt
91
+ }
92
+ return body.slice(0, 200);
93
+ }