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,590 @@
1
+ import { createHash } from "node:crypto";
2
+ import { PRODUCT_NAME } from "#app/brand";
3
+ import type { CostDelta } from "#app/mcp/terraform/cost";
4
+ import { NATIVE_SOURCE } from "#app/mcp/terraform/nativeRules";
5
+ import {
6
+ type Autonomy,
7
+ type BlastTier,
8
+ type Concern,
9
+ type ConcernGroup,
10
+ SEVERITY_RANK,
11
+ type Severity,
12
+ } from "#app/mcp/terraform/types";
13
+
14
+ // --- grouping --------------------------------------------------------------
15
+
16
+ function groupId(file: string): string {
17
+ return createHash("sha1").update(`group|${file}`).digest("hex").slice(0, 12);
18
+ }
19
+
20
+ function ruleGroupId(ruleId: string): string {
21
+ return createHash("sha1")
22
+ .update(`rulegroup|${ruleId}`)
23
+ .digest("hex")
24
+ .slice(0, 12);
25
+ }
26
+
27
+ function maxSeverity(cs: Concern[]): Severity {
28
+ return cs.reduce<Severity>(
29
+ (max, c) =>
30
+ SEVERITY_RANK[c.severity] > SEVERITY_RANK[max] ? c.severity : max,
31
+ "info",
32
+ );
33
+ }
34
+
35
+ function sortGroups(groups: ConcernGroup[]): ConcernGroup[] {
36
+ return groups.sort((a, b) => {
37
+ const sev = SEVERITY_RANK[b.severity] - SEVERITY_RANK[a.severity];
38
+ if (sev !== 0) return sev;
39
+ return a.id.localeCompare(b.id);
40
+ });
41
+ }
42
+
43
+ /** group concerns by file into scoped units, sorted by max severity. */
44
+ export function groupConcerns(concerns: Concern[]): ConcernGroup[] {
45
+ const byFile = new Map<string, Concern[]>();
46
+ for (const c of concerns) {
47
+ const arr = byFile.get(c.location.file) ?? [];
48
+ arr.push(c);
49
+ byFile.set(c.location.file, arr);
50
+ }
51
+ const groups: ConcernGroup[] = [];
52
+ for (const [file, cs] of byFile) {
53
+ groups.push({
54
+ id: groupId(file),
55
+ file,
56
+ files: [file],
57
+ grouping: "file",
58
+ severity: maxSeverity(cs),
59
+ concern_count: cs.length,
60
+ rule_ids: [...new Set(cs.map((c) => c.rule_id))].sort(),
61
+ concern_ids: cs.map((c) => c.id),
62
+ });
63
+ }
64
+ return sortGroups(groups);
65
+ }
66
+
67
+ /**
68
+ * group concerns by RULE across files instead of by file. When a single
69
+ * rule fires in many files ("add `tags` to every resource", "enable encryption
70
+ * on every bucket"), fixing it as ONE coherent change is far better than N
71
+ * near-identical per-file PRs. Each group covers one `rule_id` and lists every
72
+ * `file` it spans; the branch key (`remediate/<id>`) is rule-derived and stable.
73
+ * Opt-in (scan `group_by: "rule"`) — by-file stays the default because it keeps
74
+ * each PR's blast radius smaller; by-rule suits sweeping, low-risk rules.
75
+ */
76
+ export function groupConcernsByRule(concerns: Concern[]): ConcernGroup[] {
77
+ const byRule = new Map<string, Concern[]>();
78
+ for (const c of concerns) {
79
+ const arr = byRule.get(c.rule_id) ?? [];
80
+ arr.push(c);
81
+ byRule.set(c.rule_id, arr);
82
+ }
83
+ const groups: ConcernGroup[] = [];
84
+ for (const [ruleId, cs] of byRule) {
85
+ const files = [...new Set(cs.map((c) => c.location.file))].sort();
86
+ groups.push({
87
+ id: ruleGroupId(ruleId),
88
+ file: files.length === 1 ? files[0]! : `${files.length} files`,
89
+ files,
90
+ grouping: "rule",
91
+ severity: maxSeverity(cs),
92
+ concern_count: cs.length,
93
+ rule_ids: [ruleId],
94
+ concern_ids: cs.map((c) => c.id),
95
+ });
96
+ }
97
+ return sortGroups(groups);
98
+ }
99
+
100
+ /**
101
+ * annotate each group with an autonomy decision. Works for BOTH grouping
102
+ * modes: it resolves a group's concerns by `concern_ids` membership (not by
103
+ * `file`, which is just a label for by-rule groups), so the severity/category
104
+ * policy applies identically. Blast radius isn't known until terraform_plan
105
+ * runs, so it can only escalate a group later (the plan tool + prompt apply the
106
+ * `high`-blast override); at scan time autonomy is severity/category-driven.
107
+ */
108
+ export function annotateGroups(
109
+ groups: ConcernGroup[],
110
+ all: Concern[],
111
+ threshold: Severity,
112
+ ): ConcernGroup[] {
113
+ const byId = new Map(all.map((c) => [c.id, c]));
114
+ return groups.map((g) => {
115
+ const groupConcerns = g.concern_ids
116
+ .map((id) => byId.get(id))
117
+ .filter((c): c is Concern => !!c);
118
+ const decision = classifyAutonomy(groupConcerns, threshold);
119
+ return {
120
+ ...g,
121
+ autonomy: decision.autonomy,
122
+ autonomy_reasons: decision.reasons,
123
+ };
124
+ });
125
+ }
126
+
127
+ // --- atomic vs batched PRs -------------------------------------------
128
+
129
+ export interface BatchPlan {
130
+ /** group ids safe to combine into ONE low-risk PR (`remediate/batch-<hash>`). */
131
+ batchable: string[];
132
+ /** group ids that must each get their own PR (security / higher severity /
133
+ * needs-human / large blast). */
134
+ isolated: string[];
135
+ /** deterministic branch name for the batch (stable for the same member set). */
136
+ batch_branch: string | null;
137
+ }
138
+
139
+ /** a group is safe to batch when it's low-risk: severity `low`/`info` AND its
140
+ * autonomy decision is `auto` (no escalating security finding, no high blast). */
141
+ function isBatchable(g: ConcernGroup): boolean {
142
+ const lowRisk = g.severity === "low" || g.severity === "info";
143
+ return lowRisk && g.autonomy !== "needs-human";
144
+ }
145
+
146
+ /**
147
+ * split annotated groups into a single low-risk BATCH (merged into one
148
+ * easy-to-review PR) and the riskier groups that each stay ISOLATED in their own
149
+ * PR (so they can be reviewed/reverted independently). The batch branch name
150
+ * hashes the sorted member ids, so re-runs over the same set reuse the branch
151
+ * (idempotent). Returns `batch_branch: null` when fewer than two groups are
152
+ * batchable (one group is just a normal single-group PR, not a batch).
153
+ */
154
+ export function planBatches(groups: ConcernGroup[]): BatchPlan {
155
+ const batchable = groups
156
+ .filter(isBatchable)
157
+ .map((g) => g.id)
158
+ .sort();
159
+ const isolated = groups
160
+ .filter((g) => !isBatchable(g))
161
+ .map((g) => g.id)
162
+ .sort();
163
+ const batch_branch =
164
+ batchable.length >= 2
165
+ ? `remediate/batch-${createHash("sha1").update(batchable.join("|")).digest("hex").slice(0, 12)}`
166
+ : null;
167
+ return { batchable, isolated, batch_branch };
168
+ }
169
+
170
+ // --- per-finding explanation (rule documentation links) --------------
171
+
172
+ /**
173
+ * Resolve the canonical documentation URL for a concern's rule, for the PR's
174
+ * per-finding explanation. Prefers the scanner's own `remediation_hint` when it
175
+ * is already a URL (checkov guideline, tflint rule link, trivy reference).
176
+ * Otherwise derives the well-known page deterministically: a trivy `AVD-*` rule
177
+ * maps to its Aqua Vulnerability Database page. Returns null when no canonical
178
+ * URL is known (the agent then explains from `evidence` alone).
179
+ */
180
+ export function ruleDocUrl(
181
+ concern: Pick<Concern, "rule_id" | "remediation_hint">,
182
+ ): string | null {
183
+ const hint = concern.remediation_hint?.trim();
184
+ if (hint && /^https?:\/\//i.test(hint)) return hint;
185
+ // trivy:AVD-AWS-0088 → https://avd.aquasec.com/misconfig/avd-aws-0088
186
+ const trivyMatch = concern.rule_id.match(/^trivy:(AVD-[A-Z0-9-]+)$/i);
187
+ if (trivyMatch)
188
+ return `https://avd.aquasec.com/misconfig/${trivyMatch[1]!.toLowerCase()}`;
189
+ return null;
190
+ }
191
+
192
+ /** distinct rule→doc-url map for a group, for the PR body's per-finding links. */
193
+ export function docUrlsForGroup(
194
+ g: ConcernGroup,
195
+ all: Concern[],
196
+ ): Record<string, string> {
197
+ const byId = new Map(all.map((c) => [c.id, c]));
198
+ const out: Record<string, string> = {};
199
+ for (const id of g.concern_ids) {
200
+ const c = byId.get(id);
201
+ if (!c) continue;
202
+ const url = ruleDocUrl(c);
203
+ if (url) out[c.rule_id] = url;
204
+ }
205
+ return out;
206
+ }
207
+
208
+ // --- severity-driven autonomy ---------------------------------------
209
+
210
+ export interface AutonomyDecision {
211
+ autonomy: Autonomy;
212
+ /** human-readable reasons a group was escalated (empty for `auto`). */
213
+ reasons: string[];
214
+ }
215
+
216
+ /**
217
+ * Decide whether a group of concerns can be auto-fixed and opened as a normal
218
+ * PR (`auto`), or must be flagged for human review (`needs-human`). Trivial
219
+ * findings (style/correctness, deprecated args, missing tags, formatting) open
220
+ * as normal; high-severity SECURITY findings escalate by default, as does a
221
+ * `high` blast radius regardless of finding severity (overrides upward).
222
+ *
223
+ * `threshold` is the minimum severity at which a *security* concern escalates
224
+ * (default `high`, so critical/high security → human; medium/low → auto). The
225
+ * decision is deterministic and computed from the `Concern` model's existing
226
+ * `severity` + `category` — no model self-assessment.
227
+ */
228
+ export function classifyAutonomy(
229
+ concerns: Pick<Concern, "severity" | "category">[],
230
+ threshold: Severity = "high",
231
+ blastTier?: BlastTier,
232
+ ): AutonomyDecision {
233
+ const reasons: string[] = [];
234
+ const minRank = SEVERITY_RANK[threshold];
235
+ const escalating = concerns.filter(
236
+ (c) => c.category === "security" && SEVERITY_RANK[c.severity] >= minRank,
237
+ );
238
+ if (escalating.length > 0) {
239
+ const top = escalating.reduce((max, c) =>
240
+ SEVERITY_RANK[c.severity] > SEVERITY_RANK[max.severity] ? c : max,
241
+ );
242
+ reasons.push(
243
+ `${escalating.length} security concern(s) at/above the ${threshold} autonomy threshold (highest: ${top.severity})`,
244
+ );
245
+ }
246
+ if (blastTier === "high") {
247
+ reasons.push(
248
+ "high blast radius — the fix touches more than 10 resources or spans more than one module",
249
+ );
250
+ }
251
+ return { autonomy: reasons.length > 0 ? "needs-human" : "auto", reasons };
252
+ }
253
+
254
+ // --- inline suggested changes --------------------------------------
255
+
256
+ export interface SuggestionDecision {
257
+ /** true ⇒ post a GitHub one-click `suggestion` instead of opening a full PR. */
258
+ suggest: boolean;
259
+ reason: string;
260
+ }
261
+
262
+ /**
263
+ * decide whether a fix is small/low-risk enough to post as a GitHub
264
+ * one-click **suggested change** (a ` ```suggestion ` block on the existing PR)
265
+ * rather than opening a whole `remediate/*` branch + PR. Much lower friction for
266
+ * trivial fixes. Only when ALL hold: there IS an existing PR context (a comment
267
+ * trigger on a PR); the group is `low`/`info` severity; the fix is a single hunk
268
+ * in a single file; and the blast radius (when known) is `low`. Anything bigger
269
+ * keeps full-PR mode.
270
+ */
271
+ export function shouldSuggestInline(opts: {
272
+ hasPrContext: boolean;
273
+ severity: Severity;
274
+ fileCount: number;
275
+ hunkCount: number;
276
+ blastTier?: BlastTier | undefined;
277
+ }): SuggestionDecision {
278
+ if (!opts.hasPrContext)
279
+ return {
280
+ suggest: false,
281
+ reason: "no existing PR to attach a suggestion to",
282
+ };
283
+ if (opts.severity !== "low" && opts.severity !== "info") {
284
+ return {
285
+ suggest: false,
286
+ reason: `severity ${opts.severity} warrants a reviewable PR, not a one-click suggestion`,
287
+ };
288
+ }
289
+ if (opts.fileCount > 1 || opts.hunkCount > 1) {
290
+ return {
291
+ suggest: false,
292
+ reason: "multi-hunk / multi-file fix — open a full PR",
293
+ };
294
+ }
295
+ if (opts.blastTier === "high" || opts.blastTier === "medium") {
296
+ return {
297
+ suggest: false,
298
+ reason: `blast radius ${opts.blastTier} — open a full PR`,
299
+ };
300
+ }
301
+ return {
302
+ suggest: true,
303
+ reason:
304
+ "single-hunk low-risk fix on an existing PR — post as a one-click suggestion",
305
+ };
306
+ }
307
+
308
+ // --- confidence labeling -------------------------------------------
309
+
310
+ export type Confidence = "high" | "medium" | "low";
311
+
312
+ export interface ConfidenceSignals {
313
+ /** every targeted concern id was cleared by the re-scan. */
314
+ verified: boolean;
315
+ /** count of NEW concern ids the fix introduced (0 is good). */
316
+ regressionCount: number;
317
+ /** second plan matched the first. undefined when plan didn't run. */
318
+ idempotent?: boolean | undefined;
319
+ /** blast tier. undefined when plan didn't run. */
320
+ blastTier?: BlastTier | undefined;
321
+ /** cost direction. undefined when infracost didn't run. */
322
+ costDirection?: CostDelta["direction"] | undefined;
323
+ }
324
+
325
+ export interface ConfidenceResult {
326
+ level: Confidence;
327
+ reasons: string[];
328
+ }
329
+
330
+ /**
331
+ * Derive a fix's confidence DETERMINISTICALLY from the verification evidence
332
+ * already gathered — never a model self-assessment, which keeps it honest.
333
+ *
334
+ * - A fix that didn't verify or introduced a regression is `low`:
335
+ * the proof failed, full stop.
336
+ * - Otherwise it starts `high` and is capped to `medium` by any weaker signal:
337
+ * a non-deterministic plan (`idempotent: false`), a `high` blast radius,
338
+ * a cost increase, or a signal that was *skipped* (plan /
339
+ * infracost didn't run, so we have less proof — `high` requires the full
340
+ * stack). A skipped signal lowers confidence but does not, by itself, make a
341
+ * verified, regression-free fix `low`.
342
+ */
343
+ export function computeConfidence(
344
+ signals: ConfidenceSignals,
345
+ ): ConfidenceResult {
346
+ const reasons: string[] = [];
347
+ if (!signals.verified) {
348
+ return {
349
+ level: "low",
350
+ reasons: [
351
+ "the re-scan did not confirm every targeted concern was resolved",
352
+ ],
353
+ };
354
+ }
355
+ if (signals.regressionCount > 0) {
356
+ return {
357
+ level: "low",
358
+ reasons: [
359
+ `the fix introduced ${signals.regressionCount} new concern(s) (regression)`,
360
+ ],
361
+ };
362
+ }
363
+ reasons.push(
364
+ "re-scan verified every targeted concern resolved with no regressions",
365
+ );
366
+
367
+ let level: Confidence = "high";
368
+ const capMedium = (reason: string) => {
369
+ if (level === "high") level = "medium";
370
+ reasons.push(reason);
371
+ };
372
+ if (signals.idempotent === false)
373
+ capMedium("plan is non-deterministic — a perpetual-diff smell");
374
+ if (signals.blastTier === "high")
375
+ capMedium("high blast radius — review carefully");
376
+ if (signals.costDirection === "increase")
377
+ capMedium("the fix increases monthly cost");
378
+ if (signals.idempotent === undefined || signals.blastTier === undefined) {
379
+ capMedium(
380
+ "no terraform plan evidence (no cloud credentials) — idempotency and blast radius unproven",
381
+ );
382
+ }
383
+ if (signals.costDirection === undefined) {
384
+ capMedium("no cost evidence (infracost did not run)");
385
+ }
386
+ return { level, reasons };
387
+ }
388
+
389
+ // --- honest refusal --------------------------------------------------
390
+
391
+ export interface RefusalDecision {
392
+ /** true ⇒ this concern needs a human decision; prefer a structured non-fix
393
+ * (an issue) over guessing a fix that could break the stack. */
394
+ refuse: boolean;
395
+ reason?: string;
396
+ }
397
+
398
+ // concern signatures whose correct fix needs information only a human has —
399
+ // auto-"fixing" them risks a narrow policy that breaks the stack or a wrong
400
+ // value. Matched against the rule id + evidence text (lower-cased).
401
+ const HUMAN_DECISION_SIGNATURES: { test: RegExp; reason: string }[] = [
402
+ {
403
+ test: /least[\s_-]?privilege|wildcard|iam.*("\*"|\*\s*action|action.*\*)|policy.*allows? all/i,
404
+ reason:
405
+ "narrowing an IAM policy needs the exact action/resource set the workload uses — a human decision",
406
+ },
407
+ {
408
+ test: /\bkms\b.*\bpolicy\b|key[\s_-]?policy|cmk.*polic/i,
409
+ reason:
410
+ "a KMS/CMK key policy needs the real principals and grants — a human decision",
411
+ },
412
+ {
413
+ test: /allowed?[\s_-]?cidr|restrict.*cidr|specify.*cidr|known.*ip|real.*source/i,
414
+ reason:
415
+ "tightening an ingress CIDR needs the real allowed source — a human decision",
416
+ },
417
+ ];
418
+
419
+ /**
420
+ * advisory check: would auto-fixing this concern require a judgement only
421
+ * a human can make? If so, the Remediate flow should post a STRUCTURED refusal
422
+ * (an issue describing the concern, why it won't auto-fix, and what a human
423
+ * should do) rather than guess a fix that could break the stack. Deterministic
424
+ * and conservative — it only flags the well-known human-decision classes.
425
+ */
426
+ export function classifyRefusal(
427
+ concern: Pick<Concern, "rule_id" | "evidence">,
428
+ ): RefusalDecision {
429
+ const text = `${concern.rule_id} ${concern.evidence}`.toLowerCase();
430
+ for (const sig of HUMAN_DECISION_SIGNATURES) {
431
+ if (sig.test.test(text)) return { refuse: true, reason: sig.reason };
432
+ }
433
+ return { refuse: false };
434
+ }
435
+
436
+ /**
437
+ * format a structured non-fix for a concern Infraweaver won't auto-fix. The
438
+ * output is a Markdown issue body: what's wrong, why it isn't auto-fixed, and
439
+ * the concrete next step for a human. Pure (string in → string out).
440
+ */
441
+ export function buildRefusalReport(input: {
442
+ concern: Pick<Concern, "rule_id" | "evidence" | "location">;
443
+ whyNoAutoFix: string;
444
+ humanAction: string;
445
+ }): string {
446
+ const { concern, whyNoAutoFix, humanAction } = input;
447
+ const loc = `${concern.location.file}${concern.location.line ? `:${concern.location.line}` : ""}`;
448
+ return [
449
+ `### ${PRODUCT_NAME} won't auto-fix \`${concern.rule_id}\` (needs a human decision)`,
450
+ "",
451
+ `**Where:** \`${loc}\``,
452
+ "",
453
+ `**What's wrong:** ${concern.evidence}`,
454
+ "",
455
+ `**Why it isn't auto-fixed:** ${whyNoAutoFix}`,
456
+ "",
457
+ `**What a human should do:** ${humanAction}`,
458
+ "",
459
+ `_${PRODUCT_NAME} opens a PR only when it can prove the fix is correct; for this concern it can't, so it's surfaced here instead of guessing._`,
460
+ ].join("\n");
461
+ }
462
+
463
+ // --- fix once, prevent forever ---------------------------------------
464
+
465
+ export interface PreventiveControl {
466
+ /** the mechanism that stops this class of concern recurring. */
467
+ mechanism: string;
468
+ /** a copy-pasteable config/CI snippet. */
469
+ snippet: string;
470
+ note: string;
471
+ }
472
+
473
+ /**
474
+ * alongside the patch, suggest the guardrail that stops the concern
475
+ * RECURRING: a CI gate keyed on the producing scanner. Deterministic by source
476
+ * (the scanner is the right enforcement point), parameterised by the rule id.
477
+ * Returns null for sources with no natural preventive gate.
478
+ */
479
+ export function preventiveControlFor(
480
+ concern: Pick<Concern, "source" | "rule_id">,
481
+ ): PreventiveControl | null {
482
+ // strip only the leading `<source>:` namespace (not every colon) so a rule
483
+ // name that itself contains a colon survives intact.
484
+ const prefix = `${concern.source}:`;
485
+ const bareRule = concern.rule_id.startsWith(prefix)
486
+ ? concern.rule_id.slice(prefix.length)
487
+ : concern.rule_id;
488
+ switch (concern.source) {
489
+ case "checkov":
490
+ return {
491
+ mechanism: "Checkov hard-fail in CI",
492
+ snippet: `# .checkov.yaml\nhard-fail-on:\n - ${bareRule}`,
493
+ note: `Add ${bareRule} to a Checkov hard-fail list so a PR that reintroduces it fails CI.`,
494
+ };
495
+ case "trivy":
496
+ return {
497
+ mechanism: "Trivy config scan gate in CI",
498
+ snippet: `# CI step\ntrivy config --exit-code 1 --severity HIGH,CRITICAL .`,
499
+ note: `Gate PRs on \`trivy config\` so ${bareRule} (and peers) can't be reintroduced.`,
500
+ };
501
+ case "tflint":
502
+ return {
503
+ mechanism: "tflint rule enforced in CI",
504
+ snippet: `# .tflint.hcl\nrule "${bareRule}" {\n enabled = true\n}`,
505
+ note: `Enable ${bareRule} in \`.tflint.hcl\` and run \`tflint\` in CI.`,
506
+ };
507
+ case "terraform-fmt":
508
+ return {
509
+ mechanism: "terraform fmt check in CI",
510
+ snippet: `# CI step\nterraform fmt -check -recursive`,
511
+ note: "Gate PRs on `terraform fmt -check` so formatting can't drift.",
512
+ };
513
+ case "terraform-validate":
514
+ return {
515
+ mechanism: "terraform validate in CI",
516
+ snippet: `# CI step\nterraform validate`,
517
+ note: "Run `terraform validate` in CI so this correctness error can't return.",
518
+ };
519
+ case NATIVE_SOURCE:
520
+ return {
521
+ mechanism: `${PRODUCT_NAME} PR gate (findings-gate output)`,
522
+ // The native pack always runs — there is no rule to "enable". The
523
+ // preventive control is therefore the PR gate itself: run Assess on every
524
+ // PR and fail the check on `findings-gate`, which trips on any NEW
525
+ // high/critical finding relative to the committed baseline.
526
+ snippet: [
527
+ "# .github/workflows/infraweaver-pr-gate.yml",
528
+ "- id: assess",
529
+ " uses: infraweaver-io/infraweaver-action@main",
530
+ " with:",
531
+ " mode: assess",
532
+ "- if: steps.assess.outputs.findings-gate == 'fail'",
533
+ " run: exit 1 # a NEW high/critical finding, or a required_controls regression",
534
+ ].join("\n"),
535
+ note:
536
+ `${bareRule} comes from ${PRODUCT_NAME}'s own rule pack, which runs on every scan — no rule to ` +
537
+ "enable, so the gate is the control. Commit a `.infraweaver.baseline.json` first so the gate fires " +
538
+ "on NEW findings only.",
539
+ };
540
+ default:
541
+ return null;
542
+ }
543
+ }
544
+
545
+ // --- cross-tool co-location ------------------------------------------
546
+
547
+ export interface LocationCluster {
548
+ file: string;
549
+ line: number | null;
550
+ /** the concern ids at this exact location (likely the same underlying defect). */
551
+ concern_ids: string[];
552
+ /** the distinct scanners that flagged this location. */
553
+ sources: string[];
554
+ }
555
+
556
+ /**
557
+ * surface concerns that DIFFERENT scanners flagged at the same `file:line`
558
+ * — almost always the same underlying defect (trivy ∩ checkov overlap heavily on
559
+ * e.g. S3 encryption). Reported so the agent writes ONE canonical fix + ONE
560
+ * explanation for the cluster rather than treating each as separate work. This
561
+ * is purely advisory: it NEVER removes a concern from the verification set (a
562
+ * missing id must still provably clear), so it can't drop a real finding. Only
563
+ * clusters spanning more than one scanner are returned.
564
+ */
565
+ export function clusterByLocation(concerns: Concern[]): LocationCluster[] {
566
+ const byLoc = new Map<string, Concern[]>();
567
+ for (const c of concerns) {
568
+ if (c.location.line == null) continue; // a null line isn't a precise co-location
569
+ const key = `${c.location.file}|${c.location.line}`;
570
+ const arr = byLoc.get(key) ?? [];
571
+ arr.push(c);
572
+ byLoc.set(key, arr);
573
+ }
574
+ const clusters: LocationCluster[] = [];
575
+ for (const cs of byLoc.values()) {
576
+ const first = cs[0];
577
+ if (!first) continue; // empty group can't happen (map is populated), but keep the guard explicit
578
+ const sources = [...new Set(cs.map((c) => c.source))].sort();
579
+ if (sources.length < 2) continue; // single-scanner location isn't cross-tool overlap
580
+ clusters.push({
581
+ file: first.location.file,
582
+ line: first.location.line,
583
+ concern_ids: cs.map((c) => c.id).sort(),
584
+ sources,
585
+ });
586
+ }
587
+ return clusters.sort(
588
+ (a, b) => a.file.localeCompare(b.file) || (a.line ?? 0) - (b.line ?? 0),
589
+ );
590
+ }