claude-code-workflow 6.3.36 → 6.3.37

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 (134) hide show
  1. package/.claude/commands/workflow/lite-fix.md +108 -9
  2. package/.claude/skills/ccw-loop/README.md +303 -0
  3. package/.claude/skills/ccw-loop/SKILL.md +259 -0
  4. package/.claude/skills/ccw-loop/phases/actions/action-complete.md +320 -0
  5. package/.claude/skills/ccw-loop/phases/actions/action-debug-with-file.md +485 -0
  6. package/.claude/skills/ccw-loop/phases/actions/action-develop-with-file.md +365 -0
  7. package/.claude/skills/ccw-loop/phases/actions/action-init.md +200 -0
  8. package/.claude/skills/ccw-loop/phases/actions/action-menu.md +192 -0
  9. package/.claude/skills/ccw-loop/phases/actions/action-validate-with-file.md +307 -0
  10. package/.claude/skills/ccw-loop/phases/orchestrator.md +486 -0
  11. package/.claude/skills/ccw-loop/phases/state-schema.md +474 -0
  12. package/.claude/skills/ccw-loop/specs/action-catalog.md +300 -0
  13. package/.claude/skills/ccw-loop/specs/loop-requirements.md +192 -0
  14. package/.claude/skills/ccw-loop/templates/progress-template.md +175 -0
  15. package/.claude/skills/ccw-loop/templates/understanding-template.md +303 -0
  16. package/.claude/skills/ccw-loop/templates/validation-template.md +258 -0
  17. package/ccw/dist/cli.d.ts.map +1 -1
  18. package/ccw/dist/cli.js +8 -1
  19. package/ccw/dist/cli.js.map +1 -1
  20. package/ccw/dist/commands/cli.d.ts.map +1 -1
  21. package/ccw/dist/commands/cli.js +14 -1
  22. package/ccw/dist/commands/cli.js.map +1 -1
  23. package/ccw/dist/commands/loop.d.ts +10 -0
  24. package/ccw/dist/commands/loop.d.ts.map +1 -0
  25. package/ccw/dist/commands/loop.js +289 -0
  26. package/ccw/dist/commands/loop.js.map +1 -0
  27. package/ccw/dist/core/dashboard-generator.d.ts.map +1 -1
  28. package/ccw/dist/core/dashboard-generator.js +4 -1
  29. package/ccw/dist/core/dashboard-generator.js.map +1 -1
  30. package/ccw/dist/core/routes/claude-routes.d.ts.map +1 -1
  31. package/ccw/dist/core/routes/claude-routes.js +5 -3
  32. package/ccw/dist/core/routes/claude-routes.js.map +1 -1
  33. package/ccw/dist/core/routes/cli-routes.d.ts +6 -0
  34. package/ccw/dist/core/routes/cli-routes.d.ts.map +1 -1
  35. package/ccw/dist/core/routes/cli-routes.js +42 -13
  36. package/ccw/dist/core/routes/cli-routes.js.map +1 -1
  37. package/ccw/dist/core/routes/cli-settings-routes.d.ts.map +1 -1
  38. package/ccw/dist/core/routes/cli-settings-routes.js +44 -0
  39. package/ccw/dist/core/routes/cli-settings-routes.js.map +1 -1
  40. package/ccw/dist/core/routes/codexlens/semantic-handlers.d.ts.map +1 -1
  41. package/ccw/dist/core/routes/codexlens/semantic-handlers.js +3 -2
  42. package/ccw/dist/core/routes/codexlens/semantic-handlers.js.map +1 -1
  43. package/ccw/dist/core/routes/core-memory-routes.d.ts.map +1 -1
  44. package/ccw/dist/core/routes/core-memory-routes.js +4 -2
  45. package/ccw/dist/core/routes/core-memory-routes.js.map +1 -1
  46. package/ccw/dist/core/routes/files-routes.d.ts.map +1 -1
  47. package/ccw/dist/core/routes/files-routes.js +4 -2
  48. package/ccw/dist/core/routes/files-routes.js.map +1 -1
  49. package/ccw/dist/core/routes/loop-routes.d.ts +24 -0
  50. package/ccw/dist/core/routes/loop-routes.d.ts.map +1 -0
  51. package/ccw/dist/core/routes/loop-routes.js +334 -0
  52. package/ccw/dist/core/routes/loop-routes.js.map +1 -0
  53. package/ccw/dist/core/routes/loop-v2-routes.d.ts +35 -0
  54. package/ccw/dist/core/routes/loop-v2-routes.d.ts.map +1 -0
  55. package/ccw/dist/core/routes/loop-v2-routes.js +1208 -0
  56. package/ccw/dist/core/routes/loop-v2-routes.js.map +1 -0
  57. package/ccw/dist/core/routes/memory-routes.d.ts.map +1 -1
  58. package/ccw/dist/core/routes/memory-routes.js +2 -1
  59. package/ccw/dist/core/routes/memory-routes.js.map +1 -1
  60. package/ccw/dist/core/routes/task-routes.d.ts +12 -0
  61. package/ccw/dist/core/routes/task-routes.d.ts.map +1 -0
  62. package/ccw/dist/core/routes/task-routes.js +321 -0
  63. package/ccw/dist/core/routes/task-routes.js.map +1 -0
  64. package/ccw/dist/core/routes/test-loop-routes.d.ts +11 -0
  65. package/ccw/dist/core/routes/test-loop-routes.d.ts.map +1 -0
  66. package/ccw/dist/core/routes/test-loop-routes.js +298 -0
  67. package/ccw/dist/core/routes/test-loop-routes.js.map +1 -0
  68. package/ccw/dist/core/server.d.ts.map +1 -1
  69. package/ccw/dist/core/server.js +43 -3
  70. package/ccw/dist/core/server.js.map +1 -1
  71. package/ccw/dist/core/websocket.d.ts +59 -0
  72. package/ccw/dist/core/websocket.d.ts.map +1 -1
  73. package/ccw/dist/core/websocket.js +34 -0
  74. package/ccw/dist/core/websocket.js.map +1 -1
  75. package/ccw/dist/tools/claude-cli-tools.d.ts +40 -0
  76. package/ccw/dist/tools/claude-cli-tools.d.ts.map +1 -1
  77. package/ccw/dist/tools/claude-cli-tools.js +119 -0
  78. package/ccw/dist/tools/claude-cli-tools.js.map +1 -1
  79. package/ccw/dist/tools/loop-manager.d.ts +84 -0
  80. package/ccw/dist/tools/loop-manager.d.ts.map +1 -0
  81. package/ccw/dist/tools/loop-manager.js +425 -0
  82. package/ccw/dist/tools/loop-manager.js.map +1 -0
  83. package/ccw/dist/tools/loop-state-manager.d.ts +47 -0
  84. package/ccw/dist/tools/loop-state-manager.d.ts.map +1 -0
  85. package/ccw/dist/tools/loop-state-manager.js +149 -0
  86. package/ccw/dist/tools/loop-state-manager.js.map +1 -0
  87. package/ccw/dist/tools/loop-task-manager.d.ts +138 -0
  88. package/ccw/dist/tools/loop-task-manager.d.ts.map +1 -0
  89. package/ccw/dist/tools/loop-task-manager.js +270 -0
  90. package/ccw/dist/tools/loop-task-manager.js.map +1 -0
  91. package/ccw/dist/types/index.d.ts +1 -0
  92. package/ccw/dist/types/index.d.ts.map +1 -1
  93. package/ccw/dist/types/index.js +1 -0
  94. package/ccw/dist/types/index.js.map +1 -1
  95. package/ccw/dist/types/loop.d.ts +257 -0
  96. package/ccw/dist/types/loop.d.ts.map +1 -0
  97. package/ccw/dist/types/loop.js +17 -0
  98. package/ccw/dist/types/loop.js.map +1 -0
  99. package/ccw/src/cli.ts +9 -1
  100. package/ccw/src/commands/cli.ts +14 -1
  101. package/ccw/src/commands/loop.ts +344 -0
  102. package/ccw/src/core/dashboard-generator.ts +4 -1
  103. package/ccw/src/core/routes/claude-routes.ts +5 -3
  104. package/ccw/src/core/routes/cli-routes.ts +47 -15
  105. package/ccw/src/core/routes/cli-settings-routes.ts +47 -0
  106. package/ccw/src/core/routes/codexlens/semantic-handlers.ts +3 -2
  107. package/ccw/src/core/routes/core-memory-routes.ts +4 -2
  108. package/ccw/src/core/routes/files-routes.ts +4 -2
  109. package/ccw/src/core/routes/loop-routes.ts +386 -0
  110. package/ccw/src/core/routes/loop-v2-routes.ts +1412 -0
  111. package/ccw/src/core/routes/memory-routes.ts +2 -1
  112. package/ccw/src/core/routes/task-routes.ts +361 -0
  113. package/ccw/src/core/routes/test-loop-routes.ts +312 -0
  114. package/ccw/src/core/server.ts +44 -3
  115. package/ccw/src/core/websocket.ts +104 -0
  116. package/ccw/src/templates/dashboard-css/12-cli-legacy.css +56 -0
  117. package/ccw/src/templates/dashboard-css/33-cli-stream-viewer.css +55 -0
  118. package/ccw/src/templates/dashboard-css/36-loop-monitor.css +1896 -0
  119. package/ccw/src/templates/dashboard-css/36-loop-monitor.css.backup +1877 -0
  120. package/ccw/src/templates/dashboard-js/components/cli-status.js +64 -3
  121. package/ccw/src/templates/dashboard-js/components/cli-stream-viewer.js +251 -110
  122. package/ccw/src/templates/dashboard-js/components/navigation.js +10 -0
  123. package/ccw/src/templates/dashboard-js/components/notifications.js +16 -0
  124. package/ccw/src/templates/dashboard-js/i18n.js +475 -1
  125. package/ccw/src/templates/dashboard-js/views/cli-manager.js +3 -2
  126. package/ccw/src/templates/dashboard-js/views/loop-monitor.js +3244 -0
  127. package/ccw/src/templates/dashboard.html +20 -2
  128. package/ccw/src/tools/claude-cli-tools.ts +143 -0
  129. package/ccw/src/tools/loop-manager.ts +519 -0
  130. package/ccw/src/tools/loop-state-manager.ts +173 -0
  131. package/ccw/src/tools/loop-task-manager.ts +380 -0
  132. package/ccw/src/types/index.ts +1 -0
  133. package/ccw/src/types/loop.ts +316 -0
  134. package/package.json +1 -1
