magector 2.16.9 → 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.
package/README.md CHANGED
@@ -1029,9 +1029,15 @@ lib/internal
1029
1029
  The `magento_trace_config` tool can show actual database config values alongside code analysis. Export your `core_config_data` table as JSON and place files in `.magector/config-data/`:
1030
1030
 
1031
1031
  ```bash
1032
- # Export from MySQL (one-time per environment)
1032
+ # MySQL 8.0+ with --json flag
1033
1033
  mysql -u user -p magento_db -e "SELECT scope, scope_id, path, value FROM core_config_data" --json > .magector/config-data/CZ-production.json
1034
1034
 
1035
+ # Older MySQL (no --json): pipe through python3
1036
+ mysql -u user -p magento_db -B -e "SELECT scope, scope_id, path, value FROM core_config_data" | \
1037
+ python3 -c "import sys,json; lines=sys.stdin.read().strip().split('\n'); h=lines[0].split('\t'); \
1038
+ rows=[dict(zip(h,l.split('\t'))) for l in lines[1:]]; [r.update({'scope_id':int(r['scope_id'])}) for r in rows]; \
1039
+ json.dump(rows,sys.stdout,indent=2)" > .magector/config-data/CZ-production.json
1040
+
1035
1041
  # Or from n8n/API/any tool that produces:
1036
1042
  # [{scope, scope_id, path, value}, ...]
1037
1043
  ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "magector",
3
- "version": "2.16.9",
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.9",
37
- "@magector/cli-linux-x64": "2.16.9",
38
- "@magector/cli-linux-arm64": "2.16.9",
39
- "@magector/cli-win32-x64": "2.16.9"
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
 
@@ -3945,9 +4014,11 @@ function formatTraceConfigResult(result) {
3945
4014
  let hasData = false;
3946
4015
  try { hasData = existsSync(dataDir) && readdirSync(dataDir).some(f => f.endsWith('.json')); } catch {}
3947
4016
  if (!hasData) {
3948
- text += '### Actual Values\nNo config-data exports found. Export `core_config_data` to `.magector/config-data/{env}.json` (format: `[{scope, scope_id, path, value}, ...]`).\n\n';
4017
+ text += '### Actual Values\n**ACTION REQUIRED:** No config-data exports found. Ask the user to export `core_config_data` from their database and place the JSON file at `.magector/config-data/{country}-{environment}.json` (format: `[{scope, scope_id, path, value}, ...]`). Without this data, actual config values cannot be shown.\n\n';
3949
4018
  } else {
3950
- text += '### Actual Values\nNo matching values found in config-data exports.\n\n';
4019
+ // Show which environments ARE available so agent knows what's missing
4020
+ const availableEnvs = Object.keys(loadAllConfigData()).join(', ');
4021
+ text += `### Actual Values\nNo matching values found in available exports (${availableEnvs}). If you need values from a different environment, ask the user to provide the export.\n\n`;
3951
4022
  }
3952
4023
  }
3953
4024
 
@@ -4789,7 +4860,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
4789
4860
  },
4790
4861
  {
4791
4862
  name: 'magento_trace_config',
4792
- description: 'Trace a Magento config path end-to-end: finds system.xml admin definition (label, type, source_model), PHP classes that read the value, and actual DB values from config-data exports. Use when investigating config-driven behavior ("why is this feature enabled/disabled?", "what controls marketplace payment methods?"). Accepts either an exact config path or a keyword to search for.',
4863
+ description: 'Trace a Magento config path end-to-end: finds system.xml admin definition (label, type, source_model), PHP classes that read the value, and actual DB values from config-data exports. Use when investigating config-driven behavior ("why is this feature enabled/disabled?", "what controls marketplace payment methods?"). Accepts either an exact config path or a keyword to search for. IMPORTANT: If the output says no config-data exports are available, or if the analysis needs config values from a country/environment not yet exported, ask the user to provide the export. They can run a one-time MySQL query and place the JSON file in .magector/config-data/{country}-{environment}.json.',
4793
4864
  inputSchema: {
4794
4865
  type: 'object',
4795
4866
  properties: {
@@ -4966,10 +5037,12 @@ const _callToolHandler = async (request) => {
4966
5037
  }
4967
5038
  // SONA: record search with results for follow-up tracking
4968
5039
  sessionTracker.recordToolCall(name, args || {}, arr);
5040
+ const reindexWarn = getReindexWarning();
5041
+ const searchOutput = formatSearchResults(results.slice(0, args.limit || 5));
4969
5042
  return {
4970
5043
  content: [{
4971
5044
  type: 'text',
4972
- text: formatSearchResults(results.slice(0, args.limit || 5))
5045
+ text: reindexWarn ? `${reindexWarn}\n\n${searchOutput}` : searchOutput
4973
5046
  }]
4974
5047
  };
4975
5048
  }
@@ -6022,6 +6095,15 @@ const _callToolHandler = async (request) => {
6022
6095
  const result = await traceFlow(entryPoint, entryType, depth);
6023
6096
  result.summary = buildTraceSummary(result);
6024
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
+
6025
6107
  return {
6026
6108
  content: [{
6027
6109
  type: 'text',
@@ -6068,8 +6150,21 @@ const _callToolHandler = async (request) => {
6068
6150
  text += '\n';
6069
6151
  }
6070
6152
 
6071
- if (diResult.preferences.length === 0 && diResult.plugins.length === 0 &&
6072
- 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) {
6073
6168
  text += '_No DI configuration found for this class. It may use default Magento auto-resolution or be configured under a different name._\n';
6074
6169
  }
6075
6170