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.
- package/LICENSE +661 -0
- package/README.md +244 -0
- package/dist/agents/claude.d.ts +113 -0
- package/dist/agents/claudePretoolGate.d.ts +137 -0
- package/dist/agents/dispatchMetrics.d.ts +77 -0
- package/dist/agents/gateServer.d.ts +7 -0
- package/dist/agents/index.d.ts +6 -0
- package/dist/agents/nativeFsDenies.d.ts +46 -0
- package/dist/agents/opencode.d.ts +284 -0
- package/dist/agents/opencodePlugin.d.ts +96 -0
- package/dist/agents/opencodeShared.d.ts +40 -0
- package/dist/agents/postRun.d.ts +126 -0
- package/dist/agents/reviewer.d.ts +41 -0
- package/dist/agents/sessionLabeler.d.ts +97 -0
- package/dist/agents/shared.d.ts +246 -0
- package/dist/agents/subagentModels.d.ts +19 -0
- package/dist/agents/tokenQuota.d.ts +118 -0
- package/dist/agents/writeGateSource.d.ts +22 -0
- package/dist/agents/writePolicy.d.ts +79 -0
- package/dist/brand.d.ts +67 -0
- package/dist/cli.mjs +247846 -0
- package/dist/external.d.ts +227 -0
- package/dist/i18n/scaffolding.d.ts +59 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +247061 -0
- package/dist/internal/index.d.ts +18 -0
- package/dist/internal.js +2398 -0
- package/dist/lifecycle.d.ts +2 -0
- package/dist/main.d.ts +8 -0
- package/dist/mcp/arkConfig.d.ts +1 -0
- package/dist/mcp/assess.d.ts +157 -0
- package/dist/mcp/capabilityContext.d.ts +71 -0
- package/dist/mcp/changeSummary.d.ts +52 -0
- package/dist/mcp/checkSuite.d.ts +27 -0
- package/dist/mcp/checkout.d.ts +92 -0
- package/dist/mcp/comment.d.ts +127 -0
- package/dist/mcp/commitInfo.d.ts +11 -0
- package/dist/mcp/crosswalk.d.ts +178 -0
- package/dist/mcp/crosswalkDigest.d.ts +1 -0
- package/dist/mcp/cyberEssentials.d.ts +24 -0
- package/dist/mcp/dashboard.d.ts +125 -0
- package/dist/mcp/dependencies.d.ts +12 -0
- package/dist/mcp/frameworks.d.ts +74 -0
- package/dist/mcp/geminiSanitizer.d.ts +28 -0
- package/dist/mcp/git.d.ts +60 -0
- package/dist/mcp/guardrails.d.ts +222 -0
- package/dist/mcp/issue.d.ts +20 -0
- package/dist/mcp/issueComments.d.ts +11 -0
- package/dist/mcp/issueEvents.d.ts +11 -0
- package/dist/mcp/issueInfo.d.ts +11 -0
- package/dist/mcp/labels.d.ts +14 -0
- package/dist/mcp/localContext.d.ts +26 -0
- package/dist/mcp/moduleExtraction.d.ts +79 -0
- package/dist/mcp/moduleTests.d.ts +106 -0
- package/dist/mcp/modules.d.ts +198 -0
- package/dist/mcp/output.d.ts +16 -0
- package/dist/mcp/pathSafety.d.ts +14 -0
- package/dist/mcp/policy.d.ts +50 -0
- package/dist/mcp/pr.d.ts +64 -0
- package/dist/mcp/prInfo.d.ts +11 -0
- package/dist/mcp/providerSchema.d.ts +52 -0
- package/dist/mcp/review.d.ts +212 -0
- package/dist/mcp/reviewComments.d.ts +245 -0
- package/dist/mcp/roots.d.ts +60 -0
- package/dist/mcp/scope.d.ts +26 -0
- package/dist/mcp/selectMode.d.ts +20 -0
- package/dist/mcp/server.d.ts +61 -0
- package/dist/mcp/shared.d.ts +62 -0
- package/dist/mcp/shell.d.ts +58 -0
- package/dist/mcp/staleFix.d.ts +138 -0
- package/dist/mcp/terraform/azurePrices.d.ts +8 -0
- package/dist/mcp/terraform/baseline.d.ts +57 -0
- package/dist/mcp/terraform/concernResult.d.ts +38 -0
- package/dist/mcp/terraform/cost.d.ts +55 -0
- package/dist/mcp/terraform/cspm.d.ts +80 -0
- package/dist/mcp/terraform/currency.d.ts +110 -0
- package/dist/mcp/terraform/decisions.d.ts +178 -0
- package/dist/mcp/terraform/eidas.d.ts +100 -0
- package/dist/mcp/terraform/evidence.d.ts +343 -0
- package/dist/mcp/terraform/findings.d.ts +76 -0
- package/dist/mcp/terraform/fixMemory.d.ts +81 -0
- package/dist/mcp/terraform/governance.d.ts +56 -0
- package/dist/mcp/terraform/hcl.d.ts +115 -0
- package/dist/mcp/terraform/iacLanguages.d.ts +120 -0
- package/dist/mcp/terraform/idleFloor.d.ts +78 -0
- package/dist/mcp/terraform/keyless.d.ts +68 -0
- package/dist/mcp/terraform/moduleDocs.d.ts +56 -0
- package/dist/mcp/terraform/nativeRules.d.ts +38 -0
- package/dist/mcp/terraform/nativeScan.d.ts +11 -0
- package/dist/mcp/terraform/noiseProfile.d.ts +100 -0
- package/dist/mcp/terraform/normalization.d.ts +52 -0
- package/dist/mcp/terraform/oscal.d.ts +103 -0
- package/dist/mcp/terraform/packs/azurerm-3-4.d.ts +2 -0
- package/dist/mcp/terraform/packs/cloudflare-4-5.d.ts +2 -0
- package/dist/mcp/terraform/paths.d.ts +28 -0
- package/dist/mcp/terraform/plan.d.ts +157 -0
- package/dist/mcp/terraform/planDrift.d.ts +199 -0
- package/dist/mcp/terraform/policyAuthor.d.ts +191 -0
- package/dist/mcp/terraform/prowlerOcsf.d.ts +136 -0
- package/dist/mcp/terraform/refactor.d.ts +183 -0
- package/dist/mcp/terraform/registry.d.ts +108 -0
- package/dist/mcp/terraform/risk.d.ts +41 -0
- package/dist/mcp/terraform/scanSession.d.ts +116 -0
- package/dist/mcp/terraform/scannerCache.d.ts +15 -0
- package/dist/mcp/terraform/scannerEgress.d.ts +72 -0
- package/dist/mcp/terraform/scannerJson.d.ts +13 -0
- package/dist/mcp/terraform/scanners.d.ts +205 -0
- package/dist/mcp/terraform/signing.d.ts +61 -0
- package/dist/mcp/terraform/stateLocking.d.ts +18 -0
- package/dist/mcp/terraform/subprocess.d.ts +43 -0
- package/dist/mcp/terraform/suppressions.d.ts +90 -0
- package/dist/mcp/terraform/taxonomy.d.ts +39 -0
- package/dist/mcp/terraform/tfquery.d.ts +47 -0
- package/dist/mcp/terraform/toolchainIntegrity.d.ts +82 -0
- package/dist/mcp/terraform/tools/authorPolicy.d.ts +59 -0
- package/dist/mcp/terraform/tools/detectPlanDrift.d.ts +20 -0
- package/dist/mcp/terraform/tools/emitCost.d.ts +11 -0
- package/dist/mcp/terraform/tools/emitSarif.d.ts +14 -0
- package/dist/mcp/terraform/tools/fixMemory.d.ts +19 -0
- package/dist/mcp/terraform/tools/infracostDiff.d.ts +22 -0
- package/dist/mcp/terraform/tools/ingestExternalFindings.d.ts +21 -0
- package/dist/mcp/terraform/tools/moduleLookup.d.ts +14 -0
- package/dist/mcp/terraform/tools/moduleSearch.d.ts +23 -0
- package/dist/mcp/terraform/tools/plan.d.ts +59 -0
- package/dist/mcp/terraform/tools/providerUpgrade.d.ts +55 -0
- package/dist/mcp/terraform/tools/readFindings.d.ts +17 -0
- package/dist/mcp/terraform/tools/scan.d.ts +114 -0
- package/dist/mcp/terraform/tools/validate.d.ts +31 -0
- package/dist/mcp/terraform/tools/verifyRemediation.d.ts +31 -0
- package/dist/mcp/terraform/tools/versionCurrency.d.ts +5 -0
- package/dist/mcp/terraform/tools.d.ts +16 -0
- package/dist/mcp/terraform/transformPacks.d.ts +267 -0
- package/dist/mcp/terraform/types.d.ts +226 -0
- package/dist/mcp/terraform/verification.d.ts +81 -0
- package/dist/mcp/terraform/vex.d.ts +115 -0
- package/dist/mcp/terraform/writeOnly.d.ts +67 -0
- package/dist/mcp/terraform.d.ts +51 -0
- package/dist/mcp/terratest.d.ts +85 -0
- package/dist/mcp/untrustedContent.d.ts +76 -0
- package/dist/mcp/upload.d.ts +8 -0
- package/dist/models.d.ts +181 -0
- package/dist/modes/address-reviews.d.ts +2 -0
- package/dist/modes/assess.d.ts +2 -0
- package/dist/modes/build.d.ts +2 -0
- package/dist/modes/compliance-audit.d.ts +2 -0
- package/dist/modes/cost-optimization.d.ts +2 -0
- package/dist/modes/drift-detect.d.ts +2 -0
- package/dist/modes/fix.d.ts +2 -0
- package/dist/modes/incremental-review.d.ts +2 -0
- package/dist/modes/index.d.ts +24 -0
- package/dist/modes/modernize-deprecated.d.ts +2 -0
- package/dist/modes/plan.d.ts +2 -0
- package/dist/modes/policy-gate.d.ts +2 -0
- package/dist/modes/prFormats.d.ts +3 -0
- package/dist/modes/refactor.d.ts +2 -0
- package/dist/modes/refresh-remediation.d.ts +2 -0
- package/dist/modes/remediate-and-refactor.d.ts +2 -0
- package/dist/modes/remediate.d.ts +2 -0
- package/dist/modes/resolve-conflicts.d.ts +2 -0
- package/dist/modes/review.d.ts +2 -0
- package/dist/modes/summarize-pr.d.ts +2 -0
- package/dist/modes/task.d.ts +2 -0
- package/dist/modes/terraform-code-review.d.ts +2 -0
- package/dist/modes/types.d.ts +9 -0
- package/dist/modes/update-dependencies.d.ts +2 -0
- package/dist/prep/index.d.ts +7 -0
- package/dist/prep/installNodeDependencies.d.ts +2 -0
- package/dist/prep/installPythonDependencies.d.ts +2 -0
- package/dist/prep/types.d.ts +31 -0
- package/dist/reviewQuality.d.ts +107 -0
- package/dist/skills/terraform-best-practices/SKILL.md +379 -0
- package/dist/toolState.d.ts +123 -0
- package/dist/utils/activity.d.ts +40 -0
- package/dist/utils/agent.d.ts +47 -0
- package/dist/utils/agentHangReport.d.ts +38 -0
- package/dist/utils/annotations.d.ts +17 -0
- package/dist/utils/apiFetch.d.ts +11 -0
- package/dist/utils/apiKeys.d.ts +48 -0
- package/dist/utils/apiUrl.d.ts +28 -0
- package/dist/utils/assets.d.ts +8 -0
- package/dist/utils/baseRefConfig.d.ts +121 -0
- package/dist/utils/billingErrors.d.ts +85 -0
- package/dist/utils/body.d.ts +34 -0
- package/dist/utils/buildInfraweaverFooter.d.ts +29 -0
- package/dist/utils/byokFallback.d.ts +85 -0
- package/dist/utils/changeImpact.d.ts +101 -0
- package/dist/utils/claudeSubscription.d.ts +30 -0
- package/dist/utils/cli.d.ts +10 -0
- package/dist/utils/cloudClient.d.ts +33 -0
- package/dist/utils/cloudFetchCore.d.ts +42 -0
- package/dist/utils/cloudReport.d.ts +114 -0
- package/dist/utils/codexHome.d.ts +29 -0
- package/dist/utils/codexOAuth.d.ts +60 -0
- package/dist/utils/diffCoverage.d.ts +63 -0
- package/dist/utils/env.d.ts +46 -0
- package/dist/utils/errorReport.d.ts +17 -0
- package/dist/utils/exitHandler.d.ts +8 -0
- package/dist/utils/fixDoubleEscapedString.d.ts +1 -0
- package/dist/utils/gitAuth.d.ts +84 -0
- package/dist/utils/gitAuthServer.d.ts +24 -0
- package/dist/utils/github.d.ts +104 -0
- package/dist/utils/globals.d.ts +3 -0
- package/dist/utils/infraweaverConfig.d.ts +56 -0
- package/dist/utils/install.d.ts +37 -0
- package/dist/utils/instructions.d.ts +48 -0
- package/dist/utils/keylessOidc.d.ts +28 -0
- package/dist/utils/leapingComment.d.ts +11 -0
- package/dist/utils/learnings.d.ts +62 -0
- package/dist/utils/learningsTruncate.d.ts +25 -0
- package/dist/utils/lifecycle.d.ts +57 -0
- package/dist/utils/log.d.ts +111 -0
- package/dist/utils/modelResolver.d.ts +63 -0
- package/dist/utils/moduleFetch.d.ts +39 -0
- package/dist/utils/normalizeEnv.d.ts +30 -0
- package/dist/utils/openCodeModels.d.ts +11 -0
- package/dist/utils/openaiCompatible.d.ts +26 -0
- package/dist/utils/opentofuMcp.d.ts +50 -0
- package/dist/utils/overrides.d.ts +40 -0
- package/dist/utils/packageManager.d.ts +49 -0
- package/dist/utils/patchWorkflowRunFields.d.ts +29 -0
- package/dist/utils/payload.d.ts +249 -0
- package/dist/utils/prSummary.d.ts +61 -0
- package/dist/utils/presets.d.ts +36 -0
- package/dist/utils/progressComment.d.ts +146 -0
- package/dist/utils/providerErrors.d.ts +31 -0
- package/dist/utils/proxyModel.d.ts +76 -0
- package/dist/utils/rangeDiff.d.ts +51 -0
- package/dist/utils/redact.d.ts +33 -0
- package/dist/utils/registryAuth.d.ts +52 -0
- package/dist/utils/remediationCommand.d.ts +58 -0
- package/dist/utils/retry.d.ts +38 -0
- package/dist/utils/reviewCleanup.d.ts +14 -0
- package/dist/utils/run.d.ts +9 -0
- package/dist/utils/runContext.d.ts +112 -0
- package/dist/utils/runContextData.d.ts +42 -0
- package/dist/utils/runErrorRenderer.d.ts +64 -0
- package/dist/utils/runLifecycle.d.ts +89 -0
- package/dist/utils/runStartupLog.d.ts +15 -0
- package/dist/utils/secrets.d.ts +34 -0
- package/dist/utils/setup.d.ts +90 -0
- package/dist/utils/shell.d.ts +44 -0
- package/dist/utils/skills.d.ts +10 -0
- package/dist/utils/subprocess.d.ts +81 -0
- package/dist/utils/terraformMcp.d.ts +46 -0
- package/dist/utils/time.d.ts +15 -0
- package/dist/utils/timer.d.ts +23 -0
- package/dist/utils/todoTracking.d.ts +16 -0
- package/dist/utils/token.d.ts +52 -0
- package/dist/utils/toolLicensing.d.ts +56 -0
- package/dist/utils/toolSelection.d.ts +73 -0
- package/dist/utils/toon.d.ts +16 -0
- package/dist/utils/version.d.ts +2 -0
- package/dist/utils/versioning.d.ts +7 -0
- package/dist/utils/vertex.d.ts +16 -0
- package/dist/utils/workflow.d.ts +13 -0
- package/package.json +132 -0
- package/src/agents/claude.ts +1588 -0
- package/src/agents/claudePretoolGate.ts +286 -0
- package/src/agents/dispatchMetrics.ts +162 -0
- package/src/agents/gateServer.ts +178 -0
- package/src/agents/index.ts +10 -0
- package/src/agents/nativeFsDenies.ts +164 -0
- package/src/agents/opencode.ts +1600 -0
- package/src/agents/opencodePlugin.ts +304 -0
- package/src/agents/opencodeShared.ts +134 -0
- package/src/agents/postRun.ts +615 -0
- package/src/agents/reviewer.ts +134 -0
- package/src/agents/sessionLabeler.ts +185 -0
- package/src/agents/shared.ts +377 -0
- package/src/agents/subagentModels.ts +40 -0
- package/src/agents/tokenQuota.ts +203 -0
- package/src/agents/writeGateSource.ts +148 -0
- package/src/agents/writePolicy.ts +132 -0
- package/src/brand.ts +84 -0
- package/src/cli.ts +117 -0
- package/src/commands/gha.ts +188 -0
- package/src/commands/mcp.ts +126 -0
- package/src/commands/verifyEvidence.ts +100 -0
- package/src/entry.ts +7 -0
- package/src/entryPost.ts +109 -0
- package/src/external.ts +308 -0
- package/src/i18n/scaffolding.ts +191 -0
- package/src/index.ts +7 -0
- package/src/internal/index.ts +74 -0
- package/src/lifecycle.ts +2 -0
- package/src/main.ts +815 -0
- package/src/mcp/__fixtures__/infraweaver-scratch-pr-49-review-3485940013.json +110 -0
- package/src/mcp/__fixtures__/infraweaver-scratch-pr-64-review-3531000326.json +14 -0
- package/src/mcp/__fixtures__/infraweaver-test-repo-pr-1.diff.json +67 -0
- package/src/mcp/__snapshots__/checkout.test.ts.snap +109 -0
- package/src/mcp/__snapshots__/reviewComments.test.ts.snap +71 -0
- package/src/mcp/arkConfig.ts +7 -0
- package/src/mcp/assess.ts +723 -0
- package/src/mcp/capabilityContext.ts +80 -0
- package/src/mcp/changeSummary.ts +145 -0
- package/src/mcp/checkSuite.ts +261 -0
- package/src/mcp/checkout.ts +1015 -0
- package/src/mcp/comment.ts +683 -0
- package/src/mcp/commitInfo.ts +57 -0
- package/src/mcp/crosswalk.ts +1253 -0
- package/src/mcp/crosswalkDigest.ts +5 -0
- package/src/mcp/cyberEssentials.ts +60 -0
- package/src/mcp/dashboard.ts +232 -0
- package/src/mcp/dependencies.ts +190 -0
- package/src/mcp/frameworks.ts +236 -0
- package/src/mcp/geminiSanitizer.ts +212 -0
- package/src/mcp/git.ts +1116 -0
- package/src/mcp/guardrails.ts +771 -0
- package/src/mcp/issue.ts +74 -0
- package/src/mcp/issueComments.ts +48 -0
- package/src/mcp/issueEvents.ts +100 -0
- package/src/mcp/issueInfo.ts +72 -0
- package/src/mcp/labels.ts +35 -0
- package/src/mcp/localContext.ts +65 -0
- package/src/mcp/localServer.ts +217 -0
- package/src/mcp/moduleExtraction.ts +368 -0
- package/src/mcp/moduleTests.ts +421 -0
- package/src/mcp/modules.ts +752 -0
- package/src/mcp/output.ts +71 -0
- package/src/mcp/pathSafety.ts +28 -0
- package/src/mcp/policy.ts +226 -0
- package/src/mcp/pr.ts +238 -0
- package/src/mcp/prInfo.ts +91 -0
- package/src/mcp/providerSchema.ts +175 -0
- package/src/mcp/review.ts +1081 -0
- package/src/mcp/reviewComments.ts +1137 -0
- package/src/mcp/roots.ts +217 -0
- package/src/mcp/scope.ts +82 -0
- package/src/mcp/selectMode.ts +207 -0
- package/src/mcp/server.ts +498 -0
- package/src/mcp/shared.ts +120 -0
- package/src/mcp/shell.ts +631 -0
- package/src/mcp/staleFix.ts +557 -0
- package/src/mcp/terraform/__snapshots__/moduleDocs.test.ts.snap +40 -0
- package/src/mcp/terraform/azurePrices.ts +38 -0
- package/src/mcp/terraform/baseline.ts +172 -0
- package/src/mcp/terraform/concernResult.ts +111 -0
- package/src/mcp/terraform/cost.ts +175 -0
- package/src/mcp/terraform/cspm.ts +326 -0
- package/src/mcp/terraform/currency.ts +354 -0
- package/src/mcp/terraform/decisions.ts +590 -0
- package/src/mcp/terraform/eidas.ts +134 -0
- package/src/mcp/terraform/evidence.ts +771 -0
- package/src/mcp/terraform/findings.ts +383 -0
- package/src/mcp/terraform/fixMemory.ts +253 -0
- package/src/mcp/terraform/governance.ts +231 -0
- package/src/mcp/terraform/hcl.ts +273 -0
- package/src/mcp/terraform/iacLanguages.ts +431 -0
- package/src/mcp/terraform/idleFloor.ts +304 -0
- package/src/mcp/terraform/keyless.ts +123 -0
- package/src/mcp/terraform/moduleDocs.ts +303 -0
- package/src/mcp/terraform/nativeRules.ts +152 -0
- package/src/mcp/terraform/nativeScan.ts +67 -0
- package/src/mcp/terraform/noiseProfile.ts +183 -0
- package/src/mcp/terraform/normalization.ts +265 -0
- package/src/mcp/terraform/oscal.ts +283 -0
- package/src/mcp/terraform/packs/azurerm-3-4.ts +382 -0
- package/src/mcp/terraform/packs/cloudflare-4-5.ts +114 -0
- package/src/mcp/terraform/paths.ts +59 -0
- package/src/mcp/terraform/plan.ts +377 -0
- package/src/mcp/terraform/planDrift.ts +520 -0
- package/src/mcp/terraform/policyAuthor.ts +886 -0
- package/src/mcp/terraform/prowlerOcsf.ts +380 -0
- package/src/mcp/terraform/refactor.ts +879 -0
- package/src/mcp/terraform/registry.ts +317 -0
- package/src/mcp/terraform/risk.ts +99 -0
- package/src/mcp/terraform/scanSession.ts +242 -0
- package/src/mcp/terraform/scannerCache.ts +86 -0
- package/src/mcp/terraform/scannerEgress.ts +141 -0
- package/src/mcp/terraform/scannerJson.ts +22 -0
- package/src/mcp/terraform/scanners.ts +1134 -0
- package/src/mcp/terraform/signing.ts +151 -0
- package/src/mcp/terraform/stateLocking.ts +106 -0
- package/src/mcp/terraform/subprocess.ts +102 -0
- package/src/mcp/terraform/suppressions.ts +551 -0
- package/src/mcp/terraform/taxonomy.ts +0 -0
- package/src/mcp/terraform/tfquery.ts +159 -0
- package/src/mcp/terraform/toolchainIntegrity.ts +151 -0
- package/src/mcp/terraform/tools/authorPolicy.ts +185 -0
- package/src/mcp/terraform/tools/detectPlanDrift.ts +279 -0
- package/src/mcp/terraform/tools/emitCost.ts +130 -0
- package/src/mcp/terraform/tools/emitSarif.ts +108 -0
- package/src/mcp/terraform/tools/fixMemory.ts +82 -0
- package/src/mcp/terraform/tools/infracostDiff.ts +110 -0
- package/src/mcp/terraform/tools/ingestExternalFindings.ts +259 -0
- package/src/mcp/terraform/tools/moduleLookup.ts +86 -0
- package/src/mcp/terraform/tools/moduleSearch.ts +46 -0
- package/src/mcp/terraform/tools/plan.ts +384 -0
- package/src/mcp/terraform/tools/providerUpgrade.ts +217 -0
- package/src/mcp/terraform/tools/readFindings.ts +138 -0
- package/src/mcp/terraform/tools/scan.ts +303 -0
- package/src/mcp/terraform/tools/validate.ts +160 -0
- package/src/mcp/terraform/tools/verifyRemediation.ts +190 -0
- package/src/mcp/terraform/tools/versionCurrency.ts +64 -0
- package/src/mcp/terraform/tools.ts +62 -0
- package/src/mcp/terraform/transformPacks.ts +594 -0
- package/src/mcp/terraform/types.ts +382 -0
- package/src/mcp/terraform/verification.ts +150 -0
- package/src/mcp/terraform/vex.ts +367 -0
- package/src/mcp/terraform/writeOnly.ts +252 -0
- package/src/mcp/terraform.ts +52 -0
- package/src/mcp/terratest.ts +215 -0
- package/src/mcp/untrustedContent.ts +126 -0
- package/src/mcp/upload.ts +123 -0
- package/src/models.ts +753 -0
- package/src/modes/address-reviews.ts +35 -0
- package/src/modes/assess.ts +27 -0
- package/src/modes/build.ts +86 -0
- package/src/modes/compliance-audit.ts +25 -0
- package/src/modes/cost-optimization.ts +36 -0
- package/src/modes/drift-detect.ts +31 -0
- package/src/modes/fix.ts +29 -0
- package/src/modes/incremental-review.ts +103 -0
- package/src/modes/index.ts +94 -0
- package/src/modes/modernize-deprecated.ts +36 -0
- package/src/modes/plan.ts +21 -0
- package/src/modes/policy-gate.ts +27 -0
- package/src/modes/prFormats.ts +325 -0
- package/src/modes/refactor.ts +74 -0
- package/src/modes/refresh-remediation.ts +40 -0
- package/src/modes/remediate-and-refactor.ts +42 -0
- package/src/modes/remediate.ts +78 -0
- package/src/modes/resolve-conflicts.ts +32 -0
- package/src/modes/review.ts +135 -0
- package/src/modes/summarize-pr.ts +42 -0
- package/src/modes/task.ts +26 -0
- package/src/modes/terraform-code-review.ts +68 -0
- package/src/modes/types.ts +17 -0
- package/src/modes/update-dependencies.ts +44 -0
- package/src/prep/index.ts +96 -0
- package/src/prep/installNodeDependencies.ts +251 -0
- package/src/prep/installPythonDependencies.ts +235 -0
- package/src/prep/types.ts +38 -0
- package/src/reviewQuality.ts +237 -0
- package/src/runCli.ts +335 -0
- package/src/skills/terraform-best-practices/SKILL.md +379 -0
- package/src/toolState.ts +241 -0
- package/src/utils/activity.ts +210 -0
- package/src/utils/agent.ts +245 -0
- package/src/utils/agentHangReport.ts +180 -0
- package/src/utils/annotations.ts +56 -0
- package/src/utils/apiFetch.ts +19 -0
- package/src/utils/apiKeys.ts +305 -0
- package/src/utils/apiUrl.ts +42 -0
- package/src/utils/assets.ts +123 -0
- package/src/utils/baseRefConfig.ts +254 -0
- package/src/utils/billingErrors.ts +210 -0
- package/src/utils/body.ts +168 -0
- package/src/utils/buildInfraweaverFooter.ts +104 -0
- package/src/utils/byokFallback.ts +137 -0
- package/src/utils/changeImpact.ts +330 -0
- package/src/utils/claudeSubscription.ts +93 -0
- package/src/utils/cli.ts +36 -0
- package/src/utils/cloudClient.ts +57 -0
- package/src/utils/cloudFetchCore.ts +139 -0
- package/src/utils/cloudReport.ts +316 -0
- package/src/utils/codexHome.ts +204 -0
- package/src/utils/codexOAuth.ts +154 -0
- package/src/utils/codexRefreshDetect.ts +36 -0
- package/src/utils/diffCoverage.ts +404 -0
- package/src/utils/env.ts +84 -0
- package/src/utils/errorReport.ts +94 -0
- package/src/utils/exitHandler.ts +35 -0
- package/src/utils/fixDoubleEscapedString.ts +9 -0
- package/src/utils/ghaCore.ts +13 -0
- package/src/utils/gitAuth.ts +268 -0
- package/src/utils/gitAuthServer.ts +186 -0
- package/src/utils/github.ts +676 -0
- package/src/utils/globals.ts +9 -0
- package/src/utils/humanEditCapture.ts +198 -0
- package/src/utils/infraweaverConfig.ts +202 -0
- package/src/utils/install.ts +279 -0
- package/src/utils/instructions.ts +640 -0
- package/src/utils/keylessOidc.ts +47 -0
- package/src/utils/leapingComment.ts +20 -0
- package/src/utils/learnings.ts +145 -0
- package/src/utils/learningsTruncate.ts +42 -0
- package/src/utils/lifecycle.ts +198 -0
- package/src/utils/log.ts +452 -0
- package/src/utils/modelResolver.ts +105 -0
- package/src/utils/moduleFetch.ts +138 -0
- package/src/utils/normalizeEnv.ts +106 -0
- package/src/utils/openCodeModels.ts +86 -0
- package/src/utils/openaiCompatible.ts +52 -0
- package/src/utils/opentofuMcp.ts +61 -0
- package/src/utils/overrides.ts +100 -0
- package/src/utils/packageManager.ts +257 -0
- package/src/utils/patchWorkflowRunFields.ts +173 -0
- package/src/utils/payload.ts +1206 -0
- package/src/utils/prSummary.ts +148 -0
- package/src/utils/presets.ts +105 -0
- package/src/utils/progressComment.ts +266 -0
- package/src/utils/providerErrors.ts +189 -0
- package/src/utils/proxyModel.ts +239 -0
- package/src/utils/rangeDiff.ts +182 -0
- package/src/utils/redact.ts +86 -0
- package/src/utils/registryAuth.ts +115 -0
- package/src/utils/remediationCommand.ts +144 -0
- package/src/utils/retry.ts +180 -0
- package/src/utils/reviewCleanup.ts +116 -0
- package/src/utils/run.ts +100 -0
- package/src/utils/runContext.ts +276 -0
- package/src/utils/runContextData.ts +121 -0
- package/src/utils/runErrorRenderer.ts +282 -0
- package/src/utils/runFixture.ts +91 -0
- package/src/utils/runLifecycle.ts +360 -0
- package/src/utils/runStartupLog.ts +67 -0
- package/src/utils/secrets.ts +208 -0
- package/src/utils/setup.ts +368 -0
- package/src/utils/shell.ts +132 -0
- package/src/utils/skills.ts +67 -0
- package/src/utils/subprocess.ts +474 -0
- package/src/utils/terraformMcp.ts +99 -0
- package/src/utils/time.ts +59 -0
- package/src/utils/timer.ts +72 -0
- package/src/utils/todoTracking.ts +168 -0
- package/src/utils/token.ts +294 -0
- package/src/utils/toolLicensing.ts +152 -0
- package/src/utils/toolSelection.ts +239 -0
- package/src/utils/toon.ts +76 -0
- package/src/utils/version.ts +10 -0
- package/src/utils/versioning.ts +44 -0
- package/src/utils/vertex.ts +94 -0
- package/src/utils/workflow.ts +25 -0
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import type { TransformEntry, TransformPack } from "#app/mcp/terraform/transformPacks";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* cloudflare 4.x → 5.0.
|
|
5
|
+
*
|
|
6
|
+
* This pack is deliberately THIN, and the reason is the interesting part:
|
|
7
|
+
* Cloudflare ships its own migration tool. `tf-migrate` rewrites the HCL,
|
|
8
|
+
* generates the `moved {}` blocks and updates cross-file references, and the
|
|
9
|
+
* provider carries state upgraders from v5.19 onward. Re-implementing forty
|
|
10
|
+
* resource renames here would produce a worse version of something the vendor
|
|
11
|
+
* maintains — the same "integrate upward, don't compete" call the product makes
|
|
12
|
+
* everywhere else.
|
|
13
|
+
*
|
|
14
|
+
* So what this pack contributes is the thing `tf-migrate` deliberately does not:
|
|
15
|
+
* a verdict on whether the change is safe to ship unattended. It names the
|
|
16
|
+
* renames whose state moves automatically (fine) and, separately, the handful
|
|
17
|
+
* the vendor's own guide marks **Manual** — those have no `MoveState` handler,
|
|
18
|
+
* so they need `terraform state rm` + `terraform import` and must never be
|
|
19
|
+
* applied by an agent on its own.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
const SOURCE =
|
|
23
|
+
"https://github.com/cloudflare/terraform-provider-cloudflare/blob/main/docs/guides/version-5-migration.md";
|
|
24
|
+
|
|
25
|
+
/** Renames the provider can carry across automatically (`moved {}` blocks, or a
|
|
26
|
+
* built-in `MoveState` handler on v5.19+). State survives; no replacement. */
|
|
27
|
+
const AUTO_RENAMES: [from: string, to: string][] = [
|
|
28
|
+
["cloudflare_record", "cloudflare_dns_record"],
|
|
29
|
+
["cloudflare_tunnel", "cloudflare_zero_trust_tunnel_cloudflared"],
|
|
30
|
+
["cloudflare_access_application", "cloudflare_zero_trust_access_application"],
|
|
31
|
+
["cloudflare_teams_rule", "cloudflare_zero_trust_gateway_policy"],
|
|
32
|
+
];
|
|
33
|
+
|
|
34
|
+
/** Renames where the resource also SPLIT, so a `moved {}` block carries only
|
|
35
|
+
* part of what the v4 resource managed. The remainder has to be created fresh,
|
|
36
|
+
* and saying only "renamed" would leave it silently unmanaged. */
|
|
37
|
+
const SPLIT_RENAMES: [from: string, to: string, alsoNeeded: string, what: string][] = [
|
|
38
|
+
[
|
|
39
|
+
"cloudflare_argo",
|
|
40
|
+
"cloudflare_argo_smart_routing",
|
|
41
|
+
"cloudflare_argo_tiered_caching",
|
|
42
|
+
"tiered caching",
|
|
43
|
+
],
|
|
44
|
+
];
|
|
45
|
+
|
|
46
|
+
/** Marked **Manual** in Cloudflare's rename table: no automatic state move.
|
|
47
|
+
* Applying the config edit alone destroys and recreates the resource. */
|
|
48
|
+
const MANUAL_RENAMES: [from: string, to: string][] = [
|
|
49
|
+
["cloudflare_access_custom_page", "cloudflare_zero_trust_access_custom_page"],
|
|
50
|
+
["cloudflare_access_keys_configuration", "cloudflare_zero_trust_access_key_configuration"],
|
|
51
|
+
["cloudflare_device_policy_certificates", "cloudflare_zero_trust_device_certificates"],
|
|
52
|
+
["cloudflare_gre_tunnel", "cloudflare_magic_wan_gre_tunnel"],
|
|
53
|
+
];
|
|
54
|
+
|
|
55
|
+
const ENTRIES: TransformEntry[] = [
|
|
56
|
+
{
|
|
57
|
+
id: "cloudflare.4-5.use-tf-migrate",
|
|
58
|
+
kind: "provider_setting",
|
|
59
|
+
resource_type: null,
|
|
60
|
+
from: "manual v4 → v5 rewrite",
|
|
61
|
+
to: "tf-migrate",
|
|
62
|
+
state_move: "auto",
|
|
63
|
+
forces_replacement: false,
|
|
64
|
+
note: "Cloudflare maintains `tf-migrate` (github.com/cloudflare/tf-migrate) for this upgrade: it rewrites resource types, transforms attributes, generates `moved {}` blocks and fixes cross-file references. Run it first and review its diff — do not hand-migrate what it already handles. Terraform 1.8+ is needed for `moved {}`, and provider v5.19+ ships the automatic state upgraders.",
|
|
65
|
+
},
|
|
66
|
+
...AUTO_RENAMES.map(
|
|
67
|
+
([from, to]): TransformEntry => ({
|
|
68
|
+
id: `cloudflare.4-5.resource.${from}`,
|
|
69
|
+
kind: "resource_renamed",
|
|
70
|
+
resource_type: from,
|
|
71
|
+
from,
|
|
72
|
+
to,
|
|
73
|
+
state_move: "moved_block",
|
|
74
|
+
forces_replacement: false,
|
|
75
|
+
note: `Renamed in v5: \`${from}\` → \`${to}\`. Author a \`moved {}\` block (or let \`tf-migrate\` generate it) so state follows the rename instead of the resource being destroyed and recreated.`,
|
|
76
|
+
}),
|
|
77
|
+
),
|
|
78
|
+
...SPLIT_RENAMES.map(
|
|
79
|
+
([from, to, alsoNeeded, what]): TransformEntry => ({
|
|
80
|
+
id: `cloudflare.4-5.resource.${from}`,
|
|
81
|
+
kind: "resource_renamed",
|
|
82
|
+
resource_type: from,
|
|
83
|
+
from,
|
|
84
|
+
to,
|
|
85
|
+
state_move: "moved_block",
|
|
86
|
+
forces_replacement: false,
|
|
87
|
+
value_change: `⚠️ v4's \`${from}\` managed TWO things; v5 splits them. \`${to}\` inherits the state via \`moved {}\`, but ${what} does NOT come with it — it needs a fresh \`${alsoNeeded}\` resource. Rename alone and ${what} silently stops being managed by Terraform, with no plan diff to warn you.`,
|
|
88
|
+
note: `Renamed AND split in v5: \`${from}\` → \`${to}\` + \`${alsoNeeded}\`. Author the \`moved {}\` block for the first and create the second.`,
|
|
89
|
+
}),
|
|
90
|
+
),
|
|
91
|
+
...MANUAL_RENAMES.map(
|
|
92
|
+
([from, to]): TransformEntry => ({
|
|
93
|
+
id: `cloudflare.4-5.resource.${from}`,
|
|
94
|
+
kind: "resource_renamed",
|
|
95
|
+
resource_type: from,
|
|
96
|
+
from,
|
|
97
|
+
to,
|
|
98
|
+
state_move: "manual_import",
|
|
99
|
+
forces_replacement: true,
|
|
100
|
+
note: `\`${from}\` → \`${to}\` is marked **Manual** in Cloudflare's own rename table: there is no automatic state move, so a \`moved {}\` block will not help. This needs \`terraform state rm\` + \`terraform import\` performed by a human.`,
|
|
101
|
+
}),
|
|
102
|
+
),
|
|
103
|
+
];
|
|
104
|
+
|
|
105
|
+
export const CLOUDFLARE_4_TO_5: TransformPack = {
|
|
106
|
+
provider: "cloudflare",
|
|
107
|
+
from_major: 4,
|
|
108
|
+
to_major: 5,
|
|
109
|
+
version: "2026.07",
|
|
110
|
+
source: SOURCE,
|
|
111
|
+
exhaustive: false,
|
|
112
|
+
official_tool: "tf-migrate — https://github.com/cloudflare/tf-migrate",
|
|
113
|
+
entries: ENTRIES,
|
|
114
|
+
};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
import { discoverTerraformRoots } from "#app/mcp/roots";
|
|
3
|
+
import { run } from "#app/mcp/terraform/subprocess";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Normalize a scanner-reported path to a repo-relative POSIX path. Each scanner
|
|
7
|
+
* reports the file differently — tflint gives `main.tf` (relative), trivy a
|
|
8
|
+
* scan-dir-relative `Target`, terraform an absolute path (`/repo/main.tf` or
|
|
9
|
+
* `D:\repo\main.tf`), checkov a leading-slash path (`/main.tf`). Left
|
|
10
|
+
* unnormalized, these leak into `location.file` AND the
|
|
11
|
+
* content-derived `concernId`, making the id (and the `remediate/<id>` branch)
|
|
12
|
+
* machine-dependent and breaking the ✗→✓ re-scan id match across environments.
|
|
13
|
+
* So normalize BEFORE building any Concern.
|
|
14
|
+
*/
|
|
15
|
+
export function toRepoRelative(raw: string | undefined, cwd: string): string {
|
|
16
|
+
if (!raw) return "(unknown)";
|
|
17
|
+
const posix = raw.replace(/\\/g, "/");
|
|
18
|
+
const cwdPosix = cwd.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
19
|
+
let rel = posix;
|
|
20
|
+
if (cwdPosix && posix.toLowerCase().startsWith(`${cwdPosix.toLowerCase()}/`)) {
|
|
21
|
+
rel = posix.slice(cwdPosix.length + 1);
|
|
22
|
+
}
|
|
23
|
+
// strip any leading "./" or "/" (checkov reports paths relative to its -d root
|
|
24
|
+
// with a leading separator).
|
|
25
|
+
rel = rel.replace(/^(?:\.\/|\/)+/, "");
|
|
26
|
+
return rel || "(unknown)";
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface ResolvedRoot {
|
|
30
|
+
/** absolute dir the per-root command (init/plan/validate) runs in. */
|
|
31
|
+
absDir: string;
|
|
32
|
+
/** that root's path relative to the scan `cwd` ("" when the root IS cwd) —
|
|
33
|
+
* prepended to per-root concern files so they stay cwd-relative. */
|
|
34
|
+
relDir: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* The Terraform root modules to operate on under `cwd`. A repo can hold several
|
|
39
|
+
* roots (hepcare: `terraform/` + `terraform/core/`); `terraform validate` /
|
|
40
|
+
* `plan` are per-root, so they must run in EACH. Falls back to `cwd` itself as a
|
|
41
|
+
* single root when none is detected — so a normal single-root repo behaves
|
|
42
|
+
* exactly as before (no rebasing, one iteration).
|
|
43
|
+
*/
|
|
44
|
+
export function resolveRoots(cwd: string): ResolvedRoot[] {
|
|
45
|
+
const discovered = discoverTerraformRoots(cwd);
|
|
46
|
+
if (discovered.length === 0) return [{ absDir: cwd, relDir: "" }];
|
|
47
|
+
return discovered.map((r) => ({ absDir: r.dir ? join(cwd, r.dir) : cwd, relDir: r.dir }));
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** the repo's base ref for diff-scope, or null when one can't be determined. */
|
|
51
|
+
export function resolveBaseRef(cwd: string): string | null {
|
|
52
|
+
const head = run("git", ["rev-parse", "--abbrev-ref", "origin/HEAD"], cwd);
|
|
53
|
+
if (head.status === 0 && head.stdout.trim()) return head.stdout.trim();
|
|
54
|
+
for (const ref of ["origin/main", "origin/master"]) {
|
|
55
|
+
const verify = run("git", ["rev-parse", "--verify", "--quiet", ref], cwd);
|
|
56
|
+
if (verify.status === 0 && verify.stdout.trim()) return ref;
|
|
57
|
+
}
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
import type { BlastTier } from "#app/mcp/terraform/types";
|
|
2
|
+
|
|
3
|
+
// --- terraform plan (the safety gate) -------------------------------------
|
|
4
|
+
|
|
5
|
+
export interface PlanSummary {
|
|
6
|
+
/** resources to add / change / destroy, from the plan's change_summary. */
|
|
7
|
+
add: number;
|
|
8
|
+
change: number;
|
|
9
|
+
destroy: number;
|
|
10
|
+
/** every resource with a real action (create/update/delete/replace) — the set
|
|
11
|
+
* that powers blast-radius and plan-stability. */
|
|
12
|
+
changed: { address: string; action: string }[];
|
|
13
|
+
/** resources that would be deleted or replaced — the destructive set. */
|
|
14
|
+
destructive: { address: string; action: string }[];
|
|
15
|
+
hasDestroyOrReplace: boolean;
|
|
16
|
+
/** state-only moves (`moved {}` blocks / refactors): the new address and the
|
|
17
|
+
* address it came from. Moves don't mutate live infrastructure, so they are
|
|
18
|
+
* NOT in `changed` — they power the modularization gate (`isPureMovePlan`). */
|
|
19
|
+
moved: { address: string; previousAddress: string | null }[];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Parse `terraform plan -json` (newline-delimited JSON). `change_summary` gives
|
|
24
|
+
* the add/change/destroy totals; each `planned_change` with a real action is
|
|
25
|
+
* collected into `changed`, and the delete/replace subset into `destructive`
|
|
26
|
+
* (the high-risk set a reviewer must scrutinise). Non-mutating actions are
|
|
27
|
+
* ignored, as are non-JSON / non-plan lines, so a noisy stream (provider logs,
|
|
28
|
+
* diagnostics) parses cleanly.
|
|
29
|
+
*
|
|
30
|
+
* NB on the action enum: terraform's machine-readable UI (the `-json` stream)
|
|
31
|
+
* spells no-op as `"noop"` — NOT `"no-op"` — and also emits `"move"` / `"import"`
|
|
32
|
+
* / `"forget"` for state-only operations that don't mutate live infrastructure.
|
|
33
|
+
* None of those should count toward `changed` (they'd inflate the blast
|
|
34
|
+
* radius). We skip them explicitly; `"no-op"` is tolerated too in case a wrapper
|
|
35
|
+
* or older format hyphenates it. See
|
|
36
|
+
* https://developer.hashicorp.com/terraform/internals/machine-readable-ui.
|
|
37
|
+
*/
|
|
38
|
+
const NON_MUTATING_PLAN_ACTIONS: ReadonlySet<string> = new Set([
|
|
39
|
+
"noop",
|
|
40
|
+
"no-op",
|
|
41
|
+
"read",
|
|
42
|
+
"move",
|
|
43
|
+
"import",
|
|
44
|
+
"forget",
|
|
45
|
+
]);
|
|
46
|
+
|
|
47
|
+
export function parseTerraformPlanJson(stdout: string): PlanSummary {
|
|
48
|
+
let add = 0;
|
|
49
|
+
let change = 0;
|
|
50
|
+
let destroy = 0;
|
|
51
|
+
const changed: { address: string; action: string }[] = [];
|
|
52
|
+
const destructive: { address: string; action: string }[] = [];
|
|
53
|
+
const moved: { address: string; previousAddress: string | null }[] = [];
|
|
54
|
+
for (const line of stdout.split("\n")) {
|
|
55
|
+
const trimmed = line.trim();
|
|
56
|
+
if (!trimmed.startsWith("{")) continue;
|
|
57
|
+
let msg: {
|
|
58
|
+
type?: string;
|
|
59
|
+
changes?: { add?: number; change?: number; remove?: number };
|
|
60
|
+
change?: {
|
|
61
|
+
action?: string;
|
|
62
|
+
resource?: { addr?: string; resource?: string };
|
|
63
|
+
previous_resource?: { addr?: string; resource?: string };
|
|
64
|
+
};
|
|
65
|
+
};
|
|
66
|
+
try {
|
|
67
|
+
msg = JSON.parse(trimmed);
|
|
68
|
+
} catch {
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
if (msg.type === "change_summary" && msg.changes) {
|
|
72
|
+
add = Number(msg.changes.add) || 0;
|
|
73
|
+
change = Number(msg.changes.change) || 0;
|
|
74
|
+
destroy = Number(msg.changes.remove) || 0;
|
|
75
|
+
} else if (msg.type === "planned_change" && msg.change) {
|
|
76
|
+
const action = String(msg.change.action ?? "");
|
|
77
|
+
// moves are state-only (no infra mutation) but are tracked separately —
|
|
78
|
+
// they prove a modularization refactor is a no-op (`isPureMovePlan`).
|
|
79
|
+
if (action === "move") {
|
|
80
|
+
const address =
|
|
81
|
+
msg.change.resource?.addr ||
|
|
82
|
+
msg.change.resource?.resource ||
|
|
83
|
+
"(unknown)";
|
|
84
|
+
const previousAddress =
|
|
85
|
+
msg.change.previous_resource?.addr ||
|
|
86
|
+
msg.change.previous_resource?.resource ||
|
|
87
|
+
null;
|
|
88
|
+
moved.push({ address, previousAddress });
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
if (!action || NON_MUTATING_PLAN_ACTIONS.has(action)) continue;
|
|
92
|
+
const address =
|
|
93
|
+
msg.change.resource?.addr ||
|
|
94
|
+
msg.change.resource?.resource ||
|
|
95
|
+
"(unknown)";
|
|
96
|
+
changed.push({ address, action });
|
|
97
|
+
// "delete", "replace", and the "*-then-delete" / "delete-then-*" forms.
|
|
98
|
+
if (action.includes("delete") || action === "replace") {
|
|
99
|
+
destructive.push({ address, action });
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return {
|
|
104
|
+
add,
|
|
105
|
+
change,
|
|
106
|
+
destroy,
|
|
107
|
+
changed,
|
|
108
|
+
destructive,
|
|
109
|
+
hasDestroyOrReplace: destructive.length > 0,
|
|
110
|
+
moved,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* modularization gate — true when the plan is PURELY state moves: at least
|
|
116
|
+
* one `moved` entry and zero create/update/delete/replace. That proves a
|
|
117
|
+
* resources→module refactor preserved every resource address (via `moved {}`
|
|
118
|
+
* blocks) and is a no-op on live infrastructure — the condition under which a
|
|
119
|
+
* modularization PR may proceed without human escalation.
|
|
120
|
+
*/
|
|
121
|
+
export function isPureMovePlan(plan: {
|
|
122
|
+
add: number;
|
|
123
|
+
change: number;
|
|
124
|
+
destroy: number;
|
|
125
|
+
changed: { address: string }[];
|
|
126
|
+
moved: { address: string }[];
|
|
127
|
+
}): boolean {
|
|
128
|
+
return (
|
|
129
|
+
plan.moved.length > 0 &&
|
|
130
|
+
plan.changed.length === 0 &&
|
|
131
|
+
plan.add + plan.change + plan.destroy === 0
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// --- stateful destroy/replace classification (safety gate) ------------
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Resource types that hold data/state — destroying or replacing one of these
|
|
139
|
+
* means data loss, not just recreation. A remediation that would delete or
|
|
140
|
+
* replace one is hard-blocked at push time unless the operator opts in via the
|
|
141
|
+
* `allow_replace` input. Not exhaustive: it covers the common managed
|
|
142
|
+
* datastores across AWS / Azure / GCP; extend as new ones come up.
|
|
143
|
+
*/
|
|
144
|
+
export const STATEFUL_RESOURCE_TYPES: ReadonlySet<string> = new Set([
|
|
145
|
+
// AWS
|
|
146
|
+
"aws_db_instance",
|
|
147
|
+
"aws_rds_cluster",
|
|
148
|
+
"aws_rds_cluster_instance",
|
|
149
|
+
"aws_s3_bucket",
|
|
150
|
+
"aws_ebs_volume",
|
|
151
|
+
"aws_efs_file_system",
|
|
152
|
+
"aws_dynamodb_table",
|
|
153
|
+
"aws_dynamodb_global_table",
|
|
154
|
+
"aws_elasticache_cluster",
|
|
155
|
+
"aws_elasticache_replication_group",
|
|
156
|
+
"aws_redshift_cluster",
|
|
157
|
+
"aws_docdb_cluster",
|
|
158
|
+
"aws_neptune_cluster",
|
|
159
|
+
"aws_opensearch_domain",
|
|
160
|
+
"aws_elasticsearch_domain",
|
|
161
|
+
// Azure
|
|
162
|
+
"azurerm_sql_database",
|
|
163
|
+
"azurerm_mssql_database",
|
|
164
|
+
"azurerm_postgresql_database",
|
|
165
|
+
"azurerm_postgresql_flexible_server",
|
|
166
|
+
"azurerm_mysql_database",
|
|
167
|
+
"azurerm_mysql_flexible_server",
|
|
168
|
+
"azurerm_cosmosdb_account",
|
|
169
|
+
"azurerm_cosmosdb_sql_database",
|
|
170
|
+
// The container is where the documents actually live, and replacing one (a
|
|
171
|
+
// partition-key change does exactly that) empties it. Added 2026-07-27 after
|
|
172
|
+
// TC-2.2's azurerm v3→v4 pack surfaced a documented breaking change that
|
|
173
|
+
// forces this replacement while the guard below could not see it.
|
|
174
|
+
"azurerm_cosmosdb_sql_container",
|
|
175
|
+
"azurerm_storage_account",
|
|
176
|
+
// Data-bearing children of a storage account: the share directory and the
|
|
177
|
+
// table entity hold content the account itself does not.
|
|
178
|
+
"azurerm_storage_share_directory",
|
|
179
|
+
"azurerm_storage_table_entity",
|
|
180
|
+
"azurerm_managed_disk",
|
|
181
|
+
// GCP
|
|
182
|
+
"google_sql_database_instance",
|
|
183
|
+
"google_storage_bucket",
|
|
184
|
+
"google_bigtable_instance",
|
|
185
|
+
"google_bigquery_dataset",
|
|
186
|
+
"google_spanner_database",
|
|
187
|
+
"google_redis_instance",
|
|
188
|
+
"google_filestore_instance",
|
|
189
|
+
"google_compute_disk",
|
|
190
|
+
]);
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Extract the Terraform resource TYPE from a plan address, stripping any
|
|
194
|
+
* `module.<name>.` prefixes and an instance index/key suffix:
|
|
195
|
+
* `module.db.aws_db_instance.main` -> `aws_db_instance`
|
|
196
|
+
* `aws_s3_bucket.data["prod"]` -> `aws_s3_bucket`
|
|
197
|
+
* `module.a.module.b.google_storage_bucket.x[0]` -> `google_storage_bucket`
|
|
198
|
+
* Returns "" when the address has no parseable `type.name` pair.
|
|
199
|
+
*/
|
|
200
|
+
export function resourceTypeOf(address: string): string {
|
|
201
|
+
const withoutModules = address.replace(/^(?:module\.[^.]+\.)+/, "");
|
|
202
|
+
const cleaned = withoutModules.replace(/\[[^\]]*\]$/, "");
|
|
203
|
+
const segments = cleaned.split(".");
|
|
204
|
+
return segments.at(-2) ?? "";
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
export interface DestroyClassification {
|
|
208
|
+
/** destroy/replace of a data-bearing type — high-risk, blocked by default. */
|
|
209
|
+
stateful: { address: string; action: string; type: string }[];
|
|
210
|
+
/** destroy/replace of a recreatable type — recorded, not blocked. */
|
|
211
|
+
ephemeral: { address: string; action: string; type: string }[];
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/** partition a plan's destructive set into stateful (blocked) vs ephemeral. */
|
|
215
|
+
export function classifyDestructive(
|
|
216
|
+
destructive: { address: string; action: string }[],
|
|
217
|
+
): DestroyClassification {
|
|
218
|
+
const stateful: DestroyClassification["stateful"] = [];
|
|
219
|
+
const ephemeral: DestroyClassification["ephemeral"] = [];
|
|
220
|
+
for (const d of destructive) {
|
|
221
|
+
const type = resourceTypeOf(d.address);
|
|
222
|
+
(STATEFUL_RESOURCE_TYPES.has(type) ? stateful : ephemeral).push({
|
|
223
|
+
...d,
|
|
224
|
+
type,
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
return { stateful, ephemeral };
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// --- blast-radius scoring -------------------------------------------
|
|
231
|
+
|
|
232
|
+
export interface BlastRadius {
|
|
233
|
+
tier: BlastTier;
|
|
234
|
+
/** count of resources the plan would create/update/delete/replace. */
|
|
235
|
+
resourceCount: number;
|
|
236
|
+
/** distinct module addresses touched (root resources count as `root`). */
|
|
237
|
+
modules: string[];
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Extract the module address from a resource address: the `module.X[.module.Y]`
|
|
242
|
+
* call path, or `root` for a top-level resource. Strips instance index/key from
|
|
243
|
+
* EVERY segment — a `count`/`for_each` MODULE carries its key on the module
|
|
244
|
+
* segment (`module.net[0]`), so all instances of one module collapse to one
|
|
245
|
+
* address (else a single-module fix would look cross-module). Removing keys
|
|
246
|
+
* first also tolerates a `.` inside a `for_each` string key.
|
|
247
|
+
* `aws_s3_bucket.b` -> `root`
|
|
248
|
+
* `module.db.aws_db_instance.main` -> `module.db`
|
|
249
|
+
* `module.net[0].aws_vpc.main` -> `module.net`
|
|
250
|
+
* `module.a.module.b.google_x.y[0]` -> `module.a.module.b`
|
|
251
|
+
*/
|
|
252
|
+
export function moduleAddressOf(address: string): string {
|
|
253
|
+
const cleaned = address.replace(/\[[^\]]*\]/g, "");
|
|
254
|
+
const segments = cleaned.split(".");
|
|
255
|
+
// the resource is the final `type.name` pair; anything before is the module path.
|
|
256
|
+
return segments.length <= 2
|
|
257
|
+
? "root"
|
|
258
|
+
: segments.slice(0, segments.length - 2).join(".");
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Score how much a fix touches, to route large changes through stricter review:
|
|
263
|
+
* 1–2 resources = `low`, 3–10 = `medium`, more than 10 OR spanning more than one
|
|
264
|
+
* module = `high`. A `high` blast radius should force human-in-the-loop
|
|
265
|
+
* regardless of finding severity. 0 changes is `low` (nothing to do).
|
|
266
|
+
*/
|
|
267
|
+
export function computeBlastRadius(
|
|
268
|
+
changed: { address: string }[],
|
|
269
|
+
): BlastRadius {
|
|
270
|
+
const resourceCount = changed.length;
|
|
271
|
+
const modules = [
|
|
272
|
+
...new Set(changed.map((c) => moduleAddressOf(c.address))),
|
|
273
|
+
].sort();
|
|
274
|
+
const crossModule = modules.length > 1;
|
|
275
|
+
let tier: BlastTier;
|
|
276
|
+
if (resourceCount > 10 || crossModule) tier = "high";
|
|
277
|
+
else if (resourceCount >= 3) tier = "medium";
|
|
278
|
+
else tier = "low";
|
|
279
|
+
return { tier, resourceCount, modules };
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// --- plan stability / idempotency -----------------------------------
|
|
283
|
+
|
|
284
|
+
export interface StabilityResult {
|
|
285
|
+
/** true when a second plan produced the identical change set. */
|
|
286
|
+
stable: boolean;
|
|
287
|
+
reason?: string;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/** a normalized signature of a plan's change set (summary counts + sorted
|
|
291
|
+
* address:action pairs) — two plans with the same signature are equivalent. */
|
|
292
|
+
function planSignature(s: PlanSummary): string {
|
|
293
|
+
const set = s.changed
|
|
294
|
+
.map((c) => `${c.address}:${c.action}`)
|
|
295
|
+
.sort()
|
|
296
|
+
.join(",");
|
|
297
|
+
return `+${s.add}~${s.change}-${s.destroy}|${set}`;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Compare two consecutive plans for stability. Infraweaver never `apply`s (it only
|
|
302
|
+
* opens PRs), so a true "no perpetual diff after apply" cannot be proven here —
|
|
303
|
+
* but a fix whose plan is non-deterministic (e.g. `timestamp()`, `uuid()`, an
|
|
304
|
+
* unkeyed `random_*`, or a data source that varies run-to-run) yields a DIFFERENT
|
|
305
|
+
* plan on the second run, and that is a real perpetual-diff smell we can catch
|
|
306
|
+
* without applying. Stable ⇒ the two plans matched; unstable ⇒ report it.
|
|
307
|
+
*/
|
|
308
|
+
export function comparePlanStability(
|
|
309
|
+
first: PlanSummary,
|
|
310
|
+
second: PlanSummary,
|
|
311
|
+
): StabilityResult {
|
|
312
|
+
if (planSignature(first) === planSignature(second)) return { stable: true };
|
|
313
|
+
return {
|
|
314
|
+
stable: false,
|
|
315
|
+
reason:
|
|
316
|
+
`the plan is not deterministic — a second \`terraform plan\` (same state, no apply) produced a ` +
|
|
317
|
+
`different change set (first: +${first.add} ~${first.change} -${first.destroy}; ` +
|
|
318
|
+
`second: +${second.add} ~${second.change} -${second.destroy}). This is a perpetual-diff smell, ` +
|
|
319
|
+
`usually a non-deterministic value in the config (timestamp()/uuid()/unkeyed random_*/a varying data source).`,
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// --- multi-root plan aggregation -------------------------------------------
|
|
324
|
+
|
|
325
|
+
export interface RootPlan {
|
|
326
|
+
/** display label for the root ("." for the top-level root). */
|
|
327
|
+
dir: string;
|
|
328
|
+
summary: PlanSummary;
|
|
329
|
+
stable: boolean;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
export interface AggregatedPlan {
|
|
333
|
+
add: number;
|
|
334
|
+
change: number;
|
|
335
|
+
destroy: number;
|
|
336
|
+
changed: { address: string; action: string }[];
|
|
337
|
+
destructive: { address: string; action: string }[];
|
|
338
|
+
hasDestroyOrReplace: boolean;
|
|
339
|
+
idempotent: boolean;
|
|
340
|
+
moved: { address: string; previousAddress: string | null }[];
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* Aggregate per-root plan results into one view: SUM the add/change/destroy
|
|
345
|
+
* counts, UNION the changed + destructive sets (so blast-radius and the
|
|
346
|
+
* destroy-block see every root's effect), and treat the whole run as
|
|
347
|
+
* non-idempotent if ANY root's plan was unstable. Pure. Single-root input passes
|
|
348
|
+
* straight through (identical to the pre-multi-root behaviour).
|
|
349
|
+
*/
|
|
350
|
+
export function aggregatePlans(roots: RootPlan[]): AggregatedPlan {
|
|
351
|
+
let add = 0;
|
|
352
|
+
let change = 0;
|
|
353
|
+
let destroy = 0;
|
|
354
|
+
let idempotent = true;
|
|
355
|
+
const changed: { address: string; action: string }[] = [];
|
|
356
|
+
const destructive: { address: string; action: string }[] = [];
|
|
357
|
+
const moved: { address: string; previousAddress: string | null }[] = [];
|
|
358
|
+
for (const r of roots) {
|
|
359
|
+
add += r.summary.add;
|
|
360
|
+
change += r.summary.change;
|
|
361
|
+
destroy += r.summary.destroy;
|
|
362
|
+
changed.push(...r.summary.changed);
|
|
363
|
+
destructive.push(...r.summary.destructive);
|
|
364
|
+
moved.push(...r.summary.moved);
|
|
365
|
+
if (!r.stable) idempotent = false;
|
|
366
|
+
}
|
|
367
|
+
return {
|
|
368
|
+
add,
|
|
369
|
+
change,
|
|
370
|
+
destroy,
|
|
371
|
+
changed,
|
|
372
|
+
destructive,
|
|
373
|
+
hasDestroyOrReplace: destructive.length > 0,
|
|
374
|
+
idempotent,
|
|
375
|
+
moved,
|
|
376
|
+
};
|
|
377
|
+
}
|