graph-indexer 1.0.0 → 1.0.2

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/indexer.mjs CHANGED
@@ -4,16 +4,16 @@
4
4
  * @description In-Memory Graph Indexer — Bootstrap Engine. Reads .ts/.tsx/.js/.jsx files → Tree-sitter AST → Local Embeddings → code-index.json. Zero external dependencies (only Tree-sitter). No ChromaDB.
5
5
  * @author MaquinaTech <https://github.com/MaquinaTech>
6
6
  * @copyright (c) 2026 MaquinaTech. All rights reserved.
7
- * @license GPL-3.0-only
8
- * * This program is free software: you can redistribute it and/or modify
9
- * it under the terms of the GNU General Public License as published by
10
- * the Free Software Foundation, either version 3 of the License, or
11
- * (at your option) any later version.
12
- *
13
- * This program is distributed in the hope that it will be useful,
14
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
- * GNU General Public License for more details.
7
+ * @license MIT
8
+ * Copyright (c) 2026 MaquinaTech. All rights reserved.
9
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
10
+ * of this software and associated documentation files (the "Software"), to deal
11
+ * in the Software without restriction, including without limitation the rights
12
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13
+ * copies of the Software, and to permit persons to whom the Software is
14
+ * furnished to do so, subject to the following conditions: The above copyright
15
+ * notice and this permission notice shall be included in all copies or
16
+ * substantial portions of the Software.
17
17
  */
18
18
  import fs from 'fs';
19
19
  import path from 'path';
@@ -61,7 +61,7 @@ async function main() {
61
61
  for (const absolutePath of files) {
62
62
  totalCheckedFiles++;
63
63
  const relPath = path.relative(PROJECT_ROOT, absolutePath).replace(/\\/g, '/');
64
- process.stdout.write(`\r⚡ Parsing AST: [${totalCheckedFiles}/${files.length}] Procesando: ${relPath.slice(-40)} `);
64
+ process.stdout.write(`\r⚡ Parsing AST: [${totalCheckedFiles}/${files.length}] Processing: ${relPath.slice(-40)} `);
65
65
 
66
66
  if (relPath.includes('.bundle.') || relPath.includes('.min.')) continue;
67
67
 
@@ -76,7 +76,7 @@ async function main() {
76
76
  const parser = getParserForFile(ext);
77
77
  if (!parser) continue;
78
78
 
79
- let tree = parser.parse(content);
79
+ let tree = parser.parse((offset) => offset < content.length ? content.slice(offset, offset + 4096) : null);
80
80
  const rawImports = extractImportsFromAST(tree.rootNode, ext);
81
81
  const imports = resolveLocalImports(rawImports, relPath, PROJECT_ROOT);
82
82
  indexData.graph.dependencies[relPath] = imports;
@@ -92,14 +92,14 @@ async function main() {
92
92
  }
93
93
  }
94
94
  } catch (err) {
95
- console.error(`\n💥 Error en ${relPath}: ${err.message}`);
95
+ console.error(`\n💥 Error in ${relPath}: ${err.message}`);
96
96
  }
97
97
  }
98
98
 
99
99
  const pendingChunks = indexData.chunksToEmbed || [];
100
100
  console.log(`\n\n🧠 Embedding Generation (Ollama)`);
101
- console.log(`Chunks reciclados de la caché: ${indexData.chunks.length}`);
102
- console.log(`Chunks nuevos a procesar: ${pendingChunks.length}`);
101
+ console.log(`Chunks reused from cache: ${indexData.chunks.length}`);
102
+ console.log(`New chunks to process: ${pendingChunks.length}`);
103
103
 
