magector 1.7.1 → 2.1.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.
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  **Technology-aware MCP server for Magento 2 and Adobe Commerce with intelligent indexing and search.**
4
4
 
5
- Magector is a Model Context Protocol (MCP) server that deeply understands Magento 2 and Adobe Commerce. It builds a semantic vector index of your entire codebase — 18,000+ files across hundreds of modules — and exposes 21 tools that let AI assistants search, navigate, and understand the code with domain-specific intelligence. Instead of grepping for keywords, your AI asks *"how are checkout totals calculated?"* and gets ranked, relevant results in under 50ms, enriched with Magento pattern detection (plugins, observers, controllers, DI preferences, layout XML, and 20+ more).
5
+ Magector is a Model Context Protocol (MCP) server that deeply understands Magento 2 and Adobe Commerce. It builds a semantic vector index of your entire codebase — 18,000+ files across hundreds of modules — and exposes 28 tools that let AI assistants search, navigate, and understand the code with domain-specific intelligence. Instead of grepping for keywords, your AI asks *"how are checkout totals calculated?"* and gets ranked, relevant results in under 50ms, enriched with Magento pattern detection (plugins, observers, controllers, DI preferences, layout XML, and 20+ more).
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)
@@ -58,7 +58,7 @@ The result: your AI assistant calls one MCP tool and gets ranked, pattern-enrich
58
58
  - **Complexity analysis** -- cyclomatic complexity, function count, and hotspot detection across modules
59
59
  - **Fast** -- 10-45ms queries via persistent serve process, batched ONNX embedding with adaptive thread scaling
60
60
  - **LLM description enrichment** -- generate natural-language descriptions of di.xml files using Claude, stored in SQLite, and prepend them to embedding text so descriptions influence vector search ranking (not just post-retrieval display)
61
- - **MCP server** -- 21 tools integrating with Claude Code, Cursor, and any MCP-compatible AI tool
61
+ - **MCP server** -- 28 tools integrating with Claude Code, Cursor, and any MCP-compatible AI tool
62
62
  - **Clean architecture** -- Rust core handles all indexing/search, Node.js MCP server delegates to it
63
63
 
64
64
  ---
@@ -70,7 +70,7 @@ flowchart LR
70
70
  subgraph node ["Node.js Layer"]
71
71
  direction TB
72
72
  G["CLI<br/>init · index · search · describe"]
73
- E["MCP Server<br/>21 tools · LRU cache"]
73
+ E["MCP Server<br/>28 tools · LRU cache"]
74
74
  F["Persistent Serve Process"]
75
75
  G --> F
76
76
  E --> F
@@ -347,11 +347,29 @@ For very large or CPU-constrained runs, you may also need to extend the wall-clo
347
347
  MAGECTOR_INDEX_TIMEOUT=28800000 npx magector index --threads 2 # 8 h timeout, 2 threads
