magector 2.16.1 → 2.16.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.
Files changed (2) hide show
  1. package/package.json +5 -5
  2. package/src/mcp-server.js +80 -3
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "magector",
3
- "version": "2.16.1",
3
+ "version": "2.16.2",
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.1",
37
- "@magector/cli-linux-x64": "2.16.1",
38
- "@magector/cli-linux-arm64": "2.16.1",
39
- "@magector/cli-win32-x64": "2.16.1"
36
+ "@magector/cli-darwin-arm64": "2.16.2",
37
+ "@magector/cli-linux-x64": "2.16.2",
38
+ "@magector/cli-linux-arm64": "2.16.2",
39
+ "@magector/cli-win32-x64": "2.16.2"
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
@@ -4462,7 +4487,49 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
4462
4487
  ]
4463
4488
  }));
4464
4489
 
4465
- server.setRequestHandler(CallToolRequestSchema, async (request) => {
4490
+ /**
4491
+ * Build a reindex warning string for tool responses during background re-index.
4492
+ * Returns null when not re-indexing.
4493
+ */
4494
+ function getReindexWarning() {
4495
+ if (!reindexInProgress || !reindexStartTime) return null;
4496
+ const elapsedSec = Math.round((Date.now() - reindexStartTime) / 1000);
4497
+ const elapsedStr = elapsedSec >= 60
4498
+ ? `${Math.floor(elapsedSec / 60)}m ${elapsedSec % 60}s`
4499
+ : `${elapsedSec}s`;
4500
+
4501
+ let phaseStr, etaStr;
4502
+ if (reindexPhase <= 0) {
4503
+ phaseStr = 'initializing';
4504
+ etaStr = 'est. ~40–70 min total';
4505
+ } else if (reindexPhase === 1) {
4506
+ const filesStr = reindexTotalFiles > 0 ? ` (${reindexTotalFiles.toLocaleString('en')} files)` : '';
4507
+ phaseStr = `phase 1/3: AST parsing${filesStr}`;
4508
+ etaStr = 'est. ~1–3 min for this phase, then ~40–60 min for embeddings';
4509
+ } else if (reindexPhase === 2) {
4510
+ const itemsStr = reindexItemsToEmbed > 0 ? ` (${reindexItemsToEmbed.toLocaleString('en')} items)` : '';
4511
+ phaseStr = `phase 2/3: generating embeddings${itemsStr}`;
4512
+ if (reindexPhase2Start && reindexItemsToEmbed > 0) {
4513
+ // Empirical rate: ~87k items ≈ 45 min on 8-core ONNX. Scale linearly.
4514
+ const estimatedTotalSec = Math.round((reindexItemsToEmbed / 87000) * 45 * 60);
4515
+ const phase2Elapsed = (Date.now() - reindexPhase2Start) / 1000;
4516
+ const remainingSec = Math.max(estimatedTotalSec - phase2Elapsed, 0);
4517
+ etaStr = remainingSec > 60
4518
+ ? `est. ~${Math.round(remainingSec / 60)} min remaining`
4519
+ : 'almost done with embeddings';
4520
+ } else {
4521
+ etaStr = 'est. 30–60 min for this phase';
4522
+ }
4523
+ } else {
4524
+ phaseStr = 'phase 3/3: building HNSW vector index';
4525
+ etaStr = 'est. ~5–10 min remaining';
4526
+ }
4527
+
4528
+ return `> ⏳ **Re-indexing in progress** — ${elapsedStr} elapsed, ${phaseStr}. ${etaStr}.\n` +
4529
+ `> Results below use the **previous index** — valid, but may miss recently added files.\n\n`;
4530
+ }
4531
+
4532
+ const _callToolHandler = async (request) => {
4466
4533
  const { name, arguments: args } = request.params;
4467
4534
  const reqStart = Date.now();
4468
4535
  logToFile('REQ', `${name}(${JSON.stringify(args || {})})`);
@@ -6928,6 +6995,16 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
6928
6995
  serveQuery('feedback', { signals }).catch((err) => logToFile('WARN', `Feedback signal send failed: ${err.message}`));
6929
6996
  }
6930
6997
  }
6998
+ };
6999
+
7000
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
7001
+ const result = await _callToolHandler(request);
7002
+ // Append re-index warning to non-error responses during background re-index
7003
+ if (reindexInProgress && !result?.isError && result?.content?.[0]?.type === 'text') {
7004
+ const warning = getReindexWarning();
7005
+ if (warning) result.content[0].text = warning + result.content[0].text;
7006
+ }
7007
+ return result;
6931
7008
  });
6932
7009
 
6933
7010
  server.setRequestHandler(ListResourcesRequestSchema, async () => ({