overcode 0.1.0__py3-none-any.whl
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.
- overcode/__init__.py +5 -0
- overcode/cli.py +812 -0
- overcode/config.py +72 -0
- overcode/daemon.py +1184 -0
- overcode/daemon_claude_skill.md +180 -0
- overcode/daemon_state.py +113 -0
- overcode/data_export.py +257 -0
- overcode/dependency_check.py +227 -0
- overcode/exceptions.py +219 -0
- overcode/history_reader.py +448 -0
- overcode/implementations.py +214 -0
- overcode/interfaces.py +49 -0
- overcode/launcher.py +434 -0
- overcode/logging_config.py +193 -0
- overcode/mocks.py +152 -0
- overcode/monitor_daemon.py +808 -0
- overcode/monitor_daemon_state.py +358 -0
- overcode/pid_utils.py +225 -0
- overcode/presence_logger.py +454 -0
- overcode/protocols.py +143 -0
- overcode/session_manager.py +606 -0
- overcode/settings.py +412 -0
- overcode/standing_instructions.py +276 -0
- overcode/status_constants.py +190 -0
- overcode/status_detector.py +339 -0
- overcode/status_history.py +164 -0
- overcode/status_patterns.py +264 -0
- overcode/summarizer_client.py +136 -0
- overcode/summarizer_component.py +312 -0
- overcode/supervisor_daemon.py +1000 -0
- overcode/supervisor_layout.sh +50 -0
- overcode/tmux_manager.py +228 -0
- overcode/tui.py +2549 -0
- overcode/tui_helpers.py +495 -0
- overcode/web_api.py +279 -0
- overcode/web_server.py +138 -0
- overcode/web_templates.py +563 -0
- overcode-0.1.0.dist-info/METADATA +87 -0
- overcode-0.1.0.dist-info/RECORD +43 -0
- overcode-0.1.0.dist-info/WHEEL +5 -0
- overcode-0.1.0.dist-info/entry_points.txt +2 -0
- overcode-0.1.0.dist-info/licenses/LICENSE +21 -0
- overcode-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,563 @@
|
|
|
1
|
+
"""
|
|
2
|
+
HTML/CSS/JS templates for web dashboard.
|
|
3
|
+
|
|
4
|
+
Single-page application embedded as Python string for zero external dependencies.
|
|
5
|
+
Mobile-first responsive design with dark theme matching the TUI aesthetic.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def get_dashboard_html() -> str:
|
|
10
|
+
"""Return the complete dashboard HTML page."""
|
|
11
|
+
return """<!DOCTYPE html>
|
|
12
|
+
<html lang="en">
|
|
13
|
+
<head>
|
|
14
|
+
<meta charset="UTF-8">
|
|
15
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
|
|
16
|
+
<meta name="apple-mobile-web-app-capable" content="yes">
|
|
17
|
+
<meta name="theme-color" content="#0f172a">
|
|
18
|
+
<title>Overcode Dashboard</title>
|
|
19
|
+
<style>
|
|
20
|
+
:root {
|
|
21
|
+
--bg-primary: #0f172a;
|
|
22
|
+
--bg-secondary: #1e293b;
|
|
23
|
+
--bg-tertiary: #334155;
|
|
24
|
+
--text-primary: #f1f5f9;
|
|
25
|
+
--text-secondary: #94a3b8;
|
|
26
|
+
--text-dim: #64748b;
|
|
27
|
+
--green: #22c55e;
|
|
28
|
+
--yellow: #eab308;
|
|
29
|
+
--orange: #f97316;
|
|
30
|
+
--red: #ef4444;
|
|
31
|
+
--cyan: #06b6d4;
|
|
32
|
+
--dim: #6b7280;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
* {
|
|
36
|
+
box-sizing: border-box;
|
|
37
|
+
margin: 0;
|
|
38
|
+
padding: 0;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
body {
|
|
42
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
|
|
43
|
+
background: var(--bg-primary);
|
|
44
|
+
color: var(--text-primary);
|
|
45
|
+
min-height: 100vh;
|
|
46
|
+
padding: 12px;
|
|
47
|
+
font-size: 14px;
|
|
48
|
+
line-height: 1.4;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/* Header */
|
|
52
|
+
header {
|
|
53
|
+
display: flex;
|
|
54
|
+
flex-wrap: wrap;
|
|
55
|
+
align-items: center;
|
|
56
|
+
gap: 8px;
|
|
57
|
+
margin-bottom: 16px;
|
|
58
|
+
padding-bottom: 12px;
|
|
59
|
+
border-bottom: 1px solid var(--bg-tertiary);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
h1 {
|
|
63
|
+
font-size: 20px;
|
|
64
|
+
font-weight: 600;
|
|
65
|
+
display: flex;
|
|
66
|
+
align-items: center;
|
|
67
|
+
gap: 8px;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.header-status {
|
|
71
|
+
display: flex;
|
|
72
|
+
align-items: center;
|
|
73
|
+
gap: 12px;
|
|
74
|
+
margin-left: auto;
|
|
75
|
+
font-size: 12px;
|
|
76
|
+
color: var(--text-secondary);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.status-dot {
|
|
80
|
+
width: 8px;
|
|
81
|
+
height: 8px;
|
|
82
|
+
border-radius: 50%;
|
|
83
|
+
display: inline-block;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
.status-dot.green { background: var(--green); }
|
|
87
|
+
.status-dot.red { background: var(--red); }
|
|
88
|
+
.status-dot.yellow { background: var(--yellow); }
|
|
89
|
+
|
|
90
|
+
/* Summary bar */
|
|
91
|
+
.summary {
|
|
92
|
+
display: grid;
|
|
93
|
+
grid-template-columns: repeat(2, 1fr);
|
|
94
|
+
gap: 8px;
|
|
95
|
+
margin-bottom: 16px;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
.summary-card {
|
|
99
|
+
background: var(--bg-secondary);
|
|
100
|
+
border-radius: 8px;
|
|
101
|
+
padding: 12px;
|
|
102
|
+
text-align: center;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.summary-value {
|
|
106
|
+
font-size: 24px;
|
|
107
|
+
font-weight: 700;
|
|
108
|
+
color: var(--text-primary);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
.summary-value.green { color: var(--green); }
|
|
112
|
+
.summary-value.yellow { color: var(--yellow); }
|
|
113
|
+
|
|
114
|
+
.summary-label {
|
|
115
|
+
font-size: 11px;
|
|
116
|
+
color: var(--text-secondary);
|
|
117
|
+
text-transform: uppercase;
|
|
118
|
+
letter-spacing: 0.5px;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/* Agent cards */
|
|
122
|
+
.agents {
|
|
123
|
+
display: flex;
|
|
124
|
+
flex-direction: column;
|
|
125
|
+
gap: 8px;
|
|
126
|
+
margin-bottom: 16px;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
.agent-card {
|
|
130
|
+
background: var(--bg-secondary);
|
|
131
|
+
border-radius: 8px;
|
|
132
|
+
padding: 12px;
|
|
133
|
+
border-left: 4px solid var(--dim);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
.agent-header {
|
|
137
|
+
display: flex;
|
|
138
|
+
align-items: center;
|
|
139
|
+
gap: 8px;
|
|
140
|
+
margin-bottom: 8px;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
.agent-emoji {
|
|
144
|
+
font-size: 24px;
|
|
145
|
+
line-height: 1;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
.agent-name {
|
|
149
|
+
font-weight: 600;
|
|
150
|
+
font-size: 16px;
|
|
151
|
+
flex: 1;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
.agent-time {
|
|
155
|
+
font-size: 12px;
|
|
156
|
+
color: var(--text-secondary);
|
|
157
|
+
background: var(--bg-tertiary);
|
|
158
|
+
padding: 2px 6px;
|
|
159
|
+
border-radius: 4px;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
.agent-repo {
|
|
163
|
+
font-size: 11px;
|
|
164
|
+
color: var(--text-dim);
|
|
165
|
+
margin-bottom: 8px;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
.agent-activity {
|
|
169
|
+
font-size: 12px;
|
|
170
|
+
color: var(--text-secondary);
|
|
171
|
+
margin-bottom: 8px;
|
|
172
|
+
white-space: nowrap;
|
|
173
|
+
overflow: hidden;
|
|
174
|
+
text-overflow: ellipsis;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
.agent-stats {
|
|
178
|
+
display: grid;
|
|
179
|
+
grid-template-columns: repeat(3, 1fr);
|
|
180
|
+
gap: 4px;
|
|
181
|
+
font-size: 11px;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
.stat {
|
|
185
|
+
display: flex;
|
|
186
|
+
flex-direction: column;
|
|
187
|
+
align-items: center;
|
|
188
|
+
padding: 4px;
|
|
189
|
+
background: var(--bg-tertiary);
|
|
190
|
+
border-radius: 4px;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
.stat-value {
|
|
194
|
+
font-weight: 600;
|
|
195
|
+
color: var(--text-primary);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
.stat-label {
|
|
199
|
+
color: var(--text-dim);
|
|
200
|
+
font-size: 10px;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/* Timeline section */
|
|
204
|
+
.timeline-section {
|
|
205
|
+
background: var(--bg-secondary);
|
|
206
|
+
border-radius: 8px;
|
|
207
|
+
padding: 12px;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
.timeline-header {
|
|
211
|
+
display: flex;
|
|
212
|
+
justify-content: space-between;
|
|
213
|
+
align-items: center;
|
|
214
|
+
margin-bottom: 8px;
|
|
215
|
+
font-size: 12px;
|
|
216
|
+
color: var(--text-secondary);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
.timeline-row {
|
|
220
|
+
display: flex;
|
|
221
|
+
align-items: center;
|
|
222
|
+
gap: 8px;
|
|
223
|
+
margin-bottom: 4px;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
.timeline-label {
|
|
227
|
+
width: 80px;
|
|
228
|
+
font-size: 11px;
|
|
229
|
+
color: var(--text-secondary);
|
|
230
|
+
white-space: nowrap;
|
|
231
|
+
overflow: hidden;
|
|
232
|
+
text-overflow: ellipsis;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
.timeline-bar {
|
|
236
|
+
flex: 1;
|
|
237
|
+
font-family: monospace;
|
|
238
|
+
font-size: 12px;
|
|
239
|
+
letter-spacing: -1px;
|
|
240
|
+
overflow-x: auto;
|
|
241
|
+
white-space: nowrap;
|
|
242
|
+
-webkit-overflow-scrolling: touch;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
.timeline-pct {
|
|
246
|
+
width: 36px;
|
|
247
|
+
text-align: right;
|
|
248
|
+
font-size: 11px;
|
|
249
|
+
color: var(--text-dim);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/* Legend */
|
|
253
|
+
.legend {
|
|
254
|
+
display: flex;
|
|
255
|
+
flex-wrap: wrap;
|
|
256
|
+
gap: 8px;
|
|
257
|
+
margin-top: 8px;
|
|
258
|
+
font-size: 10px;
|
|
259
|
+
color: var(--text-dim);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
.legend-item {
|
|
263
|
+
display: flex;
|
|
264
|
+
align-items: center;
|
|
265
|
+
gap: 4px;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/* Update indicator */
|
|
269
|
+
.update-info {
|
|
270
|
+
text-align: center;
|
|
271
|
+
font-size: 11px;
|
|
272
|
+
color: var(--text-dim);
|
|
273
|
+
margin-top: 16px;
|
|
274
|
+
padding-top: 12px;
|
|
275
|
+
border-top: 1px solid var(--bg-tertiary);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
.updating {
|
|
279
|
+
animation: pulse 1s ease-in-out infinite;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
@keyframes pulse {
|
|
283
|
+
0%, 100% { opacity: 1; }
|
|
284
|
+
50% { opacity: 0.5; }
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/* Error state */
|
|
288
|
+
.error {
|
|
289
|
+
background: rgba(239, 68, 68, 0.1);
|
|
290
|
+
border: 1px solid var(--red);
|
|
291
|
+
border-radius: 8px;
|
|
292
|
+
padding: 16px;
|
|
293
|
+
text-align: center;
|
|
294
|
+
color: var(--red);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/* Empty state */
|
|
298
|
+
.empty {
|
|
299
|
+
text-align: center;
|
|
300
|
+
padding: 32px;
|
|
301
|
+
color: var(--text-dim);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/* Desktop enhancements */
|
|
305
|
+
@media (min-width: 640px) {
|
|
306
|
+
body {
|
|
307
|
+
padding: 24px;
|
|
308
|
+
max-width: 800px;
|
|
309
|
+
margin: 0 auto;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
.summary {
|
|
313
|
+
grid-template-columns: repeat(4, 1fr);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
.agent-stats {
|
|
317
|
+
grid-template-columns: repeat(6, 1fr);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
h1 {
|
|
321
|
+
font-size: 24px;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
</style>
|
|
325
|
+
</head>
|
|
326
|
+
<body>
|
|
327
|
+
<header>
|
|
328
|
+
<h1>Overcode</h1>
|
|
329
|
+
<div class="header-status">
|
|
330
|
+
<span id="daemon-status">
|
|
331
|
+
<span class="status-dot" id="daemon-dot"></span>
|
|
332
|
+
<span id="daemon-text">Loading...</span>
|
|
333
|
+
</span>
|
|
334
|
+
</div>
|
|
335
|
+
</header>
|
|
336
|
+
|
|
337
|
+
<div id="content">
|
|
338
|
+
<div class="empty">Loading dashboard...</div>
|
|
339
|
+
</div>
|
|
340
|
+
|
|
341
|
+
<div class="update-info">
|
|
342
|
+
<span id="update-text">Last update: -</span>
|
|
343
|
+
</div>
|
|
344
|
+
|
|
345
|
+
<script>
|
|
346
|
+
const REFRESH_INTERVAL = 5000;
|
|
347
|
+
let lastUpdate = null;
|
|
348
|
+
|
|
349
|
+
async function fetchStatus() {
|
|
350
|
+
try {
|
|
351
|
+
const response = await fetch('/api/status');
|
|
352
|
+
if (!response.ok) throw new Error('API error');
|
|
353
|
+
return await response.json();
|
|
354
|
+
} catch (e) {
|
|
355
|
+
console.error('Fetch error:', e);
|
|
356
|
+
return null;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
async function fetchTimeline() {
|
|
361
|
+
try {
|
|
362
|
+
const response = await fetch('/api/timeline');
|
|
363
|
+
if (!response.ok) throw new Error('API error');
|
|
364
|
+
return await response.json();
|
|
365
|
+
} catch (e) {
|
|
366
|
+
console.error('Timeline fetch error:', e);
|
|
367
|
+
return null;
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
function formatTime(isoString) {
|
|
372
|
+
if (!isoString) return '-';
|
|
373
|
+
const date = new Date(isoString);
|
|
374
|
+
return date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', second: '2-digit' });
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
function updateDaemonStatus(daemon) {
|
|
378
|
+
const dot = document.getElementById('daemon-dot');
|
|
379
|
+
const text = document.getElementById('daemon-text');
|
|
380
|
+
|
|
381
|
+
if (daemon.running) {
|
|
382
|
+
dot.className = 'status-dot green';
|
|
383
|
+
text.textContent = daemon.status;
|
|
384
|
+
} else {
|
|
385
|
+
dot.className = 'status-dot red';
|
|
386
|
+
text.textContent = 'stopped';
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
function renderSummary(summary) {
|
|
391
|
+
const greenPct = summary.total_agents > 0
|
|
392
|
+
? Math.round((summary.green_agents / summary.total_agents) * 100)
|
|
393
|
+
: 0;
|
|
394
|
+
|
|
395
|
+
return `
|
|
396
|
+
<div class="summary">
|
|
397
|
+
<div class="summary-card">
|
|
398
|
+
<div class="summary-value green">${summary.green_agents}</div>
|
|
399
|
+
<div class="summary-label">Green</div>
|
|
400
|
+
</div>
|
|
401
|
+
<div class="summary-card">
|
|
402
|
+
<div class="summary-value yellow">${summary.total_agents - summary.green_agents}</div>
|
|
403
|
+
<div class="summary-label">Not Green</div>
|
|
404
|
+
</div>
|
|
405
|
+
<div class="summary-card">
|
|
406
|
+
<div class="summary-value">${summary.total_agents}</div>
|
|
407
|
+
<div class="summary-label">Total</div>
|
|
408
|
+
</div>
|
|
409
|
+
<div class="summary-card">
|
|
410
|
+
<div class="summary-value">${greenPct}%</div>
|
|
411
|
+
<div class="summary-label">Active</div>
|
|
412
|
+
</div>
|
|
413
|
+
</div>
|
|
414
|
+
`;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
function renderAgent(agent) {
|
|
418
|
+
return `
|
|
419
|
+
<div class="agent-card" style="border-left-color: ${agent.status_color_hex}">
|
|
420
|
+
<div class="agent-header">
|
|
421
|
+
<span class="agent-emoji">${agent.status_emoji}</span>
|
|
422
|
+
<span class="agent-name">${agent.name}</span>
|
|
423
|
+
<span class="agent-time">${agent.time_in_state}</span>
|
|
424
|
+
</div>
|
|
425
|
+
${agent.repo ? `<div class="agent-repo">${agent.repo}:${agent.branch}</div>` : ''}
|
|
426
|
+
${agent.activity ? `<div class="agent-activity">${agent.activity}</div>` : ''}
|
|
427
|
+
<div class="agent-stats">
|
|
428
|
+
<div class="stat">
|
|
429
|
+
<span class="stat-value" style="color: var(--green)">${agent.green_time}</span>
|
|
430
|
+
<span class="stat-label">Active</span>
|
|
431
|
+
</div>
|
|
432
|
+
<div class="stat">
|
|
433
|
+
<span class="stat-value" style="color: var(--yellow)">${agent.non_green_time}</span>
|
|
434
|
+
<span class="stat-label">Paused</span>
|
|
435
|
+
</div>
|
|
436
|
+
<div class="stat">
|
|
437
|
+
<span class="stat-value">${agent.percent_active}%</span>
|
|
438
|
+
<span class="stat-label">Efficiency</span>
|
|
439
|
+
</div>
|
|
440
|
+
<div class="stat">
|
|
441
|
+
<span class="stat-value" style="color: var(--orange)">${agent.tokens}</span>
|
|
442
|
+
<span class="stat-label">Tokens</span>
|
|
443
|
+
</div>
|
|
444
|
+
<div class="stat">
|
|
445
|
+
<span class="stat-value" style="color: var(--yellow)">${agent.human_interactions}</span>
|
|
446
|
+
<span class="stat-label">Human</span>
|
|
447
|
+
</div>
|
|
448
|
+
<div class="stat">
|
|
449
|
+
<span class="stat-value" style="color: var(--cyan)">${agent.robot_steers}</span>
|
|
450
|
+
<span class="stat-label">Robot</span>
|
|
451
|
+
</div>
|
|
452
|
+
</div>
|
|
453
|
+
</div>
|
|
454
|
+
`;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
function renderTimeline(timeline) {
|
|
458
|
+
if (!timeline || !timeline.agents || Object.keys(timeline.agents).length === 0) {
|
|
459
|
+
return '';
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
const agentRows = Object.entries(timeline.agents).map(([name, data]) => {
|
|
463
|
+
// Build timeline string from slots
|
|
464
|
+
let timelineStr = '';
|
|
465
|
+
const colors = timeline.status_colors || {};
|
|
466
|
+
const chars = timeline.status_chars || {};
|
|
467
|
+
|
|
468
|
+
// Create array for all slots
|
|
469
|
+
const slotMap = {};
|
|
470
|
+
data.slots.forEach(s => { slotMap[s.index] = s; });
|
|
471
|
+
|
|
472
|
+
for (let i = 0; i < timeline.slot_count; i++) {
|
|
473
|
+
if (slotMap[i]) {
|
|
474
|
+
const s = slotMap[i];
|
|
475
|
+
timelineStr += `<span style="color:${s.color}">${s.char}</span>`;
|
|
476
|
+
} else {
|
|
477
|
+
timelineStr += '<span style="color:var(--bg-tertiary)">-</span>';
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
return `
|
|
482
|
+
<div class="timeline-row">
|
|
483
|
+
<span class="timeline-label">${name}</span>
|
|
484
|
+
<span class="timeline-bar">${timelineStr}</span>
|
|
485
|
+
<span class="timeline-pct">${data.percent_green}%</span>
|
|
486
|
+
</div>
|
|
487
|
+
`;
|
|
488
|
+
}).join('');
|
|
489
|
+
|
|
490
|
+
return `
|
|
491
|
+
<div class="timeline-section">
|
|
492
|
+
<div class="timeline-header">
|
|
493
|
+
<span>-${timeline.hours}h</span>
|
|
494
|
+
<span>Timeline</span>
|
|
495
|
+
<span>now</span>
|
|
496
|
+
</div>
|
|
497
|
+
${agentRows}
|
|
498
|
+
<div class="legend">
|
|
499
|
+
<span class="legend-item"><span style="color:var(--green)">█</span> running</span>
|
|
500
|
+
<span class="legend-item"><span style="color:var(--yellow)">▓</span> idle</span>
|
|
501
|
+
<span class="legend-item"><span style="color:var(--orange)">▒</span> waiting</span>
|
|
502
|
+
<span class="legend-item"><span style="color:var(--red)">░</span> blocked</span>
|
|
503
|
+
<span class="legend-item"><span style="color:var(--dim)">×</span> terminated</span>
|
|
504
|
+
</div>
|
|
505
|
+
</div>
|
|
506
|
+
`;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
function renderError(message) {
|
|
510
|
+
return `<div class="error">${message}</div>`;
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
function renderEmpty() {
|
|
514
|
+
return `<div class="empty">No agents found. Start a session with:<br><code>overcode launch --name my-agent</code></div>`;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
async function refresh() {
|
|
518
|
+
const updateText = document.getElementById('update-text');
|
|
519
|
+
updateText.classList.add('updating');
|
|
520
|
+
|
|
521
|
+
const [status, timeline] = await Promise.all([
|
|
522
|
+
fetchStatus(),
|
|
523
|
+
fetchTimeline()
|
|
524
|
+
]);
|
|
525
|
+
|
|
526
|
+
updateText.classList.remove('updating');
|
|
527
|
+
|
|
528
|
+
if (!status) {
|
|
529
|
+
document.getElementById('content').innerHTML = renderError('Failed to connect to server');
|
|
530
|
+
document.getElementById('daemon-dot').className = 'status-dot red';
|
|
531
|
+
document.getElementById('daemon-text').textContent = 'error';
|
|
532
|
+
return;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
updateDaemonStatus(status.daemon);
|
|
536
|
+
|
|
537
|
+
let html = '';
|
|
538
|
+
|
|
539
|
+
if (status.agents.length === 0) {
|
|
540
|
+
html = renderEmpty();
|
|
541
|
+
} else {
|
|
542
|
+
html = renderSummary(status.summary);
|
|
543
|
+
html += '<div class="agents">';
|
|
544
|
+
status.agents.forEach(agent => {
|
|
545
|
+
html += renderAgent(agent);
|
|
546
|
+
});
|
|
547
|
+
html += '</div>';
|
|
548
|
+
html += renderTimeline(timeline);
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
document.getElementById('content').innerHTML = html;
|
|
552
|
+
lastUpdate = new Date();
|
|
553
|
+
updateText.textContent = `Last update: ${formatTime(lastUpdate.toISOString())}`;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
// Initial load
|
|
557
|
+
refresh();
|
|
558
|
+
|
|
559
|
+
// Auto-refresh
|
|
560
|
+
setInterval(refresh, REFRESH_INTERVAL);
|
|
561
|
+
</script>
|
|
562
|
+
</body>
|
|
563
|
+
</html>"""
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: overcode
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A supervisor for managing multiple Claude Code instances in tmux
|
|
5
|
+
Author: Mike Bond
|
|
6
|
+
Project-URL: Homepage, https://github.com/mkb23/overcode
|
|
7
|
+
Project-URL: Repository, https://github.com/mkb23/overcode
|
|
8
|
+
Project-URL: Issues, https://github.com/mkb23/overcode/issues
|
|
9
|
+
Classifier: Development Status :: 4 - Beta
|
|
10
|
+
Classifier: Environment :: Console
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Operating System :: MacOS
|
|
14
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
19
|
+
Requires-Python: >=3.12
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
License-File: LICENSE
|
|
22
|
+
Requires-Dist: textual>=0.40.0
|
|
23
|
+
Requires-Dist: rich>=13.0.0
|
|
24
|
+
Requires-Dist: typer>=0.9.0
|
|
25
|
+
Requires-Dist: pyyaml>=6.0
|
|
26
|
+
Provides-Extra: dev
|
|
27
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
28
|
+
Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
|
|
29
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
|
|
30
|
+
Requires-Dist: pytest-timeout>=2.1.0; extra == "dev"
|
|
31
|
+
Requires-Dist: black>=23.0.0; extra == "dev"
|
|
32
|
+
Requires-Dist: mypy>=1.0.0; extra == "dev"
|
|
33
|
+
Provides-Extra: presence
|
|
34
|
+
Requires-Dist: pyobjc-framework-Quartz>=10.0; extra == "presence"
|
|
35
|
+
Requires-Dist: pyobjc-framework-ApplicationServices>=10.0; extra == "presence"
|
|
36
|
+
Provides-Extra: export
|
|
37
|
+
Requires-Dist: pyarrow>=14.0.0; extra == "export"
|
|
38
|
+
Dynamic: license-file
|
|
39
|
+
|
|
40
|
+
# overcode
|
|
41
|
+
|
|
42
|
+
A TUI supervisor for managing multiple Claude Code agents in tmux.
|
|
43
|
+
|
|
44
|
+
Monitor status, costs, and activity across all your agents from a single dashboard.
|
|
45
|
+
|
|
46
|
+
## Installation
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
pip install overcode
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Requires: Python 3.12+, tmux, [Claude Code CLI](https://docs.anthropic.com/en/docs/claude-code)
|
|
53
|
+
|
|
54
|
+
## Quick Start
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
# Launch an agent
|
|
58
|
+
overcode launch --name my-agent --directory ~/myproject
|
|
59
|
+
|
|
60
|
+
# Open the supervisor dashboard
|
|
61
|
+
overcode supervisor
|
|
62
|
+
|
|
63
|
+
# List running agents
|
|
64
|
+
overcode list
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Features
|
|
68
|
+
|
|
69
|
+
- **Real-time TUI dashboard** - Monitor all agents at a glance
|
|
70
|
+
- **Cost tracking** - See estimated API costs per agent
|
|
71
|
+
- **Activity detection** - Know when agents need input or are working
|
|
72
|
+
- **Time tracking** - Green time (working) vs idle time metrics
|
|
73
|
+
- **Git-aware** - Auto-detects repo and branch for each agent
|
|
74
|
+
|
|
75
|
+
## TUI Controls
|
|
76
|
+
|
|
77
|
+
| Key | Action |
|
|
78
|
+
|-----|--------|
|
|
79
|
+
| `j/k` or `↑/↓` | Navigate agents |
|
|
80
|
+
| `Enter` | Attach to agent's tmux pane |
|
|
81
|
+
| `f` | Focus agent (full screen) |
|
|
82
|
+
| `k` | Kill selected agent |
|
|
83
|
+
| `q` | Quit |
|
|
84
|
+
|
|
85
|
+
## License
|
|
86
|
+
|
|
87
|
+
MIT
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
overcode/__init__.py,sha256=Khx1HKL81_NJYG9NMD-4uvzAFsJr4Te7jevga6MJ_Sk,100
|
|
2
|
+
overcode/cli.py,sha256=2b4NXrhW6FNaQsZdus4PoqGICEfd72Kv2V4wtPDDO6s,28674
|
|
3
|
+
overcode/config.py,sha256=azKI2wEeJgw7p5FjEqdJ8AJsE4_kMMY5pkBSsHxOb-E,1762
|
|
4
|
+
overcode/daemon.py,sha256=urH0zHhPpuzf-Q_khDo6haJ0lkizaDYIGa7pabVllYA,47794
|
|
5
|
+
overcode/daemon_claude_skill.md,sha256=74OoUARhconOTmfWQaHaDbWEPc7wZeywVNb6fIIuELE,5177
|
|
6
|
+
overcode/daemon_state.py,sha256=jpvCZbe-rGGsN7AeuL_eKWhSDDdHf2LaoE2OlEj83Ig,3922
|
|
7
|
+
overcode/data_export.py,sha256=ZnPXZVh6WI1lzLz51dZIK6fYMXy9CyJsVAglmUQMNKo,8220
|
|
8
|
+
overcode/dependency_check.py,sha256=dAAtKigrAJTnED9_nyZGP39wR56wNJ6bD84iKrQkIMQ,6162
|
|
9
|
+
overcode/exceptions.py,sha256=_H-GtmDRuZQ1Fucbq6_T-4tmC9C706F18poqDs6q1Kw,4795
|
|
10
|
+
overcode/history_reader.py,sha256=DYTG447nAmRHcfVmWW4FTEdra0GQ2Z5OsgFTQVhrjeE,14038
|
|
11
|
+
overcode/implementations.py,sha256=oY8cFjzO_Uqzteczn_p4PFcAsN2Zki9a22vuaCTkjL0,7470
|
|
12
|
+
overcode/interfaces.py,sha256=txVkMHHhQl7dpsBS2jx7sR-BHwKiqlAj-YRw7pFkZdg,1101
|
|
13
|
+
overcode/launcher.py,sha256=DJ0gEF9ksRn68naNK7DdL4EWwVEpOD8nUcAogebNG9I,16018
|
|
14
|
+
overcode/logging_config.py,sha256=BuioELKEdfx_amx35sVIdR1tyf2nbhfmOPLp5778lcs,5755
|
|
15
|
+
overcode/mocks.py,sha256=VA9BLNrbqjDJtohfru-ytpdUGqIVamS8wrvbTZZf2fc,5118
|
|
16
|
+
overcode/monitor_daemon.py,sha256=ThdGrhBcpK8BaCuPWmdj8p7DmT69MRtA5VUpa2hCvRA,29665
|
|
17
|
+
overcode/monitor_daemon_state.py,sha256=wD7f2BuS6d7BUS66BtGBevhv_IU3fp9x_4omhV78arE,14856
|
|
18
|
+
overcode/pid_utils.py,sha256=3mAv3f_2cI1j88I9CMGkrD70t4bIGL6x9Q0uHMUl5Ak,6609
|
|
19
|
+
overcode/presence_logger.py,sha256=0jIUPrhbeU0CDL9W2J7CbDPbAc-WPD4n4LFJJbLZ7Tc,13338
|
|
20
|
+
overcode/protocols.py,sha256=iHsh-li-5x-99FM9L3hmlsPxMJK6q6pUDf5TqtA7FpU,3969
|
|
21
|
+
overcode/session_manager.py,sha256=bTqi9jX7FfEK96SmPGTpyp-pDUr2mw1-Nc8SBXd7O0A,23057
|
|
22
|
+
overcode/settings.py,sha256=OJ7iEz3IS4fu7Rtq5T3SruxNVtGgEuogABqRrFM5iik,12811
|
|
23
|
+
overcode/standing_instructions.py,sha256=GZwhq0zT2IPMKDw1_8iTpT_9l6VkkyYTYGSD3u3AcVA,9594
|
|
24
|
+
overcode/status_constants.py,sha256=f24L3pEyI32ntH_d-p_MMdN1SHsYj2IhFrTjTLVWIVc,5817
|
|
25
|
+
overcode/status_detector.py,sha256=ga1MPnWGOsu73EV1lDzIMwZnuc7qKfvd8DiMHwX6hY0,14589
|
|
26
|
+
overcode/status_history.py,sha256=NzhVuTqHkSqgTTdwNfQ8wudCggYjhelZWxpZh6AmzCM,4778
|
|
27
|
+
overcode/status_patterns.py,sha256=5bqqlzaEwImRQvvrXA-Qaqng0F7hLUNxxQtBMniY9jQ,8182
|
|
28
|
+
overcode/summarizer_client.py,sha256=RBRx6FCaKzLGfnLb3fKOK1XxmYlqclHvt6O8pSSQFv0,4322
|
|
29
|
+
overcode/summarizer_component.py,sha256=IVLM-l2eOfiTAFQ_MX5jDm5lKBbe0nAVGkb0gMlxqhU,9797
|
|
30
|
+
overcode/supervisor_daemon.py,sha256=mxzv3jNO-TTjGIoGuI1rlIwlRUiGxqTl7KvMtVAOPIA,37518
|
|
31
|
+
overcode/supervisor_layout.sh,sha256=s0Fm4Fj7adv5IOsMx1JuA6OAXCoQx9OUnS1CmV0FOwI,1652
|
|
32
|
+
overcode/tmux_manager.py,sha256=SNqgQN-MLCIlpg1u4Zf0k6OX2uYryKpIZ3PPnF4Jwb0,7624
|
|
33
|
+
overcode/tui.py,sha256=26Mfg1BDf4NthzSWubbx4l_UAjArdWf73l6EHKhVaDU,110212
|
|
34
|
+
overcode/tui_helpers.py,sha256=3J9e9CBaJYiG_WSVNokb6MpWFQJNJbAQVb58WPmiLRA,14329
|
|
35
|
+
overcode/web_api.py,sha256=WUJEClWsbfCvTtOnhfhoMytqK8oY9HclAFPSkXAgaNk,9033
|
|
36
|
+
overcode/web_server.py,sha256=SgxW6fplNTZ6T6yc9Xj4xUs3jqd64YXZr1K09kcjn1k,4722
|
|
37
|
+
overcode/web_templates.py,sha256=9L2XWcdWDztgcqpyKaSuj3H8UB3IsAKEtfd29BE20KA,17719
|
|
38
|
+
overcode-0.1.0.dist-info/licenses/LICENSE,sha256=6C9I9dhq8QTa4P6LckWXugYJ3xm2ufZKrLymqOqRZFs,1066
|
|
39
|
+
overcode-0.1.0.dist-info/METADATA,sha256=WsByOcbTedjqlkq4ju9088PDkGiWw0kPv1QNs15xLSk,2635
|
|
40
|
+
overcode-0.1.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
41
|
+
overcode-0.1.0.dist-info/entry_points.txt,sha256=EveN-QDOji1HFbJAsIH-djI_s0E4KQC6N144arLeRb0,47
|
|
42
|
+
overcode-0.1.0.dist-info/top_level.txt,sha256=5cfXcNbSNvigE7coZgIRPCl_NDWHu_5UIBmWi9Xiupo,9
|
|
43
|
+
overcode-0.1.0.dist-info/RECORD,,
|