memor-code-cli 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +109 -0
- package/dist/IntentionalStructure/index.d.ts +13 -0
- package/dist/IntentionalStructure/index.d.ts.map +1 -0
- package/dist/IntentionalStructure/index.js +46 -0
- package/dist/IntentionalStructure/index.js.map +1 -0
- package/dist/IntentionalStructure/parseCompose.d.ts +13 -0
- package/dist/IntentionalStructure/parseCompose.d.ts.map +1 -0
- package/dist/IntentionalStructure/parseCompose.js +234 -0
- package/dist/IntentionalStructure/parseCompose.js.map +1 -0
- package/dist/IntentionalStructure/parseDeployConfigs.d.ts +7 -0
- package/dist/IntentionalStructure/parseDeployConfigs.d.ts.map +1 -0
- package/dist/IntentionalStructure/parseDeployConfigs.js +168 -0
- package/dist/IntentionalStructure/parseDeployConfigs.js.map +1 -0
- package/dist/IntentionalStructure/parseEnvWiring.d.ts +7 -0
- package/dist/IntentionalStructure/parseEnvWiring.d.ts.map +1 -0
- package/dist/IntentionalStructure/parseEnvWiring.js +159 -0
- package/dist/IntentionalStructure/parseEnvWiring.js.map +1 -0
- package/dist/IntentionalStructure/parseWorkspaces.d.ts +8 -0
- package/dist/IntentionalStructure/parseWorkspaces.d.ts.map +1 -0
- package/dist/IntentionalStructure/parseWorkspaces.js +179 -0
- package/dist/IntentionalStructure/parseWorkspaces.js.map +1 -0
- package/dist/IntentionalStructure/types.d.ts +34 -0
- package/dist/IntentionalStructure/types.d.ts.map +1 -0
- package/dist/IntentionalStructure/types.js +8 -0
- package/dist/IntentionalStructure/types.js.map +1 -0
- package/dist/RepoTypeDetection/applyRepoModeConsistency.d.ts +11 -0
- package/dist/RepoTypeDetection/applyRepoModeConsistency.d.ts.map +1 -0
- package/dist/RepoTypeDetection/applyRepoModeConsistency.js +53 -0
- package/dist/RepoTypeDetection/applyRepoModeConsistency.js.map +1 -0
- package/dist/RepoTypeDetection/detectAppArchetype.d.ts +12 -0
- package/dist/RepoTypeDetection/detectAppArchetype.d.ts.map +1 -0
- package/dist/RepoTypeDetection/detectAppArchetype.js +162 -0
- package/dist/RepoTypeDetection/detectAppArchetype.js.map +1 -0
- package/dist/RepoTypeDetection/detectPackageArchetype.d.ts +10 -0
- package/dist/RepoTypeDetection/detectPackageArchetype.d.ts.map +1 -0
- package/dist/RepoTypeDetection/detectPackageArchetype.js +149 -0
- package/dist/RepoTypeDetection/detectPackageArchetype.js.map +1 -0
- package/dist/RepoTypeDetection/detectRepoCenterSystems.d.ts +11 -0
- package/dist/RepoTypeDetection/detectRepoCenterSystems.d.ts.map +1 -0
- package/dist/RepoTypeDetection/detectRepoCenterSystems.js +123 -0
- package/dist/RepoTypeDetection/detectRepoCenterSystems.js.map +1 -0
- package/dist/RepoTypeDetection/detectRepoMode.d.ts +16 -0
- package/dist/RepoTypeDetection/detectRepoMode.d.ts.map +1 -0
- package/dist/RepoTypeDetection/detectRepoMode.js +338 -0
- package/dist/RepoTypeDetection/detectRepoMode.js.map +1 -0
- package/dist/RepoTypeDetection/detectRepoSignals.d.ts +13 -0
- package/dist/RepoTypeDetection/detectRepoSignals.d.ts.map +1 -0
- package/dist/RepoTypeDetection/detectRepoSignals.js +451 -0
- package/dist/RepoTypeDetection/detectRepoSignals.js.map +1 -0
- package/dist/RepoTypeDetection/inferSupportRole.d.ts +8 -0
- package/dist/RepoTypeDetection/inferSupportRole.d.ts.map +1 -0
- package/dist/RepoTypeDetection/inferSupportRole.js +177 -0
- package/dist/RepoTypeDetection/inferSupportRole.js.map +1 -0
- package/dist/RuntimeInference/detectEnvConsumers.d.ts +7 -0
- package/dist/RuntimeInference/detectEnvConsumers.d.ts.map +1 -0
- package/dist/RuntimeInference/detectEnvConsumers.js +108 -0
- package/dist/RuntimeInference/detectEnvConsumers.js.map +1 -0
- package/dist/RuntimeInference/detectHttpEdges.d.ts +4 -0
- package/dist/RuntimeInference/detectHttpEdges.d.ts.map +1 -0
- package/dist/RuntimeInference/detectHttpEdges.js +146 -0
- package/dist/RuntimeInference/detectHttpEdges.js.map +1 -0
- package/dist/RuntimeInference/detectOrmConsumers.d.ts +5 -0
- package/dist/RuntimeInference/detectOrmConsumers.d.ts.map +1 -0
- package/dist/RuntimeInference/detectOrmConsumers.js +117 -0
- package/dist/RuntimeInference/detectOrmConsumers.js.map +1 -0
- package/dist/RuntimeInference/detectProxyRewrites.d.ts +5 -0
- package/dist/RuntimeInference/detectProxyRewrites.d.ts.map +1 -0
- package/dist/RuntimeInference/detectProxyRewrites.js +137 -0
- package/dist/RuntimeInference/detectProxyRewrites.js.map +1 -0
- package/dist/RuntimeInference/detectTrpcEdges.d.ts +4 -0
- package/dist/RuntimeInference/detectTrpcEdges.d.ts.map +1 -0
- package/dist/RuntimeInference/detectTrpcEdges.js +137 -0
- package/dist/RuntimeInference/detectTrpcEdges.js.map +1 -0
- package/dist/RuntimeInference/index.d.ts +17 -0
- package/dist/RuntimeInference/index.d.ts.map +1 -0
- package/dist/RuntimeInference/index.js +32 -0
- package/dist/RuntimeInference/index.js.map +1 -0
- package/dist/RuntimeInference/types.d.ts +8 -0
- package/dist/RuntimeInference/types.d.ts.map +1 -0
- package/dist/RuntimeInference/types.js +3 -0
- package/dist/RuntimeInference/types.js.map +1 -0
- package/dist/SystemSynthesis/index.d.ts +27 -0
- package/dist/SystemSynthesis/index.d.ts.map +1 -0
- package/dist/SystemSynthesis/index.js +31 -0
- package/dist/SystemSynthesis/index.js.map +1 -0
- package/dist/SystemSynthesis/mergeLayerOutputs.d.ts +13 -0
- package/dist/SystemSynthesis/mergeLayerOutputs.d.ts.map +1 -0
- package/dist/SystemSynthesis/mergeLayerOutputs.js +220 -0
- package/dist/SystemSynthesis/mergeLayerOutputs.js.map +1 -0
- package/dist/SystemSynthesis/stampDeterministic.d.ts +11 -0
- package/dist/SystemSynthesis/stampDeterministic.d.ts.map +1 -0
- package/dist/SystemSynthesis/stampDeterministic.js +177 -0
- package/dist/SystemSynthesis/stampDeterministic.js.map +1 -0
- package/dist/SystemSynthesis/synthesize.d.ts +15 -0
- package/dist/SystemSynthesis/synthesize.d.ts.map +1 -0
- package/dist/SystemSynthesis/synthesize.js +258 -0
- package/dist/SystemSynthesis/synthesize.js.map +1 -0
- package/dist/SystemSynthesis/types.d.ts +110 -0
- package/dist/SystemSynthesis/types.d.ts.map +1 -0
- package/dist/SystemSynthesis/types.js +12 -0
- package/dist/SystemSynthesis/types.js.map +1 -0
- package/dist/amGeneration/buildFlowGraph.d.ts +39 -0
- package/dist/amGeneration/buildFlowGraph.d.ts.map +1 -0
- package/dist/amGeneration/buildFlowGraph.js +643 -0
- package/dist/amGeneration/buildFlowGraph.js.map +1 -0
- package/dist/amGeneration/enrichAMEdges.d.ts +16 -0
- package/dist/amGeneration/enrichAMEdges.d.ts.map +1 -0
- package/dist/amGeneration/enrichAMEdges.js +291 -0
- package/dist/amGeneration/enrichAMEdges.js.map +1 -0
- package/dist/amGeneration/generateAM.d.ts +23 -0
- package/dist/amGeneration/generateAM.d.ts.map +1 -0
- package/dist/amGeneration/generateAM.js +131 -0
- package/dist/amGeneration/generateAM.js.map +1 -0
- package/dist/amGeneration/generateAMForRepo.d.ts +6 -0
- package/dist/amGeneration/generateAMForRepo.d.ts.map +1 -0
- package/dist/amGeneration/generateAMForRepo.js +130 -0
- package/dist/amGeneration/generateAMForRepo.js.map +1 -0
- package/dist/amGeneration/generateFlows.d.ts +27 -0
- package/dist/amGeneration/generateFlows.d.ts.map +1 -0
- package/dist/amGeneration/generateFlows.js +320 -0
- package/dist/amGeneration/generateFlows.js.map +1 -0
- package/dist/amGeneration/promptBuilder.d.ts +24 -0
- package/dist/amGeneration/promptBuilder.d.ts.map +1 -0
- package/dist/amGeneration/promptBuilder.js +299 -0
- package/dist/amGeneration/promptBuilder.js.map +1 -0
- package/dist/amGeneration/selectStrategicFiles.d.ts +42 -0
- package/dist/amGeneration/selectStrategicFiles.d.ts.map +1 -0
- package/dist/amGeneration/selectStrategicFiles.js +672 -0
- package/dist/amGeneration/selectStrategicFiles.js.map +1 -0
- package/dist/amGeneration/types.d.ts +83 -0
- package/dist/amGeneration/types.d.ts.map +1 -0
- package/dist/amGeneration/types.js +9 -0
- package/dist/amGeneration/types.js.map +1 -0
- package/dist/amSections.d.ts +29 -0
- package/dist/amSections.d.ts.map +1 -0
- package/dist/amSections.js +424 -0
- package/dist/amSections.js.map +1 -0
- package/dist/analysis/analysisCache.d.ts +4 -0
- package/dist/analysis/analysisCache.d.ts.map +1 -0
- package/dist/analysis/analysisCache.js +92 -0
- package/dist/analysis/analysisCache.js.map +1 -0
- package/dist/analysis/buildBranchStory.d.ts +95 -0
- package/dist/analysis/buildBranchStory.d.ts.map +1 -0
- package/dist/analysis/buildBranchStory.js +1264 -0
- package/dist/analysis/buildBranchStory.js.map +1 -0
- package/dist/analysis/buildDetailedFileXRay.d.ts +49 -0
- package/dist/analysis/buildDetailedFileXRay.d.ts.map +1 -0
- package/dist/analysis/buildDetailedFileXRay.js +607 -0
- package/dist/analysis/buildDetailedFileXRay.js.map +1 -0
- package/dist/analysis/buildFileXRay.d.ts +35 -0
- package/dist/analysis/buildFileXRay.d.ts.map +1 -0
- package/dist/analysis/buildFileXRay.js +305 -0
- package/dist/analysis/buildFileXRay.js.map +1 -0
- package/dist/analysis/classifySilentKiller.d.ts +14 -0
- package/dist/analysis/classifySilentKiller.d.ts.map +1 -0
- package/dist/analysis/classifySilentKiller.js +235 -0
- package/dist/analysis/classifySilentKiller.js.map +1 -0
- package/dist/analysis/diffChunks.d.ts +21 -0
- package/dist/analysis/diffChunks.d.ts.map +1 -0
- package/dist/analysis/diffChunks.js +302 -0
- package/dist/analysis/diffChunks.js.map +1 -0
- package/dist/analysis/extractRouteMap.d.ts +49 -0
- package/dist/analysis/extractRouteMap.d.ts.map +1 -0
- package/dist/analysis/extractRouteMap.js +354 -0
- package/dist/analysis/extractRouteMap.js.map +1 -0
- package/dist/analysis/generateFileInsight.d.ts +19 -0
- package/dist/analysis/generateFileInsight.d.ts.map +1 -0
- package/dist/analysis/generateFileInsight.js +103 -0
- package/dist/analysis/generateFileInsight.js.map +1 -0
- package/dist/analysis/llmXRay.d.ts +39 -0
- package/dist/analysis/llmXRay.d.ts.map +1 -0
- package/dist/analysis/llmXRay.js +208 -0
- package/dist/analysis/llmXRay.js.map +1 -0
- package/dist/analysis/simulateFailure.d.ts +44 -0
- package/dist/analysis/simulateFailure.d.ts.map +1 -0
- package/dist/analysis/simulateFailure.js +407 -0
- package/dist/analysis/simulateFailure.js.map +1 -0
- package/dist/anthropic.d.ts +3 -0
- package/dist/anthropic.d.ts.map +1 -0
- package/dist/anthropic.js +16 -0
- package/dist/anthropic.js.map +1 -0
- package/dist/app/buildAppPage.d.ts +34 -0
- package/dist/app/buildAppPage.d.ts.map +1 -0
- package/dist/app/buildAppPage.js +3085 -0
- package/dist/app/buildAppPage.js.map +1 -0
- package/dist/app-bundle.js +122 -0
- package/dist/buildAppData.d.ts +3 -0
- package/dist/buildAppData.d.ts.map +1 -0
- package/dist/buildAppData.js +207 -0
- package/dist/buildAppData.js.map +1 -0
- package/dist/builders/analyzeRepo.d.ts +25 -0
- package/dist/builders/analyzeRepo.d.ts.map +1 -0
- package/dist/builders/analyzeRepo.js +873 -0
- package/dist/builders/analyzeRepo.js.map +1 -0
- package/dist/builders/buildSystemConnections.d.ts +3 -0
- package/dist/builders/buildSystemConnections.d.ts.map +1 -0
- package/dist/builders/buildSystemConnections.js +388 -0
- package/dist/builders/buildSystemConnections.js.map +1 -0
- package/dist/builders/buildTextSummary.d.ts +61 -0
- package/dist/builders/buildTextSummary.d.ts.map +1 -0
- package/dist/builders/buildTextSummary.js +178 -0
- package/dist/builders/buildTextSummary.js.map +1 -0
- package/dist/builders/deriveRecommendedStartPath.d.ts +11 -0
- package/dist/builders/deriveRecommendedStartPath.d.ts.map +1 -0
- package/dist/builders/deriveRecommendedStartPath.js +140 -0
- package/dist/builders/deriveRecommendedStartPath.js.map +1 -0
- package/dist/builders/deriveRuntimeRole.d.ts +4 -0
- package/dist/builders/deriveRuntimeRole.d.ts.map +1 -0
- package/dist/builders/deriveRuntimeRole.js +30 -0
- package/dist/builders/deriveRuntimeRole.js.map +1 -0
- package/dist/builders/detectRunCommands.d.ts +16 -0
- package/dist/builders/detectRunCommands.d.ts.map +1 -0
- package/dist/builders/detectRunCommands.js +285 -0
- package/dist/builders/detectRunCommands.js.map +1 -0
- package/dist/builders/generateSystemNarrative.d.ts +12 -0
- package/dist/builders/generateSystemNarrative.d.ts.map +1 -0
- package/dist/builders/generateSystemNarrative.js +474 -0
- package/dist/builders/generateSystemNarrative.js.map +1 -0
- package/dist/builders/gitLogParser.d.ts +15 -0
- package/dist/builders/gitLogParser.d.ts.map +1 -0
- package/dist/builders/gitLogParser.js +116 -0
- package/dist/builders/gitLogParser.js.map +1 -0
- package/dist/builders/readRepoContext.d.ts +30 -0
- package/dist/builders/readRepoContext.d.ts.map +1 -0
- package/dist/builders/readRepoContext.js +323 -0
- package/dist/builders/readRepoContext.js.map +1 -0
- package/dist/builders/readSystemReadme.d.ts +39 -0
- package/dist/builders/readSystemReadme.d.ts.map +1 -0
- package/dist/builders/readSystemReadme.js +133 -0
- package/dist/builders/readSystemReadme.js.map +1 -0
- package/dist/builders/systemRanking.d.ts +24 -0
- package/dist/builders/systemRanking.d.ts.map +1 -0
- package/dist/builders/systemRanking.js +153 -0
- package/dist/builders/systemRanking.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +170 -0
- package/dist/cli.js.map +1 -0
- package/dist/detectors/applyRunnableConfidenceGate.d.ts +8 -0
- package/dist/detectors/applyRunnableConfidenceGate.d.ts.map +1 -0
- package/dist/detectors/applyRunnableConfidenceGate.js +166 -0
- package/dist/detectors/applyRunnableConfidenceGate.js.map +1 -0
- package/dist/detectors/classifySystemType.d.ts +17 -0
- package/dist/detectors/classifySystemType.d.ts.map +1 -0
- package/dist/detectors/classifySystemType.js +460 -0
- package/dist/detectors/classifySystemType.js.map +1 -0
- package/dist/detectors/detectAppInternalUnits.d.ts +20 -0
- package/dist/detectors/detectAppInternalUnits.d.ts.map +1 -0
- package/dist/detectors/detectAppInternalUnits.js +454 -0
- package/dist/detectors/detectAppInternalUnits.js.map +1 -0
- package/dist/detectors/detectBlocks.d.ts +6 -0
- package/dist/detectors/detectBlocks.d.ts.map +1 -0
- package/dist/detectors/detectBlocks.js +354 -0
- package/dist/detectors/detectBlocks.js.map +1 -0
- package/dist/detectors/detectComposeServices.d.ts +23 -0
- package/dist/detectors/detectComposeServices.d.ts.map +1 -0
- package/dist/detectors/detectComposeServices.js +255 -0
- package/dist/detectors/detectComposeServices.js.map +1 -0
- package/dist/detectors/detectEntryPoints.d.ts +6 -0
- package/dist/detectors/detectEntryPoints.d.ts.map +1 -0
- package/dist/detectors/detectEntryPoints.js +376 -0
- package/dist/detectors/detectEntryPoints.js.map +1 -0
- package/dist/detectors/detectSubsystems.d.ts +6 -0
- package/dist/detectors/detectSubsystems.d.ts.map +1 -0
- package/dist/detectors/detectSubsystems.js +376 -0
- package/dist/detectors/detectSubsystems.js.map +1 -0
- package/dist/detectors/detectSystemCandidates.d.ts +3 -0
- package/dist/detectors/detectSystemCandidates.d.ts.map +1 -0
- package/dist/detectors/detectSystemCandidates.js +577 -0
- package/dist/detectors/detectSystemCandidates.js.map +1 -0
- package/dist/devWatcher.d.ts +14 -0
- package/dist/devWatcher.d.ts.map +1 -0
- package/dist/devWatcher.js +96 -0
- package/dist/devWatcher.js.map +1 -0
- package/dist/graph/buildCodebaseGraph.d.ts +3 -0
- package/dist/graph/buildCodebaseGraph.d.ts.map +1 -0
- package/dist/graph/buildCodebaseGraph.js +602 -0
- package/dist/graph/buildCodebaseGraph.js.map +1 -0
- package/dist/graph/buildLLMPayload.d.ts +89 -0
- package/dist/graph/buildLLMPayload.d.ts.map +1 -0
- package/dist/graph/buildLLMPayload.js +715 -0
- package/dist/graph/buildLLMPayload.js.map +1 -0
- package/dist/graph/callLLM.d.ts +16 -0
- package/dist/graph/callLLM.d.ts.map +1 -0
- package/dist/graph/callLLM.js +274 -0
- package/dist/graph/callLLM.js.map +1 -0
- package/dist/graph/types.d.ts +76 -0
- package/dist/graph/types.d.ts.map +1 -0
- package/dist/graph/types.js +5 -0
- package/dist/graph/types.js.map +1 -0
- package/dist/graph/walkExports.d.ts +2 -0
- package/dist/graph/walkExports.d.ts.map +1 -0
- package/dist/graph/walkExports.js +172 -0
- package/dist/graph/walkExports.js.map +1 -0
- package/dist/heuristics/file-patterns.json +325 -0
- package/dist/heuristics/known-packages.json +1022 -0
- package/dist/heuristics/loader.d.ts +121 -0
- package/dist/heuristics/loader.d.ts.map +1 -0
- package/dist/heuristics/loader.js +196 -0
- package/dist/heuristics/loader.js.map +1 -0
- package/dist/heuristics/repo-mode-signals.json +248 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +93 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp.d.ts +3 -0
- package/dist/mcp.d.ts.map +1 -0
- package/dist/mcp.js +356 -0
- package/dist/mcp.js.map +1 -0
- package/dist/scanner/buildImportGraph.d.ts +23 -0
- package/dist/scanner/buildImportGraph.d.ts.map +1 -0
- package/dist/scanner/buildImportGraph.js +186 -0
- package/dist/scanner/buildImportGraph.js.map +1 -0
- package/dist/scanner/detectDBOps.d.ts +21 -0
- package/dist/scanner/detectDBOps.d.ts.map +1 -0
- package/dist/scanner/detectDBOps.js +346 -0
- package/dist/scanner/detectDBOps.js.map +1 -0
- package/dist/scanner/detectOutbound.d.ts +6 -0
- package/dist/scanner/detectOutbound.d.ts.map +1 -0
- package/dist/scanner/detectOutbound.js +101 -0
- package/dist/scanner/detectOutbound.js.map +1 -0
- package/dist/scanner/detectRepoPurpose.d.ts +19 -0
- package/dist/scanner/detectRepoPurpose.d.ts.map +1 -0
- package/dist/scanner/detectRepoPurpose.js +335 -0
- package/dist/scanner/detectRepoPurpose.js.map +1 -0
- package/dist/scanner/detectRoutes.d.ts +22 -0
- package/dist/scanner/detectRoutes.d.ts.map +1 -0
- package/dist/scanner/detectRoutes.js +406 -0
- package/dist/scanner/detectRoutes.js.map +1 -0
- package/dist/scanner/filterNoise.d.ts +4 -0
- package/dist/scanner/filterNoise.d.ts.map +1 -0
- package/dist/scanner/filterNoise.js +95 -0
- package/dist/scanner/filterNoise.js.map +1 -0
- package/dist/scanner/loadTsAliases.d.ts +20 -0
- package/dist/scanner/loadTsAliases.d.ts.map +1 -0
- package/dist/scanner/loadTsAliases.js +135 -0
- package/dist/scanner/loadTsAliases.js.map +1 -0
- package/dist/scanner/routeHandlers.d.ts +15 -0
- package/dist/scanner/routeHandlers.d.ts.map +1 -0
- package/dist/scanner/routeHandlers.js +154 -0
- package/dist/scanner/routeHandlers.js.map +1 -0
- package/dist/scanner/scanRepo.d.ts +10 -0
- package/dist/scanner/scanRepo.d.ts.map +1 -0
- package/dist/scanner/scanRepo.js +165 -0
- package/dist/scanner/scanRepo.js.map +1 -0
- package/dist/scanner/walkImports.d.ts +14 -0
- package/dist/scanner/walkImports.d.ts.map +1 -0
- package/dist/scanner/walkImports.js +162 -0
- package/dist/scanner/walkImports.js.map +1 -0
- package/dist/server.d.ts +2 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +956 -0
- package/dist/server.js.map +1 -0
- package/dist/session/diff.d.ts +4 -0
- package/dist/session/diff.d.ts.map +1 -0
- package/dist/session/diff.js +150 -0
- package/dist/session/diff.js.map +1 -0
- package/dist/session/snapshot.d.ts +5 -0
- package/dist/session/snapshot.d.ts.map +1 -0
- package/dist/session/snapshot.js +114 -0
- package/dist/session/snapshot.js.map +1 -0
- package/dist/session/store.d.ts +8 -0
- package/dist/session/store.d.ts.map +1 -0
- package/dist/session/store.js +101 -0
- package/dist/session/store.js.map +1 -0
- package/dist/session/types.d.ts +46 -0
- package/dist/session/types.d.ts.map +1 -0
- package/dist/session/types.js +4 -0
- package/dist/session/types.js.map +1 -0
- package/dist/types.d.ts +350 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +4 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/file.d.ts +5 -0
- package/dist/utils/file.d.ts.map +1 -0
- package/dist/utils/file.js +76 -0
- package/dist/utils/file.js.map +1 -0
- package/dist/utils/path.d.ts +7 -0
- package/dist/utils/path.d.ts.map +1 -0
- package/dist/utils/path.js +62 -0
- package/dist/utils/path.js.map +1 -0
- package/dist/utils/text.d.ts +5 -0
- package/dist/utils/text.d.ts.map +1 -0
- package/dist/utils/text.js +29 -0
- package/dist/utils/text.js.map +1 -0
- package/dist/viewBuilders/buildSystemFolders.d.ts +39 -0
- package/dist/viewBuilders/buildSystemFolders.d.ts.map +1 -0
- package/dist/viewBuilders/buildSystemFolders.js +198 -0
- package/dist/viewBuilders/buildSystemFolders.js.map +1 -0
- package/dist/watcher/repoWatcher.d.ts +17 -0
- package/dist/watcher/repoWatcher.d.ts.map +1 -0
- package/dist/watcher/repoWatcher.js +87 -0
- package/dist/watcher/repoWatcher.js.map +1 -0
- package/package.json +102 -0
- package/public/memor_logo.svg +18 -0
- package/public/memor_transparent_logo.svg +25 -0
- package/public/tIcons/bun.svg +1 -0
- package/public/tIcons/css.svg +1 -0
- package/public/tIcons/docker.svg +3 -0
- package/public/tIcons/expressjs.svg +1 -0
- package/public/tIcons/html5.svg +6 -0
- package/public/tIcons/javascript.svg +1 -0
- package/public/tIcons/jest.svg +4 -0
- package/public/tIcons/json.svg +1 -0
- package/public/tIcons/markdown-light.svg +1 -0
- package/public/tIcons/nestjs.svg +1 -0
- package/public/tIcons/nextjs_icon_dark.svg +1 -0
- package/public/tIcons/npm.svg +1 -0
- package/public/tIcons/pnpm.svg +1 -0
- package/public/tIcons/prisma.svg +1 -0
- package/public/tIcons/react_dark.svg +11 -0
- package/public/tIcons/supabase.svg +15 -0
- package/public/tIcons/tailwindcss.svg +1 -0
- package/public/tIcons/turborepo-icon-light.svg +1 -0
- package/public/tIcons/typescript.svg +1 -0
- package/public/tIcons/vite.svg +1 -0
- package/public/tIcons/yarn.svg +1 -0
|
@@ -0,0 +1,715 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isTransportScaffold = isTransportScaffold;
|
|
4
|
+
exports.isDocPage = isDocPage;
|
|
5
|
+
exports.classifyArchetype = classifyArchetype;
|
|
6
|
+
exports.buildLLMPayloadWithMeta = buildLLMPayloadWithMeta;
|
|
7
|
+
exports.buildLLMPayload = buildLLMPayload;
|
|
8
|
+
exports.estimateTokens = estimateTokens;
|
|
9
|
+
exports.promoteFamilies = promoteFamilies;
|
|
10
|
+
exports.buildAmbiguousPayload = buildAmbiguousPayload;
|
|
11
|
+
const MAX_CLUSTERS = 50;
|
|
12
|
+
const MAX_EDGES = 20;
|
|
13
|
+
const MAX_EXPORTS_PER_CLUSTER = 8;
|
|
14
|
+
const MAX_PACKAGE_GUARANTEE = 10; // Layer 3: cap on L1 package coverage additions
|
|
15
|
+
const TOKEN_BUDGET = 2500; // Layer 2: cluster-array budget (~4500 total with meta/edges overhead)
|
|
16
|
+
// ── Filtering ─────────────────────────────────────────────────────────────
|
|
17
|
+
const NOISE_TOP_DIRS = new Set([
|
|
18
|
+
"examples", "example", "demo", "demos", "playground", "playgrounds",
|
|
19
|
+
"bench", "benchmark", "benchmarks", "fixtures", "scripts", "tools",
|
|
20
|
+
"hack", "spike", "sandbox", "test", "tests", "spec", "specs",
|
|
21
|
+
"__tests__", "__mocks__", "e2e", "cypress", "playwright",
|
|
22
|
+
"archived", "archive", "old", "deprecated", "legacy", "unused",
|
|
23
|
+
]);
|
|
24
|
+
// ── Transport scaffold detection ──────────────────────────────────────────
|
|
25
|
+
// Parameterised route handlers ([id].ts, [teamId].ts) are filesystem artifacts —
|
|
26
|
+
// they are not systems. They get filtered and their routes summarised as families.
|
|
27
|
+
const DOC_DIRS = new Set(["docs", "documentation", "doc", "website", "www", "storybook-static"]);
|
|
28
|
+
function isTransportScaffold(n) {
|
|
29
|
+
if (n.kind !== "cluster" && n.kind !== "package")
|
|
30
|
+
return false;
|
|
31
|
+
// [param] in path = parameterised route directory — a filesystem artifact, never a system.
|
|
32
|
+
// Universal across Next.js / SvelteKit / Remix / Astro / Nuxt.
|
|
33
|
+
// We drop the inDegree requirement: barrel imports (inDegree=1) don't make it a real system.
|
|
34
|
+
if (n.id.includes("[") && n.id.includes("]") && n.routes.length > 0 && n.models.length === 0)
|
|
35
|
+
return true;
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
function isDocPage(n) {
|
|
39
|
+
if (n.kind !== "cluster" && n.kind !== "package")
|
|
40
|
+
return false;
|
|
41
|
+
const parts = n.id.split("/");
|
|
42
|
+
// docs/pages/... or website/pages/... — framework routing inside a doc site
|
|
43
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
44
|
+
if (DOC_DIRS.has(parts[i]) && (parts[i + 1] === "pages" || parts[i + 1] === "src"))
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
function isNoiseNode(n) {
|
|
50
|
+
// L1-declared infra/app/queue/etc. nodes are never noise — explicitly declared
|
|
51
|
+
if (n.kind !== "cluster" && n.kind !== "package")
|
|
52
|
+
return false;
|
|
53
|
+
// Transport scaffolding must be checked before the route guard below
|
|
54
|
+
if (isTransportScaffold(n))
|
|
55
|
+
return true;
|
|
56
|
+
// Documentation site pages masquerading as routes
|
|
57
|
+
if (isDocPage(n))
|
|
58
|
+
return true;
|
|
59
|
+
// Clusters that own routes or models are NEVER noise — orientation anchors
|
|
60
|
+
if (n.routes.length > 0 || n.models.length > 0)
|
|
61
|
+
return false;
|
|
62
|
+
// Test-only clusters with no incoming edges
|
|
63
|
+
if (n.hasTests && n.inDegree === 0)
|
|
64
|
+
return true;
|
|
65
|
+
// Empty clusters
|
|
66
|
+
if (n.fileCount === 0)
|
|
67
|
+
return true;
|
|
68
|
+
// Zero-connection clusters — barrel stubs or dead code
|
|
69
|
+
if (n.inDegree === 0 && n.outDegree === 0)
|
|
70
|
+
return true;
|
|
71
|
+
// Example/demo/archived directories (case-insensitive)
|
|
72
|
+
const topDir = n.id.split("/")[0].toLowerCase();
|
|
73
|
+
if (NOISE_TOP_DIRS.has(topDir))
|
|
74
|
+
return true;
|
|
75
|
+
// Micro-clusters: single file, low connectivity, not shared
|
|
76
|
+
if (n.fileCount === 1 && n.inDegree <= 1 && n.outDegree <= 2 && !n.isShared)
|
|
77
|
+
return true;
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
// ── Layer 1: Sibling Collapse ──────────────────────────────────────────────
|
|
81
|
+
// Merge structurally identical sibling clusters pre-selection to prevent slot flooding.
|
|
82
|
+
// Only collapses when structure matches AND no semantic boundary signals differ.
|
|
83
|
+
// Example: /packages/email + /packages/pdf + /packages/sms collapse into /packages
|
|
84
|
+
// when they look identical. But /billing + /bookings do NOT collapse (different routes).
|
|
85
|
+
function getParentPath(id) {
|
|
86
|
+
const parts = id.split("/");
|
|
87
|
+
return parts.length > 1 ? parts.slice(0, -1).join("/") : "__root__";
|
|
88
|
+
}
|
|
89
|
+
function routeRoot(routes) {
|
|
90
|
+
if (routes.length === 0)
|
|
91
|
+
return null;
|
|
92
|
+
return routes[0].replace(/^\//, "").split("/")[0] || null;
|
|
93
|
+
}
|
|
94
|
+
function setsEqual(a, b) {
|
|
95
|
+
if (a.size !== b.size)
|
|
96
|
+
return false;
|
|
97
|
+
for (const v of a)
|
|
98
|
+
if (!b.has(v))
|
|
99
|
+
return false;
|
|
100
|
+
return true;
|
|
101
|
+
}
|
|
102
|
+
// Returns true if siblings are structurally similar enough to consider collapsing.
|
|
103
|
+
function structuralGate(siblings) {
|
|
104
|
+
if (siblings.length < 3)
|
|
105
|
+
return false;
|
|
106
|
+
const fileCounts = siblings.map((n) => n.fileCount);
|
|
107
|
+
const maxFC = Math.max(...fileCounts);
|
|
108
|
+
const minFC = Math.min(...fileCounts);
|
|
109
|
+
if (maxFC > minFC * 2 + 1)
|
|
110
|
+
return false; // file counts vary too much
|
|
111
|
+
const routeCounts = siblings.map((n) => n.routes.length);
|
|
112
|
+
if (Math.max(...routeCounts) - Math.min(...routeCounts) > 1)
|
|
113
|
+
return false;
|
|
114
|
+
const shape = (n) => `${n.hasTests}-${n.hasTypes}-${n.hasConfig}`;
|
|
115
|
+
if (new Set(siblings.map(shape)).size > 1)
|
|
116
|
+
return false; // different signal shapes
|
|
117
|
+
return true;
|
|
118
|
+
}
|
|
119
|
+
// Returns true if collapse should be VETOED — siblings cross semantic boundaries.
|
|
120
|
+
function semanticVeto(siblings) {
|
|
121
|
+
// 1. Different env var usage — different runtime configs = different systems
|
|
122
|
+
const firstEnv = new Set(siblings[0].envConsumes);
|
|
123
|
+
for (let i = 1; i < siblings.length; i++) {
|
|
124
|
+
if (!setsEqual(firstEnv, new Set(siblings[i].envConsumes)))
|
|
125
|
+
return true;
|
|
126
|
+
}
|
|
127
|
+
// 2. Different ORM usage — different DB access patterns
|
|
128
|
+
const firstOrm = new Set(siblings[0].ormUses);
|
|
129
|
+
for (let i = 1; i < siblings.length; i++) {
|
|
130
|
+
if (!setsEqual(firstOrm, new Set(siblings[i].ormUses)))
|
|
131
|
+
return true;
|
|
132
|
+
}
|
|
133
|
+
// 3. Non-empty model sets that differ — different domain boundaries
|
|
134
|
+
if (siblings.some((n) => n.models.length > 0)) {
|
|
135
|
+
const firstModels = new Set(siblings[0].models);
|
|
136
|
+
for (let i = 1; i < siblings.length; i++) {
|
|
137
|
+
if (!setsEqual(firstModels, new Set(siblings[i].models)))
|
|
138
|
+
return true;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
// 4. Mixed entry-point status — entry points are structurally distinct
|
|
142
|
+
if (new Set(siblings.map((n) => n.isEntryPoint)).size > 1)
|
|
143
|
+
return true;
|
|
144
|
+
// 5. Different route roots (when any sibling is route-bearing — orientation anchor)
|
|
145
|
+
const withRoutes = siblings.filter((n) => n.routes.length > 0);
|
|
146
|
+
if (withRoutes.length > 0) {
|
|
147
|
+
const roots = new Set(withRoutes.map((n) => routeRoot(n.routes)));
|
|
148
|
+
if (roots.size > 1)
|
|
149
|
+
return true;
|
|
150
|
+
}
|
|
151
|
+
return false;
|
|
152
|
+
}
|
|
153
|
+
function collapseGroup(siblings, parentId) {
|
|
154
|
+
return {
|
|
155
|
+
id: parentId,
|
|
156
|
+
name: parentId.split("/").pop() ?? parentId,
|
|
157
|
+
kind: "cluster",
|
|
158
|
+
packageId: siblings[0]?.packageId,
|
|
159
|
+
fileCount: siblings.reduce((s, n) => s + n.fileCount, 0),
|
|
160
|
+
topExports: [...new Set(siblings.flatMap((n) => n.topExports))].slice(0, 20),
|
|
161
|
+
centrality: Math.max(...siblings.map((n) => n.centrality)),
|
|
162
|
+
inDegree: Math.max(...siblings.map((n) => n.inDegree)),
|
|
163
|
+
outDegree: Math.max(...siblings.map((n) => n.outDegree)),
|
|
164
|
+
isEntryPoint: siblings[0]?.isEntryPoint ?? false,
|
|
165
|
+
isShared: siblings.some((n) => n.isShared),
|
|
166
|
+
routes: [...new Set(siblings.flatMap((n) => n.routes))],
|
|
167
|
+
models: [...new Set(siblings.flatMap((n) => n.models))],
|
|
168
|
+
hasTests: siblings.some((n) => n.hasTests),
|
|
169
|
+
hasTypes: siblings.some((n) => n.hasTypes),
|
|
170
|
+
hasConfig: siblings.some((n) => n.hasConfig),
|
|
171
|
+
envConsumes: [...new Set(siblings.flatMap((n) => n.envConsumes))],
|
|
172
|
+
ormUses: [...new Set(siblings.flatMap((n) => n.ormUses))],
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
function applySiblingCollapse(nodes) {
|
|
176
|
+
const nodeIds = new Set(nodes.map((n) => n.id));
|
|
177
|
+
const byParent = new Map();
|
|
178
|
+
for (const n of nodes) {
|
|
179
|
+
const parent = getParentPath(n.id);
|
|
180
|
+
if (parent === "__root__")
|
|
181
|
+
continue;
|
|
182
|
+
if (nodeIds.has(parent))
|
|
183
|
+
continue; // parent is a first-class node, never collapse into it
|
|
184
|
+
if (!byParent.has(parent))
|
|
185
|
+
byParent.set(parent, []);
|
|
186
|
+
byParent.get(parent).push(n);
|
|
187
|
+
}
|
|
188
|
+
const collapseMap = new Map(); // child id → merged parent id
|
|
189
|
+
const collapsedChildIds = new Set();
|
|
190
|
+
const syntheticNodes = [];
|
|
191
|
+
for (const [parentId, siblings] of byParent) {
|
|
192
|
+
if (!structuralGate(siblings))
|
|
193
|
+
continue;
|
|
194
|
+
if (semanticVeto(siblings))
|
|
195
|
+
continue;
|
|
196
|
+
syntheticNodes.push(collapseGroup(siblings, parentId));
|
|
197
|
+
for (const s of siblings) {
|
|
198
|
+
collapseMap.set(s.id, parentId);
|
|
199
|
+
collapsedChildIds.add(s.id);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
const surviving = nodes.filter((n) => !collapsedChildIds.has(n.id));
|
|
203
|
+
surviving.push(...syntheticNodes);
|
|
204
|
+
return { nodes: surviving, collapseMap };
|
|
205
|
+
}
|
|
206
|
+
function classifyArchetype(n) {
|
|
207
|
+
const name = n.name.toLowerCase();
|
|
208
|
+
const id = n.id.toLowerCase();
|
|
209
|
+
// ── Data layer ─────────────────────────────────────────────────────────────
|
|
210
|
+
// Prisma cluster (by name or ORM signal + models)
|
|
211
|
+
if (id.includes("prisma") && (n.ormUses.length > 0 || n.models.length > 0))
|
|
212
|
+
return "data-layer";
|
|
213
|
+
// Any ORM with significant models
|
|
214
|
+
if (n.ormUses.length > 0 && n.models.length > 2)
|
|
215
|
+
return "data-layer";
|
|
216
|
+
// TypeORM / Mongoose / Sequelize / Drizzle / Knex — DB-access layer regardless of models
|
|
217
|
+
if (n.ormUses.some((o) => /typeorm|mongoose|sequelize|drizzle|knex/i.test(o)))
|
|
218
|
+
return "data-layer";
|
|
219
|
+
// Non-ORM DB clients: Supabase, Firebase, Neon, PlanetScale, Turso, libSQL.
|
|
220
|
+
// Guard: the cluster name or id must also suggest it IS the data layer, not just
|
|
221
|
+
// a consumer of it. A generic "lib" that initialises supabase is not a data layer.
|
|
222
|
+
const DB_LAYER_NAME = /database|^db$|^data$|supabase|firebase|neon|planetscale|mongo|postgres|repository|repositories|repo|store|storage/i;
|
|
223
|
+
if (n.ormUses.some((o) => /supabase|firebase|firestore|neon|planetscale|turso|libsql/i.test(o)) &&
|
|
224
|
+
(DB_LAYER_NAME.test(n.name) || DB_LAYER_NAME.test(n.id)))
|
|
225
|
+
return "data-layer";
|
|
226
|
+
// Env-pattern fallback: DATABASE_URL etc. signal a data layer only when the cluster
|
|
227
|
+
// has a database-related name AND at least one model — prevents API routes that merely
|
|
228
|
+
// read SUPABASE_URL from being mis-classified.
|
|
229
|
+
const DB_ENV = /^(DATABASE_URL|MONGO_URI|MONGODB_URI|SUPABASE_URL|FIREBASE_URL|NEON_URL|TURSO_URL|PLANETSCALE_URL)$/i;
|
|
230
|
+
if (n.envConsumes.some((e) => DB_ENV.test(e)) &&
|
|
231
|
+
(DB_LAYER_NAME.test(n.name) || n.models.length >= 1) &&
|
|
232
|
+
n.inDegree >= 1)
|
|
233
|
+
return "data-layer";
|
|
234
|
+
// ── DB migrations / seeds ──────────────────────────────────────────────────
|
|
235
|
+
if ((/migrations?$|seeders?$|seeds?$/.test(name) || id.includes("/migration")) &&
|
|
236
|
+
n.routes.length === 0)
|
|
237
|
+
return "db-migration";
|
|
238
|
+
// ── Cache client ───────────────────────────────────────────────────────────
|
|
239
|
+
if (n.envConsumes.some((e) => /redis/i.test(e)) || n.ormUses.some((o) => /redis/i.test(o)))
|
|
240
|
+
return "cache-client";
|
|
241
|
+
// ── Auth / session ─────────────────────────────────────────────────────────
|
|
242
|
+
if (/auth|jwt|passport|session/.test(name) &&
|
|
243
|
+
n.envConsumes.some((e) => /jwt|secret|auth|token|password/i.test(e)))
|
|
244
|
+
return "auth-service";
|
|
245
|
+
// ── Queue / worker ─────────────────────────────────────────────────────────
|
|
246
|
+
if (/queue|worker|bull|job|cron/.test(name) && n.envConsumes.length > 0)
|
|
247
|
+
return "queue-worker";
|
|
248
|
+
// ── Email / notification service ───────────────────────────────────────────
|
|
249
|
+
if (/mailer?|mailing|smtp|sendgrid|notification|notif$|messaging$/.test(name) &&
|
|
250
|
+
n.routes.length === 0 && n.models.length < 2 &&
|
|
251
|
+
n.envConsumes.length > 0)
|
|
252
|
+
return "email-service";
|
|
253
|
+
// ── Middleware / cross-cutting concerns ────────────────────────────────────
|
|
254
|
+
if (/^middleware$|interceptors?$|guards?$|pipes?$|exception.?filters?$|decorators?$/.test(name) &&
|
|
255
|
+
n.routes.length === 0 && n.models.length === 0 &&
|
|
256
|
+
n.fileCount < 30)
|
|
257
|
+
return "middleware";
|
|
258
|
+
// ── Pure type definitions ──────────────────────────────────────────────────
|
|
259
|
+
// If hasTypes is set and there are zero runtime signals, this cluster is
|
|
260
|
+
// unambiguously a type-definitions package regardless of name.
|
|
261
|
+
if (n.hasTypes && n.routes.length === 0 && n.models.length === 0 &&
|
|
262
|
+
n.ormUses.length === 0 && n.envConsumes.length === 0 && !n.hasTests &&
|
|
263
|
+
n.fileCount < 25)
|
|
264
|
+
return "type-defs";
|
|
265
|
+
// Name-driven fallback for larger type packages (DTOs, interfaces, schemas)
|
|
266
|
+
if (n.routes.length === 0 && n.models.length === 0 &&
|
|
267
|
+
n.ormUses.length === 0 && n.envConsumes.length === 0 &&
|
|
268
|
+
/^types?$|^interfaces?$|^dtos?$|^schemas?$|^typings?$|^declarations?$|^contracts?$|^enums?$/.test(name))
|
|
269
|
+
return "type-defs";
|
|
270
|
+
// Path-driven: cluster is a sub-directory of a dto/types/interfaces/schemas folder.
|
|
271
|
+
// Covers "jira" in server/api/v1/dto/jira — name alone won't match but parent path does.
|
|
272
|
+
const DTO_PATH_SEGMENTS = new Set(["dto", "dtos", "types", "type", "interfaces", "schemas", "typings", "contracts"]);
|
|
273
|
+
const pathSegs = n.id.split("/");
|
|
274
|
+
if (pathSegs.some((seg) => DTO_PATH_SEGMENTS.has(seg.toLowerCase())) &&
|
|
275
|
+
n.routes.length === 0 && n.models.length === 0 &&
|
|
276
|
+
n.ormUses.length === 0 && n.envConsumes.length === 0)
|
|
277
|
+
return "type-defs";
|
|
278
|
+
// ── Config-only package ────────────────────────────────────────────────────
|
|
279
|
+
// Small clusters with config signal and no runtime signals are unambiguous.
|
|
280
|
+
if (n.hasConfig && n.routes.length === 0 && n.models.length === 0 &&
|
|
281
|
+
n.ormUses.length === 0 && n.envConsumes.length === 0 && n.fileCount < 8)
|
|
282
|
+
return "config";
|
|
283
|
+
// Name-driven config (larger config packages like eslint, prettier, tsconfig)
|
|
284
|
+
if (n.routes.length === 0 && n.models.length === 0 && n.fileCount < 15 &&
|
|
285
|
+
/config|tsconfig|eslint|prettier|constants?$|settings?$|^env$|environment/.test(name))
|
|
286
|
+
return "config";
|
|
287
|
+
// ── Shared utility library ─────────────────────────────────────────────────
|
|
288
|
+
// Universal utility name catch-all — these folder names are unambiguous
|
|
289
|
+
const UTIL_NAMES = new Set([
|
|
290
|
+
"utils", "util", "helpers", "helper", "common", "lib", "tools", "toolkit",
|
|
291
|
+
"shared", "core-utils", "utilities",
|
|
292
|
+
]);
|
|
293
|
+
if (UTIL_NAMES.has(name) &&
|
|
294
|
+
n.routes.length === 0 && n.models.length === 0 &&
|
|
295
|
+
n.ormUses.length === 0 && n.envConsumes.length === 0)
|
|
296
|
+
return "shared-lib";
|
|
297
|
+
// React hooks folder — unambiguously support layer
|
|
298
|
+
if ((name === "hooks" || n.name.startsWith("use") || name.endsWith("-hooks")) &&
|
|
299
|
+
n.routes.length === 0 && n.models.length === 0 && n.isShared)
|
|
300
|
+
return "shared-lib";
|
|
301
|
+
// Heavily imported with no business signals
|
|
302
|
+
if (n.isShared && n.inDegree >= 3 && n.routes.length === 0 && n.models.length === 0)
|
|
303
|
+
return "shared-lib";
|
|
304
|
+
// ── App entry ──────────────────────────────────────────────────────────────
|
|
305
|
+
if (n.isEntryPoint && n.fileCount <= 5 && n.outDegree >= 4 && n.routes.length === 0)
|
|
306
|
+
return "app-entry";
|
|
307
|
+
// ── Public web page ────────────────────────────────────────────────────────
|
|
308
|
+
// Next.js app-router page clusters: public-facing routes (not /api/, not /admin/).
|
|
309
|
+
// These are orientation anchors the LLM routinely overlooks because they have
|
|
310
|
+
// minimal signal (1 file, 1 route, no models/ORM) — classify deterministically.
|
|
311
|
+
if (n.routes.length > 0 &&
|
|
312
|
+
n.routes.every((r) => !r.startsWith("/api/") && !r.startsWith("/admin")) &&
|
|
313
|
+
n.models.length === 0 && n.ormUses.length === 0 &&
|
|
314
|
+
!n.id.includes("[") // not a transport scaffold
|
|
315
|
+
)
|
|
316
|
+
return "web-page";
|
|
317
|
+
return null;
|
|
318
|
+
}
|
|
319
|
+
// ── Layer 2: Token Budget ─────────────────────────────────────────────────
|
|
320
|
+
// Drop lowest-signal clusters when payload exceeds budget.
|
|
321
|
+
// Runtime-edge clusters are always preserved (they carry dynamic architecture facts).
|
|
322
|
+
function enforceTokenBudget(clusters, runtimeEdgeClusterIds) {
|
|
323
|
+
const budget = TOKEN_BUDGET * 4; // chars (~4 chars per token)
|
|
324
|
+
if (JSON.stringify(clusters).length <= budget)
|
|
325
|
+
return clusters;
|
|
326
|
+
const scored = clusters.map((c) => ({
|
|
327
|
+
c,
|
|
328
|
+
score: c.routes.length * 5 +
|
|
329
|
+
c.models.length * 3 +
|
|
330
|
+
(c.uses_orm?.length ?? 0) * 4 +
|
|
331
|
+
(c.env_consumes?.length ?? 0) * 2 +
|
|
332
|
+
c.files * 0.5 +
|
|
333
|
+
c.centrality * 10 +
|
|
334
|
+
(c.is_entry ? 3 : 0) +
|
|
335
|
+
(runtimeEdgeClusterIds.has(c.id) ? 1000 : 0), // never drop runtime-edge clusters
|
|
336
|
+
}));
|
|
337
|
+
scored.sort((a, b) => b.score - a.score);
|
|
338
|
+
const kept = [];
|
|
339
|
+
let chars = 0;
|
|
340
|
+
for (const { c } of scored) {
|
|
341
|
+
const len = JSON.stringify(c).length;
|
|
342
|
+
// Always keep the first item; skip others that would bust the budget
|
|
343
|
+
if (chars + len > budget && kept.length > 0)
|
|
344
|
+
continue;
|
|
345
|
+
kept.push(c);
|
|
346
|
+
chars += len;
|
|
347
|
+
}
|
|
348
|
+
return kept;
|
|
349
|
+
}
|
|
350
|
+
function buildLLMPayloadWithMeta(graph) {
|
|
351
|
+
// Split nodes: structural clusters vs declared infra nodes
|
|
352
|
+
const clusterNodes = graph.nodes.filter((n) => n.kind === "cluster" || n.kind === "package");
|
|
353
|
+
const infraNodes = graph.nodes.filter((n) => n.kind !== "cluster" && n.kind !== "package");
|
|
354
|
+
// ── Transport scaffold extraction ────────────────────────────────────────
|
|
355
|
+
// Collect route families from transport clusters before they're discarded,
|
|
356
|
+
// so the LLM still gets route orientation context in a compact form.
|
|
357
|
+
const transportNodes = clusterNodes.filter((n) => isTransportScaffold(n) || isDocPage(n));
|
|
358
|
+
const routeFamilyMap = new Map();
|
|
359
|
+
for (const n of transportNodes) {
|
|
360
|
+
for (const route of n.routes) {
|
|
361
|
+
const seg = route.replace(/^\//, "").split("/")[0];
|
|
362
|
+
const family = seg ? `/${seg}` : "/";
|
|
363
|
+
routeFamilyMap.set(family, (routeFamilyMap.get(family) ?? 0) + 1);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
const routeFamilies = [...routeFamilyMap.entries()]
|
|
367
|
+
.filter(([, count]) => count >= 2)
|
|
368
|
+
.sort((a, b) => b[1] - a[1])
|
|
369
|
+
.slice(0, 10)
|
|
370
|
+
.map(([family, count]) => ({ family: family + "/*", count }));
|
|
371
|
+
// Filter noisy cluster nodes
|
|
372
|
+
const filtered = clusterNodes.filter((n) => !isNoiseNode(n));
|
|
373
|
+
// ── Layer 1: Sibling Collapse ────────────────────────────────────────────
|
|
374
|
+
const { nodes: collapsed, collapseMap } = applySiblingCollapse(filtered);
|
|
375
|
+
const resolve = (id) => collapseMap.get(id) ?? id;
|
|
376
|
+
// ── Orientation-driven selection ─────────────────────────────────────────
|
|
377
|
+
const ROUTE_SLOTS = Math.floor(MAX_CLUSTERS * 0.45);
|
|
378
|
+
const MODEL_SLOTS = Math.floor(MAX_CLUSTERS * 0.10);
|
|
379
|
+
const INFRA_SLOTS = MAX_CLUSTERS - ROUTE_SLOTS - MODEL_SLOTS;
|
|
380
|
+
const withRoutes = collapsed
|
|
381
|
+
.filter((n) => n.routes.length > 0)
|
|
382
|
+
.sort((a, b) => (b.routes.length + b.fileCount) - (a.routes.length + a.fileCount))
|
|
383
|
+
.slice(0, ROUTE_SLOTS);
|
|
384
|
+
const orientationIds = new Set(withRoutes.map((n) => n.id));
|
|
385
|
+
const withModels = collapsed
|
|
386
|
+
.filter((n) => n.models.length > 0 && !orientationIds.has(n.id))
|
|
387
|
+
.sort((a, b) => b.models.length - a.models.length)
|
|
388
|
+
.slice(0, MODEL_SLOTS);
|
|
389
|
+
withModels.forEach((n) => orientationIds.add(n.id));
|
|
390
|
+
const orientationNodes = [...withRoutes, ...withModels];
|
|
391
|
+
// Sort rest by centrality so collapsed synthetic nodes rank fairly
|
|
392
|
+
const rest = collapsed
|
|
393
|
+
.filter((n) => !orientationIds.has(n.id))
|
|
394
|
+
.sort((a, b) => b.centrality - a.centrality);
|
|
395
|
+
const CENTRALITY_SLOTS = Math.floor(INFRA_SLOTS * 0.5);
|
|
396
|
+
const FILECOUNT_SLOTS = Math.floor(INFRA_SLOTS * 0.35);
|
|
397
|
+
const ENTRY_SLOTS = INFRA_SLOTS - CENTRALITY_SLOTS - FILECOUNT_SLOTS;
|
|
398
|
+
const byCentrality = rest.slice(0, CENTRALITY_SLOTS);
|
|
399
|
+
const centralityIds = new Set(byCentrality.map((n) => n.id));
|
|
400
|
+
const afterCentrality = rest.filter((n) => !centralityIds.has(n.id));
|
|
401
|
+
const byFileCount = afterCentrality
|
|
402
|
+
.sort((a, b) => b.fileCount - a.fileCount)
|
|
403
|
+
.slice(0, FILECOUNT_SLOTS);
|
|
404
|
+
const fileCountIds = new Set(byFileCount.map((n) => n.id));
|
|
405
|
+
const byEntry = afterCentrality
|
|
406
|
+
.filter((n) => !fileCountIds.has(n.id) && n.isEntryPoint && n.outDegree >= 2)
|
|
407
|
+
.sort((a, b) => b.outDegree - a.outDegree)
|
|
408
|
+
.slice(0, ENTRY_SLOTS);
|
|
409
|
+
const selected = [...orientationNodes, ...byCentrality, ...byFileCount, ...byEntry];
|
|
410
|
+
// ── Layer 3: L1 package coverage guarantee (capped) ──────────────────────
|
|
411
|
+
const coveredPackageIds = new Set(selected.map((n) => n.packageId).filter((id) => !!id));
|
|
412
|
+
const allPackageIds = new Set(collapsed.map((n) => n.packageId).filter((id) => !!id));
|
|
413
|
+
const selectedIds = new Set(selected.map((n) => n.id));
|
|
414
|
+
let packageGuaranteeCount = 0;
|
|
415
|
+
for (const pkgId of allPackageIds) {
|
|
416
|
+
if (packageGuaranteeCount >= MAX_PACKAGE_GUARANTEE)
|
|
417
|
+
break;
|
|
418
|
+
if (coveredPackageIds.has(pkgId))
|
|
419
|
+
continue;
|
|
420
|
+
const best = collapsed
|
|
421
|
+
.filter((n) => n.packageId === pkgId && !selectedIds.has(n.id))
|
|
422
|
+
.sort((a, b) => (b.routes.length * 5 + b.models.length * 3 + b.fileCount + b.centrality * 10) -
|
|
423
|
+
(a.routes.length * 5 + a.models.length * 3 + a.fileCount + a.centrality * 10))[0];
|
|
424
|
+
if (best) {
|
|
425
|
+
selected.push(best);
|
|
426
|
+
selectedIds.add(best.id);
|
|
427
|
+
coveredPackageIds.add(pkgId);
|
|
428
|
+
packageGuaranteeCount++;
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
// Always include declared infra nodes (they are architecture facts, not noise)
|
|
432
|
+
// Cap at 10 to avoid flooding the payload on large compose files
|
|
433
|
+
const selectedInfra = infraNodes
|
|
434
|
+
.filter((n) => graph.edges.some((e) => (e.to === n.id || e.from === n.id) &&
|
|
435
|
+
(selectedIds.has(resolve(e.from)) || selectedIds.has(resolve(e.to)))))
|
|
436
|
+
.slice(0, 10);
|
|
437
|
+
selectedInfra.forEach((n) => selectedIds.add(n.id));
|
|
438
|
+
const allSelected = [...selected, ...selectedInfra];
|
|
439
|
+
const includedIds = new Set(allSelected.map((n) => n.id));
|
|
440
|
+
// ── Import edges (with collapse remapping) ────────────────────────────────
|
|
441
|
+
const importEdges = graph.edges
|
|
442
|
+
.filter((e) => e.kind === "import")
|
|
443
|
+
.map((e) => ({ ...e, from: resolve(e.from), to: resolve(e.to) }))
|
|
444
|
+
.filter((e) => e.from !== e.to && includedIds.has(e.from) && includedIds.has(e.to))
|
|
445
|
+
.sort((a, b) => b.weight - a.weight)
|
|
446
|
+
.slice(0, MAX_EDGES);
|
|
447
|
+
// Pre-build adjacency for compact cluster representation
|
|
448
|
+
const outgoing = new Map();
|
|
449
|
+
const incoming = new Map();
|
|
450
|
+
for (const id of includedIds) {
|
|
451
|
+
outgoing.set(id, []);
|
|
452
|
+
incoming.set(id, []);
|
|
453
|
+
}
|
|
454
|
+
for (const e of importEdges) {
|
|
455
|
+
outgoing.get(e.from)?.push(e.to);
|
|
456
|
+
incoming.get(e.to)?.push(e.from);
|
|
457
|
+
}
|
|
458
|
+
// ── Runtime edges ─────────────────────────────────────────────────────────
|
|
459
|
+
const runtimeEdges = graph.edges
|
|
460
|
+
.filter((e) => e.kind === "runtime" && (includedIds.has(e.from) || includedIds.has(e.to)));
|
|
461
|
+
const runtimeEdgeClusterIds = new Set(runtimeEdges.flatMap((e) => [e.from, e.to]));
|
|
462
|
+
// ── Build compact clusters ────────────────────────────────────────────────
|
|
463
|
+
const compactClusters = allSelected.map((n) => {
|
|
464
|
+
const signals = {};
|
|
465
|
+
if (n.hasTests)
|
|
466
|
+
signals.tests = true;
|
|
467
|
+
if (n.hasTypes)
|
|
468
|
+
signals.types = true;
|
|
469
|
+
if (n.hasConfig)
|
|
470
|
+
signals.config = true;
|
|
471
|
+
// Runtime calls from this node to other nodes (HTTP, tRPC, proxy)
|
|
472
|
+
const runtimeCalls = [...new Set(runtimeEdges
|
|
473
|
+
.filter((e) => e.from === n.id &&
|
|
474
|
+
(e.type === "HTTP_CALL" || e.type === "TRPC_CALL" || e.type === "PROXY_ROUTE"))
|
|
475
|
+
.map((e) => e.to))];
|
|
476
|
+
const compact = {
|
|
477
|
+
id: n.id,
|
|
478
|
+
name: n.name,
|
|
479
|
+
files: n.fileCount,
|
|
480
|
+
exports: n.topExports.slice(0, MAX_EXPORTS_PER_CLUSTER),
|
|
481
|
+
imports_from: outgoing.get(n.id) ?? [],
|
|
482
|
+
imported_by: incoming.get(n.id) ?? [],
|
|
483
|
+
is_entry: n.isEntryPoint,
|
|
484
|
+
is_shared: n.isShared,
|
|
485
|
+
centrality: n.centrality,
|
|
486
|
+
routes: n.routes.slice(0, 6),
|
|
487
|
+
models: n.models.slice(0, 10),
|
|
488
|
+
signals,
|
|
489
|
+
};
|
|
490
|
+
if (n.kind !== "cluster" && n.kind !== "package")
|
|
491
|
+
compact.kind = n.kind;
|
|
492
|
+
const archetype = classifyArchetype(n);
|
|
493
|
+
if (archetype)
|
|
494
|
+
compact.archetype = archetype;
|
|
495
|
+
if (n.envConsumes.length > 0)
|
|
496
|
+
compact.env_consumes = n.envConsumes;
|
|
497
|
+
if (n.ormUses.length > 0)
|
|
498
|
+
compact.uses_orm = n.ormUses;
|
|
499
|
+
if (runtimeCalls.length > 0)
|
|
500
|
+
compact.runtime_calls = runtimeCalls;
|
|
501
|
+
return compact;
|
|
502
|
+
});
|
|
503
|
+
// ── Layer 2: Token Budget ─────────────────────────────────────────────────
|
|
504
|
+
const budgetedClusters = enforceTokenBudget(compactClusters, runtimeEdgeClusterIds);
|
|
505
|
+
const budgetedIds = new Set(budgetedClusters.map((c) => c.id));
|
|
506
|
+
const topEdges = importEdges
|
|
507
|
+
.filter((e) => budgetedIds.has(e.from) && budgetedIds.has(e.to))
|
|
508
|
+
.map((e) => ({
|
|
509
|
+
from: e.from,
|
|
510
|
+
to: e.to,
|
|
511
|
+
weight: e.weight,
|
|
512
|
+
symbols: e.symbols.slice(0, 3),
|
|
513
|
+
}));
|
|
514
|
+
const meta = {
|
|
515
|
+
repo_name: graph.meta.repoName,
|
|
516
|
+
total_files: graph.meta.totalFiles,
|
|
517
|
+
total_clusters: graph.meta.totalClusters,
|
|
518
|
+
frameworks: graph.meta.frameworks,
|
|
519
|
+
packages: graph.meta.packages,
|
|
520
|
+
};
|
|
521
|
+
if (graph.meta.readmeSummary)
|
|
522
|
+
meta.repo_purpose = graph.meta.readmeSummary;
|
|
523
|
+
if (graph.meta.repoStructure)
|
|
524
|
+
meta.repo_structure = graph.meta.repoStructure;
|
|
525
|
+
if (graph.meta.repoType)
|
|
526
|
+
meta.repo_type = graph.meta.repoType;
|
|
527
|
+
if (graph.meta.techStack?.length)
|
|
528
|
+
meta.tech_stack = graph.meta.techStack;
|
|
529
|
+
if (graph.meta.dataModels.length > 0)
|
|
530
|
+
meta.data_models = graph.meta.dataModels;
|
|
531
|
+
if (routeFamilies.length > 0)
|
|
532
|
+
meta.route_families = routeFamilies;
|
|
533
|
+
const payload = { meta, clusters: budgetedClusters, top_edges: topEdges };
|
|
534
|
+
// Include runtime edges that involve budgeted clusters
|
|
535
|
+
const compactRuntime = runtimeEdges
|
|
536
|
+
.filter((e) => budgetedIds.has(e.from) || budgetedIds.has(e.to))
|
|
537
|
+
.map((e) => ({
|
|
538
|
+
from: e.from,
|
|
539
|
+
to: e.to,
|
|
540
|
+
type: e.type ?? "runtime",
|
|
541
|
+
evidence: e.evidence ?? "",
|
|
542
|
+
}));
|
|
543
|
+
if (compactRuntime.length > 0)
|
|
544
|
+
payload.runtime_edges = compactRuntime;
|
|
545
|
+
const payloadStr = JSON.stringify(payload);
|
|
546
|
+
const tokens = estimateTokens(payloadStr);
|
|
547
|
+
// ── Compute metrics ───────────────────────────────────────────────────────
|
|
548
|
+
const routesTotal = clusterNodes.reduce((s, n) => s + n.routes.length, 0);
|
|
549
|
+
const routesRetained = budgetedClusters.reduce((s, c) => s + c.routes.length, 0);
|
|
550
|
+
const modelsTotal = clusterNodes.reduce((s, n) => s + n.models.length, 0);
|
|
551
|
+
const modelsRetained = budgetedClusters.reduce((s, c) => s + c.models.length, 0);
|
|
552
|
+
const ormClustersTotal = clusterNodes.filter((n) => n.ormUses.length > 0).length;
|
|
553
|
+
const ormClustersRetained = budgetedClusters.filter((c) => (c.uses_orm?.length ?? 0) > 0).length;
|
|
554
|
+
const hasSignal = (c) => c.routes.length > 0 || c.models.length > 0 ||
|
|
555
|
+
(c.uses_orm?.length ?? 0) > 0 || (c.env_consumes?.length ?? 0) > 0;
|
|
556
|
+
const semanticDensity = budgetedClusters.length === 0 ? 0
|
|
557
|
+
: budgetedClusters.filter(hasSignal).length / budgetedClusters.length;
|
|
558
|
+
const archetypeHits = budgetedClusters.filter((c) => c.archetype !== undefined).length;
|
|
559
|
+
const buildMeta = {
|
|
560
|
+
rawClusters: clusterNodes.length,
|
|
561
|
+
afterNoiseFilter: filtered.length,
|
|
562
|
+
afterSiblingCollapse: collapsed.length,
|
|
563
|
+
siblingsCollapsed: collapseMap.size,
|
|
564
|
+
transportFolded: transportNodes.length,
|
|
565
|
+
prebudgetClusters: compactClusters.length,
|
|
566
|
+
payloadClusters: budgetedClusters.length,
|
|
567
|
+
budgetDropped: compactClusters.length - budgetedClusters.length,
|
|
568
|
+
packageGuaranteeAdded: packageGuaranteeCount,
|
|
569
|
+
tokens,
|
|
570
|
+
routesTotal,
|
|
571
|
+
routesRetained,
|
|
572
|
+
modelsTotal,
|
|
573
|
+
modelsRetained,
|
|
574
|
+
ormClustersTotal,
|
|
575
|
+
ormClustersRetained,
|
|
576
|
+
semanticDensity,
|
|
577
|
+
signalRetentionRoutes: routesTotal === 0 ? 1 : routesRetained / routesTotal,
|
|
578
|
+
signalRetentionModels: modelsTotal === 0 ? 1 : modelsRetained / modelsTotal,
|
|
579
|
+
archetypeHits,
|
|
580
|
+
archetypeRate: budgetedClusters.length === 0 ? 0 : archetypeHits / budgetedClusters.length,
|
|
581
|
+
};
|
|
582
|
+
return { payload: payloadStr, meta: buildMeta };
|
|
583
|
+
}
|
|
584
|
+
function buildLLMPayload(graph) {
|
|
585
|
+
return buildLLMPayloadWithMeta(graph).payload;
|
|
586
|
+
}
|
|
587
|
+
function estimateTokens(payload) {
|
|
588
|
+
return Math.ceil(payload.length / 4);
|
|
589
|
+
}
|
|
590
|
+
// ── Family Promotion ─────────────────────────────────────────────────────────
|
|
591
|
+
// When ≥ FAMILY_THRESHOLD siblings share a parent directory, replace them with
|
|
592
|
+
// one merged cluster at the parent level. The LLM names the parent once instead
|
|
593
|
+
// of naming each child separately. Repo-agnostic: works for any framework.
|
|
594
|
+
const FAMILY_THRESHOLD = 3;
|
|
595
|
+
function parentDir(id) {
|
|
596
|
+
const i = id.lastIndexOf("/");
|
|
597
|
+
return i > 0 ? id.slice(0, i) : "";
|
|
598
|
+
}
|
|
599
|
+
function promoteFamilies(clusters) {
|
|
600
|
+
const existingIds = new Set(clusters.map((c) => c.id));
|
|
601
|
+
const byParent = new Map();
|
|
602
|
+
for (const c of clusters) {
|
|
603
|
+
const p = parentDir(c.id);
|
|
604
|
+
if (!p)
|
|
605
|
+
continue;
|
|
606
|
+
if (!byParent.has(p))
|
|
607
|
+
byParent.set(p, []);
|
|
608
|
+
byParent.get(p).push(c);
|
|
609
|
+
}
|
|
610
|
+
const toRemove = new Set();
|
|
611
|
+
const toAdd = [];
|
|
612
|
+
const parentToChildren = new Map();
|
|
613
|
+
for (const [parent, children] of byParent) {
|
|
614
|
+
if (children.length < FAMILY_THRESHOLD)
|
|
615
|
+
continue;
|
|
616
|
+
const parentCluster = clusters.find((c) => c.id === parent);
|
|
617
|
+
if (parentCluster) {
|
|
618
|
+
// Parent already a cluster — absorb children's signals into it, remove children.
|
|
619
|
+
// Routes, models, exports, envs, orms are unioned in; parent keeps its own signals.
|
|
620
|
+
const allRoutes = [...new Set([...parentCluster.routes, ...children.flatMap((c) => c.routes)])];
|
|
621
|
+
const allModels = [...new Set([...parentCluster.models, ...children.flatMap((c) => c.models)])];
|
|
622
|
+
const allExports = [...new Set([...parentCluster.exports, ...children.flatMap((c) => c.exports)])].slice(0, 10);
|
|
623
|
+
const allEnvs = [...new Set([...(parentCluster.env_consumes ?? []), ...children.flatMap((c) => c.env_consumes ?? [])])];
|
|
624
|
+
const allOrms = [...new Set([...(parentCluster.uses_orm ?? []), ...children.flatMap((c) => c.uses_orm ?? [])])];
|
|
625
|
+
const allCalls = [...new Set([...(parentCluster.runtime_calls ?? []), ...children.flatMap((c) => c.runtime_calls ?? [])])];
|
|
626
|
+
parentCluster.routes = allRoutes;
|
|
627
|
+
parentCluster.models = allModels;
|
|
628
|
+
parentCluster.exports = allExports;
|
|
629
|
+
parentCluster.files += children.reduce((s, c) => s + c.files, 0);
|
|
630
|
+
parentCluster.centrality = Math.max(parentCluster.centrality, ...children.map((c) => c.centrality));
|
|
631
|
+
if (allEnvs.length)
|
|
632
|
+
parentCluster.env_consumes = allEnvs;
|
|
633
|
+
if (allOrms.length)
|
|
634
|
+
parentCluster.uses_orm = allOrms;
|
|
635
|
+
if (allCalls.length)
|
|
636
|
+
parentCluster.runtime_calls = allCalls;
|
|
637
|
+
for (const c of children)
|
|
638
|
+
toRemove.add(c.id);
|
|
639
|
+
parentToChildren.set(parent, children.map((c) => c.id));
|
|
640
|
+
continue;
|
|
641
|
+
}
|
|
642
|
+
const merged = {
|
|
643
|
+
id: parent,
|
|
644
|
+
name: parent.split("/").pop() ?? parent,
|
|
645
|
+
files: children.reduce((s, c) => s + c.files, 0),
|
|
646
|
+
exports: [...new Set(children.flatMap((c) => c.exports))].slice(0, 10),
|
|
647
|
+
imports_from: [...new Set(children.flatMap((c) => c.imports_from))],
|
|
648
|
+
imported_by: [...new Set(children.flatMap((c) => c.imported_by))],
|
|
649
|
+
is_entry: children.some((c) => c.is_entry),
|
|
650
|
+
is_shared: children.some((c) => c.is_shared),
|
|
651
|
+
centrality: Math.max(...children.map((c) => c.centrality)),
|
|
652
|
+
routes: [...new Set(children.flatMap((c) => c.routes))],
|
|
653
|
+
models: [...new Set(children.flatMap((c) => c.models))],
|
|
654
|
+
signals: {
|
|
655
|
+
tests: children.some((c) => c.signals.tests) ? true : undefined,
|
|
656
|
+
types: children.some((c) => c.signals.types) ? true : undefined,
|
|
657
|
+
config: children.some((c) => c.signals.config) ? true : undefined,
|
|
658
|
+
},
|
|
659
|
+
};
|
|
660
|
+
const envs = [...new Set(children.flatMap((c) => c.env_consumes ?? []))];
|
|
661
|
+
const orms = [...new Set(children.flatMap((c) => c.uses_orm ?? []))];
|
|
662
|
+
const calls = [...new Set(children.flatMap((c) => c.runtime_calls ?? []))];
|
|
663
|
+
if (envs.length)
|
|
664
|
+
merged.env_consumes = envs;
|
|
665
|
+
if (orms.length)
|
|
666
|
+
merged.uses_orm = orms;
|
|
667
|
+
if (calls.length)
|
|
668
|
+
merged.runtime_calls = calls;
|
|
669
|
+
for (const c of children)
|
|
670
|
+
toRemove.add(c.id);
|
|
671
|
+
toAdd.push(merged);
|
|
672
|
+
parentToChildren.set(parent, children.map((c) => c.id));
|
|
673
|
+
}
|
|
674
|
+
return {
|
|
675
|
+
clusters: [...clusters.filter((c) => !toRemove.has(c.id)), ...toAdd],
|
|
676
|
+
parentToChildren,
|
|
677
|
+
};
|
|
678
|
+
}
|
|
679
|
+
/**
|
|
680
|
+
* Split the full payload into deterministic (classified) and ambiguous clusters.
|
|
681
|
+
* Returns the ambiguous-only payload string for LLM + the deterministic clusters
|
|
682
|
+
* for pre-naming without an API call. Core of Phase 3.
|
|
683
|
+
* Also runs family promotion so sibling-heavy directories appear as one cluster.
|
|
684
|
+
*/
|
|
685
|
+
function buildAmbiguousPayload(graph) {
|
|
686
|
+
const { payload } = buildLLMPayloadWithMeta(graph);
|
|
687
|
+
const parsed = JSON.parse(payload);
|
|
688
|
+
const routeFamilies = parsed.meta.route_families ?? [];
|
|
689
|
+
const deterministicClusters = parsed.clusters.filter((c) => c.archetype !== undefined);
|
|
690
|
+
// Infra/synthetic nodes (kind set = not a real cluster) are architecture facts captured
|
|
691
|
+
// in uses_infra — they must NOT become named systems on the AM.
|
|
692
|
+
let ambiguousClusters = parsed.clusters.filter((c) => c.archetype === undefined && !c.kind);
|
|
693
|
+
// Family promotion: merge siblings → parent before sending to LLM
|
|
694
|
+
const { clusters: promoted, parentToChildren } = promoteFamilies(ambiguousClusters);
|
|
695
|
+
ambiguousClusters = promoted;
|
|
696
|
+
if (ambiguousClusters.length === 0) {
|
|
697
|
+
return { ambiguousPayloadStr: "", deterministicClusters, routeFamilies, parentToChildren };
|
|
698
|
+
}
|
|
699
|
+
const ambiguousIds = new Set(ambiguousClusters.map((c) => c.id));
|
|
700
|
+
const ambiguousPayload = {
|
|
701
|
+
meta: parsed.meta,
|
|
702
|
+
clusters: ambiguousClusters,
|
|
703
|
+
top_edges: (parsed.top_edges ?? []).filter((e) => ambiguousIds.has(e.from) && ambiguousIds.has(e.to)),
|
|
704
|
+
...(parsed.runtime_edges
|
|
705
|
+
? { runtime_edges: parsed.runtime_edges.filter((e) => ambiguousIds.has(e.from) || ambiguousIds.has(e.to)) }
|
|
706
|
+
: {}),
|
|
707
|
+
};
|
|
708
|
+
return {
|
|
709
|
+
ambiguousPayloadStr: JSON.stringify(ambiguousPayload),
|
|
710
|
+
deterministicClusters,
|
|
711
|
+
routeFamilies,
|
|
712
|
+
parentToChildren,
|
|
713
|
+
};
|
|
714
|
+
}
|
|
715
|
+
//# sourceMappingURL=buildLLMPayload.js.map
|