@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.
- package/dist/common/astChunker.js +63 -11
- package/dist/common/indexManager.js +41 -12
- package/package.json +4 -4
|
@@ -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
|
-
//
|
|
163
|
-
|
|
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
|
-
//
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
const
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
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.
|
|
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.
|
|
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",
|