ucn 5.2.1 → 5.3.0

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 CHANGED
@@ -10,11 +10,12 @@ If you work with AI Agents, add UCN as a [Skill or MCP tool](#ai-setup). One too
10
10
  gives the agent compact, source-linked answers to caller, impact, and test
11
11
  questions, with uncertainty labeled instead of guessed.
12
12
 
13
- Find symbols, trace callers, check impact, pick the right tests, extract exact
14
- source, and spot dead code - from your terminal or your AI agent.
13
+ <img src="assets/readme/ucn-connected-scope.gif" width="736" alt="Conceptual overview of UCN's relationship views, multi-hop exploration, visible uncertainty, index reuse, and refresh after edits. Nodes and timing are illustrative, not a captured query or benchmark.">
15
14
 
16
- Supports JavaScript, TypeScript, JSX/TSX, Python, Go, Rust, Java, C, C++, C#,
17
- and HTML inline scripts. All commands, one engine, three ways to use it:
15
+ Conceptual overview · [Still image](assets/readme/ucn-connected-scope.png)
16
+
17
+ Use the CLI directly, install the [agent skill](#agent-skill-no-server-needed),
18
+ or connect through [MCP](#mcp). One engine supplies all three:
18
19
 
19
20
  ```text
20
21
  Terminal AI Agents Agent Skills
@@ -29,440 +30,140 @@ and HTML inline scripts. All commands, one engine, three ways to use it:
29
30
  └─────────────┘
30
31
  ```
31
32
 
32
- Your tools can already find text. UCN finds *the function* - its definition,
33
- its callers, its blast radius, its tests - and tells you how sure it is. It
34
- parses code the way a compiler does (tree-sitter ASTs, not regex) and answers
35
- the questions you actually have: who calls this? what breaks if I change it?
36
- which tests should I run? is this dead?
37
-
38
- It's deliberately lightweight:
39
-
40
- - **No required background process** - the CLI parses on demand, answers, and
41
- exits. MCP stays warm only when you choose to run it.
42
- - **No HTTP or network stack** - MCP uses a local, dependency-free stdio
43
- transport; UCN never opens a port.
44
- - **No language servers, no compilation** - tree-sitter does the analysis
45
- without building the project.
46
- - **No config** - point it at a directory and ask.
47
-
48
- And it's built for auditable trust. grep hands you raw matches to sift
49
- yourself; UCN separates proven edges from possible ones, explains every
50
- exclusion, and reconciles every occurrence of the name it searched. It never
51
- turns a zero into a deletion claim. CI re-derives its answers from real
52
- compilers and language servers (ts-morph, Pyright, gopls, rust-analyzer,
53
- JDT LS, Roslyn, clangd) on pinned production repositories. See
54
- [Answers you can trust](#answers-you-can-trust).
55
-
56
- <img src="https://raw.githubusercontent.com/mleoca/ucn/main/assets/demo.svg" alt="ucn show on ripgrep: signature, 123 confirmed callers with evidence types, and the ACCOUNT line reconciling all 136 occurrences of the name" width="100%">
33
+ UCN uses tree-sitter abstract syntax trees (ASTs) for static code analysis,
34
+ without compiling the project or starting
35
+ a language server. The CLI runs on demand and reuses an incremental index;
36
+ MCP keeps a process available for repeated queries. No project configuration
37
+ is required, and the cache lives outside the repository.
57
38
 
58
- <sub>Real output: one `ucn show` on [ripgrep](https://github.com/BurntSushi/ripgrep) - signature, 123 proven callers with their evidence, and an account of every occurrence of the name. No files opened.</sub>
39
+ Supports JavaScript, TypeScript, JSX/TSX, Python, Go, Rust, Java, C, C++, C#,
40
+ and HTML inline scripts.
59
41
 
60
- ## Start here
42
+ ## Install
61
43
 
62
44
  ```bash
63
45
  npm install -g ucn # Node.js 20+
64
-
65
- cd your-project
66
- ucn repo # what is this codebase?
67
- ucn find handleRequest # exact definitions, stable handles
68
- ucn show src/server.ts:42:handleRequest # the full picture
69
- ucn trace src/server.ts:42:handleRequest --direction=callers
70
- ucn impact src/server.ts:42:handleRequest # every call site, with evidence
71
- ucn tests src/server.ts:42:handleRequest --depth=3 # which tests to run
72
- ```
73
-
74
- The first command builds an incremental index; the rest reuse it. The cache
75
- lives outside your project directory, so there's nothing to gitignore.
76
-
77
- ## Understand code you didn't write
78
-
79
- What does this function do, who calls it, and how sure is the answer?
80
- `ucn show` gathers everything useful about one symbol: signature, source,
81
- callers, callees, tests, types, dependencies, examples. Project it down to
82
- just the sections you need:
83
-
84
- ```text
85
- $ ucn show detectLanguage --sections=summary,callers,callees --compact
86
-
87
- SUMMARY
88
- ───────
89
- detectLanguage(filePath: string, projectRoot = null): string|null
90
- languages/index.js:420-428 (9 lines)
91
- handle: languages/index.js:420:detectLanguage
92
- "Detect language from file path"
93
- async: no | side_effects: [none] | complexity: branches=1, depth=1
94
-
95
- RELATIONSHIPS
96
- ─────────────
97
- CALLERS — CONFIRMED (51, 30 prod + 21 test):
98
- evidence: scope-match (all)
99
- [1] cli/index.js:604 [runFileCommand]: const language = detectLanguage(filePath);
100
- [7] core/build-worker.js:39 [processFile]: const language = detectLanguage(filePath, rootDir);
101
- [17] core/project.js:472 [build]: const language = detectLanguage(filePath, this.root);
102
- [34] test/parser-unit.test.js:19: assert.strictEqual(detectLanguage('file.js'), 'javascript');
103
- ... 47 more callers
104
-
105
- CALLEES (1):
106
- evidence: exact-binding (all)
107
- [52] detectHeaderLanguage {fs} - core/compilation-database.js:217
108
- CALLEES — UNVERIFIED (1) — call syntax, receiver/binding unresolved:
109
- toLowerCase ×1 — possible-dispatch L422
110
-
111
- ACCOUNT: "detectLanguage" occurs on 79 lines in 20 files: 51 confirmed, 0 unverified,
112
- 28 non-call (18 import, 1 definition, 3 reference, 6 other-text), 0 other-target, 0 unaccounted
113
- CONTRACT: literal-name text partition complete; semantic completeness is not claimed
114
- (aliases, indirect calls, generated code, and runtime dispatch may exist).
115
- ```
116
-
117
- `find` returns stable handles in `file:line:name` form. Pass a handle to any
118
- command to pin the answer to one definition, even when several files or classes
119
- reuse the same name.
120
-
121
- ## Follow the execution path
122
-
123
- What happens when `build()` runs?
124
-
125
- ```text
126
- $ ucn trace build --depth=2
127
-
128
- build
129
- ├── compareNames (core/discovery.js:293) [regular] 3x
130
- ├── recordDiscoveryIssue (core/project.js:346) 2x
131
- │ └── [unverified] push — method-ambiguous L351
132
- ├── detectProjectPattern (core/discovery.js:760) [utility] 1x
133
- ├── parseGitignore (core/discovery.js:253) [utility] 1x
134
- │ ├── gitignoreFiles (core/discovery.js:234) [utility] 1x
135
- │ ├── compareNames (core/discovery.js:293) [utility] 1x (see above)
136
- │ └── parseGitignoreFile (core/discovery.js:152) [utility] 1x
137
- ├── gitTrackedPaths (core/discovery.js:266) [utility] 1x
138
- │ ├── hasGitMetadata (core/discovery.js:224) [utility] 1x
139
- │ └── [unverified] dirname — method-ambiguous L281,L284
140
- └── ... more callees
141
-
142
- CALLEE ACCOUNT: 11 nodes expanded · 210 call sites = 31 confirmed + 33 unverified
143
- (25 method-ambiguous, 1 possible-dispatch, 7 uncertain-receiver) + 86 external/builtin + 60 excluded
144
- ```
145
-
146
- `trace` walks callees, callers, or callers all the way up to runtime entry
147
- points (`--direction=callers --to=entrypoints`). Proven edges form the tree;
148
- calls UCN can't prove a receiver for show up as `[unverified]` leaves with a
149
- reason. The account line reconciles every call site in the expanded tree, so
150
- unresolved dispatch stays visible and counted instead of quietly vanishing.
151
-
152
- ## Answers you can trust
153
-
154
- UCN doesn't turn every matching name into a semantic claim. Watch it work
155
- through a name with two definitions and a pile of ambiguous method calls:
156
-
157
- ```text
158
- $ ucn impact saveCache
159
-
160
- Impact analysis for saveCache
161
- core/cache.js:610
162
- Note: Found 2 definitions for "saveCache". Using core/cache.js:610. Also in: core/project.js:2380. Use file= to disambiguate.
163
- CALL SITES: 5 confirmed + 15 unverified
164
- Files affected: 3
165
- BY FILE:
166
- core/project.js:2380 [saveCache]: saveCache(cachePath) { return indexCache.saveCache(this, cachePath); }
167
- test/prerelease-audit.test.js:1493: saveCache(built, cacheFile);
168
- ... (3 more)
169
- UNVERIFIED CALL SITES (15) — call syntax, no binding/receiver evidence:
170
- mcp/server.js:517: try { index.saveCache(); } catch (_) { /* best-effort */ } (possible-dispatch via local receiver)
171
- test/cache.test.js:124: index.saveCache(); (possible-dispatch via local receiver)
172
- (+13 more)
173
- ACCOUNT: "saveCache" occurs on 68 lines in 11 files: 5 confirmed, 15 unverified,
174
- 12 non-call (3 import, 1 definition, 1 reference, 7 other-text), 36 other-target, 0 unaccounted
175
- CONTRACT: literal-name text partition complete; semantic completeness is not claimed
176
- (aliases, indirect calls, generated code, and runtime dispatch may exist).
177
- ```
178
-
179
- UCN sorted all 68 places the name appears:
180
-
181
- - **5 confirmed** - call sites it can *prove* resolve to this `saveCache`,
182
- via a binding, import, receiver type, qualified path, or same-class evidence.
183
- - **15 unverified** - real call syntax it refuses to claim. `index.saveCache()`
184
- sits on an untyped receiver, so the site stays visible with its reason
185
- (`possible-dispatch via local receiver`) instead of being guessed or dropped.
186
- - **36 other-target** - occurrences that belong to the *other* `saveCache`,
187
- kept out of the answer instead of quietly inflating it.
188
- - **12 non-call** - imports, the definition, comments, strings.
189
- - **0 unaccounted** - every observed line landed in exactly one bucket.
190
-
191
- That's the payoff: an answer you (or your agent) can audit, instead of an
192
- opaque match count. A confirmed edge is evidence about the pinned target. An
193
- unverified edge is a review item with a stated reason. And a clean zero is an
194
- *observed-text* zero, not a safe-to-delete claim: aliases, generated code,
195
- reflection, runtime registration, and external consumers can live beyond the
196
- indexed evidence, and `ucn repo --sections=health --deep` reports exactly those
197
- blind spots. Even when output is truncated to fit an agent's budget, the
198
- ACCOUNT, CONTRACT, and WARNING lines survive the cut.
199
-
200
- ### Measured against ground truth
201
-
202
- Don't take the tiers on faith. Release gates re-derive UCN's answers from real
203
- compilers and language servers on a ten-repository board of pinned production
204
- codebases, and publishing is blocked unless they pass. The latest full
205
- release-board run (2026-08-24):
206
-
207
- | Repository | Pinned commit | Oracle | Caller precision | Caller recall | Callee prec / recall | Command checks |
208
- |---|---|---|---:|---:|---:|---:|
209
- | [preact-signals](https://github.com/preactjs/signals) | [`e0ce9fdf`](https://github.com/preactjs/signals/commit/e0ce9fdf92df7f0ece2c89d44554c39f36dc6882) | ts-morph | 100% | 100% | 100% / 100% | 100% |
210
- | [httpx](https://github.com/encode/httpx) | [`b5addb64`](https://github.com/encode/httpx/commit/b5addb64f0161ff6bfe94c124ef76f6a1fba5254) | Pyright | 100% | 100% | 100% / 100% | 100% |
211
- | [cobra](https://github.com/spf13/cobra) | [`ad460ea8`](https://github.com/spf13/cobra/commit/ad460ea8f249db69c943a365fb84f3a59042d54e) | gopls | 100% | 100% | 100% / 100% | 100% |
212
- | [viper](https://github.com/spf13/viper) | [`528f7416`](https://github.com/spf13/viper/commit/528f7416c4b56a4948673984b190bf8713f0c3c4) | gopls | 100% | 100% | 100% / 100% | 100% |
213
- | [ripgrep](https://github.com/BurntSushi/ripgrep) | [`82313cf9`](https://github.com/BurntSushi/ripgrep/commit/82313cf95849bfe425109ad9506a52154879b1b1) | rust-analyzer | 100% | 100% | 100% / 100% | 100% |
214
- | [clap](https://github.com/clap-rs/clap) | [`d3e59a9a`](https://github.com/clap-rs/clap/commit/d3e59a9ab214910b9dad02921b7ef42c6400de9b) | rust-analyzer | 100% | 100% | 100% / 100% | 100% |
215
- | [javapoet](https://github.com/square/javapoet) | [`b9017a95`](https://github.com/square/javapoet/commit/b9017a9503b76e11b4ad4c1a9f050e2d29112cb0) | JDT LS | 100% | 100% | 100% / 100% | 100% |
216
- | [newtonsoft-json](https://github.com/JamesNK/Newtonsoft.Json) | [`4f73e743`](https://github.com/JamesNK/Newtonsoft.Json/commit/4f73e74372445108d2c1bda37b36e6f5e43402e0) | Roslyn | 100% | 100% | 100% / 100% | 100% |
217
- | [cjson](https://github.com/DaveGamble/cJSON) | [`c859b25d`](https://github.com/DaveGamble/cJSON/commit/c859b25da02955fef659d658b8f324b5cde87be3) | clangd | 100% | 100% | 100% / 100% | 100% |
218
- | [fmt](https://github.com/fmtlib/fmt) | [`e424e3f2`](https://github.com/fmtlib/fmt/commit/e424e3f2e607da02742f73db84873b8084fc714c) | clangd | 100% | 100% | 100% / 100% | 100% |
219
-
220
- On the same run: **zero** in-scope oracle call edges missing from the answer
221
- (the release gate) on every repository, **zero** false-dead `deadcode` claims
222
- in the oracle-visible sample, **8,000 / 8,000** cross-command consistency
223
- comparisons in agreement, **10 / 10** repositories inside the performance
224
- budget (slowest normalized median cold build 17.4K lines/second by wall time,
225
- worst query p95 83.0 ms, highest peak RSS 908.5 MB), and 3,609 automated tests with no
226
- failures or skips. The same gates run in CI (the scheduled
227
- [Eval workflow](https://github.com/mleoca/ucn/actions/workflows/eval.yml) and
228
- every release tag), and `npm run trust:gate` reproduces the release board
229
- locally. Pinned sources: [`eval/lib/repos.js`](eval/lib/repos.js).
230
-
231
- Semantic runs draw a deterministic, reference-stratified sample of up to 50
232
- compiler/LSP symbols per repository, then check caller identity, callee
233
- identity, account conservation, review burden, and the public commands `find`,
234
- `show`, `source`, `trace`, `impact`, `usages`, and `tests` against that same
235
- external population. Unverified precision is reported separately and is
236
- intentionally much lower on dispatch-heavy code: those entries are review
237
- candidates, never confirmed claims.
238
-
239
- Beyond the publish gate, a scheduled board re-checks 24 pinned repositories
240
- across every supported oracle language (zod, express, hono, zustand, fastify,
241
- rich, click, attrs, grpc-go, chi, cursive, itertools, gson, jsoup, and
242
- friends), plus a rotating fresh-repo arm of codebases the engine was never
243
- tuned on. Repositories that
244
- expose a gap stay on the board; they don't get removed to keep a table pretty.
245
- These are measured results on pinned code, not a claim of universal program
246
- understanding or identical performance on every machine.
247
-
248
- ## Change code without breaking things
249
-
250
- Will this change break a call site you've never seen? Check before you edit:
251
-
252
- ```text
253
- $ ucn check expandGlob
254
-
255
- Verification: expandGlob
256
- ════════════════════════════════════════════════════════════
257
- core/discovery.js:314
258
- expandGlob (pattern: string, options: number = {}) : string[]
259
-
260
- Expected arguments: 1-2
261
-
262
- STATUS: ✓ All calls valid
263
- Total calls: 7
264
- Valid: 7
265
- Mismatches: 0
266
- Uncertain: 0
267
- Patterns: 4 in try, 4 in callback
268
-
269
- ACCOUNT: "expandGlob" occurs on 14 lines in 6 files: 7 confirmed, 0 unverified,
270
- 7 non-call (4 import, 1 definition, 2 reference, 0 other-text), 0 other-target, 0 unaccounted
271
- ```
272
-
273
- The `Patterns:` line classifies call-site structure (`inLoop`, `inTry`,
274
- `inCallback`, `awaited`) so risky sites stand out. Then preview the refactor.
275
- UCN shows exactly what would need to change and where:
276
-
277
- ```text
278
- $ ucn plan expandGlob --rename-to=expandGlobPattern
279
-
280
- Refactoring plan: rename
281
- ════════════════════════════════════════════════════════════
282
- core/discovery.js:314
283
-
284
- SIGNATURE CHANGE:
285
- Before: expandGlob (pattern: string, options: number = {}) : string[]
286
- After: expandGlobPattern (pattern: string, options: number = {}) : string[]
287
-
288
- CHANGES NEEDED: 12
289
- Files affected: 5
290
- Definition 1, calls 7, references 0, text dependencies 0, imports 4, exports 0; manual review items 0
291
-
292
- BY FILE:
293
-
294
- cli/index.js (2 changes)
295
- :771 [call]
296
- const files = expandGlob(pattern);
297
- → Rename to: const files = expandGlobPattern(pattern);
298
- :15 [import]
299
- const { expandGlob, findProjectRoot } = require('../core/discovery');
300
- → Update import: const { expandGlobPattern, findProjectRoot } = require('../core/discovery');
301
-
302
- ... (more changes in core/discovery.js, core/cache.js, core/project.js, test/integration.test.js)
303
46
  ```
304
47
 
305
- For a rename, `plan` closes the change over every relationship the index can
306
- prove: overload/signature groups, base and override declarations, Rust trait
307
- slots, Go interface slots and their satisfiers, exact call and value-reference
308
- tokens, imports/exports, Python `__all__` strings, and module-attribute
309
- references. Accessor renames also follow receiver-proven property reads and
310
- writes. It edits exact token or expression spans, so another same-named call
311
- on the same line is not swept up accidentally.
312
-
313
- Open external interfaces, incomplete ownership, unresolved dispatch, or an
314
- inexact token are marked `needsReview` instead of receiving a synthesized
315
- edit. Comments and strings in indexed source appear as separate review items
316
- and are never rewritten automatically; documentation, configuration,
317
- generated files, and unsupported languages get an explicit exact-text search
318
- handoff. `plan` previews changes; it does not modify files or replace the
319
- compiler and test suite. Before committing, point the same machinery at your
320
- Git diff:
48
+ ## In the shell
321
49
 
322
- ```bash
323
- ucn impact --staged # what did I change, and who depends on it?
324
- ucn check --staged # signature drift, orphaned functions, tests to run
325
- ```
326
-
327
- ## Pick the right tests
328
-
329
- Which tests actually exercise this function, directly or three hops away?
330
-
331
- ```text
332
- $ ucn tests expandGlob --depth=3
333
-
334
- affected-tests: expandGlob
335
- ════════════════════════════════════════════════════════════
336
- core/discovery.js:314
337
- 1 function changed → 12 functions affected (depth 3)
338
-
339
- Test files to run (30):
340
-
341
- test/integration.test.js (links: expandGlob, build, idx, setupProject)
342
- L169: const files = expandGlob('**/*.go', { root: tmpDir }); [call]
343
- test/prerelease-audit.test.js (links: isCacheStale, runInteractive, build, idx)
344
- L39: const index = idx(dir); [call]
345
- ...
346
-
347
- Summary: 12 affected → 30 statically linked test files, 5/12 functions linked (42%) · 1 possibly affected (unverified chains)
348
- ```
349
-
350
- `tests` reports static call/reference linkage, not runtime coverage. Functions
351
- reached only through unverified edges are listed separately as *possibly
352
- affected*, and empty results warn about subprocess tests, reflection, and
353
- external harnesses that may still exercise the target.
354
-
355
- ## Get the lay of the land
356
-
357
- One command answers "what is this codebase?" Here it is on ripgrep:
358
-
359
- ```text
360
- $ ucn repo
361
-
362
- PROJECT ORIENTATION — ripgrep
363
- ════════════════════════════════════════════════════════════
364
- 100 files · 4755 symbols · language mix by symbols: rust 100%
365
-
366
- TOP DIRS (by symbols):
367
- crates/core/flags 1510 symbols · 6 file(s)
368
- crates/printer/src 677 symbols · 11 file(s)
369
- crates/ignore/src 607 symbols · 8 file(s)
370
- crates/globset/src 331 symbols · 5 file(s)
371
-
372
- HOT (most-called production functions, top 8 of 2238 raw candidates):
373
- parse_low_raw — 545 call(s) · crates/core/flags/parse.rs:139
374
- SearcherBuilder.build — 123 call(s) · crates/searcher/src/searcher/mod.rs:315
375
- Searcher.search_reader — 123 call(s) · crates/searcher/src/searcher/mod.rs:727
376
- RegexMatcher.new — 100 call(s) · crates/regex/src/matcher.rs:385
377
- ...
378
-
379
- ENTRY POINTS: 426 — test 421, runtime 5
380
- TRUST: PARTIAL — 48 glob import(s), 5 unsupported source file(s) (ucn repo --sections=health --deep for detail)
381
- SKIPPED SOURCE: 5 file(s) (Shell 4, Ruby 1) — use grep/ripgrep plus a language-native analyzer.
382
-
383
- Next: ucn show parse_low_raw · ucn repo --sections=files --detailed · ucn repo --sections=health --deep
384
- ```
385
-
386
- Size, layout, hot spots, entry points, and an honest trust line. Note the
387
- `SKIPPED SOURCE` handoff: when a repo mixes in languages UCN can't parse, it
388
- says so and points you at the right tool, instead of presenting a clean-looking
389
- answer over a partial index.
390
-
391
- ## Find dead code you can act on
392
-
393
- ```text
394
- $ ucn deadcode --exclude=test # run on ripgrep
395
-
396
- Dead code: 3 unused symbol(s)
397
-
398
- crates/globset/src/serde_impl.rs
399
- [ 38- 42] Glob.deserialize (method)
400
- [ 70- 74] GlobSet.deserialize (method)
401
- crates/matcher/src/lib.rs
402
- [ 397- 399] Captures.as_match (method)
403
-
404
- 33 decorated/annotated symbol(s) hidden (framework-registered). Use --include-decorated to include them.
405
-
406
- 903 exported symbol(s) excluded from the audit (public API may have external callers). Use --include-exported to audit them.
407
-
408
- WARNING: source coverage is incomplete (5 unsupported-language); 17 candidate name(s) found in skipped source were suppressed.
409
- ```
410
-
411
- Three claims, and every one is re-checked against rust-analyzer in CI: a
412
- default-audit claim with an oracle-visible reference fails the build. Notice
413
- what it *didn't* claim: exported API that external code may call,
414
- framework-registered symbols, and anything whose name appears in files UCN
415
- couldn't parse. A literal reflection target such as `getattr(obj, "run")`
416
- also withholds matching member names from deletion candidates; recognized
417
- dynamic reflection is counted and warned because it cannot be attributed.
418
- `deadcode` is deliberately a candidate generator. Before
419
- deleting, corroborate with `usages`, `impact`, `api`, and your compiler and
420
- tests.
421
-
422
- For missing-await bugs, `ucn audit-async` lists async calls inside async
423
- functions that lack `await` (JS/TS/Python).
424
-
425
- ## Map dependencies and API surfaces
50
+ From a project directory:
426
51
 
427
52
  ```bash
428
- ucn deps src/server.ts --direction=imports --detailed
429
- ucn deps src/server.ts --direction=importers --depth=3
430
- ucn deps --cycles # circular imports
431
- ucn api # public surface of the project
432
- ucn entrypoints --type=http # runtime and framework roots
433
- ucn endpoints --bridge --unmatched # server routes with no client, and vice versa
53
+ ucn repo
54
+ ucn show handleRequest --lines
55
+ ucn source handleRequest --raw
56
+ ucn impact --staged --lines
57
+ ucn check --staged
434
58
  ```
435
59
 
436
- Cycle output separates eager import-time loops from Python chains containing
437
- a function-local/deferred edge. Deferred chains stay visible—they are not
438
- unconditional import-time cycles, but can still fail if invoked while modules
439
- are initializing.
440
-
441
- `endpoints --bridge` matches server routes to client requests across
442
- languages: Express/Fastify/Koa/NestJS/Next.js, Flask/FastAPI, Spring/JAX-RS,
443
- Go net/http (Gin/Echo/Chi/Fiber), axum/actix-web, and ASP.NET on the server
444
- side; fetch/axios, requests/httpx, RestTemplate/WebClient, reqwest, and .NET
445
- HttpClient on the client side. Exact, partial, and uncertain matches stay in
446
- separate tiers.
447
-
448
- ## Extract and search without opening whole files
60
+ `repo` maps the project. `show --lines` locates callers, `source --raw`
61
+ retrieves the implementation, and `impact` and `check` inspect a staged change.
62
+ When a name is ambiguous, `find` returns a `file:line:name` handle that
63
+ subsequent commands accept.
64
+
65
+ `--lines` returns `path:line:text` records; `--raw` returns source code.
66
+ Both fit an agent's existing scripts without parsing a human-readable report.
67
+
68
+ Text search remains useful for comments, configuration, strings, and code
69
+ outside the supported languages. `usages` provides the literal-name inventory
70
+ when the task needs every occurrence, including those that are not calls.
71
+
72
+ ## Code navigation and change analysis
73
+
74
+ `show` gathers a symbol's signature, source, callers, callees, and related
75
+ context. Select the sections you need or set an output budget to keep the
76
+ answer focused. `trace` follows the call graph across files, down into callees
77
+ or up through callers toward entry points. Unverified relationships remain
78
+ visible, and the tree's accounting reports where exploration stopped.
79
+
80
+ `impact` connects a symbol or Git diff to its callers. `tests` follows indexed
81
+ call and reference paths to identify statically linked tests, including links
82
+ several hops away. `plan` previews a rename or signature change with source
83
+ locations and review items; it does not edit files. Together these commands
84
+ support code exploration, refactoring, and change review from a terminal or
85
+ an AI agent.
86
+
87
+ ## What an answer establishes
88
+
89
+ For caller answers, UCN checks bindings, imports, receiver types, and ownership
90
+ to distinguish calls to the selected definition from other uses of its name.
91
+ Calls without enough evidence stay visible as unverified, with a reason.
92
+ A matching method name alone does not establish which implementation runs;
93
+ receiver and ownership evidence determine how the candidate is classified.
94
+
95
+ In ripgrep at [`82313cf9`](https://github.com/BurntSushi/ripgrep/commit/82313cf95849bfe425109ad9506a52154879b1b1),
96
+ the selected `file_name` helper has four confirmed call sites and one unverified
97
+ candidate. This animation accounts for all 29 matching lines, alongside import
98
+ relationships and same-name definitions drawn from UCN's output.
99
+
100
+ <a href="assets/readme/ucn-evidence-graph.gif"><img src="assets/readme/ucn-evidence-graph.gif" width="736" alt="Captured ripgrep findings for file_name: 29 matching lines in six files, partitioned into 4 confirmed calls, 1 unverified candidate, 17 non-call lines, 7 other-target lines, and 0 unaccounted. The graph shows selected import relationships and the distinct definitions; motion is illustrative."></a>
101
+
102
+ Captured findings · [Still image](assets/readme/ucn-evidence-graph.png) · [Data](assets/readme/ucn-evidence-graph.json)
103
+
104
+ The ACCOUNT line reconciles the observed name occurrences: confirmed calls,
105
+ unverified candidates, non-call occurrences, and matches attributed to another
106
+ target. CONTRACT describes the scope of that accounting. Warnings identify
107
+ source the index could not cover. These details survive text truncation for
108
+ an agent's output budget.
109
+
110
+ An empty result therefore means something specific about the inspected code.
111
+ It cannot establish that reflection, generated code, runtime registration, or
112
+ external consumers never reach a symbol. `deadcode` supplies candidates to
113
+ investigate; deletion still needs corroboration. A refactor preview still
114
+ needs the compiler and tests.
115
+
116
+ ## Accuracy and validation
117
+
118
+ Release gates compare UCN's answers with independent compilers and language
119
+ servers on a ten-repository board of pinned production codebases. The local
120
+ September 6, 2026 evaluation recorded these sampled caller results:
121
+
122
+ | Repository | Pinned commit | Oracle | Symbols sampled | Confirmed precision | In-scope recall |
123
+ |---|---|---|---:|---:|---:|
124
+ | preact-signals | `e0ce9fdf` | ts-morph | 27 | 100% | 100% |
125
+ | httpx | `b5addb64` | Pyright | 50 | 100% | 100% |
126
+ | cobra | `ad460ea8` | gopls | 50 | 100% | 100% |
127
+ | viper | `528f7416` | gopls | 50 | 100% | 100% |
128
+ | ripgrep | `82313cf9` | rust-analyzer | 41 | 100% | 100% |
129
+ | clap | `d3e59a9a` | rust-analyzer | 50 | 100% | 100% |
130
+ | javapoet | `b9017a95` | JDT LS | 50 | 100% | 100% |
131
+ | newtonsoft-json | `4f73e743` | Roslyn | 50 | 100% | 100% |
132
+ | cjson | `c859b25d` | clangd | 50 | 100% | 100% |
133
+ | fmt | `e424e3f2` | clangd | 50 | 100% | 100% |
134
+
135
+ That evaluation reported zero missing in-scope oracle edges in both caller
136
+ and callee answers, and 8,000 cross-command comparisons with zero disagreements.
137
+ The default dead-code audit found zero false-dead results among 13 scored
138
+ claims; 13 additional claims could not be pinned by the oracle and were unscored.
139
+ All ten repositories passed the performance budgets, with steady-state query
140
+ p95 from 4.5 to 76.2 ms. Those timings exclude process startup and indexing;
141
+ cold builds and cache loading are measured separately.
142
+
143
+ The samples are deterministic and stratified by reference activity. Confirmed
144
+ precision applies to scored claims; recall counts in-scope oracle edges found
145
+ in either the confirmed or unverified band. Unverified candidates, oracle
146
+ abstentions, and unscored findings remain separate. These measurements do not
147
+ establish complete runtime knowledge or identical performance on every machine.
148
+
149
+ The scheduled board covers 24 pinned repositories: the ten above plus zod,
150
+ express, hono, zustand, fastify, rich, click, attrs, grpc-go, chi, cursive,
151
+ itertools, gson, and jsoup. A rotating fresh-repository arm checks codebases
152
+ outside that pinned board.
153
+
154
+ The [repository manifest](eval/lib/repos.js) records the full commits.
155
+ The [Publish workflow](https://github.com/mleoca/ucn/actions/workflows/publish.yml)
156
+ gates releases, and the [Eval workflow](https://github.com/mleoca/ucn/actions/workflows/eval.yml)
157
+ runs the checks on schedule and on demand. Their run pages provide CI results
158
+ and evaluation artifacts. Reproduce the checks locally with the oracle
159
+ dependencies installed:
449
160
 
450
161
  ```bash
451
- ucn source core/discovery.js:314:expandGlob # exactly one function
452
- ucn source core/discovery.js --range=314-364 # exactly one range
453
- ucn search '$scope.$apply' # literal by default
454
- ucn search 'TODO|FIXME' --regex # regex is explicit
455
- ucn search --type=call --receiver=client # structural search
456
- ucn usages expandGlob --include-tests # every occurrence, classified
162
+ npm run verify
163
+ npm run trust:gate
457
164
  ```
458
165
 
459
- `usages` is the escape hatch: the complete literal-name inventory (calls,
460
- definitions, imports, references, comments, strings), for when you want
461
- everything the text contains, not just what the engine can prove. Regex search runs on an
462
- RE2-compatible linear-time engine; hostile nested repetition is rejected up
463
- front instead of hanging your terminal.
464
-
465
- ## The 18 commands
166
+ ## Commands
466
167
 
467
168
  | Task | Command |
468
169
  |---|---|
@@ -485,100 +186,32 @@ front instead of hanging your terminal.
485
186
  | Likely missing awaits | `audit-async` |
486
187
  | Stack-trace frame resolution | `stacktrace <text>` |
487
188
 
488
- Run `ucn --help` for every flag. Related modes live behind parameters rather
489
- than extra verbs: `trace` handles down, up, and to-entry-points;
490
- `impact`/`check` handle a symbol or the current Git diff; `show` projects any
491
- subset of sections. Flags that don't apply to a command produce an explicit
492
- warning instead of silently changing the task.
189
+ `deps --cycles` groups circular dependencies and distinguishes eager imports
190
+ from deferred or type-only edges. Enumeration limits are disclosed. `repo`
191
+ reports source coverage as well as project structure; its quick HOT ranking
192
+ has a disclosed refinement budget, and `repo --sections=stats --hot` requests
193
+ the exact ranking.
493
194
 
494
- ## Same engine, different transport
195
+ `endpoints --bridge` matches server routes and client requests recognized by
196
+ its framework extractors. `plan` handles code relationships
197
+ such as imports, overrides, and interface or trait methods when ownership is
198
+ resolved; ambiguous relationships remain review items.
495
199
 
496
- CLI, MCP, file mode, project mode, glob mode, and interactive mode resolve
497
- commands through the same registry, handlers, index, cache, and formatters:
498
- same answers everywhere, different delivery.
200
+ Run `ucn --help` for flags, or use the
201
+ [command reference](.claude/skills/ucn/references/commands.md).
499
202
 
500
- - The CLI prints readable text; `--json` returns a stable machine envelope.
501
- - MCP exposes exactly one tool named `ucn`. Its `command` enum lists the 18
502
- tasks, snake_cased where needed (`audit_async`, `project_dir`, `class_name`).
503
- A persistent MCP process keeps the index warm across calls.
504
- - Targeted text answers default to a 10K-character budget, broad ones to 3K,
505
- ceiling 100K (`--max-chars` / `max_chars`). Truncation preserves ACCOUNT,
506
- CONTRACT, and WARNING lines; JSON is never text-truncated.
203
+ ## Shell output
507
204
 
508
- ```json
509
- {
510
- "meta": { "command": "audit-async", "canonicalCommand": "auditAsync", "ok": true, "contract": {} },
511
- "data": {}
512
- }
513
- ```
205
+ Records and code go to stdout; accounting and notes go to stderr. Unverified
206
+ records carry a tab-separated reason. An empty listing exits 1, an error
207
+ exits 2, and a successful listing exits 0. `--lines` supports `find`, `show`,
208
+ `usages`, `search`, and `impact`; `show --lines` lists callers by default.
209
+ Use `--json` when the script needs structured fields or a tree result.
514
210
 
515
- Failures keep the envelope: `meta.ok: false`, `data: null`, and an `error`
516
- string, with the command contract when known.
517
-
518
- ## A cache that stays out of your project
519
-
520
- The incremental index lives under your user cache root, not in the repo:
521
- `UCN_CACHE_DIR` if set, else `$XDG_CACHE_HOME/ucn`, `~/Library/Caches/ucn`
522
- (macOS), `%LOCALAPPDATA%/ucn/cache` (Windows), or `~/.cache/ucn`. Canonical
523
- path hashes keep same-named checkouts separate. `--no-cache` bypasses,
524
- `--clear-cache` clears the current project, `--clear-cache --all` clears every
525
- bounded UCN cache. Old in-project `.ucn-cache` directories are migrated out
526
- automatically on first use.
527
-
528
- ## Language coverage
529
-
530
- All parsers feed the same versioned language IR and index path, and sequential
531
- and worker builds are tested to produce identical symbols, calls, imports, and
532
- evidence.
533
-
534
- - **JavaScript / TypeScript / JSX / TSX** - functions, classes,
535
- imports/exports, typed receivers, aliases, callbacks, async flow, framework
536
- roots.
537
- - **Python** - functions, classes, annotations, decorators, imports,
538
- comprehensions, context-manager bindings, async flow, framework roots.
539
- - **Go, Rust, Java** - nominal receivers, methods,
540
- inheritance/traits/interfaces, package and path ownership, overload/arity
541
- discipline, framework roots.
542
- - **C** - functions, structs, macros, includes, calls, entry points, API
543
- analysis.
544
- - **C++** - C coverage plus classes, methods, constructors, inheritance,
545
- namespaces, overloads, templates, typed field receivers, static array-shape
546
- selection, and macro requalification. Conditional macro disagreement stays
547
- visible as unverified.
548
- - **C#** - namespaces, classes/interfaces/records, fields/properties,
549
- attributes, declared property/field receiver types, overload and hiding
550
- discipline, async flow, top-level programs, .NET stack frames, and
551
- ASP.NET/HttpClient endpoints.
552
- - **HTML** - inline JavaScript and `on*` event handlers.
553
-
554
- For C and C++, a `compile_commands.json` improves header-language,
555
- include-path, and ownership context when available. UCN keeps AST-proven
556
- definitions from recoverable preprocessor branches without claiming which
557
- branch a particular build activates.
558
-
559
- ## Testing and reliability
560
-
561
- - **Regression discipline** - every fixed defect gets a focused test.
562
- - **Surface coverage** - all 18 commands run through CLI text, CLI JSON, and
563
- MCP; parity between them is guarded by architecture tests.
564
- - **External ground truth** - real compilers and language servers adjudicate
565
- caller, callee, command, and dead-code claims on pinned repositories
566
- ([see the board](#measured-against-ground-truth)).
567
- - **Release-blocking budgets** - publishing requires 100% in-scope semantic
568
- recall, ≥98% confirmed precision, a conserved account for every sample, zero
569
- cross-command disagreements, zero default-arm false-dead claims, and the
570
- performance gate (≥10K lines/second cold build by wall time and ≥3K by CPU
571
- time, query p50 ≤75 ms, p95 ≤250 ms, bounded peak RSS), all on the actual
572
- release board.
573
-
574
- ```bash
575
- npm run verify # lint + full test suite
576
- npm run trust:gate # the release board: semantic, dead-code, consistency, performance
577
- ```
578
-
579
- Gate runs write their reports under `eval/reports/` as local run artifacts;
580
- the pinned manifest is [`eval/lib/repos.js`](eval/lib/repos.js). Before a tag,
581
- the Eval workflow's pre-tag dry run must pass on the actual CI runner.
211
+ Listings have no default row cap. Explicit limits disclose what they omit,
212
+ and a shell-mode character budget fails before writing partial output.
213
+ `source --raw` extracts complete functions and classes unless an explicit
214
+ line limit is requested; any resulting truncation is reported on stderr.
582
215
 
583
216
  ## AI setup
584
217
 
@@ -657,28 +290,20 @@ The skill teaches an agent how to orient, pin symbols, choose the smallest
657
290
  useful command, interpret the evidence tiers, and recover from incomplete
658
291
  answers. It's guidance over the same engine, not a second implementation.
659
292
 
660
- ## Limitations
661
-
662
- - Static, single-project analysis - dependencies like `node_modules` and
663
- `site-packages` aren't indexed, and nothing is executed.
664
- - Reflection, generated code, runtime registration, dynamic property access,
665
- and external consumers can be invisible. UCN reports these blind spots
666
- (`repo --sections=health --deep`) rather than pretending they don't exist.
667
- - Interface, trait, template, overload, and untyped-receiver dispatch may stay
668
- in the UNVERIFIED tier with a reason instead of being guessed. When
669
- same-name definitions compete, `show` lists their stable handles once so
670
- agents can see exactly what needs disambiguation.
671
- - C/C++ analysis doesn't run the preprocessor or compiler; build-specific
672
- branches, advanced templates, and macro expansion can remain unresolved. C#
673
- analysis doesn't run Roslyn; source generators and external assembly
674
- semantics stay outside the index.
675
- - HTML has regression coverage but no compiler/LSP real-repository oracle.
676
- - Large repos take a few seconds on the first query, then use the cache.
677
-
678
- If a decision needs compiler completeness or runtime truth, use the compiler,
679
- the type checker, the test runner, or a profiler. Those are different tools
680
- for different jobs. UCN's job is getting you to the right code fast, with
681
- answers you can audit.
293
+ ## Scope
294
+
295
+ UCN analyzes indexed source in one project. It does not execute the program
296
+ or index installed dependencies such as `node_modules` and `site-packages`.
297
+ `repo --sections=health --deep` reports source coverage and known analysis
298
+ limits.
299
+
300
+ C and C++ can use `compile_commands.json` for include paths and header context,
301
+ but UCN does not run the preprocessor or reproduce a compiler's build-specific
302
+ view. C# source generators and external assemblies are also outside the
303
+ index. HTML has regression coverage but no compiler/LSP repository oracle.
304
+
305
+ The CLI, MCP, and skill share the same resolution rules and evidence. Changing
306
+ the transport does not change what the engine knows about the code.
682
307
 
683
308
  ---
684
309