graph-indexer 1.0.1 → 1.0.3
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/PROMPT.md +143 -35
- package/README.md +227 -862
- package/core-engine.mjs +363 -159
- package/indexer.mjs +8 -8
- package/init.mjs +179 -68
- package/mcp-server.mjs +397 -150
- package/package.json +13 -13
- package/parser-utils.mjs +94 -23
- package/watch-daemon.mjs +2 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "graph-indexer",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"description": "Zero-DB, Air-gapped, In-Memory Graph Indexer MCP Server with Hybrid RRF Search and Cross-Language Topology",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "core-engine.mjs",
|
|
@@ -42,17 +42,6 @@
|
|
|
42
42
|
"chokidar": "5.0.0",
|
|
43
43
|
"ignore": "5.3.2",
|
|
44
44
|
"tree-sitter": "0.21.1",
|
|
45
|
-
"tree-sitter-c-sharp": "^0.21.3",
|
|
46
|
-
"tree-sitter-css": "0.21.1",
|
|
47
|
-
"tree-sitter-go": "0.21.0",
|
|
48
|
-
"tree-sitter-java": "^0.21.0",
|
|
49
|
-
"tree-sitter-javascript": "0.21.4",
|
|
50
|
-
"tree-sitter-kotlin": "^0.3.8",
|
|
51
|
-
"tree-sitter-php": "0.23.1",
|
|
52
|
-
"tree-sitter-python": "0.21.0",
|
|
53
|
-
"tree-sitter-ruby": "^0.23.1",
|
|
54
|
-
"tree-sitter-rust": "0.21.0",
|
|
55
|
-
"tree-sitter-typescript": "0.21.2",
|
|
56
45
|
"zod": "3.23.8"
|
|
57
46
|
},
|
|
58
47
|
"keywords": [
|
|
@@ -88,6 +77,17 @@
|
|
|
88
77
|
"access": "public"
|
|
89
78
|
},
|
|
90
79
|
"optionalDependencies": {
|
|
91
|
-
"hnswlib-node": "^3.0.0"
|
|
80
|
+
"hnswlib-node": "^3.0.0",
|
|
81
|
+
"tree-sitter-c-sharp": "^0.21.3",
|
|
82
|
+
"tree-sitter-css": "0.21.1",
|
|
83
|
+
"tree-sitter-go": "0.21.0",
|
|
84
|
+
"tree-sitter-java": "^0.21.0",
|
|
85
|
+
"tree-sitter-javascript": "0.21.4",
|
|
86
|
+
"tree-sitter-kotlin": "^0.3.8",
|
|
87
|
+
"tree-sitter-php": "0.23.1",
|
|
88
|
+
"tree-sitter-python": "0.21.0",
|
|
89
|
+
"tree-sitter-ruby": "^0.23.1",
|
|
90
|
+
"tree-sitter-rust": "0.21.0",
|
|
91
|
+
"tree-sitter-typescript": "0.21.2"
|
|
92
92
|
}
|
|
93
93
|
}
|
package/parser-utils.mjs
CHANGED
|
@@ -19,34 +19,65 @@ import path from 'path';
|
|
|
19
19
|
import { createHash } from 'crypto';
|
|
20
20
|
import ignore from 'ignore';
|
|
21
21
|
import Parser from 'tree-sitter';
|
|
22
|
-
const { Query } = Parser; //
|
|
23
|
-
|
|
24
|
-
import TypeScript from 'tree-sitter-typescript';
|
|
25
|
-
import JavaScript from 'tree-sitter-javascript';
|
|
26
|
-
import CSS from 'tree-sitter-css';
|
|
27
|
-
import Python from 'tree-sitter-python';
|
|
28
|
-
import Rust from 'tree-sitter-rust';
|
|
29
|
-
import Go from 'tree-sitter-go';
|
|
30
|
-
import PHP from 'tree-sitter-php';
|
|
31
|
-
import Java from 'tree-sitter-java';
|
|
32
|
-
import Kotlin from 'tree-sitter-kotlin';
|
|
33
|
-
import CSharp from 'tree-sitter-c-sharp';
|
|
34
|
-
import Ruby from 'tree-sitter-ruby';
|
|
22
|
+
const { Query } = Parser; // Native parser query helper
|
|
23
|
+
|
|
35
24
|
import { truncateForEmbedding } from './core-engine.mjs';
|
|
36
25
|
|
|
37
26
|
export const MAX_FILE_SIZE_BYTES = 500000;
|
|
38
27
|
export const OLLAMA_HOST = process.env.OLLAMA_HOST || "http://localhost:11434";
|
|
39
28
|
|
|
29
|
+
// ─── Dynamic Language Loading ─────────────────────────────────────────────────
|
|
30
|
+
|
|
31
|
+
function _loadProjectConfig() {
|
|
32
|
+
const configPath = path.join(process.env.MCP_PROJECT_ROOT || process.cwd(), '.graph-indexer.json');
|
|
33
|
+
try {
|
|
34
|
+
if (fs.existsSync(configPath)) return JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
35
|
+
} catch { /* ignore */ }
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async function _tryLang(pkg, enabledLangs, key) {
|
|
40
|
+
if (enabledLangs && !enabledLangs.includes(key)) return null;
|
|
41
|
+
try {
|
|
42
|
+
return (await import(pkg)).default;
|
|
43
|
+
} catch {
|
|
44
|
+
process.stderr.write(`[graph-indexer] WARNING: ${pkg} not installed — ${key} files will be skipped\n`);
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const _cfg = _loadProjectConfig();
|
|
50
|
+
const _enabled = _cfg?.languages ?? null; // null = all languages
|
|
51
|
+
|
|
52
|
+
const [
|
|
53
|
+
TypeScript, JavaScript, CSS, Python, Rust,
|
|
54
|
+
Go, PHP, Java, Kotlin, CSharp, Ruby
|
|
55
|
+
] = await Promise.all([
|
|
56
|
+
_tryLang('tree-sitter-typescript', _enabled, 'typescript'),
|
|
57
|
+
_tryLang('tree-sitter-javascript', _enabled, 'javascript'),
|
|
58
|
+
_tryLang('tree-sitter-css', _enabled, 'css'),
|
|
59
|
+
_tryLang('tree-sitter-python', _enabled, 'python'),
|
|
60
|
+
_tryLang('tree-sitter-rust', _enabled, 'rust'),
|
|
61
|
+
_tryLang('tree-sitter-go', _enabled, 'go'),
|
|
62
|
+
_tryLang('tree-sitter-php', _enabled, 'php'),
|
|
63
|
+
_tryLang('tree-sitter-java', _enabled, 'java'),
|
|
64
|
+
_tryLang('tree-sitter-kotlin', _enabled, 'kotlin'),
|
|
65
|
+
_tryLang('tree-sitter-c-sharp', _enabled, 'csharp'),
|
|
66
|
+
_tryLang('tree-sitter-ruby', _enabled, 'ruby'),
|
|
67
|
+
]);
|
|
68
|
+
|
|
40
69
|
const LANGUAGE_MAP = {
|
|
41
|
-
'.ts': TypeScript.typescript, '.tsx': TypeScript.tsx,
|
|
42
|
-
'.js': JavaScript, '.jsx': JavaScript, '.mjs': JavaScript, '.cjs': JavaScript,
|
|
43
|
-
'.css': CSS, '.scss': CSS,
|
|
44
|
-
'.py': Python
|
|
45
|
-
'.
|
|
46
|
-
'.
|
|
47
|
-
'.
|
|
48
|
-
'.
|
|
49
|
-
'.
|
|
70
|
+
...(TypeScript ? { '.ts': TypeScript.typescript, '.tsx': TypeScript.tsx } : {}),
|
|
71
|
+
...(JavaScript ? { '.js': JavaScript, '.jsx': JavaScript, '.mjs': JavaScript, '.cjs': JavaScript } : {}),
|
|
72
|
+
...(CSS ? { '.css': CSS, '.scss': CSS } : {}),
|
|
73
|
+
...(Python ? { '.py': Python } : {}),
|
|
74
|
+
...(Rust ? { '.rs': Rust } : {}),
|
|
75
|
+
...(Go ? { '.go': Go } : {}),
|
|
76
|
+
...(PHP ? { '.php': PHP.php } : {}),
|
|
77
|
+
...(Java ? { '.java': Java } : {}),
|
|
78
|
+
...(Kotlin ? { '.kt': Kotlin, '.kts': Kotlin } : {}),
|
|
79
|
+
...(CSharp ? { '.cs': CSharp } : {}),
|
|
80
|
+
...(Ruby ? { '.rb': Ruby } : {}),
|
|
50
81
|
};
|
|
51
82
|
|
|
52
83
|
// 🥇 ULTRA-GENERIC QUERIES: Immune to grammar changes between TS/JS/TSX
|
|
@@ -398,6 +429,7 @@ export function extractSemanticChunks(rootNode, relPath, sourceCode, ext) {
|
|
|
398
429
|
const params = extractParams(chunkNode, ext);
|
|
399
430
|
const returnType = extractReturnType(chunkNode, ext);
|
|
400
431
|
const classContext = extractClassContext(chunkNode);
|
|
432
|
+
const typeRefs = extractTypeAnnotations(chunkNode, ext);
|
|
401
433
|
|
|
402
434
|
const id = createHash('sha256')
|
|
403
435
|
.update(`${relPath}::${chunkNode.startPosition.row}::${chunkNode.startPosition.column}`)
|
|
@@ -408,7 +440,8 @@ export function extractSemanticChunks(rootNode, relPath, sourceCode, ext) {
|
|
|
408
440
|
docstring: docstring, code_snippet: snippet, content_hash: hash,
|
|
409
441
|
start_line: chunkNode.startPosition.row + 1, end_line: chunkNode.endPosition.row + 1,
|
|
410
442
|
calls: outgoingCalls,
|
|
411
|
-
params, return_type: returnType, class_context: classContext
|
|
443
|
+
params, return_type: returnType, class_context: classContext,
|
|
444
|
+
type_refs: typeRefs,
|
|
412
445
|
});
|
|
413
446
|
}
|
|
414
447
|
} catch (e) {
|
|
@@ -709,6 +742,44 @@ export function extractClassContext(chunkNode) {
|
|
|
709
742
|
return '';
|
|
710
743
|
}
|
|
711
744
|
|
|
745
|
+
/**
|
|
746
|
+
* Frontier 2: Extract TypeScript/Python type annotation names from a chunk node.
|
|
747
|
+
* Returns simple type names (e.g. ['User', 'AuthToken', 'PaymentService'])
|
|
748
|
+
* used to enrich the inverted index and the type_refs chunk field.
|
|
749
|
+
*/
|
|
750
|
+
export function extractTypeAnnotations(chunkNode, ext) {
|
|
751
|
+
const types = new Set();
|
|
752
|
+
const JS_LIKE = ['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs'];
|
|
753
|
+
if (!JS_LIKE.includes(ext) && ext !== '.py') return [];
|
|
754
|
+
|
|
755
|
+
function walk(node) {
|
|
756
|
+
// TypeScript: type_annotation nodes contain the type text
|
|
757
|
+
if (node.type === 'type_annotation') {
|
|
758
|
+
const typeText = node.text.replace(/^:\s*/, '').trim();
|
|
759
|
+
// Extract simple identifiers from the type (skip primitives)
|
|
760
|
+
const PRIMITIVES = new Set(['string','number','boolean','void','any','unknown','never','null','undefined','object','symbol','bigint']);
|
|
761
|
+
for (const match of typeText.matchAll(/\b([A-Z][A-Za-z0-9]*)\b/g)) {
|
|
762
|
+
if (!PRIMITIVES.has(match[1].toLowerCase())) types.add(match[1]);
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
// TypeScript: generic_type, predefined_type (string, number…) skip, named types keep
|
|
766
|
+
else if (node.type === 'type_identifier' || node.type === 'generic_type') {
|
|
767
|
+
const name = node.children[0]?.text || node.text;
|
|
768
|
+
if (name && /^[A-Z]/.test(name)) types.add(name);
|
|
769
|
+
}
|
|
770
|
+
// Python: type comments or annotations (annotation nodes)
|
|
771
|
+
else if (node.type === 'annotation' && ext === '.py') {
|
|
772
|
+
const typeText = node.text.replace(/^->\s*|^:\s*/, '').trim();
|
|
773
|
+
for (const match of typeText.matchAll(/\b([A-Z][A-Za-z0-9]*)\b/g)) {
|
|
774
|
+
types.add(match[1]);
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
for (const child of node.children) walk(child);
|
|
778
|
+
}
|
|
779
|
+
walk(chunkNode);
|
|
780
|
+
return Array.from(types).slice(0, 20);
|
|
781
|
+
}
|
|
782
|
+
|
|
712
783
|
export function extractCalls(rootNode) {
|
|
713
784
|
const calls = new Set();
|
|
714
785
|
function walk(node) {
|
package/watch-daemon.mjs
CHANGED
|
@@ -132,11 +132,11 @@ async function processFileChange(absolutePath) {
|
|
|
132
132
|
db.saveDebounced();
|
|
133
133
|
process.stderr.write(`[daemon] 🔄 Synced: ${filename} (Cache Hit: ${newChunks.length - chunksToEmbed.length}/${newChunks.length})\n`);
|
|
134
134
|
} catch (err) {
|
|
135
|
-
process.stderr.write(`[daemon] ❌ Error
|
|
135
|
+
process.stderr.write(`[daemon] ❌ Error in ${filename}: ${err.message}\n`);
|
|
136
136
|
}
|
|
137
137
|
}
|
|
138
138
|
|
|
139
|
-
process.stderr.write(`🚀 Chokidar Watcher Daemon
|
|
139
|
+
process.stderr.write(`🚀 Chokidar Watcher Daemon started in: ${PROJECT_ROOT}\n`);
|
|
140
140
|
const watcher = chokidar.watch(PROJECT_ROOT, {
|
|
141
141
|
ignored: /(^|[\/\\])\../, persistent: true,
|
|
142
142
|
awaitWriteFinish: { stabilityThreshold: 300, pollInterval: 100 }
|