@grec0/memory-bank-mcp 0.1.39 → 0.1.41

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.
@@ -6,7 +6,12 @@
6
6
  import * as path from "path";
7
7
  import * as fs from "fs";
8
8
  import * as crypto from "crypto";
9
+ import { fileURLToPath } from "url";
10
+ import { createRequire } from "module";
9
11
  import { encode } from "gpt-tokenizer";
12
+ const __filename = fileURLToPath(import.meta.url);
13
+ const __dirname = path.dirname(__filename);
14
+ const require = createRequire(import.meta.url);
10
15
  // Constants
11
16
  const MAX_TOKENS_PER_CHUNK = 6000;
12
17
  const DEFAULT_CHUNK_OVERLAP_TOKENS = 200;
@@ -128,6 +133,7 @@ const SEMANTIC_NODE_TYPES = {
128
133
  // Cache for loaded languages
129
134
  // Using 'any' types because web-tree-sitter's TypeScript types don't work well with dynamic imports
130
135
  let ParserClass = null;
136
+ let LanguageClass = null;
131
137
  const loadedLanguages = new Map();
132
138
  let treeSitterInitialized = false;
133
139
  /**
@@ -152,15 +158,28 @@ function generateChunkId(filePath, content, startLine) {
152
158
  * Initializes Tree-sitter WASM module
153
159
  */
154
160
  async function initTreeSitter() {
155
- if (treeSitterInitialized && ParserClass) {
161
+ if (treeSitterInitialized && ParserClass && LanguageClass) {
156
162
  return true;
157
163
  }
158
164
  try {
159
165
  // Dynamic import of web-tree-sitter
160
- // web-tree-sitter exports Parser as a named export, not default
161
166
  const TreeSitterModule = await import("web-tree-sitter");
162
- // Get Parser from named export
163
- ParserClass = TreeSitterModule.Parser;
167
+ // Handle different export formats across web-tree-sitter versions:
168
+ // - v0.20.x: exports Parser as default, Language is Parser.Language
169
+ // - v0.26.x: exports Parser and Language as separate named exports
170
+ if (TreeSitterModule.Parser) {
171
+ // v0.26.x style: named exports
172
+ ParserClass = TreeSitterModule.Parser;
173
+ LanguageClass = TreeSitterModule.Language;
174
+ }
175
+ else if (TreeSitterModule.default) {
176
+ // v0.20.x style: default export is Parser, Language is a static property
177
+ ParserClass = TreeSitterModule.default;
178
+ LanguageClass = null; // Will use ParserClass.Language after init
179
+ }
180
+ else {
181
+ throw new Error('Could not find Parser in web-tree-sitter module');
182
+ }
164
183
  if (!ParserClass) {
165
184
  throw new Error('Parser class not found in web-tree-sitter module');
166
185
  }
@@ -168,6 +187,13 @@ async function initTreeSitter() {
168
187
  if (typeof ParserClass.init === 'function') {
169
188
  await ParserClass.init();
170
189
  }
190
+ // For v0.20.x, Language becomes available after init
191
+ if (!LanguageClass && ParserClass.Language) {
192
+ LanguageClass = ParserClass.Language;
193
+ }
194
+ if (!LanguageClass) {
195
+ throw new Error('Language class not found in web-tree-sitter module');
196
+ }
171
197
  treeSitterInitialized = true;
172
198
  console.error("[AST Chunker] Tree-sitter initialized successfully");
173
199
  return true;
@@ -176,6 +202,7 @@ async function initTreeSitter() {
176
202
  console.error(`[AST Chunker] Failed to initialize Tree-sitter: ${error}`);
177
203
  treeSitterInitialized = false;
178
204
  ParserClass = null;
205
+ LanguageClass = null;
179
206
  return false;
180
207
  }
181
208
  }
@@ -187,26 +214,46 @@ function getWasmPath(language) {
187
214
  if (!wasmFile) {
188
215
  return null;
189
216
  }
190
- // Try to find in node_modules
217
+ // Build comprehensive list of possible paths
218
+ // The compiled JS runs from dist/common/, so we need to account for various scenarios:
219
+ // 1. Local development: dist/common/../node_modules = dist/node_modules (wrong)
220
+ // 2. Local development: __dirname/../../node_modules = node_modules (correct)
221
+ // 3. NPM installed package: need to resolve from the package location
222
+ // 4. Global install or monorepo: process.cwd() based paths
191
223
  const possiblePaths = [
192
- path.join(__dirname, "..", "node_modules", "tree-sitter-wasms", "out", wasmFile),
193
- path.join(process.cwd(), "node_modules", "tree-sitter-wasms", "out", wasmFile),
224
+ // From dist/common/ go up two levels to project root then into node_modules
194
225
  path.join(__dirname, "..", "..", "node_modules", "tree-sitter-wasms", "out", wasmFile),
226
+ // From current working directory
227
+ path.join(process.cwd(), "node_modules", "tree-sitter-wasms", "out", wasmFile),
228
+ // From dist/ go up one level (in case __dirname is dist/)
229
+ path.join(__dirname, "..", "node_modules", "tree-sitter-wasms", "out", wasmFile),
230
+ // Three levels up (for nested structures like dist/common/subdir)
231
+ path.join(__dirname, "..", "..", "..", "node_modules", "tree-sitter-wasms", "out", wasmFile),
195
232
  ];
233
+ // Try to use require.resolve to find the package (works in most Node.js scenarios)
234
+ try {
235
+ const treeSitterWasmsPath = require.resolve("tree-sitter-wasms/package.json");
236
+ const packageDir = path.dirname(treeSitterWasmsPath);
237
+ possiblePaths.unshift(path.join(packageDir, "out", wasmFile));
238
+ }
239
+ catch {
240
+ // Package not found via require.resolve, continue with file-based search
241
+ }
196
242
  for (const p of possiblePaths) {
197
243
  if (fs.existsSync(p)) {
198
244
  return p;
199
245
  }
200
246
  }
201
247
  console.error(`[AST Chunker] WASM file not found for ${language}: ${wasmFile}`);
248
+ console.error(`[AST Chunker] Searched paths: ${possiblePaths.map(p => `\n - ${p}`).join('')}`);
202
249
  return null;
203
250
  }
204
251
  /**
205
252
  * Loads a language parser
206
253
  */
207
254
  async function loadLanguage(language) {
208
- if (!ParserClass) {
209
- console.error("[AST Chunker] Parser not initialized");
255
+ if (!ParserClass || !LanguageClass) {
256
+ console.error("[AST Chunker] Parser or Language not initialized");
210
257
  return null;
211
258
  }
212
259
  const langKey = language.toLowerCase();
@@ -217,15 +264,19 @@ async function loadLanguage(language) {
217
264
  if (!wasmPath) {
218
265
  return null;
219
266
  }
267
+ console.error(`[AST Chunker] Attempting to load WASM from: ${wasmPath}`);
220
268
  try {
221
269
  // Use the Language.load static method
222
- const lang = await ParserClass.Language.load(wasmPath);
270
+ const lang = await LanguageClass.load(wasmPath);
223
271
  loadedLanguages.set(langKey, lang);
224
272
  console.error(`[AST Chunker] Loaded language: ${language}`);
225
273
  return lang;
226
274
  }
227
275
  catch (error) {
228
- console.error(`[AST Chunker] Failed to load language ${language}: ${error}`);
276
+ console.error(`[AST Chunker] Failed to load language ${language}: ${error instanceof Error ? error.message : error}`);
277
+ if (error instanceof Error && error.stack) {
278
+ console.error(`[AST Chunker] Stack: ${error.stack}`);
279
+ }
229
280
  return null;
230
281
  }
231
282
  }
@@ -553,5 +604,6 @@ export function disposeASTChunker() {
553
604
  loadedLanguages.clear();
554
605
  treeSitterInitialized = false;
555
606
  ParserClass = null;
607
+ LanguageClass = null;
556
608
  console.error("[AST Chunker] Disposed");
557
609
  }
@@ -214,6 +214,38 @@ export class IndexManager {
214
214
  .replace(/^-|-$/g, "");
215
215
  return sanitized || "default";
216
216
  }
217
+ /**
218
+ * Processes a batch of files concurrently
219
+ */
220
+ async processBatch(batch, startIndex, totalFiles, forceReindex, projectId) {
221
+ const batchErrors = [];
222
+ const batchChangedFiles = [];
223
+ let batchChunks = 0;
224
+ let batchProcessed = 0;
225
+ const results = await Promise.all(batch.map(async (file, index) => {
226
+ console.error(`\n[${startIndex + index + 1}/${totalFiles}] Processing ${file.path}`);
227
+ return {
228
+ file,
229
+ result: await this.indexFile(file, forceReindex, projectId)
230
+ };
231
+ }));
232
+ for (const { file, result } of results) {
233
+ if (result.error) {
234
+ batchErrors.push(result.error);
235
+ }
236
+ else {
237
+ batchProcessed++;
238
+ batchChunks += result.chunksCreated;
239
+ batchChangedFiles.push(file.path);
240
+ }
241
+ }
242
+ return {
243
+ processedCount: batchProcessed,
244
+ chunksCount: batchChunks,
245
+ changedFiles: batchChangedFiles,
246
+ errors: batchErrors
247
+ };
248
+ }
217
249
  /**
218
250
  * Indexes multiple files or a directory
219
251
  */
@@ -281,18 +313,15 @@ export class IndexManager {
281
313
  const changedFiles = [];
282
314
  let totalChunks = 0;
283
315
  let processedFiles = 0;
284
- for (let i = 0; i < filesToIndex.length; i++) {
285
- const file = filesToIndex[i];
286
- console.error(`\n[${i + 1}/${filesToIndex.length}] Processing ${file.path}`);
287
- const result = await this.indexFile(file, options.forceReindex || false, projectId);
288
- if (result.error) {
289
- errors.push(result.error);
290
- }
291
- else {
292
- processedFiles++;
293
- totalChunks += result.chunksCreated;
294
- changedFiles.push(file.path);
295
- }
316
+ // Process files in batches of 5
317
+ const batchSize = 5;
318
+ for (let i = 0; i < filesToIndex.length; i += batchSize) {
319
+ const batch = filesToIndex.slice(i, i + batchSize);
320
+ const batchResult = await this.processBatch(batch, i, filesToIndex.length, options.forceReindex || false, projectId);
321
+ processedFiles += batchResult.processedCount;
322
+ totalChunks += batchResult.chunksCount;
323
+ changedFiles.push(...batchResult.changedFiles);
324
+ errors.push(...batchResult.errors);
296
325
  }
297
326
  const indexDuration = Date.now() - startTime;
298
327
  console.error(`\n=== Indexing complete ===`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@grec0/memory-bank-mcp",
3
- "version": "0.1.39",
3
+ "version": "0.1.41",
4
4
  "description": "MCP server for semantic code indexing with Memory Bank - AI-powered codebase understanding",
5
5
  "license": "MIT",
6
6
  "author": "@grec0",
@@ -40,14 +40,14 @@
40
40
  "@lancedb/lancedb": "^0.9.0",
41
41
  "@modelcontextprotocol/sdk": "1.25.2",
42
42
  "@types/node": "^22",
43
+ "better-sqlite3": "^11.7.0",
43
44
  "gpt-tokenizer": "3.4.0",
44
45
  "ignore": "^5.3.0",
45
46
  "openai": "^4.0.0",
46
47
  "tree-sitter-wasms": "0.1.13",
47
- "web-tree-sitter": "0.26.3",
48
+ "web-tree-sitter": "0.20.8",
48
49
  "zod": "^3.22.4",
49
- "zod-to-json-schema": "^3.23.5",
50
- "better-sqlite3": "^11.7.0"
50
+ "zod-to-json-schema": "^3.23.5"
51
51
  },
52
52
  "devDependencies": {
53
53
  "@jest/globals": "^29.7.0",