@sienklogic/plan-build-run 2.27.2 → 2.29.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 (33) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dashboard/bin/cli.js +3 -3
  3. package/dashboard/package.json +8 -5
  4. package/dashboard/public/css/command-center.css +382 -0
  5. package/dashboard/public/css/layout.css +481 -254
  6. package/dashboard/public/css/tokens.css +70 -61
  7. package/dashboard/public/js/sse-client.js +52 -82
  8. package/dashboard/public/js/theme-toggle.js +19 -14
  9. package/dashboard/src/components/Layout.tsx +99 -0
  10. package/dashboard/src/components/partials/ActivityStream.tsx +58 -0
  11. package/dashboard/src/components/partials/AttentionPanel.tsx +48 -0
  12. package/dashboard/src/components/partials/CurrentPhaseCard.tsx +50 -0
  13. package/dashboard/src/components/partials/PhaseTimeline.tsx +38 -0
  14. package/dashboard/src/components/partials/ProgressRing.tsx +51 -0
  15. package/dashboard/src/components/partials/QuickActions.tsx +30 -0
  16. package/dashboard/src/components/partials/StatusHeader.tsx +45 -0
  17. package/dashboard/src/global.d.ts +10 -0
  18. package/dashboard/src/index.tsx +134 -0
  19. package/dashboard/src/middleware/current-phase.ts +23 -0
  20. package/dashboard/src/routes/command-center.routes.tsx +69 -0
  21. package/dashboard/src/routes/index.routes.tsx +106 -0
  22. package/dashboard/src/services/dashboard.service.d.ts +39 -0
  23. package/dashboard/src/services/sse.service.ts +34 -0
  24. package/dashboard/src/services/todo.service.d.ts +21 -0
  25. package/dashboard/src/services/watcher.service.ts +35 -0
  26. package/dashboard/src/sse-handler.tsx +37 -0
  27. package/dashboard/src/watcher-setup.ts +25 -0
  28. package/package.json +1 -1
  29. package/plugins/copilot-pbr/plugin.json +1 -1
  30. package/plugins/cursor-pbr/.cursor-plugin/plugin.json +1 -1
  31. package/plugins/pbr/.claude-plugin/plugin.json +1 -1
  32. package/dashboard/src/services/sse.service.js +0 -58
  33. package/dashboard/src/services/watcher.service.js +0 -48
