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.
Files changed (275) hide show
  1. package/README.md +1 -0
  2. package/dashboard/dist/assets/icons-BMNb0V6L.js +1 -0
  3. package/dashboard/dist/assets/index-DHJ1Dj7u.css +1 -0
  4. package/dashboard/dist/assets/index-DV8biUkH.js +112 -0
  5. package/dashboard/dist/index.html +3 -3
  6. package/dist/bin/cli.js +8 -4
  7. package/dist/lib/agent/AgentRuntime.d.ts +2 -2
  8. package/dist/lib/agent/AgentRuntime.js +26 -18
  9. package/dist/lib/agent/core/ChatAgentPrompts.js +57 -21
  10. package/dist/lib/agent/core/LoopContext.d.ts +1 -0
  11. package/dist/lib/agent/core/ToolExecutionPipeline.js +13 -0
  12. package/dist/lib/agent/domain/ChatAgentTasks.js +4 -0
  13. package/dist/lib/agent/forced-summary.js +7 -2
  14. package/dist/lib/agent/memory/ActiveContext.d.ts +0 -2
  15. package/dist/lib/agent/memory/ActiveContext.js +0 -2
  16. package/dist/lib/agent/memory/MemoryEmbeddingStore.d.ts +49 -0
  17. package/dist/lib/agent/memory/MemoryEmbeddingStore.js +159 -0
  18. package/dist/lib/agent/memory/MemoryRetriever.d.ts +2 -0
  19. package/dist/lib/agent/memory/MemoryRetriever.js +25 -11
  20. package/dist/lib/agent/memory/MemoryStore.d.ts +8 -41
  21. package/dist/lib/agent/memory/MemoryStore.js +196 -261
  22. package/dist/lib/agent/memory/PersistentMemory.d.ts +2 -0
  23. package/dist/lib/agent/memory/PersistentMemory.js +4 -5
  24. package/dist/lib/agent/memory/SessionStore.d.ts +0 -2
  25. package/dist/lib/agent/memory/SessionStore.js +0 -2
  26. package/dist/lib/agent/tools/ast-graph.js +21 -19
  27. package/dist/lib/agent/tools/infrastructure.js +3 -2
  28. package/dist/lib/agent/tools/project-access.d.ts +2 -2
  29. package/dist/lib/agent/tools/project-access.js +5 -4
  30. package/dist/lib/bootstrap.js +2 -1
  31. package/dist/lib/cli/AiScanService.js +8 -21
  32. package/dist/lib/cli/KnowledgeSyncService.d.ts +7 -37
  33. package/dist/lib/cli/KnowledgeSyncService.js +23 -51
  34. package/dist/lib/core/ast/ProjectGraph.js +5 -27
  35. package/dist/lib/core/discovery/ConfigWatcher.d.ts +64 -0
  36. package/dist/lib/core/discovery/ConfigWatcher.js +336 -0
  37. package/dist/lib/core/discovery/CustomConfigDiscoverer.d.ts +28 -0
  38. package/dist/lib/core/discovery/CustomConfigDiscoverer.js +1303 -0
  39. package/dist/lib/core/discovery/DiscovererPreference.d.ts +44 -0
  40. package/dist/lib/core/discovery/DiscovererPreference.js +141 -0
  41. package/dist/lib/core/discovery/DiscovererRegistry.d.ts +10 -1
  42. package/dist/lib/core/discovery/DiscovererRegistry.js +42 -2
  43. package/dist/lib/core/discovery/ProjectDiscoverer.d.ts +19 -0
  44. package/dist/lib/core/discovery/index.d.ts +2 -0
  45. package/dist/lib/core/discovery/index.js +4 -0
  46. package/dist/lib/core/discovery/parsers/CMakeParser.d.ts +32 -0
  47. package/dist/lib/core/discovery/parsers/CMakeParser.js +148 -0
  48. package/dist/lib/core/discovery/parsers/GradleDslParser.d.ts +43 -0
  49. package/dist/lib/core/discovery/parsers/GradleDslParser.js +171 -0
  50. package/dist/lib/core/discovery/parsers/JsonConfigParser.d.ts +45 -0
  51. package/dist/lib/core/discovery/parsers/JsonConfigParser.js +122 -0
  52. package/dist/lib/core/discovery/parsers/RubyDslParser.d.ts +49 -0
  53. package/dist/lib/core/discovery/parsers/RubyDslParser.js +282 -0
  54. package/dist/lib/core/discovery/parsers/StarlarkParser.d.ts +33 -0
  55. package/dist/lib/core/discovery/parsers/StarlarkParser.js +229 -0
  56. package/dist/lib/core/discovery/parsers/YamlConfigParser.d.ts +37 -0
  57. package/dist/lib/core/discovery/parsers/YamlConfigParser.js +212 -0
  58. package/dist/lib/domain/dimension/DimensionRegistry.d.ts +0 -2
  59. package/dist/lib/domain/dimension/DimensionRegistry.js +0 -2
  60. package/dist/lib/domain/dimension/DimensionSop.js +44 -33
  61. package/dist/lib/domain/dimension/UnifiedDimension.d.ts +0 -2
  62. package/dist/lib/domain/dimension/UnifiedDimension.js +0 -2
  63. package/dist/lib/domain/knowledge/KnowledgeEntry.d.ts +7 -1
  64. package/dist/lib/domain/knowledge/KnowledgeEntry.js +17 -3
  65. package/dist/lib/domain/knowledge/Lifecycle.d.ts +26 -0
  66. package/dist/lib/domain/knowledge/Lifecycle.js +42 -0
  67. package/dist/lib/domain/knowledge/index.d.ts +2 -1
  68. package/dist/lib/domain/knowledge/index.js +1 -1
  69. package/dist/lib/external/ai/AiProvider.d.ts +12 -0
  70. package/dist/lib/external/ai/AiProvider.js +24 -0
  71. package/dist/lib/external/ai/AiProviderManager.d.ts +101 -0
  72. package/dist/lib/external/ai/AiProviderManager.js +193 -0
  73. package/dist/lib/external/ai/providers/ClaudeProvider.js +11 -0
  74. package/dist/lib/external/ai/providers/GoogleGeminiProvider.js +18 -0
  75. package/dist/lib/external/ai/providers/MockProvider.d.ts +21 -3
  76. package/dist/lib/external/ai/providers/MockProvider.js +290 -14
  77. package/dist/lib/external/ai/providers/OpenAiProvider.js +16 -0
  78. package/dist/lib/external/lark/LarkTransport.d.ts +5 -1
  79. package/dist/lib/external/lark/LarkTransport.js +10 -2
  80. package/dist/lib/external/mcp/handlers/bootstrap/pipeline/BootstrapSnapshot.d.ts +2 -1
  81. package/dist/lib/external/mcp/handlers/bootstrap/pipeline/BootstrapSnapshot.js +102 -153
  82. package/dist/lib/external/mcp/handlers/bootstrap/pipeline/mock-pipeline.d.ts +20 -0
  83. package/dist/lib/external/mcp/handlers/bootstrap/pipeline/mock-pipeline.js +432 -0
  84. package/dist/lib/external/mcp/handlers/bootstrap/pipeline/orchestrator.js +49 -24
  85. package/dist/lib/external/mcp/handlers/bootstrap/refine.js +8 -0
  86. package/dist/lib/external/mcp/handlers/bootstrap/shared/bootstrap-phases.d.ts +1 -1
  87. package/dist/lib/external/mcp/handlers/bootstrap/shared/bootstrap-phases.js +41 -37
  88. package/dist/lib/external/mcp/handlers/bootstrap-external.d.ts +9 -0
  89. package/dist/lib/external/mcp/handlers/bootstrap-external.js +3 -1
  90. package/dist/lib/external/mcp/handlers/bootstrap-internal.js +2 -0
  91. package/dist/lib/external/mcp/handlers/consolidated.js +2 -1
  92. package/dist/lib/external/mcp/handlers/dimension-complete-external.js +9 -4
  93. package/dist/lib/external/mcp/handlers/evolve-external.d.ts +1 -0
  94. package/dist/lib/external/mcp/handlers/evolve-external.js +18 -18
  95. package/dist/lib/external/mcp/handlers/guard.js +15 -24
  96. package/dist/lib/external/mcp/handlers/knowledge.js +5 -4
  97. package/dist/lib/external/mcp/handlers/panorama.js +9 -9
  98. package/dist/lib/external/mcp/handlers/rescan-external.js +7 -6
  99. package/dist/lib/external/mcp/handlers/rescan-internal.js +9 -5
  100. package/dist/lib/external/mcp/handlers/search.js +3 -1
  101. package/dist/lib/external/mcp/handlers/skill.js +4 -4
  102. package/dist/lib/external/mcp/handlers/structure.js +8 -12
  103. package/dist/lib/external/mcp/handlers/system.js +10 -34
  104. package/dist/lib/http/routes/ai.js +109 -30
  105. package/dist/lib/http/routes/candidates.js +11 -4
  106. package/dist/lib/http/routes/commands.js +10 -1
  107. package/dist/lib/http/routes/guardReport.js +3 -5
  108. package/dist/lib/http/routes/health.js +11 -0
  109. package/dist/lib/http/routes/modules.js +27 -0
  110. package/dist/lib/http/routes/panorama.js +12 -12
  111. package/dist/lib/http/routes/recipes.js +66 -8
  112. package/dist/lib/http/routes/remote.js +3 -13
  113. package/dist/lib/http/routes/search.js +11 -8
  114. package/dist/lib/http/utils/routeHelpers.js +2 -1
  115. package/dist/lib/infrastructure/audit/AuditLogger.d.ts +20 -3
  116. package/dist/lib/infrastructure/audit/AuditStore.d.ts +28 -29
  117. package/dist/lib/infrastructure/audit/AuditStore.js +81 -88
  118. package/dist/lib/infrastructure/database/drizzle/schema.d.ts +180 -2
  119. package/dist/lib/infrastructure/database/drizzle/schema.js +23 -3
  120. package/dist/lib/injection/ServiceContainer.d.ts +6 -5
  121. package/dist/lib/injection/ServiceContainer.js +18 -31
  122. package/dist/lib/injection/ServiceMap.d.ts +22 -0
  123. package/dist/lib/injection/modules/AiModule.d.ts +6 -9
  124. package/dist/lib/injection/modules/AiModule.js +82 -39
  125. package/dist/lib/injection/modules/AppModule.js +2 -1
  126. package/dist/lib/injection/modules/GuardModule.js +5 -5
  127. package/dist/lib/injection/modules/InfraModule.js +60 -0
  128. package/dist/lib/injection/modules/KnowledgeModule.js +86 -51
  129. package/dist/lib/injection/modules/PanoramaModule.js +16 -10
  130. package/dist/lib/injection/modules/VectorModule.js +3 -0
  131. package/dist/lib/repository/audit/AuditRepository.d.ts +107 -0
  132. package/dist/lib/repository/audit/AuditRepository.js +272 -0
  133. package/dist/lib/repository/base/RepositoryBase.d.ts +46 -0
  134. package/dist/lib/repository/base/RepositoryBase.js +32 -0
  135. package/dist/lib/repository/bootstrap/BootstrapRepository.d.ts +94 -0
  136. package/dist/lib/repository/bootstrap/BootstrapRepository.js +246 -0
  137. package/dist/lib/repository/code/CodeEntityRepository.d.ts +91 -0
  138. package/dist/lib/repository/code/CodeEntityRepository.js +361 -0
  139. package/dist/lib/repository/delivery/DeliveryRepoAdapter.d.ts +39 -0
  140. package/dist/lib/repository/delivery/DeliveryRepoAdapter.js +23 -0
  141. package/dist/lib/repository/evolution/LifecycleEventRepository.d.ts +51 -0
  142. package/dist/lib/repository/evolution/LifecycleEventRepository.js +119 -0
  143. package/dist/lib/repository/evolution/ProposalRepository.d.ts +9 -12
  144. package/dist/lib/repository/evolution/ProposalRepository.js +114 -57
  145. package/dist/lib/repository/guard/GuardViolationRepository.d.ts +104 -0
  146. package/dist/lib/repository/guard/GuardViolationRepository.js +217 -0
  147. package/dist/lib/repository/knowledge/KnowledgeEdgeRepository.d.ts +129 -0
  148. package/dist/lib/repository/knowledge/KnowledgeEdgeRepository.js +475 -0
  149. package/dist/lib/repository/knowledge/KnowledgeFileStore.d.ts +39 -0
  150. package/dist/lib/repository/knowledge/KnowledgeFileStore.js +12 -0
  151. package/dist/lib/repository/knowledge/KnowledgeRepository.impl.d.ts +295 -11
  152. package/dist/lib/repository/knowledge/KnowledgeRepository.impl.js +608 -13
  153. package/dist/lib/repository/knowledge/KnowledgeUnitOfWork.d.ts +61 -0
  154. package/dist/lib/repository/knowledge/KnowledgeUnitOfWork.js +156 -0
  155. package/dist/lib/repository/memory/MemoryRepository.d.ts +90 -0
  156. package/dist/lib/repository/memory/MemoryRepository.js +260 -0
  157. package/dist/lib/repository/search/SearchRepoAdapter.d.ts +92 -0
  158. package/dist/lib/repository/search/SearchRepoAdapter.js +124 -0
  159. package/dist/lib/repository/session/SessionRepository.d.ts +46 -0
  160. package/dist/lib/repository/session/SessionRepository.js +110 -0
  161. package/dist/lib/repository/sourceref/RecipeSourceRefRepository.d.ts +66 -0
  162. package/dist/lib/repository/sourceref/RecipeSourceRefRepository.js +182 -0
  163. package/dist/lib/repository/sync/SyncRepoAdapter.d.ts +58 -0
  164. package/dist/lib/repository/sync/SyncRepoAdapter.js +58 -0
  165. package/dist/lib/service/bootstrap/UiStartupTasks.js +5 -6
  166. package/dist/lib/service/bootstrap/bootstrap-event-types.d.ts +0 -1
  167. package/dist/lib/service/bootstrap/bootstrap-event-types.js +0 -1
  168. package/dist/lib/service/cleanup/CleanupService.d.ts +54 -7
  169. package/dist/lib/service/cleanup/CleanupService.js +291 -40
  170. package/dist/lib/service/delivery/CursorDeliveryPipeline.js +6 -8
  171. package/dist/lib/service/evolution/ConsolidationAdvisor.d.ts +4 -9
  172. package/dist/lib/service/evolution/ConsolidationAdvisor.js +34 -70
  173. package/dist/lib/service/evolution/ContentPatcher.d.ts +4 -12
  174. package/dist/lib/service/evolution/ContentPatcher.js +48 -19
  175. package/dist/lib/service/evolution/ContradictionDetector.d.ts +3 -7
  176. package/dist/lib/service/evolution/ContradictionDetector.js +17 -24
  177. package/dist/lib/service/evolution/DecayDetector.d.ts +10 -9
  178. package/dist/lib/service/evolution/DecayDetector.js +63 -57
  179. package/dist/lib/service/evolution/EnhancementSuggester.d.ts +3 -9
  180. package/dist/lib/service/evolution/EnhancementSuggester.js +42 -86
  181. package/dist/lib/service/evolution/KnowledgeMetabolism.d.ts +4 -4
  182. package/dist/lib/service/evolution/KnowledgeMetabolism.js +102 -71
  183. package/dist/lib/service/evolution/ProposalExecutor.d.ts +5 -12
  184. package/dist/lib/service/evolution/ProposalExecutor.js +64 -69
  185. package/dist/lib/service/evolution/RecipeLifecycleSupervisor.d.ts +9 -14
  186. package/dist/lib/service/evolution/RecipeLifecycleSupervisor.js +94 -155
  187. package/dist/lib/service/evolution/RecipeRelevanceAuditor.d.ts +4 -1
  188. package/dist/lib/service/evolution/RecipeRelevanceAuditor.js +50 -49
  189. package/dist/lib/service/evolution/RedundancyAnalyzer.d.ts +3 -7
  190. package/dist/lib/service/evolution/RedundancyAnalyzer.js +15 -22
  191. package/dist/lib/service/evolution/StagingManager.d.ts +6 -15
  192. package/dist/lib/service/evolution/StagingManager.js +37 -95
  193. package/dist/lib/service/evolution/createSupersedeProposal.d.ts +1 -1
  194. package/dist/lib/service/evolution/createSupersedeProposal.js +7 -8
  195. package/dist/lib/service/guard/CoverageAnalyzer.d.ts +3 -7
  196. package/dist/lib/service/guard/CoverageAnalyzer.js +9 -11
  197. package/dist/lib/service/guard/GuardCheckEngine.d.ts +3 -0
  198. package/dist/lib/service/guard/GuardCheckEngine.js +14 -22
  199. package/dist/lib/service/guard/ReverseGuard.d.ts +4 -7
  200. package/dist/lib/service/guard/ReverseGuard.js +21 -31
  201. package/dist/lib/service/guard/ViolationsStore.d.ts +15 -21
  202. package/dist/lib/service/guard/ViolationsStore.js +75 -69
  203. package/dist/lib/service/knowledge/CodeEntityGraph.d.ts +45 -63
  204. package/dist/lib/service/knowledge/CodeEntityGraph.js +418 -496
  205. package/dist/lib/service/knowledge/ConfidenceRouter.js +18 -9
  206. package/dist/lib/service/knowledge/KnowledgeFileWriter.d.ts +2 -1
  207. package/dist/lib/service/knowledge/KnowledgeGraphService.d.ts +18 -60
  208. package/dist/lib/service/knowledge/KnowledgeGraphService.js +58 -109
  209. package/dist/lib/service/knowledge/KnowledgeService.d.ts +15 -1
  210. package/dist/lib/service/knowledge/KnowledgeService.js +97 -46
  211. package/dist/lib/service/knowledge/RecipeProductionGateway.d.ts +0 -2
  212. package/dist/lib/service/knowledge/RecipeProductionGateway.js +0 -2
  213. package/dist/lib/service/knowledge/SourceRefReconciler.d.ts +5 -13
  214. package/dist/lib/service/knowledge/SourceRefReconciler.js +58 -78
  215. package/dist/lib/service/module/ModuleService.js +10 -19
  216. package/dist/lib/service/panorama/CouplingAnalyzer.d.ts +14 -3
  217. package/dist/lib/service/panorama/CouplingAnalyzer.js +137 -32
  218. package/dist/lib/service/panorama/DimensionAnalyzer.d.ts +7 -4
  219. package/dist/lib/service/panorama/DimensionAnalyzer.js +94 -33
  220. package/dist/lib/service/panorama/LayerInferrer.d.ts +16 -1
  221. package/dist/lib/service/panorama/LayerInferrer.js +118 -1
  222. package/dist/lib/service/panorama/ModuleDiscoverer.d.ts +14 -4
  223. package/dist/lib/service/panorama/ModuleDiscoverer.js +209 -61
  224. package/dist/lib/service/panorama/PanoramaAggregator.d.ts +15 -4
  225. package/dist/lib/service/panorama/PanoramaAggregator.js +128 -62
  226. package/dist/lib/service/panorama/PanoramaScanner.d.ts +5 -1
  227. package/dist/lib/service/panorama/PanoramaScanner.js +60 -31
  228. package/dist/lib/service/panorama/PanoramaService.d.ts +11 -8
  229. package/dist/lib/service/panorama/PanoramaService.js +49 -69
  230. package/dist/lib/service/panorama/PanoramaTypes.d.ts +41 -0
  231. package/dist/lib/service/panorama/RoleRefiner.d.ts +10 -5
  232. package/dist/lib/service/panorama/RoleRefiner.js +92 -282
  233. package/dist/lib/service/panorama/TechStackProfiler.d.ts +13 -0
  234. package/dist/lib/service/panorama/TechStackProfiler.js +79 -0
  235. package/dist/lib/service/quality/QualityScorer.d.ts +45 -26
  236. package/dist/lib/service/quality/QualityScorer.js +157 -83
  237. package/dist/lib/service/search/SearchEngine.d.ts +1 -0
  238. package/dist/lib/service/search/SearchEngine.js +32 -37
  239. package/dist/lib/service/signal/HitRecorder.js +5 -5
  240. package/dist/lib/service/skills/RuleRecallStrategy.js +7 -3
  241. package/dist/lib/service/skills/SignalCollector.d.ts +6 -8
  242. package/dist/lib/service/skills/SignalCollector.js +34 -60
  243. package/dist/lib/service/skills/SkillAdvisor.d.ts +7 -13
  244. package/dist/lib/service/skills/SkillAdvisor.js +30 -79
  245. package/dist/lib/service/vector/ContextualEnricher.d.ts +1 -0
  246. package/dist/lib/service/vector/ContextualEnricher.js +4 -0
  247. package/dist/lib/service/vector/SyncCoordinator.d.ts +3 -1
  248. package/dist/lib/service/vector/SyncCoordinator.js +25 -3
  249. package/dist/lib/service/vector/VectorService.d.ts +2 -0
  250. package/dist/lib/service/vector/VectorService.js +3 -0
  251. package/dist/lib/service/wiki/WikiGenerator.js +1 -1
  252. package/dist/lib/shared/LanguageProfiles.d.ts +109 -0
  253. package/dist/lib/shared/LanguageProfiles.js +939 -0
  254. package/dist/lib/shared/LanguageService.d.ts +6 -0
  255. package/dist/lib/shared/LanguageService.js +19 -0
  256. package/dist/lib/shared/constants.d.ts +19 -19
  257. package/dist/lib/shared/constants.js +10 -10
  258. package/dist/lib/shared/developer-identity.d.ts +18 -0
  259. package/dist/lib/shared/developer-identity.js +62 -0
  260. package/dist/lib/shared/schemas/http-requests.d.ts +8 -17
  261. package/dist/lib/shared/schemas/http-requests.js +9 -6
  262. package/dist/lib/shared/schemas/mcp-tools.d.ts +1 -1
  263. package/dist/lib/types/knowledge-wire.d.ts +1 -0
  264. package/dist/lib/types/project-snapshot-builder.d.ts +0 -1
  265. package/dist/lib/types/project-snapshot-builder.js +0 -1
  266. package/dist/lib/types/project-snapshot.d.ts +0 -1
  267. package/dist/lib/types/project-snapshot.js +0 -1
  268. package/dist/lib/types/snapshot-views.d.ts +0 -2
  269. package/dist/lib/types/snapshot-views.js +0 -1
  270. package/package.json +2 -1
  271. package/dashboard/dist/assets/icons-D1aVZYFW.js +0 -1
  272. package/dashboard/dist/assets/index-CxHOu8Hd.css +0 -1
  273. package/dashboard/dist/assets/index-DDdAOpYT.js +0 -128
  274. package/dist/lib/repository/base/BaseRepository.d.ts +0 -53
  275. package/dist/lib/repository/base/BaseRepository.js +0 -226
