autosnippet 3.2.21 → 3.3.2
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/dashboard/dist/assets/icons-BJ2mUBi8.js +1 -0
- package/dashboard/dist/assets/index-B659K9t5.js +128 -0
- package/dashboard/dist/assets/index-NCm40PMD.css +1 -0
- package/dashboard/dist/index.html +3 -3
- package/dist/bin/cli.d.ts +1 -1
- package/dist/bin/cli.js +244 -261
- package/dist/lib/agent/context/ExplorationTracker.d.ts +2 -0
- package/dist/lib/agent/context/ExplorationTracker.js +21 -3
- package/dist/lib/agent/core/ToolExecutionPipeline.d.ts +3 -1
- package/dist/lib/agent/core/ToolExecutionPipeline.js +8 -1
- package/dist/lib/agent/forge/DynamicComposer.d.ts +58 -0
- package/dist/lib/agent/forge/DynamicComposer.js +99 -0
- package/dist/lib/agent/forge/SandboxRunner.d.ts +60 -0
- package/dist/lib/agent/forge/SandboxRunner.js +251 -0
- package/dist/lib/agent/forge/TemporaryToolRegistry.d.ts +76 -0
- package/dist/lib/agent/forge/TemporaryToolRegistry.js +154 -0
- package/dist/lib/agent/forge/ToolForge.d.ts +92 -0
- package/dist/lib/agent/forge/ToolForge.js +239 -0
- package/dist/lib/agent/forge/ToolRequirementAnalyzer.d.ts +44 -0
- package/dist/lib/agent/forge/ToolRequirementAnalyzer.js +119 -0
- package/dist/lib/agent/tools/ToolRegistry.d.ts +2 -0
- package/dist/lib/agent/tools/ToolRegistry.js +4 -0
- package/dist/lib/agent/tools/composite.js +0 -1
- package/dist/lib/agent/tools/index.d.ts +2 -50
- package/dist/lib/agent/tools/index.js +2 -3
- package/dist/lib/agent/tools/lifecycle.d.ts +1 -58
- package/dist/lib/agent/tools/lifecycle.js +2 -75
- package/dist/lib/cli/SetupService.d.ts +46 -2
- package/dist/lib/cli/SetupService.js +2 -27
- package/dist/lib/cli/deploy/FileManifest.d.ts +0 -21
- package/dist/lib/cli/deploy/FileManifest.js +0 -11
- package/dist/lib/{platform/ios/spm → core/discovery}/SpmDiscoverer.d.ts +2 -5
- package/dist/lib/{platform/ios/spm → core/discovery}/SpmDiscoverer.js +159 -44
- package/dist/lib/core/discovery/index.d.ts +1 -1
- package/dist/lib/core/discovery/index.js +2 -2
- package/dist/lib/domain/knowledge/KnowledgeEntry.d.ts +10 -0
- package/dist/lib/domain/knowledge/KnowledgeEntry.js +2 -0
- package/dist/lib/domain/knowledge/Lifecycle.d.ts +19 -2
- package/dist/lib/domain/knowledge/Lifecycle.js +32 -6
- package/dist/lib/domain/knowledge/UnifiedValidator.d.ts +1 -5
- package/dist/lib/domain/knowledge/UnifiedValidator.js +7 -44
- package/dist/lib/domain/knowledge/values/Stats.d.ts +29 -0
- package/dist/lib/domain/knowledge/values/Stats.js +41 -0
- package/dist/lib/external/mcp/McpServer.d.ts +19 -38
- package/dist/lib/external/mcp/McpServer.js +145 -117
- package/dist/lib/external/mcp/autoApproveInjector.js +0 -2
- package/dist/lib/external/mcp/handlers/bootstrap/MissionBriefingBuilder.d.ts +26 -1
- package/dist/lib/external/mcp/handlers/bootstrap/MissionBriefingBuilder.js +41 -0
- package/dist/lib/external/mcp/handlers/bootstrap/pipeline/orchestrator.js +49 -0
- package/dist/lib/external/mcp/handlers/bootstrap/shared/bootstrap-phases.d.ts +3 -0
- package/dist/lib/external/mcp/handlers/bootstrap/shared/bootstrap-phases.js +27 -0
- package/dist/lib/external/mcp/handlers/bootstrap/skills.js +1 -1
- package/dist/lib/external/mcp/handlers/bootstrap-external.js +1 -0
- package/dist/lib/external/mcp/handlers/bootstrap-internal.js +2 -0
- package/dist/lib/external/mcp/handlers/consolidated.d.ts +116 -6
- package/dist/lib/external/mcp/handlers/consolidated.js +251 -71
- package/dist/lib/external/mcp/handlers/guard.d.ts +150 -0
- package/dist/lib/external/mcp/handlers/guard.js +245 -8
- package/dist/lib/external/mcp/handlers/knowledge.d.ts +0 -29
- package/dist/lib/external/mcp/handlers/knowledge.js +1 -76
- package/dist/lib/external/mcp/handlers/panorama.d.ts +36 -0
- package/dist/lib/external/mcp/handlers/panorama.js +156 -0
- package/dist/lib/external/mcp/handlers/system.d.ts +2 -54
- package/dist/lib/external/mcp/handlers/system.js +3 -113
- package/dist/lib/external/mcp/handlers/task.d.ts +13 -24
- package/dist/lib/external/mcp/handlers/task.js +217 -557
- package/dist/lib/external/mcp/handlers/types.d.ts +91 -8
- package/dist/lib/external/mcp/handlers/types.js +18 -1
- package/dist/lib/external/mcp/handlers/wiki-external.d.ts +18 -1
- package/dist/lib/external/mcp/handlers/wiki-external.js +16 -1
- package/dist/lib/external/mcp/tools.d.ts +14 -20
- package/dist/lib/external/mcp/tools.js +62 -91
- package/dist/lib/http/HttpServer.js +52 -6
- package/dist/lib/http/routes/{snippets.d.ts → audit.d.ts} +4 -2
- package/dist/lib/http/routes/audit.js +51 -0
- package/dist/lib/http/routes/commands.d.ts +1 -1
- package/dist/lib/http/routes/commands.js +1 -66
- package/dist/lib/http/routes/guardReport.d.ts +10 -0
- package/dist/lib/http/routes/guardReport.js +143 -0
- package/dist/lib/http/routes/knowledge.js +32 -1
- package/dist/lib/http/routes/panorama.d.ts +11 -0
- package/dist/lib/http/routes/panorama.js +322 -0
- package/dist/lib/http/routes/remote.js +0 -5
- package/dist/lib/http/routes/signals.d.ts +10 -0
- package/dist/lib/http/routes/signals.js +104 -0
- package/dist/lib/http/routes/task.d.ts +2 -3
- package/dist/lib/http/routes/task.js +17 -347
- package/dist/lib/http/routes/violations.js +1 -1
- package/dist/lib/infrastructure/audit/AuditLogger.d.ts +6 -1
- package/dist/lib/infrastructure/audit/AuditLogger.js +14 -1
- package/dist/lib/infrastructure/database/drizzle/schema.d.ts +181 -583
- package/dist/lib/infrastructure/database/drizzle/schema.js +28 -69
- package/dist/lib/infrastructure/database/migrations/004_evolution_proposals.d.ts +8 -0
- package/dist/lib/infrastructure/database/migrations/004_evolution_proposals.js +43 -0
- package/dist/lib/infrastructure/logging/Logger.d.ts +2 -0
- package/dist/lib/infrastructure/logging/Logger.js +34 -7
- package/dist/lib/infrastructure/monitoring/ErrorTracker.js +3 -1
- package/dist/lib/infrastructure/monitoring/PerformanceMonitor.d.ts +2 -2
- package/dist/lib/infrastructure/monitoring/PerformanceMonitor.js +12 -10
- package/dist/lib/infrastructure/notification/LarkNotifier.d.ts +24 -0
- package/dist/lib/infrastructure/notification/LarkNotifier.js +97 -0
- package/dist/lib/infrastructure/report/ReportStore.d.ts +45 -0
- package/dist/lib/infrastructure/report/ReportStore.js +133 -0
- package/dist/lib/infrastructure/signal/SignalAggregator.d.ts +18 -0
- package/dist/lib/infrastructure/signal/SignalAggregator.js +84 -0
- package/dist/lib/infrastructure/signal/SignalBridge.d.ts +13 -0
- package/dist/lib/infrastructure/signal/SignalBridge.js +20 -0
- package/dist/lib/infrastructure/signal/SignalBus.d.ts +63 -0
- package/dist/lib/infrastructure/signal/SignalBus.js +106 -0
- package/dist/lib/infrastructure/signal/SignalTraceWriter.d.ts +36 -0
- package/dist/lib/infrastructure/signal/SignalTraceWriter.js +130 -0
- package/dist/lib/injection/ServiceContainer.js +6 -0
- package/dist/lib/injection/ServiceMap.d.ts +16 -19
- package/dist/lib/injection/modules/AgentModule.d.ts +1 -1
- package/dist/lib/injection/modules/AgentModule.js +7 -1
- package/dist/lib/injection/modules/AppModule.d.ts +3 -4
- package/dist/lib/injection/modules/AppModule.js +7 -43
- package/dist/lib/injection/modules/GuardModule.js +59 -2
- package/dist/lib/injection/modules/InfraModule.d.ts +0 -1
- package/dist/lib/injection/modules/InfraModule.js +9 -7
- package/dist/lib/injection/modules/KnowledgeModule.js +51 -0
- package/dist/lib/injection/modules/PanoramaModule.d.ts +18 -0
- package/dist/lib/injection/modules/PanoramaModule.js +76 -0
- package/dist/lib/injection/modules/SignalModule.d.ts +10 -0
- package/dist/lib/injection/modules/SignalModule.js +84 -0
- package/dist/lib/repository/knowledge/KnowledgeRepository.impl.d.ts +1 -0
- package/dist/lib/repository/knowledge/KnowledgeRepository.impl.js +6 -0
- package/dist/lib/service/bootstrap/BootstrapTaskManager.d.ts +3 -1
- package/dist/lib/service/bootstrap/BootstrapTaskManager.js +20 -1
- package/dist/lib/service/delivery/AgentInstructionsGenerator.js +4 -5
- package/dist/lib/service/delivery/CursorDeliveryPipeline.d.ts +3 -1
- package/dist/lib/service/delivery/CursorDeliveryPipeline.js +13 -10
- package/dist/lib/service/delivery/RulesGenerator.js +3 -2
- package/dist/lib/service/evolution/ConsolidationAdvisor.d.ts +114 -0
- package/dist/lib/service/evolution/ConsolidationAdvisor.js +542 -0
- package/dist/lib/service/evolution/ContradictionDetector.d.ts +54 -0
- package/dist/lib/service/evolution/ContradictionDetector.js +253 -0
- package/dist/lib/service/evolution/DecayDetector.d.ts +71 -0
- package/dist/lib/service/evolution/DecayDetector.js +244 -0
- package/dist/lib/service/evolution/EnhancementSuggester.d.ts +38 -0
- package/dist/lib/service/evolution/EnhancementSuggester.js +220 -0
- package/dist/lib/service/evolution/KnowledgeMetabolism.d.ts +82 -0
- package/dist/lib/service/evolution/KnowledgeMetabolism.js +167 -0
- package/dist/lib/service/evolution/RedundancyAnalyzer.d.ts +53 -0
- package/dist/lib/service/evolution/RedundancyAnalyzer.js +210 -0
- package/dist/lib/service/evolution/StagingManager.d.ts +57 -0
- package/dist/lib/service/evolution/StagingManager.js +201 -0
- package/dist/lib/service/guard/ComplianceReporter.d.ts +42 -2
- package/dist/lib/service/guard/ComplianceReporter.js +43 -5
- package/dist/lib/service/guard/CoverageAnalyzer.d.ts +54 -0
- package/dist/lib/service/guard/CoverageAnalyzer.js +149 -0
- package/dist/lib/service/guard/GuardCheckEngine.d.ts +55 -1
- package/dist/lib/service/guard/GuardCheckEngine.js +508 -15
- package/dist/lib/service/guard/GuardFeedbackLoop.d.ts +3 -0
- package/dist/lib/service/guard/GuardFeedbackLoop.js +9 -0
- package/dist/lib/service/guard/ReverseGuard.d.ts +73 -0
- package/dist/lib/service/guard/ReverseGuard.js +256 -0
- package/dist/lib/service/guard/RuleLearner.d.ts +12 -0
- package/dist/lib/service/guard/RuleLearner.js +38 -0
- package/dist/lib/service/guard/UncertaintyCollector.d.ts +83 -0
- package/dist/lib/service/guard/UncertaintyCollector.js +149 -0
- package/dist/lib/service/guard/ViolationsStore.d.ts +1 -0
- package/dist/lib/service/guard/ViolationsStore.js +33 -3
- package/dist/lib/service/knowledge/ConfidenceRouter.d.ts +13 -0
- package/dist/lib/service/knowledge/ConfidenceRouter.js +14 -0
- package/dist/lib/service/knowledge/KnowledgeService.js +22 -4
- package/dist/lib/service/module/ModuleService.js +3 -13
- package/dist/lib/service/panorama/CouplingAnalyzer.d.ts +27 -0
- package/dist/lib/service/panorama/CouplingAnalyzer.js +192 -0
- package/dist/lib/service/panorama/DimensionAnalyzer.d.ts +28 -0
- package/dist/lib/service/panorama/DimensionAnalyzer.js +320 -0
- package/dist/lib/service/panorama/LayerInferrer.d.ts +19 -0
- package/dist/lib/service/panorama/LayerInferrer.js +182 -0
- package/dist/lib/service/panorama/ModuleDiscoverer.d.ts +24 -0
- package/dist/lib/service/panorama/ModuleDiscoverer.js +185 -0
- package/dist/lib/service/panorama/PanoramaAggregator.d.ts +29 -0
- package/dist/lib/service/panorama/PanoramaAggregator.js +228 -0
- package/dist/lib/service/panorama/PanoramaScanner.d.ts +52 -0
- package/dist/lib/service/panorama/PanoramaScanner.js +188 -0
- package/dist/lib/service/panorama/PanoramaService.d.ts +108 -0
- package/dist/lib/service/panorama/PanoramaService.js +220 -0
- package/dist/lib/service/panorama/PanoramaTypes.d.ts +134 -0
- package/dist/lib/service/panorama/PanoramaTypes.js +6 -0
- package/dist/lib/service/panorama/RoleRefiner.d.ts +48 -0
- package/dist/lib/service/panorama/RoleRefiner.js +535 -0
- package/dist/lib/service/search/MultiSignalRanker.d.ts +1 -0
- package/dist/lib/service/search/MultiSignalRanker.js +16 -0
- package/dist/lib/service/search/SearchEngine.d.ts +1 -0
- package/dist/lib/service/search/SearchEngine.js +9 -1
- package/dist/lib/service/search/SearchTypes.d.ts +2 -0
- package/dist/lib/service/signal/HitRecorder.d.ts +68 -0
- package/dist/lib/service/signal/HitRecorder.js +173 -0
- package/dist/lib/service/skills/SignalCollector.d.ts +3 -1
- package/dist/lib/service/skills/SignalCollector.js +31 -1
- package/dist/lib/service/task/IntentExtractor.d.ts +58 -0
- package/dist/lib/service/task/IntentExtractor.js +142 -0
- package/dist/lib/service/task/PrimeSearchPipeline.d.ts +54 -0
- package/dist/lib/service/task/PrimeSearchPipeline.js +98 -0
- package/dist/lib/shared/constants.d.ts +0 -15
- package/dist/lib/shared/constants.js +0 -10
- package/dist/lib/shared/schemas/config.d.ts +4 -1
- package/dist/lib/shared/schemas/config.js +8 -1
- package/dist/lib/shared/schemas/mcp-tools.d.ts +41 -96
- package/dist/lib/shared/schemas/mcp-tools.js +59 -119
- package/dist/scripts/analyze-signals.d.ts +20 -0
- package/dist/scripts/analyze-signals.js +155 -0
- package/dist/scripts/diagnose-mcp.js +1 -1
- package/dist/scripts/release.js +2 -10
- package/package.json +4 -19
- package/skills/autosnippet-devdocs/SKILL.md +11 -8
- package/templates/claude-code/hooks/autosnippet-session.sh +10 -15
- package/templates/cursor-hooks/hooks/session-start.sh +1 -1
- package/templates/instructions/agent-static.md +2 -1
- package/templates/instructions/conventions.md +5 -6
- package/templates/recipes-setup/README.md +1 -2
- package/dashboard/dist/assets/icons-C1dUryS-.js +0 -1
- package/dashboard/dist/assets/index-D0whuycy.css +0 -1
- package/dashboard/dist/assets/index-DdvZE4Yd.js +0 -128
- package/dist/lib/domain/task/Task.d.ts +0 -140
- package/dist/lib/domain/task/Task.js +0 -254
- package/dist/lib/domain/task/TaskDependency.d.ts +0 -23
- package/dist/lib/domain/task/TaskDependency.js +0 -34
- package/dist/lib/domain/task/TaskIdGenerator.d.ts +0 -40
- package/dist/lib/domain/task/TaskIdGenerator.js +0 -75
- package/dist/lib/domain/task/index.d.ts +0 -4
- package/dist/lib/domain/task/index.js +0 -4
- package/dist/lib/http/routes/snippets.js +0 -49
- package/dist/lib/infrastructure/database/migrations/002_add_tasks.d.ts +0 -11
- package/dist/lib/infrastructure/database/migrations/002_add_tasks.js +0 -86
- package/dist/lib/platform/ClipboardManager.d.ts +0 -24
- package/dist/lib/platform/ClipboardManager.js +0 -142
- package/dist/lib/platform/NativeUi.d.ts +0 -53
- package/dist/lib/platform/NativeUi.js +0 -284
- package/dist/lib/platform/ios/index.d.ts +0 -38
- package/dist/lib/platform/ios/index.js +0 -42
- package/dist/lib/platform/ios/routes/spm.d.ts +0 -9
- package/dist/lib/platform/ios/routes/spm.js +0 -371
- package/dist/lib/platform/ios/snippet/PlaceholderConverter.d.ts +0 -21
- package/dist/lib/platform/ios/snippet/PlaceholderConverter.js +0 -48
- package/dist/lib/platform/ios/snippet/XcodeCodec.d.ts +0 -23
- package/dist/lib/platform/ios/snippet/XcodeCodec.js +0 -96
- package/dist/lib/platform/ios/spm/DependencyGraph.d.ts +0 -56
- package/dist/lib/platform/ios/spm/DependencyGraph.js +0 -195
- package/dist/lib/platform/ios/spm/PackageSwiftParser.d.ts +0 -69
- package/dist/lib/platform/ios/spm/PackageSwiftParser.js +0 -231
- package/dist/lib/platform/ios/spm/PathFinder.d.ts +0 -28
- package/dist/lib/platform/ios/spm/PathFinder.js +0 -117
- package/dist/lib/platform/ios/spm/PolicyEngine.d.ts +0 -44
- package/dist/lib/platform/ios/spm/PolicyEngine.js +0 -79
- package/dist/lib/platform/ios/spm/SpmHelper.d.ts +0 -102
- package/dist/lib/platform/ios/spm/SpmHelper.js +0 -464
- package/dist/lib/platform/ios/xcode/HeaderResolver.d.ts +0 -33
- package/dist/lib/platform/ios/xcode/HeaderResolver.js +0 -90
- package/dist/lib/platform/ios/xcode/SaveEventFilter.d.ts +0 -66
- package/dist/lib/platform/ios/xcode/SaveEventFilter.js +0 -142
- package/dist/lib/platform/ios/xcode/XcodeAutomation.d.ts +0 -71
- package/dist/lib/platform/ios/xcode/XcodeAutomation.js +0 -327
- package/dist/lib/platform/ios/xcode/XcodeImportResolver.d.ts +0 -130
- package/dist/lib/platform/ios/xcode/XcodeImportResolver.js +0 -404
- package/dist/lib/platform/ios/xcode/XcodeIntegration.d.ts +0 -89
- package/dist/lib/platform/ios/xcode/XcodeIntegration.js +0 -588
- package/dist/lib/platform/ios/xcode/XcodeWriteUtils.d.ts +0 -99
- package/dist/lib/platform/ios/xcode/XcodeWriteUtils.js +0 -190
- package/dist/lib/repository/task/TaskRepository.impl.d.ts +0 -171
- package/dist/lib/repository/task/TaskRepository.impl.js +0 -347
- package/dist/lib/service/automation/ActionPipeline.d.ts +0 -34
- package/dist/lib/service/automation/ActionPipeline.js +0 -53
- package/dist/lib/service/automation/AutomationOrchestrator.d.ts +0 -86
- package/dist/lib/service/automation/AutomationOrchestrator.js +0 -57
- package/dist/lib/service/automation/ContextCollector.d.ts +0 -24
- package/dist/lib/service/automation/ContextCollector.js +0 -35
- package/dist/lib/service/automation/DirectiveDetector.d.ts +0 -51
- package/dist/lib/service/automation/DirectiveDetector.js +0 -112
- package/dist/lib/service/automation/FileWatcher.d.ts +0 -51
- package/dist/lib/service/automation/FileWatcher.js +0 -366
- package/dist/lib/service/automation/TriggerResolver.d.ts +0 -36
- package/dist/lib/service/automation/TriggerResolver.js +0 -62
- package/dist/lib/service/automation/handlers/AlinkHandler.d.ts +0 -7
- package/dist/lib/service/automation/handlers/AlinkHandler.js +0 -80
- package/dist/lib/service/automation/handlers/CreateHandler.d.ts +0 -11
- package/dist/lib/service/automation/handlers/CreateHandler.js +0 -170
- package/dist/lib/service/automation/handlers/GuardHandler.d.ts +0 -17
- package/dist/lib/service/automation/handlers/GuardHandler.js +0 -218
- package/dist/lib/service/automation/handlers/HeaderHandler.d.ts +0 -2
- package/dist/lib/service/automation/handlers/HeaderHandler.js +0 -32
- package/dist/lib/service/automation/handlers/SearchHandler.d.ts +0 -11
- package/dist/lib/service/automation/handlers/SearchHandler.js +0 -278
- package/dist/lib/service/snippet/SnippetFactory.d.ts +0 -101
- package/dist/lib/service/snippet/SnippetFactory.js +0 -145
- package/dist/lib/service/snippet/SnippetInstaller.d.ts +0 -91
- package/dist/lib/service/snippet/SnippetInstaller.js +0 -276
- package/dist/lib/service/snippet/codecs/SnippetCodec.d.ts +0 -44
- package/dist/lib/service/snippet/codecs/SnippetCodec.js +0 -35
- package/dist/lib/service/snippet/codecs/VSCodeCodec.d.ts +0 -27
- package/dist/lib/service/snippet/codecs/VSCodeCodec.js +0 -82
- package/dist/lib/service/task/TaskGraphService.d.ts +0 -222
- package/dist/lib/service/task/TaskGraphService.js +0 -597
- package/dist/lib/service/task/TaskKnowledgeBridge.d.ts +0 -95
- package/dist/lib/service/task/TaskKnowledgeBridge.js +0 -298
- package/dist/lib/service/task/TaskReadyEngine.d.ts +0 -84
- package/dist/lib/service/task/TaskReadyEngine.js +0 -115
- package/dist/scripts/build-native-ui.d.ts +0 -3
- package/dist/scripts/build-native-ui.js +0 -62
- package/dist/scripts/init-snippets.d.ts +0 -30
- package/dist/scripts/init-snippets.js +0 -298
- package/dist/scripts/install-full.d.ts +0 -7
- package/dist/scripts/install-full.js +0 -38
- package/resources/native-ui/README.md +0 -29
- package/resources/native-ui/combined-window.swift +0 -494
- package/resources/native-ui/main.swift +0 -598
- package/scripts/postinstall-safe.mjs +0 -89
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ModuleDiscoverer — 模块发现与文件归属
|
|
3
|
+
*
|
|
4
|
+
* 从 DB(code_entities / knowledge_edges)读取已扫描的模块数据。
|
|
5
|
+
* 前提:PanoramaScanner.ensureData() 保证 DB 中已有结构数据。
|
|
6
|
+
*
|
|
7
|
+
* 策略 1: code_entities entity_type='module' + is_part_of 边 → 完整数据
|
|
8
|
+
* 策略 1.5: module 实体存在但无 is_part_of 边 → 文件系统 + DB 路径补全
|
|
9
|
+
*
|
|
10
|
+
* 若 DB 中无 module 实体,返回空数组(由 PanoramaScanner 负责兜底扫描)。
|
|
11
|
+
*
|
|
12
|
+
* @module ModuleDiscoverer
|
|
13
|
+
*/
|
|
14
|
+
import fs from 'node:fs';
|
|
15
|
+
import path from 'node:path';
|
|
16
|
+
import { inferTargetRole } from '../../external/mcp/handlers/TargetClassifier.js';
|
|
17
|
+
/* ═══ Constants ═══════════════════════════════════════════ */
|
|
18
|
+
const SOURCE_EXTS = new Set([
|
|
19
|
+
'.swift',
|
|
20
|
+
'.ts',
|
|
21
|
+
'.tsx',
|
|
22
|
+
'.js',
|
|
23
|
+
'.jsx',
|
|
24
|
+
'.m',
|
|
25
|
+
'.mm',
|
|
26
|
+
'.h',
|
|
27
|
+
'.c',
|
|
28
|
+
'.cpp',
|
|
29
|
+
'.kt',
|
|
30
|
+
'.java',
|
|
31
|
+
'.py',
|
|
32
|
+
'.rb',
|
|
33
|
+
'.go',
|
|
34
|
+
'.rs',
|
|
35
|
+
]);
|
|
36
|
+
const SKIP_DIRS = new Set([
|
|
37
|
+
'.git',
|
|
38
|
+
'.build',
|
|
39
|
+
'.autosnippet',
|
|
40
|
+
'node_modules',
|
|
41
|
+
'build',
|
|
42
|
+
'Pods',
|
|
43
|
+
'DerivedData',
|
|
44
|
+
'.swiftpm',
|
|
45
|
+
'__pycache__',
|
|
46
|
+
'dist',
|
|
47
|
+
]);
|
|
48
|
+
/* ═══ ModuleDiscoverer Class ══════════════════════════════ */
|
|
49
|
+
export class ModuleDiscoverer {
|
|
50
|
+
#db;
|
|
51
|
+
#projectRoot;
|
|
52
|
+
constructor(db, projectRoot) {
|
|
53
|
+
this.#db = db;
|
|
54
|
+
this.#projectRoot = projectRoot;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* 从 DB 中读取已扫描的模块数据。
|
|
58
|
+
* 若无 module 实体,返回空数组(让调用侧决定是否重新扫描)。
|
|
59
|
+
*/
|
|
60
|
+
discover() {
|
|
61
|
+
// 从 code_entities 查 entity_type = 'module'
|
|
62
|
+
const moduleEntities = this.#db
|
|
63
|
+
.prepare(`SELECT DISTINCT entity_id, name FROM code_entities
|
|
64
|
+
WHERE entity_type = 'module' AND project_root = ?`)
|
|
65
|
+
.all(this.#projectRoot);
|
|
66
|
+
if (moduleEntities.length === 0) {
|
|
67
|
+
return [];
|
|
68
|
+
}
|
|
69
|
+
// 收集 is_part_of 边关联的文件
|
|
70
|
+
const moduleFiles = new Map();
|
|
71
|
+
for (const me of moduleEntities) {
|
|
72
|
+
const moduleName = me.entity_id;
|
|
73
|
+
moduleFiles.set(moduleName, new Set());
|
|
74
|
+
const parts = this.#db
|
|
75
|
+
.prepare(`SELECT ke.from_id FROM knowledge_edges ke
|
|
76
|
+
WHERE ke.to_id = ? AND ke.to_type = 'module' AND ke.relation = 'is_part_of'`)
|
|
77
|
+
.all(moduleName);
|
|
78
|
+
for (const part of parts) {
|
|
79
|
+
const entity = this.#db
|
|
80
|
+
.prepare(`SELECT file_path FROM code_entities
|
|
81
|
+
WHERE entity_id = ? AND project_root = ? LIMIT 1`)
|
|
82
|
+
.get(part.from_id, this.#projectRoot);
|
|
83
|
+
if (entity?.file_path) {
|
|
84
|
+
moduleFiles.get(moduleName).add(entity.file_path);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
// 策略 1.5: module 实体有但文件为空(SPM 只建了模块节点)
|
|
89
|
+
const totalFileCount = [...moduleFiles.values()].reduce((sum, s) => sum + s.size, 0);
|
|
90
|
+
if (totalFileCount === 0) {
|
|
91
|
+
this.#enrichModuleFiles(moduleFiles);
|
|
92
|
+
}
|
|
93
|
+
return [...moduleFiles.entries()].map(([name, files]) => ({
|
|
94
|
+
name,
|
|
95
|
+
inferredRole: inferTargetRole(name),
|
|
96
|
+
files: [...files],
|
|
97
|
+
}));
|
|
98
|
+
}
|
|
99
|
+
/* ─── 策略 1.5: 模块文件充填 ───────────────────── */
|
|
100
|
+
/**
|
|
101
|
+
* 为已知模块名填充文件路径:
|
|
102
|
+
* a. 文件系统扫描(递归 4 层找模块同名目录)
|
|
103
|
+
* b. DB code_entities.file_path 路径段匹配
|
|
104
|
+
*/
|
|
105
|
+
#enrichModuleFiles(moduleFiles) {
|
|
106
|
+
const moduleNames = [...moduleFiles.keys()];
|
|
107
|
+
// a. 文件系统扫描
|
|
108
|
+
for (const modName of moduleNames) {
|
|
109
|
+
const dir = this.#findModuleDir(this.#projectRoot, modName, 4);
|
|
110
|
+
if (dir) {
|
|
111
|
+
for (const f of this.#collectSourceFiles(dir)) {
|
|
112
|
+
moduleFiles.get(modName).add(f);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
// b. 如果 FS 扫描仍为空 → DB 路径匹配
|
|
117
|
+
const totalAfterFs = [...moduleFiles.values()].reduce((sum, s) => sum + s.size, 0);
|
|
118
|
+
if (totalAfterFs > 0) {
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
const allFiles = this.#db
|
|
122
|
+
.prepare(`SELECT DISTINCT file_path FROM code_entities
|
|
123
|
+
WHERE project_root = ? AND file_path IS NOT NULL AND entity_type != 'module'`)
|
|
124
|
+
.all(this.#projectRoot);
|
|
125
|
+
// 长名优先,避免短名误匹配
|
|
126
|
+
const sorted = [...moduleNames].sort((a, b) => b.length - a.length);
|
|
127
|
+
for (const row of allFiles) {
|
|
128
|
+
const filePath = row.file_path;
|
|
129
|
+
if (!filePath) {
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
for (const modName of sorted) {
|
|
133
|
+
if (filePath.includes(`/${modName}/`) || filePath.startsWith(`${modName}/`)) {
|
|
134
|
+
moduleFiles.get(modName).add(filePath);
|
|
135
|
+
break; // 一个文件只属于一个模块
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
/* ─── 文件系统辅助 ────────────────────────────── */
|
|
141
|
+
#findModuleDir(rootDir, targetName, maxDepth) {
|
|
142
|
+
if (maxDepth <= 0) {
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
try {
|
|
146
|
+
const entries = fs.readdirSync(rootDir, { withFileTypes: true });
|
|
147
|
+
for (const entry of entries) {
|
|
148
|
+
if (!entry.isDirectory() || SKIP_DIRS.has(entry.name) || entry.name.startsWith('.')) {
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
const fullPath = path.join(rootDir, entry.name);
|
|
152
|
+
if (entry.name === targetName) {
|
|
153
|
+
return fullPath;
|
|
154
|
+
}
|
|
155
|
+
const found = this.#findModuleDir(fullPath, targetName, maxDepth - 1);
|
|
156
|
+
if (found) {
|
|
157
|
+
return found;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
catch {
|
|
162
|
+
// 无法读取目录
|
|
163
|
+
}
|
|
164
|
+
return null;
|
|
165
|
+
}
|
|
166
|
+
#collectSourceFiles(dir) {
|
|
167
|
+
const files = [];
|
|
168
|
+
try {
|
|
169
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
170
|
+
for (const entry of entries) {
|
|
171
|
+
const fullPath = path.join(dir, entry.name);
|
|
172
|
+
if (entry.isDirectory() && !entry.name.startsWith('.') && !SKIP_DIRS.has(entry.name)) {
|
|
173
|
+
files.push(...this.#collectSourceFiles(fullPath));
|
|
174
|
+
}
|
|
175
|
+
else if (entry.isFile() && SOURCE_EXTS.has(path.extname(entry.name).toLowerCase())) {
|
|
176
|
+
files.push(fullPath);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
catch {
|
|
181
|
+
// 无法读取
|
|
182
|
+
}
|
|
183
|
+
return files;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PanoramaAggregator — 全景数据汇总
|
|
3
|
+
*
|
|
4
|
+
* 编排 RoleRefiner → CouplingAnalyzer → LayerInferrer,
|
|
5
|
+
* 汇总为统一的 PanoramaResult,附加知识覆盖率和空白区检测。
|
|
6
|
+
*
|
|
7
|
+
* @module PanoramaAggregator
|
|
8
|
+
*/
|
|
9
|
+
import type { CouplingAnalyzer } from './CouplingAnalyzer.js';
|
|
10
|
+
import { DimensionAnalyzer } from './DimensionAnalyzer.js';
|
|
11
|
+
import type { LayerInferrer } from './LayerInferrer.js';
|
|
12
|
+
import type { CeDbLike, PanoramaResult } from './PanoramaTypes.js';
|
|
13
|
+
import type { ModuleCandidate, RoleRefiner } from './RoleRefiner.js';
|
|
14
|
+
export interface PanoramaAggregatorOptions {
|
|
15
|
+
roleRefiner: RoleRefiner;
|
|
16
|
+
couplingAnalyzer: CouplingAnalyzer;
|
|
17
|
+
layerInferrer: LayerInferrer;
|
|
18
|
+
db: CeDbLike;
|
|
19
|
+
projectRoot: string;
|
|
20
|
+
dimensionAnalyzer?: DimensionAnalyzer;
|
|
21
|
+
}
|
|
22
|
+
export declare class PanoramaAggregator {
|
|
23
|
+
#private;
|
|
24
|
+
constructor(opts: PanoramaAggregatorOptions);
|
|
25
|
+
/**
|
|
26
|
+
* 计算完整全景数据
|
|
27
|
+
*/
|
|
28
|
+
compute(moduleCandidates: ModuleCandidate[]): PanoramaResult;
|
|
29
|
+
}
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PanoramaAggregator — 全景数据汇总
|
|
3
|
+
*
|
|
4
|
+
* 编排 RoleRefiner → CouplingAnalyzer → LayerInferrer,
|
|
5
|
+
* 汇总为统一的 PanoramaResult,附加知识覆盖率和空白区检测。
|
|
6
|
+
*
|
|
7
|
+
* @module PanoramaAggregator
|
|
8
|
+
*/
|
|
9
|
+
var _a;
|
|
10
|
+
import { DimensionAnalyzer } from './DimensionAnalyzer.js';
|
|
11
|
+
/* ═══ PanoramaAggregator Class ════════════════════════════ */
|
|
12
|
+
export class PanoramaAggregator {
|
|
13
|
+
#roleRefiner;
|
|
14
|
+
#couplingAnalyzer;
|
|
15
|
+
#layerInferrer;
|
|
16
|
+
#db;
|
|
17
|
+
#projectRoot;
|
|
18
|
+
#dimensionAnalyzer;
|
|
19
|
+
constructor(opts) {
|
|
20
|
+
this.#roleRefiner = opts.roleRefiner;
|
|
21
|
+
this.#couplingAnalyzer = opts.couplingAnalyzer;
|
|
22
|
+
this.#layerInferrer = opts.layerInferrer;
|
|
23
|
+
this.#db = opts.db;
|
|
24
|
+
this.#projectRoot = opts.projectRoot;
|
|
25
|
+
this.#dimensionAnalyzer = opts.dimensionAnalyzer ?? new DimensionAnalyzer(opts.db);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* 计算完整全景数据
|
|
29
|
+
*/
|
|
30
|
+
compute(moduleCandidates) {
|
|
31
|
+
// 1. RoleRefiner: 精化角色
|
|
32
|
+
const refinedRoles = this.#roleRefiner.refineAll(moduleCandidates);
|
|
33
|
+
// 2. 构建模块-文件映射
|
|
34
|
+
const moduleFiles = new Map();
|
|
35
|
+
for (const mc of moduleCandidates) {
|
|
36
|
+
moduleFiles.set(mc.name, mc.files);
|
|
37
|
+
}
|
|
38
|
+
// 3. CouplingAnalyzer: 耦合分析
|
|
39
|
+
const coupling = this.#couplingAnalyzer.analyze(moduleFiles);
|
|
40
|
+
// 4. LayerInferrer: 层级推断
|
|
41
|
+
const modules = moduleCandidates.map((m) => m.name);
|
|
42
|
+
const layers = this.#layerInferrer.infer(coupling.edges, modules, coupling.cycles);
|
|
43
|
+
// 5. 构建层级映射 (模块名 → 层级号)
|
|
44
|
+
const moduleLayerMap = new Map();
|
|
45
|
+
for (const level of layers.levels) {
|
|
46
|
+
for (const mod of level.modules) {
|
|
47
|
+
moduleLayerMap.set(mod, level.level);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
// 6. 项目级 recipe 总数(recipe scope 通常为 universal,不做模块强关联)
|
|
51
|
+
const projectRecipeCount = this.#getProjectRecipeCount();
|
|
52
|
+
// 7. 计算总文件数
|
|
53
|
+
let totalFiles = 0;
|
|
54
|
+
for (const mc of moduleCandidates) {
|
|
55
|
+
totalFiles += mc.files.length;
|
|
56
|
+
}
|
|
57
|
+
// 8. 汇总 PanoramaModule
|
|
58
|
+
// 模块 recipeCount 按文件数等比分配项目级 recipe(反映覆盖贡献度)
|
|
59
|
+
const panoramaModules = new Map();
|
|
60
|
+
for (const mc of moduleCandidates) {
|
|
61
|
+
const refined = refinedRoles.get(mc.name);
|
|
62
|
+
const metrics = coupling.metrics.get(mc.name);
|
|
63
|
+
const recipeCount = totalFiles > 0 ? Math.round((projectRecipeCount * mc.files.length) / totalFiles) : 0;
|
|
64
|
+
panoramaModules.set(mc.name, {
|
|
65
|
+
name: mc.name,
|
|
66
|
+
inferredRole: mc.inferredRole,
|
|
67
|
+
refinedRole: refined?.refinedRole ?? mc.inferredRole,
|
|
68
|
+
roleConfidence: refined?.confidence ?? 0,
|
|
69
|
+
layer: moduleLayerMap.get(mc.name) ?? 0,
|
|
70
|
+
fanIn: metrics?.fanIn ?? 0,
|
|
71
|
+
fanOut: metrics?.fanOut ?? 0,
|
|
72
|
+
files: mc.files,
|
|
73
|
+
fileCount: mc.files.length,
|
|
74
|
+
recipeCount,
|
|
75
|
+
coverageRatio: mc.files.length > 0 ? recipeCount / mc.files.length : 0,
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
// 8.5 基于模块角色重命名层级(比模块名 pattern 更准确)
|
|
79
|
+
this.#renameLayersByRole(layers, panoramaModules);
|
|
80
|
+
// 9. 多维度知识健康分析 (替代旧的基于模块文件数的覆盖率模型)
|
|
81
|
+
const moduleRoles = moduleCandidates.map((m) => {
|
|
82
|
+
const pm = panoramaModules.get(m.name);
|
|
83
|
+
return pm?.refinedRole ?? m.inferredRole;
|
|
84
|
+
});
|
|
85
|
+
const { radar, gaps } = this.#dimensionAnalyzer.analyze(moduleRoles);
|
|
86
|
+
// 10. 调用流概要
|
|
87
|
+
const callFlowSummary = this.#computeCallFlowSummary();
|
|
88
|
+
return {
|
|
89
|
+
modules: panoramaModules,
|
|
90
|
+
layers,
|
|
91
|
+
cycles: coupling.cycles,
|
|
92
|
+
gaps,
|
|
93
|
+
healthRadar: radar,
|
|
94
|
+
callFlowSummary,
|
|
95
|
+
projectRecipeCount,
|
|
96
|
+
computedAt: Date.now(),
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
/* ─── Project Recipe Count ──────────────────────── */
|
|
100
|
+
#getProjectRecipeCount() {
|
|
101
|
+
try {
|
|
102
|
+
const row = this.#db
|
|
103
|
+
.prepare(`SELECT COUNT(*) as cnt FROM knowledge_entries WHERE lifecycle IN ('active', 'pending')`)
|
|
104
|
+
.get();
|
|
105
|
+
return Number(row?.cnt ?? 0);
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
return 0;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
/* ─── Layer Naming (role-based) ─────────────────── */
|
|
112
|
+
/** 角色 → 层级名映射 */
|
|
113
|
+
static #ROLE_TO_LAYER = {
|
|
114
|
+
core: 'Foundation',
|
|
115
|
+
foundation: 'Foundation',
|
|
116
|
+
model: 'Model',
|
|
117
|
+
service: 'Service',
|
|
118
|
+
networking: 'Infrastructure',
|
|
119
|
+
storage: 'Infrastructure',
|
|
120
|
+
ui: 'UI',
|
|
121
|
+
feature: 'Feature',
|
|
122
|
+
config: 'Configuration',
|
|
123
|
+
test: 'Test',
|
|
124
|
+
app: 'Application',
|
|
125
|
+
};
|
|
126
|
+
/**
|
|
127
|
+
* 基于模块 refinedRole 投票重命名层级
|
|
128
|
+
* 比模块名 pattern 匹配更准确(避免 BDUIKit 被误匹配为 Foundation 等问题)
|
|
129
|
+
*/
|
|
130
|
+
#renameLayersByRole(layers, panoramaModules) {
|
|
131
|
+
const usedNames = new Set();
|
|
132
|
+
const maxLevel = Math.max(...layers.levels.map((l) => l.level), 0);
|
|
133
|
+
for (const level of layers.levels) {
|
|
134
|
+
// 只统计有文件的模块(排除 0 文件的第三方依赖干扰)
|
|
135
|
+
const roleVotes = new Map();
|
|
136
|
+
for (const modName of level.modules) {
|
|
137
|
+
const mod = panoramaModules.get(modName);
|
|
138
|
+
if (mod && mod.fileCount > 0) {
|
|
139
|
+
const role = mod.refinedRole || mod.inferredRole;
|
|
140
|
+
roleVotes.set(role, (roleVotes.get(role) ?? 0) + 1);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
let layerName;
|
|
144
|
+
if (roleVotes.size === 0) {
|
|
145
|
+
// 全部是 0 文件模块 → 用位置推断
|
|
146
|
+
layerName =
|
|
147
|
+
level.level === 0 ? 'Foundation' : level.level === maxLevel ? 'Application' : 'Feature';
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
// 选最高票角色
|
|
151
|
+
let bestRole = '';
|
|
152
|
+
let bestCount = 0;
|
|
153
|
+
for (const [role, count] of roleVotes) {
|
|
154
|
+
if (count > bestCount) {
|
|
155
|
+
bestRole = role;
|
|
156
|
+
bestCount = count;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
layerName = _a.#ROLE_TO_LAYER[bestRole] ?? 'Feature';
|
|
160
|
+
// 位置修正:最底层优先 Foundation,最顶层优先 Application
|
|
161
|
+
if (level.level === 0 && roleVotes.has('core')) {
|
|
162
|
+
layerName = 'Foundation';
|
|
163
|
+
}
|
|
164
|
+
else if (level.level === maxLevel && layers.levels.length > 1) {
|
|
165
|
+
layerName = 'Application';
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
// 去重:已使用的名称追加 level 号
|
|
169
|
+
if (usedNames.has(layerName)) {
|
|
170
|
+
layerName = `${layerName} ${level.level}`;
|
|
171
|
+
}
|
|
172
|
+
usedNames.add(layerName);
|
|
173
|
+
level.name = layerName;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
/* ─── Call Flow Summary ─────────────────────────── */
|
|
177
|
+
#computeCallFlowSummary() {
|
|
178
|
+
// 最频繁被调用的方法
|
|
179
|
+
const topCalled = this.#db
|
|
180
|
+
.prepare(`SELECT to_id, COUNT(*) as call_count
|
|
181
|
+
FROM knowledge_edges
|
|
182
|
+
WHERE relation = 'calls'
|
|
183
|
+
GROUP BY to_id
|
|
184
|
+
ORDER BY call_count DESC
|
|
185
|
+
LIMIT 10`)
|
|
186
|
+
.all();
|
|
187
|
+
// 入口点: 只有出度没有入度的方法
|
|
188
|
+
const entryPoints = this.#db
|
|
189
|
+
.prepare(`SELECT DISTINCT ke.from_id
|
|
190
|
+
FROM knowledge_edges ke
|
|
191
|
+
WHERE ke.relation = 'calls'
|
|
192
|
+
AND ke.from_id NOT IN (
|
|
193
|
+
SELECT to_id FROM knowledge_edges WHERE relation = 'calls'
|
|
194
|
+
)
|
|
195
|
+
LIMIT 20`)
|
|
196
|
+
.all();
|
|
197
|
+
// 数据生产者: data_flow outFlow >> inFlow
|
|
198
|
+
const dataProducers = this.#db
|
|
199
|
+
.prepare(`SELECT from_id, COUNT(*) as out_cnt
|
|
200
|
+
FROM knowledge_edges
|
|
201
|
+
WHERE relation = 'data_flow'
|
|
202
|
+
GROUP BY from_id
|
|
203
|
+
HAVING out_cnt > 3
|
|
204
|
+
ORDER BY out_cnt DESC
|
|
205
|
+
LIMIT 10`)
|
|
206
|
+
.all();
|
|
207
|
+
// 数据消费者: data_flow inFlow >> outFlow
|
|
208
|
+
const dataConsumers = this.#db
|
|
209
|
+
.prepare(`SELECT to_id, COUNT(*) as in_cnt
|
|
210
|
+
FROM knowledge_edges
|
|
211
|
+
WHERE relation = 'data_flow'
|
|
212
|
+
GROUP BY to_id
|
|
213
|
+
HAVING in_cnt > 3
|
|
214
|
+
ORDER BY in_cnt DESC
|
|
215
|
+
LIMIT 10`)
|
|
216
|
+
.all();
|
|
217
|
+
return {
|
|
218
|
+
topCalledMethods: topCalled.map((r) => ({
|
|
219
|
+
id: r.to_id,
|
|
220
|
+
callCount: Number(r.call_count),
|
|
221
|
+
})),
|
|
222
|
+
entryPoints: entryPoints.map((r) => r.from_id),
|
|
223
|
+
dataProducers: dataProducers.map((r) => r.from_id),
|
|
224
|
+
dataConsumers: dataConsumers.map((r) => r.to_id),
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
_a = PanoramaAggregator;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PanoramaScanner — 全景数据内置扫描器
|
|
3
|
+
*
|
|
4
|
+
* 在全景数据缺失时自动运行轻量级结构扫描(Phase 1→2.1),
|
|
5
|
+
* 填充 code_entities + knowledge_edges,使 PanoramaService 能产生有效数据。
|
|
6
|
+
*
|
|
7
|
+
* 非 MCP 操作,而是 PanoramaService 的内置依赖。
|
|
8
|
+
* 调用时机:
|
|
9
|
+
* - PanoramaService 发现 DB 中无 code_entities 时自动触发
|
|
10
|
+
* - 手动调用 invalidate + getResult 时检查并补充
|
|
11
|
+
*
|
|
12
|
+
* @module PanoramaScanner
|
|
13
|
+
*/
|
|
14
|
+
export interface PanoramaScannerOptions {
|
|
15
|
+
projectRoot: string;
|
|
16
|
+
container: ScannerContainer;
|
|
17
|
+
logger?: ScannerLogger;
|
|
18
|
+
}
|
|
19
|
+
export interface ScannerContainer {
|
|
20
|
+
get(name: string): any;
|
|
21
|
+
}
|
|
22
|
+
export interface ScannerLogger {
|
|
23
|
+
info(...args: unknown[]): void;
|
|
24
|
+
warn(...args: unknown[]): void;
|
|
25
|
+
}
|
|
26
|
+
export interface ScanResult {
|
|
27
|
+
entities: number;
|
|
28
|
+
edges: number;
|
|
29
|
+
modules: number;
|
|
30
|
+
durationMs: number;
|
|
31
|
+
}
|
|
32
|
+
export declare class PanoramaScanner {
|
|
33
|
+
#private;
|
|
34
|
+
constructor(opts: PanoramaScannerOptions);
|
|
35
|
+
/**
|
|
36
|
+
* 检测 DB 中是否已有该项目的 code_entities 数据
|
|
37
|
+
*/
|
|
38
|
+
hasData(): boolean;
|
|
39
|
+
/**
|
|
40
|
+
* 确保全景数据存在。无数据时自动执行扫描。
|
|
41
|
+
* 幂等:扫描过一次后不再重复(重启进程或手动 reset 可重新触发)。
|
|
42
|
+
*/
|
|
43
|
+
ensureData(): Promise<ScanResult | null>;
|
|
44
|
+
/**
|
|
45
|
+
* 执行完整扫描(强制,不检查缓存)
|
|
46
|
+
*/
|
|
47
|
+
scan(): Promise<ScanResult>;
|
|
48
|
+
/**
|
|
49
|
+
* 重置扫描状态(允许下次 ensureData 重新扫描)
|
|
50
|
+
*/
|
|
51
|
+
reset(): void;
|
|
52
|
+
}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PanoramaScanner — 全景数据内置扫描器
|
|
3
|
+
*
|
|
4
|
+
* 在全景数据缺失时自动运行轻量级结构扫描(Phase 1→2.1),
|
|
5
|
+
* 填充 code_entities + knowledge_edges,使 PanoramaService 能产生有效数据。
|
|
6
|
+
*
|
|
7
|
+
* 非 MCP 操作,而是 PanoramaService 的内置依赖。
|
|
8
|
+
* 调用时机:
|
|
9
|
+
* - PanoramaService 发现 DB 中无 code_entities 时自动触发
|
|
10
|
+
* - 手动调用 invalidate + getResult 时检查并补充
|
|
11
|
+
*
|
|
12
|
+
* @module PanoramaScanner
|
|
13
|
+
*/
|
|
14
|
+
/* ═══ Silent Logger (fallback) ════════════════════════════ */
|
|
15
|
+
const SILENT_LOGGER = {
|
|
16
|
+
info() { },
|
|
17
|
+
warn() { },
|
|
18
|
+
};
|
|
19
|
+
/* ═══ PanoramaScanner Class ═══════════════════════════════ */
|
|
20
|
+
export class PanoramaScanner {
|
|
21
|
+
#projectRoot;
|
|
22
|
+
#container;
|
|
23
|
+
#logger;
|
|
24
|
+
#hasScanned = false;
|
|
25
|
+
constructor(opts) {
|
|
26
|
+
this.#projectRoot = opts.projectRoot;
|
|
27
|
+
this.#container = opts.container;
|
|
28
|
+
this.#logger = opts.logger ?? SILENT_LOGGER;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* 检测 DB 中是否已有该项目的 code_entities 数据
|
|
32
|
+
*/
|
|
33
|
+
hasData() {
|
|
34
|
+
try {
|
|
35
|
+
const db = this.#container.get('database');
|
|
36
|
+
const rawDb = db?.getDb ? db.getDb() : db;
|
|
37
|
+
if (!rawDb?.prepare) {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
const row = rawDb
|
|
41
|
+
.prepare(`SELECT COUNT(*) as cnt FROM code_entities WHERE project_root = ?`)
|
|
42
|
+
.get(this.#projectRoot);
|
|
43
|
+
return (row?.cnt ?? 0) > 0;
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* 确保全景数据存在。无数据时自动执行扫描。
|
|
51
|
+
* 幂等:扫描过一次后不再重复(重启进程或手动 reset 可重新触发)。
|
|
52
|
+
*/
|
|
53
|
+
async ensureData() {
|
|
54
|
+
if (this.#hasScanned || this.hasData()) {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
return this.scan();
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* 执行完整扫描(强制,不检查缓存)
|
|
61
|
+
*/
|
|
62
|
+
async scan() {
|
|
63
|
+
const t0 = Date.now();
|
|
64
|
+
this.#logger.info('[PanoramaScanner] Starting structure scan...');
|
|
65
|
+
let entities = 0;
|
|
66
|
+
let edges = 0;
|
|
67
|
+
let modules = 0;
|
|
68
|
+
try {
|
|
69
|
+
const { runPhase1_FileCollection, runPhase1_5_AstAnalysis, runPhase1_6_EntityGraph, runPhase1_7_CallGraph, runPhase2_DependencyGraph, runPhase2_1_ModuleEntities, } = await import('../../external/mcp/handlers/bootstrap/shared/bootstrap-phases.js');
|
|
70
|
+
// Phase 1: 文件收集
|
|
71
|
+
const phase1 = await runPhase1_FileCollection(this.#projectRoot, this.#logger, {
|
|
72
|
+
maxFiles: 500,
|
|
73
|
+
});
|
|
74
|
+
if (!phase1.allFiles?.length) {
|
|
75
|
+
this.#logger.warn('[PanoramaScanner] No files found, skipping scan');
|
|
76
|
+
this.#hasScanned = true;
|
|
77
|
+
return { entities: 0, edges: 0, modules: 0, durationMs: Date.now() - t0 };
|
|
78
|
+
}
|
|
79
|
+
// Phase 1.5: AST 分析
|
|
80
|
+
const phase1_5 = await runPhase1_5_AstAnalysis(phase1.allFiles, phase1.langStats, this.#logger);
|
|
81
|
+
// Phase 1.6: Entity Graph 写入
|
|
82
|
+
if (phase1_5.astProjectSummary) {
|
|
83
|
+
const phase1_6 = await runPhase1_6_EntityGraph(phase1_5.astProjectSummary, this.#projectRoot, this.#container, this.#logger);
|
|
84
|
+
entities = phase1_6.codeEntityResult?.entitiesUpserted ?? 0;
|
|
85
|
+
edges = phase1_6.codeEntityResult?.edgesCreated ?? 0;
|
|
86
|
+
}
|
|
87
|
+
// Phase 1.7: Call Graph (增强耦合准确度)
|
|
88
|
+
if (phase1_5.astProjectSummary) {
|
|
89
|
+
try {
|
|
90
|
+
await runPhase1_7_CallGraph(phase1_5.astProjectSummary, this.#projectRoot, this.#container, this.#logger);
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
// Call graph 失败不阻塞
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
// Phase 2: 依赖图
|
|
97
|
+
if (phase1.discoverer) {
|
|
98
|
+
const phase2 = await runPhase2_DependencyGraph(phase1.discoverer, this.#container, this.#logger);
|
|
99
|
+
edges += phase2.depEdgesWritten;
|
|
100
|
+
// Phase 2.1: Module 实体
|
|
101
|
+
if (phase2.depGraphData) {
|
|
102
|
+
await runPhase2_1_ModuleEntities(phase2.depGraphData, this.#projectRoot, this.#container, this.#logger);
|
|
103
|
+
modules = phase2.depGraphData.nodes?.length ?? 0;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
// Phase 2.2: 目录推断兜底
|
|
107
|
+
// 当 Phase 2.1 未产出 module 实体时(无 Package.swift / build.gradle 等),
|
|
108
|
+
// 从已有 code_entities 按顶层目录分组写入 module 实体
|
|
109
|
+
if (modules === 0 && entities > 0) {
|
|
110
|
+
modules = this.#inferModulesFromDirectories();
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
catch (err) {
|
|
114
|
+
this.#logger.warn(`[PanoramaScanner] Scan failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
115
|
+
}
|
|
116
|
+
this.#hasScanned = true;
|
|
117
|
+
const durationMs = Date.now() - t0;
|
|
118
|
+
this.#logger.info(`[PanoramaScanner] Scan complete: ${entities} entities, ${edges} edges, ${modules} modules (${durationMs}ms)`);
|
|
119
|
+
return { entities, edges, modules, durationMs };
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* 重置扫描状态(允许下次 ensureData 重新扫描)
|
|
123
|
+
*/
|
|
124
|
+
reset() {
|
|
125
|
+
this.#hasScanned = false;
|
|
126
|
+
}
|
|
127
|
+
/* ─── 目录推断兜底 ─────────────────────────────── */
|
|
128
|
+
/**
|
|
129
|
+
* 从 code_entities 中按顶层目录分组写入 module 实体 + is_part_of 边。
|
|
130
|
+
* 仅在 Phase 2.1 未产出 module 时调用。
|
|
131
|
+
*/
|
|
132
|
+
#inferModulesFromDirectories() {
|
|
133
|
+
try {
|
|
134
|
+
const db = this.#container.get('database');
|
|
135
|
+
const rawDb = db?.getDb ? db.getDb() : db;
|
|
136
|
+
if (!rawDb?.prepare) {
|
|
137
|
+
return 0;
|
|
138
|
+
}
|
|
139
|
+
// 查询所有非 module 实体的文件路径
|
|
140
|
+
const rows = rawDb
|
|
141
|
+
.prepare(`SELECT DISTINCT entity_id, file_path FROM code_entities
|
|
142
|
+
WHERE project_root = ? AND file_path IS NOT NULL AND entity_type != 'module'`)
|
|
143
|
+
.all(this.#projectRoot);
|
|
144
|
+
if (rows.length === 0) {
|
|
145
|
+
return 0;
|
|
146
|
+
}
|
|
147
|
+
// 按顶层目录分组
|
|
148
|
+
const groups = new Map();
|
|
149
|
+
for (const row of rows) {
|
|
150
|
+
const filePath = row.file_path;
|
|
151
|
+
if (!filePath) {
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
const relative = filePath.startsWith(this.#projectRoot)
|
|
155
|
+
? filePath.slice(this.#projectRoot.length).replace(/^\//, '')
|
|
156
|
+
: filePath;
|
|
157
|
+
const firstDir = relative.split('/')[0];
|
|
158
|
+
if (!firstDir || firstDir.startsWith('.')) {
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
if (!groups.has(firstDir)) {
|
|
162
|
+
groups.set(firstDir, []);
|
|
163
|
+
}
|
|
164
|
+
groups.get(firstDir).push(row.entity_id);
|
|
165
|
+
}
|
|
166
|
+
if (groups.size === 0) {
|
|
167
|
+
return 0;
|
|
168
|
+
}
|
|
169
|
+
// 写入 module 实体 + is_part_of 边
|
|
170
|
+
const insertEntity = rawDb.prepare(`INSERT OR IGNORE INTO code_entities (entity_id, name, entity_type, file_path, project_root)
|
|
171
|
+
VALUES (?, ?, 'module', NULL, ?)`);
|
|
172
|
+
const insertEdge = rawDb.prepare(`INSERT OR IGNORE INTO knowledge_edges (from_id, from_type, to_id, to_type, relation, weight)
|
|
173
|
+
VALUES (?, 'entity', ?, 'module', 'is_part_of', 1.0)`);
|
|
174
|
+
for (const [dirName, entityIds] of groups) {
|
|
175
|
+
insertEntity.run(dirName, dirName, this.#projectRoot);
|
|
176
|
+
for (const entityId of entityIds) {
|
|
177
|
+
insertEdge.run(entityId, dirName);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
this.#logger.info(`[PanoramaScanner] Directory fallback: inferred ${groups.size} modules from top-level dirs`);
|
|
181
|
+
return groups.size;
|
|
182
|
+
}
|
|
183
|
+
catch (err) {
|
|
184
|
+
this.#logger.warn(`[PanoramaScanner] Directory fallback failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
185
|
+
return 0;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|