magector 1.2.14 → 1.2.15

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 CHANGED
@@ -1,20 +1,21 @@
1
1
  # Magector
2
2
 
3
- **Semantic code search engine for Magento 2, powered by ONNX embeddings and HNSW vector search.**
3
+ **Semantic code search engine for Magento 2 and Adobe Commerce, powered by ONNX embeddings and HNSW vector search.**
4
4
 
5
- Magector indexes an entire Magento 2 codebase and lets you search it with natural language. Instead of grepping for keywords, ask questions like *"how are checkout totals calculated?"* or *"where is the product price determined?"* and get ranked, relevant results in under 50ms.
5
+ Magector indexes an entire Magento 2 or Adobe Commerce codebase and lets you search it with natural language. Instead of grepping for keywords, ask questions like *"how are checkout totals calculated?"* or *"where is the product price determined?"* and get ranked, relevant results in under 50ms.
6
6
 
7
7
  [![Rust](https://img.shields.io/badge/rust-1.75+-orange.svg)](https://www.rust-lang.org)
8
8
  [![Node.js](https://img.shields.io/badge/node-18+-green.svg)](https://nodejs.org)
9
9
  [![Magento](https://img.shields.io/badge/magento-2.4.x-blue.svg)](https://magento.com)
10
- [![Accuracy](https://img.shields.io/badge/accuracy-94.9%25-brightgreen.svg)](#validation)
10
+ [![Adobe Commerce](https://img.shields.io/badge/adobe%20commerce-supported-blue.svg)](https://business.adobe.com/products/magento/magento-commerce.html)
11
+ [![Accuracy](https://img.shields.io/badge/accuracy-99.2%25-brightgreen.svg)](#validation)
11
12
  [![License: MIT](https://img.shields.io/badge/license-MIT-yellow.svg)](LICENSE)
12
13
 
13
14
  ---
14
15
 
15
16
  ## Why Magector
16
17
 
17
- Magento 2 has **18,000+ source files** across hundreds of modules. Finding the right code is slow:
18
+ Magento 2 and Adobe Commerce have **18,000+ source files** across hundreds of modules. Finding the right code is slow:
18
19
 
19
20
  | Approach | Finds semantic matches | Understands Magento patterns | Speed (18K files) |
20
21
  |----------|:---------------------:|:---------------------------:|:-----------------:|
@@ -29,7 +30,7 @@ Magector understands that a query about *"payment capture"* should return `Sales
29
30
 
30
31
  ## Magector vs Built-in AI Search
31
32
 
32
- Claude Code and Cursor both have built-in code search -- but they rely on keyword matching (`grep`/`ripgrep`) and file-tree heuristics. On a Magento 2 codebase with 18,000+ files, that approach breaks down fast.
33
+ Claude Code and Cursor both have built-in code search -- but they rely on keyword matching (`grep`/`ripgrep`) and file-tree heuristics. On a Magento 2 / Adobe Commerce codebase with 18,000+ files, that approach breaks down fast.
33
34
 
34
35
  | Capability | Claude Code / Cursor (built-in) | Magector |
35
36
  |---|---|---|
@@ -52,13 +53,14 @@ Without Magector, asking Claude Code or Cursor *"how are checkout totals calcula
52
53
  ## Features
53
54
 
54
55
  - **Semantic search** -- find code by meaning, not exact keywords
55
- - **94.9% accuracy** -- validated with 101 E2E test queries across 16 tool categories, plus 557 Rust-level test cases
56
+ - **99.2% accuracy** -- validated with 101 E2E test queries across 16 tool categories, plus 557 Rust-level test cases
56
57
  - **Hybrid search** -- combines semantic vector similarity with keyword re-ranking for best-of-both-worlds results
57
58
  - **Structured JSON output** -- results include file path, class name, methods list, role badges, and content snippets for minimal round-trips
58
59
  - **Persistent serve mode** -- keeps ONNX model and HNSW index resident in memory, eliminating cold-start latency
59
60
  - **ONNX embeddings** -- native 384-dim transformer embeddings via ONNX Runtime
60
- - **36K+ vectors** -- indexes the complete Magento 2 codebase including framework internals
61
+ - **36K+ vectors** -- indexes the complete Magento 2 / Adobe Commerce codebase including framework internals
61
62
  - **Magento-aware** -- understands controllers, plugins, observers, blocks, resolvers, repositories, and 20+ Magento patterns
63
+ - **Adobe Commerce compatible** -- works with both Magento Open Source and Adobe Commerce (B2B, Staging, and all Commerce-specific modules)
62
64
  - **AST-powered** -- tree-sitter parsing for PHP and JavaScript extracts classes, methods, namespaces, and inheritance
63
65
  - **Cross-tool discovery** -- tool descriptions include keywords and "See also" references so AI clients find the right tool on the first try
64
66
  - **Diff analysis** -- risk scoring and change classification for git commits and staged changes
@@ -137,10 +139,10 @@ flowchart TD
137
139
 
138
140
  - [Node.js 18+](https://nodejs.org)
139
141
 
140
- ### 1. Initialize in Your Magento Project
142
+ ### 1. Initialize in Your Project
141
143
 
142
144
  ```bash
143
- cd /path/to/your/magento2
145
+ cd /path/to/your/magento2 # or Adobe Commerce project
144
146
  npx magector init
145
147
  ```
146
148
 
@@ -267,7 +269,7 @@ npx magector help # Show help
267
269
 
268
270
  ## MCP Server Tools
269
271
 
270
- The MCP server exposes 19 tools for AI-assisted Magento development. All search tools return **structured JSON** with file paths, class names, methods, role badges, and content snippets -- enabling AI clients to parse results programmatically and minimize file-read round-trips.
272
+ The MCP server exposes 19 tools for AI-assisted Magento 2 and Adobe Commerce development. All search tools return **structured JSON** with file paths, class names, methods, role badges, and content snippets -- enabling AI clients to parse results programmatically and minimize file-read round-trips.
271
273
 
272
274
  ### Output Format
273
275
 
@@ -436,11 +438,11 @@ pie title Test Pass Rate (101 queries)
436
438
 
437
439
  | Metric | Value |
438
440
  |--------|-------|
439
- | **Grade** | **A (94.9/100)** |
441
+ | **Grade** | **A+ (99.2/100)** |
440
442
  | **Pass rate** | 101/101 (100%) |
441
- | **Precision** | 93.2% |
442
- | **MRR** | 99.2% |
443
- | **NDCG@10** | 85.5% |
443
+ | **Precision** | 98.7% |
444
+ | **MRR** | 99.3% |
445
+ | **NDCG@10** | 98.7% |
444
446
  | **Index size** | 35,795 vectors |
445
447
  | **Query time** | 10-45ms |
446
448
 
@@ -449,19 +451,20 @@ pie title Test Pass Rate (101 queries)
449
451
  | Tool | Pass | Precision | MRR | NDCG |
450
452
  |------|------|-----------|-----|------|
451
453
  | find_class | 100% | 100% | 100% | 100% |
452
- | find_method | 100% | 89% | 100% | 87% |
453
- | find_controller | 100% | 100% | 100% | -- |
454
+ | find_method | 100% | 98% | 92% | 97% |
455
+ | find_controller | 100% | 100% | 100% | 100% |
454
456
  | find_observer | 100% | 100% | 100% | 100% |
455
- | find_plugin | 100% | 96% | 100% | 100% |
457
+ | find_plugin | 100% | 100% | 100% | 100% |
456
458
  | find_preference | 100% | 100% | 100% | 100% |
457
459
  | find_api | 100% | 100% | 100% | 100% |
458
460
  | find_cron | 100% | 100% | 100% | 100% |
459
461
  | find_db_schema | 100% | 100% | 100% | 100% |
460
462
  | find_graphql | 100% | 100% | 100% | 100% |
461
463
  | find_block | 100% | 100% | 100% | 100% |
462
- | find_config | 100% | 89% | 89% | 93% |
463
- | find_template | 100% | 84% | 100% | 100% |
464
- | search | 100% | 99% | 100% | 100% |
464
+ | find_config | 100% | 100% | 100% | 100% |
465
+ | find_template | 100% | 100% | 100% | 100% |
466
+ | search | 100% | 100% | 100% | 100% |
467
+ | module_structure | 100% | 100% | 100% | 100% |
465
468
 
466
469
  ### Integration Tests
467
470
 
@@ -546,7 +549,7 @@ magector/
546
549
 
547
550
  ### 1. Indexing
548
551
 
549
- Magector scans every `.php`, `.js`, `.xml`, `.phtml`, and `.graphqls` file in a Magento codebase:
552
+ Magector scans every `.php`, `.js`, `.xml`, `.phtml`, and `.graphqls` file in a Magento 2 or Adobe Commerce codebase:
550
553
 
551
554
  1. **AST parsing** -- Tree-sitter extracts class names, namespaces, methods, inheritance, and interface implementations from PHP and JavaScript files
552
555
  2. **Pattern detection** -- Identifies Magento-specific patterns: controllers, models, repositories, plugins, observers, blocks, GraphQL resolvers, admin grids, cron jobs, and more
@@ -851,6 +854,7 @@ gantt
851
854
  JSON output :done, 2025-03, 15d
852
855
  Cross-tool hints :done, 2025-03, 15d
853
856
  E2E tests :done, 2025-03, 15d
857
+ Adobe Commerce :done, 2025-03, 15d
854
858
  section Next
855
859
  Method chunking :active, 2025-04, 30d
856
860
  Intent detection :2025-05, 30d
@@ -859,7 +863,6 @@ gantt
859
863
  section Future
860
864
  VSCode extension :2025-08, 60d
861
865
  Web UI :2025-10, 60d
862
- Commerce support :2026-01, 60d
863
866
  ```
864
867
 
865
868
  - [x] Hybrid search (semantic + keyword re-ranking)
@@ -867,13 +870,13 @@ gantt
867
870
  - [x] Structured JSON output (methods, badges, snippets)
868
871
  - [x] Cross-tool discovery hints for AI clients
869
872
  - [x] E2E accuracy test suite (101 queries)
873
+ - [x] Adobe Commerce support (B2B, Staging, and all Commerce-specific modules)
870
874
  - [ ] Method-level chunking (per-method vectors for direct method search)
871
875
  - [ ] Query intent classification (auto-detect "give me XML" vs "give me PHP")
872
876
  - [ ] Filtered search by file type at the vector level
873
877
  - [ ] Incremental indexing (only re-index changed files)
874
878
  - [ ] VSCode extension
875
879
  - [ ] Web UI for browsing results
876
- - [ ] Support for Magento 2 Commerce (B2B, Staging modules)
877
880
 
878
881
  ---
879
882
 
@@ -895,4 +898,4 @@ Contributions are welcome. Please:
895
898
 
896
899
  ---
897
900
 
898
- Built with Rust and Node.js for the Magento community.
901
+ Built with Rust and Node.js for the Magento and Adobe Commerce community.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "magector",
3
- "version": "1.2.14",
3
+ "version": "1.2.15",
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": "1.2.14",
37
- "@magector/cli-linux-x64": "1.2.14",
38
- "@magector/cli-linux-arm64": "1.2.14",
39
- "@magector/cli-win32-x64": "1.2.14"
36
+ "@magector/cli-darwin-arm64": "1.2.15",
37
+ "@magector/cli-linux-x64": "1.2.15",
38
+ "@magector/cli-linux-arm64": "1.2.15",
39
+ "@magector/cli-win32-x64": "1.2.15"
40
40
  },
41
41
  "keywords": [
42
42
  "magento",
package/src/mcp-server.js CHANGED
@@ -697,19 +697,27 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
697
697
 
698
698
  case 'magento_find_method': {
699
699
  const query = `method ${args.methodName} function ${args.className || ''}`.trim();
700
- const raw = await rustSearchAsync(query, 30);
700
+ const raw = await rustSearchAsync(query, 50);
701
701
  const methodLower = args.methodName.toLowerCase();
702
+ // Hard-filter: only results that actually define/contain this method
702
703
  let results = raw.map(normalizeResult).filter(r =>
703
704
  r.methodName?.toLowerCase() === methodLower ||
704
- r.methodName?.toLowerCase().includes(methodLower) ||
705
- r.methods?.some(m => m.toLowerCase() === methodLower || m.toLowerCase().includes(methodLower)) ||
706
- r.path?.toLowerCase().includes(methodLower)
705
+ r.methods?.some(m => m.toLowerCase() === methodLower || m.toLowerCase().includes(methodLower))
707
706
  );
707
+ // If not enough results from methods[], fall back to path-based matching
708
+ if (results.length < 3) {
709
+ const pathFallback = raw.map(normalizeResult).filter(r =>
710
+ r.path?.toLowerCase().includes(methodLower) &&
711
+ !results.some(existing => existing.path === r.path)
712
+ );
713
+ results = results.concat(pathFallback);
714
+ }
708
715
  // Boost exact method matches to top
709
716
  results = results.map(r => {
710
- const exact = r.methodName?.toLowerCase() === methodLower ||
711
- r.methods?.some(m => m.toLowerCase() === methodLower);
712
- return { ...r, score: (r.score || 0) + (exact ? 0.5 : 0) };
717
+ let bonus = 0;
718
+ if (r.methods?.some(m => m.toLowerCase() === methodLower)) bonus += 0.5;
719
+ if (r.methodName?.toLowerCase() === methodLower) bonus += 0.3;
720
+ return { ...r, score: (r.score || 0) + bonus };
713
721
  }).sort((a, b) => b.score - a.score);
714
722
  return {
715
723
  content: [{
@@ -722,18 +730,24 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
722
730
  case 'magento_find_config': {
723
731
  let query = args.query;
724
732
  if (args.configType && args.configType !== 'other') {
725
- query = `${args.configType}.xml ${args.query}`;
733
+ query = `${args.configType}.xml xml config ${args.query}`;
726
734
  }
727
- const raw = await rustSearchAsync(query, 30);
728
- const pathBoost = args.configType ? [`${args.configType}.xml`] : ['.xml'];
735
+ const raw = await rustSearchAsync(query, 100);
729
736
  let normalized = raw.map(normalizeResult);
730
- // Prefer XML results when configType is specified, but don't hard-exclude
731
- if (args.configType) {
732
- const xmlOnly = normalized.filter(r =>
733
- r.type === 'xml' || r.path?.endsWith('.xml') || r.path?.includes('.xml')
737
+ // Hard-filter to XML results
738
+ const xmlOnly = normalized.filter(r =>
739
+ r.type === 'xml' || r.path?.endsWith('.xml') || r.path?.includes('.xml')
740
+ );
741
+ if (xmlOnly.length > 0) normalized = xmlOnly;
742
+ // Hard-filter to specific config type when specified
743
+ if (args.configType && args.configType !== 'other') {
744
+ const configTypeFile = `${args.configType}.xml`;
745
+ const typeSpecific = normalized.filter(r =>
746
+ r.path?.includes(configTypeFile)
734
747
  );
735
- if (xmlOnly.length > 0) normalized = xmlOnly;
748
+ if (typeSpecific.length >= 3) normalized = typeSpecific;
736
749
  }
750
+ const pathBoost = args.configType ? [`${args.configType}.xml`] : ['.xml'];
737
751
  const results = rerank(normalized, { fileType: 'xml', pathContains: pathBoost });
738
752
  return {
739
753
  content: [{
@@ -747,8 +761,12 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
747
761
  let query = args.query;
748
762
  if (args.area) query = `${args.area} ${query}`;
749
763
  query += ' template phtml';
750
- const raw = await rustSearchAsync(query, 15);
751
- const results = rerank(raw.map(normalizeResult), { pathContains: ['.phtml'] });
764
+ const raw = await rustSearchAsync(query, 50);
765
+ // Hard-filter to .phtml template files only
766
+ let results = raw.map(normalizeResult).filter(r =>
767
+ r.path?.includes('.phtml') || r.type === 'template'
768
+ );
769
+ results = rerank(results, { pathContains: ['.phtml'] });
752
770
  return {
753
771
  content: [{
754
772
  type: 'text',
@@ -850,10 +868,18 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
850
868
  const namespaceParts = parts.map(p => p.charAt(0).toUpperCase() + p.slice(1));
851
869
  const query = `${namespaceParts.join(' ')} controller execute action`;
852
870
 
853
- const raw = await rustSearchAsync(query, 30);
871
+ const raw = await rustSearchAsync(query, 50);
872
+ // Prefer path-based controller detection, fall back to isController flag
854
873
  let results = raw.map(normalizeResult).filter(r =>
855
- r.isController || r.path?.includes('/Controller/')
874
+ r.path?.includes('/Controller/')
856
875
  );
876
+ if (results.length < 5) {
877
+ // Add isController-flagged results not already included
878
+ const extra = raw.map(normalizeResult).filter(r =>
879
+ r.isController && !results.some(e => e.path === r.path)
880
+ );
881
+ results = results.concat(extra);
882
+ }
857
883
 
858
884
  // Boost results whose path matches the route segments
859
885
  if (parts.length >= 2) {
@@ -947,11 +973,21 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
947
973
  }
948
974
 
949
975
  case 'magento_module_structure': {
950
- const raw = await rustSearchAsync(args.moduleName, 100);
951
- const moduleName = args.moduleName.replace('_', '/');
952
- const results = raw.map(normalizeResult).filter(r =>
953
- r.path?.includes(moduleName) || r.module?.includes(args.moduleName)
954
- );
976
+ const raw = await rustSearchAsync(args.moduleName, 200);
977
+ // Support both app/code (Magento/Catalog/) and vendor (module-catalog/) paths
978
+ const modulePath = args.moduleName.replace('_', '/') + '/';
979
+ const parts = args.moduleName.split('_');
980
+ const vendorPath = parts.length === 2
981
+ ? `module-${parts[1].toLowerCase()}/`
982
+ : '';
983
+ const results = raw.map(normalizeResult).filter(r => {
984
+ const path = r.path || '';
985
+ const mod = r.module || '';
986
+ // Exact module match or directory-level path match (trailing slash prevents Catalog matching CatalogRule)
987
+ return mod === args.moduleName ||
988
+ path.includes(modulePath) ||
989
+ (vendorPath && path.toLowerCase().includes(vendorPath));
990
+ });
955
991
 
956
992
  const structure = {
957
993
  controllers: results.filter(r => r.isController || r.path?.includes('/Controller/')),
@@ -970,24 +1006,39 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
970
1006
  )
971
1007
  };
972
1008
 
973
- let text = `## Module Structure: ${args.moduleName}\n\n`;
974
-
1009
+ // Build structured JSON output for module structure
1010
+ const structureOutput = {
1011
+ module: args.moduleName,
1012
+ totalFiles: results.length,
1013
+ categories: {}
1014
+ };
975
1015
  for (const [category, items] of Object.entries(structure)) {
976
1016
  if (items.length > 0) {
977
- text += `### ${category.charAt(0).toUpperCase() + category.slice(1)} (${items.length})\n`;
978
- items.slice(0, 10).forEach(item => {
979
- text += `- ${item.className || item.path} (${item.path})\n`;
980
- });
981
- if (items.length > 10) text += ` ... and ${items.length - 10} more\n`;
982
- text += '\n';
1017
+ structureOutput.categories[category] = {
1018
+ count: items.length,
1019
+ files: items.slice(0, 10).map(item => ({
1020
+ path: item.path,
1021
+ className: item.className || null,
1022
+ methods: item.methods?.length > 0 ? item.methods : undefined
1023
+ }))
1024
+ };
983
1025
  }
984
1026
  }
985
1027
 
986
- if (results.length === 0) {
987
- text += 'No code found for this module. Try re-indexing or check the module name.';
988
- }
1028
+ // Return both JSON and formatted summary
1029
+ const jsonOutput = JSON.stringify({
1030
+ results: results.slice(0, 50).map((r, i) => ({
1031
+ rank: i + 1,
1032
+ path: r.path,
1033
+ className: r.className || undefined,
1034
+ magentoType: r.magentoType || undefined,
1035
+ module: r.module || undefined
1036
+ })),
1037
+ count: results.length,
1038
+ structure: structureOutput.categories
1039
+ });
989
1040
 
990
- return { content: [{ type: 'text', text }] };
1041
+ return { content: [{ type: 'text', text: jsonOutput }] };
991
1042
  }
992
1043
 
993
1044
  case 'magento_analyze_diff': {