ucn 3.8.12 → 3.8.14
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 +3 -1
- package/.github/workflows/ci.yml +15 -3
- package/.github/workflows/publish.yml +20 -8
- package/README.md +1 -0
- package/cli/index.js +165 -246
- package/core/analysis.js +1400 -0
- package/core/build-worker.js +194 -0
- package/core/cache.js +105 -7
- package/core/callers.js +194 -64
- package/core/deadcode.js +22 -66
- package/core/discovery.js +9 -54
- package/core/execute.js +139 -54
- package/core/graph.js +615 -0
- package/core/output/analysis-ext.js +271 -0
- package/core/output/analysis.js +491 -0
- package/core/output/extraction.js +188 -0
- package/core/output/find.js +355 -0
- package/core/output/graph.js +399 -0
- package/core/output/refactoring.js +293 -0
- package/core/output/reporting.js +331 -0
- package/core/output/search.js +307 -0
- package/core/output/shared.js +271 -0
- package/core/output/tracing.js +416 -0
- package/core/output.js +15 -3293
- package/core/parallel-build.js +165 -0
- package/core/project.js +299 -3633
- package/core/registry.js +59 -0
- package/core/reporting.js +258 -0
- package/core/search.js +890 -0
- package/core/stacktrace.js +1 -1
- package/core/tracing.js +631 -0
- package/core/verify.js +10 -13
- package/eslint.config.js +43 -0
- package/jsconfig.json +10 -0
- package/languages/go.js +21 -2
- package/languages/html.js +8 -0
- package/languages/index.js +102 -40
- package/languages/java.js +13 -0
- package/languages/javascript.js +17 -1
- package/languages/python.js +14 -0
- package/languages/rust.js +13 -0
- package/languages/utils.js +1 -1
- package/mcp/server.js +45 -28
- package/package.json +8 -3
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* core/output/analysis-ext.js - Extended analysis formatters (related, smart, diffImpact)
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const { dynamicImportsNote, formatLineRanges } = require('./shared');
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Format related command output - text
|
|
9
|
+
*/
|
|
10
|
+
function formatRelated(related, options = {}) {
|
|
11
|
+
if (!related) {
|
|
12
|
+
return 'Function not found.';
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const lines = [];
|
|
16
|
+
|
|
17
|
+
// Header
|
|
18
|
+
lines.push(`Related to ${related.target.name}`);
|
|
19
|
+
lines.push('═'.repeat(60));
|
|
20
|
+
lines.push(`${related.target.file}:${related.target.line}`);
|
|
21
|
+
lines.push('');
|
|
22
|
+
|
|
23
|
+
// Same file
|
|
24
|
+
let relatedTruncated = false;
|
|
25
|
+
if (related.sameFile.length > 0) {
|
|
26
|
+
const maxSameFile = options.top || (options.all ? Infinity : 8);
|
|
27
|
+
lines.push(`SAME FILE (${related.sameFile.length}):`);
|
|
28
|
+
for (const f of related.sameFile.slice(0, maxSameFile)) {
|
|
29
|
+
const params = f.params ? `(${f.params})` : '';
|
|
30
|
+
lines.push(` :${f.line} ${f.name}${params}`);
|
|
31
|
+
}
|
|
32
|
+
if (related.sameFile.length > maxSameFile) {
|
|
33
|
+
relatedTruncated = true;
|
|
34
|
+
lines.push(` ... and ${related.sameFile.length - maxSameFile} more`);
|
|
35
|
+
}
|
|
36
|
+
lines.push('');
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Similar names
|
|
40
|
+
if (related.similarNames.length > 0) {
|
|
41
|
+
const similarTotal = related.similarNamesTotal || related.similarNames.length;
|
|
42
|
+
const similarLabel = similarTotal > related.similarNames.length
|
|
43
|
+
? `${related.similarNames.length} of ${similarTotal}` : `${related.similarNames.length}`;
|
|
44
|
+
lines.push(`SIMILAR NAMES (${similarLabel}):`);
|
|
45
|
+
for (const s of related.similarNames) {
|
|
46
|
+
lines.push(` ${s.name} - ${s.file}:${s.line}`);
|
|
47
|
+
lines.push(` shared: ${s.sharedParts.join(', ')}`);
|
|
48
|
+
}
|
|
49
|
+
if (similarTotal > related.similarNames.length) relatedTruncated = true;
|
|
50
|
+
lines.push('');
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Shared callers
|
|
54
|
+
if (related.sharedCallers.length > 0) {
|
|
55
|
+
const callersTotal = related.sharedCallersTotal || related.sharedCallers.length;
|
|
56
|
+
const callersLabel = callersTotal > related.sharedCallers.length
|
|
57
|
+
? `${related.sharedCallers.length} of ${callersTotal}` : `${related.sharedCallers.length}`;
|
|
58
|
+
lines.push(`CALLED BY SAME FUNCTIONS (${callersLabel}):`);
|
|
59
|
+
for (const s of related.sharedCallers) {
|
|
60
|
+
lines.push(` ${s.name} - ${s.file}:${s.line} (${s.sharedCallerCount} shared callers)`);
|
|
61
|
+
}
|
|
62
|
+
if (callersTotal > related.sharedCallers.length) relatedTruncated = true;
|
|
63
|
+
lines.push('');
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Shared callees
|
|
67
|
+
if (related.sharedCallees.length > 0) {
|
|
68
|
+
const calleesTotal = related.sharedCalleesTotal || related.sharedCallees.length;
|
|
69
|
+
const calleesLabel = calleesTotal > related.sharedCallees.length
|
|
70
|
+
? `${related.sharedCallees.length} of ${calleesTotal}` : `${related.sharedCallees.length}`;
|
|
71
|
+
lines.push(`CALLS SAME FUNCTIONS (${calleesLabel}):`);
|
|
72
|
+
for (const s of related.sharedCallees) {
|
|
73
|
+
lines.push(` ${s.name} - ${s.file}:${s.line} (${s.sharedCalleeCount} shared callees)`);
|
|
74
|
+
}
|
|
75
|
+
if (calleesTotal > related.sharedCallees.length) relatedTruncated = true;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (relatedTruncated) {
|
|
79
|
+
const allHint = options.allHint || 'Use --all to show all.';
|
|
80
|
+
lines.push(`\nSome sections truncated. ${allHint}`);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return lines.join('\n');
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Format related command output - JSON
|
|
88
|
+
*/
|
|
89
|
+
function formatRelatedJson(related) {
|
|
90
|
+
if (!related) {
|
|
91
|
+
return JSON.stringify({ found: false, error: 'Function not found' }, null, 2);
|
|
92
|
+
}
|
|
93
|
+
return JSON.stringify(related, null, 2);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Format smart command output
|
|
98
|
+
* @param {object} smart - Smart extraction result
|
|
99
|
+
* @param {object} [options] - Formatting options
|
|
100
|
+
* @param {string} [options.uncertainHint] - Custom hint for uncertain calls
|
|
101
|
+
*/
|
|
102
|
+
function formatSmart(smart, options = {}) {
|
|
103
|
+
if (!smart) return 'Function not found.';
|
|
104
|
+
|
|
105
|
+
const lines = [];
|
|
106
|
+
lines.push(`${smart.target.name} (${smart.target.file}:${smart.target.startLine})`);
|
|
107
|
+
lines.push('═'.repeat(60));
|
|
108
|
+
|
|
109
|
+
if (smart.meta) {
|
|
110
|
+
const notes = [];
|
|
111
|
+
if (smart.meta.dynamicImports) { const dn = dynamicImportsNote(smart.meta.dynamicImports, smart.meta); if (dn) notes.push(dn); }
|
|
112
|
+
if (smart.meta.uncertain) notes.push(`${smart.meta.uncertain} uncertain call(s) skipped`);
|
|
113
|
+
if (notes.length) {
|
|
114
|
+
const uncertainSuffix = smart.meta.uncertain && options.uncertainHint ? ` — ${options.uncertainHint}` : '';
|
|
115
|
+
lines.push(` Note: ${notes.join(', ')}${uncertainSuffix}`);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
lines.push(smart.target.code);
|
|
120
|
+
|
|
121
|
+
if (smart.dependencies.length > 0) {
|
|
122
|
+
lines.push('\n─── DEPENDENCIES ───');
|
|
123
|
+
for (const dep of smart.dependencies) {
|
|
124
|
+
const weight = dep.weight && dep.weight !== 'normal' ? ` [${dep.weight}]` : '';
|
|
125
|
+
lines.push(`\n// ${dep.name}${weight} (${dep.relativePath}:${dep.startLine})`);
|
|
126
|
+
lines.push(dep.code);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (smart.types && smart.types.length > 0) {
|
|
131
|
+
lines.push('\n─── TYPES ───');
|
|
132
|
+
for (const t of smart.types) {
|
|
133
|
+
lines.push(`\n// ${t.name} (${t.relativePath}:${t.startLine})`);
|
|
134
|
+
lines.push(t.code);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return lines.join('\n');
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Format smart extraction result as JSON
|
|
143
|
+
* Includes function + all dependencies
|
|
144
|
+
*/
|
|
145
|
+
function formatSmartJson(result) {
|
|
146
|
+
if (!result) return JSON.stringify({ found: false, error: 'Function not found' }, null, 2);
|
|
147
|
+
const meta = result.meta || { complete: true, skipped: 0, dynamicImports: 0, uncertain: 0 };
|
|
148
|
+
return JSON.stringify({
|
|
149
|
+
meta,
|
|
150
|
+
data: {
|
|
151
|
+
target: {
|
|
152
|
+
name: result.target.name,
|
|
153
|
+
file: result.target.file,
|
|
154
|
+
startLine: result.target.startLine,
|
|
155
|
+
endLine: result.target.endLine,
|
|
156
|
+
params: result.target.params,
|
|
157
|
+
returnType: result.target.returnType,
|
|
158
|
+
code: result.target.code
|
|
159
|
+
},
|
|
160
|
+
dependencies: result.dependencies.map(d => ({
|
|
161
|
+
name: d.name,
|
|
162
|
+
type: d.type,
|
|
163
|
+
file: d.file,
|
|
164
|
+
startLine: d.startLine,
|
|
165
|
+
endLine: d.endLine,
|
|
166
|
+
params: d.params,
|
|
167
|
+
weight: d.weight, // core, setup, utility
|
|
168
|
+
callCount: d.callCount,
|
|
169
|
+
code: d.code
|
|
170
|
+
})),
|
|
171
|
+
types: result.types || []
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Format diff impact command output - text
|
|
178
|
+
*/
|
|
179
|
+
function formatDiffImpact(result, options = {}) {
|
|
180
|
+
if (!result) return 'No diff data.';
|
|
181
|
+
|
|
182
|
+
const lines = [];
|
|
183
|
+
const MAX_CALLERS_PER_FN = options.all ? Infinity : 30;
|
|
184
|
+
|
|
185
|
+
lines.push(`Diff Impact Analysis (vs ${result.base})`);
|
|
186
|
+
lines.push('═'.repeat(60));
|
|
187
|
+
|
|
188
|
+
const s = result.summary || {};
|
|
189
|
+
const parts = [];
|
|
190
|
+
if (s.modifiedFunctions > 0) parts.push(`${s.modifiedFunctions} modified`);
|
|
191
|
+
if (s.deletedFunctions > 0) parts.push(`${s.deletedFunctions} deleted`);
|
|
192
|
+
if (s.newFunctions > 0) parts.push(`${s.newFunctions} new`);
|
|
193
|
+
parts.push(`${s.totalCallSites || 0} call sites across ${s.affectedFiles || 0} files`);
|
|
194
|
+
lines.push(parts.join(', '));
|
|
195
|
+
lines.push('');
|
|
196
|
+
|
|
197
|
+
// Modified functions
|
|
198
|
+
if (result.functions.length > 0) {
|
|
199
|
+
lines.push('MODIFIED FUNCTIONS:');
|
|
200
|
+
for (const fn of result.functions) {
|
|
201
|
+
lines.push(`\n ${fn.name}`);
|
|
202
|
+
lines.push(` ${fn.relativePath}:${fn.startLine}`);
|
|
203
|
+
lines.push(` ${fn.signature}`);
|
|
204
|
+
if (fn.addedLines.length > 0) {
|
|
205
|
+
lines.push(` Lines added: ${formatLineRanges(fn.addedLines)}`);
|
|
206
|
+
}
|
|
207
|
+
if (fn.deletedLines.length > 0) {
|
|
208
|
+
lines.push(` Lines deleted: ${formatLineRanges(fn.deletedLines)}`);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
if (fn.callers.length > 0) {
|
|
212
|
+
const displayCallers = fn.callers.slice(0, MAX_CALLERS_PER_FN);
|
|
213
|
+
const truncated = fn.callers.length - displayCallers.length;
|
|
214
|
+
lines.push(` Callers (${fn.callers.length}):`);
|
|
215
|
+
for (const c of displayCallers) {
|
|
216
|
+
const caller = c.callerName ? `[${c.callerName}]` : '';
|
|
217
|
+
lines.push(` ${c.relativePath}:${c.line} ${caller}`);
|
|
218
|
+
lines.push(` ${c.content}`);
|
|
219
|
+
}
|
|
220
|
+
if (truncated > 0) {
|
|
221
|
+
lines.push(` ... ${truncated} more callers (use file= to scope diff to specific files, or use impact with class_name= for type-filtered results)`);
|
|
222
|
+
}
|
|
223
|
+
} else {
|
|
224
|
+
lines.push(' Callers: none found');
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// New functions
|
|
230
|
+
if (result.newFunctions.length > 0) {
|
|
231
|
+
lines.push('\nNEW FUNCTIONS:');
|
|
232
|
+
for (const fn of result.newFunctions) {
|
|
233
|
+
lines.push(` ${fn.name} — ${fn.relativePath}:${fn.startLine}`);
|
|
234
|
+
lines.push(` ${fn.signature}`);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// Deleted functions
|
|
239
|
+
if (result.deletedFunctions.length > 0) {
|
|
240
|
+
lines.push('\nDELETED FUNCTIONS:');
|
|
241
|
+
for (const fn of result.deletedFunctions) {
|
|
242
|
+
lines.push(` ${fn.name} — ${fn.relativePath}:${fn.startLine}`);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Module-level changes
|
|
247
|
+
if (result.moduleLevelChanges.length > 0) {
|
|
248
|
+
lines.push('\nMODULE-LEVEL CHANGES:');
|
|
249
|
+
for (const m of result.moduleLevelChanges) {
|
|
250
|
+
const changeParts = [];
|
|
251
|
+
if (m.addedLines.length > 0) changeParts.push(`+${m.addedLines.length} lines`);
|
|
252
|
+
if (m.deletedLines.length > 0) changeParts.push(`-${m.deletedLines.length} lines`);
|
|
253
|
+
lines.push(` ${m.relativePath}: ${changeParts.join(', ')}`);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
return lines.join('\n');
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function formatDiffImpactJson(result) {
|
|
261
|
+
return JSON.stringify(result, null, 2);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
module.exports = {
|
|
265
|
+
formatRelated,
|
|
266
|
+
formatRelatedJson,
|
|
267
|
+
formatSmart,
|
|
268
|
+
formatSmartJson,
|
|
269
|
+
formatDiffImpact,
|
|
270
|
+
formatDiffImpactJson,
|
|
271
|
+
};
|