@claude-flow/cli 3.0.0-alpha.13 → 3.0.0-alpha.15

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 (106) hide show
  1. package/dist/src/commands/doctor.d.ts.map +1 -1
  2. package/dist/src/commands/doctor.js +75 -2
  3. package/dist/src/commands/doctor.js.map +1 -1
  4. package/dist/tsconfig.tsbuildinfo +1 -1
  5. package/package.json +12 -4
  6. package/.agentic-flow/intelligence.json +0 -17
  7. package/.claude-flow/agents/store.json +0 -16
  8. package/.claude-flow/daemon-state.json +0 -123
  9. package/.claude-flow/daemon-test.log +0 -0
  10. package/.claude-flow/daemon.log +0 -0
  11. package/.claude-flow/daemon2.log +0 -0
  12. package/.claude-flow/daemon3.log +0 -0
  13. package/.claude-flow/hive-mind/state.json +0 -51
  14. package/.claude-flow/metrics/agent-metrics.json +0 -1
  15. package/.claude-flow/metrics/codebase-map.json +0 -11
  16. package/.claude-flow/metrics/consolidation.json +0 -6
  17. package/.claude-flow/metrics/performance.json +0 -87
  18. package/.claude-flow/metrics/security-audit.json +0 -10
  19. package/.claude-flow/metrics/task-metrics.json +0 -10
  20. package/.claude-flow/metrics/test-gaps.json +0 -6
  21. package/__tests__/README.md +0 -140
  22. package/__tests__/TEST_SUMMARY.md +0 -144
  23. package/__tests__/cli.test.ts +0 -558
  24. package/__tests__/commands.test.ts +0 -726
  25. package/__tests__/config-adapter.test.ts +0 -362
  26. package/__tests__/config-loading.test.ts +0 -106
  27. package/__tests__/coverage/.tmp/coverage-0.json +0 -1
  28. package/__tests__/coverage/.tmp/coverage-1.json +0 -1
  29. package/__tests__/coverage/.tmp/coverage-2.json +0 -1
  30. package/__tests__/coverage/.tmp/coverage-3.json +0 -1
  31. package/__tests__/coverage/.tmp/coverage-4.json +0 -1
  32. package/__tests__/coverage/.tmp/coverage-5.json +0 -1
  33. package/__tests__/mcp-client.test.ts +0 -480
  34. package/__tests__/p1-commands.test.ts +0 -1064
  35. package/agents/architect.yaml +0 -11
  36. package/agents/coder.yaml +0 -11
  37. package/agents/reviewer.yaml +0 -10
  38. package/agents/security-architect.yaml +0 -10
  39. package/agents/tester.yaml +0 -10
  40. package/docs/CONFIG_LOADING.md +0 -236
  41. package/docs/IMPLEMENTATION_COMPLETE.md +0 -421
  42. package/docs/MCP_CLIENT_GUIDE.md +0 -620
  43. package/docs/REFACTORING_SUMMARY.md +0 -247
  44. package/scripts/publish.sh +0 -46
  45. package/src/commands/agent.ts +0 -955
  46. package/src/commands/claims.ts +0 -317
  47. package/src/commands/completions.ts +0 -558
  48. package/src/commands/config.ts +0 -452
  49. package/src/commands/daemon.ts +0 -621
  50. package/src/commands/deployment.ts +0 -323
  51. package/src/commands/doctor.ts +0 -382
  52. package/src/commands/embeddings.ts +0 -686
  53. package/src/commands/hive-mind.ts +0 -928
  54. package/src/commands/hooks.ts +0 -2603
  55. package/src/commands/index.ts +0 -154
  56. package/src/commands/init.ts +0 -597
  57. package/src/commands/mcp.ts +0 -753
  58. package/src/commands/memory.ts +0 -1161
  59. package/src/commands/migrate.ts +0 -447
  60. package/src/commands/neural.ts +0 -253
  61. package/src/commands/performance.ts +0 -292
  62. package/src/commands/plugins.ts +0 -316
  63. package/src/commands/process.ts +0 -695
  64. package/src/commands/providers.ts +0 -259
  65. package/src/commands/security.ts +0 -288
  66. package/src/commands/session.ts +0 -891
  67. package/src/commands/start.ts +0 -457
  68. package/src/commands/status.ts +0 -736
  69. package/src/commands/swarm.ts +0 -648
  70. package/src/commands/task.ts +0 -792
  71. package/src/commands/workflow.ts +0 -742
  72. package/src/config-adapter.ts +0 -210
  73. package/src/index.ts +0 -443
  74. package/src/infrastructure/in-memory-repositories.ts +0 -310
  75. package/src/init/claudemd-generator.ts +0 -631
  76. package/src/init/executor.ts +0 -762
  77. package/src/init/helpers-generator.ts +0 -628
  78. package/src/init/index.ts +0 -60
  79. package/src/init/mcp-generator.ts +0 -83
  80. package/src/init/settings-generator.ts +0 -284
  81. package/src/init/statusline-generator.ts +0 -211
  82. package/src/init/types.ts +0 -447
  83. package/src/mcp-client.ts +0 -241
  84. package/src/mcp-server.ts +0 -577
  85. package/src/mcp-tools/agent-tools.ts +0 -466
  86. package/src/mcp-tools/config-tools.ts +0 -370
  87. package/src/mcp-tools/hive-mind-tools.ts +0 -521
  88. package/src/mcp-tools/hooks-tools.ts +0 -1888
  89. package/src/mcp-tools/index.ts +0 -16
  90. package/src/mcp-tools/memory-tools.ts +0 -270
  91. package/src/mcp-tools/session-tools.ts +0 -359
  92. package/src/mcp-tools/swarm-tools.ts +0 -105
  93. package/src/mcp-tools/task-tools.ts +0 -347
  94. package/src/mcp-tools/types.ts +0 -33
  95. package/src/mcp-tools/workflow-tools.ts +0 -573
  96. package/src/output.ts +0 -639
  97. package/src/parser.ts +0 -417
  98. package/src/prompt.ts +0 -619
  99. package/src/services/index.ts +0 -15
  100. package/src/services/worker-daemon.ts +0 -726
  101. package/src/suggest.ts +0 -245
  102. package/src/types.ts +0 -287
  103. package/tmp.json +0 -0
  104. package/tsconfig.json +0 -16
  105. package/tsconfig.tsbuildinfo +0 -1
  106. package/vitest.config.ts +0 -13
