bobs-workshop 0.1.8 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +48 -28
- package/dist/dashboard/server.js +20 -23
- package/dist/dashboard/server.js.map +1 -1
- package/dist/index.js +83 -37
- package/dist/index.js.map +1 -1
- package/dist/prompts/architect.js +72 -13
- package/dist/prompts/architect.js.map +1 -1
- package/dist/prompts/debugger.js +69 -18
- package/dist/prompts/debugger.js.map +1 -1
- package/dist/prompts/engineer.js +89 -16
- package/dist/prompts/engineer.js.map +1 -1
- package/dist/prompts/orchestrator.js +125 -46
- package/dist/prompts/orchestrator.js.map +1 -1
- package/dist/prompts/reviewer.js +87 -2
- package/dist/prompts/reviewer.js.map +1 -1
- package/dist/services/activitySummarizer.js +36 -1
- package/dist/services/activitySummarizer.js.map +1 -1
- package/dist/tools/dashboardTools.js +6 -23
- package/dist/tools/dashboardTools.js.map +1 -1
- package/dist/tools/orchestratorTools.js +96 -29
- package/dist/tools/orchestratorTools.js.map +1 -1
- package/dist/tools/specTools.js +1 -19
- package/dist/tools/specTools.js.map +1 -1
- package/package.json +5 -2
- package/public/.well-known/mcp/manifest.json +471 -0
- package/public/index.html +3157 -0
- package/public/index.html.backup +2805 -0
- package/public/index.html.backup2 +1292 -0
- package/scripts/cleanup-system-logs.ts +121 -0
|
@@ -0,0 +1,1292 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en" class="h-full">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>Bob's Workshop 👷 - Enhanced Dashboard</title>
|
|
7
|
+
<script src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js" defer></script>
|
|
8
|
+
|
|
9
|
+
<!-- Google Fonts: Poppins and Outfit -->
|
|
10
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
11
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
12
|
+
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&family=Outfit:wght@300;400;500;600;700&display=swap" rel="stylesheet">
|
|
13
|
+
|
|
14
|
+
<style>
|
|
15
|
+
:root {
|
|
16
|
+
/* Color System */
|
|
17
|
+
--bg-primary: linear-gradient(to bottom, #000000 0%, #1a1a1a 50%, #2d2d30 100%);
|
|
18
|
+
--bg-glass: rgba(255, 255, 255, 0.1);
|
|
19
|
+
--bg-glass-hover: rgba(255, 255, 255, 0.15);
|
|
20
|
+
--border-glass: rgba(255, 255, 255, 0.2);
|
|
21
|
+
--text-primary: #ffffff;
|
|
22
|
+
--text-secondary: rgba(255, 255, 255, 0.8);
|
|
23
|
+
--text-muted: rgba(255, 255, 255, 0.6);
|
|
24
|
+
--accent-blue: linear-gradient(135deg, #3b82f6 0%, #6366f1 100%);
|
|
25
|
+
--accent-green: #10b981;
|
|
26
|
+
--accent-yellow: #f59e0b;
|
|
27
|
+
--accent-red: #ef4444;
|
|
28
|
+
|
|
29
|
+
/* Typography */
|
|
30
|
+
--font-heading: 'Poppins', sans-serif;
|
|
31
|
+
--font-body: 'Outfit', sans-serif;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
* {
|
|
35
|
+
box-sizing: border-box;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
body {
|
|
39
|
+
margin: 0;
|
|
40
|
+
padding: 0;
|
|
41
|
+
font-family: var(--font-body);
|
|
42
|
+
background: var(--bg-primary);
|
|
43
|
+
color: var(--text-primary);
|
|
44
|
+
min-height: 100vh;
|
|
45
|
+
overflow-x: hidden;
|
|
46
|
+
zoom: 0.69;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/* Glass morphism utility classes */
|
|
50
|
+
.glass {
|
|
51
|
+
background: var(--bg-glass);
|
|
52
|
+
backdrop-filter: blur(12px);
|
|
53
|
+
-webkit-backdrop-filter: blur(12px);
|
|
54
|
+
border: 1px solid var(--border-glass);
|
|
55
|
+
border-radius: 16px;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.glass-hover {
|
|
59
|
+
transition: all 0.3s ease;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.glass-hover:hover {
|
|
63
|
+
background: var(--bg-glass-hover);
|
|
64
|
+
transform: translateY(-2px);
|
|
65
|
+
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/* Typography */
|
|
69
|
+
.heading {
|
|
70
|
+
font-family: var(--font-heading);
|
|
71
|
+
font-weight: 600;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.text-gradient {
|
|
75
|
+
background: var(--accent-blue);
|
|
76
|
+
-webkit-background-clip: text;
|
|
77
|
+
-webkit-text-fill-color: transparent;
|
|
78
|
+
background-clip: text;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/* Header */
|
|
82
|
+
.header {
|
|
83
|
+
background: var(--bg-glass);
|
|
84
|
+
backdrop-filter: blur(20px);
|
|
85
|
+
border-bottom: 1px solid var(--border-glass);
|
|
86
|
+
padding: 1.5rem 2rem;
|
|
87
|
+
display: flex;
|
|
88
|
+
justify-content: space-between;
|
|
89
|
+
align-items: center;
|
|
90
|
+
position: sticky;
|
|
91
|
+
top: 0;
|
|
92
|
+
z-index: 50;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
.logo {
|
|
96
|
+
font-family: var(--font-heading);
|
|
97
|
+
font-size: 1.5rem;
|
|
98
|
+
font-weight: 700;
|
|
99
|
+
display: flex;
|
|
100
|
+
align-items: center;
|
|
101
|
+
gap: 0.5rem;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/* Metrics Cards */
|
|
105
|
+
.metrics-grid {
|
|
106
|
+
display: grid;
|
|
107
|
+
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
|
108
|
+
gap: 1.5rem;
|
|
109
|
+
padding: 2rem;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
.metric-card {
|
|
113
|
+
padding: 1.5rem;
|
|
114
|
+
background: var(--bg-glass);
|
|
115
|
+
backdrop-filter: blur(12px);
|
|
116
|
+
border-radius: 16px;
|
|
117
|
+
border: 1px solid var(--border-glass);
|
|
118
|
+
transition: all 0.3s ease;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
.metric-card:hover {
|
|
122
|
+
background: var(--bg-glass-hover);
|
|
123
|
+
transform: translateY(-4px);
|
|
124
|
+
box-shadow: 0 12px 40px rgba(0, 0, 0, 0.4);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
.metric-value {
|
|
128
|
+
font-family: var(--font-heading);
|
|
129
|
+
font-size: 2.5rem;
|
|
130
|
+
font-weight: 700;
|
|
131
|
+
margin-bottom: 0.5rem;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
.metric-label {
|
|
135
|
+
font-size: 0.875rem;
|
|
136
|
+
color: var(--text-secondary);
|
|
137
|
+
text-transform: uppercase;
|
|
138
|
+
letter-spacing: 0.05em;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/* Spec Table */
|
|
142
|
+
.spec-container {
|
|
143
|
+
margin: 2rem;
|
|
144
|
+
background: var(--bg-glass);
|
|
145
|
+
backdrop-filter: blur(12px);
|
|
146
|
+
border-radius: 20px;
|
|
147
|
+
border: 1px solid var(--border-glass);
|
|
148
|
+
overflow: hidden;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
.spec-header {
|
|
152
|
+
padding: 1.5rem 2rem;
|
|
153
|
+
background: linear-gradient(135deg, rgba(59, 130, 246, 0.1) 0%, rgba(99, 102, 241, 0.1) 100%);
|
|
154
|
+
border-bottom: 1px solid var(--border-glass);
|
|
155
|
+
display: flex;
|
|
156
|
+
justify-content: space-between;
|
|
157
|
+
align-items: center;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
.spec-table {
|
|
161
|
+
width: 100%;
|
|
162
|
+
border-collapse: collapse;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
.spec-table th {
|
|
166
|
+
background: rgba(255, 255, 255, 0.05);
|
|
167
|
+
padding: 1rem 1.5rem;
|
|
168
|
+
text-align: left;
|
|
169
|
+
font-family: var(--font-heading);
|
|
170
|
+
font-weight: 600;
|
|
171
|
+
font-size: 0.875rem;
|
|
172
|
+
color: var(--text-secondary);
|
|
173
|
+
text-transform: uppercase;
|
|
174
|
+
letter-spacing: 0.05em;
|
|
175
|
+
border-bottom: 1px solid var(--border-glass);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
.spec-table td {
|
|
179
|
+
padding: 1rem 1.5rem;
|
|
180
|
+
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
|
|
181
|
+
transition: all 0.2s ease;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
.spec-table tr:hover td {
|
|
185
|
+
background: rgba(255, 255, 255, 0.02);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/* Status badges */
|
|
189
|
+
.status-badge {
|
|
190
|
+
padding: 0.25rem 0.75rem;
|
|
191
|
+
border-radius: 20px;
|
|
192
|
+
font-size: 0.75rem;
|
|
193
|
+
font-weight: 600;
|
|
194
|
+
text-transform: uppercase;
|
|
195
|
+
letter-spacing: 0.05em;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
.status-draft { background: rgba(156, 163, 175, 0.2); color: #d1d5db; }
|
|
199
|
+
.status-planning { background: rgba(245, 158, 11, 0.2); color: #fbbf24; }
|
|
200
|
+
.status-in-progress { background: rgba(59, 130, 246, 0.2); color: #60a5fa; }
|
|
201
|
+
.status-completed { background: rgba(16, 185, 129, 0.2); color: #34d399; }
|
|
202
|
+
|
|
203
|
+
/* Buttons */
|
|
204
|
+
.btn {
|
|
205
|
+
padding: 0.75rem 1.5rem;
|
|
206
|
+
border-radius: 12px;
|
|
207
|
+
font-family: var(--font-heading);
|
|
208
|
+
font-weight: 600;
|
|
209
|
+
font-size: 0.875rem;
|
|
210
|
+
border: none;
|
|
211
|
+
cursor: pointer;
|
|
212
|
+
transition: all 0.3s ease;
|
|
213
|
+
display: inline-flex;
|
|
214
|
+
align-items: center;
|
|
215
|
+
gap: 0.5rem;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
.btn-primary {
|
|
219
|
+
background: var(--accent-blue);
|
|
220
|
+
color: white;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
.btn-primary:hover {
|
|
224
|
+
transform: translateY(-2px);
|
|
225
|
+
box-shadow: 0 8px 25px rgba(59, 130, 246, 0.4);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
.btn-secondary {
|
|
229
|
+
background: var(--bg-glass);
|
|
230
|
+
backdrop-filter: blur(12px);
|
|
231
|
+
color: var(--text-primary);
|
|
232
|
+
border: 1px solid var(--border-glass);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
.btn-secondary:hover {
|
|
236
|
+
background: var(--bg-glass-hover);
|
|
237
|
+
transform: translateY(-2px);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/* Modal */
|
|
241
|
+
.modal-overlay {
|
|
242
|
+
position: fixed;
|
|
243
|
+
inset: 0;
|
|
244
|
+
background: rgba(0, 0, 0, 0.8);
|
|
245
|
+
backdrop-filter: blur(8px);
|
|
246
|
+
display: flex;
|
|
247
|
+
align-items: center;
|
|
248
|
+
justify-content: center;
|
|
249
|
+
z-index: 100;
|
|
250
|
+
padding: 2rem;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
.modal-content {
|
|
254
|
+
background: var(--bg-glass);
|
|
255
|
+
backdrop-filter: blur(20px);
|
|
256
|
+
border: 1px solid var(--border-glass);
|
|
257
|
+
border-radius: 24px;
|
|
258
|
+
width: 90vw;
|
|
259
|
+
height: 85vh;
|
|
260
|
+
max-width: none;
|
|
261
|
+
display: grid;
|
|
262
|
+
grid-template-columns: 60% 40%;
|
|
263
|
+
grid-template-rows: auto 1fr;
|
|
264
|
+
overflow: hidden;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
.modal-header {
|
|
268
|
+
grid-column: 1 / -1;
|
|
269
|
+
background: linear-gradient(135deg, rgba(59, 130, 246, 0.1) 0%, rgba(99, 102, 241, 0.1) 100%);
|
|
270
|
+
padding: 1.5rem 2rem;
|
|
271
|
+
border-bottom: 1px solid var(--border-glass);
|
|
272
|
+
display: flex;
|
|
273
|
+
justify-content: space-between;
|
|
274
|
+
align-items: center;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
.modal-title-section {
|
|
278
|
+
display: flex;
|
|
279
|
+
align-items: center;
|
|
280
|
+
gap: 1rem;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
.worktree-info {
|
|
284
|
+
background: rgba(255, 255, 255, 0.1);
|
|
285
|
+
padding: 0.5rem 1rem;
|
|
286
|
+
border-radius: 20px;
|
|
287
|
+
font-size: 0.875rem;
|
|
288
|
+
display: flex;
|
|
289
|
+
align-items: center;
|
|
290
|
+
gap: 0.5rem;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
.modal-main-container {
|
|
294
|
+
display: flex;
|
|
295
|
+
flex-direction: column;
|
|
296
|
+
overflow: hidden;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
.modal-nav {
|
|
300
|
+
padding: 1rem 2rem 0;
|
|
301
|
+
border-bottom: 1px solid var(--border-glass);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
.nav-tabs {
|
|
305
|
+
display: flex;
|
|
306
|
+
gap: 0.5rem;
|
|
307
|
+
margin-bottom: 1rem;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
.nav-tab {
|
|
311
|
+
padding: 0.5rem 1rem;
|
|
312
|
+
border-radius: 20px;
|
|
313
|
+
background: transparent;
|
|
314
|
+
border: 1px solid rgba(255, 255, 255, 0.1);
|
|
315
|
+
color: var(--text-secondary);
|
|
316
|
+
font-family: var(--font-body);
|
|
317
|
+
font-size: 0.875rem;
|
|
318
|
+
cursor: pointer;
|
|
319
|
+
transition: all 0.2s ease;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
.nav-tab:hover {
|
|
323
|
+
background: rgba(255, 255, 255, 0.05);
|
|
324
|
+
color: var(--text-primary);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
.nav-tab.active {
|
|
328
|
+
background: var(--accent-blue);
|
|
329
|
+
color: white;
|
|
330
|
+
border-color: var(--accent-blue);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
.modal-main {
|
|
334
|
+
padding: 2rem;
|
|
335
|
+
overflow-y: auto;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
.modal-activity {
|
|
339
|
+
border-left: 1px solid var(--border-glass);
|
|
340
|
+
overflow: hidden;
|
|
341
|
+
background: rgba(0, 0, 0, 0.1);
|
|
342
|
+
display: flex;
|
|
343
|
+
flex-direction: column;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
.activity-nav {
|
|
347
|
+
padding: 1rem 1.5rem 0;
|
|
348
|
+
border-bottom: 1px solid var(--border-glass);
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
.activity-tabs {
|
|
352
|
+
display: flex;
|
|
353
|
+
gap: 0.5rem;
|
|
354
|
+
margin-bottom: 1rem;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
.activity-tab {
|
|
358
|
+
padding: 0.5rem 1rem;
|
|
359
|
+
border-radius: 20px;
|
|
360
|
+
background: transparent;
|
|
361
|
+
border: 1px solid rgba(255, 255, 255, 0.1);
|
|
362
|
+
color: var(--text-secondary);
|
|
363
|
+
font-family: var(--font-body);
|
|
364
|
+
font-size: 0.875rem;
|
|
365
|
+
cursor: pointer;
|
|
366
|
+
transition: all 0.2s ease;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
.activity-tab:hover {
|
|
370
|
+
background: rgba(255, 255, 255, 0.05);
|
|
371
|
+
color: var(--text-primary);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
.activity-tab.active {
|
|
375
|
+
background: var(--accent-blue);
|
|
376
|
+
color: white;
|
|
377
|
+
border-color: var(--accent-blue);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
.activity-content {
|
|
381
|
+
flex: 1;
|
|
382
|
+
padding: 1.5rem;
|
|
383
|
+
overflow-y: auto;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
.sidebar-nav {
|
|
387
|
+
list-style: none;
|
|
388
|
+
padding: 0;
|
|
389
|
+
margin: 0;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
.sidebar-nav li {
|
|
393
|
+
margin-bottom: 0.5rem;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
.sidebar-nav button {
|
|
397
|
+
width: 100%;
|
|
398
|
+
text-align: left;
|
|
399
|
+
padding: 0.75rem;
|
|
400
|
+
border-radius: 8px;
|
|
401
|
+
background: transparent;
|
|
402
|
+
border: none;
|
|
403
|
+
color: var(--text-secondary);
|
|
404
|
+
font-family: var(--font-body);
|
|
405
|
+
cursor: pointer;
|
|
406
|
+
transition: all 0.2s ease;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
.sidebar-nav button:hover {
|
|
410
|
+
background: rgba(255, 255, 255, 0.05);
|
|
411
|
+
color: var(--text-primary);
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
.sidebar-nav button.active {
|
|
415
|
+
background: var(--accent-blue);
|
|
416
|
+
color: white;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
/* Activity items */
|
|
420
|
+
.activity-item {
|
|
421
|
+
background: rgba(255, 255, 255, 0.03);
|
|
422
|
+
border-radius: 8px;
|
|
423
|
+
padding: 0.75rem;
|
|
424
|
+
margin-bottom: 0.5rem;
|
|
425
|
+
border: 1px solid rgba(255, 255, 255, 0.05);
|
|
426
|
+
transition: all 0.2s ease;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
.activity-item:hover {
|
|
430
|
+
background: rgba(255, 255, 255, 0.05);
|
|
431
|
+
border-color: rgba(255, 255, 255, 0.1);
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
.activity-time {
|
|
435
|
+
font-size: 0.7rem;
|
|
436
|
+
color: var(--text-muted);
|
|
437
|
+
margin-bottom: 0.25rem;
|
|
438
|
+
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
.activity-role {
|
|
442
|
+
font-weight: 600;
|
|
443
|
+
color: var(--text-primary);
|
|
444
|
+
margin-bottom: 0.25rem;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
.activity-note {
|
|
448
|
+
font-size: 0.875rem;
|
|
449
|
+
color: var(--text-secondary);
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
/* Responsive design */
|
|
453
|
+
@media (max-width: 768px) {
|
|
454
|
+
.metrics-grid {
|
|
455
|
+
grid-template-columns: 1fr;
|
|
456
|
+
padding: 1rem;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
.spec-container {
|
|
460
|
+
margin: 1rem;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
.modal-content {
|
|
464
|
+
grid-template-columns: 1fr;
|
|
465
|
+
grid-template-rows: auto auto 1fr auto;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
.modal-sidebar,
|
|
469
|
+
.modal-activity {
|
|
470
|
+
border: none;
|
|
471
|
+
border-top: 1px solid var(--border-glass);
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
/* Loading animation */
|
|
476
|
+
.loading {
|
|
477
|
+
display: inline-block;
|
|
478
|
+
width: 20px;
|
|
479
|
+
height: 20px;
|
|
480
|
+
border: 2px solid rgba(255, 255, 255, 0.3);
|
|
481
|
+
border-radius: 50%;
|
|
482
|
+
border-top-color: #ffffff;
|
|
483
|
+
animation: spin 1s ease-in-out infinite;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
@keyframes spin {
|
|
487
|
+
to { transform: rotate(360deg); }
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
/* Fade in animation */
|
|
491
|
+
.fade-in {
|
|
492
|
+
animation: fadeIn 0.5s ease-out;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
@keyframes fadeIn {
|
|
496
|
+
from { opacity: 0; transform: translateY(20px); }
|
|
497
|
+
to { opacity: 1; transform: translateY(0); }
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
/* Status Banner */
|
|
501
|
+
.status-banner {
|
|
502
|
+
background: var(--bg-glass);
|
|
503
|
+
backdrop-filter: blur(12px);
|
|
504
|
+
border: 1px solid var(--border-glass);
|
|
505
|
+
border-radius: 16px;
|
|
506
|
+
margin: 1.5rem 2rem;
|
|
507
|
+
padding: 1.5rem 2rem;
|
|
508
|
+
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
.status-content {
|
|
512
|
+
display: grid;
|
|
513
|
+
grid-template-columns: 1fr auto 1fr;
|
|
514
|
+
align-items: center;
|
|
515
|
+
gap: 1rem;
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
.status-left {
|
|
519
|
+
display: flex;
|
|
520
|
+
align-items: center;
|
|
521
|
+
gap: 0.75rem;
|
|
522
|
+
font-size: 0.95rem;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
.workshop-icon {
|
|
526
|
+
font-size: 1.2rem;
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
.workshop-name {
|
|
530
|
+
font-weight: 600;
|
|
531
|
+
color: var(--text-primary);
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
.status-separator {
|
|
535
|
+
color: var(--text-muted);
|
|
536
|
+
margin: 0 0.25rem;
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
.build-status .building {
|
|
540
|
+
color: var(--accent-yellow);
|
|
541
|
+
display: flex;
|
|
542
|
+
align-items: center;
|
|
543
|
+
gap: 0.5rem;
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
.build-status .idle {
|
|
547
|
+
color: var(--text-secondary);
|
|
548
|
+
display: flex;
|
|
549
|
+
align-items: center;
|
|
550
|
+
gap: 0.5rem;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
.building-icon {
|
|
554
|
+
animation: spin 2s linear infinite;
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
.status-right {
|
|
558
|
+
font-size: 0.9rem;
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
.worktree-stats {
|
|
562
|
+
display: flex;
|
|
563
|
+
align-items: center;
|
|
564
|
+
gap: 0.5rem;
|
|
565
|
+
flex-wrap: wrap;
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
.stat-label {
|
|
569
|
+
color: var(--text-secondary);
|
|
570
|
+
font-weight: 600;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
.stat-value {
|
|
574
|
+
color: var(--text-primary);
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
.stat-value.draft {
|
|
578
|
+
color: #9ca3af;
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
.stat-value.in-progress {
|
|
582
|
+
color: var(--accent-yellow);
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
.stat-value.done {
|
|
586
|
+
color: var(--accent-green);
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
.stat-value.committed {
|
|
590
|
+
color: var(--accent-green);
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
.stat-value.dirty {
|
|
594
|
+
color: var(--accent-yellow);
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
.stat-value.clean {
|
|
598
|
+
color: var(--text-secondary);
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
/* Responsive adjustments for status banner */
|
|
602
|
+
@media (max-width: 768px) {
|
|
603
|
+
.status-banner {
|
|
604
|
+
margin: 1rem;
|
|
605
|
+
padding: 1rem;
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
.status-content {
|
|
609
|
+
flex-direction: column;
|
|
610
|
+
align-items: flex-start;
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
.worktree-stats {
|
|
614
|
+
font-size: 0.8rem;
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
</style>
|
|
618
|
+
</head>
|
|
619
|
+
|
|
620
|
+
<body class="h-full"
|
|
621
|
+
x-data="{
|
|
622
|
+
selectedSpec: null,
|
|
623
|
+
section: 'summary',
|
|
624
|
+
specs: [],
|
|
625
|
+
filteredSpecs: [],
|
|
626
|
+
loading: false,
|
|
627
|
+
searchTerm: '',
|
|
628
|
+
categoryFilter: 'all',
|
|
629
|
+
statusFilter: 'all',
|
|
630
|
+
showActivityLog: false,
|
|
631
|
+
activities: [],
|
|
632
|
+
async fetchSpecs() {
|
|
633
|
+
this.loading = true;
|
|
634
|
+
try {
|
|
635
|
+
const response = await fetch('/api/tools/bob.spec.list');
|
|
636
|
+
const data = await response.json();
|
|
637
|
+
this.specs = data.specs || [];
|
|
638
|
+
this.filteredSpecs = [...this.specs];
|
|
639
|
+
this.filterSpecs();
|
|
640
|
+
} catch (error) {
|
|
641
|
+
console.error('Failed to fetch specs:', error);
|
|
642
|
+
} finally {
|
|
643
|
+
this.loading = false;
|
|
644
|
+
}
|
|
645
|
+
},
|
|
646
|
+
filterSpecs() {
|
|
647
|
+
this.filteredSpecs = this.specs.filter(spec => {
|
|
648
|
+
const matchesSearch = this.searchTerm === '' ||
|
|
649
|
+
spec.title.toLowerCase().includes(this.searchTerm.toLowerCase());
|
|
650
|
+
const matchesCategory = this.categoryFilter === 'all' ||
|
|
651
|
+
spec.category === this.categoryFilter;
|
|
652
|
+
const matchesStatus = this.statusFilter === 'all' ||
|
|
653
|
+
spec.state === this.statusFilter;
|
|
654
|
+
return matchesSearch && matchesCategory && matchesStatus;
|
|
655
|
+
});
|
|
656
|
+
},
|
|
657
|
+
async openSpec(specId) {
|
|
658
|
+
try {
|
|
659
|
+
const response = await fetch('/api/tools/bob.spec.get', {
|
|
660
|
+
method: 'POST',
|
|
661
|
+
headers: { 'Content-Type': 'application/json' },
|
|
662
|
+
body: JSON.stringify({ spec_id: specId })
|
|
663
|
+
});
|
|
664
|
+
const spec = await response.json();
|
|
665
|
+
this.selectedSpec = spec;
|
|
666
|
+
this.section = 'summary';
|
|
667
|
+
} catch (error) {
|
|
668
|
+
console.error('Failed to fetch spec:', error);
|
|
669
|
+
alert('Failed to load SPEC details');
|
|
670
|
+
}
|
|
671
|
+
},
|
|
672
|
+
async exportToMarkdown(specId) {
|
|
673
|
+
try {
|
|
674
|
+
const response = await fetch('/api/tools/bob.spec.export', {
|
|
675
|
+
method: 'POST',
|
|
676
|
+
headers: { 'Content-Type': 'application/json' },
|
|
677
|
+
body: JSON.stringify({ spec_id: specId })
|
|
678
|
+
});
|
|
679
|
+
const data = await response.json();
|
|
680
|
+
if (data.markdown_path) {
|
|
681
|
+
alert(`✅ SPEC exported to: ${data.markdown_path}`);
|
|
682
|
+
} else {
|
|
683
|
+
alert('❌ Export failed');
|
|
684
|
+
}
|
|
685
|
+
} catch (error) {
|
|
686
|
+
console.error('Export error:', error);
|
|
687
|
+
alert('❌ Export failed');
|
|
688
|
+
}
|
|
689
|
+
},
|
|
690
|
+
getWorktreeName(spec) {
|
|
691
|
+
// Generate worktree name from spec title
|
|
692
|
+
if (!spec || !spec.title) return 'none';
|
|
693
|
+
return 'feature/' + spec.title.toLowerCase()
|
|
694
|
+
.replace(/[^a-z0-9\s]/g, '')
|
|
695
|
+
.replace(/\s+/g, '-')
|
|
696
|
+
.substring(0, 20);
|
|
697
|
+
},
|
|
698
|
+
loadActivityLog() {
|
|
699
|
+
// Generate mock activity log
|
|
700
|
+
this.activities = [
|
|
701
|
+
{
|
|
702
|
+
timestamp: new Date(Date.now() - 3600000).toISOString(),
|
|
703
|
+
type: 'spec_created',
|
|
704
|
+
author: 'bob-architect',
|
|
705
|
+
message: 'Created new SPEC: Enhanced Bob MCP Dashboard',
|
|
706
|
+
spec_id: 'SPEC-2025-09-20-enhanced-bob-mcp-dashboard-with-alpine-design'
|
|
707
|
+
},
|
|
708
|
+
{
|
|
709
|
+
timestamp: new Date(Date.now() - 7200000).toISOString(),
|
|
710
|
+
type: 'worktree_created',
|
|
711
|
+
author: 'bob-engineer',
|
|
712
|
+
message: 'Created worktree: feature/dashboard-enhancement',
|
|
713
|
+
branch: 'feature/dashboard-enhancement'
|
|
714
|
+
},
|
|
715
|
+
{
|
|
716
|
+
timestamp: new Date(Date.now() - 10800000).toISOString(),
|
|
717
|
+
type: 'commit',
|
|
718
|
+
author: 'bob-engineer',
|
|
719
|
+
message: 'Added glassmorphism design and Alpine.js components',
|
|
720
|
+
files: ['public/index.html', 'styles/dashboard.css']
|
|
721
|
+
},
|
|
722
|
+
{
|
|
723
|
+
timestamp: new Date(Date.now() - 14400000).toISOString(),
|
|
724
|
+
type: 'spec_updated',
|
|
725
|
+
author: 'bob-architect',
|
|
726
|
+
message: 'Updated implementation plan with search/filter requirements',
|
|
727
|
+
spec_id: 'SPEC-2025-09-20-enhanced-bob-mcp-dashboard-with-alpine-design'
|
|
728
|
+
}
|
|
729
|
+
];
|
|
730
|
+
this.showActivityLog = true;
|
|
731
|
+
}
|
|
732
|
+
}"
|
|
733
|
+
x-init="fetchSpecs()"
|
|
734
|
+
@keydown.escape.window="selectedSpec = null; showActivityLog = false">
|
|
735
|
+
|
|
736
|
+
<!-- Header -->
|
|
737
|
+
<header class="header">
|
|
738
|
+
<div class="logo">
|
|
739
|
+
<span>👷</span>
|
|
740
|
+
<span class="text-gradient">Bob's Workshop</span>
|
|
741
|
+
</div>
|
|
742
|
+
<div style="font-size: 1rem; color: var(--text-secondary); white-space: nowrap; min-width: 200px;"
|
|
743
|
+
x-data="{ projectName: 'bobs-workshop' }">
|
|
744
|
+
now servicing: <span x-text="projectName"></span>
|
|
745
|
+
</div>
|
|
746
|
+
</header>
|
|
747
|
+
|
|
748
|
+
<!-- Top Status Banner -->
|
|
749
|
+
<div class="status-banner fade-in"
|
|
750
|
+
x-data="{
|
|
751
|
+
worktrees: { committed: 0, dirty: 0, clean: 0 },
|
|
752
|
+
specs: { draft: 0, 'in-progress': 0, completed: 0 },
|
|
753
|
+
projectName: 'bobs-workshop',
|
|
754
|
+
buildStatus: 'idle',
|
|
755
|
+
async loadStatus() {
|
|
756
|
+
try {
|
|
757
|
+
const [worktreesRes, specsRes] = await Promise.all([
|
|
758
|
+
fetch('/api/tools/bob.worktree.list').catch(() => ({ json: () => ({committed:0,dirty:0,clean:0}) })),
|
|
759
|
+
fetch('/api/tools/bob.spec.list')
|
|
760
|
+
]);
|
|
761
|
+
|
|
762
|
+
const worktreesData = await worktreesRes.json();
|
|
763
|
+
this.worktrees = worktreesData;
|
|
764
|
+
|
|
765
|
+
const specsData = await specsRes.json();
|
|
766
|
+
const allSpecs = specsData.specs || [];
|
|
767
|
+
|
|
768
|
+
// Count specs by state
|
|
769
|
+
this.specs = {
|
|
770
|
+
draft: allSpecs.filter(s => s.state === 'draft').length,
|
|
771
|
+
'in-progress': allSpecs.filter(s => s.state === 'in-progress').length,
|
|
772
|
+
completed: allSpecs.filter(s => s.state === 'completed').length
|
|
773
|
+
};
|
|
774
|
+
|
|
775
|
+
// Check if anything is currently building
|
|
776
|
+
this.buildStatus = this.specs['in-progress'] > 0 ? 'building' : 'idle';
|
|
777
|
+
} catch (error) {
|
|
778
|
+
console.error('Failed to load status:', error);
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
}"
|
|
782
|
+
x-init="loadStatus()">
|
|
783
|
+
|
|
784
|
+
<div class="status-content">
|
|
785
|
+
<div class="status-left">
|
|
786
|
+
<span class="workshop-icon">👷</span>
|
|
787
|
+
<span class="workshop-name">Bob's Workshop</span>
|
|
788
|
+
<span class="status-separator">|</span>
|
|
789
|
+
<span class="build-status">
|
|
790
|
+
<template x-if="buildStatus === 'building'">
|
|
791
|
+
<span class="building">
|
|
792
|
+
<span class="building-icon">⚙️</span>
|
|
793
|
+
ready
|
|
794
|
+
</span>
|
|
795
|
+
</template>
|
|
796
|
+
<template x-if="buildStatus === 'idle'">
|
|
797
|
+
<span class="idle">
|
|
798
|
+
<span class="idle-icon">💤</span>
|
|
799
|
+
ready
|
|
800
|
+
</span>
|
|
801
|
+
</template>
|
|
802
|
+
</span>
|
|
803
|
+
</div>
|
|
804
|
+
|
|
805
|
+
<div class="status-center" style="display: flex; align-items: center; gap: 1rem;">
|
|
806
|
+
<!-- SPECs metrics -->
|
|
807
|
+
<div class="worktree-stats" style="display: flex; align-items: center; gap: 0.5rem;">
|
|
808
|
+
<span class="stat-label">SPECs:</span>
|
|
809
|
+
<span class="stat-value draft" x-text="specs.draft + 'd'"></span>
|
|
810
|
+
<span class="status-separator">|</span>
|
|
811
|
+
<span class="stat-value in-progress" x-text="specs['in-progress'] + 'p'"></span>
|
|
812
|
+
<span class="status-separator">|</span>
|
|
813
|
+
<span class="stat-value done" x-text="specs.completed + '✓'"></span>
|
|
814
|
+
</div>
|
|
815
|
+
|
|
816
|
+
<!-- Worktree metrics -->
|
|
817
|
+
<div class="worktree-stats" style="display: flex; align-items: center; gap: 0.5rem;">
|
|
818
|
+
<span class="stat-label">Trees:</span>
|
|
819
|
+
<span class="stat-value committed" x-text="worktrees.committed + 'c'"></span>
|
|
820
|
+
<span class="status-separator">|</span>
|
|
821
|
+
<span class="stat-value dirty" x-text="worktrees.dirty + '!'"></span>
|
|
822
|
+
<span class="status-separator">|</span>
|
|
823
|
+
<span class="stat-value clean" x-text="worktrees.clean + '~'"></span>
|
|
824
|
+
</div>
|
|
825
|
+
</div>
|
|
826
|
+
|
|
827
|
+
<div class="status-right">
|
|
828
|
+
<!-- Keep right side for other info -->
|
|
829
|
+
</div>
|
|
830
|
+
</div>
|
|
831
|
+
</div>
|
|
832
|
+
|
|
833
|
+
<!-- Worktree List -->
|
|
834
|
+
<div class="worktree-container fade-in"
|
|
835
|
+
x-data="{
|
|
836
|
+
worktrees: [],
|
|
837
|
+
loading: false,
|
|
838
|
+
collapsed: false,
|
|
839
|
+
async loadWorktrees() {
|
|
840
|
+
this.loading = true;
|
|
841
|
+
try {
|
|
842
|
+
const response = await fetch('/api/tools/bob.worktree.list');
|
|
843
|
+
const data = await response.json();
|
|
844
|
+
|
|
845
|
+
// Format worktrees for display
|
|
846
|
+
this.worktrees = [
|
|
847
|
+
...data.committed?.map(name => ({ name, status: 'committed', color: 'var(--accent-green)' })) || [],
|
|
848
|
+
...data.dirty?.map(name => ({ name, status: 'dirty', color: 'var(--accent-yellow)' })) || [],
|
|
849
|
+
...data.clean?.map(name => ({ name, status: 'clean', color: 'var(--text-secondary)' })) || []
|
|
850
|
+
];
|
|
851
|
+
} catch (error) {
|
|
852
|
+
console.error('Failed to load worktrees:', error);
|
|
853
|
+
this.worktrees = [];
|
|
854
|
+
} finally {
|
|
855
|
+
this.loading = false;
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
}"
|
|
859
|
+
x-init="loadWorktrees()"
|
|
860
|
+
style="margin: 1.5rem 2rem;">
|
|
861
|
+
|
|
862
|
+
<div class="glass" style="border-radius: 16px; padding: 1.5rem;">
|
|
863
|
+
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 1rem;">
|
|
864
|
+
<div style="display: flex; align-items: center; gap: 0.5rem; cursor: pointer;" @click="collapsed = !collapsed">
|
|
865
|
+
<span x-show="!collapsed" style="transition: transform 0.2s;">🔽</span>
|
|
866
|
+
<span x-show="collapsed" style="transition: transform 0.2s;">▶️</span>
|
|
867
|
+
<h3 class="heading" style="margin: 0; font-size: 1.1rem;">Active Worktrees</h3>
|
|
868
|
+
<span style="font-size: 0.8rem; color: var(--text-muted);">(<span x-text="worktrees.length"></span>)</span>
|
|
869
|
+
</div>
|
|
870
|
+
<button class="btn btn-secondary" style="padding: 0.5rem 1rem; font-size: 0.75rem;" @click="loadWorktrees()">
|
|
871
|
+
🔄 Refresh
|
|
872
|
+
</button>
|
|
873
|
+
</div>
|
|
874
|
+
|
|
875
|
+
<div x-show="!collapsed">
|
|
876
|
+
<div x-show="loading" style="text-align: center; padding: 1rem; color: var(--text-muted);">
|
|
877
|
+
<span class="loading"></span> Loading worktrees...
|
|
878
|
+
</div>
|
|
879
|
+
|
|
880
|
+
<div x-show="!loading && worktrees.length === 0" style="text-align: center; padding: 2rem; color: var(--text-muted);">
|
|
881
|
+
No active worktrees found
|
|
882
|
+
</div>
|
|
883
|
+
|
|
884
|
+
<div x-show="!loading && worktrees.length > 0"
|
|
885
|
+
style="display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 1rem;"
|
|
886
|
+
x-transition:enter="transition ease-out duration-300"
|
|
887
|
+
x-transition:enter-start="opacity-0 transform scale-95"
|
|
888
|
+
x-transition:enter-end="opacity-100 transform scale-100">
|
|
889
|
+
<template x-for="worktree in worktrees" :key="worktree.name">
|
|
890
|
+
<div class="glass-hover"
|
|
891
|
+
style="padding: 1rem; border-radius: 12px; border: 1px solid var(--border-glass); background: rgba(255, 255, 255, 0.02);">
|
|
892
|
+
<div style="display: flex; justify-content: space-between; align-items: center;">
|
|
893
|
+
<div>
|
|
894
|
+
<div style="font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace; font-size: 0.9rem; color: var(--text-primary); margin-bottom: 0.25rem;"
|
|
895
|
+
x-text="worktree.name"></div>
|
|
896
|
+
<div style="font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.05em; font-weight: 600;"
|
|
897
|
+
:style="`color: ${worktree.color}`"
|
|
898
|
+
x-text="worktree.status"></div>
|
|
899
|
+
</div>
|
|
900
|
+
<div style="font-size: 1.2rem;">
|
|
901
|
+
<span x-show="worktree.status === 'committed'">✅</span>
|
|
902
|
+
<span x-show="worktree.status === 'dirty'">⚠️</span>
|
|
903
|
+
<span x-show="worktree.status === 'clean'">💤</span>
|
|
904
|
+
</div>
|
|
905
|
+
</div>
|
|
906
|
+
</div>
|
|
907
|
+
</template>
|
|
908
|
+
</div>
|
|
909
|
+
</div>
|
|
910
|
+
</div>
|
|
911
|
+
</div>
|
|
912
|
+
|
|
913
|
+
<!-- Spec Table -->
|
|
914
|
+
<div class="spec-container fade-in">
|
|
915
|
+
<div class="spec-header">
|
|
916
|
+
<h2 class="heading" style="margin: 0; font-size: 1.25rem;">SPEC Explorer</h2>
|
|
917
|
+
<div style="display: flex; gap: 1rem; align-items: center;">
|
|
918
|
+
<span x-show="loading" class="loading"></span>
|
|
919
|
+
<button class="btn btn-secondary" @click="fetchSpecs()">
|
|
920
|
+
🔄 Refresh
|
|
921
|
+
</button>
|
|
922
|
+
</div>
|
|
923
|
+
</div>
|
|
924
|
+
|
|
925
|
+
<!-- Search and Filter Controls -->
|
|
926
|
+
<div style="padding: 1.5rem; border-bottom: 1px solid var(--border-glass); background: rgba(255, 255, 255, 0.02);">
|
|
927
|
+
<div style="display: grid; grid-template-columns: 2fr 1fr 1fr auto; gap: 1rem; align-items: center;">
|
|
928
|
+
|
|
929
|
+
<!-- Search Input -->
|
|
930
|
+
<div style="position: relative;">
|
|
931
|
+
<input type="text"
|
|
932
|
+
placeholder="🔍 Search SPECs..."
|
|
933
|
+
x-model="searchTerm"
|
|
934
|
+
@input="filterSpecs()"
|
|
935
|
+
style="width: 100%; padding: 0.75rem 1rem; background: var(--bg-glass); border: 1px solid var(--border-glass); border-radius: 8px; color: var(--text-primary); font-family: var(--font-body);">
|
|
936
|
+
</div>
|
|
937
|
+
|
|
938
|
+
<!-- Category Filter -->
|
|
939
|
+
<select x-model="categoryFilter"
|
|
940
|
+
@change="filterSpecs()"
|
|
941
|
+
style="padding: 0.75rem 1rem; background: var(--bg-glass); border: 1px solid var(--border-glass); border-radius: 8px; color: var(--text-primary); font-family: var(--font-body);">
|
|
942
|
+
<option value="all">All Categories</option>
|
|
943
|
+
<option value="frontend">Frontend</option>
|
|
944
|
+
<option value="backend">Backend</option>
|
|
945
|
+
<option value="fullstack">Fullstack</option>
|
|
946
|
+
<option value="general">General</option>
|
|
947
|
+
</select>
|
|
948
|
+
|
|
949
|
+
<!-- Status Filter -->
|
|
950
|
+
<select x-model="statusFilter"
|
|
951
|
+
@change="filterSpecs()"
|
|
952
|
+
style="padding: 0.75rem 1rem; background: var(--bg-glass); border: 1px solid var(--border-glass); border-radius: 8px; color: var(--text-primary); font-family: var(--font-body);">
|
|
953
|
+
<option value="all">All Status</option>
|
|
954
|
+
<option value="draft">Draft</option>
|
|
955
|
+
<option value="planning">Planning</option>
|
|
956
|
+
<option value="in-progress">In Progress</option>
|
|
957
|
+
<option value="completed">Completed</option>
|
|
958
|
+
</select>
|
|
959
|
+
|
|
960
|
+
<!-- Clear Filters -->
|
|
961
|
+
<button class="btn btn-secondary"
|
|
962
|
+
@click="searchTerm = ''; categoryFilter = 'all'; statusFilter = 'all'; filterSpecs()"
|
|
963
|
+
style="padding: 0.75rem;">
|
|
964
|
+
✕ Clear
|
|
965
|
+
</button>
|
|
966
|
+
</div>
|
|
967
|
+
|
|
968
|
+
<!-- Results Count -->
|
|
969
|
+
<div style="margin-top: 1rem; font-size: 0.875rem; color: var(--text-secondary);">
|
|
970
|
+
<span x-text="filteredSpecs.length"></span> of <span x-text="specs.length"></span> SPECs
|
|
971
|
+
</div>
|
|
972
|
+
</div>
|
|
973
|
+
|
|
974
|
+
<table class="spec-table">
|
|
975
|
+
<thead>
|
|
976
|
+
<tr>
|
|
977
|
+
<th>Title</th>
|
|
978
|
+
<th>Category</th>
|
|
979
|
+
<th>Status</th>
|
|
980
|
+
<th>Worktree</th>
|
|
981
|
+
<th>Modified</th>
|
|
982
|
+
<th>Actions</th>
|
|
983
|
+
</tr>
|
|
984
|
+
</thead>
|
|
985
|
+
<tbody>
|
|
986
|
+
<template x-for="spec in filteredSpecs" :key="spec.spec_id">
|
|
987
|
+
<tr>
|
|
988
|
+
<td>
|
|
989
|
+
<div style="font-weight: 600; cursor: pointer; color: var(--text-primary);"
|
|
990
|
+
x-text="spec.title"
|
|
991
|
+
@click="openSpec(spec.spec_id)"></div>
|
|
992
|
+
<div style="font-size: 0.75rem; color: var(--text-muted); margin-top: 0.25rem;"
|
|
993
|
+
x-text="spec.spec_id"></div>
|
|
994
|
+
</td>
|
|
995
|
+
<td style="color: var(--text-secondary);" x-text="spec.category || '—'"></td>
|
|
996
|
+
<td>
|
|
997
|
+
<span class="status-badge"
|
|
998
|
+
:class="`status-${spec.state}`"
|
|
999
|
+
x-text="spec.state"></span>
|
|
1000
|
+
</td>
|
|
1001
|
+
<td style="color: var(--text-secondary); font-family: monospace; font-size: 0.875rem;">
|
|
1002
|
+
<div x-data="{ worktreeName: getWorktreeName(spec) }">
|
|
1003
|
+
<span x-show="worktreeName !== 'none'" x-text="worktreeName" style="color: var(--accent-blue);"></span>
|
|
1004
|
+
<span x-show="worktreeName === 'none'" style="color: var(--text-muted);">—</span>
|
|
1005
|
+
<div x-show="worktreeName !== 'none'" style="font-size: 0.75rem; margin-top: 0.25rem;">
|
|
1006
|
+
<span style="color: var(--accent-green);">✓ clean</span>
|
|
1007
|
+
</div>
|
|
1008
|
+
</div>
|
|
1009
|
+
</td>
|
|
1010
|
+
<td style="color: var(--text-secondary); font-size: 0.875rem;"
|
|
1011
|
+
x-text="new Date(spec.updated_at).toLocaleDateString()"></td>
|
|
1012
|
+
<td>
|
|
1013
|
+
<button class="btn btn-primary" style="padding: 0.5rem 1rem; font-size: 0.75rem;"
|
|
1014
|
+
@click="openSpec(spec.spec_id)">
|
|
1015
|
+
View
|
|
1016
|
+
</button>
|
|
1017
|
+
</td>
|
|
1018
|
+
</tr>
|
|
1019
|
+
</template>
|
|
1020
|
+
</tbody>
|
|
1021
|
+
</table>
|
|
1022
|
+
|
|
1023
|
+
<div x-show="filteredSpecs.length === 0 && !loading"
|
|
1024
|
+
style="text-align: center; padding: 3rem; color: var(--text-muted);">
|
|
1025
|
+
<div x-show="specs.length === 0">No SPECs found. Create your first SPEC to get started!</div>
|
|
1026
|
+
<div x-show="specs.length > 0">No SPECs match your current filters.</div>
|
|
1027
|
+
</div>
|
|
1028
|
+
|
|
1029
|
+
<!-- Activity Log Button -->
|
|
1030
|
+
<div style="padding: 1.5rem; border-top: 1px solid var(--border-glass); text-center;">
|
|
1031
|
+
<button class="btn btn-secondary" @click="loadActivityLog()">
|
|
1032
|
+
📊 View Activity Log
|
|
1033
|
+
</button>
|
|
1034
|
+
</div>
|
|
1035
|
+
</div>
|
|
1036
|
+
|
|
1037
|
+
<!-- Enhanced Modal -->
|
|
1038
|
+
<div class="modal-overlay"
|
|
1039
|
+
x-show="selectedSpec"
|
|
1040
|
+
x-transition:enter="transition ease-out duration-300"
|
|
1041
|
+
x-transition:enter-start="opacity-0"
|
|
1042
|
+
x-transition:enter-end="opacity-100"
|
|
1043
|
+
x-transition:leave="transition ease-in duration-200"
|
|
1044
|
+
x-transition:leave-start="opacity-100"
|
|
1045
|
+
x-transition:leave-end="opacity-0"
|
|
1046
|
+
@click.self="selectedSpec=null">
|
|
1047
|
+
|
|
1048
|
+
<div class="modal-content"
|
|
1049
|
+
x-show="selectedSpec"
|
|
1050
|
+
x-transition:enter="transition ease-out duration-300"
|
|
1051
|
+
x-transition:enter-start="opacity-0 scale-95"
|
|
1052
|
+
x-transition:enter-end="opacity-100 scale-100"
|
|
1053
|
+
x-transition:leave="transition ease-in duration-200"
|
|
1054
|
+
x-transition:leave-start="opacity-100 scale-100"
|
|
1055
|
+
x-transition:leave-end="opacity-0 scale-95">
|
|
1056
|
+
|
|
1057
|
+
<!-- Modal Header -->
|
|
1058
|
+
<div class="modal-header">
|
|
1059
|
+
<h2 class="heading" style="margin: 0; font-size: 1.5rem;" x-text="selectedSpec?.title"></h2>
|
|
1060
|
+
<div style="display: flex; gap: 1rem;">
|
|
1061
|
+
<button class="btn btn-primary" @click="exportToMarkdown(selectedSpec?.spec_id)">
|
|
1062
|
+
📄 Export
|
|
1063
|
+
</button>
|
|
1064
|
+
<button class="btn btn-secondary" @click="selectedSpec=null">
|
|
1065
|
+
✕
|
|
1066
|
+
</button>
|
|
1067
|
+
</div>
|
|
1068
|
+
</div>
|
|
1069
|
+
|
|
1070
|
+
<!-- Left Sidebar -->
|
|
1071
|
+
<div class="modal-sidebar">
|
|
1072
|
+
<h3 style="font-family: var(--font-heading); font-size: 0.875rem; margin: 0 0 1rem 0; color: var(--text-secondary); text-transform: uppercase; letter-spacing: 0.05em;">
|
|
1073
|
+
Sections
|
|
1074
|
+
</h3>
|
|
1075
|
+
<ul class="sidebar-nav">
|
|
1076
|
+
<li><button :class="{'active': section==='summary'}" @click="section='summary'">Executive Summary</button></li>
|
|
1077
|
+
<li><button :class="{'active': section==='product'}" @click="section='product'">Product Specs</button></li>
|
|
1078
|
+
<li><button :class="{'active': section==='architecture'}" @click="section='architecture'">Architecture</button></li>
|
|
1079
|
+
<li><button :class="{'active': section==='implementation'}" @click="section='implementation'">Implementation</button></li>
|
|
1080
|
+
<li><button :class="{'active': section==='research'}" @click="section='research'">Research</button></li>
|
|
1081
|
+
<li><button :class="{'active': section==='testing'}" @click="section='testing'">Testing</button></li>
|
|
1082
|
+
<li><button :class="{'active': section==='review'}" @click="section='review'">Review</button></li>
|
|
1083
|
+
</ul>
|
|
1084
|
+
</div>
|
|
1085
|
+
|
|
1086
|
+
<!-- Main Content -->
|
|
1087
|
+
<div class="modal-main">
|
|
1088
|
+
<template x-if="section==='summary'">
|
|
1089
|
+
<div>
|
|
1090
|
+
<h3 class="heading" style="margin: 0 0 1rem 0;">Executive Summary</h3>
|
|
1091
|
+
<div style="background: rgba(255, 255, 255, 0.03); padding: 1.5rem; border-radius: 12px; border: 1px solid rgba(255, 255, 255, 0.05);">
|
|
1092
|
+
<p x-text="selectedSpec?.sections?.executive_summary?.content || 'No executive summary yet'"></p>
|
|
1093
|
+
</div>
|
|
1094
|
+
</div>
|
|
1095
|
+
</template>
|
|
1096
|
+
|
|
1097
|
+
<template x-if="section==='product'">
|
|
1098
|
+
<div>
|
|
1099
|
+
<h3 class="heading" style="margin: 0 0 1rem 0;">Product Specifications</h3>
|
|
1100
|
+
<div style="background: rgba(255, 255, 255, 0.03); padding: 1.5rem; border-radius: 12px; border: 1px solid rgba(255, 255, 255, 0.05);">
|
|
1101
|
+
<p x-text="selectedSpec?.sections?.product_specifications?.content || 'No product specifications yet'"></p>
|
|
1102
|
+
</div>
|
|
1103
|
+
</div>
|
|
1104
|
+
</template>
|
|
1105
|
+
|
|
1106
|
+
<template x-if="section==='architecture'">
|
|
1107
|
+
<div>
|
|
1108
|
+
<h3 class="heading" style="margin: 0 0 1rem 0;">Architecture Analysis</h3>
|
|
1109
|
+
<div style="background: rgba(255, 255, 255, 0.03); padding: 1.5rem; border-radius: 12px; border: 1px solid rgba(255, 255, 255, 0.05);">
|
|
1110
|
+
<pre style="white-space: pre-wrap; font-family: var(--font-body); margin: 0;" x-text="selectedSpec?.sections?.architecture_analysis?.content || 'No architecture analysis yet'"></pre>
|
|
1111
|
+
</div>
|
|
1112
|
+
</div>
|
|
1113
|
+
</template>
|
|
1114
|
+
|
|
1115
|
+
<template x-if="section==='implementation'">
|
|
1116
|
+
<div>
|
|
1117
|
+
<h3 class="heading" style="margin: 0 0 1rem 0;">Implementation Plan</h3>
|
|
1118
|
+
<div style="background: rgba(255, 255, 255, 0.03); padding: 1.5rem; border-radius: 12px; border: 1px solid rgba(255, 255, 255, 0.05);">
|
|
1119
|
+
<pre style="white-space: pre-wrap; font-family: var(--font-body); margin: 0;" x-text="selectedSpec?.sections?.implementation_plan?.content || 'No implementation plan yet'"></pre>
|
|
1120
|
+
</div>
|
|
1121
|
+
</div>
|
|
1122
|
+
</template>
|
|
1123
|
+
|
|
1124
|
+
<template x-if="section==='research'">
|
|
1125
|
+
<div>
|
|
1126
|
+
<h3 class="heading" style="margin: 0 0 1rem 0;">Research Notes</h3>
|
|
1127
|
+
<div style="background: rgba(255, 255, 255, 0.03); padding: 1.5rem; border-radius: 12px; border: 1px solid rgba(255, 255, 255, 0.05);">
|
|
1128
|
+
<p x-text="selectedSpec?.sections?.research?.content || 'No research notes yet'"></p>
|
|
1129
|
+
</div>
|
|
1130
|
+
</div>
|
|
1131
|
+
</template>
|
|
1132
|
+
|
|
1133
|
+
<template x-if="section==='testing'">
|
|
1134
|
+
<div>
|
|
1135
|
+
<h3 class="heading" style="margin: 0 0 1rem 0;">Testing</h3>
|
|
1136
|
+
<div style="background: rgba(255, 255, 255, 0.03); padding: 1.5rem; border-radius: 12px; border: 1px solid rgba(255, 255, 255, 0.05);">
|
|
1137
|
+
<p x-text="selectedSpec?.sections?.testing?.content || 'No testing notes yet'"></p>
|
|
1138
|
+
</div>
|
|
1139
|
+
</div>
|
|
1140
|
+
</template>
|
|
1141
|
+
|
|
1142
|
+
<template x-if="section==='review'">
|
|
1143
|
+
<div>
|
|
1144
|
+
<h3 class="heading" style="margin: 0 0 1rem 0;">Review Notes</h3>
|
|
1145
|
+
<div style="background: rgba(255, 255, 255, 0.03); padding: 1.5rem; border-radius: 12px; border: 1px solid rgba(255, 255, 255, 0.05);">
|
|
1146
|
+
<p x-text="selectedSpec?.sections?.review?.content || 'No review notes yet'"></p>
|
|
1147
|
+
</div>
|
|
1148
|
+
</div>
|
|
1149
|
+
</template>
|
|
1150
|
+
</div>
|
|
1151
|
+
|
|
1152
|
+
<!-- Right Sidebar: Activity & Files -->
|
|
1153
|
+
<div class="modal-activity">
|
|
1154
|
+
<!-- Activity Log Section -->
|
|
1155
|
+
<div style="margin-bottom: 2rem;">
|
|
1156
|
+
<h3 class="heading" style="margin: 0 0 1rem 0; font-size: 1rem;">📊 Activity Log</h3>
|
|
1157
|
+
<div style="max-height: 300px; overflow-y: auto;">
|
|
1158
|
+
<template x-for="event in (selectedSpec?.timeline || []).slice(-5)" :key="event.timestamp">
|
|
1159
|
+
<div class="activity-item">
|
|
1160
|
+
<div class="activity-time" x-text="new Date(event.timestamp).toLocaleString()"></div>
|
|
1161
|
+
<div class="activity-role">
|
|
1162
|
+
<span :class="{
|
|
1163
|
+
'status-completed': event.type === 'execution',
|
|
1164
|
+
'status-draft': event.type === 'debug'
|
|
1165
|
+
}"
|
|
1166
|
+
class="status-badge"
|
|
1167
|
+
x-text="event.type === 'execution' ? 'Engineer' : 'Debugger'"></span>
|
|
1168
|
+
</div>
|
|
1169
|
+
<div class="activity-note" x-text="event.action || event.note || event.fix || 'No details'"></div>
|
|
1170
|
+
<div x-show="event.files_changed && event.files_changed.length > 0"
|
|
1171
|
+
style="margin-top: 0.5rem; font-size: 0.75rem; color: var(--text-muted);">
|
|
1172
|
+
<span x-text="event.files_changed.length + ' file(s) changed'"></span>
|
|
1173
|
+
</div>
|
|
1174
|
+
</div>
|
|
1175
|
+
</template>
|
|
1176
|
+
<div x-show="!selectedSpec?.timeline || selectedSpec.timeline.length === 0"
|
|
1177
|
+
style="text-align: center; padding: 1rem; color: var(--text-muted); font-size: 0.875rem;">
|
|
1178
|
+
No activity yet
|
|
1179
|
+
</div>
|
|
1180
|
+
</div>
|
|
1181
|
+
</div>
|
|
1182
|
+
|
|
1183
|
+
<!-- Files Modified Section -->
|
|
1184
|
+
<div>
|
|
1185
|
+
<h3 class="heading" style="margin: 0 0 1rem 0; font-size: 1rem;">📁 Files Modified</h3>
|
|
1186
|
+
<div style="max-height: 300px; overflow-y: auto;">
|
|
1187
|
+
<template x-for="fileChange in (selectedSpec?.file_changes || []).slice(-10)" :key="fileChange.timestamp + fileChange.file">
|
|
1188
|
+
<div style="background: rgba(255, 255, 255, 0.02); border-radius: 6px; padding: 0.75rem; margin-bottom: 0.5rem; border: 1px solid rgba(255, 255, 255, 0.03);">
|
|
1189
|
+
<div style="font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace; font-size: 0.8rem; color: var(--text-primary); margin-bottom: 0.25rem;"
|
|
1190
|
+
x-text="fileChange.file"></div>
|
|
1191
|
+
<div style="display: flex; justify-content: space-between; align-items: center;">
|
|
1192
|
+
<span :class="{
|
|
1193
|
+
'status-completed': fileChange.type === 'modified',
|
|
1194
|
+
'status-in-progress': fileChange.type === 'created',
|
|
1195
|
+
'status-draft': fileChange.type === 'deleted'
|
|
1196
|
+
}"
|
|
1197
|
+
class="status-badge"
|
|
1198
|
+
style="font-size: 0.65rem; padding: 0.15rem 0.5rem;"
|
|
1199
|
+
x-text="fileChange.type || 'modified'"></span>
|
|
1200
|
+
<span style="font-size: 0.7rem; color: var(--text-muted);"
|
|
1201
|
+
x-text="new Date(fileChange.timestamp).toLocaleTimeString()"></span>
|
|
1202
|
+
</div>
|
|
1203
|
+
</div>
|
|
1204
|
+
</template>
|
|
1205
|
+
<div x-show="!selectedSpec?.file_changes || selectedSpec.file_changes.length === 0"
|
|
1206
|
+
style="text-align: center; padding: 1rem; color: var(--text-muted); font-size: 0.875rem;">
|
|
1207
|
+
No files modified yet
|
|
1208
|
+
</div>
|
|
1209
|
+
</div>
|
|
1210
|
+
</div>
|
|
1211
|
+
</div>
|
|
1212
|
+
</div>
|
|
1213
|
+
</div>
|
|
1214
|
+
|
|
1215
|
+
<!-- Activity Log Modal -->
|
|
1216
|
+
<div class="modal-overlay"
|
|
1217
|
+
x-show="showActivityLog"
|
|
1218
|
+
x-transition:enter="transition ease-out duration-300"
|
|
1219
|
+
x-transition:enter-start="opacity-0"
|
|
1220
|
+
x-transition:enter-end="opacity-100"
|
|
1221
|
+
x-transition:leave="transition ease-in duration-200"
|
|
1222
|
+
x-transition:leave-start="opacity-100"
|
|
1223
|
+
x-transition:leave-end="opacity-0"
|
|
1224
|
+
@click.self="showActivityLog=false">
|
|
1225
|
+
|
|
1226
|
+
<div class="modal-content"
|
|
1227
|
+
x-show="showActivityLog"
|
|
1228
|
+
x-transition:enter="transition ease-out duration-300"
|
|
1229
|
+
x-transition:enter-start="opacity-0 scale-95"
|
|
1230
|
+
x-transition:enter-end="opacity-100 scale-100"
|
|
1231
|
+
x-transition:leave="transition ease-in duration-200"
|
|
1232
|
+
x-transition:leave-start="opacity-100 scale-100"
|
|
1233
|
+
x-transition:leave-end="opacity-0 scale-95"
|
|
1234
|
+
style="grid-template-columns: 1fr; grid-template-rows: auto 1fr;">
|
|
1235
|
+
|
|
1236
|
+
<!-- Activity Log Header -->
|
|
1237
|
+
<div class="modal-header">
|
|
1238
|
+
<h2 class="heading" style="margin: 0; font-size: 1.5rem;">📊 Activity Log</h2>
|
|
1239
|
+
<div style="display: flex; gap: 1rem;">
|
|
1240
|
+
<button class="btn btn-secondary" @click="showActivityLog=false">
|
|
1241
|
+
✕ Close
|
|
1242
|
+
</button>
|
|
1243
|
+
</div>
|
|
1244
|
+
</div>
|
|
1245
|
+
|
|
1246
|
+
<!-- Activity Log Content -->
|
|
1247
|
+
<div class="modal-main">
|
|
1248
|
+
<div style="margin-bottom: 1.5rem;">
|
|
1249
|
+
<h3 class="heading" style="margin: 0 0 1rem 0;">Recent Activity</h3>
|
|
1250
|
+
<div style="background: rgba(255, 255, 255, 0.03); padding: 1.5rem; border-radius: 12px; border: 1px solid rgba(255, 255, 255, 0.05);">
|
|
1251
|
+
|
|
1252
|
+
<template x-for="activity in activities" :key="activity.timestamp">
|
|
1253
|
+
<div style="border-bottom: 1px solid rgba(255, 255, 255, 0.05); padding: 1rem 0;">
|
|
1254
|
+
<div style="display: flex; justify-content: between; align-items: start; margin-bottom: 0.5rem;">
|
|
1255
|
+
<div>
|
|
1256
|
+
<span class="status-badge"
|
|
1257
|
+
:class="{
|
|
1258
|
+
'status-completed': activity.type === 'spec_created',
|
|
1259
|
+
'status-in-progress': activity.type === 'worktree_created',
|
|
1260
|
+
'status-planning': activity.type === 'commit',
|
|
1261
|
+
'status-draft': activity.type === 'spec_updated'
|
|
1262
|
+
}"
|
|
1263
|
+
x-text="activity.type.replace('_', ' ')"></span>
|
|
1264
|
+
<span style="margin-left: 1rem; color: var(--text-secondary); font-size: 0.875rem;"
|
|
1265
|
+
x-text="new Date(activity.timestamp).toLocaleString()"></span>
|
|
1266
|
+
</div>
|
|
1267
|
+
</div>
|
|
1268
|
+
<div style="color: var(--text-primary); margin-bottom: 0.5rem;"
|
|
1269
|
+
x-text="activity.message"></div>
|
|
1270
|
+
<div style="color: var(--text-secondary); font-size: 0.875rem;">
|
|
1271
|
+
<span>by </span>
|
|
1272
|
+
<span style="color: var(--accent-blue);" x-text="activity.author"></span>
|
|
1273
|
+
<template x-if="activity.files">
|
|
1274
|
+
<span>
|
|
1275
|
+
• Modified: <span x-text="activity.files.join(', ')"></span>
|
|
1276
|
+
</span>
|
|
1277
|
+
</template>
|
|
1278
|
+
</div>
|
|
1279
|
+
</div>
|
|
1280
|
+
</template>
|
|
1281
|
+
|
|
1282
|
+
<div x-show="activities.length === 0" style="text-align: center; padding: 2rem; color: var(--text-muted);">
|
|
1283
|
+
No recent activity
|
|
1284
|
+
</div>
|
|
1285
|
+
</div>
|
|
1286
|
+
</div>
|
|
1287
|
+
</div>
|
|
1288
|
+
</div>
|
|
1289
|
+
</div>
|
|
1290
|
+
|
|
1291
|
+
</body>
|
|
1292
|
+
</html>
|