magector 2.7.1 → 2.7.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 +51 -3
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "magector",
3
- "version": "2.7.1",
3
+ "version": "2.7.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.7.1",
37
- "@magector/cli-linux-x64": "2.7.1",
38
- "@magector/cli-linux-arm64": "2.7.1",
39
- "@magector/cli-win32-x64": "2.7.1"
36
+ "@magector/cli-darwin-arm64": "2.7.2",
37
+ "@magector/cli-linux-x64": "2.7.2",
38
+ "@magector/cli-linux-arm64": "2.7.2",
39
+ "@magector/cli-win32-x64": "2.7.2"
40
40
  },
41
41
  "keywords": [
42
42
  "magento",
package/src/mcp-server.js CHANGED
@@ -2303,6 +2303,33 @@ async function analyzeImpact(className) {
2303
2303
  ...diTrace.virtualTypes.map(v => ({ type: 'virtualType', file: v.file, detail: `${v.name} extends ${v.type}` })),
2304
2304
  ...diTrace.argumentOverrides.map(a => ({ type: 'argument', file: a.file, detail: `${a.target}.${a.argumentName}` }))
2305
2305
  ];
2306
+ // Also find where this class is used AS a plugin/preference/virtualType implementation
2307
+ if (references.diXmlReferences.length === 0 && root) {
2308
+ const diFiles = await getDiXmlFiles(root);
2309
+ for (const { content, relPath } of diFiles) {
2310
+ if (!content.toLowerCase().includes(shortName.toLowerCase())) continue;
2311
+ // Check if class appears as plugin type
2312
+ const pluginTypeRegex = /<plugin\s+[^>]*type="([^"]+)"[^>]*\/?>/g;
2313
+ let pm;
2314
+ while ((pm = pluginTypeRegex.exec(content)) !== null) {
2315
+ if (pm[1].toLowerCase().includes(shortName.toLowerCase())) {
2316
+ // Find parent <type> to know the target
2317
+ const beforePlugin = content.slice(0, pm.index);
2318
+ const typeMatch = beforePlugin.match(/<type\s+name="([^"]+)"[^>]*>\s*$/m);
2319
+ const target = typeMatch ? typeMatch[1] : 'unknown';
2320
+ references.diXmlReferences.push({ type: 'registered-as-plugin', file: relPath, detail: `plugin on ${target} → ${pm[1]}` });
2321
+ }
2322
+ }
2323
+ // Check if class appears as preference type
2324
+ const prefRegex = /<preference\s+for="([^"]+)"\s+type="([^"]+)"\s*\/?>/g;
2325
+ let prm;
2326
+ while ((prm = prefRegex.exec(content)) !== null) {
2327
+ if (prm[2].toLowerCase().includes(shortName.toLowerCase())) {
2328
+ references.diXmlReferences.push({ type: 'registered-as-preference', file: relPath, detail: `preference for ${prm[1]} → ${prm[2]}` });
2329
+ }
2330
+ }
2331
+ }
2332
+ }
2306
2333
 
2307
2334
  // Check PHP files for direct references
2308
2335
  const escapedShort = shortName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
@@ -5789,13 +5816,34 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
5789
5816
  case 'magento_module_structure': {
5790
5817
  const raw = await rustSearchAsync(a.moduleName, 200);
5791
5818
  const modulePath = a.moduleName.replace('_', '/') + '/';
5792
- const parts = a.moduleName.split('_');
5793
- const vendorPath = parts.length === 2 ? `module-${parts[1].toLowerCase()}/` : '';
5794
- const res = raw.map(normalizeResult).filter(r => {
5819
+ const mParts = a.moduleName.split('_');
5820
+ // Hyphenate camelCase for vendor path: OrderSplit order-split
5821
+ const vendorPath = mParts.length === 2
5822
+ ? `module-${mParts[1].replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase()}/`
5823
+ : '';
5824
+ let res = raw.map(normalizeResult).filter(r => {
5795
5825
  const p = r.path || '';
5796
5826
  const mod = r.module || '';
5797
5827
  return mod === a.moduleName || p.includes(modulePath) || (vendorPath && p.toLowerCase().includes(vendorPath));
5798
5828
  });
5829
+ // Filesystem fallback
5830
+ if (res.length === 0 && config.magentoRoot && vendorPath) {
5831
+ try {
5832
+ const vendorGlob = `**/${vendorPath}**/*.{php,xml,phtml}`;
5833
+ const files = await glob(vendorGlob, { cwd: config.magentoRoot, absolute: false, nodir: true });
5834
+ for (const f of files.slice(0, 100)) {
5835
+ const entry = { path: f, score: 0.5 };
5836
+ if (f.includes('/Controller/')) entry.isController = true;
5837
+ if (f.includes('/Model/')) entry.isModel = true;
5838
+ if (f.includes('/Plugin/')) entry.isPlugin = true;
5839
+ if (f.includes('/Observer/')) entry.isObserver = true;
5840
+ if (f.endsWith('.xml')) entry.type = 'xml';
5841
+ const phpMatch = f.match(/\/([A-Z]\w+)\.php$/);
5842
+ if (phpMatch) entry.className = phpMatch[1];
5843
+ res.push(entry);
5844
+ }
5845
+ } catch {}
5846
+ }
5799
5847
  text = `Module: ${a.moduleName} (${res.length} files)\n`;
5800
5848
  const cats = { controllers: '/Controller/', models: '/Model/', plugins: '/Plugin/', observers: '/Observer/', api: '/Api/' };
5801
5849
  for (const [cat, pattern] of Object.entries(cats)) {