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