ucn 3.8.15 → 3.8.16
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/cli/index.js +45 -37
- package/core/execute.js +5 -4
- package/core/registry.js +1 -1
- package/core/search.js +21 -2
- package/mcp/server.js +65 -65
- package/package.json +1 -1
package/cli/index.js
CHANGED
|
@@ -14,7 +14,7 @@ const { parseFile, detectLanguage } = require('../core/parser');
|
|
|
14
14
|
const { ProjectIndex } = require('../core/project');
|
|
15
15
|
const { expandGlob, findProjectRoot } = require('../core/discovery');
|
|
16
16
|
const output = require('../core/output');
|
|
17
|
-
|
|
17
|
+
const { escapeRegExp } = require('../core/shared');
|
|
18
18
|
const { getCliCommandSet, resolveCommand, FLAG_APPLICABILITY, toCliName } = require('../core/registry');
|
|
19
19
|
const { execute } = require('../core/execute');
|
|
20
20
|
const { ExpandCache } = require('../core/expand-cache');
|
|
@@ -115,6 +115,7 @@ function parseFlags(tokens) {
|
|
|
115
115
|
showConfidence: !tokens.includes('--no-confidence'),
|
|
116
116
|
minConfidence: parseFloat(getValueFlag('--min-confidence') || '0') || 0,
|
|
117
117
|
framework: getValueFlag('--framework'),
|
|
118
|
+
stack: getValueFlag('--stack'),
|
|
118
119
|
workers: (() => {
|
|
119
120
|
const v = getValueFlag('--workers');
|
|
120
121
|
if (v === null) return undefined;
|
|
@@ -327,11 +328,11 @@ function runFileCommand(filePath, command, arg) {
|
|
|
327
328
|
fn: { name: arg, file: relativePath, ...flags },
|
|
328
329
|
class: { name: arg, file: relativePath, ...flags },
|
|
329
330
|
find: { name: arg, file: relativePath, ...flags },
|
|
330
|
-
usages: { name: arg, ...flags },
|
|
331
|
+
usages: { name: arg, file: relativePath, ...flags },
|
|
331
332
|
search: { term: arg, ...flags },
|
|
332
333
|
lines: { file: relativePath, range: arg },
|
|
333
|
-
typedef: { name: arg, ...flags },
|
|
334
|
-
api: { file: relativePath },
|
|
334
|
+
typedef: { name: arg, file: relativePath, ...flags },
|
|
335
|
+
api: { file: relativePath, limit: flags.limit },
|
|
335
336
|
};
|
|
336
337
|
|
|
337
338
|
const { ok, result, error, note } = execute(index, canonical, paramsByCommand[canonical]);
|
|
@@ -427,6 +428,11 @@ function runProjectCommand(rootDir, command, arg) {
|
|
|
427
428
|
if (!usedCache) {
|
|
428
429
|
index.build(null, { quiet: flags.quiet, forceRebuild: cacheWasLoaded, followSymlinks: flags.followSymlinks, maxFiles: flags.maxFiles, workers: flags.workers });
|
|
429
430
|
needsCacheSave = flags.cache;
|
|
431
|
+
// Clear stale expand cache — line ranges may have shifted after rebuild
|
|
432
|
+
try {
|
|
433
|
+
const expandPath = path.join(index.root, '.ucn-cache', 'expandable.json');
|
|
434
|
+
if (fs.existsSync(expandPath)) fs.unlinkSync(expandPath);
|
|
435
|
+
} catch (_) { /* best-effort */ }
|
|
430
436
|
}
|
|
431
437
|
|
|
432
438
|
try {
|
|
@@ -439,7 +445,7 @@ function runProjectCommand(rootDir, command, arg) {
|
|
|
439
445
|
// Map from camelCase flag name to CLI flag string
|
|
440
446
|
const flagToCli = (f) => '--' + f.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
|
|
441
447
|
// Flags that are global (not command-specific) or have truthy defaults — skip warning for these
|
|
442
|
-
const globalFlags = new Set(['json', 'quiet', 'cache', 'clearCache', 'followSymlinks', 'maxFiles', 'verbose', 'expand', 'interactive', '
|
|
448
|
+
const globalFlags = new Set(['json', 'quiet', 'cache', 'clearCache', 'followSymlinks', 'maxFiles', 'verbose', 'expand', 'interactive', 'showConfidence']);
|
|
443
449
|
for (const [key, value] of Object.entries(flags)) {
|
|
444
450
|
if (globalFlags.has(key)) continue;
|
|
445
451
|
// Skip falsy/default values (0, undefined, false, empty array)
|
|
@@ -487,7 +493,7 @@ function runProjectCommand(rootDir, command, arg) {
|
|
|
487
493
|
}
|
|
488
494
|
|
|
489
495
|
case 'example': {
|
|
490
|
-
const { ok, result, error } = execute(index, 'example', { name: arg });
|
|
496
|
+
const { ok, result, error } = execute(index, 'example', { name: arg, file: flags.file, className: flags.className });
|
|
491
497
|
if (!ok) fail(error);
|
|
492
498
|
printOutput(result,
|
|
493
499
|
r => output.formatExampleJson(r, arg),
|
|
@@ -550,7 +556,7 @@ function runProjectCommand(rootDir, command, arg) {
|
|
|
550
556
|
const items = cached?.items || [];
|
|
551
557
|
const match = items.find(i => i.num === expandNum);
|
|
552
558
|
const { ok, result, error } = execute(index, 'expand', {
|
|
553
|
-
match, itemNum: expandNum, itemCount: items.length
|
|
559
|
+
match, itemNum: expandNum, itemCount: items.length, validateRoot: true
|
|
554
560
|
});
|
|
555
561
|
if (!ok) fail(error);
|
|
556
562
|
console.log(result.text);
|
|
@@ -558,21 +564,23 @@ function runProjectCommand(rootDir, command, arg) {
|
|
|
558
564
|
}
|
|
559
565
|
|
|
560
566
|
case 'smart': {
|
|
561
|
-
const { ok, result, error } = execute(index, 'smart', { name: arg, ...flags });
|
|
567
|
+
const { ok, result, error, note } = execute(index, 'smart', { name: arg, ...flags });
|
|
562
568
|
if (!ok) fail(error);
|
|
563
569
|
printOutput(result, output.formatSmartJson, r => output.formatSmart(r, {
|
|
564
570
|
uncertainHint: 'use --include-uncertain to include all'
|
|
565
571
|
}));
|
|
572
|
+
if (note) console.error(note);
|
|
566
573
|
break;
|
|
567
574
|
}
|
|
568
575
|
|
|
569
576
|
case 'about': {
|
|
570
|
-
const { ok, result, error } = execute(index, 'about', { name: arg, ...flags });
|
|
577
|
+
const { ok, result, error, note } = execute(index, 'about', { name: arg, ...flags });
|
|
571
578
|
if (!ok) fail(error);
|
|
572
579
|
printOutput(result,
|
|
573
580
|
output.formatAboutJson,
|
|
574
581
|
r => output.formatAbout(r, { expand: flags.expand, root: index.root, depth: flags.depth, showConfidence: flags.showConfidence })
|
|
575
582
|
);
|
|
583
|
+
if (note) console.error(note);
|
|
576
584
|
break;
|
|
577
585
|
}
|
|
578
586
|
|
|
@@ -616,7 +624,7 @@ function runProjectCommand(rootDir, command, arg) {
|
|
|
616
624
|
}
|
|
617
625
|
|
|
618
626
|
case 'stacktrace': {
|
|
619
|
-
const { ok, result, error } = execute(index, 'stacktrace', { stack: arg });
|
|
627
|
+
const { ok, result, error } = execute(index, 'stacktrace', { stack: flags.stack || arg });
|
|
620
628
|
if (!ok) fail(error);
|
|
621
629
|
printOutput(result, output.formatStackTraceJson, output.formatStackTrace);
|
|
622
630
|
break;
|
|
@@ -641,7 +649,7 @@ function runProjectCommand(rootDir, command, arg) {
|
|
|
641
649
|
|
|
642
650
|
case 'fn': {
|
|
643
651
|
requireArg(arg, 'Usage: ucn . fn <name>');
|
|
644
|
-
const { ok, result, error, note } = execute(index, 'fn', { name: arg, file: flags.file, all: flags.all });
|
|
652
|
+
const { ok, result, error, note } = execute(index, 'fn', { name: arg, file: flags.file, all: flags.all, className: flags.className });
|
|
645
653
|
if (!ok) fail(error);
|
|
646
654
|
if (note) console.error(note);
|
|
647
655
|
printOutput(result, output.formatFnResultJson, output.formatFnResult);
|
|
@@ -650,7 +658,7 @@ function runProjectCommand(rootDir, command, arg) {
|
|
|
650
658
|
|
|
651
659
|
case 'class': {
|
|
652
660
|
requireArg(arg, 'Usage: ucn . class <name>');
|
|
653
|
-
const { ok, result, error, note } = execute(index, 'class', { name: arg, file: flags.file, all: flags.all, maxLines: flags.maxLines });
|
|
661
|
+
const { ok, result, error, note } = execute(index, 'class', { name: arg, file: flags.file, all: flags.all, maxLines: flags.maxLines, className: flags.className });
|
|
654
662
|
if (!ok) fail(error);
|
|
655
663
|
if (note) console.error(note);
|
|
656
664
|
printOutput(result, output.formatClassResultJson, output.formatClassResult);
|
|
@@ -659,8 +667,9 @@ function runProjectCommand(rootDir, command, arg) {
|
|
|
659
667
|
|
|
660
668
|
case 'lines': {
|
|
661
669
|
requireArg(arg, 'Usage: ucn . lines <range> --file <path>');
|
|
662
|
-
const { ok, result, error } = execute(index, 'lines', { file: flags.file, range: arg });
|
|
670
|
+
const { ok, result, error, note } = execute(index, 'lines', { file: flags.file, range: arg });
|
|
663
671
|
if (!ok) fail(error);
|
|
672
|
+
if (note) console.error(note);
|
|
664
673
|
printOutput(result, output.formatLinesJson, r => output.formatLines(r));
|
|
665
674
|
break;
|
|
666
675
|
}
|
|
@@ -694,7 +703,7 @@ function runProjectCommand(rootDir, command, arg) {
|
|
|
694
703
|
const { ok, result, error } = execute(index, 'fileExports', { file: filePath });
|
|
695
704
|
if (!ok) fail(error);
|
|
696
705
|
printOutput(result,
|
|
697
|
-
|
|
706
|
+
output.formatFileExportsJson,
|
|
698
707
|
r => output.formatFileExports(r, filePath)
|
|
699
708
|
);
|
|
700
709
|
break;
|
|
@@ -705,11 +714,7 @@ function runProjectCommand(rootDir, command, arg) {
|
|
|
705
714
|
const { ok, result, error } = execute(index, 'graph', { file: filePath, direction: flags.direction, depth: flags.depth, all: flags.all });
|
|
706
715
|
if (!ok) fail(error);
|
|
707
716
|
printOutput(result,
|
|
708
|
-
|
|
709
|
-
root: path.relative(index.root, r.root),
|
|
710
|
-
nodes: r.nodes.map(n => ({ file: n.relativePath, depth: n.depth })),
|
|
711
|
-
edges: r.edges.map(e => ({ from: path.relative(index.root, e.from), to: path.relative(index.root, e.to) }))
|
|
712
|
-
}, null, 2),
|
|
717
|
+
output.formatGraphJson,
|
|
713
718
|
r => output.formatGraph(r, { showAll: flags.all || flags.depth != null, maxDepth: flags.depth != null ? parseInt(flags.depth, 10) : 2, file: filePath })
|
|
714
719
|
);
|
|
715
720
|
break;
|
|
@@ -725,7 +730,7 @@ function runProjectCommand(rootDir, command, arg) {
|
|
|
725
730
|
// ── Remaining commands ──────────────────────────────────────────
|
|
726
731
|
|
|
727
732
|
case 'typedef': {
|
|
728
|
-
const { ok, result, error } = execute(index, 'typedef', { name: arg, exact: flags.exact });
|
|
733
|
+
const { ok, result, error } = execute(index, 'typedef', { name: arg, exact: flags.exact, file: flags.file, className: flags.className });
|
|
729
734
|
if (!ok) fail(error);
|
|
730
735
|
printOutput(result,
|
|
731
736
|
r => output.formatTypedefJson(r, arg),
|
|
@@ -735,7 +740,7 @@ function runProjectCommand(rootDir, command, arg) {
|
|
|
735
740
|
}
|
|
736
741
|
|
|
737
742
|
case 'tests': {
|
|
738
|
-
const { ok, result, error } = execute(index, 'tests', { name: arg, callsOnly: flags.callsOnly });
|
|
743
|
+
const { ok, result, error } = execute(index, 'tests', { name: arg, callsOnly: flags.callsOnly, className: flags.className });
|
|
739
744
|
if (!ok) fail(error);
|
|
740
745
|
printOutput(result,
|
|
741
746
|
r => output.formatTestsJson(r, arg),
|
|
@@ -794,8 +799,9 @@ function runProjectCommand(rootDir, command, arg) {
|
|
|
794
799
|
}
|
|
795
800
|
|
|
796
801
|
case 'entrypoints': {
|
|
797
|
-
const { ok, result, error } = execute(index, 'entrypoints', { type: flags.type, framework: flags.framework, file: flags.file, exclude: flags.exclude });
|
|
802
|
+
const { ok, result, error, note } = execute(index, 'entrypoints', { type: flags.type, framework: flags.framework, file: flags.file, exclude: flags.exclude, includeTests: flags.includeTests, limit: flags.limit });
|
|
798
803
|
if (!ok) fail(error);
|
|
804
|
+
if (note) console.error(note);
|
|
799
805
|
printOutput(result,
|
|
800
806
|
output.formatEntrypointsJson,
|
|
801
807
|
r => output.formatEntrypoints(r)
|
|
@@ -814,8 +820,9 @@ function runProjectCommand(rootDir, command, arg) {
|
|
|
814
820
|
}
|
|
815
821
|
|
|
816
822
|
case 'diffImpact': {
|
|
817
|
-
const { ok, result, error } = execute(index, 'diffImpact', { base: flags.base, staged: flags.staged, file: flags.file });
|
|
823
|
+
const { ok, result, error, note } = execute(index, 'diffImpact', { base: flags.base, staged: flags.staged, file: flags.file, limit: flags.limit, all: flags.all });
|
|
818
824
|
if (!ok) fail(error);
|
|
825
|
+
if (note) console.error(note);
|
|
819
826
|
printOutput(result, output.formatDiffImpactJson, r => output.formatDiffImpact(r, { all: flags.all }));
|
|
820
827
|
break;
|
|
821
828
|
}
|
|
@@ -1062,10 +1069,6 @@ function searchGlobFiles(files, term) {
|
|
|
1062
1069
|
// HELPERS
|
|
1063
1070
|
// ============================================================================
|
|
1064
1071
|
|
|
1065
|
-
function escapeRegExp(text) {
|
|
1066
|
-
return text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
1067
|
-
}
|
|
1068
|
-
|
|
1069
1072
|
function isCommentOrString(line) {
|
|
1070
1073
|
const trimmed = line.trim();
|
|
1071
1074
|
return trimmed.startsWith('//') ||
|
|
@@ -1279,6 +1282,8 @@ Flags can be added per-command: context myFunc --include-methods
|
|
|
1279
1282
|
if (input === 'rebuild') {
|
|
1280
1283
|
console.log('Rebuilding index...');
|
|
1281
1284
|
index.build(null, { quiet: true, forceRebuild: true, workers: flags.workers });
|
|
1285
|
+
// Clear expand cache — stale line ranges after rebuild
|
|
1286
|
+
if (iExpandCache) iExpandCache.clearForRoot(index.root);
|
|
1282
1287
|
console.log(`Index ready: ${index.files.size} files, ${index.symbols.size} symbols`);
|
|
1283
1288
|
rl.prompt();
|
|
1284
1289
|
return;
|
|
@@ -1344,7 +1349,7 @@ const INTERACTIVE_DISPATCH = {
|
|
|
1344
1349
|
trace: { params: 'name', format: (r) => output.formatTrace(r) },
|
|
1345
1350
|
reverseTrace: { params: 'name', format: (r) => output.formatReverseTrace(r) },
|
|
1346
1351
|
related: { params: 'name', format: (r, _a, f) => output.formatRelated(r, { all: f.all, top: f.top }) },
|
|
1347
|
-
example: { params:
|
|
1352
|
+
example: { params: 'name', format: (r, a) => output.formatExample(r, a) },
|
|
1348
1353
|
|
|
1349
1354
|
// ── Finding Code ─────────────────────────────────────────────────
|
|
1350
1355
|
find: { params: 'name', format: (r, a, f) => output.formatFindDetailed(r, a, { depth: f.depth, top: f.top, all: f.all }) },
|
|
@@ -1364,12 +1369,12 @@ const INTERACTIVE_DISPATCH = {
|
|
|
1364
1369
|
// ── Refactoring Helpers ──────────────────────────────────────────
|
|
1365
1370
|
plan: { params: 'name', format: (r) => output.formatPlan(r) },
|
|
1366
1371
|
verify: { params: 'name', format: (r) => output.formatVerify(r) },
|
|
1367
|
-
diffImpact: { params:
|
|
1368
|
-
entrypoints: { params: (a, f) => ({ type: f.type, framework: f.framework, file: f.file, exclude: f.exclude }), format: (r) => output.formatEntrypoints(r) },
|
|
1372
|
+
diffImpact: { params: (a, f) => ({ base: f.base, staged: f.staged, file: f.file, limit: f.limit, all: f.all }), format: (r, _a, f) => output.formatDiffImpact(r, { all: f.all }) },
|
|
1373
|
+
entrypoints: { params: (a, f) => ({ type: f.type, framework: f.framework, file: f.file, exclude: f.exclude, includeTests: f.includeTests, limit: f.limit }), format: (r) => output.formatEntrypoints(r) },
|
|
1369
1374
|
|
|
1370
1375
|
// ── Other ────────────────────────────────────────────────────────
|
|
1371
1376
|
api: { params: (a, f) => ({ file: a || f.file, limit: f.limit }), format: (r, a, f) => output.formatApi(r, a || f.file || '.') },
|
|
1372
|
-
stacktrace: { params: (a) => ({ stack: a }), format: (r) => output.formatStackTrace(r) },
|
|
1377
|
+
stacktrace: { params: (a, f) => ({ stack: f.stack || a }), format: (r) => output.formatStackTrace(r) },
|
|
1373
1378
|
stats: { params: 'flags', format: (r, _a, f) => output.formatStats(r, { top: f.top }) },
|
|
1374
1379
|
};
|
|
1375
1380
|
|
|
@@ -1395,8 +1400,8 @@ function executeInteractiveCommand(index, command, arg, iflags = {}, cache = nul
|
|
|
1395
1400
|
switch (command) {
|
|
1396
1401
|
|
|
1397
1402
|
case 'fn': {
|
|
1398
|
-
if (!arg) { console.log('Usage: fn <name>[,name2,...] [--file=<pattern>]'); return; }
|
|
1399
|
-
const { ok, result, error, note } = execute(index, 'fn', { name: arg, file: iflags.file, all: iflags.all });
|
|
1403
|
+
if (!arg) { console.log('Usage: fn <name>[,name2,...] [--file=<pattern>] [--class-name=<class>]'); return; }
|
|
1404
|
+
const { ok, result, error, note } = execute(index, 'fn', { name: arg, file: iflags.file, all: iflags.all, className: iflags.className });
|
|
1400
1405
|
if (!ok) { console.log(error); return; }
|
|
1401
1406
|
if (note) console.log(note);
|
|
1402
1407
|
console.log(output.formatFnResult(result));
|
|
@@ -1443,7 +1448,7 @@ function executeInteractiveCommand(index, command, arg, iflags = {}, cache = nul
|
|
|
1443
1448
|
itemCount = items.length;
|
|
1444
1449
|
}
|
|
1445
1450
|
const { ok, result, error } = execute(index, 'expand', {
|
|
1446
|
-
match, itemNum: expandNum, itemCount, symbolName
|
|
1451
|
+
match, itemNum: expandNum, itemCount, symbolName, validateRoot: true
|
|
1447
1452
|
});
|
|
1448
1453
|
if (!ok) { console.log(error); return; }
|
|
1449
1454
|
console.log(result.text);
|
|
@@ -1451,7 +1456,7 @@ function executeInteractiveCommand(index, command, arg, iflags = {}, cache = nul
|
|
|
1451
1456
|
}
|
|
1452
1457
|
|
|
1453
1458
|
case 'context': {
|
|
1454
|
-
const { ok, result, error } = execute(index, 'context', { name: arg, ...iflags });
|
|
1459
|
+
const { ok, result, error, note } = execute(index, 'context', { name: arg, ...iflags });
|
|
1455
1460
|
if (!ok) { console.log(error); return; }
|
|
1456
1461
|
const { text, expandable } = output.formatContext(result, {
|
|
1457
1462
|
methodsHint: 'Note: obj.method() calls excluded — use --include-methods to include them',
|
|
@@ -1460,6 +1465,7 @@ function executeInteractiveCommand(index, command, arg, iflags = {}, cache = nul
|
|
|
1460
1465
|
showConfidence: iflags.showConfidence,
|
|
1461
1466
|
});
|
|
1462
1467
|
console.log(text);
|
|
1468
|
+
if (note) console.log(note);
|
|
1463
1469
|
if (cache) {
|
|
1464
1470
|
cache.save(index.root, arg, iflags.file, expandable);
|
|
1465
1471
|
} else {
|
|
@@ -1469,8 +1475,9 @@ function executeInteractiveCommand(index, command, arg, iflags = {}, cache = nul
|
|
|
1469
1475
|
}
|
|
1470
1476
|
|
|
1471
1477
|
case 'deadcode': {
|
|
1472
|
-
const { ok, result, error } = execute(index, 'deadcode', iflags);
|
|
1478
|
+
const { ok, result, error, note } = execute(index, 'deadcode', iflags);
|
|
1473
1479
|
if (!ok) { console.log(error); return; }
|
|
1480
|
+
if (note) console.log(note);
|
|
1474
1481
|
console.log(output.formatDeadcode(result, {
|
|
1475
1482
|
top: iflags.top,
|
|
1476
1483
|
decoratedHint: !iflags.includeDecorated && result.excludedDecorated > 0 ? `${result.excludedDecorated} decorated/annotated symbol(s) hidden (framework-registered). Use --include-decorated to include them.` : undefined,
|
|
@@ -1480,8 +1487,9 @@ function executeInteractiveCommand(index, command, arg, iflags = {}, cache = nul
|
|
|
1480
1487
|
}
|
|
1481
1488
|
|
|
1482
1489
|
case 'search': {
|
|
1483
|
-
const { ok, result, error, structural } = execute(index, 'search', { term: arg, ...iflags });
|
|
1490
|
+
const { ok, result, error, structural, note } = execute(index, 'search', { term: arg, ...iflags });
|
|
1484
1491
|
if (!ok) { console.log(error); return; }
|
|
1492
|
+
if (note) console.log(note);
|
|
1485
1493
|
if (structural) {
|
|
1486
1494
|
console.log(output.formatStructuralSearch(result));
|
|
1487
1495
|
} else {
|
package/core/execute.js
CHANGED
|
@@ -62,14 +62,14 @@ function splitClassMethod(name) {
|
|
|
62
62
|
/**
|
|
63
63
|
* Apply Class.method syntax to params object.
|
|
64
64
|
* If name contains ".", splits it and sets p.name and p.className.
|
|
65
|
-
*
|
|
65
|
+
* When p.className is already set, still split the name to extract the method
|
|
66
|
+
* part (explicit --class-name takes precedence over the class from dot notation).
|
|
66
67
|
*/
|
|
67
68
|
function applyClassMethodSyntax(p) {
|
|
68
|
-
if (p.className) return; // already set explicitly
|
|
69
69
|
const split = splitClassMethod(p.name);
|
|
70
70
|
if (split) {
|
|
71
71
|
p.name = split.methodName;
|
|
72
|
-
p.className = split.className;
|
|
72
|
+
if (!p.className) p.className = split.className;
|
|
73
73
|
}
|
|
74
74
|
}
|
|
75
75
|
|
|
@@ -343,7 +343,8 @@ const HANDLERS = {
|
|
|
343
343
|
withTypes: p.withTypes || false,
|
|
344
344
|
});
|
|
345
345
|
if (!result) return { ok: false, error: `Function "${p.name}" not found.` };
|
|
346
|
-
|
|
346
|
+
const tNote = truncationNote(index);
|
|
347
|
+
return { ok: true, result, ...(tNote && { note: tNote }) };
|
|
347
348
|
},
|
|
348
349
|
|
|
349
350
|
trace: (index, p) => {
|
package/core/registry.js
CHANGED
|
@@ -106,7 +106,7 @@ const FLAG_APPLICABILITY = {
|
|
|
106
106
|
example: ['file', 'className'],
|
|
107
107
|
related: ['file', 'className', 'top', 'all'],
|
|
108
108
|
// Finding code
|
|
109
|
-
find: ['file', 'exclude', 'className', 'includeTests', 'top', 'limit', 'exact', 'in', 'all'],
|
|
109
|
+
find: ['file', 'exclude', 'className', 'includeTests', 'top', 'limit', 'exact', 'in', 'all', 'depth'],
|
|
110
110
|
usages: ['file', 'exclude', 'className', 'includeTests', 'limit', 'codeOnly', 'context', 'in'],
|
|
111
111
|
toc: ['file', 'exclude', 'top', 'limit', 'all', 'detailed', 'topLevel', 'in'],
|
|
112
112
|
search: ['file', 'exclude', 'includeTests', 'top', 'limit', 'codeOnly', 'caseSensitive', 'context', 'regex', 'in', 'type', 'param', 'receiver', 'returns', 'decorator', 'exported', 'unused'],
|
package/core/search.js
CHANGED
|
@@ -161,7 +161,10 @@ function usages(index, name, options = {}) {
|
|
|
161
161
|
const usagesList = [];
|
|
162
162
|
|
|
163
163
|
// Resolve file pattern for --file filter
|
|
164
|
-
const
|
|
164
|
+
const fileFilterRaw = options.file ? index.resolveFilePathForQuery(options.file) : null;
|
|
165
|
+
// resolveFilePathForQuery may return error objects for ambiguous/not-found — fall back to substring matching
|
|
166
|
+
const fileFilter = typeof fileFilterRaw === 'string' ? fileFilterRaw : null;
|
|
167
|
+
const fileSubstring = options.file || null; // fallback for unresolved patterns
|
|
165
168
|
|
|
166
169
|
// Get definitions (filtered)
|
|
167
170
|
let allDefinitions = index.symbols.get(name) || [];
|
|
@@ -170,6 +173,8 @@ function usages(index, name, options = {}) {
|
|
|
170
173
|
}
|
|
171
174
|
if (fileFilter) {
|
|
172
175
|
allDefinitions = allDefinitions.filter(d => d.file === fileFilter);
|
|
176
|
+
} else if (fileSubstring) {
|
|
177
|
+
allDefinitions = allDefinitions.filter(d => d.relativePath && d.relativePath.includes(fileSubstring));
|
|
173
178
|
}
|
|
174
179
|
const definitions = options.exclude || options.in
|
|
175
180
|
? allDefinitions.filter(d => index.matchesFilters(d.relativePath, options))
|
|
@@ -187,9 +192,11 @@ function usages(index, name, options = {}) {
|
|
|
187
192
|
|
|
188
193
|
// Scan all files for usages
|
|
189
194
|
for (const [filePath, fileEntry] of index.files) {
|
|
190
|
-
// Apply --file filter
|
|
195
|
+
// Apply --file filter (exact match if resolved, substring fallback otherwise)
|
|
191
196
|
if (fileFilter && filePath !== fileFilter) {
|
|
192
197
|
continue;
|
|
198
|
+
} else if (!fileFilter && fileSubstring && !fileEntry.relativePath.includes(fileSubstring)) {
|
|
199
|
+
continue;
|
|
193
200
|
}
|
|
194
201
|
// Apply filters
|
|
195
202
|
if (!index.matchesFilters(fileEntry.relativePath, options)) {
|
|
@@ -838,9 +845,21 @@ function tests(index, nameOrFile, options = {}) {
|
|
|
838
845
|
const callPattern = new RegExp(escapeRegExp(searchTerm) + '\\s*\\(');
|
|
839
846
|
const strPattern = new RegExp("['\"`]" + escapeRegExp(searchTerm) + "['\"`]");
|
|
840
847
|
|
|
848
|
+
// When className is provided, build a pattern to scope matches.
|
|
849
|
+
// We require the test file to also reference the class name (import, instantiation, or receiver).
|
|
850
|
+
const classNameFilter = options.className
|
|
851
|
+
? new RegExp('\\b' + escapeRegExp(options.className) + '\\b')
|
|
852
|
+
: null;
|
|
853
|
+
|
|
841
854
|
for (const { path: testPath, entry } of testFiles) {
|
|
842
855
|
try {
|
|
843
856
|
const content = index._readFile(testPath);
|
|
857
|
+
|
|
858
|
+
// className scoping: skip test files that don't reference the class at all
|
|
859
|
+
if (classNameFilter && !classNameFilter.test(content)) {
|
|
860
|
+
continue;
|
|
861
|
+
}
|
|
862
|
+
|
|
844
863
|
const lines = content.split('\n');
|
|
845
864
|
const matches = [];
|
|
846
865
|
|
package/mcp/server.js
CHANGED
|
@@ -178,13 +178,6 @@ function resolveAndValidatePath(index, file) {
|
|
|
178
178
|
return resolved;
|
|
179
179
|
}
|
|
180
180
|
|
|
181
|
-
function requireName(name) {
|
|
182
|
-
if (!name || !name.trim()) {
|
|
183
|
-
return toolError('Symbol name is required.');
|
|
184
|
-
}
|
|
185
|
-
return null;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
181
|
// ============================================================================
|
|
189
182
|
// CONSOLIDATED TOOL REGISTRATION
|
|
190
183
|
// ============================================================================
|
|
@@ -332,19 +325,21 @@ server.registerTool(
|
|
|
332
325
|
|
|
333
326
|
case 'about': {
|
|
334
327
|
index = getIndex(project_dir, ep);
|
|
335
|
-
const { ok, result, error } = execute(index, 'about', ep);
|
|
336
|
-
if (!ok) return
|
|
337
|
-
|
|
328
|
+
const { ok, result, error, note } = execute(index, 'about', ep);
|
|
329
|
+
if (!ok) return toolError(error);
|
|
330
|
+
let aboutText = output.formatAbout(result, {
|
|
338
331
|
allHint: 'Repeat with all=true to show all.',
|
|
339
332
|
methodsHint: 'Note: obj.method() callers/callees excluded. Use include_methods=true to include them.',
|
|
340
333
|
showConfidence: ep.showConfidence !== false,
|
|
341
|
-
})
|
|
334
|
+
});
|
|
335
|
+
if (note) aboutText += '\n\n' + note;
|
|
336
|
+
return tr(aboutText);
|
|
342
337
|
}
|
|
343
338
|
|
|
344
339
|
case 'context': {
|
|
345
340
|
index = getIndex(project_dir, ep);
|
|
346
341
|
const { ok, result: ctx, error, note } = execute(index, 'context', ep);
|
|
347
|
-
if (!ok) return
|
|
342
|
+
if (!ok) return toolError(error);
|
|
348
343
|
const { text, expandable } = output.formatContext(ctx, {
|
|
349
344
|
expandHint: 'Use expand command with item number to see code for any item.',
|
|
350
345
|
showConfidence: ep.showConfidence !== false,
|
|
@@ -358,7 +353,7 @@ server.registerTool(
|
|
|
358
353
|
case 'impact': {
|
|
359
354
|
index = getIndex(project_dir, ep);
|
|
360
355
|
const { ok, result, error, note } = execute(index, 'impact', ep);
|
|
361
|
-
if (!ok) return
|
|
356
|
+
if (!ok) return toolError(error);
|
|
362
357
|
let impactText = output.formatImpact(result);
|
|
363
358
|
if (note) impactText += '\n\n' + note;
|
|
364
359
|
return tr(impactText);
|
|
@@ -367,7 +362,7 @@ server.registerTool(
|
|
|
367
362
|
case 'blast': {
|
|
368
363
|
index = getIndex(project_dir, ep);
|
|
369
364
|
const { ok, result, error, note } = execute(index, 'blast', ep);
|
|
370
|
-
if (!ok) return
|
|
365
|
+
if (!ok) return toolError(error);
|
|
371
366
|
let blastText = output.formatBlast(result, {
|
|
372
367
|
allHint: 'Set depth to expand all children.',
|
|
373
368
|
});
|
|
@@ -377,15 +372,17 @@ server.registerTool(
|
|
|
377
372
|
|
|
378
373
|
case 'smart': {
|
|
379
374
|
index = getIndex(project_dir, ep);
|
|
380
|
-
const { ok, result, error } = execute(index, 'smart', ep);
|
|
381
|
-
if (!ok) return
|
|
382
|
-
|
|
375
|
+
const { ok, result, error, note } = execute(index, 'smart', ep);
|
|
376
|
+
if (!ok) return toolError(error);
|
|
377
|
+
let smartText = output.formatSmart(result);
|
|
378
|
+
if (note) smartText += '\n\n' + note;
|
|
379
|
+
return tr(smartText);
|
|
383
380
|
}
|
|
384
381
|
|
|
385
382
|
case 'trace': {
|
|
386
383
|
index = getIndex(project_dir, ep);
|
|
387
384
|
const { ok, result, error, note } = execute(index, 'trace', ep);
|
|
388
|
-
if (!ok) return
|
|
385
|
+
if (!ok) return toolError(error);
|
|
389
386
|
let traceText = output.formatTrace(result, {
|
|
390
387
|
allHint: 'Set depth to expand all children.',
|
|
391
388
|
methodsHint: 'Note: obj.method() calls excluded. Use include_methods=true to include them.'
|
|
@@ -397,7 +394,7 @@ server.registerTool(
|
|
|
397
394
|
case 'reverse_trace': {
|
|
398
395
|
index = getIndex(project_dir, ep);
|
|
399
396
|
const { ok, result, error, note } = execute(index, 'reverseTrace', ep);
|
|
400
|
-
if (!ok) return
|
|
397
|
+
if (!ok) return toolError(error);
|
|
401
398
|
let rtText = output.formatReverseTrace(result, {
|
|
402
399
|
allHint: 'Set depth to expand all children.',
|
|
403
400
|
});
|
|
@@ -408,16 +405,14 @@ server.registerTool(
|
|
|
408
405
|
case 'example': {
|
|
409
406
|
index = getIndex(project_dir, ep);
|
|
410
407
|
const { ok, result, error } = execute(index, 'example', ep);
|
|
411
|
-
if (!ok) return
|
|
412
|
-
if (!result) return tr(`No usage examples found for "${ep.name}".`);
|
|
408
|
+
if (!ok) return toolError(error);
|
|
413
409
|
return tr(output.formatExample(result, ep.name));
|
|
414
410
|
}
|
|
415
411
|
|
|
416
412
|
case 'related': {
|
|
417
413
|
index = getIndex(project_dir, ep);
|
|
418
414
|
const { ok, result, error, note } = execute(index, 'related', ep);
|
|
419
|
-
if (!ok) return
|
|
420
|
-
if (!result) return tr(`Symbol "${ep.name}" not found.`);
|
|
415
|
+
if (!ok) return toolError(error);
|
|
421
416
|
let relText = output.formatRelated(result, {
|
|
422
417
|
all: ep.all || false, top: ep.top,
|
|
423
418
|
allHint: 'Repeat with all=true to show all.'
|
|
@@ -431,7 +426,7 @@ server.registerTool(
|
|
|
431
426
|
case 'find': {
|
|
432
427
|
index = getIndex(project_dir, ep);
|
|
433
428
|
const { ok, result, error, note } = execute(index, 'find', ep);
|
|
434
|
-
if (!ok) return
|
|
429
|
+
if (!ok) return toolError(error);
|
|
435
430
|
let text = output.formatFind(result, ep.name, ep.top);
|
|
436
431
|
if (note) text += '\n\n' + note;
|
|
437
432
|
return tr(text);
|
|
@@ -440,7 +435,7 @@ server.registerTool(
|
|
|
440
435
|
case 'usages': {
|
|
441
436
|
index = getIndex(project_dir, ep);
|
|
442
437
|
const { ok, result, error, note } = execute(index, 'usages', ep);
|
|
443
|
-
if (!ok) return
|
|
438
|
+
if (!ok) return toolError(error);
|
|
444
439
|
let text = output.formatUsages(result, ep.name);
|
|
445
440
|
if (note) text += '\n\n' + note;
|
|
446
441
|
return tr(text);
|
|
@@ -449,7 +444,7 @@ server.registerTool(
|
|
|
449
444
|
case 'toc': {
|
|
450
445
|
index = getIndex(project_dir, ep);
|
|
451
446
|
const { ok, result, error, note } = execute(index, 'toc', ep);
|
|
452
|
-
if (!ok) return
|
|
447
|
+
if (!ok) return toolError(error);
|
|
453
448
|
let text = output.formatToc(result, {
|
|
454
449
|
topHint: 'Set top=N or use detailed=false for compact view.'
|
|
455
450
|
});
|
|
@@ -459,25 +454,31 @@ server.registerTool(
|
|
|
459
454
|
|
|
460
455
|
case 'search': {
|
|
461
456
|
index = getIndex(project_dir, ep);
|
|
462
|
-
const { ok, result, error, structural } = execute(index, 'search', ep);
|
|
463
|
-
if (!ok) return
|
|
457
|
+
const { ok, result, error, structural, note } = execute(index, 'search', ep);
|
|
458
|
+
if (!ok) return toolError(error);
|
|
459
|
+
let searchText;
|
|
464
460
|
if (structural) {
|
|
465
|
-
|
|
461
|
+
searchText = output.formatStructuralSearch(result);
|
|
462
|
+
} else {
|
|
463
|
+
searchText = output.formatSearch(result, ep.term);
|
|
466
464
|
}
|
|
467
|
-
|
|
465
|
+
if (note) searchText += '\n\n' + note;
|
|
466
|
+
return tr(searchText);
|
|
468
467
|
}
|
|
469
468
|
|
|
470
469
|
case 'tests': {
|
|
471
470
|
index = getIndex(project_dir, ep);
|
|
472
|
-
const { ok, result, error } = execute(index, 'tests', ep);
|
|
473
|
-
if (!ok) return
|
|
474
|
-
|
|
471
|
+
const { ok, result, error, note } = execute(index, 'tests', ep);
|
|
472
|
+
if (!ok) return toolError(error);
|
|
473
|
+
let testsText = output.formatTests(result, ep.name);
|
|
474
|
+
if (note) testsText += '\n\n' + note;
|
|
475
|
+
return tr(testsText);
|
|
475
476
|
}
|
|
476
477
|
|
|
477
478
|
case 'affected_tests': {
|
|
478
479
|
index = getIndex(project_dir, ep);
|
|
479
480
|
const { ok, result, error, note } = execute(index, 'affectedTests', ep);
|
|
480
|
-
if (!ok) return
|
|
481
|
+
if (!ok) return toolError(error);
|
|
481
482
|
let atText = output.formatAffectedTests(result, { all: ep.all });
|
|
482
483
|
if (note) atText += '\n\n' + note;
|
|
483
484
|
return tr(atText);
|
|
@@ -486,7 +487,7 @@ server.registerTool(
|
|
|
486
487
|
case 'deadcode': {
|
|
487
488
|
index = getIndex(project_dir, ep);
|
|
488
489
|
const { ok, result, error, note } = execute(index, 'deadcode', ep);
|
|
489
|
-
if (!ok) return
|
|
490
|
+
if (!ok) return toolError(error);
|
|
490
491
|
const dcNote = note;
|
|
491
492
|
let dcText = output.formatDeadcode(result, {
|
|
492
493
|
top: ep.top || 0,
|
|
@@ -499,9 +500,11 @@ server.registerTool(
|
|
|
499
500
|
|
|
500
501
|
case 'entrypoints': {
|
|
501
502
|
index = getIndex(project_dir, ep);
|
|
502
|
-
const { ok, result, error } = execute(index, 'entrypoints', ep);
|
|
503
|
-
if (!ok) return
|
|
504
|
-
|
|
503
|
+
const { ok, result, error, note } = execute(index, 'entrypoints', ep);
|
|
504
|
+
if (!ok) return toolError(error);
|
|
505
|
+
let epText = output.formatEntrypoints(result);
|
|
506
|
+
if (note) epText += '\n\n' + note;
|
|
507
|
+
return tr(epText);
|
|
505
508
|
}
|
|
506
509
|
|
|
507
510
|
// ── File Dependencies ───────────────────────────────────────
|
|
@@ -509,28 +512,28 @@ server.registerTool(
|
|
|
509
512
|
case 'imports': {
|
|
510
513
|
index = getIndex(project_dir, ep);
|
|
511
514
|
const { ok, result, error } = execute(index, 'imports', ep);
|
|
512
|
-
if (!ok) return
|
|
515
|
+
if (!ok) return toolError(error);
|
|
513
516
|
return tr(output.formatImports(result, ep.file));
|
|
514
517
|
}
|
|
515
518
|
|
|
516
519
|
case 'exporters': {
|
|
517
520
|
index = getIndex(project_dir, ep);
|
|
518
521
|
const { ok, result, error } = execute(index, 'exporters', ep);
|
|
519
|
-
if (!ok) return
|
|
522
|
+
if (!ok) return toolError(error);
|
|
520
523
|
return tr(output.formatExporters(result, ep.file));
|
|
521
524
|
}
|
|
522
525
|
|
|
523
526
|
case 'file_exports': {
|
|
524
527
|
index = getIndex(project_dir, ep);
|
|
525
528
|
const { ok, result, error } = execute(index, 'fileExports', ep);
|
|
526
|
-
if (!ok) return
|
|
529
|
+
if (!ok) return toolError(error);
|
|
527
530
|
return tr(output.formatFileExports(result, ep.file));
|
|
528
531
|
}
|
|
529
532
|
|
|
530
533
|
case 'graph': {
|
|
531
534
|
index = getIndex(project_dir, ep);
|
|
532
535
|
const { ok, result, error } = execute(index, 'graph', ep);
|
|
533
|
-
if (!ok) return
|
|
536
|
+
if (!ok) return toolError(error);
|
|
534
537
|
return tr(output.formatGraph(result, {
|
|
535
538
|
showAll: ep.all || ep.depth !== undefined,
|
|
536
539
|
maxDepth: ep.depth ?? 2, file: ep.file,
|
|
@@ -542,7 +545,7 @@ server.registerTool(
|
|
|
542
545
|
case 'circular_deps': {
|
|
543
546
|
index = getIndex(project_dir, ep);
|
|
544
547
|
const { ok, result, error } = execute(index, 'circularDeps', ep);
|
|
545
|
-
if (!ok) return
|
|
548
|
+
if (!ok) return toolError(error);
|
|
546
549
|
return tr(output.formatCircularDeps(result));
|
|
547
550
|
}
|
|
548
551
|
|
|
@@ -551,22 +554,24 @@ server.registerTool(
|
|
|
551
554
|
case 'verify': {
|
|
552
555
|
index = getIndex(project_dir, ep);
|
|
553
556
|
const { ok, result, error } = execute(index, 'verify', ep);
|
|
554
|
-
if (!ok) return
|
|
557
|
+
if (!ok) return toolError(error);
|
|
555
558
|
return tr(output.formatVerify(result));
|
|
556
559
|
}
|
|
557
560
|
|
|
558
561
|
case 'plan': {
|
|
559
562
|
index = getIndex(project_dir, ep);
|
|
560
563
|
const { ok, result, error } = execute(index, 'plan', ep);
|
|
561
|
-
if (!ok) return
|
|
564
|
+
if (!ok) return toolError(error);
|
|
562
565
|
return tr(output.formatPlan(result));
|
|
563
566
|
}
|
|
564
567
|
|
|
565
568
|
case 'diff_impact': {
|
|
566
569
|
index = getIndex(project_dir, ep);
|
|
567
|
-
const { ok, result, error } = execute(index, 'diffImpact', ep);
|
|
568
|
-
if (!ok) return
|
|
569
|
-
|
|
570
|
+
const { ok, result, error, note } = execute(index, 'diffImpact', ep);
|
|
571
|
+
if (!ok) return toolError(error);
|
|
572
|
+
let diText = output.formatDiffImpact(result, { all: ep.all });
|
|
573
|
+
if (note) diText += '\n\n' + note;
|
|
574
|
+
return tr(diText);
|
|
570
575
|
}
|
|
571
576
|
|
|
572
577
|
// ── Other ───────────────────────────────────────────────────
|
|
@@ -574,21 +579,21 @@ server.registerTool(
|
|
|
574
579
|
case 'typedef': {
|
|
575
580
|
index = getIndex(project_dir, ep);
|
|
576
581
|
const { ok, result, error } = execute(index, 'typedef', ep);
|
|
577
|
-
if (!ok) return
|
|
582
|
+
if (!ok) return toolError(error);
|
|
578
583
|
return tr(output.formatTypedef(result, ep.name));
|
|
579
584
|
}
|
|
580
585
|
|
|
581
586
|
case 'stacktrace': {
|
|
582
587
|
index = getIndex(project_dir, ep);
|
|
583
588
|
const { ok, result, error } = execute(index, 'stacktrace', ep);
|
|
584
|
-
if (!ok) return
|
|
589
|
+
if (!ok) return toolError(error);
|
|
585
590
|
return tr(output.formatStackTrace(result));
|
|
586
591
|
}
|
|
587
592
|
|
|
588
593
|
case 'api': {
|
|
589
594
|
index = getIndex(project_dir, ep);
|
|
590
595
|
const { ok, result, error, note } = execute(index, 'api', ep);
|
|
591
|
-
if (!ok) return
|
|
596
|
+
if (!ok) return toolError(error);
|
|
592
597
|
let apiText = output.formatApi(result, ep.file || '.');
|
|
593
598
|
if (note) apiText += '\n\n' + note;
|
|
594
599
|
return tr(apiText);
|
|
@@ -596,19 +601,19 @@ server.registerTool(
|
|
|
596
601
|
|
|
597
602
|
case 'stats': {
|
|
598
603
|
index = getIndex(project_dir, ep);
|
|
599
|
-
const { ok, result, error } = execute(index, 'stats', ep);
|
|
600
|
-
if (!ok) return
|
|
601
|
-
|
|
604
|
+
const { ok, result, error, note } = execute(index, 'stats', ep);
|
|
605
|
+
if (!ok) return toolError(error);
|
|
606
|
+
let statsText = output.formatStats(result, { top: ep.top || 0 });
|
|
607
|
+
if (note) statsText += '\n\n' + note;
|
|
608
|
+
return tr(statsText);
|
|
602
609
|
}
|
|
603
610
|
|
|
604
611
|
// ── Extracting Code (via execute) ────────────────────────────
|
|
605
612
|
|
|
606
613
|
case 'fn': {
|
|
607
|
-
const err = requireName(ep.name);
|
|
608
|
-
if (err) return err;
|
|
609
614
|
index = getIndex(project_dir, ep);
|
|
610
615
|
const { ok, result, error, note } = execute(index, 'fn', ep);
|
|
611
|
-
if (!ok) return
|
|
616
|
+
if (!ok) return toolError(error);
|
|
612
617
|
// MCP path security: validate all result files are within project root
|
|
613
618
|
for (const entry of result.entries) {
|
|
614
619
|
const check = resolveAndValidatePath(index, entry.match.relativePath || path.relative(index.root, entry.match.file));
|
|
@@ -619,14 +624,9 @@ server.registerTool(
|
|
|
619
624
|
}
|
|
620
625
|
|
|
621
626
|
case 'class': {
|
|
622
|
-
const err = requireName(ep.name);
|
|
623
|
-
if (err) return err;
|
|
624
|
-
if (ep.maxLines !== undefined && (!Number.isInteger(ep.maxLines) || ep.maxLines < 1)) {
|
|
625
|
-
return toolError(`Invalid max_lines: ${ep.maxLines}. Must be a positive integer.`);
|
|
626
|
-
}
|
|
627
627
|
index = getIndex(project_dir, ep);
|
|
628
628
|
const { ok, result, error, note } = execute(index, 'class', ep);
|
|
629
|
-
if (!ok) return
|
|
629
|
+
if (!ok) return toolError(error); // soft error (class not found)
|
|
630
630
|
// MCP path security: validate all result files are within project root
|
|
631
631
|
for (const entry of result.entries) {
|
|
632
632
|
const check = resolveAndValidatePath(index, entry.match.relativePath || path.relative(index.root, entry.match.file));
|
|
@@ -639,7 +639,7 @@ server.registerTool(
|
|
|
639
639
|
case 'lines': {
|
|
640
640
|
index = getIndex(project_dir, ep);
|
|
641
641
|
const { ok, result, error } = execute(index, 'lines', ep);
|
|
642
|
-
if (!ok) return
|
|
642
|
+
if (!ok) return toolError(error);
|
|
643
643
|
// MCP path security: validate file is within project root
|
|
644
644
|
const check = resolveAndValidatePath(index, result.relativePath);
|
|
645
645
|
if (typeof check !== 'string') return check;
|
|
@@ -657,7 +657,7 @@ server.registerTool(
|
|
|
657
657
|
itemCount: lookup.itemCount, symbolName: lookup.symbolName,
|
|
658
658
|
validateRoot: true
|
|
659
659
|
});
|
|
660
|
-
if (!ok) return
|
|
660
|
+
if (!ok) return toolError(error);
|
|
661
661
|
return tr(result.text);
|
|
662
662
|
}
|
|
663
663
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ucn",
|
|
3
|
-
"version": "3.8.
|
|
3
|
+
"version": "3.8.16",
|
|
4
4
|
"mcpName": "io.github.mleoca/ucn",
|
|
5
5
|
"description": "Code intelligence toolkit for AI agents — extract functions, trace call chains, find callers, detect dead code without reading entire files. Works as MCP server, CLI, or agent skill. Supports JS/TS, Python, Go, Rust, Java.",
|
|
6
6
|
"main": "index.js",
|