ucn 4.2.3 → 5.0.2

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 +438 -305
  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 -140
  13. package/core/cache.js +513 -11
  14. package/core/callers.js +4920 -456
  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 +397 -19
  20. package/core/discovery.js +359 -46
  21. package/core/entrypoints.js +195 -41
  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 +212 -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 -187
  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 +317 -185
  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 +396 -13
  65. package/languages/javascript.js +199 -19
  66. package/languages/python.js +964 -22
  67. package/languages/rust.js +1317 -152
  68. package/languages/utils.js +40 -3
  69. package/mcp/server.js +254 -636
  70. package/package.json +39 -22
  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,194 +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 seven-repository board; the scheduled board covers nineteen pinned repositories 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
139
-
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
119
+ ## Follow the execution path
144
120
 
145
- CALL SITES: 2 confirmed + 11 unverified
121
+ What happens when `build()` runs?
146
122
 
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.
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):
175
204
 
176
- Current pinned release-board results:
177
-
178
- | Repository | Pinned commit | Language oracle | Confirmed caller precision | Caller recall | Callee precision/recall | Command checks |
205
+ | Repository | Pinned commit | Oracle | Caller precision | Caller recall | Callee prec / recall | Command checks |
179
206
  |---|---|---|---:|---:|---:|---:|
