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/core/bridge.js
CHANGED
|
@@ -26,7 +26,6 @@ const fs = require('fs');
|
|
|
26
26
|
const { codeUnitCompare } = require('./shared');
|
|
27
27
|
const path = require('path');
|
|
28
28
|
const { getCachedCalls } = require('./callers');
|
|
29
|
-
const { langTraits } = require('../languages');
|
|
30
29
|
|
|
31
30
|
// ============================================================================
|
|
32
31
|
// HTTP METHOD CONSTANTS
|
package/core/brief.js
CHANGED
|
@@ -16,8 +16,7 @@
|
|
|
16
16
|
|
|
17
17
|
const fs = require('fs');
|
|
18
18
|
const path = require('path');
|
|
19
|
-
const {
|
|
20
|
-
const { detectLanguage, langTraits } = require('../languages');
|
|
19
|
+
const { detectLanguage } = require('../languages');
|
|
21
20
|
const { formatSymbolHandle } = require('./shared');
|
|
22
21
|
|
|
23
22
|
// ============================================================================
|
|
@@ -377,7 +376,7 @@ function sideEffectsFor(index, symbol) {
|
|
|
377
376
|
if (index._sideEffectCache.has(key)) return index._sideEffectCache.get(key);
|
|
378
377
|
|
|
379
378
|
const filePath = path.isAbsolute(symbol.file || '') ? symbol.file : path.join(index.root, symbol.file || symbol.relativePath || '');
|
|
380
|
-
let bodyText
|
|
379
|
+
let bodyText;
|
|
381
380
|
try {
|
|
382
381
|
const content = fs.readFileSync(filePath, 'utf-8');
|
|
383
382
|
const lines = content.split('\n');
|
package/core/build-worker.js
CHANGED
|
@@ -40,6 +40,8 @@ function addSymbol(fileEntry, item, type) {
|
|
|
40
40
|
// compare full symbol shapes, but only for shapes present in their
|
|
41
41
|
// fixtures — keep this list in sync by hand when addSymbol grows.
|
|
42
42
|
if (item.paramTypes) symbol.paramTypes = item.paramTypes;
|
|
43
|
+
if (item.returnedFunctionResult) symbol.returnedFunctionResult = item.returnedFunctionResult;
|
|
44
|
+
if (item.isFunctionVariable) symbol.isFunctionVariable = true;
|
|
43
45
|
if (item.isAsync) symbol.isAsync = true;
|
|
44
46
|
if (item.isGenerator) symbol.isGenerator = true;
|
|
45
47
|
if (item.generics) symbol.generics = item.generics;
|
|
@@ -62,6 +64,8 @@ function addSymbol(fileEntry, item, type) {
|
|
|
62
64
|
if (item.traitName) symbol.traitName = item.traitName;
|
|
63
65
|
if (item.isSignature) symbol.isSignature = true;
|
|
64
66
|
if (item.memberAssigned) symbol.memberAssigned = true;
|
|
67
|
+
if (item.registryMember) symbol.registryMember = true;
|
|
68
|
+
if (item.registryContainer) symbol.registryContainer = item.registryContainer;
|
|
65
69
|
|
|
66
70
|
fileEntry.symbols.push(symbol);
|
|
67
71
|
// Property-assignment defs declare no lexical name (fix #269) — kept in
|
|
@@ -169,6 +173,7 @@ function processFile(filePath) {
|
|
|
169
173
|
bindings: [],
|
|
170
174
|
dynamicImports: dynamicCount || 0,
|
|
171
175
|
};
|
|
176
|
+
if (parsed.parseRecovery) fileEntry.parseRecovery = true;
|
|
172
177
|
if (importAliases) fileEntry.importAliases = importAliases;
|
|
173
178
|
if (parsed.moduleAssignedNames) fileEntry.moduleAssignedNames = parsed.moduleAssignedNames;
|
|
174
179
|
if (isBundled) fileEntry.isBundled = true;
|
package/core/cache.js
CHANGED
|
@@ -124,7 +124,47 @@ const UCN_VERSION = require('../package.json').version;
|
|
|
124
124
|
// v47 (fix #269): Python absolute imports resolve through the PEP-517 src
|
|
125
125
|
// layout (`import click` → src/click/__init__.py) — persisted
|
|
126
126
|
// importGraph/exportGraph/moduleResolved content changed.
|
|
127
|
-
|
|
127
|
+
// v48 (fix #270): Java interface symbols record their extends clause (the
|
|
128
|
+
// grammar's extends_interfaces child carries no `extends` field, so the
|
|
129
|
+
// field lookup silently returned nothing) — cached interface symbols lack
|
|
130
|
+
// the supertypes the deadcode heritage walk reads.
|
|
131
|
+
// v49: JS/TS module-scope object-literal dispatch members carry
|
|
132
|
+
// registryMember/registryContainer so reachability does not classify live
|
|
133
|
+
// HANDLERS[command](...) implementations as dead code.
|
|
134
|
+
// v50: persist the same registry fields from parallel workers; v49 worker-built
|
|
135
|
+
// caches silently dropped them and therefore cannot be trusted.
|
|
136
|
+
// v51: JS/TS call aliases are lexical/position-aware; old calls caches can
|
|
137
|
+
// contain resolvedName values leaked from an unrelated block or later line.
|
|
138
|
+
// v55: Rust `Self { ... }` struct expressions persist the concrete enclosing
|
|
139
|
+
// impl type as their constructor-call name.
|
|
140
|
+
// v56: Java call records preserve typed identifiers/static-factory argument
|
|
141
|
+
// kinds and capitalized static-field receiver roots for overload/dispatch
|
|
142
|
+
// identity.
|
|
143
|
+
// v57: Java capitalized type receivers are no longer also persisted as
|
|
144
|
+
// implicit-this fields.
|
|
145
|
+
// v58: Java enhanced-for variables persist their declared receiver type.
|
|
146
|
+
// v59: Java class-literal argument kinds and capitalized static-field
|
|
147
|
+
// receivers are persisted for inherited overload/field ownership.
|
|
148
|
+
// v60: argument/comment scans ignore every tree-sitter comment node kind
|
|
149
|
+
// (line_comment, block_comment, documentation_comment), not only `comment`.
|
|
150
|
+
// v61: file entries persist tree-sitter parse-recovery state so doctor never
|
|
151
|
+
// reports a recovered/possibly-partial file as a clean parse.
|
|
152
|
+
// v62: Go call records preserve receiverTypeQualifier,
|
|
153
|
+
// receiverRootTypeQualifier, and package-owned value receiver shape; symbols
|
|
154
|
+
// preserve function-valued variables and returned-function result signatures.
|
|
155
|
+
// These fields feed package identity and higher-order return flow.
|
|
156
|
+
// v63: Python constructor-derived receiver types preserve the imported module
|
|
157
|
+
// qualifier (`threading.Thread()` -> receiverTypeQualifier:'threading').
|
|
158
|
+
// This prevents external/unresolved type owners from entering the confirmed
|
|
159
|
+
// project-method tier through a bare class-name collision.
|
|
160
|
+
// v64: Rust calls whose receiver is rebound by an enclosing if-let/while-let
|
|
161
|
+
// pattern preserve receiverPatternShadow. Query-time return flow must not
|
|
162
|
+
// smear an outer binding's type onto that inner pattern binding.
|
|
163
|
+
// v65: Rust turbofish calls inside macro token trees are persisted as calls
|
|
164
|
+
// (`m.get_many::<T>()`); v64 caches misclassified them as references.
|
|
165
|
+
// v66: Rust call records preserve receiverFlowInvalidated after a non-call
|
|
166
|
+
// lexical rebinding, preventing stale return types from excluding true calls.
|
|
167
|
+
const CACHE_FORMAT_VERSION = 66;
|
|
128
168
|
|
|
129
169
|
/**
|
|
130
170
|
* Save index to cache file
|
|
@@ -440,7 +480,7 @@ function loadCache(index, cachePath) {
|
|
|
440
480
|
// Prepare lazy calls cache loading — load manifest but defer shard parsing.
|
|
441
481
|
// Shards are loaded on first getCachedCalls access via ensureCallsCacheLoaded().
|
|
442
482
|
if (index.callsCache.size === 0) {
|
|
443
|
-
_prepareCallsCache(index);
|
|
483
|
+
_prepareCallsCache(index, cacheFile);
|
|
444
484
|
}
|
|
445
485
|
|
|
446
486
|
// Build directory→files index from loaded data
|
|
@@ -580,9 +620,16 @@ function isCacheStale(index) {
|
|
|
580
620
|
* Actual shards are loaded on first ensureCallsCacheLoaded() call.
|
|
581
621
|
* @param {object} index - ProjectIndex instance
|
|
582
622
|
*/
|
|
583
|
-
function _prepareCallsCache(index) {
|
|
623
|
+
function _prepareCallsCache(index, cacheFile) {
|
|
584
624
|
if (index._callsCacheLoaded) return;
|
|
585
|
-
|
|
625
|
+
// Shards live beside the selected index.json. A custom cachePath must be
|
|
626
|
+
// a complete portable cache, not an index file that silently looks for
|
|
627
|
+
// call shards under <project>/.ucn-cache. The latter caused cache-loaded
|
|
628
|
+
// semantic queries to reparse source (or consume unrelated stale shards).
|
|
629
|
+
const cacheDir = cacheFile
|
|
630
|
+
? path.dirname(cacheFile)
|
|
631
|
+
: path.join(index.root, '.ucn-cache');
|
|
632
|
+
index._callsCacheDir = cacheDir;
|
|
586
633
|
const manifestFile = path.join(cacheDir, 'calls', 'manifest.json');
|
|
587
634
|
if (fs.existsSync(manifestFile)) {
|
|
588
635
|
try {
|
|
@@ -627,7 +674,7 @@ function loadCallsCache(index) {
|
|
|
627
674
|
|
|
628
675
|
// Legacy format: single calls-cache.json
|
|
629
676
|
const callsCacheFile = index._callsCacheLegacyFile ||
|
|
630
|
-
path.join(index.root, '.ucn-cache', 'calls-cache.json');
|
|
677
|
+
path.join(index._callsCacheDir || path.join(index.root, '.ucn-cache'), 'calls-cache.json');
|
|
631
678
|
if (!fs.existsSync(callsCacheFile)) return index.callsCache.size > 0;
|
|
632
679
|
|
|
633
680
|
try {
|
|
@@ -665,7 +712,9 @@ function ensureCallsCacheLoaded(index) {
|
|
|
665
712
|
* @param {string} hash - Shard hash from manifest
|
|
666
713
|
*/
|
|
667
714
|
function _loadCallsShard(index, hash) {
|
|
668
|
-
const shardFile = path.join(
|
|
715
|
+
const shardFile = path.join(
|
|
716
|
+
index._callsCacheDir || path.join(index.root, '.ucn-cache'),
|
|
717
|
+
'calls', `${hash}.json`);
|
|
669
718
|
try {
|
|
670
719
|
const data = JSON.parse(fs.readFileSync(shardFile, 'utf-8'));
|
|
671
720
|
if (!Array.isArray(data)) return;
|