@mrxkun/mcfast-mcp 4.2.0 → 4.2.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/package.json +2 -2
- package/src/memory/memory-engine.js +2 -2
- package/src/memory/stores/base-database.js +9 -9
- package/src/memory/stores/codebase-database.js +33 -18
- package/src/memory/stores/memory-database.js +41 -31
- package/src/memory/utils/dashboard-client.js +7 -6
- package/src/memory/utils/indexer.js +8 -7
- package/src/strategies/tree-sitter/queries.js +6 -7
- package/src/utils/parallel-search.js +17 -9
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mrxkun/mcfast-mcp",
|
|
3
|
-
"version": "4.2.
|
|
3
|
+
"version": "4.2.2",
|
|
4
4
|
"description": "Ultra-fast code editing with WASM acceleration, fuzzy patching, multi-layer caching, and 8 unified tools. v4.1.12: Implement proper MCP stdio transport lifecycle and cleanup to prevent zombie processes.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -61,4 +61,4 @@
|
|
|
61
61
|
"tree-sitter-rust": "^0.24.0",
|
|
62
62
|
"web-tree-sitter": "^0.26.5"
|
|
63
63
|
}
|
|
64
|
-
}
|
|
64
|
+
}
|
|
@@ -607,8 +607,8 @@ export class MemoryEngine {
|
|
|
607
607
|
embedding = new Float32Array(cached.embedding.buffer, cached.embedding.byteOffset, cached.dimensions);
|
|
608
608
|
} else {
|
|
609
609
|
// Generate embedding
|
|
610
|
-
const
|
|
611
|
-
embedding =
|
|
610
|
+
const vector = await this.embedder.embedCode(chunk.content);
|
|
611
|
+
embedding = new Float32Array(vector);
|
|
612
612
|
|
|
613
613
|
// Cache it
|
|
614
614
|
this.memoryDb?.cacheEmbedding?.(
|
|
@@ -14,7 +14,7 @@ export class BaseDatabase {
|
|
|
14
14
|
this.db = null;
|
|
15
15
|
this.isInitialized = false;
|
|
16
16
|
this.options = options;
|
|
17
|
-
|
|
17
|
+
|
|
18
18
|
// Logger (can be replaced with proper logger)
|
|
19
19
|
this.logger = options.logger || console;
|
|
20
20
|
}
|
|
@@ -23,13 +23,13 @@ export class BaseDatabase {
|
|
|
23
23
|
if (this.isInitialized) return;
|
|
24
24
|
|
|
25
25
|
await fs.mkdir(path.dirname(this.dbPath), { recursive: true });
|
|
26
|
-
|
|
26
|
+
|
|
27
27
|
this.db = new Database(this.dbPath);
|
|
28
28
|
this.db.pragma('journal_mode = WAL');
|
|
29
|
-
|
|
29
|
+
|
|
30
30
|
this.createTables();
|
|
31
31
|
this.isInitialized = true;
|
|
32
|
-
|
|
32
|
+
|
|
33
33
|
this._log(`Initialized at: ${this.dbPath}`);
|
|
34
34
|
}
|
|
35
35
|
|
|
@@ -79,24 +79,24 @@ export class BaseDatabase {
|
|
|
79
79
|
*/
|
|
80
80
|
searchFTS(query, limit = 20) {
|
|
81
81
|
const startTime = performance.now();
|
|
82
|
-
|
|
82
|
+
|
|
83
83
|
const stmt = this.db.prepare(`
|
|
84
84
|
SELECT
|
|
85
85
|
c.*,
|
|
86
86
|
rank as bm25_score
|
|
87
87
|
FROM chunks_fts fts
|
|
88
|
-
JOIN chunks c ON fts.
|
|
88
|
+
JOIN chunks c ON fts.chunk_id = c.id
|
|
89
89
|
WHERE chunks_fts MATCH ?
|
|
90
90
|
ORDER BY rank
|
|
91
91
|
LIMIT ?
|
|
92
92
|
`);
|
|
93
|
-
|
|
93
|
+
|
|
94
94
|
const results = stmt.all(query, limit);
|
|
95
95
|
const duration = performance.now() - startTime;
|
|
96
|
-
|
|
96
|
+
|
|
97
97
|
// Log search
|
|
98
98
|
this.logSearch(query, 'fts', results.length, duration);
|
|
99
|
-
|
|
99
|
+
|
|
100
100
|
return {
|
|
101
101
|
results: results.map(r => ({
|
|
102
102
|
...r,
|
|
@@ -19,13 +19,13 @@ export class CodebaseDatabase {
|
|
|
19
19
|
if (this.isInitialized) return;
|
|
20
20
|
|
|
21
21
|
await fs.mkdir(path.dirname(this.dbPath), { recursive: true });
|
|
22
|
-
|
|
22
|
+
|
|
23
23
|
this.db = new Database(this.dbPath);
|
|
24
24
|
this.db.pragma('journal_mode = WAL');
|
|
25
|
-
|
|
25
|
+
|
|
26
26
|
this.createTables();
|
|
27
27
|
this.isInitialized = true;
|
|
28
|
-
|
|
28
|
+
|
|
29
29
|
console.error(`[CodebaseDatabase] Initialized at: ${this.dbPath}`);
|
|
30
30
|
}
|
|
31
31
|
|
|
@@ -88,26 +88,36 @@ export class CodebaseDatabase {
|
|
|
88
88
|
|
|
89
89
|
// FTS5 for code search
|
|
90
90
|
this.db.exec(`
|
|
91
|
-
|
|
91
|
+
DROP TABLE IF EXISTS chunks_fts;
|
|
92
|
+
CREATE VIRTUAL TABLE chunks_fts USING fts5(
|
|
93
|
+
chunk_id UNINDEXED,
|
|
92
94
|
content,
|
|
93
|
-
content_rowid=id,
|
|
94
95
|
tokenize='porter'
|
|
95
96
|
);
|
|
96
97
|
`);
|
|
97
98
|
|
|
99
|
+
// Backfill FTS5 index if chunks exist
|
|
100
|
+
this.db.exec(`
|
|
101
|
+
INSERT INTO chunks_fts(chunk_id, content)
|
|
102
|
+
SELECT id, content FROM chunks;
|
|
103
|
+
`);
|
|
104
|
+
|
|
98
105
|
// Triggers to sync FTS5
|
|
99
106
|
this.db.exec(`
|
|
100
|
-
|
|
101
|
-
|
|
107
|
+
DROP TRIGGER IF EXISTS chunks_ai;
|
|
108
|
+
CREATE TRIGGER chunks_ai AFTER INSERT ON chunks BEGIN
|
|
109
|
+
INSERT INTO chunks_fts(chunk_id, content) VALUES (new.id, new.content);
|
|
102
110
|
END;
|
|
103
111
|
|
|
104
|
-
|
|
105
|
-
|
|
112
|
+
DROP TRIGGER IF EXISTS chunks_ad;
|
|
113
|
+
CREATE TRIGGER chunks_ad AFTER DELETE ON chunks BEGIN
|
|
114
|
+
DELETE FROM chunks_fts WHERE chunk_id = old.id;
|
|
106
115
|
END;
|
|
107
116
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
117
|
+
DROP TRIGGER IF EXISTS chunks_au;
|
|
118
|
+
CREATE TRIGGER chunks_au AFTER UPDATE ON chunks BEGIN
|
|
119
|
+
DELETE FROM chunks_fts WHERE chunk_id = old.id;
|
|
120
|
+
INSERT INTO chunks_fts(chunk_id, content) VALUES (new.id, new.content);
|
|
111
121
|
END;
|
|
112
122
|
`);
|
|
113
123
|
|
|
@@ -202,7 +212,12 @@ export class CodebaseDatabase {
|
|
|
202
212
|
(id, file_id, type, name, line_start, line_end, signature, exported, documentation)
|
|
203
213
|
VALUES ($id, $file_id, $type, $name, $line_start, $line_end, $signature, $exported, $documentation)
|
|
204
214
|
`);
|
|
205
|
-
return stmt.run(
|
|
215
|
+
return stmt.run({
|
|
216
|
+
...fact,
|
|
217
|
+
exported: fact.exported ? 1 : 0,
|
|
218
|
+
documentation: fact.documentation || null,
|
|
219
|
+
signature: fact.signature || null
|
|
220
|
+
});
|
|
206
221
|
}
|
|
207
222
|
|
|
208
223
|
deleteFactsByFile(fileId) {
|
|
@@ -300,23 +315,23 @@ export class CodebaseDatabase {
|
|
|
300
315
|
|
|
301
316
|
searchFTS(query, limit = 20) {
|
|
302
317
|
const startTime = performance.now();
|
|
303
|
-
|
|
318
|
+
|
|
304
319
|
const stmt = this.db.prepare(`
|
|
305
320
|
SELECT
|
|
306
321
|
c.*,
|
|
307
322
|
f.path as file_path,
|
|
308
323
|
rank as bm25_score
|
|
309
324
|
FROM chunks_fts fts
|
|
310
|
-
JOIN chunks c ON fts.
|
|
325
|
+
JOIN chunks c ON fts.chunk_id = c.id
|
|
311
326
|
JOIN files f ON c.file_id = f.id
|
|
312
327
|
WHERE chunks_fts MATCH ?
|
|
313
328
|
ORDER BY rank
|
|
314
329
|
LIMIT ?
|
|
315
330
|
`);
|
|
316
|
-
|
|
331
|
+
|
|
317
332
|
const results = stmt.all(query, limit);
|
|
318
333
|
const duration = performance.now() - startTime;
|
|
319
|
-
|
|
334
|
+
|
|
320
335
|
return {
|
|
321
336
|
results: results.map(r => ({
|
|
322
337
|
chunk_id: r.id,
|
|
@@ -390,7 +405,7 @@ export class CodebaseDatabase {
|
|
|
390
405
|
const chunks = this.db.prepare('SELECT COUNT(*) as count FROM chunks').get();
|
|
391
406
|
const embeddings = this.db.prepare('SELECT COUNT(*) as count FROM embeddings').get();
|
|
392
407
|
const edits = this.db.prepare('SELECT COUNT(*) as count FROM edit_history').get();
|
|
393
|
-
|
|
408
|
+
|
|
394
409
|
return {
|
|
395
410
|
files: files.count,
|
|
396
411
|
facts: facts.count,
|
|
@@ -21,13 +21,13 @@ export class MemoryDatabase {
|
|
|
21
21
|
|
|
22
22
|
// Create directory if not exists
|
|
23
23
|
await fs.mkdir(path.dirname(this.dbPath), { recursive: true });
|
|
24
|
-
|
|
24
|
+
|
|
25
25
|
this.db = new Database(this.dbPath);
|
|
26
26
|
this.db.pragma('journal_mode = WAL');
|
|
27
|
-
|
|
27
|
+
|
|
28
28
|
this.createTables();
|
|
29
29
|
this.isInitialized = true;
|
|
30
|
-
|
|
30
|
+
|
|
31
31
|
console.error(`[MemoryDatabase] Initialized at: ${this.dbPath}`);
|
|
32
32
|
}
|
|
33
33
|
|
|
@@ -53,26 +53,36 @@ export class MemoryDatabase {
|
|
|
53
53
|
|
|
54
54
|
// FTS5 virtual table for full-text search
|
|
55
55
|
this.db.exec(`
|
|
56
|
-
|
|
56
|
+
DROP TABLE IF EXISTS chunks_fts;
|
|
57
|
+
CREATE VIRTUAL TABLE chunks_fts USING fts5(
|
|
58
|
+
chunk_id UNINDEXED,
|
|
57
59
|
content,
|
|
58
|
-
content_rowid=id,
|
|
59
60
|
tokenize='porter'
|
|
60
61
|
);
|
|
61
62
|
`);
|
|
62
63
|
|
|
64
|
+
// Backfill FTS5 index if chunks exist
|
|
65
|
+
this.db.exec(`
|
|
66
|
+
INSERT INTO chunks_fts(chunk_id, content)
|
|
67
|
+
SELECT id, content FROM chunks;
|
|
68
|
+
`);
|
|
69
|
+
|
|
63
70
|
// Triggers to keep FTS5 in sync
|
|
64
71
|
this.db.exec(`
|
|
65
|
-
|
|
66
|
-
|
|
72
|
+
DROP TRIGGER IF EXISTS chunks_ai;
|
|
73
|
+
CREATE TRIGGER chunks_ai AFTER INSERT ON chunks BEGIN
|
|
74
|
+
INSERT INTO chunks_fts(chunk_id, content) VALUES (new.id, new.content);
|
|
67
75
|
END;
|
|
68
76
|
|
|
69
|
-
|
|
70
|
-
|
|
77
|
+
DROP TRIGGER IF EXISTS chunks_ad;
|
|
78
|
+
CREATE TRIGGER chunks_ad AFTER DELETE ON chunks BEGIN
|
|
79
|
+
DELETE FROM chunks_fts WHERE chunk_id = old.id;
|
|
71
80
|
END;
|
|
72
81
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
82
|
+
DROP TRIGGER IF EXISTS chunks_au;
|
|
83
|
+
CREATE TRIGGER chunks_au AFTER UPDATE ON chunks BEGIN
|
|
84
|
+
DELETE FROM chunks_fts WHERE chunk_id = old.id;
|
|
85
|
+
INSERT INTO chunks_fts(chunk_id, content) VALUES (new.id, new.content);
|
|
76
86
|
END;
|
|
77
87
|
`);
|
|
78
88
|
|
|
@@ -138,7 +148,7 @@ export class MemoryDatabase {
|
|
|
138
148
|
(id, file_path, start_line, end_line, content, content_hash, chunk_type, created_at, updated_at)
|
|
139
149
|
VALUES ($id, $file_path, $start_line, $end_line, $content, $content_hash, $chunk_type, $created_at, $updated_at)
|
|
140
150
|
`);
|
|
141
|
-
|
|
151
|
+
|
|
142
152
|
const now = Date.now();
|
|
143
153
|
return stmt.run({
|
|
144
154
|
...chunk,
|
|
@@ -234,25 +244,25 @@ export class MemoryDatabase {
|
|
|
234
244
|
*/
|
|
235
245
|
searchFTS(query, limit = 20) {
|
|
236
246
|
const startTime = performance.now();
|
|
237
|
-
|
|
247
|
+
|
|
238
248
|
// Use FTS5 rank for scoring (BM25)
|
|
239
249
|
const stmt = this.db.prepare(`
|
|
240
250
|
SELECT
|
|
241
251
|
c.*,
|
|
242
252
|
rank as bm25_score
|
|
243
253
|
FROM chunks_fts fts
|
|
244
|
-
JOIN chunks c ON fts.
|
|
254
|
+
JOIN chunks c ON fts.chunk_id = c.id
|
|
245
255
|
WHERE chunks_fts MATCH ?
|
|
246
256
|
ORDER BY rank
|
|
247
257
|
LIMIT ?
|
|
248
258
|
`);
|
|
249
|
-
|
|
259
|
+
|
|
250
260
|
const results = stmt.all(query, limit);
|
|
251
261
|
const duration = performance.now() - startTime;
|
|
252
|
-
|
|
262
|
+
|
|
253
263
|
// Log search
|
|
254
264
|
this.logSearch(query, 'fts', results.length, duration);
|
|
255
|
-
|
|
265
|
+
|
|
256
266
|
return {
|
|
257
267
|
results: results.map(r => ({
|
|
258
268
|
...r,
|
|
@@ -275,25 +285,25 @@ export class MemoryDatabase {
|
|
|
275
285
|
const startTime = performance.now();
|
|
276
286
|
const candidateMultiplier = 4;
|
|
277
287
|
const maxCandidates = limit * candidateMultiplier;
|
|
278
|
-
|
|
288
|
+
|
|
279
289
|
// Get FTS results
|
|
280
290
|
const ftsResults = this.searchFTS(query, maxCandidates);
|
|
281
|
-
|
|
291
|
+
|
|
282
292
|
// Normalize FTS scores to 0-1
|
|
283
293
|
const ftsMap = new Map();
|
|
284
294
|
ftsResults.results.forEach(r => {
|
|
285
295
|
ftsMap.set(r.id, r.score);
|
|
286
296
|
});
|
|
287
|
-
|
|
297
|
+
|
|
288
298
|
// Normalize vector scores to 0-1
|
|
289
299
|
let maxVectorScore = 0;
|
|
290
300
|
vectorResults.forEach(r => {
|
|
291
301
|
if (r.similarity > maxVectorScore) maxVectorScore = r.similarity;
|
|
292
302
|
});
|
|
293
|
-
|
|
303
|
+
|
|
294
304
|
// Combine results
|
|
295
305
|
const combined = new Map();
|
|
296
|
-
|
|
306
|
+
|
|
297
307
|
// Add vector results
|
|
298
308
|
vectorResults.forEach(r => {
|
|
299
309
|
const normalizedVectorScore = maxVectorScore > 0 ? r.similarity / maxVectorScore : 0;
|
|
@@ -308,7 +318,7 @@ export class MemoryDatabase {
|
|
|
308
318
|
sources: ['vector']
|
|
309
319
|
});
|
|
310
320
|
});
|
|
311
|
-
|
|
321
|
+
|
|
312
322
|
// Add FTS results not in vector
|
|
313
323
|
ftsResults.results.forEach(r => {
|
|
314
324
|
if (combined.has(r.id)) {
|
|
@@ -327,20 +337,20 @@ export class MemoryDatabase {
|
|
|
327
337
|
});
|
|
328
338
|
}
|
|
329
339
|
});
|
|
330
|
-
|
|
340
|
+
|
|
331
341
|
// Calculate final scores (0.7 vector + 0.3 text)
|
|
332
342
|
const results = Array.from(combined.values()).map(r => ({
|
|
333
343
|
...r,
|
|
334
344
|
finalScore: (0.7 * r.vectorScore) + (0.3 * r.textScore)
|
|
335
345
|
}));
|
|
336
|
-
|
|
346
|
+
|
|
337
347
|
// Sort by final score and filter
|
|
338
348
|
results.sort((a, b) => b.finalScore - a.finalScore);
|
|
339
349
|
const filtered = results.filter(r => r.finalScore >= 0.35).slice(0, limit);
|
|
340
|
-
|
|
350
|
+
|
|
341
351
|
const duration = performance.now() - startTime;
|
|
342
352
|
this.logSearch(query, 'hybrid', filtered.length, duration);
|
|
343
|
-
|
|
353
|
+
|
|
344
354
|
return {
|
|
345
355
|
results: filtered,
|
|
346
356
|
metadata: {
|
|
@@ -363,9 +373,9 @@ export class MemoryDatabase {
|
|
|
363
373
|
INSERT INTO search_history (query, query_hash, method, results_count, duration_ms, timestamp)
|
|
364
374
|
VALUES (?, ?, ?, ?, ?, ?)
|
|
365
375
|
`);
|
|
366
|
-
|
|
376
|
+
|
|
367
377
|
const queryHash = crypto.createHash('md5').update(query).digest('hex');
|
|
368
|
-
|
|
378
|
+
|
|
369
379
|
stmt.run(query, queryHash, method, resultsCount, Math.round(durationMs), Date.now());
|
|
370
380
|
} catch (error) {
|
|
371
381
|
// Silent fail - don't break search for logging
|
|
@@ -393,7 +403,7 @@ export class MemoryDatabase {
|
|
|
393
403
|
const chunks = this.db.prepare('SELECT COUNT(*) as count FROM chunks').get();
|
|
394
404
|
const embeddings = this.db.prepare('SELECT COUNT(*) as count FROM embeddings').get();
|
|
395
405
|
const cache = this.db.prepare('SELECT COUNT(*) as count FROM embedding_cache').get();
|
|
396
|
-
|
|
406
|
+
|
|
397
407
|
return {
|
|
398
408
|
files: files.count,
|
|
399
409
|
chunks: chunks.count,
|
|
@@ -27,7 +27,7 @@ export class DashboardClient {
|
|
|
27
27
|
*/
|
|
28
28
|
async fetch(path, options = {}) {
|
|
29
29
|
const url = `${this.baseUrl}/api/v1${path}`;
|
|
30
|
-
|
|
30
|
+
|
|
31
31
|
try {
|
|
32
32
|
const response = await fetch(url, {
|
|
33
33
|
...options,
|
|
@@ -36,12 +36,12 @@ export class DashboardClient {
|
|
|
36
36
|
...options.headers
|
|
37
37
|
}
|
|
38
38
|
});
|
|
39
|
-
|
|
39
|
+
|
|
40
40
|
if (!response.ok) {
|
|
41
41
|
const error = await response.json().catch(() => ({ error: 'Unknown error' }));
|
|
42
42
|
throw new Error(error.error || `HTTP ${response.status}`);
|
|
43
43
|
}
|
|
44
|
-
|
|
44
|
+
|
|
45
45
|
return response.json();
|
|
46
46
|
} catch (error) {
|
|
47
47
|
console.error(`[DashboardClient] API error (${path}):`, error.message);
|
|
@@ -55,11 +55,11 @@ export class DashboardClient {
|
|
|
55
55
|
async getSearchConfig() {
|
|
56
56
|
const cacheKey = 'searchConfig';
|
|
57
57
|
const cached = this.cache.get(cacheKey);
|
|
58
|
-
|
|
58
|
+
|
|
59
59
|
if (cached && Date.now() - cached.timestamp < this.cacheTTL) {
|
|
60
60
|
return cached.data;
|
|
61
61
|
}
|
|
62
|
-
|
|
62
|
+
|
|
63
63
|
try {
|
|
64
64
|
const config = await this.fetch('/settings');
|
|
65
65
|
this.cache.set(cacheKey, { data: config, timestamp: Date.now() });
|
|
@@ -70,6 +70,7 @@ export class DashboardClient {
|
|
|
70
70
|
return {
|
|
71
71
|
enable_mercury_rerank: false,
|
|
72
72
|
smart_routing_mode: 'auto',
|
|
73
|
+
bootstrap_mode: 'hybrid',
|
|
73
74
|
rerank_threshold: 0.7,
|
|
74
75
|
monthly_budget: 20,
|
|
75
76
|
usage_this_month: 0
|
|
@@ -93,7 +94,7 @@ export class DashboardClient {
|
|
|
93
94
|
}
|
|
94
95
|
})
|
|
95
96
|
});
|
|
96
|
-
|
|
97
|
+
|
|
97
98
|
return result;
|
|
98
99
|
} catch (error) {
|
|
99
100
|
console.error('[DashboardClient] Mercury rerank failed:', error.message);
|
|
@@ -18,17 +18,17 @@ export class CodeIndexer {
|
|
|
18
18
|
|
|
19
19
|
async indexFile(filePath, content) {
|
|
20
20
|
const startTime = Date.now();
|
|
21
|
-
|
|
21
|
+
|
|
22
22
|
const language = detectLanguage(filePath);
|
|
23
23
|
const contentHash = crypto.createHash('md5').update(content).digest('hex');
|
|
24
|
-
|
|
24
|
+
|
|
25
25
|
const ast = await this.parseAST(content, filePath, language);
|
|
26
26
|
const facts = ast ? await this.extractFacts(ast, filePath, language) : [];
|
|
27
27
|
const chunks = this.chunker.chunk(content, { filePath, language });
|
|
28
28
|
const embeddings = await this.generateEmbeddings(chunks, language);
|
|
29
|
-
|
|
29
|
+
|
|
30
30
|
const duration = Date.now() - startTime;
|
|
31
|
-
|
|
31
|
+
|
|
32
32
|
return {
|
|
33
33
|
file: {
|
|
34
34
|
id: this.generateFileId(filePath),
|
|
@@ -191,19 +191,20 @@ export class CodeIndexer {
|
|
|
191
191
|
// Check if a symbol is exported
|
|
192
192
|
isExported(content, symbolName) {
|
|
193
193
|
return new RegExp(`export\\s+.*\\b${symbolName}\\b`).test(content) ||
|
|
194
|
-
|
|
194
|
+
new RegExp(`export\\s+default`).test(content);
|
|
195
195
|
}
|
|
196
196
|
|
|
197
197
|
async generateEmbeddings(chunks, language = 'javascript') {
|
|
198
198
|
const embeddings = [];
|
|
199
|
-
|
|
199
|
+
|
|
200
200
|
try {
|
|
201
201
|
for (const chunk of chunks) {
|
|
202
202
|
const vector = await this.embedder.embedCode(chunk.content, language);
|
|
203
203
|
embeddings.push({
|
|
204
204
|
chunk_id: chunk.id,
|
|
205
205
|
embedding: Buffer.from(new Float32Array(vector).buffer),
|
|
206
|
-
model: 'simple-embedder'
|
|
206
|
+
model: 'simple-embedder',
|
|
207
|
+
dimensions: vector.length
|
|
207
208
|
});
|
|
208
209
|
}
|
|
209
210
|
} catch (error) {
|
|
@@ -6,20 +6,19 @@ export const QUERIES = {
|
|
|
6
6
|
typescript: {
|
|
7
7
|
definitions: `
|
|
8
8
|
(function_declaration name: (identifier) @name) @function
|
|
9
|
-
(class_declaration name: (
|
|
10
|
-
(method_definition
|
|
11
|
-
(
|
|
9
|
+
(class_declaration name: (identifier) @name) @class
|
|
10
|
+
(method_definition name: (property_identifier) @name) @method
|
|
11
|
+
(variable_declarator name: (identifier) @name value: (arrow_function)) @arrow_function
|
|
12
12
|
(export_statement
|
|
13
13
|
(function_declaration name: (identifier) @name)) @export_function
|
|
14
14
|
(export_statement
|
|
15
|
-
(class_declaration name: (
|
|
15
|
+
(class_declaration name: (identifier) @name)) @export_class
|
|
16
16
|
(export_statement
|
|
17
17
|
(lexical_declaration
|
|
18
18
|
(variable_declarator name: (identifier) @name))) @export_const
|
|
19
19
|
`,
|
|
20
20
|
references: `
|
|
21
21
|
(identifier) @ref
|
|
22
|
-
(type_identifier) @ref
|
|
23
22
|
(property_identifier) @ref
|
|
24
23
|
(shorthand_property_identifier_pattern) @ref
|
|
25
24
|
`,
|
|
@@ -46,8 +45,8 @@ export const QUERIES = {
|
|
|
46
45
|
definitions: `
|
|
47
46
|
(function_declaration name: (identifier) @name) @function
|
|
48
47
|
(class_declaration name: (identifier) @name) @class
|
|
49
|
-
(method_definition
|
|
50
|
-
(variable_declarator name: (identifier) @name
|
|
48
|
+
(method_definition name: (property_identifier) @name) @method
|
|
49
|
+
(variable_declarator name: (identifier) @name value: (arrow_function)) @arrow_function
|
|
51
50
|
(export_statement
|
|
52
51
|
(function_declaration name: (identifier) @name)) @export_function
|
|
53
52
|
(export_statement
|
|
@@ -13,12 +13,12 @@ export class ParallelSearch {
|
|
|
13
13
|
* Returns results as soon as all complete (or timeout)
|
|
14
14
|
*/
|
|
15
15
|
async searchAll(searchFns, query, limit) {
|
|
16
|
-
const searchPromises = searchFns.map(fn =>
|
|
16
|
+
const searchPromises = searchFns.map(fn =>
|
|
17
17
|
this.executeWithTimeout(fn, query, limit)
|
|
18
18
|
);
|
|
19
19
|
|
|
20
20
|
const results = await Promise.allSettled(searchPromises);
|
|
21
|
-
|
|
21
|
+
|
|
22
22
|
return results.map((result, index) => ({
|
|
23
23
|
strategy: searchFns[index].name || `search-${index}`,
|
|
24
24
|
status: result.status,
|
|
@@ -33,7 +33,7 @@ export class ParallelSearch {
|
|
|
33
33
|
async executeWithTimeout(fn, query, limit) {
|
|
34
34
|
return Promise.race([
|
|
35
35
|
fn(query, limit),
|
|
36
|
-
new Promise((_, reject) =>
|
|
36
|
+
new Promise((_, reject) =>
|
|
37
37
|
setTimeout(() => reject(new Error('Search timeout')), this.timeout)
|
|
38
38
|
)
|
|
39
39
|
]);
|
|
@@ -44,14 +44,22 @@ export class ParallelSearch {
|
|
|
44
44
|
*/
|
|
45
45
|
mergeResults(results, maxResults = 10) {
|
|
46
46
|
const allItems = [];
|
|
47
|
-
|
|
47
|
+
|
|
48
48
|
for (const result of results) {
|
|
49
49
|
if (result.status === 'fulfilled' && result.data) {
|
|
50
|
-
|
|
50
|
+
let items = [];
|
|
51
|
+
if (Array.isArray(result.data)) {
|
|
52
|
+
items = result.data;
|
|
53
|
+
} else if (result.data.results && Array.isArray(result.data.results)) {
|
|
54
|
+
items = result.data.results;
|
|
55
|
+
} else {
|
|
56
|
+
items = [result.data];
|
|
57
|
+
}
|
|
58
|
+
|
|
51
59
|
allItems.push(...items.map(item => ({
|
|
52
60
|
...item,
|
|
53
61
|
_source: result.strategy,
|
|
54
|
-
_score: item.score || item.similarity || item.confidence || 0.5
|
|
62
|
+
_score: item.score || item.similarity || item.confidence || item.bm25_score || 0.5
|
|
55
63
|
})));
|
|
56
64
|
}
|
|
57
65
|
}
|
|
@@ -96,7 +104,7 @@ export class ParallelSearch {
|
|
|
96
104
|
query,
|
|
97
105
|
limit
|
|
98
106
|
);
|
|
99
|
-
|
|
107
|
+
|
|
100
108
|
results = [...results, ...slowResults];
|
|
101
109
|
return this.mergeResults(results, limit);
|
|
102
110
|
}
|
|
@@ -108,10 +116,10 @@ export class ParallelSearch {
|
|
|
108
116
|
// Helper for MemoryEngine parallel search
|
|
109
117
|
export async function parallelMemorySearch(engine, query, limit) {
|
|
110
118
|
const parallel = new ParallelSearch();
|
|
111
|
-
|
|
119
|
+
|
|
112
120
|
const strategies = [
|
|
113
121
|
{ fn: (q, l) => engine.searchFacts(q, l), name: 'facts', fast: true },
|
|
114
|
-
{ fn: (q, l) => engine.searchFTS(q, l), name: 'fts', fast: true },
|
|
122
|
+
{ fn: (q, l) => engine.codebaseDb?.searchFTS?.(q, l) || { results: [] }, name: 'fts', fast: true },
|
|
115
123
|
{ fn: (q, l) => engine.searchVector(q, l), name: 'vector', fast: false }
|
|
116
124
|
];
|
|
117
125
|
|