@@ -6,22 +6,33 @@
6
6
  *
7
7
  * @module CouplingAnalyzer
8
8
  */
9
- import type { CeDbLike, CyclicDependency, Edge } from './PanoramaTypes.js';
9
+ import type { CodeEntityRepositoryImpl } from '../../repository/code/CodeEntityRepository.js';
10
+ import type { KnowledgeEdgeRepositoryImpl } from '../../repository/knowledge/KnowledgeEdgeRepository.js';
11
+ import type { CyclicDependency, Edge } from './PanoramaTypes.js';
10
12
  export interface CouplingMetrics {
11
13
  fanIn: number;
12
14
  fanOut: number;
13
15
  }
16
+ export interface ExternalDepMetrics {
17
+ name: string;
18
+ fanIn: number;
19
+ /** 依赖此外部库的本地模块列表 */
20
+ dependedBy: string[];
21
+ }
14
22
  export interface CouplingResult {
15
23
  cycles: CyclicDependency[];
16
24
  metrics: Map<string, CouplingMetrics>;
17
25
  edges: Edge[];
26
+ /** 外部依赖 fan-in 统计(按 fan-in 降序排列) */
27
+ externalDeps: ExternalDepMetrics[];
18
28
  }
19
29
  export declare class CouplingAnalyzer {
20
30
  #private;
21
- constructor(db: CeDbLike, projectRoot: string);
31
+ constructor(edgeRepo: KnowledgeEdgeRepositoryImpl, entityRepo: CodeEntityRepositoryImpl, projectRoot: string);
22
32
  /**
23
33
  * 分析模块间耦合关系
24
34
  * @param moduleFiles - Map<moduleName, filePaths[]>
35
+ * @param externalModules - 外部模块名集合(无源码但参与依赖图)
25
36
  */
26
- analyze(moduleFiles: Map<string, string[]>): CouplingResult;
37
+ analyze(moduleFiles: Map<string, string[]>, externalModules?: Set<string>): Promise<CouplingResult>;
27
38
  }
