autosnippet 3.3.7 → 3.3.9

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 (218) hide show
  1. package/README.md +1 -0
  2. package/dashboard/dist/assets/icons-BMNb0V6L.js +1 -0
  3. package/dashboard/dist/assets/index-DEU4tJtP.js +112 -0
  4. package/dashboard/dist/assets/index-DHJ1Dj7u.css +1 -0
  5. package/dashboard/dist/index.html +3 -3
  6. package/dist/bin/cli.js +7 -4
  7. package/dist/lib/agent/core/ChatAgentPrompts.js +57 -21
  8. package/dist/lib/agent/core/LoopContext.d.ts +1 -0
  9. package/dist/lib/agent/core/ToolExecutionPipeline.js +13 -0
  10. package/dist/lib/agent/memory/ActiveContext.d.ts +0 -2
  11. package/dist/lib/agent/memory/ActiveContext.js +0 -2
  12. package/dist/lib/agent/memory/MemoryEmbeddingStore.d.ts +49 -0
  13. package/dist/lib/agent/memory/MemoryEmbeddingStore.js +159 -0
  14. package/dist/lib/agent/memory/MemoryRetriever.d.ts +2 -0
  15. package/dist/lib/agent/memory/MemoryRetriever.js +25 -11
  16. package/dist/lib/agent/memory/MemoryStore.d.ts +8 -41
  17. package/dist/lib/agent/memory/MemoryStore.js +196 -261
  18. package/dist/lib/agent/memory/PersistentMemory.d.ts +2 -0
  19. package/dist/lib/agent/memory/PersistentMemory.js +4 -5
  20. package/dist/lib/agent/memory/SessionStore.d.ts +0 -2
  21. package/dist/lib/agent/memory/SessionStore.js +0 -2
  22. package/dist/lib/agent/tools/ast-graph.js +21 -19
  23. package/dist/lib/agent/tools/infrastructure.js +3 -2
  24. package/dist/lib/agent/tools/project-access.d.ts +2 -2
  25. package/dist/lib/agent/tools/project-access.js +5 -4
  26. package/dist/lib/bootstrap.js +8 -2
  27. package/dist/lib/cli/AiScanService.js +4 -17
  28. package/dist/lib/cli/KnowledgeSyncService.d.ts +7 -37
  29. package/dist/lib/cli/KnowledgeSyncService.js +23 -51
  30. package/dist/lib/cli/SetupService.js +5 -4
  31. package/dist/lib/core/ast/ProjectGraph.js +5 -27
  32. package/dist/lib/core/discovery/CustomConfigDiscoverer.d.ts +0 -2
  33. package/dist/lib/core/discovery/CustomConfigDiscoverer.js +0 -2
  34. package/dist/lib/domain/dimension/DimensionRegistry.d.ts +0 -2
  35. package/dist/lib/domain/dimension/DimensionRegistry.js +0 -2
  36. package/dist/lib/domain/dimension/DimensionSop.js +44 -33
  37. package/dist/lib/domain/dimension/UnifiedDimension.d.ts +0 -2
  38. package/dist/lib/domain/dimension/UnifiedDimension.js +0 -2
  39. package/dist/lib/domain/knowledge/Lifecycle.d.ts +26 -0
  40. package/dist/lib/domain/knowledge/Lifecycle.js +42 -0
  41. package/dist/lib/domain/knowledge/index.d.ts +2 -1
  42. package/dist/lib/domain/knowledge/index.js +1 -1
  43. package/dist/lib/external/mcp/McpServer.js +19 -2
  44. package/dist/lib/external/mcp/handlers/bootstrap/pipeline/BootstrapSnapshot.d.ts +2 -1
  45. package/dist/lib/external/mcp/handlers/bootstrap/pipeline/BootstrapSnapshot.js +102 -153
  46. package/dist/lib/external/mcp/handlers/bootstrap/pipeline/mock-pipeline.js +10 -29
  47. package/dist/lib/external/mcp/handlers/bootstrap/pipeline/orchestrator.js +33 -16
  48. package/dist/lib/external/mcp/handlers/bootstrap/shared/bootstrap-phases.d.ts +1 -1
  49. package/dist/lib/external/mcp/handlers/bootstrap/shared/bootstrap-phases.js +41 -37
  50. package/dist/lib/external/mcp/handlers/bootstrap-external.js +1 -1
  51. package/dist/lib/external/mcp/handlers/dimension-complete-external.js +7 -3
  52. package/dist/lib/external/mcp/handlers/evolve-external.d.ts +1 -0
  53. package/dist/lib/external/mcp/handlers/evolve-external.js +13 -16
  54. package/dist/lib/external/mcp/handlers/guard.js +15 -24
  55. package/dist/lib/external/mcp/handlers/panorama.js +9 -9
  56. package/dist/lib/external/mcp/handlers/rescan-external.js +7 -6
  57. package/dist/lib/external/mcp/handlers/rescan-internal.js +9 -5
  58. package/dist/lib/external/mcp/handlers/search.js +3 -1
  59. package/dist/lib/external/mcp/handlers/skill.js +4 -4
  60. package/dist/lib/external/mcp/handlers/structure.js +8 -12
  61. package/dist/lib/external/mcp/handlers/system.js +10 -34
  62. package/dist/lib/http/routes/ai.js +11 -13
  63. package/dist/lib/http/routes/guardReport.js +3 -5
  64. package/dist/lib/http/routes/panorama.js +12 -12
  65. package/dist/lib/http/routes/recipes.js +59 -8
  66. package/dist/lib/http/routes/remote.js +3 -13
  67. package/dist/lib/http/routes/search.js +11 -8
  68. package/dist/lib/infrastructure/audit/AuditLogger.d.ts +20 -3
  69. package/dist/lib/infrastructure/audit/AuditStore.d.ts +28 -29
  70. package/dist/lib/infrastructure/audit/AuditStore.js +81 -88
  71. package/dist/lib/infrastructure/database/DatabaseConnection.js +7 -6
  72. package/dist/lib/infrastructure/database/drizzle/schema.d.ts +180 -2
  73. package/dist/lib/infrastructure/database/drizzle/schema.js +23 -3
  74. package/dist/lib/injection/ServiceContainer.js +7 -4
  75. package/dist/lib/injection/ServiceMap.d.ts +20 -0
  76. package/dist/lib/injection/modules/AppModule.js +2 -1
  77. package/dist/lib/injection/modules/GuardModule.js +5 -5
  78. package/dist/lib/injection/modules/InfraModule.js +60 -0
  79. package/dist/lib/injection/modules/KnowledgeModule.js +86 -51
  80. package/dist/lib/injection/modules/PanoramaModule.js +16 -10
  81. package/dist/lib/injection/modules/VectorModule.js +3 -0
  82. package/dist/lib/repository/audit/AuditRepository.d.ts +107 -0
  83. package/dist/lib/repository/audit/AuditRepository.js +272 -0
  84. package/dist/lib/repository/base/RepositoryBase.d.ts +46 -0
  85. package/dist/lib/repository/base/RepositoryBase.js +32 -0
  86. package/dist/lib/repository/bootstrap/BootstrapRepository.d.ts +94 -0
  87. package/dist/lib/repository/bootstrap/BootstrapRepository.js +246 -0
  88. package/dist/lib/repository/code/CodeEntityRepository.d.ts +91 -0
  89. package/dist/lib/repository/code/CodeEntityRepository.js +361 -0
  90. package/dist/lib/repository/delivery/DeliveryRepoAdapter.d.ts +39 -0
  91. package/dist/lib/repository/delivery/DeliveryRepoAdapter.js +23 -0
  92. package/dist/lib/repository/evolution/LifecycleEventRepository.d.ts +51 -0
  93. package/dist/lib/repository/evolution/LifecycleEventRepository.js +119 -0
  94. package/dist/lib/repository/evolution/ProposalRepository.d.ts +9 -12
  95. package/dist/lib/repository/evolution/ProposalRepository.js +114 -57
  96. package/dist/lib/repository/guard/GuardViolationRepository.d.ts +104 -0
  97. package/dist/lib/repository/guard/GuardViolationRepository.js +217 -0
  98. package/dist/lib/repository/knowledge/KnowledgeEdgeRepository.d.ts +129 -0
  99. package/dist/lib/repository/knowledge/KnowledgeEdgeRepository.js +475 -0
  100. package/dist/lib/repository/knowledge/KnowledgeFileStore.d.ts +39 -0
  101. package/dist/lib/repository/knowledge/KnowledgeFileStore.js +12 -0
  102. package/dist/lib/repository/knowledge/KnowledgeRepository.impl.d.ts +295 -11
  103. package/dist/lib/repository/knowledge/KnowledgeRepository.impl.js +608 -13
  104. package/dist/lib/repository/knowledge/KnowledgeUnitOfWork.d.ts +61 -0
  105. package/dist/lib/repository/knowledge/KnowledgeUnitOfWork.js +156 -0
  106. package/dist/lib/repository/memory/MemoryRepository.d.ts +90 -0
  107. package/dist/lib/repository/memory/MemoryRepository.js +260 -0
  108. package/dist/lib/repository/search/SearchRepoAdapter.d.ts +92 -0
  109. package/dist/lib/repository/search/SearchRepoAdapter.js +124 -0
  110. package/dist/lib/repository/session/SessionRepository.d.ts +46 -0
  111. package/dist/lib/repository/session/SessionRepository.js +110 -0
  112. package/dist/lib/repository/sourceref/RecipeSourceRefRepository.d.ts +66 -0
  113. package/dist/lib/repository/sourceref/RecipeSourceRefRepository.js +182 -0
  114. package/dist/lib/repository/sync/SyncRepoAdapter.d.ts +58 -0
  115. package/dist/lib/repository/sync/SyncRepoAdapter.js +58 -0
  116. package/dist/lib/service/bootstrap/UiStartupTasks.js +5 -6
  117. package/dist/lib/service/bootstrap/bootstrap-event-types.d.ts +0 -1
  118. package/dist/lib/service/bootstrap/bootstrap-event-types.js +0 -1
  119. package/dist/lib/service/cleanup/CleanupService.js +8 -4
  120. package/dist/lib/service/delivery/CursorDeliveryPipeline.js +21 -9
  121. package/dist/lib/service/evolution/ConsolidationAdvisor.d.ts +4 -9
  122. package/dist/lib/service/evolution/ConsolidationAdvisor.js +34 -70
  123. package/dist/lib/service/evolution/ContentPatcher.d.ts +4 -12
  124. package/dist/lib/service/evolution/ContentPatcher.js +48 -19
  125. package/dist/lib/service/evolution/ContradictionDetector.d.ts +3 -7
  126. package/dist/lib/service/evolution/ContradictionDetector.js +17 -24
  127. package/dist/lib/service/evolution/DecayDetector.d.ts +10 -9
  128. package/dist/lib/service/evolution/DecayDetector.js +63 -57
  129. package/dist/lib/service/evolution/EnhancementSuggester.d.ts +3 -9
  130. package/dist/lib/service/evolution/EnhancementSuggester.js +42 -86
  131. package/dist/lib/service/evolution/KnowledgeMetabolism.d.ts +4 -4
  132. package/dist/lib/service/evolution/KnowledgeMetabolism.js +102 -71
  133. package/dist/lib/service/evolution/ProposalExecutor.d.ts +5 -12
  134. package/dist/lib/service/evolution/ProposalExecutor.js +64 -69
  135. package/dist/lib/service/evolution/RecipeLifecycleSupervisor.d.ts +9 -14
  136. package/dist/lib/service/evolution/RecipeLifecycleSupervisor.js +94 -155
  137. package/dist/lib/service/evolution/RecipeRelevanceAuditor.d.ts +4 -1
  138. package/dist/lib/service/evolution/RecipeRelevanceAuditor.js +50 -49
  139. package/dist/lib/service/evolution/RedundancyAnalyzer.d.ts +3 -7
  140. package/dist/lib/service/evolution/RedundancyAnalyzer.js +15 -22
  141. package/dist/lib/service/evolution/StagingManager.d.ts +6 -15
  142. package/dist/lib/service/evolution/StagingManager.js +37 -95
  143. package/dist/lib/service/evolution/createSupersedeProposal.d.ts +1 -1
  144. package/dist/lib/service/evolution/createSupersedeProposal.js +7 -8
  145. package/dist/lib/service/guard/CoverageAnalyzer.d.ts +3 -7
  146. package/dist/lib/service/guard/CoverageAnalyzer.js +9 -11
  147. package/dist/lib/service/guard/GuardCheckEngine.d.ts +3 -0
  148. package/dist/lib/service/guard/GuardCheckEngine.js +14 -22
  149. package/dist/lib/service/guard/ReverseGuard.d.ts +4 -7
  150. package/dist/lib/service/guard/ReverseGuard.js +21 -31
  151. package/dist/lib/service/guard/ViolationsStore.d.ts +15 -21
  152. package/dist/lib/service/guard/ViolationsStore.js +75 -69
  153. package/dist/lib/service/knowledge/CodeEntityGraph.d.ts +39 -63
  154. package/dist/lib/service/knowledge/CodeEntityGraph.js +418 -512
  155. package/dist/lib/service/knowledge/ConfidenceRouter.js +18 -9
  156. package/dist/lib/service/knowledge/KnowledgeFileWriter.d.ts +2 -1
  157. package/dist/lib/service/knowledge/KnowledgeGraphService.d.ts +18 -60
  158. package/dist/lib/service/knowledge/KnowledgeGraphService.js +58 -109
  159. package/dist/lib/service/knowledge/KnowledgeService.d.ts +15 -1
  160. package/dist/lib/service/knowledge/KnowledgeService.js +76 -38
  161. package/dist/lib/service/knowledge/RecipeProductionGateway.d.ts +0 -2
  162. package/dist/lib/service/knowledge/RecipeProductionGateway.js +0 -2
  163. package/dist/lib/service/knowledge/SourceRefReconciler.d.ts +5 -13
  164. package/dist/lib/service/knowledge/SourceRefReconciler.js +58 -78
  165. package/dist/lib/service/panorama/CouplingAnalyzer.d.ts +5 -3
  166. package/dist/lib/service/panorama/CouplingAnalyzer.js +102 -39
  167. package/dist/lib/service/panorama/DimensionAnalyzer.d.ts +7 -4
  168. package/dist/lib/service/panorama/DimensionAnalyzer.js +72 -25
  169. package/dist/lib/service/panorama/LayerInferrer.js +1 -1
  170. package/dist/lib/service/panorama/ModuleDiscoverer.d.ts +7 -6
  171. package/dist/lib/service/panorama/ModuleDiscoverer.js +174 -82
  172. package/dist/lib/service/panorama/PanoramaAggregator.d.ts +10 -3
  173. package/dist/lib/service/panorama/PanoramaAggregator.js +67 -79
  174. package/dist/lib/service/panorama/PanoramaScanner.d.ts +5 -1
  175. package/dist/lib/service/panorama/PanoramaScanner.js +32 -31
  176. package/dist/lib/service/panorama/PanoramaService.d.ts +11 -8
  177. package/dist/lib/service/panorama/PanoramaService.js +41 -66
  178. package/dist/lib/service/panorama/PanoramaTypes.d.ts +3 -0
  179. package/dist/lib/service/panorama/RoleRefiner.d.ts +8 -5
  180. package/dist/lib/service/panorama/RoleRefiner.js +52 -283
  181. package/dist/lib/service/panorama/TechStackProfiler.js +7 -119
  182. package/dist/lib/service/quality/QualityScorer.d.ts +45 -26
  183. package/dist/lib/service/quality/QualityScorer.js +157 -83
  184. package/dist/lib/service/search/SearchEngine.d.ts +1 -0
  185. package/dist/lib/service/search/SearchEngine.js +32 -37
  186. package/dist/lib/service/signal/HitRecorder.js +5 -5
  187. package/dist/lib/service/skills/RuleRecallStrategy.js +7 -3
  188. package/dist/lib/service/skills/SignalCollector.d.ts +5 -8
  189. package/dist/lib/service/skills/SignalCollector.js +28 -55
  190. package/dist/lib/service/skills/SkillAdvisor.d.ts +7 -13
  191. package/dist/lib/service/skills/SkillAdvisor.js +30 -79
  192. package/dist/lib/service/vector/SyncCoordinator.d.ts +3 -1
  193. package/dist/lib/service/vector/SyncCoordinator.js +25 -3
  194. package/dist/lib/service/vector/VectorService.d.ts +2 -0
  195. package/dist/lib/service/vector/VectorService.js +3 -0
  196. package/dist/lib/service/wiki/WikiGenerator.js +1 -1
  197. package/dist/lib/shared/LanguageProfiles.d.ts +109 -0
  198. package/dist/lib/shared/LanguageProfiles.js +939 -0
  199. package/dist/lib/shared/LanguageService.d.ts +6 -0
  200. package/dist/lib/shared/LanguageService.js +16 -0
  201. package/dist/lib/shared/PathGuard.js +16 -10
  202. package/dist/lib/shared/constants.d.ts +19 -19
  203. package/dist/lib/shared/constants.js +10 -10
  204. package/dist/lib/shared/isOwnDevRepo.d.ts +29 -4
  205. package/dist/lib/shared/isOwnDevRepo.js +64 -4
  206. package/dist/lib/shared/schemas/mcp-tools.d.ts +1 -1
  207. package/dist/lib/types/project-snapshot-builder.d.ts +0 -1
  208. package/dist/lib/types/project-snapshot-builder.js +0 -1
  209. package/dist/lib/types/project-snapshot.d.ts +0 -1
  210. package/dist/lib/types/project-snapshot.js +0 -1
  211. package/dist/lib/types/snapshot-views.d.ts +0 -2
  212. package/dist/lib/types/snapshot-views.js +0 -1
  213. package/package.json +2 -1
  214. package/dashboard/dist/assets/icons-FHns2ypa.js +0 -1
  215. package/dashboard/dist/assets/index-BRJv5Y3r.js +0 -135
  216. package/dashboard/dist/assets/index-DzoB7kxK.css +0 -1
  217. package/dist/lib/repository/base/BaseRepository.d.ts +0 -53
  218. 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
