@claude-flow/cli 3.0.0-alpha.2 → 3.0.0-alpha.20

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