@@ -6,7 +6,7 @@ import { resolvePath, getRecentPaths, normalizePathForDisplay } from '../utils/p
6
6
 
7
7
  // Import route handlers
8
8
  import { handleStatusRoutes } from './routes/status-routes.js';
9
- import { handleCliRoutes } from './routes/cli-routes.js';
9
+ import { handleCliRoutes, cleanupStaleExecutions } from './routes/cli-routes.js';
10
10
  import { handleCliSettingsRoutes } from './routes/cli-settings-routes.js';
11
11
  import { handleMemoryRoutes } from './routes/memory-routes.js';
12
12
  import { handleCoreMemoryRoutes } from './routes/core-memory-routes.js';
@@ -28,6 +28,10 @@ import { handleLiteLLMRoutes } from './routes/litellm-routes.js';
28
28
  import { handleLiteLLMApiRoutes } from './routes/litellm-api-routes.js';
29
29
  import { handleNavStatusRoutes } from './routes/nav-status-routes.js';
30
30
  import { handleAuthRoutes } from './routes/auth-routes.js';
31
+ import { handleLoopRoutes } from './routes/loop-routes.js';
32
+ import { handleLoopV2Routes } from './routes/loop-v2-routes.js';
33
+ import { handleTestLoopRoutes } from './routes/test-loop-routes.js';
34
+ import { handleTaskRoutes } from './routes/task-routes.js';
31
35
 
