projscan 4.5.0 → 4.7.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/README.md +30 -16
- package/dist/analyzers/pythonDependencyRiskCheck.js +1 -1
- package/dist/analyzers/pythonDependencyRiskCheck.js.map +1 -1
- package/dist/cli/_shared.d.ts +4 -5
- package/dist/cli/_shared.js +34 -213
- package/dist/cli/_shared.js.map +1 -1
- package/dist/cli/architectureLayers.d.ts +2 -0
- package/dist/cli/architectureLayers.js +112 -0
- package/dist/cli/architectureLayers.js.map +1 -0
- package/dist/cli/bannerDisplay.d.ts +9 -0
- package/dist/cli/bannerDisplay.js +18 -0
- package/dist/cli/bannerDisplay.js.map +1 -0
- package/dist/cli/changedIssueFilter.d.ts +10 -0
- package/dist/cli/changedIssueFilter.js +24 -0
- package/dist/cli/changedIssueFilter.js.map +1 -0
- package/dist/cli/changedOnly.d.ts +16 -0
- package/dist/cli/changedOnly.js +28 -0
- package/dist/cli/changedOnly.js.map +1 -0
- package/dist/cli/commandPath.d.ts +2 -0
- package/dist/cli/commandPath.js +12 -0
- package/dist/cli/commandPath.js.map +1 -0
- package/dist/cli/commands/agentBrief.js +11 -0
- package/dist/cli/commands/agentBrief.js.map +1 -1
- package/dist/cli/commands/analyze.js +6 -5
- package/dist/cli/commands/analyze.js.map +1 -1
- package/dist/cli/commands/ci.js +5 -4
- package/dist/cli/commands/ci.js.map +1 -1
- package/dist/cli/commands/coordinate.js +27 -0
- package/dist/cli/commands/coordinate.js.map +1 -1
- package/dist/cli/commands/doctor.js +6 -5
- package/dist/cli/commands/doctor.js.map +1 -1
- package/dist/cli/formatOptions.d.ts +4 -0
- package/dist/cli/formatOptions.js +30 -0
- package/dist/cli/formatOptions.js.map +1 -0
- package/dist/cli/pluginReporter.d.ts +14 -0
- package/dist/cli/pluginReporter.js +46 -0
- package/dist/cli/pluginReporter.js.map +1 -0
- package/dist/cli/projectConfig.d.ts +12 -0
- package/dist/cli/projectConfig.js +41 -0
- package/dist/cli/projectConfig.js.map +1 -0
- package/dist/cli/treeSlice.d.ts +3 -0
- package/dist/cli/treeSlice.js +12 -0
- package/dist/cli/treeSlice.js.map +1 -0
- package/dist/core/agentBrief.js +6 -1
- package/dist/core/agentBrief.js.map +1 -1
- package/dist/core/ast.d.ts +4 -81
- package/dist/core/ast.js +11 -637
- package/dist/core/ast.js.map +1 -1
- package/dist/core/astBodySignals.d.ts +17 -0
- package/dist/core/astBodySignals.js +106 -0
- package/dist/core/astBodySignals.js.map +1 -0
- package/dist/core/astFunctionCollector.d.ts +11 -0
- package/dist/core/astFunctionCollector.js +140 -0
- package/dist/core/astFunctionCollector.js.map +1 -0
- package/dist/core/astFunctionNames.d.ts +2 -0
- package/dist/core/astFunctionNames.js +53 -0
- package/dist/core/astFunctionNames.js.map +1 -0
- package/dist/core/astFunctionNodes.d.ts +2 -0
- package/dist/core/astFunctionNodes.js +12 -0
- package/dist/core/astFunctionNodes.js.map +1 -0
- package/dist/core/astMembers.d.ts +14 -0
- package/dist/core/astMembers.js +145 -0
- package/dist/core/astMembers.js.map +1 -0
- package/dist/core/astModuleSignals.d.ts +3 -0
- package/dist/core/astModuleSignals.js +140 -0
- package/dist/core/astModuleSignals.js.map +1 -0
- package/dist/core/astParser.d.ts +11 -0
- package/dist/core/astParser.js +38 -0
- package/dist/core/astParser.js.map +1 -0
- package/dist/core/astProgramSignals.d.ts +11 -0
- package/dist/core/astProgramSignals.js +97 -0
- package/dist/core/astProgramSignals.js.map +1 -0
- package/dist/core/astTypes.d.ts +78 -0
- package/dist/core/astTypes.js +2 -0
- package/dist/core/astTypes.js.map +1 -0
- package/dist/core/bugHunt.js +2 -142
- package/dist/core/bugHunt.js.map +1 -1
- package/dist/core/bugHuntHotspotFindings.d.ts +2 -0
- package/dist/core/bugHuntHotspotFindings.js +68 -0
- package/dist/core/bugHuntHotspotFindings.js.map +1 -0
- package/dist/core/bugHuntPreflightFindings.d.ts +3 -0
- package/dist/core/bugHuntPreflightFindings.js +115 -0
- package/dist/core/bugHuntPreflightFindings.js.map +1 -0
- package/dist/core/codeGraph.d.ts +3 -35
- package/dist/core/codeGraph.js +7 -261
- package/dist/core/codeGraph.js.map +1 -1
- package/dist/core/codeGraphFanMetrics.d.ts +17 -0
- package/dist/core/codeGraphFanMetrics.js +89 -0
- package/dist/core/codeGraphFanMetrics.js.map +1 -0
- package/dist/core/codeGraphFileSelection.d.ts +7 -0
- package/dist/core/codeGraphFileSelection.js +19 -0
- package/dist/core/codeGraphFileSelection.js.map +1 -0
- package/dist/core/codeGraphIndexes.d.ts +23 -0
- package/dist/core/codeGraphIndexes.js +57 -0
- package/dist/core/codeGraphIndexes.js.map +1 -0
- package/dist/core/codeGraphParsing.d.ts +20 -0
- package/dist/core/codeGraphParsing.js +104 -0
- package/dist/core/codeGraphParsing.js.map +1 -0
- package/dist/core/codeGraphQueries.d.ts +9 -0
- package/dist/core/codeGraphQueries.js +25 -0
- package/dist/core/codeGraphQueries.js.map +1 -0
- package/dist/core/codeGraphTypes.d.ts +28 -0
- package/dist/core/codeGraphTypes.js +2 -0
- package/dist/core/codeGraphTypes.js.map +1 -0
- package/dist/core/collisionDetector.d.ts +2 -0
- package/dist/core/collisionDetector.js +17 -12
- package/dist/core/collisionDetector.js.map +1 -1
- package/dist/core/coordination.d.ts +4 -2
- package/dist/core/coordination.js +40 -4
- package/dist/core/coordination.js.map +1 -1
- package/dist/core/coordinationEvidence.d.ts +32 -0
- package/dist/core/coordinationEvidence.js +101 -0
- package/dist/core/coordinationEvidence.js.map +1 -0
- package/dist/core/dataflow.js +3 -338
- package/dist/core/dataflow.js.map +1 -1
- package/dist/core/dataflowDatabaseSinks.d.ts +8 -0
- package/dist/core/dataflowDatabaseSinks.js +78 -0
- package/dist/core/dataflowDatabaseSinks.js.map +1 -0
- package/dist/core/dataflowRiskAssembly.d.ts +11 -0
- package/dist/core/dataflowRiskAssembly.js +117 -0
- package/dist/core/dataflowRiskAssembly.js.map +1 -0
- package/dist/core/dataflowTraversal.d.ts +25 -0
- package/dist/core/dataflowTraversal.js +200 -0
- package/dist/core/dataflowTraversal.js.map +1 -0
- package/dist/core/fileAccess.d.ts +16 -0
- package/dist/core/fileAccess.js +78 -0
- package/dist/core/fileAccess.js.map +1 -0
- package/dist/core/fileExportTypes.d.ts +2 -0
- package/dist/core/fileExportTypes.js +16 -0
- package/dist/core/fileExportTypes.js.map +1 -0
- package/dist/core/fileGraphMetrics.d.ts +4 -0
- package/dist/core/fileGraphMetrics.js +34 -0
- package/dist/core/fileGraphMetrics.js.map +1 -0
- package/dist/core/fileInspectionEvidence.d.ts +13 -0
- package/dist/core/fileInspectionEvidence.js +14 -0
- package/dist/core/fileInspectionEvidence.js.map +1 -0
- package/dist/core/fileInspectionGraph.d.ts +5 -0
- package/dist/core/fileInspectionGraph.js +29 -0
- package/dist/core/fileInspectionGraph.js.map +1 -0
- package/dist/core/fileInspectionReport.d.ts +13 -0
- package/dist/core/fileInspectionReport.js +49 -0
- package/dist/core/fileInspectionReport.js.map +1 -0
- package/dist/core/fileInspector.d.ts +5 -13
- package/dist/core/fileInspector.js +8 -239
- package/dist/core/fileInspector.js.map +1 -1
- package/dist/core/fileIssues.d.ts +1 -0
- package/dist/core/fileIssues.js +18 -0
- package/dist/core/fileIssues.js.map +1 -0
- package/dist/core/filePurpose.d.ts +2 -0
- package/dist/core/filePurpose.js +61 -0
- package/dist/core/filePurpose.js.map +1 -0
- package/dist/core/fixSuggest.d.ts +1 -9
- package/dist/core/fixSuggest.js +2 -58
- package/dist/core/fixSuggest.js.map +1 -1
- package/dist/core/fixSuggestDependencyNames.d.ts +1 -0
- package/dist/core/fixSuggestDependencyNames.js +9 -0
- package/dist/core/fixSuggestDependencyNames.js.map +1 -0
- package/dist/core/fixSuggestPreview.d.ts +10 -0
- package/dist/core/fixSuggestPreview.js +87 -0
- package/dist/core/fixSuggestPreview.js.map +1 -0
- package/dist/core/frameworkExpressSources.d.ts +4 -0
- package/dist/core/frameworkExpressSources.js +95 -0
- package/dist/core/frameworkExpressSources.js.map +1 -0
- package/dist/core/frameworkFastifySources.d.ts +4 -0
- package/dist/core/frameworkFastifySources.js +74 -0
- package/dist/core/frameworkFastifySources.js.map +1 -0
- package/dist/core/frameworkHonoSources.d.ts +4 -0
- package/dist/core/frameworkHonoSources.js +80 -0
- package/dist/core/frameworkHonoSources.js.map +1 -0
- package/dist/core/frameworkKoaSources.d.ts +4 -0
- package/dist/core/frameworkKoaSources.js +81 -0
- package/dist/core/frameworkKoaSources.js.map +1 -0
- package/dist/core/frameworkNextRouteSources.d.ts +7 -0
- package/dist/core/frameworkNextRouteSources.js +85 -0
- package/dist/core/frameworkNextRouteSources.js.map +1 -0
- package/dist/core/frameworkRemixSources.d.ts +2 -0
- package/dist/core/frameworkRemixSources.js +63 -0
- package/dist/core/frameworkRemixSources.js.map +1 -0
- package/dist/core/frameworkSources.d.ts +15 -3
- package/dist/core/frameworkSources.js +42 -210
- package/dist/core/frameworkSources.js.map +1 -1
- package/dist/core/hotspotAnalyzer.d.ts +2 -18
- package/dist/core/hotspotAnalyzer.js +15 -477
- package/dist/core/hotspotAnalyzer.js.map +1 -1
- package/dist/core/hotspotBuilder.d.ts +14 -0
- package/dist/core/hotspotBuilder.js +70 -0
- package/dist/core/hotspotBuilder.js.map +1 -0
- package/dist/core/hotspotCandidates.d.ts +9 -0
- package/dist/core/hotspotCandidates.js +63 -0
- package/dist/core/hotspotCandidates.js.map +1 -0
- package/dist/core/hotspotGit.d.ts +10 -0
- package/dist/core/hotspotGit.js +152 -0
- package/dist/core/hotspotGit.js.map +1 -0
- package/dist/core/hotspotIssues.d.ts +2 -0
- package/dist/core/hotspotIssues.js +83 -0
- package/dist/core/hotspotIssues.js.map +1 -0
- package/dist/core/hotspotLines.d.ts +2 -0
- package/dist/core/hotspotLines.js +24 -0
- package/dist/core/hotspotLines.js.map +1 -0
- package/dist/core/hotspotMemory.d.ts +2 -0
- package/dist/core/hotspotMemory.js +21 -0
- package/dist/core/hotspotMemory.js.map +1 -0
- package/dist/core/hotspotRanking.d.ts +13 -0
- package/dist/core/hotspotRanking.js +44 -0
- package/dist/core/hotspotRanking.js.map +1 -0
- package/dist/core/hotspotScoring.d.ts +23 -0
- package/dist/core/hotspotScoring.js +128 -0
- package/dist/core/hotspotScoring.js.map +1 -0
- package/dist/core/indexCache.js +3 -1
- package/dist/core/indexCache.js.map +1 -1
- package/dist/core/intentRouter.d.ts +5 -28
- package/dist/core/intentRouter.js +8 -7376
- package/dist/core/intentRouter.js.map +1 -1
- package/dist/core/intentRouterCatalog.d.ts +16 -0
- package/dist/core/intentRouterCatalog.js +1709 -0
- package/dist/core/intentRouterCatalog.js.map +1 -0
- package/dist/core/intentRouterCoordinationSignals.d.ts +12 -0
- package/dist/core/intentRouterCoordinationSignals.js +111 -0
- package/dist/core/intentRouterCoordinationSignals.js.map +1 -0
- package/dist/core/intentRouterDependencySignals.d.ts +9 -0
- package/dist/core/intentRouterDependencySignals.js +226 -0
- package/dist/core/intentRouterDependencySignals.js.map +1 -0
- package/dist/core/intentRouterKeywordContext.d.ts +14 -0
- package/dist/core/intentRouterKeywordContext.js +2 -0
- package/dist/core/intentRouterKeywordContext.js.map +1 -0
- package/dist/core/intentRouterKeywordEarlyGuards.d.ts +2 -0
- package/dist/core/intentRouterKeywordEarlyGuards.js +127 -0
- package/dist/core/intentRouterKeywordEarlyGuards.js.map +1 -0
- package/dist/core/intentRouterKeywordMatches.d.ts +3 -0
- package/dist/core/intentRouterKeywordMatches.js +31 -0
- package/dist/core/intentRouterKeywordMatches.js.map +1 -0
- package/dist/core/intentRouterKeywordSearchGuards.d.ts +2 -0
- package/dist/core/intentRouterKeywordSearchGuards.js +239 -0
- package/dist/core/intentRouterKeywordSearchGuards.js.map +1 -0
- package/dist/core/intentRouterKeywordTargetGuards.d.ts +2 -0
- package/dist/core/intentRouterKeywordTargetGuards.js +191 -0
- package/dist/core/intentRouterKeywordTargetGuards.js.map +1 -0
- package/dist/core/intentRouterKeywordToolGuards.d.ts +2 -0
- package/dist/core/intentRouterKeywordToolGuards.js +133 -0
- package/dist/core/intentRouterKeywordToolGuards.js.map +1 -0
- package/dist/core/intentRouterKeywordWeights.d.ts +4 -0
- package/dist/core/intentRouterKeywordWeights.js +1197 -0
- package/dist/core/intentRouterKeywordWeights.js.map +1 -0
- package/dist/core/intentRouterPlanningSignals.d.ts +7 -0
- package/dist/core/intentRouterPlanningSignals.js +268 -0
- package/dist/core/intentRouterPlanningSignals.js.map +1 -0
- package/dist/core/intentRouterPrDiffSignals.d.ts +1 -0
- package/dist/core/intentRouterPrDiffSignals.js +41 -0
- package/dist/core/intentRouterPrDiffSignals.js.map +1 -0
- package/dist/core/intentRouterPreflightSignals.d.ts +3 -0
- package/dist/core/intentRouterPreflightSignals.js +54 -0
- package/dist/core/intentRouterPreflightSignals.js.map +1 -0
- package/dist/core/intentRouterRegressionKeywordMatches.d.ts +1 -0
- package/dist/core/intentRouterRegressionKeywordMatches.js +176 -0
- package/dist/core/intentRouterRegressionKeywordMatches.js.map +1 -0
- package/dist/core/intentRouterRegressionSignals.d.ts +10 -0
- package/dist/core/intentRouterRegressionSignals.js +207 -0
- package/dist/core/intentRouterRegressionSignals.js.map +1 -0
- package/dist/core/intentRouterReleaseSignals.d.ts +8 -0
- package/dist/core/intentRouterReleaseSignals.js +139 -0
- package/dist/core/intentRouterReleaseSignals.js.map +1 -0
- package/dist/core/intentRouterRepoSignals.d.ts +8 -0
- package/dist/core/intentRouterRepoSignals.js +226 -0
- package/dist/core/intentRouterRepoSignals.js.map +1 -0
- package/dist/core/intentRouterResult.d.ts +16 -0
- package/dist/core/intentRouterResult.js +34 -0
- package/dist/core/intentRouterResult.js.map +1 -0
- package/dist/core/intentRouterReviewSignals.d.ts +2 -0
- package/dist/core/intentRouterReviewSignals.js +109 -0
- package/dist/core/intentRouterReviewSignals.js.map +1 -0
- package/dist/core/intentRouterRiskSignals.d.ts +12 -0
- package/dist/core/intentRouterRiskSignals.js +242 -0
- package/dist/core/intentRouterRiskSignals.js.map +1 -0
- package/dist/core/intentRouterScoring.d.ts +9 -0
- package/dist/core/intentRouterScoring.js +40 -0
- package/dist/core/intentRouterScoring.js.map +1 -0
- package/dist/core/intentRouterSearchApiSignals.d.ts +1 -0
- package/dist/core/intentRouterSearchApiSignals.js +62 -0
- package/dist/core/intentRouterSearchApiSignals.js.map +1 -0
- package/dist/core/intentRouterSearchBackgroundSignals.d.ts +1 -0
- package/dist/core/intentRouterSearchBackgroundSignals.js +55 -0
- package/dist/core/intentRouterSearchBackgroundSignals.js.map +1 -0
- package/dist/core/intentRouterSearchCommunicationSignals.d.ts +1 -0
- package/dist/core/intentRouterSearchCommunicationSignals.js +74 -0
- package/dist/core/intentRouterSearchCommunicationSignals.js.map +1 -0
- package/dist/core/intentRouterSearchDataSignals.d.ts +2 -0
- package/dist/core/intentRouterSearchDataSignals.js +98 -0
- package/dist/core/intentRouterSearchDataSignals.js.map +1 -0
- package/dist/core/intentRouterSearchDomainSignals.d.ts +1 -0
- package/dist/core/intentRouterSearchDomainSignals.js +71 -0
- package/dist/core/intentRouterSearchDomainSignals.js.map +1 -0
- package/dist/core/intentRouterSearchInfraSignals.d.ts +1 -0
- package/dist/core/intentRouterSearchInfraSignals.js +79 -0
- package/dist/core/intentRouterSearchInfraSignals.js.map +1 -0
- package/dist/core/intentRouterSearchIntegrationSignals.d.ts +1 -0
- package/dist/core/intentRouterSearchIntegrationSignals.js +117 -0
- package/dist/core/intentRouterSearchIntegrationSignals.js.map +1 -0
- package/dist/core/intentRouterSearchLookupSignals.d.ts +10 -0
- package/dist/core/intentRouterSearchLookupSignals.js +310 -0
- package/dist/core/intentRouterSearchLookupSignals.js.map +1 -0
- package/dist/core/intentRouterSearchNavigationSignals.d.ts +1 -0
- package/dist/core/intentRouterSearchNavigationSignals.js +62 -0
- package/dist/core/intentRouterSearchNavigationSignals.js.map +1 -0
- package/dist/core/intentRouterSearchOwnershipSignals.d.ts +1 -0
- package/dist/core/intentRouterSearchOwnershipSignals.js +15 -0
- package/dist/core/intentRouterSearchOwnershipSignals.js.map +1 -0
- package/dist/core/intentRouterSearchPageSignals.d.ts +1 -0
- package/dist/core/intentRouterSearchPageSignals.js +84 -0
- package/dist/core/intentRouterSearchPageSignals.js.map +1 -0
- package/dist/core/intentRouterSearchReliabilitySignals.d.ts +1 -0
- package/dist/core/intentRouterSearchReliabilitySignals.js +94 -0
- package/dist/core/intentRouterSearchReliabilitySignals.js.map +1 -0
- package/dist/core/intentRouterSearchStateSignals.d.ts +1 -0
- package/dist/core/intentRouterSearchStateSignals.js +107 -0
- package/dist/core/intentRouterSearchStateSignals.js.map +1 -0
- package/dist/core/intentRouterSearchStyleSignals.d.ts +1 -0
- package/dist/core/intentRouterSearchStyleSignals.js +99 -0
- package/dist/core/intentRouterSearchStyleSignals.js.map +1 -0
- package/dist/core/intentRouterSearchTestSignals.d.ts +1 -0
- package/dist/core/intentRouterSearchTestSignals.js +34 -0
- package/dist/core/intentRouterSearchTestSignals.js.map +1 -0
- package/dist/core/intentRouterSearchToolingSignals.d.ts +1 -0
- package/dist/core/intentRouterSearchToolingSignals.js +106 -0
- package/dist/core/intentRouterSearchToolingSignals.js.map +1 -0
- package/dist/core/intentRouterSearchUiSignals.d.ts +1 -0
- package/dist/core/intentRouterSearchUiSignals.js +77 -0
- package/dist/core/intentRouterSearchUiSignals.js.map +1 -0
- package/dist/core/intentRouterSecuritySignals.d.ts +4 -0
- package/dist/core/intentRouterSecuritySignals.js +235 -0
- package/dist/core/intentRouterSecuritySignals.js.map +1 -0
- package/dist/core/intentRouterTargetSignals.d.ts +5 -0
- package/dist/core/intentRouterTargetSignals.js +76 -0
- package/dist/core/intentRouterTargetSignals.js.map +1 -0
- package/dist/core/intentRouterTokens.d.ts +1 -0
- package/dist/core/intentRouterTokens.js +36 -0
- package/dist/core/intentRouterTokens.js.map +1 -0
- package/dist/core/intentRouterUnderstandSignals.d.ts +1 -0
- package/dist/core/intentRouterUnderstandSignals.js +171 -0
- package/dist/core/intentRouterUnderstandSignals.js.map +1 -0
- package/dist/core/intentRouterVerificationSignals.d.ts +8 -0
- package/dist/core/intentRouterVerificationSignals.js +119 -0
- package/dist/core/intentRouterVerificationSignals.js.map +1 -0
- package/dist/core/intentRouterWorkSignals.d.ts +4 -0
- package/dist/core/intentRouterWorkSignals.js +175 -0
- package/dist/core/intentRouterWorkSignals.js.map +1 -0
- package/dist/core/languages/pythonLockfiles.d.ts +15 -0
- package/dist/core/languages/pythonLockfiles.js +210 -0
- package/dist/core/languages/pythonLockfiles.js.map +1 -0
- package/dist/core/languages/pythonManifestText.d.ts +7 -0
- package/dist/core/languages/pythonManifestText.js +25 -0
- package/dist/core/languages/pythonManifestText.js.map +1 -0
- package/dist/core/languages/pythonManifests.d.ts +6 -38
- package/dist/core/languages/pythonManifests.js +27 -316
- package/dist/core/languages/pythonManifests.js.map +1 -1
- package/dist/core/languages/pythonPep508.d.ts +4 -0
- package/dist/core/languages/pythonPep508.js +14 -0
- package/dist/core/languages/pythonPep508.js.map +1 -0
- package/dist/core/languages/pythonProjectEvidence.d.ts +2 -0
- package/dist/core/languages/pythonProjectEvidence.js +29 -0
- package/dist/core/languages/pythonProjectEvidence.js.map +1 -0
- package/dist/core/languages/pythonProjectTypes.d.ts +31 -0
- package/dist/core/languages/pythonProjectTypes.js +2 -0
- package/dist/core/languages/pythonProjectTypes.js.map +1 -0
- package/dist/core/languages/pythonPyproject.d.ts +2 -0
- package/dist/core/languages/pythonPyproject.js +160 -0
- package/dist/core/languages/pythonPyproject.js.map +1 -0
- package/dist/core/languages/pythonRequirements.d.ts +9 -0
- package/dist/core/languages/pythonRequirements.js +212 -0
- package/dist/core/languages/pythonRequirements.js.map +1 -0
- package/dist/core/languages/pythonRoots.d.ts +3 -0
- package/dist/core/languages/pythonRoots.js +83 -0
- package/dist/core/languages/pythonRoots.js.map +1 -0
- package/dist/core/languages/pythonSetuptools.d.ts +6 -0
- package/dist/core/languages/pythonSetuptools.js +58 -0
- package/dist/core/languages/pythonSetuptools.js.map +1 -0
- package/dist/core/pluginManifestValidation.d.ts +41 -0
- package/dist/core/pluginManifestValidation.js +179 -0
- package/dist/core/pluginManifestValidation.js.map +1 -0
- package/dist/core/plugins.d.ts +3 -41
- package/dist/core/plugins.js +2 -129
- package/dist/core/plugins.js.map +1 -1
- package/dist/core/prDiff.js +12 -0
- package/dist/core/prDiff.js.map +1 -1
- package/dist/core/preflight.d.ts +3 -3
- package/dist/core/preflight.js +28 -542
- package/dist/core/preflight.js.map +1 -1
- package/dist/core/preflightChangedFileReasons.d.ts +14 -0
- package/dist/core/preflightChangedFileReasons.js +75 -0
- package/dist/core/preflightChangedFileReasons.js.map +1 -0
- package/dist/core/preflightChangedFiles.d.ts +9 -0
- package/dist/core/preflightChangedFiles.js +34 -0
- package/dist/core/preflightChangedFiles.js.map +1 -0
- package/dist/core/preflightContextReasons.d.ts +17 -0
- package/dist/core/preflightContextReasons.js +73 -0
- package/dist/core/preflightContextReasons.js.map +1 -0
- package/dist/core/preflightEvidence.d.ts +34 -0
- package/dist/core/preflightEvidence.js +119 -0
- package/dist/core/preflightEvidence.js.map +1 -0
- package/dist/core/preflightInputs.d.ts +15 -0
- package/dist/core/preflightInputs.js +31 -0
- package/dist/core/preflightInputs.js.map +1 -0
- package/dist/core/preflightIssueReasons.d.ts +2 -0
- package/dist/core/preflightIssueReasons.js +39 -0
- package/dist/core/preflightIssueReasons.js.map +1 -0
- package/dist/core/preflightLocalEvidence.d.ts +12 -0
- package/dist/core/preflightLocalEvidence.js +36 -0
- package/dist/core/preflightLocalEvidence.js.map +1 -0
- package/dist/core/preflightReleaseScale.d.ts +28 -0
- package/dist/core/preflightReleaseScale.js +95 -0
- package/dist/core/preflightReleaseScale.js.map +1 -0
- package/dist/core/preflightRequiredChecks.d.ts +26 -0
- package/dist/core/preflightRequiredChecks.js +96 -0
- package/dist/core/preflightRequiredChecks.js.map +1 -0
- package/dist/core/preflightReviewEvidence.d.ts +16 -0
- package/dist/core/preflightReviewEvidence.js +31 -0
- package/dist/core/preflightReviewEvidence.js.map +1 -0
- package/dist/core/preflightReviewReasons.d.ts +15 -0
- package/dist/core/preflightReviewReasons.js +76 -0
- package/dist/core/preflightReviewReasons.js.map +1 -0
- package/dist/core/preflightSuggestedActions.d.ts +15 -0
- package/dist/core/preflightSuggestedActions.js +84 -0
- package/dist/core/preflightSuggestedActions.js.map +1 -0
- package/dist/core/preflightTruncation.d.ts +6 -0
- package/dist/core/preflightTruncation.js +7 -0
- package/dist/core/preflightTruncation.js.map +1 -0
- package/dist/core/preflightVerdict.d.ts +3 -0
- package/dist/core/preflightVerdict.js +17 -0
- package/dist/core/preflightVerdict.js.map +1 -0
- package/dist/core/regressionPlan.d.ts +2 -1
- package/dist/core/regressionPlan.js +7 -1
- package/dist/core/regressionPlan.js.map +1 -1
- package/dist/core/releaseEvidence.d.ts +4 -3
- package/dist/core/releaseEvidence.js +17 -382
- package/dist/core/releaseEvidence.js.map +1 -1
- package/dist/core/releaseEvidenceArtifacts.d.ts +3 -0
- package/dist/core/releaseEvidenceArtifacts.js +65 -0
- package/dist/core/releaseEvidenceArtifacts.js.map +1 -0
- package/dist/core/releaseEvidenceBaseline.d.ts +2 -0
- package/dist/core/releaseEvidenceBaseline.js +28 -0
- package/dist/core/releaseEvidenceBaseline.js.map +1 -0
- package/dist/core/releaseEvidencePrSummary.d.ts +13 -0
- package/dist/core/releaseEvidencePrSummary.js +240 -0
- package/dist/core/releaseEvidencePrSummary.js.map +1 -0
- package/dist/core/releaseEvidenceVerdict.d.ts +6 -0
- package/dist/core/releaseEvidenceVerdict.js +54 -0
- package/dist/core/releaseEvidenceVerdict.js.map +1 -0
- package/dist/core/releaseTrain.js +3 -317
- package/dist/core/releaseTrain.js.map +1 -1
- package/dist/core/releaseTrainFallbacks.d.ts +3 -0
- package/dist/core/releaseTrainFallbacks.js +318 -0
- package/dist/core/releaseTrainFallbacks.js.map +1 -0
- package/dist/core/reportPathRedaction.d.ts +4 -0
- package/dist/core/reportPathRedaction.js +64 -0
- package/dist/core/reportPathRedaction.js.map +1 -0
- package/dist/core/reportScope.d.ts +7 -0
- package/dist/core/reportScope.js +16 -86
- package/dist/core/reportScope.js.map +1 -1
- package/dist/core/reportScopeFiltering.d.ts +9 -0
- package/dist/core/reportScopeFiltering.js +102 -0
- package/dist/core/reportScopeFiltering.js.map +1 -0
- package/dist/core/review.d.ts +2 -25
- package/dist/core/review.js +34 -1034
- package/dist/core/review.js.map +1 -1
- package/dist/core/reviewBaseSnapshot.d.ts +14 -0
- package/dist/core/reviewBaseSnapshot.js +41 -0
- package/dist/core/reviewBaseSnapshot.js.map +1 -0
- package/dist/core/reviewChangedFiles.d.ts +8 -0
- package/dist/core/reviewChangedFiles.js +63 -0
- package/dist/core/reviewChangedFiles.js.map +1 -0
- package/dist/core/reviewContractChanges.d.ts +5 -0
- package/dist/core/reviewContractChanges.js +114 -0
- package/dist/core/reviewContractChanges.js.map +1 -0
- package/dist/core/reviewCycles.d.ts +7 -0
- package/dist/core/reviewCycles.js +53 -0
- package/dist/core/reviewCycles.js.map +1 -0
- package/dist/core/reviewFindings.d.ts +17 -0
- package/dist/core/reviewFindings.js +49 -0
- package/dist/core/reviewFindings.js.map +1 -0
- package/dist/core/reviewFlowDiffs.d.ts +4 -0
- package/dist/core/reviewFlowDiffs.js +99 -0
- package/dist/core/reviewFlowDiffs.js.map +1 -0
- package/dist/core/reviewGit.d.ts +7 -0
- package/dist/core/reviewGit.js +45 -0
- package/dist/core/reviewGit.js.map +1 -0
- package/dist/core/reviewGraphEvidence.d.ts +3 -0
- package/dist/core/reviewGraphEvidence.js +55 -0
- package/dist/core/reviewGraphEvidence.js.map +1 -0
- package/dist/core/reviewHeadSnapshot.d.ts +8 -0
- package/dist/core/reviewHeadSnapshot.js +15 -0
- package/dist/core/reviewHeadSnapshot.js.map +1 -0
- package/dist/core/reviewIntent.d.ts +2 -0
- package/dist/core/reviewIntent.js +18 -0
- package/dist/core/reviewIntent.js.map +1 -0
- package/dist/core/reviewManifests.d.ts +12 -0
- package/dist/core/reviewManifests.js +124 -0
- package/dist/core/reviewManifests.js.map +1 -0
- package/dist/core/reviewNoChanges.d.ts +9 -0
- package/dist/core/reviewNoChanges.js +26 -0
- package/dist/core/reviewNoChanges.js.map +1 -0
- package/dist/core/reviewPackageScope.d.ts +4 -0
- package/dist/core/reviewPackageScope.js +24 -0
- package/dist/core/reviewPackageScope.js.map +1 -0
- package/dist/core/reviewRefs.d.ts +4 -0
- package/dist/core/reviewRefs.js +65 -0
- package/dist/core/reviewRefs.js.map +1 -0
- package/dist/core/reviewRiskyFunctions.d.ts +8 -0
- package/dist/core/reviewRiskyFunctions.js +83 -0
- package/dist/core/reviewRiskyFunctions.js.map +1 -0
- package/dist/core/reviewState.d.ts +21 -0
- package/dist/core/reviewState.js +96 -0
- package/dist/core/reviewState.js.map +1 -0
- package/dist/core/reviewTier.d.ts +18 -0
- package/dist/core/reviewTier.js +99 -0
- package/dist/core/reviewTier.js.map +1 -0
- package/dist/core/reviewVerdict.d.ts +9 -0
- package/dist/core/reviewVerdict.js +121 -0
- package/dist/core/reviewVerdict.js.map +1 -0
- package/dist/core/searchIndex.d.ts +2 -14
- package/dist/core/searchIndex.js +4 -227
- package/dist/core/searchIndex.js.map +1 -1
- package/dist/core/searchIndexFiles.d.ts +1 -0
- package/dist/core/searchIndexFiles.js +26 -0
- package/dist/core/searchIndexFiles.js.map +1 -0
- package/dist/core/searchIndexText.d.ts +15 -0
- package/dist/core/searchIndexText.js +204 -0
- package/dist/core/searchIndexText.js.map +1 -0
- package/dist/core/start.d.ts +1 -1
- package/dist/core/start.js +14 -87
- package/dist/core/start.js.map +1 -1
- package/dist/core/startAdoptionGaps.d.ts +3 -0
- package/dist/core/startAdoptionGaps.js +12 -0
- package/dist/core/startAdoptionGaps.js.map +1 -0
- package/dist/core/startEvidence.d.ts +1 -1
- package/dist/core/startEvidence.js +16 -1
- package/dist/core/startEvidence.js.map +1 -1
- package/dist/core/startInputs.d.ts +32 -0
- package/dist/core/startInputs.js +30 -0
- package/dist/core/startInputs.js.map +1 -0
- package/dist/core/startMissionPolicy.js +10 -0
- package/dist/core/startMissionPolicy.js.map +1 -1
- package/dist/core/startMode.js +7 -1
- package/dist/core/startMode.js.map +1 -1
- package/dist/core/startReportBuilder.d.ts +26 -0
- package/dist/core/startReportBuilder.js +45 -0
- package/dist/core/startReportBuilder.js.map +1 -0
- package/dist/core/startReportContext.d.ts +23 -0
- package/dist/core/startReportContext.js +51 -0
- package/dist/core/startReportContext.js.map +1 -0
- package/dist/core/startRoadmapPreview.d.ts +2 -0
- package/dist/core/startRoadmapPreview.js +31 -0
- package/dist/core/startRoadmapPreview.js.map +1 -0
- package/dist/core/taint.d.ts +2 -67
- package/dist/core/taint.js +41 -276
- package/dist/core/taint.js.map +1 -1
- package/dist/core/taintIndex.d.ts +20 -0
- package/dist/core/taintIndex.js +81 -0
- package/dist/core/taintIndex.js.map +1 -0
- package/dist/core/taintMatching.d.ts +11 -0
- package/dist/core/taintMatching.js +126 -0
- package/dist/core/taintMatching.js.map +1 -0
- package/dist/core/taintTraversal.d.ts +8 -0
- package/dist/core/taintTraversal.js +113 -0
- package/dist/core/taintTraversal.js.map +1 -0
- package/dist/core/taintTypes.d.ts +67 -0
- package/dist/core/taintTypes.js +2 -0
- package/dist/core/taintTypes.js.map +1 -0
- package/dist/core/telemetry.js +36 -30
- package/dist/core/telemetry.js.map +1 -1
- package/dist/core/upgradePreview.d.ts +1 -12
- package/dist/core/upgradePreview.js +9 -229
- package/dist/core/upgradePreview.js.map +1 -1
- package/dist/core/upgradePreviewNpmEvidence.d.ts +19 -0
- package/dist/core/upgradePreviewNpmEvidence.js +164 -0
- package/dist/core/upgradePreviewNpmEvidence.js.map +1 -0
- package/dist/core/upgradePreviewPython.d.ts +2 -0
- package/dist/core/upgradePreviewPython.js +71 -0
- package/dist/core/upgradePreviewPython.js.map +1 -0
- package/dist/index.d.ts +6 -12
- package/dist/index.js +2 -3
- package/dist/index.js.map +1 -1
- package/dist/mcp/server.d.ts +3 -24
- package/dist/mcp/server.js +32 -414
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/serverContext.d.ts +6 -0
- package/dist/mcp/serverContext.js +55 -0
- package/dist/mcp/serverContext.js.map +1 -0
- package/dist/mcp/serverDispatch.d.ts +39 -0
- package/dist/mcp/serverDispatch.js +74 -0
- package/dist/mcp/serverDispatch.js.map +1 -0
- package/dist/mcp/serverHandlers.d.ts +15 -0
- package/dist/mcp/serverHandlers.js +94 -0
- package/dist/mcp/serverHandlers.js.map +1 -0
- package/dist/mcp/serverLifecycle.d.ts +14 -0
- package/dist/mcp/serverLifecycle.js +65 -0
- package/dist/mcp/serverLifecycle.js.map +1 -0
- package/dist/mcp/serverMessage.d.ts +11 -0
- package/dist/mcp/serverMessage.js +37 -0
- package/dist/mcp/serverMessage.js.map +1 -0
- package/dist/mcp/serverSession.d.ts +6 -0
- package/dist/mcp/serverSession.js +77 -0
- package/dist/mcp/serverSession.js.map +1 -0
- package/dist/mcp/serverStdio.d.ts +7 -0
- package/dist/mcp/serverStdio.js +34 -0
- package/dist/mcp/serverStdio.js.map +1 -0
- package/dist/mcp/serverTypes.d.ts +18 -0
- package/dist/mcp/serverTypes.js +2 -0
- package/dist/mcp/serverTypes.js.map +1 -0
- package/dist/mcp/serverVersion.d.ts +1 -0
- package/dist/mcp/serverVersion.js +17 -0
- package/dist/mcp/serverVersion.js.map +1 -0
- package/dist/mcp/toolCatalog.d.ts +2 -0
- package/dist/mcp/toolCatalog.js +93 -0
- package/dist/mcp/toolCatalog.js.map +1 -0
- package/dist/mcp/tools.d.ts +2 -3
- package/dist/mcp/tools.js +15 -108
- package/dist/mcp/tools.js.map +1 -1
- package/dist/projscan-sbom.cdx.json +6 -6
- package/dist/reporters/consoleFixReporter.d.ts +3 -0
- package/dist/reporters/consoleFixReporter.js +41 -0
- package/dist/reporters/consoleFixReporter.js.map +1 -0
- package/dist/reporters/consoleReporter.d.ts +1 -3
- package/dist/reporters/consoleReporter.js +1 -42
- package/dist/reporters/consoleReporter.js.map +1 -1
- package/dist/reporters/htmlReporter.d.ts +3 -2
- package/dist/reporters/htmlReporter.js +14 -2
- package/dist/reporters/htmlReporter.js.map +1 -1
- package/dist/reporters/jsonReporter.d.ts +4 -3
- package/dist/reporters/jsonReporter.js +9 -4
- package/dist/reporters/jsonReporter.js.map +1 -1
- package/dist/reporters/markdownAnalysisReporter.d.ts +2 -1
- package/dist/reporters/markdownAnalysisReporter.js +8 -1
- package/dist/reporters/markdownAnalysisReporter.js.map +1 -1
- package/dist/reporters/markdownArchitectureReporter.d.ts +3 -0
- package/dist/reporters/markdownArchitectureReporter.js +33 -0
- package/dist/reporters/markdownArchitectureReporter.js.map +1 -0
- package/dist/reporters/markdownCouplingReporter.d.ts +2 -0
- package/dist/reporters/markdownCouplingReporter.js +43 -0
- package/dist/reporters/markdownCouplingReporter.js.map +1 -0
- package/dist/reporters/markdownCoverageReporter.d.ts +2 -0
- package/dist/reporters/markdownCoverageReporter.js +40 -0
- package/dist/reporters/markdownCoverageReporter.js.map +1 -0
- package/dist/reporters/markdownExplanationReporter.d.ts +2 -0
- package/dist/reporters/markdownExplanationReporter.js +37 -0
- package/dist/reporters/markdownExplanationReporter.js.map +1 -0
- package/dist/reporters/markdownHealthReporter.d.ts +4 -0
- package/dist/reporters/markdownHealthReporter.js +66 -0
- package/dist/reporters/markdownHealthReporter.js.map +1 -0
- package/dist/reporters/markdownHotspotReporter.d.ts +2 -0
- package/dist/reporters/markdownHotspotReporter.js +36 -0
- package/dist/reporters/markdownHotspotReporter.js.map +1 -0
- package/dist/reporters/markdownOutdatedReporter.d.ts +2 -0
- package/dist/reporters/markdownOutdatedReporter.js +31 -0
- package/dist/reporters/markdownOutdatedReporter.js.map +1 -0
- package/dist/reporters/markdownPrDiffReporter.d.ts +2 -0
- package/dist/reporters/markdownPrDiffReporter.js +63 -0
- package/dist/reporters/markdownPrDiffReporter.js.map +1 -0
- package/dist/reporters/markdownReporter.d.ts +9 -12
- package/dist/reporters/markdownReporter.js +9 -288
- package/dist/reporters/markdownReporter.js.map +1 -1
- package/dist/reporters/markdownWorkspaceReporter.d.ts +2 -0
- package/dist/reporters/markdownWorkspaceReporter.js +25 -0
- package/dist/reporters/markdownWorkspaceReporter.js.map +1 -0
- package/dist/reporters/sarifReporter.d.ts +6 -4
- package/dist/reporters/sarifReporter.js +8 -7
- package/dist/reporters/sarifReporter.js.map +1 -1
- package/dist/tool-manifest.json +3 -3
- package/dist/types/start.d.ts +14 -0
- package/dist/types.d.ts +34 -34
- package/dist/utils/config.d.ts +2 -8
- package/dist/utils/config.js +13 -211
- package/dist/utils/config.js.map +1 -1
- package/dist/utils/configBasics.d.ts +5 -0
- package/dist/utils/configBasics.js +21 -0
- package/dist/utils/configBasics.js.map +1 -0
- package/dist/utils/configHotspots.d.ts +2 -0
- package/dist/utils/configHotspots.js +15 -0
- package/dist/utils/configHotspots.js.map +1 -0
- package/dist/utils/configIssueRules.d.ts +8 -0
- package/dist/utils/configIssueRules.js +24 -0
- package/dist/utils/configIssueRules.js.map +1 -0
- package/dist/utils/configMonorepo.d.ts +2 -0
- package/dist/utils/configMonorepo.js +38 -0
- package/dist/utils/configMonorepo.js.map +1 -0
- package/dist/utils/configReportPolicies.d.ts +2 -0
- package/dist/utils/configReportPolicies.js +32 -0
- package/dist/utils/configReportPolicies.js.map +1 -0
- package/dist/utils/configScan.d.ts +2 -0
- package/dist/utils/configScan.js +15 -0
- package/dist/utils/configScan.js.map +1 -0
- package/dist/utils/configSeverity.d.ts +2 -0
- package/dist/utils/configSeverity.js +15 -0
- package/dist/utils/configSeverity.js.map +1 -0
- package/dist/utils/configSources.d.ts +5 -0
- package/dist/utils/configSources.js +55 -0
- package/dist/utils/configSources.js.map +1 -0
- package/dist/utils/configTaint.d.ts +2 -0
- package/dist/utils/configTaint.js +15 -0
- package/dist/utils/configTaint.js.map +1 -0
- package/docs/GUIDE.md +22 -11
- package/docs/ROADMAP.md +18 -7
- package/docs/examples/adoption-workflows.md +2 -2
- package/docs/examples/swarm-coordination.md +11 -0
- package/package.json +1 -1
package/dist/core/review.js
CHANGED
|
@@ -1,25 +1,11 @@
|
|
|
1
|
-
import { spawn } from 'node:child_process';
|
|
2
|
-
import fs from 'node:fs/promises';
|
|
3
|
-
import os from 'node:os';
|
|
4
|
-
import path from 'node:path';
|
|
5
|
-
import { scanRepository } from './repositoryScanner.js';
|
|
6
|
-
import { collectIssues } from './issueEngine.js';
|
|
7
|
-
import { buildCodeGraph } from './codeGraph.js';
|
|
8
|
-
import { analyzeHotspots } from './hotspotAnalyzer.js';
|
|
9
|
-
import { computeCoupling } from './couplingAnalyzer.js';
|
|
10
1
|
import { diffGraphs } from './prDiff.js';
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
|
|
18
|
-
import { loadConfig } from '../utils/config.js';
|
|
19
|
-
const HIGH_CC_THRESHOLD = 10;
|
|
20
|
-
const CC_JUMP_THRESHOLD = 5;
|
|
21
|
-
const RISK_VERDICT_BLOCK_SCORE = 80;
|
|
22
|
-
const RISK_VERDICT_REVIEW_SCORE = 40;
|
|
2
|
+
import { buildReviewBaseSnapshot } from './reviewBaseSnapshot.js';
|
|
3
|
+
import { buildReviewHeadSnapshot } from './reviewHeadSnapshot.js';
|
|
4
|
+
import { readManifests } from './reviewManifests.js';
|
|
5
|
+
import { buildReviewFindings } from './reviewFindings.js';
|
|
6
|
+
import { applyReviewIntent } from './reviewIntent.js';
|
|
7
|
+
import { resolveReviewState, unavailableReviewReport } from './reviewState.js';
|
|
8
|
+
export { selectReviewTier, shapeReviewForTier } from './reviewTier.js';
|
|
23
9
|
/**
|
|
24
10
|
* Compose a one-shot PR review. Builds head + base graphs (worktree dance),
|
|
25
11
|
* joins the structural diff with hotspot risk scores, surfaces cycles
|
|
@@ -34,1031 +20,45 @@ const RISK_VERDICT_REVIEW_SCORE = 40;
|
|
|
34
20
|
* ok - otherwise
|
|
35
21
|
*/
|
|
36
22
|
export async function computeReview(rootPath, options = {}) {
|
|
37
|
-
const
|
|
38
|
-
if (
|
|
39
|
-
return
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
head: { ref: headRef, resolvedSha: headSha },
|
|
53
|
-
prDiff: {
|
|
54
|
-
available: true,
|
|
55
|
-
base: { ref: baseRef, resolvedSha: baseSha },
|
|
56
|
-
head: { ref: headRef, resolvedSha: headSha },
|
|
57
|
-
filesAdded: [],
|
|
58
|
-
filesRemoved: [],
|
|
59
|
-
filesModified: [],
|
|
60
|
-
totalFilesChanged: 0,
|
|
61
|
-
},
|
|
62
|
-
changedFiles: [],
|
|
63
|
-
newCycles: [],
|
|
64
|
-
riskyFunctions: [],
|
|
65
|
-
dependencyChanges: [],
|
|
66
|
-
contractChanges: [],
|
|
67
|
-
newTaintFlows: [],
|
|
68
|
-
newDataflowRisks: [],
|
|
69
|
-
verdict: 'ok',
|
|
70
|
-
summary: ['No structural changes detected between base and head.'],
|
|
71
|
-
};
|
|
72
|
-
applyIntent(report, options.intent);
|
|
73
|
-
return report;
|
|
74
|
-
}
|
|
75
|
-
// Head-side data: scan + graph + issues + hotspots.
|
|
76
|
-
const headScan = await scanRepository(rootPath);
|
|
77
|
-
const headGraph = await buildCodeGraph(rootPath, headScan.files);
|
|
78
|
-
const headIssues = await collectIssues(rootPath, headScan.files);
|
|
79
|
-
const headHotspots = await analyzeHotspots(rootPath, headScan.files, headIssues, {
|
|
80
|
-
limit: 200,
|
|
81
|
-
graph: headGraph,
|
|
82
|
-
});
|
|
83
|
-
// Base-side: spin up a worktree, scan, build graph. Best-effort cleanup.
|
|
84
|
-
const worktreeDir = await mkTempWorktreeDir();
|
|
85
|
-
let baseGraph;
|
|
86
|
-
let basePackageManifests;
|
|
87
|
-
try {
|
|
88
|
-
// `--` separator before positional args. baseSha is verified through
|
|
89
|
-
// `rev-parse --verify ... ^{commit}` upstream so it's already sha-shaped,
|
|
90
|
-
// but the separator is a defense-in-depth: if a future caller pipes a
|
|
91
|
-
// user-supplied ref here without that verification, refs starting with
|
|
92
|
-
// '-' (e.g. `--upload-pack=evil`) won't be parsed as flags.
|
|
93
|
-
const addWorktree = await runGit(rootPath, [
|
|
94
|
-
'worktree',
|
|
95
|
-
'add',
|
|
96
|
-
'--detach',
|
|
97
|
-
'--',
|
|
98
|
-
worktreeDir,
|
|
99
|
-
baseSha,
|
|
100
|
-
]);
|
|
101
|
-
if (addWorktree.code !== 0) {
|
|
102
|
-
return unavailable(`Could not check out base ref "${baseRef}" for review: ${gitFailureSummary(addWorktree)}`, options, baseRef, headRef, headSha);
|
|
103
|
-
}
|
|
104
|
-
const baseScan = await scanRepository(worktreeDir);
|
|
105
|
-
baseGraph = await buildCodeGraph(worktreeDir, baseScan.files);
|
|
106
|
-
basePackageManifests = await readManifests(worktreeDir);
|
|
107
|
-
}
|
|
108
|
-
finally {
|
|
109
|
-
await runGit(rootPath, ['worktree', 'remove', '--force', worktreeDir]).catch(() => { });
|
|
110
|
-
await fs.rm(worktreeDir, { recursive: true, force: true }).catch(() => { });
|
|
111
|
-
}
|
|
23
|
+
const state = await resolveReviewState(rootPath, options);
|
|
24
|
+
if (state.kind === 'unavailable')
|
|
25
|
+
return state.report;
|
|
26
|
+
if (state.kind === 'no-change') {
|
|
27
|
+
applyReviewIntent(state.report, options.intent);
|
|
28
|
+
return state.report;
|
|
29
|
+
}
|
|
30
|
+
const { baseRef, baseSha, headRef, headSha } = state;
|
|
31
|
+
const { graph: headGraph, hotspots: headHotspots } = await buildReviewHeadSnapshot(rootPath);
|
|
32
|
+
const baseSnapshot = await buildReviewBaseSnapshot(rootPath, baseRef, baseSha);
|
|
33
|
+
if (!baseSnapshot.available) {
|
|
34
|
+
return unavailableReviewReport(baseSnapshot.reason, options, baseRef, headRef, headSha, baseSha);
|
|
35
|
+
}
|
|
36
|
+
const baseGraph = baseSnapshot.graph;
|
|
37
|
+
const basePackageManifests = baseSnapshot.packageManifests;
|
|
112
38
|
const headPackageManifests = await readManifests(rootPath);
|
|
113
39
|
const prDiff = diffGraphs(baseRef, baseSha, headRef, headSha, baseGraph, headGraph);
|
|
114
|
-
await
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
relativePath: f,
|
|
125
|
-
status: 'added',
|
|
126
|
-
riskScore: hotspotByPath.get(f) ?? null,
|
|
127
|
-
cyclomaticComplexity: headFile?.parseOk ? headFile.cyclomaticComplexity : null,
|
|
128
|
-
cyclomaticDelta: null,
|
|
129
|
-
exportsAdded: headFile?.exports.length ?? 0,
|
|
130
|
-
exportsRemoved: 0,
|
|
131
|
-
importsAdded: headFile?.imports.length ?? 0,
|
|
132
|
-
importsRemoved: 0,
|
|
133
|
-
});
|
|
134
|
-
}
|
|
135
|
-
for (const f of prDiff.filesRemoved) {
|
|
136
|
-
const baseFile = baseGraph.files.get(f);
|
|
137
|
-
changedFiles.push({
|
|
138
|
-
relativePath: f,
|
|
139
|
-
status: 'removed',
|
|
140
|
-
riskScore: null,
|
|
141
|
-
cyclomaticComplexity: null,
|
|
142
|
-
cyclomaticDelta: null,
|
|
143
|
-
exportsAdded: 0,
|
|
144
|
-
exportsRemoved: baseFile?.exports.length ?? 0,
|
|
145
|
-
importsAdded: 0,
|
|
146
|
-
importsRemoved: baseFile?.imports.length ?? 0,
|
|
147
|
-
});
|
|
148
|
-
}
|
|
149
|
-
for (const f of prDiff.filesModified) {
|
|
150
|
-
const headFile = headGraph.files.get(f.relativePath);
|
|
151
|
-
changedFiles.push({
|
|
152
|
-
relativePath: f.relativePath,
|
|
153
|
-
status: 'modified',
|
|
154
|
-
riskScore: hotspotByPath.get(f.relativePath) ?? null,
|
|
155
|
-
cyclomaticComplexity: headFile?.parseOk ? headFile.cyclomaticComplexity : null,
|
|
156
|
-
cyclomaticDelta: f.cyclomaticDelta,
|
|
157
|
-
exportsAdded: f.exportsAdded.length,
|
|
158
|
-
exportsRemoved: f.exportsRemoved.length,
|
|
159
|
-
importsAdded: f.importsAdded.length,
|
|
160
|
-
importsRemoved: f.importsRemoved.length,
|
|
161
|
-
});
|
|
162
|
-
}
|
|
163
|
-
changedFiles.sort((a, b) => (b.riskScore ?? 0) - (a.riskScore ?? 0));
|
|
164
|
-
// Cycles: compute on both sides; classify head cycles as new/expanded based
|
|
165
|
-
// on overlap with base cycles.
|
|
166
|
-
const headCoupling = computeCoupling(headGraph);
|
|
167
|
-
const baseCoupling = computeCoupling(baseGraph);
|
|
168
|
-
const newCycles = scopeCyclesToFiles(classifyNewCycles(baseCoupling.cycles, headCoupling.cycles, prDiff.filesAdded), graphScopeFiles);
|
|
169
|
-
// Risky functions: compare per-file function lists between base and head.
|
|
170
|
-
const riskyFunctions = findRiskyFunctions(baseGraph, headGraph, prDiff);
|
|
171
|
-
// Dependency changes across root + workspaces.
|
|
172
|
-
const dependencyChanges = scopeDependencyChanges(diffManifests(basePackageManifests, headPackageManifests), options.package);
|
|
173
|
-
const contractChanges = buildContractChanges(prDiff, baseGraph, headGraph, basePackageManifests, headPackageManifests, options.package);
|
|
174
|
-
// 1.6+ — taint flows newly introduced at head. A flow is "new" iff
|
|
175
|
-
// (a) the (sourceFn, sinkFn, source, sink) flow didn't exist at base, AND
|
|
176
|
-
// (b) at least one file along the flow's path is in the PR diff.
|
|
177
|
-
// (b) prevents a base-graph parse failure from avalanching every
|
|
178
|
-
// pre-existing head flow into a false "new" verdict. Project config
|
|
179
|
-
// adds user-declared sources/sinks on top of the built-in defaults.
|
|
180
|
-
const touchedFiles = new Set([
|
|
181
|
-
...prDiff.filesAdded,
|
|
182
|
-
...prDiff.filesRemoved,
|
|
183
|
-
...prDiff.filesModified.map((f) => f.relativePath),
|
|
184
|
-
]);
|
|
185
|
-
const newTaintFlows = await computeNewTaintFlows(rootPath, baseGraph, headGraph, touchedFiles);
|
|
186
|
-
const newDataflowRisks = await computeNewDataflowRisks(rootPath, baseGraph, headGraph, touchedFiles);
|
|
187
|
-
const graphEvidence = buildReviewGraphEvidence(headGraph, touchedFiles, newDataflowRisks.length, graphScopeFiles);
|
|
188
|
-
// Verdict.
|
|
189
|
-
const { verdict, summary } = decideVerdict(changedFiles, newCycles, riskyFunctions, dependencyChanges, contractChanges, newTaintFlows, newDataflowRisks);
|
|
40
|
+
const findings = await buildReviewFindings({
|
|
41
|
+
rootPath,
|
|
42
|
+
packageName: options.package,
|
|
43
|
+
prDiff,
|
|
44
|
+
baseGraph,
|
|
45
|
+
headGraph,
|
|
46
|
+
headHotspots,
|
|
47
|
+
basePackageManifests,
|
|
48
|
+
headPackageManifests,
|
|
49
|
+
});
|
|
190
50
|
const report = {
|
|
191
51
|
available: true,
|
|
192
52
|
base: { ref: baseRef, resolvedSha: baseSha },
|
|
193
53
|
head: { ref: headRef, resolvedSha: headSha },
|
|
194
54
|
prDiff,
|
|
195
|
-
|
|
196
|
-
newCycles,
|
|
197
|
-
riskyFunctions,
|
|
198
|
-
dependencyChanges,
|
|
199
|
-
contractChanges,
|
|
200
|
-
newTaintFlows,
|
|
201
|
-
newDataflowRisks,
|
|
202
|
-
graphEvidence,
|
|
203
|
-
verdict,
|
|
204
|
-
summary,
|
|
55
|
+
...findings,
|
|
205
56
|
};
|
|
206
57
|
// 1.9+ — intent grounding. Parse the agent-supplied description,
|
|
207
58
|
// annotate each finding with an alignment label, and append a
|
|
208
59
|
// small intent summary to the verdict bullets. Does NOT change the
|
|
209
60
|
// verdict — verdict stays structural.
|
|
210
|
-
|
|
61
|
+
applyReviewIntent(report, options.intent);
|
|
211
62
|
return report;
|
|
212
63
|
}
|
|
213
|
-
async function resolvePackageScopeFiles(rootPath, graph, packageName) {
|
|
214
|
-
if (!packageName)
|
|
215
|
-
return undefined;
|
|
216
|
-
const workspaces = await detectWorkspaces(rootPath);
|
|
217
|
-
return new Set(filterFilesByPackage(workspaces, packageName, [...graph.files.keys()]));
|
|
218
|
-
}
|
|
219
|
-
async function scopePrDiffToPackage(rootPath, prDiff, packageName) {
|
|
220
|
-
if (!packageName)
|
|
221
|
-
return;
|
|
222
|
-
const workspaces = await detectWorkspaces(rootPath);
|
|
223
|
-
const allChangedPaths = [
|
|
224
|
-
...prDiff.filesAdded,
|
|
225
|
-
...prDiff.filesRemoved,
|
|
226
|
-
...prDiff.filesModified.map((file) => file.relativePath),
|
|
227
|
-
];
|
|
228
|
-
const allowed = new Set(filterFilesByPackage(workspaces, packageName, allChangedPaths));
|
|
229
|
-
prDiff.filesAdded = prDiff.filesAdded.filter((file) => allowed.has(file));
|
|
230
|
-
prDiff.filesRemoved = prDiff.filesRemoved.filter((file) => allowed.has(file));
|
|
231
|
-
prDiff.filesModified = prDiff.filesModified.filter((file) => allowed.has(file.relativePath));
|
|
232
|
-
prDiff.totalFilesChanged =
|
|
233
|
-
prDiff.filesAdded.length + prDiff.filesRemoved.length + prDiff.filesModified.length;
|
|
234
|
-
}
|
|
235
|
-
function scopeDependencyChanges(changes, packageName) {
|
|
236
|
-
if (!packageName)
|
|
237
|
-
return changes;
|
|
238
|
-
return changes.filter((change) => change.workspace === packageName);
|
|
239
|
-
}
|
|
240
|
-
function applyIntent(report, rawIntent) {
|
|
241
|
-
const intent = parseIntent(rawIntent);
|
|
242
|
-
if (intent) {
|
|
243
|
-
const analysis = annotateReviewWithIntent(report, intent);
|
|
244
|
-
report.intent = {
|
|
245
|
-
raw: intent.raw,
|
|
246
|
-
action: intent.action,
|
|
247
|
-
scopeTokens: intent.scopeTokens,
|
|
248
|
-
};
|
|
249
|
-
report.intentAnalysis = {
|
|
250
|
-
totals: analysis.totals,
|
|
251
|
-
notable: analysis.notable,
|
|
252
|
-
};
|
|
253
|
-
appendIntentToSummary(report.summary, analysis);
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
async function computeNewTaintFlows(rootPath, baseGraph, headGraph, touchedFiles) {
|
|
257
|
-
const { config } = await loadConfig(rootPath);
|
|
258
|
-
const sources = config.taint?.sources ?? [];
|
|
259
|
-
const sinks = config.taint?.sinks ?? [];
|
|
260
|
-
const baseReport = computeTaint(baseGraph, { sources, sinks });
|
|
261
|
-
const headReport = computeTaint(headGraph, { sources, sinks });
|
|
262
|
-
const filterContext = { customSources: new Set(sources), customSinks: new Set(sinks) };
|
|
263
|
-
if (!headReport.available)
|
|
264
|
-
return [];
|
|
265
|
-
const baseFlowKeys = new Set(baseReport.available ? baseReport.flows.map(reviewTaintFlowKey) : []);
|
|
266
|
-
const out = [];
|
|
267
|
-
for (const flow of headReport.flows) {
|
|
268
|
-
const key = reviewTaintFlowKey(flow);
|
|
269
|
-
if (baseFlowKeys.has(key))
|
|
270
|
-
continue;
|
|
271
|
-
// Restrict to flows the PR actually had a hand in: at least one file
|
|
272
|
-
// along the path must be in the change set. A genuinely-introduced flow
|
|
273
|
-
// necessarily touches a modified file (the new source-fn, sink-fn, or
|
|
274
|
-
// intermediate hop), so this is a strict refinement — never drops a
|
|
275
|
-
// real flow. Without it, a base-graph parse failure would surface every
|
|
276
|
-
// pre-existing head flow as "new" and avalanche the verdict to block.
|
|
277
|
-
if (!flow.files.some((f) => touchedFiles.has(f)))
|
|
278
|
-
continue;
|
|
279
|
-
if (!isReviewBlockingFlow(flow, filterContext))
|
|
280
|
-
continue;
|
|
281
|
-
out.push({
|
|
282
|
-
sourceFn: flow.sourceFn,
|
|
283
|
-
sinkFn: flow.sinkFn,
|
|
284
|
-
source: flow.source,
|
|
285
|
-
sink: flow.sink,
|
|
286
|
-
pathLength: flow.path.length,
|
|
287
|
-
files: flow.files,
|
|
288
|
-
});
|
|
289
|
-
}
|
|
290
|
-
out.sort((a, b) => {
|
|
291
|
-
if (a.pathLength !== b.pathLength)
|
|
292
|
-
return a.pathLength - b.pathLength;
|
|
293
|
-
return a.sourceFn.localeCompare(b.sourceFn);
|
|
294
|
-
});
|
|
295
|
-
return out;
|
|
296
|
-
}
|
|
297
|
-
function reviewTaintFlowKey(flow) {
|
|
298
|
-
return `${flow.sourceFn}:${flow.sinkFn}:${flow.source}:${flow.sink}`;
|
|
299
|
-
}
|
|
300
|
-
function buildReviewGraphEvidence(graph, touchedFiles, dataflowRisks, scopeFiles) {
|
|
301
|
-
const evidenceGraph = scopeFiles ? scopeGraphToFiles(graph, scopeFiles) : graph;
|
|
302
|
-
const semantic = buildSemanticGraph(evidenceGraph, { maxNodes: 5_000, maxEdges: 10_000 });
|
|
303
|
-
const changedFunctions = [...touchedFiles].reduce((count, file) => {
|
|
304
|
-
return count + (evidenceGraph.files.get(file)?.functions?.length ?? 0);
|
|
305
|
-
}, 0);
|
|
306
|
-
return {
|
|
307
|
-
schemaVersion: 1,
|
|
308
|
-
changedFiles: touchedFiles.size,
|
|
309
|
-
changedFunctions,
|
|
310
|
-
totalFunctions: semantic.metrics.totalFunctions,
|
|
311
|
-
totalPackages: semantic.metrics.totalPackages,
|
|
312
|
-
totalCallEdges: semantic.edges.filter((edge) => edge.kind === 'calls').length,
|
|
313
|
-
dataflowRisks,
|
|
314
|
-
topPackages: topPackages(evidenceGraph),
|
|
315
|
-
};
|
|
316
|
-
}
|
|
317
|
-
function scopeGraphToFiles(graph, files) {
|
|
318
|
-
const scopedFiles = new Map([...graph.files.entries()].filter(([file]) => files.has(file)));
|
|
319
|
-
const packageImporters = filterImporterMap(graph.packageImporters, files);
|
|
320
|
-
const localImporters = new Map();
|
|
321
|
-
for (const [target, importers] of graph.localImporters) {
|
|
322
|
-
if (!files.has(target))
|
|
323
|
-
continue;
|
|
324
|
-
const scopedImporters = new Set([...importers].filter((file) => files.has(file)));
|
|
325
|
-
if (scopedImporters.size > 0)
|
|
326
|
-
localImporters.set(target, scopedImporters);
|
|
327
|
-
}
|
|
328
|
-
const symbolDefs = filterImporterMap(graph.symbolDefs, files);
|
|
329
|
-
return {
|
|
330
|
-
files: scopedFiles,
|
|
331
|
-
packageImporters,
|
|
332
|
-
localImporters,
|
|
333
|
-
symbolDefs,
|
|
334
|
-
scannedFiles: scopedFiles.size,
|
|
335
|
-
};
|
|
336
|
-
}
|
|
337
|
-
function filterImporterMap(source, files) {
|
|
338
|
-
const filtered = new Map();
|
|
339
|
-
for (const [name, importers] of source) {
|
|
340
|
-
const scopedImporters = new Set([...importers].filter((file) => files.has(file)));
|
|
341
|
-
if (scopedImporters.size > 0)
|
|
342
|
-
filtered.set(name, scopedImporters);
|
|
343
|
-
}
|
|
344
|
-
return filtered;
|
|
345
|
-
}
|
|
346
|
-
function topPackages(graph) {
|
|
347
|
-
return [...graph.packageImporters.entries()]
|
|
348
|
-
.map(([name, importers]) => ({ name, count: importers.size }))
|
|
349
|
-
.sort((a, b) => b.count - a.count || a.name.localeCompare(b.name))
|
|
350
|
-
.slice(0, 5)
|
|
351
|
-
.map((entry) => entry.name);
|
|
352
|
-
}
|
|
353
|
-
async function computeNewDataflowRisks(rootPath, baseGraph, headGraph, touchedFiles) {
|
|
354
|
-
const { config } = await loadConfig(rootPath);
|
|
355
|
-
const sources = config.taint?.sources ?? [];
|
|
356
|
-
const sinks = config.taint?.sinks ?? [];
|
|
357
|
-
const baseReport = computeDataflow(baseGraph, { sources, sinks });
|
|
358
|
-
const headReport = computeDataflow(headGraph, { sources, sinks });
|
|
359
|
-
const filterContext = { customSources: new Set(sources), customSinks: new Set(sinks) };
|
|
360
|
-
if (!headReport.available)
|
|
361
|
-
return [];
|
|
362
|
-
const baseRiskKeys = new Set(baseReport.available ? baseReport.risks.map(reviewDataflowRiskKey) : []);
|
|
363
|
-
const out = [];
|
|
364
|
-
for (const risk of headReport.risks) {
|
|
365
|
-
// Legacy taint flows already have their own stable review field. Keep
|
|
366
|
-
// this additive review list focused on deeper 3.0 dataflow findings.
|
|
367
|
-
if (risk.kind !== 'bridge')
|
|
368
|
-
continue;
|
|
369
|
-
const key = reviewDataflowRiskKey(risk);
|
|
370
|
-
if (baseRiskKeys.has(key))
|
|
371
|
-
continue;
|
|
372
|
-
if (!risk.files.some((f) => touchedFiles.has(f)))
|
|
373
|
-
continue;
|
|
374
|
-
if (!isReviewBlockingDataflowRisk(risk, filterContext))
|
|
375
|
-
continue;
|
|
376
|
-
out.push({
|
|
377
|
-
kind: risk.kind,
|
|
378
|
-
sourceFn: risk.sourceFn,
|
|
379
|
-
sinkFn: risk.sinkFn,
|
|
380
|
-
bridgeFn: risk.bridgeFn,
|
|
381
|
-
source: risk.source,
|
|
382
|
-
sink: risk.sink,
|
|
383
|
-
pathLength: risk.pathLength,
|
|
384
|
-
files: risk.files,
|
|
385
|
-
severity: risk.severity,
|
|
386
|
-
confidence: risk.confidence,
|
|
387
|
-
});
|
|
388
|
-
}
|
|
389
|
-
out.sort((a, b) => {
|
|
390
|
-
if (a.pathLength !== b.pathLength)
|
|
391
|
-
return a.pathLength - b.pathLength;
|
|
392
|
-
return `${a.bridgeFn ?? ''}:${a.sourceFn}:${a.sinkFn}`.localeCompare(`${b.bridgeFn ?? ''}:${b.sourceFn}:${b.sinkFn}`);
|
|
393
|
-
});
|
|
394
|
-
return out;
|
|
395
|
-
}
|
|
396
|
-
function reviewDataflowRiskKey(risk) {
|
|
397
|
-
return `${risk.kind}:${risk.bridgeFn ?? ''}:${risk.sourceFn}:${risk.sinkFn}:${risk.source}:${risk.sink}:${risk.files?.join('|') ?? ''}`;
|
|
398
|
-
}
|
|
399
|
-
// ── cycle classification ──────────────────────────────────
|
|
400
|
-
function scopeCyclesToFiles(cycles, scopeFiles) {
|
|
401
|
-
if (!scopeFiles)
|
|
402
|
-
return cycles;
|
|
403
|
-
return cycles.filter((cycle) => cycle.files.some((file) => scopeFiles.has(file)));
|
|
404
|
-
}
|
|
405
|
-
function classifyNewCycles(baseCycles, headCycles, filesAddedInPr) {
|
|
406
|
-
const added = new Set(filesAddedInPr);
|
|
407
|
-
const out = [];
|
|
408
|
-
for (const head of headCycles) {
|
|
409
|
-
const headSet = new Set(head.files);
|
|
410
|
-
let bestOverlap = 0;
|
|
411
|
-
for (const base of baseCycles) {
|
|
412
|
-
let overlap = 0;
|
|
413
|
-
for (const f of base.files)
|
|
414
|
-
if (headSet.has(f))
|
|
415
|
-
overlap++;
|
|
416
|
-
if (overlap > bestOverlap)
|
|
417
|
-
bestOverlap = overlap;
|
|
418
|
-
}
|
|
419
|
-
if (bestOverlap === 0) {
|
|
420
|
-
out.push({ files: [...head.files].sort(), size: head.files.length, classification: 'new' });
|
|
421
|
-
}
|
|
422
|
-
else if (bestOverlap < head.files.length) {
|
|
423
|
-
// cycle existed but grew
|
|
424
|
-
out.push({
|
|
425
|
-
files: [...head.files].sort(),
|
|
426
|
-
size: head.files.length,
|
|
427
|
-
classification: 'expanded',
|
|
428
|
-
});
|
|
429
|
-
}
|
|
430
|
-
// bestOverlap === head.files.length means the cycle is identical at base.
|
|
431
|
-
}
|
|
432
|
-
// Bump cycles where any file is newly added to the very front.
|
|
433
|
-
out.sort((a, b) => {
|
|
434
|
-
const aTouchesAdded = a.files.some((f) => added.has(f)) ? 0 : 1;
|
|
435
|
-
const bTouchesAdded = b.files.some((f) => added.has(f)) ? 0 : 1;
|
|
436
|
-
if (aTouchesAdded !== bTouchesAdded)
|
|
437
|
-
return aTouchesAdded - bTouchesAdded;
|
|
438
|
-
return b.size - a.size;
|
|
439
|
-
});
|
|
440
|
-
return out;
|
|
441
|
-
}
|
|
442
|
-
// ── risky function detection ──────────────────────────────
|
|
443
|
-
function findRiskyFunctions(baseGraph, headGraph, prDiff) {
|
|
444
|
-
const out = [];
|
|
445
|
-
for (const file of prDiff.filesAdded) {
|
|
446
|
-
const head = headGraph.files.get(file);
|
|
447
|
-
if (!head)
|
|
448
|
-
continue;
|
|
449
|
-
for (const fn of head.functions ?? []) {
|
|
450
|
-
if (fn.cyclomaticComplexity >= HIGH_CC_THRESHOLD) {
|
|
451
|
-
out.push({
|
|
452
|
-
file,
|
|
453
|
-
name: fn.name,
|
|
454
|
-
line: fn.line,
|
|
455
|
-
endLine: fn.endLine,
|
|
456
|
-
cyclomaticComplexity: fn.cyclomaticComplexity,
|
|
457
|
-
baseCc: null,
|
|
458
|
-
reason: 'added',
|
|
459
|
-
});
|
|
460
|
-
}
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
for (const f of prDiff.filesModified) {
|
|
464
|
-
const head = headGraph.files.get(f.relativePath);
|
|
465
|
-
const base = baseGraph.files.get(f.relativePath);
|
|
466
|
-
if (!head || !base)
|
|
467
|
-
continue;
|
|
468
|
-
// Group BOTH sides by name; we need to know the count on each side, not
|
|
469
|
-
// just on base. Many functions can share a name within a file — the
|
|
470
|
-
// dominant case is anonymous arrow callbacks all named '<anonymous>' by
|
|
471
|
-
// ast.ts. A flat Map<name,cc> would collapse them and compare every head
|
|
472
|
-
// <anonymous> to the LAST base <anonymous>, producing false-positive
|
|
473
|
-
// crossed-threshold / jumped rows for every file with ≥2 anonymous arrows
|
|
474
|
-
// of differing CC. Even a 1-base / N-head asymmetry is unsafe: a head
|
|
475
|
-
// arrow that's actually NEWLY ADDED would be paired with the single base
|
|
476
|
-
// arrow's CC and reported as 'crossed-threshold' instead of 'added'.
|
|
477
|
-
const baseByName = new Map();
|
|
478
|
-
for (const fn of base.functions ?? []) {
|
|
479
|
-
let list = baseByName.get(fn.name);
|
|
480
|
-
if (!list) {
|
|
481
|
-
list = [];
|
|
482
|
-
baseByName.set(fn.name, list);
|
|
483
|
-
}
|
|
484
|
-
list.push(fn.cyclomaticComplexity);
|
|
485
|
-
}
|
|
486
|
-
const headCountByName = new Map();
|
|
487
|
-
for (const fn of head.functions ?? []) {
|
|
488
|
-
headCountByName.set(fn.name, (headCountByName.get(fn.name) ?? 0) + 1);
|
|
489
|
-
}
|
|
490
|
-
for (const fn of head.functions ?? []) {
|
|
491
|
-
const candidates = baseByName.get(fn.name);
|
|
492
|
-
if (!candidates || candidates.length === 0) {
|
|
493
|
-
// Truly added (no base function with this name). Flag if high CC.
|
|
494
|
-
if (fn.cyclomaticComplexity >= HIGH_CC_THRESHOLD) {
|
|
495
|
-
out.push({
|
|
496
|
-
file: f.relativePath,
|
|
497
|
-
name: fn.name,
|
|
498
|
-
line: fn.line,
|
|
499
|
-
endLine: fn.endLine,
|
|
500
|
-
cyclomaticComplexity: fn.cyclomaticComplexity,
|
|
501
|
-
baseCc: null,
|
|
502
|
-
reason: 'added',
|
|
503
|
-
});
|
|
504
|
-
}
|
|
505
|
-
continue;
|
|
506
|
-
}
|
|
507
|
-
// Pair head-vs-base only when the name is unambiguous on BOTH sides
|
|
508
|
-
// (1 ↔ 1). Any other ratio (1↔N, N↔1, N↔M) means we can't reliably
|
|
509
|
-
// tell which head fn corresponds to which base fn — typically
|
|
510
|
-
// <anonymous> arrow callbacks with no stable identity. We skip the
|
|
511
|
-
// crossed-threshold / jumped checks in those cases. Regressions that
|
|
512
|
-
// legitimately make one of these high-CC can still surface via other
|
|
513
|
-
// signals (the file's overall risk score and `projscan_hotspots
|
|
514
|
-
// view: functions`).
|
|
515
|
-
if (candidates.length > 1 || (headCountByName.get(fn.name) ?? 0) > 1) {
|
|
516
|
-
continue;
|
|
517
|
-
}
|
|
518
|
-
const baseCc = candidates[0];
|
|
519
|
-
// Existed: flag if it newly crossed the threshold.
|
|
520
|
-
if (baseCc < HIGH_CC_THRESHOLD && fn.cyclomaticComplexity >= HIGH_CC_THRESHOLD) {
|
|
521
|
-
out.push({
|
|
522
|
-
file: f.relativePath,
|
|
523
|
-
name: fn.name,
|
|
524
|
-
line: fn.line,
|
|
525
|
-
endLine: fn.endLine,
|
|
526
|
-
cyclomaticComplexity: fn.cyclomaticComplexity,
|
|
527
|
-
baseCc,
|
|
528
|
-
reason: 'crossed-threshold',
|
|
529
|
-
});
|
|
530
|
-
continue;
|
|
531
|
-
}
|
|
532
|
-
// Or: jumped by JUMP threshold even if both sides under HIGH_CC_THRESHOLD.
|
|
533
|
-
if (fn.cyclomaticComplexity - baseCc >= CC_JUMP_THRESHOLD) {
|
|
534
|
-
out.push({
|
|
535
|
-
file: f.relativePath,
|
|
536
|
-
name: fn.name,
|
|
537
|
-
line: fn.line,
|
|
538
|
-
endLine: fn.endLine,
|
|
539
|
-
cyclomaticComplexity: fn.cyclomaticComplexity,
|
|
540
|
-
baseCc,
|
|
541
|
-
reason: 'jumped',
|
|
542
|
-
});
|
|
543
|
-
}
|
|
544
|
-
}
|
|
545
|
-
}
|
|
546
|
-
out.sort((a, b) => b.cyclomaticComplexity - a.cyclomaticComplexity);
|
|
547
|
-
return out;
|
|
548
|
-
}
|
|
549
|
-
async function readManifests(rootPath) {
|
|
550
|
-
// Use detectWorkspaces to enumerate; if no workspaces just read the root.
|
|
551
|
-
const { detectWorkspaces } = await import('./monorepo.js');
|
|
552
|
-
const ws = await detectWorkspaces(rootPath);
|
|
553
|
-
const out = new Map();
|
|
554
|
-
const all = ws.kind === 'none' ? [] : ws.packages;
|
|
555
|
-
if (all.length === 0) {
|
|
556
|
-
const root = await readOneManifest(rootPath, 'package.json', '');
|
|
557
|
-
if (root)
|
|
558
|
-
out.set('package.json', root);
|
|
559
|
-
return out;
|
|
560
|
-
}
|
|
561
|
-
for (const p of all) {
|
|
562
|
-
const manifestRel = p.relativePath ? `${p.relativePath}/package.json` : 'package.json';
|
|
563
|
-
const dir = path.join(rootPath, p.relativePath);
|
|
564
|
-
const m = await readOneManifest(dir, manifestRel, p.name);
|
|
565
|
-
if (m)
|
|
566
|
-
out.set(manifestRel, m);
|
|
567
|
-
}
|
|
568
|
-
return out;
|
|
569
|
-
}
|
|
570
|
-
async function readOneManifest(dir, manifestFile, workspaceName) {
|
|
571
|
-
const p = path.join(dir, 'package.json');
|
|
572
|
-
let raw;
|
|
573
|
-
try {
|
|
574
|
-
raw = await fs.readFile(p, 'utf-8');
|
|
575
|
-
}
|
|
576
|
-
catch {
|
|
577
|
-
return null;
|
|
578
|
-
}
|
|
579
|
-
let parsed;
|
|
580
|
-
try {
|
|
581
|
-
parsed = JSON.parse(raw);
|
|
582
|
-
}
|
|
583
|
-
catch {
|
|
584
|
-
return null;
|
|
585
|
-
}
|
|
586
|
-
const entrypoints = readEntrypoints(parsed);
|
|
587
|
-
return {
|
|
588
|
-
workspace: workspaceName,
|
|
589
|
-
manifestFile,
|
|
590
|
-
dependencies: parsed.dependencies ?? {},
|
|
591
|
-
devDependencies: parsed.devDependencies ?? {},
|
|
592
|
-
entrypoints,
|
|
593
|
-
entrypointFiles: readEntrypointFiles(parsed),
|
|
594
|
-
};
|
|
595
|
-
}
|
|
596
|
-
function readEntrypoints(parsed) {
|
|
597
|
-
const out = {};
|
|
598
|
-
for (const field of ['main', 'module', 'types', 'exports', 'bin']) {
|
|
599
|
-
const value = parsed[field];
|
|
600
|
-
if (value === undefined)
|
|
601
|
-
continue;
|
|
602
|
-
out[field] = entrypointValue(value);
|
|
603
|
-
}
|
|
604
|
-
return out;
|
|
605
|
-
}
|
|
606
|
-
function entrypointValue(value) {
|
|
607
|
-
return typeof value === 'string' ? value : JSON.stringify(value);
|
|
608
|
-
}
|
|
609
|
-
function diffManifests(base, head) {
|
|
610
|
-
const out = [];
|
|
611
|
-
const allManifests = new Set([...base.keys(), ...head.keys()]);
|
|
612
|
-
for (const manifestFile of allManifests) {
|
|
613
|
-
const b = base.get(manifestFile);
|
|
614
|
-
const h = head.get(manifestFile);
|
|
615
|
-
if (!b && !h)
|
|
616
|
-
continue;
|
|
617
|
-
const change = diffOneManifest(b, h, manifestFile);
|
|
618
|
-
if (change.added.length || change.removed.length || change.bumped.length) {
|
|
619
|
-
out.push(change);
|
|
620
|
-
}
|
|
621
|
-
}
|
|
622
|
-
out.sort((a, b) => a.manifestFile.localeCompare(b.manifestFile));
|
|
623
|
-
return out;
|
|
624
|
-
}
|
|
625
|
-
function buildContractChanges(prDiff, baseGraph, headGraph, baseManifests, headManifests, packageName) {
|
|
626
|
-
const changes = [];
|
|
627
|
-
const scopedBaseManifests = scopeManifestsByPackage(baseManifests, packageName);
|
|
628
|
-
const scopedHeadManifests = scopeManifestsByPackage(headManifests, packageName);
|
|
629
|
-
const publicExportFiles = buildPublicExportFileSet(scopedBaseManifests.values(), scopedHeadManifests.values(), baseGraph, headGraph);
|
|
630
|
-
for (const file of prDiff.filesAdded) {
|
|
631
|
-
if (!publicExportFiles.has(file))
|
|
632
|
-
continue;
|
|
633
|
-
const entry = headGraph.files.get(file);
|
|
634
|
-
for (const exp of entry?.exports ?? []) {
|
|
635
|
-
changes.push(exportContractChange('export-added', file, exp.name));
|
|
636
|
-
}
|
|
637
|
-
}
|
|
638
|
-
for (const file of prDiff.filesRemoved) {
|
|
639
|
-
if (!publicExportFiles.has(file))
|
|
640
|
-
continue;
|
|
641
|
-
const entry = baseGraph.files.get(file);
|
|
642
|
-
for (const exp of entry?.exports ?? []) {
|
|
643
|
-
changes.push(exportContractChange('export-removed', file, exp.name));
|
|
644
|
-
}
|
|
645
|
-
}
|
|
646
|
-
for (const file of prDiff.filesModified) {
|
|
647
|
-
if (!publicExportFiles.has(file.relativePath))
|
|
648
|
-
continue;
|
|
649
|
-
for (const symbol of file.exportsAdded) {
|
|
650
|
-
changes.push(exportContractChange('export-added', file.relativePath, symbol));
|
|
651
|
-
}
|
|
652
|
-
for (const symbol of file.exportsRemoved) {
|
|
653
|
-
changes.push(exportContractChange('export-removed', file.relativePath, symbol));
|
|
654
|
-
}
|
|
655
|
-
for (const rename of file.exportsRenamed) {
|
|
656
|
-
changes.push({
|
|
657
|
-
kind: 'export-renamed',
|
|
658
|
-
file: file.relativePath,
|
|
659
|
-
symbol: rename.to,
|
|
660
|
-
before: rename.from,
|
|
661
|
-
after: rename.to,
|
|
662
|
-
confidence: 'high',
|
|
663
|
-
why: `Export "${rename.from}" was renamed to "${rename.to}" in ${file.relativePath}; downstream imports of the old name can fail at compile time or runtime.`,
|
|
664
|
-
});
|
|
665
|
-
}
|
|
666
|
-
}
|
|
667
|
-
changes.push(...entrypointContractChanges(scopedBaseManifests, scopedHeadManifests));
|
|
668
|
-
return changes;
|
|
669
|
-
}
|
|
670
|
-
function scopeManifestsByPackage(manifests, packageName) {
|
|
671
|
-
if (!packageName)
|
|
672
|
-
return manifests;
|
|
673
|
-
return new Map([...manifests].filter(([, manifest]) => manifest.workspace === packageName));
|
|
674
|
-
}
|
|
675
|
-
function exportContractChange(kind, file, symbol) {
|
|
676
|
-
return {
|
|
677
|
-
kind,
|
|
678
|
-
file,
|
|
679
|
-
symbol,
|
|
680
|
-
confidence: 'high',
|
|
681
|
-
why: kind === 'export-added'
|
|
682
|
-
? `Export "${symbol}" was added in ${file}; downstream code may start depending on a new public API.`
|
|
683
|
-
: `Export "${symbol}" was removed from ${file}; downstream imports can fail at compile time or runtime.`,
|
|
684
|
-
};
|
|
685
|
-
}
|
|
686
|
-
function entrypointContractChanges(base, head) {
|
|
687
|
-
const out = [];
|
|
688
|
-
const allManifests = new Set([...base.keys(), ...head.keys()]);
|
|
689
|
-
for (const manifestFile of allManifests) {
|
|
690
|
-
const baseEntrypoints = base.get(manifestFile)?.entrypoints ?? {};
|
|
691
|
-
const headEntrypoints = head.get(manifestFile)?.entrypoints ?? {};
|
|
692
|
-
const fields = new Set([
|
|
693
|
-
...Object.keys(baseEntrypoints),
|
|
694
|
-
...Object.keys(headEntrypoints),
|
|
695
|
-
]);
|
|
696
|
-
for (const field of fields) {
|
|
697
|
-
const before = baseEntrypoints[field];
|
|
698
|
-
const after = headEntrypoints[field];
|
|
699
|
-
if (before === after)
|
|
700
|
-
continue;
|
|
701
|
-
const kind = field === 'exports' ? 'public-export-changed' : 'entrypoint-changed';
|
|
702
|
-
out.push({
|
|
703
|
-
kind,
|
|
704
|
-
file: manifestFile,
|
|
705
|
-
symbol: field,
|
|
706
|
-
...(before !== undefined ? { before } : {}),
|
|
707
|
-
...(after !== undefined ? { after } : {}),
|
|
708
|
-
confidence: 'high',
|
|
709
|
-
why: kind === 'public-export-changed'
|
|
710
|
-
? `${manifestFile} package "exports" changed; consumers may resolve different public modules.`
|
|
711
|
-
: `${manifestFile} package "${field}" changed from ${before ?? '<unset>'} to ${after ?? '<unset>'}; package consumers may load a different entrypoint.`,
|
|
712
|
-
});
|
|
713
|
-
}
|
|
714
|
-
}
|
|
715
|
-
return out.sort((a, b) => `${a.file}:${a.symbol ?? ''}`.localeCompare(`${b.file}:${b.symbol ?? ''}`));
|
|
716
|
-
}
|
|
717
|
-
function diffOneManifest(base, head, manifestFile) {
|
|
718
|
-
const workspace = head?.workspace ?? base?.workspace ?? '';
|
|
719
|
-
const baseDeps = base?.dependencies ?? {};
|
|
720
|
-
const baseDev = base?.devDependencies ?? {};
|
|
721
|
-
const headDeps = head?.dependencies ?? {};
|
|
722
|
-
const headDev = head?.devDependencies ?? {};
|
|
723
|
-
const added = [];
|
|
724
|
-
const removed = [];
|
|
725
|
-
const bumped = [];
|
|
726
|
-
for (const [name, version] of Object.entries(headDeps)) {
|
|
727
|
-
if (!(name in baseDeps))
|
|
728
|
-
added.push({ name, version, kind: 'dep' });
|
|
729
|
-
else if (baseDeps[name] !== version)
|
|
730
|
-
bumped.push({ name, from: baseDeps[name], to: version, kind: 'dep' });
|
|
731
|
-
}
|
|
732
|
-
for (const [name, version] of Object.entries(baseDeps)) {
|
|
733
|
-
if (!(name in headDeps))
|
|
734
|
-
removed.push({ name, version, kind: 'dep' });
|
|
735
|
-
}
|
|
736
|
-
for (const [name, version] of Object.entries(headDev)) {
|
|
737
|
-
if (!(name in baseDev))
|
|
738
|
-
added.push({ name, version, kind: 'dev' });
|
|
739
|
-
else if (baseDev[name] !== version)
|
|
740
|
-
bumped.push({ name, from: baseDev[name], to: version, kind: 'dev' });
|
|
741
|
-
}
|
|
742
|
-
for (const [name, version] of Object.entries(baseDev)) {
|
|
743
|
-
if (!(name in headDev))
|
|
744
|
-
removed.push({ name, version, kind: 'dev' });
|
|
745
|
-
}
|
|
746
|
-
added.sort((a, b) => a.name.localeCompare(b.name));
|
|
747
|
-
removed.sort((a, b) => a.name.localeCompare(b.name));
|
|
748
|
-
bumped.sort((a, b) => a.name.localeCompare(b.name));
|
|
749
|
-
return { workspace, manifestFile, added, removed, bumped };
|
|
750
|
-
}
|
|
751
|
-
// ── verdict ───────────────────────────────────────────────
|
|
752
|
-
function decideVerdict(changedFiles, newCycles, riskyFunctions, depChanges, contractChanges, newTaintFlows, newDataflowRisks) {
|
|
753
|
-
const summary = [];
|
|
754
|
-
let verdict = 'ok';
|
|
755
|
-
const maxRisk = Math.max(0, ...changedFiles.map((f) => f.riskScore ?? 0));
|
|
756
|
-
if (maxRisk >= RISK_VERDICT_BLOCK_SCORE) {
|
|
757
|
-
verdict = 'block';
|
|
758
|
-
summary.push(`Maximum changed-file risk score is ${maxRisk.toFixed(1)} (>= ${RISK_VERDICT_BLOCK_SCORE}).`);
|
|
759
|
-
}
|
|
760
|
-
else if (maxRisk >= RISK_VERDICT_REVIEW_SCORE) {
|
|
761
|
-
verdict = bumpTo(verdict, 'review');
|
|
762
|
-
summary.push(`Maximum changed-file risk score is ${maxRisk.toFixed(1)} (>= ${RISK_VERDICT_REVIEW_SCORE}).`);
|
|
763
|
-
}
|
|
764
|
-
if (newCycles.length > 0) {
|
|
765
|
-
const newOnly = newCycles.filter((c) => c.classification === 'new');
|
|
766
|
-
if (newOnly.length > 0) {
|
|
767
|
-
verdict = 'block';
|
|
768
|
-
summary.push(`${newOnly.length} new import cycle(s) introduced.`);
|
|
769
|
-
}
|
|
770
|
-
else {
|
|
771
|
-
verdict = bumpTo(verdict, 'review');
|
|
772
|
-
summary.push(`${newCycles.length} cycle(s) expanded.`);
|
|
773
|
-
}
|
|
774
|
-
}
|
|
775
|
-
if (riskyFunctions.length > 0) {
|
|
776
|
-
verdict = bumpTo(verdict, 'review');
|
|
777
|
-
summary.push(`${riskyFunctions.length} function(s) flagged: high CC added or jumped.`);
|
|
778
|
-
}
|
|
779
|
-
if (newTaintFlows.length > 0) {
|
|
780
|
-
verdict = 'block';
|
|
781
|
-
const sample = newTaintFlows
|
|
782
|
-
.slice(0, 3)
|
|
783
|
-
.map((f) => `${f.source}→${f.sink} (${f.sourceFn}${f.pathLength > 1 ? '…' : ''})`)
|
|
784
|
-
.join(', ');
|
|
785
|
-
summary.push(`${newTaintFlows.length} new taint flow(s) detected: ${sample}${newTaintFlows.length > 3 ? ', …' : ''}.`);
|
|
786
|
-
}
|
|
787
|
-
if (newDataflowRisks.length > 0) {
|
|
788
|
-
verdict = 'block';
|
|
789
|
-
const sample = newDataflowRisks
|
|
790
|
-
.slice(0, 3)
|
|
791
|
-
.map((risk) => `${risk.source}→${risk.sink} (${risk.bridgeFn ?? risk.sourceFn})`)
|
|
792
|
-
.join(', ');
|
|
793
|
-
summary.push(`${newDataflowRisks.length} new dataflow risk(s) detected: ${sample}${newDataflowRisks.length > 3 ? ', …' : ''}.`);
|
|
794
|
-
}
|
|
795
|
-
if (depChanges.length > 0) {
|
|
796
|
-
const totals = depChanges.reduce((acc, d) => {
|
|
797
|
-
acc.added += d.added.length;
|
|
798
|
-
acc.removed += d.removed.length;
|
|
799
|
-
acc.bumped += d.bumped.length;
|
|
800
|
-
return acc;
|
|
801
|
-
}, { added: 0, removed: 0, bumped: 0 });
|
|
802
|
-
if (totals.added + totals.removed + totals.bumped > 0) {
|
|
803
|
-
summary.push(`Dependency changes: +${totals.added} -${totals.removed} ~${totals.bumped}.`);
|
|
804
|
-
}
|
|
805
|
-
}
|
|
806
|
-
if (isManualReleaseSignOffBlock(verdict, maxRisk, newCycles, riskyFunctions, contractChanges, newTaintFlows, newDataflowRisks)) {
|
|
807
|
-
summary.push('Manual release sign-off required: review blocks on release-scale risk signals, not concrete cycle, risky-function, contract, taint, or dataflow defects.');
|
|
808
|
-
}
|
|
809
|
-
if (changedFiles.length === 0 && summary.length === 0) {
|
|
810
|
-
summary.push('No structural changes detected between base and head.');
|
|
811
|
-
}
|
|
812
|
-
else if (verdict === 'ok' && summary.length === 0) {
|
|
813
|
-
summary.push(`${changedFiles.length} file(s) changed; no risk signals.`);
|
|
814
|
-
}
|
|
815
|
-
return { verdict, summary };
|
|
816
|
-
}
|
|
817
|
-
function isManualReleaseSignOffBlock(verdict, maxRisk, newCycles, riskyFunctions, contractChanges, newTaintFlows, newDataflowRisks) {
|
|
818
|
-
const concreteSignals = [
|
|
819
|
-
newCycles,
|
|
820
|
-
riskyFunctions,
|
|
821
|
-
contractChanges,
|
|
822
|
-
newTaintFlows,
|
|
823
|
-
newDataflowRisks,
|
|
824
|
-
];
|
|
825
|
-
return (verdict === 'block' &&
|
|
826
|
-
maxRisk >= RISK_VERDICT_BLOCK_SCORE &&
|
|
827
|
-
concreteSignals.every((signals) => signals.length === 0));
|
|
828
|
-
}
|
|
829
|
-
function bumpTo(current, target) {
|
|
830
|
-
const order = { ok: 0, review: 1, block: 2 };
|
|
831
|
-
return order[target] > order[current] ? target : current;
|
|
832
|
-
}
|
|
833
|
-
// ── git helpers (mirror prDiff.ts; kept private to keep coupling low) ──
|
|
834
|
-
function unavailable(reason, options, baseRef = options.base ?? '', headRef = options.head ?? 'HEAD', headSha = null) {
|
|
835
|
-
return {
|
|
836
|
-
available: false,
|
|
837
|
-
reason,
|
|
838
|
-
base: { ref: baseRef, resolvedSha: null },
|
|
839
|
-
head: { ref: headRef, resolvedSha: headSha },
|
|
840
|
-
prDiff: {
|
|
841
|
-
available: false,
|
|
842
|
-
reason,
|
|
843
|
-
base: { ref: baseRef, resolvedSha: null },
|
|
844
|
-
head: { ref: headRef, resolvedSha: headSha },
|
|
845
|
-
filesAdded: [],
|
|
846
|
-
filesRemoved: [],
|
|
847
|
-
filesModified: [],
|
|
848
|
-
totalFilesChanged: 0,
|
|
849
|
-
},
|
|
850
|
-
changedFiles: [],
|
|
851
|
-
newCycles: [],
|
|
852
|
-
riskyFunctions: [],
|
|
853
|
-
dependencyChanges: [],
|
|
854
|
-
newTaintFlows: [],
|
|
855
|
-
newDataflowRisks: [],
|
|
856
|
-
verdict: 'ok',
|
|
857
|
-
summary: [reason],
|
|
858
|
-
};
|
|
859
|
-
}
|
|
860
|
-
async function isGitRepository(rootPath) {
|
|
861
|
-
const { code } = await runGit(rootPath, ['rev-parse', '--is-inside-work-tree']).catch(() => ({
|
|
862
|
-
code: 1,
|
|
863
|
-
stdout: '',
|
|
864
|
-
stderr: '',
|
|
865
|
-
}));
|
|
866
|
-
return code === 0;
|
|
867
|
-
}
|
|
868
|
-
async function isWorktreeClean(rootPath) {
|
|
869
|
-
const unstaged = await runGit(rootPath, ['diff', '--quiet', '--ignore-submodules', '--']).catch(() => ({
|
|
870
|
-
code: 1,
|
|
871
|
-
stdout: '',
|
|
872
|
-
stderr: '',
|
|
873
|
-
}));
|
|
874
|
-
if (unstaged.code !== 0)
|
|
875
|
-
return false;
|
|
876
|
-
const staged = await runGit(rootPath, [
|
|
877
|
-
'diff',
|
|
878
|
-
'--cached',
|
|
879
|
-
'--quiet',
|
|
880
|
-
'--ignore-submodules',
|
|
881
|
-
'--',
|
|
882
|
-
]).catch(() => ({
|
|
883
|
-
code: 1,
|
|
884
|
-
stdout: '',
|
|
885
|
-
stderr: '',
|
|
886
|
-
}));
|
|
887
|
-
if (staged.code !== 0)
|
|
888
|
-
return false;
|
|
889
|
-
const untracked = await runGit(rootPath, ['ls-files', '--others', '--exclude-standard']).catch(() => ({
|
|
890
|
-
code: 1,
|
|
891
|
-
stdout: '',
|
|
892
|
-
stderr: '',
|
|
893
|
-
}));
|
|
894
|
-
return untracked.code === 0 && untracked.stdout.trim().length === 0;
|
|
895
|
-
}
|
|
896
|
-
async function resolveSha(rootPath, ref) {
|
|
897
|
-
const { code, stdout } = await runGit(rootPath, [
|
|
898
|
-
'rev-parse',
|
|
899
|
-
'--verify',
|
|
900
|
-
`${ref}^{commit}`,
|
|
901
|
-
]).catch(() => ({ code: 1, stdout: '', stderr: '' }));
|
|
902
|
-
if (code !== 0)
|
|
903
|
-
return null;
|
|
904
|
-
const sha = stdout.trim();
|
|
905
|
-
return sha || null;
|
|
906
|
-
}
|
|
907
|
-
async function pickDefaultBase(rootPath) {
|
|
908
|
-
for (const candidate of ['origin/main', 'main', 'origin/master', 'master']) {
|
|
909
|
-
if (await resolveSha(rootPath, candidate))
|
|
910
|
-
return candidate;
|
|
911
|
-
}
|
|
912
|
-
return 'HEAD~1';
|
|
913
|
-
}
|
|
914
|
-
async function mkTempWorktreeDir() {
|
|
915
|
-
return fs.mkdtemp(path.join(os.tmpdir(), 'projscan-review-'));
|
|
916
|
-
}
|
|
917
|
-
function gitFailureSummary(result) {
|
|
918
|
-
const message = (result.stderr || result.stdout).trim().replace(/\s+/g, ' ');
|
|
919
|
-
return message || `git exited with code ${result.code}`;
|
|
920
|
-
}
|
|
921
|
-
/**
|
|
922
|
-
* 1.9+ — Default cap on any single `git` invocation made from the
|
|
923
|
-
* review pipeline (worktree add/remove, rev-parse, etc.). Without it
|
|
924
|
-
* a hung git operation (credential prompt, blocking hook, dead remote)
|
|
925
|
-
* would hang the MCP server until kill. Mirror of prDiff.ts's same
|
|
926
|
-
* default; kept as a sibling rather than shared because runGit here
|
|
927
|
-
* is intentionally minimal and `prDiff.runGit` carries extra
|
|
928
|
-
* timeoutMs override plumbing that this caller doesn't need.
|
|
929
|
-
*/
|
|
930
|
-
const DEFAULT_GIT_TIMEOUT_MS = 30_000;
|
|
931
|
-
function runGit(cwd, args) {
|
|
932
|
-
return new Promise((resolve, reject) => {
|
|
933
|
-
// Detach stdin so credential prompts / interactive hooks see EOF
|
|
934
|
-
// and exit instead of waiting forever.
|
|
935
|
-
const child = spawn('git', args, { cwd, env: process.env, stdio: ['ignore', 'pipe', 'pipe'] });
|
|
936
|
-
let stdout = '';
|
|
937
|
-
let stderr = '';
|
|
938
|
-
let settled = false;
|
|
939
|
-
const timer = setTimeout(() => {
|
|
940
|
-
if (settled)
|
|
941
|
-
return;
|
|
942
|
-
settled = true;
|
|
943
|
-
child.kill('SIGKILL');
|
|
944
|
-
reject(new Error(`git command timed out after ${DEFAULT_GIT_TIMEOUT_MS}ms`));
|
|
945
|
-
}, DEFAULT_GIT_TIMEOUT_MS);
|
|
946
|
-
child.stdout.on('data', (d) => (stdout += d.toString()));
|
|
947
|
-
child.stderr.on('data', (d) => (stderr += d.toString()));
|
|
948
|
-
child.on('error', (err) => {
|
|
949
|
-
if (settled)
|
|
950
|
-
return;
|
|
951
|
-
settled = true;
|
|
952
|
-
clearTimeout(timer);
|
|
953
|
-
reject(err);
|
|
954
|
-
});
|
|
955
|
-
child.on('close', (code) => {
|
|
956
|
-
if (settled)
|
|
957
|
-
return;
|
|
958
|
-
settled = true;
|
|
959
|
-
clearTimeout(timer);
|
|
960
|
-
resolve({ code: code ?? 1, stdout, stderr });
|
|
961
|
-
});
|
|
962
|
-
});
|
|
963
|
-
}
|
|
964
|
-
/**
|
|
965
|
-
* 1.5+ — pick a review tier based on the caller's token budget.
|
|
966
|
-
*
|
|
967
|
-
* <3000 → 'verdict-only' (verdict + summary + totals)
|
|
968
|
-
* <7000 → 'summary' (verdict + summary + top files / top cycles / etc.)
|
|
969
|
-
* else → 'full' (everything)
|
|
970
|
-
*
|
|
971
|
-
* `0`, `undefined`, and any non-positive value all mean "no budget given"
|
|
972
|
-
* — the caller wants the full report. The tier names are stable (clients
|
|
973
|
-
* can read them off the response and key behavior off them).
|
|
974
|
-
*/
|
|
975
|
-
export function selectReviewTier(maxCostTokens) {
|
|
976
|
-
if (typeof maxCostTokens !== 'number' || !Number.isFinite(maxCostTokens) || maxCostTokens <= 0) {
|
|
977
|
-
return 'full';
|
|
978
|
-
}
|
|
979
|
-
if (maxCostTokens < 3000)
|
|
980
|
-
return 'verdict-only';
|
|
981
|
-
if (maxCostTokens < 7000)
|
|
982
|
-
return 'summary';
|
|
983
|
-
return 'full';
|
|
984
|
-
}
|
|
985
|
-
/**
|
|
986
|
-
* Reshape a full ReviewReport for the chosen tier. The caller passes a
|
|
987
|
-
* fully-populated report from `computeReview`; we return a plain object
|
|
988
|
-
* sized for the tier. Returning `Record<string, unknown>` (rather than
|
|
989
|
-
* narrowing the ReviewReport type) keeps the type contract simple for
|
|
990
|
-
* the dispatcher and avoids an over-engineered union.
|
|
991
|
-
*
|
|
992
|
-
* `unavailable` reports (no diff, missing base, etc.) pass through as-is
|
|
993
|
-
* — there's nothing to shape; the verdict + reason already convey
|
|
994
|
-
* everything the agent needs.
|
|
995
|
-
*/
|
|
996
|
-
export function shapeReviewForTier(report, tier) {
|
|
997
|
-
if (!report.available || tier === 'full') {
|
|
998
|
-
return { ...report, tier };
|
|
999
|
-
}
|
|
1000
|
-
const filesChanged = report.changedFiles.length;
|
|
1001
|
-
const cyclesAdded = report.newCycles.length;
|
|
1002
|
-
const riskyFunctionsAdded = report.riskyFunctions.length;
|
|
1003
|
-
const depsChanged = report.dependencyChanges.length;
|
|
1004
|
-
const taintFlowsAdded = report.newTaintFlows?.length ?? 0;
|
|
1005
|
-
const dataflowRisksAdded = report.newDataflowRisks?.length ?? 0;
|
|
1006
|
-
const contractChanges = report.contractChanges?.length ?? 0;
|
|
1007
|
-
const totals = {
|
|
1008
|
-
filesChanged,
|
|
1009
|
-
cyclesAdded,
|
|
1010
|
-
riskyFunctionsAdded,
|
|
1011
|
-
depsChanged,
|
|
1012
|
-
taintFlowsAdded,
|
|
1013
|
-
dataflowRisksAdded,
|
|
1014
|
-
contractChanges,
|
|
1015
|
-
};
|
|
1016
|
-
if (tier === 'verdict-only') {
|
|
1017
|
-
return {
|
|
1018
|
-
available: report.available,
|
|
1019
|
-
base: report.base,
|
|
1020
|
-
head: report.head,
|
|
1021
|
-
verdict: report.verdict,
|
|
1022
|
-
summary: report.summary,
|
|
1023
|
-
totals,
|
|
1024
|
-
graphEvidence: report.graphEvidence,
|
|
1025
|
-
tier,
|
|
1026
|
-
};
|
|
1027
|
-
}
|
|
1028
|
-
// summary tier: keep the verdict, the top-N of each list, and aggregate totals.
|
|
1029
|
-
// Drop per-file expansion lists in prDiff that bloat the response.
|
|
1030
|
-
const TOP = 5;
|
|
1031
|
-
const trimmedPrDiff = {
|
|
1032
|
-
available: report.prDiff.available,
|
|
1033
|
-
base: report.prDiff.base,
|
|
1034
|
-
head: report.prDiff.head,
|
|
1035
|
-
totalFilesChanged: report.prDiff.totalFilesChanged,
|
|
1036
|
-
filesAdded: report.prDiff.filesAdded.slice(0, TOP),
|
|
1037
|
-
filesRemoved: report.prDiff.filesRemoved.slice(0, TOP),
|
|
1038
|
-
filesModified: report.prDiff.filesModified.slice(0, TOP).map((f) => ({
|
|
1039
|
-
relativePath: f.relativePath,
|
|
1040
|
-
// Keep the deltas; drop the heavy added/removed export & import arrays.
|
|
1041
|
-
cyclomaticDelta: f.cyclomaticDelta,
|
|
1042
|
-
fanInDelta: f.fanInDelta,
|
|
1043
|
-
})),
|
|
1044
|
-
};
|
|
1045
|
-
return {
|
|
1046
|
-
available: report.available,
|
|
1047
|
-
base: report.base,
|
|
1048
|
-
head: report.head,
|
|
1049
|
-
prDiff: trimmedPrDiff,
|
|
1050
|
-
changedFiles: report.changedFiles.slice(0, TOP),
|
|
1051
|
-
newCycles: report.newCycles.slice(0, 3),
|
|
1052
|
-
riskyFunctions: report.riskyFunctions.slice(0, 3),
|
|
1053
|
-
dependencyChanges: report.dependencyChanges.slice(0, 3),
|
|
1054
|
-
contractChanges: report.contractChanges?.slice(0, TOP) ?? [],
|
|
1055
|
-
newTaintFlows: report.newTaintFlows?.slice(0, 5) ?? [],
|
|
1056
|
-
newDataflowRisks: report.newDataflowRisks?.slice(0, 5) ?? [],
|
|
1057
|
-
graphEvidence: report.graphEvidence,
|
|
1058
|
-
verdict: report.verdict,
|
|
1059
|
-
summary: report.summary,
|
|
1060
|
-
totals,
|
|
1061
|
-
tier,
|
|
1062
|
-
};
|
|
1063
|
-
}
|
|
1064
64
|
//# sourceMappingURL=review.js.map
|