@sugar-crash-studios/vibe-forge 0.4.0

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 (201) hide show
  1. package/.claude/commands/clear-attention.md +63 -0
  2. package/.claude/commands/compact-context.md +52 -0
  3. package/.claude/commands/configure-vcs.md +102 -0
  4. package/.claude/commands/forge.md +171 -0
  5. package/.claude/commands/need-help.md +77 -0
  6. package/.claude/commands/update-status.md +64 -0
  7. package/.claude/commands/worker-loop.md +106 -0
  8. package/.claude/hooks/worker-loop.js +198 -0
  9. package/.claude/scripts/setup-worker-loop.sh +45 -0
  10. package/.claude/settings.local.json +46 -0
  11. package/LICENSE +21 -0
  12. package/README.md +238 -0
  13. package/agents/aegis/personality.md +294 -0
  14. package/agents/anvil/personality.md +276 -0
  15. package/agents/architect/personality.md +258 -0
  16. package/agents/crucible/personality.md +360 -0
  17. package/agents/ember/personality.md +291 -0
  18. package/agents/forge-master/capabilities.md +144 -0
  19. package/agents/forge-master/context-template.md +128 -0
  20. package/agents/forge-master/personality.md +138 -0
  21. package/agents/furnace/personality.md +340 -0
  22. package/agents/herald/personality.md +247 -0
  23. package/agents/loki/personality.md +108 -0
  24. package/agents/oracle/personality.md +283 -0
  25. package/agents/pixel/personality.md +113 -0
  26. package/agents/planning-hub/personality.md +320 -0
  27. package/agents/scribe/personality.md +251 -0
  28. package/agents/temper/personality.md +218 -0
  29. package/bin/cli.js +375 -0
  30. package/bin/dashboard/api/agents.js +333 -0
  31. package/bin/dashboard/api/dispatch.js +483 -0
  32. package/bin/dashboard/api/tasks.js +416 -0
  33. package/bin/dashboard/frontend/index.html +13 -0
  34. package/bin/dashboard/frontend/package.json +16 -0
  35. package/bin/dashboard/frontend/src/App.svelte +222 -0
  36. package/bin/dashboard/frontend/src/app.css +1777 -0
  37. package/bin/dashboard/frontend/src/lib/components/AgentCard.svelte +60 -0
  38. package/bin/dashboard/frontend/src/lib/components/AgentsPanel.svelte +57 -0
  39. package/bin/dashboard/frontend/src/lib/components/DispatchModal.svelte +180 -0
  40. package/bin/dashboard/frontend/src/lib/components/Footer.svelte +33 -0
  41. package/bin/dashboard/frontend/src/lib/components/Header.svelte +84 -0
  42. package/bin/dashboard/frontend/src/lib/components/IssueCard.svelte +33 -0
  43. package/bin/dashboard/frontend/src/lib/components/IssuesPanel.svelte +73 -0
  44. package/bin/dashboard/frontend/src/lib/components/KeyboardShortcutsModal.svelte +108 -0
  45. package/bin/dashboard/frontend/src/lib/components/MobileTabs.svelte +52 -0
  46. package/bin/dashboard/frontend/src/lib/components/NotificationCard.svelte +60 -0
  47. package/bin/dashboard/frontend/src/lib/components/NotificationsPanel.svelte +44 -0
  48. package/bin/dashboard/frontend/src/lib/components/TaskCard.svelte +63 -0
  49. package/bin/dashboard/frontend/src/lib/components/TasksPanel.svelte +82 -0
  50. package/bin/dashboard/frontend/src/lib/components/Toast.svelte +45 -0
  51. package/bin/dashboard/frontend/src/lib/stores/agents.js +34 -0
  52. package/bin/dashboard/frontend/src/lib/stores/issues.js +54 -0
  53. package/bin/dashboard/frontend/src/lib/stores/notifications.js +48 -0
  54. package/bin/dashboard/frontend/src/lib/stores/tasks.js +63 -0
  55. package/bin/dashboard/frontend/src/lib/stores/theme.js +33 -0
  56. package/bin/dashboard/frontend/src/lib/stores/toast.js +35 -0
  57. package/bin/dashboard/frontend/src/lib/stores/ui.js +25 -0
  58. package/bin/dashboard/frontend/src/lib/stores/voice.js +275 -0
  59. package/bin/dashboard/frontend/src/lib/stores/websocket.js +295 -0
  60. package/bin/dashboard/frontend/src/lib/utils/api.js +101 -0
  61. package/bin/dashboard/frontend/src/lib/utils/formatters.js +54 -0
  62. package/bin/dashboard/frontend/src/main.js +9 -0
  63. package/bin/dashboard/frontend/svelte.config.js +5 -0
  64. package/bin/dashboard/frontend/vite.config.js +20 -0
  65. package/bin/dashboard/public/assets/index-DnfVj9Ce.css +1 -0
  66. package/bin/dashboard/public/assets/index-Ze5h0kXQ.js +2 -0
  67. package/bin/dashboard/public/index.html +14 -0
  68. package/bin/dashboard/server.js +566 -0
  69. package/bin/forge-daemon.sh +463 -0
  70. package/bin/forge-setup.sh +645 -0
  71. package/bin/forge-spawn.sh +164 -0
  72. package/bin/forge.cmd +83 -0
  73. package/bin/forge.sh +533 -0
  74. package/bin/lib/agents.sh +177 -0
  75. package/bin/lib/colors.sh +44 -0
  76. package/bin/lib/config.sh +347 -0
  77. package/bin/lib/constants.sh +241 -0
  78. package/bin/lib/daemon/display.sh +128 -0
  79. package/bin/lib/daemon/notifications.sh +263 -0
  80. package/bin/lib/daemon/routing.sh +77 -0
  81. package/bin/lib/daemon/state.sh +115 -0
  82. package/bin/lib/daemon/sync.sh +95 -0
  83. package/bin/lib/database.sh +310 -0
  84. package/bin/lib/heimdall-setup.js +113 -0
  85. package/bin/lib/heimdall.js +265 -0
  86. package/bin/lib/json.sh +264 -0
  87. package/bin/lib/terminal.js +451 -0
  88. package/bin/lib/util.sh +126 -0
  89. package/bin/lib/vcs.js +349 -0
  90. package/config/agent-manifest.yaml +203 -0
  91. package/config/agents.json +168 -0
  92. package/config/task-template.md +159 -0
  93. package/config/task-types.yaml +106 -0
  94. package/context/agent-status/aegis.json +7 -0
  95. package/context/agent-status/anvil.json +7 -0
  96. package/context/agent-status/architect.json +7 -0
  97. package/context/agent-status/crucible.json +7 -0
  98. package/context/agent-status/ember.json +7 -0
  99. package/context/agent-status/furnace.json +7 -0
  100. package/context/agent-status/loki.json +7 -0
  101. package/context/agent-status/oracle.json +7 -0
  102. package/context/agent-status/pixel.json +7 -0
  103. package/context/agent-status/planning-hub.json +7 -0
  104. package/context/agent-status/scribe.json +7 -0
  105. package/context/agent-status/temper.json +7 -0
  106. package/context/feature-brainstorm.md +426 -0
  107. package/context/forge-state.yaml +19 -0
  108. package/context/modern-conventions.md +129 -0
  109. package/context/project-context-template.md +122 -0
  110. package/context/project-context.md +122 -0
  111. package/docs/TODO.md +150 -0
  112. package/docs/agents.md +409 -0
  113. package/docs/architecture/decisions/ADR-001-daemon-modularization.md +122 -0
  114. package/docs/architecture/vibe-lab-integration.md +684 -0
  115. package/docs/architecture.md +194 -0
  116. package/docs/bmad-gap-analysis-2026-03-31.md +444 -0
  117. package/docs/cleanup-workflow.md +329 -0
  118. package/docs/commands.md +451 -0
  119. package/docs/dashboard-mockup.html +989 -0
  120. package/docs/getting-started.md +261 -0
  121. package/docs/integration/forge-ownership-policy.md +112 -0
  122. package/docs/npm-publishing.md +132 -0
  123. package/docs/roadmap-2026.md +519 -0
  124. package/docs/security.md +144 -0
  125. package/docs/wireframes/dashboard-mvp.md +1164 -0
  126. package/docs/workflows/README.md +32 -0
  127. package/docs/workflows/azure-devops.md +108 -0
  128. package/docs/workflows/bitbucket.md +104 -0
  129. package/docs/workflows/git-only.md +130 -0
  130. package/docs/workflows/gitea.md +168 -0
  131. package/docs/workflows/github.md +103 -0
  132. package/docs/workflows/gitlab.md +105 -0
  133. package/docs/workflows.md +454 -0
  134. package/package.json +73 -0
  135. package/tasks/completed/ARCH-001-duplicate-agent-config.md +121 -0
  136. package/tasks/completed/ARCH-002-mixed-bash-node-implementation.md +88 -0
  137. package/tasks/completed/ARCH-003-worker-loop-hook-duplication.md +77 -0
  138. package/tasks/completed/ARCH-009-test-organization.md +78 -0
  139. package/tasks/completed/ARCH-011-jq-vs-nodejs-json.md +94 -0
  140. package/tasks/completed/ARCH-012-tmp-files-in-root.md +71 -0
  141. package/tasks/completed/ARCH-013-exit-code-constants.md +65 -0
  142. package/tasks/completed/ARCH-014-sed-incompatibility.md +96 -0
  143. package/tasks/completed/ARCH-015-docs-todo-tracking.md +83 -0
  144. package/tasks/completed/BUG-dash-001-tasks-filter-error.md +31 -0
  145. package/tasks/completed/BUG-dash-002-agents-unknown.md +41 -0
  146. package/tasks/completed/CLEAN-001.md +38 -0
  147. package/tasks/completed/CLEAN-002.md +43 -0
  148. package/tasks/completed/CLEAN-003.md +47 -0
  149. package/tasks/completed/CLEAN-004.md +56 -0
  150. package/tasks/completed/CLEAN-005.md +75 -0
  151. package/tasks/completed/CLEAN-006.md +47 -0
  152. package/tasks/completed/CLEAN-007.md +34 -0
  153. package/tasks/completed/CLEAN-008.md +49 -0
  154. package/tasks/completed/CLEAN-012.md +58 -0
  155. package/tasks/completed/CLEAN-013.md +45 -0
  156. package/tasks/completed/FEATURE-001a-dashboard-wireframes.md +162 -0
  157. package/tasks/completed/IMPL-007a-daemon-notifications-module.md +82 -0
  158. package/tasks/completed/IMPL-007b-daemon-sync-module.md +71 -0
  159. package/tasks/completed/IMPL-007c-daemon-state-module.md +80 -0
  160. package/tasks/completed/IMPL-007d-daemon-routing-module.md +77 -0
  161. package/tasks/completed/IMPL-007e-daemon-display-module.md +77 -0
  162. package/tasks/completed/IMPL-007f-daemon-integration.md +124 -0
  163. package/tasks/completed/PLAT-1-heimdall.md +420 -0
  164. package/tasks/completed/SEC-001-sql-injection-fix.md +58 -0
  165. package/tasks/completed/SEC-002-notification-injection-fix.md +45 -0
  166. package/tasks/completed/SEC-003-eval-injection-fix.md +54 -0
  167. package/tasks/completed/SEC-004-pid-race-condition-fix.md +49 -0
  168. package/tasks/completed/SEC-005-worker-loop-path-fix.md +51 -0
  169. package/tasks/completed/SEC-006-eval-agent-names.md +55 -0
  170. package/tasks/completed/SEC-007-spawn-escaping.md +67 -0
  171. package/tasks/completed/TASK-DASH-001-server-infrastructure.md +185 -0
  172. package/tasks/completed/TASK-anvil-001-dashboard-frontend.md +133 -0
  173. package/tasks/completed/review-bmad-aegis.md +89 -0
  174. package/tasks/completed/review-bmad-anvil.md +80 -0
  175. package/tasks/completed/review-bmad-crucible.md +81 -0
  176. package/tasks/completed/review-bmad-ember.md +90 -0
  177. package/tasks/completed/review-bmad-furnace.md +79 -0
  178. package/tasks/completed/review-bmad-pixel.md +82 -0
  179. package/tasks/completed/review-bmad-scribe.md +92 -0
  180. package/tasks/completed/review-bmad-sentinel.md +83 -0
  181. package/tasks/pending/ARCH-004-git-bash-detection-duplication.md +72 -0
  182. package/tasks/pending/ARCH-005-missing-src-directory.md +95 -0
  183. package/tasks/pending/ARCH-006-task-template-location.md +64 -0
  184. package/tasks/pending/ARCH-008-forge-master-vs-hub.md +81 -0
  185. package/tasks/pending/ARCH-010-missing-index-files.md +84 -0
  186. package/tasks/pending/CLEAN-009.md +31 -0
  187. package/tasks/pending/CLEAN-010.md +30 -0
  188. package/tasks/pending/CLEAN-011.md +30 -0
  189. package/tasks/pending/CLEAN-014.md +32 -0
  190. package/tasks/pending/DESIGN-dash-001-layout-review.md +45 -0
  191. package/tasks/pending/FEATURE-001-dashboard-mvp.md +268 -0
  192. package/tasks/review/ARCH-007-daemon-monolith.md +162 -0
  193. package/tasks/review/bmad-review-aegis.md +349 -0
  194. package/tasks/review/bmad-review-anvil.md +259 -0
  195. package/tasks/review/bmad-review-crucible.md +277 -0
  196. package/tasks/review/bmad-review-ember.md +307 -0
  197. package/tasks/review/bmad-review-furnace.md +285 -0
  198. package/tasks/review/bmad-review-pixel.md +329 -0
  199. package/tasks/review/bmad-review-scribe.md +361 -0
  200. package/tasks/review/bmad-review-sentinel.md +242 -0
  201. package/tasks/review/task-001.md +78 -0
