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.
Files changed (76) hide show
  1. package/README.md +50 -59
  2. package/dist/cli/ai-context.js +9 -9
  3. package/dist/cli/analyze.js +139 -47
  4. package/dist/cli/augment.d.ts +13 -0
  5. package/dist/cli/augment.js +33 -0
  6. package/dist/cli/claude-hooks.d.ts +22 -0
  7. package/dist/cli/claude-hooks.js +97 -0
  8. package/dist/cli/eval-server.d.ts +30 -0
  9. package/dist/cli/eval-server.js +372 -0
  10. package/dist/cli/index.js +56 -1
  11. package/dist/cli/mcp.js +9 -0
  12. package/dist/cli/setup.js +184 -5
  13. package/dist/cli/tool.d.ts +37 -0
  14. package/dist/cli/tool.js +91 -0
  15. package/dist/cli/wiki.d.ts +13 -0
  16. package/dist/cli/wiki.js +199 -0
  17. package/dist/core/augmentation/engine.d.ts +26 -0
  18. package/dist/core/augmentation/engine.js +213 -0
  19. package/dist/core/embeddings/embedder.d.ts +2 -2
  20. package/dist/core/embeddings/embedder.js +11 -11
  21. package/dist/core/embeddings/embedding-pipeline.d.ts +2 -1
  22. package/dist/core/embeddings/embedding-pipeline.js +13 -5
  23. package/dist/core/embeddings/types.d.ts +2 -2
  24. package/dist/core/ingestion/call-processor.d.ts +7 -0
  25. package/dist/core/ingestion/call-processor.js +61 -23
  26. package/dist/core/ingestion/community-processor.js +34 -26
  27. package/dist/core/ingestion/filesystem-walker.js +15 -10
  28. package/dist/core/ingestion/heritage-processor.d.ts +6 -0
  29. package/dist/core/ingestion/heritage-processor.js +68 -5
  30. package/dist/core/ingestion/import-processor.d.ts +22 -0
  31. package/dist/core/ingestion/import-processor.js +215 -20
  32. package/dist/core/ingestion/parsing-processor.d.ts +8 -1
  33. package/dist/core/ingestion/parsing-processor.js +66 -25
  34. package/dist/core/ingestion/pipeline.js +104 -40
  35. package/dist/core/ingestion/process-processor.js +1 -1
  36. package/dist/core/ingestion/workers/parse-worker.d.ts +58 -0
  37. package/dist/core/ingestion/workers/parse-worker.js +451 -0
  38. package/dist/core/ingestion/workers/worker-pool.d.ts +22 -0
  39. package/dist/core/ingestion/workers/worker-pool.js +65 -0
  40. package/dist/core/kuzu/kuzu-adapter.d.ts +15 -1
  41. package/dist/core/kuzu/kuzu-adapter.js +177 -63
  42. package/dist/core/kuzu/schema.d.ts +1 -1
  43. package/dist/core/kuzu/schema.js +3 -0
  44. package/dist/core/search/bm25-index.js +13 -15
  45. package/dist/core/wiki/generator.d.ts +96 -0
  46. package/dist/core/wiki/generator.js +674 -0
  47. package/dist/core/wiki/graph-queries.d.ts +80 -0
  48. package/dist/core/wiki/graph-queries.js +238 -0
  49. package/dist/core/wiki/html-viewer.d.ts +10 -0
  50. package/dist/core/wiki/html-viewer.js +297 -0
  51. package/dist/core/wiki/llm-client.d.ts +36 -0
  52. package/dist/core/wiki/llm-client.js +111 -0
  53. package/dist/core/wiki/prompts.d.ts +53 -0
  54. package/dist/core/wiki/prompts.js +174 -0
  55. package/dist/mcp/core/embedder.js +4 -2
  56. package/dist/mcp/core/kuzu-adapter.d.ts +2 -1
  57. package/dist/mcp/core/kuzu-adapter.js +35 -15
  58. package/dist/mcp/local/local-backend.d.ts +54 -1
  59. package/dist/mcp/local/local-backend.js +716 -171
  60. package/dist/mcp/resources.d.ts +1 -1
  61. package/dist/mcp/resources.js +111 -73
  62. package/dist/mcp/server.d.ts +1 -1
  63. package/dist/mcp/server.js +91 -22
  64. package/dist/mcp/tools.js +80 -61
  65. package/dist/storage/git.d.ts +0 -1
  66. package/dist/storage/git.js +1 -8
  67. package/dist/storage/repo-manager.d.ts +17 -0
  68. package/dist/storage/repo-manager.js +26 -0
  69. package/hooks/claude/gitnexus-hook.cjs +135 -0
  70. package/hooks/claude/pre-tool-use.sh +78 -0
  71. package/hooks/claude/session-start.sh +42 -0
  72. package/package.json +4 -2
  73. package/skills/debugging.md +24 -22
  74. package/skills/exploring.md +26 -24
  75. package/skills/impact-analysis.md +19 -13
  76. 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
