@wundr.io/cli 1.0.0

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 (213) hide show
  1. package/README.md +551 -0
  2. package/bin/wundr.js +39 -0
  3. package/dist/ai/ai-service.d.ts +152 -0
  4. package/dist/ai/ai-service.d.ts.map +1 -0
  5. package/dist/ai/ai-service.js +430 -0
  6. package/dist/ai/ai-service.js.map +1 -0
  7. package/dist/ai/claude-client.d.ts +130 -0
  8. package/dist/ai/claude-client.d.ts.map +1 -0
  9. package/dist/ai/claude-client.js +339 -0
  10. package/dist/ai/claude-client.js.map +1 -0
  11. package/dist/ai/conversation-manager.d.ts +164 -0
  12. package/dist/ai/conversation-manager.d.ts.map +1 -0
  13. package/dist/ai/conversation-manager.js +612 -0
  14. package/dist/ai/conversation-manager.js.map +1 -0
  15. package/dist/ai/index.d.ts +5 -0
  16. package/dist/ai/index.d.ts.map +1 -0
  17. package/dist/ai/index.js +8 -0
  18. package/dist/ai/index.js.map +1 -0
  19. package/dist/cli.d.ts +36 -0
  20. package/dist/cli.d.ts.map +1 -0
  21. package/dist/cli.js +173 -0
  22. package/dist/cli.js.map +1 -0
  23. package/dist/commands/ai.d.ts +89 -0
  24. package/dist/commands/ai.d.ts.map +1 -0
  25. package/dist/commands/ai.js +735 -0
  26. package/dist/commands/ai.js.map +1 -0
  27. package/dist/commands/analyze-optimized.d.ts +14 -0
  28. package/dist/commands/analyze-optimized.d.ts.map +1 -0
  29. package/dist/commands/analyze-optimized.js +437 -0
  30. package/dist/commands/analyze-optimized.js.map +1 -0
  31. package/dist/commands/analyze.d.ts +65 -0
  32. package/dist/commands/analyze.d.ts.map +1 -0
  33. package/dist/commands/analyze.js +435 -0
  34. package/dist/commands/analyze.js.map +1 -0
  35. package/dist/commands/batch.d.ts +71 -0
  36. package/dist/commands/batch.d.ts.map +1 -0
  37. package/dist/commands/batch.js +738 -0
  38. package/dist/commands/batch.js.map +1 -0
  39. package/dist/commands/chat.d.ts +71 -0
  40. package/dist/commands/chat.d.ts.map +1 -0
  41. package/dist/commands/chat.js +674 -0
  42. package/dist/commands/chat.js.map +1 -0
  43. package/dist/commands/claude-init.d.ts +28 -0
  44. package/dist/commands/claude-init.d.ts.map +1 -0
  45. package/dist/commands/claude-init.js +587 -0
  46. package/dist/commands/claude-init.js.map +1 -0
  47. package/dist/commands/claude-setup.d.ts +32 -0
  48. package/dist/commands/claude-setup.d.ts.map +1 -0
  49. package/dist/commands/claude-setup.js +570 -0
  50. package/dist/commands/claude-setup.js.map +1 -0
  51. package/dist/commands/computer-setup-commands.d.ts +39 -0
  52. package/dist/commands/computer-setup-commands.d.ts.map +1 -0
  53. package/dist/commands/computer-setup-commands.js +563 -0
  54. package/dist/commands/computer-setup-commands.js.map +1 -0
  55. package/dist/commands/computer-setup.d.ts +7 -0
  56. package/dist/commands/computer-setup.d.ts.map +1 -0
  57. package/dist/commands/computer-setup.js +481 -0
  58. package/dist/commands/computer-setup.js.map +1 -0
  59. package/dist/commands/create-command.d.ts +7 -0
  60. package/dist/commands/create-command.d.ts.map +1 -0
  61. package/dist/commands/create-command.js +158 -0
  62. package/dist/commands/create-command.js.map +1 -0
  63. package/dist/commands/create.d.ts +74 -0
  64. package/dist/commands/create.d.ts.map +1 -0
  65. package/dist/commands/create.js +556 -0
  66. package/dist/commands/create.js.map +1 -0
  67. package/dist/commands/dashboard.d.ts +91 -0
  68. package/dist/commands/dashboard.d.ts.map +1 -0
  69. package/dist/commands/dashboard.js +537 -0
  70. package/dist/commands/dashboard.js.map +1 -0
  71. package/dist/commands/govern.d.ts +70 -0
  72. package/dist/commands/govern.d.ts.map +1 -0
  73. package/dist/commands/govern.js +480 -0
  74. package/dist/commands/govern.js.map +1 -0
  75. package/dist/commands/init.d.ts +55 -0
  76. package/dist/commands/init.d.ts.map +1 -0
  77. package/dist/commands/init.js +584 -0
  78. package/dist/commands/init.js.map +1 -0
  79. package/dist/commands/performance-optimizer.d.ts +30 -0
  80. package/dist/commands/performance-optimizer.d.ts.map +1 -0
  81. package/dist/commands/performance-optimizer.js +649 -0
  82. package/dist/commands/performance-optimizer.js.map +1 -0
  83. package/dist/commands/plugins.d.ts +87 -0
  84. package/dist/commands/plugins.d.ts.map +1 -0
  85. package/dist/commands/plugins.js +685 -0
  86. package/dist/commands/plugins.js.map +1 -0
  87. package/dist/commands/setup.d.ts +29 -0
  88. package/dist/commands/setup.d.ts.map +1 -0
  89. package/dist/commands/setup.js +399 -0
  90. package/dist/commands/setup.js.map +1 -0
  91. package/dist/commands/test-init.d.ts +9 -0
  92. package/dist/commands/test-init.d.ts.map +1 -0
  93. package/dist/commands/test-init.js +222 -0
  94. package/dist/commands/test-init.js.map +1 -0
  95. package/dist/commands/test.d.ts +25 -0
  96. package/dist/commands/test.d.ts.map +1 -0
  97. package/dist/commands/test.js +217 -0
  98. package/dist/commands/test.js.map +1 -0
  99. package/dist/commands/watch.d.ts +76 -0
  100. package/dist/commands/watch.d.ts.map +1 -0
  101. package/dist/commands/watch.js +610 -0
  102. package/dist/commands/watch.js.map +1 -0
  103. package/dist/context/context-manager.d.ts +155 -0
  104. package/dist/context/context-manager.d.ts.map +1 -0
  105. package/dist/context/context-manager.js +383 -0
  106. package/dist/context/context-manager.js.map +1 -0
  107. package/dist/context/index.d.ts +3 -0
  108. package/dist/context/index.d.ts.map +1 -0
  109. package/dist/context/index.js +6 -0
  110. package/dist/context/index.js.map +1 -0
  111. package/dist/context/session-manager.d.ts +207 -0
  112. package/dist/context/session-manager.d.ts.map +1 -0
  113. package/dist/context/session-manager.js +682 -0
  114. package/dist/context/session-manager.js.map +1 -0
  115. package/dist/index.d.ts +8 -0
  116. package/dist/index.d.ts.map +1 -0
  117. package/dist/index.js +51 -0
  118. package/dist/index.js.map +1 -0
  119. package/dist/interactive/interactive-mode.d.ts +76 -0
  120. package/dist/interactive/interactive-mode.d.ts.map +1 -0
  121. package/dist/interactive/interactive-mode.js +730 -0
  122. package/dist/interactive/interactive-mode.js.map +1 -0
  123. package/dist/nlp/command-mapper.d.ts +174 -0
  124. package/dist/nlp/command-mapper.d.ts.map +1 -0
  125. package/dist/nlp/command-mapper.js +623 -0
  126. package/dist/nlp/command-mapper.js.map +1 -0
  127. package/dist/nlp/command-parser.d.ts +106 -0
  128. package/dist/nlp/command-parser.d.ts.map +1 -0
  129. package/dist/nlp/command-parser.js +416 -0
  130. package/dist/nlp/command-parser.js.map +1 -0
  131. package/dist/nlp/index.d.ts +5 -0
  132. package/dist/nlp/index.d.ts.map +1 -0
  133. package/dist/nlp/index.js +8 -0
  134. package/dist/nlp/index.js.map +1 -0
  135. package/dist/nlp/intent-classifier.d.ts +59 -0
  136. package/dist/nlp/intent-classifier.d.ts.map +1 -0
  137. package/dist/nlp/intent-classifier.js +384 -0
  138. package/dist/nlp/intent-classifier.js.map +1 -0
  139. package/dist/nlp/intent-parser.d.ts +152 -0
  140. package/dist/nlp/intent-parser.d.ts.map +1 -0
  141. package/dist/nlp/intent-parser.js +739 -0
  142. package/dist/nlp/intent-parser.js.map +1 -0
  143. package/dist/plugins/plugin-manager.d.ts +120 -0
  144. package/dist/plugins/plugin-manager.d.ts.map +1 -0
  145. package/dist/plugins/plugin-manager.js +595 -0
  146. package/dist/plugins/plugin-manager.js.map +1 -0
  147. package/dist/types/index.d.ts +224 -0
  148. package/dist/types/index.d.ts.map +1 -0
  149. package/dist/types/index.js +3 -0
  150. package/dist/types/index.js.map +1 -0
  151. package/dist/utils/config-manager.d.ts +73 -0
  152. package/dist/utils/config-manager.d.ts.map +1 -0
  153. package/dist/utils/config-manager.js +339 -0
  154. package/dist/utils/config-manager.js.map +1 -0
  155. package/dist/utils/error-handler.d.ts +46 -0
  156. package/dist/utils/error-handler.d.ts.map +1 -0
  157. package/dist/utils/error-handler.js +169 -0
  158. package/dist/utils/error-handler.js.map +1 -0
  159. package/dist/utils/logger.d.ts +25 -0
  160. package/dist/utils/logger.d.ts.map +1 -0
  161. package/dist/utils/logger.js +94 -0
  162. package/dist/utils/logger.js.map +1 -0
  163. package/package.json +119 -0
  164. package/src/ai/ai-service.ts +595 -0
  165. package/src/ai/claude-client.ts +490 -0
  166. package/src/ai/conversation-manager.ts +907 -0
  167. package/src/ai/index.ts +8 -0
  168. package/src/cli.ts +202 -0
  169. package/src/commands/ai.ts +995 -0
  170. package/src/commands/analyze-optimized.ts +641 -0
  171. package/src/commands/analyze.ts +576 -0
  172. package/src/commands/batch.ts +935 -0
  173. package/src/commands/chat.ts +876 -0
  174. package/src/commands/claude-init.ts +715 -0
  175. package/src/commands/claude-setup.ts +697 -0
  176. package/src/commands/computer-setup-commands.ts +709 -0
  177. package/src/commands/computer-setup.ts +565 -0
  178. package/src/commands/create-command.ts +175 -0
  179. package/src/commands/create.ts +727 -0
  180. package/src/commands/dashboard.ts +691 -0
  181. package/src/commands/govern.ts +635 -0
  182. package/src/commands/init.ts +677 -0
  183. package/src/commands/performance-optimizer.ts +864 -0
  184. package/src/commands/plugins.ts +848 -0
  185. package/src/commands/setup.ts +508 -0
  186. package/src/commands/test-init.ts +242 -0
  187. package/src/commands/test.ts +264 -0
  188. package/src/commands/watch.ts +755 -0
  189. package/src/context/context-manager.ts +546 -0
  190. package/src/context/index.ts +9 -0
  191. package/src/context/session-manager.ts +1019 -0
  192. package/src/index.ts +64 -0
  193. package/src/interactive/interactive-mode.ts +830 -0
  194. package/src/nlp/command-mapper.ts +885 -0
  195. package/src/nlp/command-parser.ts +564 -0
  196. package/src/nlp/index.ts +4 -0
  197. package/src/nlp/intent-classifier.ts +458 -0
  198. package/src/nlp/intent-parser.ts +1101 -0
  199. package/src/plugins/plugin-manager.ts +744 -0
  200. package/src/types/index.ts +252 -0
  201. package/src/types/modules.d.ts +56 -0
  202. package/src/utils/config-manager.ts +391 -0
  203. package/src/utils/error-handler.ts +192 -0
  204. package/src/utils/logger.ts +104 -0
  205. package/templates/batch/ci-cd.yaml +62 -0
  206. package/templates/component/{{fileName}}.test.tsx +17 -0
  207. package/templates/component/{{fileName}}.tsx +21 -0
  208. package/templates/service/{{fileName}}.ts +98 -0
  209. package/templates/wundr-test.config.js +0 -0
  210. package/test-suites/api/health.spec.ts +134 -0
  211. package/test-suites/helpers/test-config.ts +84 -0
  212. package/test-suites/ui/accessibility.spec.ts +102 -0
  213. package/test-suites/ui/smoke.spec.ts +92 -0
