claude-code-workflow 6.3.4 → 6.3.6

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 (111) hide show
  1. package/.claude/agents/issue-plan-agent.md +859 -0
  2. package/.claude/agents/issue-queue-agent.md +702 -0
  3. package/.claude/commands/issue/execute.md +453 -0
  4. package/.claude/commands/issue/manage.md +865 -0
  5. package/.claude/commands/issue/new.md +484 -0
  6. package/.claude/commands/issue/plan.md +421 -0
  7. package/.claude/commands/issue/queue.md +354 -0
  8. package/.claude/commands/{clean.md → workflow/clean.md} +5 -5
  9. package/.claude/commands/workflow/docs/analyze.md +1467 -0
  10. package/.claude/commands/workflow/docs/copyright.md +1265 -0
  11. package/.claude/commands/workflow/execute.md +0 -1
  12. package/.claude/commands/workflow/tools/conflict-resolution.md +76 -240
  13. package/.claude/commands/workflow/tools/context-gather.md +0 -2
  14. package/.claude/commands/workflow/tools/task-generate-agent.md +81 -8
  15. package/.claude/commands/workflow/tools/task-generate-tdd.md +0 -9
  16. package/.claude/commands/workflow/tools/test-context-gather.md +2 -3
  17. package/.claude/commands/workflow/tools/test-task-generate.md +0 -2
  18. package/.claude/skills/_shared/mermaid-utils.md +584 -0
  19. package/.claude/skills/command-guide/reference/agents/action-planning-agent.md +0 -2
  20. package/.claude/skills/command-guide/reference/commands/workflow/execute.md +1 -1
  21. package/.claude/skills/command-guide/reference/commands/workflow/tools/context-gather.md +1 -2
  22. package/.claude/skills/command-guide/reference/commands/workflow/tools/task-generate-tdd.md +1 -8
  23. package/.claude/skills/command-guide/reference/commands/workflow/tools/test-context-gather.md +1 -4
  24. package/.claude/skills/command-guide/reference/commands/workflow/tools/test-task-generate.md +0 -2
  25. package/.claude/skills/copyright-docs/SKILL.md +132 -0
  26. package/.claude/skills/copyright-docs/phases/01-metadata-collection.md +78 -0
  27. package/.claude/skills/copyright-docs/phases/01.5-project-exploration.md +150 -0
  28. package/.claude/skills/copyright-docs/phases/02-deep-analysis.md +664 -0
  29. package/.claude/skills/copyright-docs/phases/02.5-consolidation.md +192 -0
  30. package/.claude/skills/copyright-docs/phases/04-document-assembly.md +261 -0
  31. package/.claude/skills/copyright-docs/phases/05-compliance-refinement.md +192 -0
  32. package/.claude/skills/copyright-docs/specs/cpcc-requirements.md +121 -0
  33. package/.claude/skills/copyright-docs/templates/agent-base.md +200 -0
  34. package/.claude/skills/project-analyze/SKILL.md +162 -0
  35. package/.claude/skills/project-analyze/phases/01-requirements-discovery.md +79 -0
  36. package/.claude/skills/project-analyze/phases/02-project-exploration.md +176 -0
  37. package/.claude/skills/project-analyze/phases/03-deep-analysis.md +854 -0
  38. package/.claude/skills/project-analyze/phases/03.5-consolidation.md +233 -0
  39. package/.claude/skills/project-analyze/phases/04-report-generation.md +217 -0
  40. package/.claude/skills/project-analyze/phases/05-iterative-refinement.md +124 -0
  41. package/.claude/skills/project-analyze/specs/quality-standards.md +115 -0
  42. package/.claude/skills/project-analyze/specs/writing-style.md +152 -0
  43. package/.claude/workflows/cli-templates/schemas/conflict-resolution-schema.json +79 -65
  44. package/.claude/workflows/cli-templates/schemas/issue-task-jsonl-schema.json +136 -0
  45. package/.claude/workflows/cli-templates/schemas/issues-jsonl-schema.json +74 -0
  46. package/.claude/workflows/cli-templates/schemas/queue-schema.json +136 -0
  47. package/.claude/workflows/cli-templates/schemas/registry-schema.json +94 -0
  48. package/.claude/workflows/cli-templates/schemas/solution-schema.json +120 -0
  49. package/.claude/workflows/cli-templates/schemas/solutions-jsonl-schema.json +125 -0
  50. package/.codex/prompts/issue-execute.md +266 -0
  51. package/README.md +11 -1
  52. package/ccw/dist/cli.d.ts.map +1 -1
  53. package/ccw/dist/cli.js +25 -0
  54. package/ccw/dist/cli.js.map +1 -1
  55. package/ccw/dist/commands/cli.d.ts.map +1 -1
  56. package/ccw/dist/commands/cli.js +46 -8
  57. package/ccw/dist/commands/cli.js.map +1 -1
  58. package/ccw/dist/commands/issue.d.ts +21 -0
  59. package/ccw/dist/commands/issue.d.ts.map +1 -0
  60. package/ccw/dist/commands/issue.js +895 -0
  61. package/ccw/dist/commands/issue.js.map +1 -0
  62. package/ccw/dist/core/dashboard-generator-patch.js +1 -0
  63. package/ccw/dist/core/dashboard-generator-patch.js.map +1 -1
  64. package/ccw/dist/core/routes/cli-routes.js +2 -2
  65. package/ccw/dist/core/routes/cli-routes.js.map +1 -1
  66. package/ccw/dist/core/routes/issue-routes.d.ts +34 -0
  67. package/ccw/dist/core/routes/issue-routes.d.ts.map +1 -0
  68. package/ccw/dist/core/routes/issue-routes.js +487 -0
  69. package/ccw/dist/core/routes/issue-routes.js.map +1 -0
  70. package/ccw/dist/core/server.d.ts.map +1 -1
  71. package/ccw/dist/core/server.js +17 -2
  72. package/ccw/dist/core/server.js.map +1 -1
  73. package/ccw/dist/tools/claude-cli-tools.d.ts +7 -3
  74. package/ccw/dist/tools/claude-cli-tools.d.ts.map +1 -1
  75. package/ccw/dist/tools/claude-cli-tools.js +31 -17
  76. package/ccw/dist/tools/claude-cli-tools.js.map +1 -1
  77. package/ccw/dist/tools/smart-search.d.ts +25 -0
  78. package/ccw/dist/tools/smart-search.d.ts.map +1 -1
  79. package/ccw/dist/tools/smart-search.js +121 -17
  80. package/ccw/dist/tools/smart-search.js.map +1 -1
  81. package/ccw/src/cli.ts +26 -0
  82. package/ccw/src/commands/cli.ts +49 -7
  83. package/ccw/src/commands/issue.ts +1184 -0
  84. package/ccw/src/core/dashboard-generator-patch.ts +1 -0
  85. package/ccw/src/core/routes/cli-routes.ts +3 -3
  86. package/ccw/src/core/routes/issue-routes.ts +559 -0
  87. package/ccw/src/core/server.ts +17 -2
  88. package/ccw/src/templates/dashboard-css/32-issue-manager.css +2544 -0
  89. package/ccw/src/templates/dashboard-css/33-cli-stream-viewer.css +467 -0
  90. package/ccw/src/templates/dashboard-js/components/cli-history.js +40 -13
  91. package/ccw/src/templates/dashboard-js/components/cli-status.js +26 -2
  92. package/ccw/src/templates/dashboard-js/components/cli-stream-viewer.js +461 -0
  93. package/ccw/src/templates/dashboard-js/components/navigation.js +8 -0
  94. package/ccw/src/templates/dashboard-js/components/notifications.js +16 -0
  95. package/ccw/src/templates/dashboard-js/i18n.js +290 -2
  96. package/ccw/src/templates/dashboard-js/views/cli-manager.js +5 -0
  97. package/ccw/src/templates/dashboard-js/views/history.js +19 -4
  98. package/ccw/src/templates/dashboard-js/views/hook-manager.js +11 -5
  99. package/ccw/src/templates/dashboard-js/views/issue-manager.js +1546 -0
  100. package/ccw/src/templates/dashboard.html +55 -0
  101. package/ccw/src/tools/claude-cli-tools.ts +37 -20
  102. package/ccw/src/tools/smart-search.ts +157 -16
  103. package/codex-lens/src/codexlens/__pycache__/config.cpython-313.pyc +0 -0
  104. package/codex-lens/src/codexlens/config.py +5 -0
  105. package/codex-lens/src/codexlens/search/__pycache__/hybrid_search.cpython-313.pyc +0 -0
  106. package/codex-lens/src/codexlens/search/__pycache__/ranking.cpython-313.pyc +0 -0
  107. package/codex-lens/src/codexlens/search/hybrid_search.py +144 -11
  108. package/codex-lens/src/codexlens/search/ranking.py +267 -1
  109. package/codex-lens/src/codexlens/semantic/__pycache__/chunker.cpython-313.pyc +0 -0
  110. package/codex-lens/src/codexlens/semantic/chunker.py +55 -10
  111. package/package.json +2 -2