@@ -6,6 +6,8 @@
6
6
  *
7
7
  * @module CouplingAnalyzer
8
8
  */
9
+ import { readFileSync } from 'node:fs';
10
+ import { LanguageProfiles } from '#shared/LanguageProfiles.js';
9
11
  /* ═══ Edge Weights ════════════════════════════════════════ */
10
12
  const EDGE_WEIGHTS = {
11
13
  depends_on: 0.5,
@@ -14,17 +16,20 @@ const EDGE_WEIGHTS = {
14
16
  };
15
17
  /* ═══ CouplingAnalyzer Class ══════════════════════════════ */
16
18
  export class CouplingAnalyzer {
17
- #db;
19
+ #edgeRepo;
20
+ #entityRepo;
18
21
  #projectRoot;
19
- constructor(db, projectRoot) {
20
- this.#db = db;
22
+ constructor(edgeRepo, entityRepo, projectRoot) {
23
+ this.#edgeRepo = edgeRepo;
24
+ this.#entityRepo = entityRepo;
21
25
  this.#projectRoot = projectRoot;
22
26
  }
23
27
  /**
24
28
  * 分析模块间耦合关系
25
29
  * @param moduleFiles - Map<moduleName, filePaths[]>
30
+ * @param externalModules - 外部模块名集合(无源码但参与依赖图)
26
31
  */
27
- analyze(moduleFiles) {
32
+ async analyze(moduleFiles, externalModules) {
28
33
  // 1. 构建 file → module 反向索引
29
34
  const fileToModule = new Map();
30
35
  for (const [mod, files] of moduleFiles) {
@@ -33,7 +38,7 @@ export class CouplingAnalyzer {
33
38
  }
34
39
  }
35
40
  // 2. 从 knowledge_edges 聚合模块间边
36
- const edges = this.#buildModuleEdges(moduleFiles, fileToModule);
41
+ const edges = await this.#buildModuleEdges(moduleFiles, fileToModule);
37
42
  // 3. 建图
38
43
  const adjacency = new Map();
39
44
  const allModules = new Set(moduleFiles.keys());
@@ -66,61 +71,56 @@ export class CouplingAnalyzer {
66
71
  toM.fanIn++;
67
72
  }
68
73
  }
74
+ // 5.5 外部依赖 fan-in 统计
75
+ const externalDeps = await this.#computeExternalFanIn(moduleFiles, externalModules);
69
76
  // 去重边 (同 from→to 聚合)
70
77
  const dedupEdges = this.#deduplicateEdges(edges);
71
- return { cycles, metrics, edges: dedupEdges };
78
+ return { cycles, metrics, edges: dedupEdges, externalDeps };
72
79
  }
73
80
  /* ─── Internal helpers ──────────────────────────── */
74
- #buildModuleEdges(moduleFiles, fileToModule) {
81
+ async #buildModuleEdges(moduleFiles, fileToModule) {
75
82
  const edges = [];
76
83
  const relations = ['depends_on', 'calls', 'data_flow'];
84
+ // 跟踪哪些模块有 DB 边(用于判断是否需要 import 推断)
85
+ const modulesWithDbEdges = new Set();
77
86
  for (const relation of relations) {
78
87
  const weight = EDGE_WEIGHTS[relation] ?? 0.5;
79
88
  // 查询该类型的边(仅限当前项目:至少 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);
89
+ const rows = await this.#edgeRepo.findEdgesFilteredByEntityExistence(relation, this.#projectRoot);
92
90
  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;
91
+ const fromId = row.fromId;
92
+ const toId = row.toId;
93
+ const fromType = row.fromType;
94
+ const toType = row.toType;
97
95
  // module-to-module 直接边 (depends_on)
98
96
  if (fromType === 'module' && toType === 'module') {
99
97
  if (fromId !== toId && moduleFiles.has(fromId) && moduleFiles.has(toId)) {
100
98
  edges.push({ from: fromId, to: toId, weight, relation });
99
+ modulesWithDbEdges.add(fromId);
101
100
  }
102
101
  continue;
103
102
  }
104
103
  // entity-to-entity 边 → 解析 file → module
105
- const fromModule = this.#resolveEntityModule(fromId, fromType, fileToModule);
106
- const toModule = this.#resolveEntityModule(toId, toType, fileToModule);
104
+ const fromModule = await this.#resolveEntityModule(fromId, fromType, fileToModule);
105
+ const toModule = await this.#resolveEntityModule(toId, toType, fileToModule);
107
106
  if (fromModule && toModule && fromModule !== toModule) {
108
107
  edges.push({ from: fromModule, to: toModule, weight, relation });
108
+ modulesWithDbEdges.add(fromModule);
109
109
  }
110
110
  }
111
111
  }
112
+ // 对没有 DB 边的模块,通过 import 扫描推断依赖
113
+ const importEdges = this.#inferEdgesFromImports(moduleFiles, modulesWithDbEdges);
114
+ edges.push(...importEdges);
112
115
  return edges;
113
116
  }
114
- #resolveEntityModule(entityId, _entityType, fileToModule) {
117
+ async #resolveEntityModule(entityId, _entityType, fileToModule) {
115
118
  // 先查实体所在文件
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) {
119
+ const entity = await this.#entityRepo.findByEntityIdOnly(entityId, this.#projectRoot);
120
+ if (!entity?.filePath) {
121
121
  return null;
122
122
  }
123
- return fileToModule.get(row.file_path) ?? null;
123
+ return fileToModule.get(entity.filePath) ?? null;
124
124
  }
