ucn 3.7.16 → 3.7.18

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.
@@ -99,6 +99,12 @@ ucn deadcode --exclude=test # Skip test files (most useful)
99
99
  | Preview a rename or param change | `ucn plan <name> --rename-to=new_name` | Shows what would change without doing it |
100
100
  | File-level dependency tree | `ucn graph <file> --depth=1` | Visual import tree. Setting `--depth=N` expands all children. Can be noisy — use depth=1 for large projects. For function-level flow, use `trace` instead |
101
101
  | Find which tests cover a function | `ucn tests <name>` | Test files and test function names |
102
+ | Extract specific lines from a file | `ucn lines --file=<file> --range=10-20` | Pull a line range without reading the whole file |
103
+ | Find type definitions | `ucn typedef <name>` | Interfaces, enums, structs, traits, type aliases |
104
+ | See a project's public API | `ucn api` or `ucn api --file=<file>` | All exported/public symbols with signatures |
105
+ | Drill into context results | `ucn expand <N>` | Show source code for item N from a previous `context` call |
106
+ | Best usage example of a function | `ucn example <name>` | Finds and scores the best call site with surrounding context |
107
+ | Debug a stack trace | `ucn stacktrace --stack="<trace>"` | Parses stack frames and shows source context per frame |
102
108
 
103
109
  ## Command Format
104
110
 
@@ -126,9 +132,20 @@ ucn [target] <command> [name] [--flags]
126
132
  | `--base=<ref>` | Git ref for diff-impact (default: HEAD) |
127
133
  | `--staged` | Analyze staged changes (diff-impact) |
128
134
  | `--no-cache` | Force re-index after editing files |
135
+ | `--clear-cache` | Delete cached index entirely before running |
129
136
  | `--context=N` | Lines of surrounding context in `usages`/`search` output |
130
137
  | `--no-regex` | Force plain text search (regex is default) |
131
138
  | `--functions` | Show per-function line counts in `stats` (complexity audit) |
139
+ | `--json` | Machine-readable JSON output (wrapped in `{meta, data}`) |
140
+ | `--code-only` | Exclude matches in comments and strings (`search`/`usages`) |
141
+ | `--with-types` | Include related type definitions in `smart`/`about` output |
142
+ | `--detailed` | Show full symbol listing per file in `toc` |
143
+ | `--top=N` | Limit result count (default: 10 for most commands) |
144
+ | `--case-sensitive` | Case-sensitive text search (default: case-insensitive) |
145
+ | `--exact` | Exact name match only in `find`/`typedef` (no substring) |
146
+ | `--include-uncertain` | Include ambiguous/uncertain matches in `context`/`smart`/`about` |
147
+ | `--include-exported` | Include exported symbols in `deadcode` results |
148
+ | `--include-decorated` | Include decorated/annotated symbols in `deadcode` results |
132
149
 
133
150
  ## Workflow Integration
134
151
 
package/cli/index.js CHANGED
@@ -86,7 +86,9 @@ const flags = {
86
86
  // Regex search mode (default: ON; --no-regex to force plain text)
87
87
  regex: args.includes('--no-regex') ? false : undefined,
88
88
  // Stats: per-function line counts
89
- functions: args.includes('--functions')
89
+ functions: args.includes('--functions'),
90
+ // Class: max lines to show (0 = no limit)
91
+ maxLines: parseInt(args.find(a => a.startsWith('--max-lines='))?.split('=')[1] || '0') || null
90
92
  };
91
93
 
92
94
  // Handle --file flag with space
@@ -106,7 +108,8 @@ const knownFlags = new Set([
106
108
  '--depth', '--direction', '--add-param', '--remove-param', '--rename-to',
107
109
  '--default', '--top', '--no-follow-symlinks',
108
110
  '--base', '--staged',
109
- '--regex', '--no-regex', '--functions'
111
+ '--regex', '--no-regex', '--functions',
112
+ '--max-lines'
110
113
  ]);
