@vizzly-testing/cli 0.14.0 → 0.15.1

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 (140) hide show
  1. package/dist/cli.js +70 -68
  2. package/dist/commands/doctor.js +30 -34
  3. package/dist/commands/finalize.js +24 -23
  4. package/dist/commands/init.js +30 -28
  5. package/dist/commands/login.js +49 -55
  6. package/dist/commands/logout.js +14 -19
  7. package/dist/commands/project.js +83 -103
  8. package/dist/commands/run.js +77 -89
  9. package/dist/commands/status.js +48 -49
  10. package/dist/commands/tdd-daemon.js +90 -86
  11. package/dist/commands/tdd.js +59 -88
  12. package/dist/commands/upload.js +57 -57
  13. package/dist/commands/whoami.js +40 -45
  14. package/dist/index.js +2 -5
  15. package/dist/plugin-loader.js +15 -17
  16. package/dist/reporter/reporter-bundle.css +1 -1
  17. package/dist/reporter/reporter-bundle.iife.js +74 -41
  18. package/dist/sdk/index.js +36 -45
  19. package/dist/server/handlers/api-handler.js +14 -15
  20. package/dist/server/handlers/tdd-handler.js +34 -37
  21. package/dist/server/http-server.js +75 -869
  22. package/dist/server/middleware/cors.js +22 -0
  23. package/dist/server/middleware/json-parser.js +35 -0
  24. package/dist/server/middleware/response.js +79 -0
  25. package/dist/server/routers/assets.js +91 -0
  26. package/dist/server/routers/auth.js +144 -0
  27. package/dist/server/routers/baseline.js +163 -0
  28. package/dist/server/routers/cloud-proxy.js +146 -0
  29. package/dist/server/routers/config.js +126 -0
  30. package/dist/server/routers/dashboard.js +130 -0
  31. package/dist/server/routers/health.js +61 -0
  32. package/dist/server/routers/projects.js +168 -0
  33. package/dist/server/routers/screenshot.js +86 -0
  34. package/dist/services/auth-service.js +1 -1
  35. package/dist/services/build-manager.js +13 -40
  36. package/dist/services/config-service.js +2 -4
  37. package/dist/services/html-report-generator.js +6 -5
  38. package/dist/services/index.js +64 -0
  39. package/dist/services/project-service.js +121 -40
  40. package/dist/services/screenshot-server.js +9 -9
  41. package/dist/services/server-manager.js +11 -18
  42. package/dist/services/static-report-generator.js +3 -4
  43. package/dist/services/tdd-service.js +246 -103
  44. package/dist/services/test-runner.js +24 -25
  45. package/dist/services/uploader.js +5 -4
  46. package/dist/types/commands/init.d.ts +1 -2
  47. package/dist/types/index.d.ts +2 -3
  48. package/dist/types/plugin-loader.d.ts +1 -2
  49. package/dist/types/reporter/src/api/client.d.ts +178 -0
  50. package/dist/types/reporter/src/components/app-router.d.ts +1 -3
  51. package/dist/types/reporter/src/components/code-block.d.ts +4 -0
  52. package/dist/types/reporter/src/components/comparison/comparison-modes/onion-skin-mode.d.ts +10 -0
  53. package/dist/types/reporter/src/components/comparison/comparison-modes/overlay-mode.d.ts +11 -0
  54. package/dist/types/reporter/src/components/comparison/comparison-modes/shared/base-comparison-mode.d.ts +14 -0
  55. package/dist/types/reporter/src/components/comparison/comparison-modes/shared/image-renderer.d.ts +30 -0
  56. package/dist/types/reporter/src/components/comparison/comparison-modes/toggle-view.d.ts +8 -0
  57. package/dist/types/reporter/src/components/comparison/comparison-viewer.d.ts +4 -0
  58. package/dist/types/reporter/src/components/comparison/screenshot-display.d.ts +16 -0
  59. package/dist/types/reporter/src/components/design-system/alert.d.ts +9 -0
  60. package/dist/types/reporter/src/components/design-system/badge.d.ts +17 -0
  61. package/dist/types/reporter/src/components/design-system/button.d.ts +19 -0
  62. package/dist/types/reporter/src/components/design-system/card.d.ts +31 -0
  63. package/dist/types/reporter/src/components/design-system/empty-state.d.ts +13 -0
  64. package/dist/types/reporter/src/components/design-system/form-controls.d.ts +44 -0
  65. package/dist/types/reporter/src/components/design-system/health-ring.d.ts +7 -0
  66. package/dist/types/reporter/src/components/design-system/index.d.ts +11 -0
  67. package/dist/types/reporter/src/components/design-system/modal.d.ts +10 -0
  68. package/dist/types/reporter/src/components/design-system/skeleton.d.ts +19 -0
  69. package/dist/types/reporter/src/components/design-system/spinner.d.ts +10 -0
  70. package/dist/types/reporter/src/components/design-system/tabs.d.ts +13 -0
  71. package/dist/types/reporter/src/components/layout/header.d.ts +5 -0
  72. package/dist/types/reporter/src/components/layout/index.d.ts +2 -0
  73. package/dist/types/reporter/src/components/layout/layout.d.ts +6 -0
  74. package/dist/types/reporter/src/components/views/builds-view.d.ts +1 -0
  75. package/dist/types/reporter/src/components/views/comparison-detail-view.d.ts +1 -4
  76. package/dist/types/reporter/src/components/views/comparisons-view.d.ts +1 -6
  77. package/dist/types/reporter/src/components/views/stats-view.d.ts +1 -6
  78. package/dist/types/reporter/src/components/waiting-for-screenshots.d.ts +1 -0
  79. package/dist/types/reporter/src/hooks/queries/use-auth-queries.d.ts +15 -0
  80. package/dist/types/reporter/src/hooks/queries/use-cloud-queries.d.ts +6 -0
  81. package/dist/types/reporter/src/hooks/queries/use-config-queries.d.ts +6 -0
  82. package/dist/types/reporter/src/hooks/queries/use-tdd-queries.d.ts +9 -0
  83. package/dist/types/reporter/src/lib/query-client.d.ts +2 -0
  84. package/dist/types/reporter/src/lib/query-keys.d.ts +13 -0
  85. package/dist/types/sdk/index.d.ts +2 -4
  86. package/dist/types/server/handlers/tdd-handler.d.ts +2 -0
  87. package/dist/types/server/http-server.d.ts +1 -1
  88. package/dist/types/server/middleware/cors.d.ts +11 -0
  89. package/dist/types/server/middleware/json-parser.d.ts +10 -0
  90. package/dist/types/server/middleware/response.d.ts +50 -0
  91. package/dist/types/server/routers/assets.d.ts +6 -0
  92. package/dist/types/server/routers/auth.d.ts +9 -0
  93. package/dist/types/server/routers/baseline.d.ts +13 -0
  94. package/dist/types/server/routers/cloud-proxy.d.ts +11 -0
  95. package/dist/types/server/routers/config.d.ts +9 -0
  96. package/dist/types/server/routers/dashboard.d.ts +6 -0
  97. package/dist/types/server/routers/health.d.ts +11 -0
  98. package/dist/types/server/routers/projects.d.ts +9 -0
  99. package/dist/types/server/routers/screenshot.d.ts +11 -0
  100. package/dist/types/services/build-manager.d.ts +4 -3
  101. package/dist/types/services/config-service.d.ts +2 -3
  102. package/dist/types/services/index.d.ts +7 -0
  103. package/dist/types/services/project-service.d.ts +6 -4
  104. package/dist/types/services/screenshot-server.d.ts +5 -5
  105. package/dist/types/services/server-manager.d.ts +5 -3
  106. package/dist/types/services/tdd-service.d.ts +12 -1
  107. package/dist/types/services/test-runner.d.ts +3 -3
  108. package/dist/types/utils/output.d.ts +84 -0
  109. package/dist/utils/config-loader.js +24 -48
  110. package/dist/utils/global-config.js +2 -17
  111. package/dist/utils/output.js +445 -0
  112. package/dist/utils/security.js +3 -4
  113. package/docs/api-reference.md +0 -1
  114. package/docs/plugins.md +33 -34
  115. package/package.json +3 -2
  116. package/dist/container/index.js +0 -215
  117. package/dist/services/base-service.js +0 -154
  118. package/dist/types/container/index.d.ts +0 -59
  119. package/dist/types/reporter/src/components/comparison/viewer-modes/onion-viewer.d.ts +0 -3
  120. package/dist/types/reporter/src/components/comparison/viewer-modes/overlay-viewer.d.ts +0 -3
  121. package/dist/types/reporter/src/components/comparison/viewer-modes/side-by-side-viewer.d.ts +0 -3
  122. package/dist/types/reporter/src/components/comparison/viewer-modes/toggle-viewer.d.ts +0 -3
  123. package/dist/types/reporter/src/components/dashboard/dashboard-header.d.ts +0 -5
  124. package/dist/types/reporter/src/components/dashboard/dashboard-stats.d.ts +0 -4
  125. package/dist/types/reporter/src/components/dashboard/empty-state.d.ts +0 -8
  126. package/dist/types/reporter/src/components/ui/form-field.d.ts +0 -16
  127. package/dist/types/reporter/src/components/ui/status-badge.d.ts +0 -5
  128. package/dist/types/reporter/src/hooks/use-auth.d.ts +0 -10
  129. package/dist/types/reporter/src/hooks/use-baseline-actions.d.ts +0 -5
  130. package/dist/types/reporter/src/hooks/use-config.d.ts +0 -9
  131. package/dist/types/reporter/src/hooks/use-projects.d.ts +0 -10
  132. package/dist/types/reporter/src/hooks/use-report-data.d.ts +0 -7
  133. package/dist/types/reporter/src/hooks/use-vizzly-api.d.ts +0 -9
  134. package/dist/types/services/base-service.d.ts +0 -71
  135. package/dist/types/utils/console-ui.d.ts +0 -61
  136. package/dist/types/utils/logger-factory.d.ts +0 -26
  137. package/dist/types/utils/logger.d.ts +0 -79
  138. package/dist/utils/console-ui.js +0 -241
  139. package/dist/utils/logger-factory.js +0 -76
  140. package/dist/utils/logger.js +0 -231
