pnpm-catalog-updates 0.5.6 → 0.6.5

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 (220) 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 +61 -103
  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/LICENSE +0 -21
  20. package/README.ja.md +0 -582
  21. package/README.md +0 -690
  22. package/README.zh-CN.md +0 -630
  23. package/dist/application/services/CatalogUpdateService.d.ts +0 -209
  24. package/dist/application/services/CatalogUpdateService.d.ts.map +0 -1
  25. package/dist/application/services/CatalogUpdateService.js +0 -836
  26. package/dist/application/services/CatalogUpdateService.js.map +0 -1
  27. package/dist/application/services/WorkspaceService.d.ts +0 -139
  28. package/dist/application/services/WorkspaceService.d.ts.map +0 -1
  29. package/dist/application/services/WorkspaceService.js +0 -340
  30. package/dist/application/services/WorkspaceService.js.map +0 -1
  31. package/dist/cli/commands/CheckCommand.d.ts +0 -40
  32. package/dist/cli/commands/CheckCommand.d.ts.map +0 -1
  33. package/dist/cli/commands/CheckCommand.js +0 -177
  34. package/dist/cli/commands/CheckCommand.js.map +0 -1
  35. package/dist/cli/commands/InitCommand.d.ts +0 -53
  36. package/dist/cli/commands/InitCommand.d.ts.map +0 -1
  37. package/dist/cli/commands/InitCommand.js +0 -338
  38. package/dist/cli/commands/InitCommand.js.map +0 -1
  39. package/dist/cli/commands/SecurityCommand.d.ts +0 -113
  40. package/dist/cli/commands/SecurityCommand.d.ts.map +0 -1
  41. package/dist/cli/commands/SecurityCommand.js +0 -410
  42. package/dist/cli/commands/SecurityCommand.js.map +0 -1
  43. package/dist/cli/commands/UpdateCommand.d.ts +0 -44
  44. package/dist/cli/commands/UpdateCommand.d.ts.map +0 -1
  45. package/dist/cli/commands/UpdateCommand.js +0 -189
  46. package/dist/cli/commands/UpdateCommand.js.map +0 -1
  47. package/dist/cli/formatters/OutputFormatter.d.ts +0 -116
  48. package/dist/cli/formatters/OutputFormatter.d.ts.map +0 -1
  49. package/dist/cli/formatters/OutputFormatter.js +0 -664
  50. package/dist/cli/formatters/OutputFormatter.js.map +0 -1
  51. package/dist/cli/formatters/ProgressBar.d.ts +0 -195
  52. package/dist/cli/formatters/ProgressBar.d.ts.map +0 -1
  53. package/dist/cli/formatters/ProgressBar.js +0 -622
  54. package/dist/cli/formatters/ProgressBar.js.map +0 -1
  55. package/dist/cli/index.d.ts +0 -12
  56. package/dist/cli/index.d.ts.map +0 -1
  57. package/dist/cli/index.js +0 -492
  58. package/dist/cli/index.js.map +0 -1
  59. package/dist/cli/interactive/InteractivePrompts.d.ts +0 -85
  60. package/dist/cli/interactive/InteractivePrompts.d.ts.map +0 -1
  61. package/dist/cli/interactive/InteractivePrompts.js +0 -434
  62. package/dist/cli/interactive/InteractivePrompts.js.map +0 -1
  63. package/dist/cli/options/GlobalOptions.d.ts +0 -117
  64. package/dist/cli/options/GlobalOptions.d.ts.map +0 -1
  65. package/dist/cli/options/GlobalOptions.js +0 -278
  66. package/dist/cli/options/GlobalOptions.js.map +0 -1
  67. package/dist/cli/options/index.d.ts +0 -5
  68. package/dist/cli/options/index.d.ts.map +0 -1
  69. package/dist/cli/options/index.js +0 -5
  70. package/dist/cli/options/index.js.map +0 -1
  71. package/dist/cli/themes/ColorTheme.d.ts +0 -211
  72. package/dist/cli/themes/ColorTheme.d.ts.map +0 -1
  73. package/dist/cli/themes/ColorTheme.js +0 -267
  74. package/dist/cli/themes/ColorTheme.js.map +0 -1
  75. package/dist/cli/validators/CommandValidator.d.ts +0 -60
  76. package/dist/cli/validators/CommandValidator.d.ts.map +0 -1
  77. package/dist/cli/validators/CommandValidator.js +0 -319
  78. package/dist/cli/validators/CommandValidator.js.map +0 -1
  79. package/dist/cli/validators/index.d.ts +0 -5
  80. package/dist/cli/validators/index.d.ts.map +0 -1
  81. package/dist/cli/validators/index.js +0 -5
  82. package/dist/cli/validators/index.js.map +0 -1
  83. package/dist/common/config/Config.d.ts +0 -142
  84. package/dist/common/config/Config.d.ts.map +0 -1
  85. package/dist/common/config/Config.js +0 -382
  86. package/dist/common/config/Config.js.map +0 -1
  87. package/dist/common/config/ConfigLoader.d.ts +0 -49
  88. package/dist/common/config/ConfigLoader.d.ts.map +0 -1
  89. package/dist/common/config/ConfigLoader.js +0 -180
  90. package/dist/common/config/ConfigLoader.js.map +0 -1
  91. package/dist/common/config/PackageFilterConfig.d.ts +0 -56
  92. package/dist/common/config/PackageFilterConfig.d.ts.map +0 -1
  93. package/dist/common/config/PackageFilterConfig.js +0 -94
  94. package/dist/common/config/PackageFilterConfig.js.map +0 -1
  95. package/dist/common/config/index.d.ts +0 -8
  96. package/dist/common/config/index.d.ts.map +0 -1
  97. package/dist/common/config/index.js +0 -8
  98. package/dist/common/config/index.js.map +0 -1
  99. package/dist/common/error-handling/ErrorTracker.d.ts +0 -48
  100. package/dist/common/error-handling/ErrorTracker.d.ts.map +0 -1
  101. package/dist/common/error-handling/ErrorTracker.js +0 -93
  102. package/dist/common/error-handling/ErrorTracker.js.map +0 -1
  103. package/dist/common/error-handling/UserFriendlyErrorHandler.d.ts +0 -74
  104. package/dist/common/error-handling/UserFriendlyErrorHandler.d.ts.map +0 -1
  105. package/dist/common/error-handling/UserFriendlyErrorHandler.js +0 -703
  106. package/dist/common/error-handling/UserFriendlyErrorHandler.js.map +0 -1
  107. package/dist/common/error-handling/index.d.ts +0 -11
  108. package/dist/common/error-handling/index.d.ts.map +0 -1
  109. package/dist/common/error-handling/index.js +0 -9
  110. package/dist/common/error-handling/index.js.map +0 -1
  111. package/dist/common/logger/Logger.d.ts +0 -110
  112. package/dist/common/logger/Logger.d.ts.map +0 -1
  113. package/dist/common/logger/Logger.js +0 -289
  114. package/dist/common/logger/Logger.js.map +0 -1
  115. package/dist/common/logger/index.d.ts +0 -6
  116. package/dist/common/logger/index.d.ts.map +0 -1
  117. package/dist/common/logger/index.js +0 -6
  118. package/dist/common/logger/index.js.map +0 -1
  119. package/dist/common/types/cli.d.ts +0 -265
  120. package/dist/common/types/cli.d.ts.map +0 -1
  121. package/dist/common/types/cli.js +0 -5
  122. package/dist/common/types/cli.js.map +0 -1
  123. package/dist/common/types/core.d.ts +0 -270
  124. package/dist/common/types/core.d.ts.map +0 -1
  125. package/dist/common/types/core.js +0 -32
  126. package/dist/common/types/core.js.map +0 -1
  127. package/dist/common/types/index.d.ts +0 -8
  128. package/dist/common/types/index.d.ts.map +0 -1
  129. package/dist/common/types/index.js +0 -8
  130. package/dist/common/types/index.js.map +0 -1
  131. package/dist/common/utils/VersionChecker.d.ts +0 -54
  132. package/dist/common/utils/VersionChecker.d.ts.map +0 -1
  133. package/dist/common/utils/VersionChecker.js +0 -180
  134. package/dist/common/utils/VersionChecker.js.map +0 -1
  135. package/dist/common/utils/async.d.ts +0 -74
  136. package/dist/common/utils/async.d.ts.map +0 -1
  137. package/dist/common/utils/async.js +0 -228
  138. package/dist/common/utils/async.js.map +0 -1
  139. package/dist/common/utils/format.d.ts +0 -32
  140. package/dist/common/utils/format.d.ts.map +0 -1
  141. package/dist/common/utils/format.js +0 -121
  142. package/dist/common/utils/format.js.map +0 -1
  143. package/dist/common/utils/git.d.ts +0 -44
  144. package/dist/common/utils/git.d.ts.map +0 -1
  145. package/dist/common/utils/git.js +0 -147
  146. package/dist/common/utils/git.js.map +0 -1
  147. package/dist/common/utils/index.d.ts +0 -12
  148. package/dist/common/utils/index.d.ts.map +0 -1
  149. package/dist/common/utils/index.js +0 -12
  150. package/dist/common/utils/index.js.map +0 -1
  151. package/dist/common/utils/string.d.ts +0 -56
  152. package/dist/common/utils/string.d.ts.map +0 -1
  153. package/dist/common/utils/string.js +0 -134
  154. package/dist/common/utils/string.js.map +0 -1
  155. package/dist/common/utils/validation.d.ts +0 -88
  156. package/dist/common/utils/validation.d.ts.map +0 -1
  157. package/dist/common/utils/validation.js +0 -308
  158. package/dist/common/utils/validation.js.map +0 -1
  159. package/dist/domain/entities/Catalog.d.ts +0 -117
  160. package/dist/domain/entities/Catalog.d.ts.map +0 -1
  161. package/dist/domain/entities/Catalog.js +0 -240
  162. package/dist/domain/entities/Catalog.js.map +0 -1
  163. package/dist/domain/entities/Package.d.ts +0 -143
  164. package/dist/domain/entities/Package.d.ts.map +0 -1
  165. package/dist/domain/entities/Package.js +0 -272
  166. package/dist/domain/entities/Package.js.map +0 -1
  167. package/dist/domain/entities/Workspace.d.ts +0 -95
  168. package/dist/domain/entities/Workspace.d.ts.map +0 -1
  169. package/dist/domain/entities/Workspace.js +0 -173
  170. package/dist/domain/entities/Workspace.js.map +0 -1
  171. package/dist/domain/repositories/WorkspaceRepository.d.ts +0 -41
  172. package/dist/domain/repositories/WorkspaceRepository.d.ts.map +0 -1
  173. package/dist/domain/repositories/WorkspaceRepository.js +0 -8
  174. package/dist/domain/repositories/WorkspaceRepository.js.map +0 -1
  175. package/dist/domain/value-objects/CatalogCollection.d.ts +0 -106
  176. package/dist/domain/value-objects/CatalogCollection.d.ts.map +0 -1
  177. package/dist/domain/value-objects/CatalogCollection.js +0 -230
  178. package/dist/domain/value-objects/CatalogCollection.js.map +0 -1
  179. package/dist/domain/value-objects/PackageCollection.d.ts +0 -122
  180. package/dist/domain/value-objects/PackageCollection.d.ts.map +0 -1
  181. package/dist/domain/value-objects/PackageCollection.js +0 -263
  182. package/dist/domain/value-objects/PackageCollection.js.map +0 -1
  183. package/dist/domain/value-objects/Version.d.ts +0 -141
  184. package/dist/domain/value-objects/Version.d.ts.map +0 -1
  185. package/dist/domain/value-objects/Version.js +0 -268
  186. package/dist/domain/value-objects/Version.js.map +0 -1
  187. package/dist/domain/value-objects/WorkspaceConfig.d.ts +0 -144
  188. package/dist/domain/value-objects/WorkspaceConfig.d.ts.map +0 -1
  189. package/dist/domain/value-objects/WorkspaceConfig.js +0 -357
  190. package/dist/domain/value-objects/WorkspaceConfig.js.map +0 -1
  191. package/dist/domain/value-objects/WorkspaceId.d.ts +0 -51
  192. package/dist/domain/value-objects/WorkspaceId.d.ts.map +0 -1
  193. package/dist/domain/value-objects/WorkspaceId.js +0 -104
  194. package/dist/domain/value-objects/WorkspaceId.js.map +0 -1
  195. package/dist/domain/value-objects/WorkspacePath.d.ts +0 -75
  196. package/dist/domain/value-objects/WorkspacePath.d.ts.map +0 -1
  197. package/dist/domain/value-objects/WorkspacePath.js +0 -128
  198. package/dist/domain/value-objects/WorkspacePath.js.map +0 -1
  199. package/dist/index.d.ts +0 -25
  200. package/dist/index.d.ts.map +0 -1
  201. package/dist/infrastructure/cache/Cache.d.ts +0 -161
  202. package/dist/infrastructure/cache/Cache.d.ts.map +0 -1
  203. package/dist/infrastructure/cache/Cache.js +0 -398
  204. package/dist/infrastructure/cache/Cache.js.map +0 -1
  205. package/dist/infrastructure/cache/index.d.ts +0 -6
  206. package/dist/infrastructure/cache/index.d.ts.map +0 -1
  207. package/dist/infrastructure/cache/index.js +0 -6
  208. package/dist/infrastructure/cache/index.js.map +0 -1
  209. package/dist/infrastructure/external-services/NpmRegistryService.d.ts +0 -145
  210. package/dist/infrastructure/external-services/NpmRegistryService.d.ts.map +0 -1
  211. package/dist/infrastructure/external-services/NpmRegistryService.js +0 -466
  212. package/dist/infrastructure/external-services/NpmRegistryService.js.map +0 -1
  213. package/dist/infrastructure/file-system/FileSystemService.d.ts +0 -120
  214. package/dist/infrastructure/file-system/FileSystemService.d.ts.map +0 -1
  215. package/dist/infrastructure/file-system/FileSystemService.js +0 -663
  216. package/dist/infrastructure/file-system/FileSystemService.js.map +0 -1
  217. package/dist/infrastructure/repositories/FileWorkspaceRepository.d.ts +0 -57
  218. package/dist/infrastructure/repositories/FileWorkspaceRepository.d.ts.map +0 -1
  219. package/dist/infrastructure/repositories/FileWorkspaceRepository.js +0 -179
  220. package/dist/infrastructure/repositories/FileWorkspaceRepository.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
+ }