pcu 0.5.7 → 0.6.4

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.
Files changed (223) hide show
  1. package/bin/pcu.js +1 -1
  2. package/dist/index.js +9559 -32
  3. package/dist/index.js.map +1 -1
  4. package/package.json +36 -84
  5. package/src/cli/commands/checkCommand.ts +227 -0
  6. package/src/cli/commands/initCommand.ts +394 -0
  7. package/src/cli/commands/securityCommand.ts +569 -0
  8. package/src/cli/commands/updateCommand.ts +245 -0
  9. package/src/cli/formatters/outputFormatter.ts +830 -0
  10. package/src/cli/formatters/progressBar.ts +700 -0
  11. package/src/cli/index.ts +565 -0
  12. package/src/cli/interactive/interactivePrompts.ts +517 -0
  13. package/src/cli/options/globalOptions.ts +380 -0
  14. package/src/cli/options/index.ts +5 -0
  15. package/src/cli/themes/colorTheme.ts +379 -0
  16. package/src/cli/validators/commandValidator.ts +395 -0
  17. package/src/cli/validators/index.ts +5 -0
  18. package/src/index.ts +4 -0
  19. package/README.ja.md +0 -587
  20. package/README.md +0 -694
  21. package/README.zh-CN.md +0 -634
  22. package/dist/application/services/CatalogUpdateService.d.ts +0 -209
  23. package/dist/application/services/CatalogUpdateService.d.ts.map +0 -1
  24. package/dist/application/services/CatalogUpdateService.js +0 -839
  25. package/dist/application/services/CatalogUpdateService.js.map +0 -1
  26. package/dist/application/services/WorkspaceService.d.ts +0 -139
  27. package/dist/application/services/WorkspaceService.d.ts.map +0 -1
  28. package/dist/application/services/WorkspaceService.js +0 -340
  29. package/dist/application/services/WorkspaceService.js.map +0 -1
  30. package/dist/cli/commands/CheckCommand.d.ts +0 -40
  31. package/dist/cli/commands/CheckCommand.d.ts.map +0 -1
  32. package/dist/cli/commands/CheckCommand.js +0 -177
  33. package/dist/cli/commands/CheckCommand.js.map +0 -1
  34. package/dist/cli/commands/InitCommand.d.ts +0 -53
  35. package/dist/cli/commands/InitCommand.d.ts.map +0 -1
  36. package/dist/cli/commands/InitCommand.js +0 -338
  37. package/dist/cli/commands/InitCommand.js.map +0 -1
  38. package/dist/cli/commands/SecurityCommand.d.ts +0 -113
  39. package/dist/cli/commands/SecurityCommand.d.ts.map +0 -1
  40. package/dist/cli/commands/SecurityCommand.js +0 -410
  41. package/dist/cli/commands/SecurityCommand.js.map +0 -1
  42. package/dist/cli/commands/UpdateCommand.d.ts +0 -44
  43. package/dist/cli/commands/UpdateCommand.d.ts.map +0 -1
  44. package/dist/cli/commands/UpdateCommand.js +0 -189
  45. package/dist/cli/commands/UpdateCommand.js.map +0 -1
  46. package/dist/cli/formatters/OutputFormatter.d.ts +0 -116
  47. package/dist/cli/formatters/OutputFormatter.d.ts.map +0 -1
  48. package/dist/cli/formatters/OutputFormatter.js +0 -664
  49. package/dist/cli/formatters/OutputFormatter.js.map +0 -1
  50. package/dist/cli/formatters/ProgressBar.d.ts +0 -195
  51. package/dist/cli/formatters/ProgressBar.d.ts.map +0 -1
  52. package/dist/cli/formatters/ProgressBar.js +0 -622
  53. package/dist/cli/formatters/ProgressBar.js.map +0 -1
  54. package/dist/cli/index.d.ts +0 -12
  55. package/dist/cli/index.d.ts.map +0 -1
  56. package/dist/cli/index.js +0 -492
  57. package/dist/cli/index.js.map +0 -1
  58. package/dist/cli/interactive/InteractivePrompts.d.ts +0 -85
  59. package/dist/cli/interactive/InteractivePrompts.d.ts.map +0 -1
  60. package/dist/cli/interactive/InteractivePrompts.js +0 -434
  61. package/dist/cli/interactive/InteractivePrompts.js.map +0 -1
  62. package/dist/cli/options/GlobalOptions.d.ts +0 -117
  63. package/dist/cli/options/GlobalOptions.d.ts.map +0 -1
  64. package/dist/cli/options/GlobalOptions.js +0 -278
  65. package/dist/cli/options/GlobalOptions.js.map +0 -1
  66. package/dist/cli/options/index.d.ts +0 -5
  67. package/dist/cli/options/index.d.ts.map +0 -1
  68. package/dist/cli/options/index.js +0 -5
  69. package/dist/cli/options/index.js.map +0 -1
  70. package/dist/cli/themes/ColorTheme.d.ts +0 -211
  71. package/dist/cli/themes/ColorTheme.d.ts.map +0 -1
  72. package/dist/cli/themes/ColorTheme.js +0 -267
  73. package/dist/cli/themes/ColorTheme.js.map +0 -1
  74. package/dist/cli/validators/CommandValidator.d.ts +0 -60
  75. package/dist/cli/validators/CommandValidator.d.ts.map +0 -1
  76. package/dist/cli/validators/CommandValidator.js +0 -319
  77. package/dist/cli/validators/CommandValidator.js.map +0 -1
  78. package/dist/cli/validators/index.d.ts +0 -5
  79. package/dist/cli/validators/index.d.ts.map +0 -1
  80. package/dist/cli/validators/index.js +0 -5
  81. package/dist/cli/validators/index.js.map +0 -1
  82. package/dist/common/config/Config.d.ts +0 -142
  83. package/dist/common/config/Config.d.ts.map +0 -1
  84. package/dist/common/config/Config.js +0 -382
  85. package/dist/common/config/Config.js.map +0 -1
  86. package/dist/common/config/ConfigLoader.d.ts +0 -49
  87. package/dist/common/config/ConfigLoader.d.ts.map +0 -1
  88. package/dist/common/config/ConfigLoader.js +0 -180
  89. package/dist/common/config/ConfigLoader.js.map +0 -1
  90. package/dist/common/config/PackageFilterConfig.d.ts +0 -56
  91. package/dist/common/config/PackageFilterConfig.d.ts.map +0 -1
  92. package/dist/common/config/PackageFilterConfig.js +0 -94
  93. package/dist/common/config/PackageFilterConfig.js.map +0 -1
  94. package/dist/common/config/index.d.ts +0 -8
  95. package/dist/common/config/index.d.ts.map +0 -1
  96. package/dist/common/config/index.js +0 -8
  97. package/dist/common/config/index.js.map +0 -1
  98. package/dist/common/error-handling/ErrorTracker.d.ts +0 -48
  99. package/dist/common/error-handling/ErrorTracker.d.ts.map +0 -1
  100. package/dist/common/error-handling/ErrorTracker.js +0 -93
  101. package/dist/common/error-handling/ErrorTracker.js.map +0 -1
  102. package/dist/common/error-handling/UserFriendlyErrorHandler.d.ts +0 -74
  103. package/dist/common/error-handling/UserFriendlyErrorHandler.d.ts.map +0 -1
  104. package/dist/common/error-handling/UserFriendlyErrorHandler.js +0 -703
  105. package/dist/common/error-handling/UserFriendlyErrorHandler.js.map +0 -1
  106. package/dist/common/error-handling/index.d.ts +0 -11
  107. package/dist/common/error-handling/index.d.ts.map +0 -1
  108. package/dist/common/error-handling/index.js +0 -9
  109. package/dist/common/error-handling/index.js.map +0 -1
  110. package/dist/common/logger/Logger.d.ts +0 -110
  111. package/dist/common/logger/Logger.d.ts.map +0 -1
  112. package/dist/common/logger/Logger.js +0 -289
  113. package/dist/common/logger/Logger.js.map +0 -1
  114. package/dist/common/logger/index.d.ts +0 -6
  115. package/dist/common/logger/index.d.ts.map +0 -1
  116. package/dist/common/logger/index.js +0 -6
  117. package/dist/common/logger/index.js.map +0 -1
  118. package/dist/common/types/cli.d.ts +0 -265
  119. package/dist/common/types/cli.d.ts.map +0 -1
  120. package/dist/common/types/cli.js +0 -5
  121. package/dist/common/types/cli.js.map +0 -1
  122. package/dist/common/types/core.d.ts +0 -270
  123. package/dist/common/types/core.d.ts.map +0 -1
  124. package/dist/common/types/core.js +0 -32
  125. package/dist/common/types/core.js.map +0 -1
  126. package/dist/common/types/index.d.ts +0 -8
  127. package/dist/common/types/index.d.ts.map +0 -1
  128. package/dist/common/types/index.js +0 -8
  129. package/dist/common/types/index.js.map +0 -1
  130. package/dist/common/utils/VersionChecker.d.ts +0 -54
  131. package/dist/common/utils/VersionChecker.d.ts.map +0 -1
  132. package/dist/common/utils/VersionChecker.js +0 -180
  133. package/dist/common/utils/VersionChecker.js.map +0 -1
  134. package/dist/common/utils/async.d.ts +0 -74
  135. package/dist/common/utils/async.d.ts.map +0 -1
  136. package/dist/common/utils/async.js +0 -228
  137. package/dist/common/utils/async.js.map +0 -1
  138. package/dist/common/utils/format.d.ts +0 -32
  139. package/dist/common/utils/format.d.ts.map +0 -1
  140. package/dist/common/utils/format.js +0 -121
  141. package/dist/common/utils/format.js.map +0 -1
  142. package/dist/common/utils/git.d.ts +0 -44
  143. package/dist/common/utils/git.d.ts.map +0 -1
  144. package/dist/common/utils/git.js +0 -147
  145. package/dist/common/utils/git.js.map +0 -1
  146. package/dist/common/utils/index.d.ts +0 -12
  147. package/dist/common/utils/index.d.ts.map +0 -1
  148. package/dist/common/utils/index.js +0 -12
  149. package/dist/common/utils/index.js.map +0 -1
  150. package/dist/common/utils/string.d.ts +0 -56
  151. package/dist/common/utils/string.d.ts.map +0 -1
  152. package/dist/common/utils/string.js +0 -134
  153. package/dist/common/utils/string.js.map +0 -1
  154. package/dist/common/utils/validation.d.ts +0 -88
  155. package/dist/common/utils/validation.d.ts.map +0 -1
  156. package/dist/common/utils/validation.js +0 -308
  157. package/dist/common/utils/validation.js.map +0 -1
  158. package/dist/domain/entities/Catalog.d.ts +0 -117
  159. package/dist/domain/entities/Catalog.d.ts.map +0 -1
  160. package/dist/domain/entities/Catalog.js +0 -240
  161. package/dist/domain/entities/Catalog.js.map +0 -1
  162. package/dist/domain/entities/Package.d.ts +0 -143
  163. package/dist/domain/entities/Package.d.ts.map +0 -1
  164. package/dist/domain/entities/Package.js +0 -272
  165. package/dist/domain/entities/Package.js.map +0 -1
  166. package/dist/domain/entities/Workspace.d.ts +0 -95
  167. package/dist/domain/entities/Workspace.d.ts.map +0 -1
  168. package/dist/domain/entities/Workspace.js +0 -173
  169. package/dist/domain/entities/Workspace.js.map +0 -1
  170. package/dist/domain/repositories/WorkspaceRepository.d.ts +0 -41
  171. package/dist/domain/repositories/WorkspaceRepository.d.ts.map +0 -1
  172. package/dist/domain/repositories/WorkspaceRepository.js +0 -8
  173. package/dist/domain/repositories/WorkspaceRepository.js.map +0 -1
  174. package/dist/domain/value-objects/CatalogCollection.d.ts +0 -106
  175. package/dist/domain/value-objects/CatalogCollection.d.ts.map +0 -1
  176. package/dist/domain/value-objects/CatalogCollection.js +0 -230
  177. package/dist/domain/value-objects/CatalogCollection.js.map +0 -1
  178. package/dist/domain/value-objects/PackageCollection.d.ts +0 -122
  179. package/dist/domain/value-objects/PackageCollection.d.ts.map +0 -1
  180. package/dist/domain/value-objects/PackageCollection.js +0 -263
  181. package/dist/domain/value-objects/PackageCollection.js.map +0 -1
  182. package/dist/domain/value-objects/Version.d.ts +0 -141
  183. package/dist/domain/value-objects/Version.d.ts.map +0 -1
  184. package/dist/domain/value-objects/Version.js +0 -268
  185. package/dist/domain/value-objects/Version.js.map +0 -1
  186. package/dist/domain/value-objects/WorkspaceConfig.d.ts +0 -144
  187. package/dist/domain/value-objects/WorkspaceConfig.d.ts.map +0 -1
  188. package/dist/domain/value-objects/WorkspaceConfig.js +0 -357
  189. package/dist/domain/value-objects/WorkspaceConfig.js.map +0 -1
  190. package/dist/domain/value-objects/WorkspaceId.d.ts +0 -51
  191. package/dist/domain/value-objects/WorkspaceId.d.ts.map +0 -1
  192. package/dist/domain/value-objects/WorkspaceId.js +0 -104
  193. package/dist/domain/value-objects/WorkspaceId.js.map +0 -1
  194. package/dist/domain/value-objects/WorkspacePath.d.ts +0 -75
  195. package/dist/domain/value-objects/WorkspacePath.d.ts.map +0 -1
  196. package/dist/domain/value-objects/WorkspacePath.js +0 -128
  197. package/dist/domain/value-objects/WorkspacePath.js.map +0 -1
  198. package/dist/index.d.ts +0 -25
  199. package/dist/index.d.ts.map +0 -1
  200. package/dist/infrastructure/cache/Cache.d.ts +0 -161
  201. package/dist/infrastructure/cache/Cache.d.ts.map +0 -1
  202. package/dist/infrastructure/cache/Cache.js +0 -398
  203. package/dist/infrastructure/cache/Cache.js.map +0 -1
  204. package/dist/infrastructure/cache/index.d.ts +0 -6
  205. package/dist/infrastructure/cache/index.d.ts.map +0 -1
  206. package/dist/infrastructure/cache/index.js +0 -6
  207. package/dist/infrastructure/cache/index.js.map +0 -1
  208. package/dist/infrastructure/external-services/NpmRegistryService.d.ts +0 -153
  209. package/dist/infrastructure/external-services/NpmRegistryService.d.ts.map +0 -1
  210. package/dist/infrastructure/external-services/NpmRegistryService.js +0 -511
  211. package/dist/infrastructure/external-services/NpmRegistryService.js.map +0 -1
  212. package/dist/infrastructure/file-system/FileSystemService.d.ts +0 -120
  213. package/dist/infrastructure/file-system/FileSystemService.d.ts.map +0 -1
  214. package/dist/infrastructure/file-system/FileSystemService.js +0 -663
  215. package/dist/infrastructure/file-system/FileSystemService.js.map +0 -1
  216. package/dist/infrastructure/repositories/FileWorkspaceRepository.d.ts +0 -57
  217. package/dist/infrastructure/repositories/FileWorkspaceRepository.d.ts.map +0 -1
  218. package/dist/infrastructure/repositories/FileWorkspaceRepository.js +0 -179
  219. package/dist/infrastructure/repositories/FileWorkspaceRepository.js.map +0 -1
  220. package/dist/infrastructure/utils/NpmrcParser.d.ts +0 -40
  221. package/dist/infrastructure/utils/NpmrcParser.d.ts.map +0 -1
  222. package/dist/infrastructure/utils/NpmrcParser.js +0 -157
  223. package/dist/infrastructure/utils/NpmrcParser.js.map +0 -1
