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,231 @@
1
+ /**
2
+ * Governance rules over IaC *shape* — the two Phase-3 finding classes that are
3
+ * about how a configuration behaves as a change-control artefact, not about
4
+ * whether a resource is misconfigured.
5
+ *
6
+ * Config scanners can't produce these. Checkov and Trivy answer "is this setting
7
+ * wrong?", which presumes a setting with a right answer. Neither of these has
8
+ * one:
9
+ *
10
+ * 1. **Imperative side-effects** (Terraform 1.14 `action` blocks invoked from
11
+ * `lifecycle.action_trigger`). An apply now runs work — restart a VM, invoke a
12
+ * Lambda — that is nowhere in the resource graph and leaves no trace in state.
13
+ * That is a legitimate feature and also a change-control fact an auditor needs:
14
+ * "what does an apply do?" stopped being answerable from the plan alone. So
15
+ * this lands in the REVIEW-REQUIRED band. Calling it a failure would be wrong;
16
+ * staying silent would let the one thing a plan can't show you go unmentioned.
17
+ *
18
+ * 2. **Input-bound destroy protection** (OpenTofu 1.12 dynamic `prevent_destroy`).
19
+ * Terraform requires a literal here; OpenTofu allows an expression, which is
20
+ * the documented way to protect production and not ephemeral environments.
21
+ * Whether THIS resource is protected therefore depends on values supplied at
22
+ * plan time. The honest verdict is UNVERIFIABLE — and the rule that matters is
23
+ * the one written into the roadmap: any static rule asserting on
24
+ * `prevent_destroy` must return unverifiable when the value is an unresolved
25
+ * expression, never pass and never fail. This rule is that assertion, made
26
+ * explicit and reported in the ADVISORY band, scoped to stateful resources so
27
+ * it stays signal rather than noise.
28
+ *
29
+ * Pure: HCL text in, findings out. No filesystem, no subprocess, no schema.
30
+ */
31
+ import { parseBlocks, stripTrailingComment, topLevelAssignments } from "#app/mcp/terraform/hcl";
32
+ import {
33
+ BAND_SEVERITY,
34
+ DYNAMIC_DESTROY_PROTECTION,
35
+ IMPERATIVE_ACTION_TRIGGER,
36
+ NATIVE_SOURCE,
37
+ } from "#app/mcp/terraform/nativeRules";
38
+ import { buildConcern, type Concern } from "#app/mcp/terraform/types";
39
+
40
+ // --- imperative side-effects (action blocks + action_trigger) ----------------
41
+
42
+ export interface ActionTriggerSite {
43
+ /** the resource the trigger hangs off, `<type>.<name>`. */
44
+ resourceAddress: string;
45
+ /** 1-based line of the `action_trigger` block. */
46
+ line: number;
47
+ /** lifecycle events the trigger fires on, as written (`after_create`, …). */
48
+ events: string[];
49
+ /** the action references invoked, as written (`action.aws_lambda_invoke.x`). */
50
+ actions: string[];
51
+ /** true when the trigger is conditional — the side-effect may or may not run,
52
+ * which is strictly harder to reason about, not easier. */
53
+ conditional: boolean;
54
+ }
55
+
56
+ /** Split an HCL list literal's elements. Tolerant: an unbracketed or multi-line
57
+ * value yields whatever elements are recognisable, and an unrecognisable one
58
+ * yields none — the finding still fires on the trigger itself. */
59
+ function listElements(raw: string): string[] {
60
+ const v = stripTrailingComment(raw);
61
+ const inner = v.startsWith("[") ? v.slice(1, v.endsWith("]") ? -1 : undefined) : v;
62
+ return inner
63
+ .split(",")
64
+ .map((s) => s.trim().replace(/^"|"$/g, ""))
65
+ .filter(Boolean);
66
+ }
67
+
68
+ /**
69
+ * Every `lifecycle { action_trigger { … } }` in `hcl`, with the resource it
70
+ * belongs to. Walks resource → lifecycle → action_trigger explicitly rather than
71
+ * matching `action_trigger` anywhere, so a same-named argument elsewhere can't
72
+ * fabricate a governance finding.
73
+ */
74
+ export function findActionTriggers(hcl: string): ActionTriggerSite[] {
75
+ const out: ActionTriggerSite[] = [];
76
+ for (const resource of parseBlocks(hcl, ["resource"])) {
77
+ const [type = "", name = ""] = resource.labels;
78
+ for (const lifecycle of parseBlocks(resource.body, ["lifecycle"])) {
79
+ const lifecycleBase = resource.bodyLine + lifecycle.bodyLine - 1;
80
+ for (const trigger of parseBlocks(lifecycle.body, ["action_trigger"])) {
81
+ const args = topLevelAssignments(trigger.body);
82
+ const get = (key: string): string | undefined => args.find((a) => a.name === key)?.value;
83
+ out.push({
84
+ resourceAddress: `${type}.${name}`,
85
+ line: lifecycleBase + trigger.line - 1,
86
+ events: listElements(get("events") ?? ""),
87
+ actions: listElements(get("actions") ?? ""),
88
+ // the docs spell it both ways across pages; accept either rather than
89
+ // silently mis-report a conditional trigger as unconditional.
90
+ conditional: get("condition") !== undefined || get("conditions") !== undefined,
91
+ });
92
+ }
93
+ }
94
+ }
95
+ return out;
96
+ }
97
+
98
+ /** Declared `action "<type>" "<name>" {}` blocks — the definitions the triggers
99
+ * reference. Reported as inventory, never as a finding: declaring an action is
100
+ * inert; only invoking one from a lifecycle event has an effect. */
101
+ export function findActionDeclarations(hcl: string): { address: string; line: number }[] {
102
+ return parseBlocks(hcl, ["action"]).map((b) => ({
103
+ address: `action.${b.labels[0] ?? ""}.${b.labels[1] ?? ""}`,
104
+ line: b.line,
105
+ }));
106
+ }
107
+
108
+ export function actionTriggersToConcerns(sites: ActionTriggerSite[], file: string): Concern[] {
109
+ return sites.map((site) =>
110
+ buildConcern({
111
+ source: NATIVE_SOURCE,
112
+ rule: IMPERATIVE_ACTION_TRIGGER,
113
+ file,
114
+ line: site.line,
115
+ severity: BAND_SEVERITY["review-required"],
116
+ category: "correctness",
117
+ evidence:
118
+ `${site.resourceAddress}: an apply invokes ${site.actions.length || "one or more"} provider ` +
119
+ `action(s) on ${site.events.join(", ") || "a lifecycle event"}` +
120
+ (site.conditional ? ", conditionally" : "") +
121
+ (site.actions.length ? ` — ${site.actions.join(", ")}` : "") +
122
+ ". The side-effect is outside the resource graph and leaves no record in state",
123
+ remediation_hint:
124
+ "Not a defect to fix — a change to record. Confirm the invoked action is covered by your change " +
125
+ "approval process, that its effect is idempotent (Terraform will re-run it on the next matching " +
126
+ "event), and that whoever reads the plan knows the apply does more than the plan shows.",
127
+ }),
128
+ );
129
+ }
130
+
131
+ // --- input-bound destroy protection (OpenTofu dynamic prevent_destroy) -------
132
+
133
+ /**
134
+ * Resource types whose destruction loses data rather than just capacity. Only
135
+ * these are worth an advisory: a dynamic `prevent_destroy` on a security group is
136
+ * a deployment-shape choice, on a database it is the difference between an
137
+ * environment teardown and an incident. Matched on TYPE SUFFIX so the list stays
138
+ * provider-agnostic (`aws_db_instance`, `azurerm_mssql_database`,
139
+ * `google_sql_database_instance` all end in a stateful noun).
140
+ */
141
+ const STATEFUL_TYPE_SIGNAL =
142
+ /(?:^|_)(?:db|database|sql|instance|cluster|bucket|volume|disk|filesystem|file_system|table|vault|keyvault|key_vault|secret|snapshot|backup|registry|repository|share|queue|topic|stream|warehouse)$/;
143
+
144
+ export function isStatefulResourceType(resourceType: string): boolean {
145
+ return STATEFUL_TYPE_SIGNAL.test(resourceType);
146
+ }
147
+
148
+ /** `true` / `false` — the only values standard Terraform accepts for
149
+ * `prevent_destroy`. Anything else is an expression whose value we cannot know. */
150
+ function isBooleanLiteral(value: string): boolean {
151
+ const v = stripTrailingComment(value);
152
+ return v === "true" || v === "false";
153
+ }
154
+
155
+ export interface DynamicDestroyProtectionSite {
156
+ resourceAddress: string;
157
+ resourceType: string;
158
+ /** 1-based line of the `prevent_destroy` assignment. */
159
+ line: number;
160
+ /** the expression as written, e.g. `var.protect`. */
161
+ expression: string;
162
+ }
163
+
164
+ /**
165
+ * Every `prevent_destroy` on a STATEFUL resource whose value is an expression
166
+ * rather than a literal. A literal `true` or `false` is decidable from the code
167
+ * and reported by nobody here; a non-stateful resource is out of scope by design.
168
+ */
169
+ export function findDynamicDestroyProtection(hcl: string): DynamicDestroyProtectionSite[] {
170
+ const out: DynamicDestroyProtectionSite[] = [];
171
+ for (const resource of parseBlocks(hcl, ["resource"])) {
172
+ const [type = "", name = ""] = resource.labels;
173
+ if (!isStatefulResourceType(type)) continue;
174
+ for (const lifecycle of parseBlocks(resource.body, ["lifecycle"])) {
175
+ const lifecycleBase = resource.bodyLine + lifecycle.bodyLine - 1;
176
+ for (const a of topLevelAssignments(lifecycle.body)) {
177
+ if (a.name !== "prevent_destroy" || isBooleanLiteral(a.value)) continue;
178
+ out.push({
179
+ resourceAddress: `${type}.${name}`,
180
+ resourceType: type,
181
+ line: lifecycleBase + a.bodyLine - 1,
182
+ expression: stripTrailingComment(a.value),
183
+ });
184
+ }
185
+ }
186
+ }
187
+ return out;
188
+ }
189
+
190
+ export function dynamicDestroyProtectionToConcerns(
191
+ sites: DynamicDestroyProtectionSite[],
192
+ file: string,
193
+ ): Concern[] {
194
+ return sites.map((site) =>
195
+ buildConcern({
196
+ source: NATIVE_SOURCE,
197
+ rule: DYNAMIC_DESTROY_PROTECTION,
198
+ file,
199
+ line: site.line,
200
+ severity: BAND_SEVERITY.advisory,
201
+ category: "correctness",
202
+ evidence:
203
+ `${site.resourceAddress} is stateful and its destroy protection is bound to \`${site.expression}\` ` +
204
+ "— whether a destroy is blocked depends on plan-time input, so it cannot be determined from code",
205
+ remediation_hint:
206
+ "Legitimate for multi-environment stacks (protect production, not ephemeral environments) and " +
207
+ "reported as advisory for exactly that reason. If this resource should always be protected, a " +
208
+ "literal `prevent_destroy = true` makes the guarantee readable from the code; if it varies, record " +
209
+ "which environments resolve it to true so the protection claim is auditable.",
210
+ }),
211
+ );
212
+ }
213
+
214
+ // --- combined pack ------------------------------------------------------------
215
+
216
+ export interface GovernanceScan {
217
+ concerns: Concern[];
218
+ /** action declarations found — inventory for the run summary, not findings. */
219
+ actionDeclarations: { address: string; line: number; file: string }[];
220
+ }
221
+
222
+ /** Both governance rules over one file's HCL. Pure. */
223
+ export function scanGovernanceFile(hcl: string, file: string): GovernanceScan {
224
+ return {
225
+ concerns: [
226
+ ...actionTriggersToConcerns(findActionTriggers(hcl), file),
227
+ ...dynamicDestroyProtectionToConcerns(findDynamicDestroyProtection(hcl), file),
228
+ ],
229
+ actionDeclarations: findActionDeclarations(hcl).map((d) => ({ ...d, file })),
230
+ };
231
+ }
@@ -0,0 +1,273 @@
1
+ /**
2
+ * Shared, tolerant HCL block scanning.
3
+ *
4
+ * Every native rule in this engine ([[writeOnly]], [[governance]], [[tfquery]],
5
+ * and the provider-schema cross-check in [[scanners]]) needs the same primitive:
6
+ * walk a file's `<keyword> "label" "label" { … }` blocks and read their top-level
7
+ * arguments, without being fooled by a `}` inside a string, a commented-out brace,
8
+ * or a heredoc body. Getting that wrong doesn't produce a parse error — it
9
+ * produces a WRONG FINDING at a wrong line, which is the failure mode this
10
+ * product can least afford. So there is exactly one implementation of it.
11
+ *
12
+ * Deliberately NOT a real HCL parser. We never evaluate expressions, resolve
13
+ * variables, or validate syntax — `terraform validate` (the customer's own
14
+ * binary) owns that. This reads structure only, and every consumer is written to
15
+ * degrade to "no finding" rather than guess when the structure is unclear.
16
+ *
17
+ * Pure: string in, structure out. No filesystem, no subprocess.
18
+ */
19
+
20
+ /**
21
+ * A non-code span in HCL — a `"…"` string, a `#`/`//` line comment, or a
22
+ * `<<EOF` heredoc — that the brace/argument scanners must skip wholesale (an
23
+ * interpolation's `${…}`, a commented `}`, or a heredoc's `key = value` lines
24
+ * would otherwise corrupt brace depth or fabricate an argument). Returned for a
25
+ * span starting at `i`: `end` is the index of its LAST char (caller resumes at
26
+ * `end + 1`), and `endsLine` is true when the span finished at a line boundary
27
+ * (comment / heredoc — the next token begins a fresh statement). null when `i`
28
+ * isn't the start of a non-code span.
29
+ */
30
+ export function skipNonCode(s: string, i: number): { end: number; endsLine: boolean } | null {
31
+ const ch = s[i];
32
+ // double-quoted string (with `\` escapes). Ends mid-line.
33
+ if (ch === '"') {
34
+ let j = i + 1;
35
+ while (j < s.length && s[j] !== '"') {
36
+ if (s[j] === "\\") j++;
37
+ j++;
38
+ }
39
+ return { end: j, endsLine: false };
40
+ }
41
+ // heredoc body (`<<EOF` / `<<-EOF` / `<<~EOF`) — arbitrary text.
42
+ if (ch === "<" && s[i + 1] === "<") {
43
+ const m = /^<<[-~]?([A-Za-z_][A-Za-z0-9_]*)/.exec(s.slice(i, i + 80));
44
+ if (m) {
45
+ const delim = m[1];
46
+ const openNl = s.indexOf("\n", i);
47
+ if (openNl === -1) return { end: s.length - 1, endsLine: true };
48
+ // the closing delimiter sits alone on its own line (optionally indented).
49
+ const closeRe = new RegExp(`\\n[ \\t]*${delim}[ \\t]*(?=\\n|$)`);
50
+ const cm = closeRe.exec(s.slice(openNl));
51
+ const end = cm ? openNl + cm.index + cm[0].length - 1 : s.length - 1;
52
+ return { end, endsLine: true };
53
+ }
54
+ }
55
+ // `#` or `//` line comment.
56
+ if (ch === "#" || (ch === "/" && s[i + 1] === "/")) {
57
+ const nl = s.indexOf("\n", i);
58
+ return { end: nl === -1 ? s.length - 1 : nl, endsLine: true };
59
+ }
60
+ return null;
61
+ }
62
+
63
+ /**
64
+ * Brace-match from an opening `{` at `open`; returns the inner text + end index
65
+ * (the matching `}`), string/comment/heredoc-aware so interpolation braces, a
66
+ * commented `}`, or a heredoc body don't fool it. null when the block never
67
+ * closes (a truncated or malformed file) — callers stop rather than guess.
68
+ */
69
+ export function matchBraceBody(hcl: string, open: number): { text: string; end: number } | null {
70
+ if (open < 0) return null;
71
+ let depth = 0;
72
+ for (let i = open; i < hcl.length; i++) {
73
+ const span = skipNonCode(hcl, i);
74
+ if (span) {
75
+ i = span.end;
76
+ continue;
77
+ }
78
+ const ch = hcl[i];
79
+ if (ch === "{") depth++;
80
+ else if (ch === "}") {
81
+ depth--;
82
+ if (depth === 0) return { text: hcl.slice(open + 1, i), end: i };
83
+ }
84
+ }
85
+ return null;
86
+ }
87
+
88
+ /** 1-based line number of `index` in `hcl`. */
89
+ export function lineOf(hcl: string, index: number): number {
90
+ let line = 1;
91
+ for (let i = 0; i < index && i < hcl.length; i++) if (hcl[i] === "\n") line++;
92
+ return line;
93
+ }
94
+
95
+ /** A top-level block (`resource "aws_s3_bucket" "b" { … }`) with its source span. */
96
+ export interface HclBlock {
97
+ /** the block keyword — `resource`, `action`, `list`, … */
98
+ keyword: string;
99
+ /** the quoted labels after the keyword, in order. */
100
+ labels: string[];
101
+ /** the block's inner text (between the braces). */
102
+ body: string;
103
+ /** 1-based line of the block header. */
104
+ line: number;
105
+ /** 1-based line of the body's opening `{`. Body-relative lines add to this
106
+ * (`absolute = bodyLine + bodyRelativeLine - 1`), so a caller never needs a
107
+ * second `lineOf` scan to place an assignment. */
108
+ bodyLine: number;
109
+ /** 1-based line of the matching `}` — the block's last line. */
110
+ endLine: number;
111
+ /** index in `hcl` of the header's first char. */
112
+ start: number;
113
+ /** index in `hcl` of the body's opening `{`. */
114
+ bodyStart: number;
115
+ /** index in `hcl` of the matching `}`. */
116
+ end: number;
117
+ }
118
+
119
+ /**
120
+ * Every TOP-LEVEL block in `hcl` whose keyword is in `keywords`.
121
+ *
122
+ * Anchored to a line start so a `resource` mentioned inside a string or a nested
123
+ * body can't be mistaken for a declaration, and each match resumes past the
124
+ * block's closing brace so nested blocks of the same keyword are never
125
+ * double-reported. A block that doesn't close terminates the scan (see
126
+ * `matchBraceBody`) — an unparseable tail yields fewer findings, never wrong ones.
127
+ *
128
+ * Line numbers are counted forward as the scan advances rather than by rescanning
129
+ * from the start of the file for each one. Matches arrive in increasing index
130
+ * order, so this is exact — and it keeps a file with many blocks linear instead of
131
+ * quadratic, which matters on the monorepos this walks 2000 files of.
132
+ */
133
+ export function parseBlocks(hcl: string, keywords: readonly string[]): HclBlock[] {
134
+ const out: HclBlock[] = [];
135
+ if (keywords.length === 0) return out;
136
+ const alternation = keywords.join("|");
137
+ const re = new RegExp(`(?:^|\\n)[ \\t]*(${alternation})((?:[ \\t]+"[^"\\n]*")*)[ \\t]*\\{`, "g");
138
+
139
+ let cursor = 0; // index up to which newlines have been counted
140
+ let line = 1;
141
+ /** the 1-based line of `index`; `index` must not go backwards. */
142
+ const lineAt = (index: number): number => {
143
+ for (; cursor < index && cursor < hcl.length; cursor++) {
144
+ if (hcl[cursor] === "\n") line++;
145
+ }
146
+ return line;
147
+ };
148
+
149
+ let m: RegExpExecArray | null;
150
+ // biome-ignore lint/suspicious/noAssignInExpressions: idiomatic regex-exec iteration
151
+ while ((m = re.exec(hcl)) !== null) {
152
+ const keyword = m[1] ?? "";
153
+ const labels = [...(m[2] ?? "").matchAll(/"([^"\n]*)"/g)].map((l) => l[1] ?? "");
154
+ const bodyStart = m.index + m[0].length - 1; // the `{` the header ends on
155
+ const body = matchBraceBody(hcl, bodyStart);
156
+ if (!body) break;
157
+ const start = m.index + (hcl[m.index] === "\n" ? 1 : 0);
158
+ out.push({
159
+ keyword,
160
+ labels,
161
+ body: body.text,
162
+ line: lineAt(start),
163
+ bodyLine: lineAt(bodyStart),
164
+ endLine: lineAt(body.end),
165
+ start,
166
+ bodyStart,
167
+ end: body.end,
168
+ });
169
+ re.lastIndex = body.end + 1;
170
+ }
171
+ return out;
172
+ }
173
+
174
+ /** One `name = <value>` assignment found at the top level of a block body. */
175
+ export interface HclAssignment {
176
+ name: string;
177
+ /** the raw right-hand side, trimmed — an expression, not an evaluated value. */
178
+ value: string;
179
+ /** 1-based line of the assignment, RELATIVE TO THE BODY text. */
180
+ bodyLine: number;
181
+ }
182
+
183
+ /**
184
+ * Top-level `name = value` assignments in a block body, string/comment/heredoc-
185
+ * aware and depth-0 only (a `password` inside a nested block belongs to that
186
+ * block, not this one). Nested BLOCKS are ignored here — use `parseBlocks` on the
187
+ * body for those.
188
+ *
189
+ * The value is captured raw to the end of its line, which is right for the
190
+ * single-line assignments every rule here inspects (`password = var.x`,
191
+ * `prevent_destroy = true`). A multi-line value (a list or object literal
192
+ * spanning lines) is captured only as far as its first line; no rule asserts on
193
+ * such a value, and each is written to skip what it cannot read.
194
+ */
195
+ export function topLevelAssignments(body: string): HclAssignment[] {
196
+ const out: HclAssignment[] = [];
197
+ let depth = 0;
198
+ let atStmtStart = true;
199
+ for (let i = 0; i < body.length; i++) {
200
+ const span = skipNonCode(body, i);
201
+ if (span) {
202
+ i = span.end;
203
+ atStmtStart = span.endsLine;
204
+ continue;
205
+ }
206
+ const ch = body[i] as string;
207
+ if (ch === "{" || ch === "[" || ch === "(") {
208
+ if (ch === "{") depth++;
209
+ atStmtStart = false;
210
+ continue;
211
+ }
212
+ if (ch === "}" || ch === "]" || ch === ")") {
213
+ if (ch === "}") depth--;
214
+ atStmtStart = false;
215
+ continue;
216
+ }
217
+ if (ch === "\n") {
218
+ atStmtStart = true;
219
+ continue;
220
+ }
221
+ if (ch === " " || ch === "\t" || ch === "\r") continue;
222
+ if (depth === 0 && atStmtStart && /[A-Za-z_]/.test(ch)) {
223
+ let j = i;
224
+ while (j < body.length && /[A-Za-z0-9_-]/.test(body[j] as string)) j++;
225
+ const name = body.slice(i, j);
226
+ let k = j;
227
+ while (k < body.length && (body[k] === " " || body[k] === "\t")) k++;
228
+ // `=` but not `==` — an equality inside a bare expression is not an assignment.
229
+ if (body[k] === "=" && body[k + 1] !== "=") {
230
+ const eol = body.indexOf("\n", k);
231
+ const value = body.slice(k + 1, eol === -1 ? body.length : eol).trim();
232
+ out.push({ name, value, bodyLine: lineOf(body, i) });
233
+ }
234
+ i = j - 1;
235
+ atStmtStart = false;
236
+ continue;
237
+ }
238
+ atStmtStart = false;
239
+ }
240
+ return out;
241
+ }
242
+
243
+ /**
244
+ * Strip a trailing `# …` / `// …` comment from a captured right-hand side.
245
+ *
246
+ * Deliberately handles ONLY strings and comments, not the full `skipNonCode` set:
247
+ * a heredoc opener (`<<EOF`) is part of the value, and treating it as a span to
248
+ * stop at would silently truncate the value to the empty string — which then
249
+ * flows into a proposed edit as `password_wo = ` and produces a broken rewrite.
250
+ */
251
+ export function stripTrailingComment(value: string): string {
252
+ for (let i = 0; i < value.length; i++) {
253
+ const ch = value[i];
254
+ if (ch === '"') {
255
+ const span = skipNonCode(value, i);
256
+ if (span) i = span.end;
257
+ continue;
258
+ }
259
+ if (ch === "#" || (ch === "/" && value[i + 1] === "/")) return value.slice(0, i).trim();
260
+ }
261
+ return value.trim();
262
+ }
263
+
264
+ /** True when a right-hand side is a bare quoted string LITERAL with no
265
+ * interpolation — i.e. the value is written into the config, not referenced. */
266
+ export function isStringLiteral(value: string): boolean {
267
+ const v = stripTrailingComment(value);
268
+ if (v.length < 2 || !v.startsWith('"') || !v.endsWith('"')) return false;
269
+ // one span only: the closing quote must be the last character.
270
+ const span = skipNonCode(v, 0);
271
+ if (!span || span.end !== v.length - 1) return false;
272
+ return !/\$\{/.test(v);
273
+ }