ucn 3.7.5 → 3.7.6

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 CHANGED
@@ -15,6 +15,7 @@ const { getParser, getLanguageModule } = require('../languages');
15
15
  const { ProjectIndex } = require('../core/project');
16
16
  const { expandGlob, findProjectRoot, isTestFile } = require('../core/discovery');
17
17
  const output = require('../core/output');
18
+ const { pickBestDefinition, addTestExclusions } = require('../core/shared');
18
19
 
19
20
  // ============================================================================
20
21
  // ARGUMENT PARSING
@@ -141,13 +142,6 @@ const positionalArgs = [
141
142
  * Add test file patterns to exclusion list
142
143
  * Used by find/usages when --include-tests is not specified
143
144
  */
144
- function addTestExclusions(exclude) {
145
- const testPatterns = ['test', 'spec', '__tests__', '__mocks__', 'fixture', 'mock'];
146
- const existing = new Set(exclude.map(e => e.toLowerCase()));
147
- const additions = testPatterns.filter(p => !existing.has(p));
148
- return [...exclude, ...additions];
149
- }
150
-
151
145
  /**
152
146
  * Validate required argument and exit with usage if missing
153
147
  * @param {string} arg - The argument to validate
@@ -740,13 +734,15 @@ function runProjectCommand(rootDir, command, arg) {
740
734
  break;
741
735
  }
742
736
 
743
- case 'example':
744
- if (!arg) {
745
- console.error('Usage: ucn . example <name>');
746
- process.exit(1);
747
- }
748
- console.log(output.formatExample(index.example(arg), arg));
737
+ case 'example': {
738
+ requireArg(arg, 'Usage: ucn . example <name>');
739
+ const exampleResult = index.example(arg);
740
+ printOutput(exampleResult,
741
+ r => output.formatExampleJson(r, arg),
742
+ r => output.formatExample(r, arg)
743
+ );
749
744
  break;
745
+ }
750
746
 
751
747
  case 'context': {
752
748
  requireArg(arg, 'Usage: ucn . context <name>');
@@ -868,7 +864,7 @@ function runProjectCommand(rootDir, command, arg) {
868
864
  defaultValue: flags.defaultValue,
869
865
  file: flags.file
870
866
  });
871
- printOutput(planResult, r => JSON.stringify(r, null, 2), output.formatPlan);
867
+ printOutput(planResult, output.formatPlanJson, output.formatPlan);
872
868
  break;
873
869
  }
874
870
 
@@ -885,14 +881,14 @@ function runProjectCommand(rootDir, command, arg) {
885
881
  case 'stack': {
886
882
  requireArg(arg, 'Usage: ucn . stacktrace "<stack trace text>"\nExample: ucn . stacktrace "Error: failed\\n at parseFile (core/parser.js:90:5)"');
887
883
  const stackResult = index.parseStackTrace(arg);
888
- printOutput(stackResult, r => JSON.stringify(r, null, 2), output.formatStackTrace);
884
+ printOutput(stackResult, output.formatStackTraceJson, output.formatStackTrace);
889
885
  break;
890
886
  }
891
887
 
892
888
  case 'verify': {
893
889
  requireArg(arg, 'Usage: ucn . verify <name>');
894
890
  const verifyResult = index.verify(arg, { file: flags.file });
895
- printOutput(verifyResult, r => JSON.stringify(r, null, 2), output.formatVerify);
891
+ printOutput(verifyResult, output.formatVerifyJson, output.formatVerify);
896
892
  break;
897
893
  }
898
894
 
@@ -986,7 +982,7 @@ function runProjectCommand(rootDir, command, arg) {
986
982
  in: flags.in || subdirScope
987
983
  });
988
984
  printOutput(deadcodeResults,
989
- r => JSON.stringify({ deadcode: r }, null, 2),
985
+ output.formatDeadcodeJson,
990
986
  r => output.formatDeadcode(r)
991
987
  );
992
988
  break;
@@ -1056,15 +1052,16 @@ function runProjectCommand(rootDir, command, arg) {
1056
1052
  }
1057
1053
  }
1058
1054
 
1059
- function extractFunctionFromProject(index, name) {
1060
- const matches = index.find(name, { file: flags.file }).filter(m => m.type === 'function' || m.params !== undefined);
1055
+ function extractFunctionFromProject(index, name, overrideFlags) {
1056
+ const f = overrideFlags || flags;
1057
+ const matches = index.find(name, { file: f.file }).filter(m => m.type === 'function' || m.params !== undefined);
1061
1058
 
1062
1059
  if (matches.length === 0) {
1063
1060
  console.error(`Function "${name}" not found`);
1064
1061
  return;
1065
1062
  }
1066
1063
 
1067
- if (matches.length > 1 && !flags.file && flags.all) {
1064
+ if (matches.length > 1 && !f.file && f.all) {
1068
1065
  // Show all definitions
1069
1066
  for (let i = 0; i < matches.length; i++) {
1070
1067
  const m = matches[i];
@@ -1073,7 +1070,7 @@ function extractFunctionFromProject(index, name) {
1073
1070
  const extracted = lines.slice(m.startLine - 1, m.endLine);
1074
1071
  const fnCode = cleanHtmlScriptTags(extracted, detectLanguage(m.file)).join('\n');
1075
1072
  if (i > 0) console.log('');
1076
- if (flags.json) {
1073
+ if (f.json) {
1077
1074
  console.log(output.formatFunctionJson(m, fnCode));
1078
1075
  } else {
1079
1076
  console.log(output.formatFn(m, fnCode));
@@ -1083,7 +1080,7 @@ function extractFunctionFromProject(index, name) {
1083
1080
  }
1084
1081
 
1085
1082
  let match;
1086
- if (matches.length > 1 && !flags.file) {
1083
+ if (matches.length > 1 && !f.file) {
1087
1084
  // Auto-select best match using same scoring as resolveSymbol
1088
1085
  match = pickBestDefinition(matches);
1089
1086
  const others = matches.filter(m => m !== match).map(m => `${m.relativePath}:${m.startLine}`).join(', ');
@@ -1098,15 +1095,16 @@ function extractFunctionFromProject(index, name) {
1098
1095
  const extracted = lines.slice(match.startLine - 1, match.endLine);
1099
1096
  const fnCode = cleanHtmlScriptTags(extracted, detectLanguage(match.file)).join('\n');
1100
1097
 
1101
- if (flags.json) {
1098
+ if (f.json) {
1102
1099
  console.log(output.formatFunctionJson(match, fnCode));
1103
1100
  } else {
1104
1101
  console.log(output.formatFn(match, fnCode));
1105
1102
  }
1106
1103
  }
1107
1104
 
1108
- function extractClassFromProject(index, name) {
1109
- const matches = index.find(name, { file: flags.file }).filter(m =>
1105
+ function extractClassFromProject(index, name, overrideFlags) {
1106
+ const f = overrideFlags || flags;
1107
+ const matches = index.find(name, { file: f.file }).filter(m =>
1110
1108
  ['class', 'interface', 'type', 'enum', 'struct', 'trait'].includes(m.type)
1111
1109
  );
1112
1110
 
@@ -1115,7 +1113,7 @@ function extractClassFromProject(index, name) {
1115
1113
  return;
1116
1114
  }
1117
1115
 
1118
- if (matches.length > 1 && !flags.file && flags.all) {
1116
+ if (matches.length > 1 && !f.file && f.all) {
1119
1117
  // Show all definitions using index data (no re-parsing)
1120
1118
  for (let i = 0; i < matches.length; i++) {
1121
1119
  const m = matches[i];
@@ -1124,7 +1122,7 @@ function extractClassFromProject(index, name) {
1124
1122
  const extracted = codeLines.slice(m.startLine - 1, m.endLine);
1125
1123
  const clsCode = cleanHtmlScriptTags(extracted, detectLanguage(m.file)).join('\n');
1126
1124
  if (i > 0) console.log('');
1127
- if (flags.json) {
1125
+ if (f.json) {
1128
1126
  console.log(JSON.stringify({ ...m, code: clsCode }, null, 2));
1129
1127
  } else {
1130
1128
  console.log(output.formatClass(m, clsCode));
@@ -1134,7 +1132,7 @@ function extractClassFromProject(index, name) {
1134
1132
  }
1135
1133
 
1136
1134
  let match;
1137
- if (matches.length > 1 && !flags.file) {
1135
+ if (matches.length > 1 && !f.file) {
1138
1136
  // Auto-select best match using same scoring as resolveSymbol
1139
1137
  match = pickBestDefinition(matches);
1140
1138
  const others = matches.filter(m => m !== match).map(m => `${m.relativePath}:${m.startLine}`).join(', ');
@@ -1149,7 +1147,7 @@ function extractClassFromProject(index, name) {
1149
1147
  const extracted = codeLines.slice(match.startLine - 1, match.endLine);
1150
1148
  const clsCode = cleanHtmlScriptTags(extracted, detectLanguage(match.file)).join('\n');
1151
1149
 
1152
- if (flags.json) {
1150
+ if (f.json) {
1153
1151
  console.log(JSON.stringify({ ...match, code: clsCode }, null, 2));
1154
1152
  } else {
1155
1153
  console.log(output.formatClass(match, clsCode));
@@ -1628,30 +1626,6 @@ function printLines(lines, range) {
1628
1626
  }
1629
1627
  }
1630
1628
 
1631
- /**
1632
- * Pick the best definition from multiple matches using same scoring as resolveSymbol.
1633
- * Prefers lib/src/core over test/examples/vendor directories.
1634
- */
1635
- function pickBestDefinition(matches) {
1636
- const typeOrder = new Set(['class', 'struct', 'interface', 'type', 'impl']);
1637
- const scored = matches.map(m => {
1638
- let score = 0;
1639
- const rp = m.relativePath || '';
1640
- // Prefer class/struct/interface types (+1000) - same as resolveSymbol
1641
- if (typeOrder.has(m.type)) score += 1000;
1642
- if (isTestFile(rp, detectLanguage(m.file))) score -= 500;
1643
- if (/^(examples?|docs?|vendor|third[_-]?party|benchmarks?|samples?)\//i.test(rp)) score -= 300;
1644
- if (/^(lib|src|core|internal|pkg|crates)\//i.test(rp)) score += 200;
1645
- // Tiebreaker: prefer larger function bodies (more important/complex)
1646
- if (m.startLine && m.endLine) {
1647
- score += Math.min(m.endLine - m.startLine, 100);
1648
- }
1649
- return { match: m, score };
1650
- });
1651
- scored.sort((a, b) => b.score - a.score);
1652
- return scored[0].match;
1653
- }
1654
-
1655
1629
  function suggestSimilar(query, names) {
1656
1630
  const lower = query.toLowerCase();
1657
1631
  const similar = names.filter(n => n.toLowerCase().includes(lower));
@@ -1814,31 +1788,38 @@ function runInteractive(rootDir) {
1814
1788
  if (input === 'help') {
1815
1789
  console.log(`
1816
1790
  Commands:
1817
- toc Project overview
1818
- find <name> Find symbol
1791
+ toc Project overview (--detailed)
1792
+ find <name> Find symbol (--exact, --file=)
1819
1793
  about <name> Everything about a symbol
1820
1794
  usages <name> All usages grouped by type
1821
1795
  context <name> Callers + callees
1822
1796
  expand <N> Show code for item N from context
1823
1797
  smart <name> Function + dependencies
1824
1798
  impact <name> What breaks if changed
1825
- trace <name> Call tree
1799
+ trace <name> Call tree (--depth=N)
1826
1800
  example <name> Best usage example
1827
1801
  related <name> Sibling functions
1802
+ fn <name> Extract function code (--file=)
1803
+ class <name> Extract class code (--file=)
1804
+ lines <range> Extract lines (--file= required)
1805
+ graph <file> File dependency tree (--direction=, --depth=)
1806
+ file-exports <file> File's exported symbols
1828
1807
  imports <file> What file imports
1829
1808
  exporters <file> Who imports file
1830
- tests <name> Find tests
1831
- search <term> Text search
1809
+ tests <name> Find tests (--calls-only)
1810
+ search <term> Text search (--code-only, --case-sensitive)
1832
1811
  typedef <name> Find type definitions
1833
1812
  deadcode Find unused functions/classes
1834
1813
  verify <name> Check call sites match signature
1835
- plan <name> Preview refactoring
1814
+ plan <name> Preview refactoring (--add-param=, --remove-param=, --rename-to=)
1836
1815
  stacktrace <text> Parse a stack trace
1837
1816
  api Show public symbols
1838
1817
  diff-impact What changed and who's affected
1839
1818
  stats Index statistics
1840
1819
  rebuild Rebuild index
1841
1820
  quit Exit
1821
+
1822
+ Flags can be added per-command: context myFunc --include-methods
1842
1823
  `);
1843
1824
  rl.prompt();
1844
1825
  return;
@@ -1852,13 +1833,16 @@ Commands:
1852
1833
  return;
1853
1834
  }
1854
1835
 
1855
- // Parse command
1856
- const parts = input.split(/\s+/);
1857
- const command = parts[0];
1858
- const arg = parts.slice(1).join(' ');
1836
+ // Parse command, flags, and arg from interactive input
1837
+ const tokens = input.split(/\s+/);
1838
+ const command = tokens[0];
1839
+ const flagTokens = tokens.filter(t => t.startsWith('--'));
1840
+ const argTokens = tokens.slice(1).filter(t => !t.startsWith('--'));
1841
+ const arg = argTokens.join(' ');
1842
+ const iflags = parseInteractiveFlags(flagTokens);
1859
1843
 
1860
1844
  try {
1861
- executeInteractiveCommand(index, command, arg);
1845
+ executeInteractiveCommand(index, command, arg, iflags);
1862
1846
  } catch (e) {
1863
1847
  console.error(`Error: ${e.message}`);
1864
1848
  }
@@ -1871,12 +1855,48 @@ Commands:
1871
1855
  });
1872
1856
  }
1873
1857
 
1874
- function executeInteractiveCommand(index, command, arg) {
1858
+ /**
1859
+ * Parse flags from interactive command tokens.
1860
+ * Returns a flags object similar to the global flags but scoped to this command.
1861
+ */
1862
+ function parseInteractiveFlags(tokens) {
1863
+ return {
1864
+ file: tokens.find(a => a.startsWith('--file='))?.split('=')[1] || null,
1865
+ exclude: tokens.find(a => a.startsWith('--exclude=') || a.startsWith('--not='))?.split('=')[1]?.split(',') || [],
1866
+ in: tokens.find(a => a.startsWith('--in='))?.split('=')[1] || null,
1867
+ includeTests: tokens.includes('--include-tests'),
1868
+ includeExported: tokens.includes('--include-exported'),
1869
+ includeDecorated: tokens.includes('--include-decorated'),
1870
+ includeUncertain: tokens.includes('--include-uncertain'),
1871
+ includeMethods: tokens.includes('--include-methods=false') ? false : tokens.includes('--include-methods') ? true : undefined,
1872
+ detailed: tokens.includes('--detailed'),
1873
+ all: tokens.includes('--all'),
1874
+ exact: tokens.includes('--exact'),
1875
+ callsOnly: tokens.includes('--calls-only'),
1876
+ codeOnly: tokens.includes('--code-only'),
1877
+ caseSensitive: tokens.includes('--case-sensitive'),
1878
+ withTypes: tokens.includes('--with-types'),
1879
+ expand: tokens.includes('--expand'),
1880
+ depth: tokens.find(a => a.startsWith('--depth='))?.split('=')[1] || null,
1881
+ top: parseInt(tokens.find(a => a.startsWith('--top='))?.split('=')[1] || '0'),
1882
+ context: parseInt(tokens.find(a => a.startsWith('--context='))?.split('=')[1] || '0'),
1883
+ direction: tokens.find(a => a.startsWith('--direction='))?.split('=')[1] || null,
1884
+ addParam: tokens.find(a => a.startsWith('--add-param='))?.split('=')[1] || null,
1885
+ removeParam: tokens.find(a => a.startsWith('--remove-param='))?.split('=')[1] || null,
1886
+ renameTo: tokens.find(a => a.startsWith('--rename-to='))?.split('=')[1] || null,
1887
+ defaultValue: tokens.find(a => a.startsWith('--default='))?.split('=')[1] || null,
1888
+ base: tokens.find(a => a.startsWith('--base='))?.split('=')[1] || null,
1889
+ staged: tokens.includes('--staged'),
1890
+ maxLines: parseInt(tokens.find(a => a.startsWith('--max-lines='))?.split('=')[1] || '0') || null,
1891
+ };
1892
+ }
1893
+
1894
+ function executeInteractiveCommand(index, command, arg, iflags = {}) {
1875
1895
  switch (command) {
1876
1896
  case 'toc': {
1877
- const toc = index.getToc();
1897
+ const toc = index.getToc({ detailed: iflags.detailed });
1878
1898
  console.log(output.formatToc(toc, {
1879
- detailedHint: 'Add --detailed to list all functions, or "ucn . about <name>" for full details on a symbol'
1899
+ detailedHint: 'Add --detailed to list all functions, or "about <name>" for full details on a symbol'
1880
1900
  }));
1881
1901
  break;
1882
1902
  }
@@ -1886,11 +1906,11 @@ function executeInteractiveCommand(index, command, arg) {
1886
1906
  console.log('Usage: find <name>');
1887
1907
  return;
1888
1908
  }
1889
- const found = index.find(arg, {});
1909
+ const found = index.find(arg, { exact: iflags.exact, exclude: iflags.exclude, in: iflags.in, includeTests: iflags.includeTests });
1890
1910
  if (found.length === 0) {
1891
1911
  console.log(`No symbols found for "${arg}"`);
1892
1912
  } else {
1893
- printSymbols(found, arg, {});
1913
+ printSymbols(found, arg, { top: iflags.top });
1894
1914
  }
1895
1915
  break;
1896
1916
  }
@@ -1900,8 +1920,8 @@ function executeInteractiveCommand(index, command, arg) {
1900
1920
  console.log('Usage: about <name>');
1901
1921
  return;
1902
1922
  }
1903
- const aboutResult = index.about(arg, { includeMethods: flags.includeMethods });
1904
- console.log(output.formatAbout(aboutResult, { expand: flags.expand, root: index.root }));
1923
+ const aboutResult = index.about(arg, { includeMethods: iflags.includeMethods, includeUncertain: iflags.includeUncertain, file: iflags.file, exclude: iflags.exclude });
1924
+ console.log(output.formatAbout(aboutResult, { expand: iflags.expand, root: index.root, showAll: iflags.all }));
1905
1925
  break;
1906
1926
  }
1907
1927
 
@@ -1910,7 +1930,7 @@ function executeInteractiveCommand(index, command, arg) {
1910
1930
  console.log('Usage: usages <name>');
1911
1931
  return;
1912
1932
  }
1913
- const usages = index.usages(arg, {});
1933
+ const usages = index.usages(arg, { context: iflags.context, codeOnly: iflags.codeOnly, includeTests: iflags.includeTests });
1914
1934
  console.log(output.formatUsages(usages, arg));
1915
1935
  break;
1916
1936
  }
@@ -1920,13 +1940,13 @@ function executeInteractiveCommand(index, command, arg) {
1920
1940
  console.log('Usage: context <name>');
1921
1941
  return;
1922
1942
  }
1923
- const ctx = index.context(arg, { includeUncertain: flags.includeUncertain, includeMethods: flags.includeMethods });
1943
+ const ctx = index.context(arg, { includeUncertain: iflags.includeUncertain, includeMethods: iflags.includeMethods, file: iflags.file, exclude: iflags.exclude });
1924
1944
  if (!ctx) {
1925
1945
  console.log(`Symbol "${arg}" not found.`);
1926
1946
  } else {
1927
1947
  const { text, expandable } = output.formatContext(ctx, {
1928
1948
  methodsHint: 'Note: obj.method() calls excluded — use --include-methods to include them',
1929
- expandHint: 'Use "ucn . expand <N>" to see code for item N',
1949
+ expandHint: 'Use "expand <N>" to see code for item N',
1930
1950
  uncertainHint: 'use --include-uncertain to include all'
1931
1951
  });
1932
1952
  console.log(text);
@@ -1940,7 +1960,7 @@ function executeInteractiveCommand(index, command, arg) {
1940
1960
  console.log('Usage: smart <name>');
1941
1961
  return;
1942
1962
  }
1943
- const smart = index.smart(arg, { file: flags.file, includeUncertain: flags.includeUncertain });
1963
+ const smart = index.smart(arg, { file: iflags.file, includeUncertain: iflags.includeUncertain, withTypes: iflags.withTypes });
1944
1964
  if (smart) {
1945
1965
  console.log(output.formatSmart(smart, {
1946
1966
  uncertainHint: 'use --include-uncertain to include all'
@@ -1956,7 +1976,7 @@ function executeInteractiveCommand(index, command, arg) {
1956
1976
  console.log('Usage: impact <name>');
1957
1977
  return;
1958
1978
  }
1959
- const impactResult = index.impact(arg);
1979
+ const impactResult = index.impact(arg, { file: iflags.file });
1960
1980
  console.log(output.formatImpact(impactResult));
1961
1981
  break;
1962
1982
  }
@@ -1966,12 +1986,70 @@ function executeInteractiveCommand(index, command, arg) {
1966
1986
  console.log('Usage: trace <name>');
1967
1987
  return;
1968
1988
  }
1969
- const traceResult = index.trace(arg, { depth: 3 });
1989
+ const traceDepth = iflags.depth ? parseInt(iflags.depth) : 3;
1990
+ const traceResult = index.trace(arg, { depth: traceDepth, file: iflags.file, all: iflags.all || !!iflags.depth, includeMethods: iflags.includeMethods, includeUncertain: iflags.includeUncertain });
1970
1991
  console.log(output.formatTrace(traceResult));
1971
1992
  break;
1972
1993
  }
1973
1994
 
1974
- case 'imports': {
1995
+ case 'fn': {
1996
+ if (!arg) {
1997
+ console.log('Usage: fn <name> [--file=<pattern>]');
1998
+ return;
1999
+ }
2000
+ extractFunctionFromProject(index, arg, iflags);
2001
+ break;
2002
+ }
2003
+
2004
+ case 'class': {
2005
+ if (!arg) {
2006
+ console.log('Usage: class <name> [--file=<pattern>]');
2007
+ return;
2008
+ }
2009
+ extractClassFromProject(index, arg, iflags);
2010
+ break;
2011
+ }
2012
+
2013
+ case 'lines': {
2014
+ if (!arg || !iflags.file) {
2015
+ console.log('Usage: lines <range> --file=<file>');
2016
+ return;
2017
+ }
2018
+ const filePath = index.findFile(iflags.file);
2019
+ if (!filePath) {
2020
+ console.log(`File not found: ${iflags.file}`);
2021
+ return;
2022
+ }
2023
+ const fileContent = fs.readFileSync(filePath, 'utf-8');
2024
+ printLines(fileContent.split('\n'), arg);
2025
+ break;
2026
+ }
2027
+
2028
+ case 'graph': {
2029
+ if (!arg) {
2030
+ console.log('Usage: graph <file> [--direction=imports|importers|both] [--depth=N]');
2031
+ return;
2032
+ }
2033
+ const graphDepth = iflags.depth ? parseInt(iflags.depth) : 2;
2034
+ const graphDirection = iflags.direction || 'both';
2035
+ const graphResult = index.graph(arg, { direction: graphDirection, maxDepth: graphDepth });
2036
+ console.log(output.formatGraph(graphResult, { showAll: iflags.all, maxDepth: graphDepth }));
2037
+ break;
2038
+ }
2039
+
2040
+ case 'file-exports':
2041
+ case 'what-exports': {
2042
+ if (!arg) {
2043
+ console.log('Usage: file-exports <file>');
2044
+ return;
2045
+ }
2046
+ const fileExports = index.fileExports(arg);
2047
+ console.log(output.formatFileExports(fileExports, arg));
2048
+ break;
2049
+ }
2050
+
2051
+ case 'imports':
2052
+ case 'what-imports': {
1975
2053
  if (!arg) {
1976
2054
  console.log('Usage: imports <file>');
1977
2055
  return;
@@ -1981,7 +2059,8 @@ function executeInteractiveCommand(index, command, arg) {
1981
2059
  break;
1982
2060
  }
1983
2061
 
1984
- case 'exporters': {
2062
+ case 'exporters':
2063
+ case 'who-imports': {
1985
2064
  if (!arg) {
1986
2065
  console.log('Usage: exporters <file>');
1987
2066
  return;
@@ -1996,7 +2075,7 @@ function executeInteractiveCommand(index, command, arg) {
1996
2075
  console.log('Usage: tests <name>');
1997
2076
  return;
1998
2077
  }
1999
- const tests = index.tests(arg, { callsOnly: flags.callsOnly });
2078
+ const tests = index.tests(arg, { callsOnly: iflags.callsOnly });
2000
2079
  console.log(output.formatTests(tests, arg));
2001
2080
  break;
2002
2081
  }
@@ -2006,7 +2085,7 @@ function executeInteractiveCommand(index, command, arg) {
2006
2085
  console.log('Usage: search <term>');
2007
2086
  return;
2008
2087
  }
2009
- const results = index.search(arg, {});
2088
+ const results = index.search(arg, { codeOnly: iflags.codeOnly, caseSensitive: iflags.caseSensitive, context: iflags.context });
2010
2089
  console.log(output.formatSearch(results, arg));
2011
2090
  break;
2012
2091
  }
@@ -2029,9 +2108,9 @@ function executeInteractiveCommand(index, command, arg) {
2029
2108
 
2030
2109
  case 'diff-impact': {
2031
2110
  const diffResult = index.diffImpact({
2032
- base: flags.base || 'HEAD',
2033
- staged: flags.staged,
2034
- file: flags.file
2111
+ base: iflags.base || 'HEAD',
2112
+ staged: iflags.staged,
2113
+ file: iflags.file
2035
2114
  });
2036
2115
  console.log(output.formatDiffImpact(diffResult));
2037
2116
  break;
@@ -2069,9 +2148,11 @@ function executeInteractiveCommand(index, command, arg) {
2069
2148
 
2070
2149
  case 'deadcode': {
2071
2150
  const deadResult = index.deadcode({
2072
- includeExported: flags.includeExported,
2073
- includeDecorated: flags.includeDecorated,
2074
- includeTests: flags.includeTests
2151
+ includeExported: iflags.includeExported,
2152
+ includeDecorated: iflags.includeDecorated,
2153
+ includeTests: iflags.includeTests,
2154
+ exclude: iflags.exclude,
2155
+ in: iflags.in
2075
2156
  });
2076
2157
  console.log(output.formatDeadcode(deadResult));
2077
2158
  break;
@@ -2082,8 +2163,8 @@ function executeInteractiveCommand(index, command, arg) {
2082
2163
  console.log('Usage: related <name>');
2083
2164
  return;
2084
2165
  }
2085
- const relResult = index.related(arg, { file: flags.file });
2086
- console.log(output.formatRelated(relResult));
2166
+ const relResult = index.related(arg, { file: iflags.file, all: iflags.all });
2167
+ console.log(output.formatRelated(relResult, { showAll: iflags.all }));
2087
2168
  break;
2088
2169
  }
2089
2170
 
@@ -2098,19 +2179,19 @@ function executeInteractiveCommand(index, command, arg) {
2098
2179
 
2099
2180
  case 'plan': {
2100
2181
  if (!arg) {
2101
- console.log('Usage: plan <name> [--add-param=x] [--remove-param=x] [--rename-to=x]');
2182
+ console.log('Usage: plan <name> --add-param=x | --remove-param=x | --rename-to=x');
2102
2183
  return;
2103
2184
  }
2104
- if (!flags.addParam && !flags.removeParam && !flags.renameTo) {
2185
+ if (!iflags.addParam && !iflags.removeParam && !iflags.renameTo) {
2105
2186
  console.log('Plan requires an operation: --add-param, --remove-param, or --rename-to');
2106
2187
  return;
2107
2188
  }
2108
2189
  const planResult = index.plan(arg, {
2109
- addParam: flags.addParam,
2110
- removeParam: flags.removeParam,
2111
- renameTo: flags.renameTo,
2112
- defaultValue: flags.defaultValue,
2113
- file: flags.file
2190
+ addParam: iflags.addParam,
2191
+ removeParam: iflags.removeParam,
2192
+ renameTo: iflags.renameTo,
2193
+ defaultValue: iflags.defaultValue,
2194
+ file: iflags.file
2114
2195
  });
2115
2196
  console.log(output.formatPlan(planResult));
2116
2197
  break;
@@ -2121,7 +2202,7 @@ function executeInteractiveCommand(index, command, arg) {
2121
2202
  console.log('Usage: verify <name>');
2122
2203
  return;
2123
2204
  }
2124
- const verifyResult = index.verify(arg, { file: flags.file });
2205
+ const verifyResult = index.verify(arg, { file: iflags.file });
2125
2206
  console.log(output.formatVerify(verifyResult));
2126
2207
  break;
2127
2208
  }
package/core/discovery.js CHANGED
@@ -335,11 +335,17 @@ function walkDir(dir, options, depth = 0, visited = new Set()) {
335
335
  * @param {string[]} ignores - Patterns to always ignore
336
336
  * @param {string} [parentDir] - Parent directory path (for conditional checks)
337
337
  */
338
+ const _globRegexCache = new Map();
339
+
338
340
  function shouldIgnore(name, ignores, parentDir) {
339
341
  // Check unconditional ignores
340
342
  for (const pattern of ignores) {
341
343
  if (pattern.includes('*')) {
342
- const regex = globToRegex(pattern);
344
+ let regex = _globRegexCache.get(pattern);
345
+ if (!regex) {
346
+ regex = globToRegex(pattern);
347
+ _globRegexCache.set(pattern, regex);
348
+ }
343
349
  if (regex.test(name)) return true;
344
350
  } else if (name === pattern) {
345
351
  return true;
@@ -408,7 +414,7 @@ function detectProjectPattern(projectRoot) {
408
414
 
409
415
  if (fs.existsSync(path.join(dir, 'pom.xml')) ||
410
416
  fs.existsSync(path.join(dir, 'build.gradle'))) {
411
- extensions.push('java', 'kt');
417
+ extensions.push('java');
412
418
  }
413
419
  };
414
420