autosnippet 3.3.7 → 3.3.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dashboard/dist/assets/icons-BMNb0V6L.js +1 -0
- package/dashboard/dist/assets/index-DEU4tJtP.js +112 -0
- package/dashboard/dist/assets/index-DHJ1Dj7u.css +1 -0
- package/dashboard/dist/index.html +3 -3
- package/dist/bin/cli.js +7 -4
- 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/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 +8 -2
- package/dist/lib/cli/AiScanService.js +4 -17
- package/dist/lib/cli/KnowledgeSyncService.d.ts +7 -37
- package/dist/lib/cli/KnowledgeSyncService.js +23 -51
- package/dist/lib/cli/SetupService.js +5 -4
- package/dist/lib/core/ast/ProjectGraph.js +5 -27
- package/dist/lib/core/discovery/CustomConfigDiscoverer.d.ts +0 -2
- package/dist/lib/core/discovery/CustomConfigDiscoverer.js +0 -2
- 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/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/mcp/McpServer.js +19 -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.js +10 -29
- package/dist/lib/external/mcp/handlers/bootstrap/pipeline/orchestrator.js +33 -16
- 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.js +1 -1
- package/dist/lib/external/mcp/handlers/dimension-complete-external.js +7 -3
- package/dist/lib/external/mcp/handlers/evolve-external.d.ts +1 -0
- package/dist/lib/external/mcp/handlers/evolve-external.js +13 -16
- package/dist/lib/external/mcp/handlers/guard.js +15 -24
- 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 +11 -13
- package/dist/lib/http/routes/guardReport.js +3 -5
- package/dist/lib/http/routes/panorama.js +12 -12
- package/dist/lib/http/routes/recipes.js +59 -8
- package/dist/lib/http/routes/remote.js +3 -13
- package/dist/lib/http/routes/search.js +11 -8
- 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/DatabaseConnection.js +7 -6
- 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.js +7 -4
- package/dist/lib/injection/ServiceMap.d.ts +20 -0
- 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.js +8 -4
- package/dist/lib/service/delivery/CursorDeliveryPipeline.js +21 -9
- 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 +39 -63
- package/dist/lib/service/knowledge/CodeEntityGraph.js +418 -512
- 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 +76 -38
- 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/panorama/CouplingAnalyzer.d.ts +5 -3
- package/dist/lib/service/panorama/CouplingAnalyzer.js +102 -39
- package/dist/lib/service/panorama/DimensionAnalyzer.d.ts +7 -4
- package/dist/lib/service/panorama/DimensionAnalyzer.js +72 -25
- package/dist/lib/service/panorama/LayerInferrer.js +1 -1
- package/dist/lib/service/panorama/ModuleDiscoverer.d.ts +7 -6
- package/dist/lib/service/panorama/ModuleDiscoverer.js +174 -82
- package/dist/lib/service/panorama/PanoramaAggregator.d.ts +10 -3
- package/dist/lib/service/panorama/PanoramaAggregator.js +67 -79
- package/dist/lib/service/panorama/PanoramaScanner.d.ts +5 -1
- package/dist/lib/service/panorama/PanoramaScanner.js +32 -31
- package/dist/lib/service/panorama/PanoramaService.d.ts +11 -8
- package/dist/lib/service/panorama/PanoramaService.js +41 -66
- package/dist/lib/service/panorama/PanoramaTypes.d.ts +3 -0
- package/dist/lib/service/panorama/RoleRefiner.d.ts +8 -5
- package/dist/lib/service/panorama/RoleRefiner.js +52 -283
- package/dist/lib/service/panorama/TechStackProfiler.js +7 -119
- 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 +5 -8
- package/dist/lib/service/skills/SignalCollector.js +28 -55
- package/dist/lib/service/skills/SkillAdvisor.d.ts +7 -13
- package/dist/lib/service/skills/SkillAdvisor.js +30 -79
- 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 +16 -0
- package/dist/lib/shared/PathGuard.js +16 -10
- package/dist/lib/shared/constants.d.ts +19 -19
- package/dist/lib/shared/constants.js +10 -10
- package/dist/lib/shared/isOwnDevRepo.d.ts +29 -4
- package/dist/lib/shared/isOwnDevRepo.js +64 -4
- package/dist/lib/shared/schemas/mcp-tools.d.ts +1 -1
- 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-FHns2ypa.js +0 -1
- package/dashboard/dist/assets/index-BRJv5Y3r.js +0 -135
- package/dashboard/dist/assets/index-DzoB7kxK.css +0 -1
- package/dist/lib/repository/base/BaseRepository.d.ts +0 -53
- package/dist/lib/repository/base/BaseRepository.js +0 -226
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* KnowledgeEdgeRepository — 知识图谱边的仓储实现
|
|
3
|
+
*
|
|
4
|
+
* 从 KnowledgeGraphService 提取的数据操作,使用 Drizzle 类型安全 API。
|
|
5
|
+
* KnowledgeGraphService 将从直接 db.prepare() 迁移为调用此 Repository。
|
|
6
|
+
*/
|
|
7
|
+
import { knowledgeEdges } from '../../infrastructure/database/drizzle/schema.js';
|
|
8
|
+
import { type DrizzleTx, RepositoryBase } from '../base/RepositoryBase.js';
|
|
9
|
+
export interface KnowledgeEdge {
|
|
10
|
+
id: number;
|
|
11
|
+
fromId: string;
|
|
12
|
+
fromType: string;
|
|
13
|
+
toId: string;
|
|
14
|
+
toType: string;
|
|
15
|
+
relation: string;
|
|
16
|
+
weight: number;
|
|
17
|
+
metadata: Record<string, unknown>;
|
|
18
|
+
createdAt: number;
|
|
19
|
+
updatedAt: number;
|
|
20
|
+
}
|
|
21
|
+
export interface EdgeInsert {
|
|
22
|
+
fromId: string;
|
|
23
|
+
fromType?: string;
|
|
24
|
+
toId: string;
|
|
25
|
+
toType?: string;
|
|
26
|
+
relation: string;
|
|
27
|
+
weight?: number;
|
|
28
|
+
metadata?: Record<string, unknown>;
|
|
29
|
+
}
|
|
30
|
+
export interface EdgeStats {
|
|
31
|
+
totalEdges: number;
|
|
32
|
+
byRelation: Record<string, number>;
|
|
33
|
+
nodeTypes: string[];
|
|
34
|
+
}
|
|
35
|
+
export declare class KnowledgeEdgeRepositoryImpl extends RepositoryBase<typeof knowledgeEdges, KnowledgeEdge> {
|
|
36
|
+
#private;
|
|
37
|
+
constructor(drizzle: ConstructorParameters<typeof RepositoryBase<typeof knowledgeEdges, KnowledgeEdge>>[0]);
|
|
38
|
+
findById(id: number): Promise<KnowledgeEdge | null>;
|
|
39
|
+
create(data: EdgeInsert): Promise<KnowledgeEdge>;
|
|
40
|
+
delete(id: number): Promise<boolean>;
|
|
41
|
+
/** INSERT OR REPLACE — 按 (fromId, fromType, toId, toType, relation) 唯一约束 upsert */
|
|
42
|
+
upsertEdge(edge: EdgeInsert): Promise<KnowledgeEdge>;
|
|
43
|
+
/** 删除指定的边 */
|
|
44
|
+
removeEdge(fromId: string, fromType: string, toId: string, toType: string, relation: string): Promise<void>;
|
|
45
|
+
/** 查询指定节点的出边 */
|
|
46
|
+
findOutgoing(nodeId: string, nodeType: string): Promise<KnowledgeEdge[]>;
|
|
47
|
+
/** 查询指定节点的入边 */
|
|
48
|
+
findIncoming(nodeId: string, nodeType: string): Promise<KnowledgeEdge[]>;
|
|
49
|
+
/** 查询指定节点的入边(仅限指定关系类型) */
|
|
50
|
+
findIncomingByRelations(nodeId: string, nodeType: string, relations: string[]): Promise<KnowledgeEdge[]>;
|
|
51
|
+
/** 查询指定节点按特定关系的入边 */
|
|
52
|
+
findIncomingByRelation(nodeId: string, relation: string): Promise<KnowledgeEdge[]>;
|
|
53
|
+
/** 查询指定节点按特定关系的出边 */
|
|
54
|
+
findOutgoingByRelation(nodeId: string, relation: string): Promise<KnowledgeEdge[]>;
|
|
55
|
+
/** 查询指定节点按关系+类型条件的出边(仅 to_id 字段) */
|
|
56
|
+
findOutgoingToId(fromId: string, fromType: string, relation: string): Promise<string | null>;
|
|
57
|
+
/** 查询指定节点按多类型条件的入边 */
|
|
58
|
+
findIncomingByFromTypes(toId: string, toType: string, relation: string): Promise<KnowledgeEdge[]>;
|
|
59
|
+
/** 查询 from_id 在指定类型列表中的遵循边 — 用于 getConformances */
|
|
60
|
+
findConformances(fromId: string): Promise<string[]>;
|
|
61
|
+
/** 按关系分组统计 */
|
|
62
|
+
countByRelation(): Promise<Record<string, number>>;
|
|
63
|
+
/** 获取入度最高的节点(被引用最多),排除多语言基类和框架根类 */
|
|
64
|
+
getHotNodes(limit?: number): Promise<{
|
|
65
|
+
id: string;
|
|
66
|
+
type: string;
|
|
67
|
+
inDegree: number;
|
|
68
|
+
}[]>;
|
|
69
|
+
/** 按关系类型统计某节点的入边数 */
|
|
70
|
+
countIncomingByRelation(toId: string, relation: string): Promise<number>;
|
|
71
|
+
/** 按关系类型查询总数 */
|
|
72
|
+
countByRelationType(relation: string): Promise<number>;
|
|
73
|
+
/** 按 metadata_json LIKE 模式删除边(可选过滤关系类型) */
|
|
74
|
+
deleteByMetadataLike(pattern: string, relations?: string[]): Promise<number>;
|
|
75
|
+
/** 删除指定节点的所有出边(按 fromId + fromType) */
|
|
76
|
+
deleteOutgoing(fromId: string, fromType: string): Promise<number>;
|
|
77
|
+
/** 根据 entry ID 删除所有相关边(用于知识删除时清理图谱) */
|
|
78
|
+
deleteByEntryId(entryId: string): Promise<number>;
|
|
79
|
+
/** 按关系类型查询 */
|
|
80
|
+
findByRelation(nodeId: string, nodeType: string, relation: string): Promise<KnowledgeEdge[]>;
|
|
81
|
+
/** 获取所有边(可选类型过滤 + 限制数量) */
|
|
82
|
+
findAll(options?: {
|
|
83
|
+
nodeType?: string;
|
|
84
|
+
limit?: number;
|
|
85
|
+
}): Promise<KnowledgeEdge[]>;
|
|
86
|
+
/** 统计信息 */
|
|
87
|
+
getStats(nodeType?: string): Promise<EdgeStats>;
|
|
88
|
+
/** 在事务中执行批量边操作 */
|
|
89
|
+
batchInTransaction(fn: (tx: DrizzleTx) => void): Promise<void>;
|
|
90
|
+
/**
|
|
91
|
+
* 统计 knowledge_edges JOIN code_entities 的边数 (fan-in/fan-out 分析)
|
|
92
|
+
* direction='from': JOIN on from_id 侧 (fan-out: 模块内实体发出的边)
|
|
93
|
+
* direction='to': JOIN on to_id 侧 (fan-in: 模块内实体接收的边)
|
|
94
|
+
*/
|
|
95
|
+
countEdgesJoinedByEntityFiles(projectRoot: string, filePaths: string[], relation: string, direction: 'from' | 'to'): Promise<number>;
|
|
96
|
+
/**
|
|
97
|
+
* 查询实体使用的设计模式名称 (uses_pattern 边)
|
|
98
|
+
* 限定实体在指定项目的指定文件路径内
|
|
99
|
+
*/
|
|
100
|
+
findPatternsUsedByEntities(projectRoot: string, filePaths: string[]): Promise<string[]>;
|
|
101
|
+
/** 最频繁被调用的节点 (calls 关系 GROUP BY to_id) */
|
|
102
|
+
findTopCalledNodes(limit: number): Promise<Array<{
|
|
103
|
+
toId: string;
|
|
104
|
+
callCount: number;
|
|
105
|
+
}>>;
|
|
106
|
+
/** 入口点: 只有 calls 出度没有 calls 入度的节点 */
|
|
107
|
+
findEntryPoints(limit: number): Promise<string[]>;
|
|
108
|
+
/** 数据生产者: data_flow 出度 > threshold 的节点 */
|
|
109
|
+
findTopDataFlowSources(limit: number, threshold: number): Promise<string[]>;
|
|
110
|
+
/** 数据消费者: data_flow 入度 > threshold 的节点 */
|
|
111
|
+
findTopDataFlowSinks(limit: number, threshold: number): Promise<string[]>;
|
|
112
|
+
/**
|
|
113
|
+
* 查询指定关系的边,过滤条件:from 侧是 module 或在指定项目的 code_entities 中存在
|
|
114
|
+
* (用于 CouplingAnalyzer 构建模块间依赖边)
|
|
115
|
+
*/
|
|
116
|
+
findEdgesFilteredByEntityExistence(relation: string, projectRoot: string): Promise<Array<{
|
|
117
|
+
fromId: string;
|
|
118
|
+
fromType: string;
|
|
119
|
+
toId: string;
|
|
120
|
+
toType: string;
|
|
121
|
+
}>>;
|
|
122
|
+
/** 查询 module→module 的 depends_on 边 (fromId, toId) */
|
|
123
|
+
findModuleDependencyPairs(): Promise<Array<{
|
|
124
|
+
fromId: string;
|
|
125
|
+
toId: string;
|
|
126
|
+
}>>;
|
|
127
|
+
/** 批量 INSERT OR IGNORE 边 (不更新已存在的行) */
|
|
128
|
+
bulkInsertIgnore(edges: EdgeInsert[]): Promise<number>;
|
|
129
|
+
}
|
|
@@ -0,0 +1,475 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* KnowledgeEdgeRepository — 知识图谱边的仓储实现
|
|
3
|
+
*
|
|
4
|
+
* 从 KnowledgeGraphService 提取的数据操作,使用 Drizzle 类型安全 API。
|
|
5
|
+
* KnowledgeGraphService 将从直接 db.prepare() 迁移为调用此 Repository。
|
|
6
|
+
*/
|
|
7
|
+
import { and, count, desc, eq, exists, inArray, like, or, sql } from 'drizzle-orm';
|
|
8
|
+
import { codeEntities, knowledgeEdges } from '../../infrastructure/database/drizzle/schema.js';
|
|
9
|
+
import { LanguageProfiles } from '../../shared/LanguageProfiles.js';
|
|
10
|
+
import { unixNow } from '../../shared/utils/common.js';
|
|
11
|
+
import { RepositoryBase } from '../base/RepositoryBase.js';
|
|
12
|
+
/* ═══ Repository 实现 ═══ */
|
|
13
|
+
export class KnowledgeEdgeRepositoryImpl extends RepositoryBase {
|
|
14
|
+
constructor(drizzle) {
|
|
15
|
+
super(drizzle, knowledgeEdges);
|
|
16
|
+
}
|
|
17
|
+
/* ─── CRUD ─── */
|
|
18
|
+
async findById(id) {
|
|
19
|
+
const rows = this.drizzle.select().from(this.table).where(eq(this.table.id, id)).limit(1).all();
|
|
20
|
+
return rows.length > 0 ? this.#mapRow(rows[0]) : null;
|
|
21
|
+
}
|
|
22
|
+
async create(data) {
|
|
23
|
+
return this.upsertEdge(data);
|
|
24
|
+
}
|
|
25
|
+
async delete(id) {
|
|
26
|
+
const result = this.drizzle.delete(this.table).where(eq(this.table.id, id)).run();
|
|
27
|
+
return result.changes > 0;
|
|
28
|
+
}
|
|
29
|
+
/* ─── 核心操作 ─── */
|
|
30
|
+
/** INSERT OR REPLACE — 按 (fromId, fromType, toId, toType, relation) 唯一约束 upsert */
|
|
31
|
+
async upsertEdge(edge) {
|
|
32
|
+
const now = unixNow();
|
|
33
|
+
const metaJson = JSON.stringify(edge.metadata ?? {});
|
|
34
|
+
this.drizzle
|
|
35
|
+
.insert(this.table)
|
|
36
|
+
.values({
|
|
37
|
+
fromId: edge.fromId,
|
|
38
|
+
fromType: edge.fromType ?? 'recipe',
|
|
39
|
+
toId: edge.toId,
|
|
40
|
+
toType: edge.toType ?? 'recipe',
|
|
41
|
+
relation: edge.relation,
|
|
42
|
+
weight: edge.weight ?? 1.0,
|
|
43
|
+
metadataJson: metaJson,
|
|
44
|
+
createdAt: now,
|
|
45
|
+
updatedAt: now,
|
|
46
|
+
})
|
|
47
|
+
.onConflictDoUpdate({
|
|
48
|
+
target: [
|
|
49
|
+
this.table.fromId,
|
|
50
|
+
this.table.fromType,
|
|
51
|
+
this.table.toId,
|
|
52
|
+
this.table.toType,
|
|
53
|
+
this.table.relation,
|
|
54
|
+
],
|
|
55
|
+
set: {
|
|
56
|
+
weight: sql `${edge.weight ?? 1.0}`,
|
|
57
|
+
metadataJson: metaJson,
|
|
58
|
+
updatedAt: now,
|
|
59
|
+
},
|
|
60
|
+
})
|
|
61
|
+
.run();
|
|
62
|
+
// 返回 upserted 行
|
|
63
|
+
const rows = this.drizzle
|
|
64
|
+
.select()
|
|
65
|
+
.from(this.table)
|
|
66
|
+
.where(and(eq(this.table.fromId, edge.fromId), eq(this.table.fromType, edge.fromType ?? 'recipe'), eq(this.table.toId, edge.toId), eq(this.table.toType, edge.toType ?? 'recipe'), eq(this.table.relation, edge.relation)))
|
|
67
|
+
.limit(1)
|
|
68
|
+
.all();
|
|
69
|
+
return this.#mapRow(rows[0]);
|
|
70
|
+
}
|
|
71
|
+
/** 删除指定的边 */
|
|
72
|
+
async removeEdge(fromId, fromType, toId, toType, relation) {
|
|
73
|
+
this.drizzle
|
|
74
|
+
.delete(this.table)
|
|
75
|
+
.where(and(eq(this.table.fromId, fromId), eq(this.table.fromType, fromType), eq(this.table.toId, toId), eq(this.table.toType, toType), eq(this.table.relation, relation)))
|
|
76
|
+
.run();
|
|
77
|
+
}
|
|
78
|
+
/* ─── 查询 ─── */
|
|
79
|
+
/** 查询指定节点的出边 */
|
|
80
|
+
async findOutgoing(nodeId, nodeType) {
|
|
81
|
+
const rows = this.drizzle
|
|
82
|
+
.select()
|
|
83
|
+
.from(this.table)
|
|
84
|
+
.where(and(eq(this.table.fromId, nodeId), eq(this.table.fromType, nodeType)))
|
|
85
|
+
.all();
|
|
86
|
+
return rows.map((r) => this.#mapRow(r));
|
|
87
|
+
}
|
|
88
|
+
/** 查询指定节点的入边 */
|
|
89
|
+
async findIncoming(nodeId, nodeType) {
|
|
90
|
+
const rows = this.drizzle
|
|
91
|
+
.select()
|
|
92
|
+
.from(this.table)
|
|
93
|
+
.where(and(eq(this.table.toId, nodeId), eq(this.table.toType, nodeType)))
|
|
94
|
+
.all();
|
|
95
|
+
return rows.map((r) => this.#mapRow(r));
|
|
96
|
+
}
|
|
97
|
+
/** 查询指定节点的入边(仅限指定关系类型) */
|
|
98
|
+
async findIncomingByRelations(nodeId, nodeType, relations) {
|
|
99
|
+
const rows = this.drizzle
|
|
100
|
+
.select()
|
|
101
|
+
.from(this.table)
|
|
102
|
+
.where(and(eq(this.table.toId, nodeId), eq(this.table.toType, nodeType), inArray(this.table.relation, relations)))
|
|
103
|
+
.all();
|
|
104
|
+
return rows.map((r) => this.#mapRow(r));
|
|
105
|
+
}
|
|
106
|
+
/** 查询指定节点按特定关系的入边 */
|
|
107
|
+
async findIncomingByRelation(nodeId, relation) {
|
|
108
|
+
const rows = this.drizzle
|
|
109
|
+
.select()
|
|
110
|
+
.from(this.table)
|
|
111
|
+
.where(and(eq(this.table.toId, nodeId), eq(this.table.relation, relation)))
|
|
112
|
+
.all();
|
|
113
|
+
return rows.map((r) => this.#mapRow(r));
|
|
114
|
+
}
|
|
115
|
+
/** 查询指定节点按特定关系的出边 */
|
|
116
|
+
async findOutgoingByRelation(nodeId, relation) {
|
|
117
|
+
const rows = this.drizzle
|
|
118
|
+
.select()
|
|
119
|
+
.from(this.table)
|
|
120
|
+
.where(and(eq(this.table.fromId, nodeId), eq(this.table.relation, relation)))
|
|
121
|
+
.all();
|
|
122
|
+
return rows.map((r) => this.#mapRow(r));
|
|
123
|
+
}
|
|
124
|
+
/** 查询指定节点按关系+类型条件的出边(仅 to_id 字段) */
|
|
125
|
+
async findOutgoingToId(fromId, fromType, relation) {
|
|
126
|
+
const rows = this.drizzle
|
|
127
|
+
.select({ toId: this.table.toId })
|
|
128
|
+
.from(this.table)
|
|
129
|
+
.where(and(eq(this.table.fromId, fromId), eq(this.table.fromType, fromType), eq(this.table.relation, relation)))
|
|
130
|
+
.limit(1)
|
|
131
|
+
.all();
|
|
132
|
+
return rows.length > 0 ? rows[0].toId : null;
|
|
133
|
+
}
|
|
134
|
+
/** 查询指定节点按多类型条件的入边 */
|
|
135
|
+
async findIncomingByFromTypes(toId, toType, relation) {
|
|
136
|
+
const rows = this.drizzle
|
|
137
|
+
.select()
|
|
138
|
+
.from(this.table)
|
|
139
|
+
.where(and(eq(this.table.toId, toId), eq(this.table.toType, toType), eq(this.table.relation, relation)))
|
|
140
|
+
.all();
|
|
141
|
+
return rows.map((r) => this.#mapRow(r));
|
|
142
|
+
}
|
|
143
|
+
/** 查询 from_id 在指定类型列表中的遵循边 — 用于 getConformances */
|
|
144
|
+
async findConformances(fromId) {
|
|
145
|
+
const rows = this.drizzle
|
|
146
|
+
.select({ toId: this.table.toId })
|
|
147
|
+
.from(this.table)
|
|
148
|
+
.where(and(eq(this.table.fromId, fromId), inArray(this.table.fromType, ['class', 'category']), eq(this.table.relation, 'conforms')))
|
|
149
|
+
.all();
|
|
150
|
+
return rows.map((r) => r.toId);
|
|
151
|
+
}
|
|
152
|
+
/** 按关系分组统计 */
|
|
153
|
+
async countByRelation() {
|
|
154
|
+
const rows = this.drizzle
|
|
155
|
+
.select({
|
|
156
|
+
relation: this.table.relation,
|
|
157
|
+
cnt: count(),
|
|
158
|
+
})
|
|
159
|
+
.from(this.table)
|
|
160
|
+
.groupBy(this.table.relation)
|
|
161
|
+
.all();
|
|
162
|
+
const result = {};
|
|
163
|
+
for (const row of rows) {
|
|
164
|
+
result[row.relation] = row.cnt;
|
|
165
|
+
}
|
|
166
|
+
return result;
|
|
167
|
+
}
|
|
168
|
+
/** 获取入度最高的节点(被引用最多),排除多语言基类和框架根类 */
|
|
169
|
+
async getHotNodes(limit = 15) {
|
|
170
|
+
const exclusions = LanguageProfiles.baseClassExclusions;
|
|
171
|
+
const exclusionList = [...exclusions].map((v) => sql `${v}`);
|
|
172
|
+
const rows = this.drizzle
|
|
173
|
+
.select({
|
|
174
|
+
toId: this.table.toId,
|
|
175
|
+
toType: this.table.toType,
|
|
176
|
+
inDegree: count(),
|
|
177
|
+
})
|
|
178
|
+
.from(this.table)
|
|
179
|
+
.where(sql `${this.table.toId} NOT IN (${sql.join(exclusionList, sql `, `)})`)
|
|
180
|
+
.groupBy(this.table.toId, this.table.toType)
|
|
181
|
+
.orderBy(sql `count(*) DESC`)
|
|
182
|
+
.limit(limit)
|
|
183
|
+
.all();
|
|
184
|
+
return rows.map((r) => ({
|
|
185
|
+
id: r.toId,
|
|
186
|
+
type: r.toType,
|
|
187
|
+
inDegree: r.inDegree,
|
|
188
|
+
}));
|
|
189
|
+
}
|
|
190
|
+
/** 按关系类型统计某节点的入边数 */
|
|
191
|
+
async countIncomingByRelation(toId, relation) {
|
|
192
|
+
const [row] = this.drizzle
|
|
193
|
+
.select({ cnt: count() })
|
|
194
|
+
.from(this.table)
|
|
195
|
+
.where(and(eq(this.table.toId, toId), eq(this.table.relation, relation)))
|
|
196
|
+
.all();
|
|
197
|
+
return row?.cnt ?? 0;
|
|
198
|
+
}
|
|
199
|
+
/** 按关系类型查询总数 */
|
|
200
|
+
async countByRelationType(relation) {
|
|
201
|
+
const [row] = this.drizzle
|
|
202
|
+
.select({ cnt: count() })
|
|
203
|
+
.from(this.table)
|
|
204
|
+
.where(eq(this.table.relation, relation))
|
|
205
|
+
.all();
|
|
206
|
+
return row?.cnt ?? 0;
|
|
207
|
+
}
|
|
208
|
+
/** 按 metadata_json LIKE 模式删除边(可选过滤关系类型) */
|
|
209
|
+
async deleteByMetadataLike(pattern, relations) {
|
|
210
|
+
const conditions = [like(this.table.metadataJson, pattern)];
|
|
211
|
+
if (relations?.length) {
|
|
212
|
+
conditions.push(inArray(this.table.relation, relations));
|
|
213
|
+
}
|
|
214
|
+
const result = this.drizzle
|
|
215
|
+
.delete(this.table)
|
|
216
|
+
.where(and(...conditions))
|
|
217
|
+
.run();
|
|
218
|
+
return result.changes;
|
|
219
|
+
}
|
|
220
|
+
/** 删除指定节点的所有出边(按 fromId + fromType) */
|
|
221
|
+
async deleteOutgoing(fromId, fromType) {
|
|
222
|
+
const result = this.drizzle
|
|
223
|
+
.delete(this.table)
|
|
224
|
+
.where(and(eq(this.table.fromId, fromId), eq(this.table.fromType, fromType)))
|
|
225
|
+
.run();
|
|
226
|
+
return result.changes;
|
|
227
|
+
}
|
|
228
|
+
/** 根据 entry ID 删除所有相关边(用于知识删除时清理图谱) */
|
|
229
|
+
async deleteByEntryId(entryId) {
|
|
230
|
+
const result = this.drizzle
|
|
231
|
+
.delete(this.table)
|
|
232
|
+
.where(or(eq(this.table.fromId, entryId), eq(this.table.toId, entryId)))
|
|
233
|
+
.run();
|
|
234
|
+
return result.changes;
|
|
235
|
+
}
|
|
236
|
+
/** 按关系类型查询 */
|
|
237
|
+
async findByRelation(nodeId, nodeType, relation) {
|
|
238
|
+
const rows = this.drizzle
|
|
239
|
+
.select()
|
|
240
|
+
.from(this.table)
|
|
241
|
+
.where(or(and(eq(this.table.fromId, nodeId), eq(this.table.fromType, nodeType), eq(this.table.relation, relation)), and(eq(this.table.toId, nodeId), eq(this.table.toType, nodeType), eq(this.table.relation, relation))))
|
|
242
|
+
.all();
|
|
243
|
+
return rows.map((r) => this.#mapRow(r));
|
|
244
|
+
}
|
|
245
|
+
/** 获取所有边(可选类型过滤 + 限制数量) */
|
|
246
|
+
async findAll(options = {}) {
|
|
247
|
+
const { nodeType, limit = 1000 } = options;
|
|
248
|
+
let query = this.drizzle.select().from(this.table);
|
|
249
|
+
if (nodeType) {
|
|
250
|
+
query = query.where(and(eq(this.table.fromType, nodeType), eq(this.table.toType, nodeType)));
|
|
251
|
+
}
|
|
252
|
+
const rows = query.limit(limit).all();
|
|
253
|
+
return rows.map((r) => this.#mapRow(r));
|
|
254
|
+
}
|
|
255
|
+
/** 统计信息 */
|
|
256
|
+
async getStats(nodeType) {
|
|
257
|
+
// 总数
|
|
258
|
+
const condition = nodeType
|
|
259
|
+
? and(eq(this.table.fromType, nodeType), eq(this.table.toType, nodeType))
|
|
260
|
+
: undefined;
|
|
261
|
+
const [totalRow] = this.drizzle
|
|
262
|
+
.select({ cnt: count() })
|
|
263
|
+
.from(this.table)
|
|
264
|
+
.where(condition)
|
|
265
|
+
.all();
|
|
266
|
+
const totalEdges = totalRow?.cnt ?? 0;
|
|
267
|
+
// 按关系类型分组
|
|
268
|
+
const relationRows = this.drizzle
|
|
269
|
+
.select({
|
|
270
|
+
relation: this.table.relation,
|
|
271
|
+
cnt: count(),
|
|
272
|
+
})
|
|
273
|
+
.from(this.table)
|
|
274
|
+
.where(condition)
|
|
275
|
+
.groupBy(this.table.relation)
|
|
276
|
+
.all();
|
|
277
|
+
const byRelation = {};
|
|
278
|
+
for (const row of relationRows) {
|
|
279
|
+
byRelation[row.relation] = row.cnt;
|
|
280
|
+
}
|
|
281
|
+
// 节点类型
|
|
282
|
+
const fromTypes = this.drizzle
|
|
283
|
+
.selectDistinct({ t: this.table.fromType })
|
|
284
|
+
.from(this.table)
|
|
285
|
+
.all()
|
|
286
|
+
.map((r) => r.t);
|
|
287
|
+
const toTypes = this.drizzle
|
|
288
|
+
.selectDistinct({ t: this.table.toType })
|
|
289
|
+
.from(this.table)
|
|
290
|
+
.all()
|
|
291
|
+
.map((r) => r.t);
|
|
292
|
+
const nodeTypes = [...new Set([...fromTypes, ...toTypes])];
|
|
293
|
+
return { totalEdges, byRelation, nodeTypes };
|
|
294
|
+
}
|
|
295
|
+
/** 在事务中执行批量边操作 */
|
|
296
|
+
async batchInTransaction(fn) {
|
|
297
|
+
this.transaction(fn);
|
|
298
|
+
}
|
|
299
|
+
/* ─── Panorama 域查询 (Phase 5e) ─── */
|
|
300
|
+
/**
|
|
301
|
+
* 统计 knowledge_edges JOIN code_entities 的边数 (fan-in/fan-out 分析)
|
|
302
|
+
* direction='from': JOIN on from_id 侧 (fan-out: 模块内实体发出的边)
|
|
303
|
+
* direction='to': JOIN on to_id 侧 (fan-in: 模块内实体接收的边)
|
|
304
|
+
*/
|
|
305
|
+
async countEdgesJoinedByEntityFiles(projectRoot, filePaths, relation, direction) {
|
|
306
|
+
if (filePaths.length === 0) {
|
|
307
|
+
return 0;
|
|
308
|
+
}
|
|
309
|
+
const joinOn = direction === 'from'
|
|
310
|
+
? and(eq(this.table.fromId, codeEntities.entityId), eq(this.table.fromType, codeEntities.entityType))
|
|
311
|
+
: and(eq(this.table.toId, codeEntities.entityId), eq(this.table.toType, codeEntities.entityType));
|
|
312
|
+
const [row] = this.drizzle
|
|
313
|
+
.select({ cnt: count() })
|
|
314
|
+
.from(this.table)
|
|
315
|
+
.innerJoin(codeEntities, joinOn)
|
|
316
|
+
.where(and(eq(codeEntities.projectRoot, projectRoot), inArray(codeEntities.filePath, filePaths), eq(this.table.relation, relation)))
|
|
317
|
+
.all();
|
|
318
|
+
return row?.cnt ?? 0;
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* 查询实体使用的设计模式名称 (uses_pattern 边)
|
|
322
|
+
* 限定实体在指定项目的指定文件路径内
|
|
323
|
+
*/
|
|
324
|
+
async findPatternsUsedByEntities(projectRoot, filePaths) {
|
|
325
|
+
if (filePaths.length === 0) {
|
|
326
|
+
return [];
|
|
327
|
+
}
|
|
328
|
+
const rows = this.drizzle
|
|
329
|
+
.select({ patternName: this.table.toId })
|
|
330
|
+
.from(this.table)
|
|
331
|
+
.innerJoin(codeEntities, eq(this.table.fromId, codeEntities.entityId))
|
|
332
|
+
.where(and(eq(codeEntities.projectRoot, projectRoot), eq(this.table.relation, 'uses_pattern'), inArray(codeEntities.filePath, filePaths)))
|
|
333
|
+
.all();
|
|
334
|
+
return rows.map((r) => r.patternName);
|
|
335
|
+
}
|
|
336
|
+
/** 最频繁被调用的节点 (calls 关系 GROUP BY to_id) */
|
|
337
|
+
async findTopCalledNodes(limit) {
|
|
338
|
+
const rows = this.drizzle
|
|
339
|
+
.select({
|
|
340
|
+
toId: this.table.toId,
|
|
341
|
+
callCount: count(),
|
|
342
|
+
})
|
|
343
|
+
.from(this.table)
|
|
344
|
+
.where(eq(this.table.relation, 'calls'))
|
|
345
|
+
.groupBy(this.table.toId)
|
|
346
|
+
.orderBy(desc(count()))
|
|
347
|
+
.limit(limit)
|
|
348
|
+
.all();
|
|
349
|
+
return rows.map((r) => ({ toId: r.toId, callCount: r.callCount }));
|
|
350
|
+
}
|
|
351
|
+
/** 入口点: 只有 calls 出度没有 calls 入度的节点 */
|
|
352
|
+
async findEntryPoints(limit) {
|
|
353
|
+
const rows = this.drizzle
|
|
354
|
+
.selectDistinct({ fromId: this.table.fromId })
|
|
355
|
+
.from(this.table)
|
|
356
|
+
.where(and(eq(this.table.relation, 'calls'), sql `${this.table.fromId} NOT IN (SELECT ${this.table.toId} FROM ${this.table} WHERE ${this.table.relation} = 'calls')`))
|
|
357
|
+
.limit(limit)
|
|
358
|
+
.all();
|
|
359
|
+
return rows.map((r) => r.fromId);
|
|
360
|
+
}
|
|
361
|
+
/** 数据生产者: data_flow 出度 > threshold 的节点 */
|
|
362
|
+
async findTopDataFlowSources(limit, threshold) {
|
|
363
|
+
const rows = this.drizzle
|
|
364
|
+
.select({
|
|
365
|
+
fromId: this.table.fromId,
|
|
366
|
+
outCnt: count(),
|
|
367
|
+
})
|
|
368
|
+
.from(this.table)
|
|
369
|
+
.where(eq(this.table.relation, 'data_flow'))
|
|
370
|
+
.groupBy(this.table.fromId)
|
|
371
|
+
.having(sql `count(*) > ${threshold}`)
|
|
372
|
+
.orderBy(desc(count()))
|
|
373
|
+
.limit(limit)
|
|
374
|
+
.all();
|
|
375
|
+
return rows.map((r) => r.fromId);
|
|
376
|
+
}
|
|
377
|
+
/** 数据消费者: data_flow 入度 > threshold 的节点 */
|
|
378
|
+
async findTopDataFlowSinks(limit, threshold) {
|
|
379
|
+
const rows = this.drizzle
|
|
380
|
+
.select({
|
|
381
|
+
toId: this.table.toId,
|
|
382
|
+
inCnt: count(),
|
|
383
|
+
})
|
|
384
|
+
.from(this.table)
|
|
385
|
+
.where(eq(this.table.relation, 'data_flow'))
|
|
386
|
+
.groupBy(this.table.toId)
|
|
387
|
+
.having(sql `count(*) > ${threshold}`)
|
|
388
|
+
.orderBy(desc(count()))
|
|
389
|
+
.limit(limit)
|
|
390
|
+
.all();
|
|
391
|
+
return rows.map((r) => r.toId);
|
|
392
|
+
}
|
|
393
|
+
/**
|
|
394
|
+
* 查询指定关系的边,过滤条件:from 侧是 module 或在指定项目的 code_entities 中存在
|
|
395
|
+
* (用于 CouplingAnalyzer 构建模块间依赖边)
|
|
396
|
+
*/
|
|
397
|
+
async findEdgesFilteredByEntityExistence(relation, projectRoot) {
|
|
398
|
+
const rows = this.drizzle
|
|
399
|
+
.select({
|
|
400
|
+
fromId: this.table.fromId,
|
|
401
|
+
fromType: this.table.fromType,
|
|
402
|
+
toId: this.table.toId,
|
|
403
|
+
toType: this.table.toType,
|
|
404
|
+
})
|
|
405
|
+
.from(this.table)
|
|
406
|
+
.where(and(eq(this.table.relation, relation), or(eq(this.table.fromType, 'module'), exists(this.drizzle
|
|
407
|
+
.select({ one: sql `1` })
|
|
408
|
+
.from(codeEntities)
|
|
409
|
+
.where(and(eq(codeEntities.entityId, this.table.fromId), eq(codeEntities.projectRoot, projectRoot)))))))
|
|
410
|
+
.all();
|
|
411
|
+
return rows;
|
|
412
|
+
}
|
|
413
|
+
/** 查询 module→module 的 depends_on 边 (fromId, toId) */
|
|
414
|
+
async findModuleDependencyPairs() {
|
|
415
|
+
const rows = this.drizzle
|
|
416
|
+
.select({
|
|
417
|
+
fromId: this.table.fromId,
|
|
418
|
+
toId: this.table.toId,
|
|
419
|
+
})
|
|
420
|
+
.from(this.table)
|
|
421
|
+
.where(and(eq(this.table.relation, 'depends_on'), eq(this.table.fromType, 'module'), eq(this.table.toType, 'module')))
|
|
422
|
+
.all();
|
|
423
|
+
return rows;
|
|
424
|
+
}
|
|
425
|
+
/** 批量 INSERT OR IGNORE 边 (不更新已存在的行) */
|
|
426
|
+
async bulkInsertIgnore(edges) {
|
|
427
|
+
if (edges.length === 0) {
|
|
428
|
+
return 0;
|
|
429
|
+
}
|
|
430
|
+
let inserted = 0;
|
|
431
|
+
const now = unixNow();
|
|
432
|
+
this.transaction((tx) => {
|
|
433
|
+
for (const edge of edges) {
|
|
434
|
+
tx.insert(this.table)
|
|
435
|
+
.values({
|
|
436
|
+
fromId: edge.fromId,
|
|
437
|
+
fromType: edge.fromType ?? 'entity',
|
|
438
|
+
toId: edge.toId,
|
|
439
|
+
toType: edge.toType ?? 'recipe',
|
|
440
|
+
relation: edge.relation,
|
|
441
|
+
weight: edge.weight ?? 1.0,
|
|
442
|
+
metadataJson: JSON.stringify(edge.metadata ?? {}),
|
|
443
|
+
createdAt: now,
|
|
444
|
+
updatedAt: now,
|
|
445
|
+
})
|
|
446
|
+
.onConflictDoNothing()
|
|
447
|
+
.run();
|
|
448
|
+
inserted++;
|
|
449
|
+
}
|
|
450
|
+
});
|
|
451
|
+
return inserted;
|
|
452
|
+
}
|
|
453
|
+
/* ─── 内部辅助 ─── */
|
|
454
|
+
#mapRow(row) {
|
|
455
|
+
let metadata = {};
|
|
456
|
+
try {
|
|
457
|
+
metadata = JSON.parse(row.metadataJson ?? '{}');
|
|
458
|
+
}
|
|
459
|
+
catch {
|
|
460
|
+
/* ignore parse errors */
|
|
461
|
+
}
|
|
462
|
+
return {
|
|
463
|
+
id: row.id,
|
|
464
|
+
fromId: row.fromId,
|
|
465
|
+
fromType: row.fromType,
|
|
466
|
+
toId: row.toId,
|
|
467
|
+
toType: row.toType,
|
|
468
|
+
relation: row.relation,
|
|
469
|
+
weight: row.weight ?? 1.0,
|
|
470
|
+
metadata,
|
|
471
|
+
createdAt: row.createdAt,
|
|
472
|
+
updatedAt: row.updatedAt,
|
|
473
|
+
};
|
|
474
|
+
}
|
|
475
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* KnowledgeFileStore — .md 文件操作接口
|
|
3
|
+
*
|
|
4
|
+
* 从 KnowledgeFileWriter 类提炼的接口,使文件操作可被 mock 测试。
|
|
5
|
+
* 实现类: KnowledgeFileWriter (lib/service/knowledge/KnowledgeFileWriter.ts)
|
|
6
|
+
*
|
|
7
|
+
* 设计原则:
|
|
8
|
+
* - .md 文件 = 唯一真相源 (Source of Truth)
|
|
9
|
+
* - DB = 索引缓存
|
|
10
|
+
* - 所有写操作必须经过此接口落盘为 .md 文件
|
|
11
|
+
*/
|
|
12
|
+
import type { KnowledgeEntry } from '../../domain/knowledge/KnowledgeEntry.js';
|
|
13
|
+
/**
|
|
14
|
+
* KnowledgeFileStore — .md 文件写操作接口
|
|
15
|
+
*/
|
|
16
|
+
export interface KnowledgeFileStore {
|
|
17
|
+
/** 序列化 entry 为 YAML frontmatter + Markdown body */
|
|
18
|
+
serialize(entry: KnowledgeEntry): string;
|
|
19
|
+
/** 写入 .md 文件到 candidates/ 或 recipes/(基于 lifecycle),返回文件路径 */
|
|
20
|
+
persist(entry: KnowledgeEntry): string | null;
|
|
21
|
+
/** 删除 .md 文件,成功返回 true */
|
|
22
|
+
remove(entry: KnowledgeEntry): boolean;
|
|
23
|
+
/** 生命周期变更时在 candidates/ ↔ recipes/ 间移动文件,返回新路径 */
|
|
24
|
+
moveOnLifecycleChange(entry: KnowledgeEntry): string | null;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* KnowledgeFileScanner — .md 文件扫描接口(从 KnowledgeSyncService 提炼)
|
|
28
|
+
*
|
|
29
|
+
* 扫描逻辑当前在 KnowledgeSyncService._collectMdFiles(),
|
|
30
|
+
* 解析逻辑是模块级 parseKnowledgeMarkdown() 函数。
|
|
31
|
+
*/
|
|
32
|
+
export interface KnowledgeFileScanner {
|
|
33
|
+
/** 扫描 candidates/ + recipes/ 下所有 .md 文件路径 */
|
|
34
|
+
scanAll(): string[];
|
|
35
|
+
/** 解析单个 .md 文件内容为字段对象 */
|
|
36
|
+
parse(content: string, relPath?: string): Record<string, unknown>;
|
|
37
|
+
/** 计算内容哈希(用于检测手工编辑) */
|
|
38
|
+
computeHash(content: string): string;
|
|
39
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* KnowledgeFileStore — .md 文件操作接口
|
|
3
|
+
*
|
|
4
|
+
* 从 KnowledgeFileWriter 类提炼的接口,使文件操作可被 mock 测试。
|
|
5
|
+
* 实现类: KnowledgeFileWriter (lib/service/knowledge/KnowledgeFileWriter.ts)
|
|
6
|
+
*
|
|
7
|
+
* 设计原则:
|
|
8
|
+
* - .md 文件 = 唯一真相源 (Source of Truth)
|
|
9
|
+
* - DB = 索引缓存
|
|
10
|
+
* - 所有写操作必须经过此接口落盘为 .md 文件
|
|
11
|
+
*/
|
|
12
|
+
export {};
|