125
125
  /**
126
126
  * Tarjan 强连通分量算法
@@ -174,6 +174,111 @@ export class CouplingAnalyzer {
174
174
  severity: cycle.length > 3 ? 'error' : 'warning',
175
175
  }));
176
176
  }
177
+ /**
178
+ * 统计外部依赖的 fan-in(被多少本地模块依赖)
179
+ * 数据来源:knowledge_edges 中 from_type='module' AND to_type='module' 且 to 不在 moduleFiles 中
180
+ */
181
+ async #computeExternalFanIn(moduleFiles, externalModules) {
182
+ const fanInMap = new Map();
183
+ // 从 DB 查询 module-to-module depends_on 边
184
+ const rows = await this.#edgeRepo.findModuleDependencyPairs();
185
+ for (const row of rows) {
186
+ const fromId = row.fromId;
187
+ const toId = row.toId;
188
+ // from 必须是本地模块, to 必须是外部模块
189
+ if (!moduleFiles.has(fromId)) {
190
+ continue;
191
+ }
192
+ if (moduleFiles.has(toId)) {
193
+ continue;
194
+ }
195
+ // 如果提供了 externalModules 集合,检查 toId 是否在其中
196
+ if (externalModules && !externalModules.has(toId)) {
197
+ continue;
198
+ }
199
+ if (!fanInMap.has(toId)) {
200
+ fanInMap.set(toId, new Set());
201
+ }
202
+ fanInMap.get(toId).add(fromId);
203
+ }
204
+ // 转换为排序数组(按 fan-in 降序)
205
+ return [...fanInMap.entries()]
206
+ .map(([name, deps]) => ({
207
+ name,
208
+ fanIn: deps.size,
209
+ dependedBy: [...deps].sort(),
210
+ }))
211
+ .sort((a, b) => b.fanIn - a.fanIn);
212
+ }
213
+ /**
214
+ * 对无 DB 边的模块,扫描源文件 import 语句推断依赖。
215
+ * 典型场景:iOS 宿主应用中未声明在 Boxfile 的子模块。
216
+ */
217
+ #inferEdgesFromImports(moduleFiles, modulesWithDbEdges) {
218
+ const sourceExts = LanguageProfiles.sourceExts;
219
+ const importPatterns = LanguageProfiles.importPatterns;
220
+ const MAX_READ_BYTES = 8192; // 只读文件前 8KB(import 几乎总在文件头部)
221
+ const edges = [];
222
+ // 建立已知模块名集合(用于快速匹配 import 目标)
223
+ const knownModules = new Set(moduleFiles.keys());
224
+ for (const [modName, files] of moduleFiles) {
225
+ // 跳过已有 DB 边的模块——它们的依赖已由 Boxfile/配置声明
226
+ if (modulesWithDbEdges.has(modName)) {
227
+ continue;
228
+ }
229
+ const importedModules = new Set();
230
+ for (const filePath of files) {
231
+ // 只扫描源码文件
232
+ const dotIdx = filePath.lastIndexOf('.');
233
+ if (dotIdx < 0) {
234
+ continue;
235
+ }
236
+ const ext = filePath.slice(dotIdx).toLowerCase();
237
+ if (!sourceExts.has(ext)) {
238
+ continue;
239
+ }
240
+ let content;
241
+ try {
242
+ const fd = readFileSync(filePath, { flag: 'r' });
243
+ content =
244
+ fd.length > MAX_READ_BYTES
245
+ ? fd.subarray(0, MAX_READ_BYTES).toString('utf8')
246
+ : fd.toString('utf8');
247
+ }
248
+ catch {
249
+ continue; // 文件不可读则跳过
250
+ }
251
+ // 逐行匹配 import 模式
252
+ const lines = content.split('\n');
253
+ for (const line of lines) {
254
+ const trimmed = line.trim();
255
+ for (const pattern of importPatterns) {
256
+ const m = pattern.regex.exec(trimmed);
257
+ if (m) {
258
+ const candidates = pattern.extract(m);
259
+ for (const c of candidates) {
260
+ if (c) {
261
+ importedModules.add(c);
262
+ }
263
+ }
264
+ }
265
+ }
266
+ }
267
+ }
268
+ // 生成边:仅在目标是已知模块且非自身时
269
+ for (const imported of importedModules) {
270
+ if (imported !== modName && knownModules.has(imported)) {
271
+ edges.push({
272
+ from: modName,
273
+ to: imported,
274
+ weight: EDGE_WEIGHTS.depends_on,
275
+ relation: 'depends_on',
276
+ });
277
+ }
278
+ }
279
+ }
280
+ return edges;
281
+ }
177
282
  #deduplicateEdges(edges) {
