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,104 @@
|
|
|
1
|
+
import { PRODUCT_NAME } from "#app/brand";
|
|
2
|
+
import { chromeStrings } from "#app/i18n/scaffolding";
|
|
3
|
+
import {
|
|
4
|
+
getModelProvider,
|
|
5
|
+
modelAliases,
|
|
6
|
+
providers,
|
|
7
|
+
resolveDisplayAlias,
|
|
8
|
+
} from "#app/models";
|
|
9
|
+
import type { UserFacingLanguage } from "#app/utils/payload";
|
|
10
|
+
|
|
11
|
+
export const INFRAWEAVER_DIVIDER =
|
|
12
|
+
"<!-- INFRAWEAVER_DIVIDER_DO_NOT_REMOVE_PLZ -->";
|
|
13
|
+
|
|
14
|
+
export interface BuildInfraweaverFooterParams {
|
|
15
|
+
/** add "via Infraweaver" link */
|
|
16
|
+
triggeredBy?: boolean;
|
|
17
|
+
/** arbitrary custom parts (e.g., action links) */
|
|
18
|
+
customParts?: string[] | undefined;
|
|
19
|
+
/** model slug from payload (e.g., "anthropic/claude-opus"). shown in footer as "Using `Model Name`" */
|
|
20
|
+
model?: string | undefined;
|
|
21
|
+
/**
|
|
22
|
+
* When the action engaged the BYOK fallback, this is the slug the user
|
|
23
|
+
* had configured (e.g. "anthropic/claude-opus") — the footer renders
|
|
24
|
+
* `Using <free model> (credentials for <configured> not configured)`
|
|
25
|
+
* so the substitution is visible in PR comments + reviews.
|
|
26
|
+
*/
|
|
27
|
+
fallbackFrom?: string | undefined;
|
|
28
|
+
/** user-facing language for the fixed "Using" verb (default English); the
|
|
29
|
+
* model name + product attribution stay verbatim regardless. */
|
|
30
|
+
language?: UserFacingLanguage | undefined;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Provider display name (e.g. "Anthropic") for the slug, or the raw provider segment as a fallback. */
|
|
34
|
+
function providerDisplayName(slug: string): string {
|
|
35
|
+
try {
|
|
36
|
+
const key = getModelProvider(slug);
|
|
37
|
+
const meta = providers[key as keyof typeof providers];
|
|
38
|
+
return meta?.displayName ?? key;
|
|
39
|
+
} catch {
|
|
40
|
+
// raw IDs without a `/` (Bedrock model IDs) — never reach this function
|
|
41
|
+
// in practice because the BYOK fallback skips Bedrock, but defensively
|
|
42
|
+
// return the slug itself rather than throw if it ever does.
|
|
43
|
+
return slug;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function formatModelLabel(params: {
|
|
48
|
+
model: string;
|
|
49
|
+
fallbackFrom?: string | undefined;
|
|
50
|
+
}): string {
|
|
51
|
+
const alias =
|
|
52
|
+
resolveDisplayAlias(params.model) ??
|
|
53
|
+
// reverse-lookup: when the caller passes an effective model (proxy or
|
|
54
|
+
// resolved target like "openrouter/anthropic/claude-opus-4.7") instead of
|
|
55
|
+
// a stored alias slug, find the alias whose resolve target matches so we
|
|
56
|
+
// still render a friendly display name.
|
|
57
|
+
modelAliases.find(
|
|
58
|
+
(a) => a.resolve === params.model || a.openRouterResolve === params.model,
|
|
59
|
+
);
|
|
60
|
+
const displayName = alias?.displayName ?? params.model;
|
|
61
|
+
const base = alias?.isFree
|
|
62
|
+
? `\`${displayName}\` (free)`
|
|
63
|
+
: `\`${displayName}\``;
|
|
64
|
+
if (!params.fallbackFrom) return base;
|
|
65
|
+
return `${base} (credentials for ${providerDisplayName(params.fallbackFrom)} not configured)`;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* build a infraweaver footer with configurable parts
|
|
70
|
+
* order: action links (customParts) > model > attribution
|
|
71
|
+
*/
|
|
72
|
+
export function buildInfraweaverFooter(
|
|
73
|
+
params: BuildInfraweaverFooterParams,
|
|
74
|
+
): string {
|
|
75
|
+
const parts: string[] = [];
|
|
76
|
+
|
|
77
|
+
if (params.customParts) {
|
|
78
|
+
parts.push(...params.customParts);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (params.triggeredBy) {
|
|
82
|
+
parts.push(`via ${PRODUCT_NAME}`);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (params.model) {
|
|
86
|
+
const using = chromeStrings(params.language).using;
|
|
87
|
+
parts.push(
|
|
88
|
+
`${using} ${formatModelLabel({ model: params.model, fallbackFrom: params.fallbackFrom })}`,
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return `\n\n${INFRAWEAVER_DIVIDER}\n<sup>${parts.join(" | ")}</sup>`;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* strip any existing infraweaver footer from a comment body
|
|
97
|
+
*/
|
|
98
|
+
export function stripExistingFooter(body: string): string {
|
|
99
|
+
const dividerIndex = body.indexOf(INFRAWEAVER_DIVIDER);
|
|
100
|
+
if (dividerIndex === -1) {
|
|
101
|
+
return body;
|
|
102
|
+
}
|
|
103
|
+
return body.substring(0, dividerIndex).trimEnd();
|
|
104
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { PRODUCT_NAME } from "#app/brand";
|
|
2
|
+
import type { AgentId } from "#app/external";
|
|
3
|
+
import { getModelEnvVars, getProviderDisplayName } from "#app/models";
|
|
4
|
+
import { hasEnvVar } from "#app/utils/env";
|
|
5
|
+
import { isOpenAiCompatibleRoute } from "#app/utils/openaiCompatible";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Slug we fall back to when a BYOK-required model is configured but the
|
|
9
|
+
* runner has no provider key in env. Picked because it's free, stable, and
|
|
10
|
+
* currently served by OpenCode Zen without a key.
|
|
11
|
+
*
|
|
12
|
+
* The slug is intentionally hard-coded and not a config knob — the
|
|
13
|
+
* fallback is a safety net, not a user-facing preference, and adding a
|
|
14
|
+
* config surface here would just push the same "what to fall back to"
|
|
15
|
+
* decision into another setting that goes stale the same way.
|
|
16
|
+
*/
|
|
17
|
+
export const FREE_FALLBACK_SLUG = "opencode/big-pickle";
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Outcome of the BYOK model gate.
|
|
21
|
+
*
|
|
22
|
+
* - `use-resolved`: run the configured model as-is.
|
|
23
|
+
* - `fallback`: the runner has NO provider key for this model's provider,
|
|
24
|
+
* so swap to the free OpenCode slug — a genuine no-key safety net.
|
|
25
|
+
* - `unavailable`: a provider key IS present but the configured model is
|
|
26
|
+
* not one OpenCode can route with it. This is almost always a wrong or
|
|
27
|
+
* mistyped model id (or a key scoped to other models). We must NOT
|
|
28
|
+
* silently downgrade to the free model — that hides the misconfiguration
|
|
29
|
+
* and produces a free run the user didn't ask for (see PR #2). The caller
|
|
30
|
+
* fails loudly with the authorized-model list instead.
|
|
31
|
+
*/
|
|
32
|
+
export type FallbackDecision =
|
|
33
|
+
| { kind: "use-resolved" }
|
|
34
|
+
| { kind: "fallback"; from: string; to: string }
|
|
35
|
+
| { kind: "unavailable"; model: string };
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Does the runner have a provider credential for `resolvedModel`'s provider?
|
|
39
|
+
*
|
|
40
|
+
* Uses the provider→envVars map in `models.ts` (provider-level, since a
|
|
41
|
+
* resolved specifier like `google/gemini-3.5-flash-lite` won't match a catalog
|
|
42
|
+
* model key and correctly falls through to the provider's env vars). A present
|
|
43
|
+
* key is what distinguishes the two not-authorized situations: key present =
|
|
44
|
+
* wrong/unavailable model id (fail loudly); no key = genuine BYOK gap (free
|
|
45
|
+
* fallback).
|
|
46
|
+
*/
|
|
47
|
+
export function hasProviderKeyForModel(resolvedModel: string): boolean {
|
|
48
|
+
return getModelEnvVars(resolvedModel).some(hasEnvVar);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Decide whether to run the configured model, fall back to the free model, or
|
|
53
|
+
* fail because the model is unavailable to the present key.
|
|
54
|
+
*
|
|
55
|
+
* `authorized` is OpenCode's authoritative "what can I route right now"
|
|
56
|
+
* snapshot, captured after Codex auth.json is in place.
|
|
57
|
+
*
|
|
58
|
+
* Skip cases (always `use-resolved`, without consulting `authorized`):
|
|
59
|
+
* - No resolved model: auto-select handles it downstream.
|
|
60
|
+
* - Resolved model is the free fallback already.
|
|
61
|
+
* - Resolved model is a raw Bedrock / Vertex ID (no `/`): the routing
|
|
62
|
+
* validators (`validateBedrockSetup` / `validateVertexSetup`) cover
|
|
63
|
+
* auth + region/location/model-id; `opencode models` does not.
|
|
64
|
+
* - The selected agent is `claude`: the Claude Code harness brings its own
|
|
65
|
+
* auth and `resolveAgent` only returns it when that auth is present.
|
|
66
|
+
* `opencode models` can't see `CLAUDE_CODE_OAUTH_TOKEN`, so without this
|
|
67
|
+
* an OAuth-subscription run on an Anthropic model would land in
|
|
68
|
+
* `unavailable` (the token counts as a present provider key) and fail a
|
|
69
|
+
* run the claude harness serves fine. `validateAgentApiKey` still covers
|
|
70
|
+
* the claude path with its own Anthropic auth check.
|
|
71
|
+
*/
|
|
72
|
+
export function selectFallbackModelIfNeeded(input: {
|
|
73
|
+
resolvedModel: string | undefined;
|
|
74
|
+
authorized: Set<string>;
|
|
75
|
+
/** whether a provider key for the resolved model's provider is present in env */
|
|
76
|
+
providerKeyPresent: boolean;
|
|
77
|
+
/** which agent harness `resolveAgent` picks for the resolved model */
|
|
78
|
+
agentName: AgentId;
|
|
79
|
+
}): FallbackDecision {
|
|
80
|
+
if (!input.resolvedModel) return { kind: "use-resolved" };
|
|
81
|
+
if (input.resolvedModel === FREE_FALLBACK_SLUG)
|
|
82
|
+
return { kind: "use-resolved" };
|
|
83
|
+
if (!input.resolvedModel.includes("/")) return { kind: "use-resolved" };
|
|
84
|
+
// OpenAI-compatible BYOK ids can contain a `/` (e.g. a HuggingFace `org/model`),
|
|
85
|
+
// so the no-slash skip above misses them. `validateOpenAiCompatibleSetup` covers
|
|
86
|
+
// base-URL/model-id presence; `opencode models` can't see a custom endpoint.
|
|
87
|
+
if (isOpenAiCompatibleRoute(input.resolvedModel))
|
|
88
|
+
return { kind: "use-resolved" };
|
|
89
|
+
if (input.agentName === "claude") return { kind: "use-resolved" };
|
|
90
|
+
if (input.authorized.has(input.resolvedModel))
|
|
91
|
+
return { kind: "use-resolved" };
|
|
92
|
+
|
|
93
|
+
// resolved model is NOT in OpenCode's authorized set. split the two cases:
|
|
94
|
+
if (input.providerKeyPresent) {
|
|
95
|
+
// a key for this provider is configured — the model id is wrong or not
|
|
96
|
+
// available to this key. surface it; do not silently serve a free model.
|
|
97
|
+
return { kind: "unavailable", model: input.resolvedModel };
|
|
98
|
+
}
|
|
99
|
+
// no key at all → genuine BYOK gap → free safety net so the run still works.
|
|
100
|
+
return {
|
|
101
|
+
kind: "fallback",
|
|
102
|
+
from: input.resolvedModel,
|
|
103
|
+
to: FREE_FALLBACK_SLUG,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Loud, actionable error for the `unavailable` decision: a provider key is
|
|
109
|
+
* present but the configured model isn't one the key can serve. Lists the
|
|
110
|
+
* models OpenCode CAN route (same-provider first) so the user can copy a valid
|
|
111
|
+
* slug instead of getting a silent free downgrade.
|
|
112
|
+
*/
|
|
113
|
+
export function buildUnavailableModelError(input: {
|
|
114
|
+
model: string;
|
|
115
|
+
authorized: Set<string>;
|
|
116
|
+
}): string {
|
|
117
|
+
const provider = input.model.slice(0, input.model.indexOf("/"));
|
|
118
|
+
const providerLabel = getProviderDisplayName(input.model) ?? provider;
|
|
119
|
+
const all = [...input.authorized].sort();
|
|
120
|
+
const sameProvider = all.filter((m) => m.startsWith(`${provider}/`));
|
|
121
|
+
const shown = sameProvider.length > 0 ? sameProvider : all;
|
|
122
|
+
const list =
|
|
123
|
+
shown.length > 0
|
|
124
|
+
? shown.map((m) => ` - ${m}`).join("\n")
|
|
125
|
+
: " (none — your key does not authorize any model OpenCode can route)";
|
|
126
|
+
|
|
127
|
+
return [
|
|
128
|
+
`model "${input.model}" is not available to your ${providerLabel} key.`,
|
|
129
|
+
``,
|
|
130
|
+
`A provider credential is present, so ${PRODUCT_NAME} did not fall back to the free model —`,
|
|
131
|
+
`it surfaces this instead so you can pick a valid id. Models your key can serve:`,
|
|
132
|
+
``,
|
|
133
|
+
list,
|
|
134
|
+
``,
|
|
135
|
+
`Set the model (the \`model\` input, or \`INFRAWEAVER_MODEL\`) to one of the slugs above.`,
|
|
136
|
+
].join("\n");
|
|
137
|
+
}
|
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
import { writeFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { log } from "#app/utils/cli";
|
|
4
|
+
import { $ } from "#app/utils/shell";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Deterministic change-impact leads for Terraform review.
|
|
8
|
+
*
|
|
9
|
+
* On `checkout_pr` we hand the reviewer a supplemental, EXPLICITLY INCOMPLETE
|
|
10
|
+
* list of where the PR's changed HCL declarations are referenced elsewhere in
|
|
11
|
+
* the repo — the blast radius the raw diff alone doesn't show. It is a lead
|
|
12
|
+
* list, never a coverage source: an empty artifact is not evidence of no wider
|
|
13
|
+
* impact, and the authoritative diff read still governs the review.
|
|
14
|
+
*
|
|
15
|
+
* IaC-specific (vs. pullfrog's generic symbol grep): we extract only unambiguous
|
|
16
|
+
* HCL declarations from the diff and grep their canonical REFERENCE form, so the
|
|
17
|
+
* hits are real dependency edges rather than incidental identifier collisions:
|
|
18
|
+
* variable "x" → `var.x`
|
|
19
|
+
* output "x" → `module.<caller>.x` (regex; same-repo callers only)
|
|
20
|
+
* resource "t" "n" → `t.n`
|
|
21
|
+
* data "t" "n" → `data.t.n`
|
|
22
|
+
* module "x" → `module.x`
|
|
23
|
+
*
|
|
24
|
+
* All greps are word-bounded (`git grep -w`) so `var.env` doesn't match
|
|
25
|
+
* `var.environment` and `aws_s3_bucket.this` doesn't match `..._this_backup`.
|
|
26
|
+
*
|
|
27
|
+
* The whole thing is best-effort: any failure degrades to "no artifact", never
|
|
28
|
+
* a failed checkout.
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
export type ChangeKind = "added" | "removed";
|
|
32
|
+
|
|
33
|
+
export interface ChangeLocation {
|
|
34
|
+
path: string;
|
|
35
|
+
line: number;
|
|
36
|
+
kind: ChangeKind;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface ChangeImpactAtom {
|
|
40
|
+
/** the declaration kind, for the artifact heading. */
|
|
41
|
+
declaration: "variable" | "output" | "resource" | "data" | "module";
|
|
42
|
+
/** human label, e.g. `var.instance_type` or `aws_s3_bucket.this`. */
|
|
43
|
+
label: string;
|
|
44
|
+
/** the pattern grepped (word-bounded) across `*.tf` / `*.tfvars`; also the dedup key. */
|
|
45
|
+
ref: string;
|
|
46
|
+
/** when true, `ref` is a POSIX regex (`git grep -E`); otherwise a fixed string (`-F`). */
|
|
47
|
+
regex?: boolean;
|
|
48
|
+
/** where in the diff this declaration was touched (first added or removed). */
|
|
49
|
+
where: ChangeLocation;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export interface GrepHit {
|
|
53
|
+
path: string;
|
|
54
|
+
line: number;
|
|
55
|
+
text: string;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** a changed file, as returned by the GitHub pulls.listFiles API. */
|
|
59
|
+
export interface ChangedFile {
|
|
60
|
+
filename: string;
|
|
61
|
+
patch?: string | undefined;
|
|
62
|
+
previous_filename?: string | undefined;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// bounds — a supplemental artifact must stay small enough to skim.
|
|
66
|
+
const MAX_ATOMS = 20;
|
|
67
|
+
const MAX_REFS_PER_ATOM = 6;
|
|
68
|
+
const MAX_EXCERPT = 160;
|
|
69
|
+
const MAX_BYTES = 16_000;
|
|
70
|
+
|
|
71
|
+
const TF_DECL_PATTERNS: Array<{
|
|
72
|
+
declaration: ChangeImpactAtom["declaration"];
|
|
73
|
+
re: RegExp;
|
|
74
|
+
build: (m: RegExpMatchArray) => { label: string; ref: string; regex?: boolean };
|
|
75
|
+
}> = [
|
|
76
|
+
{
|
|
77
|
+
declaration: "resource",
|
|
78
|
+
re: /^resource\s+"([A-Za-z0-9_]+)"\s+"([A-Za-z0-9_-]+)"/,
|
|
79
|
+
build: (m) => ({ label: `${m[1]}.${m[2]}`, ref: `${m[1]}.${m[2]}` }),
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
declaration: "data",
|
|
83
|
+
re: /^data\s+"([A-Za-z0-9_]+)"\s+"([A-Za-z0-9_-]+)"/,
|
|
84
|
+
build: (m) => ({ label: `data.${m[1]}.${m[2]}`, ref: `data.${m[1]}.${m[2]}` }),
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
declaration: "variable",
|
|
88
|
+
re: /^variable\s+"([A-Za-z0-9_-]+)"/,
|
|
89
|
+
build: (m) => ({ label: `var.${m[1]}`, ref: `var.${m[1]}` }),
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
declaration: "module",
|
|
93
|
+
re: /^module\s+"([A-Za-z0-9_-]+)"/,
|
|
94
|
+
build: (m) => ({ label: `module.${m[1]}`, ref: `module.${m[1]}` }),
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
declaration: "output",
|
|
98
|
+
re: /^output\s+"([A-Za-z0-9_-]+)"/,
|
|
99
|
+
// an output is consumed as `module.<caller>.<name>`; grep that access form as
|
|
100
|
+
// a regex rather than the bare `.<name>`, which would match every attribute
|
|
101
|
+
// access named `<name>` in the tree (for common names like `id`/`arn` that is
|
|
102
|
+
// pure noise). Precise within a monorepo whose callers live in the same
|
|
103
|
+
// checkout; cross-repo consumers are invisible to a single-repo grep either way.
|
|
104
|
+
build: (m) => ({
|
|
105
|
+
label: `output.${m[1]}`,
|
|
106
|
+
ref: `module\\.[A-Za-z0-9_-]+\\.${m[1]}`,
|
|
107
|
+
regex: true,
|
|
108
|
+
}),
|
|
109
|
+
},
|
|
110
|
+
];
|
|
111
|
+
|
|
112
|
+
/** parse one declaration out of a source line, or null. */
|
|
113
|
+
function declarationOf(
|
|
114
|
+
line: string,
|
|
115
|
+
): { declaration: ChangeImpactAtom["declaration"]; label: string; ref: string; regex?: boolean } | null {
|
|
116
|
+
const trimmed = line.trimStart();
|
|
117
|
+
for (const p of TF_DECL_PATTERNS) {
|
|
118
|
+
const m = trimmed.match(p.re);
|
|
119
|
+
if (m) return { declaration: p.declaration, ...p.build(m) };
|
|
120
|
+
}
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const locationKey = (path: string, line: number): string => `${path} ${line}`;
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Walk each changed file's unified-diff patch, pulling out the HCL declarations
|
|
128
|
+
* that were added or removed and the set of added-line keys (so grep hits that
|
|
129
|
+
* land on the PR's own new lines can be excluded — impact means EXISTING code
|
|
130
|
+
* that references the changed thing).
|
|
131
|
+
*/
|
|
132
|
+
export function extractTerraformAtoms(files: ChangedFile[]): {
|
|
133
|
+
atoms: ChangeImpactAtom[];
|
|
134
|
+
addedLines: Set<string>;
|
|
135
|
+
} {
|
|
136
|
+
const byRef = new Map<string, ChangeImpactAtom>();
|
|
137
|
+
const addedLines = new Set<string>();
|
|
138
|
+
|
|
139
|
+
for (const file of files) {
|
|
140
|
+
if (!file.patch) continue;
|
|
141
|
+
// only Terraform declarations matter here.
|
|
142
|
+
if (!/\.(tf|tfvars)$/.test(file.filename)) continue;
|
|
143
|
+
let oldLine = 0;
|
|
144
|
+
let newLine = 0;
|
|
145
|
+
for (const patchLine of file.patch.split("\n")) {
|
|
146
|
+
const hunk = patchLine.match(/^@@ -(\d+)(?:,\d+)? \+(\d+)(?:,\d+)? @@/);
|
|
147
|
+
if (hunk) {
|
|
148
|
+
oldLine = Number.parseInt(hunk[1] ?? "0", 10);
|
|
149
|
+
newLine = Number.parseInt(hunk[2] ?? "0", 10);
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
if (patchLine.startsWith("+")) {
|
|
153
|
+
const decl = declarationOf(patchLine.slice(1));
|
|
154
|
+
const where: ChangeLocation = { path: file.filename, line: newLine, kind: "added" };
|
|
155
|
+
addedLines.add(locationKey(file.filename, newLine));
|
|
156
|
+
if (decl && !byRef.has(decl.ref)) byRef.set(decl.ref, { ...decl, where });
|
|
157
|
+
newLine++;
|
|
158
|
+
} else if (patchLine.startsWith("-")) {
|
|
159
|
+
const decl = declarationOf(patchLine.slice(1));
|
|
160
|
+
const oldPath = file.previous_filename ?? file.filename;
|
|
161
|
+
if (decl && !byRef.has(decl.ref)) {
|
|
162
|
+
byRef.set(decl.ref, { ...decl, where: { path: oldPath, line: oldLine, kind: "removed" } });
|
|
163
|
+
}
|
|
164
|
+
oldLine++;
|
|
165
|
+
} else if (!patchLine.startsWith("\\")) {
|
|
166
|
+
oldLine++;
|
|
167
|
+
newLine++;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return { atoms: [...byRef.values()].slice(0, MAX_ATOMS), addedLines };
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function excerpt(text: string): string {
|
|
176
|
+
const t = text.trim();
|
|
177
|
+
return t.length > MAX_EXCERPT ? `${t.slice(0, MAX_EXCERPT - 1)}…` : t;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Render the artifact from already-collected grep hits. Pure: the caller
|
|
182
|
+
* supplies the reference hits per atom (via a real `git grep` at runtime, or a
|
|
183
|
+
* stub in tests). Bounded by MAX_REFS_PER_ATOM and MAX_BYTES.
|
|
184
|
+
*/
|
|
185
|
+
export function renderChangeImpactArtifact(params: {
|
|
186
|
+
atoms: ChangeImpactAtom[];
|
|
187
|
+
hitsByRef: Map<string, GrepHit[]>;
|
|
188
|
+
addedLines: Set<string>;
|
|
189
|
+
baseSha: string;
|
|
190
|
+
headSha: string;
|
|
191
|
+
fileCount: number;
|
|
192
|
+
lookupError?: string | undefined;
|
|
193
|
+
}): { content: string; atomCount: number; referenceCount: number } {
|
|
194
|
+
const header = [
|
|
195
|
+
"# Change impact leads",
|
|
196
|
+
"",
|
|
197
|
+
"> Supplemental and explicitly incomplete. Read the authoritative `diffPath` TOC and raw lines first. Absence here is not evidence of no impact.",
|
|
198
|
+
"",
|
|
199
|
+
`Scope: base ${params.baseSha.slice(0, 7)}, head ${params.headSha.slice(0, 7)}; ${params.fileCount} changed file(s).`,
|
|
200
|
+
"Each entry is a Terraform declaration this PR changed, followed by pre-existing references to it elsewhere in the tree (the PR's own new lines are excluded).",
|
|
201
|
+
"",
|
|
202
|
+
];
|
|
203
|
+
if (params.lookupError) header.push(`Reference lookup unavailable: ${params.lookupError}.`, "");
|
|
204
|
+
|
|
205
|
+
let content = `${header.join("\n")}`;
|
|
206
|
+
let atomCount = 0;
|
|
207
|
+
let referenceCount = 0;
|
|
208
|
+
|
|
209
|
+
for (const atom of params.atoms) {
|
|
210
|
+
const hits = (params.hitsByRef.get(atom.ref) ?? []).filter(
|
|
211
|
+
(h) => !params.addedLines.has(locationKey(h.path, h.line)),
|
|
212
|
+
);
|
|
213
|
+
if (hits.length === 0) continue;
|
|
214
|
+
const shown = hits.slice(0, MAX_REFS_PER_ATOM);
|
|
215
|
+
const note =
|
|
216
|
+
hits.length > shown.length
|
|
217
|
+
? `${hits.length} reference(s); first ${shown.length} shown.`
|
|
218
|
+
: `${hits.length} reference(s).`;
|
|
219
|
+
const block = [
|
|
220
|
+
"",
|
|
221
|
+
`## \`${atom.label}\` (${atom.declaration})`,
|
|
222
|
+
"",
|
|
223
|
+
`Changed at: ${atom.where.kind} \`${atom.where.path}\`:${atom.where.line}`,
|
|
224
|
+
`References: ${note}`,
|
|
225
|
+
"",
|
|
226
|
+
...shown.map((h) => `- \`${h.path}\`:${h.line} — ${excerpt(h.text)}`),
|
|
227
|
+
"",
|
|
228
|
+
].join("\n");
|
|
229
|
+
if (Buffer.byteLength(content + block) > MAX_BYTES) break;
|
|
230
|
+
content += block;
|
|
231
|
+
atomCount++;
|
|
232
|
+
referenceCount += hits.length;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
if (atomCount === 0) {
|
|
236
|
+
const empty = "\nNo pre-existing references found for the changed declarations. This is not evidence of no wider impact.\n";
|
|
237
|
+
if (Buffer.byteLength(content + empty) <= MAX_BYTES) content += empty;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
return { content: content.endsWith("\n") ? content : `${content}\n`, atomCount, referenceCount };
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// bound the per-atom grep parse so a heavily-referenced module can't balloon
|
|
244
|
+
// memory; the renderer only shows MAX_REFS_PER_ATOM anyway but reports the count.
|
|
245
|
+
const MAX_HITS_PER_ATOM = 200;
|
|
246
|
+
|
|
247
|
+
/** parse `git grep -n` porcelain (`path:line:text`) into hits. */
|
|
248
|
+
export function parseGrepHits(output: string): GrepHit[] {
|
|
249
|
+
const hits: GrepHit[] = [];
|
|
250
|
+
for (const raw of output.split("\n")) {
|
|
251
|
+
if (!raw) continue;
|
|
252
|
+
const m = raw.match(/^(.*?):(\d+):(.*)$/);
|
|
253
|
+
if (!m) continue;
|
|
254
|
+
const [, path, lineStr, text] = m;
|
|
255
|
+
if (path === undefined || lineStr === undefined) continue;
|
|
256
|
+
hits.push({ path, line: Number.parseInt(lineStr, 10), text: text ?? "" });
|
|
257
|
+
if (hits.length >= MAX_HITS_PER_ATOM) break;
|
|
258
|
+
}
|
|
259
|
+
return hits;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Runtime entry point: extract the changed declarations, grep the checked-out
|
|
264
|
+
* head for pre-existing references to each, and write the artifact to tmpdir.
|
|
265
|
+
* Returns null when there's nothing worth writing (no TF declarations changed).
|
|
266
|
+
* Callers treat a thrown error as "no artifact" — this must never fail checkout.
|
|
267
|
+
*/
|
|
268
|
+
export function createChangeImpactArtifact(params: {
|
|
269
|
+
files: ChangedFile[];
|
|
270
|
+
tmpdir: string;
|
|
271
|
+
pullNumber: number;
|
|
272
|
+
baseSha: string;
|
|
273
|
+
headSha: string;
|
|
274
|
+
}): { path: string; atomCount: number; referenceCount: number; bytes: number } | null {
|
|
275
|
+
const { atoms, addedLines } = extractTerraformAtoms(params.files);
|
|
276
|
+
if (atoms.length === 0) return null;
|
|
277
|
+
|
|
278
|
+
const hitsByRef = new Map<string, GrepHit[]>();
|
|
279
|
+
let lookupError: string | undefined;
|
|
280
|
+
for (const atom of atoms) {
|
|
281
|
+
let status = 0;
|
|
282
|
+
// word-bounded, tracked-files-only, skip-binary grep of the checked-out head.
|
|
283
|
+
// `-w` keeps `var.env` from matching `var.environment`; `-E`/`-F` per atom
|
|
284
|
+
// (outputs grep a `module.<caller>.<name>` regex, the rest fixed strings).
|
|
285
|
+
// exit 1 = no matches (onError swallows it → ""); exit >1 = a real failure.
|
|
286
|
+
const out = $(
|
|
287
|
+
"git",
|
|
288
|
+
[
|
|
289
|
+
"grep",
|
|
290
|
+
"-n",
|
|
291
|
+
atom.regex ? "-E" : "-F",
|
|
292
|
+
"-w",
|
|
293
|
+
"-I",
|
|
294
|
+
"--no-color",
|
|
295
|
+
"-e",
|
|
296
|
+
atom.ref,
|
|
297
|
+
"--",
|
|
298
|
+
"*.tf",
|
|
299
|
+
"*.tfvars",
|
|
300
|
+
],
|
|
301
|
+
{ log: false, onError: (r) => (status = r.status) },
|
|
302
|
+
);
|
|
303
|
+
if (status > 1) {
|
|
304
|
+
lookupError = `git grep exit ${status}`;
|
|
305
|
+
continue;
|
|
306
|
+
}
|
|
307
|
+
hitsByRef.set(atom.ref, parseGrepHits(out));
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
const artifact = renderChangeImpactArtifact({
|
|
311
|
+
atoms,
|
|
312
|
+
hitsByRef,
|
|
313
|
+
addedLines,
|
|
314
|
+
baseSha: params.baseSha,
|
|
315
|
+
headSha: params.headSha,
|
|
316
|
+
fileCount: params.files.length,
|
|
317
|
+
lookupError,
|
|
318
|
+
});
|
|
319
|
+
const path = join(params.tmpdir, `pr-${params.pullNumber}-${params.headSha.slice(0, 7)}-impact.md`);
|
|
320
|
+
writeFileSync(path, artifact.content);
|
|
321
|
+
log.debug(
|
|
322
|
+
`» change impact: ${artifact.atomCount} atom(s), ${artifact.referenceCount} reference(s) → ${path}`,
|
|
323
|
+
);
|
|
324
|
+
return {
|
|
325
|
+
path,
|
|
326
|
+
atomCount: artifact.atomCount,
|
|
327
|
+
referenceCount: artifact.referenceCount,
|
|
328
|
+
bytes: Buffer.byteLength(artifact.content),
|
|
329
|
+
};
|
|
330
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { resolveModelSlug } from "#app/models";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* identity block Anthropic's OAuth gate validates on `/v1/messages` calls
|
|
5
|
+
* authenticated with a Claude Code OAuth token. haiku is currently exempt
|
|
6
|
+
* but sending it costs nothing and survives the exemption being removed.
|
|
7
|
+
*/
|
|
8
|
+
const CLAUDE_CODE_IDENTITY = "You are Claude Code, Anthropic's official CLI for Claude.";
|
|
9
|
+
|
|
10
|
+
// fallback probe model for runs with no explicit model (claude-code picks its
|
|
11
|
+
// own default there, so per-model accuracy is moot — only whether the
|
|
12
|
+
// subscription answers at all). registry-resolved so a catalog bump keeps
|
|
13
|
+
// the id fresh.
|
|
14
|
+
const fallbackResolve = resolveModelSlug("anthropic/claude-haiku");
|
|
15
|
+
if (!fallbackResolve) {
|
|
16
|
+
throw new Error("claudeSubscription preflight: anthropic/claude-haiku missing from registry");
|
|
17
|
+
}
|
|
18
|
+
const FALLBACK_PROBE_MODEL = fallbackResolve.slice(fallbackResolve.indexOf("/") + 1);
|
|
19
|
+
|
|
20
|
+
export type SubscriptionPreflight = { usable: true } | { usable: false; reason: string };
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* preflight a Claude subscription OAuth token (`CLAUDE_CODE_OAUTH_TOKEN`)
|
|
24
|
+
* with a 1-token Messages call, so the agent can fall back to
|
|
25
|
+
* `ANTHROPIC_API_KEY` when the subscription is exhausted or revoked instead
|
|
26
|
+
* of failing the whole run at its first model call. rides the same de-facto
|
|
27
|
+
* OAuth surface Claude Code itself uses: Bearer auth + the
|
|
28
|
+
* `claude-code-20250219,oauth-2025-04-20` betas + the identity system prompt.
|
|
29
|
+
*
|
|
30
|
+
* probes the run's own model when known — subscription limits can be
|
|
31
|
+
* per-model ("You've hit your Opus limit"), so a cheaper stand-in could pass
|
|
32
|
+
* preflight and still leave the run dead on arrival.
|
|
33
|
+
*
|
|
34
|
+
* fail-open by design: only 401 (revoked/expired token) and 429
|
|
35
|
+
* (session/weekly/per-model limit) mark the token unusable. network errors,
|
|
36
|
+
* 5xx, and request-shape drift (400) all keep today's subscription-first
|
|
37
|
+
* behavior, so the preflight can never fail a run that would have worked —
|
|
38
|
+
* the worst wrong answer is a run that bills the API key instead of the
|
|
39
|
+
* subscription.
|
|
40
|
+
*/
|
|
41
|
+
export async function preflightClaudeSubscription(params: {
|
|
42
|
+
token: string;
|
|
43
|
+
/** bare Anthropic model id the run will use (e.g. "claude-fable-5") */
|
|
44
|
+
model: string | undefined;
|
|
45
|
+
}): Promise<SubscriptionPreflight> {
|
|
46
|
+
let res: Response;
|
|
47
|
+
try {
|
|
48
|
+
res = await fetch("https://api.anthropic.com/v1/messages", {
|
|
49
|
+
method: "POST",
|
|
50
|
+
headers: {
|
|
51
|
+
authorization: `Bearer ${params.token}`,
|
|
52
|
+
"anthropic-beta": "claude-code-20250219,oauth-2025-04-20",
|
|
53
|
+
"anthropic-version": "2023-06-01",
|
|
54
|
+
"content-type": "application/json",
|
|
55
|
+
"x-app": "cli",
|
|
56
|
+
},
|
|
57
|
+
body: JSON.stringify({
|
|
58
|
+
model: params.model ?? FALLBACK_PROBE_MODEL,
|
|
59
|
+
max_tokens: 1,
|
|
60
|
+
system: CLAUDE_CODE_IDENTITY,
|
|
61
|
+
messages: [{ role: "user", content: "ok" }],
|
|
62
|
+
}),
|
|
63
|
+
signal: AbortSignal.timeout(10_000),
|
|
64
|
+
});
|
|
65
|
+
} catch {
|
|
66
|
+
// network failure / timeout says nothing about the credential
|
|
67
|
+
return { usable: true };
|
|
68
|
+
}
|
|
69
|
+
if (res.status !== 401 && res.status !== 429) return { usable: true };
|
|
70
|
+
const body = await res.text().catch(() => "");
|
|
71
|
+
return { usable: false, reason: `${res.status}: ${extractApiErrorMessage(body)}` };
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** pull `error.message` out of an Anthropic error payload, else a raw excerpt */
|
|
75
|
+
function extractApiErrorMessage(body: string): string {
|
|
76
|
+
try {
|
|
77
|
+
const parsed: unknown = JSON.parse(body);
|
|
78
|
+
if (
|
|
79
|
+
typeof parsed === "object" &&
|
|
80
|
+
parsed !== null &&
|
|
81
|
+
"error" in parsed &&
|
|
82
|
+
typeof parsed.error === "object" &&
|
|
83
|
+
parsed.error !== null &&
|
|
84
|
+
"message" in parsed.error &&
|
|
85
|
+
typeof parsed.error.message === "string"
|
|
86
|
+
) {
|
|
87
|
+
return parsed.error.message;
|
|
88
|
+
}
|
|
89
|
+
} catch {
|
|
90
|
+
// not json — fall through to the raw excerpt
|
|
91
|
+
}
|
|
92
|
+
return body.slice(0, 200);
|
|
93
|
+
}
|