ucn 4.1.2 → 4.2.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.
- package/.claude/skills/ucn/SKILL.md +80 -353
- package/.claude/skills/ucn/references/commands.md +93 -0
- package/.claude/skills/ucn/references/trust-contract.md +63 -0
- package/README.md +67 -44
- package/cli/index.js +24 -19
- package/core/account.js +18 -0
- package/core/analysis.js +27 -7
- package/core/bridge.js +0 -1
- package/core/brief.js +2 -3
- package/core/build-worker.js +5 -0
- package/core/cache.js +55 -6
- package/core/callers.js +1709 -162
- package/core/check.js +107 -19
- package/core/confidence.js +29 -16
- package/core/deadcode.js +84 -24
- package/core/entrypoints.js +32 -27
- package/core/execute.js +19 -3
- package/core/graph-build.js +33 -0
- package/core/output/analysis.js +53 -14
- package/core/output/check.js +11 -0
- package/core/output/doctor.js +40 -19
- package/core/output/endpoints.js +0 -2
- package/core/output/search.js +26 -1
- package/core/parser.js +1 -1
- package/core/project.js +25 -4
- package/core/registry.js +2 -2
- package/core/reporting.js +166 -108
- package/core/search.js +271 -49
- package/core/tracing.js +2 -2
- package/core/trust-matrix.js +158 -0
- package/core/verify.js +0 -1
- package/eslint.config.js +2 -2
- package/languages/go.js +143 -13
- package/languages/html.js +5 -0
- package/languages/index.js +5 -0
- package/languages/java.js +79 -12
- package/languages/javascript.js +233 -42
- package/languages/python.js +54 -15
- package/languages/rust.js +180 -22
- package/languages/utils.js +0 -1
- package/mcp/server.js +118 -38
- package/package.json +11 -4
package/mcp/server.js
CHANGED
|
@@ -123,6 +123,37 @@ const MAX_OUTPUT_CHARS = 100000; // hard ceiling even with max_chars overrid
|
|
|
123
123
|
// Broad commands (derived from registry): output is project-wide, truncation means you need a filter
|
|
124
124
|
const BROAD_COMMANDS = new Set([...BROAD_CANONICAL].map(toMcpName));
|
|
125
125
|
|
|
126
|
+
const CONTRACT_LINE_RE = /^\s*(?:ACCOUNT|CONTRACT|WARNING|FILTERED|CALLEE ACCOUNT|TREE ACCOUNT):/;
|
|
127
|
+
const MAX_PRESERVED_CONTRACT_LINES = 24;
|
|
128
|
+
const MAX_PRESERVED_CONTRACT_CHARS = 8000;
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Keep trust/accounting metadata visible even when the human-readable body is
|
|
132
|
+
* truncated. A first-N slice without this footer can turn a qualified answer
|
|
133
|
+
* into an apparently complete one for an agent.
|
|
134
|
+
*/
|
|
135
|
+
function preservedContractMetadata(fullText, visibleText) {
|
|
136
|
+
const visible = new Set(visibleText.split('\n').map(line => line.trim()));
|
|
137
|
+
const selected = [];
|
|
138
|
+
let selectedChars = 0;
|
|
139
|
+
let omitted = 0;
|
|
140
|
+
|
|
141
|
+
for (const rawLine of fullText.split('\n')) {
|
|
142
|
+
if (!CONTRACT_LINE_RE.test(rawLine)) continue;
|
|
143
|
+
const line = rawLine.trim();
|
|
144
|
+
if (!line || visible.has(line)) continue;
|
|
145
|
+
if (selected.length >= MAX_PRESERVED_CONTRACT_LINES ||
|
|
146
|
+
selectedChars + line.length + 1 > MAX_PRESERVED_CONTRACT_CHARS) {
|
|
147
|
+
omitted++;
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
selected.push(line);
|
|
151
|
+
selectedChars += line.length + 1;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return { lines: selected, omitted, complete: omitted === 0 };
|
|
155
|
+
}
|
|
156
|
+
|
|
126
157
|
function toolResult(text, command, maxChars, suffixNote) {
|
|
127
158
|
const suffix = suffixNote || '';
|
|
128
159
|
if (!text) return { content: [{ type: 'text', text: '(no output)' + suffix }] };
|
|
@@ -135,6 +166,7 @@ function toolResult(text, command, maxChars, suffixNote) {
|
|
|
135
166
|
// Cut at last newline to avoid breaking mid-line
|
|
136
167
|
const lastNewline = truncated.lastIndexOf('\n');
|
|
137
168
|
const cleanCut = lastNewline > limit * 0.8 ? truncated.substring(0, lastNewline) : truncated;
|
|
169
|
+
const contractMetadata = preservedContractMetadata(text, cleanCut);
|
|
138
170
|
// Command-specific narrowing hints
|
|
139
171
|
const hints = {
|
|
140
172
|
toc: 'Use in= to scope to a subdirectory, or detailed=false for compact view.',
|
|
@@ -146,7 +178,25 @@ function toolResult(text, command, maxChars, suffixNote) {
|
|
|
146
178
|
usages: 'Use file= to scope to specific files.',
|
|
147
179
|
};
|
|
148
180
|
const narrow = hints[command] || 'Use file=/in=/exclude= to narrow scope.';
|
|
149
|
-
|
|
181
|
+
let rendered = cleanCut + `\n\n... OUTPUT TRUNCATED: showing ${limit} of ${fullSize} chars. Full output would be ~${fullTokens} tokens. ${narrow} Use all=true to lift formatter caps; the MCP transport still has a 100K character ceiling.`;
|
|
182
|
+
if (contractMetadata.lines.length > 0 || contractMetadata.omitted > 0) {
|
|
183
|
+
rendered += '\n\nPRESERVED CONTRACT METADATA (from omitted output):';
|
|
184
|
+
if (contractMetadata.lines.length > 0) rendered += '\n' + contractMetadata.lines.join('\n');
|
|
185
|
+
if (contractMetadata.omitted > 0) {
|
|
186
|
+
rendered += `\nWARNING: ${contractMetadata.omitted} additional contract line(s) could not fit the preservation budget; narrow scope before acting.`;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
rendered += suffix;
|
|
190
|
+
return {
|
|
191
|
+
content: [{ type: 'text', text: rendered }],
|
|
192
|
+
structuredContent: {
|
|
193
|
+
truncated: true,
|
|
194
|
+
fullChars: fullSize,
|
|
195
|
+
requestedLimit: limit,
|
|
196
|
+
contractMetadata: contractMetadata.lines,
|
|
197
|
+
contractMetadataComplete: contractMetadata.complete,
|
|
198
|
+
},
|
|
199
|
+
};
|
|
150
200
|
}
|
|
151
201
|
return { content: [{ type: 'text', text: text + suffix }] };
|
|
152
202
|
}
|
|
@@ -184,12 +234,12 @@ function resolveAndValidatePath(index, file) {
|
|
|
184
234
|
// CONSOLIDATED TOOL REGISTRATION
|
|
185
235
|
// ============================================================================
|
|
186
236
|
|
|
187
|
-
const
|
|
237
|
+
const VERBOSE_TOOL_DESCRIPTION = `Code intelligence toolkit for AI agents. Extract specific functions, trace call chains, find all callers, and detect dead code without reading entire files or scanning full projects. Use instead of grep/read for code relationships. Supports JavaScript/TypeScript, Python, Go, Rust, Java, and HTML.
|
|
188
238
|
|
|
189
|
-
|
|
239
|
+
COMMON STARTING COMMANDS: orient, about, impact, trace, find
|
|
190
240
|
|
|
191
|
-
QUICK GUIDE
|
|
192
|
-
New/unfamiliar repo → orient (size, top dirs, hot functions, entry points,
|
|
241
|
+
QUICK GUIDE: choosing the right command:
|
|
242
|
+
New/unfamiliar repo → orient (size, top dirs, hot functions, entry points, readiness; run FIRST)
|
|
193
243
|
Understand a symbol → about (everything), context (callers/callees only), smart (code + called functions inline)
|
|
194
244
|
Before modifying → impact (all call sites with args), verify (signature check), plan (preview refactor)
|
|
195
245
|
Execution flow → trace (function call tree) or graph (file imports/exports)
|
|
@@ -199,39 +249,39 @@ QUICK GUIDE — choosing the right command:
|
|
|
199
249
|
Commands:
|
|
200
250
|
|
|
201
251
|
UNDERSTANDING CODE:
|
|
202
|
-
- about <name>: Definition, source, callers, callees, and tests
|
|
252
|
+
- about <name>: Definition, source, callers, callees, and tests in one call. Replaces 3-4 grep+read cycles. Your first stop for any function or class. Pass git=true for last-modified, author, and recent-changes (last 30d).
|
|
203
253
|
- context <name>: Who calls it and what does it call, without source code. Results are numbered for use with expand. For classes/structs, shows all methods instead.
|
|
204
|
-
- impact <name>: Every call site with actual arguments passed, grouped by file.
|
|
205
|
-
- blast <name>: Transitive blast radius
|
|
254
|
+
- impact <name>: Every call site with actual arguments passed, grouped by file. Use it before changing a function signature to see the affected sites.
|
|
255
|
+
- blast <name>: Transitive blast radius through callers of callers. Shows the full chain of functions affected by a change. Use depth (default: 3) to control how far up the chain to walk.
|
|
206
256
|
- smart <name>: Get a function's source with all called functions expanded inline (not constants/variables). Use to understand or modify a function and its dependencies in one read.
|
|
207
|
-
- trace <name>: Call tree from a function downward. Use to understand "what happens when X runs"
|
|
257
|
+
- trace <name>: Call tree from a function downward. Use to understand "what happens when X runs" and which modules a pipeline touches. Set depth (default: 3); setting depth expands all children.
|
|
208
258
|
- example <name>: Best real-world usage example. Automatically scores call sites by quality and returns the top one with context. Use to understand expected calling patterns. Set diverse=true to cluster call sites by argument shape and return one representative per cluster (pair with top=N, default 3).
|
|
209
|
-
- reverse_trace <name>: Upward call chain to entry points
|
|
259
|
+
- reverse_trace <name>: Upward call chain to entry points. Use to find paths that lead to a function. Set depth (default: 5) to control how far up. This complements trace, which goes downward.
|
|
210
260
|
- related <name>: Sibling functions: same file, similar names, or shared callers/callees. Find companions to update together (e.g., serialize when you're changing deserialize). Name-based, not semantic.
|
|
211
261
|
- brief <name>: Compact summary of a function: typed signature, first sentence of docstring, side-effect classification (fs/network/process/global_mutation), complexity (branches, depth, lines). Cheaper than about; more useful than fn when you don't need the body. Pass git=true for last-modified info.
|
|
212
262
|
|
|
213
263
|
FINDING CODE:
|
|
214
264
|
- find <name>: Locate definitions ranked by usage count. Supports glob patterns (e.g. find "handle*" or "_update*"). Use when you know the name but not the file.
|
|
215
265
|
- usages <name>: See every usage organized by type: definitions, calls, imports, references. Complete picture of how something is used. Use code_only=true to skip comments/strings.
|
|
216
|
-
- toc: Get a quick overview of
|
|
266
|
+
- toc: Get a quick overview of an unfamiliar project: file counts, line counts, function/class counts, and entry points. Use detailed=true for full symbol listing.
|
|
217
267
|
- search <term>: Text search (like grep, respects .gitignore). Supports regex by default (e.g. "\\d+" or "foo|bar"). Supports context=N for surrounding lines, exclude/in for file filtering. Case-insensitive by default; set case_sensitive=true for exact case. Invalid regex auto-falls back to plain text. STRUCTURAL MODE: Add type=function|class|call|method|type to query the symbol index instead of text. Combine with param=, returns=, decorator=, receiver= (for calls), exported=true, unused=true. Term becomes optional name filter (glob). Example: type=function, param=Request → all functions taking Request.
|
|
218
268
|
- tests <name>: Find test files covering a function, test case names, and how it's called in tests. Use before modifying or to find test patterns to follow.
|
|
219
269
|
- affected_tests <name>: Which tests to run after changing a function. Combines blast (transitive callers) with test detection. Shows test files, coverage %, and uncovered functions. Use depth= to control depth.
|
|
220
|
-
- deadcode:
|
|
270
|
+
- deadcode: Generate unreferenced-symbol candidates for review. Never treat the result as standalone deletion proof. Exported, decorated, and test symbols are excluded by default; use include_exported/include_decorated/include_tests to expand the audit.
|
|
221
271
|
- entrypoints: Detect framework entry points: routes, handlers, DI providers, tasks. Auto-detects Express, Flask, Spring, Gin, Actix, and more. Use framework= to filter by specific framework.
|
|
222
|
-
- endpoints: HTTP API surface
|
|
272
|
+
- endpoints: HTTP API surface with server routes and client requests. Use bridge=true to match clients to servers across language boundaries; method=/prefix= to filter; server_only/client_only to reduce output.
|
|
223
273
|
|
|
224
274
|
EXTRACTING CODE (use instead of reading entire files):
|
|
225
275
|
- fn <name>: Extract one or more functions. Comma-separated for bulk extraction (e.g. "parse,format,validate"). Use file to disambiguate.
|
|
226
276
|
- class <name>: Extract a class/struct/interface with all its methods. Handles all supported types: JS/TS, Python, Go, Rust, Java. Large classes (>200 lines) show summary; use max_lines for truncated source.
|
|
227
277
|
- lines: Extract specific lines (e.g. range="10-20" or just "15"). Requires file and range. Use when you know the exact line range you need.
|
|
228
|
-
- expand <item>: Drill into a numbered item from the last context result
|
|
278
|
+
- expand <item>: Drill into a numbered item from the last context result. Run context first in the same session. Use expand to see the selected source code.
|
|
229
279
|
|
|
230
280
|
FILE DEPENDENCIES (require file param):
|
|
231
281
|
- imports: All imports with resolved file paths. Use to understand dependencies before modifying or moving a file. Resolves relative, package, and language-specific patterns.
|
|
232
|
-
- exporters: Every file that imports
|
|
282
|
+
- exporters: Every file that imports or depends on this file. Use before moving, renaming, or deleting.
|
|
233
283
|
- file_exports: File's public API: all exported functions, classes, variables with signatures. Use to understand what a module offers before importing. Requires explicit export markers; use toc --detailed as fallback.
|
|
234
|
-
- graph: File-level dependency tree. Use to understand module
|
|
284
|
+
- graph: File-level dependency tree. Use it to understand module clusters and dependency chains. Set direction ("imports"/"importers"/"both"). Use depth=1 for large codebases.
|
|
235
285
|
- circular_deps: Detect circular import chains. Shows cycle paths and involved files. Use file= to check a specific file, exclude= to ignore paths.
|
|
236
286
|
|
|
237
287
|
REFACTORING:
|
|
@@ -241,7 +291,7 @@ REFACTORING:
|
|
|
241
291
|
- check: Pre-commit lint of pending changes against the index. Composes diff_impact + verify + affected_tests; flags ADDED functions with zero callers (ORPHAN), BROKEN_IMPORT, signature drift across call sites, and recommends which tests to run. Use base= to compare against a branch, staged=true for staged changes only.
|
|
242
292
|
|
|
243
293
|
DIAGNOSTICS:
|
|
244
|
-
- doctor:
|
|
294
|
+
- doctor: Task-specific readiness report with index health, semantic blind spots, command proof classification, and navigation/refactor/deletion levels. deep=true adds a stratified resolution-evidence profile; it is not an accuracy estimate. Use in= to scope to a subtree.
|
|
245
295
|
- orient: One-screen repo orientation for a codebase you just entered: size + language mix, densest directories, most-called functions, entry-point counts, and the trust verdict. Best FIRST command in a new repo.
|
|
246
296
|
|
|
247
297
|
OTHER:
|
|
@@ -252,12 +302,43 @@ OTHER:
|
|
|
252
302
|
- audit_async: Find async calls inside async functions that are likely missing await (probable bugs). JS/TS/Python only. Filter with file/exclude/limit.
|
|
253
303
|
|
|
254
304
|
READING OUTPUT (trust contract):
|
|
255
|
-
- Caller/impact answers
|
|
256
|
-
- A
|
|
257
|
-
- WARNING lines list unparsed files containing the symbol
|
|
305
|
+
- Caller/impact answers partition literal-name text lines. CONFIRMED entries carry binding/receiver/import evidence; UNVERIFIED entries are possible callers without target proof. ACCOUNT reconciles that text ground set. CONTRACT states the boundary explicitly.
|
|
306
|
+
- A zero account is an observed-text zero, not proof of zero semantic callers: aliases, indirect calls, generated code, and runtime dispatch can exist. Never use it alone as safe-delete evidence; review usages/deadcode, warnings, and tests.
|
|
307
|
+
- WARNING lines list unparsed files containing the symbol. Their lines were not analyzed; fall back to text search there.
|
|
258
308
|
- verify arg-checks and plan plans CONFIRMED sites only; their UNVERIFIED CALL SITES sections list candidates to review manually. check reports "N callers (+M unverified)" per changed function.
|
|
259
309
|
- context/smart/trace also account the callee side (CALLEE ACCOUNT line + unverified callees with reasons).
|
|
260
|
-
- Advisory commands (related, example, stacktrace, endpoints bridge=true) mark output "Advisory:"
|
|
310
|
+
- Advisory commands (related, example, stacktrace, endpoints bridge=true) mark output "Advisory:". These are ranked heuristics, not verified claims. Other semantic answers expose their evidence/account boundaries.` + generateMcpParamSection();
|
|
311
|
+
|
|
312
|
+
const CONCISE_TOOL_DESCRIPTION = `AST code intelligence for JavaScript/TypeScript, Python, Go, Rust, Java, and HTML.
|
|
313
|
+
|
|
314
|
+
Start:
|
|
315
|
+
- orient: repo map and task-specific readiness.
|
|
316
|
+
- find: definitions by name; brief: cheap signature/summary; about: compact symbol card.
|
|
317
|
+
|
|
318
|
+
Understand and change:
|
|
319
|
+
- context: direct callers/callees. impact: caller sites and arguments.
|
|
320
|
+
- trace: downward execution tree. reverse_trace/blast: upward/transitive impact.
|
|
321
|
+
- fn/class/lines: extract only the source needed. smart: target plus dependencies.
|
|
322
|
+
- verify: confirmed-site arity check. plan: refactor preview. check/diff_impact: change preflight.
|
|
323
|
+
- tests/affected_tests: relevant tests. usages: all AST usage kinds. deadcode: conservative candidate list.
|
|
324
|
+
|
|
325
|
+
Architecture and search:
|
|
326
|
+
- toc/stats/api/entrypoints: project surface. imports/exporters/file_exports/graph/circular_deps: file graph.
|
|
327
|
+
- search: text or structural query. endpoints: server/client HTTP surface. typedef: types.
|
|
328
|
+
- example/related/stacktrace/endpoints bridge=true are advisory heuristics.
|
|
329
|
+
|
|
330
|
+
Trust contract:
|
|
331
|
+
- CONFIRMED means binding/receiver/import evidence supports this target. UNVERIFIED means possible target; review it before a breaking change.
|
|
332
|
+
- ACCOUNT conserves literal-name text lines only. CONTRACT states whether that partition is complete and always warns that semantic completeness is not proven. A clean zero is observed-text zero, never standalone deletion proof.
|
|
333
|
+
- WARNING means parse/read blind spots. FILTERED means the displayed answer intentionally hides accounted entries.
|
|
334
|
+
- Numeric confidence fields are ordinal evidence weights, not probabilities or calibrated accuracy.
|
|
335
|
+
- MCP defaults about/context/impact to compact output; set compact=false for expressions/source. Truncated responses preserve contract metadata and expose structuredContent.truncated.
|
|
336
|
+
- Use doctor deep=true for task readiness plus a stratified evidence profile; the profile is not an accuracy measurement.
|
|
337
|
+
` + generateMcpParamSection();
|
|
338
|
+
|
|
339
|
+
const TOOL_DESCRIPTION = process.env.UCN_MCP_VERBOSE_DESCRIPTION === '1'
|
|
340
|
+
? VERBOSE_TOOL_DESCRIPTION
|
|
341
|
+
: CONCISE_TOOL_DESCRIPTION;
|
|
261
342
|
|
|
262
343
|
server.registerTool(
|
|
263
344
|
'ucn',
|
|
@@ -271,12 +352,12 @@ server.registerTool(
|
|
|
271
352
|
exclude: z.string().optional().describe('Comma-separated patterns to exclude (e.g. "test,mock,vendor")'),
|
|
272
353
|
include_tests: z.boolean().optional().describe('Include test files in results (excluded by default)'),
|
|
273
354
|
exclude_tests: z.boolean().optional().describe('Exclude test files from results. Used by entrypoints (where tests are included by default).'),
|
|
274
|
-
include_methods: z.boolean().optional().describe('Include obj.method() callee expansion in trace/blast. No effect on about/context/impact/verify
|
|
275
|
-
include_uncertain: z.boolean().optional().describe('No effect on tiered commands (about/context/impact/trace/blast/reverse_trace/affected_tests/verify/smart)
|
|
276
|
-
expand_unverified: z.boolean().optional().describe('blast/reverse_trace: follow unverified caller edges in the tree
|
|
277
|
-
min_confidence: z.number().min(0).max(1).optional().describe('Minimum
|
|
278
|
-
show_confidence: z.boolean().optional().describe('Show
|
|
279
|
-
hide_confidence: z.boolean().optional().describe('Hide
|
|
355
|
+
include_methods: z.boolean().optional().describe('Include obj.method() callee expansion in trace/blast. No effect on about/context/impact/verify; method calls are always analyzed and tiered by receiver evidence'),
|
|
356
|
+
include_uncertain: z.boolean().optional().describe('No effect on tiered commands (about/context/impact/trace/blast/reverse_trace/affected_tests/verify/smart); unverified candidates are always shown with reasons'),
|
|
357
|
+
expand_unverified: z.boolean().optional().describe('blast/reverse_trace: follow unverified caller edges in the tree; downstream nodes are marked as possible, not confirmed, impact chains'),
|
|
358
|
+
min_confidence: z.number().min(0).max(1).optional().describe('Minimum ordinal evidence weight (legacy name; not a probability) for caller/callee edges'),
|
|
359
|
+
show_confidence: z.boolean().optional().describe('Show resolution-evidence labels. Numeric weights are ordinal, not probabilities.'),
|
|
360
|
+
hide_confidence: z.boolean().optional().describe('Hide resolution-evidence labels (alias of show_confidence=false).'),
|
|
280
361
|
unreachable_only: z.boolean().optional().describe('Show only callers/callees that are unreachable from any detected entry point (about, context, impact).'),
|
|
281
362
|
with_types: z.boolean().optional().describe('Include related type definitions in output'),
|
|
282
363
|
detailed: z.boolean().optional().describe('Show full symbol listing per file'),
|
|
@@ -306,16 +387,16 @@ server.registerTool(
|
|
|
306
387
|
range: z.string().optional().describe('Line range to extract, e.g. "10-20" or "15" (lines command)'),
|
|
307
388
|
base: z.string().optional().describe('Git ref to diff against (default: HEAD). E.g. "HEAD~3", "main", a commit SHA'),
|
|
308
389
|
staged: z.boolean().optional().describe('Analyze staged changes (diff_impact command)'),
|
|
309
|
-
deep: z.boolean().optional().describe('Run
|
|
310
|
-
compact: z.boolean().optional().describe('
|
|
390
|
+
deep: z.boolean().optional().describe('Run deeper analysis (doctor: sample the ordinal resolution-evidence profile, not accuracy)'),
|
|
391
|
+
compact: z.boolean().optional().describe('Token-efficient output for about/context/impact. Defaults true on MCP; set false when source expressions are required.'),
|
|
311
392
|
case_sensitive: z.boolean().optional().describe('Case-sensitive search (default: false, case-insensitive)'),
|
|
312
393
|
all: z.boolean().optional().describe('Show all results (expand truncated sections). Applies to about, toc, related, trace, and others.'),
|
|
313
394
|
top_level: z.boolean().optional().describe('Show only top-level functions in toc (exclude nested/indented)'),
|
|
314
395
|
class_name: z.string().optional().describe('Class name to scope method analysis (e.g. "MarketDataFetcher" for close)'),
|
|
315
|
-
line: z.number().int().positive().optional().describe('Definition line pin
|
|
396
|
+
line: z.number().int().positive().optional().describe('Definition line pin. Resolves the symbol defined at this exact line (the middle component of a file:line:name handle). Disambiguates same-file same-name definitions.'),
|
|
316
397
|
limit: z.number().int().positive().max(1000000).optional().describe('Max results to return (default: 500). Caps find, usages, search, deadcode, api, toc --detailed. Must be a positive integer.'),
|
|
317
398
|
max_files: z.number().int().positive().max(10000000).optional().describe('Max files to index (default: 10000). Use for very large codebases. Must be a positive integer.'),
|
|
318
|
-
max_chars: z.number().int().positive().max(
|
|
399
|
+
max_chars: z.number().int().positive().max(100000).optional().describe('Max output chars before truncation. Targeted commands default to 10K; broad commands default to 3K. Maximum: 100K. all=true lifts formatter caps but keeps the 100K transport ceiling.'),
|
|
319
400
|
// Structural search flags (search command)
|
|
320
401
|
type: z.string().optional().describe('Symbol type filter for structural search: function, class, call, method, type. Triggers index-based search.'),
|
|
321
402
|
param: z.string().optional().describe('Filter by parameter name or type (structural search). E.g. "Request", "ctx".'),
|
|
@@ -331,18 +412,17 @@ server.registerTool(
|
|
|
331
412
|
server_only: z.boolean().optional().describe('Only list server routes (endpoints command).'),
|
|
332
413
|
client_only: z.boolean().optional().describe('Only list client requests (endpoints command).'),
|
|
333
414
|
unmatched: z.boolean().optional().describe('Only show unmatched routes/requests (endpoints command).'),
|
|
334
|
-
method: z.string().optional().describe('Filter by HTTP method (e.g. "GET", "POST")
|
|
415
|
+
method: z.string().optional().describe('Filter by HTTP method (e.g. "GET", "POST") for endpoints.'),
|
|
335
416
|
prefix: z.string().optional().describe('Filter routes/requests by path prefix (endpoints command).'),
|
|
336
417
|
hide_uncertain: z.boolean().optional().describe('Hide uncertain (interpolated-path) bridges (endpoints command).')
|
|
337
418
|
|
|
338
419
|
})
|
|
339
420
|
},
|
|
340
421
|
async (args) => {
|
|
341
|
-
const { command, project_dir } = args;
|
|
422
|
+
const { command, project_dir, ...rawParams } = args;
|
|
342
423
|
|
|
343
424
|
// Normalize ALL params once — execute() handlers pick what they need.
|
|
344
425
|
// This eliminates per-case param selection and prevents CLI/MCP drift.
|
|
345
|
-
const { command: _c, project_dir: _p, ...rawParams } = args;
|
|
346
426
|
const ep = normalizeParams(rawParams);
|
|
347
427
|
|
|
348
428
|
// Translate hide_confidence → showConfidence:false (canonical inverse).
|
|
@@ -373,7 +453,7 @@ server.registerTool(
|
|
|
373
453
|
}
|
|
374
454
|
}
|
|
375
455
|
|
|
376
|
-
// all=true
|
|
456
|
+
// all=true lifts formatter caps and raises MCP output to its hard ceiling.
|
|
377
457
|
const maxChars = ep.all ? MAX_OUTPUT_CHARS : ep.maxChars;
|
|
378
458
|
|
|
379
459
|
// Build stripping note (appended inside truncation boundary on success paths)
|
|
@@ -418,7 +498,7 @@ server.registerTool(
|
|
|
418
498
|
let aboutText = output.formatAbout(result, {
|
|
419
499
|
allHint: 'Repeat with all=true to show all.',
|
|
420
500
|
showConfidence: ep.showConfidence !== false,
|
|
421
|
-
compact: ep.compact,
|
|
501
|
+
compact: ep.compact !== false,
|
|
422
502
|
});
|
|
423
503
|
if (note) aboutText += '\n\n' + mn(note);
|
|
424
504
|
return tr(aboutText);
|
|
@@ -431,7 +511,7 @@ server.registerTool(
|
|
|
431
511
|
const { text, expandable } = output.formatContext(ctx, {
|
|
432
512
|
expandHint: 'Use expand command with item number to see code for any item.',
|
|
433
513
|
showConfidence: ep.showConfidence !== false,
|
|
434
|
-
compact: ep.compact,
|
|
514
|
+
compact: ep.compact !== false,
|
|
435
515
|
});
|
|
436
516
|
expandCacheInstance.save(index.root, ep.name, ep.file, expandable);
|
|
437
517
|
let ctxText = text;
|
|
@@ -443,7 +523,7 @@ server.registerTool(
|
|
|
443
523
|
index = getIndex(project_dir, ep);
|
|
444
524
|
const { ok, result, error, note } = execute(index, 'impact', ep);
|
|
445
525
|
if (!ok) return te(error);
|
|
446
|
-
let impactText = output.formatImpact(result, { compact: ep.compact });
|
|
526
|
+
let impactText = output.formatImpact(result, { compact: ep.compact !== false });
|
|
447
527
|
if (note) impactText += '\n\n' + mn(note);
|
|
448
528
|
return tr(impactText);
|
|
449
529
|
}
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ucn",
|
|
3
|
-
"version": "4.1
|
|
3
|
+
"version": "4.2.1",
|
|
4
4
|
"mcpName": "io.github.mleoca/ucn",
|
|
5
|
-
"description": "Code intelligence toolkit for AI agents
|
|
5
|
+
"description": "Code intelligence toolkit for AI agents: extract functions, trace call chains, find callers, and detect dead code without reading entire files. Works as MCP server, CLI, or agent skill. Supports JS/TS, Python, Go, Rust, Java.",
|
|
6
6
|
"main": "index.js",
|
|
7
7
|
"bin": {
|
|
8
8
|
"ucn": "cli/index.js",
|
|
@@ -10,12 +10,19 @@
|
|
|
10
10
|
},
|
|
11
11
|
"scripts": {
|
|
12
12
|
"version": "node scripts/sync-server-version.js && git add server.json",
|
|
13
|
-
"test": "node --test test/parser-unit.test.js test/integration.test.js test/cache.test.js test/formatter.test.js test/interactive.test.js test/feature.test.js test/regression-js.test.js test/regression-py.test.js test/regression-go.test.js test/regression-java.test.js test/regression-rust.test.js test/regression-cross.test.js test/regression-mcp.test.js test/regression-parser.test.js test/regression-commands.test.js test/regression-fixes.test.js test/regression-bugfixes.test.js test/cross-language.test.js test/accuracy.test.js test/command-coverage.test.js test/perf-optimizations.test.js test/systematic-test.js test/mcp-edge-cases.js test/conservation.test.js test/parity-test.js",
|
|
13
|
+
"test": "node --test test/parser-unit.test.js test/integration.test.js test/cache.test.js test/formatter.test.js test/interactive.test.js test/feature.test.js test/regression-js.test.js test/regression-py.test.js test/regression-go.test.js test/regression-java.test.js test/regression-rust.test.js test/regression-cross.test.js test/regression-mcp.test.js test/regression-parser.test.js test/regression-commands.test.js test/regression-fixes.test.js test/regression-bugfixes.test.js test/cross-language.test.js test/accuracy.test.js test/command-coverage.test.js test/perf-optimizations.test.js test/performance-gate-policy.test.js test/oracle-gate-policy.test.js test/systematic-test.js test/mcp-edge-cases.js test/conservation.test.js test/parity-test.js test/trust-matrix.test.js",
|
|
14
14
|
"benchmark:agent": "node test/agent-understanding-benchmark.js",
|
|
15
15
|
"eval:conservation": "node eval/conservation-real.js",
|
|
16
16
|
"eval:oracle": "node eval/run-oracle-eval.js",
|
|
17
17
|
"eval:deadcode": "node eval/run-deadcode-eval.js",
|
|
18
|
-
"
|
|
18
|
+
"eval:performance": "node --expose-gc eval/run-performance-gate.js",
|
|
19
|
+
"lint": "eslint core/ cli/ mcp/ languages/ eval/run-*.js eval/*-gate-policy.js",
|
|
20
|
+
"verify": "npm run lint && npm test",
|
|
21
|
+
"trust:gate:semantic": "node eval/run-oracle-eval.js --repo preact-signals,httpx,cobra,clap,javapoet --min-precision 0.98 --max-unscored-ratio 0.10",
|
|
22
|
+
"trust:gate:deadcode": "node eval/run-deadcode-eval.js --repo preact-signals,httpx,cobra,clap,javapoet --sample 100 --arm default",
|
|
23
|
+
"trust:gate:performance": "node --expose-gc eval/run-performance-gate.js --repo preact-signals,httpx,cobra,clap,javapoet --queries 40",
|
|
24
|
+
"trust:gate:fast": "node eval/run-oracle-eval.js --repo preact-signals,httpx --min-precision 0.98 && node --expose-gc eval/run-performance-gate.js --repo preact-signals,httpx --queries 20",
|
|
25
|
+
"trust:gate": "npm run trust:gate:semantic && npm run trust:gate:deadcode && npm run trust:gate:performance"
|
|
19
26
|
},
|
|
20
27
|
"keywords": [
|
|
21
28
|
"mcp",
|