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/brief.js
CHANGED
|
@@ -123,6 +123,7 @@ function brief(index, name, options = {}) {
|
|
|
123
123
|
...(def.decorators && def.decorators.length && { decorators: def.decorators }),
|
|
124
124
|
...(def.docstring && { docstring: firstSentence(def.docstring) }),
|
|
125
125
|
...(def.className && { className: def.className }),
|
|
126
|
+
...(def.macroExpansion && { macroExpansion: def.macroExpansion }),
|
|
126
127
|
...(def.isAsync && { isAsync: true }),
|
|
127
128
|
...(def.isGenerator && { isGenerator: true }),
|
|
128
129
|
};
|
|
@@ -186,9 +187,14 @@ function brief(index, name, options = {}) {
|
|
|
186
187
|
const fileImports = collectImportNames(fileEntry);
|
|
187
188
|
|
|
188
189
|
const sideEffects = classifySideEffects(bodyText, language, fileImports);
|
|
190
|
+
// The operation's shared tree when one is active (fix #365); the
|
|
191
|
+
// file's indexed hash pins it to this content.
|
|
192
|
+
const indexedTree = fileEntry && typeof index._getParsedTree === 'function'
|
|
193
|
+
? index._getParsedTree(filePath, fileContent, language) : null;
|
|
189
194
|
const complexity = computeAstComplexity(fileContent, language, {
|
|
190
195
|
startLine: def.startLine || 1,
|
|
191
196
|
endLine: def.endLine || def.startLine || 1,
|
|
197
|
+
...(indexedTree && { tree: indexedTree }),
|
|
192
198
|
});
|
|
193
199
|
|
|
194
200
|
return {
|
package/core/build-worker.js
CHANGED
|
@@ -3,12 +3,17 @@
|
|
|
3
3
|
/**
|
|
4
4
|
* core/build-worker.js - Worker thread for parallel index building
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
* then sends results back to the main thread via MessagePort.
|
|
6
|
+
* Claims files from a shared queue, parses each file, extracts symbols and
|
|
7
|
+
* calls, then sends results back to the main thread via MessagePort.
|
|
8
8
|
* Mirrors the indexFile() logic in project.js.
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
// Reuse V8 code caches across processes (Node >= 22.1; fix #365): compiling
|
|
12
|
+
// the engine's modules is a fixed cost of every command. Node validates each
|
|
13
|
+
// entry against the source, and NODE_DISABLE_COMPILE_CACHE turns it off.
|
|
14
|
+
try { require('module').enableCompileCache?.(); } catch (_) { /* optional */ }
|
|
15
|
+
|
|
16
|
+
const { workerData, receiveMessageOnPort } = require('worker_threads');
|
|
12
17
|
const fs = require('fs');
|
|
13
18
|
const path = require('path');
|
|
14
19
|
const crypto = require('crypto');
|
|
@@ -16,7 +21,42 @@ const { detectLanguage, getParser, getLanguageAdapter } = require('../languages'
|
|
|
16
21
|
const { validateFileIR } = require('./ir');
|
|
17
22
|
const { createFileEntryFromIR, populateFileEntryFromIR } = require('./index-ir');
|
|
18
23
|
|
|
19
|
-
const { files, rootDir, existingHashes, signal, workerIndex, port } = workerData;
|
|
24
|
+
const { files, rootDir, existingHashes, signal, workerIndex, queueIndex, port, control, dictionarySignal } = workerData;
|
|
25
|
+
const { contextFor } = require('./external-macros');
|
|
26
|
+
// The main thread computes the macro dictionary while the workers parse
|
|
27
|
+
// (fix #396) and posts it before raising the signal. A file waits for it
|
|
28
|
+
// only when its recovery consults it.
|
|
29
|
+
let macroDictionary = workerData.macroDictionary || null;
|
|
30
|
+
let dictionaryPending = !!dictionarySignal;
|
|
31
|
+
function awaitDictionary() {
|
|
32
|
+
if (dictionaryPending) {
|
|
33
|
+
Atomics.wait(new Int32Array(dictionarySignal), 0, 0);
|
|
34
|
+
macroDictionary = receiveMessageOnPort(port)?.message?.macroDictionary || null;
|
|
35
|
+
dictionaryPending = false;
|
|
36
|
+
}
|
|
37
|
+
return macroDictionary;
|
|
38
|
+
}
|
|
39
|
+
const EMPTY_MACROS = new Map();
|
|
40
|
+
function externalMacrosFor(language) {
|
|
41
|
+
if (!dictionaryPending) return macroDictionary ? contextFor(macroDictionary, language) : null;
|
|
42
|
+
if (!getLanguageAdapter(language)?.traits?.textualIncludes) return null;
|
|
43
|
+
let resolved;
|
|
44
|
+
const resolve = () => {
|
|
45
|
+
if (resolved === undefined) {
|
|
46
|
+
const dictionary = awaitDictionary();
|
|
47
|
+
resolved = dictionary ? contextFor(dictionary, language) : null;
|
|
48
|
+
}
|
|
49
|
+
return resolved;
|
|
50
|
+
};
|
|
51
|
+
// Every build's files share one dictionary, so one key serves this
|
|
52
|
+
// worker's tree cache.
|
|
53
|
+
return {
|
|
54
|
+
key: 'build',
|
|
55
|
+
consulted: new Set(),
|
|
56
|
+
get objectBodies() { return resolve()?.objectBodies || EMPTY_MACROS; },
|
|
57
|
+
get functionMacros() { return resolve()?.functionMacros || EMPTY_MACROS; },
|
|
58
|
+
};
|
|
59
|
+
}
|
|
20
60
|
const signalArray = new Int32Array(signal);
|
|
21
61
|
|
|
22
62
|
function processFile(filePath) {
|
|
@@ -43,7 +83,9 @@ function processFile(filePath) {
|
|
|
43
83
|
// by both worker and sequential builds.
|
|
44
84
|
const adapter = getLanguageAdapter(language);
|
|
45
85
|
const parser = getParser(language);
|
|
46
|
-
|
|
86
|
+
// C/C++: the project macro dictionary (fix #396), as indexFile reads it.
|
|
87
|
+
const externalMacros = externalMacrosFor(language);
|
|
88
|
+
const ir = adapter.analyze(content, parser, filePath, externalMacros ? { externalMacros } : undefined);
|
|
47
89
|
const irFailures = validateFileIR(ir);
|
|
48
90
|
if (irFailures.length > 0) {
|
|
49
91
|
throw new Error(`Invalid ${language} IR: ${irFailures.join('; ')}`);
|
|
@@ -101,23 +143,45 @@ function processFile(filePath) {
|
|
|
101
143
|
}
|
|
102
144
|
|
|
103
145
|
// Process all files
|
|
146
|
+
(async () => {
|
|
104
147
|
try {
|
|
105
|
-
|
|
106
|
-
|
|
148
|
+
for (;;) {
|
|
149
|
+
const next = Atomics.add(signalArray, queueIndex, 1);
|
|
150
|
+
if (next >= files.length) break;
|
|
151
|
+
const filePath = files[next];
|
|
152
|
+
let result;
|
|
107
153
|
try {
|
|
108
|
-
|
|
154
|
+
result = processFile(filePath);
|
|
109
155
|
} catch (e) {
|
|
110
|
-
|
|
156
|
+
result = { filePath, error: e.message };
|
|
111
157
|
}
|
|
158
|
+
port.postMessage([result]);
|
|
159
|
+
// Yield between files so native-tree finalizers of the previous
|
|
160
|
+
// file's garbage run while the build continues (fix #365): a worker
|
|
161
|
+
// that never returns to its event loop keeps every tree it parsed
|
|
162
|
+
// alive until it exits, and with no idle workers the peaks coincide.
|
|
163
|
+
await new Promise(resolve => setImmediate(resolve));
|
|
112
164
|
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
port.close();
|
|
165
|
+
// The dictionary message is consumed before any later job on the port.
|
|
166
|
+
awaitDictionary();
|
|
116
167
|
Atomics.store(signalArray, workerIndex, 1);
|
|
117
168
|
Atomics.notify(signalArray, workerIndex);
|
|
118
169
|
} catch (e) {
|
|
119
|
-
// Worker-level error
|
|
170
|
+
// Worker-level error: signal completion (2 = not reusable).
|
|
120
171
|
try { port.postMessage([]); port.close(); } catch (_) { /* ignore */ }
|
|
121
|
-
Atomics.store(signalArray, workerIndex,
|
|
172
|
+
Atomics.store(signalArray, workerIndex, 2);
|
|
122
173
|
Atomics.notify(signalArray, workerIndex);
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
// A worker kept for the build's next phase (fix #388) waits for its job:
|
|
177
|
+
// Rust macro expansion, run with this thread's already optimized parser.
|
|
178
|
+
if (control) {
|
|
179
|
+
const controlArray = new Int32Array(control);
|
|
180
|
+
Atomics.wait(controlArray, workerIndex, 0);
|
|
181
|
+
if (Atomics.load(controlArray, workerIndex) === 2) {
|
|
182
|
+
const job = receiveMessageOnPort(port)?.message;
|
|
183
|
+
if (job) require('./rust-macro-expansion').runExpansionQueue(job, port);
|
|
184
|
+
}
|
|
123
185
|
}
|
|
186
|
+
try { port.close(); } catch (_) { /* closed */ }
|
|
187
|
+
})();
|