ucn 4.2.2 → 5.0.1

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.
Files changed (72) hide show
  1. package/.claude/skills/ucn/SKILL.md +89 -77
  2. package/.claude/skills/ucn/references/commands.md +62 -68
  3. package/.claude/skills/ucn/references/trust-contract.md +31 -6
  4. package/README.md +445 -300
  5. package/assets/demo.svg +31 -0
  6. package/cli/index.js +430 -1385
  7. package/core/account.js +144 -34
  8. package/core/analysis.js +182 -72
  9. package/core/ast-analysis.js +279 -0
  10. package/core/bridge.js +205 -24
  11. package/core/brief.js +27 -58
  12. package/core/build-worker.js +21 -131
  13. package/core/cache.js +533 -11
  14. package/core/callers.js +5533 -494
  15. package/core/check.js +13 -4
  16. package/core/command-contracts.js +402 -0
  17. package/core/compilation-database.js +276 -0
  18. package/core/confidence.js +4 -1
  19. package/core/deadcode.js +421 -20
  20. package/core/discovery.js +359 -46
  21. package/core/entrypoints.js +204 -42
  22. package/core/execute.js +887 -81
  23. package/core/graph-build.js +162 -7
  24. package/core/graph.js +53 -77
  25. package/core/imports.js +65 -6
  26. package/core/index-ir.js +138 -0
  27. package/core/ir.js +195 -0
  28. package/core/output/analysis.js +216 -22
  29. package/core/output/brief.js +23 -0
  30. package/core/output/check.js +4 -0
  31. package/core/output/doctor.js +37 -6
  32. package/core/output/endpoints.js +5 -2
  33. package/core/output/extraction.js +24 -12
  34. package/core/output/find.js +141 -36
  35. package/core/output/graph.js +11 -5
  36. package/core/output/public.js +462 -0
  37. package/core/output/refactoring.js +42 -10
  38. package/core/output/reporting.js +97 -20
  39. package/core/output/search.js +24 -16
  40. package/core/output/shared.js +22 -1
  41. package/core/output/tracing.js +30 -15
  42. package/core/output-budget.js +295 -0
  43. package/core/output.js +1 -0
  44. package/core/parallel-build.js +44 -11
  45. package/core/parser.js +3 -3
  46. package/core/project.js +384 -177
  47. package/core/public-command.js +47 -0
  48. package/core/registry.js +247 -117
  49. package/core/reporting.js +312 -290
  50. package/core/search.js +371 -116
  51. package/core/semantic-provider.js +110 -0
  52. package/core/stacktrace.js +25 -0
  53. package/core/tracing.js +101 -51
  54. package/core/trust-matrix.js +19 -40
  55. package/core/verify.js +534 -37
  56. package/languages/adapter.js +218 -0
  57. package/languages/c-family.js +2791 -0
  58. package/languages/c.js +3 -0
  59. package/languages/cpp.js +3 -0
  60. package/languages/csharp.js +1402 -0
  61. package/languages/go.js +60 -21
  62. package/languages/html.js +2 -2
  63. package/languages/index.js +85 -7
  64. package/languages/java.js +428 -16
  65. package/languages/javascript.js +452 -49
  66. package/languages/python.js +1041 -32
  67. package/languages/rust.js +1415 -152
  68. package/languages/utils.js +40 -3
  69. package/mcp/server.js +254 -636
  70. package/package.json +41 -24
  71. package/eslint.config.js +0 -43
  72. package/jsconfig.json +0 -10
package/README.md CHANGED
@@ -2,15 +2,21 @@
2
2
 
3
3
  See what code does before you touch it.
4
4
 