32
36
  // Import WebSocket handling
33
37
  import { handleWebSocketUpgrade, broadcastToClients, extractSessionIdFromPath } from './websocket.js';
@@ -102,7 +106,8 @@ const MODULE_CSS_FILES = [
102
106
  '31-api-settings.css',
103
107
  '32-issue-manager.css',
104
108
  '33-cli-stream-viewer.css',
105
- '34-discovery.css'
109
+ '34-discovery.css',
110
+ '36-loop-monitor.css'
106
111
  ];
107
112
 
108
113
  // Modular JS files in dependency order
@@ -162,6 +167,7 @@ const MODULE_FILES = [
162
167
  'views/help.js',
163
168
  'views/issue-manager.js',
164
169
  'views/issue-discovery.js',
170
+ 'views/loop-monitor.js',
165
171
  'main.js'
166
172
  ];
167
173
 
@@ -359,7 +365,14 @@ function generateServerDashboard(initialPath: string): string {
359
365
  // Read and concatenate modular JS files in dependency order
360
366
  let jsContent = MODULE_FILES.map(file => {
361
367
  const filePath = join(MODULE_JS_DIR, file);
362
- return existsSync(filePath) ? readFileSync(filePath, 'utf8') : '';
368
+ if (!existsSync(filePath)) {
369
+ console.error(`[Dashboard] Critical module file not found: ${filePath}`);
370
+ console.error(`[Dashboard] Expected path relative to: ${MODULE_JS_DIR}`);
371
+ console.error(`[Dashboard] Check that the file exists and is included in the build.`);
372
+ // Return empty string with error comment to make the issue visible in browser
373
+ return `console.error('[Dashboard] Module not loaded: ${file} (see server console for details)');\n`;
374
+ }
375
+ return readFileSync(filePath, 'utf8');
363
376
  }).join('\n\n');
364
377
 
365
378
  // Inject CSS content
@@ -556,6 +569,26 @@ export async function startServer(options: ServerOptions = {}): Promise<http.Ser
556
569
  if (await handleCcwRoutes(routeContext)) return;
557
570
  }