178
283
  const key = (e) => `${e.from}→${e.to}`;
179
284
  const map = new Map();
@@ -13,17 +13,20 @@
13
13
  *
14
14
  * @module DimensionAnalyzer
15
15
  */
16
- import type { CeDbLike, HealthRadar, KnowledgeGap } from './PanoramaTypes.js';
16
+ import type { BootstrapRepositoryImpl } from '../../repository/bootstrap/BootstrapRepository.js';
17
+ import type { CodeEntityRepositoryImpl } from '../../repository/code/CodeEntityRepository.js';
18
+ import type { KnowledgeRepositoryImpl } from '../../repository/knowledge/KnowledgeRepository.impl.js';
19
+ import type { HealthRadar, KnowledgeGap } from './PanoramaTypes.js';
17
20
  export declare class DimensionAnalyzer {
18
21
  #private;
19
- constructor(db: CeDbLike);
22
+ constructor(bootstrapRepo: BootstrapRepositoryImpl, entityRepo: CodeEntityRepositoryImpl, knowledgeRepo: KnowledgeRepositoryImpl, projectRoot: string);
20
23
  /**
21
24
  * 分析项目知识健康雷达
22
25
  *
23
26
  * @param moduleRoles — 项目中存在的模块角色 (用于 gap 优先级推断)
24
27
  */
25
- analyze(moduleRoles: string[]): {
28
+ analyze(moduleRoles: string[]): Promise<{
26
29
  radar: HealthRadar;
27
30
  gaps: KnowledgeGap[];
28
- };
31
+ }>;
29
32
  }