180
207
  | [preact-signals](https://github.com/preactjs/signals) | [`e0ce9fdf`](https://github.com/preactjs/signals/commit/e0ce9fdf92df7f0ece2c89d44554c39f36dc6882) | ts-morph | 100% | 100% | 100% / 100% | 100% |
181
- | [httpx](https://github.com/encode/httpx) | [`b5addb64`](https://github.com/encode/httpx/commit/b5addb64f0161ff6bfe94c124ef76f6a1fba5254) | pyright | 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% |
182
209
  | [cobra](https://github.com/spf13/cobra) | [`ad460ea8`](https://github.com/spf13/cobra/commit/ad460ea8f249db69c943a365fb84f3a59042d54e) | gopls | 100% | 100% | 100% / 100% | 100% |
183
210
  | [viper](https://github.com/spf13/viper) | [`528f7416`](https://github.com/spf13/viper/commit/528f7416c4b56a4948673984b190bf8713f0c3c4) | gopls | 100% | 100% | 100% / 100% | 100% |
184
211
  | [ripgrep](https://github.com/BurntSushi/ripgrep) | [`82313cf9`](https://github.com/BurntSushi/ripgrep/commit/82313cf95849bfe425109ad9506a52154879b1b1) | rust-analyzer | 100% | 100% | 100% / 100% | 100% |
185
212
  | [clap](https://github.com/clap-rs/clap) | [`d3e59a9a`](https://github.com/clap-rs/clap/commit/d3e59a9ab214910b9dad02921b7ef42c6400de9b) | rust-analyzer | 100% | 100% | 100% / 100% | 100% |
186
- | [javapoet](https://github.com/square/javapoet) | [`b9017a95`](https://github.com/square/javapoet/commit/b9017a9503b76e11b4ad4c1a9f050e2d29112cb0) | jdtls | 100% | 100% | 100% / 100% | 100% |
187
-
188
- Each semantic run draws a deterministic, reference-count-stratified sample of up to 50 compiler/LSP symbols per repository. The command checks cover exact definition lookup, `find`, `fn`/`class`, `brief`, `typedef`, `usages`, `tests`, and `example` against that same external-oracle population. The dead-code arm audits up to 100 claims per release repository and currently has zero false-dead claims. The semantic gate caps configuration-unscored confirmed caller and callee evidence at 10%, so platform filtering cannot silently make a small scored precision subset look representative. Configuration-gated unverified abstentions are reported separately and never presented as confirmed evidence. 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.
189
-
190
- Evidence: [semantic rollup](eval/reports/oracle-eval-rollup-2026-07-20.md), [dead-code rollup](eval/reports/deadcode-eval-rollup-2026-07-20.md), and [performance rollup](eval/reports/performance-gate-2026-07-20.md). Reproduce all three release gates with `npm run trust:gate`; the pinned source manifest is [`eval/lib/repos.js`](eval/lib/repos.js).
191
-
192
- 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.
193
-
194
- The scheduled board covers nineteen pinned repositories, multiple sampling seeds, and a rotating fresh-repository arm. It is broader than the seven-repository publish gate and is used to expose regressions and overfitting:
195
-
196
- - JavaScript and TypeScript: zod, preact-signals, express, hono, zustand, fastify
197
- - Python: httpx, rich, click
198
- - Go: cobra, grpc-go, viper, chi
199
- - Rust: ripgrep, cursive, clap
200
- - Java: gson, javapoet, jsoup
201
-
202
- Repos that expose a gap remain on this scheduled board; they are not removed to preserve a perfect release table. HTML has parser, integration, and cross-surface regression coverage, but no compiler/LSP real-repository oracle is claimed. 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.
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.
203
244
 
204
245
  ## Change code without breaking things
205
246
 
206
- 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:
207
248
 
208
- ```
209
- $ ucn verify expandGlob
249
+ ```text
250
+ $ ucn check expandGlob
210
251
 
211
252
  Verification: expandGlob
212
253
  ════════════════════════════════════════════════════════════
213
- core/discovery.js:191
254
+ core/discovery.js:314
214
255
  expandGlob (pattern: string, options: number = {}) : string[]
215
256
 
216
257
  Expected arguments: 1-2
@@ -221,228 +262,301 @@ STATUS: ✓ All calls valid
221
262
  Mismatches: 0
222
263
  Uncertain: 0
223
264
  Patterns: 4 in try, 4 in callback
224
- ```
225
265
 
226
- 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
+ ```
227
269
 
228
- 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:
229
273
 
230
- ```
274
+ ```text
231
275
  $ ucn plan expandGlob --rename-to=expandGlobPattern
232
276
 
233
277
  Refactoring plan: rename
234
278
  ════════════════════════════════════════════════════════════
235
- core/discovery.js:191
279
+ core/discovery.js:314
236
280
 
237
281
  SIGNATURE CHANGE:
238
282
  Before: expandGlob (pattern: string, options: number = {}) : string[]
239
283
  After: expandGlobPattern (pattern: string, options: number = {}) : string[]
240
284
 
241
- CHANGES NEEDED: 11
242
- 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
243
288
 
244
289
  BY FILE:
245
290
 
246
291
  cli/index.js (2 changes)
247
- :1190
292
+ :771 [call]
248
293
  const files = expandGlob(pattern);
249
294
  → Rename to: const files = expandGlobPattern(pattern);
250
- :15
295
+ :15 [import]
251
296
  const { expandGlob, findProjectRoot } = require('../core/discovery');
252
297
  → Update import: const { expandGlobPattern, findProjectRoot } = require('../core/discovery');
253
298
 
254
- ... (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)
255
300
  ```
256
301
 
257
- Run `ucn diff-impact --staged` before committing to see what you changed and who calls it.
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:
258
305
 
259
- Or wrap the same checks in a single command:
260
-
261
- ```
262
- $ ucn check --staged
263
-
264
- Pre-commit Check vs HEAD
265
- ════════════════════════════════════════════════════════════
266
- Changed: 3 functions
267
- parseFlags (cli/index.js:165) [MODIFIED] 2 callers
268
- ...
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
269
309
  ```
270
310
 
271
- `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.
272
-
273
- ## Get the lay of the land in a new repo
311
+ ## Pick the right tests
274
312
 
275
- 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.
313
+ Which tests actually exercise this function, directly or three hops away?
276
314
 
277
- ```
278
- $ ucn orient
315
+ ```text
316
+ $ ucn tests expandGlob --depth=3
279
317
 
280
- PROJECT ORIENTATION: /path/to/project
318
+ affected-tests: expandGlob
281
319
  ════════════════════════════════════════════════════════════
282
- 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)
283
322
 
284
- TOP DIRS (by symbols):
285
- core 516 symbols · 29 file(s)
286
- languages 282 symbols · 8 file(s)
287
- test 209 symbols · 29 file(s)
288
- core/output 142 symbols · 14 file(s)
289
- ...
323
+ Test files to run (30):
290
324
 
291
- HOT (most-called production functions, top 8 of 1028):
292
- execute: 1124 call(s) · core/execute.js:1608
293
- ProjectIndex.build: 340 call(s) · core/project.js:221
294
- 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]
295
329
  ...
296
330
 
297
- ENTRY POINTS: 389; test 284, runtime 72, http 32, di 1
298
- TRUST: MEDIUM; 41 dynamic import(s), 13 eval, 6 reflection (ucn doctor for detail)
299
-
300
- 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)
301
332
  ```
302
333
 
303
- 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.
304
338
 
305
- ```
306
- $ ucn brief fetch_user
307
- fetch_user(user_id: int): dict
308
- svc.py:4-8 (5 lines)
309
- "Fetch a user from the API."
310
- async: no | side_effects: [fs, network, process] | complexity: branches=2, depth=2
311
- ```
312
-
313
- `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.
314
-
315
- ```
316
- $ ucn doctor
317
-
318
- UCN Trust Report: /path/to/project
319
- Index: 169 files, 2104 symbols
320
- Languages: javascript (72%), typescript (14%), java (4%), python (4%), rust (4%), go (3%)
321
- Cache: fresh, 344ms build
322
- Command proofs: 39/39 classified, 22 external-oracle-backed, 0 unclassified
323
-
324
- Readiness:
325
- navigation: HIGH: fresh index; no parse failures
326
- refactor: UNKNOWN: run --deep; review unverified and non-call occurrences
327
- deletion: REVIEW: usages, public API, compiler, and tests are still required
328
- ```
329
-
330
- `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.
339
+ ## Get the lay of the land
331
340
 
332
- `entrypoints` lists detected framework handlers (HTTP routes, DI beans, jobs, tests):
341
+ One command answers "what is this codebase?" Here it is on ripgrep:
333
342
 
334
- ```
335
- ucn entrypoints --type=http --framework=spring # narrow to one framework
336
- ucn entrypoints --exclude-tests # tests are included by default
337
- ```
338
-
339
- ## Find what to clean up
340
-
341
- Which tests should you run after a change? `affected-tests` walks the blast radius and finds every test that touches the affected functions:
342
-
343
- ```
344
- $ ucn affected-tests expandGlob
343
+ ```text
344
+ $ ucn repo
345
345
 
346
- affected-tests: expandGlob
346
+ PROJECT ORIENTATION — ripgrep
347
347
  ════════════════════════════════════════════════════════════
348
- core/discovery.js:191
349
- 1 function changed → 15 functions affected (depth 3)
350
-
351
- Test files to run (20):
348
+ 100 files · 4755 symbols · language mix by symbols: rust 100%
352
349
 
353
- test/integration.test.js (covers: expandGlob, build, idx, setupProject)
354
- L47: index.build(null, { quiet: true }); [call]
355
- L167: const files = expandGlob('**/*.go', { root: tmpDir }); [call]
356
- ...
357
-
358
- POSSIBLY AFFECTED (1): reachable only through unverified call edges
359
- 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
+ ...
360
362
 
361
- Uncovered (12): runGlobCommand, main, isCacheStale, runProjectCommand, runFileCommand, ...
362
- ⚠ 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.
363
366
 
364
- 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
365
368
  ```
366
369
 
367
- 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.
368
374
 
369
- ## Find unused code
375
+ ## Find dead code you can act on
370
376
 
371
- ```
377
+ ```text
372
378
  $ ucn deadcode --exclude=test # run on ripgrep
373
379
 
374
- Dead code: 8 unused symbol(s)
380
+ Dead code: 3 unused symbol(s)
375
381
 
376
382
  crates/globset/src/serde_impl.rs
377
383
  [ 38- 42] Glob.deserialize (method)
378
384
  [ 70- 74] GlobSet.deserialize (method)
379
385
  crates/matcher/src/lib.rs
380
386
  [ 397- 399] Captures.as_match (method)
381
- [ 669- 678] Matcher.try_find_iter (method) [only self-references, recursive]
382
- [ 796- 806] Matcher.try_captures_iter (method) [only self-references, recursive]
383
- ...
384
387
 
385
- 921 exported symbol(s) excluded from the audit (public API may have external callers). Use --include-exported to audit them.
386
- ```
387
-
388
- 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.
388
+ 33 decorated/annotated symbol(s) hidden (framework-registered). Use --include-decorated to include them.
389
389
 
390
- Find missing-await bugs:
390
+ 903 exported symbol(s) excluded from the audit (public API may have external callers). Use --include-exported to audit them.
391
391
 
392
- ```
393
- ucn audit-async
392
+ WARNING: source coverage is incomplete (5 unsupported-language); 17 candidate name(s) found in skipped source were suppressed.
394
393
  ```
395
394
 
396
- 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.
397
402
 
398
- ## 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).
399
405
 
400
- 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
401
407
 
402
408
  ```bash
403
- ucn endpoints --bridge
404
-
405
- # Filters
406
- ucn endpoints --bridge --unmatched # routes with no client / clients with no server
407
- ucn endpoints --bridge --method=POST
408
- 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
409
415
  ```
410
416
 
411
- 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.
412
423
 
413
- ## Extract without reading the whole file
424
+ ## Extract and search without opening whole files
414
425
 
415
- ```
416
- $ ucn fn compareNames
417
-
418
- core/discovery.js:170
419
- [ 170- 178] compareNames(a, b)
420
- ────────────────────────────────────────────────────────────
421
- function compareNames(a, b) {
422
- const aLower = a.toLowerCase();
423
- const bLower = b.toLowerCase();
424
- if (aLower < bLower) return -1;
425
- if (aLower > bLower) return 1;
426
- if (a < b) return -1;
427
- if (a > b) return 1;
428
- 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": {}
429
488
  }
430
489
  ```
431
490
 
432
- ---
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.
433
531
 
434
532
  ## Testing and reliability
435
533
 
436
- - **Fast** - uses incremental cache for optimal performance
437
- - **Discipline** - every bug fix gets a regression test, test code is ~3x the source
438
- - **Coverage** - every command, every supported language, every surface (CLI, MCP, interactive)
439
- - **Systematic** - a harness exercises all command and flag combinations against real multi-language fixtures
440
- - **Test types** - unit, integration, per-language regression, formatter, cache, MCP edge cases, architecture parity guards
441
- - **Ground truth** - caller, callee, and oracle-judgable command behavior is measured against ts-morph, pyright, gopls, rust-analyzer, and jdtls. The publish gate uses seven representative repositories; the scheduled board uses nineteen pinned repositories 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.
442
546
 
443
- ---
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.
444
555
 
445
- ## AI Setup
556
+ ## AI setup
557
+
558
+ One tool, 18 commands, compact source-linked answers that keep their trust
559
+ metadata even when truncated.
446
560
 
447
561
  ### MCP
448
562
 
@@ -458,7 +572,7 @@ code --add-mcp '{"name":"ucn","command":"npx","args":["-y","ucn","--mcp"]}'
458
572
  ```
459
573
 
460
574
  <details>
461
- <summary>Or add to the MCP config file manually</summary>
575
+ <summary>Manual MCP configuration</summary>
462
576
 
463
577
  ```json
464
578
  {
@@ -489,6 +603,8 @@ VS Code uses `.vscode/mcp.json`:
489
603
 
490
604
  ### Agent Skill (no server needed)
491
605
 
606
+ macOS / Linux:
607
+
492
608
  ```bash
493
609
  # Claude Code
494
610
  mkdir -p ~/.claude/skills
@@ -499,24 +615,41 @@ mkdir -p ~/.agents/skills
499
615
  cp -r "$(npm root -g)/ucn/.claude/skills/ucn" ~/.agents/skills/
500
616
  ```
501
617
 
502
- ---
618
+ Windows PowerShell:
503
619
 
504
- ## 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\"
505
624
 
506
- 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
+ ```
507
628
 
508
- ---
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.
509
632
 
510
633
  ## Limitations
511
634
 
512
- - Single-project scope - follows imports within the project, not into `node_modules` or `site-packages`
513
- - No runtime execution - static analysis only
514
- - Reflection (Python `getattr`, Java reflection) is invisible - the target is built at runtime
515
- - 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
516
- - 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))
517
- - Large repos take a few seconds on the first query, then use cache
518
-
519
- 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.
520
653
 
521
654
  ---
522
655