terramend 0.2.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 +145 -0
- package/dist/agents/claude.d.ts +73 -0
- package/dist/agents/claudePretoolGate.d.ts +99 -0
- package/dist/agents/gateServer.d.ts +7 -0
- package/dist/agents/index.d.ts +6 -0
- package/dist/agents/nativeFsDenies.d.ts +28 -0
- package/dist/agents/opencode.d.ts +231 -0
- package/dist/agents/opencodePlugin.d.ts +85 -0
- package/dist/agents/opencodeShared.d.ts +40 -0
- package/dist/agents/postRun.d.ts +132 -0
- package/dist/agents/reviewer.d.ts +38 -0
- package/dist/agents/sessionLabeler.d.ts +97 -0
- package/dist/agents/shared.d.ts +189 -0
- package/dist/agents/subagentModels.d.ts +19 -0
- package/dist/agents/subagentToolGates.d.ts +55 -0
- package/dist/cli.mjs +197426 -0
- package/dist/external.d.ts +227 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +196783 -0
- package/dist/internal/index.d.ts +18 -0
- package/dist/internal.js +1714 -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/checkSuite.d.ts +25 -0
- package/dist/mcp/checkout.d.ts +77 -0
- package/dist/mcp/comment.d.ts +119 -0
- package/dist/mcp/commitInfo.d.ts +9 -0
- package/dist/mcp/crosswalk.d.ts +105 -0
- package/dist/mcp/dependencies.d.ts +8 -0
- package/dist/mcp/geminiSanitizer.d.ts +28 -0
- package/dist/mcp/git.d.ts +46 -0
- package/dist/mcp/guardrails.d.ts +104 -0
- package/dist/mcp/issue.d.ts +18 -0
- package/dist/mcp/issueComments.d.ts +9 -0
- package/dist/mcp/issueEvents.d.ts +9 -0
- package/dist/mcp/issueInfo.d.ts +9 -0
- package/dist/mcp/labels.d.ts +12 -0
- package/dist/mcp/localContext.d.ts +19 -0
- package/dist/mcp/moduleExtraction.d.ts +71 -0
- package/dist/mcp/moduleTests.d.ts +104 -0
- package/dist/mcp/modules.d.ts +179 -0
- package/dist/mcp/output.d.ts +12 -0
- package/dist/mcp/pathSafety.d.ts +14 -0
- package/dist/mcp/policy.d.ts +48 -0
- package/dist/mcp/pr.d.ts +49 -0
- package/dist/mcp/prInfo.d.ts +9 -0
- package/dist/mcp/providerSchema.d.ts +50 -0
- package/dist/mcp/review.d.ts +199 -0
- package/dist/mcp/reviewComments.d.ts +178 -0
- package/dist/mcp/roots.d.ts +58 -0
- package/dist/mcp/scope.d.ts +15 -0
- package/dist/mcp/selectMode.d.ts +18 -0
- package/dist/mcp/server.d.ts +48 -0
- package/dist/mcp/shared.d.ts +47 -0
- package/dist/mcp/shell.d.ts +37 -0
- package/dist/mcp/staleFix.d.ts +51 -0
- package/dist/mcp/terraform/cost.d.ts +55 -0
- package/dist/mcp/terraform/currency.d.ts +94 -0
- package/dist/mcp/terraform/decisions.d.ts +178 -0
- package/dist/mcp/terraform/findings.d.ts +75 -0
- package/dist/mcp/terraform/plan.d.ts +157 -0
- package/dist/mcp/terraform/scanners.d.ts +131 -0
- package/dist/mcp/terraform/tools.d.ts +63 -0
- package/dist/mcp/terraform/types.d.ts +172 -0
- package/dist/mcp/terraform.d.ts +22 -0
- package/dist/mcp/terratest.d.ts +83 -0
- package/dist/mcp/upload.d.ts +6 -0
- package/dist/models.d.ts +171 -0
- package/dist/modes.d.ts +26 -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 +64 -0
- package/dist/skills/terraform-best-practices/SKILL.md +369 -0
- package/dist/toolState.d.ts +135 -0
- package/dist/utils/activity.d.ts +40 -0
- package/dist/utils/agent.d.ts +20 -0
- package/dist/utils/agentHangReport.d.ts +38 -0
- package/dist/utils/apiFetch.d.ts +19 -0
- package/dist/utils/apiKeys.d.ts +41 -0
- package/dist/utils/apiUrl.d.ts +20 -0
- package/dist/utils/assets.d.ts +8 -0
- package/dist/utils/billingErrors.d.ts +85 -0
- package/dist/utils/body.d.ts +34 -0
- package/dist/utils/buildTerramendFooter.d.ts +25 -0
- package/dist/utils/byokFallback.d.ts +85 -0
- package/dist/utils/claudeSubscription.d.ts +30 -0
- package/dist/utils/cli.d.ts +10 -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/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 +78 -0
- package/dist/utils/globals.d.ts +3 -0
- package/dist/utils/install.d.ts +60 -0
- package/dist/utils/instructions.d.ts +48 -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/normalizeEnv.d.ts +30 -0
- package/dist/utils/openCodeModels.d.ts +11 -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 +105 -0
- package/dist/utils/prSummary.d.ts +61 -0
- package/dist/utils/progressComment.d.ts +146 -0
- package/dist/utils/providerErrors.d.ts +31 -0
- package/dist/utils/rangeDiff.d.ts +51 -0
- package/dist/utils/remediationCommand.d.ts +55 -0
- package/dist/utils/retry.d.ts +13 -0
- package/dist/utils/reviewCleanup.d.ts +14 -0
- package/dist/utils/run.d.ts +9 -0
- package/dist/utils/runContext.d.ts +60 -0
- package/dist/utils/runContextData.d.ts +23 -0
- package/dist/utils/runErrorRenderer.d.ts +64 -0
- package/dist/utils/runLifecycle.d.ts +86 -0
- package/dist/utils/runStartupLog.d.ts +15 -0
- package/dist/utils/secrets.d.ts +22 -0
- package/dist/utils/setup.d.ts +90 -0
- package/dist/utils/shell.d.ts +32 -0
- package/dist/utils/skills.d.ts +10 -0
- package/dist/utils/subprocess.d.ts +80 -0
- package/dist/utils/terraformMcp.d.ts +42 -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 +39 -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 +119 -0
- package/src/agents/claude.test.ts +1016 -0
- package/src/agents/claude.ts +1246 -0
- package/src/agents/claudePretoolGate.test.ts +28 -0
- package/src/agents/claudePretoolGate.ts +173 -0
- package/src/agents/gateServer.test.ts +204 -0
- package/src/agents/gateServer.ts +124 -0
- package/src/agents/index.ts +10 -0
- package/src/agents/nativeFsDenies.ts +82 -0
- package/src/agents/opencode.test.ts +1440 -0
- package/src/agents/opencode.ts +1312 -0
- package/src/agents/opencodePlugin.ts +222 -0
- package/src/agents/opencodeShared.test.ts +34 -0
- package/src/agents/opencodeShared.ts +121 -0
- package/src/agents/postRun.test.ts +549 -0
- package/src/agents/postRun.ts +535 -0
- package/src/agents/reviewer.ts +104 -0
- package/src/agents/sessionLabeler.test.ts +247 -0
- package/src/agents/sessionLabeler.ts +178 -0
- package/src/agents/shared.test.ts +76 -0
- package/src/agents/shared.ts +292 -0
- package/src/agents/subagentModels.test.ts +113 -0
- package/src/agents/subagentModels.ts +40 -0
- package/src/agents/subagentRegistration.test.ts +41 -0
- package/src/agents/subagentToolGates.ts +114 -0
- package/src/cli.test.ts +129 -0
- package/src/cli.ts +105 -0
- package/src/commands/gha.test.ts +192 -0
- package/src/commands/gha.ts +188 -0
- package/src/commands/mcp.ts +122 -0
- package/src/config.ts +1 -0
- package/src/entry.ts +7 -0
- package/src/entryPost.stdlibOnly.test.ts +109 -0
- package/src/entryPost.ts +99 -0
- package/src/external.test.ts +16 -0
- package/src/external.ts +302 -0
- package/src/index.ts +11 -0
- package/src/internal/index.ts +71 -0
- package/src/lifecycle.ts +2 -0
- package/src/main.test.ts +873 -0
- package/src/main.ts +712 -0
- package/src/mcp/__fixtures__/terramend-scratch-pr-49-review-3485940013.json +110 -0
- package/src/mcp/__fixtures__/terramend-scratch-pr-64-review-3531000326.json +14 -0
- package/src/mcp/__fixtures__/terramend-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/checkSuite.test.ts +245 -0
- package/src/mcp/checkSuite.ts +255 -0
- package/src/mcp/checkout.test.ts +752 -0
- package/src/mcp/checkout.ts +886 -0
- package/src/mcp/comment.test.ts +772 -0
- package/src/mcp/comment.ts +582 -0
- package/src/mcp/commitInfo.test.ts +127 -0
- package/src/mcp/commitInfo.ts +61 -0
- package/src/mcp/crosswalk.test.ts +106 -0
- package/src/mcp/crosswalk.ts +339 -0
- package/src/mcp/dependencies.test.ts +309 -0
- package/src/mcp/dependencies.ts +189 -0
- package/src/mcp/geminiSanitizer.test.ts +287 -0
- package/src/mcp/geminiSanitizer.ts +207 -0
- package/src/mcp/git.test.ts +1083 -0
- package/src/mcp/git.ts +890 -0
- package/src/mcp/guardrails.test.ts +705 -0
- package/src/mcp/guardrails.ts +465 -0
- package/src/mcp/issue.test.ts +113 -0
- package/src/mcp/issue.ts +73 -0
- package/src/mcp/issueComments.test.ts +69 -0
- package/src/mcp/issueComments.ts +48 -0
- package/src/mcp/issueEvents.test.ts +134 -0
- package/src/mcp/issueEvents.ts +100 -0
- package/src/mcp/issueInfo.test.ts +104 -0
- package/src/mcp/issueInfo.ts +72 -0
- package/src/mcp/labels.test.ts +52 -0
- package/src/mcp/labels.ts +34 -0
- package/src/mcp/localContext.ts +28 -0
- package/src/mcp/localServer.test.ts +75 -0
- package/src/mcp/localServer.ts +131 -0
- package/src/mcp/moduleExtraction.test.ts +261 -0
- package/src/mcp/moduleExtraction.ts +313 -0
- package/src/mcp/moduleTests.test.ts +269 -0
- package/src/mcp/moduleTests.ts +421 -0
- package/src/mcp/modules.test.ts +640 -0
- package/src/mcp/modules.ts +696 -0
- package/src/mcp/output.test.ts +96 -0
- package/src/mcp/output.ts +70 -0
- package/src/mcp/pathSafety.test.ts +44 -0
- package/src/mcp/pathSafety.ts +28 -0
- package/src/mcp/policy.test.ts +282 -0
- package/src/mcp/policy.ts +199 -0
- package/src/mcp/pr.test.ts +387 -0
- package/src/mcp/pr.ts +194 -0
- package/src/mcp/prInfo.test.ts +96 -0
- package/src/mcp/prInfo.ts +91 -0
- package/src/mcp/providerSchema.test.ts +85 -0
- package/src/mcp/providerSchema.ts +175 -0
- package/src/mcp/review.test.ts +936 -0
- package/src/mcp/review.ts +923 -0
- package/src/mcp/reviewComments.test.ts +549 -0
- package/src/mcp/reviewComments.ts +896 -0
- package/src/mcp/roots.test.ts +175 -0
- package/src/mcp/roots.ts +217 -0
- package/src/mcp/scope.test.ts +59 -0
- package/src/mcp/scope.ts +65 -0
- package/src/mcp/security.test.ts +720 -0
- package/src/mcp/selectMode.test.ts +210 -0
- package/src/mcp/selectMode.ts +181 -0
- package/src/mcp/server.test.ts +292 -0
- package/src/mcp/server.ts +403 -0
- package/src/mcp/shared.ts +100 -0
- package/src/mcp/shell.test.ts +520 -0
- package/src/mcp/shell.ts +505 -0
- package/src/mcp/staleFix.test.ts +237 -0
- package/src/mcp/staleFix.ts +277 -0
- package/src/mcp/terraform/cost.ts +163 -0
- package/src/mcp/terraform/currency.test.ts +338 -0
- package/src/mcp/terraform/currency.ts +336 -0
- package/src/mcp/terraform/decisions.ts +527 -0
- package/src/mcp/terraform/findings.ts +333 -0
- package/src/mcp/terraform/plan.ts +348 -0
- package/src/mcp/terraform/scanners.ts +809 -0
- package/src/mcp/terraform/tools.test.ts +1071 -0
- package/src/mcp/terraform/tools.ts +908 -0
- package/src/mcp/terraform/types.ts +305 -0
- package/src/mcp/terraform.test.ts +1957 -0
- package/src/mcp/terraform.ts +23 -0
- package/src/mcp/terratest.test.ts +105 -0
- package/src/mcp/terratest.ts +196 -0
- package/src/mcp/toolFiltering.test.ts +85 -0
- package/src/mcp/upload.test.ts +180 -0
- package/src/mcp/upload.ts +112 -0
- package/src/models.test.ts +300 -0
- package/src/models.ts +708 -0
- package/src/modes.test.ts +107 -0
- package/src/modes.ts +880 -0
- package/src/prep/index.ts +43 -0
- package/src/prep/installNodeDependencies.test.ts +298 -0
- package/src/prep/installNodeDependencies.ts +196 -0
- package/src/prep/installPythonDependencies.test.ts +268 -0
- package/src/prep/installPythonDependencies.ts +199 -0
- package/src/prep/types.ts +38 -0
- package/src/reviewQuality.test.ts +63 -0
- package/src/reviewQuality.ts +134 -0
- package/src/runCli.test.ts +214 -0
- package/src/runCli.ts +282 -0
- package/src/skills/terraform-best-practices/SKILL.md +369 -0
- package/src/toolState.test.ts +45 -0
- package/src/toolState.ts +252 -0
- package/src/utils/activity.test.ts +188 -0
- package/src/utils/activity.ts +210 -0
- package/src/utils/agent.test.ts +251 -0
- package/src/utils/agent.ts +139 -0
- package/src/utils/agentHangReport.test.ts +203 -0
- package/src/utils/agentHangReport.ts +170 -0
- package/src/utils/apiFetch.test.ts +115 -0
- package/src/utils/apiFetch.ts +62 -0
- package/src/utils/apiKeys.test.ts +344 -0
- package/src/utils/apiKeys.ts +206 -0
- package/src/utils/apiUrl.test.ts +30 -0
- package/src/utils/apiUrl.ts +59 -0
- package/src/utils/assets.test.ts +153 -0
- package/src/utils/assets.ts +107 -0
- package/src/utils/billingErrors.test.ts +121 -0
- package/src/utils/billingErrors.ts +189 -0
- package/src/utils/body.test.ts +217 -0
- package/src/utils/body.ts +168 -0
- package/src/utils/buildTerramendFooter.test.ts +38 -0
- package/src/utils/buildTerramendFooter.ts +82 -0
- package/src/utils/byokFallback.test.ts +205 -0
- package/src/utils/byokFallback.ts +128 -0
- package/src/utils/claudeSubscription.test.ts +179 -0
- package/src/utils/claudeSubscription.ts +93 -0
- package/src/utils/cli.ts +31 -0
- package/src/utils/codexHome.test.ts +190 -0
- package/src/utils/codexHome.ts +191 -0
- package/src/utils/codexOAuth.ts +147 -0
- package/src/utils/codexRefreshDetect.test.ts +85 -0
- package/src/utils/codexRefreshDetect.ts +35 -0
- package/src/utils/diffCoverage.test.ts +468 -0
- package/src/utils/diffCoverage.ts +404 -0
- package/src/utils/errorReport.test.ts +135 -0
- package/src/utils/errorReport.ts +83 -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.test.ts +322 -0
- package/src/utils/gitAuth.ts +263 -0
- package/src/utils/gitAuthServer.test.ts +260 -0
- package/src/utils/gitAuthServer.ts +182 -0
- package/src/utils/github.test.ts +615 -0
- package/src/utils/github.ts +538 -0
- package/src/utils/globals.ts +9 -0
- package/src/utils/humanEditCapture.test.ts +100 -0
- package/src/utils/humanEditCapture.ts +193 -0
- package/src/utils/install.test.ts +768 -0
- package/src/utils/install.ts +492 -0
- package/src/utils/instructions.test.ts +240 -0
- package/src/utils/instructions.ts +543 -0
- package/src/utils/leapingComment.test.ts +51 -0
- package/src/utils/leapingComment.ts +18 -0
- package/src/utils/learnings.test.ts +87 -0
- package/src/utils/learnings.ts +138 -0
- package/src/utils/learningsTocRender.test.ts +116 -0
- package/src/utils/learningsTruncate.test.ts +39 -0
- package/src/utils/learningsTruncate.ts +42 -0
- package/src/utils/lifecycle.test.ts +195 -0
- package/src/utils/lifecycle.ts +198 -0
- package/src/utils/log.test.ts +402 -0
- package/src/utils/log.ts +432 -0
- package/src/utils/normalizeEnv.test.ts +91 -0
- package/src/utils/normalizeEnv.ts +106 -0
- package/src/utils/openCodeModels.ts +82 -0
- package/src/utils/overrides.test.ts +89 -0
- package/src/utils/overrides.ts +98 -0
- package/src/utils/packageManager.test.ts +321 -0
- package/src/utils/packageManager.ts +257 -0
- package/src/utils/patchWorkflowRunFields.test.ts +92 -0
- package/src/utils/patchWorkflowRunFields.ts +150 -0
- package/src/utils/payload.test.ts +497 -0
- package/src/utils/payload.ts +371 -0
- package/src/utils/postApiFetch.ts +51 -0
- package/src/utils/prSummary.test.ts +224 -0
- package/src/utils/prSummary.ts +147 -0
- package/src/utils/progressComment.ts +261 -0
- package/src/utils/providerErrors.test.ts +315 -0
- package/src/utils/providerErrors.ts +172 -0
- package/src/utils/rangeDiff.test.ts +236 -0
- package/src/utils/rangeDiff.ts +182 -0
- package/src/utils/remediationCommand.test.ts +163 -0
- package/src/utils/remediationCommand.ts +119 -0
- package/src/utils/retry.test.ts +153 -0
- package/src/utils/retry.ts +58 -0
- package/src/utils/reviewCleanup.ts +106 -0
- package/src/utils/run.ts +99 -0
- package/src/utils/runContext.ts +145 -0
- package/src/utils/runContextData.ts +58 -0
- package/src/utils/runErrorRenderer.test.ts +95 -0
- package/src/utils/runErrorRenderer.ts +259 -0
- package/src/utils/runFixture.ts +76 -0
- package/src/utils/runLifecycle.ts +237 -0
- package/src/utils/runStartupLog.ts +60 -0
- package/src/utils/secrets.test.ts +103 -0
- package/src/utils/secrets.ts +177 -0
- package/src/utils/setup.test.ts +509 -0
- package/src/utils/setup.ts +352 -0
- package/src/utils/shell.ts +103 -0
- package/src/utils/skills.test.ts +46 -0
- package/src/utils/skills.ts +67 -0
- package/src/utils/subprocess.test.ts +170 -0
- package/src/utils/subprocess.ts +438 -0
- package/src/utils/terraformMcp.test.ts +63 -0
- package/src/utils/terraformMcp.ts +83 -0
- package/src/utils/time.test.ts +105 -0
- package/src/utils/time.ts +59 -0
- package/src/utils/timer.test.ts +91 -0
- package/src/utils/timer.ts +72 -0
- package/src/utils/todoTracking.test.ts +223 -0
- package/src/utils/todoTracking.ts +167 -0
- package/src/utils/token.test.ts +239 -0
- package/src/utils/token.ts +186 -0
- package/src/utils/version.ts +10 -0
- package/src/utils/versioning.test.ts +34 -0
- package/src/utils/versioning.ts +44 -0
- package/src/utils/vertex.ts +85 -0
- package/src/utils/workflow.ts +25 -0
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { type RunResult } from "#app/mcp/terraform/types";
|
|
2
|
+
export interface CostBreakdown {
|
|
3
|
+
/** total estimated monthly cost, or null when no resources are priced. */
|
|
4
|
+
totalMonthlyCost: number | null;
|
|
5
|
+
currency: string;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Parse `infracost breakdown --format json`. The top-level `totalMonthlyCost`
|
|
9
|
+
* is a decimal string (absent / null when a project has no priced resources);
|
|
10
|
+
* `currency` defaults to USD. A missing/unparseable cost becomes null so the
|
|
11
|
+
* caller reports "unpriced" rather than a misleading $0.00.
|
|
12
|
+
*/
|
|
13
|
+
export declare function parseInfracostBreakdown(stdout: string): CostBreakdown;
|
|
14
|
+
export interface ResourceCost {
|
|
15
|
+
name: string;
|
|
16
|
+
monthlyCost: number;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Parse the per-resource monthly costs from `infracost breakdown --format json`
|
|
20
|
+
* (`projects[].breakdown.resources[]`), so a cost increase can be attributed to
|
|
21
|
+
* the specific resources that drove it instead of just a total. Skips unpriced
|
|
22
|
+
* (null/zero) resources; returns them sorted most-expensive first. Pure.
|
|
23
|
+
*/
|
|
24
|
+
export declare function parseInfracostResources(stdout: string): ResourceCost[];
|
|
25
|
+
export interface CostDelta {
|
|
26
|
+
currency: string;
|
|
27
|
+
baselineMonthly: number | null;
|
|
28
|
+
currentMonthly: number | null;
|
|
29
|
+
/** current − baseline, rounded to cents; null when either side is unknown. */
|
|
30
|
+
deltaMonthly: number | null;
|
|
31
|
+
direction: "increase" | "decrease" | "no-change" | "unknown";
|
|
32
|
+
}
|
|
33
|
+
/** Pure cost-delta computation: current (post-fix) vs the base-branch baseline. */
|
|
34
|
+
export declare function computeCostDelta(baseline: CostBreakdown | null, current: CostBreakdown): CostDelta;
|
|
35
|
+
export interface CostEscalation {
|
|
36
|
+
/** true when the monthly increase meets/exceeds the operator's threshold. */
|
|
37
|
+
escalate: boolean;
|
|
38
|
+
reason?: string;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* §4.16-next — decide whether a cost increase is large enough to escalate the PR
|
|
42
|
+
* to human review (`needs-human`). Compares the monthly delta against the
|
|
43
|
+
* operator's `cost_increase_block_usd` threshold. No threshold set, an unknown
|
|
44
|
+
* delta, or a decrease/no-change ⇒ no escalation. Pure + deterministic so the
|
|
45
|
+
* decision is auditable, not a model judgement.
|
|
46
|
+
*/
|
|
47
|
+
export declare function classifyCostEscalation(deltaMonthly: number | null, thresholdUsd: number | undefined): CostEscalation;
|
|
48
|
+
export declare function runInfracostBreakdown(scanCwd: string, key: string): RunResult;
|
|
49
|
+
/**
|
|
50
|
+
* Cost of the base-branch version of the same Terraform, computed in a detached
|
|
51
|
+
* git worktree so the current (fixed) checkout is never disturbed. Best-effort:
|
|
52
|
+
* any failure (no base ref, worktree add fails, infracost errors) returns null
|
|
53
|
+
* and the caller falls back to reporting current cost only.
|
|
54
|
+
*/
|
|
55
|
+
export declare function infracostBaseline(cwd: string, key: string, tmpdir: string): CostBreakdown | null;
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Version currency (§P1 provider currency / M3 module upgrades).
|
|
3
|
+
*
|
|
4
|
+
* Answers one question the scanners can't: "is a NEWER version available?"
|
|
5
|
+
* tflint checks that versions are *pinned*; nothing we run checks that they're
|
|
6
|
+
* *current*. This module compares the workspace's pinned provider requirements
|
|
7
|
+
* and registry-module pins against the Terraform Registry's published versions.
|
|
8
|
+
*
|
|
9
|
+
* Deliberately NOT a scanner source: results are advisory intelligence the
|
|
10
|
+
* Remediate mode turns into `chore(deps)` upgrade PRs, not `Concern`s — the
|
|
11
|
+
* finding baseline stays scanner-owned (see docs/workplan/04-implementation-plan.md).
|
|
12
|
+
*
|
|
13
|
+
* Degrades green everywhere: a per-source lookup failure is reported on that
|
|
14
|
+
* row (`lookup: "error" | "not_found" | "unsupported_source"`), and only a
|
|
15
|
+
* fully-unreachable registry (every lookup failed) becomes a tool-level skip.
|
|
16
|
+
*/
|
|
17
|
+
export declare const DEFAULT_REGISTRY_BASE_URL = "https://registry.terraform.io";
|
|
18
|
+
/**
|
|
19
|
+
* Convert ONE Terraform version constraint string (comma-separated comparators,
|
|
20
|
+
* AND semantics) into an npm semver range, or null when any comparator is
|
|
21
|
+
* unparseable. `~>` is Terraform's pessimistic operator: the RIGHTMOST given
|
|
22
|
+
* component may float (`~> 5.0` → `>=5.0.0 <6.0.0`, `~> 5.1.2` → `>=5.1.2 <5.2.0`,
|
|
23
|
+
* `~> 5` → `>=5.0.0 <6.0.0`). `!=` comparators have no single-range npm
|
|
24
|
+
* equivalent and are skipped — acceptable here because the result only ranks
|
|
25
|
+
* candidate versions, it never selects what gets installed.
|
|
26
|
+
*/
|
|
27
|
+
export declare function terraformConstraintToRange(constraint: string): string | null;
|
|
28
|
+
export interface CurrencyVerdict {
|
|
29
|
+
/** newest stable version the registry publishes (null: nothing published). */
|
|
30
|
+
latest: string | null;
|
|
31
|
+
/** newest published version the written constraint admits (null: no
|
|
32
|
+
* constraint, unparseable constraint, or nothing satisfies). */
|
|
33
|
+
newestSatisfying: string | null;
|
|
34
|
+
/** true when the registry has a stable version the constraint does NOT admit
|
|
35
|
+
* — i.e. an upgrade PR is available. Always false without a constraint. */
|
|
36
|
+
outdated: boolean;
|
|
37
|
+
/** how many MAJORs the constraint's best version trails the latest by —
|
|
38
|
+
* >0 signals an interface-risk upgrade that must be `needs-human`. */
|
|
39
|
+
majorsBehind: number;
|
|
40
|
+
}
|
|
41
|
+
export declare function classifyCurrency(params: {
|
|
42
|
+
constraint: string | null;
|
|
43
|
+
available: string[];
|
|
44
|
+
}): CurrencyVerdict;
|
|
45
|
+
export type LookupStatus = "ok" | "not_found" | "error" | "unsupported_source";
|
|
46
|
+
/** GET /v1/providers/{namespace}/{type}/versions — provider release list. */
|
|
47
|
+
export declare function fetchProviderVersions(source: string, opts?: {
|
|
48
|
+
baseUrl?: string;
|
|
49
|
+
}): Promise<{
|
|
50
|
+
status: LookupStatus;
|
|
51
|
+
versions: string[];
|
|
52
|
+
}>;
|
|
53
|
+
/** GET /v1/modules/{namespace}/{name}/{provider}/versions — registry-module
|
|
54
|
+
* release list (the response nests per-module records; the first is the
|
|
55
|
+
* requested module, the rest are dependency records we don't want). */
|
|
56
|
+
export declare function fetchModuleVersions(sourceBase: string, opts?: {
|
|
57
|
+
baseUrl?: string;
|
|
58
|
+
}): Promise<{
|
|
59
|
+
status: LookupStatus;
|
|
60
|
+
versions: string[];
|
|
61
|
+
}>;
|
|
62
|
+
export interface ProviderCurrencyRow {
|
|
63
|
+
name: string;
|
|
64
|
+
source: string;
|
|
65
|
+
constraint: string | null;
|
|
66
|
+
latest: string | null;
|
|
67
|
+
newest_satisfying: string | null;
|
|
68
|
+
outdated: boolean;
|
|
69
|
+
majors_behind: number;
|
|
70
|
+
lookup: LookupStatus;
|
|
71
|
+
}
|
|
72
|
+
export interface ModuleCurrencyRow {
|
|
73
|
+
name: string;
|
|
74
|
+
source: string;
|
|
75
|
+
version: string | null;
|
|
76
|
+
latest: string | null;
|
|
77
|
+
newest_satisfying: string | null;
|
|
78
|
+
outdated: boolean;
|
|
79
|
+
/** registry module with no `version` attribute — pin it (to `latest`). */
|
|
80
|
+
unpinned: boolean;
|
|
81
|
+
lookup: LookupStatus;
|
|
82
|
+
declared_in: string;
|
|
83
|
+
}
|
|
84
|
+
export interface CurrencyReport {
|
|
85
|
+
providers: ProviderCurrencyRow[];
|
|
86
|
+
modules: ModuleCurrencyRow[];
|
|
87
|
+
outdated_count: number;
|
|
88
|
+
unpinned_count: number;
|
|
89
|
+
lookups_attempted: number;
|
|
90
|
+
lookups_failed: number;
|
|
91
|
+
}
|
|
92
|
+
export declare function checkVersionCurrency(cwd: string, opts?: {
|
|
93
|
+
baseUrl?: string;
|
|
94
|
+
}): Promise<CurrencyReport>;
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import type { CostDelta } from "#app/mcp/terraform/cost";
|
|
2
|
+
import { type Autonomy, type BlastTier, type Concern, type ConcernGroup, type Severity } from "#app/mcp/terraform/types";
|
|
3
|
+
/** group concerns by file into scoped units, sorted by max severity. */
|
|
4
|
+
export declare function groupConcerns(concerns: Concern[]): ConcernGroup[];
|
|
5
|
+
/**
|
|
6
|
+
* §3.11 — group concerns by RULE across files instead of by file. When a single
|
|
7
|
+
* rule fires in many files ("add `tags` to every resource", "enable encryption
|
|
8
|
+
* on every bucket"), fixing it as ONE coherent change is far better than N
|
|
9
|
+
* near-identical per-file PRs. Each group covers one `rule_id` and lists every
|
|
10
|
+
* `file` it spans; the branch key (`remediate/<id>`) is rule-derived and stable.
|
|
11
|
+
* Opt-in (scan `group_by: "rule"`) — by-file stays the default because it keeps
|
|
12
|
+
* each PR's blast radius smaller; by-rule suits sweeping, low-risk rules.
|
|
13
|
+
*/
|
|
14
|
+
export declare function groupConcernsByRule(concerns: Concern[]): ConcernGroup[];
|
|
15
|
+
/**
|
|
16
|
+
* §3.9 — annotate each group with an autonomy decision. Works for BOTH grouping
|
|
17
|
+
* modes: it resolves a group's concerns by `concern_ids` membership (not by
|
|
18
|
+
* `file`, which is just a label for by-rule groups), so the severity/category
|
|
19
|
+
* policy applies identically. Blast radius isn't known until terraform_plan
|
|
20
|
+
* runs, so it can only escalate a group later (the plan tool + prompt apply the
|
|
21
|
+
* `high`-blast override); at scan time autonomy is severity/category-driven.
|
|
22
|
+
*/
|
|
23
|
+
export declare function annotateGroups(groups: ConcernGroup[], all: Concern[], threshold: Severity): ConcernGroup[];
|
|
24
|
+
export interface BatchPlan {
|
|
25
|
+
/** group ids safe to combine into ONE low-risk PR (`remediate/batch-<hash>`). */
|
|
26
|
+
batchable: string[];
|
|
27
|
+
/** group ids that must each get their own PR (security / higher severity /
|
|
28
|
+
* needs-human / large blast). */
|
|
29
|
+
isolated: string[];
|
|
30
|
+
/** deterministic branch name for the batch (stable for the same member set). */
|
|
31
|
+
batch_branch: string | null;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* §3.10 — split annotated groups into a single low-risk BATCH (merged into one
|
|
35
|
+
* easy-to-review PR) and the riskier groups that each stay ISOLATED in their own
|
|
36
|
+
* PR (so they can be reviewed/reverted independently). The batch branch name
|
|
37
|
+
* hashes the sorted member ids, so re-runs over the same set reuse the branch
|
|
38
|
+
* (idempotent). Returns `batch_branch: null` when fewer than two groups are
|
|
39
|
+
* batchable (one group is just a normal single-group PR, not a batch).
|
|
40
|
+
*/
|
|
41
|
+
export declare function planBatches(groups: ConcernGroup[]): BatchPlan;
|
|
42
|
+
/**
|
|
43
|
+
* Resolve the canonical documentation URL for a concern's rule, for the PR's
|
|
44
|
+
* per-finding explanation. Prefers the scanner's own `remediation_hint` when it
|
|
45
|
+
* is already a URL (checkov guideline, tflint rule link, trivy reference).
|
|
46
|
+
* Otherwise derives the well-known page deterministically: a trivy `AVD-*` rule
|
|
47
|
+
* maps to its Aqua Vulnerability Database page. Returns null when no canonical
|
|
48
|
+
* URL is known (the agent then explains from `evidence` alone).
|
|
49
|
+
*/
|
|
50
|
+
export declare function ruleDocUrl(concern: Pick<Concern, "rule_id" | "remediation_hint">): string | null;
|
|
51
|
+
/** distinct rule→doc-url map for a group, for the PR body's per-finding links. */
|
|
52
|
+
export declare function docUrlsForGroup(g: ConcernGroup, all: Concern[]): Record<string, string>;
|
|
53
|
+
export interface AutonomyDecision {
|
|
54
|
+
autonomy: Autonomy;
|
|
55
|
+
/** human-readable reasons a group was escalated (empty for `auto`). */
|
|
56
|
+
reasons: string[];
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Decide whether a group of concerns can be auto-fixed and opened as a normal
|
|
60
|
+
* PR (`auto`), or must be flagged for human review (`needs-human`). Trivial
|
|
61
|
+
* findings (style/correctness, deprecated args, missing tags, formatting) open
|
|
62
|
+
* as normal; high-severity SECURITY findings escalate by default, as does a
|
|
63
|
+
* `high` blast radius regardless of finding severity (§2.6 overrides upward).
|
|
64
|
+
*
|
|
65
|
+
* `threshold` is the minimum severity at which a *security* concern escalates
|
|
66
|
+
* (default `high`, so critical/high security → human; medium/low → auto). The
|
|
67
|
+
* decision is deterministic and computed from the `Concern` model's existing
|
|
68
|
+
* `severity` + `category` — no model self-assessment.
|
|
69
|
+
*/
|
|
70
|
+
export declare function classifyAutonomy(concerns: Pick<Concern, "severity" | "category">[], threshold?: Severity, blastTier?: BlastTier): AutonomyDecision;
|
|
71
|
+
export interface SuggestionDecision {
|
|
72
|
+
/** true ⇒ post a GitHub one-click `suggestion` instead of opening a full PR. */
|
|
73
|
+
suggest: boolean;
|
|
74
|
+
reason: string;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* §5.18 — decide whether a fix is small/low-risk enough to post as a GitHub
|
|
78
|
+
* one-click **suggested change** (a ` ```suggestion ` block on the existing PR)
|
|
79
|
+
* rather than opening a whole `remediate/*` branch + PR. Much lower friction for
|
|
80
|
+
* trivial fixes. Only when ALL hold: there IS an existing PR context (a comment
|
|
81
|
+
* trigger on a PR); the group is `low`/`info` severity; the fix is a single hunk
|
|
82
|
+
* in a single file; and the blast radius (when known) is `low`. Anything bigger
|
|
83
|
+
* keeps full-PR mode.
|
|
84
|
+
*/
|
|
85
|
+
export declare function shouldSuggestInline(opts: {
|
|
86
|
+
hasPrContext: boolean;
|
|
87
|
+
severity: Severity;
|
|
88
|
+
fileCount: number;
|
|
89
|
+
hunkCount: number;
|
|
90
|
+
blastTier?: BlastTier | undefined;
|
|
91
|
+
}): SuggestionDecision;
|
|
92
|
+
export type Confidence = "high" | "medium" | "low";
|
|
93
|
+
export interface ConfidenceSignals {
|
|
94
|
+
/** §1.1 — every targeted concern id was cleared by the re-scan. */
|
|
95
|
+
verified: boolean;
|
|
96
|
+
/** §1.4 — count of NEW concern ids the fix introduced (0 is good). */
|
|
97
|
+
regressionCount: number;
|
|
98
|
+
/** §1.3 — second plan matched the first. undefined when plan didn't run. */
|
|
99
|
+
idempotent?: boolean | undefined;
|
|
100
|
+
/** §2.6 — blast tier. undefined when plan didn't run. */
|
|
101
|
+
blastTier?: BlastTier | undefined;
|
|
102
|
+
/** §4.16 — cost direction. undefined when infracost didn't run. */
|
|
103
|
+
costDirection?: CostDelta["direction"] | undefined;
|
|
104
|
+
}
|
|
105
|
+
export interface ConfidenceResult {
|
|
106
|
+
level: Confidence;
|
|
107
|
+
reasons: string[];
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Derive a fix's confidence DETERMINISTICALLY from the verification evidence
|
|
111
|
+
* already gathered — never a model self-assessment, which keeps it honest.
|
|
112
|
+
*
|
|
113
|
+
* - A fix that didn't verify (§1.1) or introduced a regression (§1.4) is `low`:
|
|
114
|
+
* the proof failed, full stop.
|
|
115
|
+
* - Otherwise it starts `high` and is capped to `medium` by any weaker signal:
|
|
116
|
+
* a non-deterministic plan (§1.3 `idempotent: false`), a `high` blast radius
|
|
117
|
+
* (§2.6), a cost increase (§4.16), or a signal that was *skipped* (plan /
|
|
118
|
+
* infracost didn't run, so we have less proof — `high` requires the full
|
|
119
|
+
* stack). A skipped signal lowers confidence but does not, by itself, make a
|
|
120
|
+
* verified, regression-free fix `low`.
|
|
121
|
+
*/
|
|
122
|
+
export declare function computeConfidence(signals: ConfidenceSignals): ConfidenceResult;
|
|
123
|
+
export interface RefusalDecision {
|
|
124
|
+
/** true ⇒ this concern needs a human decision; prefer a structured non-fix
|
|
125
|
+
* (an issue) over guessing a fix that could break the stack. */
|
|
126
|
+
refuse: boolean;
|
|
127
|
+
reason?: string;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* §29 — advisory check: would auto-fixing this concern require a judgement only
|
|
131
|
+
* a human can make? If so, the Remediate flow should post a STRUCTURED refusal
|
|
132
|
+
* (an issue describing the concern, why it won't auto-fix, and what a human
|
|
133
|
+
* should do) rather than guess a fix that could break the stack. Deterministic
|
|
134
|
+
* and conservative — it only flags the well-known human-decision classes.
|
|
135
|
+
*/
|
|
136
|
+
export declare function classifyRefusal(concern: Pick<Concern, "rule_id" | "evidence">): RefusalDecision;
|
|
137
|
+
/**
|
|
138
|
+
* §29 — format a structured non-fix for a concern Terramend won't auto-fix. The
|
|
139
|
+
* output is a Markdown issue body: what's wrong, why it isn't auto-fixed, and
|
|
140
|
+
* the concrete next step for a human. Pure (string in → string out).
|
|
141
|
+
*/
|
|
142
|
+
export declare function buildRefusalReport(input: {
|
|
143
|
+
concern: Pick<Concern, "rule_id" | "evidence" | "location">;
|
|
144
|
+
whyNoAutoFix: string;
|
|
145
|
+
humanAction: string;
|
|
146
|
+
}): string;
|
|
147
|
+
export interface PreventiveControl {
|
|
148
|
+
/** the mechanism that stops this class of concern recurring. */
|
|
149
|
+
mechanism: string;
|
|
150
|
+
/** a copy-pasteable config/CI snippet. */
|
|
151
|
+
snippet: string;
|
|
152
|
+
note: string;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* §21 — alongside the patch, suggest the guardrail that stops the concern
|
|
156
|
+
* RECURRING: a CI gate keyed on the producing scanner. Deterministic by source
|
|
157
|
+
* (the scanner is the right enforcement point), parameterised by the rule id.
|
|
158
|
+
* Returns null for sources with no natural preventive gate.
|
|
159
|
+
*/
|
|
160
|
+
export declare function preventiveControlFor(concern: Pick<Concern, "source" | "rule_id">): PreventiveControl | null;
|
|
161
|
+
export interface LocationCluster {
|
|
162
|
+
file: string;
|
|
163
|
+
line: number | null;
|
|
164
|
+
/** the concern ids at this exact location (likely the same underlying defect). */
|
|
165
|
+
concern_ids: string[];
|
|
166
|
+
/** the distinct scanners that flagged this location. */
|
|
167
|
+
sources: string[];
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* §30 — surface concerns that DIFFERENT scanners flagged at the same `file:line`
|
|
171
|
+
* — almost always the same underlying defect (trivy ∩ checkov overlap heavily on
|
|
172
|
+
* e.g. S3 encryption). Reported so the agent writes ONE canonical fix + ONE
|
|
173
|
+
* explanation for the cluster rather than treating each as separate work. This
|
|
174
|
+
* is purely advisory: it NEVER removes a concern from the verification set (a
|
|
175
|
+
* missing id must still provably clear), so it can't drop a real finding. Only
|
|
176
|
+
* clusters spanning more than one scanner are returned.
|
|
177
|
+
*/
|
|
178
|
+
export declare function clusterByLocation(concerns: Concern[]): LocationCluster[];
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { type Concern } from "#app/mcp/terraform/types";
|
|
2
|
+
/**
|
|
3
|
+
* Map a reviewer `findings.json` body into Concern[]. Drops `human_only`
|
|
4
|
+
* findings (out of scope — not auto-remediable). Paths are normalized to
|
|
5
|
+
* repo-relative POSIX (same as the scanners) so ids and grouping stay portable.
|
|
6
|
+
*/
|
|
7
|
+
export declare function parseReviewerFindings(json: string, cwd?: string): Concern[];
|
|
8
|
+
interface SarifLocation {
|
|
9
|
+
physicalLocation?: {
|
|
10
|
+
artifactLocation?: {
|
|
11
|
+
uri?: string;
|
|
12
|
+
};
|
|
13
|
+
region?: {
|
|
14
|
+
startLine?: number;
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
interface SarifResult {
|
|
19
|
+
ruleId?: string;
|
|
20
|
+
level?: string;
|
|
21
|
+
message?: {
|
|
22
|
+
text?: string;
|
|
23
|
+
};
|
|
24
|
+
locations?: SarifLocation[];
|
|
25
|
+
properties?: {
|
|
26
|
+
"security-severity"?: string;
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
interface SarifRule {
|
|
30
|
+
id?: string;
|
|
31
|
+
helpUri?: string;
|
|
32
|
+
shortDescription?: {
|
|
33
|
+
text?: string;
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
interface SarifRun {
|
|
37
|
+
tool?: {
|
|
38
|
+
driver?: {
|
|
39
|
+
name?: string;
|
|
40
|
+
rules?: SarifRule[];
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
results?: SarifResult[];
|
|
44
|
+
}
|
|
45
|
+
interface SarifReport {
|
|
46
|
+
version?: string;
|
|
47
|
+
$schema?: string;
|
|
48
|
+
runs?: SarifRun[];
|
|
49
|
+
}
|
|
50
|
+
/** true when a parsed JSON object looks like a SARIF report (the standard
|
|
51
|
+
* scanner-output format) rather than a terraform-reviewer findings.json. */
|
|
52
|
+
export declare function isSarif(parsed: unknown): boolean;
|
|
53
|
+
/**
|
|
54
|
+
* Parse a SARIF 2.1.0 report (the standard scanner-output format Trivy /
|
|
55
|
+
* Checkov / tflint all emit) into Concern[]. The driver name picks the source so
|
|
56
|
+
* a finding from a scanner Terramend re-runs reproduces the SAME content id
|
|
57
|
+
* (✗→✓ verifiable); other tools collapse to `reviewer`. Rule docs come from the
|
|
58
|
+
* matching `tool.driver.rules[].helpUri`. Non-Terraform files are dropped.
|
|
59
|
+
*/
|
|
60
|
+
export declare function parseSarifFindings(json: string, cwd?: string): Concern[];
|
|
61
|
+
/** dispatch a findings file to the right parser: SARIF (standard scanner
|
|
62
|
+
* output) or a terraform-reviewer findings.json. */
|
|
63
|
+
export declare function parseFindingsFile(json: string, cwd?: string): Concern[];
|
|
64
|
+
/**
|
|
65
|
+
* Emit a set of concerns as a SARIF 2.1.0 report for GitHub code-scanning (the
|
|
66
|
+
* inverse of `parseSarifFindings` — close the loop so a Terramend scan can
|
|
67
|
+
* populate the repo's Security tab via `github/codeql-action/upload-sarif`). One
|
|
68
|
+
* `run` with the `terramend` driver, a deduped `rules` array (each rule's
|
|
69
|
+
* `helpUri` from `ruleDocUrl`), and one `result` per concern carrying its
|
|
70
|
+
* `level`, `security-severity`, message, and `file:line`. Pure + deterministic
|
|
71
|
+
* (rules sorted, stable partialFingerprints from the content id) so re-emitting
|
|
72
|
+
* an unchanged scan yields a byte-identical report.
|
|
73
|
+
*/
|
|
74
|
+
export declare function buildSarifReport(concerns: Concern[]): SarifReport;
|
|
75
|
+
export {};
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import type { BlastTier } from "#app/mcp/terraform/types";
|
|
2
|
+
export interface PlanSummary {
|
|
3
|
+
/** resources to add / change / destroy, from the plan's change_summary. */
|
|
4
|
+
add: number;
|
|
5
|
+
change: number;
|
|
6
|
+
destroy: number;
|
|
7
|
+
/** every resource with a real action (create/update/delete/replace) — the set
|
|
8
|
+
* that powers blast-radius (§2.6) and plan-stability (§1.3). */
|
|
9
|
+
changed: {
|
|
10
|
+
address: string;
|
|
11
|
+
action: string;
|
|
12
|
+
}[];
|
|
13
|
+
/** resources that would be deleted or replaced — the destructive set. */
|
|
14
|
+
destructive: {
|
|
15
|
+
address: string;
|
|
16
|
+
action: string;
|
|
17
|
+
}[];
|
|
18
|
+
hasDestroyOrReplace: boolean;
|
|
19
|
+
/** state-only moves (`moved {}` blocks / refactors): the new address and the
|
|
20
|
+
* address it came from. Moves don't mutate live infrastructure, so they are
|
|
21
|
+
* NOT in `changed` — they power the M2 modularization gate (`isPureMovePlan`). */
|
|
22
|
+
moved: {
|
|
23
|
+
address: string;
|
|
24
|
+
previousAddress: string | null;
|
|
25
|
+
}[];
|
|
26
|
+
}
|
|
27
|
+
export declare function parseTerraformPlanJson(stdout: string): PlanSummary;
|
|
28
|
+
/**
|
|
29
|
+
* §M2 modularization gate — true when the plan is PURELY state moves: at least
|
|
30
|
+
* one `moved` entry and zero create/update/delete/replace. That proves a
|
|
31
|
+
* resources→module refactor preserved every resource address (via `moved {}`
|
|
32
|
+
* blocks) and is a no-op on live infrastructure — the condition under which a
|
|
33
|
+
* modularization PR may proceed without human escalation.
|
|
34
|
+
*/
|
|
35
|
+
export declare function isPureMovePlan(plan: {
|
|
36
|
+
add: number;
|
|
37
|
+
change: number;
|
|
38
|
+
destroy: number;
|
|
39
|
+
changed: {
|
|
40
|
+
address: string;
|
|
41
|
+
}[];
|
|
42
|
+
moved: {
|
|
43
|
+
address: string;
|
|
44
|
+
}[];
|
|
45
|
+
}): boolean;
|
|
46
|
+
/**
|
|
47
|
+
* Resource types that hold data/state — destroying or replacing one of these
|
|
48
|
+
* means data loss, not just recreation. A remediation that would delete or
|
|
49
|
+
* replace one is hard-blocked at push time unless the operator opts in via the
|
|
50
|
+
* `allow_replace` input. Not exhaustive: it covers the common managed
|
|
51
|
+
* datastores across AWS / Azure / GCP; extend as new ones come up.
|
|
52
|
+
*/
|
|
53
|
+
export declare const STATEFUL_RESOURCE_TYPES: ReadonlySet<string>;
|
|
54
|
+
/**
|
|
55
|
+
* Extract the Terraform resource TYPE from a plan address, stripping any
|
|
56
|
+
* `module.<name>.` prefixes and an instance index/key suffix:
|
|
57
|
+
* `module.db.aws_db_instance.main` -> `aws_db_instance`
|
|
58
|
+
* `aws_s3_bucket.data["prod"]` -> `aws_s3_bucket`
|
|
59
|
+
* `module.a.module.b.google_storage_bucket.x[0]` -> `google_storage_bucket`
|
|
60
|
+
* Returns "" when the address has no parseable `type.name` pair.
|
|
61
|
+
*/
|
|
62
|
+
export declare function resourceTypeOf(address: string): string;
|
|
63
|
+
export interface DestroyClassification {
|
|
64
|
+
/** destroy/replace of a data-bearing type — high-risk, blocked by default. */
|
|
65
|
+
stateful: {
|
|
66
|
+
address: string;
|
|
67
|
+
action: string;
|
|
68
|
+
type: string;
|
|
69
|
+
}[];
|
|
70
|
+
/** destroy/replace of a recreatable type — recorded, not blocked. */
|
|
71
|
+
ephemeral: {
|
|
72
|
+
address: string;
|
|
73
|
+
action: string;
|
|
74
|
+
type: string;
|
|
75
|
+
}[];
|
|
76
|
+
}
|
|
77
|
+
/** partition a plan's destructive set into stateful (blocked) vs ephemeral. */
|
|
78
|
+
export declare function classifyDestructive(destructive: {
|
|
79
|
+
address: string;
|
|
80
|
+
action: string;
|
|
81
|
+
}[]): DestroyClassification;
|
|
82
|
+
export interface BlastRadius {
|
|
83
|
+
tier: BlastTier;
|
|
84
|
+
/** count of resources the plan would create/update/delete/replace. */
|
|
85
|
+
resourceCount: number;
|
|
86
|
+
/** distinct module addresses touched (root resources count as `root`). */
|
|
87
|
+
modules: string[];
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Extract the module address from a resource address: the `module.X[.module.Y]`
|
|
91
|
+
* call path, or `root` for a top-level resource. Strips instance index/key from
|
|
92
|
+
* EVERY segment — a `count`/`for_each` MODULE carries its key on the module
|
|
93
|
+
* segment (`module.net[0]`), so all instances of one module collapse to one
|
|
94
|
+
* address (else a single-module fix would look cross-module). Removing keys
|
|
95
|
+
* first also tolerates a `.` inside a `for_each` string key.
|
|
96
|
+
* `aws_s3_bucket.b` -> `root`
|
|
97
|
+
* `module.db.aws_db_instance.main` -> `module.db`
|
|
98
|
+
* `module.net[0].aws_vpc.main` -> `module.net`
|
|
99
|
+
* `module.a.module.b.google_x.y[0]` -> `module.a.module.b`
|
|
100
|
+
*/
|
|
101
|
+
export declare function moduleAddressOf(address: string): string;
|
|
102
|
+
/**
|
|
103
|
+
* Score how much a fix touches, to route large changes through stricter review:
|
|
104
|
+
* 1–2 resources = `low`, 3–10 = `medium`, more than 10 OR spanning more than one
|
|
105
|
+
* module = `high`. A `high` blast radius should force human-in-the-loop
|
|
106
|
+
* regardless of finding severity (feeds §3.9). 0 changes is `low` (nothing to do).
|
|
107
|
+
*/
|
|
108
|
+
export declare function computeBlastRadius(changed: {
|
|
109
|
+
address: string;
|
|
110
|
+
}[]): BlastRadius;
|
|
111
|
+
export interface StabilityResult {
|
|
112
|
+
/** true when a second plan produced the identical change set. */
|
|
113
|
+
stable: boolean;
|
|
114
|
+
reason?: string;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Compare two consecutive plans for stability. Terramend never `apply`s (it only
|
|
118
|
+
* opens PRs), so a true "no perpetual diff after apply" cannot be proven here —
|
|
119
|
+
* but a fix whose plan is non-deterministic (e.g. `timestamp()`, `uuid()`, an
|
|
120
|
+
* unkeyed `random_*`, or a data source that varies run-to-run) yields a DIFFERENT
|
|
121
|
+
* plan on the second run, and that is a real perpetual-diff smell we can catch
|
|
122
|
+
* without applying. Stable ⇒ the two plans matched; unstable ⇒ report it.
|
|
123
|
+
*/
|
|
124
|
+
export declare function comparePlanStability(first: PlanSummary, second: PlanSummary): StabilityResult;
|
|
125
|
+
export interface RootPlan {
|
|
126
|
+
/** display label for the root ("." for the top-level root). */
|
|
127
|
+
dir: string;
|
|
128
|
+
summary: PlanSummary;
|
|
129
|
+
stable: boolean;
|
|
130
|
+
}
|
|
131
|
+
export interface AggregatedPlan {
|
|
132
|
+
add: number;
|
|
133
|
+
change: number;
|
|
134
|
+
destroy: number;
|
|
135
|
+
changed: {
|
|
136
|
+
address: string;
|
|
137
|
+
action: string;
|
|
138
|
+
}[];
|
|
139
|
+
destructive: {
|
|
140
|
+
address: string;
|
|
141
|
+
action: string;
|
|
142
|
+
}[];
|
|
143
|
+
hasDestroyOrReplace: boolean;
|
|
144
|
+
idempotent: boolean;
|
|
145
|
+
moved: {
|
|
146
|
+
address: string;
|
|
147
|
+
previousAddress: string | null;
|
|
148
|
+
}[];
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Aggregate per-root plan results into one view: SUM the add/change/destroy
|
|
152
|
+
* counts, UNION the changed + destructive sets (so blast-radius and the
|
|
153
|
+
* destroy-block see every root's effect), and treat the whole run as
|
|
154
|
+
* non-idempotent if ANY root's plan was unstable. Pure. Single-root input passes
|
|
155
|
+
* straight through (identical to the pre-multi-root behaviour).
|
|
156
|
+
*/
|
|
157
|
+
export declare function aggregatePlans(roots: RootPlan[]): AggregatedPlan;
|