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,723 @@
1
+ import { type } from "arktype";
2
+ import { COMMENT_COMMAND, PRODUCT_NAME } from "#app/brand";
3
+ import { buildCrosswalkReport, type CrosswalkReport } from "#app/mcp/crosswalk";
4
+ import {
5
+ cyberEssentialsControlsAtRisk,
6
+ cyberEssentialsScore,
7
+ } from "#app/mcp/cyberEssentials";
8
+ import type { LocalToolContext } from "#app/mcp/localContext";
9
+ import { execute, tool, toolOk } from "#app/mcp/shared";
10
+ import {
11
+ type BaselineDelta,
12
+ baselineDelta,
13
+ loadBaselineWithMeta,
14
+ } from "#app/mcp/terraform/baseline";
15
+ import {
16
+ applyScannerAvailability,
17
+ checkovFrameworksFor,
18
+ detectIacLanguages,
19
+ type IacLanguage,
20
+ inLanguageScope,
21
+ LANGUAGE_LABEL,
22
+ type LanguageScope,
23
+ languageOfConcern,
24
+ resolveLanguageScope,
25
+ } from "#app/mcp/terraform/iacLanguages";
26
+ import { scanSessionOf } from "#app/mcp/terraform/scanSession";
27
+ import { attachSourceSnippets, runScanners } from "#app/mcp/terraform/scanners";
28
+ import { suppressConcerns } from "#app/mcp/terraform/suppressions";
29
+ import {
30
+ type Concern,
31
+ dedupe,
32
+ type ScannerOutcome,
33
+ SEVERITY_RANK,
34
+ type Severity,
35
+ sortConcerns,
36
+ } from "#app/mcp/terraform/types";
37
+ import {
38
+ buildVerificationSummary,
39
+ type VerificationSummary,
40
+ } from "#app/mcp/terraform/verification";
41
+ import { log } from "#app/utils/cli";
42
+ import { resolveModuleCredentialEnv } from "#app/utils/registryAuth";
43
+ import {
44
+ type ResolvedToolSelection,
45
+ resolveToolSelection,
46
+ } from "#app/utils/toolSelection";
47
+
48
+ /**
49
+ * Assess pillar — the read-only product. Infraweaver's scanner
50
+ * engine has two modes off ONE codebase: Remediate = engine + fix loop + verify;
51
+ * **Assess = engine, read-only**. This surfaces that read-only half as a
52
+ * first-class deliverable: run the deterministic scanners, normalise into the
53
+ * findings schema, map to the compliance crosswalk, and produce a
54
+ * **scorecard** + an auditor-facing markdown report — WITHOUT touching the
55
+ * Terraform or opening a PR. No cloud credentials, no writes.
56
+ *
57
+ * The scorecard is deterministic (computed from tool results, never the model's
58
+ * word) so a CI gate can branch on `posture` and an assessor gets a reproducible,
59
+ * framework-mapped report.
60
+ */
61
+
62
+ export type AssessPosture = "clean" | "advisory" | "action-required";
63
+
64
+ export interface AssessTopRisk {
65
+ rule_id: string;
66
+ severity: Severity;
67
+ file: string;
68
+ line: number | null;
69
+ evidence: string;
70
+ }
71
+
72
+ /** A finding a human explicitly accepted (a suppression with a mandatory reason).
73
+ * Surfaced as the report's audit trail — who waived what, why, and until when. */
74
+ export interface AssessWaiver {
75
+ rule_id: string;
76
+ severity: Severity;
77
+ file: string;
78
+ line: number | null;
79
+ reason: string;
80
+ source: "inline" | "policy";
81
+ expires: string | null;
82
+ }
83
+
84
+ /** Per-language breadth line for the multi-language Assess scorecard. Honest by
85
+ * construction: a language is either `scanned` (a scanner covered it) or listed
86
+ * in `unsupported` (detected, but no static scanner can assess it — e.g. Pulumi),
87
+ * never silently dropped. `by_language` counts OPEN concerns per scanned language. */
88
+ export interface AssessmentLanguages {
89
+ /** every IaC language detected in the tree (superset of scanned + unsupported). */
90
+ detected: IacLanguage[];
91
+ /** languages this run reported on (scope ∩ what a scanner can read). */
92
+ scanned: IacLanguage[];
93
+ /** file count per detected language — the breadth signal. */
94
+ file_counts: Partial<Record<IacLanguage, number>>;
95
+ /** open-concern count per scanned language (0 for a clean language). */
96
+ by_language: Partial<Record<IacLanguage, number>>;
97
+ /** detected languages NO installed scanner can statically assess — surfaced,
98
+ * never scanned-and-silent (each with the honest reason). */
99
+ unsupported: { language: IacLanguage; reason: string }[];
100
+ }
101
+
102
+ export interface AssessmentScorecard {
103
+ /** clean (0 concerns) · advisory (only medium/low/info) · action-required (≥1 critical/high). */
104
+ posture: AssessPosture;
105
+ total: number;
106
+ by_severity: Record<Severity, number>;
107
+ /** multi-language breadth: which IaC languages were detected/scanned/unsupported
108
+ * and the open-concern count per scanned language. Terraform-only trees still
109
+ * report here (just one scanned language). */
110
+ languages: AssessmentLanguages;
111
+ /** highest-severity concerns first, capped — the "what to look at first" list. */
112
+ top_risks: AssessTopRisk[];
113
+ compliance: {
114
+ /** frameworks this scan touched (from the crosswalk's by_framework index). */
115
+ frameworks: string[];
116
+ /** distinct controls touched across all frameworks. */
117
+ controls_touched: number;
118
+ /** concerns that mapped to ≥1 control vs none (honest coverage signal). */
119
+ mapped: number;
120
+ unmapped: number;
121
+ version: string;
122
+ reviewed: string;
123
+ };
124
+ /** Cyber Essentials posture (0–100, indicative) + the distinct CE controls the
125
+ * OPEN concerns leave at risk — the report headline a CISO reads first. */
126
+ cyber_essentials: {
127
+ score: number;
128
+ controls_at_risk: string[];
129
+ };
130
+ /** findings a human explicitly accepted — the report's de-emphasised audit
131
+ * trail (excluded from posture/counts above; an expired waiver auto-resurfaces). */
132
+ waivers: AssessWaiver[];
133
+ /** five-status verification taxonomy: per-concern fail / not-code-verifiable +
134
+ * the scanner coverage (verified vs inconclusive). Keeps a "clean" posture
135
+ * honest — e.g. "clean, but tflint inconclusive (not run)". */
136
+ verification: VerificationSummary;
137
+ }
138
+
139
+ /** posture from the severity distribution: any critical/high ⇒ action-required;
140
+ * any lower-severity concern ⇒ advisory; nothing ⇒ clean. */
141
+ export function assessPosture(
142
+ bySeverity: Record<Severity, number>,
143
+ ): AssessPosture {
144
+ if (bySeverity.critical > 0 || bySeverity.high > 0) return "action-required";
145
+ if (bySeverity.medium > 0 || bySeverity.low > 0 || bySeverity.info > 0)
146
+ return "advisory";
147
+ return "clean";
148
+ }
149
+
150
+ const TOP_RISK_CAP = 10;
151
+
152
+ /**
153
+ * Build the deterministic assessment scorecard from a scan's concerns + their
154
+ * crosswalk report. Pure. `concerns` is the OPEN (non-suppressed) severity-sorted,
155
+ * deduped, Terraform-only set — posture, counts and the CE score reflect only what
156
+ * is unresolved; `suppressed` carries the accepted findings for the audit trail.
157
+ * `crosswalk` is `buildCrosswalkReport(concerns)`.
158
+ */
159
+ export function buildAssessment(
160
+ concerns: Concern[],
161
+ crosswalk: CrosswalkReport,
162
+ verification: VerificationSummary,
163
+ suppressed: Concern[] = [],
164
+ languageScope?: LanguageScope,
165
+ ): AssessmentScorecard {
166
+ const by_severity: Record<Severity, number> = {
167
+ critical: 0,
168
+ high: 0,
169
+ medium: 0,
170
+ low: 0,
171
+ info: 0,
172
+ };
173
+ for (const c of concerns) by_severity[c.severity]++;
174
+
175
+ // Multi-language breadth. Default (no scope supplied) = the historical
176
+ // Terraform-only shape, so every existing caller/test keeps a single scanned
177
+ // language without change.
178
+ const by_language: Partial<Record<IacLanguage, number>> = {};
179
+ for (const c of concerns) {
180
+ const lang = languageOfConcern(c);
181
+ by_language[lang] = (by_language[lang] ?? 0) + 1;
182
+ }
183
+ const languages: AssessmentLanguages = languageScope
184
+ ? {
185
+ detected: languageScope.detected,
186
+ scanned: languageScope.scanned,
187
+ file_counts: languageScope.fileCounts,
188
+ by_language,
189
+ unsupported: languageScope.unsupported,
190
+ }
191
+ : {
192
+ detected: ["terraform"],
193
+ scanned: ["terraform"],
194
+ file_counts: {},
195
+ by_language,
196
+ unsupported: [],
197
+ };
198
+
199
+ const top_risks = [...concerns]
200
+ .sort((a, b) => SEVERITY_RANK[b.severity] - SEVERITY_RANK[a.severity])
201
+ .slice(0, TOP_RISK_CAP)
202
+ .map((c) => ({
203
+ rule_id: c.rule_id,
204
+ severity: c.severity,
205
+ file: c.location.file,
206
+ line: c.location.line,
207
+ evidence: c.evidence,
208
+ }));
209
+
210
+ const controls_touched = Object.values(crosswalk.by_framework).reduce(
211
+ (n, controls) => n + controls.length,
212
+ 0,
213
+ );
214
+
215
+ const waivers: AssessWaiver[] = suppressed
216
+ .filter((c) => c.suppression)
217
+ .map((c) => ({
218
+ rule_id: c.rule_id,
219
+ severity: c.severity,
220
+ file: c.location.file,
221
+ line: c.location.line,
222
+ reason: c.suppression?.reason ?? "",
223
+ source: c.suppression?.source ?? "policy",
224
+ expires: c.suppression?.expires ?? null,
225
+ }));
226
+
227
+ return {
228
+ posture: assessPosture(by_severity),
229
+ total: concerns.length,
230
+ by_severity,
231
+ languages,
232
+ top_risks,
233
+ compliance: {
234
+ frameworks: Object.keys(crosswalk.by_framework),
235
+ controls_touched,
236
+ mapped: crosswalk.entries.length,
237
+ unmapped: crosswalk.unmapped_concern_ids.length,
238
+ version: crosswalk.version,
239
+ reviewed: crosswalk.reviewed,
240
+ },
241
+ cyber_essentials: {
242
+ score: cyberEssentialsScore(concerns),
243
+ controls_at_risk: cyberEssentialsControlsAtRisk(concerns),
244
+ },
245
+ waivers,
246
+ verification,
247
+ };
248
+ }
249
+
250
+ const POSTURE_BANNER: Record<AssessPosture, string> = {
251
+ clean:
252
+ "> [!NOTE]\n> ✅ **Clean** — no best-practice concerns found at the configured threshold.",
253
+ advisory:
254
+ "> [!WARNING]\n> ⚠️ **Advisory** — concerns found, but none critical/high. Plan to address them.",
255
+ "action-required":
256
+ "> [!CAUTION]\n> 🚨 **Action required** — critical/high-severity concerns found. Review before relying on this Terraform.",
257
+ };
258
+
259
+ /** Optional render-time context the scorecard can't hold on its own. `delta` is
260
+ * the run-to-run change vs the committed baseline checkpoint (null when no
261
+ * baseline exists — there's nothing to compare against). */
262
+ export interface AssessRenderContext {
263
+ delta?: { delta: BaselineDelta; since: string | null } | null;
264
+ }
265
+
266
+ /** The Cyber Essentials posture headline — the CISO-facing number the report
267
+ * leads with (mirrors a "Failed (22%)"-style lead, but as a 0–100 score). */
268
+ function renderCyberEssentialsHeadline(s: AssessmentScorecard): string {
269
+ const { score, controls_at_risk } = s.cyber_essentials;
270
+ const atRisk = controls_at_risk.length
271
+ ? `${controls_at_risk.length} control(s) at risk: ${controls_at_risk.join(", ")}`
272
+ : "no mapped controls at risk";
273
+ return `> **Cyber Essentials posture: ${score} / 100** (indicative) — ${atRisk}.`;
274
+ }
275
+
276
+ /** The run-to-run delta line — what changed since the baseline checkpoint.
277
+ * Reads "no status changes" when nothing was added or fixed, mirroring the
278
+ * push-to-push signal reviewers expect. */
279
+ function renderDeltaLine(delta: BaselineDelta, since: string | null): string {
280
+ const ref = since ? `baseline (${since})` : "baseline";
281
+ if (delta.added === 0 && delta.fixed === 0) {
282
+ return `**Since ${ref}:** no status changes — ${delta.unchanged} carried.`;
283
+ }
284
+ return (
285
+ `**Since ${ref}:** 🔺 ${delta.added} new · ✅ ${delta.fixed} fixed · ` +
286
+ `${delta.unchanged} unchanged.`
287
+ );
288
+ }
289
+
290
+ /** The accepted/waived findings audit trail — de-emphasised (collapsed), kept out
291
+ * of the posture above, and honest that an expired waiver resurfaces the finding. */
292
+ function renderWaiverBlock(waivers: AssessWaiver[]): string[] {
293
+ const lines: string[] = [];
294
+ lines.push(
295
+ `<details><summary>🤝 Accepted / waived findings (${waivers.length})</summary>`,
296
+ );
297
+ lines.push("");
298
+ lines.push(
299
+ "Explicitly accepted by a human (a suppression with a mandatory reason). " +
300
+ "These do **not** affect the posture above; an expired waiver auto-resurfaces the finding.",
301
+ );
302
+ lines.push("");
303
+ for (const w of waivers) {
304
+ const loc = w.line !== null ? `\`${w.file}:${w.line}\`` : `\`${w.file}\``;
305
+ const expiry = w.expires ? `expires ${w.expires}` : "no expiry";
306
+ lines.push(
307
+ `- ${severityEmoji(w.severity)} \`${w.rule_id}\` — ${loc} — ${w.reason} ` +
308
+ `(source: ${w.source}, ${expiry})`,
309
+ );
310
+ }
311
+ lines.push("");
312
+ lines.push("</details>");
313
+ return lines;
314
+ }
315
+
316
+ /** The in-comment "fix it from here" call-to-action — names the exact
317
+ * `@infraweaver fix …` comment commands a reviewer can post to dispatch a scoped,
318
+ * `✗→✓`-verified remediation PR (the comment-trigger surface; it never merges).
319
+ * Only rendered when there's something open to fix. */
320
+ function renderRemediationCta(): string[] {
321
+ return [
322
+ "> [!TIP]",
323
+ `> **Fix it from here** — comment one of these and ${PRODUCT_NAME} opens a scoped PR that proves ` +
324
+ "each concern cleared (`✗→✓`) for your review (it never merges):",
325
+ ">",
326
+ `> - \`${COMMENT_COMMAND} fix all high-severity\` — every concern at/above a severity`,
327
+ `> - \`${COMMENT_COMMAND} fix #<id>\` — a single finding (the id from Top risks)`,
328
+ `> - \`${COMMENT_COMMAND} fix <file>\` — one file's group`,
329
+ `> - \`${COMMENT_COMMAND} fix all\` — everything (bounded by \`max_prs\`)`,
330
+ ];
331
+ }
332
+
333
+ /**
334
+ * The multi-language breadth section. Renders ONLY when there's something worth
335
+ * saying — Assess scanned a non-Terraform language, or a detected language could
336
+ * not be scanned (the honest coverage gap). A pure Terraform tree returns [] so
337
+ * the report is byte-for-byte unchanged. Deterministic; language order matches
338
+ * `scanned` (most files first). Pure.
339
+ */
340
+ function renderLanguageBlock(langs: AssessmentLanguages): string[] {
341
+ const nonTf = langs.scanned.filter((l) => l !== "terraform");
342
+ if (nonTf.length === 0 && langs.unsupported.length === 0) return [];
343
+
344
+ const lines: string[] = ["### IaC languages scanned", ""];
345
+ const cells = langs.scanned.map((l) => {
346
+ const n = langs.by_language[l] ?? 0;
347
+ return `\`${LANGUAGE_LABEL[l]}: ${n}\``;
348
+ });
349
+ lines.push(
350
+ `Assess is Terraform-first but reads every IaC language your scanners parse. ` +
351
+ `Scanned **${langs.scanned.length}** language(s): ${cells.join(" · ")}.`,
352
+ );
353
+ // Remediate scope caveat — the moat line, stated plainly so a reader never
354
+ // mistakes multi-language Assess for multi-language auto-fix.
355
+ if (nonTf.length > 0) {
356
+ lines.push("");
357
+ lines.push(
358
+ `> Findings outside Terraform are **assessed, not auto-remediated** — the proven \`✗→✓\` fix loop ` +
359
+ `is Terraform-specific. Fix non-Terraform findings by hand using the guidance above.`,
360
+ );
361
+ }
362
+ if (langs.unsupported.length > 0) {
363
+ lines.push("");
364
+ lines.push("> [!WARNING]");
365
+ lines.push(
366
+ "> **Detected but not assessed** (no static scanner covers these — a coverage gap, not a pass):",
367
+ );
368
+ for (const u of langs.unsupported) {
369
+ lines.push(`> - \`${LANGUAGE_LABEL[u.language]}\` — ${u.reason}`);
370
+ }
371
+ }
372
+ return lines;
373
+ }
374
+
375
+ /**
376
+ * Render the scorecard as a deterministic, auditor-facing markdown report (the
377
+ * Assess deliverable). Built entirely from the scorecard so it's reproducible and
378
+ * model-independent. Pure.
379
+ */
380
+ export function renderAssessmentMarkdown(
381
+ s: AssessmentScorecard,
382
+ ctx: AssessRenderContext = {},
383
+ ): string {
384
+ const lines: string[] = [];
385
+ lines.push(POSTURE_BANNER[s.posture]);
386
+ lines.push("");
387
+ lines.push(renderCyberEssentialsHeadline(s));
388
+ lines.push("");
389
+ // Title reflects the breadth honestly: "Terraform" when that's all that was
390
+ // scanned (the common case), "IaC" when Assess widened to other languages.
391
+ const multiLang = s.languages.scanned.some((l) => l !== "terraform");
392
+ lines.push(`## ${multiLang ? "IaC" : "Terraform"} best-practice assessment`);
393
+ lines.push("");
394
+ const sevOrder: Severity[] = ["critical", "high", "medium", "low", "info"];
395
+ const sevCells = sevOrder
396
+ .filter((sev) => s.by_severity[sev] > 0)
397
+ .map((sev) => `\`${sev}: ${s.by_severity[sev]}\``);
398
+ lines.push(
399
+ `**${s.total} concern${s.total === 1 ? "" : "s"}** — ${
400
+ sevCells.length ? sevCells.join(" · ") : "none"
401
+ }`,
402
+ );
403
+ if (ctx.delta) {
404
+ lines.push("");
405
+ lines.push(renderDeltaLine(ctx.delta.delta, ctx.delta.since));
406
+ }
407
+ lines.push("");
408
+
409
+ // Multi-language breadth — only when Assess widened past Terraform, or when a
410
+ // detected language couldn't be scanned (the honest coverage gap). A pure
411
+ // Terraform tree keeps the report exactly as before.
412
+ const langBlock = renderLanguageBlock(s.languages);
413
+ if (langBlock.length > 0) {
414
+ lines.push(...langBlock);
415
+ lines.push("");
416
+ }
417
+
418
+ if (s.top_risks.length > 0) {
419
+ lines.push("### Top risks");
420
+ lines.push("");
421
+ for (const r of s.top_risks) {
422
+ const loc = r.line !== null ? `\`${r.file}:${r.line}\`` : `\`${r.file}\``;
423
+ lines.push(
424
+ `- ${severityEmoji(r.severity)} \`${r.rule_id}\` — ${loc} — ${r.evidence}`,
425
+ );
426
+ }
427
+ lines.push("");
428
+ }
429
+
430
+ lines.push("### Compliance crosswalk");
431
+ lines.push("");
432
+ if (s.compliance.frameworks.length > 0) {
433
+ lines.push(
434
+ `Indicative alignment (crosswalk v${s.compliance.version}, reviewed ${s.compliance.reviewed}) — ` +
435
+ `**not an audit verdict**. Touches ${s.compliance.controls_touched} control(s) across ` +
436
+ `${s.compliance.frameworks.length} framework(s): ${s.compliance.frameworks.join(", ")}.`,
437
+ );
438
+ if (s.compliance.unmapped > 0) {
439
+ lines.push("");
440
+ lines.push(
441
+ `> ${s.compliance.unmapped} concern(s) did not map to a control in the starter rule-pack (honest coverage gap).`,
442
+ );
443
+ }
444
+ } else {
445
+ lines.push(
446
+ "No concerns mapped to a compliance control in the starter rule-pack.",
447
+ );
448
+ }
449
+ lines.push("");
450
+
451
+ // five-status verification taxonomy — keep the posture honest about coverage.
452
+ const v = s.verification;
453
+ lines.push("### Verification coverage");
454
+ lines.push("");
455
+ lines.push(
456
+ `Code-verified by: ${v.coverage.verified.length ? v.coverage.verified.join(", ") : "none"}.`,
457
+ );
458
+ if (v.coverage.inconclusive.length > 0) {
459
+ lines.push("");
460
+ lines.push(
461
+ `> [!WARNING]\n> **Inconclusive** (a coverage gap, not a pass) — these checks did not run:`,
462
+ );
463
+ for (const t of v.coverage.inconclusive)
464
+ lines.push(`> - \`${t.source}\` — ${t.reason}`);
465
+ }
466
+ if (v.counts.not_code_verifiable > 0) {
467
+ lines.push("");
468
+ lines.push(
469
+ `> ${v.counts.not_code_verifiable} concern(s) are **not code-verifiable** — they need a human/process decision (e.g. IAM least-privilege, a KMS key policy) the engine can flag but not prove.`,
470
+ );
471
+ }
472
+ lines.push("");
473
+ lines.push(`_${v.note}_`);
474
+
475
+ if (s.waivers.length > 0) {
476
+ lines.push("");
477
+ lines.push(...renderWaiverBlock(s.waivers));
478
+ }
479
+
480
+ // The "@infraweaver fix …" CTA is offered ONLY when there's a Terraform concern
481
+ // to fix — the fix loop is Terraform-only, so inviting a fix in a repo whose
482
+ // open findings are all CloudFormation/Kubernetes/etc. would advertise a no-op
483
+ // (and contradict the "assessed, not auto-remediated" caveat above). A pure
484
+ // Terraform tree is unchanged (every open concern counts under `terraform`).
485
+ if ((s.languages.by_language.terraform ?? 0) > 0) {
486
+ lines.push("");
487
+ lines.push(...renderRemediationCta());
488
+ }
489
+
490
+ lines.push("");
491
+ lines.push(
492
+ "_Read-only assessment — no Terraform was modified and no PR was opened._",
493
+ );
494
+ return lines.join("\n");
495
+ }
496
+
497
+ function severityEmoji(sev: Severity): string {
498
+ switch (sev) {
499
+ case "critical":
500
+ return "🚨";
501
+ case "high":
502
+ return "⚠️";
503
+ case "medium":
504
+ return "🔶";
505
+ case "low":
506
+ return "ℹ️";
507
+ default:
508
+ return "·";
509
+ }
510
+ }
511
+
512
+ export const TerraformAssessParams = type({
513
+ "severity_threshold?": type(
514
+ "'critical' | 'high' | 'medium' | 'low' | 'info'",
515
+ ).describe(
516
+ "minimum severity to include (default: the run's configured threshold, else low).",
517
+ ),
518
+ });
519
+
520
+ /** the full read-only assessment pipeline: scan (honouring the licence gate
521
+ * + module-fetch credential) → crosswalk → verification taxonomy → scorecard.
522
+ * Shared by `terraform_assess` and the evidence-bundle emitter so both report the
523
+ * identical posture from the identical toolchain. Pure-ish (only the scanners do
524
+ * I/O); no writes. */
525
+ export function runAssessmentPipeline(
526
+ ctx: LocalToolContext,
527
+ threshold: Severity,
528
+ ): {
529
+ cwd: string;
530
+ selection: ResolvedToolSelection;
531
+ /** the resolved multi-language scope (detected / scanned / unsupported). */
532
+ languageScope: LanguageScope;
533
+ outcomes: ScannerOutcome[];
534
+ /** the full annotated set (suppression metadata attached) — what the Cloud report needs. */
535
+ concerns: Concern[];
536
+ /** the OPEN subset — what posture/score/crosswalk reflect. */
537
+ open: Concern[];
538
+ /** the accepted/waived subset — the audit trail. */
539
+ suppressed: Concern[];
540
+ crosswalk: CrosswalkReport;
541
+ verification: VerificationSummary;
542
+ scorecard: AssessmentScorecard;
543
+ /** run-to-run change vs the committed baseline; null when no baseline exists. */
544
+ delta: { delta: BaselineDelta; since: string | null } | null;
545
+ } {
546
+ const cwd = ctx.payload.cwd ?? process.cwd();
547
+ const minRank = SEVERITY_RANK[threshold];
548
+
549
+ // Multi-language Assess: detect which IaC languages the tree contains and
550
+ // resolve the operator's `iac_languages` scope against it. This is a READ-ONLY
551
+ // surface, so it may widen past Terraform — trivy + checkov already parse
552
+ // CloudFormation / Kubernetes / Helm / Dockerfile / ARM / Bicep, and the fix
553
+ // loop stays Terraform-first regardless. Detected-but-unscannable languages
554
+ // (Pulumi) are surfaced as `unsupported`, never scanned-and-silent.
555
+ const languageScope = resolveLanguageScope(
556
+ ctx.payload.iacLanguages,
557
+ detectIacLanguages(cwd),
558
+ );
559
+
560
+ // same licence gate + module credentials (git-transport + private-registry
561
+ // token) as terraform_scan, so a gated tool (e.g. tflint) shows up as
562
+ // INCONCLUSIVE coverage, not a silent pass, and a private cross-repo/registry
563
+ // module still resolves during the read-only scan's init. The resolved selection
564
+ // is returned so the read-only/auditor surfaces can be as transparent about the
565
+ // toolchain (gated / disabled / mistyped tokens) as terraform_scan is — silence
566
+ // on the assess side would read as "fully covered".
567
+ const selection = resolveToolSelection(ctx.payload);
568
+ const outcomes = runScanners(cwd, {
569
+ selection,
570
+ terraformEnv: resolveModuleCredentialEnv(ctx.payload, {
571
+ sshKeyDir: ctx.tmpdir,
572
+ }),
573
+ // scope checkov to the detected/requested languages (trivy scans every format
574
+ // it finds; the language tag + scope filter below keep both honest).
575
+ checkovFrameworks: checkovFrameworksFor(languageScope.scanned),
576
+ toolsNetwork: ctx.payload.toolsNetwork,
577
+ });
578
+ // Re-check the scope against what ACTUALLY ran: a language whose only covering
579
+ // scanner was skipped/disabled this run must report as a coverage gap, never
580
+ // as scanned-clean-with-zero-findings.
581
+ const effectiveScope = applyScannerAvailability(languageScope, {
582
+ trivy: outcomes.some((o) => o.source === "trivy" && o.ran),
583
+ checkov: outcomes.some((o) => o.source === "checkov" && o.ran),
584
+ });
585
+ const inScope = inLanguageScope(effectiveScope);
586
+ const scanned = attachSourceSnippets(
587
+ sortConcerns(dedupe(outcomes.flatMap((o) => o.concerns)))
588
+ .filter(inScope)
589
+ .filter((c) => SEVERITY_RANK[c.severity] >= minRank),
590
+ cwd,
591
+ );
592
+ // Annotate suppressions once, then split. Posture, counts, score and crosswalk
593
+ // reflect only the OPEN findings (a suppression is an accepted finding — kept
594
+ // out of the posture and surfaced in the waiver audit trail instead); `concerns`
595
+ // stays the full annotated set the Cloud report carries.
596
+ const concerns = suppressConcerns(scanned, cwd);
597
+ const open = concerns.filter((c) => !c.suppression);
598
+ const suppressed = concerns.filter((c) => c.suppression);
599
+
600
+ const crosswalk = buildCrosswalkReport(
601
+ open.map((c) => ({
602
+ id: c.id,
603
+ rule_id: c.rule_id,
604
+ evidence: c.evidence,
605
+ category: c.category,
606
+ severity: c.severity,
607
+ location: c.location,
608
+ })),
609
+ );
610
+ const verification = buildVerificationSummary(open, outcomes);
611
+ const scorecard = buildAssessment(
612
+ open,
613
+ crosswalk,
614
+ verification,
615
+ suppressed,
616
+ effectiveScope,
617
+ );
618
+
619
+ // Run-to-run delta vs the committed baseline checkpoint (the engine's own
620
+ // reference point); null when no baseline exists — nothing to compare against.
621
+ const baseline = loadBaselineWithMeta(cwd);
622
+ const delta = baseline
623
+ ? { delta: baselineDelta(open, baseline.keys), since: baseline.created }
624
+ : null;
625
+
626
+ return {
627
+ cwd,
628
+ selection,
629
+ languageScope: effectiveScope,
630
+ outcomes,
631
+ concerns,
632
+ open,
633
+ suppressed,
634
+ crosswalk,
635
+ verification,
636
+ scorecard,
637
+ delta,
638
+ };
639
+ }
640
+
641
+ export function TerraformAssessTool(ctx: LocalToolContext) {
642
+ return tool({
643
+ name: "terraform_assess",
644
+ description:
645
+ "Read-only IaC best-practice ASSESSMENT (the Assess pillar — the scanner engine surfaced as a " +
646
+ "product). Terraform-first, but it also assesses every other IaC language the installed scanners parse " +
647
+ "(CloudFormation, Kubernetes, Helm, Dockerfile, Azure ARM/Bicep, Serverless, Ansible; OpenTofu `.tf`/" +
648
+ "`.tofu`) — scope it with the `iac_languages` input. Runs the deterministic scanners, then returns a " +
649
+ "deterministic `scorecard` (overall `posture` — clean / advisory / action-required, `by_severity` counts, " +
650
+ "a `languages` breadth block, `top_risks`, an indicative compliance-crosswalk summary, and a five-status " +
651
+ "`verification` coverage block) plus a ready-to-post `markdown` report. It NEVER modifies any file or opens " +
652
+ "a PR — use it to report posture (e.g. in a job summary or a CI gate on `posture`). A detected language no " +
653
+ "scanner can read (e.g. Pulumi) is reported under `languages.unsupported`, never silently dropped. Pairs " +
654
+ "with `terraform_emit_sarif` (Security tab), `terraform_emit_evidence` (auditor bundle), and `infracost_diff` " +
655
+ "/ `terraform_version_currency`. For the proven fix loop (Terraform only), use `terraform_scan` + Remediate.",
656
+ parameters: TerraformAssessParams,
657
+ execute: execute(async ({ severity_threshold }) => {
658
+ const configured = ctx.payload.severityThreshold as Severity | undefined;
659
+ const threshold: Severity = severity_threshold ?? configured ?? "low";
660
+
661
+ const {
662
+ cwd,
663
+ selection,
664
+ languageScope,
665
+ outcomes,
666
+ concerns,
667
+ scorecard,
668
+ delta,
669
+ } = runAssessmentPipeline(ctx, threshold);
670
+ // a mistyped iac_languages token is silently ignored otherwise — surface it
671
+ // like a mistyped tools_enabled token so the operator knows their scope
672
+ // narrowing didn't take.
673
+ if (languageScope.unknown_tokens.length > 0) {
674
+ log.warning(
675
+ `» iac_languages: ignoring unrecognised language(s) [${languageScope.unknown_tokens.join(", ")}]`,
676
+ );
677
+ }
678
+ // Publish the reported concern set (the pipeline already applied suppressions)
679
+ // so finalizeSuccessRun's emitFindingsOutputs + reportToCloud fire — assess IS
680
+ // the report path, but lastScanConcerns was only ever set by terraform_scan
681
+ // (Remediate), so Cloud reporting silently no-opped in Assess.
682
+ scanSessionOf(ctx.toolState).lastScanConcerns = concerns;
683
+ const markdown = renderAssessmentMarkdown(scorecard, { delta });
684
+
685
+ const ran = outcomes.filter((o) => o.ran).map((o) => o.source);
686
+ // a mistyped tools_enabled token is otherwise silently ignored; in a
687
+ // read-only assessment that's a footgun (the operator thinks they configured
688
+ // something they didn't). Surface it the same way terraform_scan does.
689
+ if (selection.unknownTokens.length > 0) {
690
+ log.warning(
691
+ `» tools_enabled: ignoring unrecognised tool(s) [${selection.unknownTokens.join(", ")}]`,
692
+ );
693
+ }
694
+ log.info(
695
+ `» terraform_assess: ${scorecard.posture} — ${scorecard.total} concern(s) ` +
696
+ `(${scorecard.compliance.controls_touched} control(s) across ${scorecard.compliance.frameworks.length} framework(s)) ` +
697
+ `across [${scorecard.languages.scanned.join(", ")}] from [${ran.join(", ")}]`,
698
+ );
699
+ return toolOk({
700
+ scanned_dir: cwd,
701
+ scanners_ran: ran,
702
+ // multi-language breadth: the languages detected/scanned + any detected
703
+ // language no scanner could assess (surfaced, never silently dropped).
704
+ languages: {
705
+ detected: languageScope.detected,
706
+ scanned: languageScope.scanned,
707
+ unsupported: languageScope.unsupported,
708
+ unknown_tokens: languageScope.unknown_tokens,
709
+ },
710
+ // parity with terraform_scan: which tools were licence-gated off, which
711
+ // the operator disabled, and which tokens were unrecognised — so the
712
+ // assessment is honest about its own coverage, not silently partial.
713
+ tool_selection: {
714
+ licence_gated: selection.gated,
715
+ disabled: selection.disabled,
716
+ unknown_tokens: selection.unknownTokens,
717
+ },
718
+ scorecard,
719
+ markdown,
720
+ });
721
+ }),
722
+ });
723
+ }