@@ -0,0 +1,461 @@
1
+ /**
2
+ * CLI Stream Viewer Component
3
+ * Real-time streaming output viewer for CLI executions
4
+ */
5
+
6
+ // ===== State Management =====
7
+ let cliStreamExecutions = {}; // { executionId: { tool, mode, output, status, startTime, endTime } }
8
+ let activeStreamTab = null;
9
+ let autoScrollEnabled = true;
10
+ let isCliStreamViewerOpen = false;
11
+
12
+ const MAX_OUTPUT_LINES = 5000; // Prevent memory issues
13
+
14
+ // ===== Initialization =====
15
+ function initCliStreamViewer() {
16
+ // Initialize keyboard shortcuts
17
+ document.addEventListener('keydown', function(e) {
18
+ if (e.key === 'Escape' && isCliStreamViewerOpen) {
19
+ toggleCliStreamViewer();
20
+ }
21
+ });
22
+
23
+ // Initialize scroll detection for auto-scroll
24
+ const content = document.getElementById('cliStreamContent');
25
+ if (content) {
26
+ content.addEventListener('scroll', handleStreamContentScroll);
27
+ }
28
+ }
29
+
30
+ // ===== Panel Control =====
31
+ function toggleCliStreamViewer() {
32
+ const viewer = document.getElementById('cliStreamViewer');
33
+ const overlay = document.getElementById('cliStreamOverlay');
34
+
35
+ if (!viewer || !overlay) return;
36
+
37
+ isCliStreamViewerOpen = !isCliStreamViewerOpen;
38
+
39
+ if (isCliStreamViewerOpen) {
40
+ viewer.classList.add('open');
41
+ overlay.classList.add('open');
42
+
43
+ // If no active tab but have executions, select the first one
44
+ if (!activeStreamTab && Object.keys(cliStreamExecutions).length > 0) {
45
+ const firstId = Object.keys(cliStreamExecutions)[0];
46
+ switchStreamTab(firstId);
47
+ } else {
48
+ renderStreamContent(activeStreamTab);
49
+ }
50
+
51
+ // Re-init lucide icons
52
+ if (typeof lucide !== 'undefined') {
53
+ lucide.createIcons();
54
+ }
55
+ } else {
56
+ viewer.classList.remove('open');
57
+ overlay.classList.remove('open');
58
+ }
59
+ }
60
+
61
+ // ===== WebSocket Event Handlers =====
62
+ function handleCliStreamStarted(payload) {
63
+ const { executionId, tool, mode, timestamp } = payload;
64
+
65
+ // Create new execution record
66
+ cliStreamExecutions[executionId] = {
67
+ tool: tool || 'cli',
68
+ mode: mode || 'analysis',
69
+ output: [],
70
+ status: 'running',
71
+ startTime: timestamp ? new Date(timestamp).getTime() : Date.now(),
72
+ endTime: null
73
+ };
74
+
75
+ // Add system message
76
+ cliStreamExecutions[executionId].output.push({
77
+ type: 'system',
78
+ content: `[${new Date().toLocaleTimeString()}] CLI execution started: ${tool} (${mode} mode)`,
79
+ timestamp: Date.now()
80
+ });
81
+
82
+ // If this is the first execution or panel is open, select it
83
+ if (!activeStreamTab || isCliStreamViewerOpen) {
84
+ activeStreamTab = executionId;
85
+ }
86
+
87
+ renderStreamTabs();
88
+ renderStreamContent(activeStreamTab);
89
+ updateStreamBadge();
90
+
91
+ // Auto-open panel if configured (optional)
92
+ // if (!isCliStreamViewerOpen) toggleCliStreamViewer();
93
+ }
94
+
95
+ function handleCliStreamOutput(payload) {
96
+ const { executionId, chunkType, data } = payload;
97
+
98
+ const exec = cliStreamExecutions[executionId];
99
+ if (!exec) return;
100
+
101
+ // Parse and add output lines
102
+ const content = typeof data === 'string' ? data : JSON.stringify(data);
103
+ const lines = content.split('\n');
104
+
105
+ lines.forEach(line => {
106
+ if (line.trim() || lines.length === 1) { // Keep empty lines if it's the only content
107
+ exec.output.push({
108
+ type: chunkType || 'stdout',
109
+ content: line,
110
+ timestamp: Date.now()
111
+ });
112
+ }
113
+ });
114
+
115
+ // Trim if too long
116
+ if (exec.output.length > MAX_OUTPUT_LINES) {
117
+ exec.output = exec.output.slice(-MAX_OUTPUT_LINES);
118
+ }
119
+
120
+ // Update UI if this is the active tab
121
+ if (activeStreamTab === executionId && isCliStreamViewerOpen) {
122
+ requestAnimationFrame(() => {
123
+ renderStreamContent(executionId);
124
+ });
125
+ }
126
+
127
+ // Update badge to show activity
128
+ updateStreamBadge();
129
+ }
130
+
131
+ function handleCliStreamCompleted(payload) {
132
+ const { executionId, success, duration, timestamp } = payload;
133
+
134
+ const exec = cliStreamExecutions[executionId];
135
+ if (!exec) return;
136
+
137
+ exec.status = success ? 'completed' : 'error';
138
+ exec.endTime = timestamp ? new Date(timestamp).getTime() : Date.now();
139
+
140
+ // Add completion message
141
+ const durationText = duration ? ` (${formatDuration(duration)})` : '';
142
+ const statusText = success ? 'completed successfully' : 'failed';
143
+ exec.output.push({
144
+ type: 'system',
145
+ content: `[${new Date().toLocaleTimeString()}] CLI execution ${statusText}${durationText}`,
146
+ timestamp: Date.now()
147
+ });
148
+
149
+ renderStreamTabs();
150
+ if (activeStreamTab === executionId) {
151
+ renderStreamContent(executionId);
152
+ }
153
+ updateStreamBadge();
154
+ }
155
+
156
+ function handleCliStreamError(payload) {
157
+ const { executionId, error, timestamp } = payload;
158
+
159
+ const exec = cliStreamExecutions[executionId];
160
+ if (!exec) return;
161
+
162
+ exec.status = 'error';
163
+ exec.endTime = timestamp ? new Date(timestamp).getTime() : Date.now();
164
+
165
+ // Add error message
166
+ exec.output.push({
167
+ type: 'stderr',
168
+ content: `[ERROR] ${error || 'Unknown error occurred'}`,
169
+ timestamp: Date.now()
170
+ });
171
+
172
+ renderStreamTabs();
173
+ if (activeStreamTab === executionId) {
174
+ renderStreamContent(executionId);
175
+ }
176
+ updateStreamBadge();
177
+ }
178
+
179
+ // ===== UI Rendering =====
180
+ function renderStreamTabs() {
181
+ const tabsContainer = document.getElementById('cliStreamTabs');
182
+ if (!tabsContainer) return;
183
+
184
+ const execIds = Object.keys(cliStreamExecutions);
185
+
186
+ if (execIds.length === 0) {
187
+ tabsContainer.innerHTML = '';
188
+ return;
189
+ }
190
+
191
+ // Sort: running first, then by start time (newest first)
192
+ execIds.sort((a, b) => {
193
+ const execA = cliStreamExecutions[a];
194
+ const execB = cliStreamExecutions[b];
195
+
196
+ if (execA.status === 'running' && execB.status !== 'running') return -1;
197
+ if (execA.status !== 'running' && execB.status === 'running') return 1;
198
+ return execB.startTime - execA.startTime;
199
+ });
200
+
201
+ tabsContainer.innerHTML = execIds.map(id => {
202
+ const exec = cliStreamExecutions[id];
203
+ const isActive = id === activeStreamTab;
204
+ const canClose = exec.status !== 'running';
205
+
206
+ return `
207
+ <div class="cli-stream-tab ${isActive ? 'active' : ''}"
208
+ onclick="switchStreamTab('${id}')"
209
+ data-execution-id="${id}">
210
+ <span class="cli-stream-tab-status ${exec.status}"></span>
211
+ <span class="cli-stream-tab-tool">${escapeHtml(exec.tool)}</span>
212
+ <span class="cli-stream-tab-mode">${exec.mode}</span>
213
+ <button class="cli-stream-tab-close ${canClose ? '' : 'disabled'}"
214
+ onclick="event.stopPropagation(); closeStream('${id}')"
215
+ title="${canClose ? _streamT('cliStream.close') : _streamT('cliStream.cannotCloseRunning')}"
216
+ ${canClose ? '' : 'disabled'}>×</button>
217
+ </div>
218
+ `;
219
+ }).join('');
220
+
221
+ // Update count badge
222
+ const countBadge = document.getElementById('cliStreamCountBadge');
223
+ if (countBadge) {
224
+ const runningCount = execIds.filter(id => cliStreamExecutions[id].status === 'running').length;
225
+ countBadge.textContent = execIds.length;
226
+ countBadge.classList.toggle('has-running', runningCount > 0);
227
+ }
228
+ }
229
+
230
+ function renderStreamContent(executionId) {
231
+ const contentContainer = document.getElementById('cliStreamContent');
232
+ if (!contentContainer) return;
233
+
234
+ const exec = executionId ? cliStreamExecutions[executionId] : null;
235
+
236
+ if (!exec) {
237
+ // Show empty state
238
+ contentContainer.innerHTML = `
239
+ <div class="cli-stream-empty">
240
+ <i data-lucide="terminal"></i>
241
+ <div class="cli-stream-empty-title" data-i18n="cliStream.noStreams">${_streamT('cliStream.noStreams')}</div>
242
+ <div class="cli-stream-empty-hint" data-i18n="cliStream.noStreamsHint">${_streamT('cliStream.noStreamsHint')}</div>
243
+ </div>
244
+ `;
245
+ if (typeof lucide !== 'undefined') lucide.createIcons();
246
+ return;
247
+ }
248
+
249
+ // Check if should auto-scroll
250
+ const wasAtBottom = contentContainer.scrollHeight - contentContainer.scrollTop <= contentContainer.clientHeight + 50;
251
+
252
+ // Render output lines
253
+ contentContainer.innerHTML = exec.output.map(line =>
254
+ `<div class="cli-stream-line ${line.type}">${escapeHtml(line.content)}</div>`
255
+ ).join('');
256
+
257
+ // Auto-scroll if enabled and was at bottom
258
+ if (autoScrollEnabled && wasAtBottom) {
259
+ contentContainer.scrollTop = contentContainer.scrollHeight;
260
+ }
261
+
262
+ // Update status bar
263
+ renderStreamStatus(executionId);
264
+ }
265
+
266
+ function renderStreamStatus(executionId) {
267
+ const statusContainer = document.getElementById('cliStreamStatus');
268
+ if (!statusContainer) return;
269
+
270
+ const exec = executionId ? cliStreamExecutions[executionId] : null;
271
+
272
+ if (!exec) {
273
+ statusContainer.innerHTML = '';
274
+ return;
275
+ }
276
+
277
+ const duration = exec.endTime
278
+ ? formatDuration(exec.endTime - exec.startTime)
279
+ : formatDuration(Date.now() - exec.startTime);
280
+
281
+ const statusLabel = exec.status === 'running'
282
+ ? _streamT('cliStream.running')
283
+ : exec.status === 'completed'
284
+ ? _streamT('cliStream.completed')
285
+ : _streamT('cliStream.error');
286
+
287
+ statusContainer.innerHTML = `
288
+ <div class="cli-stream-status-info">
289
+ <div class="cli-stream-status-item">
290
+ <span class="cli-stream-tab-status ${exec.status}"></span>
291
+ <span>${statusLabel}</span>
292
+ </div>
293
+ <div class="cli-stream-status-item">
294
+ <i data-lucide="clock"></i>
295
+ <span>${duration}</span>
296
+ </div>
297
+ <div class="cli-stream-status-item">
298
+ <i data-lucide="file-text"></i>
299
+ <span>${exec.output.length} ${_streamT('cliStream.lines') || 'lines'}</span>
300
+ </div>
301
+ </div>
302
+ <div class="cli-stream-status-actions">
303
+ <button class="cli-stream-toggle-btn ${autoScrollEnabled ? 'active' : ''}"
304
+ onclick="toggleAutoScroll()"
305
+ title="${_streamT('cliStream.autoScroll')}">
306
+ <i data-lucide="arrow-down-to-line"></i>
307
+ <span data-i18n="cliStream.autoScroll">${_streamT('cliStream.autoScroll')}</span>
308
+ </button>
309
+ </div>
310
+ `;
311
+
312
+ if (typeof lucide !== 'undefined') lucide.createIcons();
313
+
314
+ // Update duration periodically for running executions
315
+ if (exec.status === 'running') {
316
+ setTimeout(() => {
317
+ if (activeStreamTab === executionId && cliStreamExecutions[executionId]?.status === 'running') {
318
+ renderStreamStatus(executionId);
319
+ }
320
+ }, 1000);
321
+ }
322
+ }
323
+
324
+ function switchStreamTab(executionId) {
325
+ if (!cliStreamExecutions[executionId]) return;
326
+
327
+ activeStreamTab = executionId;
328
+ renderStreamTabs();
329
+ renderStreamContent(executionId);
330
+ }
331
+
332
+ function updateStreamBadge() {
333
+ const badge = document.getElementById('cliStreamBadge');
334
+ if (!badge) return;
335
+
336
+ const runningCount = Object.values(cliStreamExecutions).filter(e => e.status === 'running').length;
337
+
338
+ if (runningCount > 0) {
339
+ badge.textContent = runningCount;
340
+ badge.classList.add('has-running');
341
+ } else {
342
+ badge.textContent = '';
343
+ badge.classList.remove('has-running');
344
+ }
345
+ }
346
+
347
+ // ===== User Actions =====
348
+ function closeStream(executionId) {
349
+ const exec = cliStreamExecutions[executionId];
350
+ if (!exec || exec.status === 'running') return;
351
+
352
+ delete cliStreamExecutions[executionId];
353
+
354
+ // Switch to another tab if this was active
355
+ if (activeStreamTab === executionId) {
356
+ const remaining = Object.keys(cliStreamExecutions);
357
+ activeStreamTab = remaining.length > 0 ? remaining[0] : null;
358
+ }
359
+
360
+ renderStreamTabs();
361
+ renderStreamContent(activeStreamTab);
362
+ updateStreamBadge();
363
+ }
364
+
365
+ function clearCompletedStreams() {
366
+ const toRemove = Object.keys(cliStreamExecutions).filter(
367
+ id => cliStreamExecutions[id].status !== 'running'
368
+ );
369
+
370
+ toRemove.forEach(id => delete cliStreamExecutions[id]);
371
+
372
+ // Update active tab if needed
373
+ if (activeStreamTab && !cliStreamExecutions[activeStreamTab]) {
374
+ const remaining = Object.keys(cliStreamExecutions);
375
+ activeStreamTab = remaining.length > 0 ? remaining[0] : null;
376
+ }
377
+
378
+ renderStreamTabs();
379
+ renderStreamContent(activeStreamTab);
380
+ updateStreamBadge();
381
+ }
382
+
383
+ function toggleAutoScroll() {
384
+ autoScrollEnabled = !autoScrollEnabled;
385
+
386
+ if (autoScrollEnabled && activeStreamTab) {
387
+ const content = document.getElementById('cliStreamContent');
388
+ if (content) {
389
+ content.scrollTop = content.scrollHeight;
390
+ }
391
+ }
392
+
393
+ renderStreamStatus(activeStreamTab);
394
+ }
395
+
396
+ function handleStreamContentScroll() {
397
+ const content = document.getElementById('cliStreamContent');
398
+ if (!content) return;
399
+
400
+ // If user scrolls up, disable auto-scroll
401
+ const isAtBottom = content.scrollHeight - content.scrollTop <= content.clientHeight + 50;
402
+ if (!isAtBottom && autoScrollEnabled) {
403
+ autoScrollEnabled = false;
404
+ renderStreamStatus(activeStreamTab);
405
+ }
406
+ }
407
+
408
+ // ===== Helper Functions =====
409
+ function formatDuration(ms) {
410
+ if (ms < 1000) return `${ms}ms`;
411
+
412
+ const seconds = Math.floor(ms / 1000);
413
+ if (seconds < 60) return `${seconds}s`;
414
+
415
+ const minutes = Math.floor(seconds / 60);
416
+ const remainingSeconds = seconds % 60;
417
+ if (minutes < 60) return `${minutes}m ${remainingSeconds}s`;
418
+
419
+ const hours = Math.floor(minutes / 60);
420
+ const remainingMinutes = minutes % 60;
421
+ return `${hours}h ${remainingMinutes}m`;
422
+ }
423
+
424
+ function escapeHtml(text) {
425
+ if (!text) return '';
426
+ const div = document.createElement('div');
427
+ div.textContent = text;
428
+ return div.innerHTML;
429
+ }
430
+
431
+ // Translation helper with fallback (uses global t from i18n.js)
432
+ function _streamT(key) {
433
+ // First try global t() from i18n.js
434
+ if (typeof t === 'function' && t !== _streamT) {
435
+ try {
436
+ return t(key);
437
+ } catch (e) {
438
+ // Fall through to fallbacks
439
+ }
440
+ }
441
+ // Fallback values
442
+ const fallbacks = {
443
+ 'cliStream.noStreams': 'No active CLI executions',
444
+ 'cliStream.noStreamsHint': 'Start a CLI command to see streaming output',
445
+ 'cliStream.running': 'Running',
446
+ 'cliStream.completed': 'Completed',
447
+ 'cliStream.error': 'Error',
448
+ 'cliStream.autoScroll': 'Auto-scroll',
449
+ 'cliStream.close': 'Close',
450
+ 'cliStream.cannotCloseRunning': 'Cannot close running execution',
451
+ 'cliStream.lines': 'lines'
452
+ };
453
+ return fallbacks[key] || key;
454
+ }
455
+
456
+ // Initialize when DOM is ready
457
+ if (document.readyState === 'loading') {
458
+ document.addEventListener('DOMContentLoaded', initCliStreamViewer);
459
+ } else {
460
+ initCliStreamViewer();
461
+ }
@@ -155,6 +155,12 @@ function initNavigation() {
155
155
  } else {
156
156
  console.error('renderApiSettings not defined - please refresh the page');
157
157
  }
