@proletariat/cli 0.3.58 → 0.3.59

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 (189) hide show
  1. package/dist/commands/{spec → dashboard}/index.d.ts +4 -4
  2. package/dist/commands/dashboard/index.js +117 -0
  3. package/dist/commands/dashboard/index.js.map +1 -0
  4. package/dist/commands/execution/config.js +5 -4
  5. package/dist/commands/execution/config.js.map +1 -1
  6. package/dist/commands/execution/stop.js +4 -2
  7. package/dist/commands/execution/stop.js.map +1 -1
  8. package/dist/commands/execution/view.js +3 -0
  9. package/dist/commands/execution/view.js.map +1 -1
  10. package/dist/commands/init.d.ts +1 -0
  11. package/dist/commands/init.js +40 -3
  12. package/dist/commands/init.js.map +1 -1
  13. package/dist/commands/mcp-server.js +1 -2
  14. package/dist/commands/mcp-server.js.map +1 -1
  15. package/dist/commands/{spec/link/index.d.ts → session/exec.d.ts} +5 -3
  16. package/dist/commands/session/exec.js +205 -0
  17. package/dist/commands/session/exec.js.map +1 -0
  18. package/dist/commands/session/index.js +12 -0
  19. package/dist/commands/session/index.js.map +1 -1
  20. package/dist/commands/{spec/delete.d.ts → session/inspect.d.ts} +3 -3
  21. package/dist/commands/session/inspect.js +316 -0
  22. package/dist/commands/session/inspect.js.map +1 -0
  23. package/dist/commands/session/peek.d.ts +15 -0
  24. package/dist/commands/session/peek.js +141 -8
  25. package/dist/commands/session/peek.js.map +1 -1
  26. package/dist/commands/session/poke.d.ts +4 -1
  27. package/dist/commands/session/poke.js +175 -20
  28. package/dist/commands/session/poke.js.map +1 -1
  29. package/dist/commands/{spec/link/depends.d.ts → session/restart.d.ts} +6 -4
  30. package/dist/commands/session/restart.js +320 -0
  31. package/dist/commands/session/restart.js.map +1 -0
  32. package/dist/commands/tools/add.d.ts +20 -0
  33. package/dist/commands/tools/add.js +129 -0
  34. package/dist/commands/tools/add.js.map +1 -0
  35. package/dist/commands/tools/check.d.ts +10 -0
  36. package/dist/commands/tools/check.js +75 -0
  37. package/dist/commands/tools/check.js.map +1 -0
  38. package/dist/commands/tools/detect.d.ts +11 -0
  39. package/dist/commands/tools/detect.js +107 -0
  40. package/dist/commands/tools/detect.js.map +1 -0
  41. package/dist/commands/tools/index.d.ts +11 -0
  42. package/dist/commands/tools/index.js +87 -0
  43. package/dist/commands/tools/index.js.map +1 -0
  44. package/dist/commands/tools/remove.d.ts +13 -0
  45. package/dist/commands/tools/remove.js +55 -0
  46. package/dist/commands/tools/remove.js.map +1 -0
  47. package/dist/commands/{spec/view.d.ts → trello/configure.d.ts} +6 -6
  48. package/dist/commands/trello/configure.js +259 -0
  49. package/dist/commands/trello/configure.js.map +1 -0
  50. package/dist/commands/{spec/plan.d.ts → trello/import.d.ts} +3 -5
  51. package/dist/commands/trello/import.js +241 -0
  52. package/dist/commands/trello/import.js.map +1 -0
  53. package/dist/commands/{spec/ticket.d.ts → trello/sync.d.ts} +5 -6
  54. package/dist/commands/trello/sync.js +190 -0
  55. package/dist/commands/trello/sync.js.map +1 -0
  56. package/dist/commands/work/start.d.ts +1 -0
  57. package/dist/commands/work/start.js +17 -39
  58. package/dist/commands/work/start.js.map +1 -1
  59. package/dist/lib/dashboard/data.d.ts +64 -0
  60. package/dist/lib/dashboard/data.js +259 -0
  61. package/dist/lib/dashboard/data.js.map +1 -0
  62. package/dist/lib/dashboard/html.d.ts +7 -0
  63. package/dist/lib/dashboard/html.js +682 -0
  64. package/dist/lib/dashboard/html.js.map +1 -0
  65. package/dist/lib/dashboard/server.d.ts +20 -0
  66. package/dist/lib/dashboard/server.js +114 -0
  67. package/dist/lib/dashboard/server.js.map +1 -0
  68. package/dist/lib/execution/config.d.ts +8 -0
  69. package/dist/lib/execution/config.js +83 -4
  70. package/dist/lib/execution/config.js.map +1 -1
  71. package/dist/lib/execution/runners.d.ts +36 -3
  72. package/dist/lib/execution/runners.js +216 -35
  73. package/dist/lib/execution/runners.js.map +1 -1
  74. package/dist/lib/execution/spawner.d.ts +2 -0
  75. package/dist/lib/execution/spawner.js +22 -31
  76. package/dist/lib/execution/spawner.js.map +1 -1
  77. package/dist/lib/execution/types.d.ts +26 -5
  78. package/dist/lib/execution/types.js +24 -0
  79. package/dist/lib/execution/types.js.map +1 -1
  80. package/dist/lib/external-issues/adapters.d.ts +17 -0
  81. package/dist/lib/external-issues/adapters.js +88 -0
  82. package/dist/lib/external-issues/adapters.js.map +1 -1
  83. package/dist/lib/external-issues/mapping-store.js +1 -1
  84. package/dist/lib/external-issues/trello.d.ts +80 -0
  85. package/dist/lib/external-issues/trello.js +266 -0
  86. package/dist/lib/external-issues/trello.js.map +1 -0
  87. package/dist/lib/external-issues/types.d.ts +3 -3
  88. package/dist/lib/external-issues/types.js +1 -1
  89. package/dist/lib/external-issues/types.js.map +1 -1
  90. package/dist/lib/linear/client.d.ts +4 -3
  91. package/dist/lib/linear/client.js +185 -122
  92. package/dist/lib/linear/client.js.map +1 -1
  93. package/dist/lib/mcp/tools/cli-passthrough.js +77 -0
  94. package/dist/lib/mcp/tools/cli-passthrough.js.map +1 -1
  95. package/dist/lib/mcp/tools/index.d.ts +0 -1
  96. package/dist/lib/mcp/tools/index.js +0 -1
  97. package/dist/lib/mcp/tools/index.js.map +1 -1
  98. package/dist/lib/onboarding/detect-tools.d.ts +15 -0
  99. package/dist/lib/onboarding/detect-tools.js +44 -0
  100. package/dist/lib/onboarding/detect-tools.js.map +1 -0
  101. package/dist/lib/onboarding/index.d.ts +2 -0
  102. package/dist/lib/onboarding/index.js +3 -0
  103. package/dist/lib/onboarding/index.js.map +1 -0
  104. package/dist/lib/onboarding/wizard.d.ts +25 -0
  105. package/dist/lib/onboarding/wizard.js +156 -0
  106. package/dist/lib/onboarding/wizard.js.map +1 -0
  107. package/dist/lib/pmo/schema.d.ts +2 -1
  108. package/dist/lib/pmo/schema.js +3 -1
  109. package/dist/lib/pmo/schema.js.map +1 -1
  110. package/dist/lib/runners/claude-code-runner.js +6 -0
  111. package/dist/lib/runners/claude-code-runner.js.map +1 -1
  112. package/dist/lib/tool-registry/detect.d.ts +20 -0
  113. package/dist/lib/tool-registry/detect.js +95 -0
  114. package/dist/lib/tool-registry/detect.js.map +1 -0
  115. package/dist/lib/tool-registry/index.d.ts +10 -0
  116. package/dist/lib/tool-registry/index.js +13 -0
  117. package/dist/lib/tool-registry/index.js.map +1 -0
  118. package/dist/lib/tool-registry/policy.d.ts +32 -0
  119. package/dist/lib/tool-registry/policy.js +97 -0
  120. package/dist/lib/tool-registry/policy.js.map +1 -0
  121. package/dist/lib/tool-registry/registry.d.ts +42 -0
  122. package/dist/lib/tool-registry/registry.js +120 -0
  123. package/dist/lib/tool-registry/registry.js.map +1 -0
  124. package/dist/lib/tool-registry/spawn.d.ts +50 -0
  125. package/dist/lib/tool-registry/spawn.js +103 -0
  126. package/dist/lib/tool-registry/spawn.js.map +1 -0
  127. package/dist/lib/tool-registry/types.d.ts +56 -0
  128. package/dist/lib/tool-registry/types.js +109 -0
  129. package/dist/lib/tool-registry/types.js.map +1 -0
  130. package/dist/lib/trello/client.d.ts +23 -0
  131. package/dist/lib/trello/client.js +114 -0
  132. package/dist/lib/trello/client.js.map +1 -0
  133. package/dist/lib/trello/config.d.ts +55 -0
  134. package/dist/lib/trello/config.js +127 -0
  135. package/dist/lib/trello/config.js.map +1 -0
  136. package/dist/lib/trello/index.d.ts +5 -0
  137. package/dist/lib/trello/index.js +5 -0
  138. package/dist/lib/trello/index.js.map +1 -0
  139. package/dist/lib/trello/mapper.d.ts +13 -0
  140. package/dist/lib/trello/mapper.js +71 -0
  141. package/dist/lib/trello/mapper.js.map +1 -0
  142. package/dist/lib/trello/sync.d.ts +13 -0
  143. package/dist/lib/trello/sync.js +38 -0
  144. package/dist/lib/trello/sync.js.map +1 -0
  145. package/dist/lib/trello/types.d.ts +53 -0
  146. package/dist/lib/trello/types.js +2 -0
  147. package/dist/lib/trello/types.js.map +1 -0
  148. package/dist/lib/work-source/client.js +17 -0
  149. package/dist/lib/work-source/client.js.map +1 -1
  150. package/dist/lib/work-source/config.d.ts +1 -1
  151. package/dist/lib/work-source/config.js +11 -1
  152. package/dist/lib/work-source/config.js.map +1 -1
  153. package/oclif.manifest.json +3460 -3476
  154. package/package.json +6 -2
  155. package/dist/commands/spec/create.d.ts +0 -20
  156. package/dist/commands/spec/create.js +0 -171
  157. package/dist/commands/spec/create.js.map +0 -1
  158. package/dist/commands/spec/delete.js +0 -112
  159. package/dist/commands/spec/delete.js.map +0 -1
  160. package/dist/commands/spec/edit.d.ts +0 -23
  161. package/dist/commands/spec/edit.js +0 -262
  162. package/dist/commands/spec/edit.js.map +0 -1
  163. package/dist/commands/spec/index.js +0 -88
  164. package/dist/commands/spec/index.js.map +0 -1
  165. package/dist/commands/spec/link/depends.js +0 -87
  166. package/dist/commands/spec/link/depends.js.map +0 -1
  167. package/dist/commands/spec/link/index.js +0 -93
  168. package/dist/commands/spec/link/index.js.map +0 -1
  169. package/dist/commands/spec/link/remove.d.ts +0 -18
  170. package/dist/commands/spec/link/remove.js +0 -91
  171. package/dist/commands/spec/link/remove.js.map +0 -1
  172. package/dist/commands/spec/list.d.ts +0 -14
  173. package/dist/commands/spec/list.js +0 -101
  174. package/dist/commands/spec/list.js.map +0 -1
  175. package/dist/commands/spec/plan.js +0 -102
  176. package/dist/commands/spec/plan.js.map +0 -1
  177. package/dist/commands/spec/ticket.js +0 -144
  178. package/dist/commands/spec/ticket.js.map +0 -1
  179. package/dist/commands/spec/view.js +0 -202
  180. package/dist/commands/spec/view.js.map +0 -1
  181. package/dist/lib/mcp/tools/spec.d.ts +0 -6
  182. package/dist/lib/mcp/tools/spec.js +0 -197
  183. package/dist/lib/mcp/tools/spec.js.map +0 -1
  184. package/dist/lib/pmo/spec-parser.d.ts +0 -25
  185. package/dist/lib/pmo/spec-parser.js +0 -206
  186. package/dist/lib/pmo/spec-parser.js.map +0 -1
  187. package/dist/lib/pmo/spec-types.d.ts +0 -43
  188. package/dist/lib/pmo/spec-types.js +0 -8
  189. package/dist/lib/pmo/spec-types.js.map +0 -1
