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,138 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { type } from "arktype";
4
+ import { PRODUCT_NAME } from "#app/brand";
5
+ import type { LocalToolContext } from "#app/mcp/localContext";
6
+ import { resolveWithinCwd } from "#app/mcp/pathSafety";
7
+ import { execute, tool, toolOk } from "#app/mcp/shared";
8
+ import { buildConcernResult } from "#app/mcp/terraform/concernResult";
9
+ import { docUrlsForGroup, ruleDocUrl } from "#app/mcp/terraform/decisions";
10
+ import { parseFindingsFile } from "#app/mcp/terraform/findings";
11
+ import {
12
+ type Concern,
13
+ SEVERITY_RANK,
14
+ type Severity,
15
+ skipResult,
16
+ } from "#app/mcp/terraform/types";
17
+ import { UNTRUSTED_DATA_NOTICE } from "#app/mcp/untrustedContent";
18
+ import { log } from "#app/utils/cli";
19
+
20
+ export const ReadFindingsParams = type({
21
+ "path?": type.string.describe(
22
+ "path to the Assessor's findings.json. Defaults to $INFRAWEAVER_FINDINGS_PATH, then ./findings.json in the workspace.",
23
+ ),
24
+ "severity_threshold?": type(
25
+ "'critical' | 'high' | 'medium' | 'low' | 'info'",
26
+ ).describe(
27
+ "minimum severity to report (default: the run's configured threshold, else low).",
28
+ ),
29
+ "group_by?": type("'file' | 'rule'").describe(
30
+ "'file' (default) makes one group per file; 'rule' groups a single rule across all files into one group.",
31
+ ),
32
+ });
33
+
34
+ export function ReadFindingsTool(ctx: LocalToolContext) {
35
+ return tool({
36
+ name: "read_findings",
37
+ description:
38
+ "Load best-practice concerns from a terraform-reviewer (Assessor) findings.json INSTEAD of running " +
39
+ "the scanners. Returns the SAME { concerns, groups, summary } shape as terraform_scan, so Remediate " +
40
+ "consumes it identically. `human_only` findings and non-Terraform files are dropped. Concerns from " +
41
+ "checkov / tflint / terraform-fmt re-verify deterministically (✗→✓); findings exclusive to the reviewer " +
42
+ `(tfsec / infracost / llm) carry source \`reviewer\` and can't be reproduced by ${PRODUCT_NAME}'s scanners, so ` +
43
+ "terraform_verify_remediation will report them unresolved — rely on terraform_validate + your explanation " +
44
+ "for those. Returns `found: false` (never an error) when no findings.json is present.",
45
+ parameters: ReadFindingsParams,
46
+ execute: execute(async ({ path, severity_threshold, group_by }) => {
47
+ const cwd = ctx.payload.cwd ?? process.cwd();
48
+ // SECURITY: confine the agent-supplied `path` to the workspace so it can't
49
+ // be used as an arbitrary file-read primitive. The env-var fallback is
50
+ // operator-controlled (not agent-controlled), so it stays unconfined.
51
+ const findingsPath = path
52
+ ? resolveWithinCwd(cwd, path)
53
+ : process.env.INFRAWEAVER_FINDINGS_PATH || join(cwd, "findings.json");
54
+ let raw: string;
55
+ try {
56
+ raw = readFileSync(findingsPath, "utf8");
57
+ } catch {
58
+ return skipResult(
59
+ "findings_not_found",
60
+ `no findings.json at ${findingsPath} (set the path arg or $INFRAWEAVER_FINDINGS_PATH)`,
61
+ {
62
+ key: "found",
63
+ reasonKey: "reason",
64
+ extra: { concerns: [], groups: [] },
65
+ },
66
+ );
67
+ }
68
+ let parsed: Concern[];
69
+ try {
70
+ // accept BOTH a terraform-reviewer findings.json AND a standard SARIF
71
+ // report (Trivy/Checkov/tflint -o sarif) — the dispatcher detects which.
72
+ parsed = parseFindingsFile(raw, cwd);
73
+ } catch {
74
+ return skipResult(
75
+ "findings_parse_error",
76
+ `could not parse findings file at ${findingsPath}`,
77
+ {
78
+ key: "found",
79
+ reasonKey: "reason",
80
+ extra: { concerns: [], groups: [] },
81
+ },
82
+ );
83
+ }
84
+
85
+ const configured = ctx.payload.severityThreshold as Severity | undefined;
86
+ const threshold: Severity = severity_threshold ?? configured ?? "low";
87
+ const minRank = SEVERITY_RANK[threshold];
88
+
89
+ // same concerns→result pipeline as terraform_scan (baseline, filter,
90
+ // suppress, group, batch, tally), so the rest of the Remediate checklist is
91
+ // source-agnostic. No diff-scope filter — a findings.json is already scoped.
92
+ const grouping = group_by ?? ctx.payload.grouping ?? "file";
93
+ const autonomyThreshold =
94
+ (ctx.payload.autonomyThreshold as Severity | undefined) ?? "high";
95
+ const {
96
+ active,
97
+ suppressed,
98
+ groups,
99
+ batchPlan,
100
+ bySeverity: by_severity,
101
+ } = buildConcernResult(ctx, parsed, {
102
+ cwd,
103
+ minRank,
104
+ grouping,
105
+ autonomyThreshold,
106
+ });
107
+
108
+ log.info(
109
+ `» read_findings: ${active.length} concern(s) ≥ ${threshold} from ${findingsPath}` +
110
+ (suppressed.length ? ` (${suppressed.length} suppressed)` : "") +
111
+ ` (${groups.length} ${grouping}-group(s))`,
112
+ );
113
+
114
+ return toolOk({
115
+ found: true,
116
+ source_file: findingsPath,
117
+ grouping,
118
+ summary: {
119
+ total: active.length,
120
+ suppressed: suppressed.length,
121
+ groups: groups.length,
122
+ by_severity,
123
+ },
124
+ groups: groups.map((g) => ({
125
+ ...g,
126
+ doc_urls: docUrlsForGroup(g, active),
127
+ })),
128
+ batch_plan: batchPlan,
129
+ concerns: active.map((c) => ({ ...c, doc_url: ruleDocUrl(c) })),
130
+ // LLM01. These concerns were parsed out of a file in the workspace —
131
+ // a SARIF or findings.json the repo supplied — so every `evidence`
132
+ // string arrives from outside with no provenance at all. Same notice as
133
+ // the other concern-producing tools; see `mcp/untrustedContent.ts`.
134
+ untrusted_data: UNTRUSTED_DATA_NOTICE,
135
+ });
136
+ }),
137
+ });
138
+ }
@@ -0,0 +1,303 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { type } from "arktype";
4
+ import type { LocalToolContext } from "#app/mcp/localContext";
5
+ import { walkTfFiles } from "#app/mcp/modules";
6
+ import { execute, tool, toolOk } from "#app/mcp/shared";
7
+ import { buildConcernResult } from "#app/mcp/terraform/concernResult";
8
+ import {
9
+ classifyRefusal,
10
+ clusterByLocation,
11
+ docUrlsForGroup,
12
+ type PreventiveControl,
13
+ preventiveControlFor,
14
+ ruleDocUrl,
15
+ } from "#app/mcp/terraform/decisions";
16
+ import {
17
+ EMPTY_NOISE_PROFILE,
18
+ explainNoiseAdjustment,
19
+ } from "#app/mcp/terraform/noiseProfile";
20
+ import { scoreConcern, topRisks } from "#app/mcp/terraform/risk";
21
+ import { runScannersCached } from "#app/mcp/terraform/scannerCache";
22
+ import { changedTerraformFiles } from "#app/mcp/terraform/scanners";
23
+ import { scanSessionOf } from "#app/mcp/terraform/scanSession";
24
+ import { auditNativeSkips } from "#app/mcp/terraform/suppressions";
25
+ import {
26
+ type Concern,
27
+ SEVERITY_RANK,
28
+ type Severity,
29
+ } from "#app/mcp/terraform/types";
30
+ import { UNTRUSTED_DATA_NOTICE } from "#app/mcp/untrustedContent";
31
+ import { log } from "#app/utils/cli";
32
+ import { resolveModuleCredentialEnv } from "#app/utils/registryAuth";
33
+ import { resolveToolSelection } from "#app/utils/toolSelection";
34
+
35
+ export const TerraformScanParams = type({
36
+ "scan_scope?": type("'full' | 'diff'").describe(
37
+ "'full' (default) scans the whole workspace; 'diff' limits concerns to Terraform files changed vs the base branch.",
38
+ ),
39
+ "severity_threshold?": type(
40
+ "'critical' | 'high' | 'medium' | 'low' | 'info'",
41
+ ).describe(
42
+ "minimum severity to report (default: low). 'info' includes everything.",
43
+ ),
44
+ "group_by?": type("'file' | 'rule'").describe(
45
+ "'file' (default) makes one group per file (smaller blast radius per PR). 'rule' groups a single rule's concerns across ALL files into one group — use for sweeping, low-risk rules (e.g. 'add tags everywhere') so they become one PR instead of many.",
46
+ ),
47
+ "download_external_modules?": type("boolean").describe(
48
+ "default false. When true, checkov resolves and scans INSIDE registry/git modules — REQUIRED for the refactor b.4 path (adopting a third-party module): scanning with it OFF would stop looking inside the adopted module and silently drop its findings (a false ✗→✓ / 'finding-laundering'). Leave off for normal Terraform-only scans (faster, offline).",
49
+ ),
50
+ });
51
+
52
+ export interface ScanScanners {
53
+ runScannersCached: typeof runScannersCached;
54
+ changedTerraformFiles: typeof changedTerraformFiles;
55
+ }
56
+
57
+ const realScanners: ScanScanners = { runScannersCached, changedTerraformFiles };
58
+
59
+ export async function runScan(
60
+ ctx: LocalToolContext,
61
+ {
62
+ scan_scope,
63
+ severity_threshold,
64
+ group_by,
65
+ download_external_modules,
66
+ }: typeof TerraformScanParams.infer,
67
+ scanners: ScanScanners = realScanners,
68
+ ) {
69
+ const cwd = ctx.payload.cwd ?? process.cwd();
70
+ // precedence: explicit tool arg > the run's configured severity_threshold > "low"
71
+ const configured = ctx.payload.severityThreshold as Severity | undefined;
72
+ const threshold: Severity = severity_threshold ?? configured ?? "low";
73
+ const minRank = SEVERITY_RANK[threshold];
74
+ const scope = scan_scope ?? ctx.payload.scanScope ?? "full";
75
+
76
+ // the unified tool selection (licence gate + allow/deny) and the optional
77
+ // module credentials (git-transport + private-registry token), both derived
78
+ // from the run payload so the scan and its ✗→✓ verification re-scan see the
79
+ // identical toolchain.
80
+ const selection = resolveToolSelection(ctx.payload);
81
+ const terraformEnv = resolveModuleCredentialEnv(ctx.payload, {
82
+ sshKeyDir: ctx.tmpdir,
83
+ });
84
+ if (selection.unknownTokens.length > 0) {
85
+ log.warning(
86
+ `» tools_enabled: ignoring unrecognised tool(s) [${selection.unknownTokens.join(", ")}]`,
87
+ );
88
+ }
89
+
90
+ // read-only scan path: cache by Terraform-tree content hash so a repeated
91
+ // scan on an UNCHANGED tree is instant. Self-invalidating (any edit busts the
92
+ // hash). The ✗→✓ verifier below uses runScanners DIRECTLY — never the cache —
93
+ // so verification always re-scans fresh.
94
+ const outcomes = scanners.runScannersCached(cwd, {
95
+ selection,
96
+ terraformEnv,
97
+ downloadExternalModules: download_external_modules,
98
+ toolsNetwork: ctx.payload.toolsNetwork,
99
+ });
100
+ if (ctx.payload.toolsNetwork?.unknown.length) {
101
+ log.warning(
102
+ `» tools_network: ignoring unrecognised token(s) [${ctx.payload.toolsNetwork.unknown.join(", ")}]`,
103
+ );
104
+ }
105
+
106
+ // diff scope: keep only concerns in Terraform files changed vs the base.
107
+ let scopeNote: string | undefined;
108
+ let changed: Set<string> | null = null;
109
+ if (scope === "diff") {
110
+ changed = scanners.changedTerraformFiles(cwd);
111
+ if (changed === null) {
112
+ scopeNote =
113
+ "diff scope requested but the base branch could not be determined — scanned full instead";
114
+ }
115
+ }
116
+ const inScope = (c: Concern): boolean =>
117
+ changed === null
118
+ ? true
119
+ : changed.has(c.location.file.replace(/\\/g, "/").replace(/^\.\//, ""));
120
+
121
+ // grouping mode: by-file (default, smaller per-PR blast radius) or
122
+ // by-rule (one PR per rule across all files — for sweeping low-risk rules).
123
+ const grouping = group_by ?? ctx.payload.grouping ?? "file";
124
+ const autonomyThreshold =
125
+ (ctx.payload.autonomyThreshold as Severity | undefined) ?? "high";
126
+ // shared concerns→result pipeline (baseline capture, filter, suppress, group,
127
+ // batch, tally) — also backs read_findings so both stay source-agnostic.
128
+ const result = buildConcernResult(
129
+ ctx,
130
+ outcomes.flatMap((o) => o.concerns),
131
+ {
132
+ cwd,
133
+ minRank,
134
+ grouping,
135
+ autonomyThreshold,
136
+ inScope,
137
+ },
138
+ );
139
+ const {
140
+ active,
141
+ suppressed,
142
+ groups,
143
+ batchPlan,
144
+ bySeverity: by_severity,
145
+ } = result;
146
+ // the reported (post-filter) set — read at end-of-run by finalizeSuccessRun
147
+ // to emit the SARIF artifact + findings outputs.
148
+ scanSessionOf(ctx.toolState).lastScanConcerns = result.annotated;
149
+ // G7: the org's fleet-wide dismissal record, seeded at run start. Affects the
150
+ // ORDER of `top_risks` / `risk_score` only — every concern above is already
151
+ // final by this point, and nothing here can remove one.
152
+ const noiseProfile =
153
+ scanSessionOf(ctx.toolState).noiseProfile ?? EMPTY_NOISE_PROFILE;
154
+
155
+ // Native scanner-skip directives (#checkov:skip / #tfsec:ignore / #trivy:ignore)
156
+ // are honoured by their OWN scanner — so the finding never reaches Infraweaver's
157
+ // gate or posture. Surface the ORPHANS (a skip with no corresponding concern)
158
+ // so a directive that silently hides a real issue stays auditable. Matched
159
+ // against the raw scan set (pre-threshold) so a below-threshold concern that
160
+ // DID fire isn't reported as a false orphan.
161
+ const nativeSkipAudit = auditNativeSkips(
162
+ walkTfFiles(cwd),
163
+ outcomes.flatMap((o) => o.concerns),
164
+ (rel) => {
165
+ try {
166
+ return readFileSync(join(cwd, rel), "utf-8");
167
+ } catch {
168
+ return null;
169
+ }
170
+ },
171
+ );
172
+
173
+ // concerns different scanners flagged at the same file:line (one
174
+ // canonical fix); concerns whose fix needs a human decision (prefer
175
+ // a structured refusal); the preventive control per distinct rule.
176
+ const coLocated = clusterByLocation(active);
177
+ const refusalCandidates = active
178
+ .map((c) => ({ id: c.id, ...classifyRefusal(c) }))
179
+ .filter((r) => r.refuse)
180
+ .map((r) => ({ concern_id: r.id, reason: r.reason }));
181
+ const prevention: Record<string, PreventiveControl> = {};
182
+ for (const c of active) {
183
+ if (prevention[c.rule_id]) continue;
184
+ const control = preventiveControlFor(c);
185
+ if (control) prevention[c.rule_id] = control;
186
+ }
187
+
188
+ const ran = outcomes.filter((o) => o.ran).map((o) => o.source);
189
+ const skippedScanners = outcomes
190
+ .filter((o) => !o.ran)
191
+ .map((o) => ({ source: o.source, reason: o.skipped_reason }));
192
+ const partialScanners = outcomes.flatMap((o) =>
193
+ o.partial ? [o.partial] : [],
194
+ );
195
+
196
+ log.info(
197
+ `» terraform_scan: ${active.length} concern(s) ≥ ${threshold} from [${ran.join(", ")}] ` +
198
+ `(${groups.length} ${grouping}-group(s))` +
199
+ (suppressed.length ? ` (${suppressed.length} suppressed)` : "") +
200
+ (skippedScanners.length
201
+ ? ` (skipped: ${skippedScanners.map((s) => s.source).join(", ")})`
202
+ : ""),
203
+ );
204
+
205
+ return toolOk({
206
+ scanned_dir: cwd,
207
+ scope: changed === null ? "full" : "diff",
208
+ ...(scopeNote ? { scope_note: scopeNote } : {}),
209
+ grouping,
210
+ scanners_ran: ran,
211
+ scanners_skipped: skippedScanners,
212
+ // A scanner can RUN and still be partially blind (e.g. the native pack's
213
+ // persisted-secret rule without a provider schema). Listing that here keeps
214
+ // `scanners_ran` from implying full coverage; omitted entirely when empty so
215
+ // the common result shape is unchanged.
216
+ ...(partialScanners.length ? { scanners_partial: partialScanners } : {}),
217
+ // licence-aware tool posture: which non-permissive tools are off
218
+ // for want of an explicit opt-in, and which were explicitly disabled.
219
+ tool_selection: {
220
+ licence_gated: selection.gated,
221
+ disabled: selection.disabled,
222
+ },
223
+ summary: {
224
+ total: active.length,
225
+ suppressed: suppressed.length,
226
+ groups: groups.length,
227
+ by_severity,
228
+ },
229
+ groups: groups.map((g) => ({
230
+ ...g,
231
+ doc_urls: docUrlsForGroup(g, active),
232
+ })),
233
+ batch_plan: batchPlan,
234
+ // cross-tool co-location, refusal candidates, prevention.
235
+ co_located: coLocated,
236
+ refusal_candidates: refusalCandidates,
237
+ prevention,
238
+ top_risks: topRisks(active, 5, noiseProfile),
239
+ concerns: active.map((c) => {
240
+ const risk = scoreConcern(c, noiseProfile);
241
+ return {
242
+ ...c,
243
+ doc_url: ruleDocUrl(c),
244
+ risk_score: risk.score,
245
+ // G7: never demote silently — the reason travels with the finding.
246
+ ...(risk.noise
247
+ ? { noise_reason: explainNoiseAdjustment(risk.noise) }
248
+ : {}),
249
+ };
250
+ }),
251
+ ...(nativeSkipAudit.orphans.length
252
+ ? {
253
+ orphan_native_skips: nativeSkipAudit.orphans.map((s) => ({
254
+ file: s.file,
255
+ line: s.line,
256
+ tool: s.tool,
257
+ rule: s.rule,
258
+ reason: s.reason,
259
+ ...(s.expires ? { expires: s.expires } : {}),
260
+ })),
261
+ }
262
+ : {}),
263
+ ...(suppressed.length
264
+ ? {
265
+ suppressed: suppressed.map((c) => ({
266
+ id: c.id,
267
+ rule_id: c.rule_id,
268
+ severity: c.severity,
269
+ location: c.location,
270
+ suppression: c.suppression,
271
+ })),
272
+ }
273
+ : {}),
274
+ // LLM01. `evidence`, `remediation_hint` and `code_snippet` are written by
275
+ // third-party scanners over source authored by the party under examination,
276
+ // and `orphan_native_skips[].reason` / `suppression.reason` come straight
277
+ // out of a comment in that source (`# checkov:skip=CKV_X: <anything>`). This
278
+ // result is structurally identical to `ingest_external_findings`, which has
279
+ // carried the notice since it shipped — the asymmetry was the defect, not
280
+ // the presence of the field.
281
+ untrusted_data: UNTRUSTED_DATA_NOTICE,
282
+ });
283
+ }
284
+
285
+ export function TerraformScanTool(ctx: LocalToolContext) {
286
+ return tool({
287
+ name: "terraform_scan",
288
+ description:
289
+ "Scan the Terraform in the workspace against best practices using the deterministic check tools " +
290
+ "(terraform fmt, terraform validate, tflint, trivy, checkov). Returns a stable, severity-ranked " +
291
+ "list of `concerns` — each is one best-practice issue with a content-derived `id`, the producing " +
292
+ "`source`, `rule_id`, `severity`, the `location` (file + line), and a `remediation_hint`. Concerns " +
293
+ "are also rolled up into `groups` (one per file): different scanners flag the same defect under " +
294
+ "different rule ids, so remediate ONE group per PR (its `id` is the branch/PR key; its `concern_ids` " +
295
+ "are what the ✗→✓ re-scan must confirm cleared) rather than one PR per concern. Scanners that aren't " +
296
+ "installed are reported as skipped (they never fail the scan). Also returns `co_located` (concerns " +
297
+ "different scanners flagged at the same file:line — fix as ONE canonical change), " +
298
+ "`refusal_candidates` (concerns whose fix needs a human decision — prefer a structured refusal over " +
299
+ "guessing), and `prevention` (a CI guardrail per rule that stops it recurring).",
300
+ parameters: TerraformScanParams,
301
+ execute: execute(async (input) => runScan(ctx, input)),
302
+ });
303
+ }
@@ -0,0 +1,160 @@
1
+ import { type } from "arktype";
2
+ import type { LocalToolContext } from "#app/mcp/localContext";
3
+ import { execute, tool, toolOk } from "#app/mcp/shared";
4
+ import { resolveRoots } from "#app/mcp/terraform/paths";
5
+ import { scanSessionOf } from "#app/mcp/terraform/scanSession";
6
+ import {
7
+ checkArgumentsAgainstSchema,
8
+ collectProviderRequirements,
9
+ scanFmt,
10
+ scanTflint,
11
+ scanValidate,
12
+ } from "#app/mcp/terraform/scanners";
13
+ import { dedupe, skipped, sortConcerns } from "#app/mcp/terraform/types";
14
+ import { UNTRUSTED_DATA_NOTICE } from "#app/mcp/untrustedContent";
15
+ import { resolveModuleCredentialEnv } from "#app/utils/registryAuth";
16
+ import { resolveToolSelection } from "#app/utils/toolSelection";
17
+
18
+ export const TerraformValidateParams = type({
19
+ "paths?": type.string
20
+ .array()
21
+ .describe(
22
+ "optional list of file globs/paths to limit fmt+lint to; omit to check the whole workspace",
23
+ ),
24
+ });
25
+
26
+ export interface ValidateScanners {
27
+ scanFmt: typeof scanFmt;
28
+ scanValidate: typeof scanValidate;
29
+ scanTflint: typeof scanTflint;
30
+ collectProviderRequirements: typeof collectProviderRequirements;
31
+ checkArgumentsAgainstSchema: typeof checkArgumentsAgainstSchema;
32
+ }
33
+
34
+ const realScanners: ValidateScanners = {
35
+ scanFmt,
36
+ scanValidate,
37
+ scanTflint,
38
+ collectProviderRequirements,
39
+ checkArgumentsAgainstSchema,
40
+ };
41
+
42
+ export async function runValidate(
43
+ ctx: LocalToolContext,
44
+ scanners: ValidateScanners = realScanners,
45
+ ) {
46
+ const cwd = ctx.payload.cwd ?? process.cwd();
47
+ // honour the same licence gate + module-fetch credential as
48
+ // terraform_scan: tflint (MPL-2.0) runs only when opted in; validate's init
49
+ // gets the credential so a private cross-repo module resolves.
50
+ const selection = resolveToolSelection(ctx.payload);
51
+ const terraformEnv = resolveModuleCredentialEnv(ctx.payload, {
52
+ sshKeyDir: ctx.tmpdir,
53
+ });
54
+ // `terraform validate` runs per-root (multi-root aware); fmt + tflint are
55
+ // recursive over the whole tree.
56
+ const fmtOutcome = scanners.scanFmt(cwd);
57
+ const validateOutcome = scanners.scanValidate(cwd, terraformEnv);
58
+ const tflintOutcome = selection.enabled("tflint")
59
+ ? scanners.scanTflint(cwd)
60
+ : skipped(
61
+ "tflint",
62
+ selection.offReason("tflint") ?? "disabled by tools_enabled",
63
+ );
64
+
65
+ // PRE-EXISTING `terraform fmt` drift (captured by the pre-fix scan, before
66
+ // any edit) is not this run's to fix: forcing a whole-file reformat to
67
+ // clear style drift the file already had is exactly what bloats a scoped
68
+ // remediation diff. Such drift is surfaced as advisory `preexisting_fmt_drift`
69
+ // and kept out of the `passed` gate; only NEW drift (a file the agent's
70
+ // edit left unformatted) gates. With no baseline captured (validate called
71
+ // before any scan), fall back to gating on all drift — pre-change behaviour.
72
+ const baselineDrift = scanSessionOf(ctx.toolState).baselineFmtDriftFiles;
73
+ const isPreexisting = (c: (typeof fmtOutcome.concerns)[number]) =>
74
+ baselineDrift?.includes(c.location.file) ?? false;
75
+ const gatingFmtConcerns = fmtOutcome.concerns.filter((c) => !isPreexisting(c));
76
+ const preexistingFmtDrift = [
77
+ ...new Set(
78
+ fmtOutcome.concerns.filter(isPreexisting).map((c) => c.location.file),
79
+ ),
80
+ ];
81
+
82
+ const remaining = sortConcerns(
83
+ dedupe([
84
+ ...gatingFmtConcerns,
85
+ ...validateOutcome.concerns,
86
+ ...tflintOutcome.concerns,
87
+ ]),
88
+ );
89
+ const ran = [fmtOutcome, validateOutcome, tflintOutcome]
90
+ .filter((c) => c.ran)
91
+ .map((c) => c.source);
92
+ // count of roots where terraform ran but `validate -json` couldn't be
93
+ // parsed — a real failure, not a clean tree. We genuinely don't know if
94
+ // those roots are valid, so `passed` must fail closed below rather than
95
+ // silently treating an un-validated root as passing.
96
+ const unvalidatedRoots = validateOutcome.unvalidated ?? 0;
97
+ // surface the pinned provider majors so the fix targets the right
98
+ // argument schema (deterministic, read straight from required_providers).
99
+ const providers = scanners.collectProviderRequirements(cwd);
100
+ const roots = resolveRoots(cwd).map((r) => r.relDir || ".");
101
+ // cross-check the arguments actually written in the workspace
102
+ // against the INSTALLED provider's schema, so an argument that's invalid
103
+ // for the pinned provider major (a "correct" fix for the wrong version) is
104
+ // caught here, before the PR, instead of surfacing as a `plan` failure.
105
+ // Deterministic and degrades green (omitted) when the schema isn't
106
+ // available (terraform not installed / dir not init-ed).
107
+ const schemaCheck = scanners.checkArgumentsAgainstSchema(cwd);
108
+ return toolOk({
109
+ // `passed` stays gated on fmt + validate + tflint only — those are
110
+ // authoritative (fmt counts only NEW drift; see preexisting_fmt_drift).
111
+ // The schema cross-check is a high-signal ADVISORY (a conservative HCL
112
+ // parse), surfaced separately so a parser edge case can never wrongly
113
+ // block a valid fix. Fails closed when a root ran but couldn't be
114
+ // validated: an un-validated root is "unknown", not "clean".
115
+ passed: remaining.length === 0 && unvalidatedRoots === 0,
116
+ checks_ran: ran,
117
+ // true when ≥1 Terraform root could not be validated (terraform ran but
118
+ // its `-json` output was unparseable). When set, `passed` is false even
119
+ // with no remaining_issues — re-run after fixing the root's init/state,
120
+ // or inspect it by hand; do NOT treat the clean issue list as a pass.
121
+ validate_incomplete: unvalidatedRoots > 0,
122
+ remaining_issues: remaining,
123
+ // ADVISORY (does not gate `passed`): files that already failed
124
+ // `terraform fmt` at the pre-fix baseline. Reformatting them would pull
125
+ // unrelated lines into a scoped fix — leave them, or sweep repo-wide
126
+ // formatting in a separate `fix rule terraform-fmt` PR.
127
+ preexisting_fmt_drift: preexistingFmtDrift,
128
+ providers,
129
+ // arguments written in the workspace that are NOT in the
130
+ // installed provider's schema (would break `plan` on the pinned major).
131
+ // `schema_checked` is false when the schema was unavailable (terraform
132
+ // not installed / dir not init-ed) — then `unknown_arguments` is empty
133
+ // and you should rely on `terraform_plan` to catch a bad argument.
134
+ schema_checked: schemaCheck.checked,
135
+ unknown_arguments: schemaCheck.unknown_arguments,
136
+ // the Terraform roots `validate` covered (each was init+validate'd).
137
+ roots_validated: roots,
138
+ // LLM01. `remaining_issues[].evidence` is a `terraform validate` diagnostic
139
+ // or a tflint message, and both quote the workspace's own HCL back — an
140
+ // identifier, a string literal, a heredoc. Same channel as the other
141
+ // concern-producing tools; see `mcp/untrustedContent.ts`.
142
+ untrusted_data: UNTRUSTED_DATA_NOTICE,
143
+ });
144
+ }
145
+
146
+ export function TerraformValidateTool(ctx: LocalToolContext) {
147
+ return tool({
148
+ name: "terraform_validate",
149
+ description:
150
+ "Fast pre-PR gate. Runs `terraform fmt -check`, `terraform validate` (per Terraform root — " +
151
+ "multi-root aware, see `roots_validated`), and `tflint` over the " +
152
+ "workspace and returns whether the Terraform is well-formed and idiomatic. Also reports `providers` " +
153
+ "— the pinned provider requirements (name + source + version constraint + resolved `major`): " +
154
+ "honour the pinned major when writing a fix, because argument names and valid blocks differ across " +
155
+ "provider majors, and a 'correct' fix for the wrong major just breaks `plan`. Call this AFTER " +
156
+ "applying a fix and BEFORE opening a PR — never open a PR whose `terraform_validate` did not pass.",
157
+ parameters: TerraformValidateParams,
158
+ execute: execute(async () => runValidate(ctx)),
159
+ });
160
+ }