@sienklogic/plan-build-run 2.32.0 → 2.32.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.
- package/CHANGELOG.md +7 -0
- package/dashboard/src/index.tsx +8 -2
- package/package.json +2 -2
- package/plugins/copilot-pbr/plugin.json +1 -1
- package/plugins/cursor-pbr/.cursor-plugin/plugin.json +1 -1
- package/plugins/pbr/.claude-plugin/plugin.json +1 -1
- package/dashboard/src/app.js +0 -91
- package/dashboard/src/middleware/current-phase.js +0 -25
- package/dashboard/src/middleware/errorHandler.js +0 -62
- package/dashboard/src/middleware/notFoundHandler.js +0 -9
- package/dashboard/src/routes/events.routes.js +0 -94
- package/dashboard/src/routes/index.routes.js +0 -35
- package/dashboard/src/routes/pages.routes.js +0 -853
- package/dashboard/src/views/analytics.ejs +0 -5
- package/dashboard/src/views/audit-detail.ejs +0 -5
- package/dashboard/src/views/audits.ejs +0 -5
- package/dashboard/src/views/config.ejs +0 -5
- package/dashboard/src/views/dependencies.ejs +0 -5
- package/dashboard/src/views/error.ejs +0 -20
- package/dashboard/src/views/index.ejs +0 -5
- package/dashboard/src/views/logs.ejs +0 -3
- package/dashboard/src/views/milestone-detail.ejs +0 -5
- package/dashboard/src/views/milestones.ejs +0 -5
- package/dashboard/src/views/note-detail.ejs +0 -3
- package/dashboard/src/views/notes.ejs +0 -5
- package/dashboard/src/views/partials/activity-feed.ejs +0 -27
- package/dashboard/src/views/partials/analytics-content.ejs +0 -241
- package/dashboard/src/views/partials/audit-detail-content.ejs +0 -14
- package/dashboard/src/views/partials/audits-content.ejs +0 -36
- package/dashboard/src/views/partials/breadcrumbs.ejs +0 -18
- package/dashboard/src/views/partials/config-content.ejs +0 -219
- package/dashboard/src/views/partials/dashboard-content.ejs +0 -124
- package/dashboard/src/views/partials/dependencies-content.ejs +0 -50
- package/dashboard/src/views/partials/empty-state.ejs +0 -12
- package/dashboard/src/views/partials/footer.ejs +0 -9
- package/dashboard/src/views/partials/head.ejs +0 -31
- package/dashboard/src/views/partials/header.ejs +0 -18
- package/dashboard/src/views/partials/layout-bottom.ejs +0 -8
- package/dashboard/src/views/partials/layout-top.ejs +0 -17
- package/dashboard/src/views/partials/log-entries-content.ejs +0 -17
- package/dashboard/src/views/partials/logs-content.ejs +0 -131
- package/dashboard/src/views/partials/milestone-detail-content.ejs +0 -20
- package/dashboard/src/views/partials/milestones-content.ejs +0 -127
- package/dashboard/src/views/partials/note-detail-content.ejs +0 -24
- package/dashboard/src/views/partials/notes-content.ejs +0 -28
- package/dashboard/src/views/partials/phase-content.ejs +0 -226
- package/dashboard/src/views/partials/phase-doc-content.ejs +0 -36
- package/dashboard/src/views/partials/phase-timeline.ejs +0 -27
- package/dashboard/src/views/partials/phases-content.ejs +0 -137
- package/dashboard/src/views/partials/quick-content.ejs +0 -42
- package/dashboard/src/views/partials/quick-detail-content.ejs +0 -30
- package/dashboard/src/views/partials/requirements-content.ejs +0 -44
- package/dashboard/src/views/partials/research-content.ejs +0 -56
- package/dashboard/src/views/partials/research-detail-content.ejs +0 -25
- package/dashboard/src/views/partials/roadmap-content.ejs +0 -197
- package/dashboard/src/views/partials/sidebar.ejs +0 -98
- package/dashboard/src/views/partials/todo-create-content.ejs +0 -59
- package/dashboard/src/views/partials/todo-detail-content.ejs +0 -43
- package/dashboard/src/views/partials/todos-content.ejs +0 -110
- package/dashboard/src/views/partials/todos-done-content.ejs +0 -46
- package/dashboard/src/views/phase-detail.ejs +0 -5
- package/dashboard/src/views/phase-doc.ejs +0 -5
- package/dashboard/src/views/phases.ejs +0 -5
- package/dashboard/src/views/quick-detail.ejs +0 -5
- package/dashboard/src/views/quick.ejs +0 -5
- package/dashboard/src/views/requirements.ejs +0 -3
- package/dashboard/src/views/research-detail.ejs +0 -3
- package/dashboard/src/views/research.ejs +0 -3
- package/dashboard/src/views/roadmap.ejs +0 -5
- package/dashboard/src/views/todo-create.ejs +0 -5
- package/dashboard/src/views/todo-detail.ejs +0 -5
- package/dashboard/src/views/todos-done.ejs +0 -3
- package/dashboard/src/views/todos.ejs +0 -5
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
<%- include('breadcrumbs', { breadcrumbs: typeof breadcrumbs !== 'undefined' ? breadcrumbs : [] }) %>
|
|
2
|
-
<%
|
|
3
|
-
// Show just the phase name as h1, not the full focus description
|
|
4
|
-
var dashboardTitle = (typeof currentPhase !== 'undefined' && currentPhase && currentPhase.name)
|
|
5
|
-
? currentPhase.name
|
|
6
|
-
: (typeof projectName !== 'undefined' ? projectName.split(/\s*[—–-]\s*/)[0] : 'Dashboard');
|
|
7
|
-
%>
|
|
8
|
-
<h1><%= dashboardTitle %></h1>
|
|
9
|
-
|
|
10
|
-
<!-- Status Cards -->
|
|
11
|
-
<div class="row row-cards mb-3">
|
|
12
|
-
<!-- Current Phase Card -->
|
|
13
|
-
<div class="col-sm-6 col-lg-3">
|
|
14
|
-
<div class="card">
|
|
15
|
-
<div class="card-header"><strong>Current Phase</strong></div>
|
|
16
|
-
<div class="card-body">
|
|
17
|
-
<% if (currentPhase.id > 0) { %>
|
|
18
|
-
<a href="/phases/<%= String(currentPhase.id).padStart(2, '0') %>"
|
|
19
|
-
hx-get="/phases/<%= String(currentPhase.id).padStart(2, '0') %>"
|
|
20
|
-
hx-target="#main-content"
|
|
21
|
-
hx-push-url="true">
|
|
22
|
-
<span class="status-badge" data-status="<%= currentPhase.status %>">Phase <%= currentPhase.id %></span>
|
|
23
|
-
<%= currentPhase.name %>
|
|
24
|
-
</a>
|
|
25
|
-
<% } else { %>
|
|
26
|
-
<p>No active phase</p>
|
|
27
|
-
<% } %>
|
|
28
|
-
</div>
|
|
29
|
-
<div class="card-footer"><small class="text-muted"><%= currentPhase.planStatus %></small></div>
|
|
30
|
-
</div>
|
|
31
|
-
</div>
|
|
32
|
-
|
|
33
|
-
<!-- Milestone Progress Card -->
|
|
34
|
-
<div class="col-sm-6 col-lg-3">
|
|
35
|
-
<div class="card">
|
|
36
|
-
<div class="card-header"><strong>Milestone Progress</strong></div>
|
|
37
|
-
<div class="card-body">
|
|
38
|
-
<div class="progress mb-2" style="height:0.5rem">
|
|
39
|
-
<div class="progress-bar" role="progressbar" style="width:<%= progress %>%" aria-valuenow="<%= progress %>" aria-valuemin="0" aria-valuemax="100"></div>
|
|
40
|
-
</div>
|
|
41
|
-
<span class="text-muted small"><%= progress %>%</span>
|
|
42
|
-
</div>
|
|
43
|
-
<div class="card-footer">
|
|
44
|
-
<small class="text-muted">
|
|
45
|
-
<% if (phases && phases.length > 0) { %>
|
|
46
|
-
<%= phases.filter(function(p) { return p.status === 'complete'; }).length %> of <%= phases.length %> phases complete
|
|
47
|
-
<% } else { %>—<% } %>
|
|
48
|
-
</small>
|
|
49
|
-
</div>
|
|
50
|
-
</div>
|
|
51
|
-
</div>
|
|
52
|
-
|
|
53
|
-
<!-- Pending Todos Card -->
|
|
54
|
-
<div class="col-sm-6 col-lg-3">
|
|
55
|
-
<div class="card">
|
|
56
|
-
<div class="card-header"><strong>Pending Todos</strong></div>
|
|
57
|
-
<div class="card-body">
|
|
58
|
-
<a href="/todos"
|
|
59
|
-
hx-get="/todos"
|
|
60
|
-
hx-target="#main-content"
|
|
61
|
-
hx-push-url="true"
|
|
62
|
-
class="h2 mb-0"><%= typeof pendingTodoCount !== 'undefined' ? pendingTodoCount : 0 %></a>
|
|
63
|
-
</div>
|
|
64
|
-
<div class="card-footer"><small class="text-muted">P0/P1 items need attention</small></div>
|
|
65
|
-
</div>
|
|
66
|
-
</div>
|
|
67
|
-
|
|
68
|
-
<!-- Last Activity Card -->
|
|
69
|
-
<div class="col-sm-6 col-lg-3">
|
|
70
|
-
<div class="card">
|
|
71
|
-
<div class="card-header"><strong>Last Activity</strong></div>
|
|
72
|
-
<div class="card-body">
|
|
73
|
-
<% if (lastActivity.date) { %>
|
|
74
|
-
<p class="mb-0"><%= lastActivity.date %></p>
|
|
75
|
-
<% } else { %>
|
|
76
|
-
<p class="mb-0">—</p>
|
|
77
|
-
<% } %>
|
|
78
|
-
</div>
|
|
79
|
-
<% if (lastActivity.description) { %><div class="card-footer"><small class="text-muted"><%= lastActivity.description %></small></div><% } %>
|
|
80
|
-
</div>
|
|
81
|
-
</div>
|
|
82
|
-
</div>
|
|
83
|
-
|
|
84
|
-
<!-- Phase Timeline -->
|
|
85
|
-
<section class="section-timeline">
|
|
86
|
-
<h2>Phase Timeline</h2>
|
|
87
|
-
<%- include('phase-timeline', { phases: phases, currentPhase: currentPhase }) %>
|
|
88
|
-
</section>
|
|
89
|
-
|
|
90
|
-
<!-- Activity Feed -->
|
|
91
|
-
<section class="section-activity">
|
|
92
|
-
<h2>Recent Activity</h2>
|
|
93
|
-
<%- include('activity-feed', { recentActivity: typeof recentActivity !== 'undefined' ? recentActivity : [] }) %>
|
|
94
|
-
</section>
|
|
95
|
-
|
|
96
|
-
<!-- Quick Actions -->
|
|
97
|
-
<div class="d-flex flex-wrap gap-2 mb-3">
|
|
98
|
-
<% if (typeof quickActions !== 'undefined' && quickActions && quickActions.length > 0) { %>
|
|
99
|
-
<% quickActions.forEach(function(action) { %>
|
|
100
|
-
<a class="btn <%= action.primary ? 'btn-primary' : 'btn-outline-secondary' %>"
|
|
101
|
-
href="<%= action.href %>"
|
|
102
|
-
hx-get="<%= action.href %>"
|
|
103
|
-
hx-target="#main-content"
|
|
104
|
-
hx-push-url="true"><%= action.label %></a>
|
|
105
|
-
<% }); %>
|
|
106
|
-
<% } else { %>
|
|
107
|
-
<% var phaseId = String(currentPhase.id).padStart(2, '0'); %>
|
|
108
|
-
<a class="btn btn-outline-secondary"
|
|
109
|
-
href="/phases/<%= phaseId %>"
|
|
110
|
-
hx-get="/phases/<%= phaseId %>"
|
|
111
|
-
hx-target="#main-content"
|
|
112
|
-
hx-push-url="true">Current Phase</a>
|
|
113
|
-
<a class="btn btn-outline-secondary"
|
|
114
|
-
href="/roadmap"
|
|
115
|
-
hx-get="/roadmap"
|
|
116
|
-
hx-target="#main-content"
|
|
117
|
-
hx-push-url="true">Roadmap</a>
|
|
118
|
-
<a class="btn btn-outline-secondary"
|
|
119
|
-
href="/todos/new"
|
|
120
|
-
hx-get="/todos/new"
|
|
121
|
-
hx-target="#main-content"
|
|
122
|
-
hx-push-url="true">Create Todo</a>
|
|
123
|
-
<% } %>
|
|
124
|
-
</div>
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
<%- include('breadcrumbs', { breadcrumbs: typeof breadcrumbs !== 'undefined' ? breadcrumbs : [] }) %>
|
|
2
|
-
|
|
3
|
-
<h1>Phase Dependencies</h1>
|
|
4
|
-
|
|
5
|
-
<% if (typeof mermaidCode !== 'undefined' && mermaidCode && mermaidCode.trim()) { %>
|
|
6
|
-
<div class="card mb-3">
|
|
7
|
-
<div class="card-body">
|
|
8
|
-
<pre class="mermaid"><%= mermaidCode %></pre>
|
|
9
|
-
</div>
|
|
10
|
-
</div>
|
|
11
|
-
<% } else { %>
|
|
12
|
-
<%- include('empty-state', { icon: '🔗', title: 'No dependency data available', action: '' }) %>
|
|
13
|
-
<% } %>
|
|
14
|
-
|
|
15
|
-
<script src="https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.min.js"></script>
|
|
16
|
-
<script>
|
|
17
|
-
(function() {
|
|
18
|
-
function detectDark() {
|
|
19
|
-
var explicit = document.documentElement.dataset.theme;
|
|
20
|
-
if (explicit) return explicit === 'dark';
|
|
21
|
-
return window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
|
|
22
|
-
}
|
|
23
|
-
var mermaidTheme = detectDark() ? 'dark' : 'default';
|
|
24
|
-
|
|
25
|
-
// Store original source before mermaid processes it
|
|
26
|
-
document.querySelectorAll('.mermaid').forEach(function(el) {
|
|
27
|
-
el.dataset.source = el.textContent;
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
mermaid.initialize({ startOnLoad: true, theme: mermaidTheme, securityLevel: 'loose' });
|
|
31
|
-
|
|
32
|
-
// Watch for theme changes and re-render
|
|
33
|
-
var observer = new MutationObserver(function(mutations) {
|
|
34
|
-
mutations.forEach(function(mutation) {
|
|
35
|
-
if (mutation.attributeName === 'data-theme') {
|
|
36
|
-
var newTheme = detectDark() ? 'dark' : 'default';
|
|
37
|
-
mermaid.initialize({ startOnLoad: false, theme: newTheme, securityLevel: 'loose' });
|
|
38
|
-
document.querySelectorAll('.mermaid').forEach(function(el) {
|
|
39
|
-
if (el.dataset.source) {
|
|
40
|
-
el.removeAttribute('data-processed');
|
|
41
|
-
el.innerHTML = el.dataset.source;
|
|
42
|
-
}
|
|
43
|
-
});
|
|
44
|
-
mermaid.run();
|
|
45
|
-
}
|
|
46
|
-
});
|
|
47
|
-
});
|
|
48
|
-
observer.observe(document.documentElement, { attributes: true, attributeFilter: ['data-theme'] });
|
|
49
|
-
})();
|
|
50
|
-
</script>
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
<div class="empty">
|
|
2
|
-
<% if (typeof icon !== 'undefined' && icon) { %>
|
|
3
|
-
<div class="empty-icon"><%= icon %></div>
|
|
4
|
-
<% } %>
|
|
5
|
-
<p class="empty-title"><%= typeof title !== 'undefined' ? title : 'No data available' %></p>
|
|
6
|
-
<% if (typeof subtitle !== 'undefined' && subtitle) { %>
|
|
7
|
-
<p class="empty-subtitle text-muted"><%= subtitle %></p>
|
|
8
|
-
<% } %>
|
|
9
|
-
<% if (typeof action !== 'undefined' && action) { %>
|
|
10
|
-
<div class="empty-action"><%- action %></div>
|
|
11
|
-
<% } %>
|
|
12
|
-
</div>
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
<footer class="footer footer-transparent d-print-none">
|
|
2
|
-
<div class="container-xl">
|
|
3
|
-
<div class="row text-center align-items-center flex-row-reverse">
|
|
4
|
-
<div class="col-12 col-lg-auto">
|
|
5
|
-
<small class="text-muted">PBR Dashboard v<%= typeof dashboardVersion !== 'undefined' ? dashboardVersion : '0.1.0' %></small>
|
|
6
|
-
</div>
|
|
7
|
-
</div>
|
|
8
|
-
</div>
|
|
9
|
-
</footer>
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
<head>
|
|
2
|
-
<meta charset="UTF-8">
|
|
3
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
4
|
-
<title><%= typeof title !== 'undefined' ? title : 'Plan-Build-Run' %> - Plan-Build-Run</title>
|
|
5
|
-
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='80'>P</text></svg>">
|
|
6
|
-
<link rel="preconnect" href="https://cdn.jsdelivr.net" crossorigin>
|
|
7
|
-
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@tabler/core@1.0.0-beta21/dist/css/tabler.min.css">
|
|
8
|
-
<link rel="stylesheet" href="https://cdn.jsdelivr.net/fontsource/fonts/inter@latest/latin-400-normal.min.css" media="print" onload="this.media='all'">
|
|
9
|
-
<link rel="stylesheet" href="https://cdn.jsdelivr.net/fontsource/fonts/inter@latest/latin-600-normal.min.css" media="print" onload="this.media='all'">
|
|
10
|
-
<link rel="stylesheet" href="https://cdn.jsdelivr.net/fontsource/fonts/inter@latest/latin-700-normal.min.css" media="print" onload="this.media='all'">
|
|
11
|
-
<link rel="stylesheet" href="https://cdn.jsdelivr.net/fontsource/fonts/jetbrains-mono@latest/latin-400-normal.min.css" media="print" onload="this.media='all'">
|
|
12
|
-
<noscript>
|
|
13
|
-
<link rel="stylesheet" href="https://cdn.jsdelivr.net/fontsource/fonts/inter@latest/latin-400-normal.min.css">
|
|
14
|
-
<link rel="stylesheet" href="https://cdn.jsdelivr.net/fontsource/fonts/inter@latest/latin-600-normal.min.css">
|
|
15
|
-
<link rel="stylesheet" href="https://cdn.jsdelivr.net/fontsource/fonts/inter@latest/latin-700-normal.min.css">
|
|
16
|
-
<link rel="stylesheet" href="https://cdn.jsdelivr.net/fontsource/fonts/jetbrains-mono@latest/latin-400-normal.min.css">
|
|
17
|
-
</noscript>
|
|
18
|
-
<link rel="stylesheet" href="/css/tokens.css">
|
|
19
|
-
<link rel="stylesheet" href="/css/layout.css">
|
|
20
|
-
<link rel="stylesheet" href="/css/status-colors.css">
|
|
21
|
-
<script
|
|
22
|
-
src="https://cdn.jsdelivr.net/npm/htmx.org@2.0.8/dist/htmx.min.js"
|
|
23
|
-
integrity="sha384-/TgkGk7p307TH7EXJDuUlgG3Ce1UVolAOFopFekQkkXihi5u/6OCvVKyz1W+idaz"
|
|
24
|
-
crossorigin="anonymous">
|
|
25
|
-
</script>
|
|
26
|
-
<script src="https://cdn.jsdelivr.net/npm/htmx-ext-sse@2.2.2/sse.js"></script>
|
|
27
|
-
<script src="/js/htmx-title.js"></script>
|
|
28
|
-
<script src="/js/sidebar-toggle.js"></script>
|
|
29
|
-
<script src="/js/theme-toggle.js"></script>
|
|
30
|
-
<script src="https://cdn.jsdelivr.net/npm/@tabler/core@1.0.0-beta21/dist/js/tabler.min.js" defer></script>
|
|
31
|
-
</head>
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
<header class="navbar navbar-expand-md navbar-dark d-print-none">
|
|
2
|
-
<div class="container-xl">
|
|
3
|
-
<button class="navbar-toggler" type="button" id="sidebar-toggle" aria-label="Toggle navigation">
|
|
4
|
-
<span class="navbar-toggler-icon"></span>
|
|
5
|
-
</button>
|
|
6
|
-
<div class="navbar-brand navbar-brand-autodark d-none-navbar-horizontal pe-0 pe-md-3">
|
|
7
|
-
<a href="/" hx-get="/" hx-target="#main-content" hx-push-url="true">PBR Dashboard</a>
|
|
8
|
-
</div>
|
|
9
|
-
<div class="navbar-nav flex-row order-md-last">
|
|
10
|
-
<div class="nav-item me-2 d-flex align-items-center">
|
|
11
|
-
<span id="sse-status" data-connected="false" aria-label="SSE connection status" title="Live updates: disconnected"></span>
|
|
12
|
-
</div>
|
|
13
|
-
<div class="nav-item">
|
|
14
|
-
<button id="theme-toggle" class="btn btn-ghost-secondary btn-icon" aria-label="Toggle theme">🌙</button>
|
|
15
|
-
</div>
|
|
16
|
-
</div>
|
|
17
|
-
</div>
|
|
18
|
-
</header>
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<script>(function(){var t=localStorage.getItem('pbr-theme');if(t)document.documentElement.dataset.bsTheme=t})()</script>
|
|
4
|
-
<%- include('head', { title: typeof title !== 'undefined' ? title : 'PBR Dashboard' }) %>
|
|
5
|
-
<body class="antialiased">
|
|
6
|
-
<a href="#main-content" class="skip-link">Skip to main content</a>
|
|
7
|
-
<div class="loading-bar"></div>
|
|
8
|
-
<script>
|
|
9
|
-
document.body.addEventListener('htmx:beforeRequest', function() { document.body.classList.add('htmx-request'); });
|
|
10
|
-
document.body.addEventListener('htmx:afterRequest', function() { document.body.classList.remove('htmx-request'); });
|
|
11
|
-
</script>
|
|
12
|
-
<div class="wrapper">
|
|
13
|
-
<%- include('header') %>
|
|
14
|
-
<%- include('sidebar', { activePage: typeof activePage !== 'undefined' ? activePage : '' }) %>
|
|
15
|
-
<div class="page-wrapper">
|
|
16
|
-
<div class="page-body">
|
|
17
|
-
<div class="container-xl" id="main-content">
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
<div id="log-entries"
|
|
2
|
-
style="max-height:60vh;overflow-y:auto;font-family:var(--font-mono,monospace);font-size:0.78rem;background:var(--card-bg);border:1px solid var(--card-border);border-radius:var(--card-radius);padding:0.5rem">
|
|
3
|
-
<% if (typeof logData !== 'undefined' && logData && logData.entries.length > 0) { %>
|
|
4
|
-
<% logData.entries.forEach(function(entry) { %>
|
|
5
|
-
<div class="log-entry" style="padding:0.2rem 0;border-bottom:1px solid var(--card-border);word-break:break-all">
|
|
6
|
-
<span style="color:var(--muted-fg,#888)"><%= entry.timestamp ? String(entry.timestamp).slice(0,19).replace('T',' ') : '' %></span>
|
|
7
|
-
<% if (entry.type) { %><span class="status-badge status-badge--sm" data-status="<%= entry.type %>"><%= entry.type %></span><% } %>
|
|
8
|
-
<% if (entry.tool) { %><strong><%= entry.tool %></strong><% } %>
|
|
9
|
-
<% if (entry.message) { %><span><%= String(entry.message).slice(0,200) %></span><% } %>
|
|
10
|
-
</div>
|
|
11
|
-
<% }); %>
|
|
12
|
-
<% } else if (typeof logData !== 'undefined' && logData) { %>
|
|
13
|
-
<p style="padding:0.5rem;margin:0;color:var(--muted-fg,#888)">No entries match the current filter.</p>
|
|
14
|
-
<% } else { %>
|
|
15
|
-
<p style="padding:0.5rem;margin:0;color:var(--muted-fg,#888)">Waiting for live entries...</p>
|
|
16
|
-
<% } %>
|
|
17
|
-
</div>
|
|
@@ -1,131 +0,0 @@
|
|
|
1
|
-
<%- include('breadcrumbs', { breadcrumbs: typeof breadcrumbs !== 'undefined' ? breadcrumbs : [] }) %>
|
|
2
|
-
<h1>Logs</h1>
|
|
3
|
-
|
|
4
|
-
<% if (!logFiles || logFiles.length === 0) { %>
|
|
5
|
-
<%- include('empty-state', { icon: 'L', title: 'No log files found', action: 'Log files appear in .planning/logs/ when PBR hooks run with local LLM routing enabled.' }) %>
|
|
6
|
-
<% } else { %>
|
|
7
|
-
|
|
8
|
-
<div class="d-flex gap-3 align-items-start">
|
|
9
|
-
|
|
10
|
-
<%# File browser sidebar %>
|
|
11
|
-
<aside style="min-width:220px;max-width:260px;flex-shrink:0">
|
|
12
|
-
<div class="card">
|
|
13
|
-
<div class="card-header"><strong>Log Files</strong></div>
|
|
14
|
-
<div class="list-group list-group-flush">
|
|
15
|
-
<% logFiles.forEach(function(lf) { %>
|
|
16
|
-
<a href="/logs?file=<%= lf.name %>"
|
|
17
|
-
hx-get="/logs?file=<%= lf.name %>"
|
|
18
|
-
hx-target="#main-content"
|
|
19
|
-
hx-push-url="true"
|
|
20
|
-
class="list-group-item list-group-item-action<% if (selectedFile === lf.name) { %> active<% } %>">
|
|
21
|
-
<%= lf.name %>
|
|
22
|
-
<br><small class="text-muted"><%= Math.round(lf.size / 1024) %> KB · <%= lf.modified ? lf.modified.slice(0,10) : '' %></small>
|
|
23
|
-
</a>
|
|
24
|
-
<% }); %>
|
|
25
|
-
</div>
|
|
26
|
-
</div>
|
|
27
|
-
</aside>
|
|
28
|
-
|
|
29
|
-
<%# Main log viewer %>
|
|
30
|
-
<div style="flex:1;min-width:0">
|
|
31
|
-
<% if (selectedFile) { %>
|
|
32
|
-
|
|
33
|
-
<%# Filter controls %>
|
|
34
|
-
<form hx-get="/logs"
|
|
35
|
-
hx-target="#main-content"
|
|
36
|
-
hx-push-url="true"
|
|
37
|
-
class="d-flex flex-wrap gap-2 mb-2">
|
|
38
|
-
<input type="hidden" name="file" value="<%= selectedFile %>">
|
|
39
|
-
<input type="text" name="type" placeholder="Type filter (e.g. tool_use)"
|
|
40
|
-
value="<%= typeof filters !== 'undefined' ? filters.type : '' %>"
|
|
41
|
-
class="form-control" style="width:200px">
|
|
42
|
-
<input type="search" name="q" placeholder="Search entries..."
|
|
43
|
-
value="<%= typeof filters !== 'undefined' ? filters.q : '' %>"
|
|
44
|
-
class="form-control flex-grow-1" style="min-width:160px">
|
|
45
|
-
<button type="submit" class="btn btn-outline-secondary">Filter</button>
|
|
46
|
-
<% if (typeof filters !== 'undefined' && (filters.type || filters.q)) { %>
|
|
47
|
-
<a href="/logs?file=<%= selectedFile %>"
|
|
48
|
-
hx-get="/logs?file=<%= selectedFile %>"
|
|
49
|
-
hx-target="#main-content"
|
|
50
|
-
hx-push-url="true"
|
|
51
|
-
class="btn btn-outline-secondary">Clear</a>
|
|
52
|
-
<% } %>
|
|
53
|
-
</form>
|
|
54
|
-
|
|
55
|
-
<%# Auto-scroll toggle + live indicator %>
|
|
56
|
-
<div class="d-flex align-items-center gap-3 mb-2">
|
|
57
|
-
<label class="d-flex align-items-center gap-2" style="cursor:pointer">
|
|
58
|
-
<input type="checkbox" id="auto-scroll-toggle" checked style="margin:0">
|
|
59
|
-
<small>Auto-scroll</small>
|
|
60
|
-
</label>
|
|
61
|
-
<span id="live-indicator" class="status-badge" data-status="active" style="display:none">LIVE</span>
|
|
62
|
-
</div>
|
|
63
|
-
|
|
64
|
-
<%# Entry list: SSE target + initial content %>
|
|
65
|
-
<div id="log-entries-wrap"
|
|
66
|
-
hx-ext="sse"
|
|
67
|
-
sse-connect="/logs/stream?file=<%= selectedFile %>"
|
|
68
|
-
sse-swap="log-entry"
|
|
69
|
-
hx-swap="beforeend"
|
|
70
|
-
hx-target="#log-entries"
|
|
71
|
-
style="position:relative">
|
|
72
|
-
<%- include('log-entries-content') %>
|
|
73
|
-
</div>
|
|
74
|
-
|
|
75
|
-
<%# Pagination %>
|
|
76
|
-
<% if (logData && logData.total > logData.pageSize) { %>
|
|
77
|
-
<nav class="d-flex gap-2 mt-2 align-items-center">
|
|
78
|
-
<% const totalPages = Math.ceil(logData.total / logData.pageSize); %>
|
|
79
|
-
<% if (logData.page > 1) { %>
|
|
80
|
-
<a href="/logs?file=<%= selectedFile %>&page=<%= logData.page - 1 %>&type=<%= (filters || {}).type || '' %>&q=<%= (filters || {}).q || '' %>"
|
|
81
|
-
hx-get="/logs?file=<%= selectedFile %>&page=<%= logData.page - 1 %>&type=<%= (filters || {}).type || '' %>&q=<%= (filters || {}).q || '' %>"
|
|
82
|
-
hx-target="#main-content"
|
|
83
|
-
hx-push-url="true"
|
|
84
|
-
class="btn btn-outline-secondary">← Prev</a>
|
|
85
|
-
<% } %>
|
|
86
|
-
<small>Page <%= logData.page %> of <%= totalPages %> (<%= logData.total %> entries)</small>
|
|
87
|
-
<% if (logData.page < totalPages) { %>
|
|
88
|
-
<a href="/logs?file=<%= selectedFile %>&page=<%= logData.page + 1 %>&type=<%= (filters || {}).type || '' %>&q=<%= (filters || {}).q || '' %>"
|
|
89
|
-
hx-get="/logs?file=<%= selectedFile %>&page=<%= logData.page + 1 %>&type=<%= (filters || {}).type || '' %>&q=<%= (filters || {}).q || '' %>"
|
|
90
|
-
hx-target="#main-content"
|
|
91
|
-
hx-push-url="true"
|
|
92
|
-
class="btn btn-outline-secondary">Next →</a>
|
|
93
|
-
<% } %>
|
|
94
|
-
</nav>
|
|
95
|
-
<% } %>
|
|
96
|
-
|
|
97
|
-
<% } else { %>
|
|
98
|
-
<div class="text-center py-4 text-muted">
|
|
99
|
-
<p style="font-size:1.5rem;margin-bottom:0.5rem">←</p>
|
|
100
|
-
<p>Select a log file from the list to view entries.</p>
|
|
101
|
-
</div>
|
|
102
|
-
<% } %>
|
|
103
|
-
</div>
|
|
104
|
-
</div>
|
|
105
|
-
|
|
106
|
-
<script>
|
|
107
|
-
(function () {
|
|
108
|
-
const wrap = document.getElementById('log-entries-wrap');
|
|
109
|
-
const toggle = document.getElementById('auto-scroll-toggle');
|
|
110
|
-
const indicator = document.getElementById('live-indicator');
|
|
111
|
-
|
|
112
|
-
if (!wrap || !toggle) return;
|
|
113
|
-
|
|
114
|
-
// Show live indicator when SSE connects
|
|
115
|
-
wrap.addEventListener('htmx:sseOpen', () => {
|
|
116
|
-
if (indicator) indicator.style.display = '';
|
|
117
|
-
});
|
|
118
|
-
wrap.addEventListener('htmx:sseClose', () => {
|
|
119
|
-
if (indicator) indicator.style.display = 'none';
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
// Auto-scroll: when a new log-entry SSE event adds content, scroll to bottom
|
|
123
|
-
wrap.addEventListener('htmx:afterSwap', () => {
|
|
124
|
-
if (toggle && toggle.checked) {
|
|
125
|
-
const list = document.getElementById('log-entries');
|
|
126
|
-
if (list) list.scrollTop = list.scrollHeight;
|
|
127
|
-
}
|
|
128
|
-
});
|
|
129
|
-
})();
|
|
130
|
-
</script>
|
|
131
|
-
<% } %>
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
<%- include('breadcrumbs', { breadcrumbs: typeof breadcrumbs !== 'undefined' ? breadcrumbs : [] }) %>
|
|
2
|
-
<h1>Milestone v<%= version %></h1>
|
|
3
|
-
|
|
4
|
-
<p>
|
|
5
|
-
<a href="/milestones"
|
|
6
|
-
hx-get="/milestones"
|
|
7
|
-
hx-target="#main-content"
|
|
8
|
-
hx-push-url="true">← Back to Milestones</a>
|
|
9
|
-
</p>
|
|
10
|
-
|
|
11
|
-
<% sections.forEach(function(section) { %>
|
|
12
|
-
<div class="card mb-3">
|
|
13
|
-
<div class="card-header">
|
|
14
|
-
<strong><%= section.type %></strong>
|
|
15
|
-
</div>
|
|
16
|
-
<div class="card-body markdown-body">
|
|
17
|
-
<%- section.html %>
|
|
18
|
-
</div>
|
|
19
|
-
</div>
|
|
20
|
-
<% }); %>
|
|
@@ -1,127 +0,0 @@
|
|
|
1
|
-
<%- include('breadcrumbs', { breadcrumbs: typeof breadcrumbs !== 'undefined' ? breadcrumbs : [] }) %>
|
|
2
|
-
<h1>Milestones</h1>
|
|
3
|
-
|
|
4
|
-
<%
|
|
5
|
-
// Filter out milestones that the parser already flagged as completed (collapsed in ROADMAP.md)
|
|
6
|
-
var nonCollapsed = active.filter(function(m) { return !m.completed; });
|
|
7
|
-
|
|
8
|
-
// From non-collapsed, separate truly active from ones whose phases are all complete
|
|
9
|
-
var trulyActive = nonCollapsed.filter(function(m) {
|
|
10
|
-
var msPhases = (typeof phases !== 'undefined' && phases) ? phases.filter(function(p) { return p.id >= m.startPhase && p.id <= m.endPhase; }) : [];
|
|
11
|
-
var completedPhases = msPhases.filter(function(p) { return p.status === 'complete'; }).length;
|
|
12
|
-
return msPhases.length === 0 || completedPhases < msPhases.length;
|
|
13
|
-
});
|
|
14
|
-
var completedNotArchived = nonCollapsed.filter(function(m) {
|
|
15
|
-
var msPhases = (typeof phases !== 'undefined' && phases) ? phases.filter(function(p) { return p.id >= m.startPhase && p.id <= m.endPhase; }) : [];
|
|
16
|
-
var completedPhases = msPhases.filter(function(p) { return p.status === 'complete'; }).length;
|
|
17
|
-
return msPhases.length > 0 && completedPhases >= msPhases.length;
|
|
18
|
-
});
|
|
19
|
-
%>
|
|
20
|
-
|
|
21
|
-
<% if (trulyActive.length > 0) { %>
|
|
22
|
-
<h2>Active</h2>
|
|
23
|
-
<% trulyActive.forEach(function(m) {
|
|
24
|
-
var msPhases = (typeof phases !== 'undefined' && phases) ? phases.filter(function(p) { return p.id >= m.startPhase && p.id <= m.endPhase; }) : [];
|
|
25
|
-
var totalPhases = msPhases.length;
|
|
26
|
-
var completedPhases = msPhases.filter(function(p) { return p.status === 'complete'; }).length;
|
|
27
|
-
%>
|
|
28
|
-
<div class="card mb-3">
|
|
29
|
-
<div class="card-header">
|
|
30
|
-
<strong><%= m.name %></strong>
|
|
31
|
-
<% if (m.goal) { %><small> — <%= m.goal %></small><% } %>
|
|
32
|
-
</div>
|
|
33
|
-
<div class="card-body">
|
|
34
|
-
<p>
|
|
35
|
-
Phases <%= m.startPhase %> – <%= m.endPhase %>
|
|
36
|
-
</p>
|
|
37
|
-
<% if (totalPhases > 0) { %>
|
|
38
|
-
<div class="progress mb-2" style="height:0.5rem">
|
|
39
|
-
<div class="progress-bar" role="progressbar"
|
|
40
|
-
style="width:<%= completedPhases / totalPhases * 100 %>%"
|
|
41
|
-
aria-valuenow="<%= completedPhases %>" aria-valuemin="0" aria-valuemax="<%= totalPhases %>">
|
|
42
|
-
</div>
|
|
43
|
-
</div>
|
|
44
|
-
<p><small><%= completedPhases %> of <%= totalPhases %> phases complete</small></p>
|
|
45
|
-
<% } %>
|
|
46
|
-
</div>
|
|
47
|
-
</div>
|
|
48
|
-
<% }); %>
|
|
49
|
-
<% } %>
|
|
50
|
-
|
|
51
|
-
<% if (completedNotArchived.length > 0) { %>
|
|
52
|
-
<h2>Completed (Pending Archive)</h2>
|
|
53
|
-
<% completedNotArchived.forEach(function(m) { %>
|
|
54
|
-
<div class="card mb-3">
|
|
55
|
-
<div class="card-header">
|
|
56
|
-
<strong><%= m.name %></strong>
|
|
57
|
-
<span class="status-badge status-badge--sm" data-status="complete" style="float:right">completed</span>
|
|
58
|
-
</div>
|
|
59
|
-
<div class="card-body">
|
|
60
|
-
<p>Phases <%= m.startPhase %> – <%= m.endPhase %></p>
|
|
61
|
-
</div>
|
|
62
|
-
</div>
|
|
63
|
-
<% }); %>
|
|
64
|
-
<% } %>
|
|
65
|
-
|
|
66
|
-
<% if (archived.length > 0) { %>
|
|
67
|
-
<h2>Archived</h2>
|
|
68
|
-
<div class="card mb-3">
|
|
69
|
-
<div class="table-responsive">
|
|
70
|
-
<table class="table table-vcenter">
|
|
71
|
-
<thead>
|
|
72
|
-
<tr>
|
|
73
|
-
<th scope="col">Version</th>
|
|
74
|
-
<th scope="col">Name</th>
|
|
75
|
-
<th scope="col">Date</th>
|
|
76
|
-
<th scope="col">Duration</th>
|
|
77
|
-
<th scope="col">Phases</th>
|
|
78
|
-
<th scope="col">Commits</th>
|
|
79
|
-
</tr>
|
|
80
|
-
</thead>
|
|
81
|
-
<tbody>
|
|
82
|
-
<% archived.forEach(function(m) { %>
|
|
83
|
-
<tr>
|
|
84
|
-
<td colspan="6" style="padding:0">
|
|
85
|
-
<details>
|
|
86
|
-
<summary style="padding:0.5rem 1rem;cursor:pointer">
|
|
87
|
-
<div class="row g-2" style="width:100%">
|
|
88
|
-
<div class="col-auto" style="min-width:60px"><a href="/milestones/<%= m.version %>"
|
|
89
|
-
hx-get="/milestones/<%= m.version %>"
|
|
90
|
-
hx-target="#main-content"
|
|
91
|
-
hx-push-url="true">v<%= m.version %></a></div>
|
|
92
|
-
<div class="col" style="white-space:nowrap;overflow:hidden;text-overflow:ellipsis"><%= m.name %></div>
|
|
93
|
-
<div class="col-auto"><%= m.date || '—' %></div>
|
|
94
|
-
<div class="col-auto"><%= m.duration || '—' %></div>
|
|
95
|
-
<div class="col-auto"><%= m.stats && m.stats.phaseCount ? m.stats.phaseCount : '—' %></div>
|
|
96
|
-
<div class="col-auto"><%= m.stats && m.stats.commitCount ? m.stats.commitCount : '—' %></div>
|
|
97
|
-
</div>
|
|
98
|
-
</summary>
|
|
99
|
-
<div style="padding:0.5rem 1rem 1rem">
|
|
100
|
-
<% if (m.stats && m.stats.statsHtml) { %>
|
|
101
|
-
<div><%= m.stats.statsHtml %></div>
|
|
102
|
-
<% } %>
|
|
103
|
-
<% if (m.stats && m.stats.deliverables && m.stats.deliverables.length > 0) { %>
|
|
104
|
-
<h4>Deliverables</h4>
|
|
105
|
-
<ul>
|
|
106
|
-
<% m.stats.deliverables.forEach(function(d) { %>
|
|
107
|
-
<li><a href="/milestones/<%= m.version %>"
|
|
108
|
-
hx-get="/milestones/<%= m.version %>"
|
|
109
|
-
hx-target="#main-content"
|
|
110
|
-
hx-push-url="true"><%= d %></a></li>
|
|
111
|
-
<% }); %>
|
|
112
|
-
</ul>
|
|
113
|
-
<% } %>
|
|
114
|
-
</div>
|
|
115
|
-
</details>
|
|
116
|
-
</td>
|
|
117
|
-
</tr>
|
|
118
|
-
<% }); %>
|
|
119
|
-
</tbody>
|
|
120
|
-
</table>
|
|
121
|
-
</div>
|
|
122
|
-
</div>
|
|
123
|
-
<% } %>
|
|
124
|
-
|
|
125
|
-
<% if (active.length === 0 && archived.length === 0) { %>
|
|
126
|
-
<%- include('empty-state', { icon: '🚩', title: 'No milestones yet', action: '' }) %>
|
|
127
|
-
<% } %>
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
<%- include('breadcrumbs', { breadcrumbs: typeof breadcrumbs !== 'undefined' ? breadcrumbs : [] }) %>
|
|
2
|
-
<h1>
|
|
3
|
-
<%= title %>
|
|
4
|
-
<% if (promoted) { %>
|
|
5
|
-
<span class="status-badge" data-status="complete">promoted</span>
|
|
6
|
-
<% } %>
|
|
7
|
-
</h1>
|
|
8
|
-
|
|
9
|
-
<% if (date) { %>
|
|
10
|
-
<p><small><%= date %></small></p>
|
|
11
|
-
<% } %>
|
|
12
|
-
|
|
13
|
-
<div class="card mb-3">
|
|
14
|
-
<div class="card-body">
|
|
15
|
-
<div class="markdown-body">
|
|
16
|
-
<%- html %>
|
|
17
|
-
</div>
|
|
18
|
-
</div>
|
|
19
|
-
</div>
|
|
20
|
-
|
|
21
|
-
<p><a href="/notes" class="btn btn-outline-secondary"
|
|
22
|
-
hx-get="/notes"
|
|
23
|
-
hx-target="#main-content"
|
|
24
|
-
hx-push-url="true">Back to Notes</a></p>
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
<%- include('breadcrumbs', { breadcrumbs: typeof breadcrumbs !== 'undefined' ? breadcrumbs : [] }) %>
|
|
2
|
-
<h1>Notes</h1>
|
|
3
|
-
|
|
4
|
-
<% if (notes.length === 0) { %>
|
|
5
|
-
<%- include('empty-state', { icon: 'N', title: 'No notes yet', action: 'Use /pbr:note to create project notes.' }) %>
|
|
6
|
-
<% } else { %>
|
|
7
|
-
<% notes.forEach(function(note) { %>
|
|
8
|
-
<% const noteSlug = note.filename.replace(/^\d{4}-\d{2}-\d{2}-/, '').replace(/\.md$/, ''); %>
|
|
9
|
-
<div class="card mb-2">
|
|
10
|
-
<div class="card-header">
|
|
11
|
-
<strong>
|
|
12
|
-
<a href="/notes/<%= noteSlug %>"
|
|
13
|
-
hx-get="/notes/<%= noteSlug %>"
|
|
14
|
-
hx-target="#main-content"
|
|
15
|
-
hx-push-url="true"><%= note.title %></a>
|
|
16
|
-
</strong>
|
|
17
|
-
<span style="display:flex;gap:var(--space-sm);align-items:center;margin-left:auto">
|
|
18
|
-
<% if (note.promoted) { %>
|
|
19
|
-
<span class="status-badge status-badge--sm" data-status="complete">promoted</span>
|
|
20
|
-
<% } %>
|
|
21
|
-
<% if (note.date) { %>
|
|
22
|
-
<small style="color:var(--color-text-dim)"><%= typeof note.date === 'object' ? note.date.toISOString().slice(0, 10) : String(note.date).slice(0, 10) %></small>
|
|
23
|
-
<% } %>
|
|
24
|
-
</span>
|
|
25
|
-
</div>
|
|
26
|
-
</div>
|
|
27
|
-
<% }); %>
|
|
28
|
-
<% } %>
|