348
348
  ```
349
349
 
350
+ ### Resume after timeout or interrupt
351
+
352
+ Indexing writes a crash-safe checkpoint to disk every 50 batches (~12,800 files). If the process is killed or times out mid-run, **just re-run `npx magector index`** — it auto-resumes from the last checkpoint:
353
+
354
+ ```bash
355
+ npx magector index
356
+ # ♻️ Resuming from previous run: 38400 vectors across 12200 files already indexed
357
+ # ✓ Found 79771 total files; 12200 already indexed, 67571 remaining to process
358
+ ```
359
+
360
+ The indexer collects already-embedded file paths from the existing DB, filters them out of file discovery, preserves the existing HNSW state, and only parses/embeds the files that aren't in the DB yet. Partial resume also picks up new files added to the tree since the previous run.
361
+
362
+ To force a full rebuild (e.g. after a schema change or if you want to discard stale vectors), pass `--force`:
363
+
364
+ ```bash
365
+ npx magector index --force
366
+ ```
367
+
350
368
  ---
351
369
 
352
370
  ## MCP Server Tools
353
371
 
354
- The MCP server exposes 21 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.
372
+ The MCP server exposes 28 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.
355
373
 
356
374
  ### Output Format
357
375
 
@@ -407,14 +425,31 @@ All search tools return structured JSON:
407
425
  | `magento_find_cron` | Find cron job definitions in crontab.xml |
408
426
  | `magento_find_db_schema` | Find database table definitions in db_schema.xml (declarative schema) |
409
427
 
410
- ### Flow Tracing
428
+ ### Flow & Dependency Tracing
411
429
 
412
430
  | Tool | Description |
413
431
  |------|-------------|
414
432
  | `magento_trace_flow` | Trace execution flow from an entry point (route, API, GraphQL, event, cron) -- maps controller → plugins → observers → templates in one call |
433
+ | `magento_trace_dependency` | Trace DI graph for a class/interface -- preferences, plugins, virtualTypes, argument overrides (parses all di.xml, no index needed) |
434
+ | `magento_find_event_flow` | Trace complete event chain: dispatchers → observers → handler PHP classes (parses events.xml + vector search) |
435
+ | `magento_find_layout` | Find layout XML files by handle or content -- lists blocks, containers, and referenceBlock declarations |
415
436
 
416
437
  Auto-detects entry type from pattern (`/V1/...` → API, `snake_case` → event, `camelCase` → GraphQL, `path/segments` → route), or override with `entryType`. Use `depth: "shallow"` (entry + config + plugins) or `depth: "deep"` (adds observers, layout, templates, DI preferences).
417
438
 
439
+ ### Impact & Testing
440
+
441
+ | Tool | Description |
442
+ |------|-------------|
443
+ | `magento_impact_analysis` | Analyze impact of changing a class -- finds use statements, DI references, direct instantiations, and type hints across the codebase |
444
+ | `magento_find_test` | Find PHPUnit tests for a given class/method -- searches Test/ directories for coverage, mocks, and assertions |
445
+
446
+ ### Diagnostics
447
+
448
+ | Tool | Description |
449
+ |------|-------------|
450
+ | `magento_error_parser` | Parse Magento error messages and map to root cause, affected files, and fix suggestions (10 known patterns) |
451
+ | `magento_performance_profile` | Profile a Magento subsystem (checkout_totals, order_place, product_save, etc.) for performance bottlenecks -- plugins, observers, and complexity hotspots |
452
+
418
453
  ### Analysis Tools
419
454
 
420
455
  | Tool | Description |
@@ -431,6 +466,13 @@ Auto-detects entry type from pattern (`/V1/...` → API, `snake_case` → event,
431
466
  | `magento_describe` | Generate LLM descriptions for di.xml files (requires `ANTHROPIC_API_KEY`), stored in SQLite, auto-reindexes affected files |
432
467
  | `magento_stats` | View index statistics |
433
468
 
469
+ ### Search Enhancements (v2.1)
470
+
471
+ - **Hybrid BM25+vector search** -- combines text frequency scoring with semantic vector similarity for better exact class name matches
472
+ - **Query expansion** -- automatically expands queries with Magento domain synonyms (plugin → interceptor, checkout → cart/quote/totals, etc.)
473
+ - **Module filtering** -- `moduleFilter` parameter on `magento_search` to limit results by vendor/module pattern (supports wildcards, e.g., `"Vendor_*"`)
474
+ - **Non-blocking reindex** -- old index stays usable during background rebuild; new index is built to a temp path and swapped in atomically on completion
475
+
434
476
  ### Tool Cross-References
435
477
 
436
478
  Each tool description includes "See also" hints to help AI clients chain tools effectively:
@@ -458,6 +500,14 @@ graph LR
458
500
  trc -.-> tpl
459
501
  trc -.-> api
460
502
  trc -.-> gql
503
+ dep["trace_dependency"] --> prf
504
+ dep --> plg
505
+ evf["find_event_flow"] --> obs
506
+ imp["impact_analysis"] --> dep
507
+ imp --> cls
508
+ tst["find_test"] --> cls
509
+ err["error_parser"] --> dep
510
+ lay["find_layout"] --> blk
461
511
 
462
512
  style cls fill:#4a90d9,color:#fff
463
513
  style mtd fill:#4a90d9,color:#fff
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "magector",
3
- "version": "1.7.1",
3
+ "version": "2.1.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",
@@ -20,7 +20,9 @@
20
20
  "validate:verbose": "node src/cli.js validate --verbose",
21
21
  "validate:keep": "node src/cli.js validate --verbose --keep",
22
22
  "benchmark": "node src/cli.js benchmark",
23
- "test": "node tests/mcp-server.test.js",
23
+ "test": "node tests/unit.test.js && node tests/mcp-server.test.js",
24
+ "test:unit": "node tests/unit.test.js",
25
+ "test:integration": "node tests/mcp-server.test.js",
24
26
  "test:no-index": "node tests/mcp-server.test.js --no-index",
25
27
  "test:accuracy": "node tests/mcp-accuracy.test.js",
26
28
  "test:accuracy:verbose": "node tests/mcp-accuracy.test.js --verbose",
@@ -32,15 +34,15 @@
32
34
  "dependencies": {
33
35
  "@modelcontextprotocol/sdk": "^1.0.0",
34
36
  "chalk": "^5.3.0",
35
- "glob": "^10.3.10",
37
+ "glob": "^13.0.0",
36
38
  "ora": "^8.0.1",
37
39
  "ruvector": "^0.1.96"
38
40
  },
39
41
  "optionalDependencies": {
40
- "@magector/cli-darwin-arm64": "1.7.1",
41
- "@magector/cli-linux-x64": "1.7.1",
42
- "@magector/cli-linux-arm64": "1.7.1",
43
- "@magector/cli-win32-x64": "1.7.1"
42
+ "@magector/cli-darwin-arm64": "2.1.0",
43
+ "@magector/cli-linux-x64": "2.1.0",
44
+ "@magector/cli-linux-arm64": "2.1.0",
45
+ "@magector/cli-win32-x64": "2.1.0"
44
46
  },
45
47
  "keywords": [
46
48
  "magento",
package/src/cli.js CHANGED
@@ -40,7 +40,9 @@ Index options:
40
40
  system responsive during indexing.
41
41
  --batch-size <n> Embedding batch size (default: 256). Higher = faster
42
42
  but more RAM.
43
- --force Force re-index even if index exists
43
+ --force Discard any existing index and rebuild from scratch.
44
+ Without --force, indexing auto-resumes from the last
45
+ incremental save (written every ~50 batches).
44
46
 
45
47
  Environment Variables:
46
48
  MAGENTO_ROOT Path to Magento installation (default: cwd)
@@ -122,6 +124,12 @@ async function runIndex(targetPath, opts = {}) {
122
124
  if (opts.batchSize != null) {
123
125
  indexArgs.push('--batch-size', String(opts.batchSize));
124
126
  }
127
+ // --force discards any existing partial index and rebuilds from scratch.
128
+ // Without it, the Rust indexer auto-resumes from the last incremental
129
+ // save on disk and only re-embeds files that aren't in the DB yet.
130
+ if (opts.force) {
131
+ indexArgs.push('--force');
132
+ }
125
133
  // Pass descriptions DB if it exists
126
134
  const descDbPath = path.resolve(root, '.magector', 'sqlite.db');
127
135
  if (existsSync(descDbPath)) {
@@ -137,10 +145,12 @@ async function runIndex(targetPath, opts = {}) {
137
145
  if (err.message && err.message.includes('ETIMEDOUT')) {
138
146
  console.error(
139
147
  `Indexing timed out after ${indexTimeout / 1000}s.\n` +
140
- `For large codebases or CPU-constrained environments, increase the timeout:\n` +
148
+ `Partial progress was saved to disk every ~50 batches — re-run\n` +
149
+ `'npx magector index' to auto-resume from the last checkpoint.\n` +
150
+ `\n` +
151
+ `For large codebases or CPU-constrained environments, also consider:\n` +
141
152
  ` MAGECTOR_INDEX_TIMEOUT=28800000 npx magector index # 8 hours\n` +
