modular-studio 1.0.5 → 1.1.0
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 +122 -122
- package/dist/assets/Badge-Bsy2H_p2.js +1 -0
- package/dist/assets/GraphPanel-D4X3faxA.js +47 -0
- package/dist/assets/{Input-Bgp734xs.js → Input-Dyb88Erk.js} +1 -1
- package/dist/assets/KnowledgeTab-BccWz7Np.js +5 -0
- package/dist/assets/MemoryTab-Y_66cE01.js +16 -0
- package/dist/assets/QualificationTab-Dm9dEIpM.js +1 -0
- package/dist/assets/ReviewTab-BrfXSyyf.js +104 -0
- package/dist/assets/{Section-DoJrmytO.js → Section-68XDCFTl.js} +1 -1
- package/dist/assets/TestTab-CLKRT63X.js +42 -0
- package/dist/assets/ToolsTab-xumi9Uds.js +1 -0
- package/dist/assets/icons-CS8RUPBi.js +1 -0
- package/dist/assets/index-B2bm0161.css +1 -0
- package/dist/assets/index-C626nWuA.js +422 -0
- package/dist/assets/services-BDk6yY4o.js +369 -0
- package/dist/index.html +18 -18
- package/dist-server/bin/modular-mcp.js +1 -0
- package/dist-server/server/index.d.ts.map +1 -1
- package/dist-server/server/index.js +34 -0
- package/dist-server/server/mcp/manager.d.ts +3 -0
- package/dist-server/server/mcp/manager.d.ts.map +1 -1
- package/dist-server/server/mcp/manager.js +80 -5
- package/dist-server/server/migrations/index.d.ts +11 -0
- package/dist-server/server/migrations/index.d.ts.map +1 -0
- package/dist-server/server/migrations/index.js +57 -0
- package/dist-server/server/routes/agents.d.ts.map +1 -1
- package/dist-server/server/routes/agents.js +27 -0
- package/dist-server/server/routes/analytics.d.ts +3 -0
- package/dist-server/server/routes/analytics.d.ts.map +1 -0
- package/dist-server/server/routes/analytics.js +24 -0
- package/dist-server/server/routes/cache.d.ts +3 -0
- package/dist-server/server/routes/cache.d.ts.map +1 -0
- package/dist-server/server/routes/cache.js +55 -0
- package/dist-server/server/routes/connectors/airtable.d.ts +7 -0
- package/dist-server/server/routes/connectors/airtable.d.ts.map +1 -0
- package/dist-server/server/routes/connectors/airtable.js +119 -0
- package/dist-server/server/routes/connectors/confluence.d.ts +7 -0
- package/dist-server/server/routes/connectors/confluence.d.ts.map +1 -0
- package/dist-server/server/routes/connectors/confluence.js +176 -0
- package/dist-server/server/routes/connectors/github.d.ts +7 -0
- package/dist-server/server/routes/connectors/github.d.ts.map +1 -0
- package/dist-server/server/routes/connectors/github.js +195 -0
- package/dist-server/server/routes/connectors/gmail.d.ts +7 -0
- package/dist-server/server/routes/connectors/gmail.d.ts.map +1 -0
- package/dist-server/server/routes/connectors/gmail.js +115 -0
- package/dist-server/server/routes/connectors/google-docs.d.ts +10 -0
- package/dist-server/server/routes/connectors/google-docs.d.ts.map +1 -0
- package/dist-server/server/routes/connectors/google-docs.js +165 -0
- package/dist-server/server/routes/connectors/google-drive.d.ts +7 -0
- package/dist-server/server/routes/connectors/google-drive.d.ts.map +1 -0
- package/dist-server/server/routes/connectors/google-drive.js +163 -0
- package/dist-server/server/routes/connectors/google-sheets.d.ts +7 -0
- package/dist-server/server/routes/connectors/google-sheets.d.ts.map +1 -0
- package/dist-server/server/routes/connectors/google-sheets.js +90 -0
- package/dist-server/server/routes/connectors/hubspot.d.ts +7 -0
- package/dist-server/server/routes/connectors/hubspot.d.ts.map +1 -0
- package/dist-server/server/routes/connectors/hubspot.js +134 -0
- package/dist-server/server/routes/connectors/index.d.ts +6 -0
- package/dist-server/server/routes/connectors/index.d.ts.map +1 -0
- package/dist-server/server/routes/connectors/index.js +38 -0
- package/dist-server/server/routes/connectors/jira.d.ts +7 -0
- package/dist-server/server/routes/connectors/jira.d.ts.map +1 -0
- package/dist-server/server/routes/connectors/jira.js +151 -0
- package/dist-server/server/routes/connectors/linear.d.ts +7 -0
- package/dist-server/server/routes/connectors/linear.d.ts.map +1 -0
- package/dist-server/server/routes/connectors/linear.js +154 -0
- package/dist-server/server/routes/connectors/notion.d.ts +10 -0
- package/dist-server/server/routes/connectors/notion.d.ts.map +1 -0
- package/dist-server/server/routes/connectors/notion.js +201 -0
- package/dist-server/server/routes/connectors/plane.d.ts +10 -0
- package/dist-server/server/routes/connectors/plane.d.ts.map +1 -0
- package/dist-server/server/routes/connectors/plane.js +189 -0
- package/dist-server/server/routes/connectors/shared.d.ts +25 -0
- package/dist-server/server/routes/connectors/shared.d.ts.map +1 -0
- package/dist-server/server/routes/connectors/shared.js +202 -0
- package/dist-server/server/routes/connectors/slack.d.ts +7 -0
- package/dist-server/server/routes/connectors/slack.d.ts.map +1 -0
- package/dist-server/server/routes/connectors/slack.js +153 -0
- package/dist-server/server/routes/connectors.d.ts.map +1 -1
- package/dist-server/server/routes/connectors.js +47 -17
- package/dist-server/server/routes/cost.d.ts +3 -0
- package/dist-server/server/routes/cost.d.ts.map +1 -0
- package/dist-server/server/routes/cost.js +113 -0
- package/dist-server/server/routes/graph.d.ts +11 -0
- package/dist-server/server/routes/graph.d.ts.map +1 -0
- package/dist-server/server/routes/graph.js +213 -0
- package/dist-server/server/routes/lessons.d.ts +3 -0
- package/dist-server/server/routes/lessons.d.ts.map +1 -0
- package/dist-server/server/routes/lessons.js +160 -0
- package/dist-server/server/routes/llm.d.ts.map +1 -1
- package/dist-server/server/routes/llm.js +85 -18
- package/dist-server/server/routes/memory.d.ts.map +1 -1
- package/dist-server/server/routes/memory.js +31 -0
- package/dist-server/server/routes/metaprompt-v2.d.ts +3 -0
- package/dist-server/server/routes/metaprompt-v2.d.ts.map +1 -0
- package/dist-server/server/routes/metaprompt-v2.js +104 -0
- package/dist-server/server/routes/qualification.d.ts.map +1 -1
- package/dist-server/server/routes/qualification.js +342 -334
- package/dist-server/server/routes/repo-index.d.ts.map +1 -1
- package/dist-server/server/routes/repo-index.js +7 -0
- package/dist-server/server/routes/skills-search.d.ts.map +1 -1
- package/dist-server/server/routes/skills-search.js +192 -26
- package/dist-server/server/routes/tool-analytics.d.ts +3 -0
- package/dist-server/server/routes/tool-analytics.d.ts.map +1 -0
- package/dist-server/server/routes/tool-analytics.js +47 -0
- package/dist-server/server/services/adapters/hindsightAdapter.d.ts +28 -0
- package/dist-server/server/services/adapters/hindsightAdapter.d.ts.map +1 -0
- package/dist-server/server/services/adapters/hindsightAdapter.js +63 -0
- package/dist-server/server/services/adapters/postgresAdapter.js +30 -30
- package/dist-server/server/services/adapters/sqliteAdapter.d.ts +1 -0
- package/dist-server/server/services/adapters/sqliteAdapter.d.ts.map +1 -1
- package/dist-server/server/services/adapters/sqliteAdapter.js +66 -36
- package/dist-server/server/services/agentStore.d.ts +2 -1
- package/dist-server/server/services/agentStore.d.ts.map +1 -1
- package/dist-server/server/services/agentStore.js +2 -1
- package/dist-server/server/services/correctionDetector.d.ts +22 -0
- package/dist-server/server/services/correctionDetector.d.ts.map +1 -0
- package/dist-server/server/services/correctionDetector.js +91 -0
- package/dist-server/server/services/credentialStore.d.ts +10 -0
- package/dist-server/server/services/credentialStore.d.ts.map +1 -0
- package/dist-server/server/services/credentialStore.js +123 -0
- package/dist-server/server/services/hindsightClient.d.ts +15 -0
- package/dist-server/server/services/hindsightClient.d.ts.map +1 -0
- package/dist-server/server/services/hindsightClient.js +48 -0
- package/dist-server/server/services/lessonExtractor.d.ts +21 -0
- package/dist-server/server/services/lessonExtractor.d.ts.map +1 -0
- package/dist-server/server/services/lessonExtractor.js +92 -0
- package/dist-server/server/services/repoIndexer.d.ts +7 -1
- package/dist-server/server/services/repoIndexer.d.ts.map +1 -1
- package/dist-server/server/services/repoIndexer.js +295 -94
- package/dist-server/server/services/responseCache.d.ts +24 -0
- package/dist-server/server/services/responseCache.d.ts.map +1 -0
- package/dist-server/server/services/responseCache.js +163 -0
- package/dist-server/server/services/sqliteStore.d.ts +72 -0
- package/dist-server/server/services/sqliteStore.d.ts.map +1 -1
- package/dist-server/server/services/sqliteStore.js +291 -13
- package/dist-server/src/config.d.ts +2 -0
- package/dist-server/src/config.d.ts.map +1 -0
- package/dist-server/src/config.js +3 -0
- package/dist-server/src/graph/db.d.ts +46 -0
- package/dist-server/src/graph/db.d.ts.map +1 -0
- package/dist-server/src/graph/db.js +241 -0
- package/dist-server/src/graph/extractors/code.d.ts +12 -0
- package/dist-server/src/graph/extractors/code.d.ts.map +1 -0
- package/dist-server/src/graph/extractors/code.js +239 -0
- package/dist-server/src/graph/extractors/cross-type.d.ts +16 -0
- package/dist-server/src/graph/extractors/cross-type.d.ts.map +1 -0
- package/dist-server/src/graph/extractors/cross-type.js +67 -0
- package/dist-server/src/graph/extractors/markdown.d.ts +29 -0
- package/dist-server/src/graph/extractors/markdown.d.ts.map +1 -0
- package/dist-server/src/graph/extractors/markdown.js +224 -0
- package/dist-server/src/graph/extractors/yaml.d.ts +15 -0
- package/dist-server/src/graph/extractors/yaml.d.ts.map +1 -0
- package/dist-server/src/graph/extractors/yaml.js +104 -0
- package/dist-server/src/graph/index.d.ts +62 -0
- package/dist-server/src/graph/index.d.ts.map +1 -0
- package/dist-server/src/graph/index.js +67 -0
- package/dist-server/src/graph/packer.d.ts +19 -0
- package/dist-server/src/graph/packer.d.ts.map +1 -0
- package/dist-server/src/graph/packer.js +134 -0
- package/dist-server/src/graph/resolver.d.ts +12 -0
- package/dist-server/src/graph/resolver.d.ts.map +1 -0
- package/dist-server/src/graph/resolver.js +81 -0
- package/dist-server/src/graph/scanner.d.ts +34 -0
- package/dist-server/src/graph/scanner.d.ts.map +1 -0
- package/dist-server/src/graph/scanner.js +252 -0
- package/dist-server/src/graph/traverser.d.ts +17 -0
- package/dist-server/src/graph/traverser.d.ts.map +1 -0
- package/dist-server/src/graph/traverser.js +185 -0
- package/dist-server/src/graph/types.d.ts +117 -0
- package/dist-server/src/graph/types.d.ts.map +1 -0
- package/dist-server/src/graph/types.js +63 -0
- package/dist-server/src/metaprompt/v2/assembler.d.ts +3 -0
- package/dist-server/src/metaprompt/v2/assembler.d.ts.map +1 -0
- package/dist-server/src/metaprompt/v2/assembler.js +261 -0
- package/dist-server/src/metaprompt/v2/context-strategist.d.ts +3 -0
- package/dist-server/src/metaprompt/v2/context-strategist.d.ts.map +1 -0
- package/dist-server/src/metaprompt/v2/context-strategist.js +173 -0
- package/dist-server/src/metaprompt/v2/evaluator.d.ts +3 -0
- package/dist-server/src/metaprompt/v2/evaluator.d.ts.map +1 -0
- package/dist-server/src/metaprompt/v2/evaluator.js +281 -0
- package/dist-server/src/metaprompt/v2/index.d.ts +41 -0
- package/dist-server/src/metaprompt/v2/index.d.ts.map +1 -0
- package/dist-server/src/metaprompt/v2/index.js +90 -0
- package/dist-server/src/metaprompt/v2/parser.d.ts +3 -0
- package/dist-server/src/metaprompt/v2/parser.d.ts.map +1 -0
- package/dist-server/src/metaprompt/v2/parser.js +138 -0
- package/dist-server/src/metaprompt/v2/pattern-selector.d.ts +3 -0
- package/dist-server/src/metaprompt/v2/pattern-selector.d.ts.map +1 -0
- package/dist-server/src/metaprompt/v2/pattern-selector.js +154 -0
- package/dist-server/src/metaprompt/v2/researcher.d.ts +3 -0
- package/dist-server/src/metaprompt/v2/researcher.d.ts.map +1 -0
- package/dist-server/src/metaprompt/v2/researcher.js +194 -0
- package/dist-server/src/metaprompt/v2/tool-discovery.d.ts +74 -0
- package/dist-server/src/metaprompt/v2/tool-discovery.d.ts.map +1 -0
- package/dist-server/src/metaprompt/v2/tool-discovery.js +290 -0
- package/dist-server/src/metaprompt/v2/types.d.ts +154 -0
- package/dist-server/src/metaprompt/v2/types.d.ts.map +1 -0
- package/dist-server/src/metaprompt/v2/types.js +2 -0
- package/dist-server/src/services/contradictionDetector.js +1 -1
- package/dist-server/src/services/llmService.d.ts +61 -0
- package/dist-server/src/services/llmService.d.ts.map +1 -0
- package/dist-server/src/services/llmService.js +222 -0
- package/dist-server/src/store/knowledgeBase.d.ts +6 -1
- package/dist-server/src/store/knowledgeBase.d.ts.map +1 -1
- package/dist-server/src/store/knowledgeBase.js +0 -1
- package/dist-server/src/store/lessonStore.d.ts +26 -0
- package/dist-server/src/store/lessonStore.d.ts.map +1 -0
- package/dist-server/src/store/lessonStore.js +64 -0
- package/dist-server/src/store/mcp-registry.d.ts +29 -0
- package/dist-server/src/store/mcp-registry.d.ts.map +1 -0
- package/dist-server/src/store/mcp-registry.js +1303 -0
- package/dist-server/src/store/memoryStore.d.ts +12 -1
- package/dist-server/src/store/memoryStore.d.ts.map +1 -1
- package/dist-server/src/store/memoryStore.js +9 -0
- package/dist-server/src/types/registry.types.d.ts +13 -0
- package/dist-server/src/types/registry.types.d.ts.map +1 -0
- package/dist-server/src/types/registry.types.js +2 -0
- package/dist-server/tsconfig.server.tsbuildinfo +1 -1
- package/package.json +15 -1
- package/scripts/cleanup-worktrees.ps1 +29 -0
- package/dist/assets/Badge-22Ai0eyi.js +0 -1
- package/dist/assets/KnowledgeTab-DABxirZh.js +0 -4
- package/dist/assets/MemoryTab-DZeYElIT.js +0 -16
- package/dist/assets/QualificationTab-Dfpy3J30.js +0 -1
- package/dist/assets/ReviewTab-SD8lQuCc.js +0 -103
- package/dist/assets/TestTab-PDyMF8Fw.js +0 -33
- package/dist/assets/ToolsTab-B83qGCmG.js +0 -1
- package/dist/assets/icons-C2EV-le6.js +0 -1
- package/dist/assets/index-DkpMAxX7.css +0 -1
- package/dist/assets/index-q24ug5Qs.js +0 -143
- package/dist/assets/services-BaKotDf0.js +0 -343
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Context Graph — SQLite Storage
|
|
3
|
+
*
|
|
4
|
+
* Local-first graph storage with FTS5 for symbol search.
|
|
5
|
+
* Uses sql.js (WASM SQLite) for browser compatibility.
|
|
6
|
+
*/
|
|
7
|
+
// ── Schema ────────────────────────────────────────────────────────────────────
|
|
8
|
+
const SCHEMA = `
|
|
9
|
+
CREATE TABLE IF NOT EXISTS file_nodes (
|
|
10
|
+
id TEXT PRIMARY KEY,
|
|
11
|
+
path TEXT NOT NULL UNIQUE,
|
|
12
|
+
language TEXT NOT NULL,
|
|
13
|
+
last_modified INTEGER NOT NULL,
|
|
14
|
+
content_hash TEXT NOT NULL,
|
|
15
|
+
tokens INTEGER NOT NULL
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
CREATE TABLE IF NOT EXISTS symbols (
|
|
19
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
20
|
+
file_id TEXT NOT NULL REFERENCES file_nodes(id) ON DELETE CASCADE,
|
|
21
|
+
name TEXT NOT NULL,
|
|
22
|
+
kind TEXT NOT NULL,
|
|
23
|
+
signature TEXT,
|
|
24
|
+
line_start INTEGER NOT NULL,
|
|
25
|
+
line_end INTEGER NOT NULL,
|
|
26
|
+
is_exported INTEGER NOT NULL DEFAULT 0,
|
|
27
|
+
doc TEXT,
|
|
28
|
+
tokens INTEGER NOT NULL DEFAULT 0
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
CREATE TABLE IF NOT EXISTS relations (
|
|
32
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
33
|
+
source_file TEXT NOT NULL REFERENCES file_nodes(id) ON DELETE CASCADE,
|
|
34
|
+
source_symbol TEXT,
|
|
35
|
+
target_file TEXT NOT NULL REFERENCES file_nodes(id) ON DELETE CASCADE,
|
|
36
|
+
target_symbol TEXT,
|
|
37
|
+
kind TEXT NOT NULL,
|
|
38
|
+
weight REAL NOT NULL DEFAULT 1.0,
|
|
39
|
+
metadata_json TEXT
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
CREATE INDEX IF NOT EXISTS idx_relations_source ON relations(source_file);
|
|
43
|
+
CREATE INDEX IF NOT EXISTS idx_relations_target ON relations(target_file);
|
|
44
|
+
CREATE INDEX IF NOT EXISTS idx_relations_kind ON relations(kind);
|
|
45
|
+
CREATE INDEX IF NOT EXISTS idx_symbols_name ON symbols(name);
|
|
46
|
+
CREATE INDEX IF NOT EXISTS idx_symbols_file ON symbols(file_id);
|
|
47
|
+
CREATE INDEX IF NOT EXISTS idx_file_nodes_path ON file_nodes(path);
|
|
48
|
+
`;
|
|
49
|
+
// ── In-Memory Implementation ──────────────────────────────────────────────────
|
|
50
|
+
// Uses plain Maps for fast access. Can be backed by SQLite for persistence.
|
|
51
|
+
export class GraphDB {
|
|
52
|
+
nodes = new Map();
|
|
53
|
+
relations = [];
|
|
54
|
+
symbolsByFile = new Map();
|
|
55
|
+
symbolNameIndex = new Map(); // name → file IDs
|
|
56
|
+
outgoing = new Map();
|
|
57
|
+
incoming = new Map();
|
|
58
|
+
/** Get the SQL schema for SQLite persistence */
|
|
59
|
+
static getSchema() { return SCHEMA; }
|
|
60
|
+
// ── Node Operations ──────────────────────────────────────────────────────
|
|
61
|
+
upsertNode(node) {
|
|
62
|
+
this.nodes.set(node.id, node);
|
|
63
|
+
this.symbolsByFile.set(node.id, node.symbols);
|
|
64
|
+
// Update symbol name index
|
|
65
|
+
for (const sym of node.symbols) {
|
|
66
|
+
let files = this.symbolNameIndex.get(sym.name);
|
|
67
|
+
if (!files) {
|
|
68
|
+
files = new Set();
|
|
69
|
+
this.symbolNameIndex.set(sym.name, files);
|
|
70
|
+
}
|
|
71
|
+
files.add(node.id);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
getNode(id) {
|
|
75
|
+
return this.nodes.get(id);
|
|
76
|
+
}
|
|
77
|
+
getNodeByPath(path) {
|
|
78
|
+
for (const node of this.nodes.values()) {
|
|
79
|
+
if (node.path === path)
|
|
80
|
+
return node;
|
|
81
|
+
}
|
|
82
|
+
return undefined;
|
|
83
|
+
}
|
|
84
|
+
removeNode(id) {
|
|
85
|
+
const node = this.nodes.get(id);
|
|
86
|
+
if (!node)
|
|
87
|
+
return;
|
|
88
|
+
// Remove from symbol index
|
|
89
|
+
for (const sym of node.symbols) {
|
|
90
|
+
const files = this.symbolNameIndex.get(sym.name);
|
|
91
|
+
if (files) {
|
|
92
|
+
files.delete(id);
|
|
93
|
+
if (files.size === 0)
|
|
94
|
+
this.symbolNameIndex.delete(sym.name);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
// Remove relations
|
|
98
|
+
this.relations = this.relations.filter(r => r.sourceFile !== id && r.targetFile !== id);
|
|
99
|
+
this.outgoing.delete(id);
|
|
100
|
+
this.incoming.delete(id);
|
|
101
|
+
this.rebuildRelationIndexes();
|
|
102
|
+
this.symbolsByFile.delete(id);
|
|
103
|
+
this.nodes.delete(id);
|
|
104
|
+
}
|
|
105
|
+
getAllNodes() {
|
|
106
|
+
return Array.from(this.nodes.values());
|
|
107
|
+
}
|
|
108
|
+
getNodeCount() {
|
|
109
|
+
return this.nodes.size;
|
|
110
|
+
}
|
|
111
|
+
// ── Relation Operations ───────────────────────────────────────────────────
|
|
112
|
+
addRelation(rel) {
|
|
113
|
+
// Deduplicate: same source+target+kind
|
|
114
|
+
const exists = this.relations.some(r => r.sourceFile === rel.sourceFile &&
|
|
115
|
+
r.targetFile === rel.targetFile &&
|
|
116
|
+
r.kind === rel.kind &&
|
|
117
|
+
r.sourceSymbol === rel.sourceSymbol &&
|
|
118
|
+
r.targetSymbol === rel.targetSymbol);
|
|
119
|
+
if (exists)
|
|
120
|
+
return;
|
|
121
|
+
this.relations.push(rel);
|
|
122
|
+
// Update indexes
|
|
123
|
+
const out = this.outgoing.get(rel.sourceFile) ?? [];
|
|
124
|
+
out.push(rel);
|
|
125
|
+
this.outgoing.set(rel.sourceFile, out);
|
|
126
|
+
const inc = this.incoming.get(rel.targetFile) ?? [];
|
|
127
|
+
inc.push(rel);
|
|
128
|
+
this.incoming.set(rel.targetFile, inc);
|
|
129
|
+
}
|
|
130
|
+
addRelations(rels) {
|
|
131
|
+
for (const r of rels)
|
|
132
|
+
this.addRelation(r);
|
|
133
|
+
}
|
|
134
|
+
removeRelationsForSource(fileId) {
|
|
135
|
+
const before = this.relations.length;
|
|
136
|
+
this.relations = this.relations.filter(r => r.sourceFile !== fileId);
|
|
137
|
+
this.rebuildRelationIndexes();
|
|
138
|
+
return before - this.relations.length;
|
|
139
|
+
}
|
|
140
|
+
getOutgoing(fileId) {
|
|
141
|
+
return this.outgoing.get(fileId) ?? [];
|
|
142
|
+
}
|
|
143
|
+
getIncoming(fileId) {
|
|
144
|
+
return this.incoming.get(fileId) ?? [];
|
|
145
|
+
}
|
|
146
|
+
getAllRelations() {
|
|
147
|
+
return this.relations;
|
|
148
|
+
}
|
|
149
|
+
getRelationCount() {
|
|
150
|
+
return this.relations.length;
|
|
151
|
+
}
|
|
152
|
+
// ── Symbol Lookup ─────────────────────────────────────────────────────────
|
|
153
|
+
findFilesBySymbol(symbolName) {
|
|
154
|
+
const fileIds = this.symbolNameIndex.get(symbolName);
|
|
155
|
+
if (!fileIds)
|
|
156
|
+
return [];
|
|
157
|
+
return Array.from(fileIds)
|
|
158
|
+
.map(id => this.nodes.get(id))
|
|
159
|
+
.filter((n) => n !== undefined);
|
|
160
|
+
}
|
|
161
|
+
searchSymbols(query) {
|
|
162
|
+
const q = query.toLowerCase();
|
|
163
|
+
const results = [];
|
|
164
|
+
for (const [fileId, symbols] of this.symbolsByFile) {
|
|
165
|
+
for (const sym of symbols) {
|
|
166
|
+
if (sym.name.toLowerCase().includes(q)) {
|
|
167
|
+
const file = this.nodes.get(fileId);
|
|
168
|
+
if (file)
|
|
169
|
+
results.push({ symbol: sym, file });
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return results.sort((a, b) => {
|
|
174
|
+
// Exact match first, then prefix, then contains
|
|
175
|
+
const aExact = a.symbol.name.toLowerCase() === q ? 0 : 1;
|
|
176
|
+
const bExact = b.symbol.name.toLowerCase() === q ? 0 : 1;
|
|
177
|
+
if (aExact !== bExact)
|
|
178
|
+
return aExact - bExact;
|
|
179
|
+
const aPrefix = a.symbol.name.toLowerCase().startsWith(q) ? 0 : 1;
|
|
180
|
+
const bPrefix = b.symbol.name.toLowerCase().startsWith(q) ? 0 : 1;
|
|
181
|
+
return aPrefix - bPrefix;
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
getSymbolIndex() {
|
|
185
|
+
const index = new Map();
|
|
186
|
+
for (const [name, fileIds] of this.symbolNameIndex) {
|
|
187
|
+
const files = Array.from(fileIds)
|
|
188
|
+
.map(id => this.nodes.get(id))
|
|
189
|
+
.filter((n) => n !== undefined);
|
|
190
|
+
if (files.length > 0)
|
|
191
|
+
index.set(name, files);
|
|
192
|
+
}
|
|
193
|
+
return index;
|
|
194
|
+
}
|
|
195
|
+
// ── Graph Assembly ────────────────────────────────────────────────────────
|
|
196
|
+
toContextGraph(rootPath) {
|
|
197
|
+
return {
|
|
198
|
+
nodes: new Map(this.nodes),
|
|
199
|
+
relations: [...this.relations],
|
|
200
|
+
outgoing: new Map(this.outgoing.entries()),
|
|
201
|
+
incoming: new Map(this.incoming.entries()),
|
|
202
|
+
symbolIndex: this.getSymbolIndex(),
|
|
203
|
+
rootPath,
|
|
204
|
+
lastFullScan: Date.now(),
|
|
205
|
+
version: 1,
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
// ── Stats ─────────────────────────────────────────────────────────────────
|
|
209
|
+
getStats() {
|
|
210
|
+
let symbolCount = 0;
|
|
211
|
+
for (const syms of this.symbolsByFile.values())
|
|
212
|
+
symbolCount += syms.length;
|
|
213
|
+
return {
|
|
214
|
+
nodes: this.nodes.size,
|
|
215
|
+
symbols: symbolCount,
|
|
216
|
+
relations: this.relations.length,
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
// ── Internal ──────────────────────────────────────────────────────────────
|
|
220
|
+
rebuildRelationIndexes() {
|
|
221
|
+
this.outgoing.clear();
|
|
222
|
+
this.incoming.clear();
|
|
223
|
+
for (const rel of this.relations) {
|
|
224
|
+
const out = this.outgoing.get(rel.sourceFile) ?? [];
|
|
225
|
+
out.push(rel);
|
|
226
|
+
this.outgoing.set(rel.sourceFile, out);
|
|
227
|
+
const inc = this.incoming.get(rel.targetFile) ?? [];
|
|
228
|
+
inc.push(rel);
|
|
229
|
+
this.incoming.set(rel.targetFile, inc);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
/** Clear all data */
|
|
233
|
+
clear() {
|
|
234
|
+
this.nodes.clear();
|
|
235
|
+
this.relations = [];
|
|
236
|
+
this.symbolsByFile.clear();
|
|
237
|
+
this.symbolNameIndex.clear();
|
|
238
|
+
this.outgoing.clear();
|
|
239
|
+
this.incoming.clear();
|
|
240
|
+
}
|
|
241
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Code Relation Extractor — TypeScript / Python
|
|
3
|
+
*
|
|
4
|
+
* Extracts: imports, calls, extends, implements, uses_type, tested_by, tests
|
|
5
|
+
* Regex-based (no Tree-sitter) — matches existing codeIndexer philosophy.
|
|
6
|
+
*/
|
|
7
|
+
import type { FileNode, Relation } from '../types.js';
|
|
8
|
+
/**
|
|
9
|
+
* Extract all relations from a code file.
|
|
10
|
+
*/
|
|
11
|
+
export declare function extractCodeRelations(file: FileNode, allNodes: FileNode[], content: string): Relation[];
|
|
12
|
+
//# sourceMappingURL=code.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"code.d.ts","sourceRoot":"","sources":["../../../../src/graph/extractors/code.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAA2B,MAAM,aAAa,CAAC;AAuF/E;;GAEG;AACH,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,QAAQ,EACd,QAAQ,EAAE,QAAQ,EAAE,EACpB,OAAO,EAAE,MAAM,GACd,QAAQ,EAAE,CA4KZ"}
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Code Relation Extractor — TypeScript / Python
|
|
3
|
+
*
|
|
4
|
+
* Extracts: imports, calls, extends, implements, uses_type, tested_by, tests
|
|
5
|
+
* Regex-based (no Tree-sitter) — matches existing codeIndexer philosophy.
|
|
6
|
+
*/
|
|
7
|
+
// ── Import Patterns ───────────────────────────────────────────────────────────
|
|
8
|
+
const TS_IMPORT_RE = /import\s+(?:(?:type\s+)?(?:\{[^}]*\}|\*\s+as\s+\w+|\w+)(?:\s*,\s*(?:\{[^}]*\}|\*\s+as\s+\w+|\w+))*\s+from\s+)?['"]([^'"]+)['"]/g;
|
|
9
|
+
const TS_REQUIRE_RE = /require\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
|
|
10
|
+
const TS_DYNAMIC_IMPORT_RE = /import\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
|
|
11
|
+
const PY_FROM_IMPORT_RE = /^from\s+([\w.]+)\s+import\b/gm;
|
|
12
|
+
const PY_IMPORT_RE = /^import\s+([\w.]+)/gm;
|
|
13
|
+
// ── Inheritance Patterns ──────────────────────────────────────────────────────
|
|
14
|
+
const TS_EXTENDS_RE = /class\s+\w+(?:<[^>]*>)?\s+extends\s+(\w+)/g;
|
|
15
|
+
const TS_IMPLEMENTS_RE = /class\s+\w+(?:<[^>]*>)?\s+(?:extends\s+\w+(?:<[^>]*>)?\s+)?implements\s+([\w,\s]+)/g;
|
|
16
|
+
const PY_CLASS_INHERIT_RE = /class\s+\w+\(([^)]+)\)/g;
|
|
17
|
+
// ── Test Detection ────────────────────────────────────────────────────────────
|
|
18
|
+
const TEST_FILE_PATTERNS = [
|
|
19
|
+
/\.test\.[tj]sx?$/,
|
|
20
|
+
/\.spec\.[tj]sx?$/,
|
|
21
|
+
/__tests__\//,
|
|
22
|
+
/test_[\w]+\.py$/,
|
|
23
|
+
/[\w]+_test\.py$/,
|
|
24
|
+
];
|
|
25
|
+
const TEST_CONTENT_MARKERS = /\b(describe|it|test|expect|assert|def test_|class Test)\b/;
|
|
26
|
+
const COMMON_IDENTIFIERS = new Set([
|
|
27
|
+
'get', 'set', 'map', 'filter', 'reduce', 'find', 'forEach', 'push', 'pop',
|
|
28
|
+
'shift', 'unshift', 'slice', 'splice', 'concat', 'join', 'split', 'trim',
|
|
29
|
+
'toString', 'valueOf', 'hasOwnProperty', 'keys', 'values', 'entries',
|
|
30
|
+
'then', 'catch', 'finally', 'resolve', 'reject', 'log', 'error', 'warn',
|
|
31
|
+
'next', 'done', 'run', 'start', 'stop', 'open', 'close', 'read', 'write',
|
|
32
|
+
'init', 'reset', 'clear', 'update', 'delete', 'create', 'remove', 'add',
|
|
33
|
+
'length', 'size', 'name', 'type', 'value', 'data', 'result', 'status',
|
|
34
|
+
'self', 'cls', 'super', 'this', 'print', 'len', 'range', 'str', 'int',
|
|
35
|
+
]);
|
|
36
|
+
/**
|
|
37
|
+
* Resolve a relative import path to match against known file paths.
|
|
38
|
+
* Normalizes: './utils/helper' → 'utils/helper'
|
|
39
|
+
*/
|
|
40
|
+
function normalizeImportPath(importPath, sourceDir) {
|
|
41
|
+
if (importPath.startsWith('.')) {
|
|
42
|
+
const parts = sourceDir.split('/');
|
|
43
|
+
for (const segment of importPath.split('/')) {
|
|
44
|
+
if (segment === '.')
|
|
45
|
+
continue;
|
|
46
|
+
if (segment === '..') {
|
|
47
|
+
parts.pop();
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
parts.push(segment);
|
|
51
|
+
}
|
|
52
|
+
return parts.join('/');
|
|
53
|
+
}
|
|
54
|
+
return importPath;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Try to match an import path to a FileNode.
|
|
58
|
+
*/
|
|
59
|
+
function resolveImportTarget(importPath, sourceFile, allNodes) {
|
|
60
|
+
const sourceDir = sourceFile.path.split('/').slice(0, -1).join('/');
|
|
61
|
+
const normalized = normalizeImportPath(importPath, sourceDir);
|
|
62
|
+
// Try exact match, then with extensions
|
|
63
|
+
const candidates = [
|
|
64
|
+
normalized,
|
|
65
|
+
normalized + '.ts', normalized + '.tsx',
|
|
66
|
+
normalized + '.js', normalized + '.jsx',
|
|
67
|
+
normalized + '.py',
|
|
68
|
+
normalized + '/index.ts', normalized + '/index.js',
|
|
69
|
+
normalized + '/index.tsx',
|
|
70
|
+
];
|
|
71
|
+
for (const candidate of candidates) {
|
|
72
|
+
const match = allNodes.find(n => n.path === candidate || n.path.endsWith('/' + candidate));
|
|
73
|
+
if (match)
|
|
74
|
+
return match;
|
|
75
|
+
}
|
|
76
|
+
return undefined;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Extract all relations from a code file.
|
|
80
|
+
*/
|
|
81
|
+
export function extractCodeRelations(file, allNodes, content) {
|
|
82
|
+
const relations = [];
|
|
83
|
+
if (file.language !== 'typescript' && file.language !== 'python') {
|
|
84
|
+
return relations;
|
|
85
|
+
}
|
|
86
|
+
// ── Imports ────────────────────────────────────────────────────────────────
|
|
87
|
+
const importPatterns = file.language === 'typescript'
|
|
88
|
+
? [TS_IMPORT_RE, TS_REQUIRE_RE, TS_DYNAMIC_IMPORT_RE]
|
|
89
|
+
: [PY_FROM_IMPORT_RE, PY_IMPORT_RE];
|
|
90
|
+
for (const pattern of importPatterns) {
|
|
91
|
+
pattern.lastIndex = 0;
|
|
92
|
+
let match;
|
|
93
|
+
while ((match = pattern.exec(content)) !== null) {
|
|
94
|
+
const importPath = match[1];
|
|
95
|
+
if (!importPath)
|
|
96
|
+
continue;
|
|
97
|
+
// Skip node_modules / external packages
|
|
98
|
+
if (!importPath.startsWith('.') && !importPath.startsWith('/') && file.language === 'typescript')
|
|
99
|
+
continue;
|
|
100
|
+
const target = resolveImportTarget(importPath, file, allNodes);
|
|
101
|
+
if (target && target.id !== file.id) {
|
|
102
|
+
const isDynamic = pattern === TS_DYNAMIC_IMPORT_RE;
|
|
103
|
+
relations.push({
|
|
104
|
+
sourceFile: file.id,
|
|
105
|
+
targetFile: target.id,
|
|
106
|
+
kind: 'imports',
|
|
107
|
+
weight: isDynamic ? 0.7 : 1.0,
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
// ── Extends / Implements ───────────────────────────────────────────────────
|
|
113
|
+
const inheritancePatterns = file.language === 'typescript'
|
|
114
|
+
? [
|
|
115
|
+
{ re: TS_EXTENDS_RE, kind: 'extends' },
|
|
116
|
+
{ re: TS_IMPLEMENTS_RE, kind: 'implements' },
|
|
117
|
+
]
|
|
118
|
+
: [{ re: PY_CLASS_INHERIT_RE, kind: 'extends' }];
|
|
119
|
+
for (const { re, kind } of inheritancePatterns) {
|
|
120
|
+
re.lastIndex = 0;
|
|
121
|
+
let match;
|
|
122
|
+
while ((match = re.exec(content)) !== null) {
|
|
123
|
+
const names = match[1].split(',').map(s => s.trim()).filter(Boolean);
|
|
124
|
+
for (const name of names) {
|
|
125
|
+
if (name === 'object' || name === 'Object')
|
|
126
|
+
continue;
|
|
127
|
+
const targetFiles = allNodes.filter(n => n.id !== file.id &&
|
|
128
|
+
n.symbols.some(s => s.name === name && s.isExported));
|
|
129
|
+
for (const target of targetFiles) {
|
|
130
|
+
relations.push({
|
|
131
|
+
sourceFile: file.id,
|
|
132
|
+
sourceSymbol: undefined,
|
|
133
|
+
targetFile: target.id,
|
|
134
|
+
targetSymbol: name,
|
|
135
|
+
kind,
|
|
136
|
+
weight: 0.9,
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
// ── Calls ──────────────────────────────────────────────────────────────────
|
|
143
|
+
// Build index of all exported symbols across all files (excluding this file)
|
|
144
|
+
const exportedSymbols = new Map();
|
|
145
|
+
for (const node of allNodes) {
|
|
146
|
+
if (node.id === file.id)
|
|
147
|
+
continue;
|
|
148
|
+
for (const sym of node.symbols) {
|
|
149
|
+
if (!sym.isExported)
|
|
150
|
+
continue;
|
|
151
|
+
if (sym.kind !== 'function' && sym.kind !== 'const' && sym.kind !== 'class')
|
|
152
|
+
continue;
|
|
153
|
+
const arr = exportedSymbols.get(sym.name) ?? [];
|
|
154
|
+
arr.push({ file: node, symbol: sym });
|
|
155
|
+
exportedSymbols.set(sym.name, arr);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
// Scan for function calls: identifier followed by (
|
|
159
|
+
const CALL_RE = /\b(\w+)\s*\(/g;
|
|
160
|
+
CALL_RE.lastIndex = 0;
|
|
161
|
+
let callMatch;
|
|
162
|
+
const seenCalls = new Set();
|
|
163
|
+
while ((callMatch = CALL_RE.exec(content)) !== null) {
|
|
164
|
+
const name = callMatch[1];
|
|
165
|
+
if (!name || COMMON_IDENTIFIERS.has(name))
|
|
166
|
+
continue;
|
|
167
|
+
if (name.length < 3)
|
|
168
|
+
continue;
|
|
169
|
+
if (/^[A-Z_]+$/.test(name))
|
|
170
|
+
continue; // ALL_CAPS constants
|
|
171
|
+
if (/^(if|for|while|switch|catch|return|new|throw|typeof|instanceof|void|delete|await|async)$/.test(name))
|
|
172
|
+
continue;
|
|
173
|
+
const targets = exportedSymbols.get(name);
|
|
174
|
+
if (!targets)
|
|
175
|
+
continue;
|
|
176
|
+
for (const { file: targetFile } of targets) {
|
|
177
|
+
const key = `${file.id}→${targetFile.id}:${name}`;
|
|
178
|
+
if (seenCalls.has(key))
|
|
179
|
+
continue;
|
|
180
|
+
seenCalls.add(key);
|
|
181
|
+
// Only count if we actually import from that file
|
|
182
|
+
const hasImport = relations.some(r => r.sourceFile === file.id && r.targetFile === targetFile.id && r.kind === 'imports');
|
|
183
|
+
relations.push({
|
|
184
|
+
sourceFile: file.id,
|
|
185
|
+
targetFile: targetFile.id,
|
|
186
|
+
sourceSymbol: undefined,
|
|
187
|
+
targetSymbol: name,
|
|
188
|
+
kind: 'calls',
|
|
189
|
+
weight: hasImport ? 0.9 : 0.5,
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
// ── Test Relations ─────────────────────────────────────────────────────────
|
|
194
|
+
const isTestFile = TEST_FILE_PATTERNS.some(p => p.test(file.path)) ||
|
|
195
|
+
TEST_CONTENT_MARKERS.test(content);
|
|
196
|
+
if (isTestFile) {
|
|
197
|
+
// Find source files this test imports
|
|
198
|
+
for (const rel of relations) {
|
|
199
|
+
if (rel.kind === 'imports') {
|
|
200
|
+
const target = allNodes.find(n => n.id === rel.targetFile);
|
|
201
|
+
if (target && !TEST_FILE_PATTERNS.some(p => p.test(target.path))) {
|
|
202
|
+
relations.push({
|
|
203
|
+
sourceFile: file.id,
|
|
204
|
+
targetFile: target.id,
|
|
205
|
+
kind: 'tests',
|
|
206
|
+
weight: 1.0,
|
|
207
|
+
});
|
|
208
|
+
relations.push({
|
|
209
|
+
sourceFile: target.id,
|
|
210
|
+
targetFile: file.id,
|
|
211
|
+
kind: 'tested_by',
|
|
212
|
+
weight: 1.0,
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
// Also match by filename convention: payment.test.ts → payment.ts
|
|
218
|
+
const baseName = file.path
|
|
219
|
+
.replace(/\.test\.[tj]sx?$/, '')
|
|
220
|
+
.replace(/\.spec\.[tj]sx?$/, '')
|
|
221
|
+
.replace(/__tests__\//, '');
|
|
222
|
+
for (const node of allNodes) {
|
|
223
|
+
if (node.id === file.id)
|
|
224
|
+
continue;
|
|
225
|
+
const nodeName = node.path.replace(/\.[^.]+$/, '');
|
|
226
|
+
if (nodeName === baseName || node.path === baseName + '.ts' || node.path === baseName + '.tsx') {
|
|
227
|
+
if (!relations.some(r => r.sourceFile === file.id && r.targetFile === node.id && r.kind === 'tests')) {
|
|
228
|
+
relations.push({
|
|
229
|
+
sourceFile: file.id, targetFile: node.id, kind: 'tests', weight: 0.8,
|
|
230
|
+
});
|
|
231
|
+
relations.push({
|
|
232
|
+
sourceFile: node.id, targetFile: file.id, kind: 'tested_by', weight: 0.8,
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
return relations;
|
|
239
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cross-Type Relation Extractor
|
|
3
|
+
*
|
|
4
|
+
* Extracts: documents
|
|
5
|
+
* Bridges markdown docs ↔ code files based on directory co-location and
|
|
6
|
+
* explicit backtick mentions.
|
|
7
|
+
*/
|
|
8
|
+
import type { FileNode, Relation } from '../types.js';
|
|
9
|
+
/**
|
|
10
|
+
* Extract cross-type relations from any file.
|
|
11
|
+
*
|
|
12
|
+
* Rule 1: README.md → 'documents' all code files in the same directory (weight 0.7)
|
|
13
|
+
* Rule 2: Backtick `filename.ext` mentions → 'documents' the referenced file (weight 0.6)
|
|
14
|
+
*/
|
|
15
|
+
export declare function extractCrossTypeRelations(node: FileNode, allNodes: FileNode[], content: string): Relation[];
|
|
16
|
+
//# sourceMappingURL=cross-type.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cross-type.d.ts","sourceRoot":"","sources":["../../../../src/graph/extractors/cross-type.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAsBtD;;;;;GAKG;AACH,wBAAgB,yBAAyB,CACvC,IAAI,EAAE,QAAQ,EACd,QAAQ,EAAE,QAAQ,EAAE,EACpB,OAAO,EAAE,MAAM,GACd,QAAQ,EAAE,CA6CZ"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cross-Type Relation Extractor
|
|
3
|
+
*
|
|
4
|
+
* Extracts: documents
|
|
5
|
+
* Bridges markdown docs ↔ code files based on directory co-location and
|
|
6
|
+
* explicit backtick mentions.
|
|
7
|
+
*/
|
|
8
|
+
// ── Patterns ──────────────────────────────────────────────────────────────────
|
|
9
|
+
// Backtick-quoted token that looks like a filename (has an extension)
|
|
10
|
+
const BACKTICK_FILE_RE = /`([^`\n\r\s][^`\n\r]*?\.(?:ts|tsx|js|jsx|py|md|json|yaml|yml))`/g;
|
|
11
|
+
// File extensions that represent actual source/config files (not common words)
|
|
12
|
+
const CODE_EXT_RE = /\.(?:ts|tsx|js|jsx|py)$/;
|
|
13
|
+
// ── Helpers ───────────────────────────────────────────────────────────────────
|
|
14
|
+
function dirOf(path) {
|
|
15
|
+
return path.split('/').slice(0, -1).join('/');
|
|
16
|
+
}
|
|
17
|
+
function baseNameOf(path) {
|
|
18
|
+
return path.split('/').pop() ?? '';
|
|
19
|
+
}
|
|
20
|
+
// ── Main Extractor ────────────────────────────────────────────────────────────
|
|
21
|
+
/**
|
|
22
|
+
* Extract cross-type relations from any file.
|
|
23
|
+
*
|
|
24
|
+
* Rule 1: README.md → 'documents' all code files in the same directory (weight 0.7)
|
|
25
|
+
* Rule 2: Backtick `filename.ext` mentions → 'documents' the referenced file (weight 0.6)
|
|
26
|
+
*/
|
|
27
|
+
export function extractCrossTypeRelations(node, allNodes, content) {
|
|
28
|
+
const relations = [];
|
|
29
|
+
const nodeDir = dirOf(node.path);
|
|
30
|
+
const seen = new Set();
|
|
31
|
+
function addDocs(targetId, weight) {
|
|
32
|
+
const key = `${node.id}→${targetId}:documents`;
|
|
33
|
+
if (seen.has(key))
|
|
34
|
+
return;
|
|
35
|
+
seen.add(key);
|
|
36
|
+
relations.push({ sourceFile: node.id, targetFile: targetId, kind: 'documents', weight });
|
|
37
|
+
}
|
|
38
|
+
// ── Rule 1: README documents sibling code files ───────────────────────────
|
|
39
|
+
const fileName = baseNameOf(node.path).toLowerCase();
|
|
40
|
+
if (fileName === 'readme.md') {
|
|
41
|
+
for (const other of allNodes) {
|
|
42
|
+
if (other.id === node.id)
|
|
43
|
+
continue;
|
|
44
|
+
if (dirOf(other.path) !== nodeDir)
|
|
45
|
+
continue;
|
|
46
|
+
if (!CODE_EXT_RE.test(other.path))
|
|
47
|
+
continue;
|
|
48
|
+
addDocs(other.id, 0.7);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
// ── Rule 2: Backtick-quoted filename mentions ─────────────────────────────
|
|
52
|
+
BACKTICK_FILE_RE.lastIndex = 0;
|
|
53
|
+
let match;
|
|
54
|
+
while ((match = BACKTICK_FILE_RE.exec(content)) !== null) {
|
|
55
|
+
const raw = match[1].trim();
|
|
56
|
+
if (!raw || /\s/.test(raw))
|
|
57
|
+
continue; // skip if contains spaces
|
|
58
|
+
// Match against known nodes: by exact path, path suffix, or basename
|
|
59
|
+
const target = allNodes.find(n => n.id !== node.id && (n.path === raw ||
|
|
60
|
+
n.path.endsWith('/' + raw) ||
|
|
61
|
+
baseNameOf(n.path) === raw));
|
|
62
|
+
if (!target)
|
|
63
|
+
continue;
|
|
64
|
+
addDocs(target.id, 0.6);
|
|
65
|
+
}
|
|
66
|
+
return relations;
|
|
67
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Markdown Relation Extractor
|
|
3
|
+
*
|
|
4
|
+
* Extracts: links_to, references, supersedes, continues, defined_in, depends_on
|
|
5
|
+
*/
|
|
6
|
+
import type { FileNode, Relation } from '../types.js';
|
|
7
|
+
/**
|
|
8
|
+
* Collect all defined terms across markdown files.
|
|
9
|
+
* Returns: Map<term (lowercase), { file, originalTerm }>
|
|
10
|
+
*/
|
|
11
|
+
export declare function collectDefinedTerms(mdNodes: FileNode[]): Map<string, {
|
|
12
|
+
file: FileNode;
|
|
13
|
+
term: string;
|
|
14
|
+
}>;
|
|
15
|
+
/**
|
|
16
|
+
* Extract all relations from a markdown file.
|
|
17
|
+
*/
|
|
18
|
+
export declare function extractMarkdownRelations(file: FileNode, allNodes: FileNode[], content: string): Relation[];
|
|
19
|
+
/**
|
|
20
|
+
* Extract markdown symbols (headings + defined terms) for FileNode.symbols.
|
|
21
|
+
*/
|
|
22
|
+
export declare function extractMarkdownSymbols(content: string): Array<{
|
|
23
|
+
name: string;
|
|
24
|
+
kind: 'heading' | 'definition';
|
|
25
|
+
lineStart: number;
|
|
26
|
+
lineEnd: number;
|
|
27
|
+
isExported: boolean;
|
|
28
|
+
}>;
|
|
29
|
+
//# sourceMappingURL=markdown.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"markdown.d.ts","sourceRoot":"","sources":["../../../../src/graph/extractors/markdown.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAkEtD;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,QAAQ,EAAE,GAClB,GAAG,CAAC,MAAM,EAAE;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,CAkB/C;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CACtC,IAAI,EAAE,QAAQ,EACd,QAAQ,EAAE,QAAQ,EAAE,EACpB,OAAO,EAAE,MAAM,GACd,QAAQ,EAAE,CAyHZ;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM,GAAG,KAAK,CAAC;IAC7D,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,SAAS,GAAG,YAAY,CAAC;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,OAAO,CAAC;CACrB,CAAC,CAwCD"}
|