@wundr.io/cli 1.0.0 → 1.0.2-dev.20260530174250.ef0ec927

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 (230) hide show
  1. package/README.md +696 -280
  2. package/bin/wundr.js +13 -5
  3. package/package.json +30 -9
  4. package/src/ai/ai-service.ts +6 -4
  5. package/src/ai/claude-client.ts +6 -2
  6. package/src/ai/conversation-manager.ts +12 -5
  7. package/src/cli.ts +42 -13
  8. package/src/commands/ai.ts +340 -64
  9. package/src/commands/alignment.ts +1212 -0
  10. package/src/commands/analyze-optimized.ts +371 -33
  11. package/src/commands/analyze.ts +8 -6
  12. package/src/commands/batch.ts +166 -26
  13. package/src/commands/chat.ts +20 -10
  14. package/src/commands/claude-init.ts +31 -27
  15. package/src/commands/claude-setup.ts +761 -81
  16. package/src/commands/computer-setup.ts +524 -12
  17. package/src/commands/create-command.ts +3 -3
  18. package/src/commands/create.ts +9 -6
  19. package/src/commands/dashboard.ts +11 -6
  20. package/src/commands/govern.ts +11 -6
  21. package/src/commands/governance.ts +1005 -0
  22. package/src/commands/guardian.ts +887 -0
  23. package/src/commands/init.ts +104 -11
  24. package/src/commands/orchestrator.ts +789 -0
  25. package/src/commands/performance-optimizer.ts +15 -10
  26. package/src/commands/plugins.ts +8 -5
  27. package/src/commands/project-update.ts +1156 -0
  28. package/src/commands/rag.ts +1011 -0
  29. package/src/commands/session.ts +631 -0
  30. package/src/commands/setup.ts +42 -344
  31. package/src/commands/test-init.ts +3 -2
  32. package/src/commands/test.ts +3 -2
  33. package/src/commands/watch.ts +21 -11
  34. package/src/commands/worktree.ts +1057 -0
  35. package/src/context/context-manager.ts +5 -2
  36. package/src/context/session-manager.ts +18 -7
  37. package/src/framework/command-interface.ts +520 -0
  38. package/src/framework/command-registry.ts +942 -0
  39. package/src/framework/completion-exporter.ts +383 -0
  40. package/src/framework/debug-logger.ts +519 -0
  41. package/src/framework/error-handler.ts +867 -0
  42. package/src/framework/help-generator.ts +540 -0
  43. package/src/framework/index.ts +169 -0
  44. package/src/framework/interactive-repl.ts +703 -0
  45. package/src/framework/output-formatter.ts +834 -0
  46. package/src/framework/progress-manager.ts +539 -0
  47. package/src/index.ts +3 -2
  48. package/src/interactive/interactive-mode.ts +14 -7
  49. package/src/lib/conflict-resolution.ts +818 -0
  50. package/src/lib/merge-strategy.ts +550 -0
  51. package/src/lib/safety-mechanisms.ts +451 -0
  52. package/src/lib/state-detection.ts +1030 -0
  53. package/src/nlp/command-mapper.ts +8 -3
  54. package/src/nlp/command-parser.ts +5 -2
  55. package/src/nlp/intent-parser.ts +23 -9
  56. package/src/plugins/plugin-manager.ts +50 -24
  57. package/src/tests/computer-setup-integration.test.ts +470 -0
  58. package/src/types/index.ts +1 -1
  59. package/src/types/modules.d.ts +425 -1
  60. package/src/utils/backup-rollback-manager.ts +366 -0
  61. package/src/utils/claude-config-installer.ts +823 -0
  62. package/src/utils/config-manager.ts +9 -6
  63. package/src/utils/error-handler.ts +3 -1
  64. package/src/utils/logger.ts +35 -12
  65. package/templates/batch/ci-cd.yaml +7 -7
  66. package/test-suites/api/health.spec.ts +20 -23
  67. package/test-suites/helpers/test-config.ts +14 -13
  68. package/test-suites/ui/accessibility.spec.ts +27 -22
  69. package/test-suites/ui/smoke.spec.ts +26 -21
  70. package/dist/ai/ai-service.d.ts +0 -152
  71. package/dist/ai/ai-service.d.ts.map +0 -1
  72. package/dist/ai/ai-service.js +0 -430
  73. package/dist/ai/ai-service.js.map +0 -1
  74. package/dist/ai/claude-client.d.ts +0 -130
  75. package/dist/ai/claude-client.d.ts.map +0 -1
  76. package/dist/ai/claude-client.js +0 -339
  77. package/dist/ai/claude-client.js.map +0 -1
  78. package/dist/ai/conversation-manager.d.ts +0 -164
  79. package/dist/ai/conversation-manager.d.ts.map +0 -1
  80. package/dist/ai/conversation-manager.js +0 -612
  81. package/dist/ai/conversation-manager.js.map +0 -1
  82. package/dist/ai/index.d.ts +0 -5
  83. package/dist/ai/index.d.ts.map +0 -1
  84. package/dist/ai/index.js +0 -8
  85. package/dist/ai/index.js.map +0 -1
  86. package/dist/cli.d.ts +0 -36
  87. package/dist/cli.d.ts.map +0 -1
  88. package/dist/cli.js +0 -173
  89. package/dist/cli.js.map +0 -1
  90. package/dist/commands/ai.d.ts +0 -89
  91. package/dist/commands/ai.d.ts.map +0 -1
  92. package/dist/commands/ai.js +0 -735
  93. package/dist/commands/ai.js.map +0 -1
  94. package/dist/commands/analyze-optimized.d.ts +0 -14
  95. package/dist/commands/analyze-optimized.d.ts.map +0 -1
  96. package/dist/commands/analyze-optimized.js +0 -437
  97. package/dist/commands/analyze-optimized.js.map +0 -1
  98. package/dist/commands/analyze.d.ts +0 -65
  99. package/dist/commands/analyze.d.ts.map +0 -1
  100. package/dist/commands/analyze.js +0 -435
  101. package/dist/commands/analyze.js.map +0 -1
  102. package/dist/commands/batch.d.ts +0 -71
  103. package/dist/commands/batch.d.ts.map +0 -1
  104. package/dist/commands/batch.js +0 -738
  105. package/dist/commands/batch.js.map +0 -1
  106. package/dist/commands/chat.d.ts +0 -71
  107. package/dist/commands/chat.d.ts.map +0 -1
  108. package/dist/commands/chat.js +0 -674
  109. package/dist/commands/chat.js.map +0 -1
  110. package/dist/commands/claude-init.d.ts +0 -28
  111. package/dist/commands/claude-init.d.ts.map +0 -1
  112. package/dist/commands/claude-init.js +0 -587
  113. package/dist/commands/claude-init.js.map +0 -1
  114. package/dist/commands/claude-setup.d.ts +0 -32
  115. package/dist/commands/claude-setup.d.ts.map +0 -1
  116. package/dist/commands/claude-setup.js +0 -570
  117. package/dist/commands/claude-setup.js.map +0 -1
  118. package/dist/commands/computer-setup-commands.d.ts +0 -39
  119. package/dist/commands/computer-setup-commands.d.ts.map +0 -1
  120. package/dist/commands/computer-setup-commands.js +0 -563
  121. package/dist/commands/computer-setup-commands.js.map +0 -1
  122. package/dist/commands/computer-setup.d.ts +0 -7
  123. package/dist/commands/computer-setup.d.ts.map +0 -1
  124. package/dist/commands/computer-setup.js +0 -481
  125. package/dist/commands/computer-setup.js.map +0 -1
  126. package/dist/commands/create-command.d.ts +0 -7
  127. package/dist/commands/create-command.d.ts.map +0 -1
  128. package/dist/commands/create-command.js +0 -158
  129. package/dist/commands/create-command.js.map +0 -1
  130. package/dist/commands/create.d.ts +0 -74
  131. package/dist/commands/create.d.ts.map +0 -1
  132. package/dist/commands/create.js +0 -556
  133. package/dist/commands/create.js.map +0 -1
  134. package/dist/commands/dashboard.d.ts +0 -91
  135. package/dist/commands/dashboard.d.ts.map +0 -1
  136. package/dist/commands/dashboard.js +0 -537
  137. package/dist/commands/dashboard.js.map +0 -1
  138. package/dist/commands/govern.d.ts +0 -70
  139. package/dist/commands/govern.d.ts.map +0 -1
  140. package/dist/commands/govern.js +0 -480
  141. package/dist/commands/govern.js.map +0 -1
  142. package/dist/commands/init.d.ts +0 -55
  143. package/dist/commands/init.d.ts.map +0 -1
  144. package/dist/commands/init.js +0 -584
  145. package/dist/commands/init.js.map +0 -1
  146. package/dist/commands/performance-optimizer.d.ts +0 -30
  147. package/dist/commands/performance-optimizer.d.ts.map +0 -1
  148. package/dist/commands/performance-optimizer.js +0 -649
  149. package/dist/commands/performance-optimizer.js.map +0 -1
  150. package/dist/commands/plugins.d.ts +0 -87
  151. package/dist/commands/plugins.d.ts.map +0 -1
  152. package/dist/commands/plugins.js +0 -685
  153. package/dist/commands/plugins.js.map +0 -1
  154. package/dist/commands/setup.d.ts +0 -29
  155. package/dist/commands/setup.d.ts.map +0 -1
  156. package/dist/commands/setup.js +0 -399
  157. package/dist/commands/setup.js.map +0 -1
  158. package/dist/commands/test-init.d.ts +0 -9
  159. package/dist/commands/test-init.d.ts.map +0 -1
  160. package/dist/commands/test-init.js +0 -222
  161. package/dist/commands/test-init.js.map +0 -1
  162. package/dist/commands/test.d.ts +0 -25
  163. package/dist/commands/test.d.ts.map +0 -1
  164. package/dist/commands/test.js +0 -217
  165. package/dist/commands/test.js.map +0 -1
  166. package/dist/commands/watch.d.ts +0 -76
  167. package/dist/commands/watch.d.ts.map +0 -1
  168. package/dist/commands/watch.js +0 -610
  169. package/dist/commands/watch.js.map +0 -1
  170. package/dist/context/context-manager.d.ts +0 -155
  171. package/dist/context/context-manager.d.ts.map +0 -1
  172. package/dist/context/context-manager.js +0 -383
  173. package/dist/context/context-manager.js.map +0 -1
  174. package/dist/context/index.d.ts +0 -3
  175. package/dist/context/index.d.ts.map +0 -1
  176. package/dist/context/index.js +0 -6
  177. package/dist/context/index.js.map +0 -1
  178. package/dist/context/session-manager.d.ts +0 -207
  179. package/dist/context/session-manager.d.ts.map +0 -1
  180. package/dist/context/session-manager.js +0 -682
  181. package/dist/context/session-manager.js.map +0 -1
  182. package/dist/index.d.ts +0 -8
  183. package/dist/index.d.ts.map +0 -1
  184. package/dist/index.js +0 -51
  185. package/dist/index.js.map +0 -1
  186. package/dist/interactive/interactive-mode.d.ts +0 -76
  187. package/dist/interactive/interactive-mode.d.ts.map +0 -1
  188. package/dist/interactive/interactive-mode.js +0 -730
  189. package/dist/interactive/interactive-mode.js.map +0 -1
  190. package/dist/nlp/command-mapper.d.ts +0 -174
  191. package/dist/nlp/command-mapper.d.ts.map +0 -1
  192. package/dist/nlp/command-mapper.js +0 -623
  193. package/dist/nlp/command-mapper.js.map +0 -1
  194. package/dist/nlp/command-parser.d.ts +0 -106
  195. package/dist/nlp/command-parser.d.ts.map +0 -1
  196. package/dist/nlp/command-parser.js +0 -416
  197. package/dist/nlp/command-parser.js.map +0 -1
  198. package/dist/nlp/index.d.ts +0 -5
  199. package/dist/nlp/index.d.ts.map +0 -1
  200. package/dist/nlp/index.js +0 -8
  201. package/dist/nlp/index.js.map +0 -1
  202. package/dist/nlp/intent-classifier.d.ts +0 -59
  203. package/dist/nlp/intent-classifier.d.ts.map +0 -1
  204. package/dist/nlp/intent-classifier.js +0 -384
  205. package/dist/nlp/intent-classifier.js.map +0 -1
  206. package/dist/nlp/intent-parser.d.ts +0 -152
  207. package/dist/nlp/intent-parser.d.ts.map +0 -1
  208. package/dist/nlp/intent-parser.js +0 -739
  209. package/dist/nlp/intent-parser.js.map +0 -1
  210. package/dist/plugins/plugin-manager.d.ts +0 -120
  211. package/dist/plugins/plugin-manager.d.ts.map +0 -1
  212. package/dist/plugins/plugin-manager.js +0 -595
  213. package/dist/plugins/plugin-manager.js.map +0 -1
  214. package/dist/types/index.d.ts +0 -224
  215. package/dist/types/index.d.ts.map +0 -1
  216. package/dist/types/index.js +0 -3
  217. package/dist/types/index.js.map +0 -1
  218. package/dist/utils/config-manager.d.ts +0 -73
  219. package/dist/utils/config-manager.d.ts.map +0 -1
  220. package/dist/utils/config-manager.js +0 -339
  221. package/dist/utils/config-manager.js.map +0 -1
  222. package/dist/utils/error-handler.d.ts +0 -46
  223. package/dist/utils/error-handler.d.ts.map +0 -1
  224. package/dist/utils/error-handler.js +0 -169
  225. package/dist/utils/error-handler.js.map +0 -1
  226. package/dist/utils/logger.d.ts +0 -25
  227. package/dist/utils/logger.d.ts.map +0 -1
  228. package/dist/utils/logger.js +0 -94
  229. package/dist/utils/logger.js.map +0 -1
  230. package/src/commands/computer-setup-commands.ts +0 -709
