ucn 3.8.3 → 3.8.5
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/scheduled_tasks.lock +1 -0
- package/.claude/skills/ucn/SKILL.md +6 -1
- package/README.md +270 -201
- package/cli/index.js +6 -3
- package/core/callers.js +14 -3
- package/core/deadcode.js +1 -2
- package/core/execute.js +43 -43
- package/core/output.js +6 -5
- package/core/project.js +161 -10
- package/core/shared.js +2 -0
- package/core/verify.js +55 -23
- package/languages/go.js +10 -2
- package/languages/java.js +23 -10
- package/languages/javascript.js +8 -0
- package/languages/python.js +4 -0
- package/languages/rust.js +20 -8
- package/languages/utils.js +29 -1
- package/mcp/server.js +5 -1
- package/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"sessionId":"5e2a28ce-7f65-4369-a922-8d1d902f9d8f","pid":18086,"acquiredAt":1773363188494}
|
|
@@ -26,7 +26,7 @@ Extract functions, trace call chains, find callers, and detect dead code — wit
|
|
|
26
26
|
- Language not supported (only JS/TS, Python, Go, Rust, Java, HTML)
|
|
27
27
|
- Finding files by name — use glob
|
|
28
28
|
|
|
29
|
-
## The
|
|
29
|
+
## The Commands You'll Use Most
|
|
30
30
|
|
|
31
31
|
### 1. `about` — First stop for any investigation
|
|
32
32
|
|
|
@@ -157,6 +157,7 @@ ucn [target] <command> [name] [--flags]
|
|
|
157
157
|
|
|
158
158
|
| Flag | When to use it |
|
|
159
159
|
|------|---------------|
|
|
160
|
+
| `--class-name=X` | Scope to specific class (e.g., `--class-name=Repository` for method `save`) |
|
|
160
161
|
| `--file=<pattern>` | Disambiguate when a name exists in multiple files (e.g., `--file=api`) |
|
|
161
162
|
| `--exclude=test,mock` | Focus on production code only |
|
|
162
163
|
| `--in=src/core` | Limit search to a subdirectory |
|
|
@@ -185,6 +186,10 @@ ucn [target] <command> [name] [--flags]
|
|
|
185
186
|
| `--include-uncertain` | Include ambiguous/uncertain matches in `context`/`smart`/`about` |
|
|
186
187
|
| `--show-confidence` | Show confidence scores (0.0–1.0) per caller/callee edge in `context`/`about` |
|
|
187
188
|
| `--min-confidence=N` | Filter edges below confidence threshold (e.g., `--min-confidence=0.7` keeps only high-confidence edges) |
|
|
189
|
+
| `--calls-only` | Only show call/test-case matches in `tests` (skip file-level results) |
|
|
190
|
+
| `--add-param=<name>` | Add a parameter (`plan` command). Combine with `--default=<value>` |
|
|
191
|
+
| `--remove-param=<name>` | Remove a parameter (`plan` command) |
|
|
192
|
+
| `--rename-to=<name>` | Rename a function (`plan` command) |
|
|
188
193
|
| `--include-exported` | Include exported symbols in `deadcode` results |
|
|
189
194
|
| `--include-decorated` | Include decorated/annotated symbols in `deadcode` results |
|
|
190
195
|
| `--framework=X` | Filter `entrypoints` by framework (e.g., `express`, `spring`, `celery`) |
|
package/README.md
CHANGED
|
@@ -1,34 +1,13 @@
|
|
|
1
1
|
# UCN - Universal Code Navigator
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
See what code does before you touch it.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
- Who calls this function? → without grepping the whole project
|
|
7
|
-
- What breaks if I change this? → every call site, with arguments
|
|
8
|
-
- What does this function do? → extracted with dependencies inline
|
|
9
|
-
- What code is safe to delete? → verified unused symbols
|
|
10
|
-
|
|
11
|
-
One command replaces 3-4 grep+read cycles. Powered by tree-sitter.
|
|
5
|
+
Find symbols, trace callers, check impact, pick the right tests, extract code and spot what's dead - from the terminal.
|
|
12
6
|
|
|
13
7
|
[](https://www.npmjs.com/package/ucn)
|
|
14
8
|
[](LICENSE)
|
|
15
9
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
## 60-Second Quickstart
|
|
19
|
-
|
|
20
|
-
```bash
|
|
21
|
-
npm install -g ucn
|
|
22
|
-
|
|
23
|
-
ucn toc # project overview
|
|
24
|
-
ucn fn handleRequest # extract a function without reading the file
|
|
25
|
-
ucn about handleRequest # full picture: definition, callers, callees, tests
|
|
26
|
-
ucn impact handleRequest # all call sites with arguments
|
|
27
|
-
ucn trace main --depth=3 # call tree, no file reads
|
|
28
|
-
ucn deadcode # unused functions, AST-verified
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
Supports JS/TS, Python, Go, Rust, Java, and HTML. Runs locally.
|
|
10
|
+
All commands, one engine, three surfaces:
|
|
32
11
|
|
|
33
12
|
```
|
|
34
13
|
Terminal AI Agents Agent Skills
|
|
@@ -43,11 +22,27 @@ Supports JS/TS, Python, Go, Rust, Java, and HTML. Runs locally.
|
|
|
43
22
|
└─────────────┘
|
|
44
23
|
```
|
|
45
24
|
|
|
25
|
+
Supports JavaScript, TypeScript, Python, Go, Rust, Java, and HTML inline scripts.
|
|
26
|
+
|
|
27
|
+
If you work with AI, add UCN as a [Skill or MCP](#ai-setup) and let the agent ask better code questions instead of reading whole files.
|
|
28
|
+
All commands ship as a single tool.
|
|
29
|
+
|
|
30
|
+
UCN is deliberately lightweight:
|
|
31
|
+
|
|
32
|
+
- **No background processes** - parses on demand, answers, exits
|
|
33
|
+
- **No language servers** - tree-sitter does the parsing, no compilation needed
|
|
34
|
+
- **MCP is optional** - only needed if you connect UCN to an AI agent, the CLI and Skill work on their own
|
|
35
|
+
|
|
46
36
|
---
|
|
47
37
|
|
|
48
|
-
|
|
38
|
+
```bash
|
|
39
|
+
npm install -g ucn
|
|
49
40
|
|
|
50
|
-
|
|
41
|
+
ucn trace main --depth=3 # full execution flow
|
|
42
|
+
ucn about handleRequest # definition + callers + callees + tests
|
|
43
|
+
ucn impact handleRequest # every call site with arguments
|
|
44
|
+
ucn deadcode --exclude=test # unused code, AST-verified
|
|
45
|
+
```
|
|
51
46
|
|
|
52
47
|
"What happens when `build()` runs?"
|
|
53
48
|
|
|
@@ -55,47 +50,155 @@ AI agents waste tokens reading entire files to find one function, or grep for ca
|
|
|
55
50
|
$ ucn trace build --depth=2
|
|
56
51
|
|
|
57
52
|
build
|
|
58
|
-
├── detectProjectPattern (discovery.js:
|
|
59
|
-
│ ├── checkDir (discovery.js:
|
|
60
|
-
│ └── shouldIgnore (discovery.js:
|
|
61
|
-
├── parseGitignore (discovery.js:
|
|
62
|
-
├── expandGlob (discovery.js:
|
|
63
|
-
│ ├── parseGlobPattern (discovery.js:
|
|
64
|
-
│ ├── walkDir (discovery.js:
|
|
65
|
-
│ └── compareNames (discovery.js:
|
|
66
|
-
├── indexFile (project.js:
|
|
67
|
-
│ ├── addSymbol (project.js:
|
|
53
|
+
├── detectProjectPattern (core/discovery.js:399) 1x
|
|
54
|
+
│ ├── checkDir (core/discovery.js:403) 2x
|
|
55
|
+
│ └── shouldIgnore (core/discovery.js:347) 1x
|
|
56
|
+
├── parseGitignore (core/discovery.js:130) 1x
|
|
57
|
+
├── expandGlob (core/discovery.js:190) 1x
|
|
58
|
+
│ ├── parseGlobPattern (core/discovery.js:226) 1x
|
|
59
|
+
│ ├── walkDir (core/discovery.js:283) 1x
|
|
60
|
+
│ └── compareNames (core/discovery.js:169) 1x
|
|
61
|
+
├── indexFile (core/project.js:273) 1x
|
|
62
|
+
│ ├── addSymbol (core/project.js:343) 4x
|
|
68
63
|
│ ├── detectLanguage (languages/index.js:157) 1x
|
|
69
|
-
│ ├──
|
|
70
|
-
│ └── extractImports (imports.js:19) 1x
|
|
71
|
-
├── buildImportGraph (project.js:
|
|
72
|
-
└── buildInheritanceGraph (project.js:
|
|
64
|
+
│ ├── parse (core/parser.js:69) 1x
|
|
65
|
+
│ └── extractImports (core/imports.js:19) 1x
|
|
66
|
+
├── buildImportGraph (core/project.js:549) 1x
|
|
67
|
+
└── buildInheritanceGraph (core/project.js:627) 1x
|
|
73
68
|
```
|
|
74
69
|
|
|
75
|
-
One command. No files opened.
|
|
70
|
+
One command. No files opened. Every function located by file and line.
|
|
76
71
|
|
|
77
72
|
---
|
|
78
73
|
|
|
79
|
-
##
|
|
74
|
+
## Understand code you didn't write
|
|
75
|
+
|
|
76
|
+
`ucn about` gives you everything about a function in one shot - who calls it, what it calls, which tests cover it, and the source code.
|
|
77
|
+
|
|
78
|
+
```
|
|
79
|
+
$ ucn about expandGlob
|
|
80
|
+
|
|
81
|
+
expandGlob (function)
|
|
82
|
+
════════════════════════════════════════════════════════════
|
|
83
|
+
core/discovery.js:190-221
|
|
84
|
+
expandGlob (pattern, options = {})
|
|
85
|
+
|
|
86
|
+
CALLERS (3):
|
|
87
|
+
cli/index.js:859 [runGlobCommand]
|
|
88
|
+
const files = expandGlob(pattern);
|
|
89
|
+
core/cache.js:274 [isCacheStale]
|
|
90
|
+
const currentFiles = expandGlob(pattern, globOpts);
|
|
91
|
+
core/project.js:195 [build]
|
|
92
|
+
const files = expandGlob(pattern, globOpts);
|
|
93
|
+
|
|
94
|
+
CALLEES (3):
|
|
95
|
+
parseGlobPattern [utility] - core/discovery.js:226
|
|
96
|
+
walkDir [utility] - core/discovery.js:283
|
|
97
|
+
compareNames [utility] - core/discovery.js:169
|
|
98
|
+
|
|
99
|
+
TESTS: 6 matches in 2 file(s)
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Need to trace execution upward instead? `ucn reverse-trace fn` walks the caller chain back to entry points.
|
|
103
|
+
|
|
104
|
+
## Change code without breaking things
|
|
105
|
+
|
|
106
|
+
Before touching a function, check if all existing call sites match its signature:
|
|
107
|
+
|
|
108
|
+
```
|
|
109
|
+
$ ucn verify expandGlob
|
|
110
|
+
|
|
111
|
+
expandGlob (pattern, options = {})
|
|
112
|
+
Expected arguments: 1-2
|
|
113
|
+
|
|
114
|
+
STATUS: ✓ All calls valid (7 calls, 0 mismatches)
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Then preview the refactoring. UCN shows exactly what needs to change and where:
|
|
118
|
+
|
|
119
|
+
```
|
|
120
|
+
$ ucn plan expandGlob --rename-to=expandGlobPattern
|
|
121
|
+
|
|
122
|
+
SIGNATURE CHANGE:
|
|
123
|
+
Before: expandGlob (pattern, options = {})
|
|
124
|
+
After: expandGlobPattern (pattern, options = {})
|
|
125
|
+
|
|
126
|
+
CHANGES NEEDED: 7 across 4 files
|
|
127
|
+
|
|
128
|
+
cli/index.js :859
|
|
129
|
+
const files = expandGlob(pattern);
|
|
130
|
+
→ const files = expandGlobPattern(pattern);
|
|
131
|
+
|
|
132
|
+
core/cache.js :274
|
|
133
|
+
const currentFiles = expandGlob(pattern, globOpts);
|
|
134
|
+
→ const currentFiles = expandGlobPattern(pattern, globOpts);
|
|
135
|
+
|
|
136
|
+
core/project.js :195
|
|
137
|
+
const files = expandGlob(pattern, globOpts);
|
|
138
|
+
→ const files = expandGlobPattern(pattern, globOpts);
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Run `ucn diff-impact --staged` before committing to see what you changed and who calls it.
|
|
142
|
+
|
|
143
|
+
## Find what to clean up
|
|
144
|
+
|
|
145
|
+
Which tests should you run after a change? `affected-tests` walks the blast radius and finds every test that touches the affected functions:
|
|
146
|
+
|
|
147
|
+
```
|
|
148
|
+
$ ucn affected-tests expandGlob
|
|
149
|
+
|
|
150
|
+
1 function changed → 18 functions affected (depth 3)
|
|
151
|
+
Summary: 18 affected → 12 test files, 11/18 functions covered (61%)
|
|
152
|
+
|
|
153
|
+
Uncovered (7): runGlobCommand, runProjectCommand, ...
|
|
154
|
+
⚠ These affected functions have no test references
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## Find unused code
|
|
158
|
+
|
|
159
|
+
```
|
|
160
|
+
$ ucn deadcode --exclude=test
|
|
161
|
+
|
|
162
|
+
Dead code: 1 unused symbol(s)
|
|
163
|
+
|
|
164
|
+
core/discovery.js
|
|
165
|
+
[ 162- 166] legacyResolve (function)
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## Extract without reading the whole file
|
|
169
|
+
|
|
170
|
+
```
|
|
171
|
+
$ ucn fn compareNames
|
|
80
172
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
173
|
+
core/discovery.js:169
|
|
174
|
+
[ 169- 177] compareNames(a, b)
|
|
175
|
+
────────────────────────────────────────────────────────────
|
|
176
|
+
function compareNames(a, b) {
|
|
177
|
+
const aLower = a.toLowerCase();
|
|
178
|
+
const bLower = b.toLowerCase();
|
|
179
|
+
if (aLower < bLower) return -1;
|
|
180
|
+
if (aLower > bLower) return 1;
|
|
181
|
+
if (a < b) return -1;
|
|
182
|
+
if (a > b) return 1;
|
|
183
|
+
return 0;
|
|
184
|
+
}
|
|
185
|
+
```
|
|
91
186
|
|
|
92
187
|
---
|
|
93
188
|
|
|
94
|
-
##
|
|
189
|
+
## Testing and reliability
|
|
190
|
+
|
|
191
|
+
- **Fast** - indexes its own ~25K-line codebase in under 100ms, cached after first run
|
|
192
|
+
- **Discipline** - every bug fix gets a regression test, test code is ~3x the source
|
|
193
|
+
- **Coverage** - every command, every supported language, every surface (CLI, MCP, interactive)
|
|
194
|
+
- **Systematic** - a harness exercises all command and flag combinations against real multi-language fixtures
|
|
195
|
+
- **Test types** - unit, integration, per-language regression, formatter, cache, MCP edge cases, architecture parity guards
|
|
95
196
|
|
|
96
|
-
|
|
197
|
+
---
|
|
198
|
+
|
|
199
|
+
## AI Setup
|
|
97
200
|
|
|
98
|
-
|
|
201
|
+
### MCP
|
|
99
202
|
|
|
100
203
|
```bash
|
|
101
204
|
# Claude Code
|
|
@@ -138,12 +241,8 @@ VS Code uses `.vscode/mcp.json`:
|
|
|
138
241
|
|
|
139
242
|
</details>
|
|
140
243
|
|
|
141
|
-
All commands ship as a single MCP tool - under 2KB of context.
|
|
142
|
-
|
|
143
244
|
### Agent Skill (no server needed)
|
|
144
245
|
|
|
145
|
-
Drop-in for Claude Code or Codex CLI:
|
|
146
|
-
|
|
147
246
|
```bash
|
|
148
247
|
# Claude Code
|
|
149
248
|
mkdir -p ~/.claude/skills
|
|
@@ -156,156 +255,126 @@ cp -r "$(npm root -g)/ucn/.claude/skills/ucn" ~/.agents/skills/
|
|
|
156
255
|
|
|
157
256
|
---
|
|
158
257
|
|
|
159
|
-
##
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
[
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
---
|
|
267
|
-
|
|
268
|
-
## All commands
|
|
269
|
-
|
|
270
|
-
```
|
|
271
|
-
UNDERSTAND MODIFY SAFELY
|
|
272
|
-
───────────────────── ─────────────────────
|
|
273
|
-
about full picture impact all call sites
|
|
274
|
-
context callers + callees blast transitive impact
|
|
275
|
-
smart function + helpers diff-impact git diff + callers
|
|
276
|
-
trace call tree verify signature check
|
|
277
|
-
reverse-trace callers → root plan refactor preview
|
|
278
|
-
|
|
279
|
-
FIND & EXTRACT ARCHITECTURE
|
|
280
|
-
───────────────────── ─────────────────────
|
|
281
|
-
find locate definitions imports file dependencies
|
|
282
|
-
usages all occurrences exporters reverse dependencies
|
|
283
|
-
fn extract function graph dependency tree
|
|
284
|
-
class extract class circular-deps import cycles
|
|
285
|
-
toc project overview related sibling functions
|
|
286
|
-
deadcode unused code tests find test coverage
|
|
287
|
-
search text search affected-tests tests for changes
|
|
288
|
-
example best usage example stacktrace error trace context
|
|
289
|
-
lines extract line range api public API surface
|
|
290
|
-
expand drill into context typedef type definitions
|
|
291
|
-
file-exports file's exports
|
|
292
|
-
stats project stats
|
|
258
|
+
## Full help
|
|
259
|
+
|
|
260
|
+
```text
|
|
261
|
+
UCN - Universal Code Navigator
|
|
262
|
+
|
|
263
|
+
Supported: JavaScript, TypeScript, Python, Go, Rust, Java, HTML
|
|
264
|
+
|
|
265
|
+
Usage:
|
|
266
|
+
ucn [command] [args] Project mode (current directory)
|
|
267
|
+
ucn <file> [command] [args] Single file mode
|
|
268
|
+
ucn <dir> [command] [args] Project mode (specific directory)
|
|
269
|
+
ucn "pattern" [command] [args] Glob pattern mode
|
|
270
|
+
(Default output is text; add --json for machine-readable JSON)
|
|
271
|
+
|
|
272
|
+
═══════════════════════════════════════════════════════════════════════════════
|
|
273
|
+
UNDERSTAND CODE
|
|
274
|
+
═══════════════════════════════════════════════════════════════════════════════
|
|
275
|
+
about <name> Full picture (definition, callers, callees, tests, code)
|
|
276
|
+
context <name> Who calls this + what it calls (numbered for expand)
|
|
277
|
+
smart <name> Function + all dependencies inline
|
|
278
|
+
impact <name> What breaks if changed (call sites grouped by file)
|
|
279
|
+
blast <name> Transitive blast radius (callers of callers, --depth=N)
|
|
280
|
+
trace <name> Call tree visualization (--depth=N expands all children)
|
|
281
|
+
reverse-trace <name> Upward call chain to entry points (--depth=N, default 5)
|
|
282
|
+
related <name> Find similar functions (same file, shared deps)
|
|
283
|
+
example <name> Best usage example with context
|
|
284
|
+
|
|
285
|
+
═══════════════════════════════════════════════════════════════════════════════
|
|
286
|
+
FIND CODE
|
|
287
|
+
═══════════════════════════════════════════════════════════════════════════════
|
|
288
|
+
find <name> Find symbol definitions (supports glob: find "handle*")
|
|
289
|
+
usages <name> All usages grouped: definitions, calls, imports, references
|
|
290
|
+
toc Table of contents (compact; --detailed lists all symbols)
|
|
291
|
+
search <term> Text search (regex default, --context=N, --exclude=, --in=)
|
|
292
|
+
Structural: --type=function|class|call --param= --returns= --decorator= --exported --unused
|
|
293
|
+
tests <name> Find test files for a function
|
|
294
|
+
affected-tests <n> Tests affected by a change (blast + test detection, --depth=N)
|
|
295
|
+
|
|
296
|
+
═══════════════════════════════════════════════════════════════════════════════
|
|
297
|
+
EXTRACT CODE
|
|
298
|
+
═══════════════════════════════════════════════════════════════════════════════
|
|
299
|
+
fn <name>[,n2,...] Extract function(s) (comma-separated for bulk, --file)
|
|
300
|
+
class <name> Extract class
|
|
301
|
+
lines <range> Extract line range (e.g., lines 50-100)
|
|
302
|
+
expand <N> Show code for item N from context output
|
|
303
|
+
|
|
304
|
+
═══════════════════════════════════════════════════════════════════════════════
|
|
305
|
+
FILE DEPENDENCIES
|
|
306
|
+
═══════════════════════════════════════════════════════════════════════════════
|
|
307
|
+
imports <file> What does file import
|
|
308
|
+
exporters <file> Who imports this file
|
|
309
|
+
file-exports <file> What does file export
|
|
310
|
+
graph <file> Full dependency tree (--depth=N, --direction=imports|importers|both)
|
|
311
|
+
circular-deps Detect circular import chains (--file=, --exclude=)
|
|
312
|
+
|
|
313
|
+
═══════════════════════════════════════════════════════════════════════════════
|
|
314
|
+
REFACTORING HELPERS
|
|
315
|
+
═══════════════════════════════════════════════════════════════════════════════
|
|
316
|
+
plan <name> Preview refactoring (--add-param, --remove-param, --rename-to)
|
|
317
|
+
verify <name> Check all call sites match signature
|
|
318
|
+
diff-impact What changed in git diff and who calls it (--base, --staged)
|
|
319
|
+
deadcode Find unused functions/classes
|
|
320
|
+
entrypoints Detect framework entry points (routes, DI, tasks)
|
|
321
|
+
|
|
322
|
+
═══════════════════════════════════════════════════════════════════════════════
|
|
323
|
+
OTHER
|
|
324
|
+
═══════════════════════════════════════════════════════════════════════════════
|
|
325
|
+
api Show exported/public symbols
|
|
326
|
+
typedef <name> Find type definitions
|
|
327
|
+
stats Project statistics (--functions for per-function line counts)
|
|
328
|
+
stacktrace <text> Parse stack trace, show code at each frame (alias: stack)
|
|
329
|
+
|
|
330
|
+
Common Flags:
|
|
331
|
+
--file <pattern> Filter by file path (e.g., --file=routes)
|
|
332
|
+
--exclude=a,b Exclude patterns (e.g., --exclude=test,mock)
|
|
333
|
+
--in=<path> Only in path (e.g., --in=src/core)
|
|
334
|
+
--depth=N Trace/graph depth (default: 3, also expands all children)
|
|
335
|
+
--direction=X Graph direction: imports, importers, or both (default: both)
|
|
336
|
+
--all Expand truncated sections (about, trace, graph, related)
|
|
337
|
+
--top=N Limit results (find, deadcode)
|
|
338
|
+
--limit=N Limit result count (find, usages, search, deadcode, api, toc)
|
|
339
|
+
--max-files=N Max files to index (large projects)
|
|
340
|
+
--context=N Lines of context around matches
|
|
341
|
+
--json Machine-readable output
|
|
342
|
+
--code-only Filter out comments and strings
|
|
343
|
+
--with-types Include type definitions
|
|
344
|
+
--include-tests Include test files
|
|
345
|
+
--class-name=X Scope to specific class (e.g., --class-name=Repository)
|
|
346
|
+
--include-methods Include method calls (obj.fn) in caller/callee analysis
|
|
347
|
+
--include-uncertain Include ambiguous/uncertain matches
|
|
348
|
+
--show-confidence Show confidence scores per caller/callee edge
|
|
349
|
+
--min-confidence=N Filter edges below confidence threshold (0.0-1.0)
|
|
350
|
+
--include-exported Include exported symbols in deadcode
|
|
351
|
+
--no-regex Force plain text search (regex is default)
|
|
352
|
+
--functions Show per-function line counts (stats command)
|
|
353
|
+
--include-decorated Include decorated/annotated symbols in deadcode
|
|
354
|
+
--framework=X Filter entrypoints by framework (e.g., --framework=express,spring)
|
|
355
|
+
--exact Exact name match only (find)
|
|
356
|
+
--calls-only Only show call/test-case matches (tests)
|
|
357
|
+
--case-sensitive Case-sensitive text search (search)
|
|
358
|
+
--detailed List all symbols in toc (compact by default)
|
|
359
|
+
--top-level Show only top-level functions in toc
|
|
360
|
+
--max-lines=N Max source lines for class (large classes show summary)
|
|
361
|
+
--no-cache Disable caching
|
|
362
|
+
--clear-cache Clear cache before running
|
|
363
|
+
--base=<ref> Git ref for diff-impact (default: HEAD)
|
|
293
364
|
```
|
|
294
365
|
|
|
295
366
|
---
|
|
296
367
|
|
|
297
368
|
## Limitations
|
|
298
369
|
|
|
299
|
-
|
|
370
|
+
- Single-project scope - follows imports within the project, not into `node_modules` or `site-packages`
|
|
371
|
+
- No runtime execution - static analysis only
|
|
372
|
+
- Dynamic dispatch and reflection are only partially visible or invisible
|
|
373
|
+
- JS, TS, and Python method calls can be uncertain when receiver type is unknown
|
|
374
|
+
- Large repos take a few seconds on the first query, then use cache
|
|
300
375
|
|
|
301
|
-
|
|
302
|
-
- **Static analysis only** - Can't follow `eval()`, `getattr()`, reflection, or other dynamic dispatch.
|
|
303
|
-
- **Duck-typed methods** - `obj.method()` in JS/TS/Python is marked "uncertain" when the receiver type is ambiguous. Go/Rust/Java resolve with high confidence.
|
|
304
|
-
- **Single project scope** - Follows imports within the project but not into `node_modules` or `site-packages`.
|
|
305
|
-
- **First-query index time** - A few seconds on large projects. Cached incrementally after that.
|
|
376
|
+
If you need compiler diagnostics, taint analysis, or runtime semantics, those are different tools for different jobs. UCN trades that depth for speed, portability, and zero setup.
|
|
306
377
|
|
|
307
378
|
---
|
|
308
379
|
|
|
309
|
-
## License
|
|
310
|
-
|
|
311
380
|
MIT
|
package/cli/index.js
CHANGED
|
@@ -169,7 +169,9 @@ if (unknownFlags.length > 0) {
|
|
|
169
169
|
const VALUE_FLAGS = new Set([
|
|
170
170
|
'--file', '--depth', '--top', '--context', '--direction',
|
|
171
171
|
'--add-param', '--remove-param', '--rename-to', '--default',
|
|
172
|
-
'--base', '--exclude', '--not', '--in', '--max-lines', '--class-name'
|
|
172
|
+
'--base', '--exclude', '--not', '--in', '--max-lines', '--class-name',
|
|
173
|
+
'--type', '--param', '--receiver', '--returns', '--decorator',
|
|
174
|
+
'--limit', '--max-files', '--min-confidence', '--stack', '--framework'
|
|
173
175
|
]);
|
|
174
176
|
|
|
175
177
|
// Remove flags from args, then add args after -- (which are all positional)
|
|
@@ -1122,6 +1124,7 @@ Common Flags:
|
|
|
1122
1124
|
--code-only Filter out comments and strings
|
|
1123
1125
|
--with-types Include type definitions
|
|
1124
1126
|
--include-tests Include test files
|
|
1127
|
+
--class-name=X Scope to specific class (e.g., --class-name=Repository)
|
|
1125
1128
|
--include-methods Include method calls (obj.fn) in caller/callee analysis
|
|
1126
1129
|
--include-uncertain Include ambiguous/uncertain matches
|
|
1127
1130
|
--show-confidence Show confidence scores per caller/callee edge
|
|
@@ -1246,7 +1249,7 @@ Flags can be added per-command: context myFunc --include-methods
|
|
|
1246
1249
|
const tokens = input.split(/\s+/);
|
|
1247
1250
|
const command = tokens[0];
|
|
1248
1251
|
// Flags that take a space-separated value (--flag value)
|
|
1249
|
-
const valueFlagNames = new Set(['--file', '--in', '--base', '--add-param', '--remove-param', '--rename-to', '--default', '--depth', '--top', '--context', '--max-lines', '--direction', '--exclude', '--not', '--stack']);
|
|
1252
|
+
const valueFlagNames = new Set(['--file', '--in', '--base', '--add-param', '--remove-param', '--rename-to', '--default', '--depth', '--top', '--context', '--max-lines', '--direction', '--exclude', '--not', '--stack', '--type', '--param', '--receiver', '--returns', '--decorator', '--limit', '--max-files', '--min-confidence', '--class-name', '--framework']);
|
|
1250
1253
|
const flagTokens = [];
|
|
1251
1254
|
const argTokens = [];
|
|
1252
1255
|
const skipNext = new Set();
|
|
@@ -1456,7 +1459,7 @@ function executeInteractiveCommand(index, command, arg, iflags = {}, cache = nul
|
|
|
1456
1459
|
}
|
|
1457
1460
|
|
|
1458
1461
|
case 'graph': {
|
|
1459
|
-
const { ok, result, error } = execute(index, 'graph', { file: arg,
|
|
1462
|
+
const { ok, result, error } = execute(index, 'graph', { file: arg || iflags.file, direction: iflags.direction, depth: iflags.depth, all: iflags.all });
|
|
1460
1463
|
if (!ok) { console.log(error); return; }
|
|
1461
1464
|
const graphDepth = iflags.depth ? parseInt(iflags.depth) : 2;
|
|
1462
1465
|
console.log(output.formatGraph(result, { showAll: iflags.all || !!iflags.depth, maxDepth: graphDepth, file: arg }));
|
package/core/callers.js
CHANGED
|
@@ -456,6 +456,15 @@ function findCallers(index, name, options = {}) {
|
|
|
456
456
|
const receiverLower = call.receiver.toLowerCase();
|
|
457
457
|
const matchesTarget = [...targetTypes].some(cn => cn.toLowerCase() === receiverLower);
|
|
458
458
|
if (!matchesTarget) {
|
|
459
|
+
// Rust/Go path calls (Type::method() / pkg.Method()): receiver IS the type name
|
|
460
|
+
// If it doesn't match target, it's definitely a different type — filter it
|
|
461
|
+
if (call.isPathCall && /^[A-Z]/.test(call.receiver)) {
|
|
462
|
+
isUncertain = true;
|
|
463
|
+
if (!options.includeUncertain) {
|
|
464
|
+
if (stats) stats.uncertain = (stats.uncertain || 0) + 1;
|
|
465
|
+
continue;
|
|
466
|
+
}
|
|
467
|
+
}
|
|
459
468
|
const nonTargetClasses = new Set();
|
|
460
469
|
for (const d of definitions) {
|
|
461
470
|
const t = d.className || (d.receiver && d.receiver.replace(/^\*/, ''));
|
|
@@ -915,7 +924,8 @@ function findCallees(index, def, options = {}) {
|
|
|
915
924
|
callees.set(calleeKey, {
|
|
916
925
|
name: effectiveName,
|
|
917
926
|
bindingId: bindingResolved,
|
|
918
|
-
count: 1
|
|
927
|
+
count: 1,
|
|
928
|
+
...(call.isConstructor && { isConstructor: true })
|
|
919
929
|
});
|
|
920
930
|
}
|
|
921
931
|
}
|
|
@@ -1023,7 +1033,7 @@ function findCallees(index, def, options = {}) {
|
|
|
1023
1033
|
// Pre-compute import graph for callee confidence scoring
|
|
1024
1034
|
const callerImportSet = new Set(index.importGraph.get(def.file) || []);
|
|
1025
1035
|
|
|
1026
|
-
for (const { name: calleeName, bindingId, count } of callees.values()) {
|
|
1036
|
+
for (const { name: calleeName, bindingId, count, isConstructor } of callees.values()) {
|
|
1027
1037
|
const symbols = index.symbols.get(calleeName);
|
|
1028
1038
|
if (symbols && symbols.length > 0) {
|
|
1029
1039
|
let callee = symbols[0];
|
|
@@ -1122,7 +1132,8 @@ function findCallees(index, def, options = {}) {
|
|
|
1122
1132
|
if (!bindingId && NON_CALLABLE_TYPES.has(callee.type)) {
|
|
1123
1133
|
const isFuncField = callee.type === 'field' && callee.fieldType &&
|
|
1124
1134
|
/^func\b/.test(callee.fieldType);
|
|
1125
|
-
|
|
1135
|
+
// Constructor calls (new Foo()) are always callable regardless of type
|
|
1136
|
+
if (!isFuncField && !isConstructor) continue;
|
|
1126
1137
|
}
|
|
1127
1138
|
|
|
1128
1139
|
// Skip test-file callees when caller is production code and
|