@@ -13,31 +13,34 @@
13
13
  *
14
14
  * @module DimensionAnalyzer
15
15
  */
16
- import { classifyRecipeToDimension, DIMENSION_REGISTRY } from '#domain/dimension/index.js';
17
- /* ═══ 维度定义 从统一注册表派生 ═══════════════════════ */
18
- /**
19
- * Panorama 使用全量维度注册表进行健康评估。
20
- * 所有维度(含语言/框架条件维度)都参与评估 —
21
- * 若该语言未激活但有 Recipe → 仍计入(只是不生成 gap 建议)。
22
- */
23
- const DIMENSION_DEFS = DIMENSION_REGISTRY;
16
+ import { classifyRecipeToDimension, DIMENSION_REGISTRY, resolveActiveDimensions, } from '#domain/dimension/index.js';
17
+ import { LanguageService } from '#shared/LanguageService.js';
18
+ import { COUNTABLE_LIFECYCLES } from '../../domain/knowledge/Lifecycle.js';
24
19
  /* ═══ DimensionAnalyzer Class ═════════════════════════════ */
25
20
  export class DimensionAnalyzer {
26
- #db;
27
- constructor(db) {
28
- this.#db = db;
21
+ #bootstrapRepo;
22
+ #entityRepo;
23
+ #knowledgeRepo;
24
+ #projectRoot;
25
+ constructor(bootstrapRepo, entityRepo, knowledgeRepo, projectRoot) {
26
+ this.#bootstrapRepo = bootstrapRepo;
27
+ this.#entityRepo = entityRepo;
28
+ this.#knowledgeRepo = knowledgeRepo;
29
+ this.#projectRoot = projectRoot;
29
30
  }
30
31
  /**
31
32
  * 分析项目知识健康雷达
32
33
  *
33
34
  * @param moduleRoles — 项目中存在的模块角色 (用于 gap 优先级推断)
34
35
  */
35
- analyze(moduleRoles) {
36
+ async analyze(moduleRoles) {
37
+ // 0. 按项目语言过滤活跃维度(排除无关语言/框架维度)
38
+ const activeDims = await this.#resolveActiveDims();
36
39
  // 1. 从 DB 获取所有活跃 recipe 的维度分类信息
37
- const recipes = this.#fetchRecipeMetadata();
40
+ const recipes = await this.#fetchRecipeMetadata();
38
41
  // 2. 将每条 recipe 映射到维度
39
42
  const dimensionCounts = new Map();
40
- for (const def of DIMENSION_DEFS) {
43
+ for (const def of activeDims) {
41
44
  dimensionCounts.set(def.id, { count: 0, titles: [] });
42
45
  }
43
46
  let totalRecipes = 0;
@@ -53,16 +56,16 @@ export class DimensionAnalyzer {
53
56
  }
54
57
  }
55
58
  // 3. 计算各维度得分与状态
56
- const dimensions = DIMENSION_DEFS.map((def) => {
59
+ const dimensions = activeDims.map((def) => {
57
60
  const entry = dimensionCounts.get(def.id);
58
61
  return this.#scoreDimension(def, entry.count, entry.titles);
59
62
  });
60
63
  // 4. 加权平均健康分
61
64
  let weightedSum = 0;
62
65
  let weightTotal = 0;
63
- for (let i = 0; i < DIMENSION_DEFS.length; i++) {
64
- weightedSum += dimensions[i].score * DIMENSION_DEFS[i].weight;
65
- weightTotal += DIMENSION_DEFS[i].weight;
66
+ for (let i = 0; i < activeDims.length; i++) {
67
+ weightedSum += dimensions[i].score * activeDims[i].weight;
68
+ weightTotal += activeDims[i].weight;
66
69
  }
67
70
  const overallScore = weightTotal > 0 ? Math.round(weightedSum / weightTotal) : 0;
68
71
  // 5. 统计覆盖
@@ -78,23 +81,81 @@ export class DimensionAnalyzer {
78
81
  };
79
82
  // 6. 生成维度空白 (gaps)
80
83
  const roleSet = new Set(moduleRoles);
81
- const gaps = this.#detectDimensionGaps(dimensions, roleSet);
84
+ const gaps = this.#detectDimensionGaps(dimensions, activeDims, roleSet);
82
85
  return { radar, gaps };
83
86
  }
87
+ /* ─── 按项目语言解析活跃维度 ───────────────────── */
88
+ async #resolveActiveDims() {
89
+ // 1. 优先从 bootstrap_snapshots 获取 primary_lang
90
+ try {
91
+ const primaryLang = await this.#bootstrapRepo.getLatestPrimaryLang(this.#projectRoot);
92
+ if (primaryLang) {
93
+ return resolveActiveDimensions(primaryLang);
94
+ }
95
+ }
96
+ catch {
97
+ // 无 bootstrap 数据 → 继续尝试从 code_entities 推断
98
+ }
99
+ // 2. 从 code_entities 文件扩展名推断主语言
100
+ const inferredLang = await this.#inferLanguageFromEntities();
101
+ if (inferredLang) {
102
+ return resolveActiveDimensions(inferredLang);
103
+ }
104
+ return DIMENSION_REGISTRY;
105
+ }
106
+ /**
107
+ * 从 code_entities 文件扩展名统计推断项目主语言
108
+ *
109
+ * 当无 bootstrap_snapshots 时使用(如仅执行了 scan 但未 bootstrap 的项目)
110
+ */
111
+ async #inferLanguageFromEntities() {
112
+ try {
113
+ const filePaths = await this.#entityRepo.findDistinctFilePaths(this.#projectRoot, 2000);
114
+ if (filePaths.length === 0) {
115
+ return null;
116
+ }
117
+ const langCounts = new Map();
118
+ for (const fp of filePaths) {
119
+ if (!fp) {
120
+ continue;
121
+ }
122
+ const dotIdx = fp.lastIndexOf('.');
123
+ if (dotIdx < 0) {
124
+ continue;
125
+ }
126
+ const ext = fp.slice(dotIdx).toLowerCase();
127
+ const lang = LanguageService.langFromExt(ext);
128
+ if (lang !== 'unknown') {
129
+ langCounts.set(lang, (langCounts.get(lang) ?? 0) + 1);
130
+ }
131
+ }
132
+ // .h 文件可能属于 Swift/ObjC 项目 — 如果同时存在 .swift 文件则优先 swift
133
+ if (langCounts.has('swift') && langCounts.has('objectivec')) {
134
+ const swiftCount = langCounts.get('swift');
135
+ const objcCount = langCounts.get('objectivec');
136
+ if (swiftCount >= objcCount * 0.2) {
137
+ return 'swift';
138
+ }
139
+ }
140
+ // 选最多的语言
141
+ let bestLang = '';
142
+ let bestCount = 0;
143
+ for (const [lang, _count] of langCounts) {
144
+ if (_count > bestCount) {
145
+ bestLang = lang;
146
+ bestCount = _count;
147
+ }
148
+ }
149
+ return bestLang || null;
150
+ }
151
+ catch {
152
+ return null;
153
+ }
154
+ }
84
155
  /* ─── 从 DB 获取 recipe 元数据 ─────────────────── */
85
- #fetchRecipeMetadata() {
156
+ async #fetchRecipeMetadata() {
86
157
  try {
87
- const rows = this.#db
88
- .prepare(`SELECT title, category, topicHint, kind
89
- FROM knowledge_entries
90
- WHERE lifecycle IN ('active', 'pending')`)
91
- .all();
92
- return rows.map((r) => ({
93
- title: String(r.title ?? ''),
94
- category: String(r.category ?? ''),
95
- topicHint: String(r.topicHint ?? ''),
96
- kind: String(r.kind ?? ''),
97
- }));
158
+ return await this.#knowledgeRepo.findRecipeMetadata(COUNTABLE_LIFECYCLES);
98
159
  }
99
160
  catch {
100
161
  return [];
@@ -153,11 +214,11 @@ export class DimensionAnalyzer {
153
214
  };
154
215
  }
155
216
  /* ─── 维度空白检测 ─────────────────────────────── */
156
- #detectDimensionGaps(dimensions, moduleRoles) {
217
+ #detectDimensionGaps(dimensions, activeDims, moduleRoles) {
157
218
  const gaps = [];
158
219
  for (let i = 0; i < dimensions.length; i++) {
159
220
  const dim = dimensions[i];
160
- const def = DIMENSION_DEFS[i];
221
+ const def = activeDims[i];
161
222
  if (dim.status !== 'missing' && dim.status !== 'weak') {
162
223
  continue;
163
224
  }
@@ -4,9 +4,23 @@
4
4
  * 基于模块依赖图,通过去环 + 拓扑排序 + 最长路径法推断架构层级 (L0-Ln)。
5
5
  * 底层 (L0) = Foundation/Core,顶层 = App/UI。
6
6
  *
7
+ * 当配置文件声明了明确的层级结构时(如 Boxfile 的 layer 定义),
8
+ * 优先使用配置层级,仅对未覆盖的模块做拓扑推断。
9
+ *
7
10
  * @module LayerInferrer
8
11
  */
9
12
  import type { CyclicDependency, Edge, LayerHierarchy } from './PanoramaTypes.js';
13
+ export interface ConfigLayer {
14
+ name: string;
15
+ order: number;
16
+ accessibleLayers: string[];
17
+ }
18
+ export interface InferOptions {
19
+ /** 来自配置文件的层级定义(如 Boxfile、Project.swift 等) */
20
+ configLayers?: ConfigLayer[] | null;
21
+ /** 模块 → 配置层级名映射 */
22
+ moduleLayerMap?: Map<string, string>;
23
+ }
10
24
  export declare class LayerInferrer {
11
25
  #private;
12
26
  /**
@@ -14,6 +28,7 @@ export declare class LayerInferrer {
14
28
  * @param edges - 模块间依赖边 (from depends_on/calls/data_flow to)
15
29
  * @param modules - 所有模块名
16
30
  * @param cycles - 已检测到的循环依赖
31
+ * @param options - 可选配置:configLayers(配置声明的层级)和 moduleLayerMap(模块→层级映射)
17
32
  */
18
- infer(edges: Edge[], modules: string[], cycles: CyclicDependency[]): LayerHierarchy;
33
+ infer(edges: Edge[], modules: string[], cycles: CyclicDependency[], options?: InferOptions): LayerHierarchy;
19
34
  }
@@ -4,6 +4,9 @@
4
4
  * 基于模块依赖图,通过去环 + 拓扑排序 + 最长路径法推断架构层级 (L0-Ln)。
5
5
  * 底层 (L0) = Foundation/Core,顶层 = App/UI。
6
6
  *
7
+ * 当配置文件声明了明确的层级结构时(如 Boxfile 的 layer 定义),
8
+ * 优先使用配置层级,仅对未覆盖的模块做拓扑推断。
9
+ *
7
10
  * @module LayerInferrer
8
11
  */
9
12
  /* ═══ Constants ═══════════════════════════════════════════ */
@@ -26,8 +29,122 @@ export class LayerInferrer {
26
29
  * @param edges - 模块间依赖边 (from depends_on/calls/data_flow to)
27
30
  * @param modules - 所有模块名
28
31
  * @param cycles - 已检测到的循环依赖
32
+ * @param options - 可选配置:configLayers(配置声明的层级)和 moduleLayerMap(模块→层级映射)
33
+ */
34
+ infer(edges, modules, cycles, options) {
35
+ // 如果有配置文件声明的层级且覆盖了大部分模块,优先使用
36
+ if (options?.configLayers?.length && options.moduleLayerMap?.size) {
37
+ const coverage = this.#computeConfigCoverage(modules, options.moduleLayerMap);
38
+ if (coverage >= 0.5) {
39
+ return this.#inferFromConfig(edges, modules, options.configLayers, options.moduleLayerMap);
40
+ }
41
+ }
42
+ return this.#inferFromTopology(edges, modules, cycles);
43
+ }
44
+ /* ─── Config-based layer inference ──────────────── */
45
+ /**
46
+ * 基于配置文件声明的层级直接分配
47
+ * 未被配置覆盖的模块附加到最近匹配的层级
29
48
  */
30
- infer(edges, modules, cycles) {
49
+ #inferFromConfig(edges, modules, configLayers, moduleLayerMap) {
50
+ // 按 order 排序层级
51
+ const sortedLayers = [...configLayers].sort((a, b) => a.order - b.order);
52
+ // 构建层级名 → level 映射 (底层 = L0, 反序: order 最大的是底层)
53
+ // 配置中 order=0 是最高层 (如 Accessories), order=N 是最底层 (如 Vendors)
54
+ const maxOrder = sortedLayers.length > 0 ? sortedLayers[sortedLayers.length - 1].order : 0;
55
+ const layerNameToLevel = new Map();
56
+ for (const cl of sortedLayers) {
57
+ // 反转: 配置中 order 越大越底层 → level 越小
58
+ layerNameToLevel.set(cl.name, maxOrder - cl.order);
59
+ }
60
+ // 分配每个模块到层级
61
+ const moduleLevels = new Map();
62
+ const uncovered = [];
63
+ for (const mod of modules) {
64
+ const layerName = moduleLayerMap.get(mod);
65
+ if (layerName && layerNameToLevel.has(layerName)) {
66
+ moduleLevels.set(mod, layerNameToLevel.get(layerName));
67
+ }
68
+ else {
69
+ uncovered.push(mod);
70
+ }
71
+ }
72
+ // 未覆盖的模块: 基于依赖关系推断最可能的层级
73
+ for (const mod of uncovered) {
74
+ const depEdges = edges.filter((e) => e.from === mod);
75
+ let bestLevel = maxOrder + 1; // 默认最高层
76
+ for (const e of depEdges) {
77
+ const targetLevel = moduleLevels.get(e.to);
78
+ if (targetLevel !== undefined) {
79
+ bestLevel = Math.min(bestLevel, targetLevel + 1);
80
+ }
81
+ }
82
+ // 如果没有依赖线索, 查看谁依赖它
83
+ if (bestLevel > maxOrder) {
84
+ const reverseEdges = edges.filter((e) => e.to === mod);
85
+ for (const e of reverseEdges) {
86
+ const sourceLevel = moduleLevels.get(e.from);
87
+ if (sourceLevel !== undefined) {
88
+ bestLevel = Math.min(bestLevel, Math.max(0, sourceLevel - 1));
89
+ }
90
+ }
91
+ }
92
+ // 兜底: 放到最高层
93
+ if (bestLevel > maxOrder) {
94
+ bestLevel = maxOrder;
95
+ }
96
+ moduleLevels.set(mod, bestLevel);
97
+ }
98
+ // 聚合: 同层模块分组
99
+ const layerGroups = new Map();
100
+ for (const [mod, level] of moduleLevels) {
101
+ if (!layerGroups.has(level)) {
102
+ layerGroups.set(level, []);
103
+ }
104
+ layerGroups.get(level).push(mod);
105
+ }
106
+ // 构建 levelEntries, 使用配置中的层级名
107
+ const levelToConfigName = new Map();
108
+ for (const cl of sortedLayers) {
109
+ levelToConfigName.set(maxOrder - cl.order, cl.name);
110
+ }
111
+ const sortedLevels = [...layerGroups.entries()].sort((a, b) => a[0] - b[0]);
112
+ const levelEntries = sortedLevels.map(([level, mods]) => ({
113
+ level,
114
+ name: levelToConfigName.get(level) ?? this.#inferLayerName(mods, level, sortedLevels.length),
115
+ modules: mods.sort(),
116
+ }));
117
+ // 检测层级违规(基于 access 规则)
118
+ const violations = [];
119
+ for (const edge of edges) {
120
+ const fromLevel = moduleLevels.get(edge.from);
121
+ const toLevel = moduleLevels.get(edge.to);
122
+ if (fromLevel !== undefined && toLevel !== undefined && fromLevel < toLevel) {
123
+ violations.push({
124
+ from: edge.from,
125
+ to: edge.to,
126
+ fromLayer: fromLevel,
127
+ toLayer: toLevel,
128
+ relation: edge.relation,
129
+ });
130
+ }
131
+ }
132
+ return { levels: levelEntries, violations, configBased: true };
133
+ }
134
+ #computeConfigCoverage(modules, moduleLayerMap) {
135
+ if (modules.length === 0) {
136
+ return 0;
137
+ }
138
+ let covered = 0;
139
+ for (const mod of modules) {
140
+ if (moduleLayerMap.has(mod)) {
141
+ covered++;
142
+ }
143
+ }
144
+ return covered / modules.length;
145
+ }
146
+ /* ─── Topology-based layer inference ────────────── */
147
+ #inferFromTopology(edges, modules, cycles) {
31
148
  // 1. 建图 (邻接表: from → to[])
32
149
  const adjacency = new Map();
33
150
  const reverseAdj = new Map();
@@ -11,14 +11,24 @@
11
11
  *
12
12
  * @module ModuleDiscoverer
13
13
  */
14
- import type { CeDbLike } from './PanoramaTypes.js';
14
+ import type { CodeEntityRepositoryImpl } from '../../repository/code/CodeEntityRepository.js';
15
+ import type { KnowledgeEdgeRepositoryImpl } from '../../repository/knowledge/KnowledgeEdgeRepository.js';
15
16
  import type { ModuleCandidate } from './RoleRefiner.js';
16
17
  export declare class ModuleDiscoverer {
17
18
  #private;
18
- constructor(db: CeDbLike, projectRoot: string);
19
+ constructor(entityRepo: CodeEntityRepositoryImpl, edgeRepo: KnowledgeEdgeRepositoryImpl, projectRoot: string);
19
20
  /**
20
21
  * 从 DB 中读取已扫描的模块数据。
21
- * 若无 module 实体,返回空数组(让调用侧决定是否重新扫描)。
22
+ * 若无 module 实体(含 host),返回空数组(让调用侧决定是否重新扫描)。
22
23
  */
23
- discover(): ModuleCandidate[];
24
+ discover(): Promise<ModuleCandidate[]>;
25
+ /**
26
+ * 读取 config layers 元数据(如果存在)
27
+ * @returns 从 `__config_layers__` 实体中恢复的层级定义
28
+ */
29
+ readConfigLayers(): Promise<Array<{
30
+ name: string;
31
+ order: number;
32
+ accessibleLayers: string[];
33
+ }> | null>;
24
34
  }