@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,539 @@
1
+ /**
2
+ * Progress Manager - Progress bars and spinners for long-running operations.
3
+ *
4
+ * Provides:
5
+ * - Single and multi-bar progress tracking
6
+ * - Spinner for indeterminate operations
7
+ * - ETA calculation
8
+ * - Step-based progress for sequential operations
9
+ * - Pipe-safe (no-op when stdout is not a TTY)
10
+ *
11
+ * @module framework/progress-manager
12
+ */
13
+
14
+ import chalk from 'chalk';
15
+
16
+ // ---------------------------------------------------------------------------
17
+ // Types
18
+ // ---------------------------------------------------------------------------
19
+
20
+ /**
21
+ * Configuration for a progress bar.
22
+ */
23
+ export interface ProgressBarOptions {
24
+ /** Total number of units. */
25
+ total: number;
26
+
27
+ /** Bar width in characters. Defaults to 30. */
28
+ width?: number;
29
+
30
+ /** Label displayed before the bar. */
31
+ label?: string;
32
+
33
+ /** Filled character. Defaults to '='. */
34
+ filledChar?: string;
35
+
36
+ /** Empty character. Defaults to '-'. */
37
+ emptyChar?: string;
38
+
39
+ /** Show percentage. Defaults to true. */
40
+ showPercentage?: boolean;
41
+
42
+ /** Show ETA. Defaults to true. */
43
+ showETA?: boolean;
44
+
45
+ /** Show count (current/total). Defaults to true. */
46
+ showCount?: boolean;
47
+
48
+ /** Custom format function. */
49
+ format?: (state: ProgressState) => string;
50
+ }
51
+
52
+ /**
53
+ * Current state of a progress bar.
54
+ */
55
+ export interface ProgressState {
56
+ current: number;
57
+ total: number;
58
+ percentage: number;
59
+ elapsed: number;
60
+ eta: number;
61
+ rate: number;
62
+ label: string;
63
+ }
64
+
65
+ /**
66
+ * A step in a multi-step operation.
67
+ */
68
+ export interface ProgressStep {
69
+ name: string;
70
+ status: 'pending' | 'running' | 'done' | 'failed' | 'skipped';
71
+ message?: string;
72
+ duration?: number;
73
+ }
74
+
75
+ /**
76
+ * Configuration for a multi-step tracker.
77
+ */
78
+ export interface StepTrackerOptions {
79
+ /** Whether to show step numbers. Defaults to true. */
80
+ showNumbers?: boolean;
81
+
82
+ /** Whether to show elapsed time per step. Defaults to true. */
83
+ showDuration?: boolean;
84
+ }
85
+
86
+ // ---------------------------------------------------------------------------
87
+ // Progress Bar
88
+ // ---------------------------------------------------------------------------
89
+
90
+ export class ProgressBar {
91
+ private current: number = 0;
92
+ private startTime: number;
93
+ private options: Required<
94
+ Pick<
95
+ ProgressBarOptions,
96
+ | 'total'
97
+ | 'width'
98
+ | 'label'
99
+ | 'filledChar'
100
+ | 'emptyChar'
101
+ | 'showPercentage'
102
+ | 'showETA'
103
+ | 'showCount'
104
+ >
105
+ > &
106
+ Pick<ProgressBarOptions, 'format'>;
107
+ private isTTY: boolean;
108
+ private lastRender: string = '';
109
+
110
+ constructor(options: ProgressBarOptions) {
111
+ this.startTime = Date.now();
112
+ this.isTTY = process.stderr.isTTY === true;
113
+ this.options = {
114
+ total: options.total,
115
+ width: options.width ?? 30,
116
+ label: options.label ?? '',
117
+ filledChar: options.filledChar ?? '=',
118
+ emptyChar: options.emptyChar ?? '-',
119
+ showPercentage: options.showPercentage ?? true,
120
+ showETA: options.showETA ?? true,
121
+ showCount: options.showCount ?? true,
122
+ format: options.format,
123
+ };
124
+ }
125
+
126
+ /**
127
+ * Update progress to a specific value.
128
+ */
129
+ update(value: number): void {
130
+ this.current = Math.min(value, this.options.total);
131
+ this.render();
132
+ }
133
+
134
+ /**
135
+ * Increment progress by a given amount.
136
+ */
137
+ increment(amount: number = 1): void {
138
+ this.update(this.current + amount);
139
+ }
140
+
141
+ /**
142
+ * Get the current state.
143
+ */
144
+ getState(): ProgressState {
145
+ const elapsed = Date.now() - this.startTime;
146
+ const percentage =
147
+ this.options.total > 0 ? this.current / this.options.total : 0;
148
+ const rate = elapsed > 0 ? (this.current / elapsed) * 1000 : 0;
149
+ const remaining = this.options.total - this.current;
150
+ const eta = rate > 0 ? (remaining / rate) * 1000 : 0;
151
+
152
+ return {
153
+ current: this.current,
154
+ total: this.options.total,
155
+ percentage,
156
+ elapsed,
157
+ eta,
158
+ rate,
159
+ label: this.options.label,
160
+ };
161
+ }
162
+
163
+ /**
164
+ * Render the progress bar to stderr.
165
+ */
166
+ render(): void {
167
+ if (!this.isTTY) return;
168
+
169
+ const state = this.getState();
170
+ let line: string;
171
+
172
+ if (this.options.format) {
173
+ line = this.options.format(state);
174
+ } else {
175
+ line = this.defaultFormat(state);
176
+ }
177
+
178
+ // Only rewrite if changed
179
+ if (line !== this.lastRender) {
180
+ process.stderr.clearLine(0);
181
+ process.stderr.cursorTo(0);
182
+ process.stderr.write(line);
183
+ this.lastRender = line;
184
+ }
185
+ }
186
+
187
+ /**
188
+ * Finish the progress bar (add newline).
189
+ */
190
+ finish(message?: string): void {
191
+ if (!this.isTTY) return;
192
+
193
+ this.current = this.options.total;
194
+ this.render();
195
+ process.stderr.write('\n');
196
+
197
+ if (message) {
198
+ process.stderr.write(message + '\n');
199
+ }
200
+ }
201
+
202
+ /**
203
+ * Render the bar as a string (for non-interactive use).
204
+ */
205
+ toString(): string {
206
+ const state = this.getState();
207
+ if (this.options.format) {
208
+ return this.options.format(state);
209
+ }
210
+ return this.defaultFormat(state);
211
+ }
212
+
213
+ // -------------------------------------------------------------------------
214
+ // Private
215
+ // -------------------------------------------------------------------------
216
+
217
+ private defaultFormat(state: ProgressState): string {
218
+ const parts: string[] = [];
219
+
220
+ // Label
221
+ if (this.options.label) {
222
+ parts.push(chalk.cyan(this.options.label));
223
+ }
224
+
225
+ // Bar
226
+ const filled = Math.round(state.percentage * this.options.width);
227
+ const empty = this.options.width - filled;
228
+ const bar =
229
+ chalk.cyan('[') +
230
+ chalk.green(this.options.filledChar.repeat(filled)) +
231
+ chalk.gray(this.options.emptyChar.repeat(empty)) +
232
+ chalk.cyan(']');
233
+ parts.push(bar);
234
+
235
+ // Percentage
236
+ if (this.options.showPercentage) {
237
+ parts.push(`${(state.percentage * 100).toFixed(0)}%`);
238
+ }
239
+
240
+ // Count
241
+ if (this.options.showCount) {
242
+ parts.push(chalk.gray(`${state.current}/${state.total}`));
243
+ }
244
+
245
+ // ETA
246
+ if (
247
+ this.options.showETA &&
248
+ state.current > 0 &&
249
+ state.current < state.total
250
+ ) {
251
+ parts.push(chalk.gray(`ETA: ${this.formatTime(state.eta)}`));
252
+ }
253
+
254
+ return parts.join(' ');
255
+ }
256
+
257
+ private formatTime(ms: number): string {
258
+ const seconds = Math.ceil(ms / 1000);
259
+ if (seconds < 60) return `${seconds}s`;
260
+ const minutes = Math.floor(seconds / 60);
261
+ const remainingSeconds = seconds % 60;
262
+ return `${minutes}m${remainingSeconds}s`;
263
+ }
264
+ }
265
+
266
+ // ---------------------------------------------------------------------------
267
+ // Multi-Progress Manager
268
+ // ---------------------------------------------------------------------------
269
+
270
+ export class MultiProgressManager {
271
+ private bars: Map<string, ProgressBar> = new Map();
272
+
273
+ /**
274
+ * Create and register a new progress bar.
275
+ */
276
+ create(id: string, options: ProgressBarOptions): ProgressBar {
277
+ const bar = new ProgressBar(options);
278
+ this.bars.set(id, bar);
279
+ return bar;
280
+ }
281
+
282
+ /**
283
+ * Get a progress bar by ID.
284
+ */
285
+ get(id: string): ProgressBar | undefined {
286
+ return this.bars.get(id);
287
+ }
288
+
289
+ /**
290
+ * Update a progress bar by ID.
291
+ */
292
+ update(id: string, value: number): void {
293
+ this.bars.get(id)?.update(value);
294
+ }
295
+
296
+ /**
297
+ * Finish and remove a progress bar.
298
+ */
299
+ finish(id: string, message?: string): void {
300
+ const bar = this.bars.get(id);
301
+ if (bar) {
302
+ bar.finish(message);
303
+ this.bars.delete(id);
304
+ }
305
+ }
306
+
307
+ /**
308
+ * Finish all progress bars.
309
+ */
310
+ finishAll(): void {
311
+ for (const [id, bar] of this.bars) {
312
+ bar.finish();
313
+ this.bars.delete(id);
314
+ }
315
+ }
316
+ }
317
+
318
+ // ---------------------------------------------------------------------------
319
+ // Step Tracker
320
+ // ---------------------------------------------------------------------------
321
+
322
+ /**
323
+ * Track progress through a series of named steps.
324
+ *
325
+ * @example
326
+ * ```typescript
327
+ * const tracker = new StepTracker([
328
+ * 'Installing dependencies',
329
+ * 'Running migrations',
330
+ * 'Building assets',
331
+ * 'Verifying deployment',
332
+ * ]);
333
+ *
334
+ * tracker.start(0);
335
+ * await installDeps();
336
+ * tracker.complete(0);
337
+ *
338
+ * tracker.start(1);
339
+ * await runMigrations();
340
+ * tracker.complete(1);
341
+ * ```
342
+ */
343
+ export class StepTracker {
344
+ private steps: ProgressStep[];
345
+ private startTimes: Map<number, number> = new Map();
346
+ private options: Required<StepTrackerOptions>;
347
+ private isTTY: boolean;
348
+
349
+ constructor(stepNames: string[], options: StepTrackerOptions = {}) {
350
+ this.steps = stepNames.map(name => ({
351
+ name,
352
+ status: 'pending',
353
+ }));
354
+ this.options = {
355
+ showNumbers: options.showNumbers ?? true,
356
+ showDuration: options.showDuration ?? true,
357
+ };
358
+ this.isTTY = process.stderr.isTTY === true;
359
+ }
360
+
361
+ /**
362
+ * Mark a step as running.
363
+ */
364
+ start(index: number): void {
365
+ const step = this.steps[index];
366
+ if (!step) return;
367
+
368
+ step.status = 'running';
369
+ this.startTimes.set(index, Date.now());
370
+ this.render();
371
+ }
372
+
373
+ /**
374
+ * Mark a step as completed.
375
+ */
376
+ complete(index: number, message?: string): void {
377
+ const step = this.steps[index];
378
+ if (!step) return;
379
+
380
+ step.status = 'done';
381
+ step.message = message;
382
+ this.setDuration(index);
383
+ this.render();
384
+ }
385
+
386
+ /**
387
+ * Mark a step as failed.
388
+ */
389
+ fail(index: number, message?: string): void {
390
+ const step = this.steps[index];
391
+ if (!step) return;
392
+
393
+ step.status = 'failed';
394
+ step.message = message;
395
+ this.setDuration(index);
396
+ this.render();
397
+ }
398
+
399
+ /**
400
+ * Mark a step as skipped.
401
+ */
402
+ skip(index: number, reason?: string): void {
403
+ const step = this.steps[index];
404
+ if (!step) return;
405
+
406
+ step.status = 'skipped';
407
+ step.message = reason;
408
+ this.render();
409
+ }
410
+
411
+ /**
412
+ * Get all steps with their current status.
413
+ */
414
+ getSteps(): readonly ProgressStep[] {
415
+ return this.steps;
416
+ }
417
+
418
+ /**
419
+ * Get the overall progress as a fraction.
420
+ */
421
+ getProgress(): { completed: number; total: number; percentage: number } {
422
+ const completed = this.steps.filter(
423
+ s => s.status === 'done' || s.status === 'skipped'
424
+ ).length;
425
+ return {
426
+ completed,
427
+ total: this.steps.length,
428
+ percentage: this.steps.length > 0 ? completed / this.steps.length : 0,
429
+ };
430
+ }
431
+
432
+ /**
433
+ * Render step status to stderr.
434
+ */
435
+ render(): void {
436
+ if (!this.isTTY) return;
437
+
438
+ const lines = this.steps.map((step, index) => {
439
+ const prefix = this.options.showNumbers ? `${index + 1}. ` : '';
440
+ const icon = this.statusIcon(step.status);
441
+ const name = this.statusColor(step.status, step.name);
442
+ const suffix = this.stepSuffix(step);
443
+
444
+ return ` ${prefix}${icon} ${name}${suffix}`;
445
+ });
446
+
447
+ // Move cursor up to overwrite previous render
448
+ const moveUp = this.steps.length;
449
+ if (moveUp > 0) {
450
+ process.stderr.write(`\x1b[${moveUp}A`);
451
+ }
452
+
453
+ for (const line of lines) {
454
+ process.stderr.clearLine(0);
455
+ process.stderr.write(line + '\n');
456
+ }
457
+ }
458
+
459
+ /**
460
+ * Render the final summary.
461
+ */
462
+ summary(): string {
463
+ const { completed, total } = this.getProgress();
464
+ const failed = this.steps.filter(s => s.status === 'failed').length;
465
+ const skipped = this.steps.filter(s => s.status === 'skipped').length;
466
+
467
+ const parts: string[] = [`${completed}/${total} steps completed`];
468
+
469
+ if (failed > 0) parts.push(chalk.red(`${failed} failed`));
470
+ if (skipped > 0) parts.push(chalk.yellow(`${skipped} skipped`));
471
+
472
+ return parts.join(', ');
473
+ }
474
+
475
+ // -------------------------------------------------------------------------
476
+ // Private
477
+ // -------------------------------------------------------------------------
478
+
479
+ private setDuration(index: number): void {
480
+ const start = this.startTimes.get(index);
481
+ if (start !== undefined) {
482
+ const step = this.steps[index];
483
+ if (step) {
484
+ step.duration = Date.now() - start;
485
+ }
486
+ this.startTimes.delete(index);
487
+ }
488
+ }
489
+
490
+ private statusIcon(status: ProgressStep['status']): string {
491
+ switch (status) {
492
+ case 'pending':
493
+ return chalk.gray('[ ]');
494
+ case 'running':
495
+ return chalk.cyan('[~]');
496
+ case 'done':
497
+ return chalk.green('[+]');
498
+ case 'failed':
499
+ return chalk.red('[x]');
500
+ case 'skipped':
501
+ return chalk.yellow('[-]');
502
+ }
503
+ }
504
+
505
+ private statusColor(status: ProgressStep['status'], text: string): string {
506
+ switch (status) {
507
+ case 'pending':
508
+ return chalk.gray(text);
509
+ case 'running':
510
+ return chalk.cyan(text);
511
+ case 'done':
512
+ return chalk.green(text);
513
+ case 'failed':
514
+ return chalk.red(text);
515
+ case 'skipped':
516
+ return chalk.yellow(text);
517
+ }
518
+ }
519
+
520
+ private stepSuffix(step: ProgressStep): string {
521
+ const parts: string[] = [];
522
+
523
+ if (step.message) {
524
+ parts.push(chalk.gray(` - ${step.message}`));
525
+ }
526
+
527
+ if (this.options.showDuration && step.duration !== undefined) {
528
+ parts.push(chalk.gray(` (${this.formatDuration(step.duration)})`));
529
+ }
530
+
531
+ return parts.join('');
532
+ }
533
+
534
+ private formatDuration(ms: number): string {
535
+ if (ms < 1000) return `${ms}ms`;
536
+ if (ms < 60000) return `${(ms / 1000).toFixed(1)}s`;
537
+ return `${(ms / 60000).toFixed(1)}min`;
538
+ }
539
+ }
package/src/index.ts CHANGED
@@ -1,12 +1,13 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import { Command } from 'commander';
4
3
  import chalk from 'chalk';