package/CHANGELOG.md CHANGED
@@ -5,6 +5,20 @@ All notable changes to Plan-Build-Run will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [2.29.0](https://github.com/SienkLogic/plan-build-run/compare/plan-build-run-v2.28.0...plan-build-run-v2.29.0) (2026-02-24)
9
+
10
+
11
+ ### Features
12
+
13
+ * **39-01:** add Command Center view with live-updating dashboard components ([7520c7f](https://github.com/SienkLogic/plan-build-run/commit/7520c7fd592beae5739cfd96aa73fd354047896d))
14
+
15
+ ## [2.28.0](https://github.com/SienkLogic/plan-build-run/compare/plan-build-run-v2.27.2...plan-build-run-v2.28.0) (2026-02-24)
16
+
17
+
18
+ ### Features
19
+
20
+ * **38-01:** rebuild dashboard foundation with Hono + JSX + Open Props ([56596f6](https://github.com/SienkLogic/plan-build-run/commit/56596f6fe25e59d3051838bad2c8894a25754a3b))
21
+
8
22
  ## [2.27.2](https://github.com/SienkLogic/plan-build-run/compare/plan-build-run-v2.27.1...plan-build-run-v2.27.2) (2026-02-24)
9
23
 
10
24
 
@@ -2,15 +2,15 @@
2
2
 
3
3
  import { Command } from 'commander';
4
4
  import { resolve } from 'path';
5
- import { startServer } from '../src/server.js';
5
+ import { startServer } from '../src/index.tsx';
6
6
 
7
7
  const program = new Command();
8
8
 
9
9
  program
10
10
  .name('pbr-dashboard')
11
11
  .description('Start the Plan-Build-Run planning dashboard')
12
- .option('-d, --dir <path>', 'Path to Plan-Build-Run project directory', process.cwd())
13
- .option('-p, --port <number>', 'Server port', '3000')
12
+ .option('--dir <path>', 'Path to Plan-Build-Run project directory', process.cwd())
13
+ .option('--port <number>', 'Server port', '3000')
14
14
  .parse();
15
15
 
16
16
  const options = program.opts();
@@ -7,8 +7,8 @@
7
7
  "pbr-dashboard": "./bin/cli.js"
8
8
  },
9
9
  "scripts": {
10
- "start": "node bin/cli.js",
11
- "dev": "node --watch bin/cli.js",
10
+ "start": "tsx bin/cli.js",
11
+ "dev": "tsx watch bin/cli.js",
12
12
  "test": "vitest run",
13
13
  "test:coverage": "vitest run --coverage"
14
14
  },
@@ -19,19 +19,22 @@
19
19
  ],
20
20
  "license": "MIT",
21
21
  "dependencies": {
22
+ "@hono/node-server": "^1.14",
22
23
  "chokidar": "^5.0.0",
23
24
  "commander": "^12.1.0",
24
- "ejs": "^3.1.10",
25
- "express": "^5.1.0",
26
25
  "gray-matter": "^4.0.3",
27
- "helmet": "^8.1.0",
26
+ "hono": "^4.7",
28
27
  "marked": "^17.0.1",
29
28
  "sanitize-html": "^2.13.0"
30
29
  },
31
30
  "devDependencies": {
31
+ "@types/node": "^22",
32
32
  "@vitest/coverage-v8": "^4.0.18",
33
33
  "memfs": "^4.56.10",
34
34
  "supertest": "^7.2.2",
35
+ "tsx": "^4.19",
36
+ "typed-htmx": "^0.3",
37
+ "typescript": "^5.7",
35
38
  "vitest": "^4.0.18"
36
39
  }
37
40
  }
@@ -0,0 +1,382 @@
1
+ /* ============================================
2
+ PBR Dashboard — Command Center View
3
+ Styles for progress ring, phase timeline,
4
+ attention panel, activity stream.
5
+ All values use tokens from tokens.css.
6
+ ============================================ */
7
+
8
+ /* --- Command Center Layout --- */
9
+ .command-center {
10
+ display: flex;
11
+ flex-direction: column;
12
+ gap: var(--space-lg);
13
+ padding: var(--space-lg);
14
+ max-width: 1200px;
15
+ }
16
+
17
+ .command-center__grid {
18
+ display: grid;
19
+ grid-template-columns: 1fr 1fr;
20
+ gap: var(--space-md);
21
+ }
22
+
23
+ @media (max-width: 768px) {
24
+ .command-center__grid {
25
+ grid-template-columns: 1fr;
26
+ }
27
+ }
28
+
29
+ /* --- Status Header --- */
30
+ .status-header {
31
+ display: flex;
32
+ align-items: center;
33
+ gap: var(--space-md);
34
+ flex-wrap: wrap;
35
+ }
36
+
37
+ .status-header__project {
38
+ font-size: 1.5rem;
39
+ font-weight: 700;
40
+ letter-spacing: -0.02em;
41
+ color: var(--color-text);
42
+ }
43
+
44
+ .status-header__phase {
45
+ font-size: 1rem;
46
+ color: var(--color-text-dim);
47
+ }
48
+
49
+ /* --- Milestone Progress Bar --- */
50
+ .milestone-bar {
51
+ width: 100%;
52
+ margin-top: var(--space-sm);
53
+ }
54
+
55
+ .milestone-bar__track {
56
+ height: 8px;
57
+ border-radius: var(--radius-sm);
58
+ background: var(--color-border);
59
+ overflow: hidden;
60
+ }
61
+
62
+ .milestone-bar__fill {
63
+ height: 100%;
64
+ border-radius: var(--radius-sm);
65
+ background: var(--color-accent);
66
+ transition: width var(--transition-base);
67
+ }
68
+
69
+ .milestone-bar__label {
70
+ font-size: 0.8rem;
71
+ color: var(--color-text-muted);
72
+ margin-top: var(--space-xs);
73
+ }
74
+
75
+ /* --- Progress Ring --- */
76
+ .progress-ring-wrapper {
77
+ display: flex;
78
+ align-items: center;
79
+ justify-content: center;
80
+ padding: var(--space-sm);
81
+ }
82
+
83
+ .progress-ring__svg {
84
+ transform: rotate(-90deg);
85
+ }
86
+
87
+ .progress-ring__bg {
88
+ stroke: var(--color-border);
89
+ fill: none;
90
+ }
91
+
92
+ .progress-ring__fg {
93
+ stroke: var(--color-accent);
94
+ fill: none;
95
+ stroke-linecap: round;
96
+ transition: stroke-dashoffset var(--transition-base);
97
+ }
98
+
99
+ .progress-ring__text {
100
+ transform: rotate(90deg);
101
+ font-size: 1.25rem;
102
+ font-weight: 700;
103
+ fill: var(--color-text);
104
+ text-anchor: middle;
105
+ dominant-baseline: middle;
106
+ }
107
+
108
+ /* --- Current Phase Card --- */
109
+ .current-phase-card {
110
+ grid-column: 1 / -1;
111
+ }
112
+
113
+ .card {
114
+ background: var(--card-bg);
115
+ border: 1px solid var(--card-border);
116
+ border-radius: var(--card-radius);
117
+ box-shadow: var(--card-shadow);
118
+ padding: var(--card-padding);
119
+ }
120
+
121
+ .card__header {
122
+ font-size: 0.75rem;
123
+ font-weight: 600;
124
+ text-transform: uppercase;
125
+ letter-spacing: 0.08em;
126
+ color: var(--color-text-muted);
127
+ padding-bottom: var(--space-sm);
128
+ border-bottom: 1px solid var(--color-border);
129
+ margin-bottom: var(--space-md);
130
+ }
131
+
132
+ .card__title {
133
+ font-size: 1.25rem;
134
+ font-weight: 600;
135
+ margin: 0 0 var(--space-sm) 0;
136
+ }
137
+
138
+ .card__meta {
139
+ font-size: 0.85rem;
140
+ color: var(--color-text-dim);
141
+ margin: var(--space-xs) 0;
142
+ }
143
+
144
+ /* --- Next Action --- */
145
+ .next-action {
146
+ display: flex;
147
+ align-items: center;
148
+ gap: var(--space-sm);
149
+ margin-top: var(--space-md);
150
+ padding: var(--space-sm) var(--space-md);
151
+ background: var(--color-surface);
152
+ border: 1px solid var(--color-border);
153
+ border-radius: var(--radius-sm);
154
+ }
155
+
156
+ .next-action__label {
157
+ font-size: 0.75rem;
158
+ color: var(--color-text-muted);
159
+ text-transform: uppercase;
160
+ letter-spacing: 0.06em;
161
+ }
162
+
163
+ .next-action__cmd {
164
+ font-family: var(--font-mono);
165
+ font-size: 0.9rem;
166
+ color: var(--color-accent);
167
+ flex: 1;
168
+ }
169
+
170
+ /* --- Attention Panel --- */
171
+ .attention-panel__header {
172
+ color: var(--orange-7, #c2510f);
173
+ }
174
+
175
+ .attention-panel__list {
176
+ list-style: none;
177
+ padding: 0;
178
+ margin: 0;
179
+ display: flex;
180
+ flex-direction: column;
181
+ gap: var(--space-xs);
182
+ }
183
+
184
+ .attention-panel__list li {
185
+ font-size: 0.9rem;
186
+ padding: var(--space-xs) 0;
187
+ border-bottom: 1px solid var(--color-border);
188
+ }
189
+
190
+ .attention-panel__list li:last-child {
191
+ border-bottom: none;
192
+ }
193
+
194
+ .attention-panel__clear {
195
+ font-size: 0.9rem;
196
+ color: var(--color-text-muted);
197
+ font-style: italic;
198
+ }
199
+
200
+ /* --- Phase Timeline --- */
201
+ .phase-timeline {
202
+ grid-column: 1 / -1;
203
+ }
204
+
205
+ .phase-timeline__label {
206
+ font-size: 0.75rem;
207
+ font-weight: 600;
208
+ text-transform: uppercase;
209
+ letter-spacing: 0.08em;
210
+ color: var(--color-text-muted);
211
+ margin-bottom: var(--space-sm);
212
+ }
213
+
214
+ .phase-timeline__strip {
215
+ display: flex;
216
+ flex-wrap: wrap;
217
+ gap: 4px;
218
+ }
219
+
220
+ .phase-block {
221
+ display: inline-flex;
222
+ align-items: center;
223
+ justify-content: center;
224
+ width: 32px;
225
+ height: 32px;
226
+ border-radius: var(--radius-sm);
227
+ font-size: 0.7rem;
228
+ font-weight: 600;
229
+ text-decoration: none;
230
+ cursor: pointer;
231
+ transition: transform var(--transition-fast), box-shadow var(--transition-fast);
232
+ }
233
+
234
+ .phase-block:hover {
235
+ transform: translateY(-2px);
236
+ box-shadow: var(--shadow-md);
237
+ }
238
+
239
+ .phase-block--complete {
240
+ background: var(--green-6, #2da44e);
241
+ color: #fff;
242
+ }
243
+
244
+ .phase-block--in-progress {
245
+ background: var(--blue-6, #0969da);
246
+ color: #fff;
247
+ }
248
+
249
+ .phase-block--not-started {
250
+ background: var(--color-border);
251
+ color: var(--color-text-dim);
252
+ }
253
+
254
+ .phase-block--current {
255
+ outline: 3px solid var(--color-accent);
256
+ outline-offset: 2px;
257
+ }
258
+
259
+ /* --- Activity Stream --- */
260
+ .activity-stream__list {
261
+ list-style: none;
262
+ padding: 0;
263
+ margin: 0;
264
+ display: flex;
265
+ flex-direction: column;
266
+ gap: 0;
267
+ }
268
+
269
+ .activity-item {
270
+ display: flex;
271
+ align-items: baseline;
272
+ gap: var(--space-sm);
273
+ padding: var(--space-xs) 0;
274
+ border-bottom: 1px solid var(--color-border);
275
+ font-size: 0.85rem;
276
+ }
277
+
278
+ .activity-item:last-child {
279
+ border-bottom: none;
280
+ }
281
+
282
+ .activity-item__icon {
283
+ color: var(--color-text-muted);
284
+ flex-shrink: 0;
285
+ }
286
+
287
+ .activity-item__path {
288
+ font-family: var(--font-mono);
289
+ font-size: 0.8rem;
290
+ color: var(--color-text-dim);
291
+ flex: 1;
292
+ overflow: hidden;
293
+ text-overflow: ellipsis;
294
+ white-space: nowrap;
295
+ }
296
+
297
+ .activity-item__time {
298
+ font-size: 0.75rem;
299
+ color: var(--color-text-muted);
300
+ flex-shrink: 0;
301
+ }
302
+
303
+ .activity-stream__empty {
304
+ font-size: 0.9rem;
305
+ color: var(--color-text-muted);
306
+ font-style: italic;
307
+ }
308
+
309
+ /* --- Quick Actions --- */
310
+ .quick-actions {
311
+ display: flex;
312
+ flex-direction: column;
313
+ gap: var(--space-sm);
314
+ }
315
+
316
+ .quick-actions__label {
317
+ font-size: 0.75rem;
318
+ font-weight: 600;
319
+ text-transform: uppercase;
320
+ letter-spacing: 0.08em;
321
+ color: var(--color-text-muted);
322
+ }
323
+
324
+ .quick-actions__buttons {
325
+ display: flex;
326
+ flex-wrap: wrap;
327
+ gap: var(--space-sm);
328
+ }
329
+
330
+ /* --- Buttons --- */
331
+ .btn {
332
+ display: inline-flex;
333
+ align-items: center;
334
+ justify-content: center;
335
+ padding: var(--space-xs) var(--space-md);
336
+ border-radius: var(--radius-sm);
337
+ font-size: 0.875rem;
338
+ font-weight: 500;
339
+ text-decoration: none;
340
+ cursor: pointer;
341
+ border: 1px solid transparent;
342
+ transition: background var(--transition-fast), color var(--transition-fast), border-color var(--transition-fast);
343
+ }
344
+
345
+ .btn--primary {
346
+ background: var(--color-accent);
347
+ color: #fff;
348
+ border-color: var(--color-accent);
349
+ }
350
+
351
+ .btn--primary:hover {
352
+ background: var(--color-accent-hover);
353
+ border-color: var(--color-accent-hover);
354
+ color: #fff;
355
+ text-decoration: none;
356
+ }
357
+
358
+ .btn--secondary {
359
+ background: transparent;
360
+ color: var(--color-text);
361
+ border-color: var(--color-border);
362
+ }
363
+
364
+ .btn--secondary:hover {
365
+ background: var(--color-surface-hover);
366
+ text-decoration: none;
367
+ }
368
+
369
+ .btn--ghost {
370
+ background: transparent;
371
+ color: var(--color-text-dim);
372
+ border-color: transparent;
373
+ }
374
+
375
+ .btn--ghost:hover {
376
+ background: var(--color-surface-hover);
377
+ }
378
+
379
+ .btn--sm {
380
+ padding: 2px var(--space-sm);
381
+ font-size: 0.75rem;
382
+ }