ucn 5.4.2 → 5.5.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 +58 -236
- package/.claude/skills/ucn/references/commands.md +144 -68
- package/.claude/skills/ucn/references/resolution-rules.md +171 -0
- package/.claude/skills/ucn/references/trust-contract.md +27 -33
- package/README.md +27 -16
- package/cli/index.js +45 -14
- package/core/accessors.js +118 -3
- package/core/account.js +62 -11
- package/core/analysis.js +517 -266
- package/core/ast-analysis.js +76 -117
- package/core/async-producers.js +1729 -0
- package/core/bridge.js +1877 -268
- package/core/brief.js +6 -0
- package/core/build-worker.js +78 -14
- package/core/cache.js +529 -31
- package/core/callers.js +12228 -921
- package/core/check.js +30 -38
- package/core/class-identity.js +1028 -0
- package/core/command-contracts.js +6 -6
- package/core/configuration-items.js +86 -0
- package/core/contract-membership.js +2262 -0
- package/core/cpp-scope.js +532 -0
- package/core/deadcode.js +171 -49
- package/core/decorator-binding.js +260 -0
- package/core/discovery.js +281 -164
- package/core/errors.js +52 -0
- package/core/execute.js +21 -9
- package/core/external-macros.js +326 -0
- package/core/graph-build.js +277 -8
- package/core/graph.js +35 -2
- package/core/ignore-rules.js +472 -0
- package/core/imports.js +504 -42
- package/core/index-ir.js +94 -11
- package/core/inprocess-clients.js +636 -0
- package/core/ir.js +87 -8
- package/core/macro-expansion.js +1416 -0
- package/core/output/analysis.js +18 -2
- package/core/output/brief.js +3 -0
- package/core/output/doctor.js +4 -0
- package/core/output/endpoints.js +15 -6
- package/core/output/find.js +7 -2
- package/core/output/refactoring.js +121 -14
- package/core/output/reporting.js +31 -3
- package/core/output/shared.js +14 -0
- package/core/output-budget.js +305 -122
- package/core/parallel-build.js +124 -47
- package/core/parser.js +3 -2
- package/core/project.js +472 -66
- package/core/provenance-facts.js +156 -12
- package/core/provenance-overload.js +30 -3
- package/core/provenance.js +104 -30
- package/core/pytest-fixtures.js +434 -0
- package/core/python-fixture-flow.js +17 -3
- package/core/python-modules.js +173 -0
- package/core/reflection.js +877 -0
- package/core/registry.js +1 -1
- package/core/reporting.js +234 -52
- package/core/request-helpers.js +561 -0
- package/core/route-graph.js +1922 -0
- package/core/route-lists.js +2414 -0
- package/core/rust-macro-expansion.js +3026 -0
- package/core/rust-modules.js +203 -0
- package/core/search.js +121 -29
- package/core/shared.js +19 -1
- package/core/test-name-bindings.js +256 -0
- package/core/type-denotation.js +1185 -0
- package/core/type-references.js +3229 -0
- package/core/verify.js +3360 -672
- package/languages/adapter.js +31 -5
- package/languages/c-family.js +4061 -212
- package/languages/c-macro-invocations.js +380 -0
- package/languages/c-preprocessor.js +254 -0
- package/languages/csharp.js +1406 -49
- package/languages/go.js +1243 -70
- package/languages/index.js +728 -6
- package/languages/java.js +473 -61
- package/languages/javascript.js +1270 -197
- package/languages/lexical-scope.js +1280 -0
- package/languages/python.js +1557 -72
- package/languages/rust-macro-rules.js +1020 -0
- package/languages/rust-value-flow.js +416 -0
- package/languages/rust.js +2265 -112
- package/languages/utils.js +298 -4
- package/mcp/server.js +22 -8
- package/package.json +1 -1
package/core/account.js
CHANGED
|
@@ -69,6 +69,7 @@ const UNSUPPORTED_SITE_TEXT_MAX = 160;
|
|
|
69
69
|
* fileCount: number, // files (indexed + unparsed + unsupported) with >= 1 matching line
|
|
70
70
|
* perFile: Map<string, number[]>, // absPath -> sorted 1-indexed line numbers (indexed files only)
|
|
71
71
|
* unparsed: { fileCount: number, lines: number, files: string[] }, // relative paths
|
|
72
|
+
* recovered: { fileCount, lines, files, sites }, // matched lines inside parse-error recovery regions (subset of perFile)
|
|
72
73
|
* unsupported: { fileCount: number, lines: number, files: string[],
|
|
73
74
|
* languages: Object<string, number>,
|
|
74
75
|
* sites: Array<{file: string, line: number, text: string}>,
|
|
@@ -125,11 +126,39 @@ function computeGroundSet(index, name) {
|
|
|
125
126
|
? index.discoveryIssues.map(issue => ({ ...issue })) : [];
|
|
126
127
|
unreadableFiles.sort();
|
|
127
128
|
|
|
129
|
+
// fix #367e: ground lines inside a parser-recovery region of an indexed
|
|
130
|
+
// file. They are classified like any other line, but from a partially
|
|
131
|
+
// parsed tree - disclosed, never silently trusted.
|
|
132
|
+
const recovered = { fileCount: 0, lines: 0, files: [], sites: [] };
|
|
133
|
+
for (const [filePath, matchedLines] of perFile) {
|
|
134
|
+
const regions = index.files.get(filePath)?.parseErrorRegions;
|
|
135
|
+
if (!regions || regions.length === 0) continue;
|
|
136
|
+
let inFile = 0;
|
|
137
|
+
let r = 0;
|
|
138
|
+
for (const line of matchedLines) {
|
|
139
|
+
while (r < regions.length && regions[r][1] < line) r++;
|
|
140
|
+
if (r < regions.length && regions[r][0] <= line) {
|
|
141
|
+
inFile++;
|
|
142
|
+
if (recovered.sites.length < 10) {
|
|
143
|
+
recovered.sites.push({ file: path.relative(index.root, filePath), line });
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
if (inFile > 0) {
|
|
148
|
+
recovered.fileCount++;
|
|
149
|
+
recovered.lines += inFile;
|
|
150
|
+
recovered.files.push(path.relative(index.root, filePath));
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
recovered.files.sort(codeUnitCompare);
|
|
154
|
+
recovered.sites.sort((a, b) => codeUnitCompare(a.file, b.file) || a.line - b.line);
|
|
155
|
+
|
|
128
156
|
const result = {
|
|
129
157
|
total: total + unparsed.lines + unsupported.lines,
|
|
130
158
|
fileCount: fileCount + unparsed.fileCount + unsupported.fileCount,
|
|
131
159
|
perFile,
|
|
132
160
|
unparsed,
|
|
161
|
+
recovered,
|
|
133
162
|
unsupported,
|
|
134
163
|
unreadableFiles,
|
|
135
164
|
skippedSources,
|
|
@@ -247,6 +276,23 @@ function scanUnsupportedFiles(index, name, opts = {}) {
|
|
|
247
276
|
return unsupported;
|
|
248
277
|
}
|
|
249
278
|
|
|
279
|
+
/**
|
|
280
|
+
* A set of (file, line) keys held per file: `key(file, line)` names one,
|
|
281
|
+
* and `add`/`has` take a key or a (file, line) pair. Lines compare by their
|
|
282
|
+
* text, as `${file}:${line}` strings do.
|
|
283
|
+
*/
|
|
284
|
+
class LineKeys {
|
|
285
|
+
constructor() { this.byFile = new Map(); }
|
|
286
|
+
key(file, line) { return { file: String(file), line: String(line) }; }
|
|
287
|
+
add(file, line) {
|
|
288
|
+
const key = line === undefined ? file : this.key(file, line);
|
|
289
|
+
let lines = this.byFile.get(key.file);
|
|
290
|
+
if (!lines) this.byFile.set(key.file, lines = new Set());
|
|
291
|
+
lines.add(key.line);
|
|
292
|
+
}
|
|
293
|
+
has(key) { return this.byFile.get(key.file)?.has(key.line) === true; }
|
|
294
|
+
}
|
|
295
|
+
|
|
250
296
|
/**
|
|
251
297
|
* Classify ground lines not claimed by engine results.
|
|
252
298
|
*
|
|
@@ -260,7 +306,7 @@ function scanUnsupportedFiles(index, name, opts = {}) {
|
|
|
260
306
|
* @param {object} index - ProjectIndex
|
|
261
307
|
* @param {string} name - Symbol name
|
|
262
308
|
* @param {object} groundSet - from computeGroundSet
|
|
263
|
-
* @param {
|
|
309
|
+
* @param {LineKeys} claimedKeys - (absPath, line) keys already claimed
|
|
264
310
|
* (confirmed + unverified + excluded engine lines)
|
|
265
311
|
* @returns {{
|
|
266
312
|
* nonCall: { imports: number, definitions: number, references: number, unclassifiedText: number, total: number },
|
|
@@ -282,12 +328,12 @@ function classifyGroundLines(index, name, groundSet, claimedKeys) {
|
|
|
282
328
|
if (!defLinesByFile.has(def.file)) defLinesByFile.set(def.file, new Set());
|
|
283
329
|
defLinesByFile.get(def.file).add(def.startLine);
|
|
284
330
|
}
|
|
285
|
-
let
|
|
331
|
+
let getCachedCallsBearing = null; // lazy require to avoid cycle at module load
|
|
286
332
|
|
|
287
333
|
for (const [filePath, lineNumbers] of groundSet.perFile) {
|
|
288
334
|
let unclaimed = null;
|
|
289
335
|
for (const line of lineNumbers) {
|
|
290
|
-
if (claimedKeys.has(
|
|
336
|
+
if (claimedKeys.has(claimedKeys.key(filePath, line))) continue;
|
|
291
337
|
(unclaimed || (unclaimed = [])).push(line);
|
|
292
338
|
}
|
|
293
339
|
if (!unclaimed) continue;
|
|
@@ -295,9 +341,10 @@ function classifyGroundLines(index, name, groundSet, claimedKeys) {
|
|
|
295
341
|
const fileEntry = index.files.get(filePath);
|
|
296
342
|
const relativePath = fileEntry ? fileEntry.relativePath : path.relative(index.root, filePath);
|
|
297
343
|
|
|
298
|
-
// Cheap pass: calls cache + symbol table
|
|
299
|
-
|
|
300
|
-
|
|
344
|
+
// Cheap pass: calls cache + symbol table (only records bearing the
|
|
345
|
+
// name on unclaimed lines are read below).
|
|
346
|
+
if (!getCachedCallsBearing) getCachedCallsBearing = require('./callers').getCachedCallsBearing;
|
|
347
|
+
const cachedCalls = getCachedCallsBearing(index, filePath, name, new Set(unclaimed));
|
|
301
348
|
const callLines = new Set();
|
|
302
349
|
if (Array.isArray(cachedCalls)) {
|
|
303
350
|
for (const c of cachedCalls) {
|
|
@@ -390,18 +437,21 @@ function buildAccount(index, name, parts) {
|
|
|
390
437
|
const unverifiedEntries = parts.unverifiedEntries || [];
|
|
391
438
|
const excludedEntries = parts.excludedEntries || [];
|
|
392
439
|
|
|
393
|
-
|
|
440
|
+
// (file, line) keys, kept per file (fix #405b: one composed string per
|
|
441
|
+
// ground line and per entry); a line is keyed by its text, as the
|
|
442
|
+
// composed `${file}:${line}` keys were.
|
|
443
|
+
const groundKeys = new LineKeys();
|
|
394
444
|
for (const [filePath, lineNumbers] of groundSet.perFile) {
|
|
395
|
-
for (const line of lineNumbers) groundKeys.add(
|
|
445
|
+
for (const line of lineNumbers) groundKeys.add(filePath, line);
|
|
396
446
|
}
|
|
397
447
|
|
|
398
|
-
const claimedKeys = new
|
|
448
|
+
const claimedKeys = new LineKeys();
|
|
399
449
|
const beyondText = { count: 0, sample: [] };
|
|
400
450
|
let confirmed = 0;
|
|
401
451
|
let unverified = 0;
|
|
402
452
|
|
|
403
453
|
const claim = (entry, bucket) => {
|
|
404
|
-
const key =
|
|
454
|
+
const key = claimedKeys.key(entry.file, entry.line);
|
|
405
455
|
if (claimedKeys.has(key)) return; // one bucket per line; first claim wins
|
|
406
456
|
if (!groundKeys.has(key)) {
|
|
407
457
|
// Engine found a call site grep would miss (alias / indirect name).
|
|
@@ -425,7 +475,7 @@ function buildAccount(index, name, parts) {
|
|
|
425
475
|
const excludedByReason = {};
|
|
426
476
|
let excludedTotal = 0;
|
|
427
477
|
for (const e of excludedEntries) {
|
|
428
|
-
const key =
|
|
478
|
+
const key = claimedKeys.key(e.file, e.line);
|
|
429
479
|
if (claimedKeys.has(key)) continue;
|
|
430
480
|
claimedKeys.add(key);
|
|
431
481
|
if (!groundKeys.has(key)) continue; // excluded non-ground line: irrelevant to both grep and display
|
|
@@ -473,6 +523,7 @@ function buildAccount(index, name, parts) {
|
|
|
473
523
|
line: e.line, reason: e.reason, provenance: e.provenance })) }),
|
|
474
524
|
},
|
|
475
525
|
unparsed: groundSet.unparsed,
|
|
526
|
+
...(groundSet.recovered?.lines > 0 && { recovered: groundSet.recovered }),
|
|
476
527
|
unsupported,
|
|
477
528
|
unreadableFiles: groundSet.unreadableFiles,
|
|
478
529
|
skippedSources: groundSet.skippedSources || [],
|