gitnexus 1.4.7 → 1.4.9
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 +29 -1
- package/dist/cli/ai-context.d.ts +1 -1
- package/dist/cli/ai-context.js +1 -1
- package/dist/cli/analyze.d.ts +2 -0
- package/dist/cli/analyze.js +54 -21
- package/dist/cli/index-repo.d.ts +15 -0
- package/dist/cli/index-repo.js +115 -0
- package/dist/cli/index.js +13 -3
- package/dist/cli/setup.js +90 -10
- package/dist/cli/wiki.d.ts +4 -0
- package/dist/cli/wiki.js +174 -53
- package/dist/config/supported-languages.d.ts +33 -1
- package/dist/config/supported-languages.js +32 -0
- package/dist/core/embeddings/embedder.d.ts +6 -1
- package/dist/core/embeddings/embedder.js +65 -5
- package/dist/core/embeddings/embedding-pipeline.js +11 -9
- package/dist/core/embeddings/http-client.d.ts +31 -0
- package/dist/core/embeddings/http-client.js +179 -0
- package/dist/core/embeddings/index.d.ts +1 -0
- package/dist/core/embeddings/index.js +1 -0
- package/dist/core/embeddings/types.d.ts +1 -1
- package/dist/core/graph/graph.js +9 -1
- package/dist/core/graph/types.d.ts +11 -2
- package/dist/core/ingestion/call-processor.d.ts +66 -2
- package/dist/core/ingestion/call-processor.js +650 -30
- package/dist/core/ingestion/call-routing.d.ts +9 -18
- package/dist/core/ingestion/call-routing.js +0 -19
- package/dist/core/ingestion/cobol/cobol-copy-expander.d.ts +57 -0
- package/dist/core/ingestion/cobol/cobol-copy-expander.js +385 -0
- package/dist/core/ingestion/cobol/cobol-preprocessor.d.ts +210 -0
- package/dist/core/ingestion/cobol/cobol-preprocessor.js +1509 -0
- package/dist/core/ingestion/cobol/jcl-parser.d.ts +68 -0
- package/dist/core/ingestion/cobol/jcl-parser.js +217 -0
- package/dist/core/ingestion/cobol/jcl-processor.d.ts +33 -0
- package/dist/core/ingestion/cobol/jcl-processor.js +229 -0
- package/dist/core/ingestion/cobol-processor.d.ts +54 -0
- package/dist/core/ingestion/cobol-processor.js +1186 -0
- package/dist/core/ingestion/entry-point-scoring.d.ts +17 -0
- package/dist/core/ingestion/entry-point-scoring.js +52 -28
- package/dist/core/ingestion/export-detection.d.ts +47 -8
- package/dist/core/ingestion/export-detection.js +29 -50
- package/dist/core/ingestion/field-extractor.d.ts +29 -0
- package/dist/core/ingestion/field-extractor.js +25 -0
- package/dist/core/ingestion/field-extractors/configs/c-cpp.d.ts +3 -0
- package/dist/core/ingestion/field-extractors/configs/c-cpp.js +108 -0
- package/dist/core/ingestion/field-extractors/configs/csharp.d.ts +8 -0
- package/dist/core/ingestion/field-extractors/configs/csharp.js +73 -0
- package/dist/core/ingestion/field-extractors/configs/dart.d.ts +8 -0
- package/dist/core/ingestion/field-extractors/configs/dart.js +76 -0
- package/dist/core/ingestion/field-extractors/configs/go.d.ts +11 -0
- package/dist/core/ingestion/field-extractors/configs/go.js +64 -0
- package/dist/core/ingestion/field-extractors/configs/helpers.d.ts +44 -0
- package/dist/core/ingestion/field-extractors/configs/helpers.js +134 -0
- package/dist/core/ingestion/field-extractors/configs/jvm.d.ts +3 -0
- package/dist/core/ingestion/field-extractors/configs/jvm.js +118 -0
- package/dist/core/ingestion/field-extractors/configs/php.d.ts +8 -0
- package/dist/core/ingestion/field-extractors/configs/php.js +67 -0
- package/dist/core/ingestion/field-extractors/configs/python.d.ts +12 -0
- package/dist/core/ingestion/field-extractors/configs/python.js +91 -0
- package/dist/core/ingestion/field-extractors/configs/ruby.d.ts +16 -0
- package/dist/core/ingestion/field-extractors/configs/ruby.js +75 -0
- package/dist/core/ingestion/field-extractors/configs/rust.d.ts +9 -0
- package/dist/core/ingestion/field-extractors/configs/rust.js +55 -0
- package/dist/core/ingestion/field-extractors/configs/swift.d.ts +8 -0
- package/dist/core/ingestion/field-extractors/configs/swift.js +63 -0
- package/dist/core/ingestion/field-extractors/configs/typescript-javascript.d.ts +3 -0
- package/dist/core/ingestion/field-extractors/configs/typescript-javascript.js +60 -0
- package/dist/core/ingestion/field-extractors/generic.d.ts +46 -0
- package/dist/core/ingestion/field-extractors/generic.js +111 -0
- package/dist/core/ingestion/field-extractors/typescript.d.ts +77 -0
- package/dist/core/ingestion/field-extractors/typescript.js +291 -0
- package/dist/core/ingestion/field-types.d.ts +59 -0
- package/dist/core/ingestion/field-types.js +2 -0
- package/dist/core/ingestion/framework-detection.d.ts +97 -2
- package/dist/core/ingestion/framework-detection.js +114 -14
- package/dist/core/ingestion/heritage-processor.js +62 -66
- package/dist/core/ingestion/import-processor.d.ts +9 -10
- package/dist/core/ingestion/import-processor.js +150 -196
- package/dist/core/ingestion/{resolvers → import-resolvers}/csharp.d.ts +6 -9
- package/dist/core/ingestion/{resolvers → import-resolvers}/csharp.js +20 -2
- package/dist/core/ingestion/import-resolvers/dart.d.ts +7 -0
- package/dist/core/ingestion/import-resolvers/dart.js +44 -0
- package/dist/core/ingestion/{resolvers → import-resolvers}/go.d.ts +4 -5
- package/dist/core/ingestion/{resolvers → import-resolvers}/go.js +17 -0
- package/dist/core/ingestion/{resolvers → import-resolvers}/jvm.d.ts +10 -1
- package/dist/core/ingestion/import-resolvers/jvm.js +159 -0
- package/dist/core/ingestion/import-resolvers/php.d.ts +25 -0
- package/dist/core/ingestion/import-resolvers/php.js +80 -0
- package/dist/core/ingestion/{resolvers → import-resolvers}/python.d.ts +9 -3
- package/dist/core/ingestion/{resolvers → import-resolvers}/python.js +35 -3
- package/dist/core/ingestion/{resolvers → import-resolvers}/ruby.d.ts +5 -2
- package/dist/core/ingestion/{resolvers → import-resolvers}/ruby.js +7 -2
- package/dist/core/ingestion/{resolvers → import-resolvers}/rust.d.ts +5 -2
- package/dist/core/ingestion/{resolvers → import-resolvers}/rust.js +41 -2
- package/dist/core/ingestion/{resolvers → import-resolvers}/standard.d.ts +15 -7
- package/dist/core/ingestion/{resolvers → import-resolvers}/standard.js +22 -3
- package/dist/core/ingestion/import-resolvers/swift.d.ts +7 -0
- package/dist/core/ingestion/import-resolvers/swift.js +23 -0
- package/dist/core/ingestion/import-resolvers/types.d.ts +44 -0
- package/dist/core/ingestion/import-resolvers/types.js +6 -0
- package/dist/core/ingestion/{resolvers → import-resolvers}/utils.d.ts +2 -0
- package/dist/core/ingestion/{resolvers → import-resolvers}/utils.js +7 -0
- package/dist/core/ingestion/language-config.d.ts +6 -0
- package/dist/core/ingestion/language-config.js +13 -0
- package/dist/core/ingestion/language-provider.d.ts +121 -0
- package/dist/core/ingestion/language-provider.js +24 -0
- package/dist/core/ingestion/languages/c-cpp.d.ts +12 -0
- package/dist/core/ingestion/languages/c-cpp.js +71 -0
- package/dist/core/ingestion/languages/cobol.d.ts +1 -0
- package/dist/core/ingestion/languages/cobol.js +26 -0
- package/dist/core/ingestion/languages/csharp.d.ts +8 -0
- package/dist/core/ingestion/languages/csharp.js +49 -0
- package/dist/core/ingestion/languages/dart.d.ts +12 -0
- package/dist/core/ingestion/languages/dart.js +58 -0
- package/dist/core/ingestion/languages/go.d.ts +11 -0
- package/dist/core/ingestion/languages/go.js +28 -0
- package/dist/core/ingestion/languages/index.d.ts +38 -0
- package/dist/core/ingestion/languages/index.js +63 -0
- package/dist/core/ingestion/languages/java.d.ts +9 -0
- package/dist/core/ingestion/languages/java.js +29 -0
- package/dist/core/ingestion/languages/kotlin.d.ts +9 -0
- package/dist/core/ingestion/languages/kotlin.js +53 -0
- package/dist/core/ingestion/languages/php.d.ts +8 -0
- package/dist/core/ingestion/languages/php.js +145 -0
- package/dist/core/ingestion/languages/python.d.ts +12 -0
- package/dist/core/ingestion/languages/python.js +39 -0
- package/dist/core/ingestion/languages/ruby.d.ts +9 -0
- package/dist/core/ingestion/languages/ruby.js +44 -0
- package/dist/core/ingestion/languages/rust.d.ts +12 -0
- package/dist/core/ingestion/languages/rust.js +44 -0
- package/dist/core/ingestion/languages/swift.d.ts +12 -0
- package/dist/core/ingestion/languages/swift.js +133 -0
- package/dist/core/ingestion/languages/typescript.d.ts +10 -0
- package/dist/core/ingestion/languages/typescript.js +60 -0
- package/dist/core/ingestion/markdown-processor.d.ts +17 -0
- package/dist/core/ingestion/markdown-processor.js +124 -0
- package/dist/core/ingestion/mro-processor.js +22 -18
- package/dist/core/ingestion/named-binding-processor.d.ts +18 -0
- package/dist/core/ingestion/named-binding-processor.js +42 -0
- package/dist/core/ingestion/named-bindings/csharp.d.ts +3 -0
- package/dist/core/ingestion/named-bindings/csharp.js +37 -0
- package/dist/core/ingestion/named-bindings/java.d.ts +3 -0
- package/dist/core/ingestion/named-bindings/java.js +29 -0
- package/dist/core/ingestion/named-bindings/kotlin.d.ts +3 -0
- package/dist/core/ingestion/named-bindings/kotlin.js +36 -0
- package/dist/core/ingestion/named-bindings/php.d.ts +3 -0
- package/dist/core/ingestion/named-bindings/php.js +61 -0
- package/dist/core/ingestion/named-bindings/python.d.ts +3 -0
- package/dist/core/ingestion/named-bindings/python.js +49 -0
- package/dist/core/ingestion/named-bindings/rust.d.ts +3 -0
- package/dist/core/ingestion/named-bindings/rust.js +64 -0
- package/dist/core/ingestion/named-bindings/types.d.ts +16 -0
- package/dist/core/ingestion/named-bindings/types.js +6 -0
- package/dist/core/ingestion/named-bindings/typescript.d.ts +3 -0
- package/dist/core/ingestion/named-bindings/typescript.js +58 -0
- package/dist/core/ingestion/parsing-processor.d.ts +6 -2
- package/dist/core/ingestion/parsing-processor.js +125 -85
- package/dist/core/ingestion/pipeline.d.ts +10 -0
- package/dist/core/ingestion/pipeline.js +1235 -317
- package/dist/core/ingestion/resolution-context.d.ts +5 -0
- package/dist/core/ingestion/resolution-context.js +8 -5
- package/dist/core/ingestion/route-extractors/expo.d.ts +1 -0
- package/dist/core/ingestion/route-extractors/expo.js +36 -0
- package/dist/core/ingestion/route-extractors/middleware.d.ts +47 -0
- package/dist/core/ingestion/route-extractors/middleware.js +143 -0
- package/dist/core/ingestion/route-extractors/nextjs.d.ts +3 -0
- package/dist/core/ingestion/route-extractors/nextjs.js +76 -0
- package/dist/core/ingestion/route-extractors/php.d.ts +7 -0
- package/dist/core/ingestion/route-extractors/php.js +21 -0
- package/dist/core/ingestion/route-extractors/response-shapes.d.ts +20 -0
- package/dist/core/ingestion/route-extractors/response-shapes.js +290 -0
- package/dist/core/ingestion/symbol-table.d.ts +16 -0
- package/dist/core/ingestion/symbol-table.js +20 -6
- package/dist/core/ingestion/tree-sitter-queries.d.ts +10 -9
- package/dist/core/ingestion/tree-sitter-queries.js +274 -11
- package/dist/core/ingestion/type-env.d.ts +42 -18
- package/dist/core/ingestion/type-env.js +481 -106
- package/dist/core/ingestion/type-extractors/c-cpp.d.ts +5 -0
- package/dist/core/ingestion/type-extractors/c-cpp.js +119 -0
- package/dist/core/ingestion/type-extractors/csharp.js +149 -16
- package/dist/core/ingestion/type-extractors/dart.d.ts +15 -0
- package/dist/core/ingestion/type-extractors/dart.js +371 -0
- package/dist/core/ingestion/type-extractors/jvm.js +169 -66
- package/dist/core/ingestion/type-extractors/rust.js +35 -1
- package/dist/core/ingestion/type-extractors/shared.d.ts +1 -15
- package/dist/core/ingestion/type-extractors/shared.js +14 -112
- package/dist/core/ingestion/type-extractors/swift.js +338 -7
- package/dist/core/ingestion/type-extractors/types.d.ts +40 -8
- package/dist/core/ingestion/type-extractors/typescript.js +141 -9
- package/dist/core/ingestion/utils/ast-helpers.d.ts +83 -0
- package/dist/core/ingestion/utils/ast-helpers.js +817 -0
- package/dist/core/ingestion/utils/call-analysis.d.ts +73 -0
- package/dist/core/ingestion/utils/call-analysis.js +527 -0
- package/dist/core/ingestion/utils/event-loop.d.ts +5 -0
- package/dist/core/ingestion/utils/event-loop.js +5 -0
- package/dist/core/ingestion/utils/language-detection.d.ts +9 -0
- package/dist/core/ingestion/utils/language-detection.js +70 -0
- package/dist/core/ingestion/utils/verbose.d.ts +1 -0
- package/dist/core/ingestion/utils/verbose.js +7 -0
- package/dist/core/ingestion/workers/parse-worker.d.ts +55 -5
- package/dist/core/ingestion/workers/parse-worker.js +415 -225
- package/dist/core/lbug/csv-generator.js +51 -1
- package/dist/core/lbug/lbug-adapter.d.ts +10 -0
- package/dist/core/lbug/lbug-adapter.js +75 -4
- package/dist/core/lbug/schema.d.ts +8 -4
- package/dist/core/lbug/schema.js +65 -4
- package/dist/core/tree-sitter/parser-loader.js +7 -1
- package/dist/core/wiki/cursor-client.d.ts +31 -0
- package/dist/core/wiki/cursor-client.js +127 -0
- package/dist/core/wiki/generator.d.ts +28 -9
- package/dist/core/wiki/generator.js +115 -18
- package/dist/core/wiki/graph-queries.d.ts +4 -0
- package/dist/core/wiki/graph-queries.js +7 -1
- package/dist/core/wiki/llm-client.d.ts +2 -0
- package/dist/core/wiki/llm-client.js +8 -4
- package/dist/core/wiki/prompts.d.ts +3 -3
- package/dist/core/wiki/prompts.js +6 -0
- package/dist/mcp/core/embedder.js +11 -3
- package/dist/mcp/core/lbug-adapter.d.ts +5 -0
- package/dist/mcp/core/lbug-adapter.js +23 -2
- package/dist/mcp/local/local-backend.d.ts +38 -5
- package/dist/mcp/local/local-backend.js +804 -63
- package/dist/mcp/resources.js +2 -0
- package/dist/mcp/tools.js +73 -4
- package/dist/server/api.d.ts +19 -1
- package/dist/server/api.js +66 -6
- package/dist/storage/git.d.ts +12 -0
- package/dist/storage/git.js +21 -0
- package/dist/storage/repo-manager.d.ts +3 -0
- package/package.json +25 -16
- package/dist/core/ingestion/named-binding-extraction.d.ts +0 -61
- package/dist/core/ingestion/named-binding-extraction.js +0 -363
- package/dist/core/ingestion/resolvers/index.d.ts +0 -18
- package/dist/core/ingestion/resolvers/index.js +0 -13
- package/dist/core/ingestion/resolvers/jvm.js +0 -87
- package/dist/core/ingestion/resolvers/php.d.ts +0 -15
- package/dist/core/ingestion/resolvers/php.js +0 -35
- package/dist/core/ingestion/type-extractors/index.d.ts +0 -22
- package/dist/core/ingestion/type-extractors/index.js +0 -31
- package/dist/core/ingestion/utils.d.ts +0 -138
- package/dist/core/ingestion/utils.js +0 -1290
- package/scripts/patch-tree-sitter-swift.cjs +0 -74
|
@@ -208,6 +208,12 @@ export const streamAllCSVsToDisk = async (graph, repoPath, csvDir) => {
|
|
|
208
208
|
const codeElemWriter = new BufferedCSVWriter(path.join(csvDir, 'codeelement.csv'), codeElementHeader);
|
|
209
209
|
const communityWriter = new BufferedCSVWriter(path.join(csvDir, 'community.csv'), 'id,label,heuristicLabel,keywords,description,enrichedBy,cohesion,symbolCount');
|
|
210
210
|
const processWriter = new BufferedCSVWriter(path.join(csvDir, 'process.csv'), 'id,label,heuristicLabel,processType,stepCount,communities,entryPointId,terminalId');
|
|
211
|
+
// Section nodes have an extra 'level' column
|
|
212
|
+
const sectionWriter = new BufferedCSVWriter(path.join(csvDir, 'section.csv'), 'id,name,filePath,startLine,endLine,level,content,description');
|
|
213
|
+
// Route nodes for API endpoint mapping
|
|
214
|
+
const routeWriter = new BufferedCSVWriter(path.join(csvDir, 'route.csv'), 'id,name,filePath,responseKeys,errorKeys,middleware');
|
|
215
|
+
// Tool nodes for MCP tool definitions
|
|
216
|
+
const toolWriter = new BufferedCSVWriter(path.join(csvDir, 'tool.csv'), 'id,name,filePath,description');
|
|
211
217
|
// Multi-language node types share the same CSV shape (no isExported column)
|
|
212
218
|
const multiLangHeader = 'id,name,filePath,startLine,endLine,content,description';
|
|
213
219
|
const MULTI_LANG_TYPES = ['Struct', 'Enum', 'Macro', 'Typedef', 'Union', 'Namespace', 'Trait', 'Impl',
|
|
@@ -292,6 +298,47 @@ export const streamAllCSVsToDisk = async (graph, repoPath, csvDir) => {
|
|
|
292
298
|
].join(','));
|
|
293
299
|
break;
|
|
294
300
|
}
|
|
301
|
+
case 'Section': {
|
|
302
|
+
const content = await extractContent(node, contentCache);
|
|
303
|
+
await sectionWriter.addRow([
|
|
304
|
+
escapeCSVField(node.id),
|
|
305
|
+
escapeCSVField(node.properties.name || ''),
|
|
306
|
+
escapeCSVField(node.properties.filePath || ''),
|
|
307
|
+
escapeCSVNumber(node.properties.startLine, -1),
|
|
308
|
+
escapeCSVNumber(node.properties.endLine, -1),
|
|
309
|
+
escapeCSVNumber(node.properties.level, 1),
|
|
310
|
+
escapeCSVField(content),
|
|
311
|
+
escapeCSVField(node.properties.description || ''),
|
|
312
|
+
].join(','));
|
|
313
|
+
break;
|
|
314
|
+
}
|
|
315
|
+
case 'Route': {
|
|
316
|
+
const responseKeys = node.properties.responseKeys || [];
|
|
317
|
+
// LadybugDB array literal inside a quoted CSV field: escapeCSVField wraps in "..."
|
|
318
|
+
// and the array uses single-quoted elements
|
|
319
|
+
const keysStr = `[${responseKeys.map((k) => `'${k.replace(/'/g, "''")}'`).join(',')}]`;
|
|
320
|
+
const errorKeys = node.properties.errorKeys || [];
|
|
321
|
+
const errorKeysStr = `[${errorKeys.map((k) => `'${k.replace(/'/g, "''")}'`).join(',')}]`;
|
|
322
|
+
const middleware = node.properties.middleware || [];
|
|
323
|
+
const middlewareStr = `[${middleware.map((m) => `'${m.replace(/'/g, "''")}'`).join(',')}]`;
|
|
324
|
+
await routeWriter.addRow([
|
|
325
|
+
escapeCSVField(node.id),
|
|
326
|
+
escapeCSVField(node.properties.name || ''),
|
|
327
|
+
escapeCSVField(node.properties.filePath || ''),
|
|
328
|
+
escapeCSVField(keysStr),
|
|
329
|
+
escapeCSVField(errorKeysStr),
|
|
330
|
+
escapeCSVField(middlewareStr),
|
|
331
|
+
].join(','));
|
|
332
|
+
break;
|
|
333
|
+
}
|
|
334
|
+
case 'Tool':
|
|
335
|
+
await toolWriter.addRow([
|
|
336
|
+
escapeCSVField(node.id),
|
|
337
|
+
escapeCSVField(node.properties.name || ''),
|
|
338
|
+
escapeCSVField(node.properties.filePath || ''),
|
|
339
|
+
escapeCSVField(node.properties.description || ''),
|
|
340
|
+
].join(','));
|
|
341
|
+
break;
|
|
295
342
|
default: {
|
|
296
343
|
// Code element nodes (Function, Class, Interface, CodeElement)
|
|
297
344
|
const writer = codeWriterMap[node.label];
|
|
@@ -329,7 +376,7 @@ export const streamAllCSVsToDisk = async (graph, repoPath, csvDir) => {
|
|
|
329
376
|
}
|
|
330
377
|
}
|
|
331
378
|
// Finish all node writers
|
|
332
|
-
const allWriters = [fileWriter, folderWriter, functionWriter, classWriter, interfaceWriter, methodWriter, codeElemWriter, communityWriter, processWriter, ...multiLangWriters.values()];
|
|
379
|
+
const allWriters = [fileWriter, folderWriter, functionWriter, classWriter, interfaceWriter, methodWriter, codeElemWriter, communityWriter, processWriter, sectionWriter, routeWriter, toolWriter, ...multiLangWriters.values()];
|
|
333
380
|
await Promise.all(allWriters.map(w => w.finish()));
|
|
334
381
|
// --- Stream relationship CSV ---
|
|
335
382
|
const relCsvPath = path.join(csvDir, 'relations.csv');
|
|
@@ -353,6 +400,9 @@ export const streamAllCSVsToDisk = async (graph, repoPath, csvDir) => {
|
|
|
353
400
|
['Interface', interfaceWriter], ['Method', methodWriter],
|
|
354
401
|
['CodeElement', codeElemWriter],
|
|
355
402
|
['Community', communityWriter], ['Process', processWriter],
|
|
403
|
+
['Section', sectionWriter],
|
|
404
|
+
['Route', routeWriter],
|
|
405
|
+
['Tool', toolWriter],
|
|
356
406
|
...Array.from(multiLangWriters.entries()).map(([name, w]) => [name, w]),
|
|
357
407
|
];
|
|
358
408
|
for (const [name, writer] of tableMap) {
|
|
@@ -2,6 +2,12 @@ import lbug from '@ladybugdb/core';
|
|
|
2
2
|
import { KnowledgeGraph } from '../graph/types.js';
|
|
3
3
|
/** Expose the current Database for pool adapter reuse in tests. */
|
|
4
4
|
export declare const getDatabase: () => lbug.Database | null;
|
|
5
|
+
/**
|
|
6
|
+
* Return true when the error message indicates that another process holds
|
|
7
|
+
* an exclusive lock on the LadybugDB file (e.g. `gitnexus analyze` or
|
|
8
|
+
* `gitnexus serve` running at the same time).
|
|
9
|
+
*/
|
|
10
|
+
export declare const isDbBusyError: (err: unknown) => boolean;
|
|
5
11
|
export declare const initLbug: (dbPath: string) => Promise<{
|
|
6
12
|
db: lbug.Database;
|
|
7
13
|
conn: lbug.Connection;
|
|
@@ -9,6 +15,10 @@ export declare const initLbug: (dbPath: string) => Promise<{
|
|
|
9
15
|
/**
|
|
10
16
|
* Execute multiple queries against one repo DB atomically.
|
|
11
17
|
* While the callback runs, no other request can switch the active DB.
|
|
18
|
+
*
|
|
19
|
+
* Automatically retries up to DB_LOCK_RETRY_ATTEMPTS times when the
|
|
20
|
+
* database is busy (e.g. `gitnexus analyze` holds the write lock).
|
|
21
|
+
* Each retry waits DB_LOCK_RETRY_DELAY_MS * attempt milliseconds.
|
|
12
22
|
*/
|
|
13
23
|
export declare const withLbugDb: <T>(dbPath: string, operation: () => Promise<T>) => Promise<T>;
|
|
14
24
|
export type LbugProgressCallback = (message: string) => void;
|
|
@@ -14,6 +14,22 @@ export const getDatabase = () => db;
|
|
|
14
14
|
// Global session lock for operations that touch module-level lbug globals.
|
|
15
15
|
// This guarantees no DB switch can happen while an operation is running.
|
|
16
16
|
let sessionLock = Promise.resolve();
|
|
17
|
+
/** Number of times to retry on a BUSY / lock-held error before giving up. */
|
|
18
|
+
const DB_LOCK_RETRY_ATTEMPTS = 3;
|
|
19
|
+
/** Base back-off in ms between BUSY retries (multiplied by attempt number). */
|
|
20
|
+
const DB_LOCK_RETRY_DELAY_MS = 500;
|
|
21
|
+
/**
|
|
22
|
+
* Return true when the error message indicates that another process holds
|
|
23
|
+
* an exclusive lock on the LadybugDB file (e.g. `gitnexus analyze` or
|
|
24
|
+
* `gitnexus serve` running at the same time).
|
|
25
|
+
*/
|
|
26
|
+
export const isDbBusyError = (err) => {
|
|
27
|
+
const msg = (err instanceof Error ? err.message : String(err)).toLowerCase();
|
|
28
|
+
return (msg.includes('busy')
|
|
29
|
+
|| msg.includes('lock')
|
|
30
|
+
|| msg.includes('already in use')
|
|
31
|
+
|| msg.includes('could not set lock'));
|
|
32
|
+
};
|
|
17
33
|
const runWithSessionLock = async (operation) => {
|
|
18
34
|
const previous = sessionLock;
|
|
19
35
|
let release = null;
|
|
@@ -35,12 +51,50 @@ export const initLbug = async (dbPath) => {
|
|
|
35
51
|
/**
|
|
36
52
|
* Execute multiple queries against one repo DB atomically.
|
|
37
53
|
* While the callback runs, no other request can switch the active DB.
|
|
54
|
+
*
|
|
55
|
+
* Automatically retries up to DB_LOCK_RETRY_ATTEMPTS times when the
|
|
56
|
+
* database is busy (e.g. `gitnexus analyze` holds the write lock).
|
|
57
|
+
* Each retry waits DB_LOCK_RETRY_DELAY_MS * attempt milliseconds.
|
|
38
58
|
*/
|
|
39
59
|
export const withLbugDb = async (dbPath, operation) => {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
60
|
+
let lastError;
|
|
61
|
+
for (let attempt = 1; attempt <= DB_LOCK_RETRY_ATTEMPTS; attempt++) {
|
|
62
|
+
try {
|
|
63
|
+
return await runWithSessionLock(async () => {
|
|
64
|
+
await ensureLbugInitialized(dbPath);
|
|
65
|
+
return operation();
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
catch (err) {
|
|
69
|
+
lastError = err;
|
|
70
|
+
if (!isDbBusyError(err) || attempt === DB_LOCK_RETRY_ATTEMPTS) {
|
|
71
|
+
throw err;
|
|
72
|
+
}
|
|
73
|
+
// Close stale connection inside the session lock to prevent race conditions
|
|
74
|
+
// with concurrent operations that might acquire the lock between cleanup steps
|
|
75
|
+
await runWithSessionLock(async () => {
|
|
76
|
+
try {
|
|
77
|
+
if (conn)
|
|
78
|
+
await conn.close();
|
|
79
|
+
}
|
|
80
|
+
catch { /* best-effort */ }
|
|
81
|
+
try {
|
|
82
|
+
if (db)
|
|
83
|
+
await db.close();
|
|
84
|
+
}
|
|
85
|
+
catch { /* best-effort */ }
|
|
86
|
+
conn = null;
|
|
87
|
+
db = null;
|
|
88
|
+
currentDbPath = null;
|
|
89
|
+
ftsLoaded = false;
|
|
90
|
+
});
|
|
91
|
+
// Sleep outside the lock — no need to block others while waiting
|
|
92
|
+
await new Promise(resolve => setTimeout(resolve, DB_LOCK_RETRY_DELAY_MS * attempt));
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
// This line is unreachable — the loop either returns or throws inside,
|
|
96
|
+
// but TypeScript needs an explicit throw to satisfy the return type.
|
|
97
|
+
throw lastError;
|
|
44
98
|
};
|
|
45
99
|
const ensureLbugInitialized = async (dbPath) => {
|
|
46
100
|
if (conn && currentDbPath === dbPath) {
|
|
@@ -323,6 +377,15 @@ const getCopyQuery = (table, filePath) => {
|
|
|
323
377
|
if (table === 'Process') {
|
|
324
378
|
return `COPY ${t}(id, label, heuristicLabel, processType, stepCount, communities, entryPointId, terminalId) FROM "${filePath}" ${COPY_CSV_OPTS}`;
|
|
325
379
|
}
|
|
380
|
+
if (table === 'Section') {
|
|
381
|
+
return `COPY ${t}(id, name, filePath, startLine, endLine, level, content, description) FROM "${filePath}" ${COPY_CSV_OPTS}`;
|
|
382
|
+
}
|
|
383
|
+
if (table === 'Route') {
|
|
384
|
+
return `COPY ${t}(id, name, filePath, responseKeys, errorKeys, middleware) FROM "${filePath}" ${COPY_CSV_OPTS}`;
|
|
385
|
+
}
|
|
386
|
+
if (table === 'Tool') {
|
|
387
|
+
return `COPY ${t}(id, name, filePath, description) FROM "${filePath}" ${COPY_CSV_OPTS}`;
|
|
388
|
+
}
|
|
326
389
|
if (table === 'Method') {
|
|
327
390
|
return `COPY ${t}(id, name, filePath, startLine, endLine, isExported, content, description, parameterCount, returnType) FROM "${filePath}" ${COPY_CSV_OPTS}`;
|
|
328
391
|
}
|
|
@@ -363,6 +426,10 @@ export const insertNodeToLbug = async (label, properties, dbPath) => {
|
|
|
363
426
|
else if (label === 'Folder') {
|
|
364
427
|
query = `CREATE (n:Folder {id: ${escapeValue(properties.id)}, name: ${escapeValue(properties.name)}, filePath: ${escapeValue(properties.filePath)}})`;
|
|
365
428
|
}
|
|
429
|
+
else if (label === 'Section') {
|
|
430
|
+
const descPart = properties.description ? `, description: ${escapeValue(properties.description)}` : '';
|
|
431
|
+
query = `CREATE (n:Section {id: ${escapeValue(properties.id)}, name: ${escapeValue(properties.name)}, filePath: ${escapeValue(properties.filePath)}, startLine: ${properties.startLine || 0}, endLine: ${properties.endLine || 0}, level: ${properties.level || 1}, content: ${escapeValue(properties.content || '')}${descPart}})`;
|
|
432
|
+
}
|
|
366
433
|
else if (TABLES_WITH_EXPORTED.has(label)) {
|
|
367
434
|
const descPart = properties.description ? `, description: ${escapeValue(properties.description)}` : '';
|
|
368
435
|
query = `CREATE (n:${t} {id: ${escapeValue(properties.id)}, name: ${escapeValue(properties.name)}, filePath: ${escapeValue(properties.filePath)}, startLine: ${properties.startLine || 0}, endLine: ${properties.endLine || 0}, isExported: ${!!properties.isExported}, content: ${escapeValue(properties.content || '')}${descPart}})`;
|
|
@@ -438,6 +505,10 @@ export const batchInsertNodesToLbug = async (nodes, dbPath) => {
|
|
|
438
505
|
else if (label === 'Folder') {
|
|
439
506
|
query = `MERGE (n:Folder {id: ${escapeValue(properties.id)}}) SET n.name = ${escapeValue(properties.name)}, n.filePath = ${escapeValue(properties.filePath)}`;
|
|
440
507
|
}
|
|
508
|
+
else if (label === 'Section') {
|
|
509
|
+
const descPart = properties.description ? `, n.description = ${escapeValue(properties.description)}` : '';
|
|
510
|
+
query = `MERGE (n:Section {id: ${escapeValue(properties.id)}}) SET n.name = ${escapeValue(properties.name)}, n.filePath = ${escapeValue(properties.filePath)}, n.startLine = ${properties.startLine || 0}, n.endLine = ${properties.endLine || 0}, n.level = ${properties.level || 1}, n.content = ${escapeValue(properties.content || '')}${descPart}`;
|
|
511
|
+
}
|
|
441
512
|
else if (TABLES_WITH_EXPORTED.has(label)) {
|
|
442
513
|
const descPart = properties.description ? `, n.description = ${escapeValue(properties.description)}` : '';
|
|
443
514
|
query = `MERGE (n:${t} {id: ${escapeValue(properties.id)}}) SET n.name = ${escapeValue(properties.name)}, n.filePath = ${escapeValue(properties.filePath)}, n.startLine = ${properties.startLine || 0}, n.endLine = ${properties.endLine || 0}, n.isExported = ${!!properties.isExported}, n.content = ${escapeValue(properties.content || '')}${descPart}`;
|
|
@@ -8,10 +8,10 @@
|
|
|
8
8
|
* This allows LLMs to write natural Cypher queries like:
|
|
9
9
|
* MATCH (f:Function)-[r:CodeRelation {type: 'CALLS'}]->(g:Function) RETURN f, g
|
|
10
10
|
*/
|
|
11
|
-
export declare const NODE_TABLES: readonly ["File", "Folder", "Function", "Class", "Interface", "Method", "CodeElement", "Community", "Process", "Struct", "Enum", "Macro", "Typedef", "Union", "Namespace", "Trait", "Impl", "TypeAlias", "Const", "Static", "Property", "Record", "Delegate", "Annotation", "Constructor", "Template", "Module"];
|
|
11
|
+
export declare const NODE_TABLES: readonly ["File", "Folder", "Function", "Class", "Interface", "Method", "CodeElement", "Community", "Process", "Section", "Struct", "Enum", "Macro", "Typedef", "Union", "Namespace", "Trait", "Impl", "TypeAlias", "Const", "Static", "Property", "Record", "Delegate", "Annotation", "Constructor", "Template", "Module", "Route", "Tool"];
|
|
12
12
|
export type NodeTableName = typeof NODE_TABLES[number];
|
|
13
13
|
export declare const REL_TABLE_NAME = "CodeRelation";
|
|
14
|
-
export declare const REL_TYPES: readonly ["CONTAINS", "DEFINES", "IMPORTS", "CALLS", "EXTENDS", "IMPLEMENTS", "HAS_METHOD", "HAS_PROPERTY", "ACCESSES", "OVERRIDES", "MEMBER_OF", "STEP_IN_PROCESS"];
|
|
14
|
+
export declare const REL_TYPES: readonly ["CONTAINS", "DEFINES", "IMPORTS", "CALLS", "EXTENDS", "IMPLEMENTS", "HAS_METHOD", "HAS_PROPERTY", "ACCESSES", "OVERRIDES", "MEMBER_OF", "STEP_IN_PROCESS", "HANDLES_ROUTE", "FETCHES", "HANDLES_TOOL", "ENTRY_POINT_OF", "WRAPS", "QUERIES"];
|
|
15
15
|
export type RelType = typeof REL_TYPES[number];
|
|
16
16
|
export declare const EMBEDDING_TABLE_NAME = "CodeEmbedding";
|
|
17
17
|
export declare const FILE_SCHEMA = "\nCREATE NODE TABLE File (\n id STRING,\n name STRING,\n filePath STRING,\n content STRING,\n PRIMARY KEY (id)\n)";
|
|
@@ -41,8 +41,12 @@ 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
|
|
45
|
-
export declare const
|
|
44
|
+
export declare const ROUTE_SCHEMA = "\nCREATE NODE TABLE Route (\n id STRING,\n name STRING,\n filePath STRING,\n responseKeys STRING[],\n errorKeys STRING[],\n middleware STRING[],\n PRIMARY KEY (id)\n)";
|
|
45
|
+
export declare const TOOL_SCHEMA = "\nCREATE NODE TABLE Tool (\n id STRING,\n name STRING,\n filePath STRING,\n description STRING,\n PRIMARY KEY (id)\n)";
|
|
46
|
+
export declare const SECTION_SCHEMA = "\nCREATE NODE TABLE Section (\n id STRING,\n name STRING,\n filePath STRING,\n startLine INT64,\n endLine INT64,\n level INT64,\n content STRING,\n description STRING,\n PRIMARY KEY (id)\n)";
|
|
47
|
+
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 File TO Section,\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 Function TO `Const`,\n FROM Function TO `Typedef`,\n FROM Function TO `Union`,\n FROM Function TO `Property`,\n FROM Function TO CodeElement,\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 Class TO `Trait`,\n FROM Class TO `Macro`,\n FROM Class TO `Impl`,\n FROM Class TO `Union`,\n FROM Class TO `Namespace`,\n FROM Class TO `Typedef`,\n FROM Class TO `Property`,\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 Method TO `Property`,\n FROM Method TO CodeElement,\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 Section TO Section,\n FROM Section TO File,\n FROM File TO Route,\n FROM Function TO Route,\n FROM Method TO Route,\n FROM File TO Tool,\n FROM Function TO Tool,\n FROM Method TO Tool,\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 Interface TO `Property`,\n FROM `Struct` TO Community,\n FROM `Struct` TO `Trait`,\n FROM `Struct` TO `Struct`,\n FROM `Struct` TO Class,\n FROM `Struct` TO `Enum`,\n FROM `Struct` TO Function,\n FROM `Struct` TO Method,\n FROM `Struct` TO Interface,\n FROM `Struct` TO `Constructor`,\n FROM `Struct` TO `Property`,\n FROM `Enum` TO `Enum`,\n FROM `Enum` TO Community,\n FROM `Enum` TO Class,\n FROM `Enum` TO Interface,\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 `Namespace` TO `Struct`,\n FROM `Trait` TO Method,\n FROM `Trait` TO `Constructor`,\n FROM `Trait` TO `Property`,\n FROM `Trait` TO Community,\n FROM `Impl` TO Method,\n FROM `Impl` TO `Constructor`,\n FROM `Impl` TO `Property`,\n FROM `Impl` TO Community,\n FROM `Impl` TO `Trait`,\n FROM `Impl` TO `Struct`,\n FROM `Impl` TO `Impl`,\n FROM `TypeAlias` TO Community,\n FROM `TypeAlias` TO `Trait`,\n FROM `TypeAlias` TO Class,\n FROM `Const` TO Community,\n FROM `Static` TO Community,\n FROM `Property` TO Community,\n FROM `Record` TO Method,\n FROM `Record` TO `Constructor`,\n FROM `Record` TO `Property`,\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 `Constructor` TO `Property`,\n FROM `Constructor` TO `Typedef`,\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 FROM Route TO Process,\n FROM Tool TO Process,\n type STRING,\n confidence DOUBLE,\n reason STRING,\n step INT32\n)";
|
|
48
|
+
export declare const EMBEDDING_DIMS: number;
|
|
49
|
+
export declare const EMBEDDING_SCHEMA: string;
|
|
46
50
|
/**
|
|
47
51
|
* Create vector index for semantic search
|
|
48
52
|
* Uses HNSW (Hierarchical Navigable Small World) algorithm with cosine similarity
|
package/dist/core/lbug/schema.js
CHANGED
|
@@ -12,17 +12,20 @@
|
|
|
12
12
|
// NODE TABLE NAMES
|
|
13
13
|
// ============================================================================
|
|
14
14
|
export const NODE_TABLES = [
|
|
15
|
-
'File', 'Folder', 'Function', 'Class', 'Interface', 'Method', 'CodeElement', 'Community', 'Process',
|
|
15
|
+
'File', 'Folder', 'Function', 'Class', 'Interface', 'Method', 'CodeElement', 'Community', 'Process', 'Section',
|
|
16
16
|
// Multi-language support
|
|
17
17
|
'Struct', 'Enum', 'Macro', 'Typedef', 'Union', 'Namespace', 'Trait', 'Impl',
|
|
18
|
-
'TypeAlias', 'Const', 'Static', 'Property', 'Record', 'Delegate', 'Annotation', 'Constructor', 'Template', 'Module'
|
|
18
|
+
'TypeAlias', 'Const', 'Static', 'Property', 'Record', 'Delegate', 'Annotation', 'Constructor', 'Template', 'Module',
|
|
19
|
+
'Route',
|
|
20
|
+
'Tool'
|
|
19
21
|
];
|
|
20
22
|
// ============================================================================
|
|
21
23
|
// RELATION TABLE
|
|
22
24
|
// ============================================================================
|
|
23
25
|
export const REL_TABLE_NAME = 'CodeRelation';
|
|
24
26
|
// Valid relation types
|
|
25
|
-
|
|
27
|
+
// Note: WRAPS is reserved for future middleware graph traversal (not yet emitted)
|
|
28
|
+
export const REL_TYPES = ['CONTAINS', 'DEFINES', 'IMPORTS', 'CALLS', 'EXTENDS', 'IMPLEMENTS', 'HAS_METHOD', 'HAS_PROPERTY', 'ACCESSES', 'OVERRIDES', 'MEMBER_OF', 'STEP_IN_PROCESS', 'HANDLES_ROUTE', 'FETCHES', 'HANDLES_TOOL', 'ENTRY_POINT_OF', 'WRAPS', 'QUERIES'];
|
|
26
29
|
// ============================================================================
|
|
27
30
|
// EMBEDDING TABLE
|
|
28
31
|
// ============================================================================
|
|
@@ -171,6 +174,39 @@ export const ANNOTATION_SCHEMA = CODE_ELEMENT_BASE('Annotation');
|
|
|
171
174
|
export const CONSTRUCTOR_SCHEMA = CODE_ELEMENT_BASE('Constructor');
|
|
172
175
|
export const TEMPLATE_SCHEMA = CODE_ELEMENT_BASE('Template');
|
|
173
176
|
export const MODULE_SCHEMA = CODE_ELEMENT_BASE('Module');
|
|
177
|
+
// API route endpoints (Next.js, Express, etc.)
|
|
178
|
+
export const ROUTE_SCHEMA = `
|
|
179
|
+
CREATE NODE TABLE Route (
|
|
180
|
+
id STRING,
|
|
181
|
+
name STRING,
|
|
182
|
+
filePath STRING,
|
|
183
|
+
responseKeys STRING[],
|
|
184
|
+
errorKeys STRING[],
|
|
185
|
+
middleware STRING[],
|
|
186
|
+
PRIMARY KEY (id)
|
|
187
|
+
)`;
|
|
188
|
+
// MCP tool definitions
|
|
189
|
+
export const TOOL_SCHEMA = `
|
|
190
|
+
CREATE NODE TABLE Tool (
|
|
191
|
+
id STRING,
|
|
192
|
+
name STRING,
|
|
193
|
+
filePath STRING,
|
|
194
|
+
description STRING,
|
|
195
|
+
PRIMARY KEY (id)
|
|
196
|
+
)`;
|
|
197
|
+
// Markdown heading sections
|
|
198
|
+
export const SECTION_SCHEMA = `
|
|
199
|
+
CREATE NODE TABLE Section (
|
|
200
|
+
id STRING,
|
|
201
|
+
name STRING,
|
|
202
|
+
filePath STRING,
|
|
203
|
+
startLine INT64,
|
|
204
|
+
endLine INT64,
|
|
205
|
+
level INT64,
|
|
206
|
+
content STRING,
|
|
207
|
+
description STRING,
|
|
208
|
+
PRIMARY KEY (id)
|
|
209
|
+
)`;
|
|
174
210
|
// ============================================================================
|
|
175
211
|
// RELATION TABLE SCHEMA
|
|
176
212
|
// Single table with 'type' property - connects all node tables
|
|
@@ -202,6 +238,7 @@ CREATE REL TABLE ${REL_TABLE_NAME} (
|
|
|
202
238
|
FROM File TO \`Constructor\`,
|
|
203
239
|
FROM File TO \`Template\`,
|
|
204
240
|
FROM File TO \`Module\`,
|
|
241
|
+
FROM File TO Section,
|
|
205
242
|
FROM Folder TO Folder,
|
|
206
243
|
FROM Folder TO File,
|
|
207
244
|
FROM Function TO Function,
|
|
@@ -222,6 +259,7 @@ CREATE REL TABLE ${REL_TABLE_NAME} (
|
|
|
222
259
|
FROM Function TO \`Typedef\`,
|
|
223
260
|
FROM Function TO \`Union\`,
|
|
224
261
|
FROM Function TO \`Property\`,
|
|
262
|
+
FROM Function TO CodeElement,
|
|
225
263
|
FROM Class TO Method,
|
|
226
264
|
FROM Class TO Function,
|
|
227
265
|
FROM Class TO Class,
|
|
@@ -255,6 +293,7 @@ CREATE REL TABLE ${REL_TABLE_NAME} (
|
|
|
255
293
|
FROM Method TO Interface,
|
|
256
294
|
FROM Method TO \`Constructor\`,
|
|
257
295
|
FROM Method TO \`Property\`,
|
|
296
|
+
FROM Method TO CodeElement,
|
|
258
297
|
FROM \`Template\` TO \`Template\`,
|
|
259
298
|
FROM \`Template\` TO Function,
|
|
260
299
|
FROM \`Template\` TO Method,
|
|
@@ -266,6 +305,14 @@ CREATE REL TABLE ${REL_TABLE_NAME} (
|
|
|
266
305
|
FROM \`Template\` TO Interface,
|
|
267
306
|
FROM \`Template\` TO \`Constructor\`,
|
|
268
307
|
FROM \`Module\` TO \`Module\`,
|
|
308
|
+
FROM Section TO Section,
|
|
309
|
+
FROM Section TO File,
|
|
310
|
+
FROM File TO Route,
|
|
311
|
+
FROM Function TO Route,
|
|
312
|
+
FROM Method TO Route,
|
|
313
|
+
FROM File TO Tool,
|
|
314
|
+
FROM Function TO Tool,
|
|
315
|
+
FROM Method TO Tool,
|
|
269
316
|
FROM CodeElement TO Community,
|
|
270
317
|
FROM Interface TO Community,
|
|
271
318
|
FROM Interface TO Function,
|
|
@@ -364,6 +411,8 @@ CREATE REL TABLE ${REL_TABLE_NAME} (
|
|
|
364
411
|
FROM \`Annotation\` TO Process,
|
|
365
412
|
FROM \`Template\` TO Process,
|
|
366
413
|
FROM CodeElement TO Process,
|
|
414
|
+
FROM Route TO Process,
|
|
415
|
+
FROM Tool TO Process,
|
|
367
416
|
type STRING,
|
|
368
417
|
confidence DOUBLE,
|
|
369
418
|
reason STRING,
|
|
@@ -373,10 +422,16 @@ CREATE REL TABLE ${REL_TABLE_NAME} (
|
|
|
373
422
|
// EMBEDDING TABLE SCHEMA
|
|
374
423
|
// Separate table for vector storage to avoid copy-on-write overhead
|
|
375
424
|
// ============================================================================
|
|
425
|
+
/** Embedding vector dimensions. Default 384 (snowflake-arctic-embed-xs). */
|
|
426
|
+
const _rawDims = parseInt(process.env.GITNEXUS_EMBEDDING_DIMS ?? '384', 10);
|
|
427
|
+
if (Number.isNaN(_rawDims) || _rawDims <= 0) {
|
|
428
|
+
throw new Error(`GITNEXUS_EMBEDDING_DIMS must be a positive integer, got "${process.env.GITNEXUS_EMBEDDING_DIMS}"`);
|
|
429
|
+
}
|
|
430
|
+
export const EMBEDDING_DIMS = _rawDims;
|
|
376
431
|
export const EMBEDDING_SCHEMA = `
|
|
377
432
|
CREATE NODE TABLE ${EMBEDDING_TABLE_NAME} (
|
|
378
433
|
nodeId STRING,
|
|
379
|
-
embedding FLOAT[
|
|
434
|
+
embedding FLOAT[${EMBEDDING_DIMS}],
|
|
380
435
|
PRIMARY KEY (nodeId)
|
|
381
436
|
)`;
|
|
382
437
|
/**
|
|
@@ -419,6 +474,12 @@ export const NODE_SCHEMA_QUERIES = [
|
|
|
419
474
|
CONSTRUCTOR_SCHEMA,
|
|
420
475
|
TEMPLATE_SCHEMA,
|
|
421
476
|
MODULE_SCHEMA,
|
|
477
|
+
// Markdown support
|
|
478
|
+
SECTION_SCHEMA,
|
|
479
|
+
// API routes
|
|
480
|
+
ROUTE_SCHEMA,
|
|
481
|
+
// MCP tools
|
|
482
|
+
TOOL_SCHEMA,
|
|
422
483
|
];
|
|
423
484
|
export const REL_SCHEMA_QUERIES = [
|
|
424
485
|
RELATION_SCHEMA,
|
|
@@ -12,13 +12,18 @@ import PHP from 'tree-sitter-php';
|
|
|
12
12
|
import Ruby from 'tree-sitter-ruby';
|
|
13
13
|
import { createRequire } from 'node:module';
|
|
14
14
|
import { SupportedLanguages } from '../../config/supported-languages.js';
|
|
15
|
-
// tree-sitter-swift
|
|
15
|
+
// tree-sitter-swift and tree-sitter-dart are optionalDependencies — may not be installed
|
|
16
16
|
const _require = createRequire(import.meta.url);
|
|
17
17
|
let Swift = null;
|
|
18
18
|
try {
|
|
19
19
|
Swift = _require('tree-sitter-swift');
|
|
20
20
|
}
|
|
21
21
|
catch { }
|
|
22
|
+
let Dart = null;
|
|
23
|
+
try {
|
|
24
|
+
Dart = _require('tree-sitter-dart');
|
|
25
|
+
}
|
|
26
|
+
catch { }
|
|
22
27
|
// tree-sitter-kotlin is an optionalDependency — may not be installed
|
|
23
28
|
let Kotlin = null;
|
|
24
29
|
try {
|
|
@@ -40,6 +45,7 @@ const languageMap = {
|
|
|
40
45
|
...(Kotlin ? { [SupportedLanguages.Kotlin]: Kotlin } : {}),
|
|
41
46
|
[SupportedLanguages.PHP]: PHP.php_only,
|
|
42
47
|
[SupportedLanguages.Ruby]: Ruby,
|
|
48
|
+
...(Dart ? { [SupportedLanguages.Dart]: Dart } : {}),
|
|
43
49
|
...(Swift ? { [SupportedLanguages.Swift]: Swift } : {}),
|
|
44
50
|
};
|
|
45
51
|
export const isLanguageAvailable = (language) => language in languageMap;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cursor CLI Client for Wiki Generation
|
|
3
|
+
*
|
|
4
|
+
* Wrapper for the Cursor headless CLI (`agent` command).
|
|
5
|
+
* Uses print mode for non-interactive LLM calls.
|
|
6
|
+
*
|
|
7
|
+
* Docs: https://cursor.com/docs/cli/headless
|
|
8
|
+
*/
|
|
9
|
+
import type { LLMResponse, CallLLMOptions } from './llm-client.js';
|
|
10
|
+
export interface CursorConfig {
|
|
11
|
+
model?: string;
|
|
12
|
+
workingDirectory?: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Detect if Cursor CLI is available in PATH.
|
|
16
|
+
* Returns the binary name if found ('agent'), null otherwise.
|
|
17
|
+
* Result is cached after the first call.
|
|
18
|
+
*/
|
|
19
|
+
export declare function detectCursorCLI(): string | null;
|
|
20
|
+
/**
|
|
21
|
+
* Resolve Cursor CLI configuration.
|
|
22
|
+
* Model is optional - if not provided, Cursor CLI uses its default (auto).
|
|
23
|
+
*/
|
|
24
|
+
export declare function resolveCursorConfig(overrides?: Partial<CursorConfig>): CursorConfig;
|
|
25
|
+
/**
|
|
26
|
+
* Call the Cursor CLI in print mode.
|
|
27
|
+
*
|
|
28
|
+
* Uses `agent -p --output-format text` for clean non-streaming output.
|
|
29
|
+
* The prompt is passed as the final CLI argument.
|
|
30
|
+
*/
|
|
31
|
+
export declare function callCursorLLM(prompt: string, config: CursorConfig, systemPrompt?: string, options?: CallLLMOptions): Promise<LLMResponse>;
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cursor CLI Client for Wiki Generation
|
|
3
|
+
*
|
|
4
|
+
* Wrapper for the Cursor headless CLI (`agent` command).
|
|
5
|
+
* Uses print mode for non-interactive LLM calls.
|
|
6
|
+
*
|
|
7
|
+
* Docs: https://cursor.com/docs/cli/headless
|
|
8
|
+
*/
|
|
9
|
+
import { spawn, execSync } from 'child_process';
|
|
10
|
+
function isVerbose() {
|
|
11
|
+
return process.env.GITNEXUS_VERBOSE === '1';
|
|
12
|
+
}
|
|
13
|
+
function verboseLog(...args) {
|
|
14
|
+
if (isVerbose()) {
|
|
15
|
+
console.log('[cursor-cli]', ...args);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
let cachedCursorBin;
|
|
19
|
+
/**
|
|
20
|
+
* Detect if Cursor CLI is available in PATH.
|
|
21
|
+
* Returns the binary name if found ('agent'), null otherwise.
|
|
22
|
+
* Result is cached after the first call.
|
|
23
|
+
*/
|
|
24
|
+
export function detectCursorCLI() {
|
|
25
|
+
if (cachedCursorBin !== undefined)
|
|
26
|
+
return cachedCursorBin;
|
|
27
|
+
try {
|
|
28
|
+
execSync('agent --version', { stdio: 'ignore' });
|
|
29
|
+
cachedCursorBin = 'agent';
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
cachedCursorBin = null;
|
|
33
|
+
}
|
|
34
|
+
return cachedCursorBin;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Resolve Cursor CLI configuration.
|
|
38
|
+
* Model is optional - if not provided, Cursor CLI uses its default (auto).
|
|
39
|
+
*/
|
|
40
|
+
export function resolveCursorConfig(overrides) {
|
|
41
|
+
return {
|
|
42
|
+
model: overrides?.model,
|
|
43
|
+
workingDirectory: overrides?.workingDirectory,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Call the Cursor CLI in print mode.
|
|
48
|
+
*
|
|
49
|
+
* Uses `agent -p --output-format text` for clean non-streaming output.
|
|
50
|
+
* The prompt is passed as the final CLI argument.
|
|
51
|
+
*/
|
|
52
|
+
export async function callCursorLLM(prompt, config, systemPrompt, options) {
|
|
53
|
+
const cursorBin = detectCursorCLI();
|
|
54
|
+
if (!cursorBin) {
|
|
55
|
+
throw new Error('Cursor CLI not found. Install it from https://cursor.com/docs/cli/installation');
|
|
56
|
+
}
|
|
57
|
+
// Always use text format to get clean output without agent narration/thinking.
|
|
58
|
+
// stream-json captures assistant messages which include "Let me explore..." narration
|
|
59
|
+
// that pollutes the actual content when using thinking models.
|
|
60
|
+
const fullPrompt = systemPrompt
|
|
61
|
+
? `${systemPrompt}\n\n---\n\n${prompt}`
|
|
62
|
+
: prompt;
|
|
63
|
+
const args = [
|
|
64
|
+
'-p',
|
|
65
|
+
'--output-format', 'text',
|
|
66
|
+
];
|
|
67
|
+
if (config.model) {
|
|
68
|
+
args.push('--model', config.model);
|
|
69
|
+
}
|
|
70
|
+
// Add the prompt as the final argument
|
|
71
|
+
args.push(fullPrompt);
|
|
72
|
+
verboseLog('Spawning:', cursorBin, args.slice(0, -1).join(' '), '[prompt length:', fullPrompt.length, 'chars]');
|
|
73
|
+
verboseLog('Working directory:', config.workingDirectory || process.cwd());
|
|
74
|
+
if (config.model) {
|
|
75
|
+
verboseLog('Model:', config.model);
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
verboseLog('Model: auto (default)');
|
|
79
|
+
}
|
|
80
|
+
const startTime = Date.now();
|
|
81
|
+
return new Promise((resolve, reject) => {
|
|
82
|
+
const child = spawn(cursorBin, args, {
|
|
83
|
+
cwd: config.workingDirectory || process.cwd(),
|
|
84
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
85
|
+
env: {
|
|
86
|
+
...process.env,
|
|
87
|
+
// Ensure non-interactive mode
|
|
88
|
+
CI: '1',
|
|
89
|
+
},
|
|
90
|
+
});
|
|
91
|
+
verboseLog('Process spawned with PID:', child.pid);
|
|
92
|
+
let stdout = '';
|
|
93
|
+
let stderr = '';
|
|
94
|
+
// Text mode - collect all output, report progress based on output size
|
|
95
|
+
child.stdout.on('data', (chunk) => {
|
|
96
|
+
const chunkStr = chunk.toString();
|
|
97
|
+
stdout += chunkStr;
|
|
98
|
+
verboseLog(`[stdout] received ${chunkStr.length} chars, total: ${stdout.length}`);
|
|
99
|
+
// Report progress if callback provided
|
|
100
|
+
if (options?.onChunk) {
|
|
101
|
+
options.onChunk(stdout.length);
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
child.on('close', (code) => {
|
|
105
|
+
const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
|
|
106
|
+
verboseLog(`Process exited with code ${code} after ${elapsed}s`);
|
|
107
|
+
verboseLog(`stdout length: ${stdout.length} chars`);
|
|
108
|
+
if (code !== 0) {
|
|
109
|
+
verboseLog('stderr:', stderr);
|
|
110
|
+
reject(new Error(`Cursor CLI exited with code ${code}: ${stderr}`));
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
resolve({ content: stdout.trim() });
|
|
114
|
+
});
|
|
115
|
+
child.stderr.on('data', (chunk) => {
|
|
116
|
+
const chunkStr = chunk.toString();
|
|
117
|
+
stderr += chunkStr;
|
|
118
|
+
verboseLog('[stderr]', chunkStr.trim());
|
|
119
|
+
});
|
|
120
|
+
child.on('error', (err) => {
|
|
121
|
+
verboseLog('Spawn error:', err.message);
|
|
122
|
+
reject(new Error(`Failed to spawn Cursor CLI: ${err.message}`));
|
|
123
|
+
});
|
|
124
|
+
// Close stdin immediately since we pass prompt as argument
|
|
125
|
+
child.stdin.end();
|
|
126
|
+
});
|
|
127
|
+
}
|