@grafema/util 0.3.0-beta
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/LICENSE +190 -0
- package/dist/api/GraphAPI.d.ts +87 -0
- package/dist/api/GraphAPI.d.ts.map +1 -0
- package/dist/api/GraphAPI.js +212 -0
- package/dist/api/GraphAPI.js.map +1 -0
- package/dist/api/GuaranteeAPI.d.ts +147 -0
- package/dist/api/GuaranteeAPI.d.ts.map +1 -0
- package/dist/api/GuaranteeAPI.js +290 -0
- package/dist/api/GuaranteeAPI.js.map +1 -0
- package/dist/config/ConfigLoader.d.ts +214 -0
- package/dist/config/ConfigLoader.d.ts.map +1 -0
- package/dist/config/ConfigLoader.js +441 -0
- package/dist/config/ConfigLoader.js.map +1 -0
- package/dist/config/index.d.ts +6 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +5 -0
- package/dist/config/index.js.map +1 -0
- package/dist/core/CoverageAnalyzer.d.ts +65 -0
- package/dist/core/CoverageAnalyzer.d.ts.map +1 -0
- package/dist/core/CoverageAnalyzer.js +199 -0
- package/dist/core/CoverageAnalyzer.js.map +1 -0
- package/dist/core/FileExplainer.d.ts +101 -0
- package/dist/core/FileExplainer.d.ts.map +1 -0
- package/dist/core/FileExplainer.js +140 -0
- package/dist/core/FileExplainer.js.map +1 -0
- package/dist/core/FileOverview.d.ts +124 -0
- package/dist/core/FileOverview.d.ts.map +1 -0
- package/dist/core/FileOverview.js +279 -0
- package/dist/core/FileOverview.js.map +1 -0
- package/dist/core/GrafemaUri.d.ts +66 -0
- package/dist/core/GrafemaUri.d.ts.map +1 -0
- package/dist/core/GrafemaUri.js +191 -0
- package/dist/core/GrafemaUri.js.map +1 -0
- package/dist/core/GraphBackend.d.ts +158 -0
- package/dist/core/GraphBackend.d.ts.map +1 -0
- package/dist/core/GraphBackend.js +85 -0
- package/dist/core/GraphBackend.js.map +1 -0
- package/dist/core/GraphFreshnessChecker.d.ts +33 -0
- package/dist/core/GraphFreshnessChecker.d.ts.map +1 -0
- package/dist/core/GraphFreshnessChecker.js +104 -0
- package/dist/core/GraphFreshnessChecker.js.map +1 -0
- package/dist/core/GuaranteeManager.d.ts +254 -0
- package/dist/core/GuaranteeManager.d.ts.map +1 -0
- package/dist/core/GuaranteeManager.js +447 -0
- package/dist/core/GuaranteeManager.js.map +1 -0
- package/dist/core/HashUtils.d.ts +24 -0
- package/dist/core/HashUtils.d.ts.map +1 -0
- package/dist/core/HashUtils.js +46 -0
- package/dist/core/HashUtils.js.map +1 -0
- package/dist/core/IncrementalReanalyzer.d.ts +33 -0
- package/dist/core/IncrementalReanalyzer.d.ts.map +1 -0
- package/dist/core/IncrementalReanalyzer.js +67 -0
- package/dist/core/IncrementalReanalyzer.js.map +1 -0
- package/dist/core/ResourceRegistry.d.ts +17 -0
- package/dist/core/ResourceRegistry.d.ts.map +1 -0
- package/dist/core/ResourceRegistry.js +32 -0
- package/dist/core/ResourceRegistry.js.map +1 -0
- package/dist/core/SemanticId.d.ts +159 -0
- package/dist/core/SemanticId.d.ts.map +1 -0
- package/dist/core/SemanticId.js +291 -0
- package/dist/core/SemanticId.js.map +1 -0
- package/dist/core/VersionManager.d.ts +166 -0
- package/dist/core/VersionManager.d.ts.map +1 -0
- package/dist/core/VersionManager.js +239 -0
- package/dist/core/VersionManager.js.map +1 -0
- package/dist/core/brandNodeInternal.d.ts +14 -0
- package/dist/core/brandNodeInternal.d.ts.map +1 -0
- package/dist/core/brandNodeInternal.js +4 -0
- package/dist/core/brandNodeInternal.js.map +1 -0
- package/dist/core/nodes/GuaranteeNode.d.ts +76 -0
- package/dist/core/nodes/GuaranteeNode.d.ts.map +1 -0
- package/dist/core/nodes/GuaranteeNode.js +118 -0
- package/dist/core/nodes/GuaranteeNode.js.map +1 -0
- package/dist/core/nodes/IssueNode.d.ts +73 -0
- package/dist/core/nodes/IssueNode.d.ts.map +1 -0
- package/dist/core/nodes/IssueNode.js +130 -0
- package/dist/core/nodes/IssueNode.js.map +1 -0
- package/dist/core/nodes/NodeKind.d.ts +104 -0
- package/dist/core/nodes/NodeKind.d.ts.map +1 -0
- package/dist/core/nodes/NodeKind.js +166 -0
- package/dist/core/nodes/NodeKind.js.map +1 -0
- package/dist/diagnostics/DiagnosticCollector.d.ts +103 -0
- package/dist/diagnostics/DiagnosticCollector.d.ts.map +1 -0
- package/dist/diagnostics/DiagnosticCollector.js +133 -0
- package/dist/diagnostics/DiagnosticCollector.js.map +1 -0
- package/dist/diagnostics/DiagnosticReporter.d.ts +122 -0
- package/dist/diagnostics/DiagnosticReporter.d.ts.map +1 -0
- package/dist/diagnostics/DiagnosticReporter.js +300 -0
- package/dist/diagnostics/DiagnosticReporter.js.map +1 -0
- package/dist/diagnostics/DiagnosticWriter.d.ts +31 -0
- package/dist/diagnostics/DiagnosticWriter.d.ts.map +1 -0
- package/dist/diagnostics/DiagnosticWriter.js +44 -0
- package/dist/diagnostics/DiagnosticWriter.js.map +1 -0
- package/dist/diagnostics/categories.d.ts +57 -0
- package/dist/diagnostics/categories.d.ts.map +1 -0
- package/dist/diagnostics/categories.js +71 -0
- package/dist/diagnostics/categories.js.map +1 -0
- package/dist/diagnostics/index.d.ts +17 -0
- package/dist/diagnostics/index.d.ts.map +1 -0
- package/dist/diagnostics/index.js +15 -0
- package/dist/diagnostics/index.js.map +1 -0
- package/dist/errors/GrafemaError.d.ts +200 -0
- package/dist/errors/GrafemaError.d.ts.map +1 -0
- package/dist/errors/GrafemaError.js +209 -0
- package/dist/errors/GrafemaError.js.map +1 -0
- package/dist/index.d.ts +75 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +76 -0
- package/dist/index.js.map +1 -0
- package/dist/instructions/index.d.ts +8 -0
- package/dist/instructions/index.d.ts.map +1 -0
- package/dist/instructions/index.js +20 -0
- package/dist/instructions/index.js.map +1 -0
- package/dist/instructions/onboarding.md +133 -0
- package/dist/knowledge/KnowledgeBase.d.ts +113 -0
- package/dist/knowledge/KnowledgeBase.d.ts.map +1 -0
- package/dist/knowledge/KnowledgeBase.js +420 -0
- package/dist/knowledge/KnowledgeBase.js.map +1 -0
- package/dist/knowledge/SemanticAddressResolver.d.ts +59 -0
- package/dist/knowledge/SemanticAddressResolver.d.ts.map +1 -0
- package/dist/knowledge/SemanticAddressResolver.js +160 -0
- package/dist/knowledge/SemanticAddressResolver.js.map +1 -0
- package/dist/knowledge/git-ingest.d.ts +58 -0
- package/dist/knowledge/git-ingest.d.ts.map +1 -0
- package/dist/knowledge/git-ingest.js +301 -0
- package/dist/knowledge/git-ingest.js.map +1 -0
- package/dist/knowledge/git-queries.d.ts +86 -0
- package/dist/knowledge/git-queries.d.ts.map +1 -0
- package/dist/knowledge/git-queries.js +177 -0
- package/dist/knowledge/git-queries.js.map +1 -0
- package/dist/knowledge/index.d.ts +14 -0
- package/dist/knowledge/index.d.ts.map +1 -0
- package/dist/knowledge/index.js +10 -0
- package/dist/knowledge/index.js.map +1 -0
- package/dist/knowledge/parser.d.ts +39 -0
- package/dist/knowledge/parser.d.ts.map +1 -0
- package/dist/knowledge/parser.js +292 -0
- package/dist/knowledge/parser.js.map +1 -0
- package/dist/knowledge/types.d.ts +133 -0
- package/dist/knowledge/types.d.ts.map +1 -0
- package/dist/knowledge/types.js +8 -0
- package/dist/knowledge/types.js.map +1 -0
- package/dist/logging/Logger.d.ts +98 -0
- package/dist/logging/Logger.d.ts.map +1 -0
- package/dist/logging/Logger.js +274 -0
- package/dist/logging/Logger.js.map +1 -0
- package/dist/notation/archetypes.d.ts +36 -0
- package/dist/notation/archetypes.d.ts.map +1 -0
- package/dist/notation/archetypes.js +173 -0
- package/dist/notation/archetypes.js.map +1 -0
- package/dist/notation/fold.d.ts +25 -0
- package/dist/notation/fold.d.ts.map +1 -0
- package/dist/notation/fold.js +598 -0
- package/dist/notation/fold.js.map +1 -0
- package/dist/notation/index.d.ts +18 -0
- package/dist/notation/index.d.ts.map +1 -0
- package/dist/notation/index.js +16 -0
- package/dist/notation/index.js.map +1 -0
- package/dist/notation/lodExtractor.d.ts +32 -0
- package/dist/notation/lodExtractor.d.ts.map +1 -0
- package/dist/notation/lodExtractor.js +149 -0
- package/dist/notation/lodExtractor.js.map +1 -0
- package/dist/notation/nameShortener.d.ts +22 -0
- package/dist/notation/nameShortener.d.ts.map +1 -0
- package/dist/notation/nameShortener.js +24 -0
- package/dist/notation/nameShortener.js.map +1 -0
- package/dist/notation/perspectives.d.ts +11 -0
- package/dist/notation/perspectives.d.ts.map +1 -0
- package/dist/notation/perspectives.js +16 -0
- package/dist/notation/perspectives.js.map +1 -0
- package/dist/notation/renderer.d.ts +31 -0
- package/dist/notation/renderer.d.ts.map +1 -0
- package/dist/notation/renderer.js +315 -0
- package/dist/notation/renderer.js.map +1 -0
- package/dist/notation/traceRenderer.d.ts +39 -0
- package/dist/notation/traceRenderer.d.ts.map +1 -0
- package/dist/notation/traceRenderer.js +358 -0
- package/dist/notation/traceRenderer.js.map +1 -0
- package/dist/notation/types.d.ts +66 -0
- package/dist/notation/types.d.ts.map +1 -0
- package/dist/notation/types.js +10 -0
- package/dist/notation/types.js.map +1 -0
- package/dist/queries/NodeContext.d.ts +81 -0
- package/dist/queries/NodeContext.d.ts.map +1 -0
- package/dist/queries/NodeContext.js +196 -0
- package/dist/queries/NodeContext.js.map +1 -0
- package/dist/queries/findCallsInFunction.d.ts +62 -0
- package/dist/queries/findCallsInFunction.d.ts.map +1 -0
- package/dist/queries/findCallsInFunction.js +169 -0
- package/dist/queries/findCallsInFunction.js.map +1 -0
- package/dist/queries/findContainingFunction.d.ts +57 -0
- package/dist/queries/findContainingFunction.d.ts.map +1 -0
- package/dist/queries/findContainingFunction.js +91 -0
- package/dist/queries/findContainingFunction.js.map +1 -0
- package/dist/queries/index.d.ts +18 -0
- package/dist/queries/index.d.ts.map +1 -0
- package/dist/queries/index.js +14 -0
- package/dist/queries/index.js.map +1 -0
- package/dist/queries/traceDataflow.d.ts +65 -0
- package/dist/queries/traceDataflow.d.ts.map +1 -0
- package/dist/queries/traceDataflow.js +754 -0
- package/dist/queries/traceDataflow.js.map +1 -0
- package/dist/queries/traceValues.d.ts +70 -0
- package/dist/queries/traceValues.d.ts.map +1 -0
- package/dist/queries/traceValues.js +373 -0
- package/dist/queries/traceValues.js.map +1 -0
- package/dist/queries/types.d.ts +166 -0
- package/dist/queries/types.d.ts.map +1 -0
- package/dist/queries/types.js +10 -0
- package/dist/queries/types.js.map +1 -0
- package/dist/schema/GraphSchemaExtractor.d.ts +53 -0
- package/dist/schema/GraphSchemaExtractor.d.ts.map +1 -0
- package/dist/schema/GraphSchemaExtractor.js +125 -0
- package/dist/schema/GraphSchemaExtractor.js.map +1 -0
- package/dist/schema/InterfaceSchemaExtractor.d.ts +73 -0
- package/dist/schema/InterfaceSchemaExtractor.d.ts.map +1 -0
- package/dist/schema/InterfaceSchemaExtractor.js +113 -0
- package/dist/schema/InterfaceSchemaExtractor.js.map +1 -0
- package/dist/schema/index.d.ts +5 -0
- package/dist/schema/index.d.ts.map +1 -0
- package/dist/schema/index.js +3 -0
- package/dist/schema/index.js.map +1 -0
- package/dist/storage/backends/RFDBServerBackend.d.ts +356 -0
- package/dist/storage/backends/RFDBServerBackend.d.ts.map +1 -0
- package/dist/storage/backends/RFDBServerBackend.js +748 -0
- package/dist/storage/backends/RFDBServerBackend.js.map +1 -0
- package/dist/storage/backends/typeValidation.d.ts +47 -0
- package/dist/storage/backends/typeValidation.d.ts.map +1 -0
- package/dist/storage/backends/typeValidation.js +141 -0
- package/dist/storage/backends/typeValidation.js.map +1 -0
- package/dist/utils/findRfdbBinary.d.ts +67 -0
- package/dist/utils/findRfdbBinary.d.ts.map +1 -0
- package/dist/utils/findRfdbBinary.js +261 -0
- package/dist/utils/findRfdbBinary.js.map +1 -0
- package/dist/utils/lazyDownload.d.ts +43 -0
- package/dist/utils/lazyDownload.d.ts.map +1 -0
- package/dist/utils/lazyDownload.js +175 -0
- package/dist/utils/lazyDownload.js.map +1 -0
- package/dist/utils/moduleResolution.d.ts +134 -0
- package/dist/utils/moduleResolution.d.ts.map +1 -0
- package/dist/utils/moduleResolution.js +189 -0
- package/dist/utils/moduleResolution.js.map +1 -0
- package/dist/utils/resolveNodeFile.d.ts +13 -0
- package/dist/utils/resolveNodeFile.d.ts.map +1 -0
- package/dist/utils/resolveNodeFile.js +18 -0
- package/dist/utils/resolveNodeFile.js.map +1 -0
- package/dist/utils/startRfdbServer.d.ts +63 -0
- package/dist/utils/startRfdbServer.d.ts.map +1 -0
- package/dist/utils/startRfdbServer.js +142 -0
- package/dist/utils/startRfdbServer.js.map +1 -0
- package/dist/validation/PathValidator.d.ts +80 -0
- package/dist/validation/PathValidator.d.ts.map +1 -0
- package/dist/validation/PathValidator.js +252 -0
- package/dist/validation/PathValidator.js.map +1 -0
- package/dist/version.d.ts +11 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +26 -0
- package/dist/version.js.map +1 -0
- package/package.json +50 -0
- package/src/api/GraphAPI.ts +307 -0
- package/src/api/GuaranteeAPI.ts +402 -0
- package/src/config/ConfigLoader.ts +653 -0
- package/src/config/index.ts +13 -0
- package/src/core/CoverageAnalyzer.ts +243 -0
- package/src/core/FileExplainer.ts +179 -0
- package/src/core/FileOverview.ts +397 -0
- package/src/core/GrafemaUri.ts +216 -0
- package/src/core/GraphBackend.ts +266 -0
- package/src/core/GraphFreshnessChecker.ts +145 -0
- package/src/core/GuaranteeManager.ts +684 -0
- package/src/core/HashUtils.ts +48 -0
- package/src/core/IncrementalReanalyzer.ts +106 -0
- package/src/core/ResourceRegistry.ts +39 -0
- package/src/core/SemanticId.ts +423 -0
- package/src/core/VersionManager.ts +405 -0
- package/src/core/brandNodeInternal.ts +16 -0
- package/src/core/nodes/GuaranteeNode.ts +162 -0
- package/src/core/nodes/IssueNode.ts +177 -0
- package/src/core/nodes/NodeKind.ts +192 -0
- package/src/diagnostics/DiagnosticCollector.ts +170 -0
- package/src/diagnostics/DiagnosticReporter.ts +395 -0
- package/src/diagnostics/DiagnosticWriter.ts +50 -0
- package/src/diagnostics/categories.ts +104 -0
- package/src/diagnostics/index.ts +30 -0
- package/src/errors/GrafemaError.ts +297 -0
- package/src/index.ts +261 -0
- package/src/instructions/index.ts +21 -0
- package/src/instructions/onboarding.md +133 -0
- package/src/knowledge/KnowledgeBase.ts +486 -0
- package/src/knowledge/SemanticAddressResolver.ts +191 -0
- package/src/knowledge/git-ingest.ts +402 -0
- package/src/knowledge/git-queries.ts +269 -0
- package/src/knowledge/index.ts +29 -0
- package/src/knowledge/parser.ts +294 -0
- package/src/knowledge/types.ts +146 -0
- package/src/logging/Logger.ts +303 -0
- package/src/notation/archetypes.ts +189 -0
- package/src/notation/fold.ts +696 -0
- package/src/notation/index.ts +27 -0
- package/src/notation/lodExtractor.ts +177 -0
- package/src/notation/nameShortener.ts +24 -0
- package/src/notation/perspectives.ts +18 -0
- package/src/notation/renderer.ts +394 -0
- package/src/notation/traceRenderer.ts +458 -0
- package/src/notation/types.ts +79 -0
- package/src/queries/NodeContext.ts +280 -0
- package/src/queries/findCallsInFunction.ts +249 -0
- package/src/queries/findContainingFunction.ts +124 -0
- package/src/queries/index.ts +44 -0
- package/src/queries/traceDataflow.ts +838 -0
- package/src/queries/traceValues.ts +531 -0
- package/src/queries/types.ts +191 -0
- package/src/schema/GraphSchemaExtractor.ts +177 -0
- package/src/schema/InterfaceSchemaExtractor.ts +173 -0
- package/src/schema/index.ts +5 -0
- package/src/storage/backends/RFDBServerBackend.ts +895 -0
- package/src/storage/backends/typeValidation.ts +154 -0
- package/src/utils/findRfdbBinary.ts +288 -0
- package/src/utils/lazyDownload.ts +206 -0
- package/src/utils/moduleResolution.ts +271 -0
- package/src/utils/resolveNodeFile.ts +18 -0
- package/src/utils/startRfdbServer.ts +197 -0
- package/src/validation/PathValidator.ts +334 -0
- package/src/version.ts +28 -0
|
@@ -0,0 +1,420 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* KnowledgeBase — in-memory index over git-tracked knowledge files.
|
|
3
|
+
*
|
|
4
|
+
* Scans knowledge/ directory, parses markdown files with YAML frontmatter,
|
|
5
|
+
* builds an in-memory index for fast lookups. All mutations also write to disk.
|
|
6
|
+
*/
|
|
7
|
+
import { join } from 'path';
|
|
8
|
+
import { readdirSync, statSync, readFileSync, writeFileSync, mkdirSync, existsSync } from 'fs';
|
|
9
|
+
import { parseFrontmatter, parseKBNode, serializeKBNode, parseEdgesFile, appendEdge as appendEdgeToFile, parseYamlArrayFile } from './parser.js';
|
|
10
|
+
import { SemanticAddressResolver } from './SemanticAddressResolver.js';
|
|
11
|
+
/** Pluralized directory names for each node type */
|
|
12
|
+
const TYPE_DIR = {
|
|
13
|
+
DECISION: 'decisions',
|
|
14
|
+
FACT: 'facts',
|
|
15
|
+
SESSION: 'sessions',
|
|
16
|
+
COMMIT: 'commits',
|
|
17
|
+
FILE_CHANGE: 'file-changes',
|
|
18
|
+
AUTHOR: 'authors',
|
|
19
|
+
TICKET: 'tickets',
|
|
20
|
+
INCIDENT: 'incidents',
|
|
21
|
+
};
|
|
22
|
+
export class KnowledgeBase {
|
|
23
|
+
knowledgeDir;
|
|
24
|
+
nodes = new Map();
|
|
25
|
+
edges = [];
|
|
26
|
+
loaded = false;
|
|
27
|
+
resolver = null;
|
|
28
|
+
constructor(knowledgeDir) {
|
|
29
|
+
this.knowledgeDir = knowledgeDir;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Wire up a code graph backend for resolving semantic addresses.
|
|
33
|
+
* Creates a SemanticAddressResolver that lazily resolves `relates_to`
|
|
34
|
+
* and `applies_to` addresses to current code node IDs.
|
|
35
|
+
*/
|
|
36
|
+
setBackend(backend) {
|
|
37
|
+
this.resolver = new SemanticAddressResolver(backend);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Bump the resolver's generation counter, marking all cached resolutions stale.
|
|
41
|
+
* Call after re-analysis so next resolve() re-queries the code graph.
|
|
42
|
+
*/
|
|
43
|
+
invalidateResolutionCache() {
|
|
44
|
+
this.resolver?.bumpGeneration();
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Resolve all code addresses in a node's relates_to (and applies_to for decisions).
|
|
48
|
+
* KB-internal addresses (kb:...) pass through without backend query.
|
|
49
|
+
* Returns empty array if no resolver is set.
|
|
50
|
+
*/
|
|
51
|
+
async resolveReferences(node) {
|
|
52
|
+
if (!this.resolver)
|
|
53
|
+
return [];
|
|
54
|
+
const addresses = [];
|
|
55
|
+
if (node.relates_to)
|
|
56
|
+
addresses.push(...node.relates_to);
|
|
57
|
+
if (node.type === 'DECISION') {
|
|
58
|
+
const d = node;
|
|
59
|
+
if (d.applies_to)
|
|
60
|
+
addresses.push(...d.applies_to);
|
|
61
|
+
}
|
|
62
|
+
// Filter to code addresses only (not kb: internal refs)
|
|
63
|
+
const codeAddresses = addresses.filter(a => !a.startsWith('kb:'));
|
|
64
|
+
if (codeAddresses.length === 0)
|
|
65
|
+
return [];
|
|
66
|
+
return this.resolver.resolveAll(codeAddresses);
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Find all KB nodes with code addresses that don't resolve to graph nodes.
|
|
70
|
+
* Returns pairs of (KB node ID, dangling address).
|
|
71
|
+
*/
|
|
72
|
+
async getDanglingCodeRefs() {
|
|
73
|
+
if (!this.resolver)
|
|
74
|
+
return [];
|
|
75
|
+
const results = [];
|
|
76
|
+
for (const node of this.nodes.values()) {
|
|
77
|
+
const resolved = await this.resolveReferences(node);
|
|
78
|
+
for (const r of resolved) {
|
|
79
|
+
if (r.status === 'dangling') {
|
|
80
|
+
results.push({ nodeId: node.id, address: r.address });
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return results;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Scan knowledge directory recursively, parse all .md files, build index.
|
|
88
|
+
* Malformed files are skipped with console.warn, don't crash.
|
|
89
|
+
* Missing directory succeeds with empty index.
|
|
90
|
+
*/
|
|
91
|
+
async load() {
|
|
92
|
+
this.nodes.clear();
|
|
93
|
+
this.edges = [];
|
|
94
|
+
if (!existsSync(this.knowledgeDir)) {
|
|
95
|
+
this.loaded = true;
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
// Scan for .md files recursively
|
|
99
|
+
const mdFiles = this.scanFiles(this.knowledgeDir, '.md');
|
|
100
|
+
for (const filePath of mdFiles) {
|
|
101
|
+
try {
|
|
102
|
+
const content = readFileSync(filePath, 'utf-8');
|
|
103
|
+
const { frontmatter, body } = parseFrontmatter(content);
|
|
104
|
+
const node = parseKBNode(frontmatter, body, filePath);
|
|
105
|
+
if (this.nodes.has(node.id)) {
|
|
106
|
+
const existing = this.nodes.get(node.id);
|
|
107
|
+
throw new Error(`ID collision: "${node.id}" exists in both ${existing.filePath} and ${filePath}`);
|
|
108
|
+
}
|
|
109
|
+
this.nodes.set(node.id, node);
|
|
110
|
+
}
|
|
111
|
+
catch (error) {
|
|
112
|
+
// ID collisions should propagate — they're data integrity errors
|
|
113
|
+
if (error instanceof Error && error.message.startsWith('ID collision:')) {
|
|
114
|
+
throw error;
|
|
115
|
+
}
|
|
116
|
+
console.warn(`[KnowledgeBase] Skipping malformed file ${filePath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
// Scan for .yaml array files (derived data like commits, authors)
|
|
120
|
+
const yamlFiles = this.scanFiles(this.knowledgeDir, '.yaml');
|
|
121
|
+
for (const filePath of yamlFiles) {
|
|
122
|
+
// Skip edges.yaml and meta.yaml — they have special formats
|
|
123
|
+
const fileName = filePath.split('/').pop() || '';
|
|
124
|
+
if (fileName === 'edges.yaml' || fileName === 'meta.yaml')
|
|
125
|
+
continue;
|
|
126
|
+
try {
|
|
127
|
+
const nodes = parseYamlArrayFile(filePath);
|
|
128
|
+
for (const node of nodes) {
|
|
129
|
+
if (this.nodes.has(node.id)) {
|
|
130
|
+
// For derived data, silently skip duplicates (can happen with incremental ingest)
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
this.nodes.set(node.id, node);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
catch (error) {
|
|
137
|
+
console.warn(`[KnowledgeBase] Skipping malformed YAML file ${filePath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
// Load edges
|
|
141
|
+
const edgesPath = join(this.knowledgeDir, 'edges.yaml');
|
|
142
|
+
try {
|
|
143
|
+
this.edges = parseEdgesFile(edgesPath);
|
|
144
|
+
}
|
|
145
|
+
catch (error) {
|
|
146
|
+
console.warn(`[KnowledgeBase] Failed to parse edges.yaml: ${error instanceof Error ? error.message : String(error)}`);
|
|
147
|
+
}
|
|
148
|
+
this.loaded = true;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Get a node by its semantic ID.
|
|
152
|
+
*/
|
|
153
|
+
getNode(id) {
|
|
154
|
+
return this.nodes.get(id);
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Query nodes with filters. All filters are AND-combined.
|
|
158
|
+
* When include_dangling_only is true, only returns nodes with dangling code refs.
|
|
159
|
+
* Note: include_dangling_only requires a resolver backend; without one it returns empty.
|
|
160
|
+
*/
|
|
161
|
+
async queryNodes(filter) {
|
|
162
|
+
let results = Array.from(this.nodes.values());
|
|
163
|
+
if (filter.type) {
|
|
164
|
+
results = results.filter(n => n.type === filter.type);
|
|
165
|
+
}
|
|
166
|
+
if (filter.projection) {
|
|
167
|
+
results = results.filter(n => n.projections.includes(filter.projection));
|
|
168
|
+
}
|
|
169
|
+
if (filter.text) {
|
|
170
|
+
const lower = filter.text.toLowerCase();
|
|
171
|
+
results = results.filter(n => n.content.toLowerCase().includes(lower));
|
|
172
|
+
}
|
|
173
|
+
if (filter.status) {
|
|
174
|
+
results = results.filter(n => {
|
|
175
|
+
if (n.type === 'DECISION')
|
|
176
|
+
return n.status === filter.status;
|
|
177
|
+
return false;
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
if (filter.relates_to) {
|
|
181
|
+
results = results.filter(n => n.relates_to?.includes(filter.relates_to));
|
|
182
|
+
}
|
|
183
|
+
if (filter.include_dangling_only) {
|
|
184
|
+
if (!this.resolver)
|
|
185
|
+
return [];
|
|
186
|
+
const danglingNodeIds = new Set();
|
|
187
|
+
for (const node of results) {
|
|
188
|
+
const resolved = await this.resolveReferences(node);
|
|
189
|
+
if (resolved.some(r => r.status === 'dangling')) {
|
|
190
|
+
danglingNodeIds.add(node.id);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
results = results.filter(n => danglingNodeIds.has(n.id));
|
|
194
|
+
}
|
|
195
|
+
return results;
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Find active decisions that apply to a given module/semantic address.
|
|
199
|
+
* Uses string includes matching on applies_to entries.
|
|
200
|
+
*/
|
|
201
|
+
async activeDecisionsFor(module) {
|
|
202
|
+
const decisions = await this.queryNodes({ type: 'DECISION', status: 'active' });
|
|
203
|
+
return decisions.filter(d => d.applies_to?.some(addr => addr.includes(module) || module.includes(addr)));
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Add a new node to the knowledge base.
|
|
207
|
+
* Creates the .md file on disk and updates the in-memory index.
|
|
208
|
+
*
|
|
209
|
+
* @param params - Node properties. `id` and `filePath` are auto-generated.
|
|
210
|
+
* @returns The created node.
|
|
211
|
+
*/
|
|
212
|
+
async addNode(params) {
|
|
213
|
+
const slug = params.slug || this.generateSlug(params.content);
|
|
214
|
+
const typeLower = params.type.toLowerCase();
|
|
215
|
+
const id = `kb:${typeLower}:${slug}`;
|
|
216
|
+
// Collision check
|
|
217
|
+
if (this.nodes.has(id)) {
|
|
218
|
+
throw new Error(`Slug collision: "${id}" already exists. Did you mean to supersede? Use supersedeFact() for facts.`);
|
|
219
|
+
}
|
|
220
|
+
const typeDir = TYPE_DIR[params.type] || typeLower + 's';
|
|
221
|
+
const dir = join(this.knowledgeDir, 'declared', typeDir);
|
|
222
|
+
if (!existsSync(dir)) {
|
|
223
|
+
mkdirSync(dir, { recursive: true });
|
|
224
|
+
}
|
|
225
|
+
const fileName = `${slug}.md`;
|
|
226
|
+
const filePath = join(dir, fileName);
|
|
227
|
+
// Secondary collision guard — file on disk
|
|
228
|
+
if (existsSync(filePath)) {
|
|
229
|
+
throw new Error(`File already exists: ${filePath}`);
|
|
230
|
+
}
|
|
231
|
+
const created = new Date().toISOString().split('T')[0];
|
|
232
|
+
// Build frontmatter
|
|
233
|
+
const frontmatter = {
|
|
234
|
+
id,
|
|
235
|
+
type: params.type,
|
|
236
|
+
created,
|
|
237
|
+
};
|
|
238
|
+
if (params.subtype)
|
|
239
|
+
frontmatter.subtype = params.subtype;
|
|
240
|
+
if (params.scope)
|
|
241
|
+
frontmatter.scope = params.scope;
|
|
242
|
+
if (params.projections?.length)
|
|
243
|
+
frontmatter.projections = params.projections;
|
|
244
|
+
if (params.source)
|
|
245
|
+
frontmatter.source = params.source;
|
|
246
|
+
if (params.relates_to?.length)
|
|
247
|
+
frontmatter.relates_to = params.relates_to;
|
|
248
|
+
// Decision fields
|
|
249
|
+
if (params.type === 'DECISION') {
|
|
250
|
+
frontmatter.status = params.status || 'proposed';
|
|
251
|
+
if (params.applies_to?.length)
|
|
252
|
+
frontmatter.applies_to = params.applies_to;
|
|
253
|
+
if (params.effective_from)
|
|
254
|
+
frontmatter.effective_from = params.effective_from;
|
|
255
|
+
}
|
|
256
|
+
// Fact fields
|
|
257
|
+
if (params.type === 'FACT') {
|
|
258
|
+
if (params.confidence)
|
|
259
|
+
frontmatter.confidence = params.confidence;
|
|
260
|
+
}
|
|
261
|
+
// Session fields
|
|
262
|
+
if (params.type === 'SESSION') {
|
|
263
|
+
if (params.task_id)
|
|
264
|
+
frontmatter.task_id = params.task_id;
|
|
265
|
+
if (params.session_path)
|
|
266
|
+
frontmatter.session_path = params.session_path;
|
|
267
|
+
if (params.produced?.length)
|
|
268
|
+
frontmatter.produced = params.produced;
|
|
269
|
+
}
|
|
270
|
+
// Parse to get a proper node, then serialize
|
|
271
|
+
const node = parseKBNode(frontmatter, params.content, filePath);
|
|
272
|
+
const markdown = serializeKBNode(node);
|
|
273
|
+
writeFileSync(filePath, markdown, 'utf-8');
|
|
274
|
+
// Update index
|
|
275
|
+
this.nodes.set(id, node);
|
|
276
|
+
// Create edges for relates_to
|
|
277
|
+
if (params.relates_to?.length) {
|
|
278
|
+
const edgesPath = join(this.knowledgeDir, 'edges.yaml');
|
|
279
|
+
for (const target of params.relates_to) {
|
|
280
|
+
const edge = { type: 'RELATES_TO', from: id, to: target };
|
|
281
|
+
appendEdgeToFile(edgesPath, edge);
|
|
282
|
+
this.edges.push(edge);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
return node;
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* Supersede an existing fact with a new version.
|
|
289
|
+
* Creates new fact, updates old fact with superseded_by.
|
|
290
|
+
*/
|
|
291
|
+
async supersedeFact(oldId, newContent, newSlug) {
|
|
292
|
+
const oldNode = this.nodes.get(oldId);
|
|
293
|
+
if (!oldNode) {
|
|
294
|
+
throw new Error(`Fact not found: ${oldId}`);
|
|
295
|
+
}
|
|
296
|
+
if (oldNode.type !== 'FACT') {
|
|
297
|
+
throw new Error(`Node "${oldId}" is type ${oldNode.type}, not FACT. Only facts can be superseded.`);
|
|
298
|
+
}
|
|
299
|
+
const oldFact = oldNode;
|
|
300
|
+
const slug = newSlug || this.generateSlug(newContent);
|
|
301
|
+
const newId = `kb:fact:${slug}`;
|
|
302
|
+
// Create new fact
|
|
303
|
+
const newFact = await this.addNode({
|
|
304
|
+
type: 'FACT',
|
|
305
|
+
content: newContent,
|
|
306
|
+
slug,
|
|
307
|
+
projections: oldFact.projections,
|
|
308
|
+
source: oldFact.source,
|
|
309
|
+
confidence: oldFact.confidence,
|
|
310
|
+
});
|
|
311
|
+
// Update old fact: set superseded_by and rewrite file
|
|
312
|
+
oldFact.superseded_by = newId;
|
|
313
|
+
const markdown = serializeKBNode(oldFact);
|
|
314
|
+
writeFileSync(oldFact.filePath, markdown, 'utf-8');
|
|
315
|
+
// Add supersedes edge
|
|
316
|
+
const edgesPath = join(this.knowledgeDir, 'edges.yaml');
|
|
317
|
+
const edge = { type: 'SUPERSEDES', from: newId, to: oldId };
|
|
318
|
+
appendEdgeToFile(edgesPath, edge);
|
|
319
|
+
this.edges.push(edge);
|
|
320
|
+
return { old: oldFact, new: newFact };
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* Add an edge to the knowledge graph.
|
|
324
|
+
*/
|
|
325
|
+
addEdge(edge) {
|
|
326
|
+
const edgesPath = join(this.knowledgeDir, 'edges.yaml');
|
|
327
|
+
appendEdgeToFile(edgesPath, edge);
|
|
328
|
+
this.edges.push(edge);
|
|
329
|
+
}
|
|
330
|
+
/**
|
|
331
|
+
* Get edges, optionally filtered by node ID (from or to).
|
|
332
|
+
*/
|
|
333
|
+
getEdges(nodeId) {
|
|
334
|
+
if (!nodeId)
|
|
335
|
+
return [...this.edges];
|
|
336
|
+
return this.edges.filter(e => e.from === nodeId || e.to === nodeId);
|
|
337
|
+
}
|
|
338
|
+
/**
|
|
339
|
+
* Get statistics about the knowledge base.
|
|
340
|
+
* Includes dangling code references if a resolver backend is available.
|
|
341
|
+
*/
|
|
342
|
+
async getStats() {
|
|
343
|
+
const byType = {};
|
|
344
|
+
const byLifecycle = {};
|
|
345
|
+
for (const node of this.nodes.values()) {
|
|
346
|
+
byType[node.type] = (byType[node.type] || 0) + 1;
|
|
347
|
+
byLifecycle[node.lifecycle] = (byLifecycle[node.lifecycle] || 0) + 1;
|
|
348
|
+
}
|
|
349
|
+
const edgesByType = {};
|
|
350
|
+
for (const edge of this.edges) {
|
|
351
|
+
edgesByType[edge.type] = (edgesByType[edge.type] || 0) + 1;
|
|
352
|
+
}
|
|
353
|
+
// Find dangling KB-internal refs
|
|
354
|
+
const nodeIds = new Set(this.nodes.keys());
|
|
355
|
+
const danglingRefs = [];
|
|
356
|
+
for (const edge of this.edges) {
|
|
357
|
+
if (!nodeIds.has(edge.from) && !danglingRefs.includes(edge.from)) {
|
|
358
|
+
danglingRefs.push(edge.from);
|
|
359
|
+
}
|
|
360
|
+
if (!nodeIds.has(edge.to) && !danglingRefs.includes(edge.to)) {
|
|
361
|
+
danglingRefs.push(edge.to);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
// Find dangling code references
|
|
365
|
+
const danglingCodeRefs = await this.getDanglingCodeRefs();
|
|
366
|
+
return {
|
|
367
|
+
totalNodes: this.nodes.size,
|
|
368
|
+
byType,
|
|
369
|
+
byLifecycle,
|
|
370
|
+
totalEdges: this.edges.length,
|
|
371
|
+
edgesByType,
|
|
372
|
+
danglingRefs,
|
|
373
|
+
danglingCodeRefs,
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
// --- Private helpers ---
|
|
377
|
+
/**
|
|
378
|
+
* Recursively scan directory for files with given extension.
|
|
379
|
+
*/
|
|
380
|
+
scanFiles(dir, ext) {
|
|
381
|
+
const results = [];
|
|
382
|
+
let entries;
|
|
383
|
+
try {
|
|
384
|
+
entries = readdirSync(dir);
|
|
385
|
+
}
|
|
386
|
+
catch {
|
|
387
|
+
return results;
|
|
388
|
+
}
|
|
389
|
+
for (const entry of entries) {
|
|
390
|
+
const fullPath = join(dir, entry);
|
|
391
|
+
try {
|
|
392
|
+
const stat = statSync(fullPath);
|
|
393
|
+
if (stat.isDirectory()) {
|
|
394
|
+
results.push(...this.scanFiles(fullPath, ext));
|
|
395
|
+
}
|
|
396
|
+
else if (entry.endsWith(ext)) {
|
|
397
|
+
results.push(fullPath);
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
catch {
|
|
401
|
+
// Skip unreadable entries
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
return results;
|
|
405
|
+
}
|
|
406
|
+
/**
|
|
407
|
+
* Generate a slug from content (first line, simplified).
|
|
408
|
+
*/
|
|
409
|
+
generateSlug(content) {
|
|
410
|
+
const firstLine = content.split('\n')[0].trim();
|
|
411
|
+
return firstLine
|
|
412
|
+
.toLowerCase()
|
|
413
|
+
.replace(/[^a-z0-9\s-]/g, '')
|
|
414
|
+
.replace(/\s+/g, '-')
|
|
415
|
+
.replace(/-+/g, '-')
|
|
416
|
+
.replace(/^-|-$/g, '')
|
|
417
|
+
.slice(0, 60) || 'untitled';
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
//# sourceMappingURL=KnowledgeBase.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"KnowledgeBase.js","sourceRoot":"","sources":["../../src/knowledge/KnowledgeBase.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SemanticAddressResolver — lazy resolver from semantic addresses to code graph node IDs.
|
|
3
|
+
*
|
|
4
|
+
* Semantic addresses (e.g., "src/auth.js:hashPassword:FUNCTION") are human-readable
|
|
5
|
+
* references to code. After re-analysis, node IDs may change.
|
|
6
|
+
* This resolver bridges KB references to current graph state at query time.
|
|
7
|
+
*/
|
|
8
|
+
import type { ParsedSemanticAddress, ResolvedAddress } from './types.js';
|
|
9
|
+
/** Minimal backend interface for resolving semantic addresses */
|
|
10
|
+
export interface ResolverBackend {
|
|
11
|
+
getAllNodes(filter?: Record<string, unknown>): Promise<Array<Record<string, unknown> & {
|
|
12
|
+
id: string;
|
|
13
|
+
}>>;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Parse a semantic address string into its components.
|
|
17
|
+
*
|
|
18
|
+
* Format: `file:name:TYPE` or `file:scope1:...:scopeN:name:TYPE`
|
|
19
|
+
* - Last segment = node type (FUNCTION, CLASS, VARIABLE, etc.)
|
|
20
|
+
* - First segment = file path (contains `/` or `.`)
|
|
21
|
+
* - Middle segments = scope path; last middle segment is the name
|
|
22
|
+
*
|
|
23
|
+
* @returns parsed address or null if invalid
|
|
24
|
+
*/
|
|
25
|
+
export declare function parseSemanticAddress(address: string): ParsedSemanticAddress | null;
|
|
26
|
+
export declare class SemanticAddressResolver {
|
|
27
|
+
private backend;
|
|
28
|
+
private cache;
|
|
29
|
+
private generation;
|
|
30
|
+
constructor(backend: ResolverBackend);
|
|
31
|
+
/**
|
|
32
|
+
* Increment the generation counter, marking all cached entries stale.
|
|
33
|
+
* Call this after re-analysis to force re-resolution on next access.
|
|
34
|
+
*/
|
|
35
|
+
bumpGeneration(): void;
|
|
36
|
+
/** Current generation number */
|
|
37
|
+
getGeneration(): number;
|
|
38
|
+
/**
|
|
39
|
+
* Resolve a semantic address to a code graph node ID.
|
|
40
|
+
*
|
|
41
|
+
* - `kb:` addresses pass through without backend query (KB-internal refs).
|
|
42
|
+
* - Results are cached; stale entries (resolvedAt < generation) are re-resolved.
|
|
43
|
+
*/
|
|
44
|
+
resolve(address: string): Promise<ResolvedAddress>;
|
|
45
|
+
/**
|
|
46
|
+
* Resolve multiple addresses in bulk.
|
|
47
|
+
*/
|
|
48
|
+
resolveAll(addresses: string[]): Promise<ResolvedAddress[]>;
|
|
49
|
+
/**
|
|
50
|
+
* Get all addresses that resolved as dangling at the current generation.
|
|
51
|
+
*/
|
|
52
|
+
getDanglingAddresses(): ResolvedAddress[];
|
|
53
|
+
/**
|
|
54
|
+
* Disambiguate multiple matching nodes using the scope path from the address.
|
|
55
|
+
* Matches the node whose scopePath best matches the address's scope segments.
|
|
56
|
+
*/
|
|
57
|
+
private disambiguateByScope;
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=SemanticAddressResolver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SemanticAddressResolver.d.ts","sourceRoot":"","sources":["../../src/knowledge/SemanticAddressResolver.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,qBAAqB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAEzE,iEAAiE;AACjE,MAAM,WAAW,eAAe;IAC9B,WAAW,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC,CAAC;CACzG;AAOD;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,qBAAqB,GAAG,IAAI,CAwBlF;AAED,qBAAa,uBAAuB;IAClC,OAAO,CAAC,OAAO,CAAkB;IACjC,OAAO,CAAC,KAAK,CAAiC;IAC9C,OAAO,CAAC,UAAU,CAAK;gBAEX,OAAO,EAAE,eAAe;IAIpC;;;OAGG;IACH,cAAc,IAAI,IAAI;IAItB,gCAAgC;IAChC,aAAa,IAAI,MAAM;IAIvB;;;;;OAKG;IACG,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC;IAiDxD;;OAEG;IACG,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC;IAIjE;;OAEG;IACH,oBAAoB,IAAI,eAAe,EAAE;IAUzC;;;OAGG;IACH,OAAO,CAAC,mBAAmB;CAiC5B"}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SemanticAddressResolver — lazy resolver from semantic addresses to code graph node IDs.
|
|
3
|
+
*
|
|
4
|
+
* Semantic addresses (e.g., "src/auth.js:hashPassword:FUNCTION") are human-readable
|
|
5
|
+
* references to code. After re-analysis, node IDs may change.
|
|
6
|
+
* This resolver bridges KB references to current graph state at query time.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Parse a semantic address string into its components.
|
|
10
|
+
*
|
|
11
|
+
* Format: `file:name:TYPE` or `file:scope1:...:scopeN:name:TYPE`
|
|
12
|
+
* - Last segment = node type (FUNCTION, CLASS, VARIABLE, etc.)
|
|
13
|
+
* - First segment = file path (contains `/` or `.`)
|
|
14
|
+
* - Middle segments = scope path; last middle segment is the name
|
|
15
|
+
*
|
|
16
|
+
* @returns parsed address or null if invalid
|
|
17
|
+
*/
|
|
18
|
+
export function parseSemanticAddress(address) {
|
|
19
|
+
if (!address || typeof address !== 'string')
|
|
20
|
+
return null;
|
|
21
|
+
const parts = address.split(':');
|
|
22
|
+
// Minimum: file:name:TYPE = 3 parts
|
|
23
|
+
if (parts.length < 3)
|
|
24
|
+
return null;
|
|
25
|
+
const type = parts[parts.length - 1];
|
|
26
|
+
const file = parts[0];
|
|
27
|
+
// File must contain / or . to be valid
|
|
28
|
+
if (!file.includes('/') && !file.includes('.'))
|
|
29
|
+
return null;
|
|
30
|
+
// Type must be uppercase
|
|
31
|
+
if (type !== type.toUpperCase() || !/^[A-Z_]+$/.test(type))
|
|
32
|
+
return null;
|
|
33
|
+
// Middle segments: everything between file and type
|
|
34
|
+
const middle = parts.slice(1, -1);
|
|
35
|
+
if (middle.length === 0)
|
|
36
|
+
return null;
|
|
37
|
+
const name = middle[middle.length - 1];
|
|
38
|
+
const scopePath = middle.slice(0, -1);
|
|
39
|
+
return { file, name, type, scopePath };
|
|
40
|
+
}
|
|
41
|
+
export class SemanticAddressResolver {
|
|
42
|
+
backend;
|
|
43
|
+
cache = new Map();
|
|
44
|
+
generation = 0;
|
|
45
|
+
constructor(backend) {
|
|
46
|
+
this.backend = backend;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Increment the generation counter, marking all cached entries stale.
|
|
50
|
+
* Call this after re-analysis to force re-resolution on next access.
|
|
51
|
+
*/
|
|
52
|
+
bumpGeneration() {
|
|
53
|
+
this.generation++;
|
|
54
|
+
}
|
|
55
|
+
/** Current generation number */
|
|
56
|
+
getGeneration() {
|
|
57
|
+
return this.generation;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Resolve a semantic address to a code graph node ID.
|
|
61
|
+
*
|
|
62
|
+
* - `kb:` addresses pass through without backend query (KB-internal refs).
|
|
63
|
+
* - Results are cached; stale entries (resolvedAt < generation) are re-resolved.
|
|
64
|
+
*/
|
|
65
|
+
async resolve(address) {
|
|
66
|
+
// KB-internal addresses pass through
|
|
67
|
+
if (address.startsWith('kb:')) {
|
|
68
|
+
return { address, codeNodeId: null, status: 'resolved' };
|
|
69
|
+
}
|
|
70
|
+
// Check cache
|
|
71
|
+
const cached = this.cache.get(address);
|
|
72
|
+
if (cached && cached.resolvedAt >= this.generation) {
|
|
73
|
+
return cached.result;
|
|
74
|
+
}
|
|
75
|
+
const parsed = parseSemanticAddress(address);
|
|
76
|
+
if (!parsed) {
|
|
77
|
+
const result = { address, codeNodeId: null, status: 'dangling' };
|
|
78
|
+
this.cache.set(address, { result, resolvedAt: this.generation });
|
|
79
|
+
return result;
|
|
80
|
+
}
|
|
81
|
+
// Query backend for matching nodes
|
|
82
|
+
const filter = {
|
|
83
|
+
file: parsed.file,
|
|
84
|
+
name: parsed.name,
|
|
85
|
+
nodeType: parsed.type,
|
|
86
|
+
};
|
|
87
|
+
const nodes = await this.backend.getAllNodes(filter);
|
|
88
|
+
let result;
|
|
89
|
+
if (nodes.length === 1) {
|
|
90
|
+
result = { address, codeNodeId: nodes[0].id, status: 'resolved' };
|
|
91
|
+
}
|
|
92
|
+
else if (nodes.length === 0) {
|
|
93
|
+
result = { address, codeNodeId: null, status: 'dangling' };
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
// Multiple matches — disambiguate by scope path
|
|
97
|
+
const match = this.disambiguateByScope(nodes, parsed.scopePath);
|
|
98
|
+
if (match) {
|
|
99
|
+
result = { address, codeNodeId: match.id, status: 'resolved' };
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
// Take first match as best-effort
|
|
103
|
+
result = { address, codeNodeId: nodes[0].id, status: 'resolved' };
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
this.cache.set(address, { result, resolvedAt: this.generation });
|
|
107
|
+
return result;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Resolve multiple addresses in bulk.
|
|
111
|
+
*/
|
|
112
|
+
async resolveAll(addresses) {
|
|
113
|
+
return Promise.all(addresses.map(addr => this.resolve(addr)));
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Get all addresses that resolved as dangling at the current generation.
|
|
117
|
+
*/
|
|
118
|
+
getDanglingAddresses() {
|
|
119
|
+
const results = [];
|
|
120
|
+
for (const entry of this.cache.values()) {
|
|
121
|
+
if (entry.resolvedAt >= this.generation && entry.result.status === 'dangling') {
|
|
122
|
+
results.push(entry.result);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return results;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Disambiguate multiple matching nodes using the scope path from the address.
|
|
129
|
+
* Matches the node whose scopePath best matches the address's scope segments.
|
|
130
|
+
*/
|
|
131
|
+
disambiguateByScope(nodes, addressScopePath) {
|
|
132
|
+
if (addressScopePath.length === 0)
|
|
133
|
+
return null;
|
|
134
|
+
let bestMatch = null;
|
|
135
|
+
let bestScore = -1;
|
|
136
|
+
for (const node of nodes) {
|
|
137
|
+
const nodeScopePath = node['scopePath'];
|
|
138
|
+
if (!Array.isArray(nodeScopePath))
|
|
139
|
+
continue;
|
|
140
|
+
// Score: count matching scope segments from the end
|
|
141
|
+
let score = 0;
|
|
142
|
+
const aLen = addressScopePath.length;
|
|
143
|
+
const nLen = nodeScopePath.length;
|
|
144
|
+
for (let i = 1; i <= Math.min(aLen, nLen); i++) {
|
|
145
|
+
if (addressScopePath[aLen - i] === nodeScopePath[nLen - i]) {
|
|
146
|
+
score++;
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
break;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
if (score > bestScore) {
|
|
153
|
+
bestScore = score;
|
|
154
|
+
bestMatch = node;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return bestScore > 0 ? bestMatch : null;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
//# sourceMappingURL=SemanticAddressResolver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SemanticAddressResolver.js","sourceRoot":"","sources":["../../src/knowledge/SemanticAddressResolver.ts"],"names":[],"mappings":"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"}
|