autosnippet 3.3.6 → 3.3.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dashboard/dist/assets/icons-BMNb0V6L.js +1 -0
- package/dashboard/dist/assets/index-DHJ1Dj7u.css +1 -0
- package/dashboard/dist/assets/index-DV8biUkH.js +112 -0
- package/dashboard/dist/index.html +3 -3
- package/dist/bin/cli.js +8 -4
- package/dist/lib/agent/AgentRuntime.d.ts +2 -2
- package/dist/lib/agent/AgentRuntime.js +26 -18
- package/dist/lib/agent/core/ChatAgentPrompts.js +57 -21
- package/dist/lib/agent/core/LoopContext.d.ts +1 -0
- package/dist/lib/agent/core/ToolExecutionPipeline.js +13 -0
- package/dist/lib/agent/domain/ChatAgentTasks.js +4 -0
- package/dist/lib/agent/forced-summary.js +7 -2
- package/dist/lib/agent/memory/ActiveContext.d.ts +0 -2
- package/dist/lib/agent/memory/ActiveContext.js +0 -2
- package/dist/lib/agent/memory/MemoryEmbeddingStore.d.ts +49 -0
- package/dist/lib/agent/memory/MemoryEmbeddingStore.js +159 -0
- package/dist/lib/agent/memory/MemoryRetriever.d.ts +2 -0
- package/dist/lib/agent/memory/MemoryRetriever.js +25 -11
- package/dist/lib/agent/memory/MemoryStore.d.ts +8 -41
- package/dist/lib/agent/memory/MemoryStore.js +196 -261
- package/dist/lib/agent/memory/PersistentMemory.d.ts +2 -0
- package/dist/lib/agent/memory/PersistentMemory.js +4 -5
- package/dist/lib/agent/memory/SessionStore.d.ts +0 -2
- package/dist/lib/agent/memory/SessionStore.js +0 -2
- package/dist/lib/agent/tools/ast-graph.js +21 -19
- package/dist/lib/agent/tools/infrastructure.js +3 -2
- package/dist/lib/agent/tools/project-access.d.ts +2 -2
- package/dist/lib/agent/tools/project-access.js +5 -4
- package/dist/lib/bootstrap.js +2 -1
- package/dist/lib/cli/AiScanService.js +8 -21
- package/dist/lib/cli/KnowledgeSyncService.d.ts +7 -37
- package/dist/lib/cli/KnowledgeSyncService.js +23 -51
- package/dist/lib/core/ast/ProjectGraph.js +5 -27
- package/dist/lib/core/discovery/ConfigWatcher.d.ts +64 -0
- package/dist/lib/core/discovery/ConfigWatcher.js +336 -0
- package/dist/lib/core/discovery/CustomConfigDiscoverer.d.ts +28 -0
- package/dist/lib/core/discovery/CustomConfigDiscoverer.js +1303 -0
- package/dist/lib/core/discovery/DiscovererPreference.d.ts +44 -0
- package/dist/lib/core/discovery/DiscovererPreference.js +141 -0
- package/dist/lib/core/discovery/DiscovererRegistry.d.ts +10 -1
- package/dist/lib/core/discovery/DiscovererRegistry.js +42 -2
- package/dist/lib/core/discovery/ProjectDiscoverer.d.ts +19 -0
- package/dist/lib/core/discovery/index.d.ts +2 -0
- package/dist/lib/core/discovery/index.js +4 -0
- package/dist/lib/core/discovery/parsers/CMakeParser.d.ts +32 -0
- package/dist/lib/core/discovery/parsers/CMakeParser.js +148 -0
- package/dist/lib/core/discovery/parsers/GradleDslParser.d.ts +43 -0
- package/dist/lib/core/discovery/parsers/GradleDslParser.js +171 -0
- package/dist/lib/core/discovery/parsers/JsonConfigParser.d.ts +45 -0
- package/dist/lib/core/discovery/parsers/JsonConfigParser.js +122 -0
- package/dist/lib/core/discovery/parsers/RubyDslParser.d.ts +49 -0
- package/dist/lib/core/discovery/parsers/RubyDslParser.js +282 -0
- package/dist/lib/core/discovery/parsers/StarlarkParser.d.ts +33 -0
- package/dist/lib/core/discovery/parsers/StarlarkParser.js +229 -0
- package/dist/lib/core/discovery/parsers/YamlConfigParser.d.ts +37 -0
- package/dist/lib/core/discovery/parsers/YamlConfigParser.js +212 -0
- package/dist/lib/domain/dimension/DimensionRegistry.d.ts +0 -2
- package/dist/lib/domain/dimension/DimensionRegistry.js +0 -2
- package/dist/lib/domain/dimension/DimensionSop.js +44 -33
- package/dist/lib/domain/dimension/UnifiedDimension.d.ts +0 -2
- package/dist/lib/domain/dimension/UnifiedDimension.js +0 -2
- package/dist/lib/domain/knowledge/KnowledgeEntry.d.ts +7 -1
- package/dist/lib/domain/knowledge/KnowledgeEntry.js +17 -3
- package/dist/lib/domain/knowledge/Lifecycle.d.ts +26 -0
- package/dist/lib/domain/knowledge/Lifecycle.js +42 -0
- package/dist/lib/domain/knowledge/index.d.ts +2 -1
- package/dist/lib/domain/knowledge/index.js +1 -1
- package/dist/lib/external/ai/AiProvider.d.ts +12 -0
- package/dist/lib/external/ai/AiProvider.js +24 -0
- package/dist/lib/external/ai/AiProviderManager.d.ts +101 -0
- package/dist/lib/external/ai/AiProviderManager.js +193 -0
- package/dist/lib/external/ai/providers/ClaudeProvider.js +11 -0
- package/dist/lib/external/ai/providers/GoogleGeminiProvider.js +18 -0
- package/dist/lib/external/ai/providers/MockProvider.d.ts +21 -3
- package/dist/lib/external/ai/providers/MockProvider.js +290 -14
- package/dist/lib/external/ai/providers/OpenAiProvider.js +16 -0
- package/dist/lib/external/lark/LarkTransport.d.ts +5 -1
- package/dist/lib/external/lark/LarkTransport.js +10 -2
- package/dist/lib/external/mcp/handlers/bootstrap/pipeline/BootstrapSnapshot.d.ts +2 -1
- package/dist/lib/external/mcp/handlers/bootstrap/pipeline/BootstrapSnapshot.js +102 -153
- package/dist/lib/external/mcp/handlers/bootstrap/pipeline/mock-pipeline.d.ts +20 -0
- package/dist/lib/external/mcp/handlers/bootstrap/pipeline/mock-pipeline.js +432 -0
- package/dist/lib/external/mcp/handlers/bootstrap/pipeline/orchestrator.js +49 -24
- package/dist/lib/external/mcp/handlers/bootstrap/refine.js +8 -0
- package/dist/lib/external/mcp/handlers/bootstrap/shared/bootstrap-phases.d.ts +1 -1
- package/dist/lib/external/mcp/handlers/bootstrap/shared/bootstrap-phases.js +41 -37
- package/dist/lib/external/mcp/handlers/bootstrap-external.d.ts +9 -0
- package/dist/lib/external/mcp/handlers/bootstrap-external.js +3 -1
- package/dist/lib/external/mcp/handlers/bootstrap-internal.js +2 -0
- package/dist/lib/external/mcp/handlers/consolidated.js +2 -1
- package/dist/lib/external/mcp/handlers/dimension-complete-external.js +9 -4
- package/dist/lib/external/mcp/handlers/evolve-external.d.ts +1 -0
- package/dist/lib/external/mcp/handlers/evolve-external.js +18 -18
- package/dist/lib/external/mcp/handlers/guard.js +15 -24
- package/dist/lib/external/mcp/handlers/knowledge.js +5 -4
- package/dist/lib/external/mcp/handlers/panorama.js +9 -9
- package/dist/lib/external/mcp/handlers/rescan-external.js +7 -6
- package/dist/lib/external/mcp/handlers/rescan-internal.js +9 -5
- package/dist/lib/external/mcp/handlers/search.js +3 -1
- package/dist/lib/external/mcp/handlers/skill.js +4 -4
- package/dist/lib/external/mcp/handlers/structure.js +8 -12
- package/dist/lib/external/mcp/handlers/system.js +10 -34
- package/dist/lib/http/routes/ai.js +109 -30
- package/dist/lib/http/routes/candidates.js +11 -4
- package/dist/lib/http/routes/commands.js +10 -1
- package/dist/lib/http/routes/guardReport.js +3 -5
- package/dist/lib/http/routes/health.js +11 -0
- package/dist/lib/http/routes/modules.js +27 -0
- package/dist/lib/http/routes/panorama.js +12 -12
- package/dist/lib/http/routes/recipes.js +66 -8
- package/dist/lib/http/routes/remote.js +3 -13
- package/dist/lib/http/routes/search.js +11 -8
- package/dist/lib/http/utils/routeHelpers.js +2 -1
- package/dist/lib/infrastructure/audit/AuditLogger.d.ts +20 -3
- package/dist/lib/infrastructure/audit/AuditStore.d.ts +28 -29
- package/dist/lib/infrastructure/audit/AuditStore.js +81 -88
- package/dist/lib/infrastructure/database/drizzle/schema.d.ts +180 -2
- package/dist/lib/infrastructure/database/drizzle/schema.js +23 -3
- package/dist/lib/injection/ServiceContainer.d.ts +6 -5
- package/dist/lib/injection/ServiceContainer.js +18 -31
- package/dist/lib/injection/ServiceMap.d.ts +22 -0
- package/dist/lib/injection/modules/AiModule.d.ts +6 -9
- package/dist/lib/injection/modules/AiModule.js +82 -39
- package/dist/lib/injection/modules/AppModule.js +2 -1
- package/dist/lib/injection/modules/GuardModule.js +5 -5
- package/dist/lib/injection/modules/InfraModule.js +60 -0
- package/dist/lib/injection/modules/KnowledgeModule.js +86 -51
- package/dist/lib/injection/modules/PanoramaModule.js +16 -10
- package/dist/lib/injection/modules/VectorModule.js +3 -0
- package/dist/lib/repository/audit/AuditRepository.d.ts +107 -0
- package/dist/lib/repository/audit/AuditRepository.js +272 -0
- package/dist/lib/repository/base/RepositoryBase.d.ts +46 -0
- package/dist/lib/repository/base/RepositoryBase.js +32 -0
- package/dist/lib/repository/bootstrap/BootstrapRepository.d.ts +94 -0
- package/dist/lib/repository/bootstrap/BootstrapRepository.js +246 -0
- package/dist/lib/repository/code/CodeEntityRepository.d.ts +91 -0
- package/dist/lib/repository/code/CodeEntityRepository.js +361 -0
- package/dist/lib/repository/delivery/DeliveryRepoAdapter.d.ts +39 -0
- package/dist/lib/repository/delivery/DeliveryRepoAdapter.js +23 -0
- package/dist/lib/repository/evolution/LifecycleEventRepository.d.ts +51 -0
- package/dist/lib/repository/evolution/LifecycleEventRepository.js +119 -0
- package/dist/lib/repository/evolution/ProposalRepository.d.ts +9 -12
- package/dist/lib/repository/evolution/ProposalRepository.js +114 -57
- package/dist/lib/repository/guard/GuardViolationRepository.d.ts +104 -0
- package/dist/lib/repository/guard/GuardViolationRepository.js +217 -0
- package/dist/lib/repository/knowledge/KnowledgeEdgeRepository.d.ts +129 -0
- package/dist/lib/repository/knowledge/KnowledgeEdgeRepository.js +475 -0
- package/dist/lib/repository/knowledge/KnowledgeFileStore.d.ts +39 -0
- package/dist/lib/repository/knowledge/KnowledgeFileStore.js +12 -0
- package/dist/lib/repository/knowledge/KnowledgeRepository.impl.d.ts +295 -11
- package/dist/lib/repository/knowledge/KnowledgeRepository.impl.js +608 -13
- package/dist/lib/repository/knowledge/KnowledgeUnitOfWork.d.ts +61 -0
- package/dist/lib/repository/knowledge/KnowledgeUnitOfWork.js +156 -0
- package/dist/lib/repository/memory/MemoryRepository.d.ts +90 -0
- package/dist/lib/repository/memory/MemoryRepository.js +260 -0
- package/dist/lib/repository/search/SearchRepoAdapter.d.ts +92 -0
- package/dist/lib/repository/search/SearchRepoAdapter.js +124 -0
- package/dist/lib/repository/session/SessionRepository.d.ts +46 -0
- package/dist/lib/repository/session/SessionRepository.js +110 -0
- package/dist/lib/repository/sourceref/RecipeSourceRefRepository.d.ts +66 -0
- package/dist/lib/repository/sourceref/RecipeSourceRefRepository.js +182 -0
- package/dist/lib/repository/sync/SyncRepoAdapter.d.ts +58 -0
- package/dist/lib/repository/sync/SyncRepoAdapter.js +58 -0
- package/dist/lib/service/bootstrap/UiStartupTasks.js +5 -6
- package/dist/lib/service/bootstrap/bootstrap-event-types.d.ts +0 -1
- package/dist/lib/service/bootstrap/bootstrap-event-types.js +0 -1
- package/dist/lib/service/cleanup/CleanupService.d.ts +54 -7
- package/dist/lib/service/cleanup/CleanupService.js +291 -40
- package/dist/lib/service/delivery/CursorDeliveryPipeline.js +6 -8
- package/dist/lib/service/evolution/ConsolidationAdvisor.d.ts +4 -9
- package/dist/lib/service/evolution/ConsolidationAdvisor.js +34 -70
- package/dist/lib/service/evolution/ContentPatcher.d.ts +4 -12
- package/dist/lib/service/evolution/ContentPatcher.js +48 -19
- package/dist/lib/service/evolution/ContradictionDetector.d.ts +3 -7
- package/dist/lib/service/evolution/ContradictionDetector.js +17 -24
- package/dist/lib/service/evolution/DecayDetector.d.ts +10 -9
- package/dist/lib/service/evolution/DecayDetector.js +63 -57
- package/dist/lib/service/evolution/EnhancementSuggester.d.ts +3 -9
- package/dist/lib/service/evolution/EnhancementSuggester.js +42 -86
- package/dist/lib/service/evolution/KnowledgeMetabolism.d.ts +4 -4
- package/dist/lib/service/evolution/KnowledgeMetabolism.js +102 -71
- package/dist/lib/service/evolution/ProposalExecutor.d.ts +5 -12
- package/dist/lib/service/evolution/ProposalExecutor.js +64 -69
- package/dist/lib/service/evolution/RecipeLifecycleSupervisor.d.ts +9 -14
- package/dist/lib/service/evolution/RecipeLifecycleSupervisor.js +94 -155
- package/dist/lib/service/evolution/RecipeRelevanceAuditor.d.ts +4 -1
- package/dist/lib/service/evolution/RecipeRelevanceAuditor.js +50 -49
- package/dist/lib/service/evolution/RedundancyAnalyzer.d.ts +3 -7
- package/dist/lib/service/evolution/RedundancyAnalyzer.js +15 -22
- package/dist/lib/service/evolution/StagingManager.d.ts +6 -15
- package/dist/lib/service/evolution/StagingManager.js +37 -95
- package/dist/lib/service/evolution/createSupersedeProposal.d.ts +1 -1
- package/dist/lib/service/evolution/createSupersedeProposal.js +7 -8
- package/dist/lib/service/guard/CoverageAnalyzer.d.ts +3 -7
- package/dist/lib/service/guard/CoverageAnalyzer.js +9 -11
- package/dist/lib/service/guard/GuardCheckEngine.d.ts +3 -0
- package/dist/lib/service/guard/GuardCheckEngine.js +14 -22
- package/dist/lib/service/guard/ReverseGuard.d.ts +4 -7
- package/dist/lib/service/guard/ReverseGuard.js +21 -31
- package/dist/lib/service/guard/ViolationsStore.d.ts +15 -21
- package/dist/lib/service/guard/ViolationsStore.js +75 -69
- package/dist/lib/service/knowledge/CodeEntityGraph.d.ts +45 -63
- package/dist/lib/service/knowledge/CodeEntityGraph.js +418 -496
- package/dist/lib/service/knowledge/ConfidenceRouter.js +18 -9
- package/dist/lib/service/knowledge/KnowledgeFileWriter.d.ts +2 -1
- package/dist/lib/service/knowledge/KnowledgeGraphService.d.ts +18 -60
- package/dist/lib/service/knowledge/KnowledgeGraphService.js +58 -109
- package/dist/lib/service/knowledge/KnowledgeService.d.ts +15 -1
- package/dist/lib/service/knowledge/KnowledgeService.js +97 -46
- package/dist/lib/service/knowledge/RecipeProductionGateway.d.ts +0 -2
- package/dist/lib/service/knowledge/RecipeProductionGateway.js +0 -2
- package/dist/lib/service/knowledge/SourceRefReconciler.d.ts +5 -13
- package/dist/lib/service/knowledge/SourceRefReconciler.js +58 -78
- package/dist/lib/service/module/ModuleService.js +10 -19
- package/dist/lib/service/panorama/CouplingAnalyzer.d.ts +14 -3
- package/dist/lib/service/panorama/CouplingAnalyzer.js +137 -32
- package/dist/lib/service/panorama/DimensionAnalyzer.d.ts +7 -4
- package/dist/lib/service/panorama/DimensionAnalyzer.js +94 -33
- package/dist/lib/service/panorama/LayerInferrer.d.ts +16 -1
- package/dist/lib/service/panorama/LayerInferrer.js +118 -1
- package/dist/lib/service/panorama/ModuleDiscoverer.d.ts +14 -4
- package/dist/lib/service/panorama/ModuleDiscoverer.js +209 -61
- package/dist/lib/service/panorama/PanoramaAggregator.d.ts +15 -4
- package/dist/lib/service/panorama/PanoramaAggregator.js +128 -62
- package/dist/lib/service/panorama/PanoramaScanner.d.ts +5 -1
- package/dist/lib/service/panorama/PanoramaScanner.js +60 -31
- package/dist/lib/service/panorama/PanoramaService.d.ts +11 -8
- package/dist/lib/service/panorama/PanoramaService.js +49 -69
- package/dist/lib/service/panorama/PanoramaTypes.d.ts +41 -0
- package/dist/lib/service/panorama/RoleRefiner.d.ts +10 -5
- package/dist/lib/service/panorama/RoleRefiner.js +92 -282
- package/dist/lib/service/panorama/TechStackProfiler.d.ts +13 -0
- package/dist/lib/service/panorama/TechStackProfiler.js +79 -0
- package/dist/lib/service/quality/QualityScorer.d.ts +45 -26
- package/dist/lib/service/quality/QualityScorer.js +157 -83
- package/dist/lib/service/search/SearchEngine.d.ts +1 -0
- package/dist/lib/service/search/SearchEngine.js +32 -37
- package/dist/lib/service/signal/HitRecorder.js +5 -5
- package/dist/lib/service/skills/RuleRecallStrategy.js +7 -3
- package/dist/lib/service/skills/SignalCollector.d.ts +6 -8
- package/dist/lib/service/skills/SignalCollector.js +34 -60
- package/dist/lib/service/skills/SkillAdvisor.d.ts +7 -13
- package/dist/lib/service/skills/SkillAdvisor.js +30 -79
- package/dist/lib/service/vector/ContextualEnricher.d.ts +1 -0
- package/dist/lib/service/vector/ContextualEnricher.js +4 -0
- package/dist/lib/service/vector/SyncCoordinator.d.ts +3 -1
- package/dist/lib/service/vector/SyncCoordinator.js +25 -3
- package/dist/lib/service/vector/VectorService.d.ts +2 -0
- package/dist/lib/service/vector/VectorService.js +3 -0
- package/dist/lib/service/wiki/WikiGenerator.js +1 -1
- package/dist/lib/shared/LanguageProfiles.d.ts +109 -0
- package/dist/lib/shared/LanguageProfiles.js +939 -0
- package/dist/lib/shared/LanguageService.d.ts +6 -0
- package/dist/lib/shared/LanguageService.js +19 -0
- package/dist/lib/shared/constants.d.ts +19 -19
- package/dist/lib/shared/constants.js +10 -10
- package/dist/lib/shared/developer-identity.d.ts +18 -0
- package/dist/lib/shared/developer-identity.js +62 -0
- package/dist/lib/shared/schemas/http-requests.d.ts +8 -17
- package/dist/lib/shared/schemas/http-requests.js +9 -6
- package/dist/lib/shared/schemas/mcp-tools.d.ts +1 -1
- package/dist/lib/types/knowledge-wire.d.ts +1 -0
- package/dist/lib/types/project-snapshot-builder.d.ts +0 -1
- package/dist/lib/types/project-snapshot-builder.js +0 -1
- package/dist/lib/types/project-snapshot.d.ts +0 -1
- package/dist/lib/types/project-snapshot.js +0 -1
- package/dist/lib/types/snapshot-views.d.ts +0 -2
- package/dist/lib/types/snapshot-views.js +0 -1
- package/package.json +2 -1
- package/dashboard/dist/assets/icons-D1aVZYFW.js +0 -1
- package/dashboard/dist/assets/index-CxHOu8Hd.css +0 -1
- package/dashboard/dist/assets/index-DDdAOpYT.js +0 -128
- package/dist/lib/repository/base/BaseRepository.d.ts +0 -53
- package/dist/lib/repository/base/BaseRepository.js +0 -226
|
@@ -26,15 +26,14 @@ import Logger from '../../infrastructure/logging/Logger.js';
|
|
|
26
26
|
const logger = Logger.getInstance();
|
|
27
27
|
export class CodeEntityGraph {
|
|
28
28
|
projectRoot;
|
|
29
|
-
|
|
29
|
+
#entityRepo;
|
|
30
|
+
#edgeRepo;
|
|
30
31
|
log;
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
this
|
|
32
|
+
constructor(entityRepo, edgeRepo, options = {}) {
|
|
33
|
+
this.#entityRepo = entityRepo;
|
|
34
|
+
this.#edgeRepo = edgeRepo;
|
|
34
35
|
this.projectRoot = options.projectRoot || '';
|
|
35
36
|
this.log = options.logger || logger;
|
|
36
|
-
this.#ensureTable();
|
|
37
|
-
this.#prepareStatements();
|
|
38
37
|
}
|
|
39
38
|
// ────────────────────────────────────────────
|
|
40
39
|
// Public API — 图谱构建
|
|
@@ -46,102 +45,99 @@ export class CodeEntityGraph {
|
|
|
46
45
|
*
|
|
47
46
|
* @param astSummary analyzeProject() 产出的 ProjectAstSummary
|
|
48
47
|
*/
|
|
49
|
-
populateFromAst(astSummary) {
|
|
48
|
+
async populateFromAst(astSummary) {
|
|
50
49
|
if (!astSummary) {
|
|
51
50
|
return { entitiesUpserted: 0, edgesCreated: 0, durationMs: 0 };
|
|
52
51
|
}
|
|
53
52
|
const t0 = Date.now();
|
|
54
53
|
let entities = 0;
|
|
55
54
|
let edges = 0;
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
edges++;
|
|
140
|
-
}
|
|
55
|
+
// ── 类 ──
|
|
56
|
+
for (const cls of astSummary.classes || []) {
|
|
57
|
+
await this.#upsertEntity({
|
|
58
|
+
entityId: cls.name,
|
|
59
|
+
entityType: cls.isCategory ? 'category' : 'class',
|
|
60
|
+
name: cls.name,
|
|
61
|
+
filePath: cls.file || null,
|
|
62
|
+
line: cls.line || null,
|
|
63
|
+
superclass: cls.superclass || null,
|
|
64
|
+
protocols: cls.protocols || [],
|
|
65
|
+
metadata: {
|
|
66
|
+
endLine: cls.endLine,
|
|
67
|
+
isCategory: cls.isCategory || false,
|
|
68
|
+
},
|
|
69
|
+
});
|
|
70
|
+
entities++;
|
|
71
|
+
}
|
|
72
|
+
// ── 协议 ──
|
|
73
|
+
for (const proto of astSummary.protocols || []) {
|
|
74
|
+
await this.#upsertEntity({
|
|
75
|
+
entityId: proto.name,
|
|
76
|
+
entityType: 'protocol',
|
|
77
|
+
name: proto.name,
|
|
78
|
+
filePath: proto.file || null,
|
|
79
|
+
line: proto.line || null,
|
|
80
|
+
protocols: proto.inherits || [],
|
|
81
|
+
metadata: {
|
|
82
|
+
methodCount: proto.methods?.length || 0,
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
entities++;
|
|
86
|
+
}
|
|
87
|
+
// ── Category ──
|
|
88
|
+
for (const cat of astSummary.categories || []) {
|
|
89
|
+
const catId = `${cat.className}(${cat.categoryName})`;
|
|
90
|
+
await this.#upsertEntity({
|
|
91
|
+
entityId: catId,
|
|
92
|
+
entityType: 'category',
|
|
93
|
+
name: catId,
|
|
94
|
+
filePath: cat.file || null,
|
|
95
|
+
line: cat.line || null,
|
|
96
|
+
protocols: cat.protocols || [],
|
|
97
|
+
metadata: {
|
|
98
|
+
className: cat.className,
|
|
99
|
+
categoryName: cat.categoryName,
|
|
100
|
+
methodCount: cat.methods?.length || 0,
|
|
101
|
+
},
|
|
102
|
+
});
|
|
103
|
+
entities++;
|
|
104
|
+
}
|
|
105
|
+
// ── 继承/遵循/扩展 边 (从 AST inheritanceGraph) ──
|
|
106
|
+
for (const edge of astSummary.inheritanceGraph || []) {
|
|
107
|
+
const fromType = this.#inferEntityType(edge.from, astSummary);
|
|
108
|
+
const toType = this.#inferEntityType(edge.to, astSummary);
|
|
109
|
+
await this.#addEdge(edge.from, fromType, edge.to, toType, edge.type, {
|
|
110
|
+
weight: 1.0,
|
|
111
|
+
source: 'ast-bootstrap',
|
|
112
|
+
});
|
|
113
|
+
edges++;
|
|
114
|
+
}
|
|
115
|
+
// ── 设计模式 (从 patternStats) ──
|
|
116
|
+
for (const [patternType, stat] of Object.entries(astSummary.patternStats || {})) {
|
|
117
|
+
const patternId = `pattern:${patternType}`;
|
|
118
|
+
await this.#upsertEntity({
|
|
119
|
+
entityId: patternId,
|
|
120
|
+
entityType: 'pattern',
|
|
121
|
+
name: patternType,
|
|
122
|
+
metadata: {
|
|
123
|
+
count: stat.count,
|
|
124
|
+
files: stat.files?.slice(0, 10),
|
|
125
|
+
},
|
|
126
|
+
});
|
|
127
|
+
entities++;
|
|
128
|
+
// 实例 → uses_pattern 边
|
|
129
|
+
for (const inst of (stat.instances || []).slice(0, 50)) {
|
|
130
|
+
const className = inst.className || inst.name;
|
|
131
|
+
if (className) {
|
|
132
|
+
await this.#addEdge(className, 'class', patternId, 'pattern', 'uses_pattern', {
|
|
133
|
+
weight: 0.8,
|
|
134
|
+
source: 'ast-pattern-detection',
|
|
135
|
+
file: inst.file,
|
|
136
|
+
});
|
|
137
|
+
edges++;
|
|
141
138
|
}
|
|
142
139
|
}
|
|
143
|
-
}
|
|
144
|
-
run();
|
|
140
|
+
}
|
|
145
141
|
const result = { entitiesUpserted: entities, edgesCreated: edges, durationMs: Date.now() - t0 };
|
|
146
142
|
this.log.info(`[CodeEntityGraph] AST populate: ${entities} entities, ${edges} edges (${result.durationMs}ms)`);
|
|
147
143
|
return result;
|
|
@@ -154,27 +150,40 @@ export class CodeEntityGraph {
|
|
|
154
150
|
*
|
|
155
151
|
* @param depGraphData spm.getDependencyGraph() 产出
|
|
156
152
|
*/
|
|
157
|
-
populateFromSpm(depGraphData) {
|
|
153
|
+
async populateFromSpm(depGraphData) {
|
|
158
154
|
if (!depGraphData) {
|
|
159
155
|
return { entitiesUpserted: 0, edgesCreated: 0, durationMs: 0 };
|
|
160
156
|
}
|
|
161
157
|
const t0 = Date.now();
|
|
162
158
|
let entities = 0;
|
|
163
|
-
const
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
},
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
159
|
+
for (const node of depGraphData.nodes || []) {
|
|
160
|
+
const nodeObj = typeof node === 'string' ? { id: node, label: node } : node;
|
|
161
|
+
await this.#upsertEntity({
|
|
162
|
+
entityId: nodeObj.id || nodeObj.label || String(node),
|
|
163
|
+
entityType: 'module',
|
|
164
|
+
name: nodeObj.label || nodeObj.id || String(node),
|
|
165
|
+
metadata: {
|
|
166
|
+
nodeType: nodeObj.type || 'module',
|
|
167
|
+
...(nodeObj.layer != null ? { layer: nodeObj.layer } : {}),
|
|
168
|
+
...(nodeObj.version != null ? { version: nodeObj.version } : {}),
|
|
169
|
+
...(nodeObj.group != null ? { group: nodeObj.group } : {}),
|
|
170
|
+
...(nodeObj.fullPath != null ? { fullPath: nodeObj.fullPath } : {}),
|
|
171
|
+
...(nodeObj.indirect != null ? { indirect: nodeObj.indirect } : {}),
|
|
172
|
+
},
|
|
173
|
+
});
|
|
174
|
+
entities++;
|
|
175
|
+
}
|
|
176
|
+
// 存储 layers 元数据(如果存在)到特殊实体
|
|
177
|
+
const layers = depGraphData.layers;
|
|
178
|
+
if (layers?.length) {
|
|
179
|
+
await this.#upsertEntity({
|
|
180
|
+
entityId: '__config_layers__',
|
|
181
|
+
entityType: 'config',
|
|
182
|
+
name: 'Config Layers',
|
|
183
|
+
metadata: { layers },
|
|
184
|
+
});
|
|
185
|
+
entities++;
|
|
186
|
+
}
|
|
178
187
|
const result = { entitiesUpserted: entities, edgesCreated: 0, durationMs: Date.now() - t0 };
|
|
179
188
|
this.log.info(`[CodeEntityGraph] SPM populate: ${entities} module entities (${result.durationMs}ms)`);
|
|
180
189
|
return result;
|
|
@@ -184,55 +193,52 @@ export class CodeEntityGraph {
|
|
|
184
193
|
*
|
|
185
194
|
* @param candidates 扁平关系数组或 Relations 对象
|
|
186
195
|
*/
|
|
187
|
-
populateFromCandidateRelations(candidates) {
|
|
196
|
+
async populateFromCandidateRelations(candidates) {
|
|
188
197
|
if (!candidates?.length) {
|
|
189
198
|
return { entitiesUpserted: 0, edgesCreated: 0, durationMs: 0 };
|
|
190
199
|
}
|
|
191
200
|
const t0 = Date.now();
|
|
192
201
|
let edges = 0;
|
|
193
|
-
const
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
for (const
|
|
212
|
-
|
|
213
|
-
flatRelations.push({ type, target: r.target, description: r.description });
|
|
214
|
-
}
|
|
202
|
+
for (const candidate of candidates) {
|
|
203
|
+
const title = candidate.title || candidate.id || '';
|
|
204
|
+
if (!title) {
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
// 处理 Relations 对象或扁平数组
|
|
208
|
+
let flatRelations;
|
|
209
|
+
const rels = candidate.relations;
|
|
210
|
+
if (typeof rels?.toFlatArray === 'function') {
|
|
211
|
+
flatRelations = rels.toFlatArray();
|
|
212
|
+
}
|
|
213
|
+
else if (Array.isArray(candidate.relations)) {
|
|
214
|
+
flatRelations = candidate.relations;
|
|
215
|
+
}
|
|
216
|
+
else if (candidate.relations && typeof candidate.relations === 'object') {
|
|
217
|
+
// 桶结构 → 扁平
|
|
218
|
+
flatRelations = [];
|
|
219
|
+
for (const [type, list] of Object.entries(candidate.relations)) {
|
|
220
|
+
for (const r of Array.isArray(list) ? list : []) {
|
|
221
|
+
flatRelations.push({ type, target: r.target, description: r.description });
|
|
215
222
|
}
|
|
216
223
|
}
|
|
217
|
-
|
|
224
|
+
}
|
|
225
|
+
else {
|
|
226
|
+
continue;
|
|
227
|
+
}
|
|
228
|
+
for (const rel of flatRelations) {
|
|
229
|
+
if (!rel.target) {
|
|
218
230
|
continue;
|
|
219
231
|
}
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
source: 'candidate-relations',
|
|
229
|
-
description: rel.description || '',
|
|
230
|
-
});
|
|
231
|
-
edges++;
|
|
232
|
-
}
|
|
232
|
+
// 映射关系类型到边类型
|
|
233
|
+
const relation = this.#mapRelationType(rel.type);
|
|
234
|
+
await this.#addEdge(title, 'recipe', rel.target, 'recipe', relation, {
|
|
235
|
+
weight: 0.7,
|
|
236
|
+
source: 'candidate-relations',
|
|
237
|
+
description: rel.description || '',
|
|
238
|
+
});
|
|
239
|
+
edges++;
|
|
233
240
|
}
|
|
234
|
-
}
|
|
235
|
-
run();
|
|
241
|
+
}
|
|
236
242
|
const result = { entitiesUpserted: 0, edgesCreated: edges, durationMs: Date.now() - t0 };
|
|
237
243
|
this.log.info(`[CodeEntityGraph] Candidate relations: ${edges} edges (${result.durationMs}ms)`);
|
|
238
244
|
return result;
|
|
@@ -241,97 +247,83 @@ export class CodeEntityGraph {
|
|
|
241
247
|
// Public API — 图谱查询
|
|
242
248
|
// ────────────────────────────────────────────
|
|
243
249
|
/** 获取单个实体信息 */
|
|
244
|
-
getEntity(entityId, entityType) {
|
|
245
|
-
let
|
|
250
|
+
async getEntity(entityId, entityType) {
|
|
251
|
+
let entity;
|
|
246
252
|
if (entityType) {
|
|
247
|
-
|
|
253
|
+
entity = await this.#entityRepo.findByEntityId(entityId, entityType, this.projectRoot);
|
|
248
254
|
}
|
|
249
255
|
else {
|
|
250
|
-
|
|
251
|
-
.prepare(`SELECT * FROM code_entities WHERE entity_id = ? AND project_root = ? LIMIT 1`)
|
|
252
|
-
.get(entityId, this.projectRoot);
|
|
256
|
+
entity = await this.#entityRepo.findByEntityIdOnly(entityId, this.projectRoot);
|
|
253
257
|
}
|
|
254
|
-
return
|
|
258
|
+
return entity ? this.#mapRepoEntity(entity) : null;
|
|
255
259
|
}
|
|
256
260
|
/**
|
|
257
261
|
* 按类型列出所有实体
|
|
258
262
|
* @param entityType 'class'|'protocol'|'category'|'module'|'pattern'
|
|
259
263
|
*/
|
|
260
|
-
listEntities(entityType, limit = 200) {
|
|
261
|
-
const
|
|
262
|
-
return
|
|
264
|
+
async listEntities(entityType, limit = 200) {
|
|
265
|
+
const entities = await this.#entityRepo.listByType(entityType, this.projectRoot, limit);
|
|
266
|
+
return entities.map((e) => this.#mapRepoEntity(e));
|
|
263
267
|
}
|
|
264
268
|
/**
|
|
265
269
|
* 搜索实体 (名称模糊匹配)
|
|
266
270
|
* @param [options.type] 过滤类型
|
|
267
271
|
*/
|
|
268
|
-
searchEntities(query, options = {}) {
|
|
269
|
-
const
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
params.push(options.type);
|
|
275
|
-
}
|
|
276
|
-
sql += ` ORDER BY name LIMIT ?`;
|
|
277
|
-
params.push(options.limit || 20);
|
|
278
|
-
return this.db
|
|
279
|
-
.prepare(sql)
|
|
280
|
-
.all(...params)
|
|
281
|
-
.map((r) => this.#mapEntity(r));
|
|
272
|
+
async searchEntities(query, options = {}) {
|
|
273
|
+
const entities = await this.#entityRepo.searchByName(query, this.projectRoot, {
|
|
274
|
+
entityType: options.type,
|
|
275
|
+
limit: options.limit || 20,
|
|
276
|
+
});
|
|
277
|
+
return entities.map((e) => this.#mapRepoEntity(e));
|
|
282
278
|
}
|
|
283
279
|
/**
|
|
284
280
|
* 获取实体的所有关系边
|
|
285
|
-
* @returns }
|
|
286
281
|
*/
|
|
287
|
-
getEntityEdges(entityId, entityType, direction = 'both') {
|
|
282
|
+
async getEntityEdges(entityId, entityType, direction = 'both') {
|
|
288
283
|
const outgoing = direction === 'both' || direction === 'out'
|
|
289
|
-
? this.
|
|
290
|
-
.prepare(`SELECT * FROM knowledge_edges WHERE from_id = ? AND from_type = ?`)
|
|
291
|
-
.all(entityId, entityType)
|
|
292
|
-
.map((row) => this.#mapEdge(row))
|
|
284
|
+
? await this.#edgeRepo.findOutgoing(entityId, entityType)
|
|
293
285
|
: [];
|
|
294
286
|
const incoming = direction === 'both' || direction === 'in'
|
|
295
|
-
? this.
|
|
296
|
-
.prepare(`SELECT * FROM knowledge_edges WHERE to_id = ? AND to_type = ?`)
|
|
297
|
-
.all(entityId, entityType)
|
|
298
|
-
.map((row) => this.#mapEdge(row))
|
|
287
|
+
? await this.#edgeRepo.findIncoming(entityId, entityType)
|
|
299
288
|
: [];
|
|
300
|
-
return {
|
|
289
|
+
return {
|
|
290
|
+
outgoing: outgoing.map((e) => this.#mapRepoEdge(e)),
|
|
291
|
+
incoming: incoming.map((e) => this.#mapRepoEdge(e)),
|
|
292
|
+
};
|
|
301
293
|
}
|
|
302
294
|
/**
|
|
303
295
|
* 获取继承链 (向上遍历 inherits 边)
|
|
304
296
|
* @returns 继承链 [class, parent, grandparent, ...]
|
|
305
297
|
*/
|
|
306
|
-
getInheritanceChain(className, maxDepth = 10) {
|
|
298
|
+
async getInheritanceChain(className, maxDepth = 10) {
|
|
307
299
|
const chain = [className];
|
|
308
300
|
let current = className;
|
|
309
301
|
for (let i = 0; i < maxDepth; i++) {
|
|
310
|
-
const
|
|
311
|
-
|
|
312
|
-
WHERE from_id = ? AND from_type = 'class' AND relation = 'inherits' LIMIT 1`)
|
|
313
|
-
.get(current);
|
|
314
|
-
if (!parent) {
|
|
302
|
+
const parentId = await this.#edgeRepo.findOutgoingToId(current, 'class', 'inherits');
|
|
303
|
+
if (!parentId) {
|
|
315
304
|
break;
|
|
316
305
|
}
|
|
317
|
-
chain.push(
|
|
318
|
-
current =
|
|
306
|
+
chain.push(parentId);
|
|
307
|
+
current = parentId;
|
|
319
308
|
}
|
|
320
309
|
return chain;
|
|
321
310
|
}
|
|
322
311
|
/**
|
|
323
312
|
* 获取所有子类/实现者 (向下遍历)
|
|
324
313
|
* @param entityType 'class'|'protocol'
|
|
325
|
-
* @returns >}
|
|
326
314
|
*/
|
|
327
|
-
getDescendants(entityId, entityType, maxDepth = 3) {
|
|
315
|
+
async getDescendants(entityId, entityType, maxDepth = 3) {
|
|
328
316
|
const results = [];
|
|
329
317
|
const visited = new Set();
|
|
330
318
|
const queue = [{ id: entityId, type: entityType, depth: 0 }];
|
|
331
319
|
// 类的子类/Category + 协议的遵循者
|
|
332
320
|
const relations = entityType === 'protocol' ? ['conforms', 'inherits'] : ['inherits', 'extends'];
|
|
333
321
|
while (queue.length > 0) {
|
|
334
|
-
const
|
|
322
|
+
const current = queue.shift();
|
|
323
|
+
if (!current) {
|
|
324
|
+
break;
|
|
325
|
+
}
|
|
326
|
+
const { id, type, depth } = current;
|
|
335
327
|
if (depth >= maxDepth) {
|
|
336
328
|
continue;
|
|
337
329
|
}
|
|
@@ -341,22 +333,19 @@ export class CodeEntityGraph {
|
|
|
341
333
|
}
|
|
342
334
|
visited.add(key);
|
|
343
335
|
for (const rel of relations) {
|
|
344
|
-
const children = this.
|
|
345
|
-
.prepare(`SELECT from_id, from_type FROM knowledge_edges
|
|
346
|
-
WHERE to_id = ? AND to_type = ? AND relation = ?`)
|
|
347
|
-
.all(id, type, rel);
|
|
336
|
+
const children = await this.#edgeRepo.findIncomingByFromTypes(id, type, rel);
|
|
348
337
|
for (const child of children) {
|
|
349
|
-
const childKey = `${child.
|
|
338
|
+
const childKey = `${child.fromType}:${child.fromId}`;
|
|
350
339
|
if (!visited.has(childKey)) {
|
|
351
340
|
results.push({
|
|
352
|
-
id: child.
|
|
353
|
-
type: child.
|
|
341
|
+
id: child.fromId,
|
|
342
|
+
type: child.fromType,
|
|
354
343
|
depth: depth + 1,
|
|
355
344
|
relation: rel,
|
|
356
345
|
});
|
|
357
346
|
queue.push({
|
|
358
|
-
id: child.
|
|
359
|
-
type: child.
|
|
347
|
+
id: child.fromId,
|
|
348
|
+
type: child.fromType,
|
|
360
349
|
depth: depth + 1,
|
|
361
350
|
});
|
|
362
351
|
}
|
|
@@ -366,18 +355,13 @@ export class CodeEntityGraph {
|
|
|
366
355
|
return results;
|
|
367
356
|
}
|
|
368
357
|
/** 获取协议遵循关系 (className → 遵循的协议列表) */
|
|
369
|
-
getConformances(className) {
|
|
370
|
-
|
|
371
|
-
.prepare(`SELECT to_id FROM knowledge_edges
|
|
372
|
-
WHERE from_id = ? AND from_type IN ('class', 'category') AND relation = 'conforms'`)
|
|
373
|
-
.all(className);
|
|
374
|
-
return rows.map((r) => r.to_id);
|
|
358
|
+
async getConformances(className) {
|
|
359
|
+
return this.#edgeRepo.findConformances(className);
|
|
375
360
|
}
|
|
376
361
|
/**
|
|
377
362
|
* 查找两个实体间的路径 (BFS)
|
|
378
|
-
* @returns }
|
|
379
363
|
*/
|
|
380
|
-
findPath(fromId, fromType, toId, toType, maxDepth = 5) {
|
|
364
|
+
async findPath(fromId, fromType, toId, toType, maxDepth = 5) {
|
|
381
365
|
const visited = new Set();
|
|
382
366
|
const queue = [
|
|
383
367
|
{
|
|
@@ -387,7 +371,11 @@ export class CodeEntityGraph {
|
|
|
387
371
|
},
|
|
388
372
|
];
|
|
389
373
|
while (queue.length > 0) {
|
|
390
|
-
const
|
|
374
|
+
const current = queue.shift();
|
|
375
|
+
if (!current) {
|
|
376
|
+
break;
|
|
377
|
+
}
|
|
378
|
+
const { id, type, path } = current;
|
|
391
379
|
if (path.length >= maxDepth) {
|
|
392
380
|
continue;
|
|
393
381
|
}
|
|
@@ -396,20 +384,18 @@ export class CodeEntityGraph {
|
|
|
396
384
|
continue;
|
|
397
385
|
}
|
|
398
386
|
visited.add(key);
|
|
399
|
-
const neighbors = this.
|
|
400
|
-
.prepare(`SELECT to_id, to_type, relation, weight FROM knowledge_edges WHERE from_id = ? AND from_type = ?`)
|
|
401
|
-
.all(id, type);
|
|
387
|
+
const neighbors = await this.#edgeRepo.findOutgoing(id, type);
|
|
402
388
|
for (const n of neighbors) {
|
|
403
389
|
const step = {
|
|
404
390
|
from: { id, type },
|
|
405
|
-
to: { id: n.
|
|
391
|
+
to: { id: n.toId, type: n.toType },
|
|
406
392
|
relation: n.relation,
|
|
407
393
|
};
|
|
408
394
|
const newPath = [...path, step];
|
|
409
|
-
if (n.
|
|
395
|
+
if (n.toId === toId && n.toType === toType) {
|
|
410
396
|
return { found: true, path: newPath, depth: newPath.length };
|
|
411
397
|
}
|
|
412
|
-
queue.push({ id: n.
|
|
398
|
+
queue.push({ id: n.toId, type: n.toType, path: newPath });
|
|
413
399
|
}
|
|
414
400
|
}
|
|
415
401
|
return {
|
|
@@ -420,14 +406,17 @@ export class CodeEntityGraph {
|
|
|
420
406
|
}
|
|
421
407
|
/**
|
|
422
408
|
* 影响分析: 修改某实体后,哪些实体可能受影响
|
|
423
|
-
* @returns >}
|
|
424
409
|
*/
|
|
425
|
-
getImpactRadius(entityId, entityType, maxDepth = 3) {
|
|
410
|
+
async getImpactRadius(entityId, entityType, maxDepth = 3) {
|
|
426
411
|
const impacted = [];
|
|
427
412
|
const visited = new Set();
|
|
428
413
|
const queue = [{ id: entityId, type: entityType, depth: 0 }];
|
|
429
414
|
while (queue.length > 0) {
|
|
430
|
-
const
|
|
415
|
+
const current = queue.shift();
|
|
416
|
+
if (!current) {
|
|
417
|
+
break;
|
|
418
|
+
}
|
|
419
|
+
const { id, type, depth } = current;
|
|
431
420
|
if (depth >= maxDepth) {
|
|
432
421
|
continue;
|
|
433
422
|
}
|
|
@@ -437,22 +426,19 @@ export class CodeEntityGraph {
|
|
|
437
426
|
}
|
|
438
427
|
visited.add(key);
|
|
439
428
|
// 找出所有"依赖/引用此实体"的上游
|
|
440
|
-
const dependents = this.
|
|
441
|
-
.prepare(`SELECT from_id, from_type, relation FROM knowledge_edges
|
|
442
|
-
WHERE to_id = ? AND to_type = ?`)
|
|
443
|
-
.all(id, type);
|
|
429
|
+
const dependents = await this.#edgeRepo.findIncoming(id, type);
|
|
444
430
|
for (const dep of dependents) {
|
|
445
|
-
const depKey = `${dep.
|
|
431
|
+
const depKey = `${dep.fromType}:${dep.fromId}`;
|
|
446
432
|
if (!visited.has(depKey)) {
|
|
447
433
|
impacted.push({
|
|
448
|
-
id: dep.
|
|
449
|
-
type: dep.
|
|
434
|
+
id: dep.fromId,
|
|
435
|
+
type: dep.fromType,
|
|
450
436
|
relation: dep.relation,
|
|
451
437
|
depth: depth + 1,
|
|
452
438
|
});
|
|
453
439
|
queue.push({
|
|
454
|
-
id: dep.
|
|
455
|
-
type: dep.
|
|
440
|
+
id: dep.fromId,
|
|
441
|
+
type: dep.fromType,
|
|
456
442
|
depth: depth + 1,
|
|
457
443
|
});
|
|
458
444
|
}
|
|
@@ -461,44 +447,28 @@ export class CodeEntityGraph {
|
|
|
461
447
|
return impacted;
|
|
462
448
|
}
|
|
463
449
|
/** 项目拓扑概览 — 统计信息 + 关键度排名 */
|
|
464
|
-
getTopology() {
|
|
465
|
-
const entityStats = this.
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
const
|
|
470
|
-
.prepare(`SELECT relation, COUNT(*) as count FROM knowledge_edges GROUP BY relation`)
|
|
471
|
-
.all();
|
|
472
|
-
// 入度最高的实体 = 被依赖最多
|
|
473
|
-
const hotNodes = this.db
|
|
474
|
-
.prepare(`SELECT to_id, to_type, COUNT(*) as in_degree
|
|
475
|
-
FROM knowledge_edges
|
|
476
|
-
GROUP BY to_id, to_type
|
|
477
|
-
ORDER BY in_degree DESC LIMIT 15`)
|
|
478
|
-
.all();
|
|
450
|
+
async getTopology() {
|
|
451
|
+
const entityStats = await this.#entityRepo.countByType(this.projectRoot);
|
|
452
|
+
const edgeStats = await this.#edgeRepo.countByRelation();
|
|
453
|
+
const hotNodes = await this.#edgeRepo.getHotNodes(15);
|
|
454
|
+
const totalEntities = Object.values(entityStats).reduce((sum, c) => sum + c, 0);
|
|
455
|
+
const totalEdges = Object.values(edgeStats).reduce((sum, c) => sum + c, 0);
|
|
479
456
|
return {
|
|
480
|
-
entities:
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
edges: Object.fromEntries(edgeStats.map((s) => [
|
|
485
|
-
s.relation,
|
|
486
|
-
s.count,
|
|
487
|
-
])),
|
|
488
|
-
totalEntities: entityStats.reduce((sum, s) => sum + (s.count || 0), 0),
|
|
489
|
-
totalEdges: edgeStats.reduce((sum, s) => sum + (s.count || 0), 0),
|
|
457
|
+
entities: entityStats,
|
|
458
|
+
edges: edgeStats,
|
|
459
|
+
totalEntities,
|
|
460
|
+
totalEdges,
|
|
490
461
|
hotNodes: hotNodes.map((n) => ({
|
|
491
|
-
id: n.
|
|
492
|
-
type: n.
|
|
493
|
-
inDegree: n.
|
|
462
|
+
id: n.id,
|
|
463
|
+
type: n.type,
|
|
464
|
+
inDegree: n.inDegree,
|
|
494
465
|
})),
|
|
495
466
|
};
|
|
496
467
|
}
|
|
497
468
|
/** 生成 Agent 可用的图谱上下文 (Markdown) */
|
|
498
|
-
generateContextForAgent(options = {}) {
|
|
469
|
+
async generateContextForAgent(options = {}) {
|
|
499
470
|
const maxEntities = options.maxEntities || 30;
|
|
500
|
-
const
|
|
501
|
-
const topo = this.getTopology();
|
|
471
|
+
const topo = await this.getTopology();
|
|
502
472
|
if (topo.totalEntities === 0) {
|
|
503
473
|
return '';
|
|
504
474
|
}
|
|
@@ -519,11 +489,11 @@ export class CodeEntityGraph {
|
|
|
519
489
|
lines.push('');
|
|
520
490
|
}
|
|
521
491
|
// 类继承概览
|
|
522
|
-
const classes = this.listEntities('class', maxEntities);
|
|
492
|
+
const classes = await this.listEntities('class', maxEntities);
|
|
523
493
|
if (classes.length > 0) {
|
|
524
494
|
lines.push('### 类继承关系');
|
|
525
495
|
for (const cls of classes) {
|
|
526
|
-
const chain = this.getInheritanceChain(cls.entityId, 5);
|
|
496
|
+
const chain = await this.getInheritanceChain(cls.entityId, 5);
|
|
527
497
|
if (chain.length > 1) {
|
|
528
498
|
lines.push(`- \`${chain.join(' → ')}\``);
|
|
529
499
|
}
|
|
@@ -531,11 +501,11 @@ export class CodeEntityGraph {
|
|
|
531
501
|
lines.push('');
|
|
532
502
|
}
|
|
533
503
|
// 协议
|
|
534
|
-
const protocols = this.listEntities('protocol', 15);
|
|
504
|
+
const protocols = await this.listEntities('protocol', 15);
|
|
535
505
|
if (protocols.length > 0) {
|
|
536
506
|
lines.push('### 协议');
|
|
537
507
|
for (const p of protocols) {
|
|
538
|
-
const conformers = this.getDescendants(p.entityId, 'protocol', 1);
|
|
508
|
+
const conformers = await this.getDescendants(p.entityId, 'protocol', 1);
|
|
539
509
|
const cNames = conformers.map((c) => c.id).slice(0, 5);
|
|
540
510
|
lines.push(`- \`${p.name}\` ← ${cNames.length > 0 ? cNames.map((n) => `\`${n}\``).join(', ') : '(无遵循者)'}`);
|
|
541
511
|
}
|
|
@@ -543,37 +513,36 @@ export class CodeEntityGraph {
|
|
|
543
513
|
}
|
|
544
514
|
// 调用图热路径 (Phase 5)
|
|
545
515
|
try {
|
|
546
|
-
const hotCallees = this.
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
.all();
|
|
554
|
-
if (hotCallees.length > 0) {
|
|
555
|
-
lines.push('### 调用图热路径 (Call Graph Hot Paths)');
|
|
556
|
-
for (const row of hotCallees) {
|
|
557
|
-
// 查找前几个调用者
|
|
558
|
-
const topCallers = this.db
|
|
559
|
-
.prepare(`SELECT from_id FROM knowledge_edges
|
|
560
|
-
WHERE relation = 'calls' AND to_id = ?
|
|
561
|
-
LIMIT 3`)
|
|
562
|
-
.all(row.to_id);
|
|
516
|
+
const hotCallees = await this.#edgeRepo.getHotNodes(15);
|
|
517
|
+
// Filter for 'calls' relation — use countIncomingByRelation for each
|
|
518
|
+
const callHotPaths = [];
|
|
519
|
+
for (const node of hotCallees) {
|
|
520
|
+
const callCount = await this.#edgeRepo.countIncomingByRelation(node.id, 'calls');
|
|
521
|
+
if (callCount > 0) {
|
|
522
|
+
const topCallers = await this.#edgeRepo.findIncomingByRelation(node.id, 'calls');
|
|
563
523
|
const callerNames = topCallers
|
|
564
|
-
.
|
|
524
|
+
.slice(0, 3)
|
|
525
|
+
.map((c) => `\`${c.fromId}\``)
|
|
565
526
|
.join(', ');
|
|
566
|
-
|
|
527
|
+
callHotPaths.push({
|
|
528
|
+
toId: node.id,
|
|
529
|
+
callCount,
|
|
530
|
+
callerNames: `${callerNames}${topCallers.length > 3 ? '...' : ''}`,
|
|
531
|
+
});
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
if (callHotPaths.length > 0) {
|
|
535
|
+
lines.push('### 调用图热路径 (Call Graph Hot Paths)');
|
|
536
|
+
for (const row of callHotPaths.slice(0, 15)) {
|
|
537
|
+
lines.push(`- \`${row.toId}\` ← ${row.callCount} 次调用 (${row.callerNames})`);
|
|
567
538
|
}
|
|
568
539
|
lines.push('');
|
|
569
540
|
}
|
|
570
541
|
// 数据流边摘要
|
|
571
|
-
const dataFlowCount = this.
|
|
572
|
-
|
|
573
|
-
.get();
|
|
574
|
-
if (dataFlowCount && dataFlowCount.cnt > 0) {
|
|
542
|
+
const dataFlowCount = await this.#edgeRepo.countByRelationType('data_flow');
|
|
543
|
+
if (dataFlowCount > 0) {
|
|
575
544
|
lines.push(`### 数据流`);
|
|
576
|
-
lines.push(`- 数据流边: ${dataFlowCount
|
|
545
|
+
lines.push(`- 数据流边: ${dataFlowCount} 条`);
|
|
577
546
|
lines.push('');
|
|
578
547
|
}
|
|
579
548
|
}
|
|
@@ -591,90 +560,87 @@ export class CodeEntityGraph {
|
|
|
591
560
|
* @param callEdges
|
|
592
561
|
* @param dataFlowEdges
|
|
593
562
|
*/
|
|
594
|
-
populateCallGraph(callEdges, dataFlowEdges) {
|
|
563
|
+
async populateCallGraph(callEdges, dataFlowEdges) {
|
|
595
564
|
const t0 = Date.now();
|
|
596
565
|
let edges = 0;
|
|
597
566
|
let entities = 0;
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
for (const
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
continue;
|
|
605
|
-
}
|
|
606
|
-
registeredMethods.add(fqn);
|
|
607
|
-
const entityId = this._extractEntityId(fqn);
|
|
608
|
-
const entityName = entityId; // 短名
|
|
609
|
-
const filePath = fqn.includes('::') ? fqn.split('::')[0] : null;
|
|
610
|
-
this.#upsertEntity({
|
|
611
|
-
entityId,
|
|
612
|
-
entityType: 'method',
|
|
613
|
-
name: entityName,
|
|
614
|
-
filePath,
|
|
615
|
-
metadata: { fqn, source: 'phase5-call-graph' },
|
|
616
|
-
});
|
|
617
|
-
entities++;
|
|
567
|
+
// ── 注册方法实体 (确保 from/to 的 entity 存在) ──
|
|
568
|
+
const registeredMethods = new Set();
|
|
569
|
+
for (const edge of callEdges) {
|
|
570
|
+
for (const fqn of [edge.caller, edge.callee]) {
|
|
571
|
+
if (registeredMethods.has(fqn)) {
|
|
572
|
+
continue;
|
|
618
573
|
}
|
|
574
|
+
registeredMethods.add(fqn);
|
|
575
|
+
const entityId = this._extractEntityId(fqn);
|
|
576
|
+
const entityName = entityId; // 短名
|
|
577
|
+
const filePath = fqn.includes('::') ? fqn.split('::')[0] : null;
|
|
578
|
+
await this.#upsertEntity({
|
|
579
|
+
entityId,
|
|
580
|
+
entityType: 'method',
|
|
581
|
+
name: entityName,
|
|
582
|
+
filePath,
|
|
583
|
+
metadata: { fqn, source: 'phase5-call-graph' },
|
|
584
|
+
});
|
|
585
|
+
entities++;
|
|
619
586
|
}
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
if (edge.isAwait) {
|
|
635
|
-
agg.hasAwait = true;
|
|
636
|
-
}
|
|
587
|
+
}
|
|
588
|
+
// ── 调用边 (聚合同一 caller-callee 对的多次调用,解决 Issue #4) ──
|
|
589
|
+
const aggregated = new Map(); // key = "callerId|calleeId" → aggregated metadata
|
|
590
|
+
for (const edge of callEdges) {
|
|
591
|
+
const callerId = this._extractEntityId(edge.caller);
|
|
592
|
+
const calleeId = this._extractEntityId(edge.callee);
|
|
593
|
+
const key = `${callerId}|${calleeId}`;
|
|
594
|
+
if (aggregated.has(key)) {
|
|
595
|
+
const agg = aggregated.get(key);
|
|
596
|
+
agg.callCount++;
|
|
597
|
+
agg.callSites.push({ line: edge.line, isAwait: edge.isAwait });
|
|
598
|
+
// 提升权重: direct 优先
|
|
599
|
+
if (edge.resolveMethod === 'direct') {
|
|
600
|
+
agg.resolveMethod = 'direct';
|
|
637
601
|
}
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
callerId,
|
|
641
|
-
calleeId,
|
|
642
|
-
callType: edge.callType,
|
|
643
|
-
resolveMethod: edge.resolveMethod,
|
|
644
|
-
file: edge.file,
|
|
645
|
-
hasAwait: edge.isAwait,
|
|
646
|
-
callCount: 1,
|
|
647
|
-
callSites: [{ line: edge.line, isAwait: edge.isAwait }],
|
|
648
|
-
});
|
|
602
|
+
if (edge.isAwait) {
|
|
603
|
+
agg.hasAwait = true;
|
|
649
604
|
}
|
|
650
605
|
}
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
callType:
|
|
656
|
-
resolveMethod:
|
|
657
|
-
file:
|
|
658
|
-
|
|
659
|
-
callCount:
|
|
660
|
-
callSites:
|
|
661
|
-
});
|
|
662
|
-
edges++;
|
|
663
|
-
}
|
|
664
|
-
// ── 数据流边 ──
|
|
665
|
-
for (const flow of dataFlowEdges) {
|
|
666
|
-
const fromId = this._extractEntityId(flow.from || '');
|
|
667
|
-
const toId = this._extractEntityId(flow.to || '');
|
|
668
|
-
this.#addEdge(fromId, 'method', toId, 'method', 'data_flow', {
|
|
669
|
-
weight: 0.5,
|
|
670
|
-
source: 'phase5-data-flow',
|
|
671
|
-
flowType: flow.flowType || '',
|
|
672
|
-
direction: flow.direction || '',
|
|
606
|
+
else {
|
|
607
|
+
aggregated.set(key, {
|
|
608
|
+
callerId,
|
|
609
|
+
calleeId,
|
|
610
|
+
callType: edge.callType,
|
|
611
|
+
resolveMethod: edge.resolveMethod,
|
|
612
|
+
file: edge.file,
|
|
613
|
+
hasAwait: edge.isAwait,
|
|
614
|
+
callCount: 1,
|
|
615
|
+
callSites: [{ line: edge.line, isAwait: edge.isAwait }],
|
|
673
616
|
});
|
|
674
|
-
edges++;
|
|
675
617
|
}
|
|
676
|
-
}
|
|
677
|
-
|
|
618
|
+
}
|
|
619
|
+
for (const agg of aggregated.values()) {
|
|
620
|
+
await this.#addEdge(agg.callerId, 'method', agg.calleeId, 'method', 'calls', {
|
|
621
|
+
weight: agg.resolveMethod === 'direct' ? 1.0 : 0.6,
|
|
622
|
+
source: 'phase5-call-graph',
|
|
623
|
+
callType: agg.callType,
|
|
624
|
+
resolveMethod: agg.resolveMethod,
|
|
625
|
+
file: agg.file,
|
|
626
|
+
isAwait: agg.hasAwait,
|
|
627
|
+
callCount: agg.callCount,
|
|
628
|
+
callSites: agg.callSites.slice(0, 10), // 最多保留 10 个调用点
|
|
629
|
+
});
|
|
630
|
+
edges++;
|
|
631
|
+
}
|
|
632
|
+
// ── 数据流边 ──
|
|
633
|
+
for (const flow of dataFlowEdges) {
|
|
634
|
+
const fromId = this._extractEntityId(flow.from || '');
|
|
635
|
+
const toId = this._extractEntityId(flow.to || '');
|
|
636
|
+
await this.#addEdge(fromId, 'method', toId, 'method', 'data_flow', {
|
|
637
|
+
weight: 0.5,
|
|
638
|
+
source: 'phase5-data-flow',
|
|
639
|
+
flowType: flow.flowType || '',
|
|
640
|
+
direction: flow.direction || '',
|
|
641
|
+
});
|
|
642
|
+
edges++;
|
|
643
|
+
}
|
|
678
644
|
const result = { entitiesUpserted: entities, edgesCreated: edges, durationMs: Date.now() - t0 };
|
|
679
645
|
this.log.info(`[CodeEntityGraph] Call graph: ${callEdges.length} call edges, ${dataFlowEdges.length} data flow edges, ${entities} method entities (${result.durationMs}ms)`);
|
|
680
646
|
return result;
|
|
@@ -685,26 +651,29 @@ export class CodeEntityGraph {
|
|
|
685
651
|
* @param methodId "ClassName.methodName" 或 FQN
|
|
686
652
|
* @returns >}
|
|
687
653
|
*/
|
|
688
|
-
getCallers(methodId, maxDepth = 2) {
|
|
654
|
+
async getCallers(methodId, maxDepth = 2) {
|
|
689
655
|
const results = [];
|
|
690
656
|
const visited = new Set();
|
|
691
657
|
const queue = [{ id: methodId, depth: 0 }];
|
|
692
658
|
while (queue.length > 0) {
|
|
693
|
-
const
|
|
659
|
+
const current = queue.shift();
|
|
660
|
+
if (!current) {
|
|
661
|
+
break;
|
|
662
|
+
}
|
|
663
|
+
const { id, depth } = current;
|
|
694
664
|
if (depth >= maxDepth || visited.has(id)) {
|
|
695
665
|
continue;
|
|
696
666
|
}
|
|
697
667
|
visited.add(id);
|
|
698
|
-
const callers = this.
|
|
699
|
-
for (const
|
|
700
|
-
const meta = JSON.parse(row.metadata_json || '{}');
|
|
668
|
+
const callers = await this.#edgeRepo.findIncomingByRelation(id, 'calls');
|
|
669
|
+
for (const edge of callers) {
|
|
701
670
|
results.push({
|
|
702
|
-
caller:
|
|
671
|
+
caller: edge.fromId,
|
|
703
672
|
depth: depth + 1,
|
|
704
|
-
callType:
|
|
673
|
+
callType: edge.metadata?.callType || 'unknown',
|
|
705
674
|
});
|
|
706
675
|
if (depth + 1 < maxDepth) {
|
|
707
|
-
queue.push({ id:
|
|
676
|
+
queue.push({ id: edge.fromId, depth: depth + 1 });
|
|
708
677
|
}
|
|
709
678
|
}
|
|
710
679
|
}
|
|
@@ -716,26 +685,29 @@ export class CodeEntityGraph {
|
|
|
716
685
|
* @param methodId "ClassName.methodName" 或 FQN
|
|
717
686
|
* @returns >}
|
|
718
687
|
*/
|
|
719
|
-
getCallees(methodId, maxDepth = 2) {
|
|
688
|
+
async getCallees(methodId, maxDepth = 2) {
|
|
720
689
|
const results = [];
|
|
721
690
|
const visited = new Set();
|
|
722
691
|
const queue = [{ id: methodId, depth: 0 }];
|
|
723
692
|
while (queue.length > 0) {
|
|
724
|
-
const
|
|
693
|
+
const current = queue.shift();
|
|
694
|
+
if (!current) {
|
|
695
|
+
break;
|
|
696
|
+
}
|
|
697
|
+
const { id, depth } = current;
|
|
725
698
|
if (depth >= maxDepth || visited.has(id)) {
|
|
726
699
|
continue;
|
|
727
700
|
}
|
|
728
701
|
visited.add(id);
|
|
729
|
-
const callees = this.
|
|
730
|
-
for (const
|
|
731
|
-
const meta = JSON.parse(row.metadata_json || '{}');
|
|
702
|
+
const callees = await this.#edgeRepo.findOutgoingByRelation(id, 'calls');
|
|
703
|
+
for (const edge of callees) {
|
|
732
704
|
results.push({
|
|
733
|
-
callee:
|
|
705
|
+
callee: edge.toId,
|
|
734
706
|
depth: depth + 1,
|
|
735
|
-
callType:
|
|
707
|
+
callType: edge.metadata?.callType || 'unknown',
|
|
736
708
|
});
|
|
737
709
|
if (depth + 1 < maxDepth) {
|
|
738
|
-
queue.push({ id:
|
|
710
|
+
queue.push({ id: edge.toId, depth: depth + 1 });
|
|
739
711
|
}
|
|
740
712
|
}
|
|
741
713
|
}
|
|
@@ -748,11 +720,11 @@ export class CodeEntityGraph {
|
|
|
748
720
|
* @param methodId "ClassName.methodName"
|
|
749
721
|
* @returns }
|
|
750
722
|
*/
|
|
751
|
-
getCallImpactRadius(methodId) {
|
|
752
|
-
const callers = this.getCallers(methodId, 3);
|
|
723
|
+
async getCallImpactRadius(methodId) {
|
|
724
|
+
const callers = await this.getCallers(methodId, 3);
|
|
753
725
|
const affectedFiles = new Set();
|
|
754
726
|
for (const c of callers) {
|
|
755
|
-
const entity = this.getEntity(c.caller, 'method');
|
|
727
|
+
const entity = await this.getEntity(c.caller, 'method');
|
|
756
728
|
if (entity?.filePath) {
|
|
757
729
|
affectedFiles.add(entity.filePath);
|
|
758
730
|
}
|
|
@@ -776,15 +748,12 @@ export class CodeEntityGraph {
|
|
|
776
748
|
return fqn;
|
|
777
749
|
}
|
|
778
750
|
/** 清除项目的所有代码实体 (重新 populate 前调用) */
|
|
779
|
-
clearProject() {
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
.run();
|
|
786
|
-
});
|
|
787
|
-
run();
|
|
751
|
+
async clearProject() {
|
|
752
|
+
await this.#entityRepo.clearProject(this.projectRoot);
|
|
753
|
+
// 清除 AST 产出的边 + Phase 5 调用图边 (保留 recipe/module 边)
|
|
754
|
+
await this.#edgeRepo.deleteByMetadataLike('%ast-bootstrap%');
|
|
755
|
+
await this.#edgeRepo.deleteByMetadataLike('%ast-pattern-detection%');
|
|
756
|
+
await this.#edgeRepo.deleteByMetadataLike('%phase5-%');
|
|
788
757
|
this.log.info(`[CodeEntityGraph] Cleared entities for project: ${this.projectRoot}`);
|
|
789
758
|
}
|
|
790
759
|
/**
|
|
@@ -793,104 +762,57 @@ export class CodeEntityGraph {
|
|
|
793
762
|
* @param filePaths 变更文件的相对路径列表
|
|
794
763
|
* @returns }
|
|
795
764
|
*/
|
|
796
|
-
clearCallGraphForFiles(filePaths) {
|
|
765
|
+
async clearCallGraphForFiles(filePaths) {
|
|
797
766
|
if (!filePaths?.length) {
|
|
798
767
|
return { deletedEdges: 0, deletedEntities: 0 };
|
|
799
768
|
}
|
|
800
769
|
let deletedEdges = 0;
|
|
801
770
|
let deletedEntities = 0;
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
for (const filePath of filePaths) {
|
|
817
|
-
const result = deleteEntitiesStmt.run(filePath, this.projectRoot);
|
|
818
|
-
deletedEntities += result.changes;
|
|
819
|
-
}
|
|
820
|
-
});
|
|
821
|
-
run();
|
|
771
|
+
// 1. 删除相关 call edges (metadata_json 包含 file 字段)
|
|
772
|
+
for (const filePath of filePaths) {
|
|
773
|
+
// 匹配 metadata 中 "file":"xxx" 字段
|
|
774
|
+
const changes = await this.#edgeRepo.deleteByMetadataLike(`%"file":"${filePath}"%`, [
|
|
775
|
+
'calls',
|
|
776
|
+
'data_flow',
|
|
777
|
+
]);
|
|
778
|
+
deletedEdges += changes;
|
|
779
|
+
}
|
|
780
|
+
// 2. 删除相关 method 实体
|
|
781
|
+
for (const filePath of filePaths) {
|
|
782
|
+
const changes = await this.#entityRepo.deleteByFileAndType(filePath, 'method', this.projectRoot);
|
|
783
|
+
deletedEntities += changes;
|
|
784
|
+
}
|
|
822
785
|
this.log.info(`[CodeEntityGraph] Incremental clear: ${deletedEdges} edges, ${deletedEntities} entities ` +
|
|
823
786
|
`for ${filePaths.length} files`);
|
|
824
787
|
return { deletedEdges, deletedEntities };
|
|
825
788
|
}
|
|
826
789
|
// ────────────────────────────────────────────
|
|
827
|
-
// Private — Schema & Statements
|
|
828
|
-
// ────────────────────────────────────────────
|
|
829
|
-
#ensureTable() {
|
|
830
|
-
this.db.exec(`
|
|
831
|
-
CREATE TABLE IF NOT EXISTS code_entities (
|
|
832
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
833
|
-
entity_id TEXT NOT NULL,
|
|
834
|
-
entity_type TEXT NOT NULL,
|
|
835
|
-
project_root TEXT NOT NULL,
|
|
836
|
-
name TEXT NOT NULL,
|
|
837
|
-
file_path TEXT,
|
|
838
|
-
line_number INTEGER,
|
|
839
|
-
superclass TEXT,
|
|
840
|
-
protocols TEXT DEFAULT '[]',
|
|
841
|
-
metadata_json TEXT DEFAULT '{}',
|
|
842
|
-
created_at INTEGER NOT NULL,
|
|
843
|
-
updated_at INTEGER NOT NULL,
|
|
844
|
-
UNIQUE (entity_id, entity_type, project_root)
|
|
845
|
-
);
|
|
846
|
-
CREATE INDEX IF NOT EXISTS idx_ce_project ON code_entities(project_root);
|
|
847
|
-
CREATE INDEX IF NOT EXISTS idx_ce_type ON code_entities(entity_type);
|
|
848
|
-
CREATE INDEX IF NOT EXISTS idx_ce_name ON code_entities(name);
|
|
849
|
-
CREATE INDEX IF NOT EXISTS idx_ce_file ON code_entities(file_path);
|
|
850
|
-
CREATE INDEX IF NOT EXISTS idx_ce_superclass ON code_entities(superclass);
|
|
851
|
-
`);
|
|
852
|
-
}
|
|
853
|
-
#prepareStatements() {
|
|
854
|
-
this.stmts = {
|
|
855
|
-
upsert: this.db.prepare(`
|
|
856
|
-
INSERT INTO code_entities (entity_id, entity_type, project_root, name, file_path, line_number, superclass, protocols, metadata_json, created_at, updated_at)
|
|
857
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
858
|
-
ON CONFLICT (entity_id, entity_type, project_root) DO UPDATE SET
|
|
859
|
-
name = excluded.name,
|
|
860
|
-
file_path = COALESCE(excluded.file_path, code_entities.file_path),
|
|
861
|
-
line_number = COALESCE(excluded.line_number, code_entities.line_number),
|
|
862
|
-
superclass = COALESCE(excluded.superclass, code_entities.superclass),
|
|
863
|
-
protocols = excluded.protocols,
|
|
864
|
-
metadata_json = excluded.metadata_json,
|
|
865
|
-
updated_at = excluded.updated_at
|
|
866
|
-
`),
|
|
867
|
-
getEntity: this.db.prepare(`SELECT * FROM code_entities WHERE entity_id = ? AND entity_type = ? AND project_root = ?`),
|
|
868
|
-
listByType: this.db.prepare(`SELECT * FROM code_entities WHERE entity_type = ? AND project_root = ? ORDER BY name LIMIT ?`),
|
|
869
|
-
clearEntities: this.db.prepare(`DELETE FROM code_entities WHERE project_root = ?`),
|
|
870
|
-
addEdge: this.db.prepare(`
|
|
871
|
-
INSERT OR REPLACE INTO knowledge_edges (from_id, from_type, to_id, to_type, relation, weight, metadata_json, created_at, updated_at)
|
|
872
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
873
|
-
`),
|
|
874
|
-
// Phase 5: 调用图查询 (pre-prepared 避免每次调用都创建)
|
|
875
|
-
getCallers: this.db.prepare(`SELECT from_id, from_type, metadata_json FROM knowledge_edges
|
|
876
|
-
WHERE to_id = ? AND relation = 'calls'`),
|
|
877
|
-
getCallees: this.db.prepare(`SELECT to_id, to_type, metadata_json FROM knowledge_edges
|
|
878
|
-
WHERE from_id = ? AND relation = 'calls'`),
|
|
879
|
-
getEdge: this.db.prepare(`SELECT metadata_json FROM knowledge_edges
|
|
880
|
-
WHERE from_id = ? AND from_type = ? AND to_id = ? AND to_type = ? AND relation = ?`),
|
|
881
|
-
};
|
|
882
|
-
}
|
|
883
|
-
// ────────────────────────────────────────────
|
|
884
790
|
// Private — Helpers
|
|
885
791
|
// ────────────────────────────────────────────
|
|
886
|
-
#upsertEntity(entity) {
|
|
887
|
-
|
|
888
|
-
|
|
792
|
+
async #upsertEntity(entity) {
|
|
793
|
+
await this.#entityRepo.upsert({
|
|
794
|
+
entityId: entity.entityId,
|
|
795
|
+
entityType: entity.entityType,
|
|
796
|
+
projectRoot: this.projectRoot,
|
|
797
|
+
name: entity.name,
|
|
798
|
+
filePath: entity.filePath ?? null,
|
|
799
|
+
lineNumber: entity.line ?? null,
|
|
800
|
+
superclass: entity.superclass ?? null,
|
|
801
|
+
protocols: entity.protocols ?? [],
|
|
802
|
+
metadata: entity.metadata ?? {},
|
|
803
|
+
});
|
|
889
804
|
}
|
|
890
|
-
#addEdge(fromId, fromType, toId, toType, relation, metadata = {}) {
|
|
891
|
-
const now = Math.floor(Date.now() / 1000);
|
|
805
|
+
async #addEdge(fromId, fromType, toId, toType, relation, metadata = {}) {
|
|
892
806
|
try {
|
|
893
|
-
this.
|
|
807
|
+
await this.#edgeRepo.upsertEdge({
|
|
808
|
+
fromId,
|
|
809
|
+
fromType,
|
|
810
|
+
toId,
|
|
811
|
+
toType,
|
|
812
|
+
relation,
|
|
813
|
+
weight: metadata.weight || 1.0,
|
|
814
|
+
metadata,
|
|
815
|
+
});
|
|
894
816
|
}
|
|
895
817
|
catch (err) {
|
|
896
818
|
// Ignore duplicate edge errors
|
|
@@ -932,30 +854,30 @@ export class CodeEntityGraph {
|
|
|
932
854
|
};
|
|
933
855
|
return mapping[type] || 'related';
|
|
934
856
|
}
|
|
935
|
-
#
|
|
857
|
+
#mapRepoEdge(edge) {
|
|
936
858
|
return {
|
|
937
|
-
fromId:
|
|
938
|
-
fromType:
|
|
939
|
-
toId:
|
|
940
|
-
toType:
|
|
941
|
-
relation:
|
|
942
|
-
weight:
|
|
943
|
-
metadata:
|
|
859
|
+
fromId: edge.fromId,
|
|
860
|
+
fromType: edge.fromType,
|
|
861
|
+
toId: edge.toId,
|
|
862
|
+
toType: edge.toType,
|
|
863
|
+
relation: edge.relation,
|
|
864
|
+
weight: edge.weight,
|
|
865
|
+
metadata: edge.metadata,
|
|
944
866
|
};
|
|
945
867
|
}
|
|
946
|
-
#
|
|
868
|
+
#mapRepoEntity(entity) {
|
|
947
869
|
return {
|
|
948
|
-
entityId:
|
|
949
|
-
entityType:
|
|
950
|
-
name:
|
|
951
|
-
filePath:
|
|
952
|
-
line:
|
|
953
|
-
superclass:
|
|
954
|
-
protocols:
|
|
955
|
-
metadata:
|
|
956
|
-
projectRoot:
|
|
957
|
-
createdAt:
|
|
958
|
-
updatedAt:
|
|
870
|
+
entityId: entity.entityId,
|
|
871
|
+
entityType: entity.entityType,
|
|
872
|
+
name: entity.name,
|
|
873
|
+
filePath: entity.filePath,
|
|
874
|
+
line: entity.lineNumber,
|
|
875
|
+
superclass: entity.superclass,
|
|
876
|
+
protocols: entity.protocols,
|
|
877
|
+
metadata: entity.metadata,
|
|
878
|
+
projectRoot: entity.projectRoot,
|
|
879
|
+
createdAt: entity.createdAt,
|
|
880
|
+
updatedAt: entity.updatedAt,
|
|
959
881
|
};
|
|
960
882
|
}
|
|
961
883
|
}
|