@@ -0,0 +1,366 @@
1
+ /**
2
+ * Backup and Rollback Manager
3
+ * Handles backup creation and restoration for configuration files
4
+ */
5
+
6
+ import { existsSync } from 'fs';
7
+ import * as fs from 'fs/promises';
8
+ import * as path from 'path';
9
+
10
+ import chalk from 'chalk';
11
+
12
+ import { logger } from './logger';
13
+
14
+ export interface BackupMetadata {
15
+ timestamp: string;
16
+ backupId: string;
17
+ files: BackupFile[];
18
+ reason: string;
19
+ success: boolean;
20
+ }
21
+
22
+ export interface BackupFile {
23
+ originalPath: string;
24
+ backupPath: string;
25
+ size: number;
26
+ checksum?: string;
27
+ }
28
+
29
+ export interface RollbackOptions {
30
+ backupId?: string;
31
+ dryRun?: boolean;
32
+ verbose?: boolean;
33
+ }
34
+
35
+ export class BackupRollbackManager {
36
+ private backupDir: string;
37
+ private metadataFile: string;
38
+ private homeDir: string;
39
+
40
+ constructor(backupDir?: string) {
41
+ this.homeDir = process.env.HOME || process.env.USERPROFILE || '';
42
+ this.backupDir = backupDir || path.join(this.homeDir, '.wundr', 'backups');
43
+ this.metadataFile = path.join(this.backupDir, 'metadata.json');
44
+ }
45
+
46
+ /**
47
+ * Initialize backup directory structure
48
+ */
49
+ async initialize(): Promise<void> {
50
+ try {
51
+ await fs.mkdir(this.backupDir, { recursive: true });
52
+
53
+ if (!existsSync(this.metadataFile)) {
54
+ await fs.writeFile(this.metadataFile, JSON.stringify([], null, 2));
55
+ }
56
+
57
+ logger.info('Backup manager initialized', { backupDir: this.backupDir });
58
+ } catch (error) {
59
+ logger.error('Failed to initialize backup manager', error);
60
+ throw error;
61
+ }
62
+ }
63
+
64
+ /**
65
+ * Create backup of specified files
66
+ */
67
+ async createBackup(
68
+ files: string[],
69
+ reason: string = 'Manual backup'
70
+ ): Promise<BackupMetadata> {
71
+ const backupId = this.generateBackupId();
72
+ const timestamp = new Date().toISOString();
73
+ const backupPath = path.join(this.backupDir, backupId);
74
+
75
+ logger.info('Creating backup', { backupId, files: files.length, reason });
76
+
77
+ try {
78
+ await fs.mkdir(backupPath, { recursive: true });
79
+
80
+ const backupFiles: BackupFile[] = [];
81
+
82
+ for (const filePath of files) {
83
+ const expandedPath = this.expandPath(filePath);
84
+
85
+ if (!existsSync(expandedPath)) {
86
+ logger.warn('File not found, skipping', { file: expandedPath });
87
+ continue;
88
+ }
89
+
90
+ const stats = await fs.stat(expandedPath);
91
+ const relativePath = this.getRelativePath(expandedPath);
92
+ const backupFilePath = path.join(backupPath, relativePath);
93
+
94
+ // Create directory structure
95
+ await fs.mkdir(path.dirname(backupFilePath), { recursive: true });
96
+
97
+ // Copy file
98
+ await fs.copyFile(expandedPath, backupFilePath);
99
+
100
+ backupFiles.push({
101
+ originalPath: expandedPath,
102
+ backupPath: backupFilePath,
103
+ size: stats.size,
104
+ });
105
+
106
+ logger.info('Backed up file', {
107
+ original: expandedPath,
108
+ backup: backupFilePath,
109
+ });
110
+ }
111
+
112
+ const metadata: BackupMetadata = {
113
+ timestamp,
114
+ backupId,
115
+ files: backupFiles,
116
+ reason,
117
+ success: true,
118
+ };
119
+
120
+ await this.saveMetadata(metadata);
121
+
122
+ logger.info('Backup created successfully', {
123
+ backupId,
124
+ filesBackedUp: backupFiles.length,
125
+ });
126
+
127
+ return metadata;
128
+ } catch (error) {
129
+ logger.error('Backup failed', error);
130
+
131
+ const metadata: BackupMetadata = {
132
+ timestamp,
133
+ backupId,
134
+ files: [],
135
+ reason,
136
+ success: false,
137
+ };
138
+
139
+ await this.saveMetadata(metadata);
140
+ throw error;
141
+ }
142
+ }
143
+
144
+ /**
145
+ * Restore from backup
146
+ */
147
+ async rollback(options: RollbackOptions = {}): Promise<boolean> {
148
+ const { backupId, dryRun = false, verbose = false } = options;
149
+
150
+ try {
151
+ const metadata = backupId
152
+ ? await this.getBackupMetadata(backupId)
153
+ : await this.getLatestBackup();
154
+
155
+ if (!metadata) {
156
+ logger.error('No backup found to restore');
157
+ return false;
158
+ }
159
+
160
+ logger.info('Rolling back', {
161
+ backupId: metadata.backupId,
162
+ dryRun,
163
+ files: metadata.files.length,
164
+ });
165
+
166
+ if (dryRun) {
167
+ console.log(chalk.yellow('\nšŸ” DRY RUN - No files will be modified\n'));
168
+ console.log(chalk.cyan('Files that would be restored:'));
169
+ metadata.files.forEach(file => {
170
+ console.log(chalk.white(` ${file.originalPath}`));
171
+ if (verbose) {
172
+ console.log(chalk.gray(` ← ${file.backupPath}`));
173
+ }
174
+ });
175
+ return true;
176
+ }
177
+
178
+ const restoredFiles: string[] = [];
179
+ const failedFiles: string[] = [];
180
+
181
+ for (const file of metadata.files) {
182
+ try {
183
+ // Create directory structure
184
+ await fs.mkdir(path.dirname(file.originalPath), {
185
+ recursive: true,
186
+ });
187
+
188
+ // Restore file
189
+ await fs.copyFile(file.backupPath, file.originalPath);
190
+ restoredFiles.push(file.originalPath);
191
+
192
+ logger.info('Restored file', { file: file.originalPath });
193
+ } catch (error) {
194
+ logger.error('Failed to restore file', {
195
+ file: file.originalPath,
196
+ error,
197
+ });
198
+ failedFiles.push(file.originalPath);
199
+ }
200
+ }
201
+
202
+ console.log(chalk.green(`\nāœ… Restored ${restoredFiles.length} files`));
203
+
204
+ if (failedFiles.length > 0) {
205
+ console.log(
206
+ chalk.red(`āŒ Failed to restore ${failedFiles.length} files`)
207
+ );
208
+ failedFiles.forEach(file => {
209
+ console.log(chalk.red(` - ${file}`));
210
+ });
211
+ return false;
212
+ }
213
+
214
+ return true;
215
+ } catch (error) {
216
+ logger.error('Rollback failed', error);
217
+ return false;
218
+ }
219
+ }
220
+
221
+ /**
222
+ * List all backups
223
+ */
224
+ async listBackups(): Promise<BackupMetadata[]> {
225
+ try {
226
+ const content = await fs.readFile(this.metadataFile, 'utf-8');
227
+ const backups = JSON.parse(content) as BackupMetadata[];
228
+ return backups.sort(
229
+ (a, b) =>
230
+ new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime()
231
+ );
232
+ } catch (error) {
233
+ logger.error('Failed to list backups', error);
234
+ return [];
235
+ }
236
+ }
237
+
238
+ /**
239
+ * Get specific backup metadata
240
+ */
241
+ async getBackupMetadata(backupId: string): Promise<BackupMetadata | null> {
242
+ const backups = await this.listBackups();
243
+ return backups.find(b => b.backupId === backupId) || null;
244
+ }
245
+
246
+ /**
247
+ * Get latest successful backup
248
+ */
249
+ async getLatestBackup(): Promise<BackupMetadata | null> {
250
+ const backups = await this.listBackups();
251
+ return backups.find(b => b.success) || null;
252
+ }
253
+
254
+ /**
255
+ * Delete old backups
256
+ */
257
+ async cleanupOldBackups(retainCount: number = 5): Promise<void> {
258
+ const backups = await this.listBackups();
259
+
260
+ if (backups.length <= retainCount) {
261
+ logger.info('No backups to clean up', {
262
+ current: backups.length,
263
+ retain: retainCount,
264
+ });
265
+ return;
266
+ }
267
+
268
+ const toDelete = backups.slice(retainCount);
269
+
270
+ logger.info('Cleaning up old backups', { count: toDelete.length });
271
+
272
+ for (const backup of toDelete) {
273
+ try {
274
+ const backupPath = path.join(this.backupDir, backup.backupId);
275
+ await fs.rm(backupPath, { recursive: true, force: true });
276
+ logger.info('Deleted backup', { backupId: backup.backupId });
277
+ } catch (error) {
278
+ logger.error('Failed to delete backup', {
279
+ backupId: backup.backupId,
280
+ error,
281
+ });
282
+ }
283
+ }
284
+
285
+ // Update metadata
286
+ const remaining = backups.slice(0, retainCount);
287
+ await fs.writeFile(this.metadataFile, JSON.stringify(remaining, null, 2));
288
+ }
289
+
290
+ /**
291
+ * Verify backup integrity
292
+ */
293
+ async verifyBackup(backupId: string): Promise<boolean> {
294
+ const metadata = await this.getBackupMetadata(backupId);
295
+
296
+ if (!metadata) {
297
+ logger.error('Backup not found', { backupId });
298
+ return false;
299
+ }
300
+
301
+ let valid = true;
302
+
303
+ for (const file of metadata.files) {
304
+ if (!existsSync(file.backupPath)) {
305
+ logger.error('Backup file missing', { file: file.backupPath });
306
+ valid = false;
307
+ }
308
+ }
309
+
310
+ return valid;
311
+ }
312
+
313
+ /**
314
+ * Private helper methods
315
+ */
316
+
317
+ private generateBackupId(): string {
318
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
319
+ return `backup-${timestamp}`;
320
+ }
321
+
322
+ private expandPath(filePath: string): string {
323
+ if (filePath.startsWith('~/')) {
324
+ return path.join(this.homeDir, filePath.slice(2));
325
+ }
326
+ return filePath;
327
+ }
328
+
329
+ private getRelativePath(absolutePath: string): string {
330
+ // Create relative path from home directory
331
+ if (absolutePath.startsWith(this.homeDir)) {
332
+ return path.relative(this.homeDir, absolutePath);
333
+ }
334
+ // For absolute paths outside home, use full path structure
335
+ return absolutePath.replace(/^\//, '');
336
+ }
337
+
338
+ private async saveMetadata(metadata: BackupMetadata): Promise<void> {
339
+ const backups = await this.listBackups();
340
+ backups.unshift(metadata);
341
+ await fs.writeFile(this.metadataFile, JSON.stringify(backups, null, 2));
342
+ }
343
+
344
+ /**
345
+ * Display backup information
346
+ */
347
+ displayBackupInfo(metadata: BackupMetadata): void {
348
+ console.log(chalk.cyan('\nšŸ“¦ Backup Information\n'));
349
+ console.log(chalk.white('Backup ID:'), chalk.green(metadata.backupId));
350
+ console.log(chalk.white('Timestamp:'), chalk.gray(metadata.timestamp));
351
+ console.log(chalk.white('Reason:'), chalk.gray(metadata.reason));
352
+ console.log(
353
+ chalk.white('Status:'),
354
+ metadata.success ? chalk.green('Success') : chalk.red('Failed')
355
+ );
356
+ console.log(chalk.white('Files:'), chalk.cyan(metadata.files.length));
357
+
358
+ if (metadata.files.length > 0) {
359
+ console.log(chalk.cyan('\nBacked up files:'));
360
+ metadata.files.forEach(file => {
361
+ const size = (file.size / 1024).toFixed(2);
362
+ console.log(chalk.gray(` • ${file.originalPath} (${size} KB)`));
363
+ });
364
+ }
365
+ }
366
+ }