- const SOURCE_EXTS = new Set([
19
- '.swift',
20
- '.ts',
21
- '.tsx',
22
- '.js',
23
- '.jsx',
24
- '.m',
25
- '.mm',
26
- '.h',
27
- '.c',
28
- '.cpp',
29
- '.kt',
30
- '.java',
31
- '.py',
32
- '.rb',
33
- '.go',
34
- '.rs',
35
- ]);
36
- const SKIP_DIRS = new Set([
37
- '.git',
38
- '.build',
39
- '.autosnippet',
40
- 'node_modules',
41
- 'build',
42
- 'Pods',
43
- 'DerivedData',
44
- '.swiftpm',
45
- '__pycache__',
46
- 'dist',
47
- ]);
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
- #db;
27
+ #entityRepo;
28
+ #edgeRepo;
51
29
  #projectRoot;
52
- constructor(db, projectRoot) {
53
- this.#db = db;
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.#db
63
- .prepare(`SELECT DISTINCT entity_id, name FROM code_entities
64
- WHERE entity_type = 'module' AND project_root = ?
65
- AND COALESCE(json_extract(metadata_json, '$.nodeType'), 'local') NOT IN ('external', 'host')`)
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.entity_id;
50
+ const moduleName = me.entityId;
74
51
  moduleFiles.set(moduleName, new Set());
75
- const parts = this.#db
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.#db
81
- .prepare(`SELECT file_path FROM code_entities
82
- WHERE entity_id = ? AND project_root = ? LIMIT 1`)
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
- return [...moduleFiles.entries()].map(([name, files]) => ({
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 row = this.#db
110
- .prepare(`SELECT metadata_json FROM code_entities
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 = typeof row.metadata_json === 'string' ? JSON.parse(row.metadata_json) : row.metadata_json;
87
+ const meta = entity.metadata;
118
88
  if (Array.isArray(meta.layers) && meta.layers.length > 0) {
119
- return meta.layers;
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.entity_id;
117
+ const moduleName = me.entityId;
135
118
  try {
136
- const row = this.#db
137
- .prepare(`SELECT metadata_json FROM code_entities
138
- WHERE entity_id = ? AND entity_type = 'module' AND project_root = ?
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.#db
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 row of allFiles) {
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 { CeDbLike, PanoramaResult } from './PanoramaTypes.js';
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
- db: CeDbLike;
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
- #db;
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.#db = opts.db;
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 ?? new DimensionAnalyzer(opts.db, opts.projectRoot);
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 基于模块角色重命名层级(比模块名 pattern 更准确)
93
- this.#renameLayersByRole(layers, panoramaModules);
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
- const row = this.#db
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.#db
137
- .prepare(`SELECT entity_id FROM code_entities
138
- WHERE entity_type = 'module' AND project_root = ?
139
- AND json_extract(metadata_json, '$.nodeType') IN ('external', 'host')`)
140
- .all(this.#projectRoot);
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 row = this.#db
160
- .prepare(`SELECT metadata_json FROM code_entities
161
- WHERE entity_id = ? AND entity_type = 'module' AND project_root = ?
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
- // 去重:已使用的名称追加 level 号
243
+ // 去重:优先尝试次高票角色名,仍冲突则追加位置描述
247
244
  if (usedNames.has(layerName)) {
248
- layerName = `${layerName} ${level.level}`;
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.#db
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.#db
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.#db
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.#db
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.to_id,
298
- callCount: Number(r.call_count),
285
+ id: r.toId,
286
+ callCount: r.callCount,
299
287
  })),
300
- entryPoints: entryPoints.map((r) => r.from_id),
301
- dataProducers: dataProducers.map((r) => r.from_id),
302
- dataConsumers: dataConsumers.map((r) => r.to_id),
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 可重新触发)。