gitnexus 1.1.8 → 1.2.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 +50 -59
- package/dist/cli/ai-context.js +9 -9
- package/dist/cli/analyze.js +139 -47
- package/dist/cli/augment.d.ts +13 -0
- package/dist/cli/augment.js +33 -0
- package/dist/cli/claude-hooks.d.ts +22 -0
- package/dist/cli/claude-hooks.js +97 -0
- package/dist/cli/eval-server.d.ts +30 -0
- package/dist/cli/eval-server.js +372 -0
- package/dist/cli/index.js +56 -1
- package/dist/cli/mcp.js +9 -0
- package/dist/cli/setup.js +184 -5
- package/dist/cli/tool.d.ts +37 -0
- package/dist/cli/tool.js +91 -0
- package/dist/cli/wiki.d.ts +13 -0
- package/dist/cli/wiki.js +199 -0
- package/dist/core/augmentation/engine.d.ts +26 -0
- package/dist/core/augmentation/engine.js +213 -0
- package/dist/core/embeddings/embedder.d.ts +2 -2
- package/dist/core/embeddings/embedder.js +11 -11
- package/dist/core/embeddings/embedding-pipeline.d.ts +2 -1
- package/dist/core/embeddings/embedding-pipeline.js +13 -5
- package/dist/core/embeddings/types.d.ts +2 -2
- package/dist/core/ingestion/call-processor.d.ts +7 -0
- package/dist/core/ingestion/call-processor.js +61 -23
- package/dist/core/ingestion/community-processor.js +34 -26
- package/dist/core/ingestion/filesystem-walker.js +15 -10
- package/dist/core/ingestion/heritage-processor.d.ts +6 -0
- package/dist/core/ingestion/heritage-processor.js +68 -5
- package/dist/core/ingestion/import-processor.d.ts +22 -0
- package/dist/core/ingestion/import-processor.js +215 -20
- package/dist/core/ingestion/parsing-processor.d.ts +8 -1
- package/dist/core/ingestion/parsing-processor.js +66 -25
- package/dist/core/ingestion/pipeline.js +104 -40
- package/dist/core/ingestion/process-processor.js +1 -1
- package/dist/core/ingestion/workers/parse-worker.d.ts +58 -0
- package/dist/core/ingestion/workers/parse-worker.js +451 -0
- package/dist/core/ingestion/workers/worker-pool.d.ts +22 -0
- package/dist/core/ingestion/workers/worker-pool.js +65 -0
- package/dist/core/kuzu/kuzu-adapter.d.ts +15 -1
- package/dist/core/kuzu/kuzu-adapter.js +177 -63
- package/dist/core/kuzu/schema.d.ts +1 -1
- package/dist/core/kuzu/schema.js +3 -0
- package/dist/core/search/bm25-index.js +13 -15
- package/dist/core/wiki/generator.d.ts +96 -0
- package/dist/core/wiki/generator.js +674 -0
- package/dist/core/wiki/graph-queries.d.ts +80 -0
- package/dist/core/wiki/graph-queries.js +238 -0
- package/dist/core/wiki/html-viewer.d.ts +10 -0
- package/dist/core/wiki/html-viewer.js +297 -0
- package/dist/core/wiki/llm-client.d.ts +36 -0
- package/dist/core/wiki/llm-client.js +111 -0
- package/dist/core/wiki/prompts.d.ts +53 -0
- package/dist/core/wiki/prompts.js +174 -0
- package/dist/mcp/core/embedder.js +4 -2
- package/dist/mcp/core/kuzu-adapter.d.ts +2 -1
- package/dist/mcp/core/kuzu-adapter.js +35 -15
- package/dist/mcp/local/local-backend.d.ts +54 -1
- package/dist/mcp/local/local-backend.js +716 -171
- package/dist/mcp/resources.d.ts +1 -1
- package/dist/mcp/resources.js +111 -73
- package/dist/mcp/server.d.ts +1 -1
- package/dist/mcp/server.js +91 -22
- package/dist/mcp/tools.js +80 -61
- package/dist/storage/git.d.ts +0 -1
- package/dist/storage/git.js +1 -8
- package/dist/storage/repo-manager.d.ts +17 -0
- package/dist/storage/repo-manager.js +26 -0
- package/hooks/claude/gitnexus-hook.cjs +135 -0
- package/hooks/claude/pre-tool-use.sh +78 -0
- package/hooks/claude/session-start.sh +42 -0
- package/package.json +4 -2
- package/skills/debugging.md +24 -22
- package/skills/exploring.md +26 -24
- package/skills/impact-analysis.md +19 -13
- package/skills/refactoring.md +37 -26
|
@@ -49,111 +49,163 @@ export const initKuzu = async (dbPath) => {
|
|
|
49
49
|
}
|
|
50
50
|
return { db, conn };
|
|
51
51
|
};
|
|
52
|
-
export const loadGraphToKuzu = async (graph, fileContents, storagePath) => {
|
|
52
|
+
export const loadGraphToKuzu = async (graph, fileContents, storagePath, onProgress) => {
|
|
53
53
|
if (!conn) {
|
|
54
54
|
throw new Error('KuzuDB not initialized. Call initKuzu first.');
|
|
55
55
|
}
|
|
56
|
+
const log = onProgress || (() => { });
|
|
56
57
|
const csvData = generateAllCSVs(graph, fileContents);
|
|
57
58
|
const csvDir = path.join(storagePath, 'csv');
|
|
58
59
|
await fs.mkdir(csvDir, { recursive: true });
|
|
60
|
+
log('Generating CSVs...');
|
|
59
61
|
const nodeFiles = [];
|
|
60
62
|
for (const [tableName, csv] of csvData.nodes.entries()) {
|
|
61
|
-
|
|
63
|
+
const rowCount = csv.split('\n').length - 1;
|
|
64
|
+
if (rowCount <= 0)
|
|
62
65
|
continue;
|
|
63
66
|
const filePath = path.join(csvDir, `${tableName.toLowerCase()}.csv`);
|
|
64
67
|
await fs.writeFile(filePath, csv, 'utf-8');
|
|
65
|
-
nodeFiles.push({ table: tableName, path: filePath });
|
|
68
|
+
nodeFiles.push({ table: tableName, path: filePath, rows: rowCount });
|
|
66
69
|
}
|
|
67
|
-
|
|
68
|
-
|
|
70
|
+
// Write relationship CSV to disk for bulk COPY
|
|
71
|
+
const relCsvPath = path.join(csvDir, 'relations.csv');
|
|
72
|
+
const validTables = new Set(NODE_TABLES);
|
|
73
|
+
const getNodeLabel = (nodeId) => {
|
|
74
|
+
if (nodeId.startsWith('comm_'))
|
|
75
|
+
return 'Community';
|
|
76
|
+
if (nodeId.startsWith('proc_'))
|
|
77
|
+
return 'Process';
|
|
78
|
+
return nodeId.split(':')[0];
|
|
79
|
+
};
|
|
80
|
+
const relLines = csvData.relCSV.split('\n');
|
|
81
|
+
const relHeader = relLines[0];
|
|
82
|
+
const validRelLines = [relHeader];
|
|
83
|
+
let skippedRels = 0;
|
|
84
|
+
for (let i = 1; i < relLines.length; i++) {
|
|
85
|
+
const line = relLines[i];
|
|
86
|
+
if (!line.trim())
|
|
87
|
+
continue;
|
|
88
|
+
const match = line.match(/"([^"]*)","([^"]*)"/);
|
|
89
|
+
if (!match) {
|
|
90
|
+
skippedRels++;
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
const fromLabel = getNodeLabel(match[1]);
|
|
94
|
+
const toLabel = getNodeLabel(match[2]);
|
|
95
|
+
if (!validTables.has(fromLabel) || !validTables.has(toLabel)) {
|
|
96
|
+
skippedRels++;
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
validRelLines.push(line);
|
|
100
|
+
}
|
|
101
|
+
await fs.writeFile(relCsvPath, validRelLines.join('\n'), 'utf-8');
|
|
102
|
+
// Bulk COPY all node CSVs
|
|
103
|
+
const totalSteps = nodeFiles.length + 1; // +1 for relationships
|
|
104
|
+
let stepsDone = 0;
|
|
105
|
+
for (const { table, path: filePath, rows } of nodeFiles) {
|
|
106
|
+
stepsDone++;
|
|
107
|
+
log(`Loading nodes ${stepsDone}/${totalSteps}: ${table} (${rows.toLocaleString()} rows)`);
|
|
69
108
|
const normalizedPath = normalizeCopyPath(filePath);
|
|
70
109
|
const copyQuery = getCopyQuery(table, normalizedPath);
|
|
71
|
-
// Log CSV stats for diagnostics
|
|
72
|
-
const csvContent = await fs.readFile(filePath, 'utf-8');
|
|
73
|
-
const csvLines = csvContent.split('\n').length;
|
|
74
|
-
console.log(` COPY ${table}: ${csvLines - 1} rows`);
|
|
75
110
|
try {
|
|
76
111
|
await conn.query(copyQuery);
|
|
77
112
|
}
|
|
78
113
|
catch (err) {
|
|
79
|
-
const errMsg = err instanceof Error ? err.message : String(err);
|
|
80
|
-
console.warn(`⚠️ COPY failed for ${table} (${csvLines - 1} rows): ${errMsg}`);
|
|
81
|
-
// Retry with IGNORE_ERRORS=true to skip malformed rows
|
|
82
|
-
console.log(` Retrying ${table} with IGNORE_ERRORS=true...`);
|
|
83
114
|
try {
|
|
84
115
|
const retryQuery = copyQuery.replace('auto_detect=false)', 'auto_detect=false, IGNORE_ERRORS=true)');
|
|
85
116
|
await conn.query(retryQuery);
|
|
86
|
-
console.log(` ✅ ${table} loaded with IGNORE_ERRORS (some rows may have been skipped)`);
|
|
87
117
|
}
|
|
88
118
|
catch (retryErr) {
|
|
89
119
|
const retryMsg = retryErr instanceof Error ? retryErr.message : String(retryErr);
|
|
90
|
-
|
|
91
|
-
throw retryErr;
|
|
120
|
+
throw new Error(`COPY failed for ${table}: ${retryMsg.slice(0, 200)}`);
|
|
92
121
|
}
|
|
93
122
|
}
|
|
94
123
|
}
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
const
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
return nodeId.split(':')[0];
|
|
104
|
-
};
|
|
105
|
-
// All multi-language tables are created with backticks - must always reference them with backticks
|
|
106
|
-
const escapeLabel = (label) => {
|
|
107
|
-
return BACKTICK_TABLES.has(label) ? `\`${label}\`` : label;
|
|
108
|
-
};
|
|
109
|
-
let insertedRels = 0;
|
|
110
|
-
let skippedRels = 0;
|
|
111
|
-
for (const line of relLines) {
|
|
112
|
-
try {
|
|
113
|
-
const match = line.match(/"([^"]*)","([^"]*)","([^"]*)",([0-9.]+),"([^"]*)",([0-9-]+)/);
|
|
124
|
+
// Bulk COPY relationships — split by FROM→TO label pair (KuzuDB requires it)
|
|
125
|
+
const insertedRels = validRelLines.length - 1;
|
|
126
|
+
const warnings = [];
|
|
127
|
+
if (insertedRels > 0) {
|
|
128
|
+
const relsByPair = new Map();
|
|
129
|
+
for (let i = 1; i < validRelLines.length; i++) {
|
|
130
|
+
const line = validRelLines[i];
|
|
131
|
+
const match = line.match(/"([^"]*)","([^"]*)"/);
|
|
114
132
|
if (!match)
|
|
115
133
|
continue;
|
|
116
|
-
const
|
|
117
|
-
const
|
|
118
|
-
const
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
skippedRels++;
|
|
124
|
-
continue;
|
|
134
|
+
const fromLabel = getNodeLabel(match[1]);
|
|
135
|
+
const toLabel = getNodeLabel(match[2]);
|
|
136
|
+
const pairKey = `${fromLabel}|${toLabel}`;
|
|
137
|
+
let list = relsByPair.get(pairKey);
|
|
138
|
+
if (!list) {
|
|
139
|
+
list = [];
|
|
140
|
+
relsByPair.set(pairKey, list);
|
|
125
141
|
}
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
142
|
+
list.push(line);
|
|
143
|
+
}
|
|
144
|
+
log(`Loading edges: ${insertedRels.toLocaleString()} across ${relsByPair.size} types`);
|
|
145
|
+
let pairIdx = 0;
|
|
146
|
+
let failedPairEdges = 0;
|
|
147
|
+
const failedPairLines = [];
|
|
148
|
+
for (const [pairKey, lines] of relsByPair) {
|
|
149
|
+
pairIdx++;
|
|
150
|
+
const [fromLabel, toLabel] = pairKey.split('|');
|
|
151
|
+
const pairCsvPath = path.join(csvDir, `rel_${fromLabel}_${toLabel}.csv`);
|
|
152
|
+
await fs.writeFile(pairCsvPath, relHeader + '\n' + lines.join('\n'), 'utf-8');
|
|
153
|
+
const normalizedPath = normalizeCopyPath(pairCsvPath);
|
|
154
|
+
const copyQuery = `COPY ${REL_TABLE_NAME} FROM "${normalizedPath}" (from="${fromLabel}", to="${toLabel}", HEADER=true, ESCAPE='"', DELIM=',', QUOTE='"', PARALLEL=false, auto_detect=false)`;
|
|
155
|
+
if (pairIdx % 5 === 0 || lines.length > 1000) {
|
|
156
|
+
log(`Loading edges: ${pairIdx}/${relsByPair.size} types (${fromLabel} -> ${toLabel})`);
|
|
157
|
+
}
|
|
158
|
+
try {
|
|
159
|
+
await conn.query(copyQuery);
|
|
160
|
+
}
|
|
161
|
+
catch (err) {
|
|
162
|
+
try {
|
|
163
|
+
const retryQuery = copyQuery.replace('auto_detect=false)', 'auto_detect=false, IGNORE_ERRORS=true)');
|
|
164
|
+
await conn.query(retryQuery);
|
|
165
|
+
}
|
|
166
|
+
catch (retryErr) {
|
|
167
|
+
const retryMsg = retryErr instanceof Error ? retryErr.message : String(retryErr);
|
|
168
|
+
warnings.push(`${fromLabel}->${toLabel} (${lines.length} edges): ${retryMsg.slice(0, 80)}`);
|
|
169
|
+
failedPairEdges += lines.length;
|
|
170
|
+
failedPairLines.push(...lines);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
try {
|
|
174
|
+
await fs.unlink(pairCsvPath);
|
|
175
|
+
}
|
|
176
|
+
catch { }
|
|
135
177
|
}
|
|
136
|
-
|
|
137
|
-
|
|
178
|
+
if (failedPairLines.length > 0) {
|
|
179
|
+
log(`Inserting ${failedPairEdges} edges individually (missing schema pairs)`);
|
|
180
|
+
await fallbackRelationshipInserts([relHeader, ...failedPairLines], validTables, getNodeLabel);
|
|
138
181
|
}
|
|
139
182
|
}
|
|
140
|
-
// Cleanup CSVs
|
|
183
|
+
// Cleanup all CSVs
|
|
184
|
+
try {
|
|
185
|
+
await fs.unlink(relCsvPath);
|
|
186
|
+
}
|
|
187
|
+
catch { }
|
|
141
188
|
for (const { path: filePath } of nodeFiles) {
|
|
142
189
|
try {
|
|
143
190
|
await fs.unlink(filePath);
|
|
144
191
|
}
|
|
145
|
-
catch {
|
|
146
|
-
|
|
192
|
+
catch { }
|
|
193
|
+
}
|
|
194
|
+
try {
|
|
195
|
+
const remaining = await fs.readdir(csvDir);
|
|
196
|
+
for (const f of remaining) {
|
|
197
|
+
try {
|
|
198
|
+
await fs.unlink(path.join(csvDir, f));
|
|
199
|
+
}
|
|
200
|
+
catch { }
|
|
147
201
|
}
|
|
148
202
|
}
|
|
149
|
-
|
|
203
|
+
catch { }
|
|
150
204
|
try {
|
|
151
205
|
await fs.rmdir(csvDir);
|
|
152
206
|
}
|
|
153
|
-
catch {
|
|
154
|
-
|
|
155
|
-
}
|
|
156
|
-
return { success: true, insertedRels, skippedRels };
|
|
207
|
+
catch { }
|
|
208
|
+
return { success: true, insertedRels, skippedRels, warnings };
|
|
157
209
|
};
|
|
158
210
|
// KuzuDB default ESCAPE is '\' (backslash), but our CSV uses RFC 4180 escaping ("" for literal quotes).
|
|
159
211
|
// Source code content is full of backslashes which confuse the auto-detection.
|
|
@@ -170,6 +222,37 @@ const BACKTICK_TABLES = new Set([
|
|
|
170
222
|
const escapeTableName = (table) => {
|
|
171
223
|
return BACKTICK_TABLES.has(table) ? `\`${table}\`` : table;
|
|
172
224
|
};
|
|
225
|
+
/** Fallback: insert relationships one-by-one if COPY fails */
|
|
226
|
+
const fallbackRelationshipInserts = async (validRelLines, validTables, getNodeLabel) => {
|
|
227
|
+
if (!conn)
|
|
228
|
+
return;
|
|
229
|
+
const escapeLabel = (label) => {
|
|
230
|
+
return BACKTICK_TABLES.has(label) ? `\`${label}\`` : label;
|
|
231
|
+
};
|
|
232
|
+
for (let i = 1; i < validRelLines.length; i++) {
|
|
233
|
+
const line = validRelLines[i];
|
|
234
|
+
try {
|
|
235
|
+
const match = line.match(/"([^"]*)","([^"]*)","([^"]*)",([0-9.]+),"([^"]*)",([0-9-]+)/);
|
|
236
|
+
if (!match)
|
|
237
|
+
continue;
|
|
238
|
+
const [, fromId, toId, relType, confidenceStr, reason, stepStr] = match;
|
|
239
|
+
const fromLabel = getNodeLabel(fromId);
|
|
240
|
+
const toLabel = getNodeLabel(toId);
|
|
241
|
+
if (!validTables.has(fromLabel) || !validTables.has(toLabel))
|
|
242
|
+
continue;
|
|
243
|
+
const confidence = parseFloat(confidenceStr) || 1.0;
|
|
244
|
+
const step = parseInt(stepStr) || 0;
|
|
245
|
+
await conn.query(`
|
|
246
|
+
MATCH (a:${escapeLabel(fromLabel)} {id: '${fromId.replace(/'/g, "''")}' }),
|
|
247
|
+
(b:${escapeLabel(toLabel)} {id: '${toId.replace(/'/g, "''")}' })
|
|
248
|
+
CREATE (a)-[:${REL_TABLE_NAME} {type: '${relType}', confidence: ${confidence}, reason: '${reason.replace(/'/g, "''")}', step: ${step}}]->(b)
|
|
249
|
+
`);
|
|
250
|
+
}
|
|
251
|
+
catch {
|
|
252
|
+
// skip
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
};
|
|
173
256
|
const getCopyQuery = (table, filePath) => {
|
|
174
257
|
const t = escapeTableName(table);
|
|
175
258
|
if (table === 'File') {
|
|
@@ -377,6 +460,37 @@ export const getKuzuStats = async () => {
|
|
|
377
460
|
}
|
|
378
461
|
return { nodes: totalNodes, edges: totalEdges };
|
|
379
462
|
};
|
|
463
|
+
/**
|
|
464
|
+
* Load cached embeddings from KuzuDB before a rebuild.
|
|
465
|
+
* Returns all embedding vectors so they can be re-inserted after the graph is reloaded,
|
|
466
|
+
* avoiding expensive re-embedding of unchanged nodes.
|
|
467
|
+
*/
|
|
468
|
+
export const loadCachedEmbeddings = async () => {
|
|
469
|
+
if (!conn) {
|
|
470
|
+
return { embeddingNodeIds: new Set(), embeddings: [] };
|
|
471
|
+
}
|
|
472
|
+
const embeddingNodeIds = new Set();
|
|
473
|
+
const embeddings = [];
|
|
474
|
+
try {
|
|
475
|
+
const rows = await conn.query(`MATCH (e:${EMBEDDING_TABLE_NAME}) RETURN e.nodeId AS nodeId, e.embedding AS embedding`);
|
|
476
|
+
const result = Array.isArray(rows) ? rows[0] : rows;
|
|
477
|
+
for (const row of await result.getAll()) {
|
|
478
|
+
const nodeId = String(row.nodeId ?? row[0] ?? '');
|
|
479
|
+
if (!nodeId)
|
|
480
|
+
continue;
|
|
481
|
+
embeddingNodeIds.add(nodeId);
|
|
482
|
+
const embedding = row.embedding ?? row[1];
|
|
483
|
+
if (embedding) {
|
|
484
|
+
embeddings.push({
|
|
485
|
+
nodeId,
|
|
486
|
+
embedding: Array.isArray(embedding) ? embedding.map(Number) : Array.from(embedding).map(Number),
|
|
487
|
+
});
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
catch { /* embedding table may not exist */ }
|
|
492
|
+
return { embeddingNodeIds, embeddings };
|
|
493
|
+
};
|
|
380
494
|
export const closeKuzu = async () => {
|
|
381
495
|
if (conn) {
|
|
382
496
|
try {
|
|
@@ -41,7 +41,7 @@ export declare const ANNOTATION_SCHEMA: string;
|
|
|
41
41
|
export declare const CONSTRUCTOR_SCHEMA: string;
|
|
42
42
|
export declare const TEMPLATE_SCHEMA: string;
|
|
43
43
|
export declare const MODULE_SCHEMA: string;
|
|
44
|
-
export declare const RELATION_SCHEMA = "\nCREATE REL TABLE CodeRelation (\n FROM File TO File,\n FROM File TO Folder,\n FROM File TO Function,\n FROM File TO Class,\n FROM File TO Interface,\n FROM File TO Method,\n FROM File TO CodeElement,\n FROM File TO `Struct`,\n FROM File TO `Enum`,\n FROM File TO `Macro`,\n FROM File TO `Typedef`,\n FROM File TO `Union`,\n FROM File TO `Namespace`,\n FROM File TO `Trait`,\n FROM File TO `Impl`,\n FROM File TO `TypeAlias`,\n FROM File TO `Const`,\n FROM File TO `Static`,\n FROM File TO `Property`,\n FROM File TO `Record`,\n FROM File TO `Delegate`,\n FROM File TO `Annotation`,\n FROM File TO `Constructor`,\n FROM File TO `Template`,\n FROM File TO `Module`,\n FROM Folder TO Folder,\n FROM Folder TO File,\n FROM Function TO Function,\n FROM Function TO Method,\n FROM Function TO Class,\n FROM Function TO Community,\n FROM Function TO `Macro`,\n FROM Function TO `Struct`,\n FROM Function TO `Template`,\n FROM Function TO `Enum`,\n FROM Function TO `Namespace`,\n FROM Function TO `TypeAlias`,\n FROM Function TO `Module`,\n FROM Function TO `Impl`,\n FROM Function TO Interface,\n FROM Function TO `Constructor`,\n FROM Class TO Method,\n FROM Class TO Function,\n FROM Class TO Class,\n FROM Class TO Interface,\n FROM Class TO Community,\n FROM Class TO `Template`,\n FROM Class TO `TypeAlias`,\n FROM Class TO `Struct`,\n FROM Class TO `Enum`,\n FROM Class TO `Constructor`,\n FROM Method TO Function,\n FROM Method TO Method,\n FROM Method TO Class,\n FROM Method TO Community,\n FROM Method TO `Template`,\n FROM Method TO `Struct`,\n FROM Method TO `TypeAlias`,\n FROM Method TO `Enum`,\n FROM Method TO `Macro`,\n FROM Method TO `Namespace`,\n FROM Method TO `Module`,\n FROM Method TO `Impl`,\n FROM Method TO Interface,\n FROM Method TO `Constructor`,\n FROM `Template` TO `Template`,\n FROM `Template` TO Function,\n FROM `Template` TO Method,\n FROM `Template` TO Class,\n FROM `Template` TO `Struct`,\n FROM `Template` TO `TypeAlias`,\n FROM `Template` TO `Enum`,\n FROM `Template` TO `Macro`,\n FROM `Template` TO Interface,\n FROM `Template` TO `Constructor`,\n FROM `Module` TO `Module`,\n FROM CodeElement TO Community,\n FROM Interface TO Community,\n FROM Interface TO Function,\n FROM Interface TO Method,\n FROM Interface TO Class,\n FROM Interface TO Interface,\n FROM Interface TO `TypeAlias`,\n FROM Interface TO `Struct`,\n FROM Interface TO `Constructor`,\n FROM `Struct` TO Community,\n FROM `Struct` TO `Trait`,\n FROM `Struct` TO Function,\n FROM `Struct` TO Method,\n FROM `Enum` TO Community,\n FROM `Macro` TO Community,\n FROM `Macro` TO Function,\n FROM `Macro` TO Method,\n FROM `Module` TO Function,\n FROM `Module` TO Method,\n FROM `Typedef` TO Community,\n FROM `Union` TO Community,\n FROM `Namespace` TO Community,\n FROM `Trait` TO Community,\n FROM `Impl` TO Community,\n FROM `Impl` TO `Trait`,\n FROM `TypeAlias` TO Community,\n FROM `Const` TO Community,\n FROM `Static` TO Community,\n FROM `Property` TO Community,\n FROM `Record` TO Community,\n FROM `Delegate` TO Community,\n FROM `Annotation` TO Community,\n FROM `Constructor` TO Community,\n FROM `Constructor` TO Interface,\n FROM `Constructor` TO Class,\n FROM `Constructor` TO Method,\n FROM `Constructor` TO Function,\n FROM `Constructor` TO `Constructor`,\n FROM `Constructor` TO `Struct`,\n FROM `Constructor` TO `Macro`,\n FROM `Constructor` TO `Template`,\n FROM `Constructor` TO `TypeAlias`,\n FROM `Constructor` TO `Enum`,\n FROM `Constructor` TO `Impl`,\n FROM `Constructor` TO `Namespace`,\n FROM `Template` TO Community,\n FROM `Module` TO Community,\n FROM Function TO Process,\n FROM Method TO Process,\n FROM Class TO Process,\n FROM Interface TO Process,\n FROM `Struct` TO Process,\n FROM `Constructor` TO Process,\n FROM `Module` TO Process,\n FROM `Macro` TO Process,\n FROM `Impl` TO Process,\n FROM `Typedef` TO Process,\n FROM `TypeAlias` TO Process,\n FROM `Enum` TO Process,\n FROM `Union` TO Process,\n FROM `Namespace` TO Process,\n FROM `Trait` TO Process,\n FROM `Const` TO Process,\n FROM `Static` TO Process,\n FROM `Property` TO Process,\n FROM `Record` TO Process,\n FROM `Delegate` TO Process,\n FROM `Annotation` TO Process,\n FROM `Template` TO Process,\n FROM CodeElement TO Process,\n type STRING,\n confidence DOUBLE,\n reason STRING,\n step INT32\n)";
|
|
44
|
+
export declare const RELATION_SCHEMA = "\nCREATE REL TABLE CodeRelation (\n FROM File TO File,\n FROM File TO Folder,\n FROM File TO Function,\n FROM File TO Class,\n FROM File TO Interface,\n FROM File TO Method,\n FROM File TO CodeElement,\n FROM File TO `Struct`,\n FROM File TO `Enum`,\n FROM File TO `Macro`,\n FROM File TO `Typedef`,\n FROM File TO `Union`,\n FROM File TO `Namespace`,\n FROM File TO `Trait`,\n FROM File TO `Impl`,\n FROM File TO `TypeAlias`,\n FROM File TO `Const`,\n FROM File TO `Static`,\n FROM File TO `Property`,\n FROM File TO `Record`,\n FROM File TO `Delegate`,\n FROM File TO `Annotation`,\n FROM File TO `Constructor`,\n FROM File TO `Template`,\n FROM File TO `Module`,\n FROM Folder TO Folder,\n FROM Folder TO File,\n FROM Function TO Function,\n FROM Function TO Method,\n FROM Function TO Class,\n FROM Function TO Community,\n FROM Function TO `Macro`,\n FROM Function TO `Struct`,\n FROM Function TO `Template`,\n FROM Function TO `Enum`,\n FROM Function TO `Namespace`,\n FROM Function TO `TypeAlias`,\n FROM Function TO `Module`,\n FROM Function TO `Impl`,\n FROM Function TO Interface,\n FROM Function TO `Constructor`,\n FROM Class TO Method,\n FROM Class TO Function,\n FROM Class TO Class,\n FROM Class TO Interface,\n FROM Class TO Community,\n FROM Class TO `Template`,\n FROM Class TO `TypeAlias`,\n FROM Class TO `Struct`,\n FROM Class TO `Enum`,\n FROM Class TO `Annotation`,\n FROM Class TO `Constructor`,\n FROM Method TO Function,\n FROM Method TO Method,\n FROM Method TO Class,\n FROM Method TO Community,\n FROM Method TO `Template`,\n FROM Method TO `Struct`,\n FROM Method TO `TypeAlias`,\n FROM Method TO `Enum`,\n FROM Method TO `Macro`,\n FROM Method TO `Namespace`,\n FROM Method TO `Module`,\n FROM Method TO `Impl`,\n FROM Method TO Interface,\n FROM Method TO `Constructor`,\n FROM `Template` TO `Template`,\n FROM `Template` TO Function,\n FROM `Template` TO Method,\n FROM `Template` TO Class,\n FROM `Template` TO `Struct`,\n FROM `Template` TO `TypeAlias`,\n FROM `Template` TO `Enum`,\n FROM `Template` TO `Macro`,\n FROM `Template` TO Interface,\n FROM `Template` TO `Constructor`,\n FROM `Module` TO `Module`,\n FROM CodeElement TO Community,\n FROM Interface TO Community,\n FROM Interface TO Function,\n FROM Interface TO Method,\n FROM Interface TO Class,\n FROM Interface TO Interface,\n FROM Interface TO `TypeAlias`,\n FROM Interface TO `Struct`,\n FROM Interface TO `Constructor`,\n FROM `Struct` TO Community,\n FROM `Struct` TO `Trait`,\n FROM `Struct` TO Function,\n FROM `Struct` TO Method,\n FROM `Enum` TO Community,\n FROM `Macro` TO Community,\n FROM `Macro` TO Function,\n FROM `Macro` TO Method,\n FROM `Module` TO Function,\n FROM `Module` TO Method,\n FROM `Typedef` TO Community,\n FROM `Union` TO Community,\n FROM `Namespace` TO Community,\n FROM `Trait` TO Community,\n FROM `Impl` TO Community,\n FROM `Impl` TO `Trait`,\n FROM `TypeAlias` TO Community,\n FROM `Const` TO Community,\n FROM `Static` TO Community,\n FROM `Property` TO Community,\n FROM `Record` TO Community,\n FROM `Delegate` TO Community,\n FROM `Annotation` TO Community,\n FROM `Constructor` TO Community,\n FROM `Constructor` TO Interface,\n FROM `Constructor` TO Class,\n FROM `Constructor` TO Method,\n FROM `Constructor` TO Function,\n FROM `Constructor` TO `Constructor`,\n FROM `Constructor` TO `Struct`,\n FROM `Constructor` TO `Macro`,\n FROM `Constructor` TO `Template`,\n FROM `Constructor` TO `TypeAlias`,\n FROM `Constructor` TO `Enum`,\n FROM `Constructor` TO `Annotation`,\n FROM `Constructor` TO `Impl`,\n FROM `Constructor` TO `Namespace`,\n FROM `Constructor` TO `Module`,\n FROM `Template` TO Community,\n FROM `Module` TO Community,\n FROM Function TO Process,\n FROM Method TO Process,\n FROM Class TO Process,\n FROM Interface TO Process,\n FROM `Struct` TO Process,\n FROM `Constructor` TO Process,\n FROM `Module` TO Process,\n FROM `Macro` TO Process,\n FROM `Impl` TO Process,\n FROM `Typedef` TO Process,\n FROM `TypeAlias` TO Process,\n FROM `Enum` TO Process,\n FROM `Union` TO Process,\n FROM `Namespace` TO Process,\n FROM `Trait` TO Process,\n FROM `Const` TO Process,\n FROM `Static` TO Process,\n FROM `Property` TO Process,\n FROM `Record` TO Process,\n FROM `Delegate` TO Process,\n FROM `Annotation` TO Process,\n FROM `Template` TO Process,\n FROM CodeElement TO Process,\n type STRING,\n confidence DOUBLE,\n reason STRING,\n step INT32\n)";
|
|
45
45
|
export declare const EMBEDDING_SCHEMA = "\nCREATE NODE TABLE CodeEmbedding (\n nodeId STRING,\n embedding FLOAT[384],\n PRIMARY KEY (nodeId)\n)";
|
|
46
46
|
/**
|
|
47
47
|
* Create vector index for semantic search
|
package/dist/core/kuzu/schema.js
CHANGED
|
@@ -218,6 +218,7 @@ CREATE REL TABLE ${REL_TABLE_NAME} (
|
|
|
218
218
|
FROM Class TO \`TypeAlias\`,
|
|
219
219
|
FROM Class TO \`Struct\`,
|
|
220
220
|
FROM Class TO \`Enum\`,
|
|
221
|
+
FROM Class TO \`Annotation\`,
|
|
221
222
|
FROM Class TO \`Constructor\`,
|
|
222
223
|
FROM Method TO Function,
|
|
223
224
|
FROM Method TO Method,
|
|
@@ -287,8 +288,10 @@ CREATE REL TABLE ${REL_TABLE_NAME} (
|
|
|
287
288
|
FROM \`Constructor\` TO \`Template\`,
|
|
288
289
|
FROM \`Constructor\` TO \`TypeAlias\`,
|
|
289
290
|
FROM \`Constructor\` TO \`Enum\`,
|
|
291
|
+
FROM \`Constructor\` TO \`Annotation\`,
|
|
290
292
|
FROM \`Constructor\` TO \`Impl\`,
|
|
291
293
|
FROM \`Constructor\` TO \`Namespace\`,
|
|
294
|
+
FROM \`Constructor\` TO \`Module\`,
|
|
292
295
|
FROM \`Template\` TO Community,
|
|
293
296
|
FROM \`Module\` TO Community,
|
|
294
297
|
FROM Function TO Process,
|
|
@@ -47,25 +47,23 @@ export const searchFTSFromKuzu = async (query, limit = 20, repoId) => {
|
|
|
47
47
|
let fileResults, functionResults, classResults, methodResults, interfaceResults;
|
|
48
48
|
if (repoId) {
|
|
49
49
|
// Use MCP connection pool via dynamic import
|
|
50
|
+
// IMPORTANT: KuzuDB uses a single connection per repo — queries must be sequential
|
|
51
|
+
// to avoid deadlocking. Do NOT use Promise.all here.
|
|
50
52
|
const { executeQuery } = await import('../../mcp/core/kuzu-adapter.js');
|
|
51
53
|
const executor = (cypher) => executeQuery(repoId, cypher);
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
queryFTSViaExecutor(executor, 'Interface', 'interface_fts', query, limit),
|
|
58
|
-
]);
|
|
54
|
+
fileResults = await queryFTSViaExecutor(executor, 'File', 'file_fts', query, limit);
|
|
55
|
+
functionResults = await queryFTSViaExecutor(executor, 'Function', 'function_fts', query, limit);
|
|
56
|
+
classResults = await queryFTSViaExecutor(executor, 'Class', 'class_fts', query, limit);
|
|
57
|
+
methodResults = await queryFTSViaExecutor(executor, 'Method', 'method_fts', query, limit);
|
|
58
|
+
interfaceResults = await queryFTSViaExecutor(executor, 'Interface', 'interface_fts', query, limit);
|
|
59
59
|
}
|
|
60
60
|
else {
|
|
61
|
-
// Use core kuzu adapter (CLI / pipeline context)
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
queryFTS('Interface', 'interface_fts', query, limit, false).catch(() => []),
|
|
68
|
-
]);
|
|
61
|
+
// Use core kuzu adapter (CLI / pipeline context) — also sequential for safety
|
|
62
|
+
fileResults = await queryFTS('File', 'file_fts', query, limit, false).catch(() => []);
|
|
63
|
+
functionResults = await queryFTS('Function', 'function_fts', query, limit, false).catch(() => []);
|
|
64
|
+
classResults = await queryFTS('Class', 'class_fts', query, limit, false).catch(() => []);
|
|
65
|
+
methodResults = await queryFTS('Method', 'method_fts', query, limit, false).catch(() => []);
|
|
66
|
+
interfaceResults = await queryFTS('Interface', 'interface_fts', query, limit, false).catch(() => []);
|
|
69
67
|
}
|
|
70
68
|
// Merge results by filePath, summing scores for same file
|
|
71
69
|
const merged = new Map();
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wiki Generator
|
|
3
|
+
*
|
|
4
|
+
* Orchestrates the full wiki generation pipeline:
|
|
5
|
+
* Phase 0: Validate prerequisites + gather graph structure
|
|
6
|
+
* Phase 1: Build module tree (one LLM call)
|
|
7
|
+
* Phase 2: Generate module pages (one LLM call per module, bottom-up)
|
|
8
|
+
* Phase 3: Generate overview page
|
|
9
|
+
*
|
|
10
|
+
* Supports incremental updates via git diff + module-file mapping.
|
|
11
|
+
*/
|
|
12
|
+
import { type LLMConfig } from './llm-client.js';
|
|
13
|
+
export interface WikiOptions {
|
|
14
|
+
force?: boolean;
|
|
15
|
+
model?: string;
|
|
16
|
+
baseUrl?: string;
|
|
17
|
+
apiKey?: string;
|
|
18
|
+
maxTokensPerModule?: number;
|
|
19
|
+
}
|
|
20
|
+
export interface WikiMeta {
|
|
21
|
+
fromCommit: string;
|
|
22
|
+
generatedAt: string;
|
|
23
|
+
model: string;
|
|
24
|
+
moduleFiles: Record<string, string[]>;
|
|
25
|
+
moduleTree: ModuleTreeNode[];
|
|
26
|
+
}
|
|
27
|
+
export interface ModuleTreeNode {
|
|
28
|
+
name: string;
|
|
29
|
+
slug: string;
|
|
30
|
+
files: string[];
|
|
31
|
+
children?: ModuleTreeNode[];
|
|
32
|
+
}
|
|
33
|
+
export type ProgressCallback = (phase: string, percent: number, detail?: string) => void;
|
|
34
|
+
export declare class WikiGenerator {
|
|
35
|
+
private repoPath;
|
|
36
|
+
private storagePath;
|
|
37
|
+
private wikiDir;
|
|
38
|
+
private kuzuPath;
|
|
39
|
+
private llmConfig;
|
|
40
|
+
private maxTokensPerModule;
|
|
41
|
+
private options;
|
|
42
|
+
private onProgress;
|
|
43
|
+
private failedModules;
|
|
44
|
+
constructor(repoPath: string, storagePath: string, kuzuPath: string, llmConfig: LLMConfig, options?: WikiOptions, onProgress?: ProgressCallback);
|
|
45
|
+
/**
|
|
46
|
+
* Main entry point. Runs the full pipeline or incremental update.
|
|
47
|
+
*/
|
|
48
|
+
run(): Promise<{
|
|
49
|
+
pagesGenerated: number;
|
|
50
|
+
mode: 'full' | 'incremental' | 'up-to-date';
|
|
51
|
+
failedModules: string[];
|
|
52
|
+
}>;
|
|
53
|
+
private fullGeneration;
|
|
54
|
+
private buildModuleTree;
|
|
55
|
+
/**
|
|
56
|
+
* Parse LLM grouping response. Validates all files are assigned.
|
|
57
|
+
*/
|
|
58
|
+
private parseGroupingResponse;
|
|
59
|
+
/**
|
|
60
|
+
* Fallback grouping by top-level directory when LLM parsing fails.
|
|
61
|
+
*/
|
|
62
|
+
private fallbackGrouping;
|
|
63
|
+
/**
|
|
64
|
+
* Split a large module into sub-modules by subdirectory.
|
|
65
|
+
*/
|
|
66
|
+
private splitBySubdirectory;
|
|
67
|
+
/**
|
|
68
|
+
* Recursively generate pages for a module tree node.
|
|
69
|
+
* Returns count of pages generated.
|
|
70
|
+
*/
|
|
71
|
+
private generateModulePage;
|
|
72
|
+
/**
|
|
73
|
+
* Generate a leaf module page from source code + graph data.
|
|
74
|
+
*/
|
|
75
|
+
private generateLeafPage;
|
|
76
|
+
/**
|
|
77
|
+
* Generate a parent module page from children's documentation.
|
|
78
|
+
*/
|
|
79
|
+
private generateParentPage;
|
|
80
|
+
private generateOverview;
|
|
81
|
+
private incrementalUpdate;
|
|
82
|
+
private getCurrentCommit;
|
|
83
|
+
private getChangedFiles;
|
|
84
|
+
private readSourceFiles;
|
|
85
|
+
private truncateSource;
|
|
86
|
+
private estimateModuleTokens;
|
|
87
|
+
private readProjectInfo;
|
|
88
|
+
private extractModuleFiles;
|
|
89
|
+
private countModules;
|
|
90
|
+
private findNodeBySlug;
|
|
91
|
+
private slugify;
|
|
92
|
+
private fileExists;
|
|
93
|
+
private loadWikiMeta;
|
|
94
|
+
private saveWikiMeta;
|
|
95
|
+
private saveModuleTree;
|
|
96
|
+
}
|