142
- `Or reduce CPU usage with fewer threads:\n` +
143
- ` npx magector index --threads 2`
153
+ ` npx magector index --threads 2 # lower CPU usage`
144
154
  );
145
155
  } else {
146
156
  console.error(`Indexing error: ${err.message}`);
@@ -247,20 +257,44 @@ async function main() {
247
257
 
248
258
  case 'index': {
249
259
  // First non-flag arg after `index` is the path; everything else is options.
260
+ // Must skip values belonging to flags (e.g., "4" in "--threads 4").
250
261
  const indexArgv = args.slice(1);
251
- const targetPath = indexArgv.find(a => !a.startsWith('-'));
252
262
  const indexOpts = parseArgs(indexArgv);
263
+ let targetPath = undefined;
264
+ for (let i = 0; i < indexArgv.length; i++) {
265
+ if (indexArgv[i] === '--threads' || indexArgv[i] === '--batch-size') {
266
+ i++; // skip the flag's value
267
+ } else if (indexArgv[i].startsWith('-')) {
268
+ // skip boolean flags like --force, --verbose
269
+ } else {
270
+ targetPath = indexArgv[i];
271
+ break; // first non-flag, non-value arg is the path
272
+ }
273
+ }
253
274
  await runIndex(targetPath, indexOpts);
254
275
  break;
255
276
  }
256
277
 
257
278
  case 'search': {
258
- const query = args.slice(1).filter(a => !a.startsWith('-')).join(' ');
279
+ // Build query from non-flag arguments, skipping values that belong to flags
280
+ const searchArgv = args.slice(1);
281
+ const queryParts = [];
282
+ for (let i = 0; i < searchArgv.length; i++) {
283
+ if (searchArgv[i] === '-l' || searchArgv[i] === '--limit' ||
284
+ searchArgv[i] === '-f' || searchArgv[i] === '--format') {
285
+ i++; // skip the flag's value
286
+ } else if (searchArgv[i].startsWith('-')) {
287
+ // skip boolean flags like -v, --verbose
288
+ } else {
289
+ queryParts.push(searchArgv[i]);
290
+ }
291
+ }
292
+ const query = queryParts.join(' ');
259
293
  if (!query) {
260
294
  console.error('Usage: npx magector search <query>');
261
295
  process.exit(1);
262
296
  }
263
- const opts = parseArgs(args.slice(1));
297
+ const opts = parseArgs(searchArgv);
264
298
  runSearch(query, opts);
265
299
  break;
266
300
  }
package/src/init.js CHANGED
@@ -265,6 +265,9 @@ export async function init(projectPath, opts = {}) {
265
265
  if (opts.batchSize != null) {
266
266
  indexArgs.push('--batch-size', String(opts.batchSize));
267
267
  }
268
+ if (opts.force) {
269
+ indexArgs.push('--force');
270
+ }
268
271
  execFileSync(binary, indexArgs, { timeout: initTimeout, stdio: 'inherit' });
269
272
  } catch (err) {
270
273
  if (err.status) {