558
571
 
572
+ // Loop V2 routes (/api/loops/v2/*) - must be checked before v1
573
+ if (pathname.startsWith('/api/loops/v2')) {
574
+ if (await handleLoopV2Routes(routeContext)) return;
575
+ }
576
+
577
+ // Loop V1 routes (/api/loops/*) - backward compatibility
578
+ if (pathname.startsWith('/api/loops')) {
579
+ if (await handleLoopRoutes(routeContext)) return;
580
+ }
581
+
582
+ // Task routes (/api/tasks)
583
+ if (pathname.startsWith('/api/tasks')) {
584
+ if (await handleTaskRoutes(routeContext)) return;
585
+ }
586
+
587
+ // Test loop routes (/api/test/loop*)
588
+ if (pathname.startsWith('/api/test/loop')) {
589
+ if (await handleTestLoopRoutes(routeContext)) return;
590
+ }
591
+
559
592
  // Skills routes (/api/skills*)
560
593
  if (pathname.startsWith('/api/skills')) {
561
594
  if (await handleSkillsRoutes(routeContext)) return;
@@ -690,6 +723,14 @@ export async function startServer(options: ServerOptions = {}): Promise<http.Ser
690
723
  console.log(`WebSocket endpoint available at ws://${host}:${serverPort}/ws`);
691
724
  console.log(`Hook endpoint available at POST http://${host}:${serverPort}/api/hook`);
692
725
 
726
+ // Start periodic cleanup of stale CLI executions (every 2 minutes)
727
+ const CLEANUP_INTERVAL_MS = 2 * 60 * 1000;
728
+ const cleanupInterval = setInterval(cleanupStaleExecutions, CLEANUP_INTERVAL_MS);
729
+ server.on('close', () => {
730
+ clearInterval(cleanupInterval);
731
+ console.log('[Server] Stopped CLI execution cleanup interval');
732
+ });
733
+
693
734
  // Start health check service for all enabled providers
694
735
  try {
695
736
  const healthCheckService = getHealthCheckService();
@@ -5,6 +5,64 @@ import type { Duplex } from 'stream';
5
5
  // WebSocket clients for real-time notifications
6
6
  export const wsClients = new Set<Duplex>();
7
7
 
8
+ /**
9
+ * WebSocket message types for Loop monitoring
10
+ */
11
+ export type LoopMessageType =
12
+ | 'LOOP_STATE_UPDATE'
13
+ | 'LOOP_STEP_COMPLETED'
14
+ | 'LOOP_COMPLETED'
15
+ | 'LOOP_LOG_ENTRY';
16
+
17
+ /**
18
+ * Loop State Update - fired when loop status changes
19
+ */
20
+ export interface LoopStateUpdateMessage {
21
+ type: 'LOOP_STATE_UPDATE';
22
+ loop_id: string;
23
+ status: 'created' | 'running' | 'paused' | 'completed' | 'failed';
24
+ current_iteration: number;
25
+ current_cli_step: number;
26
+ updated_at: string;
27
+ timestamp: string;
28
+ }
29
+
30
+ /**
31
+ * Loop Step Completed - fired when a CLI step finishes
32
+ */
33
+ export interface LoopStepCompletedMessage {
34
+ type: 'LOOP_STEP_COMPLETED';
35
+ loop_id: string;
36
+ step_id: string;
37
+ exit_code: number;
38
+ duration_ms: number;
39
+ output: string;
40
+ timestamp: string;
41
+ }
42
+
43
+ /**
44
+ * Loop Completed - fired when entire loop finishes
45
+ */
46
+ export interface LoopCompletedMessage {
47
+ type: 'LOOP_COMPLETED';
48
+ loop_id: string;
49
+ final_status: 'completed' | 'failed';
50
+ total_iterations: number;
51
+ reason?: string;
52
+ timestamp: string;
53
+ }
54
+
55
+ /**
56
+ * Loop Log Entry - fired for streaming log lines
57
+ */
58
+ export interface LoopLogEntryMessage {
59
+ type: 'LOOP_LOG_ENTRY';
60
+ loop_id: string;
61
+ step_id: string;
62
+ line: string;
63
+ timestamp: string;
64
+ }
65
+
8
66
  export function handleWebSocketUpgrade(req: IncomingMessage, socket: Duplex, _head: Buffer): void {
9
67
  const header = req.headers['sec-websocket-key'];
10
68
  const key = Array.isArray(header) ? header[0] : header;
@@ -196,3 +254,49 @@ export function extractSessionIdFromPath(filePath: string): string | null {
196
254
 
197
255
  return null;
198
256
  }
257
+
258
+ /**
259
+ * Loop-specific broadcast with throttling
260
+ * Throttles LOOP_STATE_UPDATE messages to avoid flooding clients
261
+ */
262
+ let lastLoopBroadcast = 0;
263
+ const LOOP_BROADCAST_THROTTLE = 1000; // 1 second
264
+
265
+ export type LoopMessage =
266
+ | Omit<LoopStateUpdateMessage, 'timestamp'>
267
+ | Omit<LoopStepCompletedMessage, 'timestamp'>
268
+ | Omit<LoopCompletedMessage, 'timestamp'>
269
+ | Omit<LoopLogEntryMessage, 'timestamp'>;
270
+
271
+ /**
272
+ * Broadcast loop state update with throttling
273
+ */
274
+ export function broadcastLoopUpdate(message: LoopMessage): void {
275
+ const now = Date.now();
276
+
277
+ // Throttle LOOP_STATE_UPDATE to reduce WebSocket traffic
278
+ if (message.type === 'LOOP_STATE_UPDATE' && now - lastLoopBroadcast < LOOP_BROADCAST_THROTTLE) {
279
+ return;
280
+ }
281
+
282
+ lastLoopBroadcast = now;
283
+
284
+ broadcastToClients({
285
+ ...message,
286
+ timestamp: new Date().toISOString()
287
+ });
288
+ }
289
+
290
+ /**
291
+ * Broadcast loop log entry (no throttling)
292
+ * Used for streaming real-time logs to Dashboard
293
+ */
294
+ export function broadcastLoopLog(loop_id: string, step_id: string, line: string): void {
295
+ broadcastToClients({
296
+ type: 'LOOP_LOG_ENTRY',
297
+ loop_id,
298
+ step_id,
299
+ line,
300
+ timestamp: new Date().toISOString()
301
+ });
302
+ }
@@ -2,6 +2,41 @@
2
2
  * Legacy Container Styles (kept for compatibility)
3
3
  * ======================================== */
4
4
 
5
+ /* CLI Stream Recovery Badge Styles */
6
+ .cli-stream-recovery-badge {
7
+ font-size: 0.5625rem;
8
+ font-weight: 600;
9
+ padding: 0.125rem 0.375rem;
10
+ background: hsl(38 92% 50% / 0.15);
11
+ color: hsl(38 92% 50%);
12
+ border-radius: 9999px;
13
+ text-transform: uppercase;
14
+ letter-spacing: 0.03em;
15
+ margin-left: 0.375rem;
16
+ }
17
+
18
+ .cli-status-recovery-badge {
19
+ font-size: 0.625rem;
20
+ font-weight: 600;
21
+ padding: 0.125rem 0.5rem;
22
+ background: hsl(38 92% 50% / 0.15);
23
+ color: hsl(38 92% 50%);
24
+ border-radius: 0.25rem;
25
+ text-transform: uppercase;
26
+ letter-spacing: 0.03em;
27
+ margin-left: 0.5rem;
28
+ }
29
+
30
+ /* Tab styling for recovered sessions */
31
+ .cli-stream-tab.recovered {
32
+ border-color: hsl(38 92% 50% / 0.3);
33
+ }
34
+
35
+ .cli-stream-tab.recovered .cli-stream-recovery-badge {
36
+ background: hsl(38 92% 50% / 0.2);
37
+ color: hsl(38 92% 55%);
38
+ }
39
+
5
40
  /* Container */
6
41
  .cli-manager-container {
7
42
  display: flex;
@@ -66,6 +101,27 @@
66
101
  color: hsl(var(--muted-foreground));
67
102
  }
68
103
 
104
+ /* CLI status actions container */
105
+ .cli-status-actions {
106
+ display: flex;
107
+ align-items: center;
108
+ gap: 0.375rem;
109
+ }
110
+
111
+ /* Spin animation for sync icon */
112
+ @keyframes spin {
113
+ from {
114
+ transform: rotate(0deg);
115
+ }
116
+ to {
117
+ transform: rotate(360deg);
118
+ }
119
+ }
120
+
121
+ .spin {
122
+ animation: spin 1s linear infinite;
123
+ }
124
+
69
125
  .cli-tools-grid {
70
126
  display: grid;
71
127
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
@@ -161,6 +161,8 @@
161
161
  display: flex;
162
162
  align-items: center;
163
163
  gap: 8px;
164
+ /* Isolate from parent transform to fix native tooltip positioning */
165
+ will-change: transform;
164
166
  }
165
167
 
166
168
  .cli-stream-action-btn {
@@ -196,6 +198,10 @@
196
198
  color: hsl(var(--muted-foreground));
197
199
  cursor: pointer;
198
200
  transition: all 0.15s;
201
+ /* Fix native tooltip positioning under transformed parent */
202
+ position: relative;
203
+ z-index: 1;
204
+ transform: translateZ(0);
199
205
  }
200
206
 
201
207
  .cli-stream-close-btn:hover {
@@ -203,6 +209,49 @@
203
209
  color: hsl(var(--destructive));
204
210
  }
205
211
 
212
+ /* Icon-only action buttons (cleaner style matching close button) */
213
+ .cli-stream-icon-btn {
214
+ display: flex;
215
+ align-items: center;
216
+ justify-content: center;
217
+ width: 28px;
218
+ height: 28px;
219
+ padding: 0;
220
+ background: transparent;
221
+ border: none;
222
+ border-radius: 4px;
223
+ color: hsl(var(--muted-foreground));
224
+ cursor: pointer;
225
+ transition: all 0.15s;
226
+ /* Fix native tooltip positioning under transformed parent */
227
+ position: relative;
228
+ z-index: 1;
229
+ /* Create new stacking context to isolate from parent transform */
230
+ transform: translateZ(0);
231
+ }
232
+
233
+ .cli-stream-icon-btn svg {
234
+ width: 16px;
235
+ height: 16px;
236
+ }
237
+
238
+ .cli-stream-icon-btn:hover {
239
+ background: hsl(var(--hover));
240
+ color: hsl(var(--foreground));
241
+ }
242
+
243
+ .cli-stream-icon-btn:first-child:hover {
244
+ /* Clear completed - green/success tint */
245
+ background: hsl(142 76% 36% / 0.1);
246
+ color: hsl(142 76% 36%);
247
+ }
248
+
249
+ .cli-stream-icon-btn:nth-child(2):hover {
250
+ /* Clear all - orange/warning tint */
251
+ background: hsl(38 92% 50% / 0.1);
252
+ color: hsl(38 92% 50%);
253
+ }
254
+
206
255
  /* ===== Tab Bar ===== */
207
256
  .cli-stream-tabs {
208
257
  display: flex;
@@ -787,6 +836,12 @@
787
836
  animation: streamBadgePulse 1.5s ease-in-out infinite;
788
837
  }
789
838
 
839
+ .cli-stream-badge.has-completed {
840
+ display: flex;
841
+ background: hsl(var(--muted) / 0.8);
842
+ color: hsl(var(--muted-foreground));
843
+ }
844
+
790
845
  @keyframes streamBadgePulse {
791
846
  0%, 100% { transform: scale(1); }
792
847
  50% { transform: scale(1.15); }