ai-mind-map 1.6.0 → 1.6.2
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/dist/change-tracker/change-log.js +123 -123
- package/dist/cli.js +83 -83
- package/dist/cli.js.map +1 -1
- package/dist/context/compressor.js +3 -3
- package/dist/context/compressor.js.map +1 -1
- package/dist/context/progressive-disclosure.js +4 -4
- package/dist/context/progressive-disclosure.js.map +1 -1
- package/dist/index.d.ts +0 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1371
- package/dist/index.js.map +1 -1
- package/dist/install.js +114 -114
- package/dist/install.js.map +1 -1
- package/dist/knowledge-graph/changelog.js +62 -62
- package/dist/knowledge-graph/dead-code.js +31 -31
- package/dist/knowledge-graph/graph.js +201 -201
- package/dist/knowledge-graph/indexer.d.ts +20 -0
- package/dist/knowledge-graph/indexer.d.ts.map +1 -1
- package/dist/knowledge-graph/indexer.js +55 -4
- package/dist/knowledge-graph/indexer.js.map +1 -1
- package/dist/knowledge-graph/semantic-search.js +50 -50
- package/dist/memory/decision-log.js +61 -61
- package/dist/memory/persistent-memory.js +70 -70
- package/dist/memory/session-memory.js +54 -54
- package/dist/tools/context-tools.js +2 -2
- package/dist/tools/context-tools.js.map +1 -1
- package/dist/tools/debug-tools.js +9 -9
- package/dist/tools/debug-tools.js.map +1 -1
- package/dist/tools/evolving-tools.js +3 -3
- package/dist/tools/evolving-tools.js.map +1 -1
- package/dist/tools/flow-tools.js +29 -29
- package/dist/tools/flow-tools.js.map +1 -1
- package/dist/tools/session-tools.js +2 -2
- package/dist/tools/session-tools.js.map +1 -1
- package/dist/tools/snapshot-tools.js +24 -24
- package/package.json +1 -1
|
@@ -12,109 +12,109 @@ import Database from 'better-sqlite3';
|
|
|
12
12
|
// ============================================================
|
|
13
13
|
// Schema SQL
|
|
14
14
|
// ============================================================
|
|
15
|
-
const SCHEMA_SQL = `
|
|
16
|
-
-- Core nodes table
|
|
17
|
-
CREATE TABLE IF NOT EXISTS nodes (
|
|
18
|
-
id TEXT PRIMARY KEY,
|
|
19
|
-
type TEXT NOT NULL,
|
|
20
|
-
name TEXT NOT NULL,
|
|
21
|
-
qualifiedName TEXT NOT NULL,
|
|
22
|
-
filePath TEXT NOT NULL,
|
|
23
|
-
startLine INTEGER NOT NULL,
|
|
24
|
-
endLine INTEGER NOT NULL,
|
|
25
|
-
signature TEXT NOT NULL DEFAULT '',
|
|
26
|
-
docComment TEXT,
|
|
27
|
-
hash TEXT NOT NULL,
|
|
28
|
-
language TEXT NOT NULL,
|
|
29
|
-
visibility TEXT NOT NULL DEFAULT 'unknown',
|
|
30
|
-
isAsync INTEGER NOT NULL DEFAULT 0,
|
|
31
|
-
isStatic INTEGER NOT NULL DEFAULT 0,
|
|
32
|
-
isExported INTEGER NOT NULL DEFAULT 0,
|
|
33
|
-
parameters TEXT,
|
|
34
|
-
returnType TEXT,
|
|
35
|
-
updatedAt INTEGER NOT NULL
|
|
36
|
-
);
|
|
37
|
-
|
|
38
|
-
-- Edges table with composite primary key
|
|
39
|
-
CREATE TABLE IF NOT EXISTS edges (
|
|
40
|
-
sourceId TEXT NOT NULL,
|
|
41
|
-
targetId TEXT NOT NULL,
|
|
42
|
-
type TEXT NOT NULL,
|
|
43
|
-
metadata TEXT,
|
|
44
|
-
PRIMARY KEY (sourceId, targetId, type)
|
|
45
|
-
);
|
|
46
|
-
|
|
47
|
-
-- Indexes for common queries
|
|
48
|
-
CREATE INDEX IF NOT EXISTS idx_nodes_filePath ON nodes(filePath);
|
|
49
|
-
CREATE INDEX IF NOT EXISTS idx_nodes_type ON nodes(type);
|
|
50
|
-
CREATE INDEX IF NOT EXISTS idx_nodes_name ON nodes(name);
|
|
51
|
-
CREATE INDEX IF NOT EXISTS idx_nodes_language ON nodes(language);
|
|
52
|
-
CREATE INDEX IF NOT EXISTS idx_nodes_hash ON nodes(hash);
|
|
53
|
-
CREATE INDEX IF NOT EXISTS idx_edges_sourceId ON edges(sourceId);
|
|
54
|
-
CREATE INDEX IF NOT EXISTS idx_edges_targetId ON edges(targetId);
|
|
55
|
-
CREATE INDEX IF NOT EXISTS idx_edges_type ON edges(type);
|
|
56
|
-
|
|
57
|
-
-- FTS5 virtual table for full-text search across names, signatures, and doc comments
|
|
58
|
-
CREATE VIRTUAL TABLE IF NOT EXISTS nodes_fts USING fts5(
|
|
59
|
-
id UNINDEXED,
|
|
60
|
-
name,
|
|
61
|
-
qualifiedName,
|
|
62
|
-
signature,
|
|
63
|
-
docComment,
|
|
64
|
-
content='nodes',
|
|
65
|
-
content_rowid='rowid',
|
|
66
|
-
tokenize='porter unicode61'
|
|
67
|
-
);
|
|
68
|
-
|
|
69
|
-
-- Triggers to keep FTS in sync with nodes table
|
|
70
|
-
CREATE TRIGGER IF NOT EXISTS nodes_ai AFTER INSERT ON nodes BEGIN
|
|
71
|
-
INSERT INTO nodes_fts(rowid, id, name, qualifiedName, signature, docComment)
|
|
72
|
-
VALUES (new.rowid, new.id, new.name, new.qualifiedName, new.signature, new.docComment);
|
|
73
|
-
END;
|
|
74
|
-
|
|
75
|
-
CREATE TRIGGER IF NOT EXISTS nodes_ad AFTER DELETE ON nodes BEGIN
|
|
76
|
-
INSERT INTO nodes_fts(nodes_fts, rowid, id, name, qualifiedName, signature, docComment)
|
|
77
|
-
VALUES ('delete', old.rowid, old.id, old.name, old.qualifiedName, old.signature, old.docComment);
|
|
78
|
-
END;
|
|
79
|
-
|
|
80
|
-
CREATE TRIGGER IF NOT EXISTS nodes_au AFTER UPDATE ON nodes BEGIN
|
|
81
|
-
INSERT INTO nodes_fts(nodes_fts, rowid, id, name, qualifiedName, signature, docComment)
|
|
82
|
-
VALUES ('delete', old.rowid, old.id, old.name, old.qualifiedName, old.signature, old.docComment);
|
|
83
|
-
INSERT INTO nodes_fts(rowid, id, name, qualifiedName, signature, docComment)
|
|
84
|
-
VALUES (new.rowid, new.id, new.name, new.qualifiedName, new.signature, new.docComment);
|
|
85
|
-
END;
|
|
86
|
-
|
|
87
|
-
-- Metadata table for tracking schema version and stats
|
|
88
|
-
CREATE TABLE IF NOT EXISTS graph_meta (
|
|
89
|
-
key TEXT PRIMARY KEY,
|
|
90
|
-
value TEXT NOT NULL
|
|
91
|
-
);
|
|
92
|
-
|
|
93
|
-
-- Learned rules: AI-taught patterns that persist per-project
|
|
94
|
-
CREATE TABLE IF NOT EXISTS learned_rules (
|
|
95
|
-
id TEXT PRIMARY KEY,
|
|
96
|
-
type TEXT NOT NULL, -- 'classification' | 'search_alias' | 'code_pattern' | 'convention'
|
|
97
|
-
name TEXT NOT NULL,
|
|
98
|
-
description TEXT NOT NULL,
|
|
99
|
-
rule TEXT NOT NULL, -- JSON: the actual rule definition
|
|
100
|
-
created_at INTEGER NOT NULL,
|
|
101
|
-
updated_at INTEGER NOT NULL,
|
|
102
|
-
used_count INTEGER NOT NULL DEFAULT 0,
|
|
103
|
-
last_used_at INTEGER,
|
|
104
|
-
created_by TEXT NOT NULL DEFAULT 'ai'
|
|
105
|
-
);
|
|
106
|
-
|
|
107
|
-
CREATE INDEX IF NOT EXISTS idx_learned_rules_type ON learned_rules(type);
|
|
108
|
-
CREATE INDEX IF NOT EXISTS idx_learned_rules_name ON learned_rules(name);
|
|
109
|
-
|
|
110
|
-
-- File index for fast staleness detection via mtime
|
|
111
|
-
CREATE TABLE IF NOT EXISTS file_index (
|
|
112
|
-
file_path TEXT PRIMARY KEY,
|
|
113
|
-
mtime_ms REAL NOT NULL,
|
|
114
|
-
size_bytes INTEGER NOT NULL,
|
|
115
|
-
content_hash TEXT NOT NULL,
|
|
116
|
-
indexed_at INTEGER NOT NULL
|
|
117
|
-
);
|
|
15
|
+
const SCHEMA_SQL = `
|
|
16
|
+
-- Core nodes table
|
|
17
|
+
CREATE TABLE IF NOT EXISTS nodes (
|
|
18
|
+
id TEXT PRIMARY KEY,
|
|
19
|
+
type TEXT NOT NULL,
|
|
20
|
+
name TEXT NOT NULL,
|
|
21
|
+
qualifiedName TEXT NOT NULL,
|
|
22
|
+
filePath TEXT NOT NULL,
|
|
23
|
+
startLine INTEGER NOT NULL,
|
|
24
|
+
endLine INTEGER NOT NULL,
|
|
25
|
+
signature TEXT NOT NULL DEFAULT '',
|
|
26
|
+
docComment TEXT,
|
|
27
|
+
hash TEXT NOT NULL,
|
|
28
|
+
language TEXT NOT NULL,
|
|
29
|
+
visibility TEXT NOT NULL DEFAULT 'unknown',
|
|
30
|
+
isAsync INTEGER NOT NULL DEFAULT 0,
|
|
31
|
+
isStatic INTEGER NOT NULL DEFAULT 0,
|
|
32
|
+
isExported INTEGER NOT NULL DEFAULT 0,
|
|
33
|
+
parameters TEXT,
|
|
34
|
+
returnType TEXT,
|
|
35
|
+
updatedAt INTEGER NOT NULL
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
-- Edges table with composite primary key
|
|
39
|
+
CREATE TABLE IF NOT EXISTS edges (
|
|
40
|
+
sourceId TEXT NOT NULL,
|
|
41
|
+
targetId TEXT NOT NULL,
|
|
42
|
+
type TEXT NOT NULL,
|
|
43
|
+
metadata TEXT,
|
|
44
|
+
PRIMARY KEY (sourceId, targetId, type)
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
-- Indexes for common queries
|
|
48
|
+
CREATE INDEX IF NOT EXISTS idx_nodes_filePath ON nodes(filePath);
|
|
49
|
+
CREATE INDEX IF NOT EXISTS idx_nodes_type ON nodes(type);
|
|
50
|
+
CREATE INDEX IF NOT EXISTS idx_nodes_name ON nodes(name);
|
|
51
|
+
CREATE INDEX IF NOT EXISTS idx_nodes_language ON nodes(language);
|
|
52
|
+
CREATE INDEX IF NOT EXISTS idx_nodes_hash ON nodes(hash);
|
|
53
|
+
CREATE INDEX IF NOT EXISTS idx_edges_sourceId ON edges(sourceId);
|
|
54
|
+
CREATE INDEX IF NOT EXISTS idx_edges_targetId ON edges(targetId);
|
|
55
|
+
CREATE INDEX IF NOT EXISTS idx_edges_type ON edges(type);
|
|
56
|
+
|
|
57
|
+
-- FTS5 virtual table for full-text search across names, signatures, and doc comments
|
|
58
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS nodes_fts USING fts5(
|
|
59
|
+
id UNINDEXED,
|
|
60
|
+
name,
|
|
61
|
+
qualifiedName,
|
|
62
|
+
signature,
|
|
63
|
+
docComment,
|
|
64
|
+
content='nodes',
|
|
65
|
+
content_rowid='rowid',
|
|
66
|
+
tokenize='porter unicode61'
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
-- Triggers to keep FTS in sync with nodes table
|
|
70
|
+
CREATE TRIGGER IF NOT EXISTS nodes_ai AFTER INSERT ON nodes BEGIN
|
|
71
|
+
INSERT INTO nodes_fts(rowid, id, name, qualifiedName, signature, docComment)
|
|
72
|
+
VALUES (new.rowid, new.id, new.name, new.qualifiedName, new.signature, new.docComment);
|
|
73
|
+
END;
|
|
74
|
+
|
|
75
|
+
CREATE TRIGGER IF NOT EXISTS nodes_ad AFTER DELETE ON nodes BEGIN
|
|
76
|
+
INSERT INTO nodes_fts(nodes_fts, rowid, id, name, qualifiedName, signature, docComment)
|
|
77
|
+
VALUES ('delete', old.rowid, old.id, old.name, old.qualifiedName, old.signature, old.docComment);
|
|
78
|
+
END;
|
|
79
|
+
|
|
80
|
+
CREATE TRIGGER IF NOT EXISTS nodes_au AFTER UPDATE ON nodes BEGIN
|
|
81
|
+
INSERT INTO nodes_fts(nodes_fts, rowid, id, name, qualifiedName, signature, docComment)
|
|
82
|
+
VALUES ('delete', old.rowid, old.id, old.name, old.qualifiedName, old.signature, old.docComment);
|
|
83
|
+
INSERT INTO nodes_fts(rowid, id, name, qualifiedName, signature, docComment)
|
|
84
|
+
VALUES (new.rowid, new.id, new.name, new.qualifiedName, new.signature, new.docComment);
|
|
85
|
+
END;
|
|
86
|
+
|
|
87
|
+
-- Metadata table for tracking schema version and stats
|
|
88
|
+
CREATE TABLE IF NOT EXISTS graph_meta (
|
|
89
|
+
key TEXT PRIMARY KEY,
|
|
90
|
+
value TEXT NOT NULL
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
-- Learned rules: AI-taught patterns that persist per-project
|
|
94
|
+
CREATE TABLE IF NOT EXISTS learned_rules (
|
|
95
|
+
id TEXT PRIMARY KEY,
|
|
96
|
+
type TEXT NOT NULL, -- 'classification' | 'search_alias' | 'code_pattern' | 'convention'
|
|
97
|
+
name TEXT NOT NULL,
|
|
98
|
+
description TEXT NOT NULL,
|
|
99
|
+
rule TEXT NOT NULL, -- JSON: the actual rule definition
|
|
100
|
+
created_at INTEGER NOT NULL,
|
|
101
|
+
updated_at INTEGER NOT NULL,
|
|
102
|
+
used_count INTEGER NOT NULL DEFAULT 0,
|
|
103
|
+
last_used_at INTEGER,
|
|
104
|
+
created_by TEXT NOT NULL DEFAULT 'ai'
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
CREATE INDEX IF NOT EXISTS idx_learned_rules_type ON learned_rules(type);
|
|
108
|
+
CREATE INDEX IF NOT EXISTS idx_learned_rules_name ON learned_rules(name);
|
|
109
|
+
|
|
110
|
+
-- File index for fast staleness detection via mtime
|
|
111
|
+
CREATE TABLE IF NOT EXISTS file_index (
|
|
112
|
+
file_path TEXT PRIMARY KEY,
|
|
113
|
+
mtime_ms REAL NOT NULL,
|
|
114
|
+
size_bytes INTEGER NOT NULL,
|
|
115
|
+
content_hash TEXT NOT NULL,
|
|
116
|
+
indexed_at INTEGER NOT NULL
|
|
117
|
+
);
|
|
118
118
|
`;
|
|
119
119
|
const SCHEMA_VERSION = '4';
|
|
120
120
|
// ============================================================
|
|
@@ -270,16 +270,16 @@ export class KnowledgeGraph {
|
|
|
270
270
|
* Insert or update a single node.
|
|
271
271
|
*/
|
|
272
272
|
upsertNode(node) {
|
|
273
|
-
this.db.prepare(`
|
|
274
|
-
INSERT OR REPLACE INTO nodes (
|
|
275
|
-
id, type, name, qualifiedName, filePath, startLine, endLine,
|
|
276
|
-
signature, docComment, hash, language, visibility,
|
|
277
|
-
isAsync, isStatic, isExported, parameters, returnType, updatedAt
|
|
278
|
-
) VALUES (
|
|
279
|
-
@id, @type, @name, @qualifiedName, @filePath, @startLine, @endLine,
|
|
280
|
-
@signature, @docComment, @hash, @language, @visibility,
|
|
281
|
-
@isAsync, @isStatic, @isExported, @parameters, @returnType, @updatedAt
|
|
282
|
-
)
|
|
273
|
+
this.db.prepare(`
|
|
274
|
+
INSERT OR REPLACE INTO nodes (
|
|
275
|
+
id, type, name, qualifiedName, filePath, startLine, endLine,
|
|
276
|
+
signature, docComment, hash, language, visibility,
|
|
277
|
+
isAsync, isStatic, isExported, parameters, returnType, updatedAt
|
|
278
|
+
) VALUES (
|
|
279
|
+
@id, @type, @name, @qualifiedName, @filePath, @startLine, @endLine,
|
|
280
|
+
@signature, @docComment, @hash, @language, @visibility,
|
|
281
|
+
@isAsync, @isStatic, @isExported, @parameters, @returnType, @updatedAt
|
|
282
|
+
)
|
|
283
283
|
`).run({
|
|
284
284
|
id: node.id,
|
|
285
285
|
type: node.type,
|
|
@@ -307,16 +307,16 @@ export class KnowledgeGraph {
|
|
|
307
307
|
upsertNodes(nodes) {
|
|
308
308
|
if (nodes.length === 0)
|
|
309
309
|
return;
|
|
310
|
-
const insertStmt = this.db.prepare(`
|
|
311
|
-
INSERT OR REPLACE INTO nodes (
|
|
312
|
-
id, type, name, qualifiedName, filePath, startLine, endLine,
|
|
313
|
-
signature, docComment, hash, language, visibility,
|
|
314
|
-
isAsync, isStatic, isExported, parameters, returnType, updatedAt
|
|
315
|
-
) VALUES (
|
|
316
|
-
@id, @type, @name, @qualifiedName, @filePath, @startLine, @endLine,
|
|
317
|
-
@signature, @docComment, @hash, @language, @visibility,
|
|
318
|
-
@isAsync, @isStatic, @isExported, @parameters, @returnType, @updatedAt
|
|
319
|
-
)
|
|
310
|
+
const insertStmt = this.db.prepare(`
|
|
311
|
+
INSERT OR REPLACE INTO nodes (
|
|
312
|
+
id, type, name, qualifiedName, filePath, startLine, endLine,
|
|
313
|
+
signature, docComment, hash, language, visibility,
|
|
314
|
+
isAsync, isStatic, isExported, parameters, returnType, updatedAt
|
|
315
|
+
) VALUES (
|
|
316
|
+
@id, @type, @name, @qualifiedName, @filePath, @startLine, @endLine,
|
|
317
|
+
@signature, @docComment, @hash, @language, @visibility,
|
|
318
|
+
@isAsync, @isStatic, @isExported, @parameters, @returnType, @updatedAt
|
|
319
|
+
)
|
|
320
320
|
`);
|
|
321
321
|
const transaction = this.db.transaction((items) => {
|
|
322
322
|
for (const node of items) {
|
|
@@ -408,9 +408,9 @@ export class KnowledgeGraph {
|
|
|
408
408
|
* Insert or update a single edge.
|
|
409
409
|
*/
|
|
410
410
|
upsertEdge(edge) {
|
|
411
|
-
this.db.prepare(`
|
|
412
|
-
INSERT OR REPLACE INTO edges (sourceId, targetId, type, metadata)
|
|
413
|
-
VALUES (@sourceId, @targetId, @type, @metadata)
|
|
411
|
+
this.db.prepare(`
|
|
412
|
+
INSERT OR REPLACE INTO edges (sourceId, targetId, type, metadata)
|
|
413
|
+
VALUES (@sourceId, @targetId, @type, @metadata)
|
|
414
414
|
`).run({
|
|
415
415
|
sourceId: edge.sourceId,
|
|
416
416
|
targetId: edge.targetId,
|
|
@@ -424,9 +424,9 @@ export class KnowledgeGraph {
|
|
|
424
424
|
upsertEdges(edges) {
|
|
425
425
|
if (edges.length === 0)
|
|
426
426
|
return;
|
|
427
|
-
const insertStmt = this.db.prepare(`
|
|
428
|
-
INSERT OR REPLACE INTO edges (sourceId, targetId, type, metadata)
|
|
429
|
-
VALUES (@sourceId, @targetId, @type, @metadata)
|
|
427
|
+
const insertStmt = this.db.prepare(`
|
|
428
|
+
INSERT OR REPLACE INTO edges (sourceId, targetId, type, metadata)
|
|
429
|
+
VALUES (@sourceId, @targetId, @type, @metadata)
|
|
430
430
|
`);
|
|
431
431
|
const transaction = this.db.transaction((items) => {
|
|
432
432
|
for (const edge of items) {
|
|
@@ -506,10 +506,10 @@ export class KnowledgeGraph {
|
|
|
506
506
|
* Find all nodes that call a given node (callers / "who calls this?").
|
|
507
507
|
*/
|
|
508
508
|
findCallers(nodeId) {
|
|
509
|
-
const rows = this.db.prepare(`
|
|
510
|
-
SELECT n.* FROM nodes n
|
|
511
|
-
JOIN edges e ON n.id = e.sourceId
|
|
512
|
-
WHERE e.targetId = ? AND e.type = 'calls'
|
|
509
|
+
const rows = this.db.prepare(`
|
|
510
|
+
SELECT n.* FROM nodes n
|
|
511
|
+
JOIN edges e ON n.id = e.sourceId
|
|
512
|
+
WHERE e.targetId = ? AND e.type = 'calls'
|
|
513
513
|
`).all(nodeId);
|
|
514
514
|
return rows.map((r) => this.rowToNode(r));
|
|
515
515
|
}
|
|
@@ -517,10 +517,10 @@ export class KnowledgeGraph {
|
|
|
517
517
|
* Find all nodes that a given node calls (callees / "what does this call?").
|
|
518
518
|
*/
|
|
519
519
|
findCallees(nodeId) {
|
|
520
|
-
const rows = this.db.prepare(`
|
|
521
|
-
SELECT n.* FROM nodes n
|
|
522
|
-
JOIN edges e ON n.id = e.targetId
|
|
523
|
-
WHERE e.sourceId = ? AND e.type = 'calls'
|
|
520
|
+
const rows = this.db.prepare(`
|
|
521
|
+
SELECT n.* FROM nodes n
|
|
522
|
+
JOIN edges e ON n.id = e.targetId
|
|
523
|
+
WHERE e.sourceId = ? AND e.type = 'calls'
|
|
524
524
|
`).all(nodeId);
|
|
525
525
|
return rows.map((r) => this.rowToNode(r));
|
|
526
526
|
}
|
|
@@ -539,9 +539,9 @@ export class KnowledgeGraph {
|
|
|
539
539
|
if (current.depth >= maxDepth || visited.has(current.id))
|
|
540
540
|
continue;
|
|
541
541
|
visited.add(current.id);
|
|
542
|
-
const parentEdges = this.db.prepare(`
|
|
543
|
-
SELECT e.targetId FROM edges e
|
|
544
|
-
WHERE e.sourceId = ? AND e.type IN ('inherits', 'implements', 'contains')
|
|
542
|
+
const parentEdges = this.db.prepare(`
|
|
543
|
+
SELECT e.targetId FROM edges e
|
|
544
|
+
WHERE e.sourceId = ? AND e.type IN ('inherits', 'implements', 'contains')
|
|
545
545
|
`).all(current.id);
|
|
546
546
|
for (const { targetId } of parentEdges) {
|
|
547
547
|
if (!visited.has(targetId)) {
|
|
@@ -570,9 +570,9 @@ export class KnowledgeGraph {
|
|
|
570
570
|
if (current.depth >= maxDepth || visited.has(current.id))
|
|
571
571
|
continue;
|
|
572
572
|
visited.add(current.id);
|
|
573
|
-
const childEdges = this.db.prepare(`
|
|
574
|
-
SELECT e.sourceId FROM edges e
|
|
575
|
-
WHERE e.targetId = ? AND e.type IN ('inherits', 'implements', 'contains')
|
|
573
|
+
const childEdges = this.db.prepare(`
|
|
574
|
+
SELECT e.sourceId FROM edges e
|
|
575
|
+
WHERE e.targetId = ? AND e.type IN ('inherits', 'implements', 'contains')
|
|
576
576
|
`).all(current.id);
|
|
577
577
|
for (const { sourceId } of childEdges) {
|
|
578
578
|
if (!visited.has(sourceId)) {
|
|
@@ -607,9 +607,9 @@ export class KnowledgeGraph {
|
|
|
607
607
|
continue;
|
|
608
608
|
visited.add(current.id);
|
|
609
609
|
// Find nodes that depend ON this node (reverse dependency)
|
|
610
|
-
const dependents = this.db.prepare(`
|
|
611
|
-
SELECT e.sourceId FROM edges e
|
|
612
|
-
WHERE e.targetId = ? AND e.type IN (${typeFilter})
|
|
610
|
+
const dependents = this.db.prepare(`
|
|
611
|
+
SELECT e.sourceId FROM edges e
|
|
612
|
+
WHERE e.targetId = ? AND e.type IN (${typeFilter})
|
|
613
613
|
`).all(current.id);
|
|
614
614
|
for (const { sourceId } of dependents) {
|
|
615
615
|
if (!visited.has(sourceId)) {
|
|
@@ -647,38 +647,38 @@ export class KnowledgeGraph {
|
|
|
647
647
|
try {
|
|
648
648
|
// Use FTS5 bm25() for proper information-retrieval ranking
|
|
649
649
|
// bm25 weights: name(10), qualifiedName(5), signature(3), docComment(1)
|
|
650
|
-
const rows = this.db.prepare(`
|
|
651
|
-
SELECT * FROM (
|
|
652
|
-
SELECT n.*, bm25(nodes_fts, 10, 5, 3, 1) AS bm25_score,
|
|
653
|
-
CASE
|
|
654
|
-
WHEN n.name = @query THEN 0
|
|
655
|
-
WHEN n.name LIKE @like THEN 1
|
|
656
|
-
WHEN n.qualifiedName LIKE @like THEN 2
|
|
657
|
-
ELSE 3
|
|
658
|
-
END AS exact_bonus
|
|
659
|
-
FROM nodes_fts fts
|
|
660
|
-
JOIN nodes n ON fts.id = n.id
|
|
661
|
-
WHERE nodes_fts MATCH @fts
|
|
662
|
-
|
|
663
|
-
UNION
|
|
664
|
-
|
|
665
|
-
SELECT n.*, 0 AS bm25_score,
|
|
666
|
-
CASE
|
|
667
|
-
WHEN n.name = @query THEN 0
|
|
668
|
-
WHEN n.name LIKE @like THEN 1
|
|
669
|
-
WHEN n.qualifiedName LIKE @like THEN 2
|
|
670
|
-
WHEN n.signature LIKE @like THEN 3
|
|
671
|
-
ELSE 4
|
|
672
|
-
END AS exact_bonus
|
|
673
|
-
FROM nodes n
|
|
674
|
-
WHERE n.name LIKE @like
|
|
675
|
-
OR n.qualifiedName LIKE @like
|
|
676
|
-
OR n.signature LIKE @like
|
|
677
|
-
OR n.docComment LIKE @like
|
|
678
|
-
)
|
|
679
|
-
GROUP BY id
|
|
680
|
-
ORDER BY MIN(exact_bonus), MIN(bm25_score), name
|
|
681
|
-
LIMIT @limit
|
|
650
|
+
const rows = this.db.prepare(`
|
|
651
|
+
SELECT * FROM (
|
|
652
|
+
SELECT n.*, bm25(nodes_fts, 10, 5, 3, 1) AS bm25_score,
|
|
653
|
+
CASE
|
|
654
|
+
WHEN n.name = @query THEN 0
|
|
655
|
+
WHEN n.name LIKE @like THEN 1
|
|
656
|
+
WHEN n.qualifiedName LIKE @like THEN 2
|
|
657
|
+
ELSE 3
|
|
658
|
+
END AS exact_bonus
|
|
659
|
+
FROM nodes_fts fts
|
|
660
|
+
JOIN nodes n ON fts.id = n.id
|
|
661
|
+
WHERE nodes_fts MATCH @fts
|
|
662
|
+
|
|
663
|
+
UNION
|
|
664
|
+
|
|
665
|
+
SELECT n.*, 0 AS bm25_score,
|
|
666
|
+
CASE
|
|
667
|
+
WHEN n.name = @query THEN 0
|
|
668
|
+
WHEN n.name LIKE @like THEN 1
|
|
669
|
+
WHEN n.qualifiedName LIKE @like THEN 2
|
|
670
|
+
WHEN n.signature LIKE @like THEN 3
|
|
671
|
+
ELSE 4
|
|
672
|
+
END AS exact_bonus
|
|
673
|
+
FROM nodes n
|
|
674
|
+
WHERE n.name LIKE @like
|
|
675
|
+
OR n.qualifiedName LIKE @like
|
|
676
|
+
OR n.signature LIKE @like
|
|
677
|
+
OR n.docComment LIKE @like
|
|
678
|
+
)
|
|
679
|
+
GROUP BY id
|
|
680
|
+
ORDER BY MIN(exact_bonus), MIN(bm25_score), name
|
|
681
|
+
LIMIT @limit
|
|
682
682
|
`).all({ query: trimmed, like: likePattern, fts: sanitized, limit });
|
|
683
683
|
return rows.map((r) => this.rowToNode(r));
|
|
684
684
|
}
|
|
@@ -707,19 +707,19 @@ export class KnowledgeGraph {
|
|
|
707
707
|
/** Fallback LIKE-based search */
|
|
708
708
|
searchLike(query, limit) {
|
|
709
709
|
const pattern = `%${query}%`;
|
|
710
|
-
const rows = this.db.prepare(`
|
|
711
|
-
SELECT * FROM nodes
|
|
712
|
-
WHERE name LIKE ? OR qualifiedName LIKE ? OR signature LIKE ? OR docComment LIKE ?
|
|
713
|
-
ORDER BY
|
|
714
|
-
CASE
|
|
715
|
-
WHEN name = ? THEN 0
|
|
716
|
-
WHEN name LIKE ? THEN 1
|
|
717
|
-
WHEN qualifiedName LIKE ? THEN 2
|
|
718
|
-
WHEN signature LIKE ? THEN 3
|
|
719
|
-
ELSE 4
|
|
720
|
-
END,
|
|
721
|
-
name
|
|
722
|
-
LIMIT ?
|
|
710
|
+
const rows = this.db.prepare(`
|
|
711
|
+
SELECT * FROM nodes
|
|
712
|
+
WHERE name LIKE ? OR qualifiedName LIKE ? OR signature LIKE ? OR docComment LIKE ?
|
|
713
|
+
ORDER BY
|
|
714
|
+
CASE
|
|
715
|
+
WHEN name = ? THEN 0
|
|
716
|
+
WHEN name LIKE ? THEN 1
|
|
717
|
+
WHEN qualifiedName LIKE ? THEN 2
|
|
718
|
+
WHEN signature LIKE ? THEN 3
|
|
719
|
+
ELSE 4
|
|
720
|
+
END,
|
|
721
|
+
name
|
|
722
|
+
LIMIT ?
|
|
723
723
|
`).all(pattern, pattern, pattern, pattern, query, pattern, pattern, pattern, limit);
|
|
724
724
|
return rows.map((r) => this.rowToNode(r));
|
|
725
725
|
}
|
|
@@ -761,10 +761,10 @@ export class KnowledgeGraph {
|
|
|
761
761
|
getProjectOverview() {
|
|
762
762
|
const overview = new Map();
|
|
763
763
|
// Single query: get ALL non-file nodes, ordered by file then line
|
|
764
|
-
const allSymbols = this.db.prepare(`
|
|
765
|
-
SELECT * FROM nodes
|
|
766
|
-
WHERE type != 'file'
|
|
767
|
-
ORDER BY filePath, startLine
|
|
764
|
+
const allSymbols = this.db.prepare(`
|
|
765
|
+
SELECT * FROM nodes
|
|
766
|
+
WHERE type != 'file'
|
|
767
|
+
ORDER BY filePath, startLine
|
|
768
768
|
`).all();
|
|
769
769
|
for (const row of allSymbols) {
|
|
770
770
|
const node = this.rowToNode(row);
|
|
@@ -925,9 +925,9 @@ export class KnowledgeGraph {
|
|
|
925
925
|
this.db.prepare('UPDATE learned_rules SET description = ?, rule = ?, updated_at = ? WHERE id = ?').run(rule.description, JSON.stringify(rule.rule), now, existing.id);
|
|
926
926
|
return { id: existing.id, created: false };
|
|
927
927
|
}
|
|
928
|
-
this.db.prepare(`
|
|
929
|
-
INSERT INTO learned_rules (id, type, name, description, rule, created_at, updated_at, created_by)
|
|
930
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
928
|
+
this.db.prepare(`
|
|
929
|
+
INSERT INTO learned_rules (id, type, name, description, rule, created_at, updated_at, created_by)
|
|
930
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
931
931
|
`).run(id, rule.type, rule.name, rule.description, JSON.stringify(rule.rule), now, now, rule.createdBy ?? 'ai');
|
|
932
932
|
return { id, created: true };
|
|
933
933
|
}
|
|
@@ -1008,9 +1008,9 @@ export class KnowledgeGraph {
|
|
|
1008
1008
|
* Upsert a file index entry after indexing.
|
|
1009
1009
|
*/
|
|
1010
1010
|
upsertFileIndex(filePath, mtimeMs, sizeBytes, contentHash) {
|
|
1011
|
-
this.db.prepare(`
|
|
1012
|
-
INSERT OR REPLACE INTO file_index (file_path, mtime_ms, size_bytes, content_hash, indexed_at)
|
|
1013
|
-
VALUES (?, ?, ?, ?, ?)
|
|
1011
|
+
this.db.prepare(`
|
|
1012
|
+
INSERT OR REPLACE INTO file_index (file_path, mtime_ms, size_bytes, content_hash, indexed_at)
|
|
1013
|
+
VALUES (?, ?, ?, ?, ?)
|
|
1014
1014
|
`).run(filePath, mtimeMs, sizeBytes, contentHash, Date.now());
|
|
1015
1015
|
}
|
|
1016
1016
|
/**
|
|
@@ -44,6 +44,13 @@ export declare class Indexer {
|
|
|
44
44
|
private config;
|
|
45
45
|
private ig;
|
|
46
46
|
private changelog;
|
|
47
|
+
/**
|
|
48
|
+
* Per-project-root ignore patterns.
|
|
49
|
+
* When multiple projects are indexed, each keeps its own .gitignore rules.
|
|
50
|
+
*/
|
|
51
|
+
private projectIgnores;
|
|
52
|
+
/** Track all project roots we've indexed */
|
|
53
|
+
private knownProjectRoots;
|
|
47
54
|
constructor(graph: KnowledgeGraph, config: MindMapConfig);
|
|
48
55
|
/** Attach a changelog engine for node-level change tracking */
|
|
49
56
|
setChangelog(changelog: ChangelogEngine): void;
|
|
@@ -51,8 +58,21 @@ export declare class Indexer {
|
|
|
51
58
|
* Re-target the indexer to a different project directory.
|
|
52
59
|
* Reloads .gitignore patterns and updates config.projectRoot.
|
|
53
60
|
* The graph is NOT cleared — nodes from multiple projects can coexist.
|
|
61
|
+
* Previous project's ignore patterns are preserved for file watcher events.
|
|
54
62
|
*/
|
|
55
63
|
setProjectRoot(newRoot: string): void;
|
|
64
|
+
/** Get the list of all indexed project roots */
|
|
65
|
+
getKnownProjectRoots(): string[];
|
|
66
|
+
/**
|
|
67
|
+
* Find the project root that a file belongs to.
|
|
68
|
+
* Matches by longest prefix (most specific root wins).
|
|
69
|
+
*/
|
|
70
|
+
private findProjectRootForFile;
|
|
71
|
+
/**
|
|
72
|
+
* Get the correct ignore instance for a file path.
|
|
73
|
+
* Falls back to the current project's ignore if no match found.
|
|
74
|
+
*/
|
|
75
|
+
private getIgnoreForFile;
|
|
56
76
|
/** Load ignore patterns from .gitignore and config */
|
|
57
77
|
private loadIgnorePatterns;
|
|
58
78
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"indexer.d.ts","sourceRoot":"","sources":["../../src/knowledge-graph/indexer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAOH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAS5C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAYtD,gDAAgD;AAChD,MAAM,MAAM,qBAAqB,GAAG,CAAC,QAAQ,EAAE,aAAa,KAAK,IAAI,CAAC;AAEtE,oCAAoC;AACpC,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,UAAU,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,UAAU,CAAC;IACnE,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,sCAAsC;AACtC,MAAM,WAAW,UAAU;IACzB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACnC;AAMD;;;GAGG;AACH,qBAAa,OAAO;IAClB,OAAO,CAAC,KAAK,CAAiB;IAC9B,OAAO,CAAC,MAAM,CAAgB;IAC9B,OAAO,CAAC,EAAE,CAA4B;IACtC,OAAO,CAAC,SAAS,CAAgC;
|
|
1
|
+
{"version":3,"file":"indexer.d.ts","sourceRoot":"","sources":["../../src/knowledge-graph/indexer.ts"],"names":[],"mappings":"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"}
|
|
@@ -31,12 +31,22 @@ export class Indexer {
|
|
|
31
31
|
config;
|
|
32
32
|
ig;
|
|
33
33
|
changelog = null;
|
|
34
|
+
/**
|
|
35
|
+
* Per-project-root ignore patterns.
|
|
36
|
+
* When multiple projects are indexed, each keeps its own .gitignore rules.
|
|
37
|
+
*/
|
|
38
|
+
projectIgnores = new Map();
|
|
39
|
+
/** Track all project roots we've indexed */
|
|
40
|
+
knownProjectRoots = [];
|
|
34
41
|
constructor(graph, config) {
|
|
35
42
|
this.graph = graph;
|
|
36
43
|
this.config = config;
|
|
37
44
|
this.ig = ignore();
|
|
38
45
|
// Load .gitignore patterns
|
|
39
46
|
this.loadIgnorePatterns();
|
|
47
|
+
// Store as the first known project
|
|
48
|
+
this.projectIgnores.set(this.config.projectRoot, this.ig);
|
|
49
|
+
this.knownProjectRoots.push(this.config.projectRoot);
|
|
40
50
|
}
|
|
41
51
|
/** Attach a changelog engine for node-level change tracking */
|
|
42
52
|
setChangelog(changelog) {
|
|
@@ -46,12 +56,50 @@ export class Indexer {
|
|
|
46
56
|
* Re-target the indexer to a different project directory.
|
|
47
57
|
* Reloads .gitignore patterns and updates config.projectRoot.
|
|
48
58
|
* The graph is NOT cleared — nodes from multiple projects can coexist.
|
|
59
|
+
* Previous project's ignore patterns are preserved for file watcher events.
|
|
49
60
|
*/
|
|
50
61
|
setProjectRoot(newRoot) {
|
|
51
62
|
this.config.projectRoot = newRoot;
|
|
52
63
|
// Reset and reload ignore patterns for the new root
|
|
53
64
|
this.ig = ignore();
|
|
54
65
|
this.loadIgnorePatterns();
|
|
66
|
+
// Store the new project's ignore patterns
|
|
67
|
+
this.projectIgnores.set(newRoot, this.ig);
|
|
68
|
+
if (!this.knownProjectRoots.includes(newRoot)) {
|
|
69
|
+
this.knownProjectRoots.push(newRoot);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
/** Get the list of all indexed project roots */
|
|
73
|
+
getKnownProjectRoots() {
|
|
74
|
+
return [...this.knownProjectRoots];
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Find the project root that a file belongs to.
|
|
78
|
+
* Matches by longest prefix (most specific root wins).
|
|
79
|
+
*/
|
|
80
|
+
findProjectRootForFile(filePath) {
|
|
81
|
+
const normalized = filePath.replace(/\\/g, '/');
|
|
82
|
+
let bestMatch = null;
|
|
83
|
+
let bestLength = 0;
|
|
84
|
+
for (const root of this.knownProjectRoots) {
|
|
85
|
+
const normalizedRoot = root.replace(/\\/g, '/');
|
|
86
|
+
if (normalized.startsWith(normalizedRoot) && normalizedRoot.length > bestLength) {
|
|
87
|
+
bestMatch = root;
|
|
88
|
+
bestLength = normalizedRoot.length;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return bestMatch;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Get the correct ignore instance for a file path.
|
|
95
|
+
* Falls back to the current project's ignore if no match found.
|
|
96
|
+
*/
|
|
97
|
+
getIgnoreForFile(filePath) {
|
|
98
|
+
const projectRoot = this.findProjectRootForFile(filePath);
|
|
99
|
+
if (projectRoot) {
|
|
100
|
+
return this.projectIgnores.get(projectRoot) ?? this.ig;
|
|
101
|
+
}
|
|
102
|
+
return this.ig;
|
|
55
103
|
}
|
|
56
104
|
/** Load ignore patterns from .gitignore and config */
|
|
57
105
|
loadIgnorePatterns() {
|
|
@@ -243,7 +291,7 @@ export class Indexer {
|
|
|
243
291
|
if (i % 100 === 0 && i > 0) {
|
|
244
292
|
const mem = process.memoryUsage();
|
|
245
293
|
if (mem.heapUsed / mem.heapTotal > MEMORY_PRESSURE_THRESHOLD) {
|
|
246
|
-
process.stderr.write(
|
|
294
|
+
process.stderr.write(`⚠ Memory pressure during fullIndex at file ${i}/${parseResults.length}. Stopping early.\n`);
|
|
247
295
|
break;
|
|
248
296
|
}
|
|
249
297
|
}
|
|
@@ -460,9 +508,12 @@ export class Indexer {
|
|
|
460
508
|
* @returns Parse result, or null if file was skipped
|
|
461
509
|
*/
|
|
462
510
|
async indexFile(filePath) {
|
|
463
|
-
//
|
|
464
|
-
const
|
|
465
|
-
|
|
511
|
+
// Determine which project this file belongs to (multi-project support)
|
|
512
|
+
const fileProjectRoot = this.findProjectRootForFile(filePath) ?? this.config.projectRoot;
|
|
513
|
+
const fileIgnore = this.getIgnoreForFile(filePath);
|
|
514
|
+
// Check if file should be ignored using the correct project's rules
|
|
515
|
+
const relPath = relative(fileProjectRoot, filePath).replace(/\\/g, '/');
|
|
516
|
+
if (fileIgnore.ignores(relPath))
|
|
466
517
|
return null;
|
|
467
518
|
if (!isSupportedFile(filePath))
|
|
468
519
|
return null;
|