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

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 (102) hide show
  1. package/package.json +12 -4
  2. package/.agentic-flow/intelligence.json +0 -17
  3. package/.claude-flow/agents/store.json +0 -16
  4. package/.claude-flow/daemon-state.json +0 -123
  5. package/.claude-flow/daemon-test.log +0 -0
  6. package/.claude-flow/daemon.log +0 -0
  7. package/.claude-flow/daemon2.log +0 -0
  8. package/.claude-flow/daemon3.log +0 -0
  9. package/.claude-flow/hive-mind/state.json +0 -51
  10. package/.claude-flow/metrics/agent-metrics.json +0 -1
  11. package/.claude-flow/metrics/codebase-map.json +0 -11
  12. package/.claude-flow/metrics/consolidation.json +0 -6
  13. package/.claude-flow/metrics/performance.json +0 -87
  14. package/.claude-flow/metrics/security-audit.json +0 -10
  15. package/.claude-flow/metrics/task-metrics.json +0 -10
  16. package/.claude-flow/metrics/test-gaps.json +0 -6
  17. package/__tests__/README.md +0 -140
  18. package/__tests__/TEST_SUMMARY.md +0 -144
  19. package/__tests__/cli.test.ts +0 -558
  20. package/__tests__/commands.test.ts +0 -726
  21. package/__tests__/config-adapter.test.ts +0 -362
  22. package/__tests__/config-loading.test.ts +0 -106
  23. package/__tests__/coverage/.tmp/coverage-0.json +0 -1
  24. package/__tests__/coverage/.tmp/coverage-1.json +0 -1
  25. package/__tests__/coverage/.tmp/coverage-2.json +0 -1
  26. package/__tests__/coverage/.tmp/coverage-3.json +0 -1
  27. package/__tests__/coverage/.tmp/coverage-4.json +0 -1
  28. package/__tests__/coverage/.tmp/coverage-5.json +0 -1
  29. package/__tests__/mcp-client.test.ts +0 -480
  30. package/__tests__/p1-commands.test.ts +0 -1064
  31. package/agents/architect.yaml +0 -11
  32. package/agents/coder.yaml +0 -11
  33. package/agents/reviewer.yaml +0 -10
  34. package/agents/security-architect.yaml +0 -10
  35. package/agents/tester.yaml +0 -10
  36. package/docs/CONFIG_LOADING.md +0 -236
  37. package/docs/IMPLEMENTATION_COMPLETE.md +0 -421
  38. package/docs/MCP_CLIENT_GUIDE.md +0 -620
  39. package/docs/REFACTORING_SUMMARY.md +0 -247
  40. package/scripts/publish.sh +0 -46
  41. package/src/commands/agent.ts +0 -955
  42. package/src/commands/claims.ts +0 -317
  43. package/src/commands/completions.ts +0 -558
  44. package/src/commands/config.ts +0 -452
  45. package/src/commands/daemon.ts +0 -621
  46. package/src/commands/deployment.ts +0 -323
  47. package/src/commands/doctor.ts +0 -382
  48. package/src/commands/embeddings.ts +0 -686
  49. package/src/commands/hive-mind.ts +0 -928
  50. package/src/commands/hooks.ts +0 -2603
  51. package/src/commands/index.ts +0 -154
  52. package/src/commands/init.ts +0 -597
  53. package/src/commands/mcp.ts +0 -753
  54. package/src/commands/memory.ts +0 -1161
  55. package/src/commands/migrate.ts +0 -447
  56. package/src/commands/neural.ts +0 -253
  57. package/src/commands/performance.ts +0 -292
  58. package/src/commands/plugins.ts +0 -316
  59. package/src/commands/process.ts +0 -695
  60. package/src/commands/providers.ts +0 -259
  61. package/src/commands/security.ts +0 -288
  62. package/src/commands/session.ts +0 -891
  63. package/src/commands/start.ts +0 -457
  64. package/src/commands/status.ts +0 -736
  65. package/src/commands/swarm.ts +0 -648
  66. package/src/commands/task.ts +0 -792
  67. package/src/commands/workflow.ts +0 -742
  68. package/src/config-adapter.ts +0 -210
  69. package/src/index.ts +0 -443
  70. package/src/infrastructure/in-memory-repositories.ts +0 -310
  71. package/src/init/claudemd-generator.ts +0 -631
  72. package/src/init/executor.ts +0 -762
  73. package/src/init/helpers-generator.ts +0 -628
  74. package/src/init/index.ts +0 -60
  75. package/src/init/mcp-generator.ts +0 -83
  76. package/src/init/settings-generator.ts +0 -284
  77. package/src/init/statusline-generator.ts +0 -211
  78. package/src/init/types.ts +0 -447
  79. package/src/mcp-client.ts +0 -241
  80. package/src/mcp-server.ts +0 -577
  81. package/src/mcp-tools/agent-tools.ts +0 -466
  82. package/src/mcp-tools/config-tools.ts +0 -370
  83. package/src/mcp-tools/hive-mind-tools.ts +0 -521
  84. package/src/mcp-tools/hooks-tools.ts +0 -1888
  85. package/src/mcp-tools/index.ts +0 -16
  86. package/src/mcp-tools/memory-tools.ts +0 -270
  87. package/src/mcp-tools/session-tools.ts +0 -359
  88. package/src/mcp-tools/swarm-tools.ts +0 -105
  89. package/src/mcp-tools/task-tools.ts +0 -347
  90. package/src/mcp-tools/types.ts +0 -33
  91. package/src/mcp-tools/workflow-tools.ts +0 -573
  92. package/src/output.ts +0 -639
  93. package/src/parser.ts +0 -417
  94. package/src/prompt.ts +0 -619
  95. package/src/services/index.ts +0 -15
  96. package/src/services/worker-daemon.ts +0 -726
  97. package/src/suggest.ts +0 -245
  98. package/src/types.ts +0 -287
  99. package/tmp.json +0 -0
  100. package/tsconfig.json +0 -16
  101. package/tsconfig.tsbuildinfo +0 -1
  102. 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();