@sienklogic/plan-build-run 2.27.0 → 2.27.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/dashboard/public/css/layout.css +7 -283
  3. package/dashboard/public/css/status-colors.css +7 -0
  4. package/dashboard/public/css/tokens.css +3 -3
  5. package/dashboard/public/js/sidebar-toggle.js +9 -31
  6. package/dashboard/public/js/theme-toggle.js +4 -4
  7. package/dashboard/src/views/partials/activity-feed.ejs +17 -9
  8. package/dashboard/src/views/partials/analytics-content.ejs +178 -88
  9. package/dashboard/src/views/partials/audit-detail-content.ejs +6 -4
  10. package/dashboard/src/views/partials/audits-content.ejs +28 -26
  11. package/dashboard/src/views/partials/breadcrumbs.ejs +8 -4
  12. package/dashboard/src/views/partials/config-content.ejs +98 -95
  13. package/dashboard/src/views/partials/dashboard-content.ejs +69 -60
  14. package/dashboard/src/views/partials/dependencies-content.ejs +5 -3
  15. package/dashboard/src/views/partials/empty-state.ejs +10 -5
  16. package/dashboard/src/views/partials/footer.ejs +8 -2
  17. package/dashboard/src/views/partials/head.ejs +2 -1
  18. package/dashboard/src/views/partials/header.ejs +16 -19
  19. package/dashboard/src/views/partials/layout-bottom.ejs +5 -40
  20. package/dashboard/src/views/partials/layout-top.ejs +6 -5
  21. package/dashboard/src/views/partials/logs-content.ejs +26 -29
  22. package/dashboard/src/views/partials/milestone-detail-content.ejs +5 -5
  23. package/dashboard/src/views/partials/milestones-content.ejs +40 -31
  24. package/dashboard/src/views/partials/note-detail-content.ejs +7 -5
  25. package/dashboard/src/views/partials/notes-content.ejs +4 -4
  26. package/dashboard/src/views/partials/phase-content.ejs +6 -8
  27. package/dashboard/src/views/partials/phase-doc-content.ejs +13 -15
  28. package/dashboard/src/views/partials/phase-timeline.ejs +22 -15
  29. package/dashboard/src/views/partials/phases-content.ejs +98 -84
  30. package/dashboard/src/views/partials/quick-content.ejs +34 -32
  31. package/dashboard/src/views/partials/quick-detail-content.ejs +20 -19
  32. package/dashboard/src/views/partials/requirements-content.ejs +6 -6
  33. package/dashboard/src/views/partials/research-content.ejs +14 -14
  34. package/dashboard/src/views/partials/research-detail-content.ejs +13 -11
  35. package/dashboard/src/views/partials/roadmap-content.ejs +145 -128
  36. package/dashboard/src/views/partials/sidebar.ejs +86 -140
  37. package/dashboard/src/views/partials/todo-create-content.ejs +51 -46
  38. package/dashboard/src/views/partials/todo-detail-content.ejs +26 -25
  39. package/dashboard/src/views/partials/todos-content.ejs +65 -62
  40. package/dashboard/src/views/partials/todos-done-content.ejs +33 -31
  41. package/package.json +1 -1
  42. package/plugins/copilot-pbr/plugin.json +1 -1
  43. package/plugins/cursor-pbr/.cursor-plugin/plugin.json +1 -1
  44. package/plugins/pbr/.claude-plugin/plugin.json +1 -1
  45. package/plugins/pbr/scripts/local-llm/metrics.js +121 -33
