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,172 @@
1
+ /**
2
+ * Baseline + gate (2c).
3
+ *
4
+ * A committed `.infraweaver.baseline.json` records the line-independent keys of the
5
+ * concerns that already existed when the baseline was taken. With a baseline in
6
+ * place the check fails only on findings that are NEW relative to it (and at or
7
+ * above the gate severity) — so a first scan on a messy repo doesn't fail the
8
+ * build wholesale, and the team isn't pushed to suppress everything at once.
9
+ *
10
+ * The gate defaults to High/Critical: only NEW high/critical findings fail the
11
+ * check. Lower-severity findings are still reported (and counted) — just not
12
+ * gated — matching the Checkov/tfsec "baseline + severity gate" convention.
13
+ *
14
+ * Pure module (the file read lives in `loadBaseline`); the gate evaluation is
15
+ * fully unit-testable.
16
+ */
17
+ import { readFileSync } from "node:fs";
18
+ import { join } from "node:path";
19
+ import { BASELINE_FILE } from "#app/brand";
20
+ import {
21
+ type Concern,
22
+ concernKeyOf,
23
+ SEVERITY_RANK,
24
+ type Severity,
25
+ } from "#app/mcp/terraform/types";
26
+
27
+ export const BASELINE_FILENAME = BASELINE_FILE;
28
+
29
+ /** Findings at/above this severity fail the gate. High = high + critical. */
30
+ export const DEFAULT_GATE_SEVERITY: Severity = "high";
31
+
32
+ export interface BaselineFile {
33
+ version: 1;
34
+ /** ISO date the baseline was taken (informational). */
35
+ created?: string;
36
+ /** line-independent concern keys (see `concernKeyOf`) present at baseline time. */
37
+ keys: string[];
38
+ }
39
+
40
+ /** The serialized baseline for the current concern set (what a `--write-baseline` emits). */
41
+ export function serializeBaseline(
42
+ concerns: Concern[],
43
+ created: string,
44
+ ): BaselineFile {
45
+ const keys = Array.from(new Set(concerns.map((c) => concernKeyOf(c)))).sort();
46
+ return { version: 1, created, keys };
47
+ }
48
+
49
+ /** Parse baseline JSON into a key set; tolerant of a malformed/old file (→ null). */
50
+ export function parseBaseline(raw: string): Set<string> | null {
51
+ let doc: unknown;
52
+ try {
53
+ doc = JSON.parse(raw);
54
+ } catch {
55
+ return null;
56
+ }
57
+ if (!doc || typeof doc !== "object") return null;
58
+ const keys = (doc as { keys?: unknown }).keys;
59
+ if (!Array.isArray(keys)) return null;
60
+ return new Set(keys.filter((k): k is string => typeof k === "string"));
61
+ }
62
+
63
+ /** Load `.infraweaver.baseline.json` under `cwd`; null when absent/unreadable/malformed. */
64
+ export function loadBaseline(cwd: string | undefined): Set<string> | null {
65
+ const root = cwd ?? process.cwd();
66
+ let raw: string;
67
+ try {
68
+ raw = readFileSync(join(root, BASELINE_FILENAME), "utf-8");
69
+ } catch {
70
+ return null;
71
+ }
72
+ return parseBaseline(raw);
73
+ }
74
+
75
+ export interface LoadedBaseline {
76
+ keys: Set<string>;
77
+ /** ISO date the baseline was taken — the report's "since baseline (…)" label. */
78
+ created: string | null;
79
+ }
80
+
81
+ /** Load the baseline WITH its `created` date (the report's delta needs both the
82
+ * keys to diff against and the date to label the comparison). Null when
83
+ * absent/unreadable/malformed. `loadBaseline` stays the keys-only path the gate uses. */
84
+ export function loadBaselineWithMeta(
85
+ cwd: string | undefined,
86
+ ): LoadedBaseline | null {
87
+ const root = cwd ?? process.cwd();
88
+ let raw: string;
89
+ try {
90
+ raw = readFileSync(join(root, BASELINE_FILENAME), "utf-8");
91
+ } catch {
92
+ return null;
93
+ }
94
+ const keys = parseBaseline(raw);
95
+ if (!keys) return null;
96
+ let created: string | null = null;
97
+ try {
98
+ const doc = JSON.parse(raw) as { created?: unknown };
99
+ if (typeof doc.created === "string") created = doc.created;
100
+ } catch {
101
+ // parseBaseline already accepted the file, so this never throws — guard anyway.
102
+ }
103
+ return { keys, created };
104
+ }
105
+
106
+ export interface BaselineDelta {
107
+ /** active keys NOT in the baseline — new since the checkpoint. */
108
+ added: number;
109
+ /** baseline keys no longer present — cleared since the checkpoint. */
110
+ fixed: number;
111
+ /** keys in both — carried, unchanged. */
112
+ unchanged: number;
113
+ }
114
+
115
+ /**
116
+ * The new / fixed / unchanged split of the current ACTIVE concern set against a
117
+ * baseline checkpoint, keyed line-independently (`concernKeyOf`) so a line shift
118
+ * never reads as a fix or a regression. Pure — the report's "what changed" line.
119
+ */
120
+ export function baselineDelta(
121
+ concerns: Concern[],
122
+ baselineKeys: Set<string>,
123
+ ): BaselineDelta {
124
+ const current = new Set(concerns.map((c) => concernKeyOf(c)));
125
+ let added = 0;
126
+ let unchanged = 0;
127
+ for (const key of current) {
128
+ if (baselineKeys.has(key)) unchanged++;
129
+ else added++;
130
+ }
131
+ let fixed = 0;
132
+ for (const key of baselineKeys) if (!current.has(key)) fixed++;
133
+ return { added, fixed, unchanged };
134
+ }
135
+
136
+ function meetsGate(severity: Severity, threshold: Severity): boolean {
137
+ return SEVERITY_RANK[severity] >= SEVERITY_RANK[threshold];
138
+ }
139
+
140
+ export interface GateResult {
141
+ /** active concerns whose key is NOT in the baseline (or all, when no baseline). */
142
+ newConcerns: Concern[];
143
+ /** the new concerns at/above the gate severity — these fail the check. */
144
+ gating: Concern[];
145
+ /** true = the gate passes (no new finding at/above the gate severity). */
146
+ pass: boolean;
147
+ /** whether a baseline was supplied (false ⇒ every active concern is "new"). */
148
+ hasBaseline: boolean;
149
+ }
150
+
151
+ /**
152
+ * Evaluate the gate. `concerns` should already be the ACTIVE set (suppressed
153
+ * concerns excluded — a suppression keeps a finding out of the gate by design).
154
+ * With no baseline (`baselineKeys` null) every active concern counts as new, so
155
+ * the gate behaves as a plain High/Critical gate.
156
+ */
157
+ export function evaluateGate(
158
+ concerns: Concern[],
159
+ baselineKeys: Set<string> | null,
160
+ threshold: Severity = DEFAULT_GATE_SEVERITY,
161
+ ): GateResult {
162
+ const newConcerns = baselineKeys
163
+ ? concerns.filter((c) => !baselineKeys.has(concernKeyOf(c)))
164
+ : [...concerns];
165
+ const gating = newConcerns.filter((c) => meetsGate(c.severity, threshold));
166
+ return {
167
+ newConcerns,
168
+ gating,
169
+ pass: gating.length === 0,
170
+ hasBaseline: baselineKeys !== null,
171
+ };
172
+ }
@@ -0,0 +1,111 @@
1
+ import type { LocalToolContext } from "#app/mcp/localContext";
2
+ import {
3
+ annotateGroups,
4
+ groupConcerns,
5
+ groupConcernsByRule,
6
+ planBatches,
7
+ } from "#app/mcp/terraform/decisions";
8
+ import { scanSessionOf } from "#app/mcp/terraform/scanSession";
9
+ import { suppressConcerns } from "#app/mcp/terraform/suppressions";
10
+ import { dedupeCrossScanner } from "#app/mcp/terraform/taxonomy";
11
+ import {
12
+ type Concern,
13
+ concernKeyOf,
14
+ dedupe,
15
+ isTerraformConcern,
16
+ SEVERITY_RANK,
17
+ type Severity,
18
+ sortConcerns,
19
+ } from "#app/mcp/terraform/types";
20
+
21
+ export interface ConcernResult {
22
+ /** non-suppressed concerns ≥ threshold, sorted */
23
+ active: Concern[];
24
+ /** concerns that matched a suppression rule */
25
+ suppressed: Concern[];
26
+ /** active ∪ suppressed (post-suppression annotation) — scan stores this as lastScanConcerns */
27
+ annotated: Concern[];
28
+ groups: ReturnType<typeof annotateGroups>;
29
+ batchPlan: ReturnType<typeof planBatches>;
30
+ bySeverity: Record<string, number>;
31
+ }
32
+
33
+ /**
34
+ * The shared concerns→result pipeline behind `terraform_scan` and `read_findings`:
35
+ * capture the line-pinned + line-independent baseline (so verify's regression diff
36
+ * is apples-to-apples), filter to in-scope Terraform concerns ≥ threshold, apply
37
+ * suppressions, group + annotate autonomy, plan batches, and tally by severity.
38
+ * Both tools then wrap their own envelope around this. The diff-scope predicate is
39
+ * `scan`-only (defaults to "everything in scope"); `lastScanConcerns` is written by
40
+ * `scan` from `annotated`, not here, since `read_findings` does not set it.
41
+ */
42
+ export function buildConcernResult(
43
+ ctx: LocalToolContext,
44
+ rawConcerns: Concern[],
45
+ opts: {
46
+ cwd: string;
47
+ minRank: number;
48
+ grouping: "file" | "rule";
49
+ autonomyThreshold: Severity;
50
+ inScope?: (c: Concern) => boolean;
51
+ /** How this call affects the session's verify baseline. `replace` (default)
52
+ * is for the full-workspace sources (`scan`, `read_findings`). The narrow
53
+ * concern sources (`detect_plan_drift`, `ingest_external_findings`) pass
54
+ * `merge`: their small set must JOIN an existing scan baseline, not clobber
55
+ * it — otherwise a later `terraform_verify_remediation` diffs the full
56
+ * re-scan against the narrow set and reports every pre-existing scanner
57
+ * concern as a false regression. */
58
+ baselineMode?: "replace" | "merge";
59
+ },
60
+ ): ConcernResult {
61
+ const fullBaseline = dedupe(rawConcerns);
62
+ const session = scanSessionOf(ctx.toolState);
63
+ const merge = opts.baselineMode === "merge";
64
+ const union = (prior: string[] | undefined, next: string[]) =>
65
+ merge ? [...new Set([...(prior ?? []), ...next])] : next;
66
+ session.baselineConcernIds = union(
67
+ session.baselineConcernIds,
68
+ fullBaseline.map((c) => c.id),
69
+ );
70
+ session.baselineConcernKeys = union(
71
+ session.baselineConcernKeys,
72
+ fullBaseline.map((c) => concernKeyOf(c)),
73
+ );
74
+ // the files already unformatted at this (pre-fix) scan. terraform_validate
75
+ // subtracts these so PRE-EXISTING `terraform fmt` drift can't force a
76
+ // whole-file reformat into a scoped fix. Captured from the raw set (fmt
77
+ // concerns survive the threshold/scope filters that shape `active`).
78
+ session.baselineFmtDriftFiles = union(
79
+ session.baselineFmtDriftFiles,
80
+ fullBaseline.filter((c) => c.source === "terraform-fmt").map((c) => c.location.file),
81
+ );
82
+
83
+ const inScope = opts.inScope ?? (() => true);
84
+ const annotated = suppressConcerns(
85
+ sortConcerns(dedupe(rawConcerns))
86
+ .filter(isTerraformConcern)
87
+ .filter(inScope)
88
+ .filter((c) => SEVERITY_RANK[c.severity] >= opts.minRank),
89
+ opts.cwd,
90
+ );
91
+ // Cross-scanner canonical dedupe (2c) is applied ONLY to the ACTIVE set that
92
+ // feeds grouping/PRs/coLocated/display — NOT to `annotated` or the baseline. The
93
+ // ✗→✓ verifier re-scans raw and key-maps requested ids through `lastScanConcerns`
94
+ // (= annotated) and `baselineConcernKeys`, both of which stay raw, so collapsing a
95
+ // cross-tool duplicate here can never make the verifier read a merged-away concern
96
+ // as resolved or as a regression. The user still gets ONE PR per cross-tool dup.
97
+ const active = dedupeCrossScanner(annotated.filter((c) => !c.suppression));
98
+ const suppressed = annotated.filter((c) => c.suppression);
99
+
100
+ const groups = annotateGroups(
101
+ opts.grouping === "rule" ? groupConcernsByRule(active) : groupConcerns(active),
102
+ active,
103
+ opts.autonomyThreshold,
104
+ );
105
+ const batchPlan = planBatches(groups);
106
+
107
+ const bySeverity: Record<string, number> = {};
108
+ for (const c of active) bySeverity[c.severity] = (bySeverity[c.severity] ?? 0) + 1;
109
+
110
+ return { active, suppressed, annotated, groups, batchPlan, bySeverity };
111
+ }
@@ -0,0 +1,175 @@
1
+ import { mkdtempSync, rmSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { type } from "arktype";
4
+ import { resolveBaseRef } from "#app/mcp/terraform/paths";
5
+ import { parseScannerJson } from "#app/mcp/terraform/scannerJson";
6
+ import { type RunResult, run } from "#app/mcp/terraform/subprocess";
7
+
8
+ // --- infracost (cost lens) ------------------------------------------------
9
+
10
+ export interface CostBreakdown {
11
+ /** total estimated monthly cost, or null when no resources are priced. */
12
+ totalMonthlyCost: number | null;
13
+ currency: string;
14
+ }
15
+
16
+ /**
17
+ * Parse `infracost breakdown --format json`. The top-level `totalMonthlyCost`
18
+ * is a decimal string (absent / null when a project has no priced resources);
19
+ * `currency` defaults to USD. A missing/unparseable cost becomes null so the
20
+ * caller reports "unpriced" rather than a misleading $0.00.
21
+ */
22
+ export function parseInfracostBreakdown(stdout: string): CostBreakdown {
23
+ const parsed = parseScannerJson(InfracostBreakdownOutput, stdout, "infracost");
24
+ const raw = parsed.totalMonthlyCost;
25
+ const num = typeof raw === "number" ? raw : raw != null ? Number.parseFloat(raw) : Number.NaN;
26
+ return {
27
+ totalMonthlyCost: Number.isFinite(num) ? num : null,
28
+ currency: parsed.currency || "USD",
29
+ };
30
+ }
31
+
32
+ const InfracostBreakdownOutput = type({
33
+ "totalMonthlyCost?": "string | number | null",
34
+ "currency?": "string",
35
+ });
36
+
37
+ export interface ResourceCost {
38
+ name: string;
39
+ monthlyCost: number;
40
+ }
41
+
42
+ /**
43
+ * Parse the per-resource monthly costs from `infracost breakdown --format json`
44
+ * (`projects[].breakdown.resources[]`), so a cost increase can be attributed to
45
+ * the specific resources that drove it instead of just a total. Skips unpriced
46
+ * (null/zero) resources; returns them sorted most-expensive first. Pure.
47
+ */
48
+ export function parseInfracostResources(stdout: string): ResourceCost[] {
49
+ let parsed: typeof InfracostResourcesOutput.infer;
50
+ try {
51
+ parsed = parseScannerJson(InfracostResourcesOutput, stdout, "infracost");
52
+ } catch {
53
+ return [];
54
+ }
55
+ const out: ResourceCost[] = [];
56
+ for (const project of parsed.projects ?? []) {
57
+ for (const r of project.breakdown?.resources ?? []) {
58
+ const raw = r.monthlyCost;
59
+ const cost =
60
+ typeof raw === "number" ? raw : raw != null ? Number.parseFloat(raw) : Number.NaN;
61
+ if (Number.isFinite(cost) && cost > 0 && r.name) {
62
+ out.push({ name: r.name, monthlyCost: Math.round(cost * 100) / 100 });
63
+ }
64
+ }
65
+ }
66
+ return out.sort((a, b) => b.monthlyCost - a.monthlyCost);
67
+ }
68
+
69
+ const InfracostResourcesOutput = type({
70
+ "projects?": type({
71
+ "breakdown?": {
72
+ "resources?": type({
73
+ "name?": "string",
74
+ "monthlyCost?": "string | number | null",
75
+ }).array(),
76
+ },
77
+ }).array(),
78
+ });
79
+
80
+ export interface CostDelta {
81
+ currency: string;
82
+ baselineMonthly: number | null;
83
+ currentMonthly: number | null;
84
+ /** current − baseline, rounded to cents; null when either side is unknown. */
85
+ deltaMonthly: number | null;
86
+ direction: "increase" | "decrease" | "no-change" | "unknown";
87
+ }
88
+
89
+ /** Pure cost-delta computation: current (post-fix) vs the base-branch baseline. */
90
+ export function computeCostDelta(
91
+ baseline: CostBreakdown | null,
92
+ current: CostBreakdown,
93
+ ): CostDelta {
94
+ const currency = current.currency || baseline?.currency || "USD";
95
+ const baselineMonthly = baseline?.totalMonthlyCost ?? null;
96
+ const currentMonthly = current.totalMonthlyCost;
97
+ if (baselineMonthly === null || currentMonthly === null) {
98
+ return { currency, baselineMonthly, currentMonthly, deltaMonthly: null, direction: "unknown" };
99
+ }
100
+ const deltaMonthly = Math.round((currentMonthly - baselineMonthly) * 100) / 100;
101
+ const direction = deltaMonthly > 0 ? "increase" : deltaMonthly < 0 ? "decrease" : "no-change";
102
+ return { currency, baselineMonthly, currentMonthly, deltaMonthly, direction };
103
+ }
104
+
105
+ export interface CostEscalation {
106
+ /** true when the monthly increase meets/exceeds the operator's threshold. */
107
+ escalate: boolean;
108
+ reason?: string;
109
+ }
110
+
111
+ /**
112
+ * decide whether a cost increase is large enough to escalate the PR
113
+ * to human review (`needs-human`). Compares the monthly delta against the
114
+ * operator's `cost_increase_block_usd` threshold. No threshold set, an unknown
115
+ * delta, or a decrease/no-change ⇒ no escalation. Pure + deterministic so the
116
+ * decision is auditable, not a model judgement.
117
+ */
118
+ export function classifyCostEscalation(
119
+ deltaMonthly: number | null,
120
+ thresholdUsd: number | undefined,
121
+ ): CostEscalation {
122
+ if (thresholdUsd === undefined || deltaMonthly === null || deltaMonthly <= 0) {
123
+ return { escalate: false };
124
+ }
125
+ if (deltaMonthly >= thresholdUsd) {
126
+ return {
127
+ escalate: true,
128
+ reason: `the fix raises monthly cost by ${deltaMonthly}, at or above the ${thresholdUsd} escalation threshold`,
129
+ };
130
+ }
131
+ return { escalate: false };
132
+ }
133
+
134
+ export function runInfracostBreakdown(scanCwd: string, key: string): RunResult {
135
+ return run("infracost", ["breakdown", "--path", ".", "--format", "json", "--no-color"], scanCwd, {
136
+ INFRACOST_API_KEY: key,
137
+ });
138
+ }
139
+
140
+ /**
141
+ * Cost of the base-branch version of the same Terraform, computed in a detached
142
+ * git worktree so the current (fixed) checkout is never disturbed. Best-effort:
143
+ * any failure (no base ref, worktree add fails, infracost errors) returns null
144
+ * and the caller falls back to reporting current cost only.
145
+ */
146
+ export function infracostBaseline(cwd: string, key: string, tmpdir: string): CostBreakdown | null {
147
+ const baseRef = resolveBaseRef(cwd);
148
+ if (!baseRef) return null;
149
+ const prefixResult = run("git", ["rev-parse", "--show-prefix"], cwd);
150
+ const prefix = prefixResult.status === 0 ? prefixResult.stdout.trim() : "";
151
+ // unique, unpredictable worktree path. the old `infracost-base-<pid>` name was
152
+ // predictable (a local actor could pre-create/symlink it) and collided when
153
+ // two baselines ran in the same process. mkdtemp gives a fresh PARENT dir;
154
+ // the worktree itself goes in a not-yet-existing child (git worktree add
155
+ // refuses a path that already exists). the finally removes both the git
156
+ // worktree registration and the parent dir.
157
+ const baseDir = mkdtempSync(join(tmpdir, "infracost-base-"));
158
+ const worktree = join(baseDir, "wt");
159
+ const add = run("git", ["worktree", "add", "--detach", worktree, baseRef], cwd);
160
+ if (add.status !== 0) {
161
+ rmSync(baseDir, { recursive: true, force: true });
162
+ return null;
163
+ }
164
+ try {
165
+ const scanCwd = prefix ? join(worktree, prefix) : worktree;
166
+ const r = runInfracostBreakdown(scanCwd, key);
167
+ if (r.missing || r.status !== 0) return null;
168
+ return parseInfracostBreakdown(r.stdout);
169
+ } catch {
170
+ return null;
171
+ } finally {
172
+ run("git", ["worktree", "remove", "--force", worktree], cwd);
173
+ rmSync(baseDir, { recursive: true, force: true });
174
+ }
175
+ }