magector 2.8.1 → 2.9.0

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 +93 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "magector",
3
- "version": "2.8.1",
3
+ "version": "2.9.0",
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.8.1",
37
- "@magector/cli-linux-x64": "2.8.1",
38
- "@magector/cli-linux-arm64": "2.8.1",
39
- "@magector/cli-win32-x64": "2.8.1"
36
+ "@magector/cli-darwin-arm64": "2.9.0",
37
+ "@magector/cli-linux-x64": "2.9.0",
38
+ "@magector/cli-linux-arm64": "2.9.0",
39
+ "@magector/cli-win32-x64": "2.9.0"
40
40
  },
41
41
  "keywords": [
42
42
  "magento",
package/src/mcp-server.js CHANGED
@@ -4035,6 +4035,44 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
4035
4035
  required: ['queries']
4036
4036
  }
4037
4037
  },
4038
+ {
4039
+ name: 'magento_grep',
4040
+ description: 'Exact text search (grep) across Magento PHP/XML/JS files. Unlike magento_search (semantic/vector), this finds EVERY occurrence of a literal string or regex pattern. Use for: finding all call sites of a method, all usages of a class name, all config references. Returns file:line:content for each match.',
4041
+ inputSchema: {
4042
+ type: 'object',
4043
+ properties: {
4044
+ pattern: {
4045
+ type: 'string',
4046
+ description: 'Text pattern to search for. Literal string or POSIX regex. Examples: "getPayment()->getMethod()", "removeButton", "sales_order_place_after", "class AddressConditions"'
4047
+ },
4048
+ path: {
4049
+ type: 'string',
4050
+ description: 'Subdirectory to search in (relative to MAGENTO_ROOT). Default: "." (entire codebase). Examples: "vendor/acme/", "app/code/", "vendor/magento/module-sales/"'
4051
+ },
4052
+ include: {
4053
+ type: 'string',
4054
+ description: 'File glob pattern to include. Default: "*.php". Examples: "*.xml", "*.{php,xml}", "*.js", "*.phtml"',
4055
+ default: '*.php'
4056
+ },
4057
+ ignoreCase: {
4058
+ type: 'boolean',
4059
+ description: 'Case-insensitive search (default: false)',
4060
+ default: false
4061
+ },
4062
+ maxResults: {
4063
+ type: 'number',
4064
+ description: 'Maximum number of matches to return (default: 50, max: 200)',
4065
+ default: 50
4066
+ },
4067
+ context: {
4068
+ type: 'number',
4069
+ description: 'Lines of context around each match (default: 0). Like grep -C.',
4070
+ default: 0
4071
+ }
4072
+ },
4073
+ required: ['pattern']
4074
+ }
4075
+ },
4038
4076
  ]
4039
4077
  }));
4040
4078
 
@@ -4053,7 +4091,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
4053
4091
  // These tools have filesystem/di.xml fallbacks — work without serve process
4054
4092
  'magento_find_class', 'magento_find_method', 'magento_find_plugin',
4055
4093
  'magento_find_observer', 'magento_find_di_wiring', 'magento_module_structure',
4056
- 'magento_batch', 'magento_find_config', 'magento_find_callers'];
4094
+ 'magento_batch', 'magento_find_config', 'magento_find_callers', 'magento_grep'];
4057
4095
  if (warmupInProgress && !indexFreeTools.includes(name)) {
4058
4096
  logToFile('REQ', `${name} → blocked (warmup: loading index)`);
4059
4097
  return {
@@ -5868,6 +5906,24 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
5868
5906
  }
5869
5907
  break;
5870
5908
  }
5909
+ case 'magento_grep': {
5910
+ const searchPath = a.path || '.';
5911
+ const include = a.include || '*.php';
5912
+ const maxRes = Math.min(a.maxResults || 30, 100);
5913
+ const gArgs = ['-rn'];
5914
+ if (a.ignoreCase) gArgs.push('-i');
5915
+ if (a.context) gArgs.push('-C', String(a.context));
5916
+ for (const pat of include.split(',').map(p => p.trim())) gArgs.push('--include=' + pat);
5917
+ gArgs.push('--', a.pattern, searchPath);
5918
+ let out;
5919
+ try {
5920
+ out = execFileSync('grep', gArgs, { cwd: config.magentoRoot, encoding: 'utf-8', timeout: 15000, maxBuffer: 5 * 1024 * 1024, stdio: ['pipe', 'pipe', 'pipe'] });
5921
+ } catch (err) { out = err.stdout || ''; }
5922
+ const gLines = out.trim().split('\n').filter(Boolean);
5923
+ text = `Found ${gLines.length} matches${gLines.length > maxRes ? ` (showing ${maxRes})` : ''}:\n`;
5924
+ for (const gl of gLines.slice(0, maxRes)) text += gl + '\n';
5925
+ break;
5926
+ }
5871
5927
  default:
5872
5928
  text = `Unsupported batch tool: ${q.tool}`;
5873
5929
  }
@@ -5884,6 +5940,42 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
5884
5940
  return { content: [{ type: 'text', text }] };
5885
5941
  }
5886
5942
 
5943
+ case 'magento_grep': {
5944
+ const root = config.magentoRoot;
5945
+ if (!root) return { content: [{ type: 'text', text: 'MAGENTO_ROOT not set.' }], isError: true };
5946
+ const searchPath = args.path || '.';
5947
+ const include = args.include || '*.php';
5948
+ const maxResults = Math.min(args.maxResults || 50, 200);
5949
+ const grepArgs = ['-rn'];
5950
+ if (args.ignoreCase) grepArgs.push('-i');
5951
+ if (args.context) grepArgs.push('-C', String(args.context));
5952
+ // Support multiple include patterns (e.g., "*.{php,xml}")
5953
+ for (const pat of include.split(',').map(p => p.trim())) {
5954
+ grepArgs.push('--include=' + pat);
5955
+ }
5956
+ grepArgs.push('--', args.pattern, searchPath);
5957
+ let output;
5958
+ try {
5959
+ output = execFileSync('grep', grepArgs, {
5960
+ cwd: root, encoding: 'utf-8', timeout: 30000,
5961
+ maxBuffer: 10 * 1024 * 1024,
5962
+ stdio: ['pipe', 'pipe', 'pipe']
5963
+ });
5964
+ } catch (err) {
5965
+ // grep returns exit code 1 when no matches found
5966
+ output = err.stdout || '';
5967
+ }
5968
+ const lines = output.trim().split('\n').filter(Boolean);
5969
+ const total = lines.length;
5970
+ const truncated = lines.slice(0, maxResults);
5971
+ let text = `## grep: \`${args.pattern}\`\n`;
5972
+ text += `Found **${total}** matches${total > maxResults ? ` (showing first ${maxResults})` : ''}\n\n`;
5973
+ for (const line of truncated) {
5974
+ text += line + '\n';
5975
+ }
5976
+ return { content: [{ type: 'text', text }] };
5977
+ }
5978
+
5887
5979
  default:
5888
5980
  return {
5889
5981
  content: [{