@@ -0,0 +1,691 @@
1
+ import { Command } from 'commander';
2
+ import { spawn } from 'child_process';
3
+ import fs from 'fs-extra';
4
+ import path from 'path';
5
+ import chalk from 'chalk';
6
+ import { ConfigManager } from '../utils/config-manager';
7
+ import { PluginManager } from '../plugins/plugin-manager';
8
+ import { logger } from '../utils/logger';
9
+ import { errorHandler } from '../utils/error-handler';
10
+
11
+ /**
12
+ * Dashboard commands for web interface and visualization
13
+ */
14
+ export class DashboardCommands {
15
+ constructor(
16
+ private program: Command,
17
+ private configManager: ConfigManager,
18
+ private pluginManager: PluginManager
19
+ ) {
20
+ this.registerCommands();
21
+ }
22
+
23
+ private registerCommands(): void {
24
+ const dashboardCmd = this.program
25
+ .command('dashboard')
26
+ .alias('dash')
27
+ .description('dashboard and visualization tools');
28
+
29
+ // Start dashboard
30
+ dashboardCmd
31
+ .command('start')
32
+ .description('start the web dashboard')
33
+ .option('--port <port>', 'dashboard port', '3000')
34
+ .option('--host <host>', 'dashboard host', 'localhost')
35
+ .option('--open', 'open browser automatically')
36
+ .option('--dev', 'start in development mode')
37
+ .action(async options => {
38
+ await this.startDashboard(options);
39
+ });
40
+
41
+ // Stop dashboard
42
+ dashboardCmd
43
+ .command('stop')
44
+ .description('stop the web dashboard')
45
+ .action(async () => {
46
+ await this.stopDashboard();
47
+ });
48
+
49
+ // Dashboard status
50
+ dashboardCmd
51
+ .command('status')
52
+ .description('check dashboard status')
53
+ .action(async () => {
54
+ await this.checkDashboardStatus();
55
+ });
56
+
57
+ // Generate reports
58
+ dashboardCmd
59
+ .command('report <type>')
60
+ .description('generate dashboard reports')
61
+ .option('--output <path>', 'output directory')
62
+ .option('--format <format>', 'report format (html, pdf, json)', 'html')
63
+ .option(
64
+ '--period <period>',
65
+ 'report period (daily, weekly, monthly)',
66
+ 'weekly'
67
+ )
68
+ .action(async (type, options) => {
69
+ await this.generateReport(type, options);
70
+ });
71
+
72
+ // Export data
73
+ dashboardCmd
74
+ .command('export <type>')
75
+ .description('export dashboard data')
76
+ .option('--output <path>', 'output file path')
77
+ .option('--format <format>', 'export format (json, csv, xlsx)', 'json')
78
+ .option('--filter <filter>', 'data filter')
79
+ .action(async (type, options) => {
80
+ await this.exportData(type, options);
81
+ });
82
+
83
+ // Import data
84
+ dashboardCmd
85
+ .command('import <file>')
86
+ .description('import data into dashboard')
87
+ .option('--type <type>', 'data type to import')
88
+ .option('--merge', 'merge with existing data')
89
+ .action(async (file, options) => {
90
+ await this.importData(file, options);
91
+ });
92
+
93
+ // Configure dashboard
94
+ dashboardCmd.command('config').description('configure dashboard settings');
95
+
96
+ dashboardCmd
97
+ .command('config set <key> <value>')
98
+ .description('set dashboard configuration')
99
+ .action(async (key, value) => {
100
+ await this.setDashboardConfig(key, value);
101
+ });
102
+
103
+ dashboardCmd
104
+ .command('config get [key]')
105
+ .description('get dashboard configuration')
106
+ .action(async key => {
107
+ await this.getDashboardConfig(key);
108
+ });
109
+
110
+ // Manage widgets
111
+ dashboardCmd.command('widget').description('manage dashboard widgets');
112
+
113
+ dashboardCmd
114
+ .command('widget add <type>')
115
+ .description('add a new widget')
116
+ .option('--config <config>', 'widget configuration')
117
+ .option('--position <position>', 'widget position')
118
+ .action(async (type, options) => {
119
+ await this.addWidget(type, options);
120
+ });
121
+
122
+ dashboardCmd
123
+ .command('widget remove <id>')
124
+ .description('remove a widget')
125
+ .action(async id => {
126
+ await this.removeWidget(id);
127
+ });
128
+
129
+ dashboardCmd
130
+ .command('widget list')
131
+ .description('list all widgets')
132
+ .action(async () => {
133
+ await this.listWidgets();
134
+ });
135
+
136
+ // Manage themes
137
+ dashboardCmd
138
+ .command('theme <action>')
139
+ .description('manage dashboard themes')
140
+ .option('--name <name>', 'theme name')
141
+ .option('--config <config>', 'theme configuration')
142
+ .action(async (action, options) => {
143
+ await this.manageTheme(action, options);
144
+ });
145
+ }
146
+
147
+ /**
148
+ * Start the web dashboard
149
+ */
150
+ private async startDashboard(options: any): Promise<void> {
151
+ try {
152
+ logger.info('Starting Wundr dashboard...');
153
+
154
+ // Check if dashboard is already running
155
+ const isRunning = await this.isDashboardRunning();
156
+ if (isRunning) {
157
+ logger.warn('Dashboard is already running');
158
+ if (options.open) {
159
+ await this.openBrowser(`http://${options.host}:${options.port}`);
160
+ }
161
+ return;
162
+ }
163
+
164
+ // Ensure dashboard assets are available
165
+ await this.ensureDashboardAssets();
166
+
167
+ // Start the dashboard server
168
+ const dashboardProcess = await this.startDashboardServer(options);
169
+
170
+ // Save process info
171
+ await this.saveDashboardProcess(dashboardProcess, options);
172
+
173
+ logger.success(
174
+ `Dashboard started at http://${options.host}:${options.port}`
175
+ );
176
+
177
+ if (options.open) {
178
+ await this.openBrowser(`http://${options.host}:${options.port}`);
179
+ }
180
+
181
+ // Keep the process alive if not in dev mode
182
+ if (!options.dev) {
183
+ logger.info(
184
+ 'Dashboard is running in the background. Use "wundr dashboard stop" to stop it.'
185
+ );
186
+ }
187
+ } catch (error) {
188
+ throw errorHandler.createError(
189
+ 'WUNDR_DASHBOARD_START_FAILED',
190
+ 'Failed to start dashboard',
191
+ { options },
192
+ true
193
+ );
194
+ }
195
+ }
196
+
197
+ /**
198
+ * Stop the web dashboard
199
+ */
200
+ private async stopDashboard(): Promise<void> {
201
+ try {
202
+ logger.info('Stopping dashboard...');
203
+
204
+ const processInfo = await this.loadDashboardProcess();
205
+ if (!processInfo) {
206
+ logger.warn('No running dashboard found');
207
+ return;
208
+ }
209
+
210
+ // Kill the dashboard process
211
+ process.kill(processInfo.pid, 'SIGTERM');
212
+
213
+ // Clean up process info
214
+ await this.cleanupDashboardProcess();
215
+
216
+ logger.success('Dashboard stopped successfully');
217
+ } catch (error) {
218
+ throw errorHandler.createError(
219
+ 'WUNDR_DASHBOARD_STOP_FAILED',
220
+ 'Failed to stop dashboard',
221
+ {},
222
+ true
223
+ );
224
+ }
225
+ }
226
+
227
+ /**
228
+ * Check dashboard status
229
+ */
230
+ private async checkDashboardStatus(): Promise<void> {
231
+ try {
232
+ const isRunning = await this.isDashboardRunning();
233
+ const processInfo = await this.loadDashboardProcess();
234
+
235
+ if (isRunning && processInfo) {
236
+ logger.success(
237
+ `Dashboard is running on http://${processInfo.host}:${processInfo.port}`
238
+ );
239
+ logger.info(`Process ID: ${processInfo.pid}`);
240
+ logger.info(
241
+ `Started: ${new Date(processInfo.started).toLocaleString()}`
242
+ );
243
+ } else {
244
+ logger.info('Dashboard is not running');
245
+ }
246
+ } catch (error) {
247
+ throw errorHandler.createError(
248
+ 'WUNDR_DASHBOARD_STATUS_FAILED',
249
+ 'Failed to check dashboard status',
250
+ {},
251
+ true
252
+ );
253
+ }
254
+ }
255
+
256
+ /**
257
+ * Generate dashboard reports
258
+ */
259
+ private async generateReport(type: string, options: any): Promise<void> {
260
+ try {
261
+ logger.info(`Generating ${type} report...`);
262
+
263
+ const reportData = await this.collectReportData(type, options.period);
264
+ const report = await this.formatReport(reportData, options.format);
265
+
266
+ const outputPath =
267
+ options.output || this.getDefaultReportPath(type, options.format);
268
+ await this.saveReport(report, outputPath);
269
+
270
+ logger.success(`Report generated: ${outputPath}`);
271
+ } catch (error) {
272
+ throw errorHandler.createError(
273
+ 'WUNDR_DASHBOARD_REPORT_FAILED',
274
+ 'Failed to generate report',
275
+ { type, options },
276
+ true
277
+ );
278
+ }
279
+ }
280
+
281
+ /**
282
+ * Export dashboard data
283
+ */
284
+ private async exportData(type: string, options: any): Promise<void> {
285
+ try {
286
+ logger.info(`Exporting ${type} data...`);
287
+
288
+ const data = await this.collectExportData(type, options.filter);
289
+ const exportedData = await this.formatExportData(data, options.format);
290
+
291
+ const outputPath =
292
+ options.output || this.getDefaultExportPath(type, options.format);
293
+ await this.saveExportData(exportedData, outputPath);
294
+
295
+ logger.success(`Data exported: ${outputPath}`);
296
+ } catch (error) {
297
+ throw errorHandler.createError(
298
+ 'WUNDR_DASHBOARD_EXPORT_FAILED',
299
+ 'Failed to export data',
300
+ { type, options },
301
+ true
302
+ );
303
+ }
304
+ }
305
+
306
+ /**
307
+ * Import data into dashboard
308
+ */
309
+ private async importData(file: string, options: any): Promise<void> {
310
+ try {
311
+ logger.info(`Importing data from ${chalk.cyan(file)}...`);
312
+
313
+ if (!(await fs.pathExists(file))) {
314
+ throw new Error(`File not found: ${file}`);
315
+ }
316
+
317
+ const data = await this.loadImportData(file);
318
+ const validatedData = await this.validateImportData(data, options.type);
319
+
320
+ if (options.merge) {
321
+ await this.mergeImportData(validatedData, options.type);
322
+ } else {
323
+ await this.replaceImportData(validatedData, options.type);
324
+ }
325
+
326
+ logger.success(`Data imported successfully from ${file}`);
327
+ } catch (error) {
328
+ throw errorHandler.createError(
329
+ 'WUNDR_DASHBOARD_IMPORT_FAILED',
330
+ 'Failed to import data',
331
+ { file, options },
332
+ true
333
+ );
334
+ }
335
+ }
336
+
337
+ /**
338
+ * Set dashboard configuration
339
+ */
340
+ private async setDashboardConfig(key: string, value: string): Promise<void> {
341
+ try {
342
+ this.configManager.set(`dashboard.${key}`, value);
343
+ await this.configManager.saveConfig();
344
+ logger.success(`Dashboard configuration updated: ${key} = ${value}`);
345
+ } catch (error) {
346
+ throw errorHandler.createError(
347
+ 'WUNDR_DASHBOARD_CONFIG_SET_FAILED',
348
+ 'Failed to set dashboard configuration',
349
+ { key, value },
350
+ true
351
+ );
352
+ }
353
+ }
354
+
355
+ /**
356
+ * Get dashboard configuration
357
+ */
358
+ private async getDashboardConfig(key?: string): Promise<void> {
359
+ try {
360
+ if (key) {
361
+ const value = this.configManager.get(`dashboard.${key}`);
362
+ console.log(`${key}: ${value}`);
363
+ } else {
364
+ const dashboardConfig = this.configManager.get('dashboard');
365
+ console.log(JSON.stringify(dashboardConfig, null, 2));
366
+ }
367
+ } catch (error) {
368
+ throw errorHandler.createError(
369
+ 'WUNDR_DASHBOARD_CONFIG_GET_FAILED',
370
+ 'Failed to get dashboard configuration',
371
+ { key },
372
+ true
373
+ );
374
+ }
375
+ }
376
+
377
+ /**
378
+ * Add a new widget
379
+ */
380
+ private async addWidget(type: string, options: any): Promise<void> {
381
+ try {
382
+ logger.info(`Adding ${type} widget...`);
383
+
384
+ const widget = {
385
+ id: `widget-${Date.now()}`,
386
+ type,
387
+ config: options.config ? JSON.parse(options.config) : {},
388
+ position: options.position
389
+ ? JSON.parse(options.position)
390
+ : { x: 0, y: 0 },
391
+ created: new Date().toISOString(),
392
+ };
393
+
394
+ await this.saveWidget(widget);
395
+ logger.success(`Widget added: ${widget.id}`);
396
+ } catch (error) {
397
+ throw errorHandler.createError(
398
+ 'WUNDR_DASHBOARD_ADD_WIDGET_FAILED',
399
+ 'Failed to add widget',
400
+ { type, options },
401
+ true
402
+ );
403
+ }
404
+ }
405
+
406
+ /**
407
+ * Remove a widget
408
+ */
409
+ private async removeWidget(id: string): Promise<void> {
410
+ try {
411
+ logger.info(`Removing widget: ${chalk.cyan(id)}`);
412
+
413
+ await this.deleteWidget(id);
414
+ logger.success(`Widget removed: ${id}`);
415
+ } catch (error) {
416
+ throw errorHandler.createError(
417
+ 'WUNDR_DASHBOARD_REMOVE_WIDGET_FAILED',
418
+ 'Failed to remove widget',
419
+ { id },
420
+ true
421
+ );
422
+ }
423
+ }
424
+
425
+ /**
426
+ * List all widgets
427
+ */
428
+ private async listWidgets(): Promise<void> {
429
+ try {
430
+ const widgets = await this.getAllWidgets();
431
+
432
+ if (widgets.length === 0) {
433
+ logger.info('No widgets configured');
434
+ return;
435
+ }
436
+
437
+ logger.info(`Dashboard widgets (${widgets.length}):`);
438
+ console.table(
439
+ widgets.map(widget => ({
440
+ ID: widget.id,
441
+ Type: widget.type,
442
+ Position: `(${widget.position.x}, ${widget.position.y})`,
443
+ Created: new Date(widget.created).toLocaleDateString(),
444
+ }))
445
+ );
446
+ } catch (error) {
447
+ throw errorHandler.createError(
448
+ 'WUNDR_DASHBOARD_LIST_WIDGETS_FAILED',
449
+ 'Failed to list widgets',
450
+ {},
451
+ true
452
+ );
453
+ }
454
+ }
455
+
456
+ /**
457
+ * Manage dashboard themes
458
+ */
459
+ private async manageTheme(action: string, options: any): Promise<void> {
460
+ try {
461
+ switch (action) {
462
+ case 'list':
463
+ await this.listThemes();
464
+ break;
465
+ case 'set':
466
+ await this.setTheme(options.name);
467
+ break;
468
+ case 'create':
469
+ await this.createTheme(options.name, options.config);
470
+ break;
471
+ case 'delete':
472
+ await this.deleteTheme(options.name);
473
+ break;
474
+ default:
475
+ throw new Error(`Unknown theme action: ${action}`);
476
+ }
477
+ } catch (error) {
478
+ throw errorHandler.createError(
479
+ 'WUNDR_DASHBOARD_THEME_FAILED',
480
+ 'Failed to manage theme',
481
+ { action, options },
482
+ true
483
+ );
484
+ }
485
+ }
486
+
487
+ /**
488
+ * Helper methods for dashboard operations
489
+ */
490
+ private async isDashboardRunning(): Promise<boolean> {
491
+ const processInfo = await this.loadDashboardProcess();
492
+ if (!processInfo) return false;
493
+
494
+ try {
495
+ process.kill(processInfo.pid, 0);
496
+ return true;
497
+ } catch {
498
+ return false;
499
+ }
500
+ }
501
+
502
+ private async ensureDashboardAssets(): Promise<void> {
503
+ // Ensure dashboard assets are available
504
+ const assetsPath = path.join(__dirname, '../../dashboard-assets');
505
+ if (!(await fs.pathExists(assetsPath))) {
506
+ logger.info('Dashboard assets not found, downloading...');
507
+ // Download or copy dashboard assets
508
+ }
509
+ }
510
+
511
+ private async startDashboardServer(options: any): Promise<any> {
512
+ // Start the actual dashboard server process
513
+ const serverPath = path.join(__dirname, '../../dashboard-server');
514
+
515
+ const child = spawn('node', [serverPath], {
516
+ env: {
517
+ ...process.env,
518
+ PORT: options.port,
519
+ HOST: options.host,
520
+ NODE_ENV: options.dev ? 'development' : 'production',
521
+ },
522
+ detached: !options.dev,
523
+ stdio: options.dev ? 'inherit' : 'ignore',
524
+ });
525
+
526
+ if (!options.dev) {
527
+ child.unref();
528
+ }
529
+
530
+ return {
531
+ pid: child.pid,
532
+ host: options.host,
533
+ port: options.port,
534
+ started: Date.now(),
535
+ };
536
+ }
537
+
538
+ private async saveDashboardProcess(
539
+ processInfo: any,
540
+ options: any
541
+ ): Promise<void> {
542
+ const processFile = path.join(process.cwd(), '.wundr', 'dashboard.pid');
543
+ await fs.ensureDir(path.dirname(processFile));
544
+ await fs.writeJson(processFile, { ...processInfo, ...options });
545
+ }
546
+
547
+ private async loadDashboardProcess(): Promise<any> {
548
+ const processFile = path.join(process.cwd(), '.wundr', 'dashboard.pid');
549
+ if (await fs.pathExists(processFile)) {
550
+ return await fs.readJson(processFile);
551
+ }
552
+ return null;
553
+ }
554
+
555
+ private async cleanupDashboardProcess(): Promise<void> {
556
+ const processFile = path.join(process.cwd(), '.wundr', 'dashboard.pid');
557
+ if (await fs.pathExists(processFile)) {
558
+ await fs.remove(processFile);
559
+ }
560
+ }
561
+
562
+ private async openBrowser(url: string): Promise<void> {
563
+ const { default: open } = await import('open');
564
+ await open(url);
565
+ }
566
+
567
+ // Report generation methods
568
+ private async collectReportData(type: string, period: string): Promise<any> {
569
+ // Collect data for report generation
570
+ return { type, period, data: [] };
571
+ }
572
+
573
+ private async formatReport(data: any, format: string): Promise<any> {
574
+ // Format report based on requested format
575
+ return data;
576
+ }
577
+
578
+ private getDefaultReportPath(type: string, format: string): string {
579
+ return `reports/wundr-${type}-report.${format}`;
580
+ }
581
+
582
+ private async saveReport(report: any, outputPath: string): Promise<void> {
583
+ await fs.ensureDir(path.dirname(outputPath));
584
+ if (typeof report === 'string') {
585
+ await fs.writeFile(outputPath, report);
586
+ } else {
587
+ await fs.writeJson(outputPath, report, { spaces: 2 });
588
+ }
589
+ }
590
+
591
+ // Data export/import methods
592
+ private async collectExportData(type: string, filter?: string): Promise<any> {
593
+ return { type, filter, data: [] };
594
+ }
595
+
596
+ private async formatExportData(data: any, format: string): Promise<any> {
597
+ return data;
598
+ }
599
+
600
+ private getDefaultExportPath(type: string, format: string): string {
601
+ return `exports/wundr-${type}-export.${format}`;
602
+ }
603
+
604
+ private async saveExportData(data: any, outputPath: string): Promise<void> {
605
+ await fs.ensureDir(path.dirname(outputPath));
606
+ await fs.writeFile(outputPath, JSON.stringify(data, null, 2));
607
+ }
608
+
609
+ private async loadImportData(file: string): Promise<any> {
610
+ return await fs.readJson(file);
611
+ }
612
+
613
+ private async validateImportData(data: any, type?: string): Promise<any> {
614
+ // Validate imported data
615
+ return data;
616
+ }
617
+
618
+ private async mergeImportData(data: any, type?: string): Promise<void> {
619
+ // Merge imported data with existing data
620
+ logger.debug('Merging import data');
621
+ }
622
+
623
+ private async replaceImportData(data: any, type?: string): Promise<void> {
624
+ // Replace existing data with imported data
625
+ logger.debug('Replacing with import data');
626
+ }
627
+
628
+ // Widget management methods
629
+ private async saveWidget(widget: any): Promise<void> {
630
+ const widgetFile = path.join(
631
+ process.cwd(),
632
+ '.wundr',
633
+ 'widgets',
634
+ `${widget.id}.json`
635
+ );
636
+ await fs.ensureDir(path.dirname(widgetFile));
637
+ await fs.writeJson(widgetFile, widget, { spaces: 2 });
638
+ }
639
+
640
+ private async deleteWidget(id: string): Promise<void> {
641
+ const widgetFile = path.join(
642
+ process.cwd(),
643
+ '.wundr',
644
+ 'widgets',
645
+ `${id}.json`
646
+ );
647
+ if (await fs.pathExists(widgetFile)) {
648
+ await fs.remove(widgetFile);
649
+ }
650
+ }
651
+
652
+ private async getAllWidgets(): Promise<any[]> {
653
+ const widgetsDir = path.join(process.cwd(), '.wundr', 'widgets');
654
+ if (!(await fs.pathExists(widgetsDir))) {
655
+ return [];
656
+ }
657
+
658
+ const files = await fs.readdir(widgetsDir);
659
+ const widgets: any[] = [];
660
+
661
+ for (const file of files.filter(f => f.endsWith('.json'))) {
662
+ const widget = await fs.readJson(path.join(widgetsDir, file));
663
+ widgets.push(widget);
664
+ }
665
+
666
+ return widgets;
667
+ }
668
+
669
+ // Theme management methods
670
+ private async listThemes(): Promise<void> {
671
+ const themes = ['default', 'dark', 'light'];
672
+ console.log('Available themes:');
673
+ themes.forEach(theme => console.log(` - ${theme}`));
674
+ }
675
+
676
+ private async setTheme(name: string): Promise<void> {
677
+ this.configManager.set('dashboard.theme', name);
678
+ await this.configManager.saveConfig();
679
+ logger.success(`Theme set to: ${name}`);
680
+ }
681
+
682
+ private async createTheme(name: string, config: string): Promise<void> {
683
+ logger.info(`Creating theme: ${name}`);
684
+ // Implementation for creating custom themes
685
+ }
686
+
687
+ private async deleteTheme(name: string): Promise<void> {
688
+ logger.info(`Deleting theme: ${name}`);
689
+ // Implementation for deleting custom themes
690
+ }
691
+ }