package/src/output.ts DELETED
@@ -1,639 +0,0 @@
1
- /**
2
- * V3 CLI Output Formatter
3
- * Advanced output formatting with tables, progress bars, and colors
4
- */
5
-
6
- import type { TableOptions, TableColumn, ProgressOptions, SpinnerOptions } from './types.js';
7
-
8
- // ============================================
9
- // Color Support
10
- // ============================================
11
-
12
- const COLORS = {
13
- // Standard colors
14
- reset: '\x1b[0m',
15
- bold: '\x1b[1m',
16
- dim: '\x1b[2m',
17
- italic: '\x1b[3m',
18
- underline: '\x1b[4m',
19
-
20
- // Foreground colors
21
- black: '\x1b[30m',
22
- red: '\x1b[31m',
23
- green: '\x1b[32m',
24
- yellow: '\x1b[33m',
25
- blue: '\x1b[34m',
26
- magenta: '\x1b[35m',
27
- cyan: '\x1b[36m',
28
- white: '\x1b[37m',
29
- gray: '\x1b[90m',
30
-
31
- // Bright foreground colors
32
- brightRed: '\x1b[91m',
33
- brightGreen: '\x1b[92m',
34
- brightYellow: '\x1b[93m',
35
- brightBlue: '\x1b[94m',
36
- brightMagenta: '\x1b[95m',
37
- brightCyan: '\x1b[96m',
38
- brightWhite: '\x1b[97m',
39
-
40
- // Background colors
41
- bgBlack: '\x1b[40m',
42
- bgRed: '\x1b[41m',
43
- bgGreen: '\x1b[42m',
44
- bgYellow: '\x1b[43m',
45
- bgBlue: '\x1b[44m',
46
- bgMagenta: '\x1b[45m',
47
- bgCyan: '\x1b[46m',
48
- bgWhite: '\x1b[47m'
49
- } as const;
50
-
51
- type ColorName = keyof typeof COLORS;
52
-
53
- export type VerbosityLevel = 'quiet' | 'normal' | 'verbose' | 'debug';
54
-
55
- export class OutputFormatter {
56
- private colorEnabled: boolean;
57
- private outputStream: NodeJS.WriteStream;
58
- private errorStream: NodeJS.WriteStream;
59
- private verbosity: VerbosityLevel;
60
-
61
- constructor(options: { color?: boolean; verbosity?: VerbosityLevel } = {}) {
62
- this.colorEnabled = options.color ?? this.supportsColor();
63
- this.outputStream = process.stdout;
64
- this.errorStream = process.stderr;
65
- this.verbosity = options.verbosity ?? 'normal';
66
- }
67
-
68
- /**
69
- * Set verbosity level
70
- * - quiet: Only errors and direct results
71
- * - normal: Errors, warnings, info, and results
72
- * - verbose: All of normal + debug messages
73
- * - debug: All output including trace
74
- */
75
- setVerbosity(level: VerbosityLevel): void {
76
- this.verbosity = level;
77
- }
78
-
79
- getVerbosity(): VerbosityLevel {
80
- return this.verbosity;
81
- }
82
-
83
- isQuiet(): boolean {
84
- return this.verbosity === 'quiet';
85
- }
86
-
87
- isVerbose(): boolean {
88
- return this.verbosity === 'verbose' || this.verbosity === 'debug';
89
- }
90
-
91
- isDebug(): boolean {
92
- return this.verbosity === 'debug';
93
- }
94
-
95
- private supportsColor(): boolean {
96
- // Check for NO_COLOR environment variable
97
- if (process.env.NO_COLOR !== undefined) return false;
98
-
99
- // Check for FORCE_COLOR environment variable
100
- if (process.env.FORCE_COLOR !== undefined) return true;
101
-
102
- // Check if stdout is a TTY
103
- return process.stdout.isTTY ?? false;
104
- }
105
-
106
- // ============================================
107
- // Color Methods
108
- // ============================================
109
-
110
- color(text: string, ...colors: ColorName[]): string {
111
- if (!this.colorEnabled) return text;
112
-
113
- const codes = colors.map(c => COLORS[c]).join('');
114
- return `${codes}${text}${COLORS.reset}`;
115
- }
116
-
117
- bold(text: string): string {
118
- return this.color(text, 'bold');
119
- }
120
-
121
- dim(text: string): string {
122
- return this.color(text, 'dim');
123
- }
124
-
125
- success(text: string): string {
126
- return this.color(text, 'green');
127
- }
128
-
129
- error(text: string): string {
130
- return this.color(text, 'red');
131
- }
132
-
133
- warning(text: string): string {
134
- return this.color(text, 'yellow');
135
- }
136
-
137
- info(text: string): string {
138
- return this.color(text, 'blue');
139
- }
140
-
141
- highlight(text: string): string {
142
- return this.color(text, 'cyan', 'bold');
143
- }
144
-
145
- // ============================================
146
- // Output Methods
147
- // ============================================
148
-
149
- write(text: string): void {
150
- this.outputStream.write(text);
151
- }
152
-
153
- writeln(text: string = ''): void {
154
- this.outputStream.write(text + '\n');
155
- }
156
-
157
- writeError(text: string): void {
158
- this.errorStream.write(text);
159
- }
160
-
161
- writeErrorln(text: string = ''): void {
162
- this.errorStream.write(text + '\n');
163
- }
164
-
165
- // ============================================
166
- // Formatted Output Methods
167
- // ============================================
168
-
169
- printSuccess(message: string): void {
170
- // Success always shows (result output)
171
- const icon = this.color('[OK]', 'green', 'bold');
172
- this.writeln(`${icon} ${message}`);
173
- }
174
-
175
- printError(message: string, details?: string): void {
176
- // Errors always show
177
- const icon = this.color('[ERROR]', 'red', 'bold');
178
- this.writeErrorln(`${icon} ${message}`);
179
- if (details) {
180
- this.writeErrorln(this.dim(` ${details}`));
181
- }
182
- }
183
-
184
- printWarning(message: string): void {
185
- // Warnings suppressed in quiet mode
186
- if (this.verbosity === 'quiet') return;
187
- const icon = this.color('[WARN]', 'yellow', 'bold');
188
- this.writeln(`${icon} ${message}`);
189
- }
190
-
191
- printInfo(message: string): void {
192
- // Info suppressed in quiet mode
193
- if (this.verbosity === 'quiet') return;
194
- const icon = this.color('[INFO]', 'blue', 'bold');
195
- this.writeln(`${icon} ${message}`);
196
- }
197
-
198
- printDebug(message: string): void {
199
- // Debug only shows in verbose/debug mode
200
- if (this.verbosity !== 'verbose' && this.verbosity !== 'debug') return;
201
- const icon = this.color('[DEBUG]', 'gray');
202
- this.writeln(`${icon} ${this.dim(message)}`);
203
- }
204
-
205
- printTrace(message: string): void {
206
- // Trace only shows in debug mode
207
- if (this.verbosity !== 'debug') return;
208
- const icon = this.color('[TRACE]', 'gray', 'dim');
209
- this.writeln(`${icon} ${this.dim(message)}`);
210
- }
211
-
212
- // ============================================
213
- // Table Formatting
214
- // ============================================
215
-
216
- table(options: TableOptions): string {
217
- const { columns, data, border = true, header = true, padding = 1, maxWidth } = options;
218
-
219
- // Calculate column widths
220
- const widths = this.calculateColumnWidths(columns, data, maxWidth);
221
-
222
- const lines: string[] = [];
223
- const pad = ' '.repeat(padding);
224
-
225
- // Border characters
226
- const borderChars = border ? {
227
- topLeft: '+', topRight: '+', bottomLeft: '+', bottomRight: '+',
228
- horizontal: '-', vertical: '|',
229
- leftT: '+', rightT: '+', topT: '+', bottomT: '+', cross: '+'
230
- } : {
231
- topLeft: '', topRight: '', bottomLeft: '', bottomRight: '',
232
- horizontal: '', vertical: ' ',
233
- leftT: '', rightT: '', topT: '', bottomT: '', cross: ''
234
- };
235
-
236
- // Top border
237
- if (border) {
238
- lines.push(this.createBorderLine(widths, borderChars, 'top', padding));
239
- }
240
-
241
- // Header row
242
- if (header) {
243
- const headerRow = columns.map((col, i) => {
244
- const text = this.truncate(col.header, widths[i]);
245
- return pad + this.alignText(this.bold(text), widths[i], col.align) + pad;
246
- }).join(borderChars.vertical);
247
-
248
- lines.push(`${borderChars.vertical}${headerRow}${borderChars.vertical}`);
249
-
250
- // Header separator
251
- if (border) {
252
- lines.push(this.createBorderLine(widths, borderChars, 'middle', padding));
253
- }
254
- }
255
-
256
- // Data rows
257
- for (const row of data) {
258
- const rowCells = columns.map((col, i) => {
259
- let value = row[col.key];
260
-
261
- // Apply formatter if provided
262
- if (col.format) {
263
- value = col.format(value);
264
- } else {
265
- value = String(value ?? '');
266
- }
267
-
268
- const text = this.truncate(String(value), widths[i]);
269
- return pad + this.alignText(text, widths[i], col.align) + pad;
270
- }).join(borderChars.vertical);
271
-
272
- lines.push(`${borderChars.vertical}${rowCells}${borderChars.vertical}`);
273
- }
274
-
275
- // Bottom border
276
- if (border) {
277
- lines.push(this.createBorderLine(widths, borderChars, 'bottom', padding));
278
- }
279
-
280
- return lines.join('\n');
281
- }
282
-
283
- printTable(options: TableOptions): void {
284
- this.writeln(this.table(options));
285
- }
286
-
287
- private calculateColumnWidths(
288
- columns: TableColumn[],
289
- data: Record<string, unknown>[],
290
- maxWidth?: number
291
- ): number[] {
292
- const widths = columns.map((col, i) => {
293
- // Start with header width
294
- let width = col.header.length;
295
-
296
- // Check all data values
297
- for (const row of data) {
298
- let value = row[col.key];
299
- if (col.format) {
300
- value = col.format(value);
301
- }
302
- const len = this.stripAnsi(String(value ?? '')).length;
303
- width = Math.max(width, len);
304
- }
305
-
306
- // Apply column-specific width limit
307
- if (col.width) {
308
- width = Math.min(width, col.width);
309
- }
310
-
311
- return width;
312
- });
313
-
314
- // Apply max width constraint
315
- if (maxWidth) {
316
- const totalWidth = widths.reduce((a, b) => a + b, 0) + (columns.length * 3) + 1;
317
- if (totalWidth > maxWidth) {
318
- const reduction = (totalWidth - maxWidth) / columns.length;
319
- return widths.map(w => Math.max(3, Math.floor(w - reduction)));
320
- }
321
- }
322
-
323
- return widths;
324
- }
325
-
326
- private createBorderLine(
327
- widths: number[],
328
- chars: Record<string, string>,
329
- position: 'top' | 'middle' | 'bottom',
330
- padding: number
331
- ): string {
332
- const cellWidth = (w: number) => chars.horizontal.repeat(w + (padding * 2));
333
- const cells = widths.map(cellWidth).join(
334
- position === 'top' ? chars.topT :
335
- position === 'bottom' ? chars.bottomT :
336
- chars.cross
337
- );
338
-
339
- const left = position === 'top' ? chars.topLeft : position === 'bottom' ? chars.bottomLeft : chars.leftT;
340
- const right = position === 'top' ? chars.topRight : position === 'bottom' ? chars.bottomRight : chars.rightT;
341
-
342
- return `${left}${cells}${right}`;
343
- }
344
-
345
- private alignText(text: string, width: number, align: 'left' | 'center' | 'right' = 'left'): string {
346
- const len = this.stripAnsi(text).length;
347
- const padding = width - len;
348
-
349
- if (padding <= 0) return text;
350
-
351
- switch (align) {
352
- case 'right':
353
- return ' '.repeat(padding) + text;
354
- case 'center':
355
- const left = Math.floor(padding / 2);
356
- const right = padding - left;
357
- return ' '.repeat(left) + text + ' '.repeat(right);
358
- default:
359
- return text + ' '.repeat(padding);
360
- }
361
- }
362
-
363
- private truncate(text: string, maxLength: number): string {
364
- const stripped = this.stripAnsi(text);
365
- if (stripped.length <= maxLength) return text;
366
- return stripped.slice(0, maxLength - 3) + '...';
367
- }
368
-
369
- private stripAnsi(text: string): string {
370
- return text.replace(/\x1b\[[0-9;]*m/g, '');
371
- }
372
-
373
- // ============================================
374
- // Progress Bar
375
- // ============================================
376
-
377
- createProgress(options: ProgressOptions): Progress {
378
- return new Progress(this, options);
379
- }
380
-
381
- progressBar(current: number, total: number, width: number = 40): string {
382
- const percent = Math.min(100, Math.max(0, (current / total) * 100));
383
- const filled = Math.round((width * percent) / 100);
384
- const empty = width - filled;
385
-
386
- const bar = this.color('#'.repeat(filled), 'green') +
387
- this.dim('-'.repeat(empty));
388
-
389
- return `[${bar}] ${percent.toFixed(1)}%`;
390
- }
391
-
392
- // ============================================
393
- // Spinner
394
- // ============================================
395
-
396
- createSpinner(options: SpinnerOptions): Spinner {
397
- return new Spinner(this, options);
398
- }
399
-
400
- // ============================================
401
- // JSON Output
402
- // ============================================
403
-
404
- json(data: unknown, pretty: boolean = true): string {
405
- return pretty ? JSON.stringify(data, null, 2) : JSON.stringify(data);
406
- }
407
-
408
- printJson(data: unknown, pretty: boolean = true): void {
409
- this.writeln(this.json(data, pretty));
410
- }
411
-
412
- // ============================================
413
- // List Output
414
- // ============================================
415
-
416
- list(items: string[], bullet: string = '-'): string {
417
- return items.map(item => ` ${bullet} ${item}`).join('\n');
418
- }
419
-
420
- printList(items: string[], bullet: string = '-'): void {
421
- this.writeln(this.list(items, bullet));
422
- }
423
-
424
- numberedList(items: string[]): string {
425
- return items.map((item, i) => ` ${i + 1}. ${item}`).join('\n');
426
- }
427
-
428
- printNumberedList(items: string[]): void {
429
- this.writeln(this.numberedList(items));
430
- }
431
-
432
- // ============================================
433
- // Box Output
434
- // ============================================
435
-
436
- box(content: string, title?: string): string {
437
- const lines = content.split('\n');
438
- const maxLen = Math.max(...lines.map(l => this.stripAnsi(l).length), title?.length ?? 0);
439
- const width = maxLen + 4;
440
-
441
- const border = {
442
- topLeft: '+', topRight: '+',
443
- bottomLeft: '+', bottomRight: '+',
444
- horizontal: '-', vertical: '|'
445
- };
446
-
447
- const result: string[] = [];
448
-
449
- // Top border with optional title
450
- if (title) {
451
- const titleText = ` ${title} `;
452
- const leftPad = Math.floor((width - titleText.length - 2) / 2);
453
- const rightPad = width - titleText.length - leftPad - 2;
454
- result.push(
455
- border.topLeft +
456
- border.horizontal.repeat(leftPad) +
457
- this.bold(titleText) +
458
- border.horizontal.repeat(rightPad) +
459
- border.topRight
460
- );
461
- } else {
462
- result.push(border.topLeft + border.horizontal.repeat(width - 2) + border.topRight);
463
- }
464
-
465
- // Content lines
466
- for (const line of lines) {
467
- const stripped = this.stripAnsi(line);
468
- const padding = maxLen - stripped.length;
469
- result.push(`${border.vertical} ${line}${' '.repeat(padding)} ${border.vertical}`);
470
- }
471
-
472
- // Bottom border
473
- result.push(border.bottomLeft + border.horizontal.repeat(width - 2) + border.bottomRight);
474
-
475
- return result.join('\n');
476
- }
477
-
478
- printBox(content: string, title?: string): void {
479
- this.writeln(this.box(content, title));
480
- }
481
-
482
- setColorEnabled(enabled: boolean): void {
483
- this.colorEnabled = enabled;
484
- }
485
-
486
- isColorEnabled(): boolean {
487
- return this.colorEnabled;
488
- }
489
- }
490
-
491
- // ============================================
492
- // Progress Class
493
- // ============================================
494
-
495
- export class Progress {
496
- private current: number;
497
- private total: number;
498
- private width: number;
499
- private startTime: number;
500
- private formatter: OutputFormatter;
501
- private showPercentage: boolean;
502
- private showETA: boolean;
503
- private lastRender: string = '';
504
-
505
- constructor(formatter: OutputFormatter, options: ProgressOptions) {
506
- this.formatter = formatter;
507
- this.current = options.current ?? 0;
508
- this.total = options.total;
509
- this.width = options.width ?? 40;
510
- this.showPercentage = options.showPercentage ?? true;
511
- this.showETA = options.showETA ?? true;
512
- this.startTime = Date.now();
513
- }
514
-
515
- update(current: number): void {
516
- this.current = current;
517
- this.render();
518
- }
519
-
520
- increment(amount: number = 1): void {
521
- this.update(this.current + amount);
522
- }
523
-
524
- render(): void {
525
- const bar = this.formatter.progressBar(this.current, this.total, this.width);
526
-
527
- let output = bar;
528
-
529
- if (this.showETA && this.current > 0) {
530
- const elapsed = Date.now() - this.startTime;
531
- const rate = this.current / elapsed;
532
- const remaining = this.total - this.current;
533
- const eta = remaining / rate;
534
-
535
- if (isFinite(eta)) {
536
- output += ` ETA: ${this.formatTime(eta)}`;
537
- }
538
- }
539
-
540
- // Clear previous line and write new
541
- if (this.lastRender) {
542
- process.stdout.write('\r' + ' '.repeat(this.lastRender.length) + '\r');
543
- }
544
-
545
- process.stdout.write(output);
546
- this.lastRender = output;
547
- }
548
-
549
- finish(): void {
550
- this.current = this.total;
551
- this.render();
552
- process.stdout.write('\n');
553
- }
554
-
555
- private formatTime(ms: number): string {
556
- const seconds = Math.floor(ms / 1000);
557
- const minutes = Math.floor(seconds / 60);
558
- const hours = Math.floor(minutes / 60);
559
-
560
- if (hours > 0) {
561
- return `${hours}h ${minutes % 60}m`;
562
- } else if (minutes > 0) {
563
- return `${minutes}m ${seconds % 60}s`;
564
- } else {
565
- return `${seconds}s`;
566
- }
567
- }
568
- }
569
-
570
- // ============================================
571
- // Spinner Class
572
- // ============================================
573
-
574
- export class Spinner {
575
- private formatter: OutputFormatter;
576
- private text: string;
577
- private frames: string[];
578
- private interval: ReturnType<typeof setInterval> | null = null;
579
- private frameIndex: number = 0;
580
-
581
- private static readonly SPINNERS: Record<string, string[]> = {
582
- dots: ['...', '..:' , '.::', ':::', '::.', ':..' ,],
583
- line: ['-', '\\', '|', '/'],
584
- arc: ['◜', '◠', '◝', '◞', '◡', '◟'],
585
- circle: ['◐', '◓', '◑', '◒'],
586
- arrows: ['←', '↖', '↑', '↗', '→', '↘', '↓', '↙']
587
- };
588
-
589
- constructor(formatter: OutputFormatter, options: SpinnerOptions) {
590
- this.formatter = formatter;
591
- this.text = options.text;
592
- this.frames = Spinner.SPINNERS[options.spinner ?? 'dots'];
593
- }
594
-
595
- start(): void {
596
- if (this.interval) return;
597
-
598
- this.interval = setInterval(() => {
599
- this.render();
600
- this.frameIndex = (this.frameIndex + 1) % this.frames.length;
601
- }, 100);
602
-
603
- this.render();
604
- }
605
-
606
- stop(message?: string): void {
607
- if (this.interval) {
608
- clearInterval(this.interval);
609
- this.interval = null;
610
- }
611
-
612
- // Clear the line
613
- process.stdout.write('\r' + ' '.repeat(this.text.length + 10) + '\r');
614
-
615
- if (message) {
616
- this.formatter.writeln(message);
617
- }
618
- }
619
-
620
- succeed(message?: string): void {
621
- this.stop(this.formatter.success(message ?? this.text));
622
- }
623
-
624
- fail(message?: string): void {
625
- this.stop(this.formatter.error(message ?? this.text));
626
- }
627
-
628
- private render(): void {
629
- const frame = this.formatter.info(this.frames[this.frameIndex]);
630
- process.stdout.write(`\r${frame} ${this.text}`);
631
- }
632
-
633
- setText(text: string): void {
634
- this.text = text;
635
- }
636
- }
637
-
638
- // Export singleton instance
639
- export const output = new OutputFormatter();