magector 2.16.5 → 2.16.6

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 +43 -18
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "magector",
3
- "version": "2.16.5",
3
+ "version": "2.16.6",
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.5",
37
- "@magector/cli-linux-x64": "2.16.5",
38
- "@magector/cli-linux-arm64": "2.16.5",
39
- "@magector/cli-win32-x64": "2.16.5"
36
+ "@magector/cli-darwin-arm64": "2.16.6",
37
+ "@magector/cli-linux-x64": "2.16.6",
38
+ "@magector/cli-linux-arm64": "2.16.6",
39
+ "@magector/cli-win32-x64": "2.16.6"
40
40
  },
41
41
  "keywords": [
42
42
  "magento",
package/src/mcp-server.js CHANGED
@@ -4901,11 +4901,14 @@ const _callToolHandler = async (request) => {
4901
4901
  let tm;
4902
4902
  while ((tm = typeBlockRegex.exec(content)) !== null) {
4903
4903
  const typeName = tm[1].replace(/\\\\/g, '\\');
4904
- // FQCN: exact match. Short name: match if type ends with the short name
4905
- const typeMatches = isFqcn
4904
+ // FQCN: exact match. Short name: match last segment OR any namespace segment
4905
+ // (catches sub-namespace types like Payment\State\CaptureCommand when searching for "Payment")
4906
+ const typeSegments = typeName.split('\\').map(s => s.toLowerCase());
4907
+ const isExactMatch = isFqcn
4906
4908
  ? typeName === normalizedTarget
4907
- : typeName.split('\\').pop().toLowerCase() === shortTarget;
4908
- if (!typeMatches) continue;
4909
+ : typeSegments[typeSegments.length - 1] === shortTarget;
4910
+ const isSubNamespace = !isFqcn && !isExactMatch && typeSegments.includes(shortTarget);
4911
+ if (!isExactMatch && !isSubNamespace) continue;
4909
4912
  const block = tm[2];
4910
4913
  const pluginRegex = /<plugin\s+([^/>]*)\/?>/g;
4911
4914
  let pm;
@@ -4926,6 +4929,7 @@ const _callToolHandler = async (request) => {
4926
4929
  pluginClass: attrs.type || '',
4927
4930
  disabled: attrs.disabled === 'true',
4928
4931
  sortOrder: attrs.sortOrder || null,
4932
+ isSubNamespace,
4929
4933
  area,
4930
4934
  file: relPath
4931
4935
  });
@@ -4942,8 +4946,9 @@ const _callToolHandler = async (request) => {
4942
4946
  if (pluginFile) {
4943
4947
  reg.methods = extractPluginMethods(pluginFile);
4944
4948
  reg.resolvedFile = pluginFile.replace(fpRoot2 + '/', '');
4945
- // Read method bodies — only for targetMethod if specified (reduces token bloat)
4946
- const methodsToRead = args.targetMethod
4949
+ // Read method bodies — for exact matches, filter by targetMethod to reduce bloat
4950
+ // For sub-namespace matches, read ALL bodies (intercepted method name may differ)
4951
+ const methodsToRead = (args.targetMethod && !reg.isSubNamespace)
4947
4952
  ? reg.methods.filter(m => m.targetMethod === args.targetMethod)
4948
4953
  : reg.methods;
4949
4954
  for (const m of methodsToRead) {
@@ -4955,12 +4960,27 @@ const _callToolHandler = async (request) => {
4955
4960
  }
4956
4961
 
4957
4962
  let text = formatSearchResults(enrichedResults);
4963
+ const exactRegs = diRegistrations.filter(r => !r.isSubNamespace);
4964
+ const subNsRegs = diRegistrations.filter(r => r.isSubNamespace);
4958
4965
  if (diRegistrations.length > 0) {
4959
- text += `\n\n### DI Plugin Registrations for ${args.targetClass} (${diRegistrations.length})\n`;
4960
- for (const reg of diRegistrations) {
4966
+ if (exactRegs.length > 0) {
4967
+ text += `\n\n### DI Plugin Registrations for ${args.targetClass} (${exactRegs.length})\n`;
4968
+ }
4969
+ if (subNsRegs.length > 0) {
4970
+ if (exactRegs.length === 0) text += '\n';
4971
+ text += `\n### Plugins on ${args.targetClass} sub-classes (${subNsRegs.length})\n`;
4972
+ text += `> These intercept classes in the \\${args.targetClass}\\ namespace subtree — operations, state commands, etc.\n\n`;
4973
+ }
4974
+ const orderedRegs = [...exactRegs, ...subNsRegs];
4975
+ let inSubNs = false;
4976
+ for (const reg of orderedRegs) {
4977
+ if (!inSubNs && reg.isSubNamespace) {
4978
+ inSubNs = true;
4979
+ }
4961
4980
  const disabledTag = reg.disabled ? ' **[DISABLED]**' : '';
4962
4981
  const sortTag = reg.sortOrder ? ` (sortOrder: ${reg.sortOrder})` : '';
4963
- text += `- **${reg.pluginName}** \`${reg.pluginClass}\` [${reg.area}]${sortTag}${disabledTag} (${reg.file})\n`;
4982
+ const targetTag = reg.isSubNamespace ? ` on \`${reg.target.split('\\').pop()}\`` : '';
4983
+ text += `- **${reg.pluginName}**${targetTag} → \`${reg.pluginClass}\` [${reg.area}]${sortTag}${disabledTag} (${reg.file})\n`;
4964
4984
  if (reg.resolvedFile) {
4965
4985
  text += ` PHP: \`${reg.resolvedFile}\`\n`;
4966
4986
  }
@@ -6257,25 +6277,30 @@ const _callToolHandler = async (request) => {
6257
6277
  while ((tm = typeBlockRegex.exec(diContent)) !== null) {
6258
6278
  if (regCount >= 8) break;
6259
6279
  const typeName = tm[1].replace(/\\\\/g, '\\');
6260
- const typeMatches = isFqcn ? typeName === normalizedTarget : typeName.split('\\').pop().toLowerCase() === shortTarget;
6261
- if (!typeMatches) continue;
6280
+ const batchSegs = typeName.split('\\').map(s => s.toLowerCase());
6281
+ const isExact = isFqcn ? typeName === normalizedTarget : batchSegs[batchSegs.length - 1] === shortTarget;
6282
+ const isSubNs = !isFqcn && !isExact && batchSegs.includes(shortTarget);
6283
+ if (!isExact && !isSubNs) continue;
6262
6284
  const block = tm[2];
6263
- const pluginRegex = /<plugin\s+([^/>]*)\/?>/g;
6285
+ const pluginRegex2 = /<plugin\s+([^/>]*)\/?>/g;
6264
6286
  let pm;
6265
- while ((pm = pluginRegex.exec(block)) !== null) {
6266
- if (regCount >= 8) break;
6287
+ while ((pm = pluginRegex2.exec(block)) !== null) {
6288
+ if (regCount >= 12) break;
6267
6289
  const attrs = {};
6268
6290
  const localAttrRe = /(\w+)="([^"]*)"/g;
6269
6291
  let am2;
6270
6292
  while ((am2 = localAttrRe.exec(pm[1])) !== null) attrs[am2[1]] = am2[2];
6271
6293
  const disabled = attrs.disabled === 'true' ? ' [DISABLED]' : '';
6272
- text += `- **${attrs.name || '?'}** \`${attrs.type || '?'}\`${disabled} (${relPath})\n`;
6273
- // Only read method bodies for plugins that intercept the targetMethod
6274
- if (attrs.type && a.targetMethod) {
6294
+ const subTag = isSubNs ? ` on \`${typeName.split('\\').pop()}\`` : '';
6295
+ text += `- **${attrs.name || '?'}**${subTag} \`${attrs.type || '?'}\`${disabled} (${relPath})\n`;
6296
+ // Read method bodies: exact matches filter by targetMethod; sub-namespace reads ALL
6297
+ if (attrs.type) {
6275
6298
  const pFile = findClassFile(config.magentoRoot, attrs.type);
6276
6299
  if (pFile) {
6277
6300
  const methods = extractPluginMethods(pFile);
6278
- const relevant = methods.filter(m => m.targetMethod === a.targetMethod);
6301
+ const relevant = (a.targetMethod && !isSubNs)
6302
+ ? methods.filter(m => m.targetMethod === a.targetMethod)
6303
+ : methods;
6279
6304
  for (const m of relevant) {
6280
6305
  const body = readFullMethodBody(pFile, m.name);
6281
6306
  text += ` - \`${m.type}\` **${m.targetMethod}** → \`${m.name}()\`\n`;