magector 2.16.10 → 2.16.11

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 +96 -3
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "magector",
3
- "version": "2.16.10",
3
+ "version": "2.16.11",
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.10",
37
- "@magector/cli-linux-x64": "2.16.10",
38
- "@magector/cli-linux-arm64": "2.16.10",
39
- "@magector/cli-win32-x64": "2.16.10"
36
+ "@magector/cli-darwin-arm64": "2.16.11",
37
+ "@magector/cli-linux-x64": "2.16.11",
38
+ "@magector/cli-linux-arm64": "2.16.11",
39
+ "@magector/cli-win32-x64": "2.16.11"
40
40
  },
41
41
  "keywords": [
42
42
  "magento",
package/src/mcp-server.js CHANGED
@@ -492,6 +492,26 @@ let reindexTotalFiles = 0;
492
492
  let reindexItemsToEmbed = 0;
493
493
  let reindexPhase2Start = null;
494
494
 
495
+ /**
496
+ * Build a short reindex progress message for tool responses.
497
+ * Returns null when not reindexing.
498
+ */
499
+ function getReindexWarning() {
500
+ if (!reindexInProgress) return null;
501
+ const phases = ['initializing', 'AST analysis', 'embedding generation', 'building HNSW graph'];
502
+ const phase = phases[reindexPhase] || 'in progress';
503
+ const elapsed = reindexStartTime ? Math.round((Date.now() - reindexStartTime) / 1000) : 0;
504
+ let msg = `⚠️ Re-indexing ${phase}`;
505
+ if (elapsed > 0) msg += ` (${elapsed}s elapsed)`;
506
+ if (reindexPhase === 2 && reindexPhase2Start && reindexItemsToEmbed > 0) {
507
+ const p2elapsed = (Date.now() - reindexPhase2Start) / 1000;
508
+ const rate = p2elapsed > 0 ? Math.round(reindexItemsToEmbed * (p2elapsed / reindexItemsToEmbed)) : 0;
509
+ if (rate > 0) msg += ` — ETA ~${Math.round((reindexItemsToEmbed / (p2elapsed > 0 ? reindexItemsToEmbed / p2elapsed : 1)) * (1 - p2elapsed / rate))}s`;
510
+ }
511
+ msg += '. Results may be from previous index or incomplete.';
512
+ return msg;
513
+ }
514
+
495
515
  /**
496
516
  * Check if the database file is compatible with the current binary.
497
517
  * Uses a cached result to avoid running stats (30-60s) on every startup.
@@ -1978,6 +1998,7 @@ async function traceDependency(className, direction = 'both') {
1978
1998
  plugins: [],
1979
1999
  virtualTypes: [],
1980
2000
  argumentOverrides: [],
2001
+ argumentInjections: [],
1981
2002
  totalDiFiles: diFiles.length
1982
2003
  };
1983
2004
 
@@ -2045,6 +2066,54 @@ async function traceDependency(className, direction = 'both') {
2045
2066
  }
2046
2067
  }
2047
2068
  }
2069
+
2070
+ // Find argument injections: className used as an argument VALUE in any <type> or <virtualType> block.
2071
+ // Catches cases where an interface is injected via constructor argument, e.g.:
2072
+ // <virtualType name="SomeVT" type="SomeClass">
2073
+ // <argument name="validator" xsi:type="object">My\Interface</argument>
2074
+ // </virtualType>
2075
+ const allBlockRegex = /<(?:type|virtualType)\s+name="([^"]+)"[^>]*(?:type="([^"]+)")?[^>]*>([\s\S]*?)<\/(?:type|virtualType)>/g;
2076
+ let blockMatch;
2077
+ while ((blockMatch = allBlockRegex.exec(content)) !== null) {
2078
+ const blockName = blockMatch[1];
2079
+ const blockType = blockMatch[2] || null;
2080
+ const blockBody = blockMatch[3];
2081
+ // Skip if this block IS the target (already covered above)
2082
+ if (blockName.toLowerCase().includes(classLower)) continue;
2083
+
2084
+ // Search argument values for the className
2085
+ const objArgRegex = /<argument\s+name="([^"]+)"[^>]*xsi:type="object"[^>]*>([^<]*)<\/argument>/g;
2086
+ let oMatch;
2087
+ while ((oMatch = objArgRegex.exec(blockBody)) !== null) {
2088
+ const argValue = oMatch[2].trim();
2089
+ if (argValue.toLowerCase().includes(classLower) || argValue.toLowerCase().includes(classShort)) {
2090
+ result.argumentInjections.push({
2091
+ injectedAs: argValue,
2092
+ intoBlock: blockName,
2093
+ blockType: blockType,
2094
+ argumentName: oMatch[1],
2095
+ file: relativePath
2096
+ });
2097
+ }
2098
+ }
2099
+
2100
+ // Also check <item> values inside array arguments
2101
+ const itemRegex = /<item\s+name="([^"]+)"[^>]*xsi:type="object"[^>]*>([^<]*)<\/item>/g;
2102
+ let iMatch;
2103
+ while ((iMatch = itemRegex.exec(blockBody)) !== null) {
2104
+ const itemValue = iMatch[2].trim();
2105
+ if (itemValue.toLowerCase().includes(classLower) || itemValue.toLowerCase().includes(classShort)) {
2106
+ result.argumentInjections.push({
2107
+ injectedAs: itemValue,
2108
+ intoBlock: blockName,
2109
+ blockType: blockType,
2110
+ argumentName: iMatch[1],
2111
+ isArrayItem: true,
2112
+ file: relativePath
2113
+ });
2114
+ }
2115
+ }
2116
+ }
2048
2117
  }
2049
2118
  }
2050
2119
 
@@ -4968,10 +5037,12 @@ const _callToolHandler = async (request) => {
4968
5037
  }
4969
5038
  // SONA: record search with results for follow-up tracking
4970
5039
  sessionTracker.recordToolCall(name, args || {}, arr);
5040
+ const reindexWarn = getReindexWarning();
5041
+ const searchOutput = formatSearchResults(results.slice(0, args.limit || 5));
4971
5042
  return {
4972
5043
  content: [{
4973
5044
  type: 'text',
4974
- text: formatSearchResults(results.slice(0, args.limit || 5))
5045
+ text: reindexWarn ? `${reindexWarn}\n\n${searchOutput}` : searchOutput
4975
5046
  }]
4976
5047
  };
4977
5048
  }
@@ -6024,6 +6095,15 @@ const _callToolHandler = async (request) => {
6024
6095
  const result = await traceFlow(entryPoint, entryType, depth);
6025
6096
  result.summary = buildTraceSummary(result);
6026
6097
 
6098
+ const reindexWarn = getReindexWarning();
6099
+ if (reindexWarn) result.warning = reindexWarn;
6100
+
6101
+ // Detect empty trace — likely caused by search unavailability
6102
+ const traceKeys = Object.keys(result.trace || {});
6103
+ if (traceKeys.length === 0 && reindexInProgress) {
6104
+ result.warning = '⚠️ Re-indexing in progress — trace returned empty results. Search-dependent tracing will produce accurate results once re-indexing completes.';
6105
+ }
6106
+
6027
6107
  return {
6028
6108
  content: [{
6029
6109
  type: 'text',
@@ -6070,8 +6150,21 @@ const _callToolHandler = async (request) => {
6070
6150
  text += '\n';
6071
6151
  }
6072
6152
 
6073
- if (diResult.preferences.length === 0 && diResult.plugins.length === 0 &&
6074
- diResult.virtualTypes.length === 0 && diResult.argumentOverrides.length === 0) {
6153
+ if (diResult.argumentInjections && diResult.argumentInjections.length > 0) {
6154
+ text += `### Argument Injections (${diResult.argumentInjections.length})\n`;
6155
+ text += `_This class is injected as a constructor argument value in the following blocks:_\n`;
6156
+ for (const a of diResult.argumentInjections) {
6157
+ const blockLabel = a.blockType ? `\`${a.intoBlock}\` (type: \`${a.blockType}\`)` : `\`${a.intoBlock}\``;
6158
+ const itemTag = a.isArrayItem ? ' [array item]' : '';
6159
+ text += `- ${blockLabel} → argument \`${a.argumentName}\`${itemTag} = \`${a.injectedAs}\` (${a.file})\n`;
6160
+ }
6161
+ text += '\n';
6162
+ }
6163
+
6164
+ const totalFound = diResult.preferences.length + diResult.plugins.length +
6165
+ diResult.virtualTypes.length + diResult.argumentOverrides.length +
6166
+ (diResult.argumentInjections?.length || 0);
6167
+ if (totalFound === 0) {
6075
6168
  text += '_No DI configuration found for this class. It may use default Magento auto-resolution or be configured under a different name._\n';
6076
6169
  }
6077
6170