ucn 3.8.13 → 3.8.14
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/.claude/skills/ucn/SKILL.md +3 -1
- package/.github/workflows/ci.yml +13 -1
- package/README.md +1 -0
- package/cli/index.js +165 -246
- package/core/analysis.js +1400 -0
- package/core/build-worker.js +194 -0
- package/core/cache.js +105 -7
- package/core/callers.js +194 -64
- package/core/deadcode.js +22 -66
- package/core/discovery.js +9 -54
- package/core/execute.js +139 -54
- package/core/graph.js +615 -0
- package/core/output/analysis-ext.js +271 -0
- package/core/output/analysis.js +491 -0
- package/core/output/extraction.js +188 -0
- package/core/output/find.js +355 -0
- package/core/output/graph.js +399 -0
- package/core/output/refactoring.js +293 -0
- package/core/output/reporting.js +331 -0
- package/core/output/search.js +307 -0
- package/core/output/shared.js +271 -0
- package/core/output/tracing.js +416 -0
- package/core/output.js +15 -3293
- package/core/parallel-build.js +165 -0
- package/core/project.js +299 -3633
- package/core/registry.js +59 -0
- package/core/reporting.js +258 -0
- package/core/search.js +890 -0
- package/core/stacktrace.js +1 -1
- package/core/tracing.js +631 -0
- package/core/verify.js +10 -13
- package/eslint.config.js +43 -0
- package/jsconfig.json +10 -0
- package/languages/go.js +21 -2
- package/languages/html.js +8 -0
- package/languages/index.js +102 -40
- package/languages/java.js +13 -0
- package/languages/javascript.js +17 -1
- package/languages/python.js +14 -0
- package/languages/rust.js +13 -0
- package/languages/utils.js +1 -1
- package/mcp/server.js +45 -28
- package/package.json +8 -3
package/cli/index.js
CHANGED
|
@@ -15,7 +15,7 @@ const { ProjectIndex } = require('../core/project');
|
|
|
15
15
|
const { expandGlob, findProjectRoot } = require('../core/discovery');
|
|
16
16
|
const output = require('../core/output');
|
|
17
17
|
// pickBestDefinition moved to execute.js — no longer needed here
|
|
18
|
-
const { getCliCommandSet, resolveCommand } = require('../core/registry');
|
|
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');
|
|
21
21
|
|
|
@@ -115,6 +115,12 @@ 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
|
+
workers: (() => {
|
|
119
|
+
const v = getValueFlag('--workers');
|
|
120
|
+
if (v === null) return undefined;
|
|
121
|
+
const n = parseInt(v, 10);
|
|
122
|
+
return isNaN(n) ? undefined : n;
|
|
123
|
+
})(),
|
|
118
124
|
};
|
|
119
125
|
}
|
|
120
126
|
|
|
@@ -142,7 +148,7 @@ const knownFlags = new Set([
|
|
|
142
148
|
'--max-lines', '--class-name', '--limit', '--max-files',
|
|
143
149
|
'--type', '--param', '--receiver', '--returns', '--decorator', '--exported', '--unused',
|
|
144
150
|
'--show-confidence', '--no-confidence', '--min-confidence',
|
|
145
|
-
'--framework'
|
|
151
|
+
'--framework', '--workers'
|
|
146
152
|
]);
|
|
147
153
|
|
|
148
154
|
// Handle help flag
|
|
@@ -171,7 +177,8 @@ const VALUE_FLAGS = new Set([
|
|
|
171
177
|
'--add-param', '--remove-param', '--rename-to', '--default',
|
|
172
178
|
'--base', '--exclude', '--not', '--in', '--max-lines', '--class-name',
|
|
173
179
|
'--type', '--param', '--receiver', '--returns', '--decorator',
|
|
174
|
-
'--limit', '--max-files', '--min-confidence', '--stack', '--framework'
|
|
180
|
+
'--limit', '--max-files', '--min-confidence', '--stack', '--framework',
|
|
181
|
+
'--workers'
|
|
175
182
|
]);
|
|
176
183
|
|
|
177
184
|
// Remove flags from args, then add args after -- (which are all positional)
|
|
@@ -346,11 +353,9 @@ function runFileCommand(filePath, command, arg) {
|
|
|
346
353
|
);
|
|
347
354
|
break;
|
|
348
355
|
case 'fn':
|
|
349
|
-
if (result.notes.length) result.notes.forEach(n => console.error('Note: ' + n));
|
|
350
356
|
printOutput(result, output.formatFnResultJson, output.formatFnResult);
|
|
351
357
|
break;
|
|
352
358
|
case 'class':
|
|
353
|
-
if (result.notes.length) result.notes.forEach(n => console.error('Note: ' + n));
|
|
354
359
|
printOutput(result, output.formatClassResultJson, output.formatClassResult);
|
|
355
360
|
break;
|
|
356
361
|
case 'lines':
|
|
@@ -420,7 +425,7 @@ function runProjectCommand(rootDir, command, arg) {
|
|
|
420
425
|
// If cache was loaded but stale, force rebuild to avoid duplicates
|
|
421
426
|
let needsCacheSave = false;
|
|
422
427
|
if (!usedCache) {
|
|
423
|
-
index.build(null, { quiet: flags.quiet, forceRebuild: cacheWasLoaded, followSymlinks: flags.followSymlinks, maxFiles: flags.maxFiles });
|
|
428
|
+
index.build(null, { quiet: flags.quiet, forceRebuild: cacheWasLoaded, followSymlinks: flags.followSymlinks, maxFiles: flags.maxFiles, workers: flags.workers });
|
|
424
429
|
needsCacheSave = flags.cache;
|
|
425
430
|
}
|
|
426
431
|
|
|
@@ -428,6 +433,23 @@ function runProjectCommand(rootDir, command, arg) {
|
|
|
428
433
|
// Resolve CLI aliases to canonical command names — dispatch on canonical
|
|
429
434
|
const canonical = resolveCommand(command, 'cli') || command;
|
|
430
435
|
|
|
436
|
+
// Warn about flags that don't apply to this command
|
|
437
|
+
const applicableFlags = FLAG_APPLICABILITY[canonical];
|
|
438
|
+
if (applicableFlags) {
|
|
439
|
+
// Map from camelCase flag name to CLI flag string
|
|
440
|
+
const flagToCli = (f) => '--' + f.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
|
|
441
|
+
// 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', 'stack', 'showConfidence']);
|
|
443
|
+
for (const [key, value] of Object.entries(flags)) {
|
|
444
|
+
if (globalFlags.has(key)) continue;
|
|
445
|
+
// Skip falsy/default values (0, undefined, false, empty array)
|
|
446
|
+
if (!value || value === 0 || (Array.isArray(value) && value.length === 0)) continue;
|
|
447
|
+
if (!applicableFlags.includes(key)) {
|
|
448
|
+
console.error(`Warning: ${flagToCli(key)} has no effect on '${toCliName(canonical)}'.`);
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
431
453
|
switch (canonical) {
|
|
432
454
|
// ── Commands using shared executor ───────────────────────────────
|
|
433
455
|
|
|
@@ -475,7 +497,7 @@ function runProjectCommand(rootDir, command, arg) {
|
|
|
475
497
|
}
|
|
476
498
|
|
|
477
499
|
case 'context': {
|
|
478
|
-
const { ok, result: ctx, error } = execute(index, 'context', { name: arg, ...flags });
|
|
500
|
+
const { ok, result: ctx, error, note } = execute(index, 'context', { name: arg, ...flags });
|
|
479
501
|
if (!ok) fail(error);
|
|
480
502
|
if (flags.json) {
|
|
481
503
|
console.log(output.formatContextJson(ctx));
|
|
@@ -513,6 +535,7 @@ function runProjectCommand(rootDir, command, arg) {
|
|
|
513
535
|
|
|
514
536
|
// Save expandable items to cache for 'expand' command
|
|
515
537
|
saveExpandableItems(expandable, index.root);
|
|
538
|
+
if (note) console.error(note);
|
|
516
539
|
}
|
|
517
540
|
break;
|
|
518
541
|
}
|
|
@@ -554,16 +577,18 @@ function runProjectCommand(rootDir, command, arg) {
|
|
|
554
577
|
}
|
|
555
578
|
|
|
556
579
|
case 'impact': {
|
|
557
|
-
const { ok, result, error } = execute(index, 'impact', { name: arg, ...flags });
|
|
580
|
+
const { ok, result, error, note } = execute(index, 'impact', { name: arg, ...flags });
|
|
558
581
|
if (!ok) fail(error);
|
|
559
582
|
printOutput(result, output.formatImpactJson, output.formatImpact);
|
|
583
|
+
if (note) console.error(note);
|
|
560
584
|
break;
|
|
561
585
|
}
|
|
562
586
|
|
|
563
587
|
case 'blast': {
|
|
564
|
-
const { ok, result, error } = execute(index, 'blast', { name: arg, ...flags });
|
|
588
|
+
const { ok, result, error, note } = execute(index, 'blast', { name: arg, ...flags });
|
|
565
589
|
if (!ok) fail(error);
|
|
566
590
|
printOutput(result, output.formatBlastJson, output.formatBlast);
|
|
591
|
+
if (note) console.error(note);
|
|
567
592
|
break;
|
|
568
593
|
}
|
|
569
594
|
|
|
@@ -575,16 +600,18 @@ function runProjectCommand(rootDir, command, arg) {
|
|
|
575
600
|
}
|
|
576
601
|
|
|
577
602
|
case 'trace': {
|
|
578
|
-
const { ok, result, error } = execute(index, 'trace', { name: arg, ...flags });
|
|
603
|
+
const { ok, result, error, note } = execute(index, 'trace', { name: arg, ...flags });
|
|
579
604
|
if (!ok) fail(error);
|
|
580
605
|
printOutput(result, output.formatTraceJson, output.formatTrace);
|
|
606
|
+
if (note) console.error(note);
|
|
581
607
|
break;
|
|
582
608
|
}
|
|
583
609
|
|
|
584
610
|
case 'reverseTrace': {
|
|
585
|
-
const { ok, result, error } = execute(index, 'reverseTrace', { name: arg, ...flags });
|
|
611
|
+
const { ok, result, error, note } = execute(index, 'reverseTrace', { name: arg, ...flags });
|
|
586
612
|
if (!ok) fail(error);
|
|
587
613
|
printOutput(result, output.formatReverseTraceJson, output.formatReverseTrace);
|
|
614
|
+
if (note) console.error(note);
|
|
588
615
|
break;
|
|
589
616
|
}
|
|
590
617
|
|
|
@@ -603,9 +630,10 @@ function runProjectCommand(rootDir, command, arg) {
|
|
|
603
630
|
}
|
|
604
631
|
|
|
605
632
|
case 'related': {
|
|
606
|
-
const { ok, result, error } = execute(index, 'related', { name: arg, ...flags });
|
|
633
|
+
const { ok, result, error, note } = execute(index, 'related', { name: arg, ...flags });
|
|
607
634
|
if (!ok) fail(error);
|
|
608
635
|
printOutput(result, output.formatRelatedJson, r => output.formatRelated(r, { all: flags.all, top: flags.top }));
|
|
636
|
+
if (note) console.error(note);
|
|
609
637
|
break;
|
|
610
638
|
}
|
|
611
639
|
|
|
@@ -613,18 +641,18 @@ function runProjectCommand(rootDir, command, arg) {
|
|
|
613
641
|
|
|
614
642
|
case 'fn': {
|
|
615
643
|
requireArg(arg, 'Usage: ucn . fn <name>');
|
|
616
|
-
const { ok, result, error } = execute(index, 'fn', { name: arg, file: flags.file, all: flags.all });
|
|
644
|
+
const { ok, result, error, note } = execute(index, 'fn', { name: arg, file: flags.file, all: flags.all });
|
|
617
645
|
if (!ok) fail(error);
|
|
618
|
-
if (
|
|
646
|
+
if (note) console.error(note);
|
|
619
647
|
printOutput(result, output.formatFnResultJson, output.formatFnResult);
|
|
620
648
|
break;
|
|
621
649
|
}
|
|
622
650
|
|
|
623
651
|
case 'class': {
|
|
624
652
|
requireArg(arg, 'Usage: ucn . class <name>');
|
|
625
|
-
const { ok, result, error } = execute(index, 'class', { name: arg, file: flags.file, all: flags.all, maxLines: flags.maxLines });
|
|
653
|
+
const { ok, result, error, note } = execute(index, 'class', { name: arg, file: flags.file, all: flags.all, maxLines: flags.maxLines });
|
|
626
654
|
if (!ok) fail(error);
|
|
627
|
-
if (
|
|
655
|
+
if (note) console.error(note);
|
|
628
656
|
printOutput(result, output.formatClassResultJson, output.formatClassResult);
|
|
629
657
|
break;
|
|
630
658
|
}
|
|
@@ -640,37 +668,41 @@ function runProjectCommand(rootDir, command, arg) {
|
|
|
640
668
|
// ── File dependency commands ────────────────────────────────────
|
|
641
669
|
|
|
642
670
|
case 'imports': {
|
|
643
|
-
const
|
|
671
|
+
const filePath = arg || flags.file;
|
|
672
|
+
const { ok, result, error } = execute(index, 'imports', { file: filePath });
|
|
644
673
|
if (!ok) fail(error);
|
|
645
674
|
printOutput(result,
|
|
646
|
-
r => output.formatImportsJson(r,
|
|
647
|
-
r => output.formatImports(r,
|
|
675
|
+
r => output.formatImportsJson(r, filePath),
|
|
676
|
+
r => output.formatImports(r, filePath)
|
|
648
677
|
);
|
|
649
678
|
break;
|
|
650
679
|
}
|
|
651
680
|
|
|
652
681
|
case 'exporters': {
|
|
653
|
-
const
|
|
682
|
+
const filePath = arg || flags.file;
|
|
683
|
+
const { ok, result, error } = execute(index, 'exporters', { file: filePath });
|
|
654
684
|
if (!ok) fail(error);
|
|
655
685
|
printOutput(result,
|
|
656
|
-
r => output.formatExportersJson(r,
|
|
657
|
-
r => output.formatExporters(r,
|
|
686
|
+
r => output.formatExportersJson(r, filePath),
|
|
687
|
+
r => output.formatExporters(r, filePath)
|
|
658
688
|
);
|
|
659
689
|
break;
|
|
660
690
|
}
|
|
661
691
|
|
|
662
692
|
case 'fileExports': {
|
|
663
|
-
const
|
|
693
|
+
const filePath = arg || flags.file;
|
|
694
|
+
const { ok, result, error } = execute(index, 'fileExports', { file: filePath });
|
|
664
695
|
if (!ok) fail(error);
|
|
665
696
|
printOutput(result,
|
|
666
|
-
r => JSON.stringify({ file:
|
|
667
|
-
r => output.formatFileExports(r,
|
|
697
|
+
r => JSON.stringify({ file: filePath, exports: r }, null, 2),
|
|
698
|
+
r => output.formatFileExports(r, filePath)
|
|
668
699
|
);
|
|
669
700
|
break;
|
|
670
701
|
}
|
|
671
702
|
|
|
672
703
|
case 'graph': {
|
|
673
|
-
const
|
|
704
|
+
const filePath = arg || flags.file;
|
|
705
|
+
const { ok, result, error } = execute(index, 'graph', { file: filePath, direction: flags.direction, depth: flags.depth, all: flags.all });
|
|
674
706
|
if (!ok) fail(error);
|
|
675
707
|
printOutput(result,
|
|
676
708
|
r => JSON.stringify({
|
|
@@ -678,7 +710,7 @@ function runProjectCommand(rootDir, command, arg) {
|
|
|
678
710
|
nodes: r.nodes.map(n => ({ file: n.relativePath, depth: n.depth })),
|
|
679
711
|
edges: r.edges.map(e => ({ from: path.relative(index.root, e.from), to: path.relative(index.root, e.to) }))
|
|
680
712
|
}, null, 2),
|
|
681
|
-
r => output.formatGraph(r, { showAll: flags.all || flags.depth != null, maxDepth: flags.depth != null ? parseInt(flags.depth, 10) : 2, file:
|
|
713
|
+
r => output.formatGraph(r, { showAll: flags.all || flags.depth != null, maxDepth: flags.depth != null ? parseInt(flags.depth, 10) : 2, file: filePath })
|
|
682
714
|
);
|
|
683
715
|
break;
|
|
684
716
|
}
|
|
@@ -713,19 +745,21 @@ function runProjectCommand(rootDir, command, arg) {
|
|
|
713
745
|
}
|
|
714
746
|
|
|
715
747
|
case 'affectedTests': {
|
|
716
|
-
const { ok, result, error } = execute(index, 'affectedTests', { name: arg, ...flags });
|
|
748
|
+
const { ok, result, error, note } = execute(index, 'affectedTests', { name: arg, ...flags });
|
|
717
749
|
if (!ok) fail(error);
|
|
718
750
|
printOutput(result, output.formatAffectedTestsJson, r => output.formatAffectedTests(r, { all: flags.all }));
|
|
751
|
+
if (note) console.error(note);
|
|
719
752
|
break;
|
|
720
753
|
}
|
|
721
754
|
|
|
722
755
|
case 'api': {
|
|
723
|
-
const
|
|
756
|
+
const filePath = arg || flags.file;
|
|
757
|
+
const { ok, result, error, note } = execute(index, 'api', { file: filePath, limit: flags.limit });
|
|
724
758
|
if (!ok) fail(error);
|
|
725
759
|
if (note) console.error(note);
|
|
726
760
|
printOutput(result,
|
|
727
|
-
r => output.formatApiJson(r,
|
|
728
|
-
r => output.formatApi(r,
|
|
761
|
+
r => output.formatApiJson(r, filePath),
|
|
762
|
+
r => output.formatApi(r, filePath)
|
|
729
763
|
);
|
|
730
764
|
break;
|
|
731
765
|
}
|
|
@@ -864,7 +898,7 @@ function runGlobCommand(pattern, command, arg) {
|
|
|
864
898
|
}
|
|
865
899
|
|
|
866
900
|
switch (command) {
|
|
867
|
-
case 'toc':
|
|
901
|
+
case 'toc': {
|
|
868
902
|
let totalFunctions = 0;
|
|
869
903
|
let totalClasses = 0;
|
|
870
904
|
let totalState = 0;
|
|
@@ -915,6 +949,7 @@ function runGlobCommand(pattern, command, arg) {
|
|
|
915
949
|
}));
|
|
916
950
|
}
|
|
917
951
|
break;
|
|
952
|
+
}
|
|
918
953
|
|
|
919
954
|
case 'find':
|
|
920
955
|
if (!arg) {
|
|
@@ -1113,33 +1148,37 @@ Common Flags:
|
|
|
1113
1148
|
--file <pattern> Filter by file path (e.g., --file=routes)
|
|
1114
1149
|
--exclude=a,b Exclude patterns (e.g., --exclude=test,mock)
|
|
1115
1150
|
--in=<path> Only in path (e.g., --in=src/core)
|
|
1116
|
-
--depth=N
|
|
1151
|
+
--depth=N Max depth: blast=3, trace=3, reverse-trace=5, graph=2, affected-tests=3
|
|
1117
1152
|
--direction=X Graph direction: imports, importers, or both (default: both)
|
|
1118
|
-
--all
|
|
1119
|
-
|
|
1120
|
-
--
|
|
1153
|
+
--all Show full results: all callers/callees (about), full tree (trace/blast),
|
|
1154
|
+
all names (related/find/fn/class/toc), all changed (diff-impact)
|
|
1155
|
+
--top=N Limit callers/callees (about), similar functions (related), search results
|
|
1156
|
+
--limit=N Limit result count (find, usages, search, deadcode, api, toc, entrypoints, diff-impact)
|
|
1121
1157
|
--max-files=N Max files to index (large projects)
|
|
1122
|
-
--context=N Lines of context around matches
|
|
1158
|
+
--context=N Lines of context around matches (search, usages)
|
|
1123
1159
|
--json Machine-readable output
|
|
1124
|
-
--code-only Filter out comments
|
|
1125
|
-
--with-types Include type definitions
|
|
1160
|
+
--code-only Filter out comments/strings (search, usages)
|
|
1161
|
+
--with-types Include type definitions (about, smart)
|
|
1162
|
+
--detailed Show all symbols in toc (not just counts)
|
|
1126
1163
|
--include-tests Include test files
|
|
1127
1164
|
--class-name=X Scope to specific class (e.g., --class-name=Repository)
|
|
1128
1165
|
--include-methods Include method calls (obj.fn) in caller/callee analysis
|
|
1129
1166
|
--include-uncertain Include ambiguous/uncertain matches
|
|
1130
|
-
--no-confidence Hide confidence scores (shown by default)
|
|
1131
|
-
--min-confidence=N Filter edges
|
|
1167
|
+
--no-confidence Hide confidence scores (shown by default in about, context)
|
|
1168
|
+
--min-confidence=N Filter low-confidence edges (about, context, blast, trace,
|
|
1169
|
+
reverse-trace, smart, affected-tests)
|
|
1170
|
+
--show-confidence Show confidence scores on caller/callee edges (about, context)
|
|
1132
1171
|
--include-exported Include exported symbols in deadcode
|
|
1133
1172
|
--no-regex Force plain text search (regex is default)
|
|
1134
1173
|
--functions Show per-function line counts (stats command)
|
|
1135
1174
|
--include-decorated Include decorated/annotated symbols in deadcode
|
|
1136
1175
|
--framework=X Filter entrypoints by framework (e.g., --framework=express,spring)
|
|
1137
|
-
--exact Exact name match only (find)
|
|
1176
|
+
--exact Exact name match only (find, typedef)
|
|
1138
1177
|
--calls-only Only show call/test-case matches (tests)
|
|
1139
1178
|
--case-sensitive Case-sensitive text search (search)
|
|
1140
|
-
--detailed List all symbols in toc (compact by default)
|
|
1141
1179
|
--top-level Show only top-level functions in toc
|
|
1142
1180
|
--max-lines=N Max source lines for class (large classes show summary)
|
|
1181
|
+
--workers=N Parallel build workers (auto-detect; 0 to disable, env: UCN_WORKERS)
|
|
1143
1182
|
--no-cache Disable caching
|
|
1144
1183
|
--clear-cache Clear cache before running
|
|
1145
1184
|
--base=<ref> Git ref for diff-impact (default: HEAD)
|
|
@@ -1165,7 +1204,7 @@ function runInteractive(rootDir) {
|
|
|
1165
1204
|
|
|
1166
1205
|
console.log('Building index...');
|
|
1167
1206
|
const index = new ProjectIndex(rootDir);
|
|
1168
|
-
index.build(null, { quiet: true });
|
|
1207
|
+
index.build(null, { quiet: true, workers: flags.workers });
|
|
1169
1208
|
const iExpandCache = new ExpandCache({ maxSize: 20 });
|
|
1170
1209
|
console.log(`Index ready: ${index.files.size} files, ${index.symbols.size} symbols`);
|
|
1171
1210
|
console.log('Type commands (e.g., "find parseFile", "about main", "toc")');
|
|
@@ -1239,7 +1278,7 @@ Flags can be added per-command: context myFunc --include-methods
|
|
|
1239
1278
|
|
|
1240
1279
|
if (input === 'rebuild') {
|
|
1241
1280
|
console.log('Rebuilding index...');
|
|
1242
|
-
index.build(null, { quiet: true, forceRebuild: true });
|
|
1281
|
+
index.build(null, { quiet: true, forceRebuild: true, workers: flags.workers });
|
|
1243
1282
|
console.log(`Index ready: ${index.files.size} files, ${index.symbols.size} symbols`);
|
|
1244
1283
|
rl.prompt();
|
|
1245
1284
|
return;
|
|
@@ -1286,25 +1325,89 @@ Flags can be added per-command: context myFunc --include-methods
|
|
|
1286
1325
|
|
|
1287
1326
|
// parseInteractiveFlags removed — both global and interactive mode now use parseFlags()
|
|
1288
1327
|
|
|
1328
|
+
// ── Data-driven interactive command dispatch ─────────────────────────────
|
|
1329
|
+
//
|
|
1330
|
+
// Each entry maps a canonical command name to:
|
|
1331
|
+
// params: (arg, iflags) => execute() params object
|
|
1332
|
+
// format: (result, arg, iflags, index) => formatted string
|
|
1333
|
+
//
|
|
1334
|
+
// The generic handler calls execute(), checks errors, prints notes, and
|
|
1335
|
+
// formats the result. Only commands with truly unique behavior (expand
|
|
1336
|
+
// cache save, file writing, conditional formatters) keep explicit cases.
|
|
1337
|
+
|
|
1338
|
+
const INTERACTIVE_DISPATCH = {
|
|
1339
|
+
// ── Understanding Code ───────────────────────────────────────────
|
|
1340
|
+
about: { params: 'name', format: (r, _a, f, idx) => output.formatAbout(r, { expand: f.expand, root: idx.root, showAll: f.all, depth: f.depth, showConfidence: f.showConfidence }) },
|
|
1341
|
+
smart: { params: 'name', format: (r) => output.formatSmart(r, { uncertainHint: 'use --include-uncertain to include all' }) },
|
|
1342
|
+
impact: { params: 'name', format: (r) => output.formatImpact(r) },
|
|
1343
|
+
blast: { params: 'name', format: (r) => output.formatBlast(r) },
|
|
1344
|
+
trace: { params: 'name', format: (r) => output.formatTrace(r) },
|
|
1345
|
+
reverseTrace: { params: 'name', format: (r) => output.formatReverseTrace(r) },
|
|
1346
|
+
related: { params: 'name', format: (r, _a, f) => output.formatRelated(r, { all: f.all, top: f.top }) },
|
|
1347
|
+
example: { params: (a) => ({ name: a }), format: (r, a) => output.formatExample(r, a) },
|
|
1348
|
+
|
|
1349
|
+
// ── Finding Code ─────────────────────────────────────────────────
|
|
1350
|
+
find: { params: 'name', format: (r, a, f) => output.formatFindDetailed(r, a, { depth: f.depth, top: f.top, all: f.all }) },
|
|
1351
|
+
usages: { params: 'name', format: (r, a) => output.formatUsages(r, a) },
|
|
1352
|
+
toc: { params: 'flags', format: (r) => output.formatToc(r, { detailedHint: 'Add --detailed to list all functions, or "about <name>" for full details on a symbol', uncertainHint: 'use --include-uncertain to include all' }) },
|
|
1353
|
+
tests: { params: 'name', format: (r, a) => output.formatTests(r, a) },
|
|
1354
|
+
affectedTests: { params: 'name', format: (r, _a, f) => output.formatAffectedTests(r, { all: f.all }) },
|
|
1355
|
+
typedef: { params: 'name', format: (r, a) => output.formatTypedef(r, a) },
|
|
1356
|
+
|
|
1357
|
+
// ── File Dependencies ────────────────────────────────────────────
|
|
1358
|
+
imports: { params: 'file', format: (r, a, f) => output.formatImports(r, a || f.file) },
|
|
1359
|
+
exporters: { params: 'file', format: (r, a, f) => output.formatExporters(r, a || f.file) },
|
|
1360
|
+
fileExports: { params: 'file', format: (r, a, f) => output.formatFileExports(r, a || f.file) },
|
|
1361
|
+
graph: { params: (a, f) => ({ file: a || f.file, direction: f.direction, depth: f.depth, all: f.all }), format: (r, a, f) => { const d = f.depth ? parseInt(f.depth) : 2; return output.formatGraph(r, { showAll: f.all || !!f.depth, maxDepth: d, file: a || f.file }); } },
|
|
1362
|
+
circularDeps: { params: (a, f) => ({ file: f.file, exclude: f.exclude }), format: (r) => output.formatCircularDeps(r) },
|
|
1363
|
+
|
|
1364
|
+
// ── Refactoring Helpers ──────────────────────────────────────────
|
|
1365
|
+
plan: { params: 'name', format: (r) => output.formatPlan(r) },
|
|
1366
|
+
verify: { params: 'name', format: (r) => output.formatVerify(r) },
|
|
1367
|
+
diffImpact: { params: 'flags', format: (r, _a, f) => output.formatDiffImpact(r, { all: f.all }) },
|
|
1368
|
+
entrypoints: { params: (a, f) => ({ type: f.type, framework: f.framework, file: f.file, exclude: f.exclude }), format: (r) => output.formatEntrypoints(r) },
|
|
1369
|
+
|
|
1370
|
+
// ── Other ────────────────────────────────────────────────────────
|
|
1371
|
+
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) },
|
|
1373
|
+
stats: { params: 'flags', format: (r, _a, f) => output.formatStats(r, { top: f.top }) },
|
|
1374
|
+
};
|
|
1375
|
+
|
|
1376
|
+
/**
|
|
1377
|
+
* Build execute() params from a dispatch entry's params descriptor.
|
|
1378
|
+
* 'name' → { name: arg, ...iflags }
|
|
1379
|
+
* 'file' → { file: arg }
|
|
1380
|
+
* 'flags' → iflags (no arg)
|
|
1381
|
+
* function → custom builder
|
|
1382
|
+
*/
|
|
1383
|
+
function buildInteractiveParams(descriptor, arg, iflags) {
|
|
1384
|
+
if (typeof descriptor === 'function') return descriptor(arg, iflags);
|
|
1385
|
+
switch (descriptor) {
|
|
1386
|
+
case 'name': return { name: arg, ...iflags };
|
|
1387
|
+
case 'file': return { file: arg || iflags.file };
|
|
1388
|
+
case 'flags': return iflags;
|
|
1389
|
+
default: return { name: arg, ...iflags };
|
|
1390
|
+
}
|
|
1391
|
+
}
|
|
1392
|
+
|
|
1289
1393
|
function executeInteractiveCommand(index, command, arg, iflags = {}, cache = null) {
|
|
1394
|
+
// ── Commands with unique behavior (not data-driven) ──────────────
|
|
1290
1395
|
switch (command) {
|
|
1291
1396
|
|
|
1292
|
-
// ── Extraction commands (via execute) ────────────────────────────
|
|
1293
|
-
|
|
1294
1397
|
case 'fn': {
|
|
1295
1398
|
if (!arg) { console.log('Usage: fn <name>[,name2,...] [--file=<pattern>]'); return; }
|
|
1296
|
-
const { ok, result, error } = execute(index, 'fn', { name: arg, file: iflags.file, all: iflags.all });
|
|
1399
|
+
const { ok, result, error, note } = execute(index, 'fn', { name: arg, file: iflags.file, all: iflags.all });
|
|
1297
1400
|
if (!ok) { console.log(error); return; }
|
|
1298
|
-
if (
|
|
1401
|
+
if (note) console.log(note);
|
|
1299
1402
|
console.log(output.formatFnResult(result));
|
|
1300
1403
|
break;
|
|
1301
1404
|
}
|
|
1302
1405
|
|
|
1303
1406
|
case 'class': {
|
|
1304
1407
|
if (!arg) { console.log('Usage: class <name> [--file=<pattern>]'); return; }
|
|
1305
|
-
const { ok, result, error } = execute(index, 'class', { name: arg, file: iflags.file, all: iflags.all, maxLines: iflags.maxLines });
|
|
1408
|
+
const { ok, result, error, note } = execute(index, 'class', { name: arg, file: iflags.file, all: iflags.all, maxLines: iflags.maxLines });
|
|
1306
1409
|
if (!ok) { console.log(error); return; }
|
|
1307
|
-
if (
|
|
1410
|
+
if (note) console.log(note);
|
|
1308
1411
|
console.log(output.formatClassResult(result));
|
|
1309
1412
|
break;
|
|
1310
1413
|
}
|
|
@@ -1347,16 +1450,6 @@ function executeInteractiveCommand(index, command, arg, iflags = {}, cache = nul
|
|
|
1347
1450
|
break;
|
|
1348
1451
|
}
|
|
1349
1452
|
|
|
1350
|
-
case 'find': {
|
|
1351
|
-
const { ok, result, error, note } = execute(index, 'find', { name: arg, ...iflags });
|
|
1352
|
-
if (!ok) { console.log(error); return; }
|
|
1353
|
-
if (note) console.log(note);
|
|
1354
|
-
console.log(output.formatFindDetailed(result, arg, { depth: iflags.depth, top: iflags.top, all: iflags.all }));
|
|
1355
|
-
break;
|
|
1356
|
-
}
|
|
1357
|
-
|
|
1358
|
-
// ── context: needs expandable items cache ────────────────────────
|
|
1359
|
-
|
|
1360
1453
|
case 'context': {
|
|
1361
1454
|
const { ok, result, error } = execute(index, 'context', { name: arg, ...iflags });
|
|
1362
1455
|
if (!ok) { console.log(error); return; }
|
|
@@ -1375,8 +1468,6 @@ function executeInteractiveCommand(index, command, arg, iflags = {}, cache = nul
|
|
|
1375
1468
|
break;
|
|
1376
1469
|
}
|
|
1377
1470
|
|
|
1378
|
-
// ── deadcode: needs result fields for hint construction ──────────
|
|
1379
|
-
|
|
1380
1471
|
case 'deadcode': {
|
|
1381
1472
|
const { ok, result, error } = execute(index, 'deadcode', iflags);
|
|
1382
1473
|
if (!ok) { console.log(error); return; }
|
|
@@ -1388,126 +1479,6 @@ function executeInteractiveCommand(index, command, arg, iflags = {}, cache = nul
|
|
|
1388
1479
|
break;
|
|
1389
1480
|
}
|
|
1390
1481
|
|
|
1391
|
-
case 'entrypoints': {
|
|
1392
|
-
const { ok, result, error } = execute(index, 'entrypoints', { type: iflags.type, framework: iflags.framework, file: iflags.file, exclude: iflags.exclude });
|
|
1393
|
-
if (!ok) { console.log(error); return; }
|
|
1394
|
-
console.log(output.formatEntrypoints(result));
|
|
1395
|
-
break;
|
|
1396
|
-
}
|
|
1397
|
-
|
|
1398
|
-
// ── Standard commands routed through execute() ───────────────────
|
|
1399
|
-
|
|
1400
|
-
case 'toc': {
|
|
1401
|
-
const { ok, result, error } = execute(index, 'toc', iflags);
|
|
1402
|
-
if (!ok) { console.log(error); return; }
|
|
1403
|
-
console.log(output.formatToc(result, {
|
|
1404
|
-
detailedHint: 'Add --detailed to list all functions, or "about <name>" for full details on a symbol',
|
|
1405
|
-
uncertainHint: 'use --include-uncertain to include all'
|
|
1406
|
-
}));
|
|
1407
|
-
break;
|
|
1408
|
-
}
|
|
1409
|
-
|
|
1410
|
-
case 'about': {
|
|
1411
|
-
const { ok, result, error } = execute(index, 'about', { name: arg, ...iflags });
|
|
1412
|
-
if (!ok) { console.log(error); return; }
|
|
1413
|
-
console.log(output.formatAbout(result, { expand: iflags.expand, root: index.root, showAll: iflags.all, depth: iflags.depth, showConfidence: iflags.showConfidence }));
|
|
1414
|
-
break;
|
|
1415
|
-
}
|
|
1416
|
-
|
|
1417
|
-
case 'usages': {
|
|
1418
|
-
const { ok, result, error } = execute(index, 'usages', { name: arg, ...iflags });
|
|
1419
|
-
if (!ok) { console.log(error); return; }
|
|
1420
|
-
console.log(output.formatUsages(result, arg));
|
|
1421
|
-
break;
|
|
1422
|
-
}
|
|
1423
|
-
|
|
1424
|
-
case 'smart': {
|
|
1425
|
-
const { ok, result, error } = execute(index, 'smart', { name: arg, ...iflags });
|
|
1426
|
-
if (!ok) { console.log(error); return; }
|
|
1427
|
-
console.log(output.formatSmart(result, {
|
|
1428
|
-
uncertainHint: 'use --include-uncertain to include all'
|
|
1429
|
-
}));
|
|
1430
|
-
break;
|
|
1431
|
-
}
|
|
1432
|
-
|
|
1433
|
-
case 'impact': {
|
|
1434
|
-
const { ok, result, error } = execute(index, 'impact', { name: arg, ...iflags });
|
|
1435
|
-
if (!ok) { console.log(error); return; }
|
|
1436
|
-
console.log(output.formatImpact(result));
|
|
1437
|
-
break;
|
|
1438
|
-
}
|
|
1439
|
-
|
|
1440
|
-
case 'blast': {
|
|
1441
|
-
const { ok, result, error } = execute(index, 'blast', { name: arg, ...iflags });
|
|
1442
|
-
if (!ok) { console.log(error); return; }
|
|
1443
|
-
console.log(output.formatBlast(result));
|
|
1444
|
-
break;
|
|
1445
|
-
}
|
|
1446
|
-
|
|
1447
|
-
case 'trace': {
|
|
1448
|
-
const { ok, result, error } = execute(index, 'trace', { name: arg, ...iflags });
|
|
1449
|
-
if (!ok) { console.log(error); return; }
|
|
1450
|
-
console.log(output.formatTrace(result));
|
|
1451
|
-
break;
|
|
1452
|
-
}
|
|
1453
|
-
|
|
1454
|
-
case 'reverseTrace': {
|
|
1455
|
-
const { ok, result, error } = execute(index, 'reverseTrace', { name: arg, ...iflags });
|
|
1456
|
-
if (!ok) { console.log(error); return; }
|
|
1457
|
-
console.log(output.formatReverseTrace(result));
|
|
1458
|
-
break;
|
|
1459
|
-
}
|
|
1460
|
-
|
|
1461
|
-
case 'graph': {
|
|
1462
|
-
const { ok, result, error } = execute(index, 'graph', { file: arg || iflags.file, direction: iflags.direction, depth: iflags.depth, all: iflags.all });
|
|
1463
|
-
if (!ok) { console.log(error); return; }
|
|
1464
|
-
const graphDepth = iflags.depth ? parseInt(iflags.depth) : 2;
|
|
1465
|
-
console.log(output.formatGraph(result, { showAll: iflags.all || !!iflags.depth, maxDepth: graphDepth, file: arg }));
|
|
1466
|
-
break;
|
|
1467
|
-
}
|
|
1468
|
-
|
|
1469
|
-
case 'circularDeps': {
|
|
1470
|
-
const { ok, result, error } = execute(index, 'circularDeps', { file: iflags.file, exclude: iflags.exclude });
|
|
1471
|
-
if (!ok) { console.log(error); return; }
|
|
1472
|
-
console.log(output.formatCircularDeps(result));
|
|
1473
|
-
break;
|
|
1474
|
-
}
|
|
1475
|
-
|
|
1476
|
-
case 'fileExports': {
|
|
1477
|
-
const { ok, result, error } = execute(index, 'fileExports', { file: arg });
|
|
1478
|
-
if (!ok) { console.log(error); return; }
|
|
1479
|
-
console.log(output.formatFileExports(result, arg));
|
|
1480
|
-
break;
|
|
1481
|
-
}
|
|
1482
|
-
|
|
1483
|
-
case 'imports': {
|
|
1484
|
-
const { ok, result, error } = execute(index, 'imports', { file: arg });
|
|
1485
|
-
if (!ok) { console.log(error); return; }
|
|
1486
|
-
console.log(output.formatImports(result, arg));
|
|
1487
|
-
break;
|
|
1488
|
-
}
|
|
1489
|
-
|
|
1490
|
-
case 'exporters': {
|
|
1491
|
-
const { ok, result, error } = execute(index, 'exporters', { file: arg });
|
|
1492
|
-
if (!ok) { console.log(error); return; }
|
|
1493
|
-
console.log(output.formatExporters(result, arg));
|
|
1494
|
-
break;
|
|
1495
|
-
}
|
|
1496
|
-
|
|
1497
|
-
case 'tests': {
|
|
1498
|
-
const { ok, result, error } = execute(index, 'tests', { name: arg, ...iflags });
|
|
1499
|
-
if (!ok) { console.log(error); return; }
|
|
1500
|
-
console.log(output.formatTests(result, arg));
|
|
1501
|
-
break;
|
|
1502
|
-
}
|
|
1503
|
-
|
|
1504
|
-
case 'affectedTests': {
|
|
1505
|
-
const { ok, result, error } = execute(index, 'affectedTests', { name: arg, ...iflags });
|
|
1506
|
-
if (!ok) { console.log(error); return; }
|
|
1507
|
-
console.log(output.formatAffectedTests(result, { all: iflags.all }));
|
|
1508
|
-
break;
|
|
1509
|
-
}
|
|
1510
|
-
|
|
1511
1482
|
case 'search': {
|
|
1512
1483
|
const { ok, result, error, structural } = execute(index, 'search', { term: arg, ...iflags });
|
|
1513
1484
|
if (!ok) { console.log(error); return; }
|
|
@@ -1519,71 +1490,19 @@ function executeInteractiveCommand(index, command, arg, iflags = {}, cache = nul
|
|
|
1519
1490
|
break;
|
|
1520
1491
|
}
|
|
1521
1492
|
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
const { ok, result, error } = execute(index,
|
|
1531
|
-
if (!ok) { console.log(error); return; }
|
|
1532
|
-
console.log(output.formatApi(result, arg || '.'));
|
|
1533
|
-
break;
|
|
1534
|
-
}
|
|
1535
|
-
|
|
1536
|
-
case 'diffImpact': {
|
|
1537
|
-
const { ok, result, error } = execute(index, 'diffImpact', iflags);
|
|
1538
|
-
if (!ok) { console.log(error); return; }
|
|
1539
|
-
console.log(output.formatDiffImpact(result, { all: iflags.all }));
|
|
1540
|
-
break;
|
|
1541
|
-
}
|
|
1542
|
-
|
|
1543
|
-
case 'stats': {
|
|
1544
|
-
const { ok, result, error } = execute(index, 'stats', iflags);
|
|
1545
|
-
if (!ok) { console.log(error); return; }
|
|
1546
|
-
console.log(output.formatStats(result, { top: iflags.top }));
|
|
1547
|
-
break;
|
|
1548
|
-
}
|
|
1549
|
-
|
|
1550
|
-
case 'related': {
|
|
1551
|
-
const { ok, result, error } = execute(index, 'related', { name: arg, ...iflags });
|
|
1552
|
-
if (!ok) { console.log(error); return; }
|
|
1553
|
-
console.log(output.formatRelated(result, { all: iflags.all, top: iflags.top }));
|
|
1554
|
-
break;
|
|
1555
|
-
}
|
|
1556
|
-
|
|
1557
|
-
case 'example': {
|
|
1558
|
-
const { ok, result, error } = execute(index, 'example', { name: arg });
|
|
1559
|
-
if (!ok) { console.log(error); return; }
|
|
1560
|
-
console.log(output.formatExample(result, arg));
|
|
1561
|
-
break;
|
|
1562
|
-
}
|
|
1563
|
-
|
|
1564
|
-
case 'plan': {
|
|
1565
|
-
const { ok, result, error } = execute(index, 'plan', { name: arg, ...iflags });
|
|
1566
|
-
if (!ok) { console.log(error); return; }
|
|
1567
|
-
console.log(output.formatPlan(result));
|
|
1568
|
-
break;
|
|
1569
|
-
}
|
|
1570
|
-
|
|
1571
|
-
case 'verify': {
|
|
1572
|
-
const { ok, result, error } = execute(index, 'verify', { name: arg, ...iflags });
|
|
1573
|
-
if (!ok) { console.log(error); return; }
|
|
1574
|
-
console.log(output.formatVerify(result));
|
|
1575
|
-
break;
|
|
1576
|
-
}
|
|
1577
|
-
|
|
1578
|
-
case 'stacktrace': {
|
|
1579
|
-
const { ok, result, error } = execute(index, 'stacktrace', { stack: arg });
|
|
1493
|
+
default: {
|
|
1494
|
+
// ── Data-driven dispatch for standard commands ────────────
|
|
1495
|
+
const entry = INTERACTIVE_DISPATCH[command];
|
|
1496
|
+
if (!entry) {
|
|
1497
|
+
console.log(`Unknown command: ${command}. Type "help" for available commands.`);
|
|
1498
|
+
return;
|
|
1499
|
+
}
|
|
1500
|
+
const params = buildInteractiveParams(entry.params, arg, iflags);
|
|
1501
|
+
const { ok, result, error, note } = execute(index, command, params);
|
|
1580
1502
|
if (!ok) { console.log(error); return; }
|
|
1581
|
-
console.log(
|
|
1582
|
-
|
|
1503
|
+
if (note) console.log(note);
|
|
1504
|
+
console.log(entry.format(result, arg, iflags, index));
|
|
1583
1505
|
}
|
|
1584
|
-
|
|
1585
|
-
default:
|
|
1586
|
-
console.log(`Unknown command: ${command}. Type "help" for available commands.`);
|
|
1587
1506
|
}
|
|
1588
1507
|
}
|
|
1589
1508
|
|