magector 2.16.1 → 2.16.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/package.json +5 -5
- package/src/mcp-server.js +96 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "magector",
|
|
3
|
-
"version": "2.16.
|
|
3
|
+
"version": "2.16.3",
|
|
4
4
|
"description": "Semantic code search for Magento 2 — index, search, MCP server",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/mcp-server.js",
|
|
@@ -33,10 +33,10 @@
|
|
|
33
33
|
"ruvector": "^0.1.96"
|
|
34
34
|
},
|
|
35
35
|
"optionalDependencies": {
|
|
36
|
-
"@magector/cli-darwin-arm64": "2.16.
|
|
37
|
-
"@magector/cli-linux-x64": "2.16.
|
|
38
|
-
"@magector/cli-linux-arm64": "2.16.
|
|
39
|
-
"@magector/cli-win32-x64": "2.16.
|
|
36
|
+
"@magector/cli-darwin-arm64": "2.16.3",
|
|
37
|
+
"@magector/cli-linux-x64": "2.16.3",
|
|
38
|
+
"@magector/cli-linux-arm64": "2.16.3",
|
|
39
|
+
"@magector/cli-win32-x64": "2.16.3"
|
|
40
40
|
},
|
|
41
41
|
"keywords": [
|
|
42
42
|
"magento",
|
package/src/mcp-server.js
CHANGED
|
@@ -423,6 +423,13 @@ let reindexInProgress = false;
|
|
|
423
423
|
let reindexProcess = null;
|
|
424
424
|
let warmupInProgress = true; // true until checkDbFormat + serve process ready
|
|
425
425
|
|
|
426
|
+
// Re-index progress tracking (updated from INDEX log lines)
|
|
427
|
+
let reindexStartTime = null;
|
|
428
|
+
let reindexPhase = 0; // 0=init, 1=AST, 2=embeddings, 3=HNSW
|
|
429
|
+
let reindexTotalFiles = 0;
|
|
430
|
+
let reindexItemsToEmbed = 0;
|
|
431
|
+
let reindexPhase2Start = null;
|
|
432
|
+
|
|
426
433
|
/**
|
|
427
434
|
* Check if the database file is compatible with the current binary.
|
|
428
435
|
* Uses a cached result to avoid running stats (30-60s) on every startup.
|
|
@@ -557,6 +564,11 @@ function startBackgroundReindex() {
|
|
|
557
564
|
}
|
|
558
565
|
|
|
559
566
|
reindexInProgress = true;
|
|
567
|
+
reindexStartTime = Date.now();
|
|
568
|
+
reindexPhase = 0;
|
|
569
|
+
reindexTotalFiles = 0;
|
|
570
|
+
reindexItemsToEmbed = 0;
|
|
571
|
+
reindexPhase2Start = null;
|
|
560
572
|
|
|
561
573
|
const hadExistingDb = existsSync(config.dbPath);
|
|
562
574
|
logToFile('WARN', `Starting background re-index to temp path. Old DB ${hadExistingDb ? 'preserved for queries' : 'not found'}.`);
|
|
@@ -590,18 +602,31 @@ function startBackgroundReindex() {
|
|
|
590
602
|
// entries arrive in large chunks instead of in real time.
|
|
591
603
|
const indexStdout = createInterface({ input: reindexProcess.stdout });
|
|
592
604
|
const indexStderr = createInterface({ input: reindexProcess.stderr });
|
|
605
|
+
const parseIndexProgress = (text) => {
|
|
606
|
+
const m = text.match(/Found (\d[\d,]+) files to index/);
|
|
607
|
+
if (m) reindexTotalFiles = parseInt(m[1].replace(/,/g, ''), 10);
|
|
608
|
+
if (text.includes('PHASE 1') || text.includes('AST analyzer')) reindexPhase = 1;
|
|
609
|
+
if (text.includes('PHASE 2') || text.includes('semantic embedding') || text.includes('Generating semantic')) {
|
|
610
|
+
if (reindexPhase < 2) { reindexPhase = 2; reindexPhase2Start = Date.now(); }
|
|
611
|
+
}
|
|
612
|
+
if (text.includes('PHASE 3') || text.includes('Building HNSW') || text.includes('HNSW')) reindexPhase = 3;
|
|
613
|
+
const em = text.match(/Items to embed: (\d[\d,]+)/);
|
|
614
|
+
if (em) reindexItemsToEmbed = parseInt(em[1].replace(/,/g, ''), 10);
|
|
615
|
+
};
|
|
593
616
|
indexStdout.on('line', (line) => {
|
|
594
617
|
const text = line.replace(/\x1b\[[0-9;]*m/g, '').trim();
|
|
595
|
-
if (text) logToFile('INDEX', text);
|
|
618
|
+
if (text) { logToFile('INDEX', text); parseIndexProgress(text); }
|
|
596
619
|
});
|
|
597
620
|
indexStderr.on('line', (line) => {
|
|
598
621
|
const text = line.replace(/\x1b\[[0-9;]*m/g, '').trim();
|
|
599
|
-
if (text) logToFile('INDEX', text);
|
|
622
|
+
if (text) { logToFile('INDEX', text); parseIndexProgress(text); }
|
|
600
623
|
});
|
|
601
624
|
|
|
602
625
|
reindexProcess.on('exit', (code) => {
|
|
603
626
|
reindexInProgress = false;
|
|
604
627
|
reindexProcess = null;
|
|
628
|
+
reindexStartTime = null;
|
|
629
|
+
reindexPhase = 0;
|
|
605
630
|
removeReindexPidFile();
|
|
606
631
|
if (code === 0) {
|
|
607
632
|
// Atomic swap: old → .bak, new → current
|
|
@@ -792,6 +817,8 @@ function startServeProcess() {
|
|
|
792
817
|
// First line is ready signal
|
|
793
818
|
if (parsed.ready) {
|
|
794
819
|
serveReady = true;
|
|
820
|
+
// Clear stale cache entries — they may contain [] from when index was unavailable
|
|
821
|
+
searchCache.clear();
|
|
795
822
|
logToFile('INFO', `Serve process ready (PID ${proc.pid})`);
|
|
796
823
|
if (serveReadyResolve) { serveReadyResolve(true); serveReadyResolve = null; }
|
|
797
824
|
// Now that serve is up, persist primary lock state to data.db
|
|
@@ -927,6 +954,7 @@ function tryConnectSocket() {
|
|
|
927
954
|
|
|
928
955
|
isSocketClient = true;
|
|
929
956
|
serveReady = true;
|
|
957
|
+
searchCache.clear(); // clear stale [] entries from before socket was available
|
|
930
958
|
logToFile('INFO', 'Connected to existing serve process via socket proxy');
|
|
931
959
|
resolve(true);
|
|
932
960
|
});
|
|
@@ -4462,7 +4490,62 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
4462
4490
|
]
|
|
4463
4491
|
}));
|
|
4464
4492
|
|
|
4465
|
-
|
|
4493
|
+
/**
|
|
4494
|
+
* Build a reindex warning string for tool responses during background re-index.
|
|
4495
|
+
* Returns null when not re-indexing.
|
|
4496
|
+
*/
|
|
4497
|
+
function getReindexWarning() {
|
|
4498
|
+
// Primary instance: use in-memory state (accurate phase + ETA)
|
|
4499
|
+
if (reindexInProgress && reindexStartTime) {
|
|
4500
|
+
const elapsedSec = Math.round((Date.now() - reindexStartTime) / 1000);
|
|
4501
|
+
const elapsedStr = elapsedSec >= 60
|
|
4502
|
+
? `${Math.floor(elapsedSec / 60)}m ${elapsedSec % 60}s`
|
|
4503
|
+
: `${elapsedSec}s`;
|
|
4504
|
+
|
|
4505
|
+
let phaseStr, etaStr;
|
|
4506
|
+
if (reindexPhase <= 0) {
|
|
4507
|
+
phaseStr = 'initializing';
|
|
4508
|
+
etaStr = 'est. ~40–70 min total';
|
|
4509
|
+
} else if (reindexPhase === 1) {
|
|
4510
|
+
const filesStr = reindexTotalFiles > 0 ? ` (${reindexTotalFiles.toLocaleString('en')} files)` : '';
|
|
4511
|
+
phaseStr = `phase 1/3: AST parsing${filesStr}`;
|
|
4512
|
+
etaStr = 'est. ~1–3 min for this phase, then ~40–60 min for embeddings';
|
|
4513
|
+
} else if (reindexPhase === 2) {
|
|
4514
|
+
const itemsStr = reindexItemsToEmbed > 0 ? ` (${reindexItemsToEmbed.toLocaleString('en')} items)` : '';
|
|
4515
|
+
phaseStr = `phase 2/3: generating embeddings${itemsStr}`;
|
|
4516
|
+
if (reindexPhase2Start && reindexItemsToEmbed > 0) {
|
|
4517
|
+
// Empirical rate: ~87k items ≈ 45 min on 8-core ONNX. Scale linearly.
|
|
4518
|
+
const estimatedTotalSec = Math.round((reindexItemsToEmbed / 87000) * 45 * 60);
|
|
4519
|
+
const phase2Elapsed = (Date.now() - reindexPhase2Start) / 1000;
|
|
4520
|
+
const remainingSec = Math.max(estimatedTotalSec - phase2Elapsed, 0);
|
|
4521
|
+
etaStr = remainingSec > 60
|
|
4522
|
+
? `est. ~${Math.round(remainingSec / 60)} min remaining`
|
|
4523
|
+
: 'almost done with embeddings';
|
|
4524
|
+
} else {
|
|
4525
|
+
etaStr = 'est. 30–60 min for this phase';
|
|
4526
|
+
}
|
|
4527
|
+
} else {
|
|
4528
|
+
phaseStr = 'phase 3/3: building HNSW vector index';
|
|
4529
|
+
etaStr = 'est. ~5–10 min remaining';
|
|
4530
|
+
}
|
|
4531
|
+
|
|
4532
|
+
return `> ⏳ **Re-indexing in progress** — ${elapsedStr} elapsed, ${phaseStr}. ${etaStr}.\n` +
|
|
4533
|
+
`> Results below use the **previous index** — valid, but may miss recently added files.\n\n`;
|
|
4534
|
+
}
|
|
4535
|
+
|
|
4536
|
+
// Secondary instance: detect via PID file (no phase info available)
|
|
4537
|
+
try {
|
|
4538
|
+
const externalPid = getRunningReindexPid();
|
|
4539
|
+
if (externalPid) {
|
|
4540
|
+
return `> ⏳ **Re-indexing in progress** (PID ${externalPid}) — semantic search may return empty results until complete.\n` +
|
|
4541
|
+
`> Grep and filesystem-based tools work normally. Check \`.magector/magector.log\` for progress.\n\n`;
|
|
4542
|
+
}
|
|
4543
|
+
} catch { /* ignore */ }
|
|
4544
|
+
|
|
4545
|
+
return null;
|
|
4546
|
+
}
|
|
4547
|
+
|
|
4548
|
+
const _callToolHandler = async (request) => {
|
|
4466
4549
|
const { name, arguments: args } = request.params;
|
|
4467
4550
|
const reqStart = Date.now();
|
|
4468
4551
|
logToFile('REQ', `${name}(${JSON.stringify(args || {})})`);
|
|
@@ -6928,6 +7011,16 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
6928
7011
|
serveQuery('feedback', { signals }).catch((err) => logToFile('WARN', `Feedback signal send failed: ${err.message}`));
|
|
6929
7012
|
}
|
|
6930
7013
|
}
|
|
7014
|
+
};
|
|
7015
|
+
|
|
7016
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
7017
|
+
const result = await _callToolHandler(request);
|
|
7018
|
+
// Append re-index warning to non-error responses during background re-index
|
|
7019
|
+
if (reindexInProgress && !result?.isError && result?.content?.[0]?.type === 'text') {
|
|
7020
|
+
const warning = getReindexWarning();
|
|
7021
|
+
if (warning) result.content[0].text = warning + result.content[0].text;
|
|
7022
|
+
}
|
|
7023
|
+
return result;
|
|
6931
7024
|
});
|
|
6932
7025
|
|
|
6933
7026
|
server.setRequestHandler(ListResourcesRequestSchema, async () => ({
|