104
104
  if (pendingChunks.length > 0) {
105
105
  const BATCH_SIZE = 64;
@@ -124,11 +124,11 @@ async function main() {
124
124
  ? `This code architectural neighborhood connects with: ${cleanDeps.join(', ')}.`
125
125
  : '';
126
126
 
127
- // 🥇 BLEED PROTECTION: Flattened linear payload for Ollama without hidden indentation
128
127
  return [
129
128
  `File Location: ${c.file_path}`,
130
129
  `Symbol Name: ${c.node_type} -> ${c.name}`,
131
130
  c.docstring ? `Developer Documentation: ${c.docstring}` : '',
131
+ c.type_refs?.length ? `Type References: ${c.type_refs.join(', ')}` : '',
132
132
  topologicalContext,
133
133
  `--- Source Code ---`,
134
134
  c.code_snippet
@@ -146,12 +146,12 @@ async function main() {
146
146
  for (const chunk of batch) indexData.chunks.push(chunk);
147
147
  }
148
148
  completedChunksCount += batch.length;
149
- process.stdout.write(`\r🤖 Embedding Progress: [${completedChunksCount}/${pendingChunks.length}] Chunks procesados...`);
149
+ process.stdout.write(`\r🤖 Embedding Progress: [${completedChunksCount}/${pendingChunks.length}] Chunks processed...`);
150
150
  };
151
151
 
152
152
  for (let i = 0; i < batches.length; i += CONCURRENCY) {
153
- const ráfaga = batches.slice(i, i + CONCURRENCY);
154
- await Promise.all(ráfaga.map(batch => worker(batch)));
153
+ const batchGroup = batches.slice(i, i + CONCURRENCY);
154
+ await Promise.all(batchGroup.map(batch => worker(batch)));
155
155
  }
156
156
  console.timeEnd("Embedding Generation Duration");
157
157
  }
package/init.mjs ADDED
@@ -0,0 +1,241 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * @file init.mjs
4
+ * @description graph-indexer init CLI — auto-configures all detected IDEs/agents.
5
+ * @author MaquinaTech <https://github.com/MaquinaTech>
6
+ * @copyright (c) 2026 MaquinaTech. All rights reserved.
7
+ * @license MIT
8
+ * Copyright (c) 2026 MaquinaTech. All rights reserved.
9
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
10
+ * of this software and associated documentation files (the "Software"), to deal
11
+ * in the Software without restriction, including without limitation the rights
12
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13
+ * copies of the Software, and to permit persons to whom the Software is
14
+ * furnished to do so, subject to the following conditions: The above copyright
15
+ * notice and this permission notice shall be included in all copies or
16
+ * substantial portions of the Software.
17
+ */
18
+ import fs from 'fs';
19
+ import path from 'path';
20
+ import os from 'os';
21
+ import { fileURLToPath } from 'url';
22
+
23
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
24
+ const isDryRun = process.argv.includes('--dry-run');
25
+ const PROJECT_ROOT = process.cwd();
26
+
27
+ // ─── MCP Server config block ──────────────────────────────────────────────────
28
+
29
+ const SERVER_CONFIG = {
30
+ command: 'node',
31
+ args: [path.join(PROJECT_ROOT, 'node_modules', 'graph-indexer', 'mcp-server.mjs')],
32
+ env: { MCP_PROJECT_ROOT: PROJECT_ROOT }
33
+ };
34
+
35
+ // ─── Utilities ────────────────────────────────────────────────────────────────
36
+
37
+ function log(msg) { process.stdout.write(msg + '\n'); }
38
+
39
+ function writeFile(filePath, content) {
40
+ if (isDryRun) {
41
+ log(` [dry-run] Would write: ${filePath}`);
42
+ return;
43
+ }
44
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
45
+ fs.writeFileSync(filePath, content, 'utf-8');
46
+ }
47
+
48
+ function readJsonSafe(filePath) {
49
+ try {
50
+ if (fs.existsSync(filePath)) {
51
+ return JSON.parse(fs.readFileSync(filePath, 'utf-8'));
52
+ }
53
+ } catch { /* malformed JSON — start fresh */ }
54
+ return null;
55
+ }
56
+
57
+ /** Deep-merge src into dst without duplicating top-level keys. */
58
+ function mergeJson(dst, src) {
59
+ for (const [k, v] of Object.entries(src)) {
60
+ if (dst[k] && typeof dst[k] === 'object' && !Array.isArray(dst[k]) && typeof v === 'object' && !Array.isArray(v)) {
61
+ mergeJson(dst[k], v);
62
+ } else {
63
+ dst[k] = v;
64
+ }
65
+ }
66
+ return dst;
67
+ }
68
+
69
+ // ─── IDE Detectors & Configurators ───────────────────────────────────────────
70
+
71
+ function configureVSCode() {
72
+ const vscodeDir = path.join(PROJECT_ROOT, '.vscode');
73
+ const configPath = path.join(vscodeDir, 'mcp.json');
74
+
75
+ const existing = readJsonSafe(configPath) || {};
76
+ if (!existing.servers) existing.servers = {};
77
+ if (existing.servers['graph-indexer']) return false; // already configured
78
+
79
+ existing.servers['graph-indexer'] = SERVER_CONFIG;
80
+ writeFile(configPath, JSON.stringify(existing, null, 2) + '\n');
81
+ return true;
82
+ }
83
+
84
+ function configureCursor() {
85
+ // Check both project-level and global Cursor config
86
+ const candidates = [
87
+ path.join(PROJECT_ROOT, '.cursor', 'mcp.json'),
88
+ path.join(os.homedir(), '.cursor', 'mcp.json'),
89
+ ];
90
+ const detected = candidates.some(p => fs.existsSync(path.dirname(p)));
91
+
92
+ // Prefer project-level config
93
+ const configPath = candidates[0];
94
+ const existing = readJsonSafe(configPath) || {};
95
+ if (!existing.mcpServers) existing.mcpServers = {};
96
+ if (existing.mcpServers['graph-indexer']) return false;
97
+
98
+ existing.mcpServers['graph-indexer'] = SERVER_CONFIG;
99
+ writeFile(configPath, JSON.stringify(existing, null, 2) + '\n');
100
+ return true;
101
+ }
102
+
103
+ function configureClaudeDesktop() {
104
+ let configPath;
105
+ if (process.platform === 'win32') {
106
+ configPath = path.join(process.env.APPDATA || '', 'Claude', 'claude_desktop_config.json');
107
+ } else {
108
+ configPath = path.join(os.homedir(), 'Library', 'Application Support', 'Claude', 'claude_desktop_config.json');
109
+ }
110
+
111
+ if (!fs.existsSync(path.dirname(configPath))) return false; // Claude not installed
112
+
113
+ const existing = readJsonSafe(configPath) || {};
114
+ if (!existing.mcpServers) existing.mcpServers = {};
115
+ if (existing.mcpServers['graph-indexer']) return false;
116
+
117
+ existing.mcpServers['graph-indexer'] = SERVER_CONFIG;
118
+ writeFile(configPath, JSON.stringify(existing, null, 2) + '\n');
119
+ return true;
120
+ }
121
+
122
+ function configureClaudeCode() {
123
+ // Claude Code stores MCP config in .mcp.json or .claude/settings.json at project root
124
+ const candidates = [
125
+ path.join(PROJECT_ROOT, '.claude', 'settings.json'),
126
+ path.join(PROJECT_ROOT, '.mcp.json'),
127
+ ];
128
+
129
+ // Prefer .claude/settings.json if directory exists, else .mcp.json
130
+ const hasClaudeDir = fs.existsSync(path.join(PROJECT_ROOT, '.claude'));
131
+ const configPath = hasClaudeDir ? candidates[0] : candidates[1];
132
+
133
+ const existing = readJsonSafe(configPath) || {};
134
+ const serverKey = hasClaudeDir ? 'mcpServers' : 'mcpServers';
135
+ if (!existing[serverKey]) existing[serverKey] = {};
136
+ if (existing[serverKey]['graph-indexer']) return false;
137
+
138
+ existing[serverKey]['graph-indexer'] = SERVER_CONFIG;
139
+ writeFile(configPath, JSON.stringify(existing, null, 2) + '\n');
140
+ return true;
141
+ }
142
+
143
+ // ─── package.json scripts ─────────────────────────────────────────────────────
144
+
145
+ function addPackageScripts() {
146
+ const pkgPath = path.join(PROJECT_ROOT, 'package.json');
147
+ if (!fs.existsSync(pkgPath)) return false;
148
+
149
+ const pkg = readJsonSafe(pkgPath);
150
+ if (!pkg) return false;
151
+
152
+ const scripts = pkg.scripts || {};
153
+ const toAdd = {
154
+ 'mcp:index': 'idx-index --repo .',
155
+ 'mcp:watch': 'idx-watch',
156
+ 'mcp:start': 'idx-mcp',
157
+ };
158
+
159
+ let changed = false;
160
+ for (const [k, v] of Object.entries(toAdd)) {
161
+ if (!scripts[k]) { scripts[k] = v; changed = true; }
162
+ }
163
+
164
+ if (changed) {
165
+ pkg.scripts = scripts;
166
+ writeFile(pkgPath, JSON.stringify(pkg, null, 2) + '\n');
167
+ }
168
+ return changed;
169
+ }
170
+
171
+ // ─── .gitignore ──────────────────────────────────────────────────────────────
172
+
173
+ function updateGitignore() {
174
+ const gitignorePath = path.join(PROJECT_ROOT, '.gitignore');
175
+ const existing = fs.existsSync(gitignorePath) ? fs.readFileSync(gitignorePath, 'utf-8') : '';
176
+
177
+ const entries = ['code-index.json', 'code-index.embeddings.bin'];
178
+ const toAdd = entries.filter(e => !existing.includes(e));
179
+ if (toAdd.length === 0) return false;
180
+
181
+ const newContent = existing.trimEnd() + '\n\n# graph-indexer runtime artifacts\n' + toAdd.join('\n') + '\n';
182
+ writeFile(gitignorePath, newContent);
183
+ return true;
184
+ }
185
+
186
+ // ─── Main ─────────────────────────────────────────────────────────────────────
187
+
188
+ log('\n🔧 graph-indexer init' + (isDryRun ? ' [dry-run]' : '') + '\n');
189
+ log(`📂 Project: ${PROJECT_ROOT}\n`);
190
+
191
+ const configured = [];
192
+ const skipped = [];
193
+
194
+ // IDE configuration
195
+ const ides = [
196
+ { name: 'VS Code', fn: configureVSCode },
197
+ { name: 'Cursor', fn: configureCursor },
198
+ { name: 'Claude Desktop', fn: configureClaudeDesktop },
199
+ { name: 'Claude Code', fn: configureClaudeCode },
200
+ ];
201
+
202
+ log('🔍 Detecting IDEs / agents...\n');
203
+ for (const { name, fn } of ides) {
204
+ try {
205
+ const result = fn();
206
+ if (result === false) {
207
+ skipped.push(name + ' (already configured or not installed)');
208
+ } else {
209
+ configured.push(name);
210
+ }
211
+ } catch (e) {
212
+ skipped.push(name + ' (error: ' + e.message + ')');
213
+ }
214
+ }
215
+
216
+ // Package scripts
217
+ const scriptsAdded = addPackageScripts();
218
+ if (scriptsAdded) configured.push('package.json scripts (mcp:index, mcp:watch, mcp:start)');
219
+ else skipped.push('package.json scripts (already present)');
220
+
221
+ // .gitignore
222
+ const gitignoreUpdated = updateGitignore();
223
+ if (gitignoreUpdated) configured.push('.gitignore (code-index.json, code-index.embeddings.bin)');
224
+ else skipped.push('.gitignore (already contains index entries)');
225
+
226
+ // ─── Summary ─────────────────────────────────────────────────────────────────
227
+
228
+ log('\n✅ Configured:\n');
229
+ if (configured.length) configured.forEach(c => log(' • ' + c));
230
+ else log(' (nothing new to configure)');
231
+
232
+ if (skipped.length) {
233
+ log('\n⏭️ Skipped:\n');
234
+ skipped.forEach(s => log(' • ' + s));
235
+ }
236
+
237
+ log('\n📋 Next steps:\n');
238
+ log(' 1. Run `npm run mcp:index` to index this project');
239
+ log(' 2. Restart your IDE to activate MCP servers');
240
+ log(' 3. Run `npm run mcp:start` if your IDE needs the server started manually');
241
+ log('\n');