magector 1.0.0 → 1.2.4

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
@@ -7,7 +7,7 @@ Magector indexes an entire Magento 2 codebase and lets you search it with natura
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.4%25-brightgreen.svg)](#validation)
10
+ [![Accuracy](https://img.shields.io/badge/accuracy-96.1%25-brightgreen.svg)](#validation)
11
11
  [![License: MIT](https://img.shields.io/badge/license-MIT-yellow.svg)](LICENSE)
12
12
 
13
13
  ---
@@ -27,10 +27,32 @@ Magector understands that a query about *"payment capture"* should return `Sales
27
27
 
28
28
  ---
29
29
 
30
+ ## Magector vs Built-in AI Search
31
+
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
+
34
+ | Capability | Claude Code / Cursor (built-in) | Magector |
35
+ |---|---|---|
36
+ | **Search method** | Keyword grep / ripgrep | Semantic vector search (ONNX embeddings) |
37
+ | **Understands intent** | No — literal string matching only | Yes — "payment capture" finds `CaptureOperation.php` |
38
+ | **Magento pattern awareness** | None — treats all PHP the same | Detects controllers, plugins, observers, blocks, resolvers, cron, and 20+ patterns |
39
+ | **Query speed (18K files)** | 200-1000ms per grep pass; multiple rounds needed | 15-45ms single pass |
40
+ | **Context window cost** | Reads many wrong files → burns tokens | Returns ranked results → AI reads only what matters |
41
+ | **Works offline** | Yes | Yes — local ONNX model, no API calls |
42
+ | **Setup** | Built-in | `npx magector init` (one command) |
43
+
44
+ ### What this means in practice
45
+
46
+ Without Magector, asking Claude Code or Cursor *"how are checkout totals calculated?"* triggers multiple grep searches, reads dozens of files, and still may miss the right ones. With Magector, the AI calls `magento_search("checkout totals calculation")` and gets the exact files ranked by relevance in one step — saving tokens and time.
47
+
48
+ **Magector doesn't replace your AI tool — it gives it a better search engine.**
49
+
50
+ ---
51
+
30
52
  ## Features
31
53
 
32
54
  - **Semantic search** -- find code by meaning, not exact keywords
33
- - **94.4% accuracy** -- validated with 557 test cases across 50+ categories
55
+ - **96.1% accuracy** -- validated with 557 test cases across 50+ categories
34
56
  - **ONNX embeddings** -- native 384-dim transformer embeddings via ONNX Runtime for higher quality search
35
57
  - **Parallel processing** -- batch embedding with parallel intelligence for faster indexing
36
58
  - **Magento-aware** -- understands controllers, plugins, observers, blocks, resolvers, repositories, and 20+ Magento patterns
@@ -271,6 +293,21 @@ magento_complexity({ module: "Magento_Catalog", threshold: 10 })
271
293
 
272
294
  ---
273
295
 
296
+ ## Supported Platforms
297
+
298
+ Pre-built binaries are provided for the following platforms:
299
+
300
+ | Platform | Architecture | Package |
301
+ |----------|-------------|---------|
302
+ | macOS | ARM64 (Apple Silicon) | `@magector/cli-darwin-arm64` |
303
+ | Linux | x86_64 | `@magector/cli-linux-x64` |
304
+ | Linux | ARM64 | `@magector/cli-linux-arm64` |
305
+ | Windows | x86_64 | `@magector/cli-win32-x64` |
306
+
307
+ > **Note:** macOS Intel (x86_64) is not supported as a pre-built binary. Intel Mac users can [build from source](#building-from-source).
308
+
309
+ ---
310
+
274
311
  ## Validation
275
312
 
276
313
  Magector is validated against the complete Magento 2.4.7 codebase with **557 test cases** across **50+ categories**.
@@ -279,8 +316,8 @@ Magector is validated against the complete Magento 2.4.7 codebase with **557 tes
279
316
 
280
317
  | Metric | Value |
281
318
  |--------|-------|
282
- | **Accuracy** | **94.4%** |
283
- | Tests passed | 526 / 557 |
319
+ | **Accuracy** | **96.1%** |
320
+ | Tests passed | 535 / 557 |
284
321
  | Index size | 17,891 vectors |
285
322
  | Query time | 15-45ms |
286
323
  | Indexing time | ~3 minutes |
@@ -338,7 +375,6 @@ magector/
338
375
  │ └── mcp-server.test.js # MCP server tests (Rust core + analysis tools)
339
376
  ├── platforms/ # Platform-specific binary packages
340
377
  │ ├── darwin-arm64/ # macOS ARM (Apple Silicon)
341
- │ ├── darwin-x64/ # macOS Intel
342
378
  │ ├── linux-x64/ # Linux x64
343
379
  │ ├── linux-arm64/ # Linux ARM64
344
380
  │ └── win32-x64/ # Windows x64
@@ -596,7 +632,7 @@ struct IndexMetadata {
596
632
 
597
633
  ## Roadmap
598
634
 
599
- - [ ] Hybrid search (semantic + BM25 keyword matching)
635
+ - [x] Hybrid search (semantic + keyword re-ranking)
600
636
  - [ ] Query intent classification (auto-detect "give me XML" vs "give me PHP")
601
637
  - [ ] Filtered search by file type at the vector level
602
638
  - [ ] Incremental indexing (only re-index changed files)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "magector",
3
- "version": "1.0.0",
3
+ "version": "1.2.4",
4
4
  "description": "Semantic code search for Magento 2 — index, search, MCP server",
5
5
  "type": "module",
6
6
  "main": "src/mcp-server.js",
package/src/binary.js CHANGED
@@ -7,7 +7,7 @@
7
7
  * 3. rust-core/target/release/magector-core (dev fallback)
8
8
  * 4. magector-core in PATH
9
9
  */
10
- import { existsSync } from 'fs';
10
+ import { existsSync, chmodSync } from 'fs';
11
11
  import { execFileSync } from 'child_process';
12
12
  import path from 'path';
13
13
  import { fileURLToPath } from 'url';
@@ -33,6 +33,10 @@ export function resolveBinary() {
33
33
  const pkgDir = path.dirname(require.resolve(`${platformPkg}/package.json`));
34
34
  const binPath = path.join(pkgDir, 'bin', BINARY_NAME);
35
35
  if (existsSync(binPath)) {
36
+ // npm doesn't preserve execute permissions — ensure the binary is executable
37
+ if (process.platform !== 'win32') {
38
+ try { chmodSync(binPath, 0o755); } catch {}
39
+ }
36
40
  return binPath;
37
41
  }
38
42
  } catch {
package/src/model.js CHANGED
@@ -8,7 +8,7 @@
8
8
  *
9
9
  * Downloads from HuggingFace if not found.
10
10
  */
11
- import { existsSync, mkdirSync, createWriteStream } from 'fs';
11
+ import { existsSync, statSync, mkdirSync, createWriteStream, unlinkSync } from 'fs';
12
12
  import { get as httpsGet } from 'https';
13
13
  import path from 'path';
14
14
  import os from 'os';
@@ -60,7 +60,10 @@ export function resolveModels() {
60
60
  }
61
61
 
62
62
  function hasModels(dir) {
63
- return MODEL_FILES.every(f => existsSync(path.join(dir, f.name)));
63
+ return MODEL_FILES.every(f => {
64
+ const p = path.join(dir, f.name);
65
+ return existsSync(p) && statSync(p).size > 0;
66
+ });
64
67
  }
65
68
 
66
69
  /**
@@ -79,7 +82,8 @@ export async function ensureModels({ silent = false } = {}) {
79
82
 
80
83
  for (const file of MODEL_FILES) {
81
84
  const dest = path.join(targetDir, file.name);
82
- if (existsSync(dest)) continue;
85
+ if (existsSync(dest) && statSync(dest).size > 0) continue;
86
+ if (existsSync(dest)) unlinkSync(dest);
83
87
 
84
88
  if (!silent) {
85
89
  process.stdout.write(` ${file.description} ... `);
@@ -101,10 +105,11 @@ function downloadFile(url, dest) {
101
105
  return new Promise((resolve, reject) => {
102
106
  const file = createWriteStream(dest);
103
107
 
104
- function follow(url) {
105
- httpsGet(url, (res) => {
108
+ function follow(currentUrl) {
109
+ httpsGet(currentUrl, (res) => {
106
110
  if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
107
- follow(res.headers.location);
111
+ const next = new URL(res.headers.location, currentUrl).href;
112
+ follow(next);
108
113
  return;
109
114
  }
110
115
  if (res.statusCode !== 200) {