autosnippet 3.3.0 → 3.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dashboard/dist/assets/icons-BJ2mUBi8.js +1 -0
- package/dashboard/dist/assets/index-B659K9t5.js +128 -0
- package/dashboard/dist/assets/index-NCm40PMD.css +1 -0
- package/dashboard/dist/index.html +3 -3
- package/dist/bin/cli.d.ts +1 -0
- package/dist/bin/cli.js +284 -142
- package/dist/lib/agent/context/ExplorationTracker.d.ts +2 -0
- package/dist/lib/agent/context/ExplorationTracker.js +21 -3
- package/dist/lib/agent/core/ToolExecutionPipeline.d.ts +3 -1
- package/dist/lib/agent/core/ToolExecutionPipeline.js +8 -1
- package/dist/lib/agent/forge/DynamicComposer.d.ts +58 -0
- package/dist/lib/agent/forge/DynamicComposer.js +99 -0
- package/dist/lib/agent/forge/SandboxRunner.d.ts +60 -0
- package/dist/lib/agent/forge/SandboxRunner.js +251 -0
- package/dist/lib/agent/forge/TemporaryToolRegistry.d.ts +76 -0
- package/dist/lib/agent/forge/TemporaryToolRegistry.js +154 -0
- package/dist/lib/agent/forge/ToolForge.d.ts +92 -0
- package/dist/lib/agent/forge/ToolForge.js +239 -0
- package/dist/lib/agent/forge/ToolRequirementAnalyzer.d.ts +44 -0
- package/dist/lib/agent/forge/ToolRequirementAnalyzer.js +119 -0
- package/dist/lib/agent/tools/ToolRegistry.d.ts +2 -0
- package/dist/lib/agent/tools/ToolRegistry.js +4 -0
- package/dist/lib/agent/tools/composite.js +0 -1
- package/dist/lib/agent/tools/index.d.ts +2 -50
- package/dist/lib/agent/tools/index.js +2 -3
- package/dist/lib/agent/tools/lifecycle.d.ts +1 -58
- package/dist/lib/agent/tools/lifecycle.js +2 -75
- package/dist/lib/cli/KnowledgeSyncService.d.ts +26 -0
- package/dist/lib/cli/KnowledgeSyncService.js +33 -1
- package/dist/lib/cli/deploy/FileManifest.d.ts +0 -21
- package/dist/lib/cli/deploy/FileManifest.js +0 -11
- package/dist/lib/domain/knowledge/KnowledgeEntry.d.ts +10 -0
- package/dist/lib/domain/knowledge/KnowledgeEntry.js +2 -0
- package/dist/lib/domain/knowledge/Lifecycle.d.ts +19 -2
- package/dist/lib/domain/knowledge/Lifecycle.js +32 -6
- package/dist/lib/domain/knowledge/UnifiedValidator.d.ts +1 -5
- package/dist/lib/domain/knowledge/UnifiedValidator.js +7 -44
- package/dist/lib/domain/knowledge/values/Stats.d.ts +29 -0
- package/dist/lib/domain/knowledge/values/Stats.js +41 -0
- package/dist/lib/external/mcp/McpServer.d.ts +19 -38
- package/dist/lib/external/mcp/McpServer.js +145 -117
- package/dist/lib/external/mcp/autoApproveInjector.js +0 -2
- package/dist/lib/external/mcp/handlers/bootstrap/MissionBriefingBuilder.d.ts +26 -1
- package/dist/lib/external/mcp/handlers/bootstrap/MissionBriefingBuilder.js +41 -0
- package/dist/lib/external/mcp/handlers/bootstrap/pipeline/orchestrator.js +49 -0
- package/dist/lib/external/mcp/handlers/bootstrap/shared/bootstrap-phases.d.ts +3 -0
- package/dist/lib/external/mcp/handlers/bootstrap/shared/bootstrap-phases.js +27 -0
- package/dist/lib/external/mcp/handlers/bootstrap/skills.js +1 -1
- package/dist/lib/external/mcp/handlers/bootstrap-external.js +1 -0
- package/dist/lib/external/mcp/handlers/bootstrap-internal.js +2 -0
- package/dist/lib/external/mcp/handlers/browse.d.ts +1 -0
- package/dist/lib/external/mcp/handlers/browse.js +2 -1
- package/dist/lib/external/mcp/handlers/consolidated.d.ts +117 -6
- package/dist/lib/external/mcp/handlers/consolidated.js +251 -71
- package/dist/lib/external/mcp/handlers/guard.d.ts +150 -0
- package/dist/lib/external/mcp/handlers/guard.js +239 -5
- package/dist/lib/external/mcp/handlers/knowledge.d.ts +0 -29
- package/dist/lib/external/mcp/handlers/knowledge.js +1 -76
- package/dist/lib/external/mcp/handlers/panorama.d.ts +36 -0
- package/dist/lib/external/mcp/handlers/panorama.js +156 -0
- package/dist/lib/external/mcp/handlers/system.d.ts +2 -54
- package/dist/lib/external/mcp/handlers/system.js +3 -113
- package/dist/lib/external/mcp/handlers/task.d.ts +13 -24
- package/dist/lib/external/mcp/handlers/task.js +218 -557
- package/dist/lib/external/mcp/handlers/types.d.ts +91 -8
- package/dist/lib/external/mcp/handlers/types.js +18 -1
- package/dist/lib/external/mcp/handlers/wiki-external.d.ts +18 -1
- package/dist/lib/external/mcp/handlers/wiki-external.js +16 -1
- package/dist/lib/external/mcp/tools.d.ts +18 -24
- package/dist/lib/external/mcp/tools.js +132 -159
- package/dist/lib/http/HttpServer.js +52 -0
- package/dist/lib/http/middleware/validate.js +7 -3
- package/dist/lib/http/routes/audit.d.ts +8 -0
- package/dist/lib/http/routes/audit.js +51 -0
- package/dist/lib/http/routes/guardReport.d.ts +10 -0
- package/dist/lib/http/routes/guardReport.js +143 -0
- package/dist/lib/http/routes/knowledge.js +32 -1
- package/dist/lib/http/routes/panorama.d.ts +11 -0
- package/dist/lib/http/routes/panorama.js +322 -0
- package/dist/lib/http/routes/signals.d.ts +10 -0
- package/dist/lib/http/routes/signals.js +104 -0
- package/dist/lib/http/routes/task.d.ts +2 -3
- package/dist/lib/http/routes/task.js +17 -347
- package/dist/lib/http/routes/violations.js +1 -1
- package/dist/lib/infrastructure/audit/AuditLogger.d.ts +6 -1
- package/dist/lib/infrastructure/audit/AuditLogger.js +14 -1
- package/dist/lib/infrastructure/database/drizzle/schema.d.ts +202 -504
- package/dist/lib/infrastructure/database/drizzle/schema.js +38 -69
- package/dist/lib/infrastructure/database/migrations/004_evolution_proposals.d.ts +8 -0
- package/dist/lib/infrastructure/database/migrations/004_evolution_proposals.js +43 -0
- package/dist/lib/infrastructure/database/migrations/005_recipe_source_refs.d.ts +9 -0
- package/dist/lib/infrastructure/database/migrations/005_recipe_source_refs.js +24 -0
- package/dist/lib/infrastructure/logging/Logger.d.ts +2 -0
- package/dist/lib/infrastructure/logging/Logger.js +34 -7
- package/dist/lib/infrastructure/monitoring/ErrorTracker.js +3 -1
- package/dist/lib/infrastructure/monitoring/PerformanceMonitor.d.ts +2 -2
- package/dist/lib/infrastructure/monitoring/PerformanceMonitor.js +12 -10
- package/dist/lib/infrastructure/notification/LarkNotifier.d.ts +24 -0
- package/dist/lib/infrastructure/notification/LarkNotifier.js +97 -0
- package/dist/lib/infrastructure/report/ReportStore.d.ts +45 -0
- package/dist/lib/infrastructure/report/ReportStore.js +133 -0
- package/dist/lib/infrastructure/signal/SignalAggregator.d.ts +18 -0
- package/dist/lib/infrastructure/signal/SignalAggregator.js +84 -0
- package/dist/lib/infrastructure/signal/SignalBridge.d.ts +13 -0
- package/dist/lib/infrastructure/signal/SignalBridge.js +20 -0
- package/dist/lib/infrastructure/signal/SignalBus.d.ts +63 -0
- package/dist/lib/infrastructure/signal/SignalBus.js +106 -0
- package/dist/lib/infrastructure/signal/SignalTraceWriter.d.ts +36 -0
- package/dist/lib/infrastructure/signal/SignalTraceWriter.js +130 -0
- package/dist/lib/infrastructure/vector/HnswVectorAdapter.js +18 -2
- package/dist/lib/injection/ServiceContainer.js +8 -0
- package/dist/lib/injection/ServiceMap.d.ts +16 -10
- package/dist/lib/injection/modules/AgentModule.d.ts +1 -1
- package/dist/lib/injection/modules/AgentModule.js +7 -1
- package/dist/lib/injection/modules/AppModule.d.ts +1 -1
- package/dist/lib/injection/modules/AppModule.js +4 -13
- package/dist/lib/injection/modules/GuardModule.js +27 -2
- package/dist/lib/injection/modules/InfraModule.d.ts +0 -1
- package/dist/lib/injection/modules/InfraModule.js +9 -7
- package/dist/lib/injection/modules/KnowledgeModule.d.ts +5 -0
- package/dist/lib/injection/modules/KnowledgeModule.js +131 -0
- package/dist/lib/injection/modules/PanoramaModule.d.ts +18 -0
- package/dist/lib/injection/modules/PanoramaModule.js +76 -0
- package/dist/lib/injection/modules/SignalModule.d.ts +10 -0
- package/dist/lib/injection/modules/SignalModule.js +84 -0
- package/dist/lib/repository/knowledge/KnowledgeRepository.impl.d.ts +1 -0
- package/dist/lib/repository/knowledge/KnowledgeRepository.impl.js +6 -0
- package/dist/lib/service/bootstrap/BootstrapTaskManager.d.ts +3 -1
- package/dist/lib/service/bootstrap/BootstrapTaskManager.js +20 -1
- package/dist/lib/service/bootstrap/UiStartupTasks.d.ts +45 -0
- package/dist/lib/service/bootstrap/UiStartupTasks.js +101 -0
- package/dist/lib/service/delivery/AgentInstructionsGenerator.js +4 -5
- package/dist/lib/service/delivery/CursorDeliveryPipeline.d.ts +3 -1
- package/dist/lib/service/delivery/CursorDeliveryPipeline.js +13 -10
- package/dist/lib/service/delivery/RulesGenerator.js +3 -2
- package/dist/lib/service/evolution/ConsolidationAdvisor.d.ts +114 -0
- package/dist/lib/service/evolution/ConsolidationAdvisor.js +542 -0
- package/dist/lib/service/evolution/ContradictionDetector.d.ts +54 -0
- package/dist/lib/service/evolution/ContradictionDetector.js +253 -0
- package/dist/lib/service/evolution/DecayDetector.d.ts +71 -0
- package/dist/lib/service/evolution/DecayDetector.js +244 -0
- package/dist/lib/service/evolution/EnhancementSuggester.d.ts +38 -0
- package/dist/lib/service/evolution/EnhancementSuggester.js +220 -0
- package/dist/lib/service/evolution/KnowledgeMetabolism.d.ts +82 -0
- package/dist/lib/service/evolution/KnowledgeMetabolism.js +167 -0
- package/dist/lib/service/evolution/RedundancyAnalyzer.d.ts +53 -0
- package/dist/lib/service/evolution/RedundancyAnalyzer.js +210 -0
- package/dist/lib/service/evolution/StagingManager.d.ts +57 -0
- package/dist/lib/service/evolution/StagingManager.js +201 -0
- package/dist/lib/service/guard/ComplianceReporter.d.ts +42 -2
- package/dist/lib/service/guard/ComplianceReporter.js +43 -5
- package/dist/lib/service/guard/CoverageAnalyzer.d.ts +54 -0
- package/dist/lib/service/guard/CoverageAnalyzer.js +149 -0
- package/dist/lib/service/guard/GuardCheckEngine.d.ts +42 -0
- package/dist/lib/service/guard/GuardCheckEngine.js +465 -14
- package/dist/lib/service/guard/GuardFeedbackLoop.d.ts +3 -0
- package/dist/lib/service/guard/GuardFeedbackLoop.js +9 -0
- package/dist/lib/service/guard/ReverseGuard.d.ts +73 -0
- package/dist/lib/service/guard/ReverseGuard.js +256 -0
- package/dist/lib/service/guard/RuleLearner.d.ts +12 -0
- package/dist/lib/service/guard/RuleLearner.js +38 -0
- package/dist/lib/service/guard/UncertaintyCollector.d.ts +83 -0
- package/dist/lib/service/guard/UncertaintyCollector.js +149 -0
- package/dist/lib/service/guard/ViolationsStore.d.ts +1 -0
- package/dist/lib/service/guard/ViolationsStore.js +33 -3
- package/dist/lib/service/knowledge/ConfidenceRouter.d.ts +13 -0
- package/dist/lib/service/knowledge/ConfidenceRouter.js +14 -0
- package/dist/lib/service/knowledge/KnowledgeService.js +22 -4
- package/dist/lib/service/knowledge/SourceRefReconciler.d.ts +68 -0
- package/dist/lib/service/knowledge/SourceRefReconciler.js +309 -0
- package/dist/lib/service/panorama/CouplingAnalyzer.d.ts +27 -0
- package/dist/lib/service/panorama/CouplingAnalyzer.js +192 -0
- package/dist/lib/service/panorama/DimensionAnalyzer.d.ts +28 -0
- package/dist/lib/service/panorama/DimensionAnalyzer.js +320 -0
- package/dist/lib/service/panorama/LayerInferrer.d.ts +19 -0
- package/dist/lib/service/panorama/LayerInferrer.js +182 -0
- package/dist/lib/service/panorama/ModuleDiscoverer.d.ts +24 -0
- package/dist/lib/service/panorama/ModuleDiscoverer.js +185 -0
- package/dist/lib/service/panorama/PanoramaAggregator.d.ts +29 -0
- package/dist/lib/service/panorama/PanoramaAggregator.js +228 -0
- package/dist/lib/service/panorama/PanoramaScanner.d.ts +52 -0
- package/dist/lib/service/panorama/PanoramaScanner.js +188 -0
- package/dist/lib/service/panorama/PanoramaService.d.ts +125 -0
- package/dist/lib/service/panorama/PanoramaService.js +363 -0
- package/dist/lib/service/panorama/PanoramaTypes.d.ts +134 -0
- package/dist/lib/service/panorama/PanoramaTypes.js +6 -0
- package/dist/lib/service/panorama/RoleRefiner.d.ts +48 -0
- package/dist/lib/service/panorama/RoleRefiner.js +535 -0
- package/dist/lib/service/search/BM25Scorer.d.ts +2 -2
- package/dist/lib/service/search/CoarseRanker.d.ts +7 -6
- package/dist/lib/service/search/CoarseRanker.js +11 -10
- package/dist/lib/service/search/FieldWeightedScorer.d.ts +81 -0
- package/dist/lib/service/search/FieldWeightedScorer.js +318 -0
- package/dist/lib/service/search/MultiSignalRanker.d.ts +3 -2
- package/dist/lib/service/search/MultiSignalRanker.js +17 -1
- package/dist/lib/service/search/SearchEngine.d.ts +9 -7
- package/dist/lib/service/search/SearchEngine.js +67 -10
- package/dist/lib/service/search/SearchTypes.d.ts +25 -3
- package/dist/lib/service/search/SearchTypes.js +6 -1
- package/dist/lib/service/signal/HitRecorder.d.ts +68 -0
- package/dist/lib/service/signal/HitRecorder.js +173 -0
- package/dist/lib/service/skills/SignalCollector.d.ts +3 -1
- package/dist/lib/service/skills/SignalCollector.js +31 -1
- package/dist/lib/service/task/IntentExtractor.d.ts +66 -0
- package/dist/lib/service/task/IntentExtractor.js +256 -0
- package/dist/lib/service/task/PrimeSearchPipeline.d.ts +54 -0
- package/dist/lib/service/task/PrimeSearchPipeline.js +113 -0
- package/dist/lib/service/vector/VectorService.d.ts +3 -0
- package/dist/lib/service/vector/VectorService.js +38 -4
- package/dist/lib/shared/schemas/mcp-tools.d.ts +41 -96
- package/dist/lib/shared/schemas/mcp-tools.js +59 -119
- package/dist/scripts/analyze-signals.d.ts +20 -0
- package/dist/scripts/analyze-signals.js +155 -0
- package/dist/scripts/diagnose-mcp.js +1 -1
- package/package.json +1 -1
- package/skills/autosnippet-create/SKILL.md +98 -89
- package/skills/autosnippet-devdocs/SKILL.md +55 -57
- package/templates/claude-code/hooks/autosnippet-session.sh +10 -15
- package/templates/cursor-hooks/hooks/session-start.sh +1 -1
- package/templates/guard-ci.yml +2 -2
- package/templates/instructions/agent-static.md +2 -1
- package/templates/instructions/conventions.md +5 -6
- package/templates/recipes-setup/README.md +1 -2
- package/templates/recipes-setup/_template.md +39 -39
- package/dashboard/dist/assets/icons-BofcEZ3f.js +0 -1
- package/dashboard/dist/assets/index-D0whuycy.css +0 -1
- package/dashboard/dist/assets/index-SiN1GChm.js +0 -128
- package/dist/lib/domain/task/Task.d.ts +0 -140
- package/dist/lib/domain/task/Task.js +0 -254
- package/dist/lib/domain/task/TaskDependency.d.ts +0 -23
- package/dist/lib/domain/task/TaskDependency.js +0 -34
- package/dist/lib/domain/task/TaskIdGenerator.d.ts +0 -40
- package/dist/lib/domain/task/TaskIdGenerator.js +0 -75
- package/dist/lib/domain/task/index.d.ts +0 -4
- package/dist/lib/domain/task/index.js +0 -4
- package/dist/lib/infrastructure/database/migrations/002_add_tasks.d.ts +0 -11
- package/dist/lib/infrastructure/database/migrations/002_add_tasks.js +0 -86
- package/dist/lib/repository/task/TaskRepository.impl.d.ts +0 -171
- package/dist/lib/repository/task/TaskRepository.impl.js +0 -347
- package/dist/lib/service/task/TaskGraphService.d.ts +0 -222
- package/dist/lib/service/task/TaskGraphService.js +0 -597
- package/dist/lib/service/task/TaskKnowledgeBridge.d.ts +0 -95
- package/dist/lib/service/task/TaskKnowledgeBridge.js +0 -298
- package/dist/lib/service/task/TaskReadyEngine.d.ts +0 -84
- package/dist/lib/service/task/TaskReadyEngine.js +0 -115
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CouplingAnalyzer — 模块耦合分析
|
|
3
|
+
*
|
|
4
|
+
* 三边融合 (import + call + dataFlow) 构建加权依赖图,
|
|
5
|
+
* 使用 Tarjan SCC 检测循环依赖,计算 fanIn/fanOut。
|
|
6
|
+
*
|
|
7
|
+
* @module CouplingAnalyzer
|
|
8
|
+
*/
|
|
9
|
+
/* ═══ Edge Weights ════════════════════════════════════════ */
|
|
10
|
+
const EDGE_WEIGHTS = {
|
|
11
|
+
depends_on: 0.5,
|
|
12
|
+
calls: 1.0,
|
|
13
|
+
data_flow: 0.8,
|
|
14
|
+
};
|
|
15
|
+
/* ═══ CouplingAnalyzer Class ══════════════════════════════ */
|
|
16
|
+
export class CouplingAnalyzer {
|
|
17
|
+
#db;
|
|
18
|
+
#projectRoot;
|
|
19
|
+
constructor(db, projectRoot) {
|
|
20
|
+
this.#db = db;
|
|
21
|
+
this.#projectRoot = projectRoot;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* 分析模块间耦合关系
|
|
25
|
+
* @param moduleFiles - Map<moduleName, filePaths[]>
|
|
26
|
+
*/
|
|
27
|
+
analyze(moduleFiles) {
|
|
28
|
+
// 1. 构建 file → module 反向索引
|
|
29
|
+
const fileToModule = new Map();
|
|
30
|
+
for (const [mod, files] of moduleFiles) {
|
|
31
|
+
for (const f of files) {
|
|
32
|
+
fileToModule.set(f, mod);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
// 2. 从 knowledge_edges 聚合模块间边
|
|
36
|
+
const edges = this.#buildModuleEdges(moduleFiles, fileToModule);
|
|
37
|
+
// 3. 建图
|
|
38
|
+
const adjacency = new Map();
|
|
39
|
+
const allModules = new Set(moduleFiles.keys());
|
|
40
|
+
for (const edge of edges) {
|
|
41
|
+
allModules.add(edge.from);
|
|
42
|
+
allModules.add(edge.to);
|
|
43
|
+
if (!adjacency.has(edge.from)) {
|
|
44
|
+
adjacency.set(edge.from, new Map());
|
|
45
|
+
}
|
|
46
|
+
const existing = adjacency.get(edge.from).get(edge.to) ?? 0;
|
|
47
|
+
adjacency.get(edge.from).set(edge.to, existing + edge.weight);
|
|
48
|
+
}
|
|
49
|
+
// 4. Tarjan SCC
|
|
50
|
+
const cycles = this.#tarjanSCC(adjacency, allModules);
|
|
51
|
+
// 5. fanIn / fanOut
|
|
52
|
+
const metrics = new Map();
|
|
53
|
+
for (const mod of allModules) {
|
|
54
|
+
metrics.set(mod, { fanIn: 0, fanOut: 0 });
|
|
55
|
+
}
|
|
56
|
+
for (const edge of edges) {
|
|
57
|
+
if (edge.from === edge.to) {
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
const fromM = metrics.get(edge.from);
|
|
61
|
+
const toM = metrics.get(edge.to);
|
|
62
|
+
if (fromM) {
|
|
63
|
+
fromM.fanOut++;
|
|
64
|
+
}
|
|
65
|
+
if (toM) {
|
|
66
|
+
toM.fanIn++;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
// 去重边 (同 from→to 聚合)
|
|
70
|
+
const dedupEdges = this.#deduplicateEdges(edges);
|
|
71
|
+
return { cycles, metrics, edges: dedupEdges };
|
|
72
|
+
}
|
|
73
|
+
/* ─── Internal helpers ──────────────────────────── */
|
|
74
|
+
#buildModuleEdges(moduleFiles, fileToModule) {
|
|
75
|
+
const edges = [];
|
|
76
|
+
const relations = ['depends_on', 'calls', 'data_flow'];
|
|
77
|
+
for (const relation of relations) {
|
|
78
|
+
const weight = EDGE_WEIGHTS[relation] ?? 0.5;
|
|
79
|
+
// 查询该类型的边(仅限当前项目:至少 from 侧实体属于本项目)
|
|
80
|
+
const rows = this.#db
|
|
81
|
+
.prepare(`SELECT ke.from_id, ke.from_type, ke.to_id, ke.to_type
|
|
82
|
+
FROM knowledge_edges ke
|
|
83
|
+
WHERE ke.relation = ?
|
|
84
|
+
AND (
|
|
85
|
+
ke.from_type = 'module'
|
|
86
|
+
OR EXISTS (
|
|
87
|
+
SELECT 1 FROM code_entities ce
|
|
88
|
+
WHERE ce.entity_id = ke.from_id AND ce.project_root = ?
|
|
89
|
+
)
|
|
90
|
+
)`)
|
|
91
|
+
.all(relation, this.#projectRoot);
|
|
92
|
+
for (const row of rows) {
|
|
93
|
+
const fromId = row.from_id;
|
|
94
|
+
const toId = row.to_id;
|
|
95
|
+
const fromType = row.from_type;
|
|
96
|
+
const toType = row.to_type;
|
|
97
|
+
// module-to-module 直接边 (depends_on)
|
|
98
|
+
if (fromType === 'module' && toType === 'module') {
|
|
99
|
+
if (fromId !== toId && moduleFiles.has(fromId) && moduleFiles.has(toId)) {
|
|
100
|
+
edges.push({ from: fromId, to: toId, weight, relation });
|
|
101
|
+
}
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
// entity-to-entity 边 → 解析 file → module
|
|
105
|
+
const fromModule = this.#resolveEntityModule(fromId, fromType, fileToModule);
|
|
106
|
+
const toModule = this.#resolveEntityModule(toId, toType, fileToModule);
|
|
107
|
+
if (fromModule && toModule && fromModule !== toModule) {
|
|
108
|
+
edges.push({ from: fromModule, to: toModule, weight, relation });
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return edges;
|
|
113
|
+
}
|
|
114
|
+
#resolveEntityModule(entityId, _entityType, fileToModule) {
|
|
115
|
+
// 先查实体所在文件
|
|
116
|
+
const row = this.#db
|
|
117
|
+
.prepare(`SELECT file_path FROM code_entities
|
|
118
|
+
WHERE entity_id = ? AND project_root = ? LIMIT 1`)
|
|
119
|
+
.get(entityId, this.#projectRoot);
|
|
120
|
+
if (!row?.file_path) {
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
return fileToModule.get(row.file_path) ?? null;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Tarjan 强连通分量算法
|
|
127
|
+
*/
|
|
128
|
+
#tarjanSCC(adjacency, allNodes) {
|
|
129
|
+
let index = 0;
|
|
130
|
+
const stack = [];
|
|
131
|
+
const onStack = new Set();
|
|
132
|
+
const indices = new Map();
|
|
133
|
+
const lowlinks = new Map();
|
|
134
|
+
const sccs = [];
|
|
135
|
+
const strongConnect = (v) => {
|
|
136
|
+
indices.set(v, index);
|
|
137
|
+
lowlinks.set(v, index);
|
|
138
|
+
index++;
|
|
139
|
+
stack.push(v);
|
|
140
|
+
onStack.add(v);
|
|
141
|
+
const neighbors = adjacency.get(v);
|
|
142
|
+
if (neighbors) {
|
|
143
|
+
for (const w of neighbors.keys()) {
|
|
144
|
+
if (!indices.has(w)) {
|
|
145
|
+
strongConnect(w);
|
|
146
|
+
lowlinks.set(v, Math.min(lowlinks.get(v), lowlinks.get(w)));
|
|
147
|
+
}
|
|
148
|
+
else if (onStack.has(w)) {
|
|
149
|
+
lowlinks.set(v, Math.min(lowlinks.get(v), indices.get(w)));
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
if (lowlinks.get(v) === indices.get(v)) {
|
|
154
|
+
const scc = [];
|
|
155
|
+
let w;
|
|
156
|
+
do {
|
|
157
|
+
w = stack.pop();
|
|
158
|
+
onStack.delete(w);
|
|
159
|
+
scc.push(w);
|
|
160
|
+
} while (w !== v);
|
|
161
|
+
sccs.push(scc);
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
for (const node of allNodes) {
|
|
165
|
+
if (!indices.has(node)) {
|
|
166
|
+
strongConnect(node);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
// 过滤出 size > 1 的 SCC (即循环依赖)
|
|
170
|
+
return sccs
|
|
171
|
+
.filter((scc) => scc.length > 1)
|
|
172
|
+
.map((cycle) => ({
|
|
173
|
+
cycle: cycle.reverse(),
|
|
174
|
+
severity: cycle.length > 3 ? 'error' : 'warning',
|
|
175
|
+
}));
|
|
176
|
+
}
|
|
177
|
+
#deduplicateEdges(edges) {
|
|
178
|
+
const key = (e) => `${e.from}→${e.to}`;
|
|
179
|
+
const map = new Map();
|
|
180
|
+
for (const e of edges) {
|
|
181
|
+
const k = key(e);
|
|
182
|
+
const existing = map.get(k);
|
|
183
|
+
if (existing) {
|
|
184
|
+
existing.weight = Math.max(existing.weight, e.weight);
|
|
185
|
+
}
|
|
186
|
+
else {
|
|
187
|
+
map.set(k, { ...e });
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
return [...map.values()];
|
|
191
|
+
}
|
|
192
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DimensionAnalyzer — 多维度知识健康分析
|
|
3
|
+
*
|
|
4
|
+
* 灵感来源:
|
|
5
|
+
* - ISO/IEC 25010 质量模型 (8 大特性: 可靠性、安全性、可维护性…)
|
|
6
|
+
* - ThoughtWorks Tech Radar (Adopt/Trial/Assess/Hold 四环)
|
|
7
|
+
* - 雷达图/蛛网图可视化模型
|
|
8
|
+
*
|
|
9
|
+
* 核心思路: 不再按「模块 × 文件数」衡量覆盖,
|
|
10
|
+
* 而是按「知识维度」衡量项目在各工程方向上的规范成熟度。
|
|
11
|
+
* 某维度 Recipe 为 0 → 该方向完全空白,标示为 gap。
|
|
12
|
+
*
|
|
13
|
+
* @module DimensionAnalyzer
|
|
14
|
+
*/
|
|
15
|
+
import type { CeDbLike, HealthRadar, KnowledgeGap } from './PanoramaTypes.js';
|
|
16
|
+
export declare class DimensionAnalyzer {
|
|
17
|
+
#private;
|
|
18
|
+
constructor(db: CeDbLike);
|
|
19
|
+
/**
|
|
20
|
+
* 分析项目知识健康雷达
|
|
21
|
+
*
|
|
22
|
+
* @param moduleRoles — 项目中存在的模块角色 (用于 gap 优先级推断)
|
|
23
|
+
*/
|
|
24
|
+
analyze(moduleRoles: string[]): {
|
|
25
|
+
radar: HealthRadar;
|
|
26
|
+
gaps: KnowledgeGap[];
|
|
27
|
+
};
|
|
28
|
+
}
|
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DimensionAnalyzer — 多维度知识健康分析
|
|
3
|
+
*
|
|
4
|
+
* 灵感来源:
|
|
5
|
+
* - ISO/IEC 25010 质量模型 (8 大特性: 可靠性、安全性、可维护性…)
|
|
6
|
+
* - ThoughtWorks Tech Radar (Adopt/Trial/Assess/Hold 四环)
|
|
7
|
+
* - 雷达图/蛛网图可视化模型
|
|
8
|
+
*
|
|
9
|
+
* 核心思路: 不再按「模块 × 文件数」衡量覆盖,
|
|
10
|
+
* 而是按「知识维度」衡量项目在各工程方向上的规范成熟度。
|
|
11
|
+
* 某维度 Recipe 为 0 → 该方向完全空白,标示为 gap。
|
|
12
|
+
*
|
|
13
|
+
* @module DimensionAnalyzer
|
|
14
|
+
*/
|
|
15
|
+
/**
|
|
16
|
+
* 标准维度列表
|
|
17
|
+
*
|
|
18
|
+
* 覆盖主流软件工程关切方向,任何项目都应有所涉猎。
|
|
19
|
+
* `topics` 与 `categories` 匹配 knowledge_entries 的字段。
|
|
20
|
+
*/
|
|
21
|
+
const DIMENSION_DEFS = [
|
|
22
|
+
{
|
|
23
|
+
id: 'architecture',
|
|
24
|
+
name: '架构设计',
|
|
25
|
+
description: '模块结构、分层策略、依赖管理、设计模式',
|
|
26
|
+
topics: ['architecture', 'scaffold', 'workflow'],
|
|
27
|
+
categories: ['architecture', 'project-profile'],
|
|
28
|
+
weight: 1.0,
|
|
29
|
+
suggestedTopics: ['module-boundary', 'dependency-rule', 'layer-strategy'],
|
|
30
|
+
relatedRoles: ['core', 'foundation', 'app'],
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
id: 'coding-standards',
|
|
34
|
+
name: '编码规范',
|
|
35
|
+
description: '命名约定、代码风格、文档注释、import 顺序',
|
|
36
|
+
topics: ['conventions'],
|
|
37
|
+
categories: ['code-standard'],
|
|
38
|
+
weight: 0.8,
|
|
39
|
+
suggestedTopics: ['naming-convention', 'code-style', 'documentation'],
|
|
40
|
+
relatedRoles: [],
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
id: 'error-handling',
|
|
44
|
+
name: '错误处理',
|
|
45
|
+
description: '异常模式、错误恢复、输入验证、防御性编程',
|
|
46
|
+
topics: ['error-handling', 'constraints'],
|
|
47
|
+
categories: [],
|
|
48
|
+
weight: 1.0,
|
|
49
|
+
suggestedTopics: ['exception-pattern', 'error-recovery', 'input-validation'],
|
|
50
|
+
relatedRoles: ['service', 'networking', 'core'],
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
id: 'concurrency',
|
|
54
|
+
name: '并发与线程',
|
|
55
|
+
description: '线程安全、异步模式、竞态条件防护、锁策略',
|
|
56
|
+
topics: ['concurrency', 'async'],
|
|
57
|
+
categories: [],
|
|
58
|
+
weight: 0.9,
|
|
59
|
+
suggestedTopics: ['thread-safety', 'async-pattern', 'race-condition'],
|
|
60
|
+
relatedRoles: ['service', 'networking', 'storage'],
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
id: 'data-management',
|
|
64
|
+
name: '数据管理',
|
|
65
|
+
description: '持久化、缓存、序列化、数据流向完整性',
|
|
66
|
+
topics: ['data', 'data-flow', 'memory'],
|
|
67
|
+
categories: ['event-and-data-flow'],
|
|
68
|
+
weight: 0.8,
|
|
69
|
+
suggestedTopics: ['persistence', 'caching', 'serialization', 'data-integrity'],
|
|
70
|
+
relatedRoles: ['storage', 'model'],
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
id: 'networking',
|
|
74
|
+
name: '网络通信',
|
|
75
|
+
description: 'API 契约、请求模式、重试策略、实时通信',
|
|
76
|
+
topics: ['networking', 'real-time'],
|
|
77
|
+
categories: [],
|
|
78
|
+
weight: 0.7,
|
|
79
|
+
suggestedTopics: ['api-contract', 'retry-strategy', 'request-pattern'],
|
|
80
|
+
relatedRoles: ['networking'],
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
id: 'ui-patterns',
|
|
84
|
+
name: '界面模式',
|
|
85
|
+
description: 'UI 组件规范、生命周期、导航、数据绑定',
|
|
86
|
+
topics: ['ui', 'binding', 'pagination'],
|
|
87
|
+
categories: [],
|
|
88
|
+
weight: 0.7,
|
|
89
|
+
suggestedTopics: ['component-pattern', 'lifecycle', 'navigation'],
|
|
90
|
+
relatedRoles: ['ui', 'feature'],
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
id: 'testing',
|
|
94
|
+
name: '测试策略',
|
|
95
|
+
description: '测试模式、Mock 策略、CI/CD 流程',
|
|
96
|
+
topics: ['testing', 'test'],
|
|
97
|
+
categories: [],
|
|
98
|
+
weight: 0.9,
|
|
99
|
+
suggestedTopics: ['unit-test', 'mock-strategy', 'ci-cd'],
|
|
100
|
+
relatedRoles: [],
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
id: 'security',
|
|
104
|
+
name: '安全',
|
|
105
|
+
description: '认证授权、输入校验、加密、权限控制',
|
|
106
|
+
topics: ['security', 'auth'],
|
|
107
|
+
categories: [],
|
|
108
|
+
weight: 1.0,
|
|
109
|
+
suggestedTopics: ['authentication', 'authorization', 'encryption'],
|
|
110
|
+
relatedRoles: ['networking', 'service'],
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
id: 'performance',
|
|
114
|
+
name: '性能优化',
|
|
115
|
+
description: '内存管理、懒加载、缓存策略、渲染优化',
|
|
116
|
+
topics: ['performance', 'optimization'],
|
|
117
|
+
categories: [],
|
|
118
|
+
weight: 0.8,
|
|
119
|
+
suggestedTopics: ['memory-management', 'lazy-loading', 'rendering'],
|
|
120
|
+
relatedRoles: ['ui', 'storage'],
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
id: 'observability',
|
|
124
|
+
name: '可观测性',
|
|
125
|
+
description: '日志规范、事件追踪、监控诊断',
|
|
126
|
+
topics: ['logging', 'event', 'monitoring'],
|
|
127
|
+
categories: [],
|
|
128
|
+
weight: 0.7,
|
|
129
|
+
suggestedTopics: ['logging-standard', 'event-tracking', 'diagnostics'],
|
|
130
|
+
relatedRoles: ['service', 'core'],
|
|
131
|
+
},
|
|
132
|
+
];
|
|
133
|
+
/* ═══ DimensionAnalyzer Class ═════════════════════════════ */
|
|
134
|
+
export class DimensionAnalyzer {
|
|
135
|
+
#db;
|
|
136
|
+
constructor(db) {
|
|
137
|
+
this.#db = db;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* 分析项目知识健康雷达
|
|
141
|
+
*
|
|
142
|
+
* @param moduleRoles — 项目中存在的模块角色 (用于 gap 优先级推断)
|
|
143
|
+
*/
|
|
144
|
+
analyze(moduleRoles) {
|
|
145
|
+
// 1. 从 DB 获取所有活跃 recipe 的维度分类信息
|
|
146
|
+
const recipes = this.#fetchRecipeMetadata();
|
|
147
|
+
// 2. 将每条 recipe 映射到维度
|
|
148
|
+
const dimensionCounts = new Map();
|
|
149
|
+
for (const def of DIMENSION_DEFS) {
|
|
150
|
+
dimensionCounts.set(def.id, { count: 0, titles: [] });
|
|
151
|
+
}
|
|
152
|
+
let totalRecipes = 0;
|
|
153
|
+
for (const recipe of recipes) {
|
|
154
|
+
totalRecipes++;
|
|
155
|
+
const dimId = this.#classifyRecipe(recipe);
|
|
156
|
+
if (dimId) {
|
|
157
|
+
const entry = dimensionCounts.get(dimId);
|
|
158
|
+
entry.count++;
|
|
159
|
+
if (entry.titles.length < 3) {
|
|
160
|
+
entry.titles.push(recipe.title);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
// 3. 计算各维度得分与状态
|
|
165
|
+
const dimensions = DIMENSION_DEFS.map((def) => {
|
|
166
|
+
const entry = dimensionCounts.get(def.id);
|
|
167
|
+
return this.#scoreDimension(def, entry.count, entry.titles);
|
|
168
|
+
});
|
|
169
|
+
// 4. 加权平均健康分
|
|
170
|
+
let weightedSum = 0;
|
|
171
|
+
let weightTotal = 0;
|
|
172
|
+
for (let i = 0; i < DIMENSION_DEFS.length; i++) {
|
|
173
|
+
weightedSum += dimensions[i].score * DIMENSION_DEFS[i].weight;
|
|
174
|
+
weightTotal += DIMENSION_DEFS[i].weight;
|
|
175
|
+
}
|
|
176
|
+
const overallScore = weightTotal > 0 ? Math.round(weightedSum / weightTotal) : 0;
|
|
177
|
+
// 5. 统计覆盖
|
|
178
|
+
const coveredDimensions = dimensions.filter((d) => d.recipeCount > 0).length;
|
|
179
|
+
const totalDimensions = dimensions.length;
|
|
180
|
+
const radar = {
|
|
181
|
+
dimensions,
|
|
182
|
+
overallScore,
|
|
183
|
+
totalRecipes,
|
|
184
|
+
coveredDimensions,
|
|
185
|
+
totalDimensions,
|
|
186
|
+
dimensionCoverage: totalDimensions > 0 ? coveredDimensions / totalDimensions : 0,
|
|
187
|
+
};
|
|
188
|
+
// 6. 生成维度空白 (gaps)
|
|
189
|
+
const roleSet = new Set(moduleRoles);
|
|
190
|
+
const gaps = this.#detectDimensionGaps(dimensions, roleSet);
|
|
191
|
+
return { radar, gaps };
|
|
192
|
+
}
|
|
193
|
+
/* ─── 从 DB 获取 recipe 元数据 ─────────────────── */
|
|
194
|
+
#fetchRecipeMetadata() {
|
|
195
|
+
try {
|
|
196
|
+
const rows = this.#db
|
|
197
|
+
.prepare(`SELECT title, category, topicHint, kind
|
|
198
|
+
FROM knowledge_entries
|
|
199
|
+
WHERE lifecycle IN ('active', 'pending')`)
|
|
200
|
+
.all();
|
|
201
|
+
return rows.map((r) => ({
|
|
202
|
+
title: String(r.title ?? ''),
|
|
203
|
+
category: String(r.category ?? ''),
|
|
204
|
+
topicHint: String(r.topicHint ?? ''),
|
|
205
|
+
kind: String(r.kind ?? ''),
|
|
206
|
+
}));
|
|
207
|
+
}
|
|
208
|
+
catch {
|
|
209
|
+
return [];
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
/* ─── Recipe → 维度分类 ────────────────────────── */
|
|
213
|
+
/**
|
|
214
|
+
* 将 recipe 分类到最匹配的维度
|
|
215
|
+
*
|
|
216
|
+
* 优先级: topicHint 精确匹配 → category 匹配 → null
|
|
217
|
+
*/
|
|
218
|
+
#classifyRecipe(recipe) {
|
|
219
|
+
// 1. topicHint 精确匹配
|
|
220
|
+
if (recipe.topicHint) {
|
|
221
|
+
for (const def of DIMENSION_DEFS) {
|
|
222
|
+
if (def.topics.includes(recipe.topicHint)) {
|
|
223
|
+
return def.id;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
// 2. category 匹配
|
|
228
|
+
if (recipe.category) {
|
|
229
|
+
for (const def of DIMENSION_DEFS) {
|
|
230
|
+
if (def.categories.includes(recipe.category)) {
|
|
231
|
+
return def.id;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
return null;
|
|
236
|
+
}
|
|
237
|
+
/* ─── 维度评分 ─────────────────────────────────── */
|
|
238
|
+
#scoreDimension(def, recipeCount, titles) {
|
|
239
|
+
// 得分: 每条 recipe 贡献 20 分, 上限 100
|
|
240
|
+
const score = Math.min(100, recipeCount * 20);
|
|
241
|
+
// 状态阈值
|
|
242
|
+
let status;
|
|
243
|
+
if (recipeCount === 0) {
|
|
244
|
+
status = 'missing';
|
|
245
|
+
}
|
|
246
|
+
else if (recipeCount === 1) {
|
|
247
|
+
status = 'weak';
|
|
248
|
+
}
|
|
249
|
+
else if (recipeCount <= 4) {
|
|
250
|
+
status = 'adequate';
|
|
251
|
+
}
|
|
252
|
+
else {
|
|
253
|
+
status = 'strong';
|
|
254
|
+
}
|
|
255
|
+
// 雷达环级 (对应 Tech Radar)
|
|
256
|
+
let level;
|
|
257
|
+
if (score >= 80) {
|
|
258
|
+
level = 'adopt';
|
|
259
|
+
}
|
|
260
|
+
else if (score >= 40) {
|
|
261
|
+
level = 'trial';
|
|
262
|
+
}
|
|
263
|
+
else if (score > 0) {
|
|
264
|
+
level = 'assess';
|
|
265
|
+
}
|
|
266
|
+
else {
|
|
267
|
+
level = 'hold';
|
|
268
|
+
}
|
|
269
|
+
return {
|
|
270
|
+
id: def.id,
|
|
271
|
+
name: def.name,
|
|
272
|
+
description: def.description,
|
|
273
|
+
recipeCount,
|
|
274
|
+
score,
|
|
275
|
+
status,
|
|
276
|
+
level,
|
|
277
|
+
topRecipes: titles,
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
/* ─── 维度空白检测 ─────────────────────────────── */
|
|
281
|
+
#detectDimensionGaps(dimensions, moduleRoles) {
|
|
282
|
+
const gaps = [];
|
|
283
|
+
for (let i = 0; i < dimensions.length; i++) {
|
|
284
|
+
const dim = dimensions[i];
|
|
285
|
+
const def = DIMENSION_DEFS[i];
|
|
286
|
+
if (dim.status !== 'missing' && dim.status !== 'weak') {
|
|
287
|
+
continue;
|
|
288
|
+
}
|
|
289
|
+
// 优先级推断: 维度权重 × 是否有关联模块角色
|
|
290
|
+
const hasRelatedModules = def.relatedRoles.length === 0 || def.relatedRoles.some((r) => moduleRoles.has(r));
|
|
291
|
+
let priority;
|
|
292
|
+
if (dim.status === 'missing' && def.weight >= 0.9) {
|
|
293
|
+
priority = 'high';
|
|
294
|
+
}
|
|
295
|
+
else if (dim.status === 'missing' && hasRelatedModules) {
|
|
296
|
+
priority = 'high';
|
|
297
|
+
}
|
|
298
|
+
else if (dim.status === 'missing') {
|
|
299
|
+
priority = 'medium';
|
|
300
|
+
}
|
|
301
|
+
else {
|
|
302
|
+
// weak
|
|
303
|
+
priority = hasRelatedModules && def.weight >= 0.9 ? 'medium' : 'low';
|
|
304
|
+
}
|
|
305
|
+
const affectedRoles = def.relatedRoles.filter((r) => moduleRoles.has(r));
|
|
306
|
+
gaps.push({
|
|
307
|
+
dimension: def.id,
|
|
308
|
+
dimensionName: def.name,
|
|
309
|
+
recipeCount: dim.recipeCount,
|
|
310
|
+
status: dim.status,
|
|
311
|
+
priority,
|
|
312
|
+
suggestedTopics: def.suggestedTopics,
|
|
313
|
+
affectedRoles,
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
// 按优先级排序
|
|
317
|
+
const priorityOrder = { high: 0, medium: 1, low: 2 };
|
|
318
|
+
return gaps.sort((a, b) => priorityOrder[a.priority] - priorityOrder[b.priority]);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LayerInferrer — 拓扑排序层级推断
|
|
3
|
+
*
|
|
4
|
+
* 基于模块依赖图,通过去环 + 拓扑排序 + 最长路径法推断架构层级 (L0-Ln)。
|
|
5
|
+
* 底层 (L0) = Foundation/Core,顶层 = App/UI。
|
|
6
|
+
*
|
|
7
|
+
* @module LayerInferrer
|
|
8
|
+
*/
|
|
9
|
+
import type { CyclicDependency, Edge, LayerHierarchy } from './PanoramaTypes.js';
|
|
10
|
+
export declare class LayerInferrer {
|
|
11
|
+
#private;
|
|
12
|
+
/**
|
|
13
|
+
* 从模块依赖边推断架构层级
|
|
14
|
+
* @param edges - 模块间依赖边 (from depends_on/calls/data_flow to)
|
|
15
|
+
* @param modules - 所有模块名
|
|
16
|
+
* @param cycles - 已检测到的循环依赖
|
|
17
|
+
*/
|
|
18
|
+
infer(edges: Edge[], modules: string[], cycles: CyclicDependency[]): LayerHierarchy;
|
|
19
|
+
}
|