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.
- package/.claude/agents/issue-plan-agent.md +859 -0
- package/.claude/agents/issue-queue-agent.md +702 -0
- package/.claude/commands/issue/execute.md +453 -0
- package/.claude/commands/issue/manage.md +865 -0
- package/.claude/commands/issue/new.md +484 -0
- package/.claude/commands/issue/plan.md +421 -0
- package/.claude/commands/issue/queue.md +354 -0
- package/.claude/commands/{clean.md → workflow/clean.md} +5 -5
- package/.claude/commands/workflow/docs/analyze.md +1467 -0
- package/.claude/commands/workflow/docs/copyright.md +1265 -0
- package/.claude/commands/workflow/execute.md +0 -1
- package/.claude/commands/workflow/tools/conflict-resolution.md +76 -240
- package/.claude/commands/workflow/tools/context-gather.md +0 -2
- package/.claude/commands/workflow/tools/task-generate-agent.md +81 -8
- package/.claude/commands/workflow/tools/task-generate-tdd.md +0 -9
- package/.claude/commands/workflow/tools/test-context-gather.md +2 -3
- package/.claude/commands/workflow/tools/test-task-generate.md +0 -2
- package/.claude/skills/_shared/mermaid-utils.md +584 -0
- package/.claude/skills/command-guide/reference/agents/action-planning-agent.md +0 -2
- package/.claude/skills/command-guide/reference/commands/workflow/execute.md +1 -1
- package/.claude/skills/command-guide/reference/commands/workflow/tools/context-gather.md +1 -2
- package/.claude/skills/command-guide/reference/commands/workflow/tools/task-generate-tdd.md +1 -8
- package/.claude/skills/command-guide/reference/commands/workflow/tools/test-context-gather.md +1 -4
- package/.claude/skills/command-guide/reference/commands/workflow/tools/test-task-generate.md +0 -2
- package/.claude/skills/copyright-docs/SKILL.md +132 -0
- package/.claude/skills/copyright-docs/phases/01-metadata-collection.md +78 -0
- package/.claude/skills/copyright-docs/phases/01.5-project-exploration.md +150 -0
- package/.claude/skills/copyright-docs/phases/02-deep-analysis.md +664 -0
- package/.claude/skills/copyright-docs/phases/02.5-consolidation.md +192 -0
- package/.claude/skills/copyright-docs/phases/04-document-assembly.md +261 -0
- package/.claude/skills/copyright-docs/phases/05-compliance-refinement.md +192 -0
- package/.claude/skills/copyright-docs/specs/cpcc-requirements.md +121 -0
- package/.claude/skills/copyright-docs/templates/agent-base.md +200 -0
- package/.claude/skills/project-analyze/SKILL.md +162 -0
- package/.claude/skills/project-analyze/phases/01-requirements-discovery.md +79 -0
- package/.claude/skills/project-analyze/phases/02-project-exploration.md +176 -0
- package/.claude/skills/project-analyze/phases/03-deep-analysis.md +854 -0
- package/.claude/skills/project-analyze/phases/03.5-consolidation.md +233 -0
- package/.claude/skills/project-analyze/phases/04-report-generation.md +217 -0
- package/.claude/skills/project-analyze/phases/05-iterative-refinement.md +124 -0
- package/.claude/skills/project-analyze/specs/quality-standards.md +115 -0
- package/.claude/skills/project-analyze/specs/writing-style.md +152 -0
- package/.claude/workflows/cli-templates/schemas/conflict-resolution-schema.json +79 -65
- package/.claude/workflows/cli-templates/schemas/issue-task-jsonl-schema.json +136 -0
- package/.claude/workflows/cli-templates/schemas/issues-jsonl-schema.json +74 -0
- package/.claude/workflows/cli-templates/schemas/queue-schema.json +136 -0
- package/.claude/workflows/cli-templates/schemas/registry-schema.json +94 -0
- package/.claude/workflows/cli-templates/schemas/solution-schema.json +120 -0
- package/.claude/workflows/cli-templates/schemas/solutions-jsonl-schema.json +125 -0
- package/.codex/prompts/issue-execute.md +266 -0
- package/README.md +11 -1
- package/ccw/dist/cli.d.ts.map +1 -1
- package/ccw/dist/cli.js +25 -0
- package/ccw/dist/cli.js.map +1 -1
- package/ccw/dist/commands/cli.d.ts.map +1 -1
- package/ccw/dist/commands/cli.js +46 -8
- package/ccw/dist/commands/cli.js.map +1 -1
- package/ccw/dist/commands/issue.d.ts +21 -0
- package/ccw/dist/commands/issue.d.ts.map +1 -0
- package/ccw/dist/commands/issue.js +895 -0
- package/ccw/dist/commands/issue.js.map +1 -0
- package/ccw/dist/core/dashboard-generator-patch.js +1 -0
- package/ccw/dist/core/dashboard-generator-patch.js.map +1 -1
- package/ccw/dist/core/routes/cli-routes.js +2 -2
- package/ccw/dist/core/routes/cli-routes.js.map +1 -1
- package/ccw/dist/core/routes/issue-routes.d.ts +34 -0
- package/ccw/dist/core/routes/issue-routes.d.ts.map +1 -0
- package/ccw/dist/core/routes/issue-routes.js +487 -0
- package/ccw/dist/core/routes/issue-routes.js.map +1 -0
- package/ccw/dist/core/server.d.ts.map +1 -1
- package/ccw/dist/core/server.js +17 -2
- package/ccw/dist/core/server.js.map +1 -1
- package/ccw/dist/tools/claude-cli-tools.d.ts +7 -3
- package/ccw/dist/tools/claude-cli-tools.d.ts.map +1 -1
- package/ccw/dist/tools/claude-cli-tools.js +31 -17
- package/ccw/dist/tools/claude-cli-tools.js.map +1 -1
- package/ccw/dist/tools/smart-search.d.ts +25 -0
- package/ccw/dist/tools/smart-search.d.ts.map +1 -1
- package/ccw/dist/tools/smart-search.js +121 -17
- package/ccw/dist/tools/smart-search.js.map +1 -1
- package/ccw/src/cli.ts +26 -0
- package/ccw/src/commands/cli.ts +49 -7
- package/ccw/src/commands/issue.ts +1184 -0
- package/ccw/src/core/dashboard-generator-patch.ts +1 -0
- package/ccw/src/core/routes/cli-routes.ts +3 -3
- package/ccw/src/core/routes/issue-routes.ts +559 -0
- package/ccw/src/core/server.ts +17 -2
- package/ccw/src/templates/dashboard-css/32-issue-manager.css +2544 -0
- package/ccw/src/templates/dashboard-css/33-cli-stream-viewer.css +467 -0
- package/ccw/src/templates/dashboard-js/components/cli-history.js +40 -13
- package/ccw/src/templates/dashboard-js/components/cli-status.js +26 -2
- package/ccw/src/templates/dashboard-js/components/cli-stream-viewer.js +461 -0
- package/ccw/src/templates/dashboard-js/components/navigation.js +8 -0
- package/ccw/src/templates/dashboard-js/components/notifications.js +16 -0
- package/ccw/src/templates/dashboard-js/i18n.js +290 -2
- package/ccw/src/templates/dashboard-js/views/cli-manager.js +5 -0
- package/ccw/src/templates/dashboard-js/views/history.js +19 -4
- package/ccw/src/templates/dashboard-js/views/hook-manager.js +11 -5
- package/ccw/src/templates/dashboard-js/views/issue-manager.js +1546 -0
- package/ccw/src/templates/dashboard.html +55 -0
- package/ccw/src/tools/claude-cli-tools.ts +37 -20
- package/ccw/src/tools/smart-search.ts +157 -16
- package/codex-lens/src/codexlens/__pycache__/config.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/config.py +5 -0
- package/codex-lens/src/codexlens/search/__pycache__/hybrid_search.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/search/__pycache__/ranking.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/search/hybrid_search.py +144 -11
- package/codex-lens/src/codexlens/search/ranking.py +267 -1
- package/codex-lens/src/codexlens/semantic/__pycache__/chunker.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/chunker.py +55 -10
- 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
|