158
+ } else if (currentView === 'issue-manager') {
159
+ if (typeof renderIssueManager === 'function') {
160
+ renderIssueManager();
161
+ } else {
162
+ console.error('renderIssueManager not defined - please refresh the page');
163
+ }
158
164
  }
159
165
  });
160
166
  });
@@ -199,6 +205,8 @@ function updateContentTitle() {
199
205
  titleEl.textContent = t('title.codexLensManager');
200
206
  } else if (currentView === 'api-settings') {
201
207
  titleEl.textContent = t('title.apiSettings');
208
+ } else if (currentView === 'issue-manager') {
209
+ titleEl.textContent = t('title.issueManager');
202
210
  } else if (currentView === 'liteTasks') {
203
211
  const names = { 'lite-plan': t('title.litePlanSessions'), 'lite-fix': t('title.liteFixSessions') };
204
212
  titleEl.textContent = names[currentLiteType] || t('title.liteTasks');
@@ -217,24 +217,40 @@ function handleNotification(data) {
217
217
  if (typeof handleCliExecutionStarted === 'function') {
218
218
  handleCliExecutionStarted(payload);
219
219
  }
220
+ // Route to CLI Stream Viewer
221
+ if (typeof handleCliStreamStarted === 'function') {
222
+ handleCliStreamStarted(payload);
223
+ }
220
224
  break;
221
225
 
222
226
  case 'CLI_OUTPUT':
223
227
  if (typeof handleCliOutput === 'function') {
224
228
  handleCliOutput(payload);
225
229
  }
230
+ // Route to CLI Stream Viewer
231
+ if (typeof handleCliStreamOutput === 'function') {
232
+ handleCliStreamOutput(payload);
233
+ }
226
234
  break;
227
235
 
228
236
  case 'CLI_EXECUTION_COMPLETED':
229
237
  if (typeof handleCliExecutionCompleted === 'function') {
230
238
  handleCliExecutionCompleted(payload);
231
239
  }
240
+ // Route to CLI Stream Viewer
241
+ if (typeof handleCliStreamCompleted === 'function') {
242
+ handleCliStreamCompleted(payload);
243
+ }
232
244
  break;
233
245
 
234
246
  case 'CLI_EXECUTION_ERROR':
235
247
  if (typeof handleCliExecutionError === 'function') {
236
248
  handleCliExecutionError(payload);
237
249
  }
250
+ // Route to CLI Stream Viewer
251
+ if (typeof handleCliStreamError === 'function') {
252
+ handleCliStreamError(payload);
253
+ }
238
254
  break;
239
255
 
240
256
  // CLI Review Events