autosnippet 3.3.7 → 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 +7 -4
- 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/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 +4 -17
- 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/CustomConfigDiscoverer.d.ts +0 -2
- package/dist/lib/core/discovery/CustomConfigDiscoverer.js +0 -2
- 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/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/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/orchestrator.js +33 -16
- 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.js +1 -1
- package/dist/lib/external/mcp/handlers/dimension-complete-external.js +7 -3
- package/dist/lib/external/mcp/handlers/evolve-external.d.ts +1 -0
- package/dist/lib/external/mcp/handlers/evolve-external.js +13 -16
- package/dist/lib/external/mcp/handlers/guard.js +15 -24
- 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 +11 -13
- package/dist/lib/http/routes/guardReport.js +3 -5
- package/dist/lib/http/routes/panorama.js +12 -12
- package/dist/lib/http/routes/recipes.js +59 -8
- package/dist/lib/http/routes/remote.js +3 -13
- package/dist/lib/http/routes/search.js +11 -8
- 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.js +7 -4
- package/dist/lib/injection/ServiceMap.d.ts +20 -0
- 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.js +8 -4
- 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 +39 -63
- package/dist/lib/service/knowledge/CodeEntityGraph.js +418 -512
- 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 +76 -38
- 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/panorama/CouplingAnalyzer.d.ts +5 -3
- package/dist/lib/service/panorama/CouplingAnalyzer.js +102 -39
- package/dist/lib/service/panorama/DimensionAnalyzer.d.ts +7 -4
- package/dist/lib/service/panorama/DimensionAnalyzer.js +72 -25
- package/dist/lib/service/panorama/LayerInferrer.js +1 -1
- package/dist/lib/service/panorama/ModuleDiscoverer.d.ts +7 -6
- package/dist/lib/service/panorama/ModuleDiscoverer.js +174 -82
- package/dist/lib/service/panorama/PanoramaAggregator.d.ts +10 -3
- package/dist/lib/service/panorama/PanoramaAggregator.js +67 -79
- package/dist/lib/service/panorama/PanoramaScanner.d.ts +5 -1
- package/dist/lib/service/panorama/PanoramaScanner.js +32 -31
- package/dist/lib/service/panorama/PanoramaService.d.ts +11 -8
- package/dist/lib/service/panorama/PanoramaService.js +41 -66
- package/dist/lib/service/panorama/PanoramaTypes.d.ts +3 -0
- package/dist/lib/service/panorama/RoleRefiner.d.ts +8 -5
- package/dist/lib/service/panorama/RoleRefiner.js +52 -283
- package/dist/lib/service/panorama/TechStackProfiler.js +7 -119
- 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 +5 -8
- package/dist/lib/service/skills/SignalCollector.js +28 -55
- package/dist/lib/service/skills/SkillAdvisor.d.ts +7 -13
- package/dist/lib/service/skills/SkillAdvisor.js +30 -79
- 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 +16 -0
- package/dist/lib/shared/constants.d.ts +19 -19
- package/dist/lib/shared/constants.js +10 -10
- package/dist/lib/shared/schemas/mcp-tools.d.ts +1 -1
- 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-FHns2ypa.js +0 -1
- package/dashboard/dist/assets/index-BRJv5Y3r.js +0 -135
- package/dashboard/dist/assets/index-DzoB7kxK.css +0 -1
- package/dist/lib/repository/base/BaseRepository.d.ts +0 -53
- package/dist/lib/repository/base/BaseRepository.js +0 -226
|
@@ -13,110 +13,93 @@
|
|
|
13
13
|
*/
|
|
14
14
|
import fs from 'node:fs';
|
|
15
15
|
import path from 'node:path';
|
|
16
|
+
import { LanguageProfiles } from '#shared/LanguageProfiles.js';
|
|
16
17
|
import { inferTargetRole } from '../../external/mcp/handlers/TargetClassifier.js';
|
|
17
18
|
/* ═══ Constants ═══════════════════════════════════════════ */
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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
|
-
]);
|
|
19
|
+
// All language-specific constants are now in LanguageProfiles.
|
|
20
|
+
// These aliases delegate to the unified registry for backward compat:
|
|
21
|
+
const SOURCE_EXTS = LanguageProfiles.sourceExts;
|
|
22
|
+
const SKIP_DIRS = LanguageProfiles.skipDirs;
|
|
23
|
+
const HOST_SKIP_SUFFIXES = LanguageProfiles.artifactSuffixes;
|
|
24
|
+
const HOST_VENDOR_DIRS = LanguageProfiles.vendorDirs;
|
|
48
25
|
/* ═══ ModuleDiscoverer Class ══════════════════════════════ */
|
|
49
26
|
export class ModuleDiscoverer {
|
|
50
|
-
#
|
|
27
|
+
#entityRepo;
|
|
28
|
+
#edgeRepo;
|
|
51
29
|
#projectRoot;
|
|
52
|
-
constructor(
|
|
53
|
-
this.#
|
|
30
|
+
constructor(entityRepo, edgeRepo, projectRoot) {
|
|
31
|
+
this.#entityRepo = entityRepo;
|
|
32
|
+
this.#edgeRepo = edgeRepo;
|
|
54
33
|
this.#projectRoot = projectRoot;
|
|
55
34
|
}
|
|
56
35
|
/**
|
|
57
36
|
* 从 DB 中读取已扫描的模块数据。
|
|
58
|
-
* 若无 module
|
|
37
|
+
* 若无 module 实体(含 host),返回空数组(让调用侧决定是否重新扫描)。
|
|
59
38
|
*/
|
|
60
|
-
discover() {
|
|
39
|
+
async discover() {
|
|
61
40
|
// 从 code_entities 查 entity_type = 'module'(排除 external/host 节点)
|
|
62
|
-
const moduleEntities = this.#
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
.all(this.#projectRoot);
|
|
67
|
-
if (moduleEntities.length === 0) {
|
|
41
|
+
const moduleEntities = await this.#entityRepo.findLocalModules(this.#projectRoot);
|
|
42
|
+
// 检查是否存在 host 模块(用于后续分解)
|
|
43
|
+
const hasHostModules = await this.#hasHostModules();
|
|
44
|
+
if (moduleEntities.length === 0 && !hasHostModules) {
|
|
68
45
|
return [];
|
|
69
46
|
}
|
|
70
47
|
// 收集 is_part_of 边关联的文件
|
|
71
48
|
const moduleFiles = new Map();
|
|
72
49
|
for (const me of moduleEntities) {
|
|
73
|
-
const moduleName = me.
|
|
50
|
+
const moduleName = me.entityId;
|
|
74
51
|
moduleFiles.set(moduleName, new Set());
|
|
75
|
-
const parts = this.#
|
|
76
|
-
.prepare(`SELECT ke.from_id FROM knowledge_edges ke
|
|
77
|
-
WHERE ke.to_id = ? AND ke.to_type = 'module' AND ke.relation = 'is_part_of'`)
|
|
78
|
-
.all(moduleName);
|
|
52
|
+
const parts = await this.#edgeRepo.findIncomingByRelation(moduleName, 'is_part_of');
|
|
79
53
|
for (const part of parts) {
|
|
80
|
-
const entity = this.#
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
.get(part.from_id, this.#projectRoot);
|
|
84
|
-
if (entity?.file_path) {
|
|
85
|
-
moduleFiles.get(moduleName).add(entity.file_path);
|
|
54
|
+
const entity = await this.#entityRepo.findByEntityIdOnly(part.fromId, this.#projectRoot);
|
|
55
|
+
if (entity?.filePath) {
|
|
56
|
+
moduleFiles.get(moduleName).add(entity.filePath);
|
|
86
57
|
}
|
|
87
58
|
}
|
|
88
59
|
}
|
|
89
60
|
// 策略 1.5: module 实体有但文件为空(SPM 只建了模块节点)
|
|
90
61
|
const totalFileCount = [...moduleFiles.values()].reduce((sum, s) => sum + s.size, 0);
|
|
91
62
|
if (totalFileCount === 0) {
|
|
92
|
-
this.#enrichModuleFiles(moduleFiles);
|
|
63
|
+
await this.#enrichModuleFiles(moduleFiles);
|
|
93
64
|
}
|
|
94
65
|
// 读取模块 metadata 中的 configLayer 信息
|
|
95
|
-
const moduleLayerMap = this.#readModuleLayerMetadata(moduleEntities);
|
|
96
|
-
|
|
66
|
+
const moduleLayerMap = await this.#readModuleLayerMetadata(moduleEntities);
|
|
67
|
+
const regularModules = [...moduleFiles.entries()].map(([name, files]) => ({
|
|
97
68
|
name,
|
|
98
69
|
inferredRole: inferTargetRole(name),
|
|
99
70
|
files: [...files],
|
|
100
71
|
configLayer: moduleLayerMap.get(name),
|
|
101
72
|
}));
|
|
73
|
+
// 策略 2: 分解 host 模块(主工程目录)为子模块
|
|
74
|
+
const hostSubModules = await this.#decomposeHostModules(moduleFiles);
|
|
75
|
+
return [...regularModules, ...hostSubModules];
|
|
102
76
|
}
|
|
103
77
|
/**
|
|
104
78
|
* 读取 config layers 元数据(如果存在)
|
|
105
79
|
* @returns 从 `__config_layers__` 实体中恢复的层级定义
|
|
106
80
|
*/
|
|
107
|
-
readConfigLayers() {
|
|
81
|
+
async readConfigLayers() {
|
|
108
82
|
try {
|
|
109
|
-
const
|
|
110
|
-
|
|
111
|
-
WHERE entity_id = '__config_layers__' AND entity_type = 'config' AND project_root = ?
|
|
112
|
-
LIMIT 1`)
|
|
113
|
-
.get(this.#projectRoot);
|
|
114
|
-
if (!row?.metadata_json) {
|
|
83
|
+
const entity = await this.#entityRepo.findByEntityIdOnly('__config_layers__', this.#projectRoot);
|
|
84
|
+
if (!entity?.metadata) {
|
|
115
85
|
return null;
|
|
116
86
|
}
|
|
117
|
-
const meta =
|
|
87
|
+
const meta = entity.metadata;
|
|
118
88
|
if (Array.isArray(meta.layers) && meta.layers.length > 0) {
|
|
119
|
-
|
|
89
|
+
const layers = meta.layers;
|
|
90
|
+
// 当存在 host 模块时,注入 Application 层(位于所有配置层之上)
|
|
91
|
+
if (await this.#hasHostModules()) {
|
|
92
|
+
const minOrder = Math.min(...layers.map((l) => l.order));
|
|
93
|
+
const hasAppLayer = layers.some((l) => l.name.toLowerCase() === 'application' || l.name.toLowerCase() === 'app');
|
|
94
|
+
if (!hasAppLayer) {
|
|
95
|
+
layers.unshift({
|
|
96
|
+
name: 'Application',
|
|
97
|
+
order: minOrder - 1,
|
|
98
|
+
accessibleLayers: layers.map((l) => l.name),
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return layers;
|
|
120
103
|
}
|
|
121
104
|
}
|
|
122
105
|
catch {
|
|
@@ -128,20 +111,14 @@ export class ModuleDiscoverer {
|
|
|
128
111
|
/**
|
|
129
112
|
* 从 code_entities metadata 中读取每个模块的 layer 信息
|
|
130
113
|
*/
|
|
131
|
-
#readModuleLayerMetadata(moduleEntities) {
|
|
114
|
+
async #readModuleLayerMetadata(moduleEntities) {
|
|
132
115
|
const result = new Map();
|
|
133
116
|
for (const me of moduleEntities) {
|
|
134
|
-
const moduleName = me.
|
|
117
|
+
const moduleName = me.entityId;
|
|
135
118
|
try {
|
|
136
|
-
const
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
LIMIT 1`)
|
|
140
|
-
.get(moduleName, this.#projectRoot);
|
|
141
|
-
if (row?.metadata_json) {
|
|
142
|
-
const meta = typeof row.metadata_json === 'string'
|
|
143
|
-
? JSON.parse(row.metadata_json)
|
|
144
|
-
: row.metadata_json;
|
|
119
|
+
const entity = await this.#entityRepo.findByEntityIdOnly(moduleName, this.#projectRoot);
|
|
120
|
+
if (entity?.metadata) {
|
|
121
|
+
const meta = entity.metadata;
|
|
145
122
|
if (meta.layer && typeof meta.layer === 'string') {
|
|
146
123
|
result.set(moduleName, meta.layer);
|
|
147
124
|
}
|
|
@@ -153,12 +130,24 @@ export class ModuleDiscoverer {
|
|
|
153
130
|
}
|
|
154
131
|
return result;
|
|
155
132
|
}
|
|
133
|
+
/**
|
|
134
|
+
* 检查 DB 中是否存在 nodeType='host' 的模块实体
|
|
135
|
+
*/
|
|
136
|
+
async #hasHostModules() {
|
|
137
|
+
try {
|
|
138
|
+
const cnt = await this.#entityRepo.countModulesByNodeType(this.#projectRoot, 'host');
|
|
139
|
+
return cnt > 0;
|
|
140
|
+
}
|
|
141
|
+
catch {
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
156
145
|
/**
|
|
157
146
|
* 为已知模块名填充文件路径:
|
|
158
147
|
* a. 文件系统扫描(递归 4 层找模块同名目录)
|
|
159
148
|
* b. DB code_entities.file_path 路径段匹配
|
|
160
149
|
*/
|
|
161
|
-
#enrichModuleFiles(moduleFiles) {
|
|
150
|
+
async #enrichModuleFiles(moduleFiles) {
|
|
162
151
|
const moduleNames = [...moduleFiles.keys()];
|
|
163
152
|
// a. 文件系统扫描
|
|
164
153
|
for (const modName of moduleNames) {
|
|
@@ -174,14 +163,10 @@ export class ModuleDiscoverer {
|
|
|
174
163
|
if (totalAfterFs > 0) {
|
|
175
164
|
return;
|
|
176
165
|
}
|
|
177
|
-
const allFiles = this.#
|
|
178
|
-
.prepare(`SELECT DISTINCT file_path FROM code_entities
|
|
179
|
-
WHERE project_root = ? AND file_path IS NOT NULL AND entity_type != 'module'`)
|
|
180
|
-
.all(this.#projectRoot);
|
|
166
|
+
const allFiles = await this.#entityRepo.findDistinctFilePaths(this.#projectRoot);
|
|
181
167
|
// 长名优先,避免短名误匹配
|
|
182
168
|
const sorted = [...moduleNames].sort((a, b) => b.length - a.length);
|
|
183
|
-
for (const
|
|
184
|
-
const filePath = row.file_path;
|
|
169
|
+
for (const filePath of allFiles) {
|
|
185
170
|
if (!filePath) {
|
|
186
171
|
continue;
|
|
187
172
|
}
|
|
@@ -238,4 +223,111 @@ export class ModuleDiscoverer {
|
|
|
238
223
|
}
|
|
239
224
|
return files;
|
|
240
225
|
}
|
|
226
|
+
/* ─── 策略 2: Host 模块分解 ────────────────────── */
|
|
227
|
+
/**
|
|
228
|
+
* 分解 host 类型模块(主工程目录)为子模块。
|
|
229
|
+
*
|
|
230
|
+
* 适用场景:混合项目(如 Boxfile/EasyBox + 主工程未模块化代码)中,
|
|
231
|
+
* host 模块包含大量按文件夹组织但未声明为独立模块的业务代码。
|
|
232
|
+
*
|
|
233
|
+
* 策略:
|
|
234
|
+
* 1. 从 DB 查询 nodeType='host' 的模块实体
|
|
235
|
+
* 2. 扫描 host 目录的子文件夹,每个含 ≥2 个源文件的文件夹视为隐式子模块
|
|
236
|
+
* 3. 排除已被现有模块覆盖的文件(避免重复计数)
|
|
237
|
+
* 4. 跳过资源目录(.xcassets, .bundle, .lproj 等)和第三方代码目录
|
|
238
|
+
* 5. 当项目有 configLayers 时,为子模块分配 Application 层(host 在所有声明层之上)
|
|
239
|
+
*/
|
|
240
|
+
async #decomposeHostModules(existingModuleFiles) {
|
|
241
|
+
const hostEntities = await this.#entityRepo.findModulesByNodeTypes(this.#projectRoot, ['host']);
|
|
242
|
+
if (hostEntities.length === 0) {
|
|
243
|
+
return [];
|
|
244
|
+
}
|
|
245
|
+
// 检查是否有 configLayers — 决定是否分配 Application 层
|
|
246
|
+
const hasConfigLayers = (await this.readConfigLayers()) !== null;
|
|
247
|
+
// 已有模块名 + 所有已归属文件(用于去重)
|
|
248
|
+
const existingNames = new Set(existingModuleFiles.keys());
|
|
249
|
+
const allExistingFiles = new Set();
|
|
250
|
+
for (const files of existingModuleFiles.values()) {
|
|
251
|
+
for (const f of files) {
|
|
252
|
+
allExistingFiles.add(f);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
const result = [];
|
|
256
|
+
for (const host of hostEntities) {
|
|
257
|
+
let meta = {};
|
|
258
|
+
try {
|
|
259
|
+
meta = host.metadata ?? {};
|
|
260
|
+
}
|
|
261
|
+
catch {
|
|
262
|
+
/* skip parse error */
|
|
263
|
+
}
|
|
264
|
+
const hostName = host.name;
|
|
265
|
+
const hostDir = meta.fullPath || path.join(this.#projectRoot, hostName);
|
|
266
|
+
if (!this.#isDirectory(hostDir)) {
|
|
267
|
+
continue;
|
|
268
|
+
}
|
|
269
|
+
let entries;
|
|
270
|
+
try {
|
|
271
|
+
entries = fs.readdirSync(hostDir, { withFileTypes: true });
|
|
272
|
+
}
|
|
273
|
+
catch {
|
|
274
|
+
continue;
|
|
275
|
+
}
|
|
276
|
+
const rootFiles = [];
|
|
277
|
+
for (const entry of entries) {
|
|
278
|
+
if (entry.isDirectory()) {
|
|
279
|
+
// 跳过隐藏目录、构建产物、资源目录
|
|
280
|
+
if (SKIP_DIRS.has(entry.name) || entry.name.startsWith('.')) {
|
|
281
|
+
continue;
|
|
282
|
+
}
|
|
283
|
+
if (HOST_SKIP_SUFFIXES.some((suffix) => entry.name.endsWith(suffix))) {
|
|
284
|
+
continue;
|
|
285
|
+
}
|
|
286
|
+
// 跳过第三方/供应商代码目录
|
|
287
|
+
if (HOST_VENDOR_DIRS.has(entry.name.toLowerCase())) {
|
|
288
|
+
continue;
|
|
289
|
+
}
|
|
290
|
+
const dirPath = path.join(hostDir, entry.name);
|
|
291
|
+
const files = this.#collectSourceFiles(dirPath).filter((f) => !allExistingFiles.has(f));
|
|
292
|
+
if (files.length < 2) {
|
|
293
|
+
continue;
|
|
294
|
+
}
|
|
295
|
+
// 名称冲突时加 host 前缀
|
|
296
|
+
const moduleName = existingNames.has(entry.name)
|
|
297
|
+
? `${hostName}/${entry.name}`
|
|
298
|
+
: entry.name;
|
|
299
|
+
result.push({
|
|
300
|
+
name: moduleName,
|
|
301
|
+
inferredRole: inferTargetRole(entry.name),
|
|
302
|
+
files,
|
|
303
|
+
configLayer: hasConfigLayers ? 'Application' : undefined,
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
else if (entry.isFile() && SOURCE_EXTS.has(path.extname(entry.name).toLowerCase())) {
|
|
307
|
+
const fullPath = path.join(hostDir, entry.name);
|
|
308
|
+
if (!allExistingFiles.has(fullPath)) {
|
|
309
|
+
rootFiles.push(fullPath);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
// 主工程根级文件归入宿主模块本身
|
|
314
|
+
if (rootFiles.length > 0) {
|
|
315
|
+
result.push({
|
|
316
|
+
name: hostName,
|
|
317
|
+
inferredRole: 'app',
|
|
318
|
+
files: rootFiles,
|
|
319
|
+
configLayer: hasConfigLayers ? 'Application' : undefined,
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
return result;
|
|
324
|
+
}
|
|
325
|
+
#isDirectory(dirPath) {
|
|
326
|
+
try {
|
|
327
|
+
return fs.statSync(dirPath).isDirectory();
|
|
328
|
+
}
|
|
329
|
+
catch {
|
|
330
|
+
return false;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
241
333
|
}
|
|
@@ -6,16 +6,23 @@
|
|
|
6
6
|
*
|
|
7
7
|
* @module PanoramaAggregator
|
|
8
8
|
*/
|
|
9
|
+
import type { BootstrapRepositoryImpl } from '../../repository/bootstrap/BootstrapRepository.js';
|
|
10
|
+
import type { CodeEntityRepositoryImpl } from '../../repository/code/CodeEntityRepository.js';
|
|
11
|
+
import type { KnowledgeEdgeRepositoryImpl } from '../../repository/knowledge/KnowledgeEdgeRepository.js';
|
|
12
|
+
import type { KnowledgeRepositoryImpl } from '../../repository/knowledge/KnowledgeRepository.impl.js';
|
|
9
13
|
import type { CouplingAnalyzer } from './CouplingAnalyzer.js';
|
|
10
14
|
import { DimensionAnalyzer } from './DimensionAnalyzer.js';
|
|
11
15
|
import type { ConfigLayer, LayerInferrer } from './LayerInferrer.js';
|
|
12
|
-
import type {
|
|
16
|
+
import type { PanoramaResult } from './PanoramaTypes.js';
|
|
13
17
|
import type { ModuleCandidate, RoleRefiner } from './RoleRefiner.js';
|
|
14
18
|
export interface PanoramaAggregatorOptions {
|
|
15
19
|
roleRefiner: RoleRefiner;
|
|
16
20
|
couplingAnalyzer: CouplingAnalyzer;
|
|
17
21
|
layerInferrer: LayerInferrer;
|
|
18
|
-
|
|
22
|
+
bootstrapRepo: BootstrapRepositoryImpl;
|
|
23
|
+
entityRepo: CodeEntityRepositoryImpl;
|
|
24
|
+
edgeRepo: KnowledgeEdgeRepositoryImpl;
|
|
25
|
+
knowledgeRepo: KnowledgeRepositoryImpl;
|
|
19
26
|
projectRoot: string;
|
|
20
27
|
dimensionAnalyzer?: DimensionAnalyzer;
|
|
21
28
|
}
|
|
@@ -29,5 +36,5 @@ export declare class PanoramaAggregator {
|
|
|
29
36
|
*/
|
|
30
37
|
compute(moduleCandidates: ModuleCandidate[], options?: {
|
|
31
38
|
configLayers?: ConfigLayer[] | null;
|
|
32
|
-
}): PanoramaResult
|
|
39
|
+
}): Promise<PanoramaResult>;
|
|
33
40
|
}
|
|
@@ -14,34 +14,39 @@ export class PanoramaAggregator {
|
|
|
14
14
|
#roleRefiner;
|
|
15
15
|
#couplingAnalyzer;
|
|
16
16
|
#layerInferrer;
|
|
17
|
-
#
|
|
17
|
+
#entityRepo;
|
|
18
|
+
#edgeRepo;
|
|
19
|
+
#knowledgeRepo;
|
|
18
20
|
#projectRoot;
|
|
19
21
|
#dimensionAnalyzer;
|
|
20
22
|
constructor(opts) {
|
|
21
23
|
this.#roleRefiner = opts.roleRefiner;
|
|
22
24
|
this.#couplingAnalyzer = opts.couplingAnalyzer;
|
|
23
25
|
this.#layerInferrer = opts.layerInferrer;
|
|
24
|
-
this.#
|
|
26
|
+
this.#entityRepo = opts.entityRepo;
|
|
27
|
+
this.#edgeRepo = opts.edgeRepo;
|
|
28
|
+
this.#knowledgeRepo = opts.knowledgeRepo;
|
|
25
29
|
this.#projectRoot = opts.projectRoot;
|
|
26
30
|
this.#dimensionAnalyzer =
|
|
27
|
-
opts.dimensionAnalyzer ??
|
|
31
|
+
opts.dimensionAnalyzer ??
|
|
32
|
+
new DimensionAnalyzer(opts.bootstrapRepo, opts.entityRepo, opts.knowledgeRepo, opts.projectRoot);
|
|
28
33
|
}
|
|
29
34
|
/**
|
|
30
35
|
* 计算完整全景数据
|
|
31
36
|
* @param moduleCandidates 模块候选列表
|
|
32
37
|
* @param options.configLayers 来自配置文件的层级声明(如 Boxfile layer 定义)
|
|
33
38
|
*/
|
|
34
|
-
compute(moduleCandidates, options) {
|
|
39
|
+
async compute(moduleCandidates, options) {
|
|
35
40
|
// 1. RoleRefiner: 精化角色
|
|
36
|
-
const refinedRoles = this.#roleRefiner.refineAll(moduleCandidates);
|
|
41
|
+
const refinedRoles = await this.#roleRefiner.refineAll(moduleCandidates);
|
|
37
42
|
// 2. 构建模块-文件映射
|
|
38
43
|
const moduleFiles = new Map();
|
|
39
44
|
for (const mc of moduleCandidates) {
|
|
40
45
|
moduleFiles.set(mc.name, mc.files);
|
|
41
46
|
}
|
|
42
47
|
// 3. CouplingAnalyzer: 耦合分析(含外部依赖 fan-in)
|
|
43
|
-
const externalModules = this.#collectExternalModules();
|
|
44
|
-
const coupling = this.#couplingAnalyzer.analyze(moduleFiles, externalModules);
|
|
48
|
+
const externalModules = await this.#collectExternalModules();
|
|
49
|
+
const coupling = await this.#couplingAnalyzer.analyze(moduleFiles, externalModules);
|
|
45
50
|
// 4. LayerInferrer: 层级推断(优先使用配置层级)
|
|
46
51
|
const modules = moduleCandidates.map((m) => m.name);
|
|
47
52
|
const configModuleLayerMap = new Map();
|
|
@@ -62,7 +67,7 @@ export class PanoramaAggregator {
|
|
|
62
67
|
}
|
|
63
68
|
}
|
|
64
69
|
// 6. 项目级 recipe 总数(recipe scope 通常为 universal,不做模块强关联)
|
|
65
|
-
const projectRecipeCount = this.#getProjectRecipeCount();
|
|
70
|
+
const projectRecipeCount = await this.#getProjectRecipeCount();
|
|
66
71
|
// 7. 计算总文件数
|
|
67
72
|
let totalFiles = 0;
|
|
68
73
|
for (const mc of moduleCandidates) {
|
|
@@ -89,18 +94,20 @@ export class PanoramaAggregator {
|
|
|
89
94
|
coverageRatio: mc.files.length > 0 ? recipeCount / mc.files.length : 0,
|
|
90
95
|
});
|
|
91
96
|
}
|
|
92
|
-
// 8.5
|
|
93
|
-
|
|
97
|
+
// 8.5 基于模块角色重命名层级(仅在拓扑推断模式下;配置层级保留原名)
|
|
98
|
+
if (!layers.configBased) {
|
|
99
|
+
this.#renameLayersByRole(layers, panoramaModules);
|
|
100
|
+
}
|
|
94
101
|
// 9. 多维度知识健康分析 (替代旧的基于模块文件数的覆盖率模型)
|
|
95
102
|
const moduleRoles = moduleCandidates.map((m) => {
|
|
96
103
|
const pm = panoramaModules.get(m.name);
|
|
97
104
|
return pm?.refinedRole ?? m.inferredRole;
|
|
98
105
|
});
|
|
99
|
-
const { radar, gaps } = this.#dimensionAnalyzer.analyze(moduleRoles);
|
|
106
|
+
const { radar, gaps } = await this.#dimensionAnalyzer.analyze(moduleRoles);
|
|
100
107
|
// 10. 调用流概要
|
|
101
|
-
const callFlowSummary = this.#computeCallFlowSummary();
|
|
108
|
+
const callFlowSummary = await this.#computeCallFlowSummary();
|
|
102
109
|
// 11. 外部依赖概况 + 技术栈画像
|
|
103
|
-
const externalDeps = this.#buildExternalDepProfiles(coupling.externalDeps);
|
|
110
|
+
const externalDeps = await this.#buildExternalDepProfiles(coupling.externalDeps);
|
|
104
111
|
const techStack = externalDeps.length > 0 ? profileTechStack(externalDeps) : null;
|
|
105
112
|
return {
|
|
106
113
|
modules: panoramaModules,
|
|
@@ -116,12 +123,9 @@ export class PanoramaAggregator {
|
|
|
116
123
|
};
|
|
117
124
|
}
|
|
118
125
|
/* ─── Project Recipe Count ──────────────────────── */
|
|
119
|
-
#getProjectRecipeCount() {
|
|
126
|
+
async #getProjectRecipeCount() {
|
|
120
127
|
try {
|
|
121
|
-
|
|
122
|
-
.prepare(`SELECT COUNT(*) as cnt FROM knowledge_entries WHERE lifecycle IN ('active', 'pending')`)
|
|
123
|
-
.get();
|
|
124
|
-
return Number(row?.cnt ?? 0);
|
|
128
|
+
return await this.#knowledgeRepo.countByCountableLifecycles();
|
|
125
129
|
}
|
|
126
130
|
catch {
|
|
127
131
|
return 0;
|
|
@@ -131,14 +135,13 @@ export class PanoramaAggregator {
|
|
|
131
135
|
/**
|
|
132
136
|
* 从 code_entities 收集标记为 external 的模块名
|
|
133
137
|
*/
|
|
134
|
-
#collectExternalModules() {
|
|
138
|
+
async #collectExternalModules() {
|
|
135
139
|
try {
|
|
136
|
-
const rows = this.#
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
return new Set(rows.map((r) => r.entity_id));
|
|
140
|
+
const rows = await this.#entityRepo.findModulesByNodeTypes(this.#projectRoot, [
|
|
141
|
+
'external',
|
|
142
|
+
'host',
|
|
143
|
+
]);
|
|
144
|
+
return new Set(rows.map((r) => r.entityId));
|
|
142
145
|
}
|
|
143
146
|
catch {
|
|
144
147
|
return new Set();
|
|
@@ -148,23 +151,17 @@ export class PanoramaAggregator {
|
|
|
148
151
|
* 将 CouplingAnalyzer 的外部依赖统计转为 ExternalDepProfile
|
|
149
152
|
* 并从 code_entities 补充 layer/version 元数据
|
|
150
153
|
*/
|
|
151
|
-
#buildExternalDepProfiles(rawExternalDeps) {
|
|
154
|
+
async #buildExternalDepProfiles(rawExternalDeps) {
|
|
152
155
|
if (rawExternalDeps.length === 0) {
|
|
153
156
|
return [];
|
|
154
157
|
}
|
|
155
|
-
//
|
|
158
|
+
// 查询外部依赖的元数据
|
|
156
159
|
const metadataMap = new Map();
|
|
157
160
|
try {
|
|
158
161
|
for (const dep of rawExternalDeps) {
|
|
159
|
-
const
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
LIMIT 1`)
|
|
163
|
-
.get(dep.name, this.#projectRoot);
|
|
164
|
-
if (row?.metadata_json) {
|
|
165
|
-
const meta = typeof row.metadata_json === 'string'
|
|
166
|
-
? JSON.parse(row.metadata_json)
|
|
167
|
-
: row.metadata_json;
|
|
162
|
+
const entity = await this.#entityRepo.findByEntityIdOnly(dep.name, this.#projectRoot);
|
|
163
|
+
if (entity?.metadata) {
|
|
164
|
+
const meta = entity.metadata;
|
|
168
165
|
metadataMap.set(dep.name, {
|
|
169
166
|
layer: meta.layer,
|
|
170
167
|
version: meta.version,
|
|
@@ -243,63 +240,54 @@ export class PanoramaAggregator {
|
|
|
243
240
|
layerName = 'Application';
|
|
244
241
|
}
|
|
245
242
|
}
|
|
246
|
-
//
|
|
243
|
+
// 去重:优先尝试次高票角色名,仍冲突则追加位置描述
|
|
247
244
|
if (usedNames.has(layerName)) {
|
|
248
|
-
|
|
245
|
+
// 尝试次高票角色
|
|
246
|
+
let resolved = false;
|
|
247
|
+
if (roleVotes.size > 1) {
|
|
248
|
+
const sortedRoles = [...roleVotes.entries()].sort((a, b) => b[1] - a[1]);
|
|
249
|
+
for (let i = 1; i < sortedRoles.length; i++) {
|
|
250
|
+
const altName = _a.#ROLE_TO_LAYER[sortedRoles[i][0]] ?? sortedRoles[i][0];
|
|
251
|
+
if (!usedNames.has(altName)) {
|
|
252
|
+
layerName = altName;
|
|
253
|
+
resolved = true;
|
|
254
|
+
break;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
// 仍冲突:使用位置描述而非数字后缀
|
|
259
|
+
if (!resolved && usedNames.has(layerName)) {
|
|
260
|
+
const pos = level.level <= maxLevel * 0.33
|
|
261
|
+
? 'Core'
|
|
262
|
+
: level.level >= maxLevel * 0.67
|
|
263
|
+
? 'App'
|
|
264
|
+
: 'Mid';
|
|
265
|
+
const qualifiedName = `${pos} ${layerName}`;
|
|
266
|
+
layerName = usedNames.has(qualifiedName) ? `${layerName} L${level.level}` : qualifiedName;
|
|
267
|
+
}
|
|
249
268
|
}
|
|
250
269
|
usedNames.add(layerName);
|
|
251
270
|
level.name = layerName;
|
|
252
271
|
}
|
|
253
272
|
}
|
|
254
273
|
/* ─── Call Flow Summary ─────────────────────────── */
|
|
255
|
-
#computeCallFlowSummary() {
|
|
274
|
+
async #computeCallFlowSummary() {
|
|
256
275
|
// 最频繁被调用的方法
|
|
257
|
-
const topCalled = this.#
|
|
258
|
-
.prepare(`SELECT to_id, COUNT(*) as call_count
|
|
259
|
-
FROM knowledge_edges
|
|
260
|
-
WHERE relation = 'calls'
|
|
261
|
-
GROUP BY to_id
|
|
262
|
-
ORDER BY call_count DESC
|
|
263
|
-
LIMIT 10`)
|
|
264
|
-
.all();
|
|
276
|
+
const topCalled = await this.#edgeRepo.findTopCalledNodes(10);
|
|
265
277
|
// 入口点: 只有出度没有入度的方法
|
|
266
|
-
const entryPoints = this.#
|
|
267
|
-
.prepare(`SELECT DISTINCT ke.from_id
|
|
268
|
-
FROM knowledge_edges ke
|
|
269
|
-
WHERE ke.relation = 'calls'
|
|
270
|
-
AND ke.from_id NOT IN (
|
|
271
|
-
SELECT to_id FROM knowledge_edges WHERE relation = 'calls'
|
|
272
|
-
)
|
|
273
|
-
LIMIT 20`)
|
|
274
|
-
.all();
|
|
278
|
+
const entryPoints = await this.#edgeRepo.findEntryPoints(20);
|
|
275
279
|
// 数据生产者: data_flow outFlow >> inFlow
|
|
276
|
-
const dataProducers = this.#
|
|
277
|
-
.prepare(`SELECT from_id, COUNT(*) as out_cnt
|
|
278
|
-
FROM knowledge_edges
|
|
279
|
-
WHERE relation = 'data_flow'
|
|
280
|
-
GROUP BY from_id
|
|
281
|
-
HAVING out_cnt > 3
|
|
282
|
-
ORDER BY out_cnt DESC
|
|
283
|
-
LIMIT 10`)
|
|
284
|
-
.all();
|
|
280
|
+
const dataProducers = await this.#edgeRepo.findTopDataFlowSources(10, 3);
|
|
285
281
|
// 数据消费者: data_flow inFlow >> outFlow
|
|
286
|
-
const dataConsumers = this.#
|
|
287
|
-
.prepare(`SELECT to_id, COUNT(*) as in_cnt
|
|
288
|
-
FROM knowledge_edges
|
|
289
|
-
WHERE relation = 'data_flow'
|
|
290
|
-
GROUP BY to_id
|
|
291
|
-
HAVING in_cnt > 3
|
|
292
|
-
ORDER BY in_cnt DESC
|
|
293
|
-
LIMIT 10`)
|
|
294
|
-
.all();
|
|
282
|
+
const dataConsumers = await this.#edgeRepo.findTopDataFlowSinks(10, 3);
|
|
295
283
|
return {
|
|
296
284
|
topCalledMethods: topCalled.map((r) => ({
|
|
297
|
-
id: r.
|
|
298
|
-
callCount:
|
|
285
|
+
id: r.toId,
|
|
286
|
+
callCount: r.callCount,
|
|
299
287
|
})),
|
|
300
|
-
entryPoints
|
|
301
|
-
dataProducers
|
|
302
|
-
dataConsumers
|
|
288
|
+
entryPoints,
|
|
289
|
+
dataProducers,
|
|
290
|
+
dataConsumers,
|
|
303
291
|
};
|
|
304
292
|
}
|
|
305
293
|
}
|
|
@@ -11,9 +11,13 @@
|
|
|
11
11
|
*
|
|
12
12
|
* @module PanoramaScanner
|
|
13
13
|
*/
|
|
14
|
+
import type { CodeEntityRepositoryImpl } from '../../repository/code/CodeEntityRepository.js';
|
|
15
|
+
import type { KnowledgeEdgeRepositoryImpl } from '../../repository/knowledge/KnowledgeEdgeRepository.js';
|
|
14
16
|
export interface PanoramaScannerOptions {
|
|
15
17
|
projectRoot: string;
|
|
16
18
|
container: ScannerContainer;
|
|
19
|
+
entityRepo: CodeEntityRepositoryImpl;
|
|
20
|
+
edgeRepo: KnowledgeEdgeRepositoryImpl;
|
|
17
21
|
logger?: ScannerLogger;
|
|
18
22
|
}
|
|
19
23
|
export interface ScannerContainer {
|
|
@@ -35,7 +39,7 @@ export declare class PanoramaScanner {
|
|
|
35
39
|
/**
|
|
36
40
|
* 检测 DB 中是否已有该项目的 code_entities 数据
|
|
37
41
|
*/
|
|
38
|
-
hasData(): boolean
|
|
42
|
+
hasData(): Promise<boolean>;
|
|
39
43
|
/**
|
|
40
44
|
* 确保全景数据存在。无数据时自动执行扫描。
|
|
41
45
|
* 幂等:扫描过一次后不再重复(重启进程或手动 reset 可重新触发)。
|