magector 2.6.1 → 2.6.2
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/cli.js +8 -0
- package/src/mcp-server.js +92 -8
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "magector",
|
|
3
|
-
"version": "2.6.
|
|
3
|
+
"version": "2.6.2",
|
|
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.6.
|
|
37
|
-
"@magector/cli-linux-x64": "2.6.
|
|
38
|
-
"@magector/cli-linux-arm64": "2.6.
|
|
39
|
-
"@magector/cli-win32-x64": "2.6.
|
|
36
|
+
"@magector/cli-darwin-arm64": "2.6.2",
|
|
37
|
+
"@magector/cli-linux-x64": "2.6.2",
|
|
38
|
+
"@magector/cli-linux-arm64": "2.6.2",
|
|
39
|
+
"@magector/cli-win32-x64": "2.6.2"
|
|
40
40
|
},
|
|
41
41
|
"keywords": [
|
|
42
42
|
"magento",
|
package/src/cli.js
CHANGED
|
@@ -12,6 +12,8 @@ import { resolveBinary } from './binary.js';
|
|
|
12
12
|
import { ensureModels, resolveModels } from './model.js';
|
|
13
13
|
import { init, setup } from './init.js';
|
|
14
14
|
import { checkForUpdate } from './update.js';
|
|
15
|
+
import { createRequire } from 'module';
|
|
16
|
+
const __cliPkg = createRequire(import.meta.url)('../package.json');
|
|
15
17
|
|
|
16
18
|
const args = process.argv.slice(2);
|
|
17
19
|
const command = args[0];
|
|
@@ -327,6 +329,12 @@ async function main() {
|
|
|
327
329
|
await import('./validation/benchmark.js');
|
|
328
330
|
break;
|
|
329
331
|
|
|
332
|
+
case 'version':
|
|
333
|
+
case '--version':
|
|
334
|
+
case '-V':
|
|
335
|
+
console.log(`magector v${__cliPkg.version}`);
|
|
336
|
+
break;
|
|
337
|
+
|
|
330
338
|
case 'help':
|
|
331
339
|
case '--help':
|
|
332
340
|
case '-h':
|
package/src/mcp-server.js
CHANGED
|
@@ -2279,7 +2279,20 @@ async function analyzeImpact(className) {
|
|
|
2279
2279
|
// Use vector search to find candidate files (much faster than globbing all PHP)
|
|
2280
2280
|
const rawSearch = await rustSearchAsync(`${shortName} ${className}`, 50).catch(() => []);
|
|
2281
2281
|
const raw = Array.isArray(rawSearch) ? rawSearch : [];
|
|
2282
|
-
|
|
2282
|
+
let relatedPaths = raw.map(r => normalizeResult(r)).filter(r => r.path);
|
|
2283
|
+
|
|
2284
|
+
// Filesystem fallback: if vector search found too few files, find the class file via glob
|
|
2285
|
+
if (relatedPaths.length < 5 && root) {
|
|
2286
|
+
try {
|
|
2287
|
+
const classFiles = await glob(`**/${shortName}.php`, { cwd: root, absolute: false, nodir: true });
|
|
2288
|
+
const existingPaths = new Set(relatedPaths.map(r => r.path));
|
|
2289
|
+
for (const f of classFiles) {
|
|
2290
|
+
if (!existingPaths.has(f)) {
|
|
2291
|
+
relatedPaths.push({ path: f, className: shortName, score: 0.3 });
|
|
2292
|
+
}
|
|
2293
|
+
}
|
|
2294
|
+
} catch {}
|
|
2295
|
+
}
|
|
2283
2296
|
|
|
2284
2297
|
// Check DI references via xml parsing
|
|
2285
2298
|
const diTrace = await traceDependency(className, 'both');
|
|
@@ -4075,10 +4088,49 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
4075
4088
|
const query = `${args.className} ${ns}`.trim();
|
|
4076
4089
|
const raw = await rustSearchAsync(query, 30);
|
|
4077
4090
|
const classLower = args.className.toLowerCase();
|
|
4078
|
-
|
|
4091
|
+
let results = raw.map(normalizeResult).filter(r =>
|
|
4079
4092
|
r.className?.toLowerCase().includes(classLower) ||
|
|
4080
4093
|
r.path?.toLowerCase().includes(classLower.replace(/\\/g, '/'))
|
|
4081
4094
|
);
|
|
4095
|
+
|
|
4096
|
+
// Filesystem fallback: if vector search found nothing, glob for ClassName.php
|
|
4097
|
+
if (results.length === 0 && config.magentoRoot) {
|
|
4098
|
+
const shortName = args.className.split('\\').pop();
|
|
4099
|
+
const globPattern = `**/${shortName}.php`;
|
|
4100
|
+
try {
|
|
4101
|
+
const files = await glob(globPattern, { cwd: config.magentoRoot, absolute: false, nodir: true, ignore: ['**/test/**', '**/tests/**', '**/Test/**'] });
|
|
4102
|
+
// Filter by namespace if provided
|
|
4103
|
+
const nsLower = ns.toLowerCase().replace(/\\\\/g, '/').replace(/\\/g, '/');
|
|
4104
|
+
const matched = files.filter(f => {
|
|
4105
|
+
if (!nsLower) return true;
|
|
4106
|
+
return f.toLowerCase().includes(nsLower);
|
|
4107
|
+
}).slice(0, 10);
|
|
4108
|
+
// Build result entries from file paths
|
|
4109
|
+
for (const filePath of matched) {
|
|
4110
|
+
const absPath = path.join(config.magentoRoot, filePath);
|
|
4111
|
+
let className = shortName;
|
|
4112
|
+
try {
|
|
4113
|
+
const content = readFileSync(absPath, 'utf-8');
|
|
4114
|
+
const nsMatch = content.match(/namespace\s+([\w\\]+)/);
|
|
4115
|
+
if (nsMatch) className = nsMatch[1] + '\\' + shortName;
|
|
4116
|
+
const methodsFound = [];
|
|
4117
|
+
const methodRegex = /public\s+function\s+(\w+)\s*\(/g;
|
|
4118
|
+
let mm;
|
|
4119
|
+
while ((mm = methodRegex.exec(content)) !== null) methodsFound.push(mm[1]);
|
|
4120
|
+
results.push({
|
|
4121
|
+
path: filePath,
|
|
4122
|
+
className,
|
|
4123
|
+
methods: methodsFound,
|
|
4124
|
+
score: 0.5,
|
|
4125
|
+
searchText: content.slice(0, 300)
|
|
4126
|
+
});
|
|
4127
|
+
} catch {
|
|
4128
|
+
results.push({ path: filePath, className, score: 0.5 });
|
|
4129
|
+
}
|
|
4130
|
+
}
|
|
4131
|
+
} catch {}
|
|
4132
|
+
}
|
|
4133
|
+
|
|
4082
4134
|
return {
|
|
4083
4135
|
content: [{
|
|
4084
4136
|
type: 'text',
|
|
@@ -4632,18 +4684,40 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
4632
4684
|
// Support both app/code (Magento/Catalog/) and vendor (module-catalog/) paths
|
|
4633
4685
|
const modulePath = args.moduleName.replace('_', '/') + '/';
|
|
4634
4686
|
const parts = args.moduleName.split('_');
|
|
4687
|
+
// Hyphenate camelCase for vendor path: OrderSplit → order-split
|
|
4635
4688
|
const vendorPath = parts.length === 2
|
|
4636
|
-
? `module-${parts[1].toLowerCase()}/`
|
|
4689
|
+
? `module-${parts[1].replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase()}/`
|
|
4637
4690
|
: '';
|
|
4638
|
-
|
|
4639
|
-
const
|
|
4691
|
+
let results = raw.map(normalizeResult).filter(r => {
|
|
4692
|
+
const p = r.path || '';
|
|
4640
4693
|
const mod = r.module || '';
|
|
4641
4694
|
// Exact module match or directory-level path match (trailing slash prevents Catalog matching CatalogRule)
|
|
4642
4695
|
return mod === args.moduleName ||
|
|
4643
|
-
|
|
4644
|
-
(vendorPath &&
|
|
4696
|
+
p.includes(modulePath) ||
|
|
4697
|
+
(vendorPath && p.toLowerCase().includes(vendorPath));
|
|
4645
4698
|
});
|
|
4646
4699
|
|
|
4700
|
+
// Filesystem fallback: if vector search found nothing, glob the module directory
|
|
4701
|
+
if (results.length === 0 && config.magentoRoot && vendorPath) {
|
|
4702
|
+
try {
|
|
4703
|
+
const vendorGlob = `**/${vendorPath}**/*.{php,xml,phtml}`;
|
|
4704
|
+
const files = await glob(vendorGlob, { cwd: config.magentoRoot, absolute: false, nodir: true });
|
|
4705
|
+
for (const f of files.slice(0, 100)) {
|
|
4706
|
+
const entry = { path: f, score: 0.5 };
|
|
4707
|
+
if (f.includes('/Controller/')) entry.isController = true;
|
|
4708
|
+
if (f.includes('/Model/')) entry.isModel = true;
|
|
4709
|
+
if (f.includes('/Block/')) entry.isBlock = true;
|
|
4710
|
+
if (f.includes('/Plugin/')) entry.isPlugin = true;
|
|
4711
|
+
if (f.includes('/Observer/')) entry.isObserver = true;
|
|
4712
|
+
if (f.endsWith('.xml')) entry.type = 'xml';
|
|
4713
|
+
// Extract class name from path
|
|
4714
|
+
const phpMatch = f.match(/\/([A-Z]\w+)\.php$/);
|
|
4715
|
+
if (phpMatch) entry.className = phpMatch[1];
|
|
4716
|
+
results.push(entry);
|
|
4717
|
+
}
|
|
4718
|
+
} catch {}
|
|
4719
|
+
}
|
|
4720
|
+
|
|
4647
4721
|
const structure = {
|
|
4648
4722
|
controllers: results.filter(r => r.isController || r.path?.includes('/Controller/')),
|
|
4649
4723
|
models: results.filter(r => r.isModel || (r.path?.includes('/Model/') && !r.path?.includes('ResourceModel'))),
|
|
@@ -5490,9 +5564,19 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
5490
5564
|
const qr = `${a.className} ${ns}`.trim();
|
|
5491
5565
|
const raw = await rustSearchAsync(qr, 30);
|
|
5492
5566
|
const cl = a.className.toLowerCase();
|
|
5493
|
-
|
|
5567
|
+
let res = raw.map(normalizeResult).filter(r =>
|
|
5494
5568
|
r.className?.toLowerCase().includes(cl) || r.path?.toLowerCase().includes(cl.replace(/\\/g, '/'))
|
|
5495
5569
|
);
|
|
5570
|
+
// Filesystem fallback for batch find_class
|
|
5571
|
+
if (res.length === 0 && config.magentoRoot) {
|
|
5572
|
+
const shortName = a.className.split('\\').pop();
|
|
5573
|
+
try {
|
|
5574
|
+
const files = await glob(`**/${shortName}.php`, { cwd: config.magentoRoot, absolute: false, nodir: true });
|
|
5575
|
+
for (const f of files.slice(0, 5)) {
|
|
5576
|
+
res.push({ path: f, className: shortName, score: 0.5 });
|
|
5577
|
+
}
|
|
5578
|
+
} catch {}
|
|
5579
|
+
}
|
|
5496
5580
|
text = formatSearchResults(res.slice(0, 5));
|
|
5497
5581
|
break;
|
|
5498
5582
|
}
|