4
+ import { Command } from 'commander';
5
5
  import figlet from 'figlet';
6
+
6
7
  import { version } from '../package.json';
7
8
  import { WundrCLI } from './cli';
8
- import { logger } from './utils/logger';
9
9
  import { errorHandler } from './utils/error-handler';
10
+ import { logger } from './utils/logger';
10
11
 
11
12
  /**
12
13
  * Main CLI entry point
@@ -1,11 +1,14 @@
1
- import inquirer from 'inquirer';
2
1
  import blessed from 'blessed';
3
2
  import chalk from 'chalk';
4
- import { ConfigManager } from '../utils/config-manager';
5
- import { PluginManager } from '../plugins/plugin-manager';
6
- import { logger } from '../utils/logger';
3
+ import inquirer from 'inquirer';
4
+
5
+ import { TUILayout, ChatSession } from '../types';
7
6
  import { errorHandler } from '../utils/error-handler';
8
- import { InteractiveSession, TUILayout, ChatSession } from '../types';
7
+ import { logger } from '../utils/logger';
8
+
9
+ import type { PluginManager } from '../plugins/plugin-manager';
10
+ import type { InteractiveSession } from '../types';
11
+ import type { ConfigManager } from '../utils/config-manager';
9
12
 
10
13
  /**
11
14
  * Interactive mode manager for wizard, TUI, and chat interfaces
@@ -71,8 +74,12 @@ export class InteractiveMode {
71
74
  const { spawn } = await import('child_process');
72
75
  const chatArgs = ['chat', 'start'];
73
76
 
74
- if (options.model) chatArgs.push('--model', options.model);
75
- if (options.context) chatArgs.push('--context', options.context);
77
+ if (options.model) {
78
+ chatArgs.push('--model', options.model);
79
+ }
80
+ if (options.context) {
81
+ chatArgs.push('--context', options.context);
82
+ }
76
83
 
77
84
  const child = spawn('wundr', chatArgs, {
78
85
  stdio: 'inherit',