neuronlayer 0.1.7 → 0.1.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.
Potentially problematic release.
This version of neuronlayer might be problematic. Click here for more details.
- package/README.md +101 -216
- package/dist/index.js +1031 -171
- package/package.json +2 -2
- package/src/cli/commands.ts +88 -27
- package/src/core/adr-exporter.ts +4 -4
- package/src/core/engine.ts +60 -10
- package/src/core/ghost-mode.ts +54 -1
- package/src/core/project-manager.ts +7 -3
- package/src/core/refresh/index.ts +1 -1
- package/src/index.ts +44 -3
- package/src/indexing/ast.ts +356 -51
- package/src/indexing/indexer.ts +28 -3
- package/src/server/gateways/index.ts +473 -473
- package/src/server/gateways/memory-ghost.ts +343 -343
- package/src/server/gateways/memory-query.ts +452 -452
- package/src/server/gateways/memory-record.ts +346 -346
- package/src/server/gateways/memory-review.ts +410 -410
- package/src/server/gateways/memory-status.ts +517 -517
- package/src/server/gateways/memory-verify.ts +392 -392
- package/src/server/http.ts +228 -0
- package/src/server/mcp.ts +7 -7
- package/src/server/resources.ts +85 -85
- package/src/server/tools.ts +2460 -2331
- package/src/storage/tier2.ts +212 -4
- package/src/types/index.ts +2 -2
- package/src/utils/config.ts +5 -4
- package/real-benchmark.mjs +0 -322
package/src/indexing/ast.ts
CHANGED
|
@@ -80,6 +80,30 @@ const LANGUAGE_CONFIGS: Record<string, LanguageConfig> = {
|
|
|
80
80
|
(import_from_statement) @import
|
|
81
81
|
`
|
|
82
82
|
}
|
|
83
|
+
},
|
|
84
|
+
go: {
|
|
85
|
+
wasmFile: 'tree-sitter-go.wasm',
|
|
86
|
+
extensions: ['.go'],
|
|
87
|
+
queries: {
|
|
88
|
+
functions: `(function_declaration name: (identifier) @name) @func`,
|
|
89
|
+
classes: `(type_declaration (type_spec name: (type_identifier) @name type: (struct_type))) @class`
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
rust: {
|
|
93
|
+
wasmFile: 'tree-sitter-rust.wasm',
|
|
94
|
+
extensions: ['.rs'],
|
|
95
|
+
queries: {
|
|
96
|
+
functions: `(function_item name: (identifier) @name) @func`,
|
|
97
|
+
classes: `(struct_item name: (type_identifier) @name) @class`
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
java: {
|
|
101
|
+
wasmFile: 'tree-sitter-java.wasm',
|
|
102
|
+
extensions: ['.java'],
|
|
103
|
+
queries: {
|
|
104
|
+
functions: `(method_declaration name: (identifier) @name) @func`,
|
|
105
|
+
classes: `(class_declaration name: (identifier) @name) @class`
|
|
106
|
+
}
|
|
83
107
|
}
|
|
84
108
|
};
|
|
85
109
|
|
|
@@ -95,41 +119,15 @@ export class ASTParser {
|
|
|
95
119
|
|
|
96
120
|
async initialize(): Promise<void> {
|
|
97
121
|
if (this.initialized) return;
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
this.parser = new Parser();
|
|
102
|
-
this.initialized = true;
|
|
103
|
-
console.error('AST Parser initialized');
|
|
104
|
-
} catch (error) {
|
|
105
|
-
console.error('Failed to initialize AST parser:', error);
|
|
106
|
-
throw error;
|
|
107
|
-
}
|
|
122
|
+
// Using regex-based parsing for reliable cross-platform support
|
|
123
|
+
// Tree-sitter WASM support reserved for future enhancement
|
|
124
|
+
this.initialized = true;
|
|
108
125
|
}
|
|
109
126
|
|
|
110
|
-
private async loadLanguage(
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
const config = LANGUAGE_CONFIGS[langName];
|
|
116
|
-
if (!config) {
|
|
117
|
-
return null;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
try {
|
|
121
|
-
// Try to load from node_modules or bundled location
|
|
122
|
-
const wasmDir = join(this.dataDir, 'wasm');
|
|
123
|
-
const wasmPath = join(wasmDir, config.wasmFile);
|
|
124
|
-
|
|
125
|
-
// For now, we'll use a simplified approach without external WASM files
|
|
126
|
-
// In production, you'd download these from tree-sitter releases
|
|
127
|
-
console.error(`Language ${langName} WASM not available yet`);
|
|
128
|
-
return null;
|
|
129
|
-
} catch (error) {
|
|
130
|
-
console.error(`Failed to load language ${langName}:`, error);
|
|
131
|
-
return null;
|
|
132
|
-
}
|
|
127
|
+
private async loadLanguage(_langName: string): Promise<Parser.Language | null> {
|
|
128
|
+
// Tree-sitter WASM loading not implemented - using regex fallback
|
|
129
|
+
// Language configs are retained for future tree-sitter support
|
|
130
|
+
return null;
|
|
133
131
|
}
|
|
134
132
|
|
|
135
133
|
getLanguageForFile(filePath: string): string | null {
|
|
@@ -152,11 +150,13 @@ export class ASTParser {
|
|
|
152
150
|
await this.initialize();
|
|
153
151
|
}
|
|
154
152
|
|
|
155
|
-
//
|
|
153
|
+
// Regex-based parsing - reliable cross-platform symbol extraction
|
|
156
154
|
return this.parseWithRegex(filePath, content);
|
|
157
155
|
}
|
|
158
156
|
|
|
159
|
-
// Regex-based parsing
|
|
157
|
+
// Regex-based parsing for symbol extraction
|
|
158
|
+
// Handles: functions, classes, interfaces, types, imports, exports
|
|
159
|
+
// Supports: TypeScript, JavaScript, Python, Go, Rust, Java
|
|
160
160
|
private parseWithRegex(filePath: string, content: string): {
|
|
161
161
|
symbols: CodeSymbol[];
|
|
162
162
|
imports: Import[];
|
|
@@ -172,6 +172,12 @@ export class ASTParser {
|
|
|
172
172
|
this.parseTypeScriptJS(filePath, content, lines, symbols, imports, exports);
|
|
173
173
|
} else if (lang === 'python') {
|
|
174
174
|
this.parsePython(filePath, content, lines, symbols, imports, exports);
|
|
175
|
+
} else if (lang === 'go') {
|
|
176
|
+
this.parseGo(filePath, content, lines, symbols, imports, exports);
|
|
177
|
+
} else if (lang === 'rust') {
|
|
178
|
+
this.parseRust(filePath, content, lines, symbols, imports, exports);
|
|
179
|
+
} else if (lang === 'java') {
|
|
180
|
+
this.parseJava(filePath, content, lines, symbols, imports, exports);
|
|
175
181
|
}
|
|
176
182
|
|
|
177
183
|
return { symbols, imports, exports };
|
|
@@ -187,37 +193,60 @@ export class ASTParser {
|
|
|
187
193
|
): void {
|
|
188
194
|
// Patterns for TypeScript/JavaScript
|
|
189
195
|
const patterns = {
|
|
190
|
-
// Functions: function name(),
|
|
191
|
-
function: /^(?:export\s+)?(?:async\s+)?function\s+(\w+)/,
|
|
192
|
-
|
|
196
|
+
// Functions: function name(), export default function name(), const name = () =>
|
|
197
|
+
function: /^(?:export\s+)?(?:default\s+)?(?:async\s+)?function\s+(\w+)/,
|
|
198
|
+
// Arrow functions: handles type annotations and destructured params
|
|
199
|
+
arrowFunc: /^(?:export\s+)?(?:const|let|var)\s+(\w+)\s*(?::\s*[^=]+)?\s*=\s*(?:async\s+)?(?:\([^)]*\)|[a-zA-Z_]\w*)\s*(?::\s*[^=]+)?\s*=>/,
|
|
193
200
|
// Classes
|
|
194
|
-
class: /^(?:export\s+)?(?:abstract\s+)?class\s+(\w+)/,
|
|
201
|
+
class: /^(?:export\s+)?(?:default\s+)?(?:abstract\s+)?class\s+(\w+)/,
|
|
195
202
|
// Interfaces (TS only)
|
|
196
203
|
interface: /^(?:export\s+)?interface\s+(\w+)/,
|
|
197
204
|
// Types (TS only)
|
|
198
|
-
type: /^(?:export\s+)?type\s+(\w+)\s*=/,
|
|
199
|
-
// Imports
|
|
200
|
-
import: /^import\s+(?:(\w+)(?:\s*,\s*)?)?(?:\{([^}]+)\})?\s*from\s*['"]([^'"]+)['"]/,
|
|
201
|
-
importAll: /^import\s
|
|
205
|
+
type: /^(?:export\s+)?type\s+(\w+)\s*(?:<[^>]*>)?\s*=/,
|
|
206
|
+
// Imports - supports 'import type'
|
|
207
|
+
import: /^import\s+(?:type\s+)?(?:(\w+)(?:\s*,\s*)?)?(?:\{([^}]+)\})?\s*from\s*['"]([^'"]+)['"]/,
|
|
208
|
+
importAll: /^import\s+(?:type\s+)?\*\s+as\s+(\w+)\s+from\s*['"]([^'"]+)['"]/,
|
|
202
209
|
importSideEffect: /^import\s*['"]([^'"]+)['"]/,
|
|
203
210
|
// Exports
|
|
204
|
-
exportNamed: /^export\s
|
|
211
|
+
exportNamed: /^export\s+(?:type\s+)?\{([^}]+)\}/,
|
|
205
212
|
exportDefault: /^export\s+default\s+(?:class|function|const|let|var)?\s*(\w+)?/,
|
|
206
213
|
exportDirect: /^export\s+(?:const|let|var|function|class|interface|type|enum|async\s+function)\s+(\w+)/,
|
|
207
214
|
// Enums (TS)
|
|
208
215
|
enum: /^(?:export\s+)?(?:const\s+)?enum\s+(\w+)/,
|
|
209
|
-
// Methods inside classes
|
|
210
|
-
method: /^\s+(?:async\s+)?(?:static\s+)?(?:private\s+|public\s+|protected\s+)?(\w+)\s
|
|
216
|
+
// Methods inside classes - handles generics, readonly, and all modifiers
|
|
217
|
+
method: /^\s+(?:async\s+)?(?:static\s+)?(?:readonly\s+)?(?:private\s+|public\s+|protected\s+)?(?:get\s+|set\s+)?(\w+)\s*(?:<[^>]+>)?\s*\(/,
|
|
211
218
|
};
|
|
212
219
|
|
|
213
220
|
let currentClass: { name: string; startLine: number } | null = null;
|
|
214
221
|
let braceDepth = 0;
|
|
222
|
+
let inBlockComment = false;
|
|
215
223
|
|
|
216
224
|
for (let i = 0; i < lines.length; i++) {
|
|
217
225
|
const line = lines[i] || '';
|
|
218
226
|
const trimmed = line.trim();
|
|
219
227
|
const lineNum = i + 1;
|
|
220
228
|
|
|
229
|
+
// Track block comments properly
|
|
230
|
+
if (inBlockComment) {
|
|
231
|
+
if (trimmed.includes('*/')) {
|
|
232
|
+
inBlockComment = false;
|
|
233
|
+
}
|
|
234
|
+
continue;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// Skip single-line comments
|
|
238
|
+
if (trimmed.startsWith('//')) {
|
|
239
|
+
continue;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// Handle block comment start
|
|
243
|
+
if (trimmed.startsWith('/*')) {
|
|
244
|
+
if (!trimmed.includes('*/')) {
|
|
245
|
+
inBlockComment = true;
|
|
246
|
+
}
|
|
247
|
+
continue;
|
|
248
|
+
}
|
|
249
|
+
|
|
221
250
|
// Track brace depth for class scope
|
|
222
251
|
braceDepth += (line.match(/\{/g) || []).length;
|
|
223
252
|
braceDepth -= (line.match(/\}/g) || []).length;
|
|
@@ -231,11 +260,6 @@ export class ASTParser {
|
|
|
231
260
|
currentClass = null;
|
|
232
261
|
}
|
|
233
262
|
|
|
234
|
-
// Skip comments
|
|
235
|
-
if (trimmed.startsWith('//') || trimmed.startsWith('/*') || trimmed.startsWith('*')) {
|
|
236
|
-
continue;
|
|
237
|
-
}
|
|
238
|
-
|
|
239
263
|
// Functions
|
|
240
264
|
let match = trimmed.match(patterns.function);
|
|
241
265
|
if (match && match[1]) {
|
|
@@ -511,6 +535,287 @@ export class ASTParser {
|
|
|
511
535
|
}
|
|
512
536
|
}
|
|
513
537
|
|
|
538
|
+
private parseGo(
|
|
539
|
+
filePath: string,
|
|
540
|
+
content: string,
|
|
541
|
+
lines: string[],
|
|
542
|
+
symbols: CodeSymbol[],
|
|
543
|
+
imports: Import[],
|
|
544
|
+
exports: Export[]
|
|
545
|
+
): void {
|
|
546
|
+
let inImportBlock = false;
|
|
547
|
+
|
|
548
|
+
for (let i = 0; i < lines.length; i++) {
|
|
549
|
+
const line = lines[i] || '';
|
|
550
|
+
const trimmed = line.trim();
|
|
551
|
+
const lineNum = i + 1;
|
|
552
|
+
|
|
553
|
+
// Skip comments
|
|
554
|
+
if (trimmed.startsWith('//')) continue;
|
|
555
|
+
|
|
556
|
+
// Handle import blocks
|
|
557
|
+
if (trimmed === 'import (') {
|
|
558
|
+
inImportBlock = true;
|
|
559
|
+
continue;
|
|
560
|
+
}
|
|
561
|
+
if (inImportBlock && trimmed === ')') {
|
|
562
|
+
inImportBlock = false;
|
|
563
|
+
continue;
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
// Single import or import block item
|
|
567
|
+
const importMatch = inImportBlock
|
|
568
|
+
? trimmed.match(/^(?:(\w+)\s+)?"([^"]+)"/)
|
|
569
|
+
: trimmed.match(/^import\s+(?:(\w+)\s+)?"([^"]+)"/);
|
|
570
|
+
if (importMatch) {
|
|
571
|
+
const alias = importMatch[1];
|
|
572
|
+
const path = importMatch[2] || '';
|
|
573
|
+
const pkg = alias || path.split('/').pop() || '';
|
|
574
|
+
imports.push({
|
|
575
|
+
fileId: 0,
|
|
576
|
+
filePath,
|
|
577
|
+
importedFrom: path,
|
|
578
|
+
importedSymbols: [pkg],
|
|
579
|
+
isDefault: false,
|
|
580
|
+
isNamespace: false,
|
|
581
|
+
lineNumber: lineNum
|
|
582
|
+
});
|
|
583
|
+
continue;
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
// Functions and methods
|
|
587
|
+
const funcMatch = trimmed.match(/^func\s+(?:\((\w+)\s+\*?(\w+)\)\s+)?(\w+)\s*\(/);
|
|
588
|
+
if (funcMatch) {
|
|
589
|
+
const receiver = funcMatch[2];
|
|
590
|
+
const name = receiver ? `${receiver}.${funcMatch[3]}` : (funcMatch[3] || '');
|
|
591
|
+
symbols.push({
|
|
592
|
+
fileId: 0,
|
|
593
|
+
filePath,
|
|
594
|
+
kind: receiver ? 'method' : 'function',
|
|
595
|
+
name,
|
|
596
|
+
lineStart: lineNum,
|
|
597
|
+
lineEnd: this.findBlockEnd(lines, i),
|
|
598
|
+
exported: /^[A-Z]/.test(funcMatch[3] || ''),
|
|
599
|
+
signature: trimmed.split('{')[0]?.trim()
|
|
600
|
+
});
|
|
601
|
+
continue;
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
// Structs
|
|
605
|
+
const structMatch = trimmed.match(/^type\s+(\w+)\s+struct\s*\{?/);
|
|
606
|
+
if (structMatch) {
|
|
607
|
+
symbols.push({
|
|
608
|
+
fileId: 0,
|
|
609
|
+
filePath,
|
|
610
|
+
kind: 'class',
|
|
611
|
+
name: structMatch[1] || '',
|
|
612
|
+
lineStart: lineNum,
|
|
613
|
+
lineEnd: this.findBlockEnd(lines, i),
|
|
614
|
+
exported: /^[A-Z]/.test(structMatch[1] || '')
|
|
615
|
+
});
|
|
616
|
+
continue;
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
// Interfaces
|
|
620
|
+
const ifaceMatch = trimmed.match(/^type\s+(\w+)\s+interface\s*\{?/);
|
|
621
|
+
if (ifaceMatch) {
|
|
622
|
+
symbols.push({
|
|
623
|
+
fileId: 0,
|
|
624
|
+
filePath,
|
|
625
|
+
kind: 'interface',
|
|
626
|
+
name: ifaceMatch[1] || '',
|
|
627
|
+
lineStart: lineNum,
|
|
628
|
+
lineEnd: this.findBlockEnd(lines, i),
|
|
629
|
+
exported: /^[A-Z]/.test(ifaceMatch[1] || '')
|
|
630
|
+
});
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
private parseRust(
|
|
636
|
+
filePath: string,
|
|
637
|
+
content: string,
|
|
638
|
+
lines: string[],
|
|
639
|
+
symbols: CodeSymbol[],
|
|
640
|
+
imports: Import[],
|
|
641
|
+
exports: Export[]
|
|
642
|
+
): void {
|
|
643
|
+
for (let i = 0; i < lines.length; i++) {
|
|
644
|
+
const line = lines[i] || '';
|
|
645
|
+
const trimmed = line.trim();
|
|
646
|
+
const lineNum = i + 1;
|
|
647
|
+
|
|
648
|
+
// Skip comments
|
|
649
|
+
if (trimmed.startsWith('//')) continue;
|
|
650
|
+
|
|
651
|
+
// Functions
|
|
652
|
+
const fnMatch = trimmed.match(/^(?:pub\s+)?(?:async\s+)?fn\s+(\w+)/);
|
|
653
|
+
if (fnMatch) {
|
|
654
|
+
symbols.push({
|
|
655
|
+
fileId: 0,
|
|
656
|
+
filePath,
|
|
657
|
+
kind: 'function',
|
|
658
|
+
name: fnMatch[1] || '',
|
|
659
|
+
lineStart: lineNum,
|
|
660
|
+
lineEnd: this.findBlockEnd(lines, i),
|
|
661
|
+
exported: trimmed.startsWith('pub'),
|
|
662
|
+
signature: trimmed.split('{')[0]?.trim()
|
|
663
|
+
});
|
|
664
|
+
continue;
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
// Structs
|
|
668
|
+
const structMatch = trimmed.match(/^(?:pub\s+)?struct\s+(\w+)/);
|
|
669
|
+
if (structMatch) {
|
|
670
|
+
symbols.push({
|
|
671
|
+
fileId: 0,
|
|
672
|
+
filePath,
|
|
673
|
+
kind: 'class',
|
|
674
|
+
name: structMatch[1] || '',
|
|
675
|
+
lineStart: lineNum,
|
|
676
|
+
lineEnd: this.findBlockEnd(lines, i),
|
|
677
|
+
exported: trimmed.startsWith('pub')
|
|
678
|
+
});
|
|
679
|
+
continue;
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
// Enums
|
|
683
|
+
const enumMatch = trimmed.match(/^(?:pub\s+)?enum\s+(\w+)/);
|
|
684
|
+
if (enumMatch) {
|
|
685
|
+
symbols.push({
|
|
686
|
+
fileId: 0,
|
|
687
|
+
filePath,
|
|
688
|
+
kind: 'enum',
|
|
689
|
+
name: enumMatch[1] || '',
|
|
690
|
+
lineStart: lineNum,
|
|
691
|
+
lineEnd: this.findBlockEnd(lines, i),
|
|
692
|
+
exported: trimmed.startsWith('pub')
|
|
693
|
+
});
|
|
694
|
+
continue;
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
// Traits (similar to interfaces)
|
|
698
|
+
const traitMatch = trimmed.match(/^(?:pub\s+)?trait\s+(\w+)/);
|
|
699
|
+
if (traitMatch) {
|
|
700
|
+
symbols.push({
|
|
701
|
+
fileId: 0,
|
|
702
|
+
filePath,
|
|
703
|
+
kind: 'interface',
|
|
704
|
+
name: traitMatch[1] || '',
|
|
705
|
+
lineStart: lineNum,
|
|
706
|
+
lineEnd: this.findBlockEnd(lines, i),
|
|
707
|
+
exported: trimmed.startsWith('pub')
|
|
708
|
+
});
|
|
709
|
+
continue;
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
// Impl blocks
|
|
713
|
+
const implMatch = trimmed.match(/^impl\s+(?:<[^>]+>\s+)?(?:(\w+)\s+for\s+)?(\w+)/);
|
|
714
|
+
if (implMatch) {
|
|
715
|
+
const traitName = implMatch[1];
|
|
716
|
+
const typeName = implMatch[2] || '';
|
|
717
|
+
const name = traitName ? `${traitName} for ${typeName}` : typeName;
|
|
718
|
+
symbols.push({
|
|
719
|
+
fileId: 0,
|
|
720
|
+
filePath,
|
|
721
|
+
kind: 'class',
|
|
722
|
+
name: `impl ${name}`,
|
|
723
|
+
lineStart: lineNum,
|
|
724
|
+
lineEnd: this.findBlockEnd(lines, i),
|
|
725
|
+
exported: false
|
|
726
|
+
});
|
|
727
|
+
continue;
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
// use statements
|
|
731
|
+
const useMatch = trimmed.match(/^(?:pub\s+)?use\s+(.+);/);
|
|
732
|
+
if (useMatch) {
|
|
733
|
+
const path = (useMatch[1] || '').replace(/::/g, '/');
|
|
734
|
+
imports.push({
|
|
735
|
+
fileId: 0,
|
|
736
|
+
filePath,
|
|
737
|
+
importedFrom: path,
|
|
738
|
+
importedSymbols: [path.split('/').pop()?.replace(/[{}]/g, '') || ''],
|
|
739
|
+
isDefault: false,
|
|
740
|
+
isNamespace: path.includes('*'),
|
|
741
|
+
lineNumber: lineNum
|
|
742
|
+
});
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
private parseJava(
|
|
748
|
+
filePath: string,
|
|
749
|
+
content: string,
|
|
750
|
+
lines: string[],
|
|
751
|
+
symbols: CodeSymbol[],
|
|
752
|
+
imports: Import[],
|
|
753
|
+
exports: Export[]
|
|
754
|
+
): void {
|
|
755
|
+
let currentClass: string | null = null;
|
|
756
|
+
|
|
757
|
+
for (let i = 0; i < lines.length; i++) {
|
|
758
|
+
const line = lines[i] || '';
|
|
759
|
+
const trimmed = line.trim();
|
|
760
|
+
const lineNum = i + 1;
|
|
761
|
+
|
|
762
|
+
// Skip comments
|
|
763
|
+
if (trimmed.startsWith('//') || trimmed.startsWith('*') || trimmed.startsWith('/*')) continue;
|
|
764
|
+
|
|
765
|
+
// Imports
|
|
766
|
+
const importMatch = trimmed.match(/^import\s+(?:static\s+)?([^;]+);/);
|
|
767
|
+
if (importMatch) {
|
|
768
|
+
const path = importMatch[1] || '';
|
|
769
|
+
imports.push({
|
|
770
|
+
fileId: 0,
|
|
771
|
+
filePath,
|
|
772
|
+
importedFrom: path,
|
|
773
|
+
importedSymbols: [path.split('.').pop() || ''],
|
|
774
|
+
isDefault: false,
|
|
775
|
+
isNamespace: path.endsWith('*'),
|
|
776
|
+
lineNumber: lineNum
|
|
777
|
+
});
|
|
778
|
+
continue;
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
// Classes and interfaces
|
|
782
|
+
const classMatch = trimmed.match(/^(?:public\s+|private\s+|protected\s+)?(?:abstract\s+)?(?:final\s+)?(class|interface|enum)\s+(\w+)/);
|
|
783
|
+
if (classMatch) {
|
|
784
|
+
currentClass = classMatch[2] || '';
|
|
785
|
+
symbols.push({
|
|
786
|
+
fileId: 0,
|
|
787
|
+
filePath,
|
|
788
|
+
kind: classMatch[1] === 'interface' ? 'interface' : classMatch[1] === 'enum' ? 'enum' : 'class',
|
|
789
|
+
name: currentClass,
|
|
790
|
+
lineStart: lineNum,
|
|
791
|
+
lineEnd: this.findBlockEnd(lines, i),
|
|
792
|
+
exported: trimmed.includes('public')
|
|
793
|
+
});
|
|
794
|
+
continue;
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
// Methods
|
|
798
|
+
const methodMatch = trimmed.match(/^(?:public\s+|private\s+|protected\s+)?(?:static\s+)?(?:final\s+)?(?:synchronized\s+)?(?:abstract\s+)?(?:<[^>]+>\s+)?(\w+(?:<[^>]+>)?)\s+(\w+)\s*\(/);
|
|
799
|
+
if (methodMatch && currentClass && !['if', 'for', 'while', 'switch', 'catch', 'class', 'interface', 'enum'].includes(methodMatch[2] || '')) {
|
|
800
|
+
const returnType = methodMatch[1];
|
|
801
|
+
const methodName = methodMatch[2] || '';
|
|
802
|
+
// Skip constructors (name matches class name)
|
|
803
|
+
if (methodName !== currentClass) {
|
|
804
|
+
symbols.push({
|
|
805
|
+
fileId: 0,
|
|
806
|
+
filePath,
|
|
807
|
+
kind: 'method',
|
|
808
|
+
name: `${currentClass}.${methodName}`,
|
|
809
|
+
lineStart: lineNum,
|
|
810
|
+
lineEnd: this.findBlockEnd(lines, i),
|
|
811
|
+
exported: trimmed.includes('public'),
|
|
812
|
+
signature: `${returnType} ${methodName}(...)`
|
|
813
|
+
});
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
|
|
514
819
|
private findBlockEnd(lines: string[], startIndex: number): number {
|
|
515
820
|
let braceCount = 0;
|
|
516
821
|
let started = false;
|
package/src/indexing/indexer.ts
CHANGED
|
@@ -7,10 +7,10 @@ import { ASTParser } from './ast.js';
|
|
|
7
7
|
import { FileWatcher, type FileEvent } from './watcher.js';
|
|
8
8
|
import { Tier2Storage } from '../storage/tier2.js';
|
|
9
9
|
import { isCodeFile, detectLanguage, hashContent, getPreview, countLines } from '../utils/files.js';
|
|
10
|
-
import type {
|
|
10
|
+
import type { NeuronLayerConfig, IndexingProgress } from '../types/index.js';
|
|
11
11
|
|
|
12
12
|
export class Indexer extends EventEmitter {
|
|
13
|
-
private config:
|
|
13
|
+
private config: NeuronLayerConfig;
|
|
14
14
|
private embeddingGenerator: EmbeddingGenerator;
|
|
15
15
|
private astParser: ASTParser;
|
|
16
16
|
private watcher: FileWatcher;
|
|
@@ -19,7 +19,7 @@ export class Indexer extends EventEmitter {
|
|
|
19
19
|
private pendingFiles: Set<string> = new Set();
|
|
20
20
|
private processTimeout: NodeJS.Timeout | null = null;
|
|
21
21
|
|
|
22
|
-
constructor(config:
|
|
22
|
+
constructor(config: NeuronLayerConfig, tier2: Tier2Storage) {
|
|
23
23
|
super();
|
|
24
24
|
this.config = config;
|
|
25
25
|
this.tier2 = tier2;
|
|
@@ -149,6 +149,17 @@ export class Indexer extends EventEmitter {
|
|
|
149
149
|
const exportsWithFileId = parsed.exports.map(e => ({ ...e, fileId }));
|
|
150
150
|
this.tier2.insertExports(exportsWithFileId);
|
|
151
151
|
}
|
|
152
|
+
|
|
153
|
+
// Build dependency edges from imports
|
|
154
|
+
if (parsed.imports.length > 0) {
|
|
155
|
+
this.tier2.clearDependencies(fileId);
|
|
156
|
+
for (const imp of parsed.imports) {
|
|
157
|
+
const targetFile = this.tier2.resolveImportToFile(relativePath, imp.importedFrom);
|
|
158
|
+
if (targetFile) {
|
|
159
|
+
this.tier2.addDependency(fileId, targetFile.id, 'imports');
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
152
163
|
}
|
|
153
164
|
} catch (astError) {
|
|
154
165
|
// AST parsing is optional, don't fail the whole index
|
|
@@ -156,6 +167,20 @@ export class Indexer extends EventEmitter {
|
|
|
156
167
|
}
|
|
157
168
|
|
|
158
169
|
this.emit('fileIndexed', relativePath);
|
|
170
|
+
|
|
171
|
+
// Emit impact warning for changed files (not during initial indexing)
|
|
172
|
+
if (!this.isIndexing) {
|
|
173
|
+
const dependents = this.tier2.getFileDependents(relativePath);
|
|
174
|
+
if (dependents.length > 0) {
|
|
175
|
+
this.emit('fileImpact', {
|
|
176
|
+
file: relativePath,
|
|
177
|
+
affectedFiles: dependents.map(d => d.file),
|
|
178
|
+
affectedCount: dependents.length,
|
|
179
|
+
imports: dependents.map(d => ({ file: d.file, symbols: d.imports }))
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
159
184
|
return true; // Actually indexed
|
|
160
185
|
} catch (error) {
|
|
161
186
|
console.error(`Error indexing ${absolutePath}:`, error);
|