ucn 5.4.0 → 5.4.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.
package/core/verify.js CHANGED
@@ -247,14 +247,26 @@ function classifyCallContext(callNode, language) {
247
247
  /**
248
248
  * Find a call expression node at the target line matching funcName
249
249
  */
250
- function findCallNode(node, callTypes, targetRow, funcName, occurrence = 0) {
250
+ function findCallNode(node, callTypes, targetRow, funcName, occurrence = 0, site = null) {
251
251
  // Several same-name calls can share one line (`greet("a") + greet("b")`,
252
252
  // f-strings) — fix #231: callers pass the site's per-line ordinal so each
253
253
  // record is arg-checked against ITS OWN node, not the line's first.
254
254
  // Records and this walk are both pre-order, so ordinals align; an
255
255
  // out-of-range ordinal falls back to the first match (never worse than
256
256
  // the pre-fix behavior when a parse shape hides a node).
257
- const matches = _collectCallNodes(node, callTypes, targetRow, funcName, occurrence + 1);
257
+ const matches = _collectCallNodes(node, callTypes, targetRow, funcName, site ? Infinity : occurrence + 1);
258
+ if (Number.isInteger(site?.start)) {
259
+ // The resolver already identified the callee token. Preserve that
260
+ // identity through argument extraction, including aliases and several
261
+ // calls on one line (some of which may target another declaration).
262
+ const exact = matches.filter(candidate => {
263
+ const callee = candidate.childForFieldName('function') ||
264
+ candidate.childForFieldName('name') ||
265
+ candidate.childForFieldName('constructor') || candidate.childForFieldName('type');
266
+ return callee && callee.startIndex <= site.start && callee.endIndex >= site.end;
267
+ });
268
+ if (exact.length === 1) return exact[0];
269
+ }
258
270
  return matches[occurrence] || matches[0] || null;
259
271
  }
260
272
 
@@ -795,8 +807,10 @@ function computePlanCallSites(index, name, def) {
795
807
  content: c.content,
796
808
  usageType: 'call',
797
809
  receiver: c.receiver,
810
+ calledAs: c.calledAs,
811
+ callSite: c.provenance?.facts?.site,
798
812
  };
799
- const siteKey = `${c.file}:${c.line}`;
813
+ const siteKey = `${c.file}:${c.line}:${c.calledAs || name}`;
800
814
  const occurrence = planLineSeen.get(siteKey) || 0;
801
815
  planLineSeen.set(siteKey, occurrence + 1);
802
816
  const analysis = analyzeCallSite(index, call, name, occurrence);
@@ -898,7 +912,9 @@ function analyzeCallSite(index, call, funcName, occurrence = 0) {
898
912
  const targetRow = call.line - 1; // tree-sitter is 0-indexed
899
913
 
900
914
  // Find the call expression at the target line matching funcName
901
- const callNode = findCallNode(tree.rootNode, callTypes, targetRow, funcName, occurrence);
915
+ const spelling = call.calledAs && call.calledAs !== 'bound' ? call.calledAs : funcName;
916
+ const callNode = findCallNode(tree.rootNode, callTypes, targetRow, spelling, occurrence,
917
+ call.callSite || call.provenance?.facts?.site);
902
918
  if (!callNode) return { args: null, argCount: 0 };
903
919
 
904
920
  // Check if this is a method call (obj.func()) vs a direct call (func())
@@ -1353,6 +1369,8 @@ function verify(index, name, options = {}) {
1353
1369
  content: c.content,
1354
1370
  usageType: 'call',
1355
1371
  receiver: c.receiver,
1372
+ calledAs: c.calledAs,
1373
+ callSite: c.provenance?.facts?.site,
1356
1374
  // Preserve receiver identity through the usage-shaped adapter. Go
1357
1375
  // permits a local value to have the same spelling as its type; only
1358
1376
  // a type-qualified call is a method expression with an explicit
@@ -1394,7 +1412,7 @@ function verify(index, name, options = {}) {
1394
1412
 
1395
1413
  const verifyLineSeen = new Map(); // 'file:line' -> per-line ordinal (fix #231)
1396
1414
  for (const call of calls) {
1397
- const siteKey = `${call.file}:${call.line}`;
1415
+ const siteKey = `${call.file}:${call.line}:${call.calledAs || name}`;
1398
1416
  const occurrence = verifyLineSeen.get(siteKey) || 0;
1399
1417
  verifyLineSeen.set(siteKey, occurrence + 1);
1400
1418
  const analysis = analyzeCallSite(index, call, name, occurrence);
package/mcp/server.js CHANGED
@@ -119,7 +119,7 @@ const server = new StdioMcpServer({
119
119
 
120
120
  function toolResult(text, command, maxChars, suffixNote, params = {}) {
121
121
  const suffix = suffixNote || '';
122
- const budget = applyOutputBudget(text, {
122
+ const budget = applyOutputBudget(text + suffix, {
123
123
  command,
124
124
  maxChars,
125
125
  surface: 'mcp',
@@ -130,7 +130,7 @@ function toolResult(text, command, maxChars, suffixNote, params = {}) {
130
130
  // narrowing hints; preserved contract lines follow). A structuredContent
131
131
  // side-channel is rendered INSTEAD of content by MCP clients that prefer
132
132
  // structured results, which discards the entire answer (fix #284).
133
- return { content: [{ type: 'text', text: budget.text + suffix }] };
133
+ return { content: [{ type: 'text', text: budget.text }] };
134
134
  }
135
135
 
136
136
  function toolError(message) {
@@ -307,7 +307,7 @@ const INPUT_SHAPE = {
307
307
  line: integerParam('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.', { exclusiveMinimum: 0, maximum: Number.MAX_SAFE_INTEGER }),
308
308
  limit: integerParam('Max results to return (default: 500; structural search: 50; usages and lines: uncapped). Caps find, usages, search, deadcode, api, and repo files. Must be a positive integer.', { exclusiveMinimum: 0, maximum: 1000000 }),
309
309
  max_files: integerParam('Max files to index (default: 10000). Use for very large codebases. Must be a positive integer.', { exclusiveMinimum: 0, maximum: 10000000 }),
310
- max_chars: integerParam('Max output chars before truncation. Broad sweep commands (repo, entrypoints, endpoints, deadcode, deps, check, audit_async) default to 3K; all other commands default to 10K. Maximum: 100K. all=true lifts formatter caps but keeps the 100K transport ceiling.', { exclusiveMinimum: 0, maximum: 100000 }),
310
+ max_chars: integerParam('Max output characters, not UTF-8 bytes, including notes and preserved metadata. Broad sweep commands (repo, entrypoints, endpoints, deadcode, deps, check, audit_async) default to 3K; all other commands default to 10K. Maximum: 100K. all=true lifts formatter caps but keeps the 100K transport ceiling.', { exclusiveMinimum: 0, maximum: 100000 }),
311
311
  type: stringParam('Symbol type filter for structural search: function, class, call, method, type, state, field, constant, macro. Triggers index-based search.'),
312
312
  param: stringParam('Filter by parameter name or type (structural search). E.g. "Request", "ctx".'),
313
313
  receiver: stringParam('Filter calls by receiver (structural search, type=call). E.g. "db", "http".'),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ucn",
3
- "version": "5.4.0",
3
+ "version": "5.4.2",
4
4
  "mcpName": "io.github.mleoca/ucn",
5
5
  "description": "Auditable AST code intelligence for AI agents: 18 task-oriented commands through one MCP tool, CLI, or agent skill. Supports JS/TS, Python, Go, Rust, Java, C, C++, C#, and HTML.",
6
6
  "main": "index.js",
@@ -10,7 +10,7 @@
10
10
  },
11
11
  "scripts": {
12
12
  "version": "node scripts/sync-server-version.js && git add server.json",
13
- "test": "node --test test/audit-5.3.8.test.js test/audit-5.3.7.test.js test/audit-5.3.6.test.js test/evidence-provenance.test.js test/provenance-unit.test.js test/shell-output.test.js 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-c-family.test.js test/regression-cross.test.js test/regression-mcp.test.js test/mcp-protocol.test.js test/mcp-sdk-compat.test.js test/dependency-security.test.js test/regression-parser.test.js test/regression-commands.test.js test/regression-fixes.test.js test/regression-bugfixes.test.js test/release-surface-regressions.test.js test/prerelease-audit.test.js test/release-readiness-audit.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/outcome-policy.test.js test/consistency-eval.test.js test/agent-public-surface-benchmark.test.js test/command-contracts.test.js test/language-adapter.test.js test/systematic-test.js test/mcp-edge-cases.js test/conservation.test.js test/parity-test.js test/trust-matrix.test.js",
13
+ "test": "node --test test/audit-5.4.0.test.js test/audit-5.3.8.test.js test/audit-5.3.7.test.js test/audit-5.3.6.test.js test/evidence-provenance.test.js test/provenance-unit.test.js test/shell-output.test.js 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-c-family.test.js test/regression-cross.test.js test/regression-mcp.test.js test/mcp-protocol.test.js test/mcp-sdk-compat.test.js test/dependency-security.test.js test/regression-parser.test.js test/regression-commands.test.js test/regression-fixes.test.js test/regression-bugfixes.test.js test/release-surface-regressions.test.js test/prerelease-audit.test.js test/release-readiness-audit.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/outcome-policy.test.js test/consistency-eval.test.js test/agent-public-surface-benchmark.test.js test/command-contracts.test.js test/language-adapter.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-public-surface-benchmark.js",
15
15
  "benchmark:agent:gate": "node test/agent-public-surface-benchmark.js --gate",
16
16
  "benchmark:agent:legacy": "node test/agent-understanding-benchmark.js",