5
- Find symbols, trace callers, check impact, pick the right tests, extract code and spot what's dead - from the terminal.
6
-
7
5
  [![npm](https://img.shields.io/npm/v/ucn)](https://www.npmjs.com/package/ucn)
8
6
  [![tests](https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/mleoca/0e10a790e16ab61ddd233e05645e203e/raw/ucn-tests.json)](https://github.com/mleoca/ucn/actions/workflows/ci.yml)
9
7
  [![license](https://img.shields.io/npm/l/ucn)](LICENSE)
10
8
 
11
- All commands, one engine, three surfaces:
9
+ If you work with AI Agents, add UCN as a [Skill or MCP tool](#ai-setup). One tool
10
+ gives the agent compact, source-linked answers to caller, impact, and test
11
+ questions, with uncertainty labeled instead of guessed.
12
12
 
13
- ```
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.
15
+
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:
18
+
19
+ ```text
14
20
  Terminal AI Agents Agent Skills
15
21
  │ │ │
16
22
  CLI MCP Skill
@@ -23,182 +29,229 @@ All commands, one engine, three surfaces:
23
29
  └─────────────┘
24
30
  ```
25
31
 
26
- Supports JavaScript, TypeScript, Python, Go, Rust, Java, and HTML inline scripts.
27
-
28
- 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.
29
- All commands ship as a single tool.
30
-
31
- UCN is deliberately lightweight:
32
-
33
- - **No background processes** - parses on demand, answers, exits
34
- - **No language servers** - tree-sitter does the parsing, no compilation needed
35
- - **MCP is optional** - only needed if you connect UCN to an AI agent, the CLI and Skill work on their own
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?
36
37
 
37
- And it's built for **auditable trust**. grep hands you raw text matches to verify yourself; UCN separates target-backed edges from possible edges, explains exclusions, and reconciles its observed text set. It does not turn a zero into a deletion claim. CI re-derives answers from real compilers and language servers (ts-morph, pyright, gopls, rust-analyzer, jdtls). Publishing is gated on a representative five-repository board; the scheduled board covers nineteen plus rotating fresh repositories. See [Answers you can trust](#answers-you-can-trust).
38
+ It's deliberately lightweight:
38
39
 
39
- ### Same engine, different transport
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 language servers, no compilation** - tree-sitter does the analysis
43
+ without building the project.
44
+ - **No config** - point it at a directory and ask.
40
45
 
41
- The CLI and MCP tool use the same command registry, handlers, project index, persisted cache, and output formatters. The skill is guidance for choosing and interpreting those commands; it is not a third analysis engine.
46
+ And it's built for auditable trust. grep hands you raw matches to sift
47
+ yourself; UCN separates proven edges from possible ones, explains every
48
+ exclusion, and reconciles every occurrence of the name it searched. It never
49
+ turns a zero into a deletion claim. CI re-derives its answers from real
50
+ compilers and language servers (ts-morph, Pyright, gopls, rust-analyzer,
51
+ JDT LS, Roslyn, clangd) on pinned production repositories. See
52
+ [Answers you can trust](#answers-you-can-trust).
42
53
 
43
- The transports intentionally have different defaults:
54
+ <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%">
44
55
 
45
- - CLI prints full text unless `--compact` is passed. `--json` emits raw machine-readable JSON.
46
- - MCP defaults `about`, `context`, and `impact` to compact text. Targeted commands have a 10K character default, broad commands have a 3K default, and the hard ceiling is 100K. Truncated answers retain contract metadata.
47
- - MCP commands and parameters use snake_case. CLI commands and flags use hyphenated names.
48
- - A persistent MCP server keeps the process and index warm across calls. Repeated calls are normally faster than launching the CLI for every query, while semantic execution and cache behavior remain shared.
56
+ <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>
49
57
 
50
- When comparing text output, use equivalent flags and compact settings. Transport-specific retry hints use the spelling appropriate to that surface, so the final hint line may differ.
51
-
52
- ---
58
+ ## Start here
53
59
 
54
60
  ```bash
55
- npm install -g ucn # Node.js 20+
61
+ npm install -g ucn # Node.js 20+
56
62
 
57
- ucn orient # first look at any repo: size, hot spots, trust
58
- ucn trace main --depth=3 # full execution flow
59
- ucn about handleRequest # definition + callers + callees + tests
60
- ucn impact handleRequest # every call site with arguments
61
- ucn deadcode --exclude=test # unused code, AST-verified
63
+ cd your-project
64
+ ucn repo # what is this codebase?
65
+ ucn find handleRequest # exact definitions, stable handles
66
+ ucn show src/server.ts:42:handleRequest # the full picture
67
+ ucn trace src/server.ts:42:handleRequest --direction=callers
68
+ ucn impact src/server.ts:42:handleRequest # every call site, with evidence
69
+ ucn tests src/server.ts:42:handleRequest --depth=3 # which tests to run
62
70
  ```
63
71
 
64
- "What happens when `build()` runs?"
65
-
66
- ```
67
- $ ucn trace build --depth=2
68
-
69
- build
70
- ├── detectProjectPattern (core/discovery.js:450) 1x
71
- ├── parseGitignore (core/discovery.js:131) 1x
72
- ├── expandGlob (core/discovery.js:199) 1x
73
- │ ├── parseGlobPattern (core/discovery.js:238) 1x
74
- │ ├── walkDir (core/discovery.js:295) 1x
75
- │ └── compareNames (core/discovery.js:178) 1x
76
- ├── parallelBuild (core/parallel-build.js:25) 1x
77
- ├── indexFile (core/project.js:397) 1x
78
- │ ├── addSymbol (core/project.js:502) 4x
79
- │ ├── detectLanguage (languages/index.js:344) 1x
80
- │ ├── parse (core/parser.js:69) 1x
81
- │ ├── extractImports (core/imports.js:19) 1x
82
- │ └── extractExports (core/imports.js:44) 1x
83
- ├── buildImportGraph (core/project.js:798) 1x
84
- └── buildInheritanceGraph (core/project.js:803) 1x
85
- … calls UCN can't prove a receiver for (arr.push(), obj.get()) show as
86
- [unverified] leaves (abridged here)
87
-
88
- CALLEE ACCOUNT: 26 nodes expanded · 394 call sites = 61 confirmed + 162 unverified (162 uncertain-receiver) + 68 external/builtin + 103 excluded
89
- ```
90
-
91
- One command, no files opened. The `CALLEE ACCOUNT:` line reconciles all 394 indexed call sites into explicit buckets.
92
-
93
- ---
72
+ The first command builds an incremental index; the rest reuse it. The cache
73
+ lives outside your project directory, so there's nothing to gitignore.
94
74
 
95
75
  ## Understand code you didn't write
96
76
 
97
- `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.
98
-
99
- ```
100
- $ ucn about expandGlob
101
-
102
- expandGlob (function)
103
- ════════════════════════════════════════════════════════════
104
- core/discovery.js:199-233 → core/discovery.js:199:expandGlob
105
- expandGlob (pattern: string, options: number = {}) : string[]
106
-
107
- USAGES: 8 total
108
- 3 calls, 3 imports, 2 references
109
-
110
- CALLERS: CONFIRMED (7, 3 prod + 4 test):
77
+ What does this function do, who calls it, and how sure is the answer?
78
+ `ucn show` gathers everything useful about one symbol: signature, source,
79
+ callers, callees, tests, types, dependencies, examples. Project it down to
80
+ just the sections you need:
81
+
82
+ ```text
83
+ $ ucn show detectLanguage --sections=summary,callers,callees --compact
84
+
85
+ SUMMARY
86
+ ───────
87
+ detectLanguage(filePath: string, projectRoot = null): string|null
88
+ languages/index.js:420-428 (9 lines)
89
+ handle: languages/index.js:420:detectLanguage
90
+ "Detect language from file path"
91
+ async: no | side_effects: [none] | complexity: branches=1, depth=1
92
+
93
+ RELATIONSHIPS
94
+ ─────────────
95
+ CALLERS — CONFIRMED (51, 30 prod + 21 test):
111
96
  evidence: scope-match (all)
112
- cli/index.js:1267 [runGlobCommand]
113
- const files = expandGlob(pattern);
114
- core/cache.js:548 [isCacheStale] [unreachable]
115
- const currentFiles = expandGlob(pattern, globOpts);
116
- core/project.js:257 [build]
117
- files = expandGlob(pattern, globOpts);
118
- test callers:
119
- test/integration.test.js:167
120
- const files = expandGlob('**/*.go', { root: tmpDir });
121
- ... (3 more test callers)
122
-
123
- CALLEES (3):
97
+ [1] cli/index.js:604 [runFileCommand]: const language = detectLanguage(filePath);
98
+ [7] core/build-worker.js:39 [processFile]: const language = detectLanguage(filePath, rootDir);
99
+ [17] core/project.js:472 [build]: const language = detectLanguage(filePath, this.root);
100
+ [34] test/parser-unit.test.js:19: assert.strictEqual(detectLanguage('file.js'), 'javascript');
101
+ ... 47 more callers
102
+
103
+ CALLEES (1):
124
104
  evidence: exact-binding (all)
125
- parseGlobPattern [utility] - core/discovery.js:238 (1x)
126
- walkDir [utility] {fs} - core/discovery.js:295 (1x)
127
- compareNames [utility] - core/discovery.js:178 (1x)
105
+ [52] detectHeaderLanguage {fs} - core/compilation-database.js:217
106
+ CALLEES — UNVERIFIED (1) — call syntax, receiver/binding unresolved:
107
+ toLowerCase ×1 — possible-dispatch L422
128
108
 
129
- ACCOUNT: "expandGlob" occurs on 14 lines in 6 files: 7 confirmed, 0 unverified,
130
- 7 non-call (4 import, 1 definition, 2 reference, 0 other-text), 0 other-target, 0 unaccounted
109
+ ACCOUNT: "detectLanguage" occurs on 79 lines in 20 files: 51 confirmed, 0 unverified,
110
+ 28 non-call (18 import, 1 definition, 3 reference, 6 other-text), 0 other-target, 0 unaccounted
131
111
  CONTRACT: literal-name text partition complete; semantic completeness is not claimed
132
-
133
- TESTS: 5 matches in 1 file(s)
112
+ (aliases, indirect calls, generated code, and runtime dispatch may exist).
134
113
  ```
135
114
 
136
- Callers split into **CONFIRMED** (binding/receiver/import evidence, with production before tests) and **UNVERIFIED** (found but unproven, each with a reason). `ACCOUNT:` reconciles the literal-name text set; `CONTRACT:` states its scope and completeness. Neither claims that aliases, generated code, reflection, runtime registration, or external consumers do not exist. Tune the evidence display with `--min-confidence` / `--hide-confidence` / `--git`; walk callers *upward* with `ucn reverse-trace fn`.
115
+ `find` returns stable handles in `file:line:name` form. Pass a handle to any
116
+ command to pin the answer to one definition, even when several files or classes
117
+ reuse the same name.
137
118
 
138
- ## Answers you can trust
119
+ ## Follow the execution path
139
120
 
140
- UCN doesn't just find a name. It shows the identity evidence it has and the uncertainty it retains. Every answer from `about`, `context`, and `impact` partitions *every observed literal-name occurrence* into auditable buckets:
141
-
142
- ```
143
- $ ucn impact saveCache
121
+ What happens when `build()` runs?
144
122
 
145
- CALL SITES: 2 confirmed + 11 unverified
146
-
147
- test/regression-go.test.js (2 calls)
148
- :2007
149
- saveCache(index, cachePath);
123
+ ```text
124
+ $ ucn trace build --depth=2
150
125
 
151
- UNVERIFIED CALL SITES (11): call syntax, no binding/receiver evidence
152
- core/project.js:2126: saveCache(cachePath) { ... } (call-not-resolved)
153
- mcp/server.js:810: try { index.saveCache(); } catch (_) ... (method-ambiguous)
154
- test/cache.test.js:1804: index.saveCache(); (method-ambiguous)
155
- ... (8 more)
126
+ build
127
+ ├── compareNames (core/discovery.js:293) [regular] 3x
128
+ ├── recordDiscoveryIssue (core/project.js:346) 2x
129
+ │ └── [unverified] push — method-ambiguous L351
130
+ ├── detectProjectPattern (core/discovery.js:760) [utility] 1x
131
+ ├── parseGitignore (core/discovery.js:253) [utility] 1x
132
+ │ ├── gitignoreFiles (core/discovery.js:234) [utility] 1x
133
+ │ ├── compareNames (core/discovery.js:293) [utility] 1x (see above)
134
+ │ └── parseGitignoreFile (core/discovery.js:152) [utility] 1x
135
+ ├── gitTrackedPaths (core/discovery.js:266) [utility] 1x
136
+ │ ├── hasGitMetadata (core/discovery.js:224) [utility] 1x
137
+ │ └── [unverified] dirname — method-ambiguous L281,L284
138
+ └── ... more callees
139
+
140
+ CALLEE ACCOUNT: 11 nodes expanded · 210 call sites = 31 confirmed + 33 unverified
141
+ (25 method-ambiguous, 1 possible-dispatch, 7 uncertain-receiver) + 86 external/builtin + 60 excluded
142
+ ```
143
+
144
+ `trace` walks callees, callers, or callers all the way up to runtime entry
145
+ points (`--direction=callers --to=entrypoints`). Proven edges form the tree;
146
+ calls UCN can't prove a receiver for show up as `[unverified]` leaves with a
147
+ reason. The account line reconciles every call site in the expanded tree, so
148
+ unresolved dispatch stays visible and counted instead of quietly vanishing.
156
149
 
157
- ACCOUNT: "saveCache" occurs on 55 lines in 8 files: 2 confirmed, 11 unverified,
158
- 11 non-call (2 import, 1 definition, 1 reference, 7 other-text), 31 other-target, 0 unaccounted
159
- CONTRACT: literal-name text partition complete; semantic completeness is not claimed
160
- ```
150
+ ## Answers you can trust
161
151
 
162
- UCN sorts every one of the 55 places the name appears:
152
+ UCN doesn't turn every matching name into a semantic claim. Watch it work
153
+ through a name with two definitions and a pile of ambiguous method calls:
163
154
 
164
- - **2 confirmed**: call sites it can prove resolve to *this* `saveCache`.
165
- - **11 unverified**: real call sites it found but won't claim. `index.saveCache()` has an untyped receiver, so UCN can't prove which `saveCache` runs; it shows the site and the reason (`method-ambiguous`) instead of guessing.
166
- - **31 other-target**: occurrences that belong to a *different* `saveCache`, kept separate so they never pollute the answer.
167
- - **11 non-call**: imports, the definition, plain text.
168
- - **`0 unaccounted`**: every line in the observed literal-name set was assigned to a bucket.
155
+ ```text
156
+ $ ucn impact saveCache
169
157
 
170
- The payoff is an answer an agent can audit instead of a single opaque match count. A confirmed edge is evidence for the pinned target; an unverified edge requires review. A clean account with no callers is an **observed-text zero**, not semantic-zero or safe-delete proof. Before deleting anything, run `ucn usages`, inspect entry points and public API exposure, check `ucn doctor --deep` deletion readiness, and corroborate with the compiler/type checker and tests.
158
+ Impact analysis for saveCache
159
+ core/cache.js:610
160
+ Note: Found 2 definitions for "saveCache". Using core/cache.js:610. Also in: core/project.js:2380. Use file= to disambiguate.
161
+ CALL SITES: 5 confirmed + 15 unverified
162
+ Files affected: 3
163
+ BY FILE:
164
+ core/project.js:2380 [saveCache]: saveCache(cachePath) { return indexCache.saveCache(this, cachePath); }
165
+ test/prerelease-audit.test.js:1493: saveCache(built, cacheFile);
166
+ ... (3 more)
167
+ UNVERIFIED CALL SITES (15) — call syntax, no binding/receiver evidence:
168
+ mcp/server.js:517: try { index.saveCache(); } catch (_) { /* best-effort */ } (possible-dispatch via local receiver)
169
+ test/cache.test.js:124: index.saveCache(); (possible-dispatch via local receiver)
170
+ (+13 more)
171
+ ACCOUNT: "saveCache" occurs on 68 lines in 11 files: 5 confirmed, 15 unverified,
172
+ 12 non-call (3 import, 1 definition, 1 reference, 7 other-text), 36 other-target, 0 unaccounted
173
+ CONTRACT: literal-name text partition complete; semantic completeness is not claimed
174
+ (aliases, indirect calls, generated code, and runtime dispatch may exist).
175
+ ```
176
+
177
+ UCN sorted all 68 places the name appears:
178
+
179
+ - **5 confirmed** - call sites it can *prove* resolve to this `saveCache`,
180
+ via a binding, import, receiver type, qualified path, or same-class evidence.
181
+ - **15 unverified** - real call syntax it refuses to claim. `index.saveCache()`
182
+ sits on an untyped receiver, so the site stays visible with its reason
183
+ (`possible-dispatch via local receiver`) instead of being guessed or dropped.
184
+ - **36 other-target** - occurrences that belong to the *other* `saveCache`,
185
+ kept out of the answer instead of quietly inflating it.
186
+ - **12 non-call** - imports, the definition, comments, strings.
187
+ - **0 unaccounted** - every observed line landed in exactly one bucket.
188
+
189
+ That's the payoff: an answer you (or your agent) can audit, instead of an
190
+ opaque match count. A confirmed edge is evidence about the pinned target. An
191
+ unverified edge is a review item with a stated reason. And a clean zero is an
192
+ *observed-text* zero, not a safe-to-delete claim: aliases, generated code,
193
+ reflection, runtime registration, and external consumers can live beyond the
194
+ indexed evidence, and `ucn repo --sections=health --deep` reports exactly those
195
+ blind spots. Even when output is truncated to fit an agent's budget, the
196
+ ACCOUNT, CONTRACT, and WARNING lines survive the cut.
171
197
 
172
198
  ### Measured against ground truth
173
199
 
174
- This is a release gate, not a promise of universal program understanding. CI re-derives UCN answers from real compilers and language servers. The pinned release board must pass at least 98% confirmed precision, zero semantically missing in-scope edges, a conserved caller account, command-surface checks, dead-code checks, and performance budgets.
175
-
176
- Current pinned release-board results:
177
-
178
- | Repository | Language oracle | Confirmed caller precision | Caller recall | Callee precision/recall | Command checks |
179
- |---|---|---:|---:|---:|---:|
180
- | preact-signals | ts-morph | 100% | 100% | 100% / 100% | 100% |
181
- | httpx | pyright | 100% | 100% | 100% / 100% | 100% |
182
- | cobra | gopls | 100% | 100% | 100% / 100% | 100% |
183
- | clap | rust-analyzer | 100% | 100% | 100% / 100% | 100% |
184
- | javapoet | jdtls | 100% | 100% | 100% / 100% | 100% |
185
-
186
- The command checks cover exact definition lookup, `find`, `fn`/`class`, `brief`, `typedef`, `usages`, `tests`, and `example` against the same external oracle population. The dead-code arm currently has zero false-dead claims on the release board. The semantic gate also caps configuration-unscored caller and callee evidence at 10%, so platform filtering cannot silently make a small scored subset look representative. The performance arm runs every repository in an isolated process, takes three fresh-cache startup samples, reports median and maximum first-query latency, and independently gates steady-state p50/p95 and peak memory.
187
-
188
- Unverified precision is reported separately and is intentionally much lower on dispatch-heavy code. Unverified entries are review candidates, not confirmed claims. Rust feature-gated sites that one compiler configuration cannot load are reported as unscored rather than counted as passes.
189
-
190
- The scheduled board covers nineteen pinned repositories, multiple sampling seeds, and a rotating fresh-repository arm. It is broader than the five-repository publish gate and is used to expose regressions and overfitting. The tree commands `trace`, `blast`, `reverse-trace`, and `affected-tests` follow the same evidence discipline. Run `ucn doctor --deep` for task-specific readiness on your repository.
200
+ Don't take the tiers on faith. Release gates re-derive UCN's answers from real
201
+ compilers and language servers on a ten-repository board of pinned production
202
+ codebases, and publishing is blocked unless they pass. The latest full
203
+ release-board run (2026-08-11):
204
+
205
+ | Repository | Pinned commit | Oracle | Caller precision | Caller recall | Callee prec / recall | Command checks |
206
+ |---|---|---|---:|---:|---:|---:|
207
+ | [preact-signals](https://github.com/preactjs/signals) | [`e0ce9fdf`](https://github.com/preactjs/signals/commit/e0ce9fdf92df7f0ece2c89d44554c39f36dc6882) | ts-morph | 100% | 100% | 100% / 100% | 100% |
208
+ | [httpx](https://github.com/encode/httpx) | [`b5addb64`](https://github.com/encode/httpx/commit/b5addb64f0161ff6bfe94c124ef76f6a1fba5254) | Pyright | 100% | 100% | 100% / 100% | 100% |
209
+ | [cobra](https://github.com/spf13/cobra) | [`ad460ea8`](https://github.com/spf13/cobra/commit/ad460ea8f249db69c943a365fb84f3a59042d54e) | gopls | 100% | 100% | 100% / 100% | 100% |
210
+ | [viper](https://github.com/spf13/viper) | [`528f7416`](https://github.com/spf13/viper/commit/528f7416c4b56a4948673984b190bf8713f0c3c4) | gopls | 100% | 100% | 100% / 100% | 100% |
211
+ | [ripgrep](https://github.com/BurntSushi/ripgrep) | [`82313cf9`](https://github.com/BurntSushi/ripgrep/commit/82313cf95849bfe425109ad9506a52154879b1b1) | rust-analyzer | 100% | 100% | 100% / 100% | 100% |
212
+ | [clap](https://github.com/clap-rs/clap) | [`d3e59a9a`](https://github.com/clap-rs/clap/commit/d3e59a9ab214910b9dad02921b7ef42c6400de9b) | rust-analyzer | 100% | 100% | 100% / 100% | 100% |
213
+ | [javapoet](https://github.com/square/javapoet) | [`b9017a95`](https://github.com/square/javapoet/commit/b9017a9503b76e11b4ad4c1a9f050e2d29112cb0) | JDT LS | 100% | 100% | 100% / 100% | 100% |
214
+ | [newtonsoft-json](https://github.com/JamesNK/Newtonsoft.Json) | [`4f73e743`](https://github.com/JamesNK/Newtonsoft.Json/commit/4f73e74372445108d2c1bda37b36e6f5e43402e0) | Roslyn | 99.4% | 100% | 100% / 100% | 100% |
215
+ | [cjson](https://github.com/DaveGamble/cJSON) | [`c859b25d`](https://github.com/DaveGamble/cJSON/commit/c859b25da02955fef659d658b8f324b5cde87be3) | clangd | 100% | 100% | 100% / 100% | 100% |
216
+ | [fmt](https://github.com/fmtlib/fmt) | [`e424e3f2`](https://github.com/fmtlib/fmt/commit/e424e3f2e607da02742f73db84873b8084fc714c) | clangd | 99.6% | 100% | 100% / 100% | 100% |
217
+
218
+ On the same run: **zero** in-scope oracle call edges missing from the answer
219
+ (the release gate) on every repository, **zero** false-dead `deadcode` claims
220
+ in the oracle-visible sample, **8,000 / 8,000** cross-command consistency
221
+ comparisons in agreement, **10 / 10** repositories inside the performance
222
+ budget (slowest median cold build 15.4K lines/second by wall time, worst query
223
+ p95 73.8 ms, highest peak RSS 771 MB), and 3,383 automated tests with no
224
+ failures or skips. The same gates run in CI (the scheduled
225
+ [Eval workflow](https://github.com/mleoca/ucn/actions/workflows/eval.yml) and
226
+ every release tag), and `npm run trust:gate` reproduces the release board
227
+ locally. Pinned sources: [`eval/lib/repos.js`](eval/lib/repos.js).
228
+
229
+ Semantic runs draw a deterministic, reference-stratified sample of up to 50
230
+ compiler/LSP symbols per repository, then check caller identity, callee
231
+ identity, account conservation, review burden, and the public commands `find`,
232
+ `show`, `source`, `trace`, `impact`, `usages`, and `tests` against that same
233
+ external population. Unverified precision is reported separately and is
234
+ intentionally much lower on dispatch-heavy code: those entries are review
235
+ candidates, never confirmed claims.
236
+
237
+ Beyond the publish gate, a scheduled board re-checks 22 pinned repositories
238
+ across every supported oracle language (zod, express, hono, zustand, fastify,
239
+ rich, click, grpc-go, chi, cursive, gson, jsoup, and friends), plus a rotating
240
+ fresh-repo arm of codebases the engine was never tuned on. Repositories that
241
+ expose a gap stay on the board; they don't get removed to keep a table pretty.
242
+ These are measured results on pinned code, not a claim of universal program
243
+ understanding or identical performance on every machine.
191
244
 
192
245
  ## Change code without breaking things
193
246
 
194
- Before touching a function, check if all existing call sites match its signature:
247
+ Will this change break a call site you've never seen? Check before you edit:
195
248
 
196
- ```
197
- $ ucn verify expandGlob
249
+ ```text
250
+ $ ucn check expandGlob
198
251
 
199
252
  Verification: expandGlob
200
253
  ════════════════════════════════════════════════════════════
201
- core/discovery.js:191
254
+ core/discovery.js:314
202
255
  expandGlob (pattern: string, options: number = {}) : string[]
203
256
 
204
257
  Expected arguments: 1-2
@@ -209,228 +262,301 @@ STATUS: ✓ All calls valid
209
262
  Mismatches: 0
210
263
  Uncertain: 0
211
264
  Patterns: 4 in try, 4 in callback
212
- ```
213
265
 
214
- The `Patterns:` line surfaces structural classification of each call site (`inLoop`, `inTry`, `inCallback`, `inTestCase`, `awaited`) so you can spot risky call sites such as calls inside loops or missing `await`. The same line appears on `impact` and inside `about`.
266
+ ACCOUNT: "expandGlob" occurs on 14 lines in 6 files: 7 confirmed, 0 unverified,
267
+ 7 non-call (4 import, 1 definition, 2 reference, 0 other-text), 0 other-target, 0 unaccounted
268
+ ```
215
269
 
216
- Then preview the refactoring. UCN shows exactly what needs to change and where:
270
+ The `Patterns:` line classifies call-site structure (`inLoop`, `inTry`,
271
+ `inCallback`, `awaited`) so risky sites stand out. Then preview the refactor.
272
+ UCN shows exactly what would need to change and where:
217
273
 
218
- ```
274
+ ```text
219
275
  $ ucn plan expandGlob --rename-to=expandGlobPattern
220
276
 
221
277
  Refactoring plan: rename
222
278
  ════════════════════════════════════════════════════════════
223
- core/discovery.js:191
279
+ core/discovery.js:314
224
280
 
225
281
  SIGNATURE CHANGE:
226
282
  Before: expandGlob (pattern: string, options: number = {}) : string[]
227
283
  After: expandGlobPattern (pattern: string, options: number = {}) : string[]
228
284
 
229
- CHANGES NEEDED: 11
230
- Files affected: 4
285
+ CHANGES NEEDED: 12
286
+ Files affected: 5
287
+ Definition 1, calls/references 7, imports 4, exports 0; manual review required for 0 of these changes
231
288
 
232
289
  BY FILE:
233
290
 
234
291
  cli/index.js (2 changes)
235
- :1190
292
+ :771 [call]
236
293
  const files = expandGlob(pattern);
237
294
  → Rename to: const files = expandGlobPattern(pattern);
238
- :15
295
+ :15 [import]
239
296
  const { expandGlob, findProjectRoot } = require('../core/discovery');
240
297
  → Update import: const { expandGlobPattern, findProjectRoot } = require('../core/discovery');
241
298
 
242
- ... (more changes in core/cache.js, core/project.js, test/integration.test.js)
299
+ ... (more changes in core/discovery.js, core/cache.js, core/project.js, test/integration.test.js)
243
300
  ```
244
301
 
245
- Run `ucn diff-impact --staged` before committing to see what you changed and who calls it.
246
-
247
- Or wrap the same checks in a single command:
248
-
249
- ```
250
- $ ucn check --staged
302
+ Anything `plan` can't represent safely is marked `needsReview` instead of
303
+ being silently rewritten. Before committing, point the same machinery at your
304
+ Git diff:
251
305
 
252
- Pre-commit Check vs HEAD
253
- ════════════════════════════════════════════════════════════
254
- Changed: 3 functions
255
- parseFlags (cli/index.js:165) [MODIFIED] 2 callers
256
- ...
306
+ ```bash
307
+ ucn impact --staged # what did I change, and who depends on it?
308
+ ucn check --staged # signature drift, orphaned functions, tests to run
257
309
  ```
258
310
 
259
- `ucn check` composes `diff-impact` + `verify` + `affected-tests` in one shot. It flags added functions with no callers, signature drift across call sites, and recommends which tests to run.
311
+ ## Pick the right tests
260
312
 
261
- ## Get the lay of the land in a new repo
313
+ Which tests actually exercise this function, directly or three hops away?
262
314
 
263
- One command answers "what is this codebase?": size and language mix, where the code lives, the most-called production functions, entry points, and how far to trust the index.
315
+ ```text
316
+ $ ucn tests expandGlob --depth=3
264
317
 
265
- ```
266
- $ ucn orient
267
-
268
- PROJECT ORIENTATION: /path/to/project
318
+ affected-tests: expandGlob
269
319
  ════════════════════════════════════════════════════════════
270
- 169 files · 2111 symbols · javascript 67%, rust 8%, typescript 8%, java 7%, go 5%, python 5%
320
+ core/discovery.js:314
321
+ 1 function changed → 12 functions affected (depth 3)
271
322
 
272
- TOP DIRS (by symbols):
273
- core 516 symbols · 29 file(s)
274
- languages 282 symbols · 8 file(s)
275
- test 209 symbols · 29 file(s)
276
- core/output 142 symbols · 14 file(s)
277
- ...
323
+ Test files to run (30):
278
324
 
279
- HOT (most-called production functions, top 8 of 1028):
280
- execute: 1124 call(s) · core/execute.js:1608
281
- ProjectIndex.build: 340 call(s) · core/project.js:221
282
- getParser: 150 call(s) · languages/index.js:312
325
+ test/integration.test.js (links: expandGlob, build, idx, setupProject)
326
+ L169: const files = expandGlob('**/*.go', { root: tmpDir }); [call]
327
+ test/prerelease-audit.test.js (links: isCacheStale, runInteractive, build, idx)
328
+ L39: const index = idx(dir); [call]
283
329
  ...
284
330
 
285
- ENTRY POINTS: 389; test 284, runtime 72, http 32, di 1
286
- TRUST: MEDIUM; 41 dynamic import(s), 13 eval, 6 reflection (ucn doctor for detail)
287
-
288
- Next: ucn about execute · ucn toc --detailed · ucn stats --hot --top=20 · ucn doctor --deep
331
+ Summary: 12 affected → 30 statically linked test files, 5/12 functions linked (42%) · 1 possibly affected (unverified chains)
289
332
  ```
290
333
 
291
- Then drill in:
334
+ `tests` reports static call/reference linkage, not runtime coverage. Functions
335
+ reached only through unverified edges are listed separately as *possibly
336
+ affected*, and empty results warn about subprocess tests, reflection, and
337
+ external harnesses that may still exercise the target.
292
338
 
293
- ```
294
- $ ucn brief fetch_user
295
- fetch_user(user_id: int): dict
296
- svc.py:4-8 (5 lines)
297
- "Fetch a user from the API."
298
- async: no | side_effects: [fs, network, process] | complexity: branches=2, depth=2
299
- ```
300
-
301
- `brief` is the lighter alternative to `about`: typed signature, first sentence of the docstring, side-effect classification, and complexity, all in one screen. Pair with `--git` to see who last touched it and how often.
339
+ ## Get the lay of the land
302
340
 
303
- ```
304
- $ ucn doctor
305
-
306
- UCN Trust Report: /path/to/project
307
- Index: 169 files, 2104 symbols
308
- Languages: javascript (72%), typescript (14%), java (4%), python (4%), rust (4%), go (3%)
309
- Cache: fresh, 344ms build
310
- Command proofs: 39/39 classified, 22 external-oracle-backed, 0 unclassified
311
-
312
- Readiness:
313
- navigation: HIGH: fresh index; no parse failures
314
- refactor: UNKNOWN: run --deep; review unverified and non-call occurrences
315
- deletion: REVIEW: usages, public API, compiler, and tests are still required
316
- ```
341
+ One command answers "what is this codebase?" Here it is on ripgrep:
317
342
 
318
- `doctor` reports task-specific readiness for the index: file/symbol counts, blind spots (dynamic imports, eval, reflection), parse failures, command-proof classification, and separate navigation/refactor/deletion levels. Use `--deep` to sample the resolution evidence profile. This profile is not measured accuracy; use the oracle reports for accuracy.
343
+ ```text
344
+ $ ucn repo
319
345
 
320
- `entrypoints` lists detected framework handlers (HTTP routes, DI beans, jobs, tests):
321
-
322
- ```
323
- ucn entrypoints --type=http --framework=spring # narrow to one framework
324
- ucn entrypoints --exclude-tests # tests are included by default
325
- ```
326
-
327
- ## Find what to clean up
328
-
329
- Which tests should you run after a change? `affected-tests` walks the blast radius and finds every test that touches the affected functions:
330
-
331
- ```
332
- $ ucn affected-tests expandGlob
333
-
334
- affected-tests: expandGlob
346
+ PROJECT ORIENTATION — ripgrep
335
347
  ════════════════════════════════════════════════════════════
336
- core/discovery.js:191
337
- 1 function changed → 15 functions affected (depth 3)
348
+ 100 files · 4755 symbols · language mix by symbols: rust 100%
338
349
 
339
- Test files to run (20):
340
-
341
- test/integration.test.js (covers: expandGlob, build, idx, setupProject)
342
- L47: index.build(null, { quiet: true }); [call]
343
- L167: const files = expandGlob('**/*.go', { root: tmpDir }); [call]
344
- ...
345
-
346
- POSSIBLY AFFECTED (1): reachable only through unverified call edges
347
- doctor
350
+ TOP DIRS (by symbols):
351
+ crates/core/flags 1510 symbols · 6 file(s)
352
+ crates/printer/src 677 symbols · 11 file(s)
353
+ crates/ignore/src 607 symbols · 8 file(s)
354
+ crates/globset/src 331 symbols · 5 file(s)
355
+
356
+ HOT (most-called production functions, top 8 of 2238 raw candidates):
357
+ parse_low_raw — 545 call(s) · crates/core/flags/parse.rs:139
358
+ SearcherBuilder.build — 123 call(s) · crates/searcher/src/searcher/mod.rs:315
359
+ Searcher.search_reader — 123 call(s) · crates/searcher/src/searcher/mod.rs:727
360
+ RegexMatcher.new — 100 call(s) · crates/regex/src/matcher.rs:385
361
+ ...
348
362
 
349
- Uncovered (12): runGlobCommand, main, isCacheStale, runProjectCommand, runFileCommand, ...
350
- ⚠ These affected functions have no test references
363
+ ENTRY POINTS: 426 — test 421, runtime 5
364
+ TRUST: PARTIAL — 48 glob import(s), 5 unsupported source file(s) (ucn repo --sections=health --deep for detail)
365
+ SKIPPED SOURCE: 5 file(s) (Shell 4, Ruby 1) — use grep/ripgrep plus a language-native analyzer.
351
366
 
352
- Summary: 15 affected → 20 test files, 3/15 functions covered (20%) · 1 possibly affected (unverified chains)
367
+ Next: ucn show parse_low_raw · ucn repo --sections=files --detailed · ucn repo --sections=health --deep
353
368
  ```
354
369
 
355
- The confirmed closure is what you run; `POSSIBLY AFFECTED` lists functions reached only through unverified edges. These extra tests are worth reviewing and remain separate.
370
+ Size, layout, hot spots, entry points, and an honest trust line. Note the
371
+ `SKIPPED SOURCE` handoff: when a repo mixes in languages UCN can't parse, it
372
+ says so and points you at the right tool, instead of presenting a clean-looking
373
+ answer over a partial index.
356
374
 
357
- ## Find unused code
375
+ ## Find dead code you can act on
358
376
 
359
- ```
377
+ ```text
360
378
  $ ucn deadcode --exclude=test # run on ripgrep
361
379
 
362
- Dead code: 8 unused symbol(s)
380
+ Dead code: 3 unused symbol(s)
363
381
 
364
382
  crates/globset/src/serde_impl.rs
365
383
  [ 38- 42] Glob.deserialize (method)
366
384
  [ 70- 74] GlobSet.deserialize (method)
367
385
  crates/matcher/src/lib.rs
368
386
  [ 397- 399] Captures.as_match (method)
369
- [ 669- 678] Matcher.try_find_iter (method) [only self-references, recursive]
370
- [ 796- 806] Matcher.try_captures_iter (method) [only self-references, recursive]
371
- ...
372
387
 
373
- 921 exported symbol(s) excluded from the audit (public API may have external callers). Use --include-exported to audit them.
374
- ```
388
+ 33 decorated/annotated symbol(s) hidden (framework-registered). Use --include-decorated to include them.
375
389
 
376
- Classes, structs, traits, and enums are audited alongside functions. Symbols whose only call sites live inside their own definitions are claimed too, marked `[only self-references, recursive]`. Deadcode claims are re-derived against compiler/LSP ground truth in CI. A default-audit claim with an oracle-visible reference fails the build.
390
+ 903 exported symbol(s) excluded from the audit (public API may have external callers). Use --include-exported to audit them.
377
391
 
378
- Find missing-await bugs:
379
-
380
- ```
381
- ucn audit-async
392
+ WARNING: source coverage is incomplete (5 unsupported-language); 17 candidate name(s) found in skipped source were suppressed.
382
393
  ```
383
394
 
384
- Lists async calls inside async functions that lack `await` (JS/TS/Python).
395
+ Three claims, and every one is re-checked against rust-analyzer in CI: a
396
+ default-audit claim with an oracle-visible reference fails the build. Notice
397
+ what it *didn't* claim: exported API that external code may call,
398
+ framework-registered symbols, and anything whose name appears in files UCN
399
+ couldn't parse. `deadcode` is deliberately a candidate generator. Before
400
+ deleting, corroborate with `usages`, `impact`, `api`, and your compiler and
401
+ tests.
385
402
 
386
- ## Map your API surface across languages
403
+ For missing-await bugs, `ucn audit-async` lists async calls inside async
404
+ functions that lack `await` (JS/TS/Python).
387
405
 
388
- UCN can match server routes to client requests across the supported languages: Express/Fastify/Koa/NestJS/Next.js, Flask/FastAPI, Spring/JAX-RS, Go net/http (Gin/Echo/Chi/Fiber), and axum/actix-web on the server side; fetch/axios, requests/httpx, RestTemplate/WebClient, and reqwest on the client side.
406
+ ## Map dependencies and API surfaces
389
407
 
390
408
  ```bash
391
- ucn endpoints --bridge
392
-
393
- # Filters
394
- ucn endpoints --bridge --unmatched # routes with no client / clients with no server
395
- ucn endpoints --bridge --method=POST
396
- ucn endpoints --bridge --prefix=/api
409
+ ucn deps src/server.ts --direction=imports --detailed
410
+ ucn deps src/server.ts --direction=importers --depth=3
411
+ ucn deps --cycles # circular imports
412
+ ucn api # public surface of the project
413
+ ucn entrypoints --type=http # runtime and framework roots
414
+ ucn endpoints --bridge --unmatched # server routes with no client, and vice versa
397
415
  ```
398
416
 
399
- Match confidence: `EXACT` (literal-literal), `PARTIAL` (server param ↔ client literal), `UNCERTAIN` (template-literal client). Use `--hide-uncertain` to drop the noisy tier.
417
+ `endpoints --bridge` matches server routes to client requests across
418
+ languages: Express/Fastify/Koa/NestJS/Next.js, Flask/FastAPI, Spring/JAX-RS,
419
+ Go net/http (Gin/Echo/Chi/Fiber), axum/actix-web, and ASP.NET on the server
420
+ side; fetch/axios, requests/httpx, RestTemplate/WebClient, reqwest, and .NET
421
+ HttpClient on the client side. Exact, partial, and uncertain matches stay in
422
+ separate tiers.
400
423
 
401
- ## Extract without reading the whole file
424
+ ## Extract and search without opening whole files
402
425
 
403
- ```
404
- $ ucn fn compareNames
405
-
406
- core/discovery.js:170
407
- [ 170- 178] compareNames(a, b)
408
- ────────────────────────────────────────────────────────────
409
- function compareNames(a, b) {
410
- const aLower = a.toLowerCase();
411
- const bLower = b.toLowerCase();
412
- if (aLower < bLower) return -1;
413
- if (aLower > bLower) return 1;
414
- if (a < b) return -1;
415
- if (a > b) return 1;
416
- return 0;
426
+ ```bash
427
+ ucn source core/discovery.js:314:expandGlob # exactly one function
428
+ ucn source core/discovery.js --range=314-364 # exactly one range
429
+ ucn search '$scope.$apply' # literal by default
430
+ ucn search 'TODO|FIXME' --regex # regex is explicit
431
+ ucn search --type=call --receiver=client # structural search
432
+ ucn usages expandGlob --include-tests # every occurrence, classified
433
+ ```
434
+
435
+ `usages` is the escape hatch: the complete literal-name inventory (calls,
436
+ definitions, imports, references, comments, strings), for when you want
437
+ everything the text contains, not just what the engine can prove. Regex search runs on an
438
+ RE2-compatible linear-time engine; hostile nested repetition is rejected up
439
+ front instead of hanging your terminal.
440
+
441
+ ## The 18 commands
442
+
443
+ | Task | Command |
444
+ |---|---|
445
+ | Repository orientation and health | `repo [--sections=summary,files,stats,health] [--deep]` |
446
+ | Symbol summary and relationships | `show <symbol> [--sections=...]` |
447
+ | Definition lookup | `find <name> [--type=type] [--with-source]` |
448
+ | Complete literal-name inventory | `usages <name>` |
449
+ | Literal, regex, or structural search | `search [term] [--regex] [structural flags]` |
450
+ | Exact source extraction | `source <symbol\|file:range>` |
451
+ | Call trees: down, up, or to entry points | `trace <symbol> [--direction=...] [--to=entrypoints]` |
452
+ | Symbol or Git-diff impact | `impact [symbol] [--staged]` |
453
+ | Direct or transitively linked tests | `tests <symbol> [--depth=N]` |
454
+ | Signature or pre-commit validation | `check [symbol] [--staged]` |
455
+ | Refactor preview | `plan <symbol> --rename-to=...` |
456
+ | Imports, importers, and cycles | `deps [file] [--direction=...] [--cycles]` |
457
+ | Project or file public API | `api [file]` |
458
+ | Runtime and framework roots | `entrypoints` |
459
+ | Server/client HTTP surface | `endpoints [--bridge]` |
460
+ | Conservative dead-code candidates | `deadcode` |
461
+ | Likely missing awaits | `audit-async` |
462
+ | Stack-trace frame resolution | `stacktrace <text>` |
463
+
464
+ Run `ucn --help` for every flag. Related modes live behind parameters rather
465
+ than extra verbs: `trace` handles down, up, and to-entry-points;
466
+ `impact`/`check` handle a symbol or the current Git diff; `show` projects any
467
+ subset of sections. Flags that don't apply to a command produce an explicit
468
+ warning instead of silently changing the task.
469
+
470
+ ## Same engine, different transport
471
+
472
+ CLI, MCP, file mode, project mode, glob mode, and interactive mode resolve
473
+ commands through the same registry, handlers, index, cache, and formatters:
474
+ same answers everywhere, different delivery.
475
+
476
+ - The CLI prints readable text; `--json` returns a stable machine envelope.
477
+ - MCP exposes exactly one tool named `ucn`. Its `command` enum lists the 18
478
+ tasks, snake_cased where needed (`audit_async`, `project_dir`, `class_name`).
479
+ A persistent MCP process keeps the index warm across calls.
480
+ - Targeted text answers default to a 10K-character budget, broad ones to 3K,
481
+ ceiling 100K (`--max-chars` / `max_chars`). Truncation preserves ACCOUNT,
482
+ CONTRACT, and WARNING lines; JSON is never text-truncated.
483
+
484
+ ```json
485
+ {
486
+ "meta": { "command": "audit-async", "canonicalCommand": "auditAsync", "ok": true, "contract": {} },
487
+ "data": {}
417
488
  }
418
489
  ```
419
490
 
420
- ---
491
+ Failures keep the envelope: `meta.ok: false`, `data: null`, and an `error`
492
+ string, with the command contract when known.
493
+
494
+ ## A cache that stays out of your project
495
+
496
+ The incremental index lives under your user cache root, not in the repo:
497
+ `UCN_CACHE_DIR` if set, else `$XDG_CACHE_HOME/ucn`, `~/Library/Caches/ucn`
498
+ (macOS), `%LOCALAPPDATA%/ucn/cache` (Windows), or `~/.cache/ucn`. Canonical
499
+ path hashes keep same-named checkouts separate. `--no-cache` bypasses,
500
+ `--clear-cache` clears the current project, `--clear-cache --all` clears every
501
+ bounded UCN cache. Old in-project `.ucn-cache` directories are migrated out
502
+ automatically on first use.
503
+
504
+ ## Language coverage
505
+
506
+ All parsers feed the same versioned language IR and index path, and sequential
507
+ and worker builds are tested to produce identical symbols, calls, imports, and
508
+ evidence.
509
+
510
+ - **JavaScript / TypeScript / JSX / TSX** - functions, classes,
511
+ imports/exports, typed receivers, aliases, callbacks, async flow, framework
512
+ roots.
513
+ - **Python** - functions, classes, annotations, decorators, imports,
514
+ comprehensions, context-manager bindings, async flow, framework roots.
515
+ - **Go, Rust, Java** - nominal receivers, methods,
516
+ inheritance/traits/interfaces, package and path ownership, overload/arity
517
+ discipline, framework roots.
518
+ - **C** - functions, structs, macros, includes, calls, entry points, API
519
+ analysis.
520
+ - **C++** - C coverage plus classes, methods, constructors, inheritance,
521
+ namespaces, overloads, templates, typed field receivers.
522
+ - **C#** - namespaces, classes/interfaces/records, fields/properties,
523
+ attributes, overload-aware calls, async flow, top-level programs, .NET stack
524
+ frames, ASP.NET/HttpClient endpoints.
525
+ - **HTML** - inline JavaScript and `on*` event handlers.
526
+
527
+ For C and C++, a `compile_commands.json` improves header-language,
528
+ include-path, and ownership context when available. UCN keeps AST-proven
529
+ definitions from recoverable preprocessor branches without claiming which
530
+ branch a particular build activates.
421
531
 
422
532
  ## Testing and reliability
423
533
 
424
- - **Fast** - uses incremental cache for optimal performance
425
- - **Discipline** - every bug fix gets a regression test, test code is ~3x the source
426
- - **Coverage** - every command, every supported language, every surface (CLI, MCP, interactive)
427
- - **Systematic** - a harness exercises all command and flag combinations against real multi-language fixtures
428
- - **Test types** - unit, integration, per-language regression, formatter, cache, MCP edge cases, architecture parity guards
429
- - **Ground truth** - caller, callee, and oracle-judgable command behavior is measured against ts-morph, pyright, gopls, rust-analyzer, and jdtls. The publish gate uses five representative repositories; the scheduled board uses nineteen plus a rotating fresh-repository arm. Gates track confirmed precision, semantic recall, conservation, observed-zero agreement, oracle configuration coverage, dead-code false positives, isolated startup latency, steady-state latency, and peak memory (see [Answers you can trust](#answers-you-can-trust))
534
+ - **Regression discipline** - every fixed defect gets a focused test.
535
+ - **Surface coverage** - all 18 commands run through CLI text, CLI JSON, and
536
+ MCP; parity between them is guarded by architecture tests.
537
+ - **External ground truth** - real compilers and language servers adjudicate
538
+ caller, callee, command, and dead-code claims on pinned repositories
539
+ ([see the board](#measured-against-ground-truth)).
540
+ - **Release-blocking budgets** - publishing requires 100% in-scope semantic
541
+ recall, ≥98% confirmed precision, a conserved account for every sample, zero
542
+ cross-command disagreements, zero default-arm false-dead claims, and the
543
+ performance gate (≥10K lines/second cold build by wall time and ≥3K by CPU
544
+ time, query p50 ≤75 ms, p95 ≤250 ms, bounded peak RSS), all on the actual
545
+ release board.
430
546
 
431
- ---
547
+ ```bash
548
+ npm run verify # lint + full test suite
549
+ npm run trust:gate # the release board: semantic, dead-code, consistency, performance
550
+ ```
551
+
552
+ Gate runs write their reports under `eval/reports/` as local run artifacts;
553
+ the pinned manifest is [`eval/lib/repos.js`](eval/lib/repos.js). Before a tag,
554
+ the Eval workflow's pre-tag dry run must pass on the actual CI runner.
432
555
 
433
- ## AI Setup
556
+ ## AI setup
557
+
558
+ One tool, 18 commands, compact source-linked answers that keep their trust
559
+ metadata even when truncated.
434
560
 
435
561
  ### MCP
436
562
 
@@ -446,7 +572,7 @@ code --add-mcp '{"name":"ucn","command":"npx","args":["-y","ucn","--mcp"]}'
446
572
  ```
447
573
 
448
574
  <details>
449
- <summary>Or add to the MCP config file manually</summary>
575
+ <summary>Manual MCP configuration</summary>
450
576
 
451
577
  ```json
452
578
  {
@@ -477,6 +603,8 @@ VS Code uses `.vscode/mcp.json`:
477
603
 
478
604
  ### Agent Skill (no server needed)
479
605
 
606
+ macOS / Linux:
607
+
480
608
  ```bash
481
609
  # Claude Code
482
610
  mkdir -p ~/.claude/skills
@@ -487,24 +615,41 @@ mkdir -p ~/.agents/skills
487
615
  cp -r "$(npm root -g)/ucn/.claude/skills/ucn" ~/.agents/skills/
488
616
  ```
489
617
 
490
- ---
618
+ Windows PowerShell:
491
619
 
492
- ## Full help
620
+ ```powershell
621
+ $npmRoot = npm root -g
622
+ New-Item -ItemType Directory -Force "$env:USERPROFILE\.claude\skills"
623
+ Copy-Item -Recurse "$npmRoot\ucn\.claude\skills\ucn" "$env:USERPROFILE\.claude\skills\"
493
624
 
494
- Run `ucn --help` for the full command list and flags.
625
+ New-Item -ItemType Directory -Force "$env:USERPROFILE\.agents\skills"
626
+ Copy-Item -Recurse "$npmRoot\ucn\.claude\skills\ucn" "$env:USERPROFILE\.agents\skills\"
627
+ ```
495
628
 
496
- ---
629
+ The skill teaches an agent how to orient, pin symbols, choose the smallest
630
+ useful command, interpret the evidence tiers, and recover from incomplete
631
+ answers. It's guidance over the same engine, not a second implementation.
497
632
 
498
633
  ## Limitations
499
634
 
500
- - Single-project scope - follows imports within the project, not into `node_modules` or `site-packages`
501
- - No runtime execution - static analysis only
502
- - Reflection (Python `getattr`, Java reflection) is invisible - the target is built at runtime
503
- - Interface/trait dispatch can't be resolved to a single impl, but candidates are surfaced as `possible-dispatch` in the unverified tier, never silently dropped
504
- - JS, TS, and Python method calls on an untyped receiver can't always be proven - UCN surfaces these in the UNVERIFIED tier with a reason rather than dropping or guessing them ([Answers you can trust](#answers-you-can-trust))
505
- - Large repos take a few seconds on the first query, then use cache
506
-
507
- 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.
635
+ - Static, single-project analysis - dependencies like `node_modules` and
636
+ `site-packages` aren't indexed, and nothing is executed.
637
+ - Reflection, generated code, runtime registration, dynamic property access,
638
+ and external consumers can be invisible. UCN reports these blind spots
639
+ (`repo --sections=health --deep`) rather than pretending they don't exist.
640
+ - Interface, trait, template, overload, and untyped-receiver dispatch may stay
641
+ in the UNVERIFIED tier with a reason instead of being guessed.
642
+ - C/C++ analysis doesn't run the preprocessor or compiler; build-specific
643
+ branches, advanced templates, and macro expansion can remain unresolved. C#
644
+ analysis doesn't run Roslyn; source generators and external assembly
645
+ semantics stay outside the index.
646
+ - HTML has regression coverage but no compiler/LSP real-repository oracle.
647
+ - Large repos take a few seconds on the first query, then use the cache.
648
+
649
+ If a decision needs compiler completeness or runtime truth, use the compiler,
650
+ the type checker, the test runner, or a profiler. Those are different tools
651
+ for different jobs. UCN's job is getting you to the right code fast, with
652
+ answers you can audit.
508
653
 
509
654
  ---
510
655