pnpm-catalog-updates 1.0.3 → 1.1.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/README.md +15 -0
- package/dist/index.js +22031 -10684
- package/dist/index.js.map +1 -1
- package/package.json +7 -2
- package/src/cli/__tests__/commandRegistrar.test.ts +248 -0
- package/src/cli/commandRegistrar.ts +785 -0
- package/src/cli/commands/__tests__/aiCommand.test.ts +161 -0
- package/src/cli/commands/__tests__/analyzeCommand.test.ts +283 -0
- package/src/cli/commands/__tests__/checkCommand.test.ts +435 -0
- package/src/cli/commands/__tests__/graphCommand.test.ts +312 -0
- package/src/cli/commands/__tests__/initCommand.test.ts +317 -0
- package/src/cli/commands/__tests__/rollbackCommand.test.ts +400 -0
- package/src/cli/commands/__tests__/securityCommand.test.ts +467 -0
- package/src/cli/commands/__tests__/themeCommand.test.ts +166 -0
- package/src/cli/commands/__tests__/updateCommand.test.ts +720 -0
- package/src/cli/commands/__tests__/workspaceCommand.test.ts +286 -0
- package/src/cli/commands/aiCommand.ts +163 -0
- package/src/cli/commands/analyzeCommand.ts +219 -0
- package/src/cli/commands/checkCommand.ts +91 -98
- package/src/cli/commands/graphCommand.ts +475 -0
- package/src/cli/commands/initCommand.ts +64 -54
- package/src/cli/commands/rollbackCommand.ts +334 -0
- package/src/cli/commands/securityCommand.ts +165 -100
- package/src/cli/commands/themeCommand.ts +148 -0
- package/src/cli/commands/updateCommand.ts +215 -263
- package/src/cli/commands/workspaceCommand.ts +73 -0
- package/src/cli/constants/cliChoices.ts +93 -0
- package/src/cli/formatters/__tests__/__snapshots__/outputFormatter.test.ts.snap +557 -0
- package/src/cli/formatters/__tests__/ciFormatter.test.ts +526 -0
- package/src/cli/formatters/__tests__/outputFormatter.test.ts +448 -0
- package/src/cli/formatters/__tests__/progressBar.test.ts +709 -0
- package/src/cli/formatters/ciFormatter.ts +964 -0
- package/src/cli/formatters/colorUtils.ts +145 -0
- package/src/cli/formatters/outputFormatter.ts +615 -332
- package/src/cli/formatters/progressBar.ts +43 -52
- package/src/cli/formatters/versionFormatter.ts +132 -0
- package/src/cli/handlers/aiAnalysisHandler.ts +205 -0
- package/src/cli/handlers/changelogHandler.ts +113 -0
- package/src/cli/handlers/index.ts +9 -0
- package/src/cli/handlers/installHandler.ts +130 -0
- package/src/cli/index.ts +175 -726
- package/src/cli/interactive/InteractiveOptionsCollector.ts +387 -0
- package/src/cli/interactive/interactivePrompts.ts +189 -83
- package/src/cli/interactive/optionUtils.ts +89 -0
- package/src/cli/themes/colorTheme.ts +43 -16
- package/src/cli/utils/cliOutput.ts +118 -0
- package/src/cli/utils/commandHelpers.ts +249 -0
- package/src/cli/validators/commandValidator.ts +321 -336
- package/src/cli/validators/index.ts +37 -2
- package/src/cli/options/globalOptions.ts +0 -437
- package/src/cli/options/index.ts +0 -5
|
@@ -2,38 +2,78 @@
|
|
|
2
2
|
* Output Formatter
|
|
3
3
|
*
|
|
4
4
|
* Provides formatted output for CLI commands in various formats.
|
|
5
|
-
* Supports table, JSON, YAML, and
|
|
5
|
+
* Supports table, JSON, YAML, minimal, and CI/CD output formats.
|
|
6
|
+
* CI formats include GitHub Actions, GitLab CI, JUnit XML, and SARIF.
|
|
6
7
|
*/
|
|
7
8
|
|
|
8
9
|
import type {
|
|
9
10
|
AnalysisResult,
|
|
10
11
|
ImpactAnalysis,
|
|
11
12
|
OutdatedReport,
|
|
13
|
+
UpdatePlan,
|
|
12
14
|
UpdateResult,
|
|
15
|
+
WorkspaceInfo,
|
|
13
16
|
WorkspaceStats,
|
|
14
17
|
WorkspaceValidationReport,
|
|
15
18
|
} from '@pcu/core'
|
|
19
|
+
import { countUpdateTypes } from '@pcu/core'
|
|
20
|
+
import { t } from '@pcu/utils'
|
|
16
21
|
import chalk from 'chalk'
|
|
17
22
|
import Table from 'cli-table3'
|
|
18
23
|
import YAML from 'yaml'
|
|
19
24
|
import type { SecurityReport } from '../commands/securityCommand.js'
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
25
|
+
import { CIFormatter, type CIOutputFormat } from './ciFormatter.js'
|
|
26
|
+
import { ColorUtils } from './colorUtils.js'
|
|
27
|
+
import { VersionFormatter } from './versionFormatter.js'
|
|
28
|
+
|
|
29
|
+
export type OutputFormat =
|
|
30
|
+
| 'table'
|
|
31
|
+
| 'json'
|
|
32
|
+
| 'yaml'
|
|
33
|
+
| 'minimal'
|
|
34
|
+
| 'github'
|
|
35
|
+
| 'gitlab'
|
|
36
|
+
| 'junit'
|
|
37
|
+
| 'sarif'
|
|
38
|
+
|
|
39
|
+
export type CIFormat = 'github' | 'gitlab' | 'junit' | 'sarif'
|
|
40
|
+
|
|
41
|
+
export function isCIFormat(format: OutputFormat): format is CIFormat {
|
|
42
|
+
return ['github', 'gitlab', 'junit', 'sarif'].includes(format)
|
|
43
|
+
}
|
|
26
44
|
|
|
27
45
|
export class OutputFormatter {
|
|
46
|
+
private ciFormatter: CIFormatter | null = null
|
|
47
|
+
private readonly colorUtils: ColorUtils
|
|
48
|
+
private readonly versionFormatter: VersionFormatter
|
|
49
|
+
|
|
28
50
|
constructor(
|
|
29
51
|
private readonly format: OutputFormat = 'table',
|
|
30
52
|
private readonly useColor: boolean = true
|
|
31
|
-
) {
|
|
53
|
+
) {
|
|
54
|
+
this.colorUtils = new ColorUtils(useColor)
|
|
55
|
+
this.versionFormatter = new VersionFormatter(this.colorUtils, useColor)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Get or create CIFormatter instance for CI output formats
|
|
60
|
+
*/
|
|
61
|
+
private getCIFormatter(): CIFormatter {
|
|
62
|
+
if (!this.ciFormatter) {
|
|
63
|
+
this.ciFormatter = new CIFormatter(this.format as CIOutputFormat)
|
|
64
|
+
}
|
|
65
|
+
return this.ciFormatter
|
|
66
|
+
}
|
|
32
67
|
|
|
33
68
|
/**
|
|
34
69
|
* Format outdated dependencies report
|
|
35
70
|
*/
|
|
36
71
|
formatOutdatedReport(report: OutdatedReport): string {
|
|
72
|
+
// Delegate to CIFormatter for CI output formats
|
|
73
|
+
if (isCIFormat(this.format)) {
|
|
74
|
+
return this.getCIFormatter().formatOutdatedReport(report)
|
|
75
|
+
}
|
|
76
|
+
|
|
37
77
|
switch (this.format) {
|
|
38
78
|
case 'json':
|
|
39
79
|
return JSON.stringify(report, null, 2)
|
|
@@ -50,6 +90,11 @@ export class OutputFormatter {
|
|
|
50
90
|
* Format update result
|
|
51
91
|
*/
|
|
52
92
|
formatUpdateResult(result: UpdateResult): string {
|
|
93
|
+
// Delegate to CIFormatter for CI output formats
|
|
94
|
+
if (isCIFormat(this.format)) {
|
|
95
|
+
return this.getCIFormatter().formatUpdateResult(result)
|
|
96
|
+
}
|
|
97
|
+
|
|
53
98
|
switch (this.format) {
|
|
54
99
|
case 'json':
|
|
55
100
|
return JSON.stringify(result, null, 2)
|
|
@@ -62,6 +107,27 @@ export class OutputFormatter {
|
|
|
62
107
|
}
|
|
63
108
|
}
|
|
64
109
|
|
|
110
|
+
/**
|
|
111
|
+
* Format update plan (for dry-run mode)
|
|
112
|
+
*/
|
|
113
|
+
formatUpdatePlan(plan: UpdatePlan): string {
|
|
114
|
+
// Delegate to CIFormatter for CI output formats
|
|
115
|
+
if (isCIFormat(this.format)) {
|
|
116
|
+
return this.getCIFormatter().formatUpdatePlan(plan)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
switch (this.format) {
|
|
120
|
+
case 'json':
|
|
121
|
+
return JSON.stringify(plan, null, 2)
|
|
122
|
+
case 'yaml':
|
|
123
|
+
return YAML.stringify(plan)
|
|
124
|
+
case 'minimal':
|
|
125
|
+
return this.formatUpdatePlanMinimal(plan)
|
|
126
|
+
default:
|
|
127
|
+
return this.formatUpdatePlanTable(plan)
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
65
131
|
/**
|
|
66
132
|
* Format impact analysis
|
|
67
133
|
*/
|
|
@@ -82,6 +148,11 @@ export class OutputFormatter {
|
|
|
82
148
|
* Format workspace validation report
|
|
83
149
|
*/
|
|
84
150
|
formatValidationReport(report: WorkspaceValidationReport): string {
|
|
151
|
+
// Delegate to CIFormatter for CI output formats
|
|
152
|
+
if (isCIFormat(this.format)) {
|
|
153
|
+
return this.getCIFormatter().formatValidationReport(report)
|
|
154
|
+
}
|
|
155
|
+
|
|
85
156
|
switch (this.format) {
|
|
86
157
|
case 'json':
|
|
87
158
|
return JSON.stringify(report, null, 2)
|
|
@@ -94,6 +165,22 @@ export class OutputFormatter {
|
|
|
94
165
|
}
|
|
95
166
|
}
|
|
96
167
|
|
|
168
|
+
/**
|
|
169
|
+
* Format workspace information
|
|
170
|
+
*/
|
|
171
|
+
formatWorkspaceInfo(info: WorkspaceInfo): string {
|
|
172
|
+
switch (this.format) {
|
|
173
|
+
case 'json':
|
|
174
|
+
return JSON.stringify(info, null, 2)
|
|
175
|
+
case 'yaml':
|
|
176
|
+
return YAML.stringify(info)
|
|
177
|
+
case 'minimal':
|
|
178
|
+
return this.formatWorkspaceInfoMinimal(info)
|
|
179
|
+
default:
|
|
180
|
+
return this.formatWorkspaceInfoTable(info)
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
97
184
|
/**
|
|
98
185
|
* Format workspace statistics
|
|
99
186
|
*/
|
|
@@ -114,6 +201,11 @@ export class OutputFormatter {
|
|
|
114
201
|
* Format security report
|
|
115
202
|
*/
|
|
116
203
|
formatSecurityReport(report: SecurityReport): string {
|
|
204
|
+
// Delegate to CIFormatter for CI output formats
|
|
205
|
+
if (isCIFormat(this.format)) {
|
|
206
|
+
return this.getCIFormatter().formatSecurityReport(report)
|
|
207
|
+
}
|
|
208
|
+
|
|
117
209
|
switch (this.format) {
|
|
118
210
|
case 'json':
|
|
119
211
|
return JSON.stringify(report, null, 2)
|
|
@@ -153,35 +245,50 @@ export class OutputFormatter {
|
|
|
153
245
|
const lines: string[] = []
|
|
154
246
|
|
|
155
247
|
// Header
|
|
156
|
-
lines.push(
|
|
157
|
-
|
|
248
|
+
lines.push(
|
|
249
|
+
this.colorUtils.colorize(chalk.bold, `\n${t('format.workspace')}: ${report.workspace.name}`)
|
|
250
|
+
)
|
|
251
|
+
lines.push(
|
|
252
|
+
this.colorUtils.colorize(chalk.gray, `${t('format.path')}: ${report.workspace.path}`)
|
|
253
|
+
)
|
|
158
254
|
|
|
159
255
|
if (!report.hasUpdates) {
|
|
160
|
-
lines.push(this.colorize(chalk.green,
|
|
256
|
+
lines.push(this.colorUtils.colorize(chalk.green, `\n✅ ${t('format.allUpToDate')}`))
|
|
161
257
|
return lines.join('\n')
|
|
162
258
|
}
|
|
163
259
|
|
|
164
260
|
lines.push(
|
|
165
|
-
this.colorize(
|
|
261
|
+
this.colorUtils.colorize(
|
|
262
|
+
chalk.yellow,
|
|
263
|
+
`\n${t('format.foundOutdated', { count: String(report.totalOutdated) })}\n`
|
|
264
|
+
)
|
|
166
265
|
)
|
|
167
266
|
|
|
168
267
|
for (const catalogInfo of report.catalogs) {
|
|
169
268
|
if (catalogInfo.outdatedCount === 0) continue
|
|
170
269
|
|
|
171
|
-
lines.push(
|
|
270
|
+
lines.push(
|
|
271
|
+
this.colorUtils.colorize(chalk.bold, `${t('format.catalog')}: ${catalogInfo.catalogName}`)
|
|
272
|
+
)
|
|
172
273
|
|
|
173
274
|
const table = new Table({
|
|
174
|
-
head: this.colorizeHeaders([
|
|
275
|
+
head: this.colorUtils.colorizeHeaders([
|
|
276
|
+
t('table.header.package'),
|
|
277
|
+
t('table.header.current'),
|
|
278
|
+
t('table.header.latest'),
|
|
279
|
+
t('table.header.type'),
|
|
280
|
+
t('table.header.packagesCount'),
|
|
281
|
+
]),
|
|
175
282
|
style: { head: [], border: [] },
|
|
176
283
|
colWidths: [25, 15, 15, 8, 20],
|
|
177
284
|
})
|
|
178
285
|
|
|
179
286
|
for (const dep of catalogInfo.outdatedDependencies) {
|
|
180
|
-
const typeColor = this.getUpdateTypeColor(dep.updateType)
|
|
181
|
-
const securityIcon = dep.isSecurityUpdate ? '
|
|
287
|
+
const typeColor = this.colorUtils.getUpdateTypeColor(dep.updateType)
|
|
288
|
+
const securityIcon = dep.isSecurityUpdate ? '[SEC] ' : ''
|
|
182
289
|
|
|
183
290
|
// Colorize version differences
|
|
184
|
-
const { currentColored, latestColored } = this.colorizeVersionDiff(
|
|
291
|
+
const { currentColored, latestColored } = this.versionFormatter.colorizeVersionDiff(
|
|
185
292
|
dep.currentVersion,
|
|
186
293
|
dep.latestVersion,
|
|
187
294
|
dep.updateType
|
|
@@ -191,8 +298,8 @@ export class OutputFormatter {
|
|
|
191
298
|
`${securityIcon}${dep.packageName}`,
|
|
192
299
|
currentColored,
|
|
193
300
|
latestColored,
|
|
194
|
-
this.colorize(typeColor, dep.updateType),
|
|
195
|
-
|
|
301
|
+
this.colorUtils.colorize(typeColor, dep.updateType),
|
|
302
|
+
t('common.packagesCount', { count: String(dep.affectedPackages.length) }),
|
|
196
303
|
])
|
|
197
304
|
}
|
|
198
305
|
|
|
@@ -208,7 +315,7 @@ export class OutputFormatter {
|
|
|
208
315
|
*/
|
|
209
316
|
private formatOutdatedMinimal(report: OutdatedReport): string {
|
|
210
317
|
if (!report.hasUpdates) {
|
|
211
|
-
return '
|
|
318
|
+
return t('format.allUpToDate')
|
|
212
319
|
}
|
|
213
320
|
|
|
214
321
|
// Collect all dependencies first to calculate max package name width
|
|
@@ -221,8 +328,8 @@ export class OutputFormatter {
|
|
|
221
328
|
|
|
222
329
|
for (const catalogInfo of report.catalogs) {
|
|
223
330
|
for (const dep of catalogInfo.outdatedDependencies) {
|
|
224
|
-
const securityIcon = dep.isSecurityUpdate ? '
|
|
225
|
-
const { currentColored, latestColored } = this.colorizeVersionDiff(
|
|
331
|
+
const securityIcon = dep.isSecurityUpdate ? '[SEC] ' : ''
|
|
332
|
+
const { currentColored, latestColored } = this.versionFormatter.colorizeVersionDiff(
|
|
226
333
|
dep.currentVersion,
|
|
227
334
|
dep.latestVersion,
|
|
228
335
|
dep.updateType
|
|
@@ -242,19 +349,16 @@ export class OutputFormatter {
|
|
|
242
349
|
)
|
|
243
350
|
|
|
244
351
|
// Calculate max version widths (need to strip color codes for accurate width calculation)
|
|
245
|
-
const
|
|
246
|
-
|
|
352
|
+
const maxCurrentWidth = Math.max(
|
|
353
|
+
...allDeps.map((dep) => this.colorUtils.stripAnsi(dep.currentColored).length)
|
|
354
|
+
)
|
|
247
355
|
|
|
248
356
|
// Format lines with proper alignment
|
|
249
357
|
const lines: string[] = []
|
|
250
358
|
for (const dep of allDeps) {
|
|
251
359
|
const nameWithIcon = dep.securityIcon + dep.packageName
|
|
252
360
|
const paddedName = nameWithIcon.padEnd(maxNameWidth)
|
|
253
|
-
|
|
254
|
-
// For current version alignment, we need to pad the visible text, not the colored version
|
|
255
|
-
const currentVisible = stripAnsi(dep.currentColored)
|
|
256
|
-
const currentPadding = maxCurrentWidth - currentVisible.length
|
|
257
|
-
const paddedCurrent = dep.currentColored + ' '.repeat(currentPadding)
|
|
361
|
+
const paddedCurrent = this.colorUtils.padAnsi(dep.currentColored, maxCurrentWidth)
|
|
258
362
|
|
|
259
363
|
lines.push(`${paddedName} ${paddedCurrent} → ${dep.latestColored}`)
|
|
260
364
|
}
|
|
@@ -269,31 +373,44 @@ export class OutputFormatter {
|
|
|
269
373
|
const lines: string[] = []
|
|
270
374
|
|
|
271
375
|
// Header
|
|
272
|
-
lines.push(
|
|
376
|
+
lines.push(
|
|
377
|
+
this.colorUtils.colorize(chalk.bold, `\n${t('format.workspace')}: ${result.workspace.name}`)
|
|
378
|
+
)
|
|
273
379
|
|
|
274
380
|
if (result.success) {
|
|
275
|
-
lines.push(this.colorize(chalk.green,
|
|
381
|
+
lines.push(this.colorUtils.colorize(chalk.green, `✅ ${t('format.updateCompleted')}`))
|
|
276
382
|
} else {
|
|
277
|
-
lines.push(this.colorize(chalk.red,
|
|
383
|
+
lines.push(this.colorUtils.colorize(chalk.red, `❌ ${t('format.updateFailed')}`))
|
|
278
384
|
}
|
|
279
385
|
|
|
280
386
|
lines.push('')
|
|
281
387
|
|
|
282
388
|
// Updated dependencies
|
|
283
389
|
if (result.updatedDependencies.length > 0) {
|
|
284
|
-
lines.push(
|
|
390
|
+
lines.push(
|
|
391
|
+
this.colorUtils.colorize(
|
|
392
|
+
chalk.green,
|
|
393
|
+
`${t('format.updatedCount', { count: String(result.totalUpdated) })}:`
|
|
394
|
+
)
|
|
395
|
+
)
|
|
285
396
|
|
|
286
397
|
const table = new Table({
|
|
287
|
-
head: this.colorizeHeaders([
|
|
398
|
+
head: this.colorUtils.colorizeHeaders([
|
|
399
|
+
t('table.header.catalog'),
|
|
400
|
+
t('table.header.package'),
|
|
401
|
+
t('table.header.from'),
|
|
402
|
+
t('table.header.to'),
|
|
403
|
+
t('table.header.type'),
|
|
404
|
+
]),
|
|
288
405
|
style: { head: [], border: [] },
|
|
289
406
|
colWidths: [15, 25, 15, 15, 8],
|
|
290
407
|
})
|
|
291
408
|
|
|
292
409
|
for (const dep of result.updatedDependencies) {
|
|
293
|
-
const typeColor = this.getUpdateTypeColor(dep.updateType)
|
|
410
|
+
const typeColor = this.colorUtils.getUpdateTypeColor(dep.updateType)
|
|
294
411
|
|
|
295
412
|
// Colorize version differences
|
|
296
|
-
const { currentColored, latestColored } = this.colorizeVersionDiff(
|
|
413
|
+
const { currentColored, latestColored } = this.versionFormatter.colorizeVersionDiff(
|
|
297
414
|
dep.fromVersion,
|
|
298
415
|
dep.toVersion,
|
|
299
416
|
dep.updateType
|
|
@@ -304,7 +421,7 @@ export class OutputFormatter {
|
|
|
304
421
|
dep.packageName,
|
|
305
422
|
currentColored,
|
|
306
423
|
latestColored,
|
|
307
|
-
this.colorize(typeColor, dep.updateType),
|
|
424
|
+
this.colorUtils.colorize(typeColor, dep.updateType),
|
|
308
425
|
])
|
|
309
426
|
}
|
|
310
427
|
|
|
@@ -314,7 +431,12 @@ export class OutputFormatter {
|
|
|
314
431
|
|
|
315
432
|
// Skipped dependencies
|
|
316
433
|
if (result.skippedDependencies.length > 0) {
|
|
317
|
-
lines.push(
|
|
434
|
+
lines.push(
|
|
435
|
+
this.colorUtils.colorize(
|
|
436
|
+
chalk.yellow,
|
|
437
|
+
`⚠️ ${t('format.skippedDeps')} (${result.totalSkipped}):`
|
|
438
|
+
)
|
|
439
|
+
)
|
|
318
440
|
|
|
319
441
|
for (const dep of result.skippedDependencies) {
|
|
320
442
|
lines.push(` ${dep.catalogName}:${dep.packageName} - ${dep.reason}`)
|
|
@@ -324,10 +446,15 @@ export class OutputFormatter {
|
|
|
324
446
|
|
|
325
447
|
// Errors
|
|
326
448
|
if (result.errors.length > 0) {
|
|
327
|
-
lines.push(
|
|
449
|
+
lines.push(
|
|
450
|
+
this.colorUtils.colorize(
|
|
451
|
+
chalk.red,
|
|
452
|
+
`❌ ${t('format.errorCount', { count: String(result.totalErrors) })}:`
|
|
453
|
+
)
|
|
454
|
+
)
|
|
328
455
|
|
|
329
456
|
for (const error of result.errors) {
|
|
330
|
-
const prefix = error.fatal ? '
|
|
457
|
+
const prefix = error.fatal ? '!!' : '⚠️'
|
|
331
458
|
lines.push(` ${prefix} ${error.catalogName}:${error.packageName} - ${error.error}`)
|
|
332
459
|
}
|
|
333
460
|
}
|
|
@@ -342,15 +469,15 @@ export class OutputFormatter {
|
|
|
342
469
|
const lines: string[] = []
|
|
343
470
|
|
|
344
471
|
if (result.success) {
|
|
345
|
-
lines.push(
|
|
472
|
+
lines.push(t('format.updatedCount', { count: String(result.totalUpdated) }))
|
|
346
473
|
} else {
|
|
347
|
-
lines.push(
|
|
474
|
+
lines.push(t('format.errorCount', { count: String(result.totalErrors) }))
|
|
348
475
|
}
|
|
349
476
|
|
|
350
477
|
if (result.updatedDependencies.length > 0) {
|
|
351
478
|
// Collect version info for alignment calculation
|
|
352
479
|
const depsWithVersions = result.updatedDependencies.map((dep) => {
|
|
353
|
-
const { currentColored, latestColored } = this.colorizeVersionDiff(
|
|
480
|
+
const { currentColored, latestColored } = this.versionFormatter.colorizeVersionDiff(
|
|
354
481
|
dep.fromVersion,
|
|
355
482
|
dep.toVersion,
|
|
356
483
|
dep.updateType
|
|
@@ -364,19 +491,13 @@ export class OutputFormatter {
|
|
|
364
491
|
|
|
365
492
|
// Calculate max widths for alignment
|
|
366
493
|
const maxNameWidth = Math.max(...depsWithVersions.map((dep) => dep.packageName.length))
|
|
367
|
-
|
|
368
|
-
const stripAnsi = (str: string) => str.replace(ansiRegex, '')
|
|
369
494
|
const maxCurrentWidth = Math.max(
|
|
370
|
-
...depsWithVersions.map((dep) => stripAnsi(dep.currentColored).length)
|
|
495
|
+
...depsWithVersions.map((dep) => this.colorUtils.stripAnsi(dep.currentColored).length)
|
|
371
496
|
)
|
|
372
497
|
|
|
373
498
|
for (const dep of depsWithVersions) {
|
|
374
499
|
const paddedName = dep.packageName.padEnd(maxNameWidth)
|
|
375
|
-
|
|
376
|
-
// Pad current version for alignment
|
|
377
|
-
const currentVisible = stripAnsi(dep.currentColored)
|
|
378
|
-
const currentPadding = maxCurrentWidth - currentVisible.length
|
|
379
|
-
const paddedCurrent = dep.currentColored + ' '.repeat(currentPadding)
|
|
500
|
+
const paddedCurrent = this.colorUtils.padAnsi(dep.currentColored, maxCurrentWidth)
|
|
380
501
|
|
|
381
502
|
lines.push(`${paddedName} ${paddedCurrent} → ${dep.latestColored}`)
|
|
382
503
|
}
|
|
@@ -385,6 +506,167 @@ export class OutputFormatter {
|
|
|
385
506
|
return lines.join('\n')
|
|
386
507
|
}
|
|
387
508
|
|
|
509
|
+
/**
|
|
510
|
+
* Format update plan as table (for dry-run mode)
|
|
511
|
+
*/
|
|
512
|
+
private formatUpdatePlanTable(plan: UpdatePlan): string {
|
|
513
|
+
const lines: string[] = []
|
|
514
|
+
|
|
515
|
+
// Header
|
|
516
|
+
lines.push(
|
|
517
|
+
this.colorUtils.colorize(chalk.bold, `\n${t('format.workspace')}: ${plan.workspace.name}`)
|
|
518
|
+
)
|
|
519
|
+
lines.push(this.colorUtils.colorize(chalk.gray, `${t('format.path')}: ${plan.workspace.path}`))
|
|
520
|
+
|
|
521
|
+
if (plan.totalUpdates === 0) {
|
|
522
|
+
lines.push(this.colorUtils.colorize(chalk.green, `\n✅ ${t('format.noUpdatesPlanned')}`))
|
|
523
|
+
return lines.join('\n')
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
lines.push(
|
|
527
|
+
this.colorUtils.colorize(
|
|
528
|
+
chalk.cyan,
|
|
529
|
+
`\n${t('format.plannedUpdates', { count: String(plan.totalUpdates) })}`
|
|
530
|
+
)
|
|
531
|
+
)
|
|
532
|
+
lines.push('')
|
|
533
|
+
|
|
534
|
+
// Updates table
|
|
535
|
+
if (plan.updates.length > 0) {
|
|
536
|
+
const table = new Table({
|
|
537
|
+
head: this.colorUtils.colorizeHeaders([
|
|
538
|
+
t('table.header.catalog'),
|
|
539
|
+
t('table.header.package'),
|
|
540
|
+
t('table.header.current'),
|
|
541
|
+
t('table.header.new'),
|
|
542
|
+
t('table.header.type'),
|
|
543
|
+
]),
|
|
544
|
+
style: { head: [], border: [] },
|
|
545
|
+
colWidths: [15, 30, 15, 15, 8],
|
|
546
|
+
})
|
|
547
|
+
|
|
548
|
+
for (const update of plan.updates) {
|
|
549
|
+
const typeColor = this.colorUtils.getUpdateTypeColor(update.updateType)
|
|
550
|
+
const { currentColored, latestColored } = this.versionFormatter.colorizeVersionDiff(
|
|
551
|
+
update.currentVersion,
|
|
552
|
+
update.newVersion,
|
|
553
|
+
update.updateType
|
|
554
|
+
)
|
|
555
|
+
|
|
556
|
+
table.push([
|
|
557
|
+
update.catalogName,
|
|
558
|
+
update.packageName,
|
|
559
|
+
currentColored,
|
|
560
|
+
latestColored,
|
|
561
|
+
this.colorUtils.colorize(typeColor, update.updateType),
|
|
562
|
+
])
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
lines.push(table.toString())
|
|
566
|
+
lines.push('')
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
// Conflicts warning
|
|
570
|
+
if (plan.hasConflicts && plan.conflicts.length > 0) {
|
|
571
|
+
lines.push(this.colorUtils.colorize(chalk.yellow, `⚠️ ${t('format.versionConflicts')}:`))
|
|
572
|
+
lines.push('')
|
|
573
|
+
|
|
574
|
+
for (const conflict of plan.conflicts) {
|
|
575
|
+
lines.push(this.colorUtils.colorize(chalk.bold, ` ${conflict.packageName}:`))
|
|
576
|
+
for (const catalog of conflict.catalogs) {
|
|
577
|
+
lines.push(
|
|
578
|
+
` ${catalog.catalogName}: ${catalog.currentVersion} → ${catalog.proposedVersion}`
|
|
579
|
+
)
|
|
580
|
+
}
|
|
581
|
+
if (conflict.recommendation) {
|
|
582
|
+
lines.push(
|
|
583
|
+
this.colorUtils.colorize(
|
|
584
|
+
chalk.gray,
|
|
585
|
+
` ${t('format.recommendation')}: ${conflict.recommendation}`
|
|
586
|
+
)
|
|
587
|
+
)
|
|
588
|
+
}
|
|
589
|
+
lines.push('')
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
// Summary breakdown
|
|
594
|
+
const updateTypes = countUpdateTypes(plan.updates)
|
|
595
|
+
|
|
596
|
+
lines.push(this.colorUtils.colorize(chalk.bold, `${t('format.summary')}:`))
|
|
597
|
+
if (updateTypes.major > 0) {
|
|
598
|
+
lines.push(
|
|
599
|
+
this.colorUtils.colorize(
|
|
600
|
+
chalk.red,
|
|
601
|
+
` • ${t('command.check.majorUpdates', { count: String(updateTypes.major) })}`
|
|
602
|
+
)
|
|
603
|
+
)
|
|
604
|
+
}
|
|
605
|
+
if (updateTypes.minor > 0) {
|
|
606
|
+
lines.push(
|
|
607
|
+
this.colorUtils.colorize(
|
|
608
|
+
chalk.yellow,
|
|
609
|
+
` • ${t('command.check.minorUpdates', { count: String(updateTypes.minor) })}`
|
|
610
|
+
)
|
|
611
|
+
)
|
|
612
|
+
}
|
|
613
|
+
if (updateTypes.patch > 0) {
|
|
614
|
+
lines.push(
|
|
615
|
+
this.colorUtils.colorize(
|
|
616
|
+
chalk.green,
|
|
617
|
+
` • ${t('command.check.patchUpdates', { count: String(updateTypes.patch) })}`
|
|
618
|
+
)
|
|
619
|
+
)
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
return lines.join('\n')
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
/**
|
|
626
|
+
* Format update plan minimally (for dry-run mode)
|
|
627
|
+
*/
|
|
628
|
+
private formatUpdatePlanMinimal(plan: UpdatePlan): string {
|
|
629
|
+
if (plan.totalUpdates === 0) {
|
|
630
|
+
return t('format.noUpdatesPlanned')
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
// Collect update info for alignment calculation
|
|
634
|
+
const updatesWithVersions = plan.updates.map((update) => {
|
|
635
|
+
const { currentColored, latestColored } = this.versionFormatter.colorizeVersionDiff(
|
|
636
|
+
update.currentVersion,
|
|
637
|
+
update.newVersion,
|
|
638
|
+
update.updateType
|
|
639
|
+
)
|
|
640
|
+
return {
|
|
641
|
+
packageName: update.packageName,
|
|
642
|
+
currentColored,
|
|
643
|
+
latestColored,
|
|
644
|
+
}
|
|
645
|
+
})
|
|
646
|
+
|
|
647
|
+
// Calculate max widths for alignment
|
|
648
|
+
const maxNameWidth = Math.max(...updatesWithVersions.map((u) => u.packageName.length))
|
|
649
|
+
const maxCurrentWidth = Math.max(
|
|
650
|
+
...updatesWithVersions.map((u) => this.colorUtils.stripAnsi(u.currentColored).length)
|
|
651
|
+
)
|
|
652
|
+
|
|
653
|
+
const lines: string[] = []
|
|
654
|
+
for (const update of updatesWithVersions) {
|
|
655
|
+
const paddedName = update.packageName.padEnd(maxNameWidth)
|
|
656
|
+
const paddedCurrent = this.colorUtils.padAnsi(update.currentColored, maxCurrentWidth)
|
|
657
|
+
|
|
658
|
+
lines.push(`${paddedName} ${paddedCurrent} → ${update.latestColored}`)
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
// Add conflicts warning if any
|
|
662
|
+
if (plan.hasConflicts && plan.conflicts.length > 0) {
|
|
663
|
+
lines.push('')
|
|
664
|
+
lines.push(`⚠️ ${plan.conflicts.length} ${t('format.conflictsDetected')}`)
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
return lines.join('\n')
|
|
668
|
+
}
|
|
669
|
+
|
|
388
670
|
/**
|
|
389
671
|
* Format impact analysis as table
|
|
390
672
|
*/
|
|
@@ -392,35 +674,57 @@ export class OutputFormatter {
|
|
|
392
674
|
const lines: string[] = []
|
|
393
675
|
|
|
394
676
|
// Header
|
|
395
|
-
lines.push(this.colorize(chalk.bold, `\n🔍 Impact Analysis: ${analysis.packageName}`))
|
|
396
|
-
lines.push(this.colorize(chalk.gray, `Catalog: ${analysis.catalogName}`))
|
|
397
677
|
lines.push(
|
|
398
|
-
this.colorize(
|
|
678
|
+
this.colorUtils.colorize(
|
|
679
|
+
chalk.bold,
|
|
680
|
+
`\n${t('format.impactAnalysis')}: ${analysis.packageName}`
|
|
681
|
+
)
|
|
682
|
+
)
|
|
683
|
+
lines.push(
|
|
684
|
+
this.colorUtils.colorize(chalk.gray, `${t('format.catalog')}: ${analysis.catalogName}`)
|
|
685
|
+
)
|
|
686
|
+
lines.push(
|
|
687
|
+
this.colorUtils.colorize(
|
|
688
|
+
chalk.gray,
|
|
689
|
+
`${t('format.updateInfo')}: ${analysis.currentVersion} → ${analysis.proposedVersion}`
|
|
690
|
+
)
|
|
691
|
+
)
|
|
692
|
+
lines.push(
|
|
693
|
+
this.colorUtils.colorize(chalk.gray, `${t('table.header.type')}: ${analysis.updateType}`)
|
|
399
694
|
)
|
|
400
|
-
lines.push(this.colorize(chalk.gray, `Type: ${analysis.updateType}`))
|
|
401
695
|
|
|
402
696
|
// Risk level
|
|
403
|
-
const riskColor = this.getRiskColor(analysis.riskLevel)
|
|
404
|
-
lines.push(
|
|
697
|
+
const riskColor = this.colorUtils.getRiskColor(analysis.riskLevel)
|
|
698
|
+
lines.push(
|
|
699
|
+
this.colorUtils.colorize(
|
|
700
|
+
riskColor,
|
|
701
|
+
`${t('format.riskLevel')}: ${analysis.riskLevel.toUpperCase()}`
|
|
702
|
+
)
|
|
703
|
+
)
|
|
405
704
|
lines.push('')
|
|
406
705
|
|
|
407
706
|
// Affected packages
|
|
408
707
|
if (analysis.affectedPackages.length > 0) {
|
|
409
|
-
lines.push(this.colorize(chalk.bold, '
|
|
708
|
+
lines.push(this.colorUtils.colorize(chalk.bold, `${t('format.affectedPackages')}:`))
|
|
410
709
|
|
|
411
710
|
const table = new Table({
|
|
412
|
-
head: this.colorizeHeaders([
|
|
711
|
+
head: this.colorUtils.colorizeHeaders([
|
|
712
|
+
t('table.header.package'),
|
|
713
|
+
t('table.header.path'),
|
|
714
|
+
t('table.header.dependencyType'),
|
|
715
|
+
t('table.header.risk'),
|
|
716
|
+
]),
|
|
413
717
|
style: { head: [], border: [] },
|
|
414
718
|
colWidths: [20, 30, 15, 10],
|
|
415
719
|
})
|
|
416
720
|
|
|
417
721
|
for (const pkg of analysis.affectedPackages) {
|
|
418
|
-
const riskColor = this.getRiskColor(pkg.compatibilityRisk)
|
|
722
|
+
const riskColor = this.colorUtils.getRiskColor(pkg.compatibilityRisk)
|
|
419
723
|
table.push([
|
|
420
724
|
pkg.packageName,
|
|
421
725
|
pkg.packagePath,
|
|
422
726
|
pkg.dependencyType,
|
|
423
|
-
this.colorize(riskColor, pkg.compatibilityRisk),
|
|
727
|
+
this.colorUtils.colorize(riskColor, pkg.compatibilityRisk),
|
|
424
728
|
])
|
|
425
729
|
}
|
|
426
730
|
|
|
@@ -430,22 +734,22 @@ export class OutputFormatter {
|
|
|
430
734
|
|
|
431
735
|
// Security impact
|
|
432
736
|
if (analysis.securityImpact.hasVulnerabilities) {
|
|
433
|
-
lines.push(this.colorize(chalk.bold, '
|
|
737
|
+
lines.push(this.colorUtils.colorize(chalk.bold, `${t('format.securityImpact')}:`))
|
|
434
738
|
|
|
435
739
|
if (analysis.securityImpact.fixedVulnerabilities > 0) {
|
|
436
740
|
lines.push(
|
|
437
|
-
this.colorize(
|
|
741
|
+
this.colorUtils.colorize(
|
|
438
742
|
chalk.green,
|
|
439
|
-
` ✅
|
|
743
|
+
` ✅ ${t('format.fixesVulns', { count: String(analysis.securityImpact.fixedVulnerabilities) })}`
|
|
440
744
|
)
|
|
441
745
|
)
|
|
442
746
|
}
|
|
443
747
|
|
|
444
748
|
if (analysis.securityImpact.newVulnerabilities > 0) {
|
|
445
749
|
lines.push(
|
|
446
|
-
this.colorize(
|
|
750
|
+
this.colorUtils.colorize(
|
|
447
751
|
chalk.red,
|
|
448
|
-
` ⚠️
|
|
752
|
+
` ⚠️ ${t('format.introducesVulns', { count: String(analysis.securityImpact.newVulnerabilities) })}`
|
|
449
753
|
)
|
|
450
754
|
)
|
|
451
755
|
}
|
|
@@ -455,7 +759,7 @@ export class OutputFormatter {
|
|
|
455
759
|
|
|
456
760
|
// Recommendations
|
|
457
761
|
if (analysis.recommendations.length > 0) {
|
|
458
|
-
lines.push(this.colorize(chalk.bold, '
|
|
762
|
+
lines.push(this.colorUtils.colorize(chalk.bold, `${t('format.recommendations')}:`))
|
|
459
763
|
for (const rec of analysis.recommendations) {
|
|
460
764
|
lines.push(` ${rec}`)
|
|
461
765
|
}
|
|
@@ -470,8 +774,8 @@ export class OutputFormatter {
|
|
|
470
774
|
private formatImpactMinimal(analysis: ImpactAnalysis): string {
|
|
471
775
|
return [
|
|
472
776
|
`${analysis.packageName}: ${analysis.currentVersion} → ${analysis.proposedVersion}`,
|
|
473
|
-
|
|
474
|
-
|
|
777
|
+
`${t('format.riskLevel')}: ${analysis.riskLevel}`,
|
|
778
|
+
`${t('format.affectedPackages')}: ${analysis.affectedPackages.length} ${t('format.packages')}`,
|
|
475
779
|
].join('\n')
|
|
476
780
|
}
|
|
477
781
|
|
|
@@ -485,21 +789,28 @@ export class OutputFormatter {
|
|
|
485
789
|
const statusIcon = report.isValid ? '✅' : '❌'
|
|
486
790
|
const statusColor = report.isValid ? chalk.green : chalk.red
|
|
487
791
|
|
|
488
|
-
lines.push(
|
|
489
|
-
|
|
792
|
+
lines.push(
|
|
793
|
+
this.colorUtils.colorize(chalk.bold, `\n${statusIcon} ${t('format.workspaceValidation')}`)
|
|
794
|
+
)
|
|
795
|
+
lines.push(
|
|
796
|
+
this.colorUtils.colorize(
|
|
797
|
+
statusColor,
|
|
798
|
+
`${t('format.status')}: ${report.isValid ? t('format.valid') : t('format.invalid')}`
|
|
799
|
+
)
|
|
800
|
+
)
|
|
490
801
|
lines.push('')
|
|
491
802
|
|
|
492
803
|
// Workspace info
|
|
493
|
-
lines.push(this.colorize(chalk.bold, '
|
|
494
|
-
lines.push(`
|
|
495
|
-
lines.push(`
|
|
496
|
-
lines.push(`
|
|
497
|
-
lines.push(`
|
|
804
|
+
lines.push(this.colorUtils.colorize(chalk.bold, `${t('format.workspaceInfo')}:`))
|
|
805
|
+
lines.push(` ${t('format.path')}: ${report.workspace.path}`)
|
|
806
|
+
lines.push(` ${t('format.name')}: ${report.workspace.name}`)
|
|
807
|
+
lines.push(` ${t('format.packages')}: ${report.workspace.packageCount}`)
|
|
808
|
+
lines.push(` ${t('format.catalogs')}: ${report.workspace.catalogCount}`)
|
|
498
809
|
lines.push('')
|
|
499
810
|
|
|
500
811
|
// Errors
|
|
501
812
|
if (report.errors.length > 0) {
|
|
502
|
-
lines.push(this.colorize(chalk.red,
|
|
813
|
+
lines.push(this.colorUtils.colorize(chalk.red, `❌ ${t('format.errors')}:`))
|
|
503
814
|
for (const error of report.errors) {
|
|
504
815
|
lines.push(` • ${error}`)
|
|
505
816
|
}
|
|
@@ -508,7 +819,7 @@ export class OutputFormatter {
|
|
|
508
819
|
|
|
509
820
|
// Warnings
|
|
510
821
|
if (report.warnings.length > 0) {
|
|
511
|
-
lines.push(this.colorize(chalk.yellow, '
|
|
822
|
+
lines.push(this.colorUtils.colorize(chalk.yellow, `⚠️ ${t('format.warnings')}:`))
|
|
512
823
|
for (const warning of report.warnings) {
|
|
513
824
|
lines.push(` • ${warning}`)
|
|
514
825
|
}
|
|
@@ -517,7 +828,7 @@ export class OutputFormatter {
|
|
|
517
828
|
|
|
518
829
|
// Recommendations
|
|
519
830
|
if (report.recommendations.length > 0) {
|
|
520
|
-
lines.push(this.colorize(chalk.blue, '
|
|
831
|
+
lines.push(this.colorUtils.colorize(chalk.blue, `${t('format.recommendations')}:`))
|
|
521
832
|
for (const rec of report.recommendations) {
|
|
522
833
|
lines.push(` • ${rec}`)
|
|
523
834
|
}
|
|
@@ -530,11 +841,124 @@ export class OutputFormatter {
|
|
|
530
841
|
* Format validation report minimally
|
|
531
842
|
*/
|
|
532
843
|
private formatValidationMinimal(report: WorkspaceValidationReport): string {
|
|
533
|
-
const status = report.isValid ? '
|
|
844
|
+
const status = report.isValid ? t('format.valid') : t('format.invalid')
|
|
534
845
|
const errors = report.errors.length
|
|
535
846
|
const warnings = report.warnings.length
|
|
536
847
|
|
|
537
|
-
return `${status} (${errors} errors, ${warnings} warnings)`
|
|
848
|
+
return `${status} (${errors} ${t('format.errors')}, ${warnings} ${t('format.warnings')})`
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
/**
|
|
852
|
+
* Format workspace information as beautiful table
|
|
853
|
+
*/
|
|
854
|
+
private formatWorkspaceInfoTable(info: WorkspaceInfo): string {
|
|
855
|
+
const lines: string[] = []
|
|
856
|
+
|
|
857
|
+
// Use cli-table3 for reliable alignment
|
|
858
|
+
const table = new Table({
|
|
859
|
+
chars: {
|
|
860
|
+
top: '─',
|
|
861
|
+
'top-mid': '┬',
|
|
862
|
+
'top-left': '╭',
|
|
863
|
+
'top-right': '╮',
|
|
864
|
+
bottom: '─',
|
|
865
|
+
'bottom-mid': '┴',
|
|
866
|
+
'bottom-left': '╰',
|
|
867
|
+
'bottom-right': '╯',
|
|
868
|
+
left: '│',
|
|
869
|
+
'left-mid': '├',
|
|
870
|
+
mid: '─',
|
|
871
|
+
'mid-mid': '┼',
|
|
872
|
+
right: '│',
|
|
873
|
+
'right-mid': '┤',
|
|
874
|
+
middle: '│',
|
|
875
|
+
},
|
|
876
|
+
style: {
|
|
877
|
+
head: [],
|
|
878
|
+
border: this.useColor ? ['cyan'] : [],
|
|
879
|
+
'padding-left': 1,
|
|
880
|
+
'padding-right': 1,
|
|
881
|
+
},
|
|
882
|
+
colWidths: [20, 55],
|
|
883
|
+
wordWrap: true,
|
|
884
|
+
})
|
|
885
|
+
|
|
886
|
+
// Header row
|
|
887
|
+
table.push([
|
|
888
|
+
{
|
|
889
|
+
colSpan: 2,
|
|
890
|
+
content: this.colorUtils.colorize(chalk.bold.cyan, 'WORKSPACE'),
|
|
891
|
+
hAlign: 'center',
|
|
892
|
+
},
|
|
893
|
+
])
|
|
894
|
+
|
|
895
|
+
// Status icon (default to valid if not specified)
|
|
896
|
+
const isValid = info.isValid ?? true
|
|
897
|
+
const statusIcon = isValid
|
|
898
|
+
? this.colorUtils.colorize(chalk.green, '✓')
|
|
899
|
+
: this.colorUtils.colorize(chalk.red, '✗')
|
|
900
|
+
const statusText = isValid
|
|
901
|
+
? this.colorUtils.colorize(chalk.green, 'Valid')
|
|
902
|
+
: this.colorUtils.colorize(chalk.red, 'Invalid')
|
|
903
|
+
|
|
904
|
+
// Data rows - clean, no emojis
|
|
905
|
+
table.push([
|
|
906
|
+
this.colorUtils.colorize(chalk.gray, 'Name'),
|
|
907
|
+
this.colorUtils.colorize(chalk.bold.white, info.name),
|
|
908
|
+
])
|
|
909
|
+
|
|
910
|
+
table.push([
|
|
911
|
+
this.colorUtils.colorize(chalk.gray, 'Path'),
|
|
912
|
+
this.colorUtils.colorize(chalk.dim, info.path),
|
|
913
|
+
])
|
|
914
|
+
|
|
915
|
+
table.push([this.colorUtils.colorize(chalk.gray, 'Status'), `${statusIcon} ${statusText}`])
|
|
916
|
+
|
|
917
|
+
table.push([
|
|
918
|
+
this.colorUtils.colorize(chalk.gray, 'Packages'),
|
|
919
|
+
this.colorUtils.colorize(
|
|
920
|
+
info.packageCount > 0 ? chalk.green : chalk.yellow,
|
|
921
|
+
String(info.packageCount)
|
|
922
|
+
),
|
|
923
|
+
])
|
|
924
|
+
|
|
925
|
+
table.push([
|
|
926
|
+
this.colorUtils.colorize(chalk.gray, 'Catalogs'),
|
|
927
|
+
this.colorUtils.colorize(
|
|
928
|
+
info.catalogCount > 0 ? chalk.green : chalk.yellow,
|
|
929
|
+
String(info.catalogCount)
|
|
930
|
+
),
|
|
931
|
+
])
|
|
932
|
+
|
|
933
|
+
const catalogNames = info.catalogNames ?? []
|
|
934
|
+
if (catalogNames.length > 0) {
|
|
935
|
+
const catalogTags = catalogNames
|
|
936
|
+
.map((name: string) => this.colorUtils.colorize(chalk.cyan, name))
|
|
937
|
+
.join(this.colorUtils.colorize(chalk.gray, ', '))
|
|
938
|
+
|
|
939
|
+
table.push([this.colorUtils.colorize(chalk.gray, 'Catalog Names'), catalogTags])
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
lines.push('')
|
|
943
|
+
lines.push(table.toString())
|
|
944
|
+
lines.push('')
|
|
945
|
+
|
|
946
|
+
return lines.join('\n')
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
/**
|
|
950
|
+
* Format workspace information minimally
|
|
951
|
+
*/
|
|
952
|
+
private formatWorkspaceInfoMinimal(info: WorkspaceInfo): string {
|
|
953
|
+
const lines: string[] = []
|
|
954
|
+
lines.push(`${info.name} (${info.path})`)
|
|
955
|
+
lines.push(
|
|
956
|
+
`${t('command.workspace.packages')}: ${info.packageCount}, ${t('command.workspace.catalogs')}: ${info.catalogCount}`
|
|
957
|
+
)
|
|
958
|
+
if (info.catalogNames && info.catalogNames.length > 0) {
|
|
959
|
+
lines.push(`${t('command.workspace.catalogNames')}: ${info.catalogNames.join(', ')}`)
|
|
960
|
+
}
|
|
961
|
+
return lines.join('\n')
|
|
538
962
|
}
|
|
539
963
|
|
|
540
964
|
/**
|
|
@@ -543,26 +967,34 @@ export class OutputFormatter {
|
|
|
543
967
|
private formatStatsTable(stats: WorkspaceStats): string {
|
|
544
968
|
const lines: string[] = []
|
|
545
969
|
|
|
546
|
-
lines.push(this.colorize(chalk.bold, `\n
|
|
547
|
-
lines.push(
|
|
970
|
+
lines.push(this.colorUtils.colorize(chalk.bold, `\n${t('format.workspaceStatistics')}`))
|
|
971
|
+
lines.push(
|
|
972
|
+
this.colorUtils.colorize(chalk.gray, `${t('format.workspace')}: ${stats.workspace.name}`)
|
|
973
|
+
)
|
|
548
974
|
lines.push('')
|
|
549
975
|
|
|
550
976
|
const table = new Table({
|
|
551
|
-
head: this.colorizeHeaders(['
|
|
977
|
+
head: this.colorUtils.colorizeHeaders([t('table.header.metric'), t('table.header.count')]),
|
|
552
978
|
style: { head: [], border: [] },
|
|
553
979
|
colWidths: [30, 10],
|
|
554
980
|
})
|
|
555
981
|
|
|
556
|
-
table.push(['
|
|
557
|
-
table.push([
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
table.push(['
|
|
562
|
-
table.push(['
|
|
563
|
-
table.push(['
|
|
564
|
-
table.push(['
|
|
565
|
-
table.push(['
|
|
982
|
+
table.push([t('stats.totalPackages'), stats.packages.total.toString()])
|
|
983
|
+
table.push([
|
|
984
|
+
t('stats.packagesWithCatalogRefs'),
|
|
985
|
+
stats.packages.withCatalogReferences.toString(),
|
|
986
|
+
])
|
|
987
|
+
table.push([t('stats.totalCatalogs'), stats.catalogs.total.toString()])
|
|
988
|
+
table.push([t('stats.catalogEntries'), stats.catalogs.totalEntries.toString()])
|
|
989
|
+
table.push([t('stats.totalDependencies'), stats.dependencies.total.toString()])
|
|
990
|
+
table.push([t('stats.catalogReferences'), stats.dependencies.catalogReferences.toString()])
|
|
991
|
+
table.push([t('stats.dependencies'), stats.dependencies.byType.dependencies.toString()])
|
|
992
|
+
table.push([t('stats.devDependencies'), stats.dependencies.byType.devDependencies.toString()])
|
|
993
|
+
table.push([t('stats.peerDependencies'), stats.dependencies.byType.peerDependencies.toString()])
|
|
994
|
+
table.push([
|
|
995
|
+
t('stats.optionalDependencies'),
|
|
996
|
+
stats.dependencies.byType.optionalDependencies.toString(),
|
|
997
|
+
])
|
|
566
998
|
|
|
567
999
|
lines.push(table.toString())
|
|
568
1000
|
|
|
@@ -574,9 +1006,9 @@ export class OutputFormatter {
|
|
|
574
1006
|
*/
|
|
575
1007
|
private formatStatsMinimal(stats: WorkspaceStats): string {
|
|
576
1008
|
return [
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
1009
|
+
`${t('format.packages')}: ${stats.packages.total}`,
|
|
1010
|
+
`${t('format.catalogs')}: ${stats.catalogs.total}`,
|
|
1011
|
+
`${t('stats.totalDependencies')}: ${stats.dependencies.total}`,
|
|
580
1012
|
].join(', ')
|
|
581
1013
|
}
|
|
582
1014
|
|
|
@@ -587,31 +1019,59 @@ export class OutputFormatter {
|
|
|
587
1019
|
const lines: string[] = []
|
|
588
1020
|
|
|
589
1021
|
// Header
|
|
590
|
-
lines.push(this.colorize(chalk.bold,
|
|
591
|
-
lines.push(this.colorize(chalk.gray, `Workspace: ${report.metadata.workspacePath}`))
|
|
1022
|
+
lines.push(this.colorUtils.colorize(chalk.bold, `\n${t('format.securityReport')}`))
|
|
592
1023
|
lines.push(
|
|
593
|
-
this.colorize(
|
|
1024
|
+
this.colorUtils.colorize(
|
|
1025
|
+
chalk.gray,
|
|
1026
|
+
`${t('format.workspace')}: ${report.metadata.workspacePath}`
|
|
1027
|
+
)
|
|
1028
|
+
)
|
|
1029
|
+
lines.push(
|
|
1030
|
+
this.colorUtils.colorize(
|
|
1031
|
+
chalk.gray,
|
|
1032
|
+
`${t('format.scanDate')}: ${new Date(report.metadata.scanDate).toLocaleString()}`
|
|
1033
|
+
)
|
|
1034
|
+
)
|
|
1035
|
+
lines.push(
|
|
1036
|
+
this.colorUtils.colorize(
|
|
1037
|
+
chalk.gray,
|
|
1038
|
+
`${t('format.tools')}: ${report.metadata.scanTools.join(', ')}`
|
|
1039
|
+
)
|
|
594
1040
|
)
|
|
595
|
-
lines.push(this.colorize(chalk.gray, `Tools: ${report.metadata.scanTools.join(', ')}`))
|
|
596
1041
|
|
|
597
1042
|
// Summary
|
|
598
1043
|
lines.push('')
|
|
599
|
-
lines.push(this.colorize(chalk.bold, '
|
|
1044
|
+
lines.push(this.colorUtils.colorize(chalk.bold, `${t('format.summary')}:`))
|
|
600
1045
|
|
|
601
1046
|
const summaryTable = new Table({
|
|
602
|
-
head: this.colorizeHeaders(['
|
|
1047
|
+
head: this.colorUtils.colorizeHeaders([t('table.header.severity'), t('table.header.count')]),
|
|
603
1048
|
style: { head: [], border: [] },
|
|
604
1049
|
colWidths: [15, 10],
|
|
605
1050
|
})
|
|
606
1051
|
|
|
607
|
-
summaryTable.push(['Critical', this.colorize(chalk.red, report.summary.critical.toString())])
|
|
608
|
-
summaryTable.push(['High', this.colorize(chalk.yellow, report.summary.high.toString())])
|
|
609
|
-
summaryTable.push(['Moderate', this.colorize(chalk.blue, report.summary.moderate.toString())])
|
|
610
|
-
summaryTable.push(['Low', this.colorize(chalk.green, report.summary.low.toString())])
|
|
611
|
-
summaryTable.push(['Info', this.colorize(chalk.gray, report.summary.info.toString())])
|
|
612
1052
|
summaryTable.push([
|
|
613
|
-
'
|
|
614
|
-
this.colorize(chalk.
|
|
1053
|
+
t('severity.critical'),
|
|
1054
|
+
this.colorUtils.colorize(chalk.red, report.summary.critical.toString()),
|
|
1055
|
+
])
|
|
1056
|
+
summaryTable.push([
|
|
1057
|
+
t('severity.high'),
|
|
1058
|
+
this.colorUtils.colorize(chalk.yellow, report.summary.high.toString()),
|
|
1059
|
+
])
|
|
1060
|
+
summaryTable.push([
|
|
1061
|
+
t('severity.moderate'),
|
|
1062
|
+
this.colorUtils.colorize(chalk.blue, report.summary.moderate.toString()),
|
|
1063
|
+
])
|
|
1064
|
+
summaryTable.push([
|
|
1065
|
+
t('severity.low'),
|
|
1066
|
+
this.colorUtils.colorize(chalk.green, report.summary.low.toString()),
|
|
1067
|
+
])
|
|
1068
|
+
summaryTable.push([
|
|
1069
|
+
t('severity.info'),
|
|
1070
|
+
this.colorUtils.colorize(chalk.gray, report.summary.info.toString()),
|
|
1071
|
+
])
|
|
1072
|
+
summaryTable.push([
|
|
1073
|
+
t('severity.total'),
|
|
1074
|
+
this.colorUtils.colorize(chalk.bold, report.summary.totalVulnerabilities.toString()),
|
|
615
1075
|
])
|
|
616
1076
|
|
|
617
1077
|
lines.push(summaryTable.toString())
|
|
@@ -619,25 +1079,30 @@ export class OutputFormatter {
|
|
|
619
1079
|
// Vulnerabilities
|
|
620
1080
|
if (report.vulnerabilities.length > 0) {
|
|
621
1081
|
lines.push('')
|
|
622
|
-
lines.push(this.colorize(chalk.bold, '
|
|
1082
|
+
lines.push(this.colorUtils.colorize(chalk.bold, `${t('format.vulnerabilities')}:`))
|
|
623
1083
|
|
|
624
1084
|
const vulnTable = new Table({
|
|
625
|
-
head: this.colorizeHeaders([
|
|
1085
|
+
head: this.colorUtils.colorizeHeaders([
|
|
1086
|
+
t('table.header.package'),
|
|
1087
|
+
t('table.header.severity'),
|
|
1088
|
+
t('table.header.title'),
|
|
1089
|
+
t('table.header.fixAvailable'),
|
|
1090
|
+
]),
|
|
626
1091
|
style: { head: [], border: [] },
|
|
627
1092
|
colWidths: [20, 12, 40, 15],
|
|
628
1093
|
})
|
|
629
1094
|
|
|
630
1095
|
for (const vuln of report.vulnerabilities) {
|
|
631
|
-
const severityColor = this.getSeverityColor(vuln.severity)
|
|
1096
|
+
const severityColor = this.colorUtils.getSeverityColor(vuln.severity)
|
|
632
1097
|
const fixStatus = vuln.fixAvailable
|
|
633
1098
|
? typeof vuln.fixAvailable === 'string'
|
|
634
1099
|
? vuln.fixAvailable
|
|
635
|
-
: '
|
|
636
|
-
: '
|
|
1100
|
+
: t('common.yes')
|
|
1101
|
+
: t('common.no')
|
|
637
1102
|
|
|
638
1103
|
vulnTable.push([
|
|
639
1104
|
vuln.package,
|
|
640
|
-
this.colorize(severityColor, vuln.severity.toUpperCase()),
|
|
1105
|
+
this.colorUtils.colorize(severityColor, vuln.severity.toUpperCase()),
|
|
641
1106
|
vuln.title.length > 35 ? `${vuln.title.substring(0, 35)}...` : vuln.title,
|
|
642
1107
|
fixStatus,
|
|
643
1108
|
])
|
|
@@ -649,7 +1114,7 @@ export class OutputFormatter {
|
|
|
649
1114
|
// Recommendations
|
|
650
1115
|
if (report.recommendations.length > 0) {
|
|
651
1116
|
lines.push('')
|
|
652
|
-
lines.push(this.colorize(chalk.bold, '
|
|
1117
|
+
lines.push(this.colorUtils.colorize(chalk.bold, `${t('format.recommendations')}:`))
|
|
653
1118
|
|
|
654
1119
|
for (const rec of report.recommendations) {
|
|
655
1120
|
lines.push(` ${rec.package}: ${rec.currentVersion} → ${rec.recommendedVersion}`)
|
|
@@ -666,147 +1131,18 @@ export class OutputFormatter {
|
|
|
666
1131
|
private formatSecurityMinimal(report: SecurityReport): string {
|
|
667
1132
|
const vulnerabilities = report.summary.totalVulnerabilities
|
|
668
1133
|
if (vulnerabilities === 0) {
|
|
669
|
-
return '
|
|
1134
|
+
return t('format.noVulnsFound')
|
|
670
1135
|
}
|
|
671
1136
|
|
|
672
1137
|
return [
|
|
673
|
-
`${vulnerabilities} vulnerabilities
|
|
674
|
-
`
|
|
675
|
-
`
|
|
676
|
-
`
|
|
677
|
-
`
|
|
1138
|
+
`${t('format.vulnerabilities')}: ${vulnerabilities}`,
|
|
1139
|
+
` ${t('severity.critical')}: ${report.summary.critical}`,
|
|
1140
|
+
` ${t('severity.high')}: ${report.summary.high}`,
|
|
1141
|
+
` ${t('severity.moderate')}: ${report.summary.moderate}`,
|
|
1142
|
+
` ${t('severity.low')}: ${report.summary.low}`,
|
|
678
1143
|
].join('\n')
|
|
679
1144
|
}
|
|
680
1145
|
|
|
681
|
-
/**
|
|
682
|
-
* Get color for severity level
|
|
683
|
-
*/
|
|
684
|
-
private getSeverityColor(severity: string): typeof chalk {
|
|
685
|
-
switch (severity.toLowerCase()) {
|
|
686
|
-
case 'critical':
|
|
687
|
-
return chalk.red
|
|
688
|
-
case 'high':
|
|
689
|
-
return chalk.yellow
|
|
690
|
-
case 'moderate':
|
|
691
|
-
return chalk.blue
|
|
692
|
-
case 'low':
|
|
693
|
-
return chalk.green
|
|
694
|
-
default:
|
|
695
|
-
return chalk.gray
|
|
696
|
-
}
|
|
697
|
-
}
|
|
698
|
-
|
|
699
|
-
/**
|
|
700
|
-
* Apply color if color is enabled
|
|
701
|
-
*/
|
|
702
|
-
private colorize(colorFn: typeof chalk, text: string): string {
|
|
703
|
-
return this.useColor ? colorFn(text) : text
|
|
704
|
-
}
|
|
705
|
-
|
|
706
|
-
/**
|
|
707
|
-
* Colorize table headers
|
|
708
|
-
*/
|
|
709
|
-
private colorizeHeaders(headers: string[]): string[] {
|
|
710
|
-
return this.useColor ? headers.map((h) => chalk.bold.cyan(h)) : headers
|
|
711
|
-
}
|
|
712
|
-
|
|
713
|
-
/**
|
|
714
|
-
* Get color for update type
|
|
715
|
-
*/
|
|
716
|
-
private getUpdateTypeColor(updateType: string): typeof chalk {
|
|
717
|
-
switch (updateType) {
|
|
718
|
-
case 'major':
|
|
719
|
-
return chalk.red
|
|
720
|
-
case 'minor':
|
|
721
|
-
return chalk.yellow
|
|
722
|
-
case 'patch':
|
|
723
|
-
return chalk.green
|
|
724
|
-
default:
|
|
725
|
-
return chalk.gray
|
|
726
|
-
}
|
|
727
|
-
}
|
|
728
|
-
|
|
729
|
-
/**
|
|
730
|
-
* Colorize version differences between current and latest
|
|
731
|
-
*/
|
|
732
|
-
private colorizeVersionDiff(
|
|
733
|
-
current: string,
|
|
734
|
-
latest: string,
|
|
735
|
-
updateType: string
|
|
736
|
-
): {
|
|
737
|
-
currentColored: string
|
|
738
|
-
latestColored: string
|
|
739
|
-
} {
|
|
740
|
-
if (!this.useColor) {
|
|
741
|
-
return { currentColored: current, latestColored: latest }
|
|
742
|
-
}
|
|
743
|
-
|
|
744
|
-
// Parse version numbers to identify different parts
|
|
745
|
-
const parseVersion = (version: string) => {
|
|
746
|
-
// Remove leading ^ or ~ or other prefix characters
|
|
747
|
-
const cleanVersion = version.replace(/^[\^~>=<]+/, '')
|
|
748
|
-
const parts = cleanVersion.split('.')
|
|
749
|
-
return {
|
|
750
|
-
major: parts[0] || '0',
|
|
751
|
-
minor: parts[1] || '0',
|
|
752
|
-
patch: parts[2] || '0',
|
|
753
|
-
extra: parts.slice(3).join('.'),
|
|
754
|
-
prefix: version.substring(0, version.length - cleanVersion.length),
|
|
755
|
-
}
|
|
756
|
-
}
|
|
757
|
-
|
|
758
|
-
const currentParts = parseVersion(current)
|
|
759
|
-
const latestParts = parseVersion(latest)
|
|
760
|
-
|
|
761
|
-
// Determine color based on update type for highlighting differences
|
|
762
|
-
const diffColor = this.getUpdateTypeColor(updateType)
|
|
763
|
-
|
|
764
|
-
// Build colored version strings by comparing each part
|
|
765
|
-
const colorCurrentPart = (part: string, latestPart: string, isChanged: boolean) => {
|
|
766
|
-
if (isChanged && part !== latestPart) {
|
|
767
|
-
return chalk.dim.white(part) // Dim white for old version part
|
|
768
|
-
}
|
|
769
|
-
return chalk.white(part) // Unchanged parts in white
|
|
770
|
-
}
|
|
771
|
-
|
|
772
|
-
const colorLatestPart = (part: string, currentPart: string, isChanged: boolean) => {
|
|
773
|
-
if (isChanged && part !== currentPart) {
|
|
774
|
-
return diffColor(part) // Highlight the new version part with update type color
|
|
775
|
-
}
|
|
776
|
-
return chalk.white(part) // Unchanged parts in white
|
|
777
|
-
}
|
|
778
|
-
|
|
779
|
-
// Check which parts are different
|
|
780
|
-
const majorChanged = currentParts.major !== latestParts.major
|
|
781
|
-
const minorChanged = currentParts.minor !== latestParts.minor
|
|
782
|
-
const patchChanged = currentParts.patch !== latestParts.patch
|
|
783
|
-
const extraChanged = currentParts.extra !== latestParts.extra
|
|
784
|
-
|
|
785
|
-
// Build colored current version
|
|
786
|
-
let currentColored = currentParts.prefix
|
|
787
|
-
currentColored += colorCurrentPart(currentParts.major, latestParts.major, majorChanged)
|
|
788
|
-
currentColored += '.'
|
|
789
|
-
currentColored += colorCurrentPart(currentParts.minor, latestParts.minor, minorChanged)
|
|
790
|
-
currentColored += '.'
|
|
791
|
-
currentColored += colorCurrentPart(currentParts.patch, latestParts.patch, patchChanged)
|
|
792
|
-
if (currentParts.extra) {
|
|
793
|
-
currentColored += `.${colorCurrentPart(currentParts.extra, latestParts.extra, extraChanged)}`
|
|
794
|
-
}
|
|
795
|
-
|
|
796
|
-
// Build colored latest version
|
|
797
|
-
let latestColored = latestParts.prefix
|
|
798
|
-
latestColored += colorLatestPart(latestParts.major, currentParts.major, majorChanged)
|
|
799
|
-
latestColored += '.'
|
|
800
|
-
latestColored += colorLatestPart(latestParts.minor, currentParts.minor, minorChanged)
|
|
801
|
-
latestColored += '.'
|
|
802
|
-
latestColored += colorLatestPart(latestParts.patch, currentParts.patch, patchChanged)
|
|
803
|
-
if (latestParts.extra) {
|
|
804
|
-
latestColored += `.${colorLatestPart(latestParts.extra, currentParts.extra, extraChanged)}`
|
|
805
|
-
}
|
|
806
|
-
|
|
807
|
-
return { currentColored, latestColored }
|
|
808
|
-
}
|
|
809
|
-
|
|
810
1146
|
/**
|
|
811
1147
|
* Format AI analysis result
|
|
812
1148
|
*/
|
|
@@ -840,37 +1176,45 @@ export class OutputFormatter {
|
|
|
840
1176
|
|
|
841
1177
|
lines.push('')
|
|
842
1178
|
lines.push(headerColor('═══════════════════════════════════════════════════════════════'))
|
|
843
|
-
lines.push(headerColor(
|
|
1179
|
+
lines.push(headerColor(` ${t('aiReport.title')}`))
|
|
844
1180
|
lines.push(headerColor('═══════════════════════════════════════════════════════════════'))
|
|
845
1181
|
lines.push('')
|
|
846
1182
|
|
|
847
1183
|
// Provider and analysis info
|
|
848
|
-
lines.push(`${infoColor('
|
|
849
|
-
lines.push(`${infoColor('
|
|
850
|
-
lines.push(
|
|
1184
|
+
lines.push(`${infoColor(t('aiReport.provider'))} ${providerColor(aiResult.provider)}`)
|
|
1185
|
+
lines.push(`${infoColor(t('aiReport.analysisType'))} ${aiResult.analysisType}`)
|
|
1186
|
+
lines.push(
|
|
1187
|
+
`${infoColor(t('aiReport.confidence'))} ${this.colorUtils.formatConfidence(aiResult.confidence)}`
|
|
1188
|
+
)
|
|
851
1189
|
lines.push('')
|
|
852
1190
|
|
|
853
1191
|
// Summary
|
|
854
|
-
lines.push(headerColor('
|
|
1192
|
+
lines.push(headerColor(t('aiReport.summary')))
|
|
855
1193
|
lines.push(headerColor('───────────────────────────────────────────────────────────────'))
|
|
856
1194
|
lines.push(aiResult.summary)
|
|
857
1195
|
lines.push('')
|
|
858
1196
|
|
|
859
1197
|
// Recommendations table
|
|
860
1198
|
if (aiResult.recommendations.length > 0) {
|
|
861
|
-
lines.push(headerColor('
|
|
1199
|
+
lines.push(headerColor(t('aiReport.recommendations')))
|
|
862
1200
|
lines.push(headerColor('───────────────────────────────────────────────────────────────'))
|
|
863
1201
|
|
|
864
1202
|
const table = new Table({
|
|
865
|
-
head: [
|
|
1203
|
+
head: [
|
|
1204
|
+
t('aiReport.tablePackage'),
|
|
1205
|
+
t('aiReport.tableVersionChange'),
|
|
1206
|
+
t('aiReport.tableAction'),
|
|
1207
|
+
t('aiReport.tableRisk'),
|
|
1208
|
+
t('aiReport.tableReason'),
|
|
1209
|
+
],
|
|
866
1210
|
style: { head: this.useColor ? ['cyan'] : [] },
|
|
867
1211
|
colWidths: [20, 20, 10, 10, 35],
|
|
868
1212
|
wordWrap: true,
|
|
869
1213
|
})
|
|
870
1214
|
|
|
871
1215
|
for (const rec of aiResult.recommendations) {
|
|
872
|
-
const riskColor = this.getRiskColor(rec.riskLevel)
|
|
873
|
-
const actionColor = this.getActionColor(rec.action)
|
|
1216
|
+
const riskColor = this.colorUtils.getRiskColor(rec.riskLevel)
|
|
1217
|
+
const actionColor = this.colorUtils.getActionColor(rec.action)
|
|
874
1218
|
|
|
875
1219
|
table.push([
|
|
876
1220
|
rec.package,
|
|
@@ -891,7 +1235,7 @@ export class OutputFormatter {
|
|
|
891
1235
|
.flatMap((r) => r.breakingChanges || [])
|
|
892
1236
|
|
|
893
1237
|
if (allBreakingChanges.length > 0) {
|
|
894
|
-
lines.push(errorColor('
|
|
1238
|
+
lines.push(errorColor(t('aiReport.breakingChanges')))
|
|
895
1239
|
lines.push(headerColor('───────────────────────────────────────────────────────────────'))
|
|
896
1240
|
for (const change of allBreakingChanges) {
|
|
897
1241
|
lines.push(` ${warningColor('•')} ${change}`)
|
|
@@ -905,7 +1249,7 @@ export class OutputFormatter {
|
|
|
905
1249
|
.flatMap((r) => r.securityFixes || [])
|
|
906
1250
|
|
|
907
1251
|
if (allSecurityFixes.length > 0) {
|
|
908
|
-
lines.push(successColor('
|
|
1252
|
+
lines.push(successColor(t('aiReport.securityFixes')))
|
|
909
1253
|
lines.push(headerColor('───────────────────────────────────────────────────────────────'))
|
|
910
1254
|
for (const fix of allSecurityFixes) {
|
|
911
1255
|
lines.push(` ${successColor('•')} ${fix}`)
|
|
@@ -915,7 +1259,7 @@ export class OutputFormatter {
|
|
|
915
1259
|
|
|
916
1260
|
// Warnings
|
|
917
1261
|
if (aiResult.warnings && aiResult.warnings.length > 0) {
|
|
918
|
-
lines.push(warningColor('
|
|
1262
|
+
lines.push(warningColor(t('aiReport.warnings')))
|
|
919
1263
|
lines.push(headerColor('───────────────────────────────────────────────────────────────'))
|
|
920
1264
|
for (const warning of aiResult.warnings) {
|
|
921
1265
|
lines.push(` ${warningColor('•')} ${warning}`)
|
|
@@ -925,7 +1269,7 @@ export class OutputFormatter {
|
|
|
925
1269
|
|
|
926
1270
|
// Details
|
|
927
1271
|
if (aiResult.details) {
|
|
928
|
-
lines.push(infoColor('
|
|
1272
|
+
lines.push(infoColor(t('aiReport.details')))
|
|
929
1273
|
lines.push(headerColor('───────────────────────────────────────────────────────────────'))
|
|
930
1274
|
lines.push(aiResult.details)
|
|
931
1275
|
lines.push('')
|
|
@@ -933,7 +1277,7 @@ export class OutputFormatter {
|
|
|
933
1277
|
|
|
934
1278
|
// Basic analysis info (if provided)
|
|
935
1279
|
if (basicAnalysis) {
|
|
936
|
-
lines.push(mutedColor('
|
|
1280
|
+
lines.push(mutedColor(t('aiReport.affectedPackages')))
|
|
937
1281
|
lines.push(headerColor('───────────────────────────────────────────────────────────────'))
|
|
938
1282
|
if (basicAnalysis.affectedPackages.length > 0) {
|
|
939
1283
|
for (const pkg of basicAnalysis.affectedPackages.slice(0, 10)) {
|
|
@@ -945,11 +1289,11 @@ export class OutputFormatter {
|
|
|
945
1289
|
}
|
|
946
1290
|
if (basicAnalysis.affectedPackages.length > 10) {
|
|
947
1291
|
lines.push(
|
|
948
|
-
` ${mutedColor(`
|
|
1292
|
+
` ${mutedColor(` ${t('aiReport.andMore', { count: basicAnalysis.affectedPackages.length - 10 })}`)}`
|
|
949
1293
|
)
|
|
950
1294
|
}
|
|
951
1295
|
} else {
|
|
952
|
-
lines.push(` ${mutedColor('
|
|
1296
|
+
lines.push(` ${mutedColor(t('aiReport.noPackagesAffected'))}`)
|
|
953
1297
|
}
|
|
954
1298
|
lines.push('')
|
|
955
1299
|
}
|
|
@@ -960,12 +1304,12 @@ export class OutputFormatter {
|
|
|
960
1304
|
aiResult.timestamp instanceof Date
|
|
961
1305
|
? aiResult.timestamp.toISOString()
|
|
962
1306
|
: String(aiResult.timestamp)
|
|
963
|
-
lines.push(mutedColor(
|
|
1307
|
+
lines.push(mutedColor(t('aiReport.generatedAt', { timestamp })))
|
|
964
1308
|
if (aiResult.processingTimeMs) {
|
|
965
|
-
lines.push(mutedColor(
|
|
1309
|
+
lines.push(mutedColor(t('aiReport.processingTime', { time: aiResult.processingTimeMs })))
|
|
966
1310
|
}
|
|
967
1311
|
if (aiResult.tokensUsed) {
|
|
968
|
-
lines.push(mutedColor(
|
|
1312
|
+
lines.push(mutedColor(t('aiReport.tokensUsed', { tokens: aiResult.tokensUsed })))
|
|
969
1313
|
}
|
|
970
1314
|
lines.push('')
|
|
971
1315
|
|
|
@@ -991,65 +1335,4 @@ export class OutputFormatter {
|
|
|
991
1335
|
|
|
992
1336
|
return lines.join('\n')
|
|
993
1337
|
}
|
|
994
|
-
|
|
995
|
-
/**
|
|
996
|
-
* Format confidence score with color
|
|
997
|
-
*/
|
|
998
|
-
private formatConfidence(confidence: number): string {
|
|
999
|
-
const percentage = Math.round(confidence * 100)
|
|
1000
|
-
const bar =
|
|
1001
|
-
'█'.repeat(Math.floor(percentage / 10)) + '░'.repeat(10 - Math.floor(percentage / 10))
|
|
1002
|
-
|
|
1003
|
-
if (!this.useColor) {
|
|
1004
|
-
return `${bar} ${percentage}%`
|
|
1005
|
-
}
|
|
1006
|
-
|
|
1007
|
-
if (confidence >= 0.8) {
|
|
1008
|
-
return chalk.green(`${bar} ${percentage}%`)
|
|
1009
|
-
} else if (confidence >= 0.5) {
|
|
1010
|
-
return chalk.yellow(`${bar} ${percentage}%`)
|
|
1011
|
-
} else {
|
|
1012
|
-
return chalk.red(`${bar} ${percentage}%`)
|
|
1013
|
-
}
|
|
1014
|
-
}
|
|
1015
|
-
|
|
1016
|
-
/**
|
|
1017
|
-
* Get color for risk level
|
|
1018
|
-
*/
|
|
1019
|
-
private getRiskColor(riskLevel: string): typeof chalk {
|
|
1020
|
-
if (!this.useColor) return chalk
|
|
1021
|
-
|
|
1022
|
-
switch (riskLevel) {
|
|
1023
|
-
case 'critical':
|
|
1024
|
-
return chalk.red.bold
|
|
1025
|
-
case 'high':
|
|
1026
|
-
return chalk.red
|
|
1027
|
-
case 'medium':
|
|
1028
|
-
return chalk.yellow
|
|
1029
|
-
case 'low':
|
|
1030
|
-
return chalk.green
|
|
1031
|
-
default:
|
|
1032
|
-
return chalk.gray
|
|
1033
|
-
}
|
|
1034
|
-
}
|
|
1035
|
-
|
|
1036
|
-
/**
|
|
1037
|
-
* Get color for action
|
|
1038
|
-
*/
|
|
1039
|
-
private getActionColor(action: string): typeof chalk {
|
|
1040
|
-
if (!this.useColor) return chalk
|
|
1041
|
-
|
|
1042
|
-
switch (action) {
|
|
1043
|
-
case 'update':
|
|
1044
|
-
return chalk.green
|
|
1045
|
-
case 'wait':
|
|
1046
|
-
return chalk.yellow
|
|
1047
|
-
case 'skip':
|
|
1048
|
-
return chalk.red
|
|
1049
|
-
case 'review':
|
|
1050
|
-
return chalk.cyan
|
|
1051
|
-
default:
|
|
1052
|
-
return chalk.white
|
|
1053
|
-
}
|
|
1054
|
-
}
|
|
1055
1338
|
}
|