@veewo/gitnexus 1.5.2 → 1.5.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/clean.d.ts +3 -2
- package/dist/cli/clean.js +33 -12
- package/dist/config/supported-languages.d.ts +2 -1
- package/dist/config/supported-languages.js +1 -0
- package/dist/core/ingestion/call-routing.js +1 -0
- package/dist/core/ingestion/export-detection.js +1 -0
- package/dist/core/ingestion/parsing-processor.js +4 -2
- package/dist/core/ingestion/tree-sitter-queries.d.ts +1 -0
- package/dist/core/ingestion/tree-sitter-queries.js +62 -0
- package/dist/core/ingestion/type-extractors/index.d.ts +1 -0
- package/dist/core/ingestion/type-extractors/index.js +8 -0
- package/dist/core/ingestion/utils.js +3 -0
- package/dist/core/ingestion/workers/parse-worker.js +66 -35
- package/dist/core/tree-sitter/parser-loader.d.ts +7 -3
- package/dist/core/tree-sitter/parser-loader.js +65 -26
- package/package.json +3 -2
- package/skills/gitnexus-cli.md +71 -22
package/dist/cli/clean.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Clean Command
|
|
3
3
|
*
|
|
4
|
-
* Removes the
|
|
5
|
-
* Also unregisters
|
|
4
|
+
* Removes the GitNexus index from the current repository while preserving
|
|
5
|
+
* configuration files (e.g. sync-manifest.txt). Also unregisters the repo
|
|
6
|
+
* from the global registry.
|
|
6
7
|
*/
|
|
7
8
|
export declare const cleanCommand: (options?: {
|
|
8
9
|
force?: boolean;
|
package/dist/cli/clean.js
CHANGED
|
@@ -1,11 +1,32 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Clean Command
|
|
3
3
|
*
|
|
4
|
-
* Removes the
|
|
5
|
-
* Also unregisters
|
|
4
|
+
* Removes the GitNexus index from the current repository while preserving
|
|
5
|
+
* configuration files (e.g. sync-manifest.txt). Also unregisters the repo
|
|
6
|
+
* from the global registry.
|
|
6
7
|
*/
|
|
7
8
|
import fs from 'fs/promises';
|
|
9
|
+
import path from 'path';
|
|
8
10
|
import { findRepo, unregisterRepo, listRegisteredRepos } from '../storage/repo-manager.js';
|
|
11
|
+
/** Files under .gitnexus/ that are configuration, not index data. */
|
|
12
|
+
const PRESERVE_FILES = new Set(['sync-manifest.txt']);
|
|
13
|
+
async function cleanStoragePath(storagePath) {
|
|
14
|
+
let entries;
|
|
15
|
+
try {
|
|
16
|
+
entries = await fs.readdir(storagePath);
|
|
17
|
+
}
|
|
18
|
+
catch (err) {
|
|
19
|
+
if (err.code === 'ENOENT')
|
|
20
|
+
return;
|
|
21
|
+
throw err;
|
|
22
|
+
}
|
|
23
|
+
for (const entry of entries) {
|
|
24
|
+
if (PRESERVE_FILES.has(entry))
|
|
25
|
+
continue;
|
|
26
|
+
const fullPath = path.join(storagePath, entry);
|
|
27
|
+
await fs.rm(fullPath, { recursive: true, force: true });
|
|
28
|
+
}
|
|
29
|
+
}
|
|
9
30
|
export const cleanCommand = async (options) => {
|
|
10
31
|
// --all flag: clean all indexed repos
|
|
11
32
|
if (options?.all) {
|
|
@@ -15,22 +36,22 @@ export const cleanCommand = async (options) => {
|
|
|
15
36
|
console.log('No indexed repositories found.');
|
|
16
37
|
return;
|
|
17
38
|
}
|
|
18
|
-
console.log(`This will
|
|
39
|
+
console.log(`This will clean GitNexus indexes for ${entries.length} repo(s):`);
|
|
19
40
|
for (const entry of entries) {
|
|
20
41
|
console.log(` - ${entry.name} (${entry.path})`);
|
|
21
42
|
}
|
|
22
|
-
console.log('\nRun with --force to confirm
|
|
43
|
+
console.log('\nRun with --force to confirm.');
|
|
23
44
|
return;
|
|
24
45
|
}
|
|
25
46
|
const entries = await listRegisteredRepos();
|
|
26
47
|
for (const entry of entries) {
|
|
27
48
|
try {
|
|
28
|
-
await
|
|
49
|
+
await cleanStoragePath(entry.storagePath);
|
|
29
50
|
await unregisterRepo(entry.path);
|
|
30
|
-
console.log(`
|
|
51
|
+
console.log(`Cleaned: ${entry.name} (${entry.storagePath})`);
|
|
31
52
|
}
|
|
32
53
|
catch (err) {
|
|
33
|
-
console.error(`Failed to
|
|
54
|
+
console.error(`Failed to clean ${entry.name}:`, err);
|
|
34
55
|
}
|
|
35
56
|
}
|
|
36
57
|
return;
|
|
@@ -44,17 +65,17 @@ export const cleanCommand = async (options) => {
|
|
|
44
65
|
}
|
|
45
66
|
const repoName = repo.repoPath.split(/[/\\]/).pop() || repo.repoPath;
|
|
46
67
|
if (!options?.force) {
|
|
47
|
-
console.log(`This will
|
|
68
|
+
console.log(`This will clean the GitNexus index for: ${repoName}`);
|
|
48
69
|
console.log(` Path: ${repo.storagePath}`);
|
|
49
|
-
console.log('\nRun with --force to confirm
|
|
70
|
+
console.log('\nRun with --force to confirm.');
|
|
50
71
|
return;
|
|
51
72
|
}
|
|
52
73
|
try {
|
|
53
|
-
await
|
|
74
|
+
await cleanStoragePath(repo.storagePath);
|
|
54
75
|
await unregisterRepo(repo.repoPath);
|
|
55
|
-
console.log(`
|
|
76
|
+
console.log(`Cleaned: ${repo.storagePath}`);
|
|
56
77
|
}
|
|
57
78
|
catch (err) {
|
|
58
|
-
console.error('Failed to
|
|
79
|
+
console.error('Failed to clean:', err);
|
|
59
80
|
}
|
|
60
81
|
};
|
|
@@ -28,6 +28,7 @@ export const callRouters = {
|
|
|
28
28
|
[SupportedLanguages.CPlusPlus]: noRouting,
|
|
29
29
|
[SupportedLanguages.C]: noRouting,
|
|
30
30
|
[SupportedLanguages.Ruby]: routeRubyCall,
|
|
31
|
+
[SupportedLanguages.GDScript]: noRouting,
|
|
31
32
|
};
|
|
32
33
|
// ── Pre-allocated singletons for common return values ────────────────────────
|
|
33
34
|
const CALL_RESULT = { kind: 'call' };
|
|
@@ -212,6 +212,7 @@ const exportCheckers = {
|
|
|
212
212
|
[SupportedLanguages.PHP]: phpExportChecker,
|
|
213
213
|
[SupportedLanguages.Swift]: swiftExportChecker,
|
|
214
214
|
[SupportedLanguages.Ruby]: (_node, _name) => true,
|
|
215
|
+
[SupportedLanguages.GDScript]: (_node, name) => !name.startsWith('_'),
|
|
215
216
|
};
|
|
216
217
|
// ============================================================================
|
|
217
218
|
// Public API
|
|
@@ -104,8 +104,10 @@ const processParsingSequential = async (graph, files, symbolTable, astCache, onF
|
|
|
104
104
|
skippedLanguages.set(language, (skippedLanguages.get(language) || 0) + 1);
|
|
105
105
|
continue;
|
|
106
106
|
}
|
|
107
|
-
// Skip files larger than the max tree-sitter buffer (32 MB)
|
|
108
|
-
|
|
107
|
+
// Skip files larger than the max tree-sitter buffer (32 MB).
|
|
108
|
+
// Use UTF-8 bytes, not JS string length, because tree-sitter buffer sizing
|
|
109
|
+
// is byte-oriented and multi-byte source can otherwise slip past the cap.
|
|
110
|
+
if (Buffer.byteLength(file.content, 'utf8') > TREE_SITTER_MAX_BUFFER)
|
|
109
111
|
continue;
|
|
110
112
|
try {
|
|
111
113
|
await loadLanguage(language, file.path);
|
|
@@ -11,5 +11,6 @@ export declare const RUST_QUERIES = "\n; Functions & Items\n(function_item name:
|
|
|
11
11
|
export declare const PHP_QUERIES = "\n; \u2500\u2500 Namespace \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n(namespace_definition\n name: (namespace_name) @name) @definition.namespace\n\n; \u2500\u2500 Classes \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n(class_declaration\n name: (name) @name) @definition.class\n\n; \u2500\u2500 Interfaces \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n(interface_declaration\n name: (name) @name) @definition.interface\n\n; \u2500\u2500 Traits \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n(trait_declaration\n name: (name) @name) @definition.trait\n\n; \u2500\u2500 Enums (PHP 8.1) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n(enum_declaration\n name: (name) @name) @definition.enum\n\n; \u2500\u2500 Top-level functions \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n(function_definition\n name: (name) @name) @definition.function\n\n; \u2500\u2500 Methods (including constructors) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n(method_declaration\n name: (name) @name) @definition.method\n\n; \u2500\u2500 Class properties (including Eloquent $fillable, $casts, etc.) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n(property_declaration\n (property_element\n (variable_name\n (name) @name))) @definition.property\n\n; \u2500\u2500 Imports: use statements \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n; Simple: use App\\Models\\User;\n(namespace_use_declaration\n (namespace_use_clause\n (qualified_name) @import.source)) @import\n\n; \u2500\u2500 Function/method calls \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n; Regular function call: foo()\n(function_call_expression\n function: (name) @call.name) @call\n\n; Method call: $obj->method()\n(member_call_expression\n name: (name) @call.name) @call\n\n; Nullsafe method call: $obj?->method()\n(nullsafe_member_call_expression\n name: (name) @call.name) @call\n\n; Static call: Foo::bar() (php_only uses scoped_call_expression)\n(scoped_call_expression\n name: (name) @call.name) @call\n\n; Constructor call: new User()\n(object_creation_expression (name) @call.name) @call\n\n; \u2500\u2500 Heritage: extends \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n(class_declaration\n name: (name) @heritage.class\n (base_clause\n [(name) (qualified_name)] @heritage.extends)) @heritage\n\n; \u2500\u2500 Heritage: implements \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n(class_declaration\n name: (name) @heritage.class\n (class_interface_clause\n [(name) (qualified_name)] @heritage.implements)) @heritage.impl\n\n; \u2500\u2500 Heritage: use trait (must capture enclosing class name) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n(class_declaration\n name: (name) @heritage.class\n body: (declaration_list\n (use_declaration\n [(name) (qualified_name)] @heritage.trait))) @heritage\n";
|
|
12
12
|
export declare const RUBY_QUERIES = "\n; \u2500\u2500 Modules \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n(module\n name: (constant) @name) @definition.module\n\n; \u2500\u2500 Classes \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n(class\n name: (constant) @name) @definition.class\n\n; \u2500\u2500 Instance methods \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n(method\n name: (identifier) @name) @definition.method\n\n; \u2500\u2500 Singleton (class-level) methods \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n(singleton_method\n name: (identifier) @name) @definition.method\n\n; \u2500\u2500 All calls (require, include, attr_*, and regular calls routed in JS) \u2500\u2500\u2500\u2500\u2500\n(call\n method: (identifier) @call.name) @call\n\n; \u2500\u2500 Bare calls without parens (identifiers at statement level are method calls) \u2500\n; NOTE: This may over-capture variable reads as calls (e.g. 'result' at\n; statement level). Ruby's grammar makes bare identifiers ambiguous \u2014 they\n; could be local variables or zero-arity method calls. Post-processing via\n; isBuiltInOrNoise and symbol resolution filtering suppresses most false\n; positives, but a variable name that coincidentally matches a method name\n; elsewhere may produce a false CALLS edge.\n(body_statement\n (identifier) @call.name @call)\n\n; \u2500\u2500 Heritage: class < SuperClass \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n(class\n name: (constant) @heritage.class\n superclass: (superclass\n (constant) @heritage.extends)) @heritage\n";
|
|
13
13
|
export declare const KOTLIN_QUERIES = "\n; \u2500\u2500 Interfaces \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n; tree-sitter-kotlin (fwcd) has no interface_declaration node type.\n; Interfaces are class_declaration nodes with an anonymous \"interface\" keyword child.\n(class_declaration\n \"interface\"\n (type_identifier) @name) @definition.interface\n\n; \u2500\u2500 Classes (regular, data, sealed, enum) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n; All have the anonymous \"class\" keyword child. enum class has both\n; \"enum\" and \"class\" children \u2014 the \"class\" child still matches.\n(class_declaration\n \"class\"\n (type_identifier) @name) @definition.class\n\n; \u2500\u2500 Object declarations (Kotlin singletons) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n(object_declaration\n (type_identifier) @name) @definition.class\n\n; \u2500\u2500 Companion objects (named only) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n(companion_object\n (type_identifier) @name) @definition.class\n\n; \u2500\u2500 Functions (top-level, member, extension) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n(function_declaration\n (simple_identifier) @name) @definition.function\n\n; \u2500\u2500 Properties \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n(property_declaration\n (variable_declaration\n (simple_identifier) @name)) @definition.property\n\n; \u2500\u2500 Enum entries \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n(enum_entry\n (simple_identifier) @name) @definition.enum\n\n; \u2500\u2500 Type aliases \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n(type_alias\n (type_identifier) @name) @definition.type\n\n; \u2500\u2500 Imports \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n(import_header\n (identifier) @import.source) @import\n\n; \u2500\u2500 Function calls (direct) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n(call_expression\n (simple_identifier) @call.name) @call\n\n; \u2500\u2500 Method calls (via navigation: obj.method()) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n(call_expression\n (navigation_expression\n (navigation_suffix\n (simple_identifier) @call.name))) @call\n\n; \u2500\u2500 Constructor invocations \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n(constructor_invocation\n (user_type\n (type_identifier) @call.name)) @call\n\n; \u2500\u2500 Infix function calls (e.g., a to b, x until y) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n(infix_expression\n (simple_identifier) @call.name) @call\n\n; \u2500\u2500 Heritage: extends / implements via delegation_specifier \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n; Interface implementation (bare user_type): class Foo : Bar\n(class_declaration\n (type_identifier) @heritage.class\n (delegation_specifier\n (user_type (type_identifier) @heritage.extends))) @heritage\n\n; Class extension (constructor_invocation): class Foo : Bar()\n(class_declaration\n (type_identifier) @heritage.class\n (delegation_specifier\n (constructor_invocation\n (user_type (type_identifier) @heritage.extends)))) @heritage\n";
|
|
14
|
+
export declare const GDSCRIPT_QUERIES = "\n; \u2500\u2500 Class Name (file-level class) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n(class_name_statement\n (name) @name) @definition.class\n\n; \u2500\u2500 Inner Class Definition \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n(class_definition\n name: (name) @name) @definition.class\n\n; \u2500\u2500 Functions & Methods \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n(function_definition\n name: (name) @name) @definition.function\n\n; \u2500\u2500 Constructor (_init) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n(constructor_definition) @definition.constructor\n\n; \u2500\u2500 Constructor with name capture for symbol extraction \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n(constructor_definition\n parameters: (parameters)) @definition.constructor\n\n; \u2500\u2500 Signals \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n(signal_statement\n name: (name) @name) @definition.signal\n\n; \u2500\u2500 Enums \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n(enum_definition\n name: (name) @name) @definition.enum\n\n; \u2500\u2500 Constants \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n(const_statement\n (name) @name) @definition.const\n\n; \u2500\u2500 Extends (inheritance) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n(extends_statement\n (type (identifier) @heritage.extends)) @heritage\n\n; \u2500\u2500 Class with extends \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n(class_definition\n name: (name) @heritage.class\n extends: (extends_statement\n (type (identifier) @heritage.extends))) @heritage\n\n; \u2500\u2500 Preload/Load (imports) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n; Only preload(\"res://...\") and load(\"res://...\") are real imports.\n; Generic call expressions must NOT be tagged @import to avoid double-classifying\n; call sites as imports (which corrupts import resolution and call routing).\n(call\n function: (identifier) @_preload\n arguments: (arguments (string) @import.source)) @import\n(#eq? @_preload \"preload\")\n\n(call\n function: (identifier) @_load\n arguments: (arguments (string) @import.source)) @import\n(#eq? @_load \"load\")\n\n; \u2500\u2500 Function Calls \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n(call\n function: (identifier) @call.name) @call\n\n";
|
|
14
15
|
export declare const SWIFT_QUERIES = "\n; Classes\n(class_declaration \"class\" name: (type_identifier) @name) @definition.class\n\n; Structs\n(class_declaration \"struct\" name: (type_identifier) @name) @definition.struct\n\n; Enums\n(class_declaration \"enum\" name: (type_identifier) @name) @definition.enum\n\n; Extensions (mapped to class \u2014 no dedicated label in schema)\n(class_declaration \"extension\" name: (user_type (type_identifier) @name)) @definition.class\n\n; Actors\n(class_declaration \"actor\" name: (type_identifier) @name) @definition.class\n\n; Protocols (mapped to interface)\n(protocol_declaration name: (type_identifier) @name) @definition.interface\n\n; Type aliases\n(typealias_declaration name: (type_identifier) @name) @definition.type\n\n; Functions (top-level and methods)\n(function_declaration name: (simple_identifier) @name) @definition.function\n\n; Protocol method declarations\n(protocol_function_declaration name: (simple_identifier) @name) @definition.method\n\n; Initializers\n(init_declaration) @definition.constructor\n\n; Properties (stored and computed)\n(property_declaration (pattern (simple_identifier) @name)) @definition.property\n\n; Imports\n(import_declaration (identifier (simple_identifier) @import.source)) @import\n\n; Calls - direct function calls\n(call_expression (simple_identifier) @call.name) @call\n\n; Calls - member/navigation calls (obj.method())\n(call_expression (navigation_expression (navigation_suffix (simple_identifier) @call.name))) @call\n\n; Heritage - class/struct/enum inheritance and protocol conformance\n(class_declaration name: (type_identifier) @heritage.class\n (inheritance_specifier inherits_from: (user_type (type_identifier) @heritage.extends))) @heritage\n\n; Heritage - protocol inheritance\n(protocol_declaration name: (type_identifier) @heritage.class\n (inheritance_specifier inherits_from: (user_type (type_identifier) @heritage.extends))) @heritage\n\n; Heritage - extension protocol conformance (e.g. extension Foo: SomeProtocol)\n; Extensions wrap the name in user_type unlike class/struct/enum declarations\n(class_declaration \"extension\" name: (user_type (type_identifier) @heritage.class)\n (inheritance_specifier inherits_from: (user_type (type_identifier) @heritage.extends))) @heritage\n";
|
|
15
16
|
export declare const LANGUAGE_QUERIES: Record<SupportedLanguages, string>;
|
|
@@ -615,6 +615,67 @@ export const KOTLIN_QUERIES = `
|
|
|
615
615
|
(user_type (type_identifier) @heritage.extends)))) @heritage
|
|
616
616
|
`;
|
|
617
617
|
// Swift queries - works with tree-sitter-swift
|
|
618
|
+
export const GDSCRIPT_QUERIES = `
|
|
619
|
+
; ── Class Name (file-level class) ───────────────────────────────────────────
|
|
620
|
+
(class_name_statement
|
|
621
|
+
(name) @name) @definition.class
|
|
622
|
+
|
|
623
|
+
; ── Inner Class Definition ──────────────────────────────────────────────────
|
|
624
|
+
(class_definition
|
|
625
|
+
name: (name) @name) @definition.class
|
|
626
|
+
|
|
627
|
+
; ── Functions & Methods ─────────────────────────────────────────────────────
|
|
628
|
+
(function_definition
|
|
629
|
+
name: (name) @name) @definition.function
|
|
630
|
+
|
|
631
|
+
; ── Constructor (_init) ─────────────────────────────────────────────────────
|
|
632
|
+
(constructor_definition) @definition.constructor
|
|
633
|
+
|
|
634
|
+
; ── Constructor with name capture for symbol extraction ────────────────────
|
|
635
|
+
(constructor_definition
|
|
636
|
+
parameters: (parameters)) @definition.constructor
|
|
637
|
+
|
|
638
|
+
; ── Signals ─────────────────────────────────────────────────────────────────
|
|
639
|
+
(signal_statement
|
|
640
|
+
name: (name) @name) @definition.signal
|
|
641
|
+
|
|
642
|
+
; ── Enums ───────────────────────────────────────────────────────────────────
|
|
643
|
+
(enum_definition
|
|
644
|
+
name: (name) @name) @definition.enum
|
|
645
|
+
|
|
646
|
+
; ── Constants ───────────────────────────────────────────────────────────────
|
|
647
|
+
(const_statement
|
|
648
|
+
(name) @name) @definition.const
|
|
649
|
+
|
|
650
|
+
; ── Extends (inheritance) ───────────────────────────────────────────────────
|
|
651
|
+
(extends_statement
|
|
652
|
+
(type (identifier) @heritage.extends)) @heritage
|
|
653
|
+
|
|
654
|
+
; ── Class with extends ──────────────────────────────────────────────────────
|
|
655
|
+
(class_definition
|
|
656
|
+
name: (name) @heritage.class
|
|
657
|
+
extends: (extends_statement
|
|
658
|
+
(type (identifier) @heritage.extends))) @heritage
|
|
659
|
+
|
|
660
|
+
; ── Preload/Load (imports) ──────────────────────────────────────────────────
|
|
661
|
+
; Only preload("res://...") and load("res://...") are real imports.
|
|
662
|
+
; Generic call expressions must NOT be tagged @import to avoid double-classifying
|
|
663
|
+
; call sites as imports (which corrupts import resolution and call routing).
|
|
664
|
+
(call
|
|
665
|
+
function: (identifier) @_preload
|
|
666
|
+
arguments: (arguments (string) @import.source)) @import
|
|
667
|
+
(#eq? @_preload "preload")
|
|
668
|
+
|
|
669
|
+
(call
|
|
670
|
+
function: (identifier) @_load
|
|
671
|
+
arguments: (arguments (string) @import.source)) @import
|
|
672
|
+
(#eq? @_load "load")
|
|
673
|
+
|
|
674
|
+
; ── Function Calls ──────────────────────────────────────────────────────────
|
|
675
|
+
(call
|
|
676
|
+
function: (identifier) @call.name) @call
|
|
677
|
+
|
|
678
|
+
`;
|
|
618
679
|
export const SWIFT_QUERIES = `
|
|
619
680
|
; Classes
|
|
620
681
|
(class_declaration "class" name: (type_identifier) @name) @definition.class
|
|
@@ -685,4 +746,5 @@ export const LANGUAGE_QUERIES = {
|
|
|
685
746
|
[SupportedLanguages.PHP]: PHP_QUERIES,
|
|
686
747
|
[SupportedLanguages.Kotlin]: KOTLIN_QUERIES,
|
|
687
748
|
[SupportedLanguages.Swift]: SWIFT_QUERIES,
|
|
749
|
+
[SupportedLanguages.GDScript]: GDSCRIPT_QUERIES,
|
|
688
750
|
};
|
|
@@ -17,6 +17,7 @@ export declare const typeConfigs: {
|
|
|
17
17
|
cpp: LanguageTypeConfig;
|
|
18
18
|
php: LanguageTypeConfig;
|
|
19
19
|
ruby: LanguageTypeConfig;
|
|
20
|
+
gdscript: any;
|
|
20
21
|
};
|
|
21
22
|
export type { LanguageTypeConfig, TypeBindingExtractor, ParameterExtractor, ConstructorBindingScanner, ForLoopExtractor, PendingAssignmentExtractor, PatternBindingExtractor, } from './types.js';
|
|
22
23
|
export { TYPED_PARAMETER_TYPES, extractSimpleTypeName, extractGenericTypeArgs, extractVarName, findChildByType, extractRubyConstructorAssignment } from './shared.js';
|
|
@@ -27,5 +27,13 @@ export const typeConfigs = {
|
|
|
27
27
|
[SupportedLanguages.CPlusPlus]: cCppConfig,
|
|
28
28
|
[SupportedLanguages.PHP]: phpConfig,
|
|
29
29
|
[SupportedLanguages.Ruby]: rubyConfig,
|
|
30
|
+
[SupportedLanguages.GDScript]: {
|
|
31
|
+
declarationNodeTypes: new Set(),
|
|
32
|
+
extractDeclaration: () => null,
|
|
33
|
+
extractParameter: () => null,
|
|
34
|
+
extractConstructor: () => null,
|
|
35
|
+
extractReturnBinding: () => null,
|
|
36
|
+
extractPatternBinding: () => null,
|
|
37
|
+
},
|
|
30
38
|
};
|
|
31
39
|
export { TYPED_PARAMETER_TYPES, extractSimpleTypeName, extractGenericTypeArgs, extractVarName, findChildByType, extractRubyConstructorAssignment } from './shared.js';
|
|
@@ -612,6 +612,9 @@ export const getLanguageFromFilename = (filename) => {
|
|
|
612
612
|
// Swift (extensions)
|
|
613
613
|
if (filename.endsWith('.swift'))
|
|
614
614
|
return SupportedLanguages.Swift;
|
|
615
|
+
// GDScript (Godot Engine)
|
|
616
|
+
if (filename.endsWith('.gd'))
|
|
617
|
+
return SupportedLanguages.GDScript;
|
|
615
618
|
return null;
|
|
616
619
|
};
|
|
617
620
|
const CALL_ARGUMENT_LIST_TYPES = new Set([
|
|
@@ -14,20 +14,8 @@ import Ruby from 'tree-sitter-ruby';
|
|
|
14
14
|
import { createRequire } from 'node:module';
|
|
15
15
|
import { SupportedLanguages } from '../../../config/supported-languages.js';
|
|
16
16
|
import { LANGUAGE_QUERIES } from '../tree-sitter-queries.js';
|
|
17
|
-
import { TREE_SITTER_MAX_BUFFER } from '../constants.js';
|
|
18
|
-
// tree-sitter-swift is an optionalDependency — may not be installed
|
|
17
|
+
import { getTreeSitterBufferSize, TREE_SITTER_MAX_BUFFER } from '../constants.js';
|
|
19
18
|
const _require = createRequire(import.meta.url);
|
|
20
|
-
let Swift = null;
|
|
21
|
-
try {
|
|
22
|
-
Swift = _require('tree-sitter-swift');
|
|
23
|
-
}
|
|
24
|
-
catch { }
|
|
25
|
-
// tree-sitter-kotlin is an optionalDependency — may not be installed
|
|
26
|
-
let Kotlin = null;
|
|
27
|
-
try {
|
|
28
|
-
Kotlin = _require('tree-sitter-kotlin');
|
|
29
|
-
}
|
|
30
|
-
catch { }
|
|
31
19
|
import { getLanguageFromFilename, FUNCTION_NODE_TYPES, extractFunctionName, isBuiltInOrNoise, getDefinitionNodeFromCaptures, findEnclosingClassId, extractMethodSignature, countCallArguments, inferCallForm, extractReceiverName, extractReceiverNode, CALL_EXPRESSION_TYPES, extractCallChain, } from '../utils.js';
|
|
32
20
|
import { buildTypeEnv } from '../type-env.js';
|
|
33
21
|
import { isNodeExported } from '../export-detection.js';
|
|
@@ -41,7 +29,7 @@ import { callRouters } from '../call-routing.js';
|
|
|
41
29
|
// Worker-local parser + language map
|
|
42
30
|
// ============================================================================
|
|
43
31
|
const parser = new Parser();
|
|
44
|
-
const
|
|
32
|
+
const requiredLanguageMap = {
|
|
45
33
|
[SupportedLanguages.JavaScript]: JavaScript,
|
|
46
34
|
[SupportedLanguages.TypeScript]: TypeScript.typescript,
|
|
47
35
|
[`${SupportedLanguages.TypeScript}:tsx`]: TypeScript.tsx,
|
|
@@ -52,10 +40,60 @@ const languageMap = {
|
|
|
52
40
|
[SupportedLanguages.CSharp]: CSharp,
|
|
53
41
|
[SupportedLanguages.Go]: Go,
|
|
54
42
|
[SupportedLanguages.Rust]: Rust,
|
|
55
|
-
...(Kotlin ? { [SupportedLanguages.Kotlin]: Kotlin } : {}),
|
|
56
43
|
[SupportedLanguages.PHP]: PHP.php_only,
|
|
57
44
|
[SupportedLanguages.Ruby]: Ruby,
|
|
58
|
-
|
|
45
|
+
};
|
|
46
|
+
const optionalLanguagePackages = {
|
|
47
|
+
[SupportedLanguages.GDScript]: 'tree-sitter-gdscript',
|
|
48
|
+
[SupportedLanguages.Swift]: 'tree-sitter-swift',
|
|
49
|
+
[SupportedLanguages.Kotlin]: 'tree-sitter-kotlin',
|
|
50
|
+
};
|
|
51
|
+
const optionalLanguageCache = new Map();
|
|
52
|
+
const optionalAvailabilityCache = new Map();
|
|
53
|
+
const isOptionalLanguageInstalled = (language) => {
|
|
54
|
+
if (optionalAvailabilityCache.has(language)) {
|
|
55
|
+
return optionalAvailabilityCache.get(language);
|
|
56
|
+
}
|
|
57
|
+
const packageName = optionalLanguagePackages[language];
|
|
58
|
+
if (!packageName) {
|
|
59
|
+
optionalAvailabilityCache.set(language, false);
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
try {
|
|
63
|
+
_require.resolve(packageName);
|
|
64
|
+
optionalAvailabilityCache.set(language, true);
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
optionalAvailabilityCache.set(language, false);
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
const loadOptionalLanguage = (language) => {
|
|
73
|
+
if (optionalLanguageCache.has(language)) {
|
|
74
|
+
return optionalLanguageCache.get(language);
|
|
75
|
+
}
|
|
76
|
+
const packageName = optionalLanguagePackages[language];
|
|
77
|
+
if (!packageName) {
|
|
78
|
+
optionalLanguageCache.set(language, null);
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
try {
|
|
82
|
+
const grammar = _require(packageName);
|
|
83
|
+
optionalLanguageCache.set(language, grammar);
|
|
84
|
+
return grammar;
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
optionalLanguageCache.set(language, null);
|
|
88
|
+
optionalAvailabilityCache.set(language, false);
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
const resolveLanguage = (key, language) => {
|
|
93
|
+
if (key in requiredLanguageMap) {
|
|
94
|
+
return requiredLanguageMap[key];
|
|
95
|
+
}
|
|
96
|
+
return loadOptionalLanguage(language);
|
|
59
97
|
};
|
|
60
98
|
/**
|
|
61
99
|
* Check if a language grammar is available in this worker.
|
|
@@ -67,13 +105,13 @@ const isLanguageAvailable = (language, filePath) => {
|
|
|
67
105
|
const key = language === SupportedLanguages.TypeScript && filePath.endsWith('.tsx')
|
|
68
106
|
? `${language}:tsx`
|
|
69
107
|
: language;
|
|
70
|
-
return key in
|
|
108
|
+
return key in requiredLanguageMap || isOptionalLanguageInstalled(language);
|
|
71
109
|
};
|
|
72
110
|
const setLanguage = (language, filePath) => {
|
|
73
111
|
const key = language === SupportedLanguages.TypeScript && filePath.endsWith('.tsx')
|
|
74
112
|
? `${language}:tsx`
|
|
75
113
|
: language;
|
|
76
|
-
const lang =
|
|
114
|
+
const lang = resolveLanguage(key, language);
|
|
77
115
|
if (!lang)
|
|
78
116
|
throw new Error(`Unsupported language: ${language}`);
|
|
79
117
|
parser.setLanguage(lang);
|
|
@@ -713,27 +751,23 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
|
|
|
713
751
|
return;
|
|
714
752
|
}
|
|
715
753
|
for (const file of files) {
|
|
716
|
-
// Skip files larger than the max tree-sitter buffer (32 MB)
|
|
717
|
-
|
|
754
|
+
// Skip files larger than the max tree-sitter buffer (32 MB).
|
|
755
|
+
// tree-sitter buffer sizing is byte-oriented; JS string length undercounts
|
|
756
|
+
// UTF-8 multi-byte source and can route oversized input into native code.
|
|
757
|
+
if (Buffer.byteLength(file.content, 'utf8') > TREE_SITTER_MAX_BUFFER)
|
|
718
758
|
continue;
|
|
719
759
|
let tree;
|
|
720
760
|
let usedRawContentFallback = false;
|
|
721
761
|
try {
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
if (index >= file.content.length)
|
|
725
|
-
return null;
|
|
726
|
-
return file.content.slice(index, index + MAX_CHUNK);
|
|
762
|
+
tree = parser.parse(file.content, null, {
|
|
763
|
+
bufferSize: getTreeSitterBufferSize(Buffer.byteLength(file.content, 'utf8')),
|
|
727
764
|
});
|
|
728
765
|
}
|
|
729
766
|
catch (err) {
|
|
730
767
|
if (file.rawContent && file.rawContent !== file.content) {
|
|
731
768
|
try {
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
if (index >= file.rawContent.length)
|
|
735
|
-
return null;
|
|
736
|
-
return file.rawContent.slice(index, index + MAX_CHUNK);
|
|
769
|
+
tree = parser.parse(file.rawContent, null, {
|
|
770
|
+
bufferSize: getTreeSitterBufferSize(Buffer.byteLength(file.rawContent, 'utf8')),
|
|
737
771
|
});
|
|
738
772
|
usedRawContentFallback = true;
|
|
739
773
|
}
|
|
@@ -749,11 +783,8 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
|
|
|
749
783
|
}
|
|
750
784
|
if (file.rawContent && file.rawContent !== file.content && tree.rootNode?.hasError) {
|
|
751
785
|
try {
|
|
752
|
-
const
|
|
753
|
-
|
|
754
|
-
if (index >= file.rawContent.length)
|
|
755
|
-
return null;
|
|
756
|
-
return file.rawContent.slice(index, index + MAX_CHUNK);
|
|
786
|
+
const rawTree = parser.parse(file.rawContent, null, {
|
|
787
|
+
bufferSize: getTreeSitterBufferSize(Buffer.byteLength(file.rawContent, 'utf8')),
|
|
757
788
|
});
|
|
758
789
|
if (!rawTree.rootNode?.hasError) {
|
|
759
790
|
tree = rawTree;
|
|
@@ -4,9 +4,13 @@ export declare const isLanguageAvailable: (language: SupportedLanguages) => bool
|
|
|
4
4
|
export declare const loadParser: () => Promise<Parser>;
|
|
5
5
|
export declare const loadLanguage: (language: SupportedLanguages, filePath?: string) => Promise<void>;
|
|
6
6
|
/**
|
|
7
|
-
* Parse source code using tree-sitter's
|
|
8
|
-
*
|
|
9
|
-
*
|
|
7
|
+
* Parse source code using tree-sitter's string input path with an adaptive
|
|
8
|
+
* native buffer size.
|
|
9
|
+
*
|
|
10
|
+
* The callback input API receives byte offsets. Returning JavaScript string
|
|
11
|
+
* slices from those byte offsets is unsafe for UTF-8/multi-byte content and has
|
|
12
|
+
* caused native tree-sitter crashes in large repositories. Use the stable string
|
|
13
|
+
* input path instead and raise tree-sitter's internal buffer for large files.
|
|
10
14
|
*
|
|
11
15
|
* @param content - Full source file content as UTF-8 string
|
|
12
16
|
* @param oldTree - Optional previous tree for incremental parsing (must call tree.edit() first)
|
|
@@ -12,21 +12,10 @@ 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
|
-
|
|
15
|
+
import { getTreeSitterBufferSize } from '../ingestion/constants.js';
|
|
16
16
|
const _require = createRequire(import.meta.url);
|
|
17
|
-
let Swift = null;
|
|
18
|
-
try {
|
|
19
|
-
Swift = _require('tree-sitter-swift');
|
|
20
|
-
}
|
|
21
|
-
catch { }
|
|
22
|
-
// tree-sitter-kotlin is an optionalDependency — may not be installed
|
|
23
|
-
let Kotlin = null;
|
|
24
|
-
try {
|
|
25
|
-
Kotlin = _require('tree-sitter-kotlin');
|
|
26
|
-
}
|
|
27
|
-
catch { }
|
|
28
17
|
let parser = null;
|
|
29
|
-
const
|
|
18
|
+
const requiredLanguageMap = {
|
|
30
19
|
[SupportedLanguages.JavaScript]: JavaScript,
|
|
31
20
|
[SupportedLanguages.TypeScript]: TypeScript.typescript,
|
|
32
21
|
[`${SupportedLanguages.TypeScript}:tsx`]: TypeScript.tsx,
|
|
@@ -37,12 +26,62 @@ const languageMap = {
|
|
|
37
26
|
[SupportedLanguages.CSharp]: CSharp,
|
|
38
27
|
[SupportedLanguages.Go]: Go,
|
|
39
28
|
[SupportedLanguages.Rust]: Rust,
|
|
40
|
-
...(Kotlin ? { [SupportedLanguages.Kotlin]: Kotlin } : {}),
|
|
41
29
|
[SupportedLanguages.PHP]: PHP.php_only,
|
|
42
30
|
[SupportedLanguages.Ruby]: Ruby,
|
|
43
|
-
...(Swift ? { [SupportedLanguages.Swift]: Swift } : {}),
|
|
44
31
|
};
|
|
45
|
-
|
|
32
|
+
const optionalLanguagePackages = {
|
|
33
|
+
[SupportedLanguages.GDScript]: 'tree-sitter-gdscript',
|
|
34
|
+
[SupportedLanguages.Swift]: 'tree-sitter-swift',
|
|
35
|
+
[SupportedLanguages.Kotlin]: 'tree-sitter-kotlin',
|
|
36
|
+
};
|
|
37
|
+
const optionalLanguageCache = new Map();
|
|
38
|
+
const optionalAvailabilityCache = new Map();
|
|
39
|
+
const isOptionalLanguageInstalled = (language) => {
|
|
40
|
+
if (optionalAvailabilityCache.has(language)) {
|
|
41
|
+
return optionalAvailabilityCache.get(language);
|
|
42
|
+
}
|
|
43
|
+
const packageName = optionalLanguagePackages[language];
|
|
44
|
+
if (!packageName) {
|
|
45
|
+
optionalAvailabilityCache.set(language, false);
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
try {
|
|
49
|
+
_require.resolve(packageName);
|
|
50
|
+
optionalAvailabilityCache.set(language, true);
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
optionalAvailabilityCache.set(language, false);
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
const loadOptionalLanguage = (language) => {
|
|
59
|
+
if (optionalLanguageCache.has(language)) {
|
|
60
|
+
return optionalLanguageCache.get(language);
|
|
61
|
+
}
|
|
62
|
+
const packageName = optionalLanguagePackages[language];
|
|
63
|
+
if (!packageName) {
|
|
64
|
+
optionalLanguageCache.set(language, null);
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
try {
|
|
68
|
+
const grammar = _require(packageName);
|
|
69
|
+
optionalLanguageCache.set(language, grammar);
|
|
70
|
+
return grammar;
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
optionalLanguageCache.set(language, null);
|
|
74
|
+
optionalAvailabilityCache.set(language, false);
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
const resolveLanguage = (key, language) => {
|
|
79
|
+
if (key in requiredLanguageMap) {
|
|
80
|
+
return requiredLanguageMap[key];
|
|
81
|
+
}
|
|
82
|
+
return loadOptionalLanguage(language);
|
|
83
|
+
};
|
|
84
|
+
export const isLanguageAvailable = (language) => language in requiredLanguageMap || isOptionalLanguageInstalled(language);
|
|
46
85
|
export const loadParser = async () => {
|
|
47
86
|
if (parser)
|
|
48
87
|
return parser;
|
|
@@ -55,17 +94,20 @@ export const loadLanguage = async (language, filePath) => {
|
|
|
55
94
|
const key = language === SupportedLanguages.TypeScript && filePath?.endsWith('.tsx')
|
|
56
95
|
? `${language}:tsx`
|
|
57
96
|
: language;
|
|
58
|
-
const lang =
|
|
97
|
+
const lang = resolveLanguage(key, language);
|
|
59
98
|
if (!lang) {
|
|
60
99
|
throw new Error(`Unsupported language: ${language}`);
|
|
61
100
|
}
|
|
62
101
|
parser.setLanguage(lang);
|
|
63
102
|
};
|
|
64
|
-
const MAX_CHUNK = 4096;
|
|
65
103
|
/**
|
|
66
|
-
* Parse source code using tree-sitter's
|
|
67
|
-
*
|
|
68
|
-
*
|
|
104
|
+
* Parse source code using tree-sitter's string input path with an adaptive
|
|
105
|
+
* native buffer size.
|
|
106
|
+
*
|
|
107
|
+
* The callback input API receives byte offsets. Returning JavaScript string
|
|
108
|
+
* slices from those byte offsets is unsafe for UTF-8/multi-byte content and has
|
|
109
|
+
* caused native tree-sitter crashes in large repositories. Use the stable string
|
|
110
|
+
* input path instead and raise tree-sitter's internal buffer for large files.
|
|
69
111
|
*
|
|
70
112
|
* @param content - Full source file content as UTF-8 string
|
|
71
113
|
* @param oldTree - Optional previous tree for incremental parsing (must call tree.edit() first)
|
|
@@ -74,9 +116,6 @@ const MAX_CHUNK = 4096;
|
|
|
74
116
|
export const parseContent = (content, oldTree) => {
|
|
75
117
|
if (!parser)
|
|
76
118
|
throw new Error('Parser not initialized — call loadParser() first');
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
return null;
|
|
80
|
-
return content.slice(index, index + MAX_CHUNK);
|
|
81
|
-
}, oldTree);
|
|
119
|
+
const bufferSize = getTreeSitterBufferSize(Buffer.byteLength(content, 'utf8'));
|
|
120
|
+
return parser.parse(content, oldTree ?? null, { bufferSize });
|
|
82
121
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@veewo/gitnexus",
|
|
3
|
-
"version": "1.5.
|
|
3
|
+
"version": "1.5.4",
|
|
4
4
|
"description": "Graph-powered code intelligence for AI agents. Index any codebase, query via MCP or CLI.",
|
|
5
5
|
"author": "Abhigyan Patwari",
|
|
6
6
|
"license": "PolyForm-Noncommercial-1.0.0",
|
|
@@ -101,7 +101,8 @@
|
|
|
101
101
|
},
|
|
102
102
|
"optionalDependencies": {
|
|
103
103
|
"tree-sitter-kotlin": "^0.3.8",
|
|
104
|
-
"tree-sitter-swift": "^0.6.0"
|
|
104
|
+
"tree-sitter-swift": "^0.6.0",
|
|
105
|
+
"tree-sitter-gdscript": "^6.1.0"
|
|
105
106
|
},
|
|
106
107
|
"devDependencies": {
|
|
107
108
|
"@types/cli-progress": "^3.11.6",
|
package/skills/gitnexus-cli.md
CHANGED
|
@@ -60,35 +60,83 @@ $GN analyze
|
|
|
60
60
|
|
|
61
61
|
Run from the project root. This parses all source files, builds the knowledge graph, writes it to `.gitnexus/`, and generates CLAUDE.md / AGENTS.md context files.
|
|
62
62
|
|
|
63
|
-
| Flag
|
|
64
|
-
|
|
65
|
-
| `--force`
|
|
66
|
-
| `--embeddings`
|
|
67
|
-
| `--
|
|
68
|
-
| `--csharp-define-csproj <path>` | Load C# `DefineConstants` from a `.csproj` and normalize `#if/#elif/#else/#endif` before parsing |
|
|
69
|
-
| `--scope-prefix <prefix>` | Limit analysis to a path prefix (e.g., `--scope-prefix Assets/` for Unity) |
|
|
70
|
-
| `--scope-manifest <file>` | Read scope rules from a manifest file (e.g., `.gitnexus/sync-manifest.txt`) |
|
|
71
|
-
| `--sync-manifest-policy <policy>` | Drift policy when explicit CLI values differ from manifest directives: `ask|update|keep|error` |
|
|
72
|
-
| `--skills` | Generate repo-specific skill files from detected code communities |
|
|
63
|
+
| Flag | Effect |
|
|
64
|
+
|------|--------|
|
|
65
|
+
| `--force` | Force full re-index even if up to date |
|
|
66
|
+
| `--embeddings` | Enable embedding generation (off by default) |
|
|
67
|
+
| `--skills` | Generate repo-specific skill files from detected communities |
|
|
73
68
|
|
|
74
|
-
**
|
|
69
|
+
**Two mutually exclusive paths. Choose one per rebuild.**
|
|
75
70
|
|
|
76
|
-
|
|
71
|
+
#### Path A: sync-manifest managed (recommended for Unity / monorepo)
|
|
77
72
|
|
|
78
|
-
|
|
73
|
+
If `.gitnexus/sync-manifest.txt` exists, `analyze` **auto-uses** it when you do **not** pass `--scope-prefix` or `--scope-manifest`.
|
|
79
74
|
|
|
80
|
-
|
|
75
|
+
```bash
|
|
76
|
+
# Unity project with an existing manifest — this is the normal rebuild command
|
|
77
|
+
$GN analyze --force
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
The manifest controls scope rules, extensions, and repo alias. Example:
|
|
81
|
+
|
|
82
|
+
```
|
|
83
|
+
Assets/
|
|
84
|
+
Packages/
|
|
85
|
+
@extensions=.cs,.meta
|
|
86
|
+
@repoAlias=neonspark-core
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
- Non-`@` lines = path-prefix scope rules
|
|
90
|
+
- `@extensions=<csv>` = file extension filter
|
|
91
|
+
- `@repoAlias=<name>` = stable repo alias
|
|
92
|
+
- `@embeddings=<true|false>` = embedding toggle
|
|
93
|
+
|
|
94
|
+
**Drift guard:** If you pass `--extensions` / `--repo-alias` / `--embeddings` while a manifest exists, CLI compares them. Use `--sync-manifest-policy` to control: `ask|update|keep|error` (default `ask`; non-TTY requires explicit policy).
|
|
95
|
+
|
|
96
|
+
**Do not mix Path A and Path B.** Passing `--scope-prefix` or `--extensions` when a manifest exists triggers the drift guard and may error out in non-TTY environments.
|
|
97
|
+
|
|
98
|
+
#### Path B: manual CLI flags (first-time or simple projects)
|
|
81
99
|
|
|
82
|
-
|
|
100
|
+
Use when no sync-manifest exists:
|
|
83
101
|
|
|
84
|
-
|
|
85
|
-
|
|
102
|
+
```bash
|
|
103
|
+
# Unity project, first-time index
|
|
104
|
+
$GN analyze --force --extensions ".cs,.meta" --scope-prefix Assets/ --repo-alias neonspark-core
|
|
105
|
+
|
|
106
|
+
# Generic project
|
|
107
|
+
$GN analyze --force --extensions ".ts,.tsx" --scope-prefix src/
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
| Manual flag | Effect |
|
|
111
|
+
|-------------|--------|
|
|
112
|
+
| `--extensions <ext>` | Comma-separated file extensions |
|
|
113
|
+
| `--scope-prefix <prefix>` | Add a path prefix rule (repeatable) |
|
|
114
|
+
| `--scope-manifest <file>` | Read scope rules from a manifest file |
|
|
115
|
+
| `--repo-alias <name>` | Override indexed repository name |
|
|
116
|
+
| `--csharp-define-csproj <path>` | Load C# `DefineConstants` from `.csproj` for `#if` normalization |
|
|
117
|
+
|
|
118
|
+
**C# preprocessing (Unity):** For projects with heavy conditional compilation, add `--csharp-define-csproj /path/to/Assembly-CSharp.csproj` (neonspark: `/Volumes/Shuttle/projects/neonspark/Assembly-CSharp.csproj`). Without it, C# files are parsed raw and tree-sitter may mishandle `#if` branches.
|
|
119
|
+
|
|
120
|
+
#### Rebuild recovery — when analyze hangs or crashes
|
|
121
|
+
|
|
122
|
+
If `analyze --force` hangs (no progress after 5+ minutes) or crashes leaving a corrupted index:
|
|
86
123
|
|
|
87
124
|
```bash
|
|
88
|
-
|
|
89
|
-
$GN
|
|
125
|
+
# 1. Clean the corrupted index (preserves sync-manifest.txt)
|
|
126
|
+
$GN clean --force
|
|
127
|
+
|
|
128
|
+
# 2. Rebuild
|
|
129
|
+
$GN analyze --force
|
|
90
130
|
```
|
|
91
131
|
|
|
132
|
+
**When to clean before rebuild:**
|
|
133
|
+
- Previous run left `.gitnexus/csv/` with no `relations.csv` (crash mid-streaming)
|
|
134
|
+
- `.gitnexus/lbug.wal` exists but `lbug` is tiny (LadybugDB in unrecovered state)
|
|
135
|
+
- Any `analyze` run hangs indefinitely in the "Loading into LadybugDB..." phase
|
|
136
|
+
- After `gitnexus clean`, always run `analyze --force` to rebuild
|
|
137
|
+
|
|
138
|
+
**When to run:** First time in a project, after major code changes, or when `gitnexus://repo/{name}/context` reports the index is stale.
|
|
139
|
+
|
|
92
140
|
### status — Check index freshness
|
|
93
141
|
|
|
94
142
|
```bash
|
|
@@ -97,13 +145,13 @@ $GN status
|
|
|
97
145
|
|
|
98
146
|
Shows whether the current repo has a GitNexus index, when it was last updated, and symbol/relationship counts. Use this to check if re-indexing is needed.
|
|
99
147
|
|
|
100
|
-
### clean — Delete the index
|
|
148
|
+
### clean — Delete the index (preserves config)
|
|
101
149
|
|
|
102
150
|
```bash
|
|
103
|
-
$GN clean
|
|
151
|
+
$GN clean --force
|
|
104
152
|
```
|
|
105
153
|
|
|
106
|
-
|
|
154
|
+
Removes the GitNexus index (graph, CSVs, LadybugDB) from `.gitnexus/` while **preserving `sync-manifest.txt`** and other configuration files. Use this to recover from a corrupted index before re-indexing.
|
|
107
155
|
|
|
108
156
|
| Flag | Effect |
|
|
109
157
|
| --------- | ------------------------------------------------- |
|
|
@@ -189,6 +237,7 @@ $GN unity-ui-trace "Assets/NEON/VeewoUI/Uxml/BarScreen/Patch/PatchItemPreview.ux
|
|
|
189
237
|
- **"Not inside a git repository"**: Run from a directory inside a git repo
|
|
190
238
|
- **Index is stale after re-analyzing**: Restart Claude Code to reload the MCP server
|
|
191
239
|
- **Embeddings slow**: Omit `--embeddings` (it's off by default) or set `OPENAI_API_KEY` for faster API-based embedding
|
|
240
|
+
- **`analyze --force` hangs or crashes**: Run `$GN clean --force` to remove the corrupted index (sync-manifest is preserved), then `$GN analyze --force` to rebuild. Common corruption signatures: `.gitnexus/csv/` exists but `relations.csv` is missing; `.gitnexus/lbug.wal` exists while `lbug` is only a few KB.
|
|
192
241
|
|
|
193
242
|
## Runtime-Chain Closure Guard
|
|
194
243
|
|