ucn 4.2.3 → 5.0.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/.claude/skills/ucn/SKILL.md +89 -77
- package/.claude/skills/ucn/references/commands.md +62 -68
- package/.claude/skills/ucn/references/trust-contract.md +31 -6
- package/README.md +438 -305
- package/assets/demo.svg +31 -0
- package/cli/index.js +430 -1385
- package/core/account.js +144 -34
- package/core/analysis.js +182 -72
- package/core/ast-analysis.js +279 -0
- package/core/bridge.js +205 -24
- package/core/brief.js +27 -58
- package/core/build-worker.js +21 -140
- package/core/cache.js +513 -11
- package/core/callers.js +4920 -456
- package/core/check.js +13 -4
- package/core/command-contracts.js +402 -0
- package/core/compilation-database.js +276 -0
- package/core/confidence.js +4 -1
- package/core/deadcode.js +397 -19
- package/core/discovery.js +359 -46
- package/core/entrypoints.js +195 -41
- package/core/execute.js +887 -81
- package/core/graph-build.js +162 -7
- package/core/graph.js +53 -77
- package/core/imports.js +65 -6
- package/core/index-ir.js +138 -0
- package/core/ir.js +195 -0
- package/core/output/analysis.js +212 -22
- package/core/output/brief.js +23 -0
- package/core/output/check.js +4 -0
- package/core/output/doctor.js +37 -6
- package/core/output/endpoints.js +5 -2
- package/core/output/extraction.js +24 -12
- package/core/output/find.js +141 -36
- package/core/output/graph.js +11 -5
- package/core/output/public.js +462 -0
- package/core/output/refactoring.js +42 -10
- package/core/output/reporting.js +97 -20
- package/core/output/search.js +24 -16
- package/core/output/shared.js +22 -1
- package/core/output/tracing.js +30 -15
- package/core/output-budget.js +295 -0
- package/core/output.js +1 -0
- package/core/parallel-build.js +44 -11
- package/core/parser.js +3 -3
- package/core/project.js +384 -187
- package/core/public-command.js +47 -0
- package/core/registry.js +247 -117
- package/core/reporting.js +312 -290
- package/core/search.js +317 -185
- package/core/semantic-provider.js +110 -0
- package/core/stacktrace.js +25 -0
- package/core/tracing.js +101 -51
- package/core/trust-matrix.js +19 -40
- package/core/verify.js +534 -37
- package/languages/adapter.js +218 -0
- package/languages/c-family.js +2791 -0
- package/languages/c.js +3 -0
- package/languages/cpp.js +3 -0
- package/languages/csharp.js +1402 -0
- package/languages/go.js +60 -21
- package/languages/html.js +2 -2
- package/languages/index.js +85 -7
- package/languages/java.js +396 -13
- package/languages/javascript.js +199 -19
- package/languages/python.js +964 -22
- package/languages/rust.js +1317 -152
- package/languages/utils.js +40 -3
- package/mcp/server.js +254 -636
- package/package.json +39 -22
- package/eslint.config.js +0 -43
- package/jsconfig.json +0 -10
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { createFileIR, validateFileIR } = require('../core/ir');
|
|
4
|
+
|
|
5
|
+
const REQUIRED_ADAPTER_OPERATIONS = Object.freeze([
|
|
6
|
+
'parse',
|
|
7
|
+
'findCallsInCode',
|
|
8
|
+
'findImportsInCode',
|
|
9
|
+
'findExportsInCode',
|
|
10
|
+
'findUsagesInCode',
|
|
11
|
+
'isEntryPoint',
|
|
12
|
+
'getEntryPointKind',
|
|
13
|
+
]);
|
|
14
|
+
|
|
15
|
+
const OPTIONAL_HELPERS = Object.freeze([
|
|
16
|
+
'findFunctions',
|
|
17
|
+
'findClasses',
|
|
18
|
+
'findStateObjects',
|
|
19
|
+
'findMacros',
|
|
20
|
+
'findCallbackUsages',
|
|
21
|
+
'findInstanceAttributeTypes',
|
|
22
|
+
'findReExports',
|
|
23
|
+
'extractScriptBlocks',
|
|
24
|
+
'buildVirtualJSContent',
|
|
25
|
+
'extractEventHandlerCalls',
|
|
26
|
+
'findTestCallRanges',
|
|
27
|
+
'getBuiltinCallReturnType',
|
|
28
|
+
'getBuiltinFieldType',
|
|
29
|
+
'isPlatformConcreteCall',
|
|
30
|
+
'isPlatformConcreteType',
|
|
31
|
+
]);
|
|
32
|
+
|
|
33
|
+
function adapterCapabilities(languageModule) {
|
|
34
|
+
return Object.freeze({
|
|
35
|
+
symbols: typeof languageModule.parse === 'function',
|
|
36
|
+
calls: typeof languageModule.findCallsInCode === 'function',
|
|
37
|
+
imports: typeof languageModule.findImportsInCode === 'function',
|
|
38
|
+
exports: typeof languageModule.findExportsInCode === 'function',
|
|
39
|
+
usages: typeof languageModule.findUsagesInCode === 'function',
|
|
40
|
+
callbacks: typeof languageModule.findCallbackUsages === 'function',
|
|
41
|
+
reExports: typeof languageModule.findReExports === 'function',
|
|
42
|
+
entrypoints: typeof languageModule.getEntryPointKind === 'function',
|
|
43
|
+
semanticProvider: false,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Wrap one parser implementation in the v5 language-adapter contract.
|
|
49
|
+
* Generic code receives normalized methods and capabilities. Optional
|
|
50
|
+
* grammar-specific helpers are explicitly named on the adapter; the raw
|
|
51
|
+
* parser module is never exposed.
|
|
52
|
+
*/
|
|
53
|
+
function createLanguageAdapter(config) {
|
|
54
|
+
if (!config || !config.name) throw new Error('Language adapter name is required');
|
|
55
|
+
if (typeof config.module !== 'function') {
|
|
56
|
+
throw new Error(`${config.name}: language module factory is required`);
|
|
57
|
+
}
|
|
58
|
+
const languageModule = config.module();
|
|
59
|
+
const capabilities = adapterCapabilities(languageModule);
|
|
60
|
+
const adapter = {
|
|
61
|
+
id: config.name,
|
|
62
|
+
extensions: Object.freeze([...(config.extensions || [])]),
|
|
63
|
+
grammar: config.treeSitterLang || config.name,
|
|
64
|
+
traits: Object.freeze({ ...(config.traits || {}) }),
|
|
65
|
+
capabilities,
|
|
66
|
+
managesOwnParseTree: !!languageModule.managesOwnParseTree,
|
|
67
|
+
loadGrammar: config.treeSitterModule,
|
|
68
|
+
parse(code, parser) {
|
|
69
|
+
return languageModule.parse(code, parser);
|
|
70
|
+
},
|
|
71
|
+
findCalls(code, parser) {
|
|
72
|
+
return languageModule.findCallsInCode(code, parser);
|
|
73
|
+
},
|
|
74
|
+
findImports(code, parser) {
|
|
75
|
+
return languageModule.findImportsInCode(code, parser);
|
|
76
|
+
},
|
|
77
|
+
findExports(code, parser) {
|
|
78
|
+
return languageModule.findExportsInCode(code, parser);
|
|
79
|
+
},
|
|
80
|
+
findUsages(code, name, parser, tree) {
|
|
81
|
+
return languageModule.findUsagesInCode(code, name, parser, tree);
|
|
82
|
+
},
|
|
83
|
+
getEntryPointKind(symbol) {
|
|
84
|
+
return languageModule.getEntryPointKind(symbol);
|
|
85
|
+
},
|
|
86
|
+
isEntryPoint(symbol) {
|
|
87
|
+
return languageModule.isEntryPoint(symbol);
|
|
88
|
+
},
|
|
89
|
+
analyze(code, parser, file = null) {
|
|
90
|
+
// Full-file indexing consumes immutable records, not native ASTs.
|
|
91
|
+
// Parsers with a heavier internal recovery cache (notably C/C++)
|
|
92
|
+
// may release analysis-only trees as soon as those records have
|
|
93
|
+
// been extracted. Direct parser/query calls omit this option and
|
|
94
|
+
// retain their bounded cross-operation cache.
|
|
95
|
+
const parsed = languageModule.parse(code, parser, {
|
|
96
|
+
releaseAnalysisTree: true,
|
|
97
|
+
});
|
|
98
|
+
const parseProvidesFacts = !!languageModule.parseProvidesAnalysisFacts;
|
|
99
|
+
const imports = parseProvidesFacts
|
|
100
|
+
? (parsed.imports || [])
|
|
101
|
+
: languageModule.findImportsInCode(code, parser);
|
|
102
|
+
const exports = parseProvidesFacts
|
|
103
|
+
? (parsed.exports || [])
|
|
104
|
+
: languageModule.findExportsInCode(code, parser);
|
|
105
|
+
if (!parseProvidesFacts) {
|
|
106
|
+
parsed.imports = imports;
|
|
107
|
+
parsed.exports = exports;
|
|
108
|
+
}
|
|
109
|
+
const callOptions = {};
|
|
110
|
+
if (config.traits?.hasReceiverPackageCalls) {
|
|
111
|
+
callOptions.imports = imports.flatMap(item => item.names || []);
|
|
112
|
+
}
|
|
113
|
+
const calls = parseProvidesFacts && Array.isArray(parsed.calls)
|
|
114
|
+
? parsed.calls
|
|
115
|
+
: languageModule.findCallsInCode(code, parser, callOptions);
|
|
116
|
+
return createFileIR({
|
|
117
|
+
language: config.name,
|
|
118
|
+
file,
|
|
119
|
+
parsed,
|
|
120
|
+
calls,
|
|
121
|
+
capabilities,
|
|
122
|
+
});
|
|
123
|
+
},
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
// AST implementation aliases retained inside the adapter contract so
|
|
127
|
+
// focused parser tests and HTML delegation exercise the same object as
|
|
128
|
+
// production. These are not a second module surface.
|
|
129
|
+
adapter.findCallsInCode = adapter.findCalls;
|
|
130
|
+
adapter.findImportsInCode = adapter.findImports;
|
|
131
|
+
adapter.findExportsInCode = adapter.findExports;
|
|
132
|
+
adapter.findUsagesInCode = adapter.findUsages;
|
|
133
|
+
for (const helper of OPTIONAL_HELPERS) {
|
|
134
|
+
if (typeof languageModule[helper] === 'function') {
|
|
135
|
+
adapter[helper] = languageModule[helper].bind(languageModule);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return Object.freeze(adapter);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Skeleton adapter used by conformance tests and new-language scaffolding.
|
|
143
|
+
* It proves a language can join the registry with navigation-safe empty
|
|
144
|
+
* results before grammar-specific extraction is implemented. It is never
|
|
145
|
+
* advertised as a supported language.
|
|
146
|
+
*/
|
|
147
|
+
function createNoopLanguageAdapter({
|
|
148
|
+
id = 'noop',
|
|
149
|
+
extensions = ['.noop'],
|
|
150
|
+
traits = {},
|
|
151
|
+
} = {}) {
|
|
152
|
+
const emptyParse = code => ({
|
|
153
|
+
language: id,
|
|
154
|
+
totalLines: code ? code.split('\n').length : 0,
|
|
155
|
+
functions: [],
|
|
156
|
+
classes: [],
|
|
157
|
+
stateObjects: [],
|
|
158
|
+
imports: [],
|
|
159
|
+
exports: [],
|
|
160
|
+
});
|
|
161
|
+
const languageModule = {
|
|
162
|
+
parse: emptyParse,
|
|
163
|
+
findCallsInCode: () => [],
|
|
164
|
+
findImportsInCode: () => [],
|
|
165
|
+
findExportsInCode: () => [],
|
|
166
|
+
findUsagesInCode: () => [],
|
|
167
|
+
isEntryPoint: () => false,
|
|
168
|
+
getEntryPointKind: () => null,
|
|
169
|
+
};
|
|
170
|
+
return createLanguageAdapter({
|
|
171
|
+
name: id,
|
|
172
|
+
extensions,
|
|
173
|
+
treeSitterLang: id,
|
|
174
|
+
module: () => languageModule,
|
|
175
|
+
treeSitterModule: () => null,
|
|
176
|
+
traits,
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function validateLanguageAdapter(adapter, { analyzeSample = false } = {}) {
|
|
181
|
+
const failures = [];
|
|
182
|
+
if (!adapter || typeof adapter !== 'object') return ['adapter must be an object'];
|
|
183
|
+
if (!adapter.id) failures.push('id is required');
|
|
184
|
+
if (!Array.isArray(adapter.extensions) || adapter.extensions.length === 0) {
|
|
185
|
+
failures.push(`${adapter.id || 'adapter'}: at least one extension is required`);
|
|
186
|
+
}
|
|
187
|
+
for (const method of ['parse', 'findCalls', 'findImports', 'findExports',
|
|
188
|
+
'findUsages', 'getEntryPointKind', 'isEntryPoint', 'analyze']) {
|
|
189
|
+
if (typeof adapter[method] !== 'function') {
|
|
190
|
+
failures.push(`${adapter.id || 'adapter'}: missing ${method}()`);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
for (const method of REQUIRED_ADAPTER_OPERATIONS) {
|
|
194
|
+
if (typeof adapter[method] !== 'function') {
|
|
195
|
+
failures.push(`${adapter.id || 'adapter'}: missing parser operation ${method}()`);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
if (!adapter.capabilities || typeof adapter.capabilities !== 'object') {
|
|
199
|
+
failures.push(`${adapter.id || 'adapter'}: capabilities are required`);
|
|
200
|
+
}
|
|
201
|
+
if (analyzeSample && failures.length === 0) {
|
|
202
|
+
try {
|
|
203
|
+
const ir = adapter.analyze('', null, 'empty' + adapter.extensions[0]);
|
|
204
|
+
failures.push(...validateFileIR(ir).map(failure => `${adapter.id}: ${failure}`));
|
|
205
|
+
} catch (error) {
|
|
206
|
+
failures.push(`${adapter.id}: sample analysis failed: ${error.message}`);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
return failures;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
module.exports = {
|
|
213
|
+
REQUIRED_ADAPTER_OPERATIONS,
|
|
214
|
+
OPTIONAL_HELPERS,
|
|
215
|
+
createLanguageAdapter,
|
|
216
|
+
createNoopLanguageAdapter,
|
|
217
|
+
validateLanguageAdapter,
|
|
218
|
+
};
|