package/CHANGELOG.md CHANGED
@@ -5,6 +5,13 @@ 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.27.1](https://github.com/SienkLogic/plan-build-run/compare/plan-build-run-v2.27.0...plan-build-run-v2.27.1) (2026-02-24)
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * **tools:** prevent lifetime LLM metrics from plateauing at 200 entries ([2cbdaa4](https://github.com/SienkLogic/plan-build-run/commit/2cbdaa4b4402110cc4bb0f6505d034f2162aa782))
14
+
8
15
  ## [2.27.0](https://github.com/SienkLogic/plan-build-run/compare/plan-build-run-v2.26.2...plan-build-run-v2.27.0) (2026-02-24)
9
16
 
10
17
 
@@ -27,103 +27,6 @@ p { line-height: 1.65; }
27
27
 
28
28
  small { font-size: 0.8125rem; color: var(--color-text-dim); }
29
29
 
30
- /* --- Page Grid Layout --- */
31
- .page-wrapper {
32
- display: grid;
33
- gap: 0;
34
- grid-template-columns: var(--size-sidebar) 1fr;
35
- grid-template-rows: var(--size-header) 1fr auto;
36
- grid-template-areas:
37
- "header header"
38
- "sidebar content"
39
- "footer footer";
40
- min-height: 100vh;
41
- }
42
-
43
- /* --- Header --- */
44
- .page-wrapper > header {
45
- grid-area: header;
46
- display: flex;
47
- align-items: center;
48
- padding: 0 var(--space-xl);
49
- border-bottom: 1px solid var(--color-border);
50
- background: var(--color-surface-raised);
51
- backdrop-filter: blur(8px);
52
- position: sticky;
53
- top: 0;
54
- z-index: 10;
55
- }
56
-
57
- .page-wrapper > header nav {
58
- display: flex;
59
- justify-content: space-between;
60
- align-items: center;
61
- width: 100%;
62
- margin: 0;
63
- padding: 0;
64
- }
65
-
66
- .page-wrapper > header nav strong {
67
- font-size: 1rem;
68
- font-weight: 600;
69
- letter-spacing: -0.02em;
70
- white-space: nowrap;
71
- }
72
-
73
- .page-wrapper > header nav ul {
74
- list-style: none;
75
- margin: 0;
76
- padding: 0;
77
- }
78
-
79
- /* --- Sidebar --- */
80
- .page-wrapper > aside.sidebar {
81
- grid-area: sidebar;
82
- padding: var(--space-lg) 0;
83
- border-right: 1px solid var(--color-border);
84
- background: var(--color-surface-raised);
85
- position: sticky;
86
- top: var(--size-header);
87
- height: calc(100vh - var(--size-header));
88
- overflow-y: auto;
89
- }
90
-
91
- aside.sidebar nav ul {
92
- list-style: none;
93
- padding: 0;
94
- margin: 0;
95
- }
96
-
97
- aside.sidebar nav li {
98
- margin: 0;
99
- padding: 0;
100
- }
101
-
102
- aside.sidebar nav a {
103
- display: flex;
104
- align-items: center;
105
- padding: 0.6rem var(--space-lg);
106
- text-decoration: none;
107
- border-left: 3px solid transparent;
108
- font-size: 0.9rem;
109
- font-weight: 500;
110
- color: var(--color-text-dim);
111
- transition: all var(--transition-fast);
112
- }
113
-
114
- aside.sidebar nav a:hover {
115
- color: var(--pico-color);
116
- background: var(--color-surface-hover);
117
- border-left-color: var(--color-border);
118
- }
119
-
120
- aside.sidebar nav a[aria-current="page"] {
121
- font-weight: 600;
122
- color: var(--color-accent);
123
- border-left-color: var(--color-accent);
124
- background: var(--color-surface-hover);
125
- }
126
-
127
30
  /* --- Sidebar: Current Phase Card --- */
128
31
  .sidebar-current-phase {
129
32
  padding: var(--space-md) var(--space-lg);
@@ -188,59 +91,6 @@ aside.sidebar nav a[aria-current="page"] {
188
91
  white-space: nowrap;
189
92
  }
190
93
 
191
- /* --- Sidebar: Section Details --- */
192
- aside.sidebar details {
193
- border: none;
194
- margin: 0;
195
- padding: 0;
196
- }
197
-
198
- aside.sidebar details + details {
199
- margin-top: var(--space-xs);
200
- }
201
-
202
- aside.sidebar details summary {
203
- text-transform: uppercase;
204
- font-size: 0.6875rem;
205
- font-weight: 600;
206
- letter-spacing: 0.06em;
207
- color: var(--color-text-dim);
208
- padding: var(--space-xs) var(--space-lg);
209
- cursor: pointer;
210
- border-left: 3px solid transparent;
211
- }
212
-
213
- aside.sidebar details[open] summary {
214
- color: var(--color-accent);
215
- border-left-color: var(--color-accent);
216
- }
217
-
218
- aside.sidebar details ul {
219
- margin: 0;
220
- padding-bottom: var(--space-xs);
221
- }
222
-
223
- /* --- Main Content --- */
224
- .page-wrapper > main {
225
- grid-area: content;
226
- padding: var(--space-xl) var(--space-2xl);
227
- overflow-y: auto;
228
- max-width: calc(var(--content-max-width) + var(--space-2xl) * 2);
229
- }
230
-
231
- /* --- Footer --- */
232
- .page-wrapper > footer {
233
- grid-area: footer;
234
- padding: var(--space-md) var(--space-xl);
235
- border-top: 1px solid var(--color-border);
236
- text-align: center;
237
- }
238
-
239
- .page-wrapper > footer small {
240
- font-size: 0.75rem;
241
- color: var(--color-text-dim);
242
- }
243
-
244
94
  /* --- Card Component --- */
245
95
  .card,
246
96
  article {
@@ -423,36 +273,6 @@ details li {
423
273
  margin: var(--space-lg) 0;
424
274
  }
425
275
 
426
- /* --- Breadcrumbs --- */
427
- .breadcrumbs {
428
- display: flex;
429
- list-style: none;
430
- padding: 0;
431
- margin: 0 0 var(--space-md) 0;
432
- font-size: 0.85rem;
433
- gap: var(--space-xs);
434
- }
435
-
436
- .breadcrumbs li + li::before {
437
- content: ">";
438
- margin-right: var(--space-xs);
439
- color: var(--color-text-dim);
440
- }
441
-
442
- .breadcrumbs a {
443
- color: var(--color-accent);
444
- text-decoration: none;
445
- }
446
-
447
- .breadcrumbs a:hover {
448
- text-decoration: underline;
449
- }
450
-
451
- .breadcrumbs [aria-current="page"] {
452
- color: var(--color-text-dim);
453
- font-weight: 500;
454
- }
455
-
456
276
  /* --- Phase Timeline (homepage) --- */
457
277
  .phase-timeline {
458
278
  list-style: none;
@@ -586,8 +406,8 @@ main > p:first-of-type > a[href="/"]:hover {
586
406
  top: 0;
587
407
  z-index: 1000;
588
408
  padding: var(--space-sm) var(--space-md);
589
- background: var(--pico-primary);
590
- color: var(--pico-primary-inverse);
409
+ background: var(--tblr-primary);
410
+ color: var(--tblr-primary-fg, #fff);
591
411
  text-decoration: none;
592
412
  font-weight: 600;
593
413
  }
@@ -600,24 +420,13 @@ main > p:first-of-type > a[href="/"]:hover {
600
420
 
601
421
  /* --- Focus Visible --- */
602
422
  :focus-visible {
603
- outline: 2px solid var(--pico-primary);
423
+ outline: 2px solid var(--tblr-primary);
604
424
  outline-offset: 2px;
605
425
  }
606
426
 
607
- /* --- Empty State --- */
608
- .empty-state {
609
- text-align: center;
610
- padding: var(--space-2xl) var(--space-lg);
611
- }
612
-
613
- .empty-state h3 {
614
- color: var(--color-text-dim);
615
- font-weight: 500;
616
- }
617
-
618
427
  /* --- Error Card --- */
619
428
  .error-card {
620
- border-left: 4px solid var(--pico-del-color);
429
+ border-left: 4px solid var(--tblr-danger);
621
430
  padding: var(--space-lg);
622
431
  margin: var(--space-lg) 0;
623
432
  background: var(--color-surface-raised);
@@ -659,7 +468,7 @@ main > p:first-of-type > a[href="/"]:hover {
659
468
  /* --- Bar Chart --- */
660
469
  .bar-chart-row { display: flex; align-items: center; gap: var(--space-sm); margin-bottom: var(--space-xs); }
661
470
  .bar-chart-label { min-width: 160px; font-size: 0.875rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
662
- .bar-chart-bar { height: 1.5rem; border-radius: var(--radius-sm); background: var(--pico-primary); color: var(--pico-primary-inverse, #fff); font-size: 0.75rem; display: flex; align-items: center; padding-left: var(--space-sm); min-width: 2rem; transition: width var(--transition-base); }
471
+ .bar-chart-bar { height: 1.5rem; border-radius: var(--radius-sm); background: var(--tblr-primary); color: var(--tblr-primary-fg, #fff); font-size: 0.75rem; display: flex; align-items: center; padding-left: var(--space-sm); min-width: 2rem; transition: width var(--transition-base); }
663
472
 
664
473
  /* --- Loading Bar --- */
665
474
  .loading-bar {
@@ -674,7 +483,7 @@ main > p:first-of-type > a[href="/"]:hover {
674
483
 
675
484
  .htmx-request .loading-bar {
676
485
  display: block;
677
- background: var(--pico-primary);
486
+ background: var(--tblr-primary);
678
487
  animation: loading-shimmer 1.2s ease-in-out infinite;
679
488
  }
680
489
 
@@ -683,93 +492,12 @@ main > p:first-of-type > a[href="/"]:hover {
683
492
  100% { transform: translateX(100%); }
684
493
  }
685
494
 
686
- /* --- Sidebar Backdrop (hidden by default, shown on mobile) --- */
687
- .sidebar-backdrop {
688
- display: none;
689
- }
690
-
691
- /* --- Mobile hamburger toggle --- */
692
- .sidebar-toggle {
693
- display: none;
694
- background: none;
695
- border: 1px solid var(--color-border);
696
- border-radius: var(--radius-sm);
697
- color: var(--pico-color);
698
- font-size: 1.25rem;
699
- padding: 0.25rem 0.5rem;
700
- cursor: pointer;
701
- line-height: 1;
702
- }
703
-
704
495
  /* ============================================
705
496
  Responsive Breakpoints
706
497
  ============================================ */
707
498
 
708
- /* Tablet: narrower sidebar */
709
- @media (max-width: 1024px) {
710
- :root {
711
- --size-sidebar: 180px;
712
- }
713
-
714
- .page-wrapper > main {
715
- padding: var(--space-lg) var(--space-xl);
716
- }
717
- }
718
-
719
- /* Mobile: sidebar overlays content */
499
+ /* Mobile: compact typography */
720
500
  @media (max-width: 768px) {
721
- .page-wrapper {
722
- grid-template-columns: 1fr;
723
- grid-template-rows: var(--size-header) 1fr auto;
724
- grid-template-areas:
725
- "header"
726
- "content"
727
- "footer";
728
- }
729
-
730
- .sidebar-toggle {
731
- display: block;
732
- }
733
-
734
- .page-wrapper > aside.sidebar {
735
- position: fixed;
736
- top: var(--size-header);
737
- left: 0;
738
- width: 260px;
739
- height: calc(100vh - var(--size-header));
740
- z-index: 20;
741
- transform: translateX(-100%);
742
- transition: var(--transition-transform);
743
- display: block;
744
- border-right: 1px solid var(--color-border);
745
- border-bottom: none;
746
- padding: var(--space-lg) 0;
747
- overflow-y: auto;
748
- background: var(--pico-background-color, var(--color-dark-surface));
749
- }
750
-
751
- .page-wrapper > aside.sidebar.open {
752
- transform: translateX(0);
753
- box-shadow: 4px 0 24px var(--overlay-bg);
754
- }
755
-
756
- .sidebar-backdrop {
757
- display: none;
758
- position: fixed;
759
- inset: 0;
760
- top: var(--size-header);
761
- background: var(--overlay-bg-heavy);
762
- z-index: 19;
763
- }
764
-
765
- .sidebar-backdrop.open {
766
- display: block;
767
- }
768
-
769
- .page-wrapper > main {
770
- padding: var(--space-lg) var(--space-md);
771
- }
772
-
773
501
  h1 { font-size: 1.4rem; }
774
502
  h2 { font-size: 1.15rem; }
775
503
 
@@ -810,10 +538,6 @@ main > p:first-of-type > a[href="/"]:hover {
810
538
  font-size: 14px;
811
539
  }
812
540
 
813
- .page-wrapper > main {
814
- padding: var(--space-md);
815
- }
816
-
817
541
  article > header {
818
542
  padding: var(--space-sm) var(--space-md);
819
543
  }
@@ -97,6 +97,13 @@
97
97
  border-color: rgba(129, 140, 248, 0.2);
98
98
  }
99
99
 
100
+ /* Tabler .badge compatibility shim */
101
+ .badge.status-badge {
102
+ font-size: var(--badge-font-size-base);
103
+ font-weight: 600;
104
+ letter-spacing: 0.02em;
105
+ }
106
+
100
107
  /* Size variants */
101
108
  .status-badge--sm {
102
109
  padding: var(--badge-padding-sm);
@@ -13,7 +13,7 @@
13
13
  --color-border: rgba(0, 0, 0, 0.1);
14
14
  --color-text-dim: rgba(0, 0, 0, 0.5);
15
15
  --color-text: #1a1a2e;
16
- --color-accent: var(--pico-primary);
16
+ --color-accent: var(--tblr-primary, #0054a6);
17
17
 
18
18
  /* Spacing */
19
19
  --space-xs: 0.25rem;
@@ -73,7 +73,7 @@
73
73
  }
74
74
 
75
75
  /* --- Dark Mode (explicit attribute) --- */
76
- [data-theme="dark"] {
76
+ [data-bs-theme="dark"] {
77
77
  --color-surface: #13131a;
78
78
  --color-surface-raised: rgba(255, 255, 255, 0.03);
79
79
  --color-surface-hover: rgba(255, 255, 255, 0.06);
@@ -84,7 +84,7 @@
84
84
 
85
85
  /* --- Dark Mode (system preference, unless light forced) --- */
86
86
  @media (prefers-color-scheme: dark) {
87
- :root:not([data-theme="light"]) {
87
+ :root:not([data-bs-theme="light"]) {
88
88
  --color-surface: #13131a;
89
89
  --color-surface-raised: rgba(255, 255, 255, 0.03);
90
90
  --color-surface-hover: rgba(255, 255, 255, 0.06);
@@ -1,34 +1,12 @@
1
- // Mobile sidebar toggle with backdrop overlay
1
+ // sidebar-toggle.js Tabler vertical navbar toggle shim
2
+ // Tabler's Bootstrap JS handles collapse via data-bs-toggle="collapse".
3
+ // This file remains for the header button (#sidebar-toggle) which triggers
4
+ // the .navbar-vertical collapse on mobile.
2
5
  document.addEventListener('DOMContentLoaded', function() {
3
- var toggle = document.querySelector('.sidebar-toggle');
4
- var sidebar = document.querySelector('.sidebar');
5
- if (!toggle || !sidebar) return;
6
-
7
- // Create backdrop element
8
- var backdrop = document.createElement('div');
9
- backdrop.className = 'sidebar-backdrop';
10
- document.body.appendChild(backdrop);
11
-
12
- function closeSidebar() {
13
- sidebar.classList.remove('open');
14
- backdrop.classList.remove('open');
15
- }
16
-
17
- function toggleSidebar() {
18
- sidebar.classList.toggle('open');
19
- backdrop.classList.toggle('open');
20
- }
21
-
22
- toggle.addEventListener('click', toggleSidebar);
23
- backdrop.addEventListener('click', closeSidebar);
24
-
25
- // Close sidebar when a nav link is clicked (mobile)
26
- var navLinks = sidebar.querySelectorAll('a');
27
- navLinks.forEach(function(link) {
28
- link.addEventListener('click', function() {
29
- if (window.innerWidth <= 768) {
30
- closeSidebar();
31
- }
32
- });
6
+ var toggle = document.getElementById('sidebar-toggle');
7
+ var sidebarMenu = document.getElementById('sidebar-menu');
8
+ if (!toggle || !sidebarMenu) return;
9
+ toggle.addEventListener('click', function() {
10
+ sidebarMenu.classList.toggle('show');
33
11
  });
34
12
  });
@@ -1,11 +1,11 @@
1
- /* theme-toggle.js — Toggle light/dark theme via data-theme attribute + localStorage */
1
+ /* theme-toggle.js — Toggle light/dark theme via data-bs-theme attribute + localStorage */
2
2
  (function () {
3
3
  'use strict';
4
4
 
5
5
  var STORAGE_KEY = 'pbr-theme';
6
6
 
7
7
  function getEffectiveTheme() {
8
- var explicit = document.documentElement.dataset.theme;
8
+ var explicit = document.documentElement.dataset.bsTheme;
9
9
  if (explicit === 'light' || explicit === 'dark') return explicit;
10
10
  return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
11
11
  }
@@ -23,7 +23,7 @@
23
23
  // Apply stored theme (also done in layout-top inline script for flash prevention)
24
24
  var stored = localStorage.getItem(STORAGE_KEY);
25
25
  if (stored) {
26
- document.documentElement.dataset.theme = stored;
26
+ document.documentElement.dataset.bsTheme = stored;
27
27
  }
28
28
 
29
29
  updateIcon(btn, getEffectiveTheme());
@@ -31,7 +31,7 @@
31
31
  btn.addEventListener('click', function () {
32
32
  var current = getEffectiveTheme();
33
33
  var next = current === 'dark' ? 'light' : 'dark';
34
- document.documentElement.dataset.theme = next;
34
+ document.documentElement.dataset.bsTheme = next;
35
35
  localStorage.setItem(STORAGE_KEY, next);
36
36
  updateIcon(btn, next);
37
37
  });
@@ -1,19 +1,27 @@
1
1
  <% if (!recentActivity || recentActivity.length === 0) { %>
2
- <p class="muted">No recent activity.</p>
2
+ <%- include('empty-state', { title: 'No recent activity' }) %>
3
3
  <% } else { %>
4
- <ul class="activity-feed">
4
+ <div class="list-group list-group-flush">
5
5
  <% recentActivity.forEach(function(item) { %>
6
6
  <%
7
7
  // Format path: strip .planning/ prefix for brevity
8
- var displayPath = item.path.replace(/^\.planning\//, '');
8
+ var displayPath = (item.path || item.description || String(item)).replace(/^\.planning\//, '');
9
9
  // Format timestamp: show just date+time, no timezone
10
- var ts = item.timestamp || '';
10
+ var ts = item.timestamp || item.time || item.date || '';
11
11
  var dateOnly = ts.replace(/\s+[-+]\d{4}$/, '').replace(/:\d{2}$/, '');
12
12
  %>
13
- <li class="activity-item">
14
- <span class="activity-path"><%= displayPath %></span>
15
- <time class="activity-time" datetime="<%= ts %>"><%= dateOnly %></time>
16
- </li>
13
+ <div class="list-group-item">
14
+ <div class="row align-items-center">
15
+ <div class="col text-truncate">
16
+ <span class="text-truncate small"><%= displayPath %></span>
17
+ </div>
18
+ <% if (dateOnly) { %>
19
+ <div class="col-auto">
20
+ <time class="text-muted small" datetime="<%= ts %>"><%= dateOnly %></time>
21
+ </div>
22
+ <% } %>
23
+ </div>
24
+ </div>
17
25
  <% }); %>
18
- </ul>
26
+ </div>
19
27
  <% } %>