@@ -0,0 +1,682 @@
1
+ /**
2
+ * Dashboard HTML Template
3
+ *
4
+ * Returns a complete self-contained HTML page for the web dashboard.
5
+ * Dark theme, responsive layout with kanban board, agents, sessions, and PRs.
6
+ */
7
+ function escapeHtml(str) {
8
+ return str
9
+ .replace(/&/g, '&')
10
+ .replace(/</g, '&lt;')
11
+ .replace(/>/g, '&gt;')
12
+ .replace(/"/g, '&quot;')
13
+ .replace(/'/g, '&#039;');
14
+ }
15
+ export function getDashboardHTML(port) {
16
+ return `<!DOCTYPE html>
17
+ <html lang="en">
18
+ <head>
19
+ <meta charset="UTF-8">
20
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
21
+ <title>prlt dashboard</title>
22
+ <style>
23
+ * { margin: 0; padding: 0; box-sizing: border-box; }
24
+
25
+ :root {
26
+ --bg: #0d1117;
27
+ --bg-secondary: #161b22;
28
+ --bg-tertiary: #21262d;
29
+ --border: #30363d;
30
+ --text: #e6edf3;
31
+ --text-muted: #8b949e;
32
+ --accent: #58a6ff;
33
+ --green: #3fb950;
34
+ --red: #f85149;
35
+ --yellow: #d29922;
36
+ --orange: #db6d28;
37
+ --purple: #bc8cff;
38
+ --pink: #f778ba;
39
+ }
40
+
41
+ body {
42
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif;
43
+ background: var(--bg);
44
+ color: var(--text);
45
+ line-height: 1.5;
46
+ min-height: 100vh;
47
+ }
48
+
49
+ /* Header */
50
+ .header {
51
+ background: var(--bg-secondary);
52
+ border-bottom: 1px solid var(--border);
53
+ padding: 12px 24px;
54
+ display: flex;
55
+ align-items: center;
56
+ justify-content: space-between;
57
+ position: sticky;
58
+ top: 0;
59
+ z-index: 100;
60
+ }
61
+
62
+ .header-left {
63
+ display: flex;
64
+ align-items: center;
65
+ gap: 12px;
66
+ }
67
+
68
+ .header h1 {
69
+ font-size: 16px;
70
+ font-weight: 600;
71
+ color: var(--text);
72
+ }
73
+
74
+ .header h1 span {
75
+ color: var(--accent);
76
+ }
77
+
78
+ .project-name {
79
+ font-size: 13px;
80
+ color: var(--text-muted);
81
+ padding: 2px 8px;
82
+ background: var(--bg-tertiary);
83
+ border-radius: 4px;
84
+ }
85
+
86
+ .header-right {
87
+ display: flex;
88
+ align-items: center;
89
+ gap: 12px;
90
+ font-size: 12px;
91
+ color: var(--text-muted);
92
+ }
93
+
94
+ .status-dot {
95
+ width: 8px;
96
+ height: 8px;
97
+ border-radius: 50%;
98
+ display: inline-block;
99
+ }
100
+
101
+ .status-dot.connected { background: var(--green); }
102
+ .status-dot.disconnected { background: var(--red); }
103
+
104
+ /* Main layout */
105
+ .main {
106
+ padding: 24px;
107
+ max-width: 1600px;
108
+ margin: 0 auto;
109
+ }
110
+
111
+ .section {
112
+ margin-bottom: 32px;
113
+ }
114
+
115
+ .section-header {
116
+ display: flex;
117
+ align-items: center;
118
+ gap: 8px;
119
+ margin-bottom: 12px;
120
+ font-size: 14px;
121
+ font-weight: 600;
122
+ color: var(--text-muted);
123
+ text-transform: uppercase;
124
+ letter-spacing: 0.5px;
125
+ }
126
+
127
+ .section-header .count {
128
+ font-size: 11px;
129
+ background: var(--bg-tertiary);
130
+ color: var(--text-muted);
131
+ padding: 1px 6px;
132
+ border-radius: 10px;
133
+ font-weight: 500;
134
+ }
135
+
136
+ /* Kanban Board */
137
+ .kanban {
138
+ display: flex;
139
+ gap: 12px;
140
+ overflow-x: auto;
141
+ padding-bottom: 8px;
142
+ }
143
+
144
+ .kanban-column {
145
+ min-width: 240px;
146
+ max-width: 300px;
147
+ flex: 1;
148
+ background: var(--bg-secondary);
149
+ border: 1px solid var(--border);
150
+ border-radius: 8px;
151
+ display: flex;
152
+ flex-direction: column;
153
+ max-height: 500px;
154
+ }
155
+
156
+ .kanban-column-header {
157
+ padding: 10px 12px;
158
+ border-bottom: 1px solid var(--border);
159
+ font-size: 13px;
160
+ font-weight: 600;
161
+ display: flex;
162
+ justify-content: space-between;
163
+ align-items: center;
164
+ flex-shrink: 0;
165
+ }
166
+
167
+ .kanban-column-header .ticket-count {
168
+ font-size: 11px;
169
+ color: var(--text-muted);
170
+ background: var(--bg-tertiary);
171
+ padding: 1px 6px;
172
+ border-radius: 10px;
173
+ }
174
+
175
+ .kanban-column-body {
176
+ padding: 8px;
177
+ overflow-y: auto;
178
+ flex: 1;
179
+ }
180
+
181
+ .ticket-card {
182
+ background: var(--bg-tertiary);
183
+ border: 1px solid var(--border);
184
+ border-radius: 6px;
185
+ padding: 10px 12px;
186
+ margin-bottom: 6px;
187
+ cursor: default;
188
+ transition: border-color 0.15s;
189
+ }
190
+
191
+ .ticket-card:hover {
192
+ border-color: var(--accent);
193
+ }
194
+
195
+ .ticket-id {
196
+ font-size: 11px;
197
+ font-weight: 600;
198
+ color: var(--accent);
199
+ font-family: 'SF Mono', SFMono-Regular, Consolas, monospace;
200
+ }
201
+
202
+ .ticket-title {
203
+ font-size: 13px;
204
+ margin-top: 4px;
205
+ line-height: 1.4;
206
+ }
207
+
208
+ .ticket-meta {
209
+ display: flex;
210
+ gap: 6px;
211
+ margin-top: 6px;
212
+ flex-wrap: wrap;
213
+ }
214
+
215
+ .badge {
216
+ font-size: 10px;
217
+ padding: 1px 6px;
218
+ border-radius: 10px;
219
+ font-weight: 500;
220
+ }
221
+
222
+ .badge-priority { background: #1f1d2e; }
223
+ .badge-priority.P0 { color: var(--red); border: 1px solid var(--red); }
224
+ .badge-priority.P1 { color: var(--orange); border: 1px solid var(--orange); }
225
+ .badge-priority.P2 { color: var(--yellow); border: 1px solid var(--yellow); }
226
+ .badge-priority.P3 { color: var(--text-muted); border: 1px solid var(--border); }
227
+
228
+ .badge-category {
229
+ color: var(--purple);
230
+ border: 1px solid rgba(188, 140, 255, 0.3);
231
+ }
232
+
233
+ .badge-assignee {
234
+ color: var(--pink);
235
+ border: 1px solid rgba(247, 120, 186, 0.3);
236
+ }
237
+
238
+ .badge-label {
239
+ color: var(--text-muted);
240
+ border: 1px solid var(--border);
241
+ }
242
+
243
+ /* Agent Cards */
244
+ .agent-grid {
245
+ display: grid;
246
+ grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
247
+ gap: 12px;
248
+ }
249
+
250
+ .agent-card {
251
+ background: var(--bg-secondary);
252
+ border: 1px solid var(--border);
253
+ border-radius: 8px;
254
+ padding: 14px 16px;
255
+ transition: border-color 0.15s;
256
+ }
257
+
258
+ .agent-card:hover {
259
+ border-color: var(--accent);
260
+ }
261
+
262
+ .agent-card.active {
263
+ border-left: 3px solid var(--green);
264
+ }
265
+
266
+ .agent-card.idle {
267
+ border-left: 3px solid var(--text-muted);
268
+ }
269
+
270
+ .agent-name {
271
+ font-size: 14px;
272
+ font-weight: 600;
273
+ display: flex;
274
+ align-items: center;
275
+ gap: 6px;
276
+ }
277
+
278
+ .agent-status-indicator {
279
+ width: 8px;
280
+ height: 8px;
281
+ border-radius: 50%;
282
+ }
283
+
284
+ .agent-status-indicator.active { background: var(--green); }
285
+ .agent-status-indicator.idle { background: var(--text-muted); }
286
+
287
+ .agent-detail {
288
+ font-size: 12px;
289
+ color: var(--text-muted);
290
+ margin-top: 6px;
291
+ }
292
+
293
+ .agent-tickets {
294
+ display: flex;
295
+ gap: 4px;
296
+ flex-wrap: wrap;
297
+ margin-top: 8px;
298
+ }
299
+
300
+ .agent-ticket-badge {
301
+ font-size: 10px;
302
+ font-family: 'SF Mono', SFMono-Regular, Consolas, monospace;
303
+ color: var(--accent);
304
+ background: rgba(88, 166, 255, 0.1);
305
+ border: 1px solid rgba(88, 166, 255, 0.2);
306
+ padding: 1px 6px;
307
+ border-radius: 4px;
308
+ }
309
+
310
+ /* Sessions Table */
311
+ .table-wrapper {
312
+ background: var(--bg-secondary);
313
+ border: 1px solid var(--border);
314
+ border-radius: 8px;
315
+ overflow: hidden;
316
+ }
317
+
318
+ table {
319
+ width: 100%;
320
+ border-collapse: collapse;
321
+ }
322
+
323
+ th {
324
+ text-align: left;
325
+ padding: 10px 14px;
326
+ font-size: 11px;
327
+ font-weight: 600;
328
+ color: var(--text-muted);
329
+ text-transform: uppercase;
330
+ letter-spacing: 0.5px;
331
+ border-bottom: 1px solid var(--border);
332
+ background: var(--bg-secondary);
333
+ }
334
+
335
+ td {
336
+ padding: 10px 14px;
337
+ font-size: 13px;
338
+ border-bottom: 1px solid var(--border);
339
+ }
340
+
341
+ tr:last-child td {
342
+ border-bottom: none;
343
+ }
344
+
345
+ tr:hover td {
346
+ background: var(--bg-tertiary);
347
+ }
348
+
349
+ .session-id {
350
+ font-family: 'SF Mono', SFMono-Regular, Consolas, monospace;
351
+ font-size: 12px;
352
+ color: var(--text-muted);
353
+ }
354
+
355
+ .status-badge {
356
+ font-size: 11px;
357
+ padding: 2px 8px;
358
+ border-radius: 10px;
359
+ font-weight: 500;
360
+ }
361
+
362
+ .status-badge.running { background: rgba(63, 185, 80, 0.15); color: var(--green); }
363
+ .status-badge.starting { background: rgba(210, 153, 34, 0.15); color: var(--yellow); }
364
+ .status-badge.orphan { background: rgba(219, 109, 40, 0.15); color: var(--orange); }
365
+
366
+ /* PR List */
367
+ .pr-list {
368
+ display: flex;
369
+ flex-direction: column;
370
+ gap: 6px;
371
+ }
372
+
373
+ .pr-item {
374
+ background: var(--bg-secondary);
375
+ border: 1px solid var(--border);
376
+ border-radius: 8px;
377
+ padding: 12px 16px;
378
+ display: flex;
379
+ align-items: center;
380
+ gap: 12px;
381
+ transition: border-color 0.15s;
382
+ }
383
+
384
+ .pr-item:hover {
385
+ border-color: var(--accent);
386
+ }
387
+
388
+ .pr-number {
389
+ font-family: 'SF Mono', SFMono-Regular, Consolas, monospace;
390
+ font-size: 13px;
391
+ color: var(--accent);
392
+ font-weight: 600;
393
+ min-width: 50px;
394
+ }
395
+
396
+ .pr-title {
397
+ flex: 1;
398
+ font-size: 13px;
399
+ }
400
+
401
+ .pr-branch {
402
+ font-size: 11px;
403
+ font-family: 'SF Mono', SFMono-Regular, Consolas, monospace;
404
+ color: var(--text-muted);
405
+ background: var(--bg-tertiary);
406
+ padding: 2px 8px;
407
+ border-radius: 4px;
408
+ max-width: 250px;
409
+ overflow: hidden;
410
+ text-overflow: ellipsis;
411
+ white-space: nowrap;
412
+ }
413
+
414
+ .ci-badge {
415
+ font-size: 11px;
416
+ padding: 2px 8px;
417
+ border-radius: 10px;
418
+ font-weight: 500;
419
+ white-space: nowrap;
420
+ }
421
+
422
+ .ci-badge.success { background: rgba(63, 185, 80, 0.15); color: var(--green); }
423
+ .ci-badge.failure { background: rgba(248, 81, 73, 0.15); color: var(--red); }
424
+ .ci-badge.pending { background: rgba(210, 153, 34, 0.15); color: var(--yellow); }
425
+ .ci-badge.unknown { background: var(--bg-tertiary); color: var(--text-muted); }
426
+
427
+ .draft-badge {
428
+ font-size: 10px;
429
+ padding: 1px 6px;
430
+ border-radius: 10px;
431
+ background: var(--bg-tertiary);
432
+ color: var(--text-muted);
433
+ border: 1px solid var(--border);
434
+ }
435
+
436
+ /* Empty state */
437
+ .empty-state {
438
+ text-align: center;
439
+ padding: 32px;
440
+ color: var(--text-muted);
441
+ font-size: 13px;
442
+ }
443
+
444
+ /* Link styling */
445
+ a {
446
+ color: var(--accent);
447
+ text-decoration: none;
448
+ }
449
+
450
+ a:hover {
451
+ text-decoration: underline;
452
+ }
453
+
454
+ /* Scrollbar */
455
+ ::-webkit-scrollbar { width: 6px; height: 6px; }
456
+ ::-webkit-scrollbar-track { background: transparent; }
457
+ ::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; }
458
+ ::-webkit-scrollbar-thumb:hover { background: var(--text-muted); }
459
+ </style>
460
+ </head>
461
+ <body>
462
+ <div class="header">
463
+ <div class="header-left">
464
+ <h1><span>prlt</span> dashboard</h1>
465
+ <span class="project-name" id="project-name">loading...</span>
466
+ </div>
467
+ <div class="header-right">
468
+ <span class="status-dot disconnected" id="status-dot"></span>
469
+ <span id="status-text">Connecting...</span>
470
+ <span id="last-updated"></span>
471
+ </div>
472
+ </div>
473
+
474
+ <div class="main">
475
+ <!-- Board Section -->
476
+ <div class="section" id="board-section">
477
+ <div class="section-header">Board <span class="count" id="board-count">0</span></div>
478
+ <div class="kanban" id="kanban"></div>
479
+ </div>
480
+
481
+ <!-- Agents Section -->
482
+ <div class="section" id="agents-section">
483
+ <div class="section-header">Agents <span class="count" id="agents-count">0</span></div>
484
+ <div class="agent-grid" id="agents-grid"></div>
485
+ </div>
486
+
487
+ <!-- Sessions Section -->
488
+ <div class="section" id="sessions-section">
489
+ <div class="section-header">Sessions <span class="count" id="sessions-count">0</span></div>
490
+ <div class="table-wrapper">
491
+ <table>
492
+ <thead>
493
+ <tr>
494
+ <th>Session</th>
495
+ <th>Ticket</th>
496
+ <th>Agent</th>
497
+ <th>Environment</th>
498
+ <th>Status</th>
499
+ </tr>
500
+ </thead>
501
+ <tbody id="sessions-body"></tbody>
502
+ </table>
503
+ </div>
504
+ </div>
505
+
506
+ <!-- PRs Section -->
507
+ <div class="section" id="prs-section">
508
+ <div class="section-header">Pull Requests <span class="count" id="prs-count">0</span></div>
509
+ <div class="pr-list" id="pr-list"></div>
510
+ </div>
511
+ </div>
512
+
513
+ <script>
514
+ // Escape helper
515
+ function esc(str) {
516
+ if (!str) return '';
517
+ const d = document.createElement('div');
518
+ d.textContent = str;
519
+ return d.innerHTML;
520
+ }
521
+
522
+ // Render functions
523
+ function renderBoard(board) {
524
+ const kanban = document.getElementById('kanban');
525
+ const cols = board.columns || [];
526
+ let totalTickets = 0;
527
+ cols.forEach(c => totalTickets += (c.tickets || []).length);
528
+ document.getElementById('board-count').textContent = totalTickets;
529
+
530
+ if (cols.length === 0) {
531
+ kanban.innerHTML = '<div class="empty-state">No board data</div>';
532
+ return;
533
+ }
534
+
535
+ kanban.innerHTML = cols.map(col => {
536
+ const tickets = col.tickets || [];
537
+ return '<div class="kanban-column">' +
538
+ '<div class="kanban-column-header">' +
539
+ '<span>' + esc(col.name) + '</span>' +
540
+ '<span class="ticket-count">' + tickets.length + '</span>' +
541
+ '</div>' +
542
+ '<div class="kanban-column-body">' +
543
+ (tickets.length === 0 ? '' : tickets.map(t => {
544
+ let meta = '';
545
+ if (t.priority) meta += '<span class="badge badge-priority ' + esc(t.priority) + '">' + esc(t.priority) + '</span>';
546
+ if (t.category) meta += '<span class="badge badge-category">' + esc(t.category) + '</span>';
547
+ if (t.assignee) meta += '<span class="badge badge-assignee">' + esc(t.assignee) + '</span>';
548
+ (t.labels || []).forEach(l => { meta += '<span class="badge badge-label">' + esc(l) + '</span>'; });
549
+ return '<div class="ticket-card">' +
550
+ '<div class="ticket-id">' + esc(t.id) + '</div>' +
551
+ '<div class="ticket-title">' + esc(t.title) + '</div>' +
552
+ (meta ? '<div class="ticket-meta">' + meta + '</div>' : '') +
553
+ '</div>';
554
+ }).join('')) +
555
+ '</div>' +
556
+ '</div>';
557
+ }).join('');
558
+ }
559
+
560
+ function renderAgents(agents) {
561
+ const grid = document.getElementById('agents-grid');
562
+ document.getElementById('agents-count').textContent = agents.length;
563
+
564
+ if (agents.length === 0) {
565
+ grid.innerHTML = '<div class="empty-state">No agents found</div>';
566
+ return;
567
+ }
568
+
569
+ grid.innerHTML = agents.map(a => {
570
+ const isActive = a.hasActiveSessions;
571
+ const cls = isActive ? 'active' : 'idle';
572
+ let tickets = '';
573
+ if (a.assignedTickets.length > 0) {
574
+ tickets = '<div class="agent-tickets">' +
575
+ a.assignedTickets.map(t => '<span class="agent-ticket-badge">' + esc(t) + '</span>').join('') +
576
+ '</div>';
577
+ }
578
+ return '<div class="agent-card ' + cls + '">' +
579
+ '<div class="agent-name">' +
580
+ '<span class="agent-status-indicator ' + cls + '"></span>' +
581
+ esc(a.name) +
582
+ '</div>' +
583
+ (a.branch ? '<div class="agent-detail">' + esc(a.branch) + '</div>' : '') +
584
+ tickets +
585
+ '</div>';
586
+ }).join('');
587
+ }
588
+
589
+ function renderSessions(sessions) {
590
+ const body = document.getElementById('sessions-body');
591
+ document.getElementById('sessions-count').textContent = sessions.length;
592
+
593
+ if (sessions.length === 0) {
594
+ body.innerHTML = '<tr><td colspan="5" class="empty-state">No active sessions</td></tr>';
595
+ return;
596
+ }
597
+
598
+ body.innerHTML = sessions.map(s => {
599
+ const envIcon = s.environment === 'container' ? '&#x1F433;' : '&#x1F4BB;';
600
+ return '<tr>' +
601
+ '<td class="session-id">' + esc(s.sessionId) + '</td>' +
602
+ '<td>' + esc(s.ticketId) + '</td>' +
603
+ '<td>' + esc(s.agentName) + '</td>' +
604
+ '<td>' + envIcon + ' ' + esc(s.environment) + '</td>' +
605
+ '<td><span class="status-badge ' + esc(s.status) + '">' + esc(s.status) + '</span></td>' +
606
+ '</tr>';
607
+ }).join('');
608
+ }
609
+
610
+ function renderPRs(prs) {
611
+ const list = document.getElementById('pr-list');
612
+ document.getElementById('prs-count').textContent = prs.length;
613
+
614
+ if (prs.length === 0) {
615
+ list.innerHTML = '<div class="empty-state">No open pull requests</div>';
616
+ return;
617
+ }
618
+
619
+ list.innerHTML = prs.map(pr => {
620
+ const ciClass = pr.ciStatus || 'unknown';
621
+ const ciLabel = { success: 'CI passed', failure: 'CI failed', pending: 'CI running', unknown: 'CI unknown' }[ciClass] || 'CI unknown';
622
+ return '<div class="pr-item">' +
623
+ '<span class="pr-number"><a href="' + esc(pr.url) + '" target="_blank">#' + pr.number + '</a></span>' +
624
+ '<span class="pr-title">' + esc(pr.title) + '</span>' +
625
+ (pr.isDraft ? '<span class="draft-badge">draft</span>' : '') +
626
+ '<span class="pr-branch">' + esc(pr.headBranch) + '</span>' +
627
+ '<span class="ci-badge ' + ciClass + '">' + ciLabel + '</span>' +
628
+ '</div>';
629
+ }).join('');
630
+ }
631
+
632
+ function renderAll(data) {
633
+ document.getElementById('project-name').textContent = data.projectName || data.projectId;
634
+ const ts = new Date(data.timestamp);
635
+ document.getElementById('last-updated').textContent = 'Updated ' + ts.toLocaleTimeString();
636
+ renderBoard(data.board);
637
+ renderAgents(data.agents);
638
+ renderSessions(data.sessions);
639
+ renderPRs(data.prs);
640
+ }
641
+
642
+ // Initial fetch
643
+ fetch('/api/data')
644
+ .then(r => r.json())
645
+ .then(data => renderAll(data))
646
+ .catch(err => console.error('Initial fetch failed:', err));
647
+
648
+ // SSE for live updates
649
+ const dot = document.getElementById('status-dot');
650
+ const statusText = document.getElementById('status-text');
651
+
652
+ function connectSSE() {
653
+ const es = new EventSource('/api/events');
654
+
655
+ es.onopen = () => {
656
+ dot.className = 'status-dot connected';
657
+ statusText.textContent = 'Live';
658
+ };
659
+
660
+ es.onmessage = (event) => {
661
+ try {
662
+ const data = JSON.parse(event.data);
663
+ renderAll(data);
664
+ } catch (e) {
665
+ console.error('SSE parse error:', e);
666
+ }
667
+ };
668
+
669
+ es.onerror = () => {
670
+ dot.className = 'status-dot disconnected';
671
+ statusText.textContent = 'Reconnecting...';
672
+ es.close();
673
+ setTimeout(connectSSE, 3000);
674
+ };
675
+ }
676
+
677
+ connectSSE();
678
+ </script>
679
+ </body>
680
+ </html>`;
681
+ }
682
+ //# sourceMappingURL=html.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"html.js","sourceRoot":"","sources":["../../../src/lib/dashboard/html.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,SAAS,UAAU,CAAC,GAAW;IAC7B,OAAO,GAAG;SACP,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC;SACtB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC;SACvB,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAA;AAC5B,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,IAAY;IAC3C,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QAwpBD,CAAA;AACR,CAAC"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Dashboard HTTP Server
3
+ *
4
+ * Serves the dashboard HTML, JSON API, and SSE events.
5
+ * Zero external dependencies — uses Node.js built-in http module.
6
+ */
7
+ import * as http from 'node:http';
8
+ import type { PMOStorage } from '../pmo/types.js';
9
+ export interface DashboardServerOptions {
10
+ port: number;
11
+ storage: PMOStorage;
12
+ projectId: string;
13
+ projectName: string;
14
+ }
15
+ export interface DashboardServer {
16
+ server: http.Server;
17
+ url: string;
18
+ close: () => Promise<void>;
19
+ }
20
+ export declare function createDashboardServer(options: DashboardServerOptions): Promise<DashboardServer>;