ucn 4.1.3 → 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 +51 -6
- package/core/callers.js +1709 -162
- package/core/check.js +107 -19
- package/core/confidence.js +29 -16
- package/core/deadcode.js +1 -2
- 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 +61 -4
- 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
|
@@ -128,7 +128,43 @@ const UCN_VERSION = require('../package.json').version;
|
|
|
128
128
|
// grammar's extends_interfaces child carries no `extends` field, so the
|
|
129
129
|
// field lookup silently returned nothing) — cached interface symbols lack
|
|
130
130
|
// the supertypes the deadcode heritage walk reads.
|
|
131
|
-
|
|
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;
|
|
132
168
|
|
|
133
169
|
/**
|
|
134
170
|
* Save index to cache file
|
|
@@ -444,7 +480,7 @@ function loadCache(index, cachePath) {
|
|
|
444
480
|
// Prepare lazy calls cache loading — load manifest but defer shard parsing.
|
|
445
481
|
// Shards are loaded on first getCachedCalls access via ensureCallsCacheLoaded().
|
|
446
482
|
if (index.callsCache.size === 0) {
|
|
447
|
-
_prepareCallsCache(index);
|
|
483
|
+
_prepareCallsCache(index, cacheFile);
|
|
448
484
|
}
|
|
449
485
|
|
|
450
486
|
// Build directory→files index from loaded data
|
|
@@ -584,9 +620,16 @@ function isCacheStale(index) {
|
|
|
584
620
|
* Actual shards are loaded on first ensureCallsCacheLoaded() call.
|
|
585
621
|
* @param {object} index - ProjectIndex instance
|
|
586
622
|
*/
|
|
587
|
-
function _prepareCallsCache(index) {
|
|
623
|
+
function _prepareCallsCache(index, cacheFile) {
|
|
588
624
|
if (index._callsCacheLoaded) return;
|
|
589
|
-
|
|
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;
|
|
590
633
|
const manifestFile = path.join(cacheDir, 'calls', 'manifest.json');
|
|
591
634
|
if (fs.existsSync(manifestFile)) {
|
|
592
635
|
try {
|
|
@@ -631,7 +674,7 @@ function loadCallsCache(index) {
|
|
|
631
674
|
|
|
632
675
|
// Legacy format: single calls-cache.json
|
|
633
676
|
const callsCacheFile = index._callsCacheLegacyFile ||
|
|
634
|
-
path.join(index.root, '.ucn-cache', 'calls-cache.json');
|
|
677
|
+
path.join(index._callsCacheDir || path.join(index.root, '.ucn-cache'), 'calls-cache.json');
|
|
635
678
|
if (!fs.existsSync(callsCacheFile)) return index.callsCache.size > 0;
|
|
636
679
|
|
|
637
680
|
try {
|
|
@@ -669,7 +712,9 @@ function ensureCallsCacheLoaded(index) {
|
|
|
669
712
|
* @param {string} hash - Shard hash from manifest
|
|
670
713
|
*/
|
|
671
714
|
function _loadCallsShard(index, hash) {
|
|
672
|
-
const shardFile = path.join(
|
|
715
|
+
const shardFile = path.join(
|
|
716
|
+
index._callsCacheDir || path.join(index.root, '.ucn-cache'),
|
|
717
|
+
'calls', `${hash}.json`);
|
|
673
718
|
try {
|
|
674
719
|
const data = JSON.parse(fs.readFileSync(shardFile, 'utf-8'));
|
|
675
720
|
if (!Array.isArray(data)) return;
|