ucn 3.7.6 → 3.7.8
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/README.md +2 -1
- package/cli/index.js +3 -3
- package/core/output.js +14 -2
- package/mcp/server.js +1 -0
- package/package.json +1 -1
- package/test/parser.test.js +28 -0
package/README.md
CHANGED
|
@@ -5,6 +5,7 @@ UCN gives AI agents call-graph-level understanding of code. Instead of reading e
|
|
|
5
5
|
Designed for large codebases where agents waste context on reading large files. UCN's surgical output means agents spend tokens on reasoning, not on ingesting thousands of lines to find three callers, discourages agents from cutting corners, as without UCN, agents working with large codebases tend to skip parts of the code structure, assuming they have "enough data".
|
|
6
6
|
|
|
7
7
|
Everything runs locally on your machine and nothing leaves your project.
|
|
8
|
+
The ucn mcp is kept light, as all 28 commands ship as a single MCP tool, under 2k tokens total.
|
|
8
9
|
|
|
9
10
|
---
|
|
10
11
|
|
|
@@ -43,7 +44,7 @@ Precise answers without reading files.
|
|
|
43
44
|
│ $ ucn about myFunc Works standalone, no agent required. │
|
|
44
45
|
│ │
|
|
45
46
|
│ 2. MCP Server Any MCP-compatible AI agent connects │
|
|
46
|
-
│ $ ucn --mcp and gets 28 commands automatically.
|
|
47
|
+
│ $ ucn --mcp and gets 28 commands automatically. │
|
|
47
48
|
│ │
|
|
48
49
|
│ 3. Agent Skill Drop-in skill for Claude Code and │
|
|
49
50
|
│ /ucn about myFunc OpenAI Codex CLI. No server needed. │
|
package/cli/index.js
CHANGED
|
@@ -983,7 +983,7 @@ function runProjectCommand(rootDir, command, arg) {
|
|
|
983
983
|
});
|
|
984
984
|
printOutput(deadcodeResults,
|
|
985
985
|
output.formatDeadcodeJson,
|
|
986
|
-
r => output.formatDeadcode(r)
|
|
986
|
+
r => output.formatDeadcode(r, { top: flags.top })
|
|
987
987
|
);
|
|
988
988
|
break;
|
|
989
989
|
}
|
|
@@ -1720,7 +1720,7 @@ Common Flags:
|
|
|
1720
1720
|
--depth=N Trace/graph depth (default: 3, also expands all children)
|
|
1721
1721
|
--direction=X Graph direction: imports, importers, or both (default: both)
|
|
1722
1722
|
--all Expand truncated sections (about, trace, graph, related)
|
|
1723
|
-
--top=N Limit results (find)
|
|
1723
|
+
--top=N Limit results (find, deadcode)
|
|
1724
1724
|
--context=N Lines of context around matches
|
|
1725
1725
|
--json Machine-readable output
|
|
1726
1726
|
--code-only Filter out comments and strings
|
|
@@ -2154,7 +2154,7 @@ function executeInteractiveCommand(index, command, arg, iflags = {}) {
|
|
|
2154
2154
|
exclude: iflags.exclude,
|
|
2155
2155
|
in: iflags.in
|
|
2156
2156
|
});
|
|
2157
|
-
console.log(output.formatDeadcode(deadResult));
|
|
2157
|
+
console.log(output.formatDeadcode(deadResult, { top: iflags.top }));
|
|
2158
2158
|
break;
|
|
2159
2159
|
}
|
|
2160
2160
|
|
package/core/output.js
CHANGED
|
@@ -1724,12 +1724,20 @@ function formatDeadcode(results, options = {}) {
|
|
|
1724
1724
|
}
|
|
1725
1725
|
|
|
1726
1726
|
const lines = [];
|
|
1727
|
+
const top = options.top > 0 ? options.top : 0;
|
|
1728
|
+
const showing = top > 0 ? results.slice(0, top) : results;
|
|
1729
|
+
const hidden = results.length - showing.length;
|
|
1730
|
+
|
|
1727
1731
|
if (results.length > 0) {
|
|
1728
|
-
|
|
1732
|
+
if (hidden > 0) {
|
|
1733
|
+
lines.push(`Dead code: ${results.length} unused symbol(s) (showing ${showing.length})\n`);
|
|
1734
|
+
} else {
|
|
1735
|
+
lines.push(`Dead code: ${results.length} unused symbol(s)\n`);
|
|
1736
|
+
}
|
|
1729
1737
|
}
|
|
1730
1738
|
|
|
1731
1739
|
let currentFile = null;
|
|
1732
|
-
for (const item of
|
|
1740
|
+
for (const item of showing) {
|
|
1733
1741
|
if (item.file !== currentFile) {
|
|
1734
1742
|
currentFile = item.file;
|
|
1735
1743
|
lines.push(item.file);
|
|
@@ -1747,6 +1755,10 @@ function formatDeadcode(results, options = {}) {
|
|
|
1747
1755
|
lines.push(` ${lineRange(item.startLine, item.endLine)} ${item.name} (${item.type})${exported}${hintStr}`);
|
|
1748
1756
|
}
|
|
1749
1757
|
|
|
1758
|
+
if (hidden > 0) {
|
|
1759
|
+
lines.push(`\n${hidden} more result(s) not shown. Use --top=${results.length} or --all to see all.`);
|
|
1760
|
+
}
|
|
1761
|
+
|
|
1750
1762
|
// Show counts of excluded items with expansion hints
|
|
1751
1763
|
if (results.length === 0) {
|
|
1752
1764
|
lines.push('No dead code found.');
|
package/mcp/server.js
CHANGED
|
@@ -451,6 +451,7 @@ server.registerTool(
|
|
|
451
451
|
includeTests: include_tests || false
|
|
452
452
|
});
|
|
453
453
|
return toolResult(output.formatDeadcode(result, {
|
|
454
|
+
top: top || 0,
|
|
454
455
|
decoratedHint: !include_decorated && result.excludedDecorated > 0 ? `${result.excludedDecorated} decorated/annotated symbol(s) hidden (framework-registered). Use include_decorated=true to include them.` : undefined,
|
|
455
456
|
exportedHint: !include_exported && result.excludedExported > 0 ? `${result.excludedExported} exported symbol(s) excluded (all have callers). Use include_exported=true to audit them.` : undefined
|
|
456
457
|
}));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ucn",
|
|
3
|
-
"version": "3.7.
|
|
3
|
+
"version": "3.7.8",
|
|
4
4
|
"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.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
package/test/parser.test.js
CHANGED
|
@@ -9647,6 +9647,34 @@ it('formatDeadcode handles zero exclusions without hints', () => {
|
|
|
9647
9647
|
assert.ok(text.includes('helper'), 'Should still show the result');
|
|
9648
9648
|
});
|
|
9649
9649
|
|
|
9650
|
+
it('formatDeadcode respects --top option', () => {
|
|
9651
|
+
const { formatDeadcode } = require('../core/output');
|
|
9652
|
+
|
|
9653
|
+
const results = [
|
|
9654
|
+
{ name: 'a', type: 'function', file: 'a.js', startLine: 1, endLine: 3, isExported: false },
|
|
9655
|
+
{ name: 'b', type: 'function', file: 'b.js', startLine: 1, endLine: 3, isExported: false },
|
|
9656
|
+
{ name: 'c', type: 'function', file: 'c.js', startLine: 1, endLine: 3, isExported: false },
|
|
9657
|
+
{ name: 'd', type: 'function', file: 'd.js', startLine: 1, endLine: 3, isExported: false },
|
|
9658
|
+
{ name: 'e', type: 'function', file: 'e.js', startLine: 1, endLine: 3, isExported: false }
|
|
9659
|
+
];
|
|
9660
|
+
results.excludedDecorated = 0;
|
|
9661
|
+
results.excludedExported = 0;
|
|
9662
|
+
|
|
9663
|
+
// With top=2, should show only 2 results
|
|
9664
|
+
const text = formatDeadcode(results, { top: 2 });
|
|
9665
|
+
assert.ok(text.includes('(showing 2)'), 'Should indicate showing 2');
|
|
9666
|
+
assert.ok(text.includes('a (function)'), 'Should show first result');
|
|
9667
|
+
assert.ok(text.includes('b (function)'), 'Should show second result');
|
|
9668
|
+
assert.ok(!text.includes('c (function)'), 'Should not show third result');
|
|
9669
|
+
assert.ok(text.includes('3 more result(s) not shown'), 'Should show hidden count');
|
|
9670
|
+
|
|
9671
|
+
// Without top, should show all results
|
|
9672
|
+
const textAll = formatDeadcode(results);
|
|
9673
|
+
assert.ok(!textAll.includes('showing'), 'Should not indicate partial results');
|
|
9674
|
+
assert.ok(textAll.includes('e (function)'), 'Should show all results');
|
|
9675
|
+
assert.ok(!textAll.includes('more result(s) not shown'), 'Should not show hidden hint');
|
|
9676
|
+
});
|
|
9677
|
+
|
|
9650
9678
|
it('deadcode Python: simple decorators NOT excluded (only attribute access)', () => {
|
|
9651
9679
|
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'ucn-dc-simple-deco-'));
|
|
9652
9680
|
try {
|