- if (csv.split('\n').length <= 1)
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
- const relLines = csvData.relCSV.split('\n').slice(1).filter(line => line.trim());
68
- for (const { table, path: filePath } of nodeFiles) {
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
- console.error(`❌ COPY failed for ${table} even with IGNORE_ERRORS: ${retryMsg}`);
91
- throw retryErr;
120
+ throw new Error(`COPY failed for ${table}: ${retryMsg.slice(0, 200)}`);
92
121
  }
93
122
  }
94
123
  }
95
- console.log('✅ All COPY commands succeeded. Starting relationship insertion...');
96
- // Build a set of valid table names for fast lookup
97
- const validTables = new Set(NODE_TABLES);
98
- const getNodeLabel = (nodeId) => {
99
- if (nodeId.startsWith('comm_'))
100
- return 'Community';
101
- if (nodeId.startsWith('proc_'))
102
- return 'Process';
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 [, fromId, toId, relType, confidenceStr, reason, stepStr] = match;
117
- const fromLabel = getNodeLabel(fromId);
118
- const toLabel = getNodeLabel(toId);
119
- // Skip relationships where either node's label doesn't have a table in KuzuDB
120
- // (e.g. Variable, Import, Type nodes that aren't in the schema)
121
- // Querying a non-existent table causes a fatal native crash
122
- if (!validTables.has(fromLabel) || !validTables.has(toLabel)) {
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
- const confidence = parseFloat(confidenceStr) || 1.0;
127
- const step = parseInt(stepStr) || 0;
128
- const insertQuery = `
129
- MATCH (a:${escapeLabel(fromLabel)} {id: '${fromId.replace(/'/g, "''")}' }),
130
- (b:${escapeLabel(toLabel)} {id: '${toId.replace(/'/g, "''")}' })
131
- CREATE (a)-[:${REL_TABLE_NAME} {type: '${relType}', confidence: ${confidence}, reason: '${reason.replace(/'/g, "''")}', step: ${step}}]->(b)
132
- `;
133
- await conn.query(insertQuery);
134
- insertedRels++;
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
- catch {
137
- skippedRels++;
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
- // ignore
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
- // Remove empty csv directory
203
+ catch { }
150
204
  try {
151
205
  await fs.rmdir(csvDir);
152
206
  }
153
- catch {
154
- // ignore if not empty or other error
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
@@ -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
- [fileResults, functionResults, classResults, methodResults, interfaceResults] = await Promise.all([
53
- queryFTSViaExecutor(executor, 'File', 'file_fts', query, limit),
54
- queryFTSViaExecutor(executor, 'Function', 'function_fts', query, limit),
55
- queryFTSViaExecutor(executor, 'Class', 'class_fts', query, limit),
56
- queryFTSViaExecutor(executor, 'Method', 'method_fts', query, limit),
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
- [fileResults, functionResults, classResults, methodResults, interfaceResults] = await Promise.all([
63
- queryFTS('File', 'file_fts', query, limit, false).catch(() => []),
64
- queryFTS('Function', 'function_fts', query, limit, false).catch(() => []),
65
- queryFTS('Class', 'class_fts', query, limit, false).catch(() => []),
66
- queryFTS('Method', 'method_fts', query, limit, false).catch(() => []),
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
+ }