magector 2.6.5 → 2.7.1

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 +78 -3
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "magector",
3
- "version": "2.6.5",
3
+ "version": "2.7.1",
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.6.5",
37
- "@magector/cli-linux-x64": "2.6.5",
38
- "@magector/cli-linux-arm64": "2.6.5",
39
- "@magector/cli-win32-x64": "2.6.5"
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"
40
40
  },
41
41
  "keywords": [
42
42
  "magento",
package/src/mcp-server.js CHANGED
@@ -5640,7 +5640,54 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
5640
5640
  const res = raw.map(normalizeResult).filter(r =>
5641
5641
  r.magentoType === 'plugin' || r.path?.toLowerCase().includes('plugin')
5642
5642
  );
5643
- text = formatSearchResults(res.slice(0, 10));
5643
+ text = formatSearchResults(res.slice(0, 3));
5644
+ // DI registrations + method bodies (compact: only targetMethod bodies)
5645
+ if (a.targetClass) {
5646
+ const diFiles = await getDiXmlFiles(config.magentoRoot);
5647
+ const normalizedTarget = a.targetClass.replace(/\\\\/g, '\\');
5648
+ const isFqcn = normalizedTarget.includes('\\');
5649
+ const shortTarget = normalizedTarget.split('\\').pop().toLowerCase();
5650
+ let regCount = 0;
5651
+ text += '\n\n### DI Registrations\n';
5652
+ for (const { content: diContent, relPath } of diFiles) {
5653
+ if (regCount >= 8) break;
5654
+ if (!diContent.includes(isFqcn ? normalizedTarget : a.targetClass)) continue;
5655
+ const typeBlockRegex = /<type\s+name="([^"]+)"[^>]*>([\s\S]*?)<\/type>/g;
5656
+ let tm;
5657
+ while ((tm = typeBlockRegex.exec(diContent)) !== null) {
5658
+ if (regCount >= 8) break;
5659
+ const typeName = tm[1].replace(/\\\\/g, '\\');
5660
+ const typeMatches = isFqcn ? typeName === normalizedTarget : typeName.split('\\').pop().toLowerCase() === shortTarget;
5661
+ if (!typeMatches) continue;
5662
+ const block = tm[2];
5663
+ const pluginRegex = /<plugin\s+([^/>]*)\/?>/g;
5664
+ let pm;
5665
+ while ((pm = pluginRegex.exec(block)) !== null) {
5666
+ if (regCount >= 8) break;
5667
+ const attrs = {};
5668
+ const localAttrRe = /(\w+)="([^"]*)"/g;
5669
+ let am2;
5670
+ while ((am2 = localAttrRe.exec(pm[1])) !== null) attrs[am2[1]] = am2[2];
5671
+ const disabled = attrs.disabled === 'true' ? ' [DISABLED]' : '';
5672
+ text += `- **${attrs.name || '?'}** → \`${attrs.type || '?'}\`${disabled} (${relPath})\n`;
5673
+ // Only read method bodies for plugins that intercept the targetMethod
5674
+ if (attrs.type && a.targetMethod) {
5675
+ const pFile = findClassFile(config.magentoRoot, attrs.type);
5676
+ if (pFile) {
5677
+ const methods = extractPluginMethods(pFile);
5678
+ const relevant = methods.filter(m => m.targetMethod === a.targetMethod);
5679
+ for (const m of relevant) {
5680
+ const body = readFullMethodBody(pFile, m.name);
5681
+ text += ` - \`${m.type}\` **${m.targetMethod}** → \`${m.name}()\`\n`;
5682
+ if (body) text += ' ' + '```php\n ' + body.split('\n').join('\n ') + '\n ' + '```\n';
5683
+ }
5684
+ }
5685
+ }
5686
+ regCount++;
5687
+ }
5688
+ }
5689
+ }
5690
+ }
5644
5691
  break;
5645
5692
  }
5646
5693
  case 'magento_find_observer': {
@@ -5702,9 +5749,37 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
5702
5749
  let res = raw.map(normalizeResult).filter(r =>
5703
5750
  r.methodName?.toLowerCase() === ml || r.methods?.some(m => m.toLowerCase() === ml)
5704
5751
  );
5752
+ // Filesystem fallback: grep -rl for method signature
5753
+ if (res.length === 0 && config.magentoRoot) {
5754
+ const methodSig = `function ${a.methodName}(`;
5755
+ const classShort = a.className ? a.className.split('\\').pop() : null;
5756
+ try {
5757
+ let files = [];
5758
+ if (classShort) {
5759
+ files = await glob(`**/${classShort}.php`, { cwd: config.magentoRoot, absolute: false, nodir: true });
5760
+ } else {
5761
+ const grepResult = execFileSync('grep', ['-rl', '--include=*.php', methodSig, '.'],
5762
+ { cwd: config.magentoRoot, encoding: 'utf-8', timeout: 15000, stdio: ['pipe', 'pipe', 'pipe'] });
5763
+ files = grepResult.trim().split('\n').filter(Boolean).map(f => f.replace(/^\.\//, ''));
5764
+ }
5765
+ for (const f of files.slice(0, 10)) {
5766
+ const absP = f.startsWith('/') ? f : path.join(config.magentoRoot, f);
5767
+ let content;
5768
+ try { content = readFileSync(absP, 'utf-8'); } catch { continue; }
5769
+ if (!content.includes(methodSig)) continue;
5770
+ let cn = null;
5771
+ const nsM = content.match(/namespace\s+([\w\\]+)/);
5772
+ const clM = content.match(/class\s+(\w+)/);
5773
+ if (nsM && clM) cn = nsM[1] + '\\' + clM[1];
5774
+ const body = readFullMethodBody(absP, a.methodName);
5775
+ res.push({ path: f, className: cn, methodName: a.methodName, score: 0.5, fullMethodBody: body || undefined });
5776
+ if (res.length >= 5) break;
5777
+ }
5778
+ } catch {}
5779
+ }
5705
5780
  for (const r of res.slice(0, 5)) {
5706
- if (r.path?.endsWith('.php')) {
5707
- const body = readFullMethodBody(r.path, a.methodName);
5781
+ if (r.path?.endsWith('.php') && !r.fullMethodBody) {
5782
+ const body = readFullMethodBody(path.join(config.magentoRoot, r.path), a.methodName);
5708
5783
  if (body) r.fullMethodBody = body;
5709
5784
  }
5710
5785
  }