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 +7 -1
- package/package.json +5 -5
- package/src/mcp-server.js +101 -6
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
|
-
#
|
|
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.
|
|
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.
|
|
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.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\
|
|
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
|
-
|
|
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:
|
|
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.
|
|
6072
|
-
|
|
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
|
|