magector 2.13.0 → 2.13.1
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/package.json +5 -5
- package/src/mcp-server.js +59 -31
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "magector",
|
|
3
|
-
"version": "2.13.
|
|
3
|
+
"version": "2.13.1",
|
|
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.13.
|
|
37
|
-
"@magector/cli-linux-x64": "2.13.
|
|
38
|
-
"@magector/cli-linux-arm64": "2.13.
|
|
39
|
-
"@magector/cli-win32-x64": "2.13.
|
|
36
|
+
"@magector/cli-darwin-arm64": "2.13.1",
|
|
37
|
+
"@magector/cli-linux-x64": "2.13.1",
|
|
38
|
+
"@magector/cli-linux-arm64": "2.13.1",
|
|
39
|
+
"@magector/cli-win32-x64": "2.13.1"
|
|
40
40
|
},
|
|
41
41
|
"keywords": [
|
|
42
42
|
"magento",
|
package/src/mcp-server.js
CHANGED
|
@@ -3439,9 +3439,11 @@ const ENRICHMENT_DB_PATH = (root) => path.join(root, '.magector', 'enrichment.db
|
|
|
3439
3439
|
function hasNullGuard(lines, matchLineIdx, receiverExpr, guardRadius = 6) {
|
|
3440
3440
|
const start = Math.max(0, matchLineIdx - guardRadius);
|
|
3441
3441
|
const end = Math.min(lines.length - 1, matchLineIdx + guardRadius);
|
|
3442
|
+
const matchLine = lines[matchLineIdx] || '';
|
|
3442
3443
|
const window = lines.slice(start, end + 1).join('\n');
|
|
3443
3444
|
|
|
3444
|
-
if
|
|
3445
|
+
// ?-> only counts if it's on the same line as the chain (avoid false positives from unrelated variables)
|
|
3446
|
+
if (matchLine.includes('?->')) return true;
|
|
3445
3447
|
if (/\?\?|\?:/.test(window)) return true;
|
|
3446
3448
|
|
|
3447
3449
|
if (receiverExpr) {
|
|
@@ -3455,7 +3457,7 @@ function hasNullGuard(lines, matchLineIdx, receiverExpr, guardRadius = 6) {
|
|
|
3455
3457
|
* Scan vendor/ PHP files for ->first()->second() chains and store null-safety
|
|
3456
3458
|
* analysis in enrichment.db. Called by magento_enrich and after magento_index.
|
|
3457
3459
|
*/
|
|
3458
|
-
async function enrichMethodChains(root
|
|
3460
|
+
async function enrichMethodChains(root) {
|
|
3459
3461
|
const dbPath = ENRICHMENT_DB_PATH(root);
|
|
3460
3462
|
|
|
3461
3463
|
// Use node:sqlite (built-in, no deps)
|
|
@@ -3497,36 +3499,63 @@ async function enrichMethodChains(root, options = {}) {
|
|
|
3497
3499
|
);
|
|
3498
3500
|
const deleteFile = db.prepare('DELETE FROM method_chains WHERE file = ?');
|
|
3499
3501
|
|
|
3500
|
-
//
|
|
3501
|
-
|
|
3502
|
-
|
|
3503
|
-
|
|
3504
|
-
|
|
3505
|
-
|
|
3506
|
-
|
|
3507
|
-
|
|
3508
|
-
|
|
3502
|
+
// Build line-offset index for O(1) line number lookups
|
|
3503
|
+
function buildLineIndex(content) {
|
|
3504
|
+
const offsets = [0];
|
|
3505
|
+
let idx = 0;
|
|
3506
|
+
while ((idx = content.indexOf('\n', idx)) !== -1) {
|
|
3507
|
+
idx++;
|
|
3508
|
+
offsets.push(idx);
|
|
3509
|
+
}
|
|
3510
|
+
return offsets;
|
|
3511
|
+
}
|
|
3509
3512
|
|
|
3510
|
-
|
|
3511
|
-
let
|
|
3512
|
-
while (
|
|
3513
|
-
const
|
|
3514
|
-
|
|
3515
|
-
file: relPath, line: lineNum,
|
|
3516
|
-
chain: `->${m[2]}()->${m[3]}()`,
|
|
3517
|
-
firstMethod: m[2], secondMethod: m[3],
|
|
3518
|
-
hasNullGuard: hasNullGuard(lines, lineNum - 1, m[1]) ? 1 : 0
|
|
3519
|
-
});
|
|
3520
|
-
chains++;
|
|
3513
|
+
function lineFromOffset(offsets, charIndex) {
|
|
3514
|
+
let lo = 0, hi = offsets.length - 1;
|
|
3515
|
+
while (lo < hi) {
|
|
3516
|
+
const mid = (lo + hi + 1) >> 1;
|
|
3517
|
+
if (offsets[mid] <= charIndex) lo = mid; else hi = mid - 1;
|
|
3521
3518
|
}
|
|
3519
|
+
return lo + 1; // 1-based
|
|
3520
|
+
}
|
|
3521
|
+
|
|
3522
|
+
db.exec('BEGIN');
|
|
3523
|
+
try {
|
|
3524
|
+
for (const phpFile of phpFiles) {
|
|
3525
|
+
let content;
|
|
3526
|
+
try { content = readFileSync(phpFile, 'utf-8'); } catch { continue; }
|
|
3527
|
+
if (!content.includes('->')) continue;
|
|
3528
|
+
|
|
3529
|
+
const relPath = phpFile.replace(root + '/', '');
|
|
3530
|
+
const lines = content.split('\n');
|
|
3531
|
+
const lineOffsets = buildLineIndex(content);
|
|
3532
|
+
const rows = [];
|
|
3522
3533
|
|
|
3523
|
-
|
|
3524
|
-
|
|
3525
|
-
|
|
3526
|
-
|
|
3534
|
+
chainRegex.lastIndex = 0;
|
|
3535
|
+
let m;
|
|
3536
|
+
while ((m = chainRegex.exec(content)) !== null) {
|
|
3537
|
+
const lineNum = lineFromOffset(lineOffsets, m.index);
|
|
3538
|
+
rows.push({
|
|
3539
|
+
file: relPath, line: lineNum,
|
|
3540
|
+
chain: `->${m[2]}()->${m[3]}()`,
|
|
3541
|
+
firstMethod: m[2], secondMethod: m[3],
|
|
3542
|
+
hasNullGuard: hasNullGuard(lines, lineNum - 1, m[1]) ? 1 : 0
|
|
3543
|
+
});
|
|
3544
|
+
chains++;
|
|
3527
3545
|
}
|
|
3546
|
+
|
|
3547
|
+
if (rows.length > 0) {
|
|
3548
|
+
deleteFile.run(relPath);
|
|
3549
|
+
for (const r of rows) {
|
|
3550
|
+
insertStmt.run(r.file, r.line, r.chain, r.firstMethod, r.secondMethod, r.hasNullGuard, now);
|
|
3551
|
+
}
|
|
3552
|
+
}
|
|
3553
|
+
scanned++;
|
|
3528
3554
|
}
|
|
3529
|
-
|
|
3555
|
+
db.exec('COMMIT');
|
|
3556
|
+
} catch (err) {
|
|
3557
|
+
db.exec('ROLLBACK');
|
|
3558
|
+
throw err;
|
|
3530
3559
|
}
|
|
3531
3560
|
|
|
3532
3561
|
db.close();
|
|
@@ -4736,7 +4765,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
4736
4765
|
const root = args.path || config.magentoRoot;
|
|
4737
4766
|
const output = rustIndex(root);
|
|
4738
4767
|
// Auto-enrich after indexing: runs in background, doesn't block response
|
|
4739
|
-
enrichMethodChains(root
|
|
4768
|
+
enrichMethodChains(root).then(({ scanned, chains }) => {
|
|
4740
4769
|
logToFile('INFO', `Auto-enrich complete: ${scanned} files, ${chains} chains`);
|
|
4741
4770
|
}).catch(err => {
|
|
4742
4771
|
logToFile('WARN', `Auto-enrich failed: ${err.message}`);
|
|
@@ -6342,8 +6371,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
6342
6371
|
const maxRes = Math.min(a.maxResults || 30, 100);
|
|
6343
6372
|
const batchCtx = a.context !== undefined ? a.context : 4;
|
|
6344
6373
|
const batchFilesOnly = a.filesOnly || false;
|
|
6345
|
-
const gArgs = ['-rn', '-E'];
|
|
6346
|
-
if (batchFilesOnly) { gArgs[0] = '-rl'; gArgs.splice(1, 1); } // -rl = recursive + files-only, drop -n
|
|
6374
|
+
const gArgs = batchFilesOnly ? ['-rl', '-E'] : ['-rn', '-E'];
|
|
6347
6375
|
if (a.ignoreCase) gArgs.push('-i');
|
|
6348
6376
|
if (!batchFilesOnly && batchCtx > 0) gArgs.push('-C', String(batchCtx));
|
|
6349
6377
|
for (const pat of include.split(',').map(p => p.trim())) gArgs.push('--include=' + pat);
|
|
@@ -6647,7 +6675,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
6647
6675
|
if (!root) return { content: [{ type: 'text', text: 'MAGENTO_ROOT not set.' }], isError: true };
|
|
6648
6676
|
let text = `## magento_enrich\n\nScanning vendor/ PHP files for method chains...\n`;
|
|
6649
6677
|
try {
|
|
6650
|
-
const { scanned, chains } = await enrichMethodChains(root
|
|
6678
|
+
const { scanned, chains } = await enrichMethodChains(root);
|
|
6651
6679
|
text += `\n✅ **Done**\n- Files scanned: ${scanned}\n- Method chains indexed: ${chains}\n- Null-risk index saved to: \`.magector/enrichment.db\`\n\nUse \`magento_find_null_risks\` to query unsafe chains.`;
|
|
6652
6680
|
} catch (err) {
|
|
6653
6681
|
text += `\n❌ Error: ${err.message}`;
|