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 +27 -24
- package/package.json +5 -5
- package/src/mcp-server.js +87 -36
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
|
[](https://www.rust-lang.org)
|
|
8
8
|
[](https://nodejs.org)
|
|
9
9
|
[](https://magento.com)
|
|
10
|
-
[](https://business.adobe.com/products/magento/magento-commerce.html)
|
|
11
|
+
[](#validation)
|
|
11
12
|
[](LICENSE)
|
|
12
13
|
|
|
13
14
|
---
|
|
14
15
|
|
|
15
16
|
## Why Magector
|
|
16
17
|
|
|
17
|
-
Magento 2
|
|
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
|
-
- **
|
|
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
|
|
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 (
|
|
441
|
+
| **Grade** | **A+ (99.2/100)** |
|
|
440
442
|
| **Pass rate** | 101/101 (100%) |
|
|
441
|
-
| **Precision** |
|
|
442
|
-
| **MRR** | 99.
|
|
443
|
-
| **NDCG@10** |
|
|
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% |
|
|
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% |
|
|
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% |
|
|
463
|
-
| find_template | 100% |
|
|
464
|
-
| search | 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.
|
|
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.
|
|
37
|
-
"@magector/cli-linux-x64": "1.2.
|
|
38
|
-
"@magector/cli-linux-arm64": "1.2.
|
|
39
|
-
"@magector/cli-win32-x64": "1.2.
|
|
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,
|
|
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.
|
|
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
|
-
|
|
711
|
-
|
|
712
|
-
|
|
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,
|
|
728
|
-
const pathBoost = args.configType ? [`${args.configType}.xml`] : ['.xml'];
|
|
735
|
+
const raw = await rustSearchAsync(query, 100);
|
|
729
736
|
let normalized = raw.map(normalizeResult);
|
|
730
|
-
//
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
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 (
|
|
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,
|
|
751
|
-
|
|
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,
|
|
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.
|
|
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,
|
|
951
|
-
|
|
952
|
-
const
|
|
953
|
-
|
|
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
|
-
|
|
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
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
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
|
-
|
|
987
|
-
|
|
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': {
|