111
114
 
112
115
  // Handle help flag
@@ -841,7 +844,7 @@ function runProjectCommand(rootDir, command, arg) {
841
844
 
842
845
  case 'about': {
843
846
  requireArg(arg, 'Usage: ucn . about <name>');
844
- const aboutResult = index.about(arg, { withTypes: flags.withTypes, file: flags.file, all: flags.all, includeMethods: flags.includeMethods, includeUncertain: flags.includeUncertain, exclude: flags.exclude });
847
+ const aboutResult = index.about(arg, { withTypes: flags.withTypes, file: flags.file, all: flags.all, includeMethods: flags.includeMethods, includeUncertain: flags.includeUncertain, exclude: flags.exclude, maxCallers: flags.top || undefined, maxCallees: flags.top || undefined });
845
848
  printOutput(aboutResult,
846
849
  output.formatAboutJson,
847
850
  r => output.formatAbout(r, { expand: flags.expand, root: index.root, depth: flags.depth })
@@ -899,8 +902,8 @@ function runProjectCommand(rootDir, command, arg) {
899
902
 
900
903
  case 'related': {
901
904
  requireArg(arg, 'Usage: ucn . related <name>');
902
- const relatedResult = index.related(arg, { file: flags.file, all: flags.all });
903
- printOutput(relatedResult, output.formatRelatedJson, r => output.formatRelated(r, { showAll: flags.all }));
905
+ const relatedResult = index.related(arg, { file: flags.file, top: flags.top, all: flags.all });
906
+ printOutput(relatedResult, output.formatRelatedJson, r => output.formatRelated(r, { showAll: flags.all, top: flags.top }));
904
907
  break;
905
908
  }
906
909
 
@@ -997,7 +1000,11 @@ function runProjectCommand(rootDir, command, arg) {
997
1000
  });
998
1001
  printOutput(deadcodeResults,
999
1002
  output.formatDeadcodeJson,
1000
- r => output.formatDeadcode(r, { top: flags.top })
1003
+ r => output.formatDeadcode(r, {
1004
+ top: flags.top,
1005
+ decoratedHint: !flags.includeDecorated && deadcodeResults.excludedDecorated > 0 ? `${deadcodeResults.excludedDecorated} decorated/annotated symbol(s) hidden (framework-registered). Use --include-decorated to include them.` : undefined,
1006
+ exportedHint: !flags.includeExported && deadcodeResults.excludedExported > 0 ? `${deadcodeResults.excludedExported} exported symbol(s) excluded (all have callers). Use --include-exported to audit them.` : undefined
1007
+ })
1001
1008
  );
1002
1009
  break;
1003
1010
  }
@@ -1162,6 +1169,45 @@ function extractClassFromProject(index, name, overrideFlags) {
1162
1169
  // Use index data directly instead of re-parsing the file
1163
1170
  const code = fs.readFileSync(match.file, 'utf-8');
1164
1171
  const codeLines = code.split('\n');
1172
+ const classLineCount = match.endLine - match.startLine + 1;
1173
+
1174
+ // Large class summary (>200 lines) when no --max-lines specified
1175
+ if (classLineCount > 200 && !f.maxLines) {
1176
+ if (f.json) {
1177
+ const extracted = codeLines.slice(match.startLine - 1, match.endLine);
1178
+ const clsCode = cleanHtmlScriptTags(extracted, detectLanguage(match.file)).join('\n');
1179
+ console.log(JSON.stringify({ ...match, code: clsCode }, null, 2));
1180
+ } else {
1181
+ const lines = [];
1182
+ lines.push(`${match.relativePath}:${match.startLine}`);
1183
+ lines.push(`${output.lineRange(match.startLine, match.endLine)} ${output.formatClassSignature(match)}`);
1184
+ lines.push('\u2500'.repeat(60));
1185
+ const methods = index.findMethodsForType(match.name);
1186
+ if (methods.length > 0) {
1187
+ lines.push(`\nMethods (${methods.length}):`);
1188
+ for (const m of methods) {
1189
+ lines.push(` ${output.formatFunctionSignature(m)} [line ${m.startLine}]`);
1190
+ }
1191
+ }
1192
+ lines.push(`\nClass is ${classLineCount} lines. Use --max-lines=N to see source, or "fn <method>" for individual methods.`);
1193
+ console.log(lines.join('\n'));
1194
+ }
1195
+ return;
1196
+ }
1197
+
1198
+ // Truncated source with --max-lines
1199
+ if (f.maxLines && classLineCount > f.maxLines) {
1200
+ const truncated = codeLines.slice(match.startLine - 1, match.startLine - 1 + f.maxLines);
1201
+ const truncatedCode = cleanHtmlScriptTags(truncated, detectLanguage(match.file)).join('\n');
1202
+ if (f.json) {
1203
+ console.log(JSON.stringify({ ...match, code: truncatedCode, truncated: true, totalLines: classLineCount }, null, 2));
1204
+ } else {
1205
+ console.log(output.formatClass(match, truncatedCode));
1206
+ console.log(`\n... showing ${f.maxLines} of ${classLineCount} lines`);
1207
+ }
1208
+ return;
1209
+ }
1210
+
1165
1211
  const extracted = codeLines.slice(match.startLine - 1, match.endLine);
1166
1212
  const clsCode = cleanHtmlScriptTags(extracted, detectLanguage(match.file)).join('\n');
1167
1213
 
@@ -1902,6 +1948,7 @@ function parseInteractiveFlags(tokens) {
1902
1948
  includeUncertain: tokens.includes('--include-uncertain'),
1903
1949
  includeMethods: tokens.includes('--include-methods=false') ? false : tokens.includes('--include-methods') ? true : undefined,
1904
1950
  detailed: tokens.includes('--detailed'),
1951
+ topLevel: tokens.includes('--top-level'),
1905
1952
  all: tokens.includes('--all'),
1906
1953
  exact: tokens.includes('--exact'),
1907
1954
  callsOnly: tokens.includes('--calls-only'),
@@ -1928,9 +1975,10 @@ function parseInteractiveFlags(tokens) {
1928
1975
  function executeInteractiveCommand(index, command, arg, iflags = {}) {
1929
1976
  switch (command) {
1930
1977
  case 'toc': {
1931
- const toc = index.getToc({ detailed: iflags.detailed });
1978
+ const toc = index.getToc({ detailed: iflags.detailed, topLevel: iflags.topLevel, all: iflags.all, top: iflags.top });
1932
1979
  console.log(output.formatToc(toc, {
1933
- detailedHint: 'Add --detailed to list all functions, or "about <name>" for full details on a symbol'
1980
+ detailedHint: 'Add --detailed to list all functions, or "about <name>" for full details on a symbol',
1981
+ uncertainHint: 'use --include-uncertain to include all'
1934
1982
  }));
1935
1983
  break;
1936
1984
  }
@@ -1940,7 +1988,8 @@ function executeInteractiveCommand(index, command, arg, iflags = {}) {
1940
1988
  console.log('Usage: find <name>');
1941
1989
  return;
1942
1990
  }
1943
- const found = index.find(arg, { exact: iflags.exact, exclude: iflags.exclude, in: iflags.in, includeTests: iflags.includeTests });
1991
+ const findExclude = iflags.includeTests ? iflags.exclude : addTestExclusions(iflags.exclude);
1992
+ const found = index.find(arg, { file: iflags.file, exact: iflags.exact, exclude: findExclude, in: iflags.in });
1944
1993
  if (found.length === 0) {
1945
1994
  console.log(`No symbols found for "${arg}"`);
1946
1995
  } else {
@@ -1954,7 +2003,7 @@ function executeInteractiveCommand(index, command, arg, iflags = {}) {
1954
2003
  console.log('Usage: about <name>');
1955
2004
  return;
1956
2005
  }
1957
- const aboutResult = index.about(arg, { includeMethods: iflags.includeMethods, includeUncertain: iflags.includeUncertain, file: iflags.file, exclude: iflags.exclude });
2006
+ const aboutResult = index.about(arg, { withTypes: iflags.withTypes, file: iflags.file, all: iflags.all, includeMethods: iflags.includeMethods, includeUncertain: iflags.includeUncertain, exclude: iflags.exclude, maxCallers: iflags.top || undefined, maxCallees: iflags.top || undefined });
1958
2007
  console.log(output.formatAbout(aboutResult, { expand: iflags.expand, root: index.root, showAll: iflags.all }));
1959
2008
  break;
1960
2009
  }
@@ -1964,7 +2013,8 @@ function executeInteractiveCommand(index, command, arg, iflags = {}) {
1964
2013
  console.log('Usage: usages <name>');
1965
2014
  return;
1966
2015
  }
1967
- const usages = index.usages(arg, { context: iflags.context, codeOnly: iflags.codeOnly, includeTests: iflags.includeTests });
2016
+ const usagesExclude = iflags.includeTests ? iflags.exclude : addTestExclusions(iflags.exclude);
2017
+ const usages = index.usages(arg, { codeOnly: iflags.codeOnly, context: iflags.context, exclude: usagesExclude, in: iflags.in });
1968
2018
  console.log(output.formatUsages(usages, arg));
1969
2019
  break;
1970
2020
  }
@@ -1994,7 +2044,7 @@ function executeInteractiveCommand(index, command, arg, iflags = {}) {
1994
2044
  console.log('Usage: smart <name>');
1995
2045
  return;
1996
2046
  }
1997
- const smart = index.smart(arg, { file: iflags.file, includeUncertain: iflags.includeUncertain, withTypes: iflags.withTypes });
2047
+ const smart = index.smart(arg, { file: iflags.file, withTypes: iflags.withTypes, includeMethods: iflags.includeMethods, includeUncertain: iflags.includeUncertain });
1998
2048
  if (smart) {
1999
2049
  console.log(output.formatSmart(smart, {
2000
2050
  uncertainHint: 'use --include-uncertain to include all'
@@ -2010,7 +2060,7 @@ function executeInteractiveCommand(index, command, arg, iflags = {}) {
2010
2060
  console.log('Usage: impact <name>');
2011
2061
  return;
2012
2062
  }
2013
- const impactResult = index.impact(arg, { file: iflags.file });
2063
+ const impactResult = index.impact(arg, { file: iflags.file, exclude: iflags.exclude });
2014
2064
  console.log(output.formatImpact(impactResult));
2015
2065
  break;
2016
2066
  }
@@ -2076,7 +2126,7 @@ function executeInteractiveCommand(index, command, arg, iflags = {}) {
2076
2126
  const graphDepth = iflags.depth ? parseInt(iflags.depth) : 2;
2077
2127
  const graphDirection = iflags.direction || 'both';
2078
2128
  const graphResult = index.graph(arg, { direction: graphDirection, maxDepth: graphDepth });
2079
- console.log(output.formatGraph(graphResult, { showAll: iflags.all, maxDepth: graphDepth }));
2129
+ console.log(output.formatGraph(graphResult, { showAll: iflags.all || !!iflags.depth, maxDepth: graphDepth, file: arg }));
2080
2130
  break;
2081
2131
  }
2082
2132
 
@@ -2128,7 +2178,8 @@ function executeInteractiveCommand(index, command, arg, iflags = {}) {
2128
2178
  console.log('Usage: search <term>');
2129
2179
  return;
2130
2180
  }
2131
- const results = index.search(arg, { codeOnly: iflags.codeOnly, caseSensitive: iflags.caseSensitive, context: iflags.context, exclude: iflags.exclude, in: iflags.in, regex: iflags.regex });
2181
+ const searchExclude = iflags.includeTests ? iflags.exclude : addTestExclusions(iflags.exclude);
2182
+ const results = index.search(arg, { codeOnly: iflags.codeOnly, caseSensitive: iflags.caseSensitive, context: iflags.context, exclude: searchExclude, in: iflags.in, regex: iflags.regex });
2132
2183
  console.log(output.formatSearch(results, arg));
2133
2184
  break;
2134
2185
  }
@@ -2138,14 +2189,14 @@ function executeInteractiveCommand(index, command, arg, iflags = {}) {
2138
2189
  console.log('Usage: typedef <name>');
2139
2190
  return;
2140
2191
  }
2141
- const types = index.typedef(arg);
2192
+ const types = index.typedef(arg, { exact: iflags.exact });
2142
2193
  console.log(output.formatTypedef(types, arg));
2143
2194
  break;
2144
2195
  }
2145
2196
 
2146
2197
  case 'api': {
2147
- const api = index.api();
2148
- console.log(output.formatApi(api, '.'));
2198
+ const api = index.api(arg);
2199
+ console.log(output.formatApi(api, arg || '.'));
2149
2200
  break;
2150
2201
  }
2151
2202
 
@@ -2197,7 +2248,11 @@ function executeInteractiveCommand(index, command, arg, iflags = {}) {
2197
2248
  exclude: iflags.exclude,
2198
2249
  in: iflags.in
2199
2250
  });
2200
- console.log(output.formatDeadcode(deadResult, { top: iflags.top }));
2251
+ console.log(output.formatDeadcode(deadResult, {
2252
+ top: iflags.top,
2253
+ decoratedHint: !iflags.includeDecorated && deadResult.excludedDecorated > 0 ? `${deadResult.excludedDecorated} decorated/annotated symbol(s) hidden (framework-registered). Use --include-decorated to include them.` : undefined,
2254
+ exportedHint: !iflags.includeExported && deadResult.excludedExported > 0 ? `${deadResult.excludedExported} exported symbol(s) excluded (all have callers). Use --include-exported to audit them.` : undefined
2255
+ }));
2201
2256
  break;
2202
2257
  }
2203
2258
 
@@ -2206,8 +2261,8 @@ function executeInteractiveCommand(index, command, arg, iflags = {}) {
2206
2261
  console.log('Usage: related <name>');
2207
2262
  return;
2208
2263
  }
2209
- const relResult = index.related(arg, { file: iflags.file, all: iflags.all });
2210
- console.log(output.formatRelated(relResult, { showAll: iflags.all }));
2264
+ const relResult = index.related(arg, { file: iflags.file, top: iflags.top, all: iflags.all });
2265
+ console.log(output.formatRelated(relResult, { showAll: iflags.all, top: iflags.top }));
2211
2266
  break;
2212
2267
  }
2213
2268
 
package/mcp/server.js CHANGED
@@ -268,7 +268,9 @@ server.registerTool(
268
268
  range: z.string().optional().describe('Line range to extract, e.g. "10-20" or "15" (lines command)'),
269
269
  base: z.string().optional().describe('Git ref to diff against (default: HEAD). E.g. "HEAD~3", "main", a commit SHA'),
270
270
  staged: z.boolean().optional().describe('Analyze staged changes (diff_impact command)'),
271
- case_sensitive: z.boolean().optional().describe('Case-sensitive search (default: false, case-insensitive)')
271
+ case_sensitive: z.boolean().optional().describe('Case-sensitive search (default: false, case-insensitive)'),
272
+ all: z.boolean().optional().describe('Show all results (expand truncated sections). Applies to about, toc, related, trace, and others.'),
273
+ top_level: z.boolean().optional().describe('Show only top-level functions in toc (exclude nested/indented)')
272
274
  })
273
275
  },
274
276
  async (args) => {
@@ -278,7 +280,7 @@ server.registerTool(
278
280
  include_exported, include_decorated, calls_only, max_lines,
279
281
  direction, term, add_param, remove_param, rename_to,
280
282
  default_value, stack, item, range, base, staged,
281
- case_sensitive, regex, functions } = args;
283
+ case_sensitive, regex, functions, all, top_level } = args;
282
284
 
283
285
  try {
284
286
  switch (command) {
@@ -291,9 +293,9 @@ server.registerTool(
291
293
  const err = requireName(name);
292
294
  if (err) return err;
293
295
  const index = getIndex(project_dir);
294
- const result = index.about(name, { file, exclude: parseExclude(exclude), withTypes: with_types || false, includeMethods: include_methods ?? undefined, maxCallers: top, maxCallees: top });
296
+ const result = index.about(name, { file, exclude: parseExclude(exclude), withTypes: with_types || false, includeMethods: include_methods ?? undefined, includeUncertain: include_uncertain || false, all: all || false, maxCallers: top, maxCallees: top });
295
297
  return toolResult(output.formatAbout(result, {
296
- allHint: 'Repeat with top set higher to show all.',
298
+ allHint: 'Repeat with all=true to show all.',
297
299
  methodsHint: 'Note: obj.method() callers/callees excluded. Use include_methods=true to include them.'
298
300
  }));
299
301
  }
@@ -378,12 +380,12 @@ server.registerTool(
378
380
  const err = requireName(name);
379
381
  if (err) return err;
380
382
  const index = getIndex(project_dir);
381
- const result = index.related(name, { file, top, all: top !== undefined });
383
+ const result = index.related(name, { file, top, all: all || false });
382
384
  if (!result) return toolResult(`Symbol "${name}" not found.`);
383
385
  return toolResult(output.formatRelated(result, {
384
- showAll: top !== undefined,
386
+ showAll: all || false,
385
387
  top,
386
- allHint: 'Repeat with top set higher to show all.'
388
+ allHint: 'Repeat with all=true to show all.'
387
389
  }));
388
390
  }
389
391
 
@@ -416,7 +418,7 @@ server.registerTool(
416
418
 
417
419
  case 'toc': {
418
420
  const index = getIndex(project_dir);
419
- const toc = index.getToc({ detailed: detailed || false, top });
421
+ const toc = index.getToc({ detailed: detailed || false, topLevel: top_level || false, all: all || false, top });
420
422
  return toolResult(output.formatToc(toc, {
421
423
  topHint: 'Set top=N or use detailed=false for compact view.'
422
424
  }));
@@ -484,6 +486,19 @@ server.registerTool(
484
486
  continue;
485
487
  }
486
488
 
489
+ // Show all definitions when all=true and multiple matches
490
+ if (matches.length > 1 && !file && all) {
491
+ for (const m of matches) {
492
+ const mPathCheck = resolveAndValidatePath(index, m.relativePath || path.relative(index.root, m.file));
493
+ if (typeof mPathCheck !== 'string') return mPathCheck;
494
+ const mCode = fs.readFileSync(m.file, 'utf-8');
495
+ const mLines = mCode.split('\n');
496
+ const mFnCode = mLines.slice(m.startLine - 1, m.endLine).join('\n');
497
+ parts.push(output.formatFn(m, mFnCode));
498
+ }
499
+ continue;
500
+ }
501
+
487
502
  const match = matches.length > 1 ? pickBestDefinition(matches) : matches[0];
488
503
  const fnPathCheck = resolveAndValidatePath(index, match.relativePath || path.relative(index.root, match.file));
489
504
  if (typeof fnPathCheck !== 'string') return fnPathCheck;
@@ -493,7 +508,7 @@ server.registerTool(
493
508
 
494
509
  let note = '';
495
510
  if (matches.length > 1 && !file) {
496
- note = `Note: Found ${matches.length} definitions for "${fnName}". Showing ${match.relativePath}:${match.startLine}. Use file parameter to disambiguate.\n`;
511
+ note = `Note: Found ${matches.length} definitions for "${fnName}". Showing ${match.relativePath}:${match.startLine}. Use file parameter or all=true to show all.\n`;
497
512
  }
498
513
  parts.push(note + output.formatFn(match, fnCode));
499
514
  }
@@ -514,6 +529,20 @@ server.registerTool(
514
529
  return toolResult(`Class "${name}" not found.`);
515
530
  }
516
531
 
532
+ // Show all definitions when all=true and multiple matches
533
+ if (matches.length > 1 && !file && all) {
534
+ const allParts = [];
535
+ for (const m of matches) {
536
+ const mPathCheck = resolveAndValidatePath(index, m.relativePath || path.relative(index.root, m.file));
537
+ if (typeof mPathCheck !== 'string') return mPathCheck;
538
+ const mCode = fs.readFileSync(m.file, 'utf-8');
539
+ const mLines = mCode.split('\n');
540
+ const clsCode = mLines.slice(m.startLine - 1, m.endLine).join('\n');
541
+ allParts.push(output.formatClass(m, clsCode));
542
+ }
543
+ return toolResult(allParts.join('\n\n'));
544
+ }
545
+
517
546
  const match = matches.length > 1 ? pickBestDefinition(matches) : matches[0];
518
547
  // Validate file is within project root
519
548
  const clsPathCheck = resolveAndValidatePath(index, match.relativePath || path.relative(index.root, match.file));
@@ -728,7 +757,8 @@ server.registerTool(
728
757
  if (result?.error === 'file-not-found') return toolError(`File not found in project: ${file}`);
729
758
  if (result?.error === 'file-ambiguous') return toolError(`Ambiguous file "${file}". Candidates:\n${result.candidates.map(c => ' ' + c).join('\n')}`);
730
759
  return toolResult(output.formatGraph(result, {
731
- showAll: depth !== undefined,
760
+ showAll: all || depth !== undefined,
761
+ maxDepth: depth ?? 2,
732
762
  file,
733
763
  depthHint: 'Set depth parameter for deeper graph.',
734
764
  allHint: 'Set depth to expand all children.'
@@ -810,7 +840,7 @@ server.registerTool(
810
840
  case 'stats': {
811
841
  const index = getIndex(project_dir);
812
842
  const stats = index.getStats({ functions: functions || false });
813
- return toolResult(output.formatStats(stats, { top: top || 30 }));
843
+ return toolResult(output.formatStats(stats, { top: top || 0 }));
814
844
  }
815
845
 
816
846
  default:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ucn",
3
- "version": "3.7.16",
3
+ "version": "3.7.18",
4
4
  "mcpName": "io.github.mleoca/ucn",
5
5
  "description": "Universal Code Navigator — AST-based call graph analysis for AI agents. Find callers, trace impact, detect dead code across JS/TS, Python, Go, Rust, Java, and HTML. CLI, MCP server, and agent skill.",
6
6
  "main": "index.js",
@@ -9,7 +9,7 @@
9
9
  "ucn-mcp": "mcp/server.js"
10
10
  },
11
11
  "scripts": {
12
- "test": "node --test test/parser.test.js test/accuracy.test.js test/systematic-test.js test/mcp-edge-cases.js"
12
+ "test": "node --test test/parser-unit.test.js test/integration.test.js test/cache.test.js test/formatter.test.js test/interactive.test.js test/feature.test.js test/regression-js.test.js test/regression-py.test.js test/regression-go.test.js test/regression-java.test.js test/regression-rust.test.js test/regression-cross.test.js test/accuracy.test.js test/systematic-test.js test/mcp-edge-cases.js test/parity-test.js"
13
13
  },
14
14
  "keywords": [
15
15
  "mcp",