edhindex 1.0.2 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +43 -9
- package/dist/cli/commands/index_cmd.d.ts.map +1 -1
- package/dist/cli/commands/index_cmd.js +2 -0
- package/dist/cli/commands/index_cmd.js.map +1 -1
- package/dist/cli/commands/start.d.ts.map +1 -1
- package/dist/cli/commands/start.js +3 -1
- package/dist/cli/commands/start.js.map +1 -1
- package/dist/config/index.js +1 -1
- package/dist/config/index.js.map +1 -1
- package/dist/graph/builder.d.ts.map +1 -1
- package/dist/graph/builder.js +5 -0
- package/dist/graph/builder.js.map +1 -1
- package/dist/indexer/chunker.d.ts +2 -2
- package/dist/indexer/chunker.d.ts.map +1 -1
- package/dist/indexer/chunker.js +7 -5
- package/dist/indexer/chunker.js.map +1 -1
- package/dist/indexer/file-utils.d.ts +2 -3
- package/dist/indexer/file-utils.d.ts.map +1 -1
- package/dist/indexer/file-utils.js +4 -33
- package/dist/indexer/file-utils.js.map +1 -1
- package/dist/indexer/fixture.test.d.ts +2 -0
- package/dist/indexer/fixture.test.d.ts.map +1 -0
- package/dist/indexer/fixture.test.js +104 -0
- package/dist/indexer/fixture.test.js.map +1 -0
- package/dist/indexer/indexer.d.ts +3 -1
- package/dist/indexer/indexer.d.ts.map +1 -1
- package/dist/indexer/indexer.js +8 -32
- package/dist/indexer/indexer.js.map +1 -1
- package/dist/indexer/parser.d.ts.map +1 -1
- package/dist/indexer/parser.js +196 -14
- package/dist/indexer/parser.js.map +1 -1
- package/dist/indexer/parser.test.d.ts +2 -0
- package/dist/indexer/parser.test.d.ts.map +1 -0
- package/dist/indexer/parser.test.js +120 -0
- package/dist/indexer/parser.test.js.map +1 -0
- package/dist/language/adapters/c.d.ts +7 -0
- package/dist/language/adapters/c.d.ts.map +1 -0
- package/dist/language/adapters/c.js +25 -0
- package/dist/language/adapters/c.js.map +1 -0
- package/dist/language/adapters/cpp.d.ts +7 -0
- package/dist/language/adapters/cpp.d.ts.map +1 -0
- package/dist/language/adapters/cpp.js +29 -0
- package/dist/language/adapters/cpp.js.map +1 -0
- package/dist/language/adapters/csharp.d.ts +7 -0
- package/dist/language/adapters/csharp.d.ts.map +1 -0
- package/dist/language/adapters/csharp.js +32 -0
- package/dist/language/adapters/csharp.js.map +1 -0
- package/dist/language/adapters/css.d.ts +10 -0
- package/dist/language/adapters/css.d.ts.map +1 -0
- package/dist/language/adapters/css.js +54 -0
- package/dist/language/adapters/css.js.map +1 -0
- package/dist/language/adapters/go.d.ts +7 -0
- package/dist/language/adapters/go.d.ts.map +1 -0
- package/dist/language/adapters/go.js +26 -0
- package/dist/language/adapters/go.js.map +1 -0
- package/dist/language/adapters/html.d.ts +11 -0
- package/dist/language/adapters/html.d.ts.map +1 -0
- package/dist/language/adapters/html.js +67 -0
- package/dist/language/adapters/html.js.map +1 -0
- package/dist/language/adapters/java.d.ts +7 -0
- package/dist/language/adapters/java.d.ts.map +1 -0
- package/dist/language/adapters/java.js +30 -0
- package/dist/language/adapters/java.js.map +1 -0
- package/dist/language/adapters/javascript.d.ts +7 -0
- package/dist/language/adapters/javascript.d.ts.map +1 -0
- package/dist/language/adapters/javascript.js +31 -0
- package/dist/language/adapters/javascript.js.map +1 -0
- package/dist/language/adapters/json.d.ts +11 -0
- package/dist/language/adapters/json.d.ts.map +1 -0
- package/dist/language/adapters/json.js +57 -0
- package/dist/language/adapters/json.js.map +1 -0
- package/dist/language/adapters/php.d.ts +7 -0
- package/dist/language/adapters/php.d.ts.map +1 -0
- package/dist/language/adapters/php.js +29 -0
- package/dist/language/adapters/php.js.map +1 -0
- package/dist/language/adapters/python.d.ts +7 -0
- package/dist/language/adapters/python.d.ts.map +1 -0
- package/dist/language/adapters/python.js +27 -0
- package/dist/language/adapters/python.js.map +1 -0
- package/dist/language/adapters/ruby.d.ts +7 -0
- package/dist/language/adapters/ruby.d.ts.map +1 -0
- package/dist/language/adapters/ruby.js +29 -0
- package/dist/language/adapters/ruby.js.map +1 -0
- package/dist/language/adapters/rust.d.ts +7 -0
- package/dist/language/adapters/rust.d.ts.map +1 -0
- package/dist/language/adapters/rust.js +31 -0
- package/dist/language/adapters/rust.js.map +1 -0
- package/dist/language/adapters/typescript.d.ts +7 -0
- package/dist/language/adapters/typescript.d.ts.map +1 -0
- package/dist/language/adapters/typescript.js +35 -0
- package/dist/language/adapters/typescript.js.map +1 -0
- package/dist/language/base.d.ts +34 -0
- package/dist/language/base.d.ts.map +1 -0
- package/dist/language/base.js +144 -0
- package/dist/language/base.js.map +1 -0
- package/dist/language/generic.d.ts +11 -0
- package/dist/language/generic.d.ts.map +1 -0
- package/dist/language/generic.js +51 -0
- package/dist/language/generic.js.map +1 -0
- package/dist/language/registry.d.ts +29 -0
- package/dist/language/registry.d.ts.map +1 -0
- package/dist/language/registry.js +111 -0
- package/dist/language/registry.js.map +1 -0
- package/dist/language/types.d.ts +55 -0
- package/dist/language/types.d.ts.map +1 -0
- package/dist/language/types.js +23 -0
- package/dist/language/types.js.map +1 -0
- package/package.json +19 -3
- package/src/cli/commands/index_cmd.ts +2 -0
- package/src/cli/commands/start.ts +3 -1
- package/src/config/index.ts +1 -1
- package/src/graph/builder.ts +5 -0
- package/src/indexer/chunker.ts +9 -7
- package/src/indexer/file-utils.ts +6 -38
- package/src/indexer/fixture.test.ts +121 -0
- package/src/indexer/indexer.ts +17 -41
- package/src/indexer/parser.test.ts +127 -0
- package/src/indexer/parser.ts +194 -13
- package/src/language/adapters/c.ts +27 -0
- package/src/language/adapters/cpp.ts +31 -0
- package/src/language/adapters/csharp.ts +34 -0
- package/src/language/adapters/css.ts +57 -0
- package/src/language/adapters/go.ts +28 -0
- package/src/language/adapters/html.ts +68 -0
- package/src/language/adapters/java.ts +32 -0
- package/src/language/adapters/javascript.ts +33 -0
- package/src/language/adapters/json.ts +60 -0
- package/src/language/adapters/php.ts +31 -0
- package/src/language/adapters/python.ts +29 -0
- package/src/language/adapters/ruby.ts +31 -0
- package/src/language/adapters/rust.ts +33 -0
- package/src/language/adapters/typescript.ts +37 -0
- package/src/language/base.ts +167 -0
- package/src/language/generic.ts +58 -0
- package/src/language/registry.ts +126 -0
- package/src/language/types.ts +58 -0
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { TypeScriptAdapter } from './adapters/typescript.js';
|
|
2
|
+
import { JavaScriptAdapter } from './adapters/javascript.js';
|
|
3
|
+
import { PythonAdapter } from './adapters/python.js';
|
|
4
|
+
import { GoAdapter } from './adapters/go.js';
|
|
5
|
+
import { RustAdapter } from './adapters/rust.js';
|
|
6
|
+
import { JavaAdapter } from './adapters/java.js';
|
|
7
|
+
import { RubyAdapter } from './adapters/ruby.js';
|
|
8
|
+
import { CAdapter } from './adapters/c.js';
|
|
9
|
+
import { CppAdapter } from './adapters/cpp.js';
|
|
10
|
+
import { CSharpAdapter } from './adapters/csharp.js';
|
|
11
|
+
import { PhpAdapter } from './adapters/php.js';
|
|
12
|
+
import { HtmlAdapter } from './adapters/html.js';
|
|
13
|
+
import { CssAdapter } from './adapters/css.js';
|
|
14
|
+
import { JsonAdapter } from './adapters/json.js';
|
|
15
|
+
import { GenericAdapter } from './generic.js';
|
|
16
|
+
export class LanguageRegistry {
|
|
17
|
+
adapters = [];
|
|
18
|
+
extMap = new Map();
|
|
19
|
+
nameMap = new Map();
|
|
20
|
+
generic;
|
|
21
|
+
constructor() {
|
|
22
|
+
this.generic = new GenericAdapter();
|
|
23
|
+
this.registerDefaults();
|
|
24
|
+
}
|
|
25
|
+
registerDefaults() {
|
|
26
|
+
this.register('ts', new TypeScriptAdapter(), 1);
|
|
27
|
+
this.register('js', new JavaScriptAdapter(), 1);
|
|
28
|
+
this.register('py', new PythonAdapter(), 1);
|
|
29
|
+
this.register('go', new GoAdapter(), 1);
|
|
30
|
+
this.register('rs', new RustAdapter(), 1);
|
|
31
|
+
this.register('java', new JavaAdapter(), 1);
|
|
32
|
+
this.register('rb', new RubyAdapter(), 1);
|
|
33
|
+
this.register('c', new CAdapter(), 1);
|
|
34
|
+
this.register('cpp', new CppAdapter(), 1);
|
|
35
|
+
this.register('csharp', new CSharpAdapter(), 1);
|
|
36
|
+
this.register('php', new PhpAdapter(), 1);
|
|
37
|
+
this.register('html', new HtmlAdapter(), 2);
|
|
38
|
+
this.register('css', new CssAdapter(), 2);
|
|
39
|
+
this.register('json', new JsonAdapter(), 2);
|
|
40
|
+
}
|
|
41
|
+
register(key, adapter, tier) {
|
|
42
|
+
const entry = { key, adapter, tier };
|
|
43
|
+
this.adapters.push(entry);
|
|
44
|
+
for (const ext of adapter.fileExtensions()) {
|
|
45
|
+
const lower = ext.toLowerCase();
|
|
46
|
+
if (!this.extMap.has(lower)) {
|
|
47
|
+
this.extMap.set(lower, entry);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
for (const name of adapter.fileNames()) {
|
|
51
|
+
const lower = name.toLowerCase();
|
|
52
|
+
if (!this.nameMap.has(lower)) {
|
|
53
|
+
this.nameMap.set(lower, entry);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
getAdapter(filePath) {
|
|
58
|
+
const lower = filePath.toLowerCase();
|
|
59
|
+
const fileName = lower.split('/').pop() || lower;
|
|
60
|
+
const nameEntry = this.nameMap.get(fileName);
|
|
61
|
+
if (nameEntry)
|
|
62
|
+
return { adapter: nameEntry.adapter, key: nameEntry.key };
|
|
63
|
+
for (const [ext, entry] of this.extMap) {
|
|
64
|
+
if (lower.endsWith(ext))
|
|
65
|
+
return { adapter: entry.adapter, key: entry.key };
|
|
66
|
+
}
|
|
67
|
+
return { adapter: this.generic, key: 'generic' };
|
|
68
|
+
}
|
|
69
|
+
getAdapterByKey(key) {
|
|
70
|
+
const entry = this.adapters.find(a => a.key === key);
|
|
71
|
+
if (!entry)
|
|
72
|
+
return null;
|
|
73
|
+
return { adapter: entry.adapter, key: entry.key };
|
|
74
|
+
}
|
|
75
|
+
getAllAdapters() {
|
|
76
|
+
return [...this.adapters];
|
|
77
|
+
}
|
|
78
|
+
getInfoList() {
|
|
79
|
+
return this.adapters.map(a => ({
|
|
80
|
+
key: a.key,
|
|
81
|
+
name: a.adapter.languageName(),
|
|
82
|
+
extensions: a.adapter.fileExtensions(),
|
|
83
|
+
tier: a.tier,
|
|
84
|
+
supportsSymbols: a.adapter.supportsSymbols(),
|
|
85
|
+
}));
|
|
86
|
+
}
|
|
87
|
+
getExtensionsForKeys(keys) {
|
|
88
|
+
const exts = [];
|
|
89
|
+
for (const key of keys) {
|
|
90
|
+
const entry = this.adapters.find(a => a.key === key);
|
|
91
|
+
if (entry)
|
|
92
|
+
exts.push(...entry.adapter.fileExtensions());
|
|
93
|
+
}
|
|
94
|
+
return exts;
|
|
95
|
+
}
|
|
96
|
+
async initialize(keys) {
|
|
97
|
+
for (const key of keys) {
|
|
98
|
+
const entry = this.adapters.find(a => a.key === key);
|
|
99
|
+
if (entry && entry.adapter instanceof (await import('./base.js')).BaseTreeSitterAdapter) {
|
|
100
|
+
try {
|
|
101
|
+
await entry.adapter.loadGrammar();
|
|
102
|
+
}
|
|
103
|
+
catch (e) {
|
|
104
|
+
// grammar load failure is non-fatal
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
export const defaultRegistry = new LanguageRegistry();
|
|
111
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/language/registry.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
export declare enum SymbolType {
|
|
2
|
+
Workspace = "workspace",
|
|
3
|
+
Folder = "folder",
|
|
4
|
+
File = "file",
|
|
5
|
+
Module = "module",
|
|
6
|
+
Namespace = "namespace",
|
|
7
|
+
Class = "class",
|
|
8
|
+
Interface = "interface",
|
|
9
|
+
Enum = "enum",
|
|
10
|
+
Struct = "struct",
|
|
11
|
+
Trait = "trait",
|
|
12
|
+
Function = "function",
|
|
13
|
+
Method = "method",
|
|
14
|
+
Variable = "variable",
|
|
15
|
+
Constant = "constant",
|
|
16
|
+
Property = "property",
|
|
17
|
+
Import = "import",
|
|
18
|
+
Export = "export",
|
|
19
|
+
Comment = "comment",
|
|
20
|
+
Unknown = "unknown"
|
|
21
|
+
}
|
|
22
|
+
export interface CodeSymbol {
|
|
23
|
+
id: string;
|
|
24
|
+
name: string;
|
|
25
|
+
type: SymbolType;
|
|
26
|
+
language: string;
|
|
27
|
+
file: string;
|
|
28
|
+
startLine: number;
|
|
29
|
+
endLine: number;
|
|
30
|
+
parent: string | null;
|
|
31
|
+
children: CodeSymbol[];
|
|
32
|
+
metadata: Record<string, unknown>;
|
|
33
|
+
}
|
|
34
|
+
export interface ParseResult {
|
|
35
|
+
symbols: CodeSymbol[];
|
|
36
|
+
imports: string[];
|
|
37
|
+
exports: string[];
|
|
38
|
+
comments: string[];
|
|
39
|
+
}
|
|
40
|
+
export interface LanguageAdapter {
|
|
41
|
+
languageName(): string;
|
|
42
|
+
fileExtensions(): string[];
|
|
43
|
+
fileNames(): string[];
|
|
44
|
+
canParse(filePath: string): boolean;
|
|
45
|
+
parse(content: string, filePath: string): Promise<ParseResult>;
|
|
46
|
+
supportsSymbols(): boolean;
|
|
47
|
+
}
|
|
48
|
+
export interface AdapterInfo {
|
|
49
|
+
key: string;
|
|
50
|
+
name: string;
|
|
51
|
+
extensions: string[];
|
|
52
|
+
tier: 1 | 2 | 3;
|
|
53
|
+
supportsSymbols: boolean;
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/language/types.ts"],"names":[],"mappings":"AAAA,oBAAY,UAAU;IACpB,SAAS,cAAc;IACvB,MAAM,WAAW;IACjB,IAAI,SAAS;IACb,MAAM,WAAW;IACjB,SAAS,cAAc;IACvB,KAAK,UAAU;IACf,SAAS,cAAc;IACvB,IAAI,SAAS;IACb,MAAM,WAAW;IACjB,KAAK,UAAU;IACf,QAAQ,aAAa;IACrB,MAAM,WAAW;IACjB,QAAQ,aAAa;IACrB,QAAQ,aAAa;IACrB,QAAQ,aAAa;IACrB,MAAM,WAAW;IACjB,MAAM,WAAW;IACjB,OAAO,YAAY;IACnB,OAAO,YAAY;CACpB;AAED,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,UAAU,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,QAAQ,EAAE,UAAU,EAAE,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,eAAe;IAC9B,YAAY,IAAI,MAAM,CAAC;IACvB,cAAc,IAAI,MAAM,EAAE,CAAC;IAC3B,SAAS,IAAI,MAAM,EAAE,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;IACpC,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;IAC/D,eAAe,IAAI,OAAO,CAAC;CAC5B;AAED,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,IAAI,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAChB,eAAe,EAAE,OAAO,CAAC;CAC1B"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export var SymbolType;
|
|
2
|
+
(function (SymbolType) {
|
|
3
|
+
SymbolType["Workspace"] = "workspace";
|
|
4
|
+
SymbolType["Folder"] = "folder";
|
|
5
|
+
SymbolType["File"] = "file";
|
|
6
|
+
SymbolType["Module"] = "module";
|
|
7
|
+
SymbolType["Namespace"] = "namespace";
|
|
8
|
+
SymbolType["Class"] = "class";
|
|
9
|
+
SymbolType["Interface"] = "interface";
|
|
10
|
+
SymbolType["Enum"] = "enum";
|
|
11
|
+
SymbolType["Struct"] = "struct";
|
|
12
|
+
SymbolType["Trait"] = "trait";
|
|
13
|
+
SymbolType["Function"] = "function";
|
|
14
|
+
SymbolType["Method"] = "method";
|
|
15
|
+
SymbolType["Variable"] = "variable";
|
|
16
|
+
SymbolType["Constant"] = "constant";
|
|
17
|
+
SymbolType["Property"] = "property";
|
|
18
|
+
SymbolType["Import"] = "import";
|
|
19
|
+
SymbolType["Export"] = "export";
|
|
20
|
+
SymbolType["Comment"] = "comment";
|
|
21
|
+
SymbolType["Unknown"] = "unknown";
|
|
22
|
+
})(SymbolType || (SymbolType = {}));
|
|
23
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/language/types.ts"],"names":[],"mappings":"AAAA,MAAM,CAAN,IAAY,UAoBX;AApBD,WAAY,UAAU;IACpB,qCAAuB,CAAA;IACvB,+BAAiB,CAAA;IACjB,2BAAa,CAAA;IACb,+BAAiB,CAAA;IACjB,qCAAuB,CAAA;IACvB,6BAAe,CAAA;IACf,qCAAuB,CAAA;IACvB,2BAAa,CAAA;IACb,+BAAiB,CAAA;IACjB,6BAAe,CAAA;IACf,mCAAqB,CAAA;IACrB,+BAAiB,CAAA;IACjB,mCAAqB,CAAA;IACrB,mCAAqB,CAAA;IACrB,mCAAqB,CAAA;IACrB,+BAAiB,CAAA;IACjB,+BAAiB,CAAA;IACjB,iCAAmB,CAAA;IACnB,iCAAmB,CAAA;AACrB,CAAC,EApBW,UAAU,KAAV,UAAU,QAoBrB"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "edhindex",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Local-first hybrid code search engine with knowledge graph — BM25 + vector + rerank, tree-sitter parsing, MCP server",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"code-search",
|
|
@@ -43,7 +43,8 @@
|
|
|
43
43
|
"build": "tsc",
|
|
44
44
|
"prepublishOnly": "npm run build",
|
|
45
45
|
"start": "node dist/cli/index.js",
|
|
46
|
-
"dev": "npx tsx src/cli/index.ts"
|
|
46
|
+
"dev": "npx tsx src/cli/index.ts",
|
|
47
|
+
"test": "vitest run"
|
|
47
48
|
},
|
|
48
49
|
"dependencies": {
|
|
49
50
|
"@huggingface/transformers": "^4.2.0",
|
|
@@ -55,8 +56,22 @@
|
|
|
55
56
|
"cli-progress": "^3.12.0",
|
|
56
57
|
"commander": "^15.0.0",
|
|
57
58
|
"simple-git": "^3.36.0",
|
|
59
|
+
"tree-sitter-c": "^0.24.1",
|
|
60
|
+
"tree-sitter-c-sharp": "^0.23.5",
|
|
61
|
+
"tree-sitter-cpp": "^0.23.4",
|
|
62
|
+
"tree-sitter-css": "^0.25.0",
|
|
63
|
+
"tree-sitter-dart": "^1.0.0",
|
|
58
64
|
"tree-sitter-go": "^0.25.0",
|
|
65
|
+
"tree-sitter-haskell": "^0.23.1",
|
|
66
|
+
"tree-sitter-html": "^0.23.2",
|
|
67
|
+
"tree-sitter-java": "^0.23.5",
|
|
68
|
+
"tree-sitter-json": "^0.24.8",
|
|
69
|
+
"tree-sitter-php": "^0.24.2",
|
|
59
70
|
"tree-sitter-python": "^0.25.0",
|
|
71
|
+
"tree-sitter-ruby": "^0.23.1",
|
|
72
|
+
"tree-sitter-rust": "^0.24.0",
|
|
73
|
+
"tree-sitter-scala": "^0.24.0",
|
|
74
|
+
"tree-sitter-solidity": "^1.2.13",
|
|
60
75
|
"tree-sitter-typescript": "^0.23.2",
|
|
61
76
|
"web-tree-sitter": "^0.26.11"
|
|
62
77
|
},
|
|
@@ -64,6 +79,7 @@
|
|
|
64
79
|
"@types/better-sqlite3": "^7.6.13",
|
|
65
80
|
"@types/cli-progress": "^3.11.6",
|
|
66
81
|
"@types/node": "^22.13.0",
|
|
67
|
-
"typescript": "^5.7.0"
|
|
82
|
+
"typescript": "^5.7.0",
|
|
83
|
+
"vitest": "^4.1.10"
|
|
68
84
|
}
|
|
69
85
|
}
|
|
@@ -9,6 +9,7 @@ import { FTSStore } from '../../storage/fts.js';
|
|
|
9
9
|
import { TransformersEmbeddingProvider } from '../../embeddings/transformers.js';
|
|
10
10
|
import { LanceDBStore } from '../../vector/lancedb.js';
|
|
11
11
|
import { Indexer } from '../../indexer/indexer.js';
|
|
12
|
+
import { defaultRegistry } from '../../language/registry.js';
|
|
12
13
|
import cliProgress from 'cli-progress';
|
|
13
14
|
import chalk from 'chalk';
|
|
14
15
|
|
|
@@ -44,6 +45,7 @@ export async function indexCommand(rootPath: string) {
|
|
|
44
45
|
config,
|
|
45
46
|
rootPath,
|
|
46
47
|
indexDir,
|
|
48
|
+
registry: defaultRegistry,
|
|
47
49
|
onIndex: async (result) => {
|
|
48
50
|
for (const chunk of result.chunks) {
|
|
49
51
|
metadataStore.upsertChunk({
|
|
@@ -9,6 +9,7 @@ import { FTSStore } from '../../storage/fts.js';
|
|
|
9
9
|
import { TransformersEmbeddingProvider, TransformersReranker } from '../../embeddings/transformers.js';
|
|
10
10
|
import { LanceDBStore } from '../../vector/lancedb.js';
|
|
11
11
|
import { Indexer } from '../../indexer/indexer.js';
|
|
12
|
+
import { defaultRegistry } from '../../language/registry.js';
|
|
12
13
|
import { SearchEngine } from '../../retrieval/search.js';
|
|
13
14
|
import { createMCPServer } from '../../mcp/server.js';
|
|
14
15
|
import { FileWatcher } from '../../watcher/index.js';
|
|
@@ -96,6 +97,7 @@ export async function startCommand(rootPath: string) {
|
|
|
96
97
|
config,
|
|
97
98
|
rootPath,
|
|
98
99
|
indexDir,
|
|
100
|
+
registry: defaultRegistry,
|
|
99
101
|
onIndex: async (result) => {
|
|
100
102
|
for (const chunk of result.chunks) {
|
|
101
103
|
metadataStore.upsertChunk({
|
|
@@ -196,7 +198,7 @@ export async function startCommand(rootPath: string) {
|
|
|
196
198
|
watcher = new FileWatcher(rootPath, async (filePath) => {
|
|
197
199
|
logger.info(`File changed: ${filePath}`);
|
|
198
200
|
const { scanFiles } = await import('../../indexer/file-utils.js');
|
|
199
|
-
const files = scanFiles(rootPath,
|
|
201
|
+
const files = scanFiles(rootPath, defaultRegistry);
|
|
200
202
|
const file = files.find(f => f.relativePath === filePath);
|
|
201
203
|
if (file) {
|
|
202
204
|
const oldChunks = metadataStore.getChunksForFile(filePath);
|
package/src/config/index.ts
CHANGED
|
@@ -23,7 +23,7 @@ export interface Config {
|
|
|
23
23
|
|
|
24
24
|
const DEFAULT_CONFIG: Config = {
|
|
25
25
|
model: 'balanced',
|
|
26
|
-
languages: ['ts', 'js', 'go', '
|
|
26
|
+
languages: ['ts', 'js', 'py', 'go', 'rs', 'java', 'rb', 'c', 'cpp', 'csharp', 'php', 'html', 'css', 'json'],
|
|
27
27
|
watch: true,
|
|
28
28
|
rerank: true,
|
|
29
29
|
maxResults: 10,
|
package/src/graph/builder.ts
CHANGED
|
@@ -217,10 +217,15 @@ export class GraphBuilder {
|
|
|
217
217
|
case 'class': return 'class';
|
|
218
218
|
case 'interface': return 'interface';
|
|
219
219
|
case 'enum': return 'enum';
|
|
220
|
+
case 'struct': return 'class';
|
|
221
|
+
case 'trait': return 'interface';
|
|
220
222
|
case 'function': return 'function';
|
|
221
223
|
case 'method': return 'method';
|
|
222
224
|
case 'variable': return 'variable';
|
|
225
|
+
case 'constant': return 'variable';
|
|
226
|
+
case 'property': return 'variable';
|
|
223
227
|
case 'module': return 'module';
|
|
228
|
+
case 'namespace': return 'module';
|
|
224
229
|
default: return 'module';
|
|
225
230
|
}
|
|
226
231
|
}
|
package/src/indexer/chunker.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto';
|
|
2
|
-
import {
|
|
2
|
+
import { CodeSymbol } from '../language/types.js';
|
|
3
3
|
import { logger } from '../logging.js';
|
|
4
4
|
|
|
5
5
|
export interface Chunk {
|
|
@@ -31,7 +31,7 @@ export async function chunkFile(
|
|
|
31
31
|
filePath: string,
|
|
32
32
|
relativePath: string,
|
|
33
33
|
language: string,
|
|
34
|
-
symbols:
|
|
34
|
+
symbols: CodeSymbol[],
|
|
35
35
|
content: string,
|
|
36
36
|
lines: string[],
|
|
37
37
|
imports: string[],
|
|
@@ -43,13 +43,15 @@ export async function chunkFile(
|
|
|
43
43
|
method: 1,
|
|
44
44
|
class: 2,
|
|
45
45
|
interface: 3,
|
|
46
|
+
struct: 3,
|
|
47
|
+
trait: 3,
|
|
46
48
|
enum: 4,
|
|
47
49
|
module: 5,
|
|
48
50
|
};
|
|
49
51
|
|
|
50
52
|
const sorted = [...symbols].sort((a, b) => {
|
|
51
|
-
const aP = priorityOrder[a.
|
|
52
|
-
const bP = priorityOrder[b.
|
|
53
|
+
const aP = priorityOrder[a.type] ?? 99;
|
|
54
|
+
const bP = priorityOrder[b.type] ?? 99;
|
|
53
55
|
if (aP !== bP) return aP - bP;
|
|
54
56
|
return a.startLine - b.startLine;
|
|
55
57
|
});
|
|
@@ -60,7 +62,7 @@ export async function chunkFile(
|
|
|
60
62
|
if (isCovered(covered, sym.startLine, sym.endLine)) continue;
|
|
61
63
|
markCovered(covered, sym.startLine, sym.endLine);
|
|
62
64
|
|
|
63
|
-
const chunkContent =
|
|
65
|
+
const chunkContent = content.slice(
|
|
64
66
|
content.split('\n').slice(0, sym.startLine - 1).join('\n').length + (sym.startLine > 1 ? 1 : 0),
|
|
65
67
|
content.split('\n').slice(0, sym.endLine).join('\n').length
|
|
66
68
|
);
|
|
@@ -72,7 +74,7 @@ export async function chunkFile(
|
|
|
72
74
|
file: relativePath,
|
|
73
75
|
language,
|
|
74
76
|
symbol: sym.name,
|
|
75
|
-
kind: sym.
|
|
77
|
+
kind: sym.type,
|
|
76
78
|
parent: null,
|
|
77
79
|
hash,
|
|
78
80
|
startLine: sym.startLine,
|
|
@@ -83,7 +85,7 @@ export async function chunkFile(
|
|
|
83
85
|
});
|
|
84
86
|
} else {
|
|
85
87
|
const childChunks = splitRecursive(
|
|
86
|
-
chunkContent, lines, sym.startLine, sym.name, sym.
|
|
88
|
+
chunkContent, lines, sym.startLine, sym.name, sym.type,
|
|
87
89
|
relativePath, language, imports, exports
|
|
88
90
|
);
|
|
89
91
|
chunks.push(...childChunks);
|
|
@@ -2,7 +2,8 @@ import { readdirSync, statSync, readFileSync } from 'node:fs';
|
|
|
2
2
|
import { join, relative } from 'node:path';
|
|
3
3
|
import { createHash } from 'node:crypto';
|
|
4
4
|
import { checkSymlinkSafety } from '../security.js';
|
|
5
|
-
import { createIgnoreRules
|
|
5
|
+
import { createIgnoreRules } from './ignore.js';
|
|
6
|
+
import { LanguageRegistry } from '../language/registry.js';
|
|
6
7
|
import { logger } from '../logging.js';
|
|
7
8
|
|
|
8
9
|
const MAX_FILE_SIZE = 10 * 1024 * 1024;
|
|
@@ -18,29 +19,6 @@ const BINARY_EXTENSIONS = new Set([
|
|
|
18
19
|
'.mp3', '.wav', '.flac', '.ogg',
|
|
19
20
|
]);
|
|
20
21
|
|
|
21
|
-
const LANGUAGE_EXTENSIONS: Record<string, string[]> = {
|
|
22
|
-
ts: ['.ts', '.tsx', '.mts', '.cts'],
|
|
23
|
-
js: ['.js', '.jsx', '.mjs', '.cjs'],
|
|
24
|
-
py: ['.py', '.pyw'],
|
|
25
|
-
go: ['.go'],
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
const EXTENSION_TO_LANG: Record<string, string> = {};
|
|
29
|
-
for (const [lang, exts] of Object.entries(LANGUAGE_EXTENSIONS)) {
|
|
30
|
-
for (const ext of exts) {
|
|
31
|
-
EXTENSION_TO_LANG[ext] = lang;
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export function getLanguageForFile(filePath: string): string | null {
|
|
36
|
-
const ext = filePath.toLowerCase().split('.').pop();
|
|
37
|
-
return EXTENSION_TO_LANG[`.${ext}`] || null;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export function languageFromExtension(ext: string): string | null {
|
|
41
|
-
return EXTENSION_TO_LANG[ext.toLowerCase()] || null;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
22
|
export function isIndexableFile(filePath: string, stat: { size: number }): boolean {
|
|
45
23
|
if (stat.size > MAX_FILE_SIZE) return false;
|
|
46
24
|
if (stat.size === 0) return false;
|
|
@@ -73,16 +51,10 @@ export interface ScannedFile {
|
|
|
73
51
|
size: number;
|
|
74
52
|
}
|
|
75
53
|
|
|
76
|
-
export function scanFiles(rootPath: string,
|
|
54
|
+
export function scanFiles(rootPath: string, registry: LanguageRegistry): ScannedFile[] {
|
|
77
55
|
const ignore = createIgnoreRules(rootPath);
|
|
78
56
|
const results: ScannedFile[] = [];
|
|
79
57
|
|
|
80
|
-
const allowedExts = new Set<string>();
|
|
81
|
-
for (const lang of languages) {
|
|
82
|
-
const exts = LANGUAGE_EXTENSIONS[lang];
|
|
83
|
-
if (exts) for (const ext of exts) allowedExts.add(ext);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
58
|
function walk(dir: string) {
|
|
87
59
|
let entries: string[];
|
|
88
60
|
try {
|
|
@@ -106,19 +78,17 @@ export function scanFiles(rootPath: string, languages: string[]): ScannedFile[]
|
|
|
106
78
|
if (!checkSymlinkSafety(rootPath, fullPath)) continue;
|
|
107
79
|
}
|
|
108
80
|
|
|
109
|
-
const ext = '.' + (entry.split('.').pop() || '').toLowerCase();
|
|
110
|
-
if (!allowedExts.has(ext)) continue;
|
|
111
81
|
if (!isIndexableFile(fullPath, stat)) continue;
|
|
112
82
|
|
|
113
|
-
const
|
|
114
|
-
if (!
|
|
83
|
+
const { adapter, key } = registry.getAdapter(relPath);
|
|
84
|
+
if (!adapter.canParse(relPath)) continue;
|
|
115
85
|
|
|
116
86
|
try {
|
|
117
87
|
const hash = computeFileHash(fullPath);
|
|
118
88
|
results.push({
|
|
119
89
|
path: fullPath,
|
|
120
90
|
relativePath: relPath,
|
|
121
|
-
language:
|
|
91
|
+
language: key,
|
|
122
92
|
hash,
|
|
123
93
|
size: stat.size,
|
|
124
94
|
});
|
|
@@ -135,5 +105,3 @@ export function scanFiles(rootPath: string, languages: string[]): ScannedFile[]
|
|
|
135
105
|
walk(rootPath);
|
|
136
106
|
return results;
|
|
137
107
|
}
|
|
138
|
-
|
|
139
|
-
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { describe, it, expect, beforeAll } from 'vitest';
|
|
2
|
+
import { readFileSync } from 'node:fs';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { LanguageRegistry } from '../language/registry.js';
|
|
5
|
+
|
|
6
|
+
const FIXTURES_DIR = join(import.meta.dirname, '../../test-fixtures');
|
|
7
|
+
|
|
8
|
+
const registry = new LanguageRegistry();
|
|
9
|
+
|
|
10
|
+
const FIXTURES: Array<{ key: string; file: string; minSymbols: number }> = [
|
|
11
|
+
{ key: 'ts', file: 'sample.ts', minSymbols: 3 },
|
|
12
|
+
{ key: 'js', file: 'sample.js', minSymbols: 2 },
|
|
13
|
+
{ key: 'py', file: 'sample.py', minSymbols: 2 },
|
|
14
|
+
{ key: 'go', file: 'sample.go', minSymbols: 2 },
|
|
15
|
+
{ key: 'rs', file: 'sample.rs', minSymbols: 3 },
|
|
16
|
+
{ key: 'java', file: 'sample.java', minSymbols: 2 },
|
|
17
|
+
{ key: 'rb', file: 'sample.rb', minSymbols: 2 },
|
|
18
|
+
{ key: 'c', file: 'sample.c', minSymbols: 2 },
|
|
19
|
+
{ key: 'cpp', file: 'sample.cpp', minSymbols: 2 },
|
|
20
|
+
{ key: 'csharp', file: 'sample.cs', minSymbols: 2 },
|
|
21
|
+
{ key: 'php', file: 'sample.php', minSymbols: 2 },
|
|
22
|
+
{ key: 'html', file: 'sample.html', minSymbols: 1 },
|
|
23
|
+
{ key: 'css', file: 'sample.css', minSymbols: 1 },
|
|
24
|
+
{ key: 'json', file: 'sample.json', minSymbols: 1 },
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
function countLines(content: string): number {
|
|
28
|
+
return content.split('\n').length;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
for (const { key, file, minSymbols } of FIXTURES) {
|
|
32
|
+
describe(`${key} adapter — ${file}`, () => {
|
|
33
|
+
let content: string;
|
|
34
|
+
let lineCount: number;
|
|
35
|
+
|
|
36
|
+
beforeAll(() => {
|
|
37
|
+
const filePath = join(FIXTURES_DIR, file);
|
|
38
|
+
content = readFileSync(filePath, 'utf-8');
|
|
39
|
+
lineCount = countLines(content);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('fixture file exists and has 100+ lines', () => {
|
|
43
|
+
expect(lineCount).toBeGreaterThanOrEqual(100);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('adapter can parse the file', async () => {
|
|
47
|
+
const { adapter } = registry.getAdapter(file);
|
|
48
|
+
expect(adapter).toBeTruthy();
|
|
49
|
+
expect(adapter.canParse(file)).toBe(true);
|
|
50
|
+
|
|
51
|
+
const result = await adapter.parse(content, file);
|
|
52
|
+
expect(result).toBeTruthy();
|
|
53
|
+
expect(Array.isArray(result.symbols)).toBe(true);
|
|
54
|
+
expect(Array.isArray(result.imports)).toBe(true);
|
|
55
|
+
expect(Array.isArray(result.exports)).toBe(true);
|
|
56
|
+
expect(Array.isArray(result.comments)).toBe(true);
|
|
57
|
+
}, 30000);
|
|
58
|
+
|
|
59
|
+
it('extracts the expected number of symbols', async () => {
|
|
60
|
+
const { adapter } = registry.getAdapter(file);
|
|
61
|
+
const result = await adapter.parse(content, file);
|
|
62
|
+
expect(result.symbols.length).toBeGreaterThanOrEqual(minSymbols);
|
|
63
|
+
}, 30000);
|
|
64
|
+
|
|
65
|
+
it('every symbol has required fields', async () => {
|
|
66
|
+
const { adapter } = registry.getAdapter(file);
|
|
67
|
+
const result = await adapter.parse(content, file);
|
|
68
|
+
|
|
69
|
+
for (const sym of result.symbols) {
|
|
70
|
+
expect(sym.id).toBeTruthy();
|
|
71
|
+
expect(sym.name).toBeTruthy();
|
|
72
|
+
expect(sym.type).toBeTruthy();
|
|
73
|
+
expect(sym.language).toBeTruthy();
|
|
74
|
+
expect(sym.file).toBe(file);
|
|
75
|
+
expect(typeof sym.startLine).toBe('number');
|
|
76
|
+
expect(typeof sym.endLine).toBe('number');
|
|
77
|
+
expect(sym.startLine).toBeGreaterThan(0);
|
|
78
|
+
expect(sym.endLine).toBeGreaterThanOrEqual(sym.startLine);
|
|
79
|
+
expect(sym.endLine).toBeLessThanOrEqual(lineCount);
|
|
80
|
+
expect(Array.isArray(sym.children)).toBe(true);
|
|
81
|
+
}
|
|
82
|
+
}, 30000);
|
|
83
|
+
|
|
84
|
+
it('symbol start lines are within file bounds', async () => {
|
|
85
|
+
const { adapter } = registry.getAdapter(file);
|
|
86
|
+
const result = await adapter.parse(content, file);
|
|
87
|
+
|
|
88
|
+
for (const sym of result.symbols) {
|
|
89
|
+
expect(sym.startLine).toBeLessThanOrEqual(lineCount);
|
|
90
|
+
expect(sym.endLine).toBeLessThanOrEqual(lineCount);
|
|
91
|
+
}
|
|
92
|
+
}, 30000);
|
|
93
|
+
|
|
94
|
+
it('produces different symbols for different fixtures', async () => {
|
|
95
|
+
const { adapter } = registry.getAdapter(file);
|
|
96
|
+
const result = await adapter.parse(content, file);
|
|
97
|
+
|
|
98
|
+
const names = result.symbols.map(s => s.name);
|
|
99
|
+
const unique = new Set(names);
|
|
100
|
+
expect(unique.size).toBeGreaterThanOrEqual(Math.min(minSymbols, names.length));
|
|
101
|
+
}, 30000);
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
describe('All fixtures', () => {
|
|
106
|
+
it('every fixture file exists', () => {
|
|
107
|
+
for (const { file } of FIXTURES) {
|
|
108
|
+
const filePath = join(FIXTURES_DIR, file);
|
|
109
|
+
const content = readFileSync(filePath, 'utf-8');
|
|
110
|
+
expect(content.length).toBeGreaterThan(0);
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it('registry maps every fixture extension correctly', () => {
|
|
115
|
+
for (const { key, file } of FIXTURES) {
|
|
116
|
+
const { adapter, key: resolvedKey } = registry.getAdapter(file);
|
|
117
|
+
expect(adapter).toBeTruthy();
|
|
118
|
+
expect(resolvedKey).toBe(key);
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
});
|