@@ -2,7 +2,7 @@ import { writeFileSync, readFileSync, existsSync, unlinkSync, mkdirSync } from '
2
2
  import { join } from 'path';
3
3
  import { homedir } from 'os';
4
4
  import { spawn } from 'child_process';
5
- import { ConsoleUI } from '../utils/console-ui.js';
5
+ import * as output from '../utils/output.js';
6
6
  import { tddCommand } from './tdd.js';
7
7
 
8
8
  /**
@@ -11,7 +11,7 @@ import { tddCommand } from './tdd.js';
11
11
  * @param {Object} globalOptions - Global CLI options
12
12
  */
13
13
  export async function tddStartCommand(options = {}, globalOptions = {}) {
14
- const ui = new ConsoleUI({
14
+ output.configure({
15
15
  json: globalOptions.json,
16
16
  verbose: globalOptions.verbose,
17
17
  color: !globalOptions.noColor
@@ -19,9 +19,9 @@ export async function tddStartCommand(options = {}, globalOptions = {}) {
19
19
 
20
20
  // Check if server already running
21
21
  if (await isServerRunning(options.port || 47392)) {
22
- const port = options.port || 47392;
23
- ui.info(`TDD server already running at http://localhost:${port}`);
24
- ui.info(`Dashboard: http://localhost:${port}/dashboard`);
22
+ let port = options.port || 47392;
23
+ output.info(`TDD server already running at http://localhost:${port}`);
24
+ output.info(`Dashboard: http://localhost:${port}/dashboard`);
25
25
  if (options.open) {
26
26
  openDashboard(port);
27
27
  }
@@ -29,21 +29,21 @@ export async function tddStartCommand(options = {}, globalOptions = {}) {
29
29
  }
30
30
  try {
31
31
  // Ensure .vizzly directory exists
32
- const vizzlyDir = join(process.cwd(), '.vizzly');
32
+ let vizzlyDir = join(process.cwd(), '.vizzly');
33
33
  if (!existsSync(vizzlyDir)) {
34
34
  mkdirSync(vizzlyDir, {
35
35
  recursive: true
36
36
  });
37
37
  }
38
- const port = options.port || 47392;
38
+ let port = options.port || 47392;
39
39
 
40
40
  // Show loading indicator if downloading baselines (but not in verbose mode since child shows progress)
41
41
  if (options.baselineBuild && !globalOptions.verbose) {
42
- ui.startSpinner(`Downloading baselines from build ${options.baselineBuild}...`);
42
+ output.startSpinner(`Downloading baselines from build ${options.baselineBuild}...`);
43
43
  }
44
44
 
45
45
  // Spawn child process with stdio inherited during init for direct error visibility
46
- const child = spawn(process.execPath, [process.argv[1],
46
+ let child = spawn(process.execPath, [process.argv[1],
47
47
  // CLI entry point
48
48
  'tdd', 'start', '--daemon-child',
49
49
  // Special flag for child process
@@ -72,18 +72,22 @@ export async function tddStartCommand(options = {}, globalOptions = {}) {
72
72
  });
73
73
 
74
74
  // Timeout after 30 seconds to prevent indefinite wait
75
- setTimeout(() => {
75
+ let timeoutId = setTimeout(() => {
76
76
  if (!initComplete && !initFailed) {
77
77
  initFailed = true;
78
78
  resolve();
79
79
  }
80
80
  }, 30000);
81
+
82
+ // Clear timeout if we resolve early
83
+ child.on('disconnect', () => clearTimeout(timeoutId));
84
+ child.on('exit', () => clearTimeout(timeoutId));
81
85
  });
82
86
  if (initFailed) {
83
87
  if (options.baselineBuild && !globalOptions.verbose) {
84
- ui.stopSpinner();
88
+ output.stopSpinner();
85
89
  }
86
- ui.error('TDD server failed to start');
90
+ output.error('TDD server failed to start');
87
91
  process.exit(1);
88
92
  }
89
93
 
@@ -91,32 +95,32 @@ export async function tddStartCommand(options = {}, globalOptions = {}) {
91
95
  child.unref();
92
96
 
93
97
  // Verify server started with retries
94
- const maxRetries = 10;
95
- const retryDelay = 200; // Start with 200ms
98
+ let maxRetries = 10;
99
+ let retryDelay = 200; // Start with 200ms
96
100
  let running = false;
97
101
  for (let i = 0; i < maxRetries && !running; i++) {
98
102
  await new Promise(resolve => setTimeout(resolve, retryDelay * (i + 1)));
99
103
  running = await isServerRunning(port);
100
104
  }
101
105
  if (options.baselineBuild && !globalOptions.verbose) {
102
- ui.stopSpinner();
106
+ output.stopSpinner();
103
107
  }
104
108
  if (!running) {
105
- ui.error('Failed to start TDD server - server not responding to health checks');
109
+ output.error('Failed to start TDD server - server not responding to health checks');
106
110
  process.exit(1);
107
111
  }
108
- ui.success(`TDD server started at http://localhost:${port}`);
112
+ output.success(`TDD server started at http://localhost:${port}`);
109
113
 
110
114
  // Write server info to global location for SDK discovery (iOS/Swift can read this)
111
115
  try {
112
- const globalVizzlyDir = join(homedir(), '.vizzly');
116
+ let globalVizzlyDir = join(homedir(), '.vizzly');
113
117
  if (!existsSync(globalVizzlyDir)) {
114
118
  mkdirSync(globalVizzlyDir, {
115
119
  recursive: true
116
120
  });
117
121
  }
118
- const globalServerFile = join(globalVizzlyDir, 'server.json');
119
- const serverInfo = {
122
+ let globalServerFile = join(globalVizzlyDir, 'server.json');
123
+ let serverInfo = {
120
124
  pid: child.pid,
121
125
  port: port.toString(),
122
126
  startTime: Date.now()
@@ -125,27 +129,27 @@ export async function tddStartCommand(options = {}, globalOptions = {}) {
125
129
  } catch {
126
130
  // Non-fatal, SDK can still use health check
127
131
  }
128
- ui.info('');
129
- ui.info('Dashboard:');
130
- ui.info(` http://localhost:${port}/`);
131
- ui.info('');
132
- ui.info('Available views:');
133
- ui.info(` Comparisons: http://localhost:${port}/`);
134
- ui.info(` Stats: http://localhost:${port}/stats`);
135
- ui.info(` Settings: http://localhost:${port}/settings`);
136
- ui.info(` Projects: http://localhost:${port}/projects`);
137
- ui.info('');
138
- ui.info('Next steps:');
139
- ui.info(' 1. Run your tests in watch mode (e.g., npm test -- --watch)');
140
- ui.info(' 2. View live visual comparisons in the dashboard');
141
- ui.info(' 3. Accept/reject baselines directly in the UI');
142
- ui.info('');
143
- ui.info('Stop server: npx vizzly dev stop');
132
+ output.blank();
133
+ output.info('Dashboard:');
134
+ output.info(` http://localhost:${port}/`);
135
+ output.blank();
136
+ output.info('Available views:');
137
+ output.info(` Comparisons: http://localhost:${port}/`);
138
+ output.info(` Stats: http://localhost:${port}/stats`);
139
+ output.info(` Settings: http://localhost:${port}/settings`);
140
+ output.info(` Projects: http://localhost:${port}/projects`);
141
+ output.blank();
142
+ output.info('Next steps:');
143
+ output.info(' 1. Run your tests in watch mode (e.g., npm test -- --watch)');
144
+ output.info(' 2. View live visual comparisons in the dashboard');
145
+ output.info(' 3. Accept/reject baselines directly in the UI');
146
+ output.blank();
147
+ output.info('Stop server: npx vizzly dev stop');
144
148
  if (options.open) {
145
149
  openDashboard(port);
146
150
  }
147
151
  } catch (error) {
148
- ui.error('Failed to start TDD daemon', error);
152
+ output.error('Failed to start TDD daemon', error);
149
153
  process.exit(1);
150
154
  }
151
155
  }
@@ -156,11 +160,11 @@ export async function tddStartCommand(options = {}, globalOptions = {}) {
156
160
  * @private
157
161
  */
158
162
  export async function runDaemonChild(options = {}, globalOptions = {}) {
159
- const vizzlyDir = join(process.cwd(), '.vizzly');
160
- const port = options.port || 47392;
163
+ let vizzlyDir = join(process.cwd(), '.vizzly');
164
+ let port = options.port || 47392;
161
165
  try {
162
166
  // Use existing tddCommand but with daemon mode
163
- const {
167
+ let {
164
168
  cleanup
165
169
  } = await tddCommand(null,
166
170
  // No test command - server only
@@ -175,9 +179,9 @@ export async function runDaemonChild(options = {}, globalOptions = {}) {
175
179
  }
176
180
 
177
181
  // Store our PID for the stop command
178
- const pidFile = join(vizzlyDir, 'server.pid');
182
+ let pidFile = join(vizzlyDir, 'server.pid');
179
183
  writeFileSync(pidFile, process.pid.toString());
180
- const serverInfo = {
184
+ let serverInfo = {
181
185
  pid: process.pid,
182
186
  port: port,
183
187
  startTime: Date.now()
@@ -185,16 +189,16 @@ export async function runDaemonChild(options = {}, globalOptions = {}) {
185
189
  writeFileSync(join(vizzlyDir, 'server.json'), JSON.stringify(serverInfo, null, 2));
186
190
 
187
191
  // Set up graceful shutdown
188
- const handleShutdown = async () => {
192
+ let handleShutdown = async () => {
189
193
  try {
190
194
  // Clean up PID files
191
195
  if (existsSync(pidFile)) unlinkSync(pidFile);
192
- const serverFile = join(vizzlyDir, 'server.json');
196
+ let serverFile = join(vizzlyDir, 'server.json');
193
197
  if (existsSync(serverFile)) unlinkSync(serverFile);
194
198
 
195
199
  // Clean up global server file
196
200
  try {
197
- const globalServerFile = join(homedir(), '.vizzly', 'server.json');
201
+ let globalServerFile = join(homedir(), '.vizzly', 'server.json');
198
202
  if (existsSync(globalServerFile)) unlinkSync(globalServerFile);
199
203
  } catch {
200
204
  // Non-fatal
@@ -227,14 +231,14 @@ export async function runDaemonChild(options = {}, globalOptions = {}) {
227
231
  * @param {Object} globalOptions - Global CLI options
228
232
  */
229
233
  export async function tddStopCommand(options = {}, globalOptions = {}) {
230
- const ui = new ConsoleUI({
234
+ output.configure({
231
235
  json: globalOptions.json,
232
236
  verbose: globalOptions.verbose,
233
237
  color: !globalOptions.noColor
234
238
  });
235
- const vizzlyDir = join(process.cwd(), '.vizzly');
236
- const pidFile = join(vizzlyDir, 'server.pid');
237
- const serverFile = join(vizzlyDir, 'server.json');
239
+ let vizzlyDir = join(process.cwd(), '.vizzly');
240
+ let pidFile = join(vizzlyDir, 'server.pid');
241
+ let serverFile = join(vizzlyDir, 'server.json');
238
242
 
239
243
  // First try to find process by PID file
240
244
  let pid = null;
@@ -247,10 +251,10 @@ export async function tddStopCommand(options = {}, globalOptions = {}) {
247
251
  }
248
252
 
249
253
  // If no PID file or invalid, try to find by port using lsof
250
- const port = options.port || 47392;
254
+ let port = options.port || 47392;
251
255
  if (!pid) {
252
256
  try {
253
- const lsofProcess = spawn('lsof', ['-ti', `:${port}`], {
257
+ let lsofProcess = spawn('lsof', ['-ti', `:${port}`], {
254
258
  stdio: 'pipe'
255
259
  });
256
260
  let lsofOutput = '';
@@ -260,7 +264,7 @@ export async function tddStopCommand(options = {}, globalOptions = {}) {
260
264
  await new Promise(resolve => {
261
265
  lsofProcess.on('close', code => {
262
266
  if (code === 0 && lsofOutput.trim()) {
263
- const foundPid = parseInt(lsofOutput.trim().split('\n')[0], 10);
267
+ let foundPid = parseInt(lsofOutput.trim().split('\n')[0], 10);
264
268
  if (foundPid && !isNaN(foundPid)) {
265
269
  pid = foundPid;
266
270
  }
@@ -277,7 +281,7 @@ export async function tddStopCommand(options = {}, globalOptions = {}) {
277
281
  }
278
282
  }
279
283
  if (!pid) {
280
- ui.warning('No TDD server running');
284
+ output.warn('No TDD server running');
281
285
 
282
286
  // Clean up any stale files
283
287
  if (existsSync(pidFile)) unlinkSync(pidFile);
@@ -287,7 +291,7 @@ export async function tddStopCommand(options = {}, globalOptions = {}) {
287
291
  try {
288
292
  // Try to kill the process gracefully
289
293
  process.kill(pid, 'SIGTERM');
290
- ui.info(`Stopping TDD server (PID: ${pid})...`);
294
+ output.info(`Stopping TDD server (PID: ${pid})...`);
291
295
 
292
296
  // Give it a moment to shut down gracefully
293
297
  await new Promise(resolve => setTimeout(resolve, 2000));
@@ -297,7 +301,7 @@ export async function tddStopCommand(options = {}, globalOptions = {}) {
297
301
  process.kill(pid, 0); // Just check if process exists
298
302
  // If we get here, process is still running, force kill it
299
303
  process.kill(pid, 'SIGKILL');
300
- ui.info('Force killed TDD server');
304
+ output.info('Force killed TDD server');
301
305
  } catch {
302
306
  // Process is gone, which is what we want
303
307
  }
@@ -305,15 +309,15 @@ export async function tddStopCommand(options = {}, globalOptions = {}) {
305
309
  // Clean up files
306
310
  if (existsSync(pidFile)) unlinkSync(pidFile);
307
311
  if (existsSync(serverFile)) unlinkSync(serverFile);
308
- ui.success('TDD server stopped');
312
+ output.success('TDD server stopped');
309
313
  } catch (error) {
310
314
  if (error.code === 'ESRCH') {
311
315
  // Process not found - clean up stale files
312
- ui.warning('TDD server was not running (cleaning up stale files)');
316
+ output.warn('TDD server was not running (cleaning up stale files)');
313
317
  if (existsSync(pidFile)) unlinkSync(pidFile);
314
318
  if (existsSync(serverFile)) unlinkSync(serverFile);
315
319
  } else {
316
- ui.error('Error stopping TDD server', error);
320
+ output.error('Error stopping TDD server', error);
317
321
  }
318
322
  }
319
323
  }
@@ -324,20 +328,20 @@ export async function tddStopCommand(options = {}, globalOptions = {}) {
324
328
  * @param {Object} globalOptions - Global CLI options
325
329
  */
326
330
  export async function tddStatusCommand(options, globalOptions = {}) {
327
- const ui = new ConsoleUI({
331
+ output.configure({
328
332
  json: globalOptions.json,
329
333
  verbose: globalOptions.verbose,
330
334
  color: !globalOptions.noColor
331
335
  });
332
- const vizzlyDir = join(process.cwd(), '.vizzly');
333
- const pidFile = join(vizzlyDir, 'server.pid');
334
- const serverFile = join(vizzlyDir, 'server.json');
336
+ let vizzlyDir = join(process.cwd(), '.vizzly');
337
+ let pidFile = join(vizzlyDir, 'server.pid');
338
+ let serverFile = join(vizzlyDir, 'server.json');
335
339
  if (!existsSync(pidFile)) {
336
- ui.info('TDD server not running');
340
+ output.info('TDD server not running');
337
341
  return;
338
342
  }
339
343
  try {
340
- const pid = parseInt(readFileSync(pidFile, 'utf8').trim(), 10);
344
+ let pid = parseInt(readFileSync(pidFile, 'utf8').trim(), 10);
341
345
 
342
346
  // Check if process is actually running
343
347
  process.kill(pid, 0); // Signal 0 just checks if process exists
@@ -350,40 +354,40 @@ export async function tddStatusCommand(options, globalOptions = {}) {
350
354
  }
351
355
 
352
356
  // Try to check health endpoint
353
- const health = await checkServerHealth(serverInfo.port);
357
+ let health = await checkServerHealth(serverInfo.port);
354
358
  if (health.running) {
355
- ui.success(`TDD server running (PID: ${pid})`);
356
- ui.info(`Dashboard: http://localhost:${serverInfo.port}/`);
357
- ui.info('');
358
- ui.info('Available views:');
359
- ui.info(` Comparisons: http://localhost:${serverInfo.port}/`);
360
- ui.info(` Stats: http://localhost:${serverInfo.port}/stats`);
361
- ui.info(` Settings: http://localhost:${serverInfo.port}/settings`);
362
- ui.info(` Projects: http://localhost:${serverInfo.port}/projects`);
359
+ output.success(`TDD server running (PID: ${pid})`);
360
+ output.info(`Dashboard: http://localhost:${serverInfo.port}/`);
361
+ output.blank();
362
+ output.info('Available views:');
363
+ output.info(` Comparisons: http://localhost:${serverInfo.port}/`);
364
+ output.info(` Stats: http://localhost:${serverInfo.port}/stats`);
365
+ output.info(` Settings: http://localhost:${serverInfo.port}/settings`);
366
+ output.info(` Projects: http://localhost:${serverInfo.port}/projects`);
363
367
  if (serverInfo.startTime) {
364
- const uptime = Math.floor((Date.now() - serverInfo.startTime) / 1000);
365
- const hours = Math.floor(uptime / 3600);
366
- const minutes = Math.floor(uptime % 3600 / 60);
367
- const seconds = uptime % 60;
368
+ let uptime = Math.floor((Date.now() - serverInfo.startTime) / 1000);
369
+ let hours = Math.floor(uptime / 3600);
370
+ let minutes = Math.floor(uptime % 3600 / 60);
371
+ let seconds = uptime % 60;
368
372
  let uptimeStr = '';
369
373
  if (hours > 0) uptimeStr += `${hours}h `;
370
374
  if (minutes > 0 || hours > 0) uptimeStr += `${minutes}m `;
371
375
  uptimeStr += `${seconds}s`;
372
- ui.info('');
373
- ui.info(`Uptime: ${uptimeStr}`);
376
+ output.blank();
377
+ output.info(`Uptime: ${uptimeStr}`);
374
378
  }
375
379
  } else {
376
- ui.warning('TDD server process exists but not responding to health checks');
380
+ output.warn('TDD server process exists but not responding to health checks');
377
381
  }
378
382
  } catch (error) {
379
383
  if (error.code === 'ESRCH') {
380
- ui.warning('TDD server process not found (cleaning up stale files)');
384
+ output.warn('TDD server process not found (cleaning up stale files)');
381
385
  unlinkSync(pidFile);
382
386
  if (existsSync(serverFile)) {
383
387
  unlinkSync(serverFile);
384
388
  }
385
389
  } else {
386
- ui.error('Error checking TDD server status', error);
390
+ output.error('Error checking TDD server status', error);
387
391
  }
388
392
  }
389
393
  }
@@ -394,7 +398,7 @@ export async function tddStatusCommand(options, globalOptions = {}) {
394
398
  */
395
399
  async function isServerRunning(port = 47392) {
396
400
  try {
397
- const health = await checkServerHealth(port);
401
+ let health = await checkServerHealth(port);
398
402
  return health.running;
399
403
  } catch {
400
404
  return false;
@@ -407,8 +411,8 @@ async function isServerRunning(port = 47392) {
407
411
  */
408
412
  async function checkServerHealth(port = 47392) {
409
413
  try {
410
- const response = await fetch(`http://localhost:${port}/health`);
411
- const data = await response.json();
414
+ let response = await fetch(`http://localhost:${port}/health`);
415
+ let data = await response.json();
412
416
  return {
413
417
  running: response.ok,
414
418
  port: data.port,
@@ -426,7 +430,7 @@ async function checkServerHealth(port = 47392) {
426
430
  * @private
427
431
  */
428
432
  function openDashboard(port = 47392) {
429
- const url = `http://localhost:${port}/dashboard`;
433
+ let url = `http://localhost:${port}/dashboard`;
430
434
 
431
435
  // Cross-platform open command
432
436
  let openCmd;