bobs-workshop 0.3.3 → 3.1.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.
Files changed (200) hide show
  1. package/LICENSE +2 -2
  2. package/README.md +199 -210
  3. package/bin/bobs-workshop.js +109 -0
  4. package/config/agents.json +27 -0
  5. package/dist/plugins/bobs-workshop.js +34 -0
  6. package/dist/tools/background-agent/cancel.d.ts +3 -0
  7. package/dist/tools/background-agent/cancel.d.ts.map +1 -0
  8. package/dist/tools/background-agent/cancel.js +52 -0
  9. package/dist/tools/background-agent/concurrency.d.ts +15 -0
  10. package/dist/tools/background-agent/concurrency.d.ts.map +1 -0
  11. package/dist/tools/background-agent/concurrency.js +61 -0
  12. package/dist/tools/background-agent/index.d.ts +8 -0
  13. package/dist/tools/background-agent/index.d.ts.map +1 -0
  14. package/dist/tools/background-agent/index.js +7 -0
  15. package/dist/tools/background-agent/launch.d.ts +6 -0
  16. package/dist/tools/background-agent/launch.d.ts.map +1 -0
  17. package/dist/tools/background-agent/launch.js +33 -0
  18. package/dist/tools/background-agent/list.d.ts +7 -0
  19. package/dist/tools/background-agent/list.d.ts.map +1 -0
  20. package/dist/tools/background-agent/list.js +40 -0
  21. package/dist/tools/background-agent/manager.d.ts +29 -0
  22. package/dist/tools/background-agent/manager.d.ts.map +1 -0
  23. package/dist/tools/background-agent/manager.js +388 -0
  24. package/dist/tools/background-agent/output.d.ts +3 -0
  25. package/dist/tools/background-agent/output.d.ts.map +1 -0
  26. package/dist/tools/background-agent/output.js +41 -0
  27. package/dist/tools/background-agent/types.d.ts +46 -0
  28. package/dist/tools/background-agent/types.d.ts.map +1 -0
  29. package/dist/tools/background-agent/types.js +1 -0
  30. package/dist/tools/index.d.ts +9 -0
  31. package/dist/tools/index.d.ts.map +1 -0
  32. package/dist/tools/index.js +8 -0
  33. package/dist/tools/manual/index.d.ts +3 -0
  34. package/dist/tools/manual/index.d.ts.map +1 -0
  35. package/dist/tools/manual/index.js +2 -0
  36. package/dist/tools/manual/manual-update.d.ts +4 -0
  37. package/dist/tools/manual/manual-update.d.ts.map +1 -0
  38. package/dist/tools/manual/manual-update.js +190 -0
  39. package/dist/tools/manual/verify-manual.d.ts +4 -0
  40. package/dist/tools/manual/verify-manual.d.ts.map +1 -0
  41. package/dist/tools/manual/verify-manual.js +51 -0
  42. package/package.json +34 -66
  43. package/postinstall.js +193 -0
  44. package/src/agents/alice.md +466 -0
  45. package/src/agents/bob-rev.md +493 -0
  46. package/src/agents/bob-send.md +277 -0
  47. package/src/agents/bob.md +442 -0
  48. package/src/agents/trace.md +451 -0
  49. package/src/plugins/bobs-workshop.ts +45 -0
  50. package/src/skills/api-patterns/SKILL.md +376 -0
  51. package/src/skills/architecture/SKILL.md +271 -0
  52. package/src/skills/bobs-workshop/performance/icon.svg +3 -0
  53. package/src/skills/brainstorming/SKILL.md +210 -0
  54. package/src/skills/clean-code/SKILL.md +151 -0
  55. package/src/skills/code-review-checklist/SKILL.md +220 -0
  56. package/src/skills/database-design/SKILL.md +271 -0
  57. package/src/skills/exploration/SKILL.md +257 -0
  58. package/src/skills/frontend-ui-ux/SKILL.md +78 -0
  59. package/src/skills/git-master/SKILL.md +1105 -0
  60. package/src/skills/performance/SKILL.md +144 -0
  61. package/src/skills/performance/icon.svg +3 -0
  62. package/src/skills/plan-writing/SKILL.md +225 -0
  63. package/src/skills/security/SKILL.md +410 -0
  64. package/src/skills/simplification/SKILL.md +238 -0
  65. package/src/skills/systematic-debugging/SKILL.md +175 -0
  66. package/src/skills/testing-patterns/SKILL.md +305 -0
  67. package/src/skills/verification/SKILL.md +286 -0
  68. package/src/tools/background-agent/cancel.ts +67 -0
  69. package/src/tools/background-agent/concurrency.ts +71 -0
  70. package/src/tools/background-agent/index.ts +7 -0
  71. package/src/tools/background-agent/launch.ts +39 -0
  72. package/src/tools/background-agent/list.ts +50 -0
  73. package/src/tools/background-agent/manager.ts +466 -0
  74. package/src/tools/background-agent/output.ts +57 -0
  75. package/src/tools/background-agent/types.ts +55 -0
  76. package/src/tools/index.ts +8 -0
  77. package/src/tools/manual/index.ts +2 -0
  78. package/src/tools/manual/manual-update.ts +197 -0
  79. package/src/tools/manual/verify-manual.ts +60 -0
  80. package/uninstall.js +64 -0
  81. package/Claude.md +0 -162
  82. package/bin/bobs-mcp-server.js +0 -11
  83. package/bin/bobs-mcp.js +0 -130
  84. package/dist/api/taskLogger.js +0 -106
  85. package/dist/api/taskLogger.js.map +0 -1
  86. package/dist/cli/checker.js +0 -401
  87. package/dist/cli/checker.js.map +0 -1
  88. package/dist/cli/cleanup.js +0 -131
  89. package/dist/cli/cleanup.js.map +0 -1
  90. package/dist/cli/debug.js +0 -157
  91. package/dist/cli/debug.js.map +0 -1
  92. package/dist/cli/health.js +0 -97
  93. package/dist/cli/health.js.map +0 -1
  94. package/dist/cli/setup.js +0 -81
  95. package/dist/cli/setup.js.map +0 -1
  96. package/dist/cli/workshop.js +0 -42
  97. package/dist/cli/workshop.js.map +0 -1
  98. package/dist/dashboard/server.js +0 -1203
  99. package/dist/dashboard/server.js.map +0 -1
  100. package/dist/index.js +0 -960
  101. package/dist/index.js.map +0 -1
  102. package/dist/prompts/architect.js +0 -221
  103. package/dist/prompts/architect.js.map +0 -1
  104. package/dist/prompts/debugger.js +0 -257
  105. package/dist/prompts/debugger.js.map +0 -1
  106. package/dist/prompts/engineer.js +0 -249
  107. package/dist/prompts/engineer.js.map +0 -1
  108. package/dist/prompts/orchestrator.js +0 -304
  109. package/dist/prompts/orchestrator.js.map +0 -1
  110. package/dist/prompts/reviewer.js +0 -289
  111. package/dist/prompts/reviewer.js.map +0 -1
  112. package/dist/services/activitySummarizer.js +0 -388
  113. package/dist/services/activitySummarizer.js.map +0 -1
  114. package/dist/services/changeValidator.js +0 -396
  115. package/dist/services/changeValidator.js.map +0 -1
  116. package/dist/services/claudeOrchestrator.js +0 -343
  117. package/dist/services/claudeOrchestrator.js.map +0 -1
  118. package/dist/services/fileMonitor.js +0 -250
  119. package/dist/services/fileMonitor.js.map +0 -1
  120. package/dist/services/implementationSummarizer.js +0 -306
  121. package/dist/services/implementationSummarizer.js.map +0 -1
  122. package/dist/services/liveMonitor.js +0 -315
  123. package/dist/services/liveMonitor.js.map +0 -1
  124. package/dist/services/mcpAuditLogger.js +0 -104
  125. package/dist/services/mcpAuditLogger.js.map +0 -1
  126. package/dist/services/mcpLogger.js +0 -223
  127. package/dist/services/mcpLogger.js.map +0 -1
  128. package/dist/services/tmuxManager.js +0 -541
  129. package/dist/services/tmuxManager.js.map +0 -1
  130. package/dist/tools/approvalTools.js +0 -244
  131. package/dist/tools/approvalTools.js.map +0 -1
  132. package/dist/tools/autoDebugger.js +0 -147
  133. package/dist/tools/autoDebugger.js.map +0 -1
  134. package/dist/tools/cleanupService.js +0 -221
  135. package/dist/tools/cleanupService.js.map +0 -1
  136. package/dist/tools/dashboardTools.js +0 -342
  137. package/dist/tools/dashboardTools.js.map +0 -1
  138. package/dist/tools/developmentNudges.js +0 -336
  139. package/dist/tools/developmentNudges.js.map +0 -1
  140. package/dist/tools/gitTools.js +0 -741
  141. package/dist/tools/gitTools.js.map +0 -1
  142. package/dist/tools/orchestratorTools.js +0 -832
  143. package/dist/tools/orchestratorTools.js.map +0 -1
  144. package/dist/tools/searchCache.js +0 -64
  145. package/dist/tools/searchCache.js.map +0 -1
  146. package/dist/tools/searchTools.js +0 -1107
  147. package/dist/tools/searchTools.js.map +0 -1
  148. package/dist/tools/semgrep-patterns.js +0 -296
  149. package/dist/tools/semgrep-patterns.js.map +0 -1
  150. package/dist/tools/specTools.js +0 -332
  151. package/dist/tools/specTools.js.map +0 -1
  152. package/dist/tools/structural/__tests__/orchestrator.test.js +0 -61
  153. package/dist/tools/structural/__tests__/orchestrator.test.js.map +0 -1
  154. package/dist/tools/structural/cache.js +0 -226
  155. package/dist/tools/structural/cache.js.map +0 -1
  156. package/dist/tools/structural/engines/python/index.js +0 -118
  157. package/dist/tools/structural/engines/python/index.js.map +0 -1
  158. package/dist/tools/structural/engines/typescript/__tests__/typescript-engine.test.js +0 -97
  159. package/dist/tools/structural/engines/typescript/__tests__/typescript-engine.test.js.map +0 -1
  160. package/dist/tools/structural/engines/typescript/analyzer.js +0 -433
  161. package/dist/tools/structural/engines/typescript/analyzer.js.map +0 -1
  162. package/dist/tools/structural/engines/typescript/index.js +0 -381
  163. package/dist/tools/structural/engines/typescript/index.js.map +0 -1
  164. package/dist/tools/structural/engines/typescript/utils.js +0 -279
  165. package/dist/tools/structural/engines/typescript/utils.js.map +0 -1
  166. package/dist/tools/structural/index.js +0 -248
  167. package/dist/tools/structural/index.js.map +0 -1
  168. package/dist/tools/structural/types.js +0 -18
  169. package/dist/tools/structural/types.js.map +0 -1
  170. package/dist/tools/tmuxTools.js +0 -100
  171. package/dist/tools/tmuxTools.js.map +0 -1
  172. package/dist/tools/workRecorder.js +0 -215
  173. package/dist/tools/workRecorder.js.map +0 -1
  174. package/dist/tools/worktreeTools.js +0 -705
  175. package/dist/tools/worktreeTools.js.map +0 -1
  176. package/dist/utils/__tests__/integration.test.js +0 -57
  177. package/dist/utils/__tests__/integration.test.js.map +0 -1
  178. package/dist/utils/__tests__/serverDetection.test.js +0 -151
  179. package/dist/utils/__tests__/serverDetection.test.js.map +0 -1
  180. package/dist/utils/errorHandling.js +0 -336
  181. package/dist/utils/errorHandling.js.map +0 -1
  182. package/dist/utils/processManager.js +0 -172
  183. package/dist/utils/processManager.js.map +0 -1
  184. package/dist/utils/reliability.js +0 -263
  185. package/dist/utils/reliability.js.map +0 -1
  186. package/dist/utils/responseFormatter.js +0 -250
  187. package/dist/utils/responseFormatter.js.map +0 -1
  188. package/dist/utils/serverDetection.js +0 -133
  189. package/dist/utils/serverDetection.js.map +0 -1
  190. package/dist/utils/specMigration.js +0 -105
  191. package/dist/utils/specMigration.js.map +0 -1
  192. package/dist/validation/schemas.js +0 -299
  193. package/dist/validation/schemas.js.map +0 -1
  194. package/public/.well-known/mcp/manifest.json +0 -473
  195. package/public/index.html +0 -3157
  196. package/public/index.html.backup +0 -2805
  197. package/public/index.html.backup2 +0 -1292
  198. package/scripts/cleanup-system-logs.ts +0 -121
  199. package/scripts/init-workspace.js +0 -63
  200. package/scripts/install-search-tools.js +0 -116
@@ -1,1292 +0,0 @@
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>