@wundr.io/cli 1.0.0 → 1.0.1

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