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
@@ -14,9 +14,11 @@ import { ConflictError, NotFoundError, ValidationError } from '../../shared/erro
14
14
  */
15
15
  export class KnowledgeService {
16
16
  _confidenceRouter;
17
+ _edgeRepo;
17
18
  _eventBus;
18
19
  _fileWriter;
19
20
  _knowledgeGraphService;
21
+ _proposalRepo;
20
22
  _qualityScorer;
21
23
  _skillHooks;
22
24
  auditLogger;
@@ -33,6 +35,8 @@ export class KnowledgeService {
33
35
  this._confidenceRouter = options.confidenceRouter || null;
34
36
  this._qualityScorer = options.qualityScorer || null;
35
37
  this._eventBus = options.eventBus || null;
38
+ this._edgeRepo = options.edgeRepo || null;
39
+ this._proposalRepo = options.proposalRepo || null;
36
40
  this.logger = Logger.getInstance();
37
41
  }
38
42
  /* ═══ CRUD ══════════════════════════════════════════════ */
@@ -93,13 +97,17 @@ export class KnowledgeService {
93
97
  // 注意: staging 条目由 StagingManager.checkAndPromote() 在到期后自动转为 active。
94
98
  // autoApprovable 标记保留,供前端显示「推荐批准」徽章。
95
99
  // CursorDelivery 已支持高置信度 staging/pending 条目的交付。
100
+ // ── file-first: 先落盘 .md,再写 DB(文件=真相源) ──
101
+ // fileWriter.persist() 会设置 entry.sourceFile,
102
+ // 后续 repository.create() 自动包含 sourceFile 字段,无需异步回写。
103
+ if (this._fileWriter) {
104
+ this._fileWriter.persist(entry);
105
+ }
96
106
  const saved = await this.repository.create(entry);
97
107
  // 同步 relations → knowledge_edges
98
108
  this._syncRelationsToGraph(saved.id, saved.relations);
99
109
  // 自动发现同域条目建立 related 边(best effort, 不阻塞)
100
110
  this._autoDiscoverRelations(saved.id, saved).catch((err) => this.logger.warn('_autoDiscoverRelations error', { id: saved.id, error: err.message }));
101
- // 落盘 .md 文件
102
- this._persistToFile(saved);
103
111
  // 审计日志
104
112
  await this._audit('create_knowledge', saved.id, context.userId, {
105
113
  title: saved.title,
@@ -237,13 +245,20 @@ export class KnowledgeService {
237
245
  throw new ValidationError('No updatable fields provided');
238
246
  }
239
247
  dbUpdates.updatedAt = Math.floor(Date.now() / 1000);
248
+ // ── file-first: 先落盘 .md,再写 DB(文件=真相源) ──
249
+ if (this._fileWriter) {
250
+ Object.assign(_entry, dbUpdates);
251
+ this._fileWriter.persist(_entry);
252
+ // fileWriter 可能更新 sourceFile,同步到 dbUpdates
253
+ if (_entry.sourceFile) {
254
+ dbUpdates.sourceFile = _entry.sourceFile;
255
+ }
256
+ }
240
257
  const updated = await this.repository.update(id, dbUpdates);
241
258
  // 若 relations 变更,同步到 knowledge_edges
242
259
  if (dbUpdates.relations) {
243
260
  this._syncRelationsToGraph(id, data.relations);
244
261
  }
245
- // 落盘
246
- this._persistToFile(updated);
247
262
  await this._audit('update_knowledge', id, context.userId, {
248
263
  fields: Object.keys(dbUpdates),
249
264
  });
@@ -520,17 +535,40 @@ export class KnowledgeService {
520
535
  // 为 QualityScorer 适配输入字段
521
536
  const scorerInput = this._adaptForScorer(entry);
522
537
  const result = this._qualityScorer.score(scorerInput);
523
- // 更新 Quality 值对象
524
- await this.repository.update(id, {
525
- quality: JSON.stringify({
526
- completeness: result.dimensions.completeness,
527
- adaptation: result.dimensions.metadata,
528
- documentation: result.dimensions.format,
529
- overall: result.score,
530
- grade: result.grade,
531
- }),
538
+ // 更新 Quality 值对象;同步计算 authority(0‑5)
539
+ const qualityJson = {
540
+ completeness: result.dimensions.completeness,
541
+ adaptation: result.dimensions.deliveryReady,
542
+ documentation: result.dimensions.contentDepth,
543
+ overall: result.score,
544
+ grade: result.grade,
545
+ };
546
+ // 当 authority 从未手动设置(仍为 0)时,从 quality.overall 自动推导
547
+ const currentAuthority = entry.stats?.authority ?? 0;
548
+ const updatePayload = {
549
+ quality: JSON.stringify(qualityJson),
532
550
  updatedAt: Math.floor(Date.now() / 1000),
533
- });
551
+ };
552
+ if (currentAuthority === 0 && result.score > 0) {
553
+ const statsObj = entry.stats?.toJSON?.() ?? (typeof entry.stats === 'object' ? { ...entry.stats } : {});
554
+ updatePayload.stats = JSON.stringify({
555
+ ...statsObj,
556
+ authority: Math.round(result.score * 5),
557
+ });
558
+ }
559
+ await this.repository.update(id, updatePayload);
560
+ // ── .md 文件同步: quality 更新后重新落盘,保持文件=真相源 ──
561
+ if (this._fileWriter) {
562
+ try {
563
+ const updated = await this.repository.findById(id);
564
+ if (updated) {
565
+ this._fileWriter.persist(updated);
566
+ }
567
+ }
568
+ catch {
569
+ /* best effort — 不阻塞质量更新流程 */
570
+ }
571
+ }
534
572
  if (context.userId) {
535
573
  await this._audit('update_knowledge_quality', id, context.userId, {
536
574
  score: result.score,
@@ -565,6 +603,8 @@ export class KnowledgeService {
565
603
  detail: `Lifecycle ${method} failed for ${id}`,
566
604
  });
567
605
  }
606
+ // 标记操作人到最后一条 lifecycleHistory 条目
607
+ entry.stampLastTransition(context.userId);
568
608
  // 构建 DB 更新
569
609
  // 注意: 不在此处 JSON.stringify — repository.update() 内部
570
610
  // 通过 _entityToRow() 统一执行序列化, 传入原始值即可
@@ -592,19 +632,11 @@ export class KnowledgeService {
592
632
  if (entry.autoApprovable !== undefined) {
593
633
  dbUpdates.autoApprovable = entry.autoApprovable ? 1 : 0;
594
634
  }
595
- const updated = await this.repository.update(id, dbUpdates);
596
- // 文件位置迁移(candidate ↔ recipe 目录)
635
+ // ── file-first: 先迁移 .md 文件,再更新 DB lifecycle(文件=真相源) ──
597
636
  if (this._fileWriter) {
598
- try {
599
- this._fileWriter.moveOnLifecycleChange(updated);
600
- }
601
- catch (err) {
602
- this.logger.warn('moveOnLifecycleChange failed (non-blocking)', {
603
- id,
604
- error: err instanceof Error ? err.message : String(err),
605
- });
606
- }
637
+ this._fileWriter.moveOnLifecycleChange(entry);
607
638
  }
639
+ const updated = await this.repository.update(id, dbUpdates);
608
640
  await this._audit(`${method}_knowledge`, id, context.userId, {
609
641
  from: prevLifecycle,
610
642
  to: entry.lifecycle,
@@ -659,24 +691,43 @@ export class KnowledgeService {
659
691
  }
660
692
  /**
661
693
  * 为 QualityScorer 适配输入
662
- * QualityScorer 需要: title, trigger, code, language, category, summary, usageGuide, headers, tags
694
+ * QualityScorer v2 needs: title, trigger, description, language, category,
695
+ * doClause, dontClause, whenClause, coreCode, usageGuide,
696
+ * contentMarkdown, contentRationale, reasoningWhyStandard, reasoningSources,
697
+ * reasoningConfidence, source, headers, tags, views, clicks, rating
663
698
  */
664
699
  _adaptForScorer(entry) {
665
- // 从 Stats 值对象提取 engagement 指标,映射到 QualityScorer 期望的 views/clicks/rating
700
+ // 从 Stats 值对象提取 engagement 指标
666
701
  const stats = entry.stats && typeof entry.stats === 'object'
667
702
  ? entry.stats
668
703
  : {};
704
+ // 从 Content 值对象提取深度字段
705
+ const content = entry.content && typeof entry.content === 'object'
706
+ ? entry.content
707
+ : {};
708
+ // 从 Reasoning 值对象提取溯源字段
709
+ const reasoning = entry.reasoning && typeof entry.reasoning === 'object'
710
+ ? entry.reasoning
711
+ : {};
669
712
  return {
670
713
  title: entry.title,
671
714
  trigger: entry.trigger,
672
- code: entry.content?.pattern || entry.content?.markdown || '',
715
+ description: entry.description || '',
673
716
  language: entry.language,
674
717
  category: entry.category,
675
- summary: entry.description || '',
676
- usageGuide: entry.content?.markdown || entry.doClause || '',
718
+ doClause: entry.doClause || '',
719
+ dontClause: entry.dontClause || '',
720
+ whenClause: entry.whenClause || '',
721
+ coreCode: entry.coreCode || '',
722
+ usageGuide: entry.usageGuide || content.markdown || entry.doClause || '',
723
+ contentMarkdown: content.markdown || '',
724
+ contentRationale: content.rationale || '',
725
+ reasoningWhyStandard: reasoning.whyStandard || '',
726
+ reasoningSources: reasoning.sources || [],
727
+ reasoningConfidence: reasoning.confidence || 0,
728
+ source: entry.source || '',
677
729
  headers: entry.headers || [],
678
730
  tags: entry.tags || [],
679
- // engagement: views → views, adoptions+applications → clicks, authority → rating
680
731
  views: (stats.views ?? 0) + (stats.searchHits ?? 0),
681
732
  clicks: (stats.adoptions ?? 0) + (stats.applications ?? 0) + (stats.guardHits ?? 0),
682
733
  rating: stats.authority ?? 0,
@@ -695,20 +746,22 @@ export class KnowledgeService {
695
746
  }
696
747
  try {
697
748
  const candidates = [];
698
- // 仅与已发布 Recipe(active)建立关联,不与其他候选(pending)互关联
699
- const activeOnly = { lifecycle: Lifecycle.ACTIVE };
700
- // moduleName 查同模块已发布条目
749
+ // 与可消费 Recipe(active/staging/evolving)建立关联
750
+ const consumableFilter = {
751
+ lifecycle: [Lifecycle.ACTIVE, Lifecycle.STAGING, Lifecycle.EVOLVING],
752
+ };
753
+ // 按 moduleName 查同模块可消费条目
701
754
  if (entry.moduleName) {
702
- const sameModule = await this.repository.findWithPagination({ ...activeOnly, moduleName: entry.moduleName }, { page: 1, pageSize: 20 });
755
+ const sameModule = await this.repository.findWithPagination({ ...consumableFilter, moduleName: entry.moduleName }, { page: 1, pageSize: 20 });
703
756
  for (const r of sameModule.data) {
704
757
  if (r.id !== id) {
705
758
  candidates.push({ target: r.id, relation: 'related', weight: 0.8 });
706
759
  }
707
760
  }
708
761
  }
709
- // 按 category 查同类已发布条目(弱关联)
762
+ // 按 category 查同类可消费条目(弱关联)
710
763
  if (entry.category && candidates.length < 10) {
711
- const sameCat = await this.repository.findWithPagination({ ...activeOnly, category: entry.category }, { page: 1, pageSize: 10 });
764
+ const sameCat = await this.repository.findWithPagination({ ...consumableFilter, category: entry.category }, { page: 1, pageSize: 10 });
712
765
  for (const r of sameCat.data) {
713
766
  if (r.id !== id && !candidates.some((c) => c.target === r.id)) {
714
767
  candidates.push({ target: r.id, relation: 'related', weight: 0.4 });
@@ -757,9 +810,9 @@ export class KnowledgeService {
757
810
  return;
758
811
  }
759
812
  try {
760
- gs.db
761
- .prepare(`DELETE FROM knowledge_edges WHERE from_id = ? AND from_type = 'knowledge'`)
762
- .run(id);
813
+ if (this._edgeRepo) {
814
+ this._edgeRepo.deleteOutgoing(id, 'knowledge');
815
+ }
763
816
  if (!relations || typeof relations !== 'object') {
764
817
  return;
765
818
  }
@@ -791,12 +844,11 @@ export class KnowledgeService {
791
844
  }
792
845
  /** 删除所有关联边 */
793
846
  _removeAllEdges(id) {
794
- const gs = this._knowledgeGraphService;
795
- if (!gs) {
847
+ if (!this._edgeRepo) {
796
848
  return;
797
849
  }
798
850
  try {
799
- gs.db.prepare(`DELETE FROM knowledge_edges WHERE from_id = ? OR to_id = ?`).run(id, id);
851
+ this._edgeRepo.deleteByEntryId(id);
800
852
  }
801
853
  catch (err) {
802
854
  this.logger.warn('Failed to remove edges', {
@@ -807,12 +859,11 @@ export class KnowledgeService {
807
859
  }
808
860
  /** 删除关联的 evolution_proposals(target_recipe_id 无 CASCADE) */
809
861
  _removeRelatedProposals(id) {
810
- const gs = this._knowledgeGraphService;
811
- if (!gs) {
862
+ if (!this._proposalRepo) {
812
863
  return;
813
864
  }
814
865
  try {
815
- gs.db.prepare(`DELETE FROM evolution_proposals WHERE target_recipe_id = ?`).run(id);
866
+ this._proposalRepo.deleteByTargetRecipeId(id);
816
867
  }
817
868
  catch (err) {
818
869
  this.logger.warn('Failed to remove related proposals', {
@@ -11,8 +11,6 @@
11
11
  * 5. Quality Scoring — 质量评分
12
12
  * 6. Supersede Proposal — 创建替代提案
13
13
  * 7. Audit — 统一审计
14
- *
15
- * @see docs/copilot/recipe-lifecycle-management.md §6
16
14
  */
17
15
  /** Lightweight log interface — avoids importing static-only Logger class. */
18
16
  interface GatewayLogger {
@@ -11,8 +11,6 @@
11
11
  * 5. Quality Scoring — 质量评分
12
12
  * 6. Supersede Proposal — 创建替代提案
13
13
  * 7. Audit — 统一审计
14
- *
15
- * @see docs/copilot/recipe-lifecycle-management.md §6
16
14
  */
17
15
  import { UnifiedValidator } from '#domain/knowledge/UnifiedValidator.js';
18
16
  /* ═══════════════════ Gateway ═══════════════════ */
@@ -10,15 +10,8 @@
10
10
  * stale — 路径失效,无法自动修复
11
11
  */
12
12
  import type { SignalBus } from '../../infrastructure/signal/SignalBus.js';
13
- interface DatabaseLike {
14
- prepare(sql: string): {
15
- all(...params: unknown[]): Record<string, unknown>[];
16
- get(...params: unknown[]): Record<string, unknown> | undefined;
17
- run(...params: unknown[]): {
18
- changes: number;
19
- };
20
- };
21
- }
13
+ import type KnowledgeRepositoryImpl from '../../repository/knowledge/KnowledgeRepository.impl.js';
14
+ import type { RecipeSourceRefRepositoryImpl } from '../../repository/sourceref/RecipeSourceRefRepository.js';
22
15
  export interface ReconcileReport {
23
16
  /** 新插入的 sourceRef 条目 */
24
17
  inserted: number;
@@ -45,7 +38,7 @@ export interface ApplyReport {
45
38
  }
46
39
  export declare class SourceRefReconciler {
47
40
  #private;
48
- constructor(projectRoot: string, db: DatabaseLike, options?: {
41
+ constructor(projectRoot: string, sourceRefRepo: RecipeSourceRefRepositoryImpl, knowledgeRepo: KnowledgeRepositoryImpl, options?: {
49
42
  ttlMs?: number;
50
43
  signalBus?: SignalBus;
51
44
  });
@@ -55,7 +48,7 @@ export declare class SourceRefReconciler {
55
48
  */
56
49
  reconcile(opts?: {
57
50
  force?: boolean;
58
- }): ReconcileReport;
51
+ }): Promise<ReconcileReport>;
59
52
  /**
60
53
  * 对 stale 条目尝试 git rename 修复。
61
54
  * 使用 execFile() 安全执行 git log(防止命令注入)。
@@ -65,6 +58,5 @@ export declare class SourceRefReconciler {
65
58
  * 将 renamed 条目的 new_path 写回 Recipe .md 文件的 _reasoning.sources。
66
59
  * 完成后 status → active。
67
60
  */
68
- applyRepairs(): ApplyReport;
61
+ applyRepairs(): Promise<ApplyReport>;
69
62
  }
70
- export {};
@@ -20,13 +20,15 @@ const execFileAsync = promisify(execFile);
20
20
  const DEFAULT_TTL_MS = 24 * 60 * 60 * 1000;
21
21
  export class SourceRefReconciler {
22
22
  #projectRoot;
23
- #db;
23
+ #sourceRefRepo;
24
+ #knowledgeRepo;
24
25
  #signalBus;
25
26
  #logger = Logger.getInstance();
26
27
  #ttlMs;
27
- constructor(projectRoot, db, options) {
28
+ constructor(projectRoot, sourceRefRepo, knowledgeRepo, options) {
28
29
  this.#projectRoot = projectRoot;
29
- this.#db = db;
30
+ this.#sourceRefRepo = sourceRefRepo;
31
+ this.#knowledgeRepo = knowledgeRepo;
30
32
  this.#signalBus = options?.signalBus ?? null;
31
33
  this.#ttlMs = options?.ttlMs ?? DEFAULT_TTL_MS;
32
34
  }
@@ -34,7 +36,7 @@ export class SourceRefReconciler {
34
36
  * 从 knowledge_entries.reasoning 填充 recipe_source_refs 表。
35
37
  * 对已有条目验证路径存在性,更新 status。
36
38
  */
37
- reconcile(opts) {
39
+ async reconcile(opts) {
38
40
  const force = opts?.force ?? false;
39
41
  const report = {
40
42
  inserted: 0,
@@ -43,12 +45,13 @@ export class SourceRefReconciler {
43
45
  skipped: 0,
44
46
  recipesProcessed: 0,
45
47
  };
46
- // 确保表存在(兼容未跑 migration 的场景)
47
- this.#ensureTable();
48
+ // 确保表可访问
49
+ if (!this.#sourceRefRepo.isAccessible()) {
50
+ this.#logger.warn('SourceRefReconciler: recipe_source_refs table not accessible, skipping');
51
+ return report;
52
+ }
48
53
  // 获取所有有 reasoning 的知识条目
49
- const rows = this.#db
50
- .prepare(`SELECT id, reasoning FROM knowledge_entries WHERE reasoning IS NOT NULL AND reasoning != '{}'`)
51
- .all();
54
+ const rows = await this.#knowledgeRepo.findAllIdAndReasoning();
52
55
  const now = Date.now();
53
56
  for (const row of rows) {
54
57
  let sources = [];
@@ -67,12 +70,10 @@ export class SourceRefReconciler {
67
70
  report.recipesProcessed++;
68
71
  for (const sourcePath of sources) {
69
72
  // 检查是否已有记录
70
- const existing = this.#db
71
- .prepare(`SELECT status, verified_at FROM recipe_source_refs WHERE recipe_id = ? AND source_path = ?`)
72
- .get(row.id, sourcePath);
73
+ const existing = this.#sourceRefRepo.findOne(row.id, sourcePath);
73
74
  if (existing && !force) {
74
75
  // TTL 检查:跳过近期已验证的条目
75
- if (now - existing.verified_at < this.#ttlMs) {
76
+ if (now - existing.verifiedAt < this.#ttlMs) {
76
77
  report.skipped++;
77
78
  if (existing.status === 'active') {
78
79
  report.active++;
@@ -89,24 +90,34 @@ export class SourceRefReconciler {
89
90
  if (existing) {
90
91
  // 更新已有记录
91
92
  if (exists) {
92
- this.#db
93
- .prepare(`UPDATE recipe_source_refs SET status = 'active', new_path = NULL, verified_at = ? WHERE recipe_id = ? AND source_path = ?`)
94
- .run(now, row.id, sourcePath);
93
+ this.#sourceRefRepo.upsert({
94
+ recipeId: row.id,
95
+ sourcePath,
96
+ status: 'active',
97
+ newPath: null,
98
+ verifiedAt: now,
99
+ });
95
100
  report.active++;
96
101
  }
97
102
  else {
98
- this.#db
99
- .prepare(`UPDATE recipe_source_refs SET status = 'stale', verified_at = ? WHERE recipe_id = ? AND source_path = ?`)
100
- .run(now, row.id, sourcePath);
103
+ this.#sourceRefRepo.upsert({
104
+ recipeId: row.id,
105
+ sourcePath,
106
+ status: 'stale',
107
+ verifiedAt: now,
108
+ });
101
109
  report.stale++;
102
110
  }
103
111
  }
104
112
  else {
105
113
  // 新增记录
106
114
  const status = exists ? 'active' : 'stale';
107
- this.#db
108
- .prepare(`INSERT OR REPLACE INTO recipe_source_refs (recipe_id, source_path, status, verified_at) VALUES (?, ?, ?, ?)`)
109
- .run(row.id, sourcePath, status, now);
115
+ this.#sourceRefRepo.upsert({
116
+ recipeId: row.id,
117
+ sourcePath,
118
+ status,
119
+ verifiedAt: now,
120
+ });
110
121
  report.inserted++;
111
122
  if (exists) {
112
123
  report.active++;
@@ -139,21 +150,15 @@ export class SourceRefReconciler {
139
150
  return;
140
151
  }
141
152
  try {
142
- const staleRecipes = this.#db
143
- .prepare(`SELECT recipe_id, COUNT(*) AS stale_count,
144
- (SELECT COUNT(*) FROM recipe_source_refs r2 WHERE r2.recipe_id = r.recipe_id) AS total_count
145
- FROM recipe_source_refs r
146
- WHERE status = 'stale'
147
- GROUP BY recipe_id`)
148
- .all();
153
+ const staleRecipes = this.#sourceRefRepo.getStaleCountsByRecipe();
149
154
  for (const row of staleRecipes) {
150
- const staleRatio = row.stale_count / row.total_count;
155
+ const staleRatio = row.staleCount / row.totalCount;
151
156
  this.#signalBus.send('quality', 'SourceRefReconciler', staleRatio, {
152
- target: row.recipe_id,
157
+ target: row.recipeId,
153
158
  metadata: {
154
159
  reason: 'source_ref_stale',
155
- staleCount: row.stale_count,
156
- totalRefs: row.total_count,
160
+ staleCount: row.staleCount,
161
+ totalRefs: row.totalCount,
157
162
  },
158
163
  });
159
164
  }
@@ -169,9 +174,7 @@ export class SourceRefReconciler {
169
174
  async repairRenames() {
170
175
  const report = { renamed: 0, stillStale: 0 };
171
176
  // 获取所有 stale 条目
172
- const staleRows = this.#db
173
- .prepare(`SELECT recipe_id, source_path FROM recipe_source_refs WHERE status = 'stale'`)
174
- .all();
177
+ const staleRows = this.#sourceRefRepo.findStale();
175
178
  if (staleRows.length === 0) {
176
179
  return report;
177
180
  }
@@ -179,14 +182,18 @@ export class SourceRefReconciler {
179
182
  const renameMap = await this.#getGitRenameMap();
180
183
  const now = Date.now();
181
184
  for (const row of staleRows) {
182
- const newPath = renameMap.get(row.source_path);
185
+ const newPath = renameMap.get(row.sourcePath);
183
186
  if (newPath) {
184
187
  // 验证 newPath 存在
185
188
  const absNewPath = path.resolve(this.#projectRoot, newPath);
186
189
  if (fs.existsSync(absNewPath)) {
187
- this.#db
188
- .prepare(`UPDATE recipe_source_refs SET status = 'renamed', new_path = ?, verified_at = ? WHERE recipe_id = ? AND source_path = ?`)
189
- .run(newPath, now, row.recipe_id, row.source_path);
190
+ this.#sourceRefRepo.upsert({
191
+ recipeId: row.recipeId,
192
+ sourcePath: row.sourcePath,
193
+ status: 'renamed',
194
+ newPath,
195
+ verifiedAt: now,
196
+ });
190
197
  report.renamed++;
191
198
  continue;
192
199
  }
@@ -215,29 +222,25 @@ export class SourceRefReconciler {
215
222
  * 将 renamed 条目的 new_path 写回 Recipe .md 文件的 _reasoning.sources。
216
223
  * 完成后 status → active。
217
224
  */
218
- applyRepairs() {
225
+ async applyRepairs() {
219
226
  const report = { applied: 0, failed: 0 };
220
- const renamedRows = this.#db
221
- .prepare(`SELECT recipe_id, source_path, new_path FROM recipe_source_refs WHERE status = 'renamed' AND new_path IS NOT NULL`)
222
- .all();
227
+ const renamedRows = this.#sourceRefRepo.findRenamed();
223
228
  if (renamedRows.length === 0) {
224
229
  return report;
225
230
  }
226
- // 按 recipe_id 分组
231
+ // 按 recipeId 分组
227
232
  const byRecipe = new Map();
228
233
  for (const row of renamedRows) {
229
- if (!byRecipe.has(row.recipe_id)) {
230
- byRecipe.set(row.recipe_id, []);
234
+ if (!byRecipe.has(row.recipeId)) {
235
+ byRecipe.set(row.recipeId, []);
231
236
  }
232
- byRecipe.get(row.recipe_id)?.push({ source_path: row.source_path, new_path: row.new_path });
237
+ byRecipe.get(row.recipeId)?.push({ sourcePath: row.sourcePath, newPath: row.newPath });
233
238
  }
234
239
  // 获取 recipe 的 sourceFile 以定位 .md 文件
235
240
  const now = Date.now();
236
241
  for (const [recipeId, renames] of byRecipe) {
237
242
  try {
238
- const entry = this.#db
239
- .prepare(`SELECT sourceFile, reasoning FROM knowledge_entries WHERE id = ?`)
240
- .get(recipeId);
243
+ const entry = await this.#knowledgeRepo.findSourceFileAndReasoning(recipeId);
241
244
  if (!entry?.sourceFile || !entry.reasoning) {
242
245
  report.failed += renames.length;
243
246
  continue;
@@ -260,9 +263,9 @@ export class SourceRefReconciler {
260
263
  const sources = Array.isArray(reasoning.sources) ? [...reasoning.sources] : [];
261
264
  let modified = false;
262
265
  for (const rename of renames) {
263
- const idx = sources.indexOf(rename.source_path);
266
+ const idx = sources.indexOf(rename.sourcePath);
264
267
  if (idx >= 0) {
265
- sources[idx] = rename.new_path;
268
+ sources[idx] = rename.newPath;
266
269
  modified = true;
267
270
  }
268
271
  }
@@ -272,14 +275,10 @@ export class SourceRefReconciler {
272
275
  // 查找 YAML frontmatter 中的 reasoning 并替换
273
276
  const updatedReasoning = JSON.stringify(reasoning);
274
277
  // 更新 DB reasoning 列
275
- this.#db
276
- .prepare(`UPDATE knowledge_entries SET reasoning = ?, updatedAt = ? WHERE id = ?`)
277
- .run(updatedReasoning, now, recipeId);
278
+ await this.#knowledgeRepo.updateReasoning(recipeId, updatedReasoning, now);
278
279
  // 更新 recipe_source_refs 状态
279
280
  for (const rename of renames) {
280
- this.#db
281
- .prepare(`UPDATE recipe_source_refs SET status = 'active', source_path = ?, new_path = NULL, verified_at = ? WHERE recipe_id = ? AND source_path = ?`)
282
- .run(rename.new_path, now, recipeId, rename.source_path);
281
+ this.#sourceRefRepo.replaceSourcePath(recipeId, rename.sourcePath, rename.newPath, now);
283
282
  }
284
283
  report.applied += renames.length;
285
284
  }
@@ -301,25 +300,6 @@ export class SourceRefReconciler {
301
300
  return report;
302
301
  }
303
302
  /* ═══ Private helpers ═══════════════════════════════ */
304
- #ensureTable() {
305
- try {
306
- this.#db.prepare(`SELECT 1 FROM recipe_source_refs LIMIT 1`).get();
307
- }
308
- catch {
309
- // 表不存在,创建之
310
- this.#db.exec?.(`CREATE TABLE IF NOT EXISTS recipe_source_refs (
311
- recipe_id TEXT NOT NULL,
312
- source_path TEXT NOT NULL,
313
- status TEXT NOT NULL DEFAULT 'active',
314
- new_path TEXT,
315
- verified_at INTEGER NOT NULL,
316
- PRIMARY KEY (recipe_id, source_path),
317
- FOREIGN KEY (recipe_id) REFERENCES knowledge_entries(id) ON DELETE CASCADE
318
- );
319
- CREATE INDEX IF NOT EXISTS idx_rsr_path ON recipe_source_refs(source_path);
320
- CREATE INDEX IF NOT EXISTS idx_rsr_status ON recipe_source_refs(status);`);
321
- }
322
- }
323
303
  /**
324
304
  * 通过 git log 获取 rename 映射(旧路径 → 新路径)
325
305
  * 使用 execFile 防止命令注入
@@ -352,8 +352,10 @@ export class ModuleService {
352
352
  }
353
353
  const scannedFiles = files.map((f) => ({ name: f.name, path: f.relativePath }));
354
354
  this.#logger.info(`[ModuleService] scanTarget: ${targetName}, ${files.length} files`);
355
- // 3. AI 提取
356
- if (!this.#agentFactory) {
355
+ // 3. AI 提取 — mock 模式或无 agentFactory 时直接跳过
356
+ const aiManager = this.#container?.singletons
357
+ ?._aiProviderManager;
358
+ if (!this.#agentFactory || aiManager?.isMock) {
357
359
  return {
358
360
  recipes: [],
359
361
  scannedFiles,
@@ -375,21 +377,7 @@ export class ModuleService {
375
377
  this.#enrichRecipes(recipes);
376
378
  const result = { recipes, scannedFiles };
377
379
  if (recipes.length === 0) {
378
- // 检查是否因为 AI Provider mock 导致空结果
379
- try {
380
- const singletons = this.#container?.singletons;
381
- const aiProvider = singletons?.aiProvider;
382
- if (!aiProvider || aiProvider.name === 'mock') {
383
- result.noAi = true;
384
- result.message = 'AI 未配置,已跳过智能提取。请在 .env 中设置 API Key 后重试。';
385
- }
386
- else {
387
- result.message = `AI 提取完成,但未发现可复用的代码模式(${targetName}, ${files.length} 个文件)`;
388
- }
389
- }
390
- catch {
391
- result.message = `AI 提取完成,但未发现可复用的代码模式(${targetName}, ${files.length} 个文件)`;
392
- }
380
+ result.message = `AI 提取完成,但未发现可复用的代码模式(${targetName}, ${files.length} 个文件)`;
393
381
  }
394
382
  onProgress?.({
395
383
  type: 'scan:completed',
@@ -464,13 +452,15 @@ export class ModuleService {
464
452
  path: f.relativePath,
465
453
  targetName: f.targetName,
466
454
  }));
467
- // 3. AI 提取 Recipes
455
+ // 3. AI 提取 Recipes — mock 模式跳过
468
456
  const allRecipes = [];
469
457
  const PER_BATCH_TIMEOUT = options.batchTimeout || 90000;
470
458
  const startTime = Date.now();
471
459
  const TOTAL_TIMEOUT = options.totalTimeout || 540000;
472
460
  let timedOut = false;
473
- if (this.#agentFactory) {
461
+ const scanAiMgr = this.#container?.singletons
462
+ ?._aiProviderManager;
463
+ if (this.#agentFactory && !scanAiMgr?.isMock) {
474
464
  const BATCH_SIZE = options.batchSize || 20;
475
465
  for (let i = 0; i < allFiles.length; i += BATCH_SIZE) {
476
466
  if (Date.now() - startTime > TOTAL_TIMEOUT) {
@@ -613,6 +603,7 @@ export class ModuleService {
613
603
  go: 'go',
614
604
  jvm: 'java',
615
605
  python: 'python',
606
+ customConfig: 'swift',
616
607
  generic: 'unknown',
617
608
  };
618
609
  return map[id] || 'unknown';