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.
- package/README.md +1 -0
- package/dashboard/dist/assets/icons-BMNb0V6L.js +1 -0
- package/dashboard/dist/assets/index-DHJ1Dj7u.css +1 -0
- package/dashboard/dist/assets/index-DV8biUkH.js +112 -0
- package/dashboard/dist/index.html +3 -3
- package/dist/bin/cli.js +8 -4
- package/dist/lib/agent/AgentRuntime.d.ts +2 -2
- package/dist/lib/agent/AgentRuntime.js +26 -18
- package/dist/lib/agent/core/ChatAgentPrompts.js +57 -21
- package/dist/lib/agent/core/LoopContext.d.ts +1 -0
- package/dist/lib/agent/core/ToolExecutionPipeline.js +13 -0
- package/dist/lib/agent/domain/ChatAgentTasks.js +4 -0
- package/dist/lib/agent/forced-summary.js +7 -2
- package/dist/lib/agent/memory/ActiveContext.d.ts +0 -2
- package/dist/lib/agent/memory/ActiveContext.js +0 -2
- package/dist/lib/agent/memory/MemoryEmbeddingStore.d.ts +49 -0
- package/dist/lib/agent/memory/MemoryEmbeddingStore.js +159 -0
- package/dist/lib/agent/memory/MemoryRetriever.d.ts +2 -0
- package/dist/lib/agent/memory/MemoryRetriever.js +25 -11
- package/dist/lib/agent/memory/MemoryStore.d.ts +8 -41
- package/dist/lib/agent/memory/MemoryStore.js +196 -261
- package/dist/lib/agent/memory/PersistentMemory.d.ts +2 -0
- package/dist/lib/agent/memory/PersistentMemory.js +4 -5
- package/dist/lib/agent/memory/SessionStore.d.ts +0 -2
- package/dist/lib/agent/memory/SessionStore.js +0 -2
- package/dist/lib/agent/tools/ast-graph.js +21 -19
- package/dist/lib/agent/tools/infrastructure.js +3 -2
- package/dist/lib/agent/tools/project-access.d.ts +2 -2
- package/dist/lib/agent/tools/project-access.js +5 -4
- package/dist/lib/bootstrap.js +2 -1
- package/dist/lib/cli/AiScanService.js +8 -21
- package/dist/lib/cli/KnowledgeSyncService.d.ts +7 -37
- package/dist/lib/cli/KnowledgeSyncService.js +23 -51
- package/dist/lib/core/ast/ProjectGraph.js +5 -27
- package/dist/lib/core/discovery/ConfigWatcher.d.ts +64 -0
- package/dist/lib/core/discovery/ConfigWatcher.js +336 -0
- package/dist/lib/core/discovery/CustomConfigDiscoverer.d.ts +28 -0
- package/dist/lib/core/discovery/CustomConfigDiscoverer.js +1303 -0
- package/dist/lib/core/discovery/DiscovererPreference.d.ts +44 -0
- package/dist/lib/core/discovery/DiscovererPreference.js +141 -0
- package/dist/lib/core/discovery/DiscovererRegistry.d.ts +10 -1
- package/dist/lib/core/discovery/DiscovererRegistry.js +42 -2
- package/dist/lib/core/discovery/ProjectDiscoverer.d.ts +19 -0
- package/dist/lib/core/discovery/index.d.ts +2 -0
- package/dist/lib/core/discovery/index.js +4 -0
- package/dist/lib/core/discovery/parsers/CMakeParser.d.ts +32 -0
- package/dist/lib/core/discovery/parsers/CMakeParser.js +148 -0
- package/dist/lib/core/discovery/parsers/GradleDslParser.d.ts +43 -0
- package/dist/lib/core/discovery/parsers/GradleDslParser.js +171 -0
- package/dist/lib/core/discovery/parsers/JsonConfigParser.d.ts +45 -0
- package/dist/lib/core/discovery/parsers/JsonConfigParser.js +122 -0
- package/dist/lib/core/discovery/parsers/RubyDslParser.d.ts +49 -0
- package/dist/lib/core/discovery/parsers/RubyDslParser.js +282 -0
- package/dist/lib/core/discovery/parsers/StarlarkParser.d.ts +33 -0
- package/dist/lib/core/discovery/parsers/StarlarkParser.js +229 -0
- package/dist/lib/core/discovery/parsers/YamlConfigParser.d.ts +37 -0
- package/dist/lib/core/discovery/parsers/YamlConfigParser.js +212 -0
- package/dist/lib/domain/dimension/DimensionRegistry.d.ts +0 -2
- package/dist/lib/domain/dimension/DimensionRegistry.js +0 -2
- package/dist/lib/domain/dimension/DimensionSop.js +44 -33
- package/dist/lib/domain/dimension/UnifiedDimension.d.ts +0 -2
- package/dist/lib/domain/dimension/UnifiedDimension.js +0 -2
- package/dist/lib/domain/knowledge/KnowledgeEntry.d.ts +7 -1
- package/dist/lib/domain/knowledge/KnowledgeEntry.js +17 -3
- package/dist/lib/domain/knowledge/Lifecycle.d.ts +26 -0
- package/dist/lib/domain/knowledge/Lifecycle.js +42 -0
- package/dist/lib/domain/knowledge/index.d.ts +2 -1
- package/dist/lib/domain/knowledge/index.js +1 -1
- package/dist/lib/external/ai/AiProvider.d.ts +12 -0
- package/dist/lib/external/ai/AiProvider.js +24 -0
- package/dist/lib/external/ai/AiProviderManager.d.ts +101 -0
- package/dist/lib/external/ai/AiProviderManager.js +193 -0
- package/dist/lib/external/ai/providers/ClaudeProvider.js +11 -0
- package/dist/lib/external/ai/providers/GoogleGeminiProvider.js +18 -0
- package/dist/lib/external/ai/providers/MockProvider.d.ts +21 -3
- package/dist/lib/external/ai/providers/MockProvider.js +290 -14
- package/dist/lib/external/ai/providers/OpenAiProvider.js +16 -0
- package/dist/lib/external/lark/LarkTransport.d.ts +5 -1
- package/dist/lib/external/lark/LarkTransport.js +10 -2
- package/dist/lib/external/mcp/handlers/bootstrap/pipeline/BootstrapSnapshot.d.ts +2 -1
- package/dist/lib/external/mcp/handlers/bootstrap/pipeline/BootstrapSnapshot.js +102 -153
- package/dist/lib/external/mcp/handlers/bootstrap/pipeline/mock-pipeline.d.ts +20 -0
- package/dist/lib/external/mcp/handlers/bootstrap/pipeline/mock-pipeline.js +432 -0
- package/dist/lib/external/mcp/handlers/bootstrap/pipeline/orchestrator.js +49 -24
- package/dist/lib/external/mcp/handlers/bootstrap/refine.js +8 -0
- package/dist/lib/external/mcp/handlers/bootstrap/shared/bootstrap-phases.d.ts +1 -1
- package/dist/lib/external/mcp/handlers/bootstrap/shared/bootstrap-phases.js +41 -37
- package/dist/lib/external/mcp/handlers/bootstrap-external.d.ts +9 -0
- package/dist/lib/external/mcp/handlers/bootstrap-external.js +3 -1
- package/dist/lib/external/mcp/handlers/bootstrap-internal.js +2 -0
- package/dist/lib/external/mcp/handlers/consolidated.js +2 -1
- package/dist/lib/external/mcp/handlers/dimension-complete-external.js +9 -4
- package/dist/lib/external/mcp/handlers/evolve-external.d.ts +1 -0
- package/dist/lib/external/mcp/handlers/evolve-external.js +18 -18
- package/dist/lib/external/mcp/handlers/guard.js +15 -24
- package/dist/lib/external/mcp/handlers/knowledge.js +5 -4
- package/dist/lib/external/mcp/handlers/panorama.js +9 -9
- package/dist/lib/external/mcp/handlers/rescan-external.js +7 -6
- package/dist/lib/external/mcp/handlers/rescan-internal.js +9 -5
- package/dist/lib/external/mcp/handlers/search.js +3 -1
- package/dist/lib/external/mcp/handlers/skill.js +4 -4
- package/dist/lib/external/mcp/handlers/structure.js +8 -12
- package/dist/lib/external/mcp/handlers/system.js +10 -34
- package/dist/lib/http/routes/ai.js +109 -30
- package/dist/lib/http/routes/candidates.js +11 -4
- package/dist/lib/http/routes/commands.js +10 -1
- package/dist/lib/http/routes/guardReport.js +3 -5
- package/dist/lib/http/routes/health.js +11 -0
- package/dist/lib/http/routes/modules.js +27 -0
- package/dist/lib/http/routes/panorama.js +12 -12
- package/dist/lib/http/routes/recipes.js +66 -8
- package/dist/lib/http/routes/remote.js +3 -13
- package/dist/lib/http/routes/search.js +11 -8
- package/dist/lib/http/utils/routeHelpers.js +2 -1
- package/dist/lib/infrastructure/audit/AuditLogger.d.ts +20 -3
- package/dist/lib/infrastructure/audit/AuditStore.d.ts +28 -29
- package/dist/lib/infrastructure/audit/AuditStore.js +81 -88
- package/dist/lib/infrastructure/database/drizzle/schema.d.ts +180 -2
- package/dist/lib/infrastructure/database/drizzle/schema.js +23 -3
- package/dist/lib/injection/ServiceContainer.d.ts +6 -5
- package/dist/lib/injection/ServiceContainer.js +18 -31
- package/dist/lib/injection/ServiceMap.d.ts +22 -0
- package/dist/lib/injection/modules/AiModule.d.ts +6 -9
- package/dist/lib/injection/modules/AiModule.js +82 -39
- package/dist/lib/injection/modules/AppModule.js +2 -1
- package/dist/lib/injection/modules/GuardModule.js +5 -5
- package/dist/lib/injection/modules/InfraModule.js +60 -0
- package/dist/lib/injection/modules/KnowledgeModule.js +86 -51
- package/dist/lib/injection/modules/PanoramaModule.js +16 -10
- package/dist/lib/injection/modules/VectorModule.js +3 -0
- package/dist/lib/repository/audit/AuditRepository.d.ts +107 -0
- package/dist/lib/repository/audit/AuditRepository.js +272 -0
- package/dist/lib/repository/base/RepositoryBase.d.ts +46 -0
- package/dist/lib/repository/base/RepositoryBase.js +32 -0
- package/dist/lib/repository/bootstrap/BootstrapRepository.d.ts +94 -0
- package/dist/lib/repository/bootstrap/BootstrapRepository.js +246 -0
- package/dist/lib/repository/code/CodeEntityRepository.d.ts +91 -0
- package/dist/lib/repository/code/CodeEntityRepository.js +361 -0
- package/dist/lib/repository/delivery/DeliveryRepoAdapter.d.ts +39 -0
- package/dist/lib/repository/delivery/DeliveryRepoAdapter.js +23 -0
- package/dist/lib/repository/evolution/LifecycleEventRepository.d.ts +51 -0
- package/dist/lib/repository/evolution/LifecycleEventRepository.js +119 -0
- package/dist/lib/repository/evolution/ProposalRepository.d.ts +9 -12
- package/dist/lib/repository/evolution/ProposalRepository.js +114 -57
- package/dist/lib/repository/guard/GuardViolationRepository.d.ts +104 -0
- package/dist/lib/repository/guard/GuardViolationRepository.js +217 -0
- package/dist/lib/repository/knowledge/KnowledgeEdgeRepository.d.ts +129 -0
- package/dist/lib/repository/knowledge/KnowledgeEdgeRepository.js +475 -0
- package/dist/lib/repository/knowledge/KnowledgeFileStore.d.ts +39 -0
- package/dist/lib/repository/knowledge/KnowledgeFileStore.js +12 -0
- package/dist/lib/repository/knowledge/KnowledgeRepository.impl.d.ts +295 -11
- package/dist/lib/repository/knowledge/KnowledgeRepository.impl.js +608 -13
- package/dist/lib/repository/knowledge/KnowledgeUnitOfWork.d.ts +61 -0
- package/dist/lib/repository/knowledge/KnowledgeUnitOfWork.js +156 -0
- package/dist/lib/repository/memory/MemoryRepository.d.ts +90 -0
- package/dist/lib/repository/memory/MemoryRepository.js +260 -0
- package/dist/lib/repository/search/SearchRepoAdapter.d.ts +92 -0
- package/dist/lib/repository/search/SearchRepoAdapter.js +124 -0
- package/dist/lib/repository/session/SessionRepository.d.ts +46 -0
- package/dist/lib/repository/session/SessionRepository.js +110 -0
- package/dist/lib/repository/sourceref/RecipeSourceRefRepository.d.ts +66 -0
- package/dist/lib/repository/sourceref/RecipeSourceRefRepository.js +182 -0
- package/dist/lib/repository/sync/SyncRepoAdapter.d.ts +58 -0
- package/dist/lib/repository/sync/SyncRepoAdapter.js +58 -0
- package/dist/lib/service/bootstrap/UiStartupTasks.js +5 -6
- package/dist/lib/service/bootstrap/bootstrap-event-types.d.ts +0 -1
- package/dist/lib/service/bootstrap/bootstrap-event-types.js +0 -1
- package/dist/lib/service/cleanup/CleanupService.d.ts +54 -7
- package/dist/lib/service/cleanup/CleanupService.js +291 -40
- package/dist/lib/service/delivery/CursorDeliveryPipeline.js +6 -8
- package/dist/lib/service/evolution/ConsolidationAdvisor.d.ts +4 -9
- package/dist/lib/service/evolution/ConsolidationAdvisor.js +34 -70
- package/dist/lib/service/evolution/ContentPatcher.d.ts +4 -12
- package/dist/lib/service/evolution/ContentPatcher.js +48 -19
- package/dist/lib/service/evolution/ContradictionDetector.d.ts +3 -7
- package/dist/lib/service/evolution/ContradictionDetector.js +17 -24
- package/dist/lib/service/evolution/DecayDetector.d.ts +10 -9
- package/dist/lib/service/evolution/DecayDetector.js +63 -57
- package/dist/lib/service/evolution/EnhancementSuggester.d.ts +3 -9
- package/dist/lib/service/evolution/EnhancementSuggester.js +42 -86
- package/dist/lib/service/evolution/KnowledgeMetabolism.d.ts +4 -4
- package/dist/lib/service/evolution/KnowledgeMetabolism.js +102 -71
- package/dist/lib/service/evolution/ProposalExecutor.d.ts +5 -12
- package/dist/lib/service/evolution/ProposalExecutor.js +64 -69
- package/dist/lib/service/evolution/RecipeLifecycleSupervisor.d.ts +9 -14
- package/dist/lib/service/evolution/RecipeLifecycleSupervisor.js +94 -155
- package/dist/lib/service/evolution/RecipeRelevanceAuditor.d.ts +4 -1
- package/dist/lib/service/evolution/RecipeRelevanceAuditor.js +50 -49
- package/dist/lib/service/evolution/RedundancyAnalyzer.d.ts +3 -7
- package/dist/lib/service/evolution/RedundancyAnalyzer.js +15 -22
- package/dist/lib/service/evolution/StagingManager.d.ts +6 -15
- package/dist/lib/service/evolution/StagingManager.js +37 -95
- package/dist/lib/service/evolution/createSupersedeProposal.d.ts +1 -1
- package/dist/lib/service/evolution/createSupersedeProposal.js +7 -8
- package/dist/lib/service/guard/CoverageAnalyzer.d.ts +3 -7
- package/dist/lib/service/guard/CoverageAnalyzer.js +9 -11
- package/dist/lib/service/guard/GuardCheckEngine.d.ts +3 -0
- package/dist/lib/service/guard/GuardCheckEngine.js +14 -22
- package/dist/lib/service/guard/ReverseGuard.d.ts +4 -7
- package/dist/lib/service/guard/ReverseGuard.js +21 -31
- package/dist/lib/service/guard/ViolationsStore.d.ts +15 -21
- package/dist/lib/service/guard/ViolationsStore.js +75 -69
- package/dist/lib/service/knowledge/CodeEntityGraph.d.ts +45 -63
- package/dist/lib/service/knowledge/CodeEntityGraph.js +418 -496
- package/dist/lib/service/knowledge/ConfidenceRouter.js +18 -9
- package/dist/lib/service/knowledge/KnowledgeFileWriter.d.ts +2 -1
- package/dist/lib/service/knowledge/KnowledgeGraphService.d.ts +18 -60
- package/dist/lib/service/knowledge/KnowledgeGraphService.js +58 -109
- package/dist/lib/service/knowledge/KnowledgeService.d.ts +15 -1
- package/dist/lib/service/knowledge/KnowledgeService.js +97 -46
- package/dist/lib/service/knowledge/RecipeProductionGateway.d.ts +0 -2
- package/dist/lib/service/knowledge/RecipeProductionGateway.js +0 -2
- package/dist/lib/service/knowledge/SourceRefReconciler.d.ts +5 -13
- package/dist/lib/service/knowledge/SourceRefReconciler.js +58 -78
- package/dist/lib/service/module/ModuleService.js +10 -19
- package/dist/lib/service/panorama/CouplingAnalyzer.d.ts +14 -3
- package/dist/lib/service/panorama/CouplingAnalyzer.js +137 -32
- package/dist/lib/service/panorama/DimensionAnalyzer.d.ts +7 -4
- package/dist/lib/service/panorama/DimensionAnalyzer.js +94 -33
- package/dist/lib/service/panorama/LayerInferrer.d.ts +16 -1
- package/dist/lib/service/panorama/LayerInferrer.js +118 -1
- package/dist/lib/service/panorama/ModuleDiscoverer.d.ts +14 -4
- package/dist/lib/service/panorama/ModuleDiscoverer.js +209 -61
- package/dist/lib/service/panorama/PanoramaAggregator.d.ts +15 -4
- package/dist/lib/service/panorama/PanoramaAggregator.js +128 -62
- package/dist/lib/service/panorama/PanoramaScanner.d.ts +5 -1
- package/dist/lib/service/panorama/PanoramaScanner.js +60 -31
- package/dist/lib/service/panorama/PanoramaService.d.ts +11 -8
- package/dist/lib/service/panorama/PanoramaService.js +49 -69
- package/dist/lib/service/panorama/PanoramaTypes.d.ts +41 -0
- package/dist/lib/service/panorama/RoleRefiner.d.ts +10 -5
- package/dist/lib/service/panorama/RoleRefiner.js +92 -282
- package/dist/lib/service/panorama/TechStackProfiler.d.ts +13 -0
- package/dist/lib/service/panorama/TechStackProfiler.js +79 -0
- package/dist/lib/service/quality/QualityScorer.d.ts +45 -26
- package/dist/lib/service/quality/QualityScorer.js +157 -83
- package/dist/lib/service/search/SearchEngine.d.ts +1 -0
- package/dist/lib/service/search/SearchEngine.js +32 -37
- package/dist/lib/service/signal/HitRecorder.js +5 -5
- package/dist/lib/service/skills/RuleRecallStrategy.js +7 -3
- package/dist/lib/service/skills/SignalCollector.d.ts +6 -8
- package/dist/lib/service/skills/SignalCollector.js +34 -60
- package/dist/lib/service/skills/SkillAdvisor.d.ts +7 -13
- package/dist/lib/service/skills/SkillAdvisor.js +30 -79
- package/dist/lib/service/vector/ContextualEnricher.d.ts +1 -0
- package/dist/lib/service/vector/ContextualEnricher.js +4 -0
- package/dist/lib/service/vector/SyncCoordinator.d.ts +3 -1
- package/dist/lib/service/vector/SyncCoordinator.js +25 -3
- package/dist/lib/service/vector/VectorService.d.ts +2 -0
- package/dist/lib/service/vector/VectorService.js +3 -0
- package/dist/lib/service/wiki/WikiGenerator.js +1 -1
- package/dist/lib/shared/LanguageProfiles.d.ts +109 -0
- package/dist/lib/shared/LanguageProfiles.js +939 -0
- package/dist/lib/shared/LanguageService.d.ts +6 -0
- package/dist/lib/shared/LanguageService.js +19 -0
- package/dist/lib/shared/constants.d.ts +19 -19
- package/dist/lib/shared/constants.js +10 -10
- package/dist/lib/shared/developer-identity.d.ts +18 -0
- package/dist/lib/shared/developer-identity.js +62 -0
- package/dist/lib/shared/schemas/http-requests.d.ts +8 -17
- package/dist/lib/shared/schemas/http-requests.js +9 -6
- package/dist/lib/shared/schemas/mcp-tools.d.ts +1 -1
- package/dist/lib/types/knowledge-wire.d.ts +1 -0
- package/dist/lib/types/project-snapshot-builder.d.ts +0 -1
- package/dist/lib/types/project-snapshot-builder.js +0 -1
- package/dist/lib/types/project-snapshot.d.ts +0 -1
- package/dist/lib/types/project-snapshot.js +0 -1
- package/dist/lib/types/snapshot-views.d.ts +0 -2
- package/dist/lib/types/snapshot-views.js +0 -1
- package/package.json +2 -1
- package/dashboard/dist/assets/icons-D1aVZYFW.js +0 -1
- package/dashboard/dist/assets/index-CxHOu8Hd.css +0 -1
- package/dashboard/dist/assets/index-DDdAOpYT.js +0 -128
- package/dist/lib/repository/base/BaseRepository.d.ts +0 -53
- 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
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
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
|
-
|
|
596
|
-
// 文件位置迁移(candidate ↔ recipe 目录)
|
|
635
|
+
// ── file-first: 先迁移 .md 文件,再更新 DB lifecycle(文件=真相源) ──
|
|
597
636
|
if (this._fileWriter) {
|
|
598
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
715
|
+
description: entry.description || '',
|
|
673
716
|
language: entry.language,
|
|
674
717
|
category: entry.category,
|
|
675
|
-
|
|
676
|
-
|
|
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
|
-
//
|
|
699
|
-
const
|
|
700
|
-
|
|
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({ ...
|
|
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({ ...
|
|
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
|
-
|
|
761
|
-
.
|
|
762
|
-
|
|
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
|
-
|
|
795
|
-
if (!gs) {
|
|
847
|
+
if (!this._edgeRepo) {
|
|
796
848
|
return;
|
|
797
849
|
}
|
|
798
850
|
try {
|
|
799
|
-
|
|
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
|
-
|
|
811
|
-
if (!gs) {
|
|
862
|
+
if (!this._proposalRepo) {
|
|
812
863
|
return;
|
|
813
864
|
}
|
|
814
865
|
try {
|
|
815
|
-
|
|
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
|
-
|
|
14
|
-
|
|
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,
|
|
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
|
-
#
|
|
23
|
+
#sourceRefRepo;
|
|
24
|
+
#knowledgeRepo;
|
|
24
25
|
#signalBus;
|
|
25
26
|
#logger = Logger.getInstance();
|
|
26
27
|
#ttlMs;
|
|
27
|
-
constructor(projectRoot,
|
|
28
|
+
constructor(projectRoot, sourceRefRepo, knowledgeRepo, options) {
|
|
28
29
|
this.#projectRoot = projectRoot;
|
|
29
|
-
this.#
|
|
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
|
-
//
|
|
47
|
-
this.#
|
|
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.#
|
|
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.#
|
|
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.
|
|
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.#
|
|
93
|
-
.
|
|
94
|
-
|
|
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.#
|
|
99
|
-
.
|
|
100
|
-
|
|
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.#
|
|
108
|
-
.
|
|
109
|
-
|
|
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.#
|
|
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.
|
|
155
|
+
const staleRatio = row.staleCount / row.totalCount;
|
|
151
156
|
this.#signalBus.send('quality', 'SourceRefReconciler', staleRatio, {
|
|
152
|
-
target: row.
|
|
157
|
+
target: row.recipeId,
|
|
153
158
|
metadata: {
|
|
154
159
|
reason: 'source_ref_stale',
|
|
155
|
-
staleCount: row.
|
|
156
|
-
totalRefs: row.
|
|
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.#
|
|
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.
|
|
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.#
|
|
188
|
-
.
|
|
189
|
-
|
|
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.#
|
|
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
|
-
// 按
|
|
231
|
+
// 按 recipeId 分组
|
|
227
232
|
const byRecipe = new Map();
|
|
228
233
|
for (const row of renamedRows) {
|
|
229
|
-
if (!byRecipe.has(row.
|
|
230
|
-
byRecipe.set(row.
|
|
234
|
+
if (!byRecipe.has(row.recipeId)) {
|
|
235
|
+
byRecipe.set(row.recipeId, []);
|
|
231
236
|
}
|
|
232
|
-
byRecipe.get(row.
|
|
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.#
|
|
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.
|
|
266
|
+
const idx = sources.indexOf(rename.sourcePath);
|
|
264
267
|
if (idx >= 0) {
|
|
265
|
-
sources[idx] = rename.
|
|
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.#
|
|
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.#
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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';
|