@@ -0,0 +1,830 @@
1
+ /**
2
+ * Output Formatter
3
+ *
4
+ * Provides formatted output for CLI commands in various formats.
5
+ * Supports table, JSON, YAML, and minimal output formats.
6
+ */
7
+
8
+ import { ImpactAnalysis, OutdatedReport, UpdateResult } from '@pcu/core';
9
+ import { WorkspaceStats, WorkspaceValidationReport } from '@pcu/core';
10
+ import { SecurityReport } from '../commands/securityCommand.js';
11
+
12
+ import chalk from 'chalk';
13
+ import Table from 'cli-table3';
14
+ import YAML from 'yaml';
15
+
16
+ export type OutputFormat = 'table' | 'json' | 'yaml' | 'minimal';
17
+
18
+ // Build ANSI escape regex without literal control characters
19
+ const ANSI_ESCAPE = String.fromCharCode(27);
20
+ const ansiRegex: RegExp = new RegExp(`${ANSI_ESCAPE}\\[[0-9;]*m`, 'g');
21
+
22
+ export class OutputFormatter {
23
+ constructor(
24
+ private readonly format: OutputFormat = 'table',
25
+ private readonly useColor: boolean = true
26
+ ) {}
27
+
28
+ /**
29
+ * Format outdated dependencies report
30
+ */
31
+ formatOutdatedReport(report: OutdatedReport): string {
32
+ switch (this.format) {
33
+ case 'json':
34
+ return JSON.stringify(report, null, 2);
35
+ case 'yaml':
36
+ return YAML.stringify(report);
37
+ case 'minimal':
38
+ return this.formatOutdatedMinimal(report);
39
+ case 'table':
40
+ default:
41
+ return this.formatOutdatedTable(report);
42
+ }
43
+ }
44
+
45
+ /**
46
+ * Format update result
47
+ */
48
+ formatUpdateResult(result: UpdateResult): string {
49
+ switch (this.format) {
50
+ case 'json':
51
+ return JSON.stringify(result, null, 2);
52
+ case 'yaml':
53
+ return YAML.stringify(result);
54
+ case 'minimal':
55
+ return this.formatUpdateMinimal(result);
56
+ case 'table':
57
+ default:
58
+ return this.formatUpdateTable(result);
59
+ }
60
+ }
61
+
62
+ /**
63
+ * Format impact analysis
64
+ */
65
+ formatImpactAnalysis(analysis: ImpactAnalysis): string {
66
+ switch (this.format) {
67
+ case 'json':
68
+ return JSON.stringify(analysis, null, 2);
69
+ case 'yaml':
70
+ return YAML.stringify(analysis);
71
+ case 'minimal':
72
+ return this.formatImpactMinimal(analysis);
73
+ case 'table':
74
+ default:
75
+ return this.formatImpactTable(analysis);
76
+ }
77
+ }
78
+
79
+ /**
80
+ * Format workspace validation report
81
+ */
82
+ formatValidationReport(report: WorkspaceValidationReport): string {
83
+ switch (this.format) {
84
+ case 'json':
85
+ return JSON.stringify(report, null, 2);
86
+ case 'yaml':
87
+ return YAML.stringify(report);
88
+ case 'minimal':
89
+ return this.formatValidationMinimal(report);
90
+ case 'table':
91
+ default:
92
+ return this.formatValidationTable(report);
93
+ }
94
+ }
95
+
96
+ /**
97
+ * Format workspace statistics
98
+ */
99
+ formatWorkspaceStats(stats: WorkspaceStats): string {
100
+ switch (this.format) {
101
+ case 'json':
102
+ return JSON.stringify(stats, null, 2);
103
+ case 'yaml':
104
+ return YAML.stringify(stats);
105
+ case 'minimal':
106
+ return this.formatStatsMinimal(stats);
107
+ case 'table':
108
+ default:
109
+ return this.formatStatsTable(stats);
110
+ }
111
+ }
112
+
113
+ /**
114
+ * Format security report
115
+ */
116
+ formatSecurityReport(report: SecurityReport): string {
117
+ switch (this.format) {
118
+ case 'json':
119
+ return JSON.stringify(report, null, 2);
120
+ case 'yaml':
121
+ return YAML.stringify(report);
122
+ case 'minimal':
123
+ return this.formatSecurityMinimal(report);
124
+ case 'table':
125
+ default:
126
+ return this.formatSecurityTable(report);
127
+ }
128
+ }
129
+
130
+ /**
131
+ * Format simple message with optional styling
132
+ */
133
+ formatMessage(message: string, type: 'success' | 'error' | 'warning' | 'info' = 'info'): string {
134
+ if (!this.useColor) {
135
+ return message;
136
+ }
137
+
138
+ switch (type) {
139
+ case 'success':
140
+ return chalk.green(message);
141
+ case 'error':
142
+ return chalk.red(message);
143
+ case 'warning':
144
+ return chalk.yellow(message);
145
+ case 'info':
146
+ default:
147
+ return chalk.blue(message);
148
+ }
149
+ }
150
+
151
+ /**
152
+ * Format outdated dependencies as table
153
+ */
154
+ private formatOutdatedTable(report: OutdatedReport): string {
155
+ const lines: string[] = [];
156
+
157
+ // Header
158
+ lines.push(this.colorize(chalk.bold, `\n📦 Workspace: ${report.workspace.name}`));
159
+ lines.push(this.colorize(chalk.gray, `Path: ${report.workspace.path}`));
160
+
161
+ if (!report.hasUpdates) {
162
+ lines.push(this.colorize(chalk.green, '\n✅ All catalog dependencies are up to date!'));
163
+ return lines.join('\n');
164
+ }
165
+
166
+ lines.push(
167
+ this.colorize(chalk.yellow, `\n🔄 Found ${report.totalOutdated} outdated dependencies\n`)
168
+ );
169
+
170
+ for (const catalogInfo of report.catalogs) {
171
+ if (catalogInfo.outdatedCount === 0) continue;
172
+
173
+ lines.push(this.colorize(chalk.bold, `📋 Catalog: ${catalogInfo.catalogName}`));
174
+
175
+ const table = new Table({
176
+ head: this.colorizeHeaders(['Package', 'Current', 'Latest', 'Type', 'Packages']),
177
+ style: { head: [], border: [] },
178
+ colWidths: [25, 15, 15, 8, 20],
179
+ });
180
+
181
+ for (const dep of catalogInfo.outdatedDependencies) {
182
+ const typeColor = this.getUpdateTypeColor(dep.updateType);
183
+ const securityIcon = dep.isSecurityUpdate ? '🔒 ' : '';
184
+
185
+ // Colorize version differences
186
+ const { currentColored, latestColored } = this.colorizeVersionDiff(
187
+ dep.currentVersion,
188
+ dep.latestVersion,
189
+ dep.updateType
190
+ );
191
+
192
+ table.push([
193
+ `${securityIcon}${dep.packageName}`,
194
+ currentColored,
195
+ latestColored,
196
+ this.colorize(typeColor, dep.updateType),
197
+ `${dep.affectedPackages.length} package(s)`,
198
+ ]);
199
+ }
200
+
201
+ lines.push(table.toString());
202
+ lines.push('');
203
+ }
204
+
205
+ return lines.join('\n');
206
+ }
207
+
208
+ /**
209
+ * Format outdated dependencies minimally (npm-check-updates style)
210
+ */
211
+ private formatOutdatedMinimal(report: OutdatedReport): string {
212
+ if (!report.hasUpdates) {
213
+ return 'All dependencies up to date';
214
+ }
215
+
216
+ // Collect all dependencies first to calculate max package name width
217
+ const allDeps: Array<{
218
+ securityIcon: string;
219
+ packageName: string;
220
+ currentColored: string;
221
+ latestColored: string;
222
+ }> = [];
223
+
224
+ for (const catalogInfo of report.catalogs) {
225
+ for (const dep of catalogInfo.outdatedDependencies) {
226
+ const securityIcon = dep.isSecurityUpdate ? '🔒 ' : '';
227
+ const { currentColored, latestColored } = this.colorizeVersionDiff(
228
+ dep.currentVersion,
229
+ dep.latestVersion,
230
+ dep.updateType
231
+ );
232
+ allDeps.push({
233
+ securityIcon,
234
+ packageName: dep.packageName,
235
+ currentColored,
236
+ latestColored,
237
+ });
238
+ }
239
+ }
240
+
241
+ // Calculate max widths for alignment
242
+ const maxNameWidth = Math.max(
243
+ ...allDeps.map((dep) => (dep.securityIcon + dep.packageName).length)
244
+ );
245
+
246
+ // Calculate max version widths (need to strip color codes for accurate width calculation)
247
+ const stripAnsi = (str: string) => str.replace(ansiRegex, '');
248
+ const maxCurrentWidth = Math.max(...allDeps.map((dep) => stripAnsi(dep.currentColored).length));
249
+
250
+ // Format lines with proper alignment
251
+ const lines: string[] = [];
252
+ for (const dep of allDeps) {
253
+ const nameWithIcon = dep.securityIcon + dep.packageName;
254
+ const paddedName = nameWithIcon.padEnd(maxNameWidth);
255
+
256
+ // For current version alignment, we need to pad the visible text, not the colored version
257
+ const currentVisible = stripAnsi(dep.currentColored);
258
+ const currentPadding = maxCurrentWidth - currentVisible.length;
259
+ const paddedCurrent = dep.currentColored + ' '.repeat(currentPadding);
260
+
261
+ lines.push(`${paddedName} ${paddedCurrent} → ${dep.latestColored}`);
262
+ }
263
+
264
+ return lines.join('\n');
265
+ }
266
+
267
+ /**
268
+ * Format update result as table
269
+ */
270
+ private formatUpdateTable(result: UpdateResult): string {
271
+ const lines: string[] = [];
272
+
273
+ // Header
274
+ lines.push(this.colorize(chalk.bold, `\n📦 Workspace: ${result.workspace.name}`));
275
+
276
+ if (result.success) {
277
+ lines.push(this.colorize(chalk.green, '✅ Update completed successfully!'));
278
+ } else {
279
+ lines.push(this.colorize(chalk.red, '❌ Update completed with errors'));
280
+ }
281
+
282
+ lines.push('');
283
+
284
+ // Updated dependencies
285
+ if (result.updatedDependencies.length > 0) {
286
+ lines.push(this.colorize(chalk.green, `🎉 Updated ${result.totalUpdated} dependencies:`));
287
+
288
+ const table = new Table({
289
+ head: this.colorizeHeaders(['Catalog', 'Package', 'From', 'To', 'Type']),
290
+ style: { head: [], border: [] },
291
+ colWidths: [15, 25, 15, 15, 8],
292
+ });
293
+
294
+ for (const dep of result.updatedDependencies) {
295
+ const typeColor = this.getUpdateTypeColor(dep.updateType);
296
+
297
+ // Colorize version differences
298
+ const { currentColored, latestColored } = this.colorizeVersionDiff(
299
+ dep.fromVersion,
300
+ dep.toVersion,
301
+ dep.updateType
302
+ );
303
+
304
+ table.push([
305
+ dep.catalogName,
306
+ dep.packageName,
307
+ currentColored,
308
+ latestColored,
309
+ this.colorize(typeColor, dep.updateType),
310
+ ]);
311
+ }
312
+
313
+ lines.push(table.toString());
314
+ lines.push('');
315
+ }
316
+
317
+ // Skipped dependencies
318
+ if (result.skippedDependencies.length > 0) {
319
+ lines.push(this.colorize(chalk.yellow, `⚠️ Skipped ${result.totalSkipped} dependencies:`));
320
+
321
+ for (const dep of result.skippedDependencies) {
322
+ lines.push(` ${dep.catalogName}:${dep.packageName} - ${dep.reason}`);
323
+ }
324
+ lines.push('');
325
+ }
326
+
327
+ // Errors
328
+ if (result.errors.length > 0) {
329
+ lines.push(this.colorize(chalk.red, `❌ ${result.totalErrors} errors occurred:`));
330
+
331
+ for (const error of result.errors) {
332
+ const prefix = error.fatal ? '💥' : '⚠️ ';
333
+ lines.push(` ${prefix} ${error.catalogName}:${error.packageName} - ${error.error}`);
334
+ }
335
+ }
336
+
337
+ return lines.join('\n');
338
+ }
339
+
340
+ /**
341
+ * Format update result minimally (npm-check-updates style)
342
+ */
343
+ private formatUpdateMinimal(result: UpdateResult): string {
344
+ const lines: string[] = [];
345
+
346
+ if (result.success) {
347
+ lines.push(`Updated ${result.totalUpdated} dependencies`);
348
+ } else {
349
+ lines.push(`Update failed with ${result.totalErrors} errors`);
350
+ }
351
+
352
+ if (result.updatedDependencies.length > 0) {
353
+ // Collect version info for alignment calculation
354
+ const depsWithVersions = result.updatedDependencies.map((dep) => {
355
+ const { currentColored, latestColored } = this.colorizeVersionDiff(
356
+ dep.fromVersion,
357
+ dep.toVersion,
358
+ dep.updateType
359
+ );
360
+ return {
361
+ packageName: dep.packageName,
362
+ currentColored,
363
+ latestColored,
364
+ };
365
+ });
366
+
367
+ // Calculate max widths for alignment
368
+ const maxNameWidth = Math.max(...depsWithVersions.map((dep) => dep.packageName.length));
369
+
370
+ const stripAnsi = (str: string) => str.replace(ansiRegex, '');
371
+ const maxCurrentWidth = Math.max(
372
+ ...depsWithVersions.map((dep) => stripAnsi(dep.currentColored).length)
373
+ );
374
+
375
+ for (const dep of depsWithVersions) {
376
+ const paddedName = dep.packageName.padEnd(maxNameWidth);
377
+
378
+ // Pad current version for alignment
379
+ const currentVisible = stripAnsi(dep.currentColored);
380
+ const currentPadding = maxCurrentWidth - currentVisible.length;
381
+ const paddedCurrent = dep.currentColored + ' '.repeat(currentPadding);
382
+
383
+ lines.push(`${paddedName} ${paddedCurrent} → ${dep.latestColored}`);
384
+ }
385
+ }
386
+
387
+ return lines.join('\n');
388
+ }
389
+
390
+ /**
391
+ * Format impact analysis as table
392
+ */
393
+ private formatImpactTable(analysis: ImpactAnalysis): string {
394
+ const lines: string[] = [];
395
+
396
+ // Header
397
+ lines.push(this.colorize(chalk.bold, `\n🔍 Impact Analysis: ${analysis.packageName}`));
398
+ lines.push(this.colorize(chalk.gray, `Catalog: ${analysis.catalogName}`));
399
+ lines.push(
400
+ this.colorize(chalk.gray, `Update: ${analysis.currentVersion} → ${analysis.proposedVersion}`)
401
+ );
402
+ lines.push(this.colorize(chalk.gray, `Type: ${analysis.updateType}`));
403
+
404
+ // Risk level
405
+ const riskColor = this.getRiskColor(analysis.riskLevel);
406
+ lines.push(this.colorize(riskColor, `Risk Level: ${analysis.riskLevel.toUpperCase()}`));
407
+ lines.push('');
408
+
409
+ // Affected packages
410
+ if (analysis.affectedPackages.length > 0) {
411
+ lines.push(this.colorize(chalk.bold, '📦 Affected Packages:'));
412
+
413
+ const table = new Table({
414
+ head: this.colorizeHeaders(['Package', 'Path', 'Dependency Type', 'Risk']),
415
+ style: { head: [], border: [] },
416
+ colWidths: [20, 30, 15, 10],
417
+ });
418
+
419
+ for (const pkg of analysis.affectedPackages) {
420
+ const riskColor = this.getRiskColor(pkg.compatibilityRisk);
421
+ table.push([
422
+ pkg.packageName,
423
+ pkg.packagePath,
424
+ pkg.dependencyType,
425
+ this.colorize(riskColor, pkg.compatibilityRisk),
426
+ ]);
427
+ }
428
+
429
+ lines.push(table.toString());
430
+ lines.push('');
431
+ }
432
+
433
+ // Security impact
434
+ if (analysis.securityImpact.hasVulnerabilities) {
435
+ lines.push(this.colorize(chalk.bold, '🔒 Security Impact:'));
436
+
437
+ if (analysis.securityImpact.fixedVulnerabilities > 0) {
438
+ lines.push(
439
+ this.colorize(
440
+ chalk.green,
441
+ ` ✅ Fixes ${analysis.securityImpact.fixedVulnerabilities} vulnerabilities`
442
+ )
443
+ );
444
+ }
445
+
446
+ if (analysis.securityImpact.newVulnerabilities > 0) {
447
+ lines.push(
448
+ this.colorize(
449
+ chalk.red,
450
+ ` ⚠️ Introduces ${analysis.securityImpact.newVulnerabilities} vulnerabilities`
451
+ )
452
+ );
453
+ }
454
+
455
+ lines.push('');
456
+ }
457
+
458
+ // Recommendations
459
+ if (analysis.recommendations.length > 0) {
460
+ lines.push(this.colorize(chalk.bold, '💡 Recommendations:'));
461
+ for (const rec of analysis.recommendations) {
462
+ lines.push(` ${rec}`);
463
+ }
464
+ }
465
+
466
+ return lines.join('\n');
467
+ }
468
+
469
+ /**
470
+ * Format impact analysis minimally
471
+ */
472
+ private formatImpactMinimal(analysis: ImpactAnalysis): string {
473
+ return [
474
+ `${analysis.packageName}: ${analysis.currentVersion} → ${analysis.proposedVersion}`,
475
+ `Risk: ${analysis.riskLevel}`,
476
+ `Affected: ${analysis.affectedPackages.length} packages`,
477
+ ].join('\n');
478
+ }
479
+
480
+ /**
481
+ * Format validation report as table
482
+ */
483
+ private formatValidationTable(report: WorkspaceValidationReport): string {
484
+ const lines: string[] = [];
485
+
486
+ // Header
487
+ const statusIcon = report.isValid ? '✅' : '❌';
488
+ const statusColor = report.isValid ? chalk.green : chalk.red;
489
+
490
+ lines.push(this.colorize(chalk.bold, `\n${statusIcon} Workspace Validation`));
491
+ lines.push(this.colorize(statusColor, `Status: ${report.isValid ? 'VALID' : 'INVALID'}`));
492
+ lines.push('');
493
+
494
+ // Workspace info
495
+ lines.push(this.colorize(chalk.bold, '📦 Workspace Information:'));
496
+ lines.push(` Path: ${report.workspace.path}`);
497
+ lines.push(` Name: ${report.workspace.name}`);
498
+ lines.push(` Packages: ${report.workspace.packageCount}`);
499
+ lines.push(` Catalogs: ${report.workspace.catalogCount}`);
500
+ lines.push('');
501
+
502
+ // Errors
503
+ if (report.errors.length > 0) {
504
+ lines.push(this.colorize(chalk.red, '❌ Errors:'));
505
+ for (const error of report.errors) {
506
+ lines.push(` • ${error}`);
507
+ }
508
+ lines.push('');
509
+ }
510
+
511
+ // Warnings
512
+ if (report.warnings.length > 0) {
513
+ lines.push(this.colorize(chalk.yellow, '⚠️ Warnings:'));
514
+ for (const warning of report.warnings) {
515
+ lines.push(` • ${warning}`);
516
+ }
517
+ lines.push('');
518
+ }
519
+
520
+ // Recommendations
521
+ if (report.recommendations.length > 0) {
522
+ lines.push(this.colorize(chalk.blue, '💡 Recommendations:'));
523
+ for (const rec of report.recommendations) {
524
+ lines.push(` • ${rec}`);
525
+ }
526
+ }
527
+
528
+ return lines.join('\n');
529
+ }
530
+
531
+ /**
532
+ * Format validation report minimally
533
+ */
534
+ private formatValidationMinimal(report: WorkspaceValidationReport): string {
535
+ const status = report.isValid ? 'VALID' : 'INVALID';
536
+ const errors = report.errors.length;
537
+ const warnings = report.warnings.length;
538
+
539
+ return `${status} (${errors} errors, ${warnings} warnings)`;
540
+ }
541
+
542
+ /**
543
+ * Format workspace statistics as table
544
+ */
545
+ private formatStatsTable(stats: WorkspaceStats): string {
546
+ const lines: string[] = [];
547
+
548
+ lines.push(this.colorize(chalk.bold, `\n📊 Workspace Statistics`));
549
+ lines.push(this.colorize(chalk.gray, `Workspace: ${stats.workspace.name}`));
550
+ lines.push('');
551
+
552
+ const table = new Table({
553
+ head: this.colorizeHeaders(['Metric', 'Count']),
554
+ style: { head: [], border: [] },
555
+ colWidths: [30, 10],
556
+ });
557
+
558
+ table.push(['Total Packages', stats.packages.total.toString()]);
559
+ table.push(['Packages with Catalog Refs', stats.packages.withCatalogReferences.toString()]);
560
+ table.push(['Total Catalogs', stats.catalogs.total.toString()]);
561
+ table.push(['Catalog Entries', stats.catalogs.totalEntries.toString()]);
562
+ table.push(['Total Dependencies', stats.dependencies.total.toString()]);
563
+ table.push(['Catalog References', stats.dependencies.catalogReferences.toString()]);
564
+ table.push(['Dependencies', stats.dependencies.byType.dependencies.toString()]);
565
+ table.push(['Dev Dependencies', stats.dependencies.byType.devDependencies.toString()]);
566
+ table.push(['Peer Dependencies', stats.dependencies.byType.peerDependencies.toString()]);
567
+ table.push([
568
+ 'Optional Dependencies',
569
+ stats.dependencies.byType.optionalDependencies.toString(),
570
+ ]);
571
+
572
+ lines.push(table.toString());
573
+
574
+ return lines.join('\n');
575
+ }
576
+
577
+ /**
578
+ * Format workspace statistics minimally
579
+ */
580
+ private formatStatsMinimal(stats: WorkspaceStats): string {
581
+ return [
582
+ `Packages: ${stats.packages.total}`,
583
+ `Catalogs: ${stats.catalogs.total}`,
584
+ `Dependencies: ${stats.dependencies.total}`,
585
+ ].join(', ');
586
+ }
587
+
588
+ /**
589
+ * Format security report as table
590
+ */
591
+ private formatSecurityTable(report: SecurityReport): string {
592
+ const lines: string[] = [];
593
+
594
+ // Header
595
+ lines.push(this.colorize(chalk.bold, '\n🔒 Security Report'));
596
+ lines.push(this.colorize(chalk.gray, `Workspace: ${report.metadata.workspacePath}`));
597
+ lines.push(
598
+ this.colorize(chalk.gray, `Scan Date: ${new Date(report.metadata.scanDate).toLocaleString()}`)
599
+ );
600
+ lines.push(this.colorize(chalk.gray, `Tools: ${report.metadata.scanTools.join(', ')}`));
601
+
602
+ // Summary
603
+ lines.push('');
604
+ lines.push(this.colorize(chalk.bold, '📊 Summary:'));
605
+
606
+ const summaryTable = new Table({
607
+ head: this.colorizeHeaders(['Severity', 'Count']),
608
+ style: { head: [], border: [] },
609
+ colWidths: [15, 10],
610
+ });
611
+
612
+ summaryTable.push(['Critical', this.colorize(chalk.red, report.summary.critical.toString())]);
613
+ summaryTable.push(['High', this.colorize(chalk.yellow, report.summary.high.toString())]);
614
+ summaryTable.push(['Moderate', this.colorize(chalk.blue, report.summary.moderate.toString())]);
615
+ summaryTable.push(['Low', this.colorize(chalk.green, report.summary.low.toString())]);
616
+ summaryTable.push(['Info', this.colorize(chalk.gray, report.summary.info.toString())]);
617
+ summaryTable.push([
618
+ 'Total',
619
+ this.colorize(chalk.bold, report.summary.totalVulnerabilities.toString()),
620
+ ]);
621
+
622
+ lines.push(summaryTable.toString());
623
+
624
+ // Vulnerabilities
625
+ if (report.vulnerabilities.length > 0) {
626
+ lines.push('');
627
+ lines.push(this.colorize(chalk.bold, '🐛 Vulnerabilities:'));
628
+
629
+ const vulnTable = new Table({
630
+ head: this.colorizeHeaders(['Package', 'Severity', 'Title', 'Fix Available']),
631
+ style: { head: [], border: [] },
632
+ colWidths: [20, 12, 40, 15],
633
+ });
634
+
635
+ for (const vuln of report.vulnerabilities) {
636
+ const severityColor = this.getSeverityColor(vuln.severity);
637
+ const fixStatus = vuln.fixAvailable
638
+ ? typeof vuln.fixAvailable === 'string'
639
+ ? vuln.fixAvailable
640
+ : 'Yes'
641
+ : 'No';
642
+
643
+ vulnTable.push([
644
+ vuln.package,
645
+ this.colorize(severityColor, vuln.severity.toUpperCase()),
646
+ vuln.title.length > 35 ? vuln.title.substring(0, 35) + '...' : vuln.title,
647
+ fixStatus,
648
+ ]);
649
+ }
650
+
651
+ lines.push(vulnTable.toString());
652
+ }
653
+
654
+ // Recommendations
655
+ if (report.recommendations.length > 0) {
656
+ lines.push('');
657
+ lines.push(this.colorize(chalk.bold, '💡 Recommendations:'));
658
+
659
+ for (const rec of report.recommendations) {
660
+ lines.push(` ${rec.package}: ${rec.currentVersion} → ${rec.recommendedVersion}`);
661
+ lines.push(` ${rec.reason} (${rec.impact})`);
662
+ }
663
+ }
664
+
665
+ return lines.join('\n');
666
+ }
667
+
668
+ /**
669
+ * Format security report minimally
670
+ */
671
+ private formatSecurityMinimal(report: SecurityReport): string {
672
+ const vulnerabilities = report.summary.totalVulnerabilities;
673
+ if (vulnerabilities === 0) {
674
+ return 'No vulnerabilities found';
675
+ }
676
+
677
+ return [
678
+ `${vulnerabilities} vulnerabilities found:`,
679
+ ` Critical: ${report.summary.critical}`,
680
+ ` High: ${report.summary.high}`,
681
+ ` Moderate: ${report.summary.moderate}`,
682
+ ` Low: ${report.summary.low}`,
683
+ ].join('\n');
684
+ }
685
+
686
+ /**
687
+ * Get color for severity level
688
+ */
689
+ private getSeverityColor(severity: string): typeof chalk {
690
+ switch (severity.toLowerCase()) {
691
+ case 'critical':
692
+ return chalk.red;
693
+ case 'high':
694
+ return chalk.yellow;
695
+ case 'moderate':
696
+ return chalk.blue;
697
+ case 'low':
698
+ return chalk.green;
699
+ default:
700
+ return chalk.gray;
701
+ }
702
+ }
703
+
704
+ /**
705
+ * Apply color if color is enabled
706
+ */
707
+ private colorize(colorFn: typeof chalk, text: string): string {
708
+ return this.useColor ? colorFn(text) : text;
709
+ }
710
+
711
+ /**
712
+ * Colorize table headers
713
+ */
714
+ private colorizeHeaders(headers: string[]): string[] {
715
+ return this.useColor ? headers.map((h) => chalk.bold.cyan(h)) : headers;
716
+ }
717
+
718
+ /**
719
+ * Get color for update type
720
+ */
721
+ private getUpdateTypeColor(updateType: string): typeof chalk {
722
+ switch (updateType) {
723
+ case 'major':
724
+ return chalk.red;
725
+ case 'minor':
726
+ return chalk.yellow;
727
+ case 'patch':
728
+ return chalk.green;
729
+ default:
730
+ return chalk.gray;
731
+ }
732
+ }
733
+
734
+ /**
735
+ * Get color for risk level
736
+ */
737
+ private getRiskColor(riskLevel: string): typeof chalk {
738
+ switch (riskLevel) {
739
+ case 'high':
740
+ return chalk.red;
741
+ case 'medium':
742
+ return chalk.yellow;
743
+ case 'low':
744
+ return chalk.green;
745
+ default:
746
+ return chalk.gray;
747
+ }
748
+ }
749
+
750
+ /**
751
+ * Colorize version differences between current and latest
752
+ */
753
+ private colorizeVersionDiff(
754
+ current: string,
755
+ latest: string,
756
+ updateType: string
757
+ ): {
758
+ currentColored: string;
759
+ latestColored: string;
760
+ } {
761
+ if (!this.useColor) {
762
+ return { currentColored: current, latestColored: latest };
763
+ }
764
+
765
+ // Parse version numbers to identify different parts
766
+ const parseVersion = (version: string) => {
767
+ // Remove leading ^ or ~ or other prefix characters
768
+ const cleanVersion = version.replace(/^[\^~>=<]+/, '');
769
+ const parts = cleanVersion.split('.');
770
+ return {
771
+ major: parts[0] || '0',
772
+ minor: parts[1] || '0',
773
+ patch: parts[2] || '0',
774
+ extra: parts.slice(3).join('.'),
775
+ prefix: version.substring(0, version.length - cleanVersion.length),
776
+ };
777
+ };
778
+
779
+ const currentParts = parseVersion(current);
780
+ const latestParts = parseVersion(latest);
781
+
782
+ // Determine color based on update type for highlighting differences
783
+ const diffColor = this.getUpdateTypeColor(updateType);
784
+
785
+ // Build colored version strings by comparing each part
786
+ const colorCurrentPart = (part: string, latestPart: string, isChanged: boolean) => {
787
+ if (isChanged && part !== latestPart) {
788
+ return chalk.dim.white(part); // Dim white for old version part
789
+ }
790
+ return chalk.white(part); // Unchanged parts in white
791
+ };
792
+
793
+ const colorLatestPart = (part: string, currentPart: string, isChanged: boolean) => {
794
+ if (isChanged && part !== currentPart) {
795
+ return diffColor(part); // Highlight the new version part with update type color
796
+ }
797
+ return chalk.white(part); // Unchanged parts in white
798
+ };
799
+
800
+ // Check which parts are different
801
+ const majorChanged = currentParts.major !== latestParts.major;
802
+ const minorChanged = currentParts.minor !== latestParts.minor;
803
+ const patchChanged = currentParts.patch !== latestParts.patch;
804
+ const extraChanged = currentParts.extra !== latestParts.extra;
805
+
806
+ // Build colored current version
807
+ let currentColored = currentParts.prefix;
808
+ currentColored += colorCurrentPart(currentParts.major, latestParts.major, majorChanged);
809
+ currentColored += '.';
810
+ currentColored += colorCurrentPart(currentParts.minor, latestParts.minor, minorChanged);
811
+ currentColored += '.';
812
+ currentColored += colorCurrentPart(currentParts.patch, latestParts.patch, patchChanged);
813
+ if (currentParts.extra) {
814
+ currentColored += '.' + colorCurrentPart(currentParts.extra, latestParts.extra, extraChanged);
815
+ }
816
+
817
+ // Build colored latest version
818
+ let latestColored = latestParts.prefix;
819
+ latestColored += colorLatestPart(latestParts.major, currentParts.major, majorChanged);
820
+ latestColored += '.';
821
+ latestColored += colorLatestPart(latestParts.minor, currentParts.minor, minorChanged);
822
+ latestColored += '.';
823
+ latestColored += colorLatestPart(latestParts.patch, currentParts.patch, patchChanged);
824
+ if (latestParts.extra) {
825
+ latestColored += '.' + colorLatestPart(latestParts.extra, currentParts.extra, extraChanged);
826
+ }
827
+
828
+ return { currentColored, latestColored };
829
+ }
830
+ }