@@ -0,0 +1,989 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en" data-theme="dark">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Vibe Forge Dashboard — Mockup</title>
7
+ <style>
8
+ /* ── CSS Custom Properties ─────────────────────────────────────────── */
9
+ :root {
10
+ --bg-primary: #0D1117;
11
+ --bg-secondary: #161B22;
12
+ --bg-tertiary: #21262D;
13
+ --bg-hover: #30363D;
14
+ --bg-active: #484F58;
15
+ --border: #30363D;
16
+ --border-muted: #21262D;
17
+ --text-primary: #F0F6FC;
18
+ --text-secondary:#8B949E;
19
+ --text-muted: #6E7681;
20
+ --text-link: #58A6FF;
21
+ --accent: #238636;
22
+ --accent2: #1F6FEB;
23
+ --danger: #F85149;
24
+ --warning: #D29922;
25
+ --success: #3FB950;
26
+ --info: #58A6FF;
27
+ --header-h: 60px;
28
+ --footer-h: 32px;
29
+ --radius-sm: 4px;
30
+ --radius-md: 8px;
31
+ --radius-lg: 12px;
32
+ --font-sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif;
33
+ --font-mono: 'SF Mono', 'Consolas', 'Liberation Mono', Menlo, monospace;
34
+ --shadow-md: 0 4px 12px rgba(0,0,0,.4);
35
+ --shadow-lg: 0 8px 24px rgba(0,0,0,.6);
36
+ /* agent accent colors */
37
+ --c-hub: #A371F7;
38
+ --c-anvil: #58A6FF;
39
+ --c-furnace: #F0883E;
40
+ --c-crucible: #3FB950;
41
+ --c-scribe: #56D4DD;
42
+ --c-sentinel: #D29922;
43
+ --c-ember: #F85149;
44
+ --c-architect: #DB61A2;
45
+ --c-aegis: #E3B341;
46
+ --c-pixel: #F778BA;
47
+ }
48
+ [data-theme="light"] {
49
+ --bg-primary: #FFFFFF;
50
+ --bg-secondary: #F6F8FA;
51
+ --bg-tertiary: #EAEEF2;
52
+ --bg-hover: #F3F4F6;
53
+ --bg-active: #E2E5EA;
54
+ --border: #D0D7DE;
55
+ --border-muted: #EAEEF2;
56
+ --text-primary: #1F2328;
57
+ --text-secondary:#656D76;
58
+ --text-muted: #9198A1;
59
+ --text-link: #0969DA;
60
+ --accent: #1A7F37;
61
+ --accent2: #0550AE;
62
+ --danger: #CF222E;
63
+ --warning: #9A6700;
64
+ --success: #1A7F37;
65
+ --info: #0550AE;
66
+ }
67
+
68
+ /* ── Reset & Base ──────────────────────────────────────────────────── */
69
+ *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
70
+ body {
71
+ font-family: var(--font-sans);
72
+ font-size: 14px;
73
+ background: var(--bg-primary);
74
+ color: var(--text-primary);
75
+ height: 100vh;
76
+ display: grid;
77
+ grid-template-rows: var(--header-h) 1fr var(--footer-h);
78
+ overflow: hidden;
79
+ }
80
+
81
+ /* ── Header ────────────────────────────────────────────────────────── */
82
+ .header {
83
+ display: flex;
84
+ align-items: center;
85
+ justify-content: space-between;
86
+ padding: 0 16px;
87
+ background: var(--bg-secondary);
88
+ border-bottom: 1px solid var(--border);
89
+ position: sticky;
90
+ top: 0;
91
+ z-index: 100;
92
+ }
93
+ .logo {
94
+ display: flex;
95
+ align-items: center;
96
+ gap: 10px;
97
+ font-weight: 700;
98
+ font-size: 15px;
99
+ letter-spacing: .5px;
100
+ text-transform: uppercase;
101
+ color: var(--text-primary);
102
+ cursor: pointer;
103
+ text-decoration: none;
104
+ }
105
+ .logo-icon {
106
+ width: 32px; height: 32px;
107
+ background: var(--c-furnace);
108
+ border-radius: var(--radius-md);
109
+ display: flex; align-items: center; justify-content: center;
110
+ font-size: 16px;
111
+ }
112
+ .logo span { color: var(--c-furnace); }
113
+ .header-actions { display: flex; align-items: center; gap: 8px; }
114
+ .ws-badge {
115
+ display: flex; align-items: center; gap: 5px;
116
+ font-size: 12px; color: var(--text-secondary);
117
+ }
118
+ .ws-dot {
119
+ width: 8px; height: 8px; border-radius: 50%;
120
+ background: var(--success);
121
+ box-shadow: 0 0 6px var(--success);
122
+ animation: pulse 2s infinite;
123
+ }
124
+ @keyframes pulse { 0%,100%{opacity:1} 50%{opacity:.5} }
125
+ .btn-icon {
126
+ width: 32px; height: 32px;
127
+ background: transparent;
128
+ border: 1px solid var(--border);
129
+ border-radius: var(--radius-md);
130
+ color: var(--text-secondary);
131
+ cursor: pointer;
132
+ display: flex; align-items: center; justify-content: center;
133
+ font-size: 14px;
134
+ transition: background .15s, color .15s;
135
+ }
136
+ .btn-icon:hover { background: var(--bg-hover); color: var(--text-primary); }
137
+
138
+ /* ── Main Content ──────────────────────────────────────────────────── */
139
+ .main {
140
+ display: grid;
141
+ grid-template-rows: 1fr auto;
142
+ overflow: hidden;
143
+ }
144
+ .panels-top {
145
+ display: grid;
146
+ grid-template-columns: 1fr 1fr 1fr;
147
+ gap: 0;
148
+ border-bottom: 1px solid var(--border);
149
+ overflow: hidden;
150
+ }
151
+ .panel {
152
+ display: flex; flex-direction: column;
153
+ border-right: 1px solid var(--border);
154
+ overflow: hidden;
155
+ }
156
+ .panel:last-child { border-right: none; }
157
+ .panel-header {
158
+ display: flex; align-items: center; justify-content: space-between;
159
+ padding: 12px 16px;
160
+ border-bottom: 1px solid var(--border);
161
+ background: var(--bg-secondary);
162
+ flex-shrink: 0;
163
+ }
164
+ .panel-title {
165
+ font-size: 13px; font-weight: 600;
166
+ text-transform: uppercase; letter-spacing: .5px;
167
+ color: var(--text-secondary);
168
+ display: flex; align-items: center; gap: 8px;
169
+ }
170
+ .panel-count {
171
+ background: var(--bg-tertiary);
172
+ border: 1px solid var(--border);
173
+ border-radius: 10px;
174
+ padding: 1px 7px;
175
+ font-size: 11px;
176
+ color: var(--text-secondary);
177
+ }
178
+ .panel-tabs {
179
+ display: flex; border-bottom: 1px solid var(--border);
180
+ background: var(--bg-secondary);
181
+ flex-shrink: 0;
182
+ }
183
+ .filter-tab {
184
+ padding: 8px 14px;
185
+ font-size: 12px; font-weight: 500;
186
+ color: var(--text-secondary);
187
+ cursor: pointer;
188
+ border: none; background: none;
189
+ border-bottom: 2px solid transparent;
190
+ transition: color .15s;
191
+ display: flex; align-items: center; gap: 5px;
192
+ }
193
+ .filter-tab.active {
194
+ color: var(--text-primary);
195
+ border-bottom-color: var(--accent2);
196
+ }
197
+ .filter-tab:hover { color: var(--text-primary); }
198
+ .tab-count {
199
+ background: var(--bg-active);
200
+ border-radius: 8px;
201
+ padding: 1px 5px;
202
+ font-size: 10px;
203
+ }
204
+ .panel-content { flex: 1; overflow-y: auto; padding: 8px; }
205
+
206
+ /* scrollbar */
207
+ .panel-content::-webkit-scrollbar { width: 4px; }
208
+ .panel-content::-webkit-scrollbar-track { background: transparent; }
209
+ .panel-content::-webkit-scrollbar-thumb { background: var(--border); border-radius: 2px; }
210
+
211
+ /* ── Task Cards ─────────────────────────────────────────────────────── */
212
+ .task-card {
213
+ background: var(--bg-secondary);
214
+ border: 1px solid var(--border);
215
+ border-radius: var(--radius-md);
216
+ padding: 12px;
217
+ margin-bottom: 6px;
218
+ cursor: pointer;
219
+ transition: background .15s, box-shadow .15s;
220
+ }
221
+ .task-card:hover { background: var(--bg-tertiary); box-shadow: var(--shadow-md); }
222
+ .task-card.selected { border-color: var(--accent2); background: color-mix(in srgb, var(--accent2) 10%, transparent); }
223
+ .task-top { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; }
224
+ .priority-dot {
225
+ width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0;
226
+ }
227
+ .p-critical { background: var(--danger); box-shadow: 0 0 5px var(--danger); }
228
+ .p-high { background: var(--c-furnace); }
229
+ .p-medium { background: var(--warning); }
230
+ .p-low { background: var(--info); }
231
+ .task-id {
232
+ font-family: var(--font-mono); font-size: 11px;
233
+ color: var(--text-muted); font-weight: 600;
234
+ }
235
+ .task-title {
236
+ font-size: 13px; font-weight: 500; color: var(--text-primary);
237
+ line-height: 1.4; margin-bottom: 8px;
238
+ }
239
+ .task-meta { display: flex; align-items: center; justify-content: space-between; }
240
+ .agent-badge {
241
+ display: inline-flex; align-items: center;
242
+ padding: 2px 8px; border-radius: 10px;
243
+ font-size: 11px; font-weight: 600;
244
+ }
245
+ .task-time { font-size: 11px; color: var(--text-muted); }
246
+
247
+ /* ── Agent Cards ────────────────────────────────────────────────────── */
248
+ .agent-card {
249
+ background: var(--bg-secondary);
250
+ border: 1px solid var(--border);
251
+ border-radius: var(--radius-md);
252
+ padding: 12px;
253
+ margin-bottom: 6px;
254
+ display: flex; align-items: center; gap: 12px;
255
+ }
256
+ .agent-avatar {
257
+ width: 40px; height: 40px; border-radius: var(--radius-md);
258
+ display: flex; align-items: center; justify-content: center;
259
+ font-weight: 700; font-size: 13px; flex-shrink: 0;
260
+ }
261
+ .agent-info { flex: 1; min-width: 0; }
262
+ .agent-name { font-size: 13px; font-weight: 600; margin-bottom: 3px; }
263
+ .agent-status { display: flex; align-items: center; gap: 5px; }
264
+ .status-dot {
265
+ width: 7px; height: 7px; border-radius: 50%;
266
+ }
267
+ .s-idle { background: var(--success); }
268
+ .s-working { background: var(--info); box-shadow: 0 0 5px var(--info); animation: pulse 1.5s infinite; }
269
+ .s-blocked { background: var(--warning); }
270
+ .s-offline { background: var(--text-muted); }
271
+ .s-testing { background: var(--c-crucible); animation: pulse 1.5s infinite; }
272
+ .status-text { font-size: 12px; color: var(--text-secondary); }
273
+ .agent-task { font-size: 11px; color: var(--text-muted); margin-top: 3px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
274
+ .progress-bar-wrap { margin-top: 6px; background: var(--bg-tertiary); border-radius: 3px; height: 4px; overflow: hidden; }
275
+ .progress-bar-fill { height: 100%; border-radius: 3px; transition: width .3s; }
276
+
277
+ /* agent color helpers */
278
+ .av-hub { background: color-mix(in srgb, var(--c-hub) 20%, transparent); color: var(--c-hub); }
279
+ .av-anvil { background: color-mix(in srgb, var(--c-anvil) 20%, transparent); color: var(--c-anvil); }
280
+ .av-furnace { background: color-mix(in srgb, var(--c-furnace) 20%, transparent); color: var(--c-furnace); }
281
+ .av-crucible { background: color-mix(in srgb, var(--c-crucible) 20%, transparent); color: var(--c-crucible); }
282
+ .av-sentinel { background: color-mix(in srgb, var(--c-sentinel) 20%, transparent); color: var(--c-sentinel); }
283
+ .av-architect { background: color-mix(in srgb, var(--c-architect) 20%, transparent); color: var(--c-architect); }
284
+ .av-ember { background: color-mix(in srgb, var(--c-ember) 20%, transparent); color: var(--c-ember); }
285
+ .av-aegis { background: color-mix(in srgb, var(--c-aegis) 20%, transparent); color: var(--c-aegis); }
286
+ .av-pixel { background: color-mix(in srgb, var(--c-pixel) 20%, transparent); color: var(--c-pixel); }
287
+ .av-scribe { background: color-mix(in srgb, var(--c-scribe) 20%, transparent); color: var(--c-scribe); }
288
+
289
+ .badge-hub { background: color-mix(in srgb, var(--c-hub) 15%, transparent); color: var(--c-hub); }
290
+ .badge-anvil { background: color-mix(in srgb, var(--c-anvil) 15%, transparent); color: var(--c-anvil); }
291
+ .badge-furnace { background: color-mix(in srgb, var(--c-furnace) 15%, transparent); color: var(--c-furnace); }
292
+ .badge-crucible { background: color-mix(in srgb, var(--c-crucible) 15%, transparent); color: var(--c-crucible); }
293
+ .badge-sentinel { background: color-mix(in srgb, var(--c-sentinel) 15%, transparent); color: var(--c-sentinel); }
294
+ .badge-architect { background: color-mix(in srgb, var(--c-architect) 15%, transparent); color: var(--c-architect); }
295
+ .badge-ember { background: color-mix(in srgb, var(--c-ember) 15%, transparent); color: var(--c-ember); }
296
+ .badge-aegis { background: color-mix(in srgb, var(--c-aegis) 15%, transparent); color: var(--c-aegis); }
297
+ .badge-pixel { background: color-mix(in srgb, var(--c-pixel) 15%, transparent); color: var(--c-pixel); }
298
+
299
+ /* ── Notifications ──────────────────────────────────────────────────── */
300
+ .notif-group-label {
301
+ font-size: 10px; font-weight: 600; text-transform: uppercase;
302
+ letter-spacing: .5px; color: var(--text-muted);
303
+ padding: 8px 4px 4px;
304
+ }
305
+ .notif-card {
306
+ background: var(--bg-secondary);
307
+ border: 1px solid var(--border);
308
+ border-radius: var(--radius-md);
309
+ padding: 10px 12px;
310
+ margin-bottom: 6px;
311
+ display: flex; gap: 10px; align-items: flex-start;
312
+ cursor: pointer; transition: background .15s;
313
+ }
314
+ .notif-card:hover { background: var(--bg-tertiary); }
315
+ .notif-card.unread { border-left: 3px solid var(--accent2); }
316
+ .notif-icon {
317
+ width: 24px; height: 24px; border-radius: 50%;
318
+ display: flex; align-items: center; justify-content: center;
319
+ font-size: 11px; font-weight: 700; flex-shrink: 0; margin-top: 1px;
320
+ }
321
+ .ni-success { background: color-mix(in srgb, var(--success) 20%, transparent); color: var(--success); }
322
+ .ni-error { background: color-mix(in srgb, var(--danger) 20%, transparent); color: var(--danger); }
323
+ .ni-info { background: color-mix(in srgb, var(--info) 20%, transparent); color: var(--info); }
324
+ .ni-warning { background: color-mix(in srgb, var(--warning) 20%, transparent); color: var(--warning); }
325
+ .ni-attn { background: color-mix(in srgb, var(--c-furnace) 20%, transparent); color: var(--c-furnace); }
326
+ .notif-body { flex: 1; min-width: 0; }
327
+ .notif-title { font-size: 12px; font-weight: 600; margin-bottom: 2px; }
328
+ .notif-desc { font-size: 11px; color: var(--text-secondary); line-height: 1.4; margin-bottom: 4px; }
329
+ .notif-time { font-size: 10px; color: var(--text-muted); }
330
+ .unread-dot {
331
+ width: 6px; height: 6px; border-radius: 50%;
332
+ background: var(--accent2); flex-shrink: 0; margin-top: 6px;
333
+ }
334
+
335
+ /* ── Issues Panel ────────────────────────────────────────────────────── */
336
+ .issues-panel {
337
+ border-top: 1px solid var(--border);
338
+ background: var(--bg-primary);
339
+ max-height: 220px;
340
+ display: flex; flex-direction: column;
341
+ }
342
+ .issues-header {
343
+ display: flex; align-items: center; justify-content: space-between;
344
+ padding: 10px 16px;
345
+ border-bottom: 1px solid var(--border);
346
+ background: var(--bg-secondary);
347
+ flex-shrink: 0;
348
+ }
349
+ .issues-title {
350
+ font-size: 13px; font-weight: 600;
351
+ text-transform: uppercase; letter-spacing: .5px;
352
+ color: var(--text-secondary);
353
+ display: flex; align-items: center; gap: 8px;
354
+ }
355
+ .issues-list {
356
+ display: flex; gap: 8px;
357
+ overflow-x: auto; padding: 10px 16px;
358
+ flex: 1;
359
+ }
360
+ .issues-list::-webkit-scrollbar { height: 4px; }
361
+ .issues-list::-webkit-scrollbar-thumb { background: var(--border); border-radius: 2px; }
362
+ .issue-card {
363
+ background: var(--bg-secondary);
364
+ border: 1px solid var(--border);
365
+ border-radius: var(--radius-md);
366
+ padding: 10px 12px;
367
+ min-width: 260px; max-width: 300px;
368
+ flex-shrink: 0;
369
+ display: flex; gap: 10px; align-items: flex-start;
370
+ transition: box-shadow .15s;
371
+ }
372
+ .issue-card:hover { box-shadow: var(--shadow-md); }
373
+ .issue-cat {
374
+ width: 36px; height: 36px; border-radius: var(--radius-sm);
375
+ display: flex; align-items: center; justify-content: center;
376
+ font-weight: 700; font-size: 11px; flex-shrink: 0;
377
+ }
378
+ .ic-doc { background: color-mix(in srgb, var(--c-hub) 20%, transparent); color: var(--c-hub); }
379
+ .ic-test { background: color-mix(in srgb, var(--danger) 20%, transparent); color: var(--danger); }
380
+ .ic-sec { background: color-mix(in srgb, var(--danger) 20%, transparent); color: var(--danger); }
381
+ .ic-cov { background: color-mix(in srgb, var(--warning) 20%, transparent); color: var(--warning); }
382
+ .ic-todo { background: color-mix(in srgb, var(--info) 20%, transparent); color: var(--info); }
383
+ .ic-rev { background: color-mix(in srgb, var(--c-furnace) 20%, transparent); color: var(--c-furnace); }
384
+ .issue-body { flex: 1; min-width: 0; }
385
+ .issue-badge {
386
+ display: inline-block; padding: 1px 6px;
387
+ border-radius: var(--radius-sm);
388
+ font-size: 10px; font-weight: 700; margin-bottom: 4px;
389
+ }
390
+ .ib-doc { background: color-mix(in srgb, var(--c-hub) 25%, transparent); color: var(--c-hub); }
391
+ .ib-test { background: color-mix(in srgb, var(--danger) 25%, transparent); color: var(--danger); }
392
+ .ib-sec { background: color-mix(in srgb, var(--danger) 25%, transparent); color: var(--danger); }
393
+ .ib-cov { background: color-mix(in srgb, var(--warning) 25%, transparent); color: var(--warning); }
394
+ .ib-todo { background: color-mix(in srgb, var(--info) 25%, transparent); color: var(--info); }
395
+ .ib-rev { background: color-mix(in srgb, var(--c-furnace) 25%, transparent); color: var(--c-furnace); }
396
+ .issue-title { font-size: 12px; font-weight: 500; line-height: 1.3; margin-bottom: 4px; }
397
+ .issue-meta { font-size: 11px; color: var(--text-muted); margin-bottom: 8px; }
398
+ .btn-dispatch {
399
+ width: 100%;
400
+ background: color-mix(in srgb, var(--accent2) 15%, transparent);
401
+ color: var(--accent2);
402
+ border: 1px solid color-mix(in srgb, var(--accent2) 30%, transparent);
403
+ border-radius: var(--radius-sm);
404
+ padding: 5px 8px;
405
+ font-size: 11px; font-weight: 600;
406
+ cursor: pointer; transition: background .15s;
407
+ text-align: left;
408
+ }
409
+ .btn-dispatch:hover { background: color-mix(in srgb, var(--accent2) 25%, transparent); }
410
+
411
+ /* ── Footer ─────────────────────────────────────────────────────────── */
412
+ .footer {
413
+ display: flex; align-items: center; justify-content: space-between;
414
+ padding: 0 16px;
415
+ background: var(--bg-secondary);
416
+ border-top: 1px solid var(--border);
417
+ font-size: 11px; color: var(--text-muted);
418
+ }
419
+ .footer-left { display: flex; align-items: center; gap: 8px; }
420
+ .footer-right { display: flex; align-items: center; gap: 12px; }
421
+ kbd {
422
+ background: var(--bg-tertiary);
423
+ border: 1px solid var(--border);
424
+ border-radius: 3px;
425
+ padding: 1px 5px;
426
+ font-family: var(--font-mono);
427
+ font-size: 10px; color: var(--text-secondary);
428
+ box-shadow: 0 1px 0 var(--border);
429
+ }
430
+
431
+ /* ── Modal ───────────────────────────────────────────────────────────── */
432
+ .modal-backdrop {
433
+ position: fixed; inset: 0;
434
+ background: rgba(0,0,0,.7);
435
+ z-index: 200;
436
+ display: none; align-items: center; justify-content: center;
437
+ }
438
+ .modal-backdrop.open { display: flex; }
439
+ .modal {
440
+ background: var(--bg-secondary);
441
+ border: 1px solid var(--border);
442
+ border-radius: var(--radius-lg);
443
+ width: 100%; max-width: 520px;
444
+ box-shadow: var(--shadow-lg);
445
+ overflow: hidden;
446
+ }
447
+ .modal-header {
448
+ display: flex; align-items: center; justify-content: space-between;
449
+ padding: 16px 20px;
450
+ border-bottom: 1px solid var(--border);
451
+ font-weight: 600; font-size: 15px;
452
+ }
453
+ .modal-body { padding: 20px; }
454
+ .modal-footer {
455
+ padding: 16px 20px;
456
+ border-top: 1px solid var(--border);
457
+ display: flex; justify-content: flex-end; gap: 8px;
458
+ }
459
+ .dispatch-info { background: var(--bg-tertiary); border-radius: var(--radius-md); padding: 12px; margin-bottom: 16px; }
460
+ .dispatch-row { display: flex; justify-content: space-between; padding: 4px 0; font-size: 13px; }
461
+ .dispatch-row span:first-child { color: var(--text-muted); }
462
+ .dispatch-preview {
463
+ background: var(--bg-primary);
464
+ border: 1px solid var(--border);
465
+ border-radius: var(--radius-md);
466
+ padding: 12px;
467
+ }
468
+ .preview-id { font-family: var(--font-mono); font-size: 11px; color: var(--text-muted); margin-bottom: 6px; }
469
+ .preview-title { font-size: 14px; font-weight: 600; margin-bottom: 8px; }
470
+ .preview-meta { display: flex; gap: 8px; }
471
+ .btn { padding: 7px 16px; border-radius: var(--radius-md); font-size: 13px; font-weight: 500; cursor: pointer; border: 1px solid var(--border); transition: background .15s; }
472
+ .btn-primary { background: var(--accent); color: #fff; border-color: var(--accent); }
473
+ .btn-primary:hover { filter: brightness(1.1); }
474
+ .btn-secondary { background: var(--bg-tertiary); color: var(--text-primary); }
475
+ .btn-secondary:hover { background: var(--bg-hover); }
476
+
477
+ /* ── Shortcuts modal ─────────────────────────────────────────────────── */
478
+ .shortcut-group { margin-bottom: 16px; }
479
+ .shortcut-group-title { font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: .5px; color: var(--text-muted); margin-bottom: 8px; }
480
+ .shortcut-row { display: flex; align-items: center; justify-content: space-between; padding: 5px 0; font-size: 13px; }
481
+ .shortcut-keys { display: flex; gap: 4px; }
482
+
483
+ /* ── Toast ─────────────────────────────────────────────────────────── */
484
+ .toast-container {
485
+ position: fixed; bottom: calc(var(--footer-h) + 12px); right: 16px;
486
+ z-index: 300; display: flex; flex-direction: column; gap: 8px;
487
+ max-width: 380px;
488
+ }
489
+ .toast {
490
+ background: var(--bg-secondary);
491
+ border: 1px solid var(--border);
492
+ border-radius: var(--radius-md);
493
+ padding: 12px 14px;
494
+ display: flex; gap: 10px; align-items: flex-start;
495
+ box-shadow: var(--shadow-md);
496
+ animation: slideIn .2s ease;
497
+ }
498
+ @keyframes slideIn { from { transform: translateX(120%); opacity:0; } to { transform: translateX(0); opacity:1; } }
499
+ .toast-icon { width: 20px; height: 20px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 10px; font-weight: 700; flex-shrink: 0; }
500
+ .toast-body { flex: 1; }
501
+ .toast-title { font-size: 13px; font-weight: 600; margin-bottom: 2px; }
502
+ .toast-msg { font-size: 12px; color: var(--text-secondary); }
503
+ </style>
504
+ </head>
505
+ <body>
506
+
507
+ <!-- ── Header ─────────────────────────────────────────────────────────── -->
508
+ <header class="header">
509
+ <a class="logo" href="#">
510
+ <div class="logo-icon">⚒</div>
511
+ VIBE <span>FORGE</span>&nbsp;DASHBOARD
512
+ </a>
513
+ <div class="header-actions">
514
+ <div class="ws-badge">
515
+ <div class="ws-dot"></div>
516
+ Live
517
+ </div>
518
+ <button class="btn-icon" title="Settings">⚙</button>
519
+ <button class="btn-icon" id="themeBtn" title="Toggle theme" onclick="toggleTheme()">☀</button>
520
+ </div>
521
+ </header>
522
+
523
+ <!-- ── Main ───────────────────────────────────────────────────────────── -->
524
+ <main class="main">
525
+ <div class="panels-top">
526
+
527
+ <!-- TASKS PANEL -->
528
+ <div class="panel">
529
+ <div class="panel-header">
530
+ <div class="panel-title">
531
+ Tasks
532
+ <span class="panel-count">12</span>
533
+ </div>
534
+ <button class="btn-icon" style="width:24px;height:24px;font-size:12px">↻</button>
535
+ </div>
536
+ <div class="panel-tabs">
537
+ <button class="filter-tab active">Pending <span class="tab-count">5</span></button>
538
+ <button class="filter-tab">Active <span class="tab-count">3</span></button>
539
+ <button class="filter-tab">Review <span class="tab-count">2</span></button>
540
+ <button class="filter-tab">Done <span class="tab-count">2</span></button>
541
+ </div>
542
+ <div class="panel-content">
543
+
544
+ <div class="task-card selected">
545
+ <div class="task-top">
546
+ <div class="priority-dot p-critical"></div>
547
+ <span class="task-id">PLAT-2</span>
548
+ </div>
549
+ <div class="task-title">Implement relay WebSocket authentication for forge↔lab integration</div>
550
+ <div class="task-meta">
551
+ <span class="agent-badge badge-furnace">@Furnace</span>
552
+ <span class="task-time">just now</span>
553
+ </div>
554
+ </div>
555
+
556
+ <div class="task-card">
557
+ <div class="task-top">
558
+ <div class="priority-dot p-high"></div>
559
+ <span class="task-id">IPC-1</span>
560
+ </div>
561
+ <div class="task-title">Lab dispatcher: write inbox tasks to worker-inbox directory</div>
562
+ <div class="task-meta">
563
+ <span class="agent-badge badge-anvil">@Anvil</span>
564
+ <span class="task-time">14 min ago</span>
565
+ </div>
566
+ </div>
567
+
568
+ <div class="task-card">
569
+ <div class="task-top">
570
+ <div class="priority-dot p-high"></div>
571
+ <span class="task-id">IPC-2</span>
572
+ </div>
573
+ <div class="task-title">Forge daemon: poll worker-inbox and spawn workers on task arrival</div>
574
+ <div class="task-meta">
575
+ <span class="agent-badge badge-furnace">@Furnace</span>
576
+ <span class="task-time">32 min ago</span>
577
+ </div>
578
+ </div>
579
+
580
+ <div class="task-card">
581
+ <div class="task-top">
582
+ <div class="priority-dot p-medium"></div>
583
+ <span class="task-id">FORGE-4</span>
584
+ </div>
585
+ <div class="task-title">Planning Hub: expose create_story MCP tool for lab promotion flow</div>
586
+ <div class="task-meta">
587
+ <span class="agent-badge badge-architect">@Arch</span>
588
+ <span class="task-time">1 hr ago</span>
589
+ </div>
590
+ </div>
591
+
592
+ <div class="task-card">
593
+ <div class="task-top">
594
+ <div class="priority-dot p-low"></div>
595
+ <span class="task-id">DOC-012</span>
596
+ </div>
597
+ <div class="task-title">Update agent-manifest.yaml with new Heimdall context schema fields</div>
598
+ <div class="task-meta">
599
+ <span class="agent-badge" style="background:color-mix(in srgb,var(--c-scribe) 15%,transparent);color:var(--c-scribe)">@Scribe</span>
600
+ <span class="task-time">2 hr ago</span>
601
+ </div>
602
+ </div>
603
+
604
+ </div>
605
+ </div>
606
+
607
+ <!-- AGENTS PANEL -->
608
+ <div class="panel">
609
+ <div class="panel-header">
610
+ <div class="panel-title">
611
+ Agents
612
+ <span class="panel-count">10</span>
613
+ </div>
614
+ <button class="btn-icon" style="width:24px;height:24px;font-size:12px">↻</button>
615
+ </div>
616
+ <div class="panel-content" style="padding-top:10px">
617
+
618
+ <div class="agent-card">
619
+ <div class="agent-avatar av-hub">H</div>
620
+ <div class="agent-info">
621
+ <div class="agent-name">Planning Hub</div>
622
+ <div class="agent-status"><div class="status-dot s-working"></div><span class="status-text">Working</span></div>
623
+ <div class="agent-task">Enriching PLAT-2 dev notes</div>
624
+ <div class="progress-bar-wrap"><div class="progress-bar-fill" style="width:60%;background:var(--c-hub)"></div></div>
625
+ </div>
626
+ </div>
627
+
628
+ <div class="agent-card">
629
+ <div class="agent-avatar av-furnace">F</div>
630
+ <div class="agent-info">
631
+ <div class="agent-name">Furnace</div>
632
+ <div class="agent-status"><div class="status-dot s-working"></div><span class="status-text">Working · PLAT-2</span></div>
633
+ <div class="agent-task">Implementing relay auth middleware</div>
634
+ <div class="progress-bar-wrap"><div class="progress-bar-fill" style="width:35%;background:var(--c-furnace)"></div></div>
635
+ </div>
636
+ </div>
637
+
638
+ <div class="agent-card">
639
+ <div class="agent-avatar av-crucible">C</div>
640
+ <div class="agent-info">
641
+ <div class="agent-name">Crucible</div>
642
+ <div class="agent-status"><div class="status-dot s-testing"></div><span class="status-text">Testing · IPC-7</span></div>
643
+ <div class="agent-task">Running integration test suite</div>
644
+ <div class="progress-bar-wrap"><div class="progress-bar-fill" style="width:78%;background:var(--c-crucible)"></div></div>
645
+ </div>
646
+ </div>
647
+
648
+ <div class="agent-card">
649
+ <div class="agent-avatar av-sentinel">Se</div>
650
+ <div class="agent-info">
651
+ <div class="agent-name">Sentinel</div>
652
+ <div class="agent-status"><div class="status-dot s-working"></div><span class="status-text">Reviewing · IPC-6</span></div>
653
+ <div class="agent-task">Auditing handoff schema changes</div>
654
+ </div>
655
+ </div>
656
+
657
+ <div class="agent-card">
658
+ <div class="agent-avatar av-anvil">A</div>
659
+ <div class="agent-info">
660
+ <div class="agent-name">Anvil</div>
661
+ <div class="agent-status"><div class="status-dot s-idle"></div><span class="status-text">Idle</span></div>
662
+ </div>
663
+ </div>
664
+
665
+ <div class="agent-card">
666
+ <div class="agent-avatar av-architect">Ar</div>
667
+ <div class="agent-info">
668
+ <div class="agent-name">Architect</div>
669
+ <div class="agent-status"><div class="status-dot s-idle"></div><span class="status-text">Idle</span></div>
670
+ </div>
671
+ </div>
672
+
673
+ <div class="agent-card">
674
+ <div class="agent-avatar av-aegis">Ae</div>
675
+ <div class="agent-info">
676
+ <div class="agent-name">Aegis</div>
677
+ <div class="agent-status"><div class="status-dot s-idle"></div><span class="status-text">Idle</span></div>
678
+ </div>
679
+ </div>
680
+
681
+ <div class="agent-card">
682
+ <div class="agent-avatar av-ember">E</div>
683
+ <div class="agent-info">
684
+ <div class="agent-name">Ember</div>
685
+ <div class="agent-status"><div class="status-dot s-blocked"></div><span class="status-text">Blocked · CI-003</span></div>
686
+ <div class="agent-task">Needs AWS staging credentials</div>
687
+ </div>
688
+ </div>
689
+
690
+ </div>
691
+ </div>
692
+
693
+ <!-- NOTIFICATIONS PANEL -->
694
+ <div class="panel">
695
+ <div class="panel-header">
696
+ <div class="panel-title">
697
+ Notifications
698
+ <span class="panel-count" style="background:color-mix(in srgb,var(--accent2) 20%,transparent);color:var(--accent2);border-color:color-mix(in srgb,var(--accent2) 40%,transparent)">3 new</span>
699
+ </div>
700
+ <button class="btn-icon" style="width:24px;height:24px;font-size:11px;color:var(--text-muted)">✓✓</button>
701
+ </div>
702
+ <div class="panel-content">
703
+
704
+ <div class="notif-group-label">Today</div>
705
+
706
+ <div class="notif-card unread">
707
+ <div class="notif-icon ni-error">✕</div>
708
+ <div class="notif-body">
709
+ <div class="notif-title">Heimdall: Gjallarhorn Sounded</div>
710
+ <div class="notif-desc">Worker <strong>anvil</strong> hit 3 violations on FORGE-7 — escalated to human review</div>
711
+ <div class="notif-time">8 min ago</div>
712
+ </div>
713
+ <div class="unread-dot"></div>
714
+ </div>
715
+
716
+ <div class="notif-card unread">
717
+ <div class="notif-icon ni-success">✓</div>
718
+ <div class="notif-body">
719
+ <div class="notif-title">PLAT-1 merged to main</div>
720
+ <div class="notif-desc">Heimdall pre-tool interceptor shipped · 34/34 tests passing</div>
721
+ <div class="notif-time">2 hr ago</div>
722
+ </div>
723
+ <div class="unread-dot"></div>
724
+ </div>
725
+
726
+ <div class="notif-card unread">
727
+ <div class="notif-icon ni-attn">!</div>
728
+ <div class="notif-body">
729
+ <div class="notif-title">Attention: Ember blocked</div>
730
+ <div class="notif-desc">Needs AWS staging credentials for CI-003 deployment pipeline</div>
731
+ <div class="notif-time">45 min ago</div>
732
+ </div>
733
+ <div class="unread-dot"></div>
734
+ </div>
735
+
736
+ <div class="notif-group-label">Yesterday</div>
737
+
738
+ <div class="notif-card">
739
+ <div class="notif-icon ni-info">i</div>
740
+ <div class="notif-body">
741
+ <div class="notif-title">IPC-7 ready for review</div>
742
+ <div class="notif-desc">Daemon modularization complete — 165 tests passing</div>
743
+ <div class="notif-time">Yesterday, 4:22 PM</div>
744
+ </div>
745
+ </div>
746
+
747
+ <div class="notif-card">
748
+ <div class="notif-icon ni-success">✓</div>
749
+ <div class="notif-body">
750
+ <div class="notif-title">Branch protection enabled</div>
751
+ <div class="notif-desc">sugar-crash-studios/vibe-forge · main · test-unit, lint, shellcheck</div>
752
+ <div class="notif-time">Yesterday, 2:15 PM</div>
753
+ </div>
754
+ </div>
755
+
756
+ </div>
757
+ </div>
758
+ </div>
759
+
760
+ <!-- ISSUES PANEL -->
761
+ <div class="issues-panel">
762
+ <div class="issues-header">
763
+ <div class="issues-title">
764
+ Detected Issues
765
+ <span class="panel-count">6</span>
766
+ </div>
767
+ <div style="display:flex;gap:6px;align-items:center">
768
+ <span style="font-size:11px;color:var(--text-muted)">Auto-scan: 30m ago</span>
769
+ <button class="btn-icon" style="width:24px;height:24px;font-size:12px">↻</button>
770
+ <button class="btn-icon" style="width:24px;height:24px;font-size:12px">⌄</button>
771
+ </div>
772
+ </div>
773
+ <div class="issues-list">
774
+
775
+ <div class="issue-card">
776
+ <div class="issue-cat ic-sec">S</div>
777
+ <div class="issue-body">
778
+ <span class="issue-badge ib-sec">SEC</span>
779
+ <div class="issue-title">Dependency: express@4.18.2 has known XSS path</div>
780
+ <div class="issue-meta">CVE-2024-43796 · Critical</div>
781
+ <button class="btn-dispatch" onclick="openDispatch('SEC-001','aegis')">Dispatch Aegis &rsaquo;&rsaquo;</button>
782
+ </div>
783
+ </div>
784
+
785
+ <div class="issue-card">
786
+ <div class="issue-cat ic-test">T</div>
787
+ <div class="issue-body">
788
+ <span class="issue-badge ib-test">TEST</span>
789
+ <div class="issue-title">Failing tests detected in CI run #142</div>
790
+ <div class="issue-meta">3 failing · tests/integration/relay.test.js · High</div>
791
+ <button class="btn-dispatch" onclick="openDispatch('TEST-003','crucible')">Dispatch Crucible &rsaquo;&rsaquo;</button>
792
+ </div>
793
+ </div>
794
+
795
+ <div class="issue-card">
796
+ <div class="issue-cat ic-doc">D</div>
797
+ <div class="issue-body">
798
+ <span class="issue-badge ib-doc">DOC</span>
799
+ <div class="issue-title">Stale docs: vibe-lab-integration.md</div>
800
+ <div class="issue-meta">Last updated 38 days ago · Threshold: 30 days · Medium</div>
801
+ <button class="btn-dispatch" onclick="openDispatch('DOC-004','scribe')">Dispatch Scribe &rsaquo;&rsaquo;</button>
802
+ </div>
803
+ </div>
804
+
805
+ <div class="issue-card">
806
+ <div class="issue-cat ic-cov">C</div>
807
+ <div class="issue-body">
808
+ <span class="issue-badge ib-cov">COV</span>
809
+ <div class="issue-title">Coverage dropped: bin/lib/database.js at 61%</div>
810
+ <div class="issue-meta">Threshold: 80% · Delta: -19% · Medium</div>
811
+ <button class="btn-dispatch" onclick="openDispatch('COV-002','crucible')">Dispatch Crucible &rsaquo;&rsaquo;</button>
812
+ </div>
813
+ </div>
814
+
815
+ <div class="issue-card">
816
+ <div class="issue-cat ic-rev">R</div>
817
+ <div class="issue-body">
818
+ <span class="issue-badge ib-rev">REVIEW</span>
819
+ <div class="issue-title">PR #17 pending review 48h</div>
820
+ <div class="issue-meta">feat: IPC inbox polling · @furnace · High</div>
821
+ <button class="btn-dispatch" onclick="openDispatch('REV-001','sentinel')">Dispatch Sentinel &rsaquo;&rsaquo;</button>
822
+ </div>
823
+ </div>
824
+
825
+ <div class="issue-card">
826
+ <div class="issue-cat ic-todo">TD</div>
827
+ <div class="issue-body">
828
+ <span class="issue-badge ib-todo">TODO</span>
829
+ <div class="issue-title">14 TODO/FIXME markers found</div>
830
+ <div class="issue-meta">bin/forge-daemon.sh (6), bin/lib/util.sh (5), bin/cli.js (3) · Low</div>
831
+ <button class="btn-dispatch" onclick="openDispatch('TODO-001',null)">Create Task &rsaquo;&rsaquo;</button>
832
+ </div>
833
+ </div>
834
+
835
+ </div>
836
+ </div>
837
+ </main>
838
+
839
+ <!-- ── Footer ─────────────────────────────────────────────────────────── -->
840
+ <footer class="footer">
841
+ <div class="footer-left">
842
+ <div class="ws-dot" style="width:6px;height:6px"></div>
843
+ <span>Connected · ws://localhost:2800/ws · updated just now</span>
844
+ </div>
845
+ <div class="footer-right">
846
+ <span>Vibe Forge v1.1.0</span>
847
+ <button class="btn-icon" style="width:20px;height:20px;font-size:10px;font-weight:700" onclick="document.getElementById('shortcutsModal').classList.add('open')">?</button>
848
+ </div>
849
+ </footer>
850
+
851
+ <!-- ── Dispatch Modal ─────────────────────────────────────────────────── -->
852
+ <div class="modal-backdrop" id="dispatchModal">
853
+ <div class="modal">
854
+ <div class="modal-header">
855
+ <span id="dispatchTitle">Dispatch Task</span>
856
+ <button class="btn-icon" style="width:28px;height:28px" onclick="document.getElementById('dispatchModal').classList.remove('open')">✕</button>
857
+ </div>
858
+ <div class="modal-body">
859
+ <div class="dispatch-info">
860
+ <div class="dispatch-row"><span>Issue</span><span id="dispatchIssue">—</span></div>
861
+ <div class="dispatch-row"><span>Agent</span><span id="dispatchAgent">—</span></div>
862
+ <div class="dispatch-row"><span>Priority</span><span id="dispatchPri">—</span></div>
863
+ </div>
864
+ <div style="font-size:12px;color:var(--text-muted);margin-bottom:8px">This will create a new task:</div>
865
+ <div class="dispatch-preview">
866
+ <div class="preview-id" id="dispatchId">TASK-AUTO-000</div>
867
+ <div class="preview-title" id="dispatchPreviewTitle">Fix: Issue Title</div>
868
+ <div class="preview-meta">
869
+ <span class="agent-badge badge-sentinel" id="dispatchAgentBadge">@Agent</span>
870
+ </div>
871
+ </div>
872
+ </div>
873
+ <div class="modal-footer">
874
+ <button class="btn btn-secondary" onclick="document.getElementById('dispatchModal').classList.remove('open')">Cancel</button>
875
+ <button class="btn btn-primary" onclick="confirmDispatch()">Confirm Dispatch</button>
876
+ </div>
877
+ </div>
878
+ </div>
879
+
880
+ <!-- ── Keyboard Shortcuts Modal ───────────────────────────────────────── -->
881
+ <div class="modal-backdrop" id="shortcutsModal" onclick="if(event.target===this)this.classList.remove('open')">
882
+ <div class="modal" style="max-width:400px">
883
+ <div class="modal-header">
884
+ Keyboard Shortcuts
885
+ <button class="btn-icon" style="width:28px;height:28px" onclick="document.getElementById('shortcutsModal').classList.remove('open')">✕</button>
886
+ </div>
887
+ <div class="modal-body">
888
+ <div class="shortcut-group">
889
+ <div class="shortcut-group-title">Navigation</div>
890
+ <div class="shortcut-row"><span>Focus panel</span><div class="shortcut-keys"><kbd>1</kbd><kbd>2</kbd><kbd>3</kbd><kbd>4</kbd></div></div>
891
+ <div class="shortcut-row"><span>Move down / up</span><div class="shortcut-keys"><kbd>j</kbd><kbd>k</kbd></div></div>
892
+ <div class="shortcut-row"><span>Expand / select</span><div class="shortcut-keys"><kbd>Enter</kbd></div></div>
893
+ <div class="shortcut-row"><span>Close / deselect</span><div class="shortcut-keys"><kbd>Esc</kbd></div></div>
894
+ </div>
895
+ <div class="shortcut-group">
896
+ <div class="shortcut-group-title">Actions</div>
897
+ <div class="shortcut-row"><span>Dispatch issue</span><div class="shortcut-keys"><kbd>d</kbd></div></div>
898
+ <div class="shortcut-row"><span>Refresh panel</span><div class="shortcut-keys"><kbd>r</kbd></div></div>
899
+ <div class="shortcut-row"><span>Toggle theme</span><div class="shortcut-keys"><kbd>t</kbd></div></div>
900
+ </div>
901
+ <div class="shortcut-group">
902
+ <div class="shortcut-group-title">General</div>
903
+ <div class="shortcut-row"><span>This help</span><div class="shortcut-keys"><kbd>?</kbd></div></div>
904
+ <div class="shortcut-row"><span>Next element</span><div class="shortcut-keys"><kbd>Tab</kbd></div></div>
905
+ </div>
906
+ </div>
907
+ </div>
908
+ </div>
909
+
910
+ <!-- ── Toast ─────────────────────────────────────────────────────────── -->
911
+ <div class="toast-container" id="toastContainer"></div>
912
+
913
+ <script>
914
+ const ISSUES = {
915
+ 'SEC-001': { title: 'Fix: express@4.18.2 XSS vulnerability', agent: 'Aegis', badge: 'badge-aegis', priority: 'Critical' },
916
+ 'TEST-003':{ title: 'Fix: failing relay integration tests', agent: 'Crucible', badge: 'badge-crucible', priority: 'High' },
917
+ 'DOC-004': { title: 'Fix: Stale vibe-lab-integration.md', agent: 'Scribe', badge: 'badge-scribe', priority: 'Medium' },
918
+ 'COV-002': { title: 'Fix: database.js coverage below 80%', agent: 'Crucible', badge: 'badge-crucible', priority: 'Medium' },
919
+ 'REV-001': { title: 'Review: PR #17 IPC inbox polling', agent: 'Sentinel', badge: 'badge-sentinel', priority: 'High' },
920
+ 'TODO-001':{ title: 'Clean up 14 TODO/FIXME markers', agent: 'Team', badge: '', priority: 'Low' },
921
+ };
922
+
923
+ let pendingDispatch = null;
924
+
925
+ function openDispatch(id, agent) {
926
+ const issue = ISSUES[id];
927
+ if (!issue) return;
928
+ pendingDispatch = id;
929
+ document.getElementById('dispatchTitle').textContent = `Dispatch to ${issue.agent}`;
930
+ document.getElementById('dispatchIssue').textContent = id;
931
+ document.getElementById('dispatchAgent').textContent = issue.agent;
932
+ document.getElementById('dispatchPri').textContent = issue.priority;
933
+ document.getElementById('dispatchId').textContent = `TASK-AUTO-${Date.now().toString().slice(-5)}`;
934
+ document.getElementById('dispatchPreviewTitle').textContent = issue.title;
935
+ const badge = document.getElementById('dispatchAgentBadge');
936
+ badge.textContent = `@${issue.agent}`;
937
+ badge.className = `agent-badge ${issue.badge}`;
938
+ document.getElementById('dispatchModal').classList.add('open');
939
+ }
940
+
941
+ function confirmDispatch() {
942
+ document.getElementById('dispatchModal').classList.remove('open');
943
+ const issue = pendingDispatch ? ISSUES[pendingDispatch] : null;
944
+ showToast('success', 'Task dispatched', `${issue?.title || 'Task'} sent to ${issue?.agent || 'agent'}`);
945
+ pendingDispatch = null;
946
+ }
947
+
948
+ function showToast(type, title, msg) {
949
+ const icons = { success:'✓', error:'✕', info:'i', warning:'!' };
950
+ const classes = { success:'ni-success', error:'ni-error', info:'ni-info', warning:'ni-warning' };
951
+ const t = document.createElement('div');
952
+ t.className = 'toast';
953
+ t.innerHTML = `
954
+ <div class="toast-icon ${classes[type]}">${icons[type]}</div>
955
+ <div class="toast-body">
956
+ <div class="toast-title">${title}</div>
957
+ <div class="toast-msg">${msg}</div>
958
+ </div>`;
959
+ document.getElementById('toastContainer').appendChild(t);
960
+ setTimeout(() => t.remove(), 4000);
961
+ }
962
+
963
+ function toggleTheme() {
964
+ const html = document.documentElement;
965
+ const isDark = html.getAttribute('data-theme') === 'dark';
966
+ html.setAttribute('data-theme', isDark ? 'light' : 'dark');
967
+ document.getElementById('themeBtn').textContent = isDark ? '☽' : '☀';
968
+ }
969
+
970
+ // filter tabs click
971
+ document.querySelectorAll('.filter-tab').forEach(tab => {
972
+ tab.addEventListener('click', () => {
973
+ tab.closest('.panel-tabs').querySelectorAll('.filter-tab').forEach(t => t.classList.remove('active'));
974
+ tab.classList.add('active');
975
+ });
976
+ });
977
+
978
+ // keyboard shortcuts
979
+ document.addEventListener('keydown', e => {
980
+ if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') return;
981
+ if (e.key === 't') toggleTheme();
982
+ if (e.key === '?') document.getElementById('shortcutsModal').classList.toggle('open');
983
+ if (e.key === 'Escape') {
984
+ document.querySelectorAll('.modal-backdrop.open').forEach(m => m.classList.remove('open'));
985
+ }
986
+ });
987
+ </script>
988
+ </body>
989
+ </html>