@sienklogic/plan-build-run 2.17.1 → 2.18.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 +21 -0
- package/dashboard/public/css/layout.css +9 -0
- package/dashboard/public/js/sse-client.js +1 -0
- package/dashboard/src/app.js +9 -0
- package/dashboard/src/routes/index.routes.js +5 -2
- package/dashboard/src/routes/pages.routes.js +35 -5
- package/dashboard/src/services/analytics.service.js +2 -4
- package/dashboard/src/services/notes.service.js +50 -0
- package/dashboard/src/services/phase.service.js +8 -3
- package/dashboard/src/views/error.ejs +1 -1
- package/dashboard/src/views/notes.ejs +5 -0
- package/dashboard/src/views/partials/analytics-content.ejs +23 -4
- package/dashboard/src/views/partials/dashboard-content.ejs +69 -63
- package/dashboard/src/views/partials/dependencies-content.ejs +33 -1
- package/dashboard/src/views/partials/footer.ejs +1 -1
- package/dashboard/src/views/partials/header.ejs +1 -1
- package/dashboard/src/views/partials/milestones-content.ejs +9 -1
- package/dashboard/src/views/partials/notes-content.ejs +23 -0
- package/dashboard/src/views/partials/phase-content.ejs +4 -1
- package/dashboard/src/views/partials/phase-doc-content.ejs +1 -1
- package/dashboard/src/views/partials/phases-content.ejs +8 -2
- package/dashboard/src/views/partials/roadmap-content.ejs +16 -4
- package/dashboard/src/views/partials/sidebar.ejs +8 -0
- package/package.json +1 -1
- package/plugins/copilot-pbr/plugin.json +1 -1
- package/plugins/cursor-pbr/.cursor-plugin/plugin.json +1 -1
- package/plugins/cursor-pbr/agents/debugger.md +1 -1
- package/plugins/cursor-pbr/agents/executor.md +1 -1
- package/plugins/cursor-pbr/agents/general.md +1 -1
- package/plugins/cursor-pbr/agents/planner.md +1 -1
- package/plugins/pbr/.claude-plugin/plugin.json +1 -1
- package/plugins/pbr/agents/debugger.md +1 -1
- package/plugins/pbr/agents/executor.md +1 -1
- package/plugins/pbr/agents/general.md +1 -1
- package/plugins/pbr/agents/planner.md +1 -1
- package/plugins/pbr/scripts/check-state-sync.js +8 -8
- package/plugins/pbr/scripts/context-budget-check.js +1 -1
- package/plugins/pbr/scripts/pbr-tools.js +3 -3
- package/plugins/pbr/skills/build/SKILL.md +1 -1
- package/plugins/pbr/skills/continue/SKILL.md +1 -1
- package/plugins/pbr/skills/plan/SKILL.md +1 -1
- package/plugins/pbr/skills/review/SKILL.md +1 -1
- package/dashboard/src/views/layout.ejs +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,27 @@ 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.18.1](https://github.com/SienkLogic/plan-build-run/compare/plan-build-run-v2.18.0...plan-build-run-v2.18.1) (2026-02-22)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Bug Fixes
|
|
12
|
+
|
|
13
|
+
* **tools:** add Skill tool to 4 PBR skills that use auto-advance chaining ([2be11a4](https://github.com/SienkLogic/plan-build-run/commit/2be11a4dd4fe0ad875787fc65d7f217919f4a662))
|
|
14
|
+
* **tools:** update critical agents to use model: sonnet instead of inherit ([6d66573](https://github.com/SienkLogic/plan-build-run/commit/6d66573b72352a1c412ff7e1e74adee2e1d2b59f))
|
|
15
|
+
|
|
16
|
+
## [2.18.0](https://github.com/SienkLogic/plan-build-run/compare/plan-build-run-v2.17.1...plan-build-run-v2.18.0) (2026-02-22)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
### Features
|
|
20
|
+
|
|
21
|
+
* **16-01:** redesign dashboard home, fix analytics duration, add bar charts, mermaid dark mode ([9ea3936](https://github.com/SienkLogic/plan-build-run/commit/9ea3936bb5c35305b57250c83e596c4704bc8868))
|
|
22
|
+
* **17-01:** add notes page, verification viewer, milestone progress bars, dynamic footer version ([71625ff](https://github.com/SienkLogic/plan-build-run/commit/71625ff769638be8d75840b48f455a1fe5664b88))
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
### Bug Fixes
|
|
26
|
+
|
|
27
|
+
* **18-01:** HTMX navigation consistency, SSE tooltip, error page fix, remove deprecated layout ([0f40f3c](https://github.com/SienkLogic/plan-build-run/commit/0f40f3c9de389f7eb33f8eb83c78a0880f485e4e))
|
|
28
|
+
|
|
8
29
|
## [2.17.1](https://github.com/SienkLogic/plan-build-run/compare/plan-build-run-v2.17.0...plan-build-run-v2.17.1) (2026-02-22)
|
|
9
30
|
|
|
10
31
|
|
|
@@ -500,6 +500,15 @@ main > p:first-of-type > a[href="/"]:hover {
|
|
|
500
500
|
font-size: 0.875rem;
|
|
501
501
|
}
|
|
502
502
|
|
|
503
|
+
/* --- Stat Values --- */
|
|
504
|
+
.stat-value { font-size: 2rem; font-weight: 700; line-height: 1.2; }
|
|
505
|
+
.stat-unit { font-size: 0.875rem; color: var(--color-dim); margin-left: 0.25rem; }
|
|
506
|
+
|
|
507
|
+
/* --- Bar Chart --- */
|
|
508
|
+
.bar-chart-row { display: flex; align-items: center; gap: var(--space-sm); margin-bottom: var(--space-xs); }
|
|
509
|
+
.bar-chart-label { min-width: 160px; font-size: 0.875rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
510
|
+
.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 0.3s ease; }
|
|
511
|
+
|
|
503
512
|
/* --- Loading Bar --- */
|
|
504
513
|
.loading-bar {
|
|
505
514
|
position: fixed;
|
package/dashboard/src/app.js
CHANGED
|
@@ -9,6 +9,7 @@ import eventsRouter from './routes/events.routes.js';
|
|
|
9
9
|
import notFoundHandler from './middleware/notFoundHandler.js';
|
|
10
10
|
import errorHandler from './middleware/errorHandler.js';
|
|
11
11
|
import currentPhaseMiddleware from './middleware/current-phase.js';
|
|
12
|
+
import { readFileSync } from 'node:fs';
|
|
12
13
|
|
|
13
14
|
const __filename = fileURLToPath(import.meta.url);
|
|
14
15
|
const __dirname = dirname(__filename);
|
|
@@ -35,6 +36,14 @@ export function createApp(config) {
|
|
|
35
36
|
// Store config for access in routes/services
|
|
36
37
|
app.locals.projectDir = config.projectDir;
|
|
37
38
|
|
|
39
|
+
// Read dashboard version from package.json for footer display
|
|
40
|
+
try {
|
|
41
|
+
const pkg = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf-8'));
|
|
42
|
+
app.locals.dashboardVersion = pkg.version || '0.0.0';
|
|
43
|
+
} catch (_e) {
|
|
44
|
+
app.locals.dashboardVersion = '0.0.0';
|
|
45
|
+
}
|
|
46
|
+
|
|
38
47
|
// View engine setup -- all paths use path.join (cross-platform)
|
|
39
48
|
app.set('views', join(__dirname, 'views'));
|
|
40
49
|
app.set('view engine', 'ejs');
|
|
@@ -1,20 +1,23 @@
|
|
|
1
1
|
import { Router } from 'express';
|
|
2
2
|
import { getHomepage } from '../services/project.service.js';
|
|
3
3
|
import { getDashboardData } from '../services/dashboard.service.js';
|
|
4
|
+
import { listPendingTodos } from '../services/todo.service.js';
|
|
4
5
|
|
|
5
6
|
const router = Router();
|
|
6
7
|
|
|
7
8
|
router.get('/', async (req, res) => {
|
|
8
9
|
const projectDir = req.app.locals.projectDir;
|
|
9
10
|
|
|
10
|
-
const [homepageData, dashboardData] = await Promise.all([
|
|
11
|
+
const [homepageData, dashboardData, pendingTodos] = await Promise.all([
|
|
11
12
|
getHomepage(projectDir),
|
|
12
|
-
getDashboardData(projectDir)
|
|
13
|
+
getDashboardData(projectDir),
|
|
14
|
+
listPendingTodos(projectDir).catch(() => [])
|
|
13
15
|
]);
|
|
14
16
|
|
|
15
17
|
const templateData = {
|
|
16
18
|
...homepageData,
|
|
17
19
|
...dashboardData,
|
|
20
|
+
pendingTodoCount: pendingTodos.length,
|
|
18
21
|
activePage: 'dashboard',
|
|
19
22
|
currentPath: '/',
|
|
20
23
|
breadcrumbs: []
|
|
@@ -5,6 +5,7 @@ import { parseStateFile, derivePhaseStatuses } from '../services/dashboard.servi
|
|
|
5
5
|
import { listPendingTodos, getTodoDetail, createTodo, completeTodo } from '../services/todo.service.js';
|
|
6
6
|
import { getAllMilestones, getMilestoneDetail } from '../services/milestone.service.js';
|
|
7
7
|
import { getProjectAnalytics } from '../services/analytics.service.js';
|
|
8
|
+
import { listNotes } from '../services/notes.service.js';
|
|
8
9
|
|
|
9
10
|
const router = Router();
|
|
10
11
|
|
|
@@ -81,8 +82,8 @@ router.get('/phases/:phaseId/:planId/:docType', async (req, res) => {
|
|
|
81
82
|
}
|
|
82
83
|
|
|
83
84
|
// Validate docType
|
|
84
|
-
if (docType !== 'plan' && docType !== 'summary') {
|
|
85
|
-
const err = new Error('Document type must be "plan" or "
|
|
85
|
+
if (docType !== 'plan' && docType !== 'summary' && docType !== 'verification') {
|
|
86
|
+
const err = new Error('Document type must be "plan", "summary", or "verification"');
|
|
86
87
|
err.status = 404;
|
|
87
88
|
throw err;
|
|
88
89
|
}
|
|
@@ -91,12 +92,13 @@ router.get('/phases/:phaseId/:planId/:docType', async (req, res) => {
|
|
|
91
92
|
const doc = await getPhaseDocument(projectDir, phaseId, planId, docType);
|
|
92
93
|
|
|
93
94
|
if (!doc) {
|
|
94
|
-
const
|
|
95
|
+
const labels = { plan: 'Plan', summary: 'Summary', verification: 'Verification' };
|
|
96
|
+
const err = new Error(`${labels[docType] || docType} ${planId} not found for phase ${phaseId}`);
|
|
95
97
|
err.status = 404;
|
|
96
98
|
throw err;
|
|
97
99
|
}
|
|
98
100
|
|
|
99
|
-
const docLabel = docType === 'plan' ? 'Plan' : 'Summary';
|
|
101
|
+
const docLabel = docType === 'plan' ? 'Plan' : docType === 'verification' ? 'Verification' : 'Summary';
|
|
100
102
|
const templateData = {
|
|
101
103
|
title: `${docLabel} ${planId} — Phase ${phaseId}: ${doc.phaseName}`,
|
|
102
104
|
activePage: 'phases',
|
|
@@ -271,13 +273,20 @@ router.post('/todos/:id/done', async (req, res) => {
|
|
|
271
273
|
|
|
272
274
|
router.get('/milestones', async (req, res) => {
|
|
273
275
|
const projectDir = req.app.locals.projectDir;
|
|
274
|
-
const milestoneData = await
|
|
276
|
+
const [milestoneData, roadmapData, stateData] = await Promise.all([
|
|
277
|
+
getAllMilestones(projectDir),
|
|
278
|
+
getRoadmapData(projectDir),
|
|
279
|
+
parseStateFile(projectDir)
|
|
280
|
+
]);
|
|
281
|
+
|
|
282
|
+
const phases = derivePhaseStatuses(roadmapData.phases, stateData.currentPhase);
|
|
275
283
|
|
|
276
284
|
const templateData = {
|
|
277
285
|
title: 'Milestones',
|
|
278
286
|
activePage: 'milestones',
|
|
279
287
|
currentPath: '/milestones',
|
|
280
288
|
breadcrumbs: [{ label: 'Milestones' }],
|
|
289
|
+
phases,
|
|
281
290
|
...milestoneData
|
|
282
291
|
};
|
|
283
292
|
|
|
@@ -368,6 +377,27 @@ router.get('/analytics', async (req, res) => {
|
|
|
368
377
|
}
|
|
369
378
|
});
|
|
370
379
|
|
|
380
|
+
router.get('/notes', async (req, res) => {
|
|
381
|
+
const projectDir = req.app.locals.projectDir;
|
|
382
|
+
const notes = await listNotes(projectDir);
|
|
383
|
+
|
|
384
|
+
const templateData = {
|
|
385
|
+
title: 'Notes',
|
|
386
|
+
activePage: 'notes',
|
|
387
|
+
currentPath: '/notes',
|
|
388
|
+
breadcrumbs: [{ label: 'Notes' }],
|
|
389
|
+
notes
|
|
390
|
+
};
|
|
391
|
+
|
|
392
|
+
res.setHeader('Vary', 'HX-Request');
|
|
393
|
+
|
|
394
|
+
if (req.get('HX-Request') === 'true') {
|
|
395
|
+
res.render('partials/notes-content', templateData);
|
|
396
|
+
} else {
|
|
397
|
+
res.render('notes', templateData);
|
|
398
|
+
}
|
|
399
|
+
});
|
|
400
|
+
|
|
371
401
|
router.get('/roadmap', async (req, res) => {
|
|
372
402
|
const projectDir = req.app.locals.projectDir;
|
|
373
403
|
const [roadmapData, stateData] = await Promise.all([
|
|
@@ -71,9 +71,9 @@ export async function getProjectAnalytics(projectDir) {
|
|
|
71
71
|
const scopePattern = new RegExp(`\\(${phaseNum}-`);
|
|
72
72
|
const commitCount = allLogLines.filter(line => scopePattern.test(line)).length;
|
|
73
73
|
|
|
74
|
-
// Phase duration from git dates
|
|
74
|
+
// Phase duration from git dates (match commits by scope pattern in message)
|
|
75
75
|
const dateOutput = await git(projectDir, [
|
|
76
|
-
'log', '--format=%aI
|
|
76
|
+
'log', '--all', `--format=%aI`, `--grep=(${phaseNum}-`
|
|
77
77
|
]);
|
|
78
78
|
const dates = dateOutput.trim().split('\n').filter(Boolean).map(d => new Date(d));
|
|
79
79
|
let duration = null;
|
|
@@ -82,8 +82,6 @@ export async function getProjectAnalytics(projectDir) {
|
|
|
82
82
|
const latest = new Date(Math.max(...dates));
|
|
83
83
|
const days = Math.round((latest - earliest) / (1000 * 60 * 60 * 24));
|
|
84
84
|
duration = `${days}d`;
|
|
85
|
-
} else if (dates.length === 1) {
|
|
86
|
-
duration = '0d';
|
|
87
85
|
}
|
|
88
86
|
|
|
89
87
|
// Plan count
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { readdir } from 'node:fs/promises';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { readMarkdownFile } from '../repositories/planning.repository.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* List all notes from .planning/notes/*.md, sorted by date descending.
|
|
7
|
+
*
|
|
8
|
+
* @param {string} projectDir - Absolute path to the project root
|
|
9
|
+
* @returns {Promise<Array<{filename: string, title: string, date: string|null, promoted: boolean, html: string}>>}
|
|
10
|
+
*/
|
|
11
|
+
export async function listNotes(projectDir) {
|
|
12
|
+
const notesDir = join(projectDir, '.planning', 'notes');
|
|
13
|
+
|
|
14
|
+
let entries;
|
|
15
|
+
try {
|
|
16
|
+
entries = await readdir(notesDir);
|
|
17
|
+
} catch (err) {
|
|
18
|
+
if (err.code === 'ENOENT') return [];
|
|
19
|
+
throw err;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const mdFiles = entries.filter(f => f.endsWith('.md')).sort().reverse();
|
|
23
|
+
|
|
24
|
+
const results = await Promise.allSettled(
|
|
25
|
+
mdFiles.map(f => readMarkdownFile(join(notesDir, f)))
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
const notes = [];
|
|
29
|
+
for (let i = 0; i < mdFiles.length; i++) {
|
|
30
|
+
const result = results[i];
|
|
31
|
+
if (result.status !== 'fulfilled') continue;
|
|
32
|
+
|
|
33
|
+
const { frontmatter, html } = result.value;
|
|
34
|
+
const filename = mdFiles[i];
|
|
35
|
+
|
|
36
|
+
// Derive title from filename: strip date prefix and extension, title-case
|
|
37
|
+
const slug = filename.replace(/^\d{4}-\d{2}-\d{2}-/, '').replace(/\.md$/, '');
|
|
38
|
+
const title = slug.split('-').map(w => w.charAt(0).toUpperCase() + w.slice(1)).join(' ');
|
|
39
|
+
|
|
40
|
+
notes.push({
|
|
41
|
+
filename,
|
|
42
|
+
title,
|
|
43
|
+
date: frontmatter.date || null,
|
|
44
|
+
promoted: !!frontmatter.promoted,
|
|
45
|
+
html
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return notes;
|
|
50
|
+
}
|
|
@@ -202,9 +202,14 @@ export async function getPhaseDocument(projectDir, phaseId, planId, docType) {
|
|
|
202
202
|
|
|
203
203
|
// Try plan ID-prefixed filename first, then fall back to plain PLAN.md
|
|
204
204
|
// Supports both "01-01-PLAN.md" and "PLAN.md" naming conventions
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
205
|
+
let fileNames;
|
|
206
|
+
if (docType === 'plan') {
|
|
207
|
+
fileNames = [`${planId}-PLAN.md`, 'PLAN.md'];
|
|
208
|
+
} else if (docType === 'verification') {
|
|
209
|
+
fileNames = ['VERIFICATION.md'];
|
|
210
|
+
} else {
|
|
211
|
+
fileNames = [`SUMMARY-${planId}.md`];
|
|
212
|
+
}
|
|
208
213
|
|
|
209
214
|
for (const fileName of fileNames) {
|
|
210
215
|
const filePath = validatePath(phaseFullPath, fileName);
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
</details>
|
|
14
14
|
<% } %>
|
|
15
15
|
<div class="error-card__action">
|
|
16
|
-
<
|
|
16
|
+
<button onclick="history.back()" class="outline">Go back</button> | <a href="/">Return to Dashboard</a>
|
|
17
17
|
</div>
|
|
18
18
|
</div>
|
|
19
19
|
|
|
@@ -12,19 +12,23 @@
|
|
|
12
12
|
<div class="grid">
|
|
13
13
|
<article>
|
|
14
14
|
<header>Total Commits</header>
|
|
15
|
-
<strong
|
|
15
|
+
<strong class="stat-value"><%= analytics.summary.totalCommits %></strong>
|
|
16
|
+
<span class="stat-unit">commits</span>
|
|
16
17
|
</article>
|
|
17
18
|
<article>
|
|
18
19
|
<header>Total Phases</header>
|
|
19
|
-
<strong
|
|
20
|
+
<strong class="stat-value"><%= analytics.summary.totalPhases %></strong>
|
|
21
|
+
<span class="stat-unit">phases</span>
|
|
20
22
|
</article>
|
|
21
23
|
<article>
|
|
22
24
|
<header>Avg Duration</header>
|
|
23
|
-
<strong
|
|
25
|
+
<strong class="stat-value"><%= analytics.summary.avgDuration %></strong>
|
|
26
|
+
<span class="stat-unit">days</span>
|
|
24
27
|
</article>
|
|
25
28
|
<article>
|
|
26
29
|
<header>Lines Changed</header>
|
|
27
|
-
<strong
|
|
30
|
+
<strong class="stat-value"><%= analytics.summary.totalLinesChanged.toLocaleString() %></strong>
|
|
31
|
+
<span class="stat-unit">lines</span>
|
|
28
32
|
</article>
|
|
29
33
|
</div>
|
|
30
34
|
<% } %>
|
|
@@ -64,6 +68,21 @@
|
|
|
64
68
|
</table>
|
|
65
69
|
</div>
|
|
66
70
|
</article>
|
|
71
|
+
|
|
72
|
+
<% const maxCommits = Math.max(...analytics.phases.map(p => p.commitCount), 1); %>
|
|
73
|
+
<article>
|
|
74
|
+
<header>Commits by Phase</header>
|
|
75
|
+
<div style="padding: var(--space-md) var(--space-lg);">
|
|
76
|
+
<% analytics.phases.sort((a, b) => a.phaseId.localeCompare(b.phaseId)).forEach(phase => { %>
|
|
77
|
+
<div class="bar-chart-row">
|
|
78
|
+
<span class="bar-chart-label"><%= phase.phaseId %> - <%= phase.phaseName %></span>
|
|
79
|
+
<div class="bar-chart-bar" style="width: <%= (phase.commitCount / maxCommits * 100) %>%">
|
|
80
|
+
<%= phase.commitCount %>
|
|
81
|
+
</div>
|
|
82
|
+
</div>
|
|
83
|
+
<% }) %>
|
|
84
|
+
</div>
|
|
85
|
+
</article>
|
|
67
86
|
<% } else if (typeof analytics === 'undefined' || !analytics.summary) { %>
|
|
68
87
|
<%- include('empty-state', { icon: '📊', title: 'No analytics data available', action: '' }) %>
|
|
69
88
|
<% } else { %>
|
|
@@ -1,73 +1,79 @@
|
|
|
1
1
|
<%- include('breadcrumbs', { breadcrumbs: typeof breadcrumbs !== 'undefined' ? breadcrumbs : [] }) %>
|
|
2
2
|
<h1><%= projectName %></h1>
|
|
3
3
|
|
|
4
|
-
<!--
|
|
5
|
-
<
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
4
|
+
<!-- Hero Stat Row -->
|
|
5
|
+
<div class="grid">
|
|
6
|
+
<!-- Current Phase -->
|
|
7
|
+
<article>
|
|
8
|
+
<header><strong>Current Phase</strong></header>
|
|
9
|
+
<% if (currentPhase.id > 0) { %>
|
|
10
|
+
<p class="stat-value">
|
|
11
|
+
<a href="/phases/<%= String(currentPhase.id).padStart(2, '0') %>"
|
|
12
|
+
hx-get="/phases/<%= String(currentPhase.id).padStart(2, '0') %>"
|
|
13
|
+
hx-target="#main-content"
|
|
14
|
+
hx-push-url="true">
|
|
15
|
+
<span class="status-badge" data-status="in-progress">Phase <%= currentPhase.id %></span>
|
|
16
|
+
<%= currentPhase.name %>
|
|
17
|
+
</a>
|
|
18
|
+
</p>
|
|
19
|
+
<% } else { %>
|
|
20
|
+
<p class="stat-value">No active phase</p>
|
|
21
|
+
<% } %>
|
|
22
|
+
</article>
|
|
18
23
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
24
|
+
<!-- Progress -->
|
|
25
|
+
<article>
|
|
26
|
+
<header><strong>Progress</strong></header>
|
|
27
|
+
<p class="stat-value"><%= progress %>%</p>
|
|
28
|
+
<progress value="<%= progress %>" max="100"></progress>
|
|
29
|
+
</article>
|
|
23
30
|
|
|
24
|
-
<!--
|
|
25
|
-
<article>
|
|
26
|
-
|
|
27
|
-
<
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
31
|
+
<!-- Pending Todos -->
|
|
32
|
+
<article>
|
|
33
|
+
<header><strong>Pending Todos</strong></header>
|
|
34
|
+
<p class="stat-value">
|
|
35
|
+
<a href="/todos"
|
|
36
|
+
hx-get="/todos"
|
|
37
|
+
hx-target="#main-content"
|
|
38
|
+
hx-push-url="true">
|
|
39
|
+
<%= typeof pendingTodoCount !== 'undefined' ? pendingTodoCount : 0 %>
|
|
40
|
+
</a>
|
|
41
|
+
</p>
|
|
42
|
+
</article>
|
|
32
43
|
|
|
33
|
-
<!--
|
|
34
|
-
<article>
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
</div>
|
|
65
|
-
<% } else { %>
|
|
66
|
-
<p>No phases found. Add a ROADMAP.md file to your .planning/ directory to see phases here.</p>
|
|
67
|
-
<% } %>
|
|
68
|
-
</article>
|
|
44
|
+
<!-- Last Activity -->
|
|
45
|
+
<article>
|
|
46
|
+
<header><strong>Last Activity</strong></header>
|
|
47
|
+
<% if (lastActivity.date) { %>
|
|
48
|
+
<p class="stat-value"><%= lastActivity.date %></p>
|
|
49
|
+
<p><small><%= lastActivity.description %></small></p>
|
|
50
|
+
<% } else { %>
|
|
51
|
+
<p class="stat-value">None</p>
|
|
52
|
+
<% } %>
|
|
53
|
+
</article>
|
|
54
|
+
</div>
|
|
55
|
+
|
|
56
|
+
<!-- Quick Actions -->
|
|
57
|
+
<div>
|
|
58
|
+
<% var phaseId = String(currentPhase.id).padStart(2, '0'); %>
|
|
59
|
+
<a role="button" class="outline"
|
|
60
|
+
href="/phases/<%= phaseId %>"
|
|
61
|
+
hx-get="/phases/<%= phaseId %>"
|
|
62
|
+
hx-target="#main-content"
|
|
63
|
+
hx-push-url="true">Current Phase</a>
|
|
64
|
+
<a role="button" class="outline"
|
|
65
|
+
href="/roadmap"
|
|
66
|
+
hx-get="/roadmap"
|
|
67
|
+
hx-target="#main-content"
|
|
68
|
+
hx-push-url="true">Roadmap</a>
|
|
69
|
+
<a role="button" class="outline"
|
|
70
|
+
href="/todos/new"
|
|
71
|
+
hx-get="/todos/new"
|
|
72
|
+
hx-target="#main-content"
|
|
73
|
+
hx-push-url="true">Create Todo</a>
|
|
74
|
+
</div>
|
|
69
75
|
|
|
70
|
-
<!--
|
|
76
|
+
<!-- Project Notes (if available) -->
|
|
71
77
|
<% if (content && content !== '<p>No project README found.</p>') { %>
|
|
72
78
|
<article>
|
|
73
79
|
<header>
|
|
@@ -12,5 +12,37 @@
|
|
|
12
12
|
|
|
13
13
|
<script src="https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.min.js"></script>
|
|
14
14
|
<script>
|
|
15
|
-
|
|
15
|
+
(function() {
|
|
16
|
+
function detectDark() {
|
|
17
|
+
var explicit = document.documentElement.dataset.theme;
|
|
18
|
+
if (explicit) return explicit === 'dark';
|
|
19
|
+
return window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
|
|
20
|
+
}
|
|
21
|
+
var mermaidTheme = detectDark() ? 'dark' : 'default';
|
|
22
|
+
|
|
23
|
+
// Store original source before mermaid processes it
|
|
24
|
+
document.querySelectorAll('.mermaid').forEach(function(el) {
|
|
25
|
+
el.dataset.source = el.textContent;
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
mermaid.initialize({ startOnLoad: true, theme: mermaidTheme, securityLevel: 'loose' });
|
|
29
|
+
|
|
30
|
+
// Watch for theme changes and re-render
|
|
31
|
+
var observer = new MutationObserver(function(mutations) {
|
|
32
|
+
mutations.forEach(function(mutation) {
|
|
33
|
+
if (mutation.attributeName === 'data-theme') {
|
|
34
|
+
var newTheme = detectDark() ? 'dark' : 'default';
|
|
35
|
+
mermaid.initialize({ startOnLoad: false, theme: newTheme, securityLevel: 'loose' });
|
|
36
|
+
document.querySelectorAll('.mermaid').forEach(function(el) {
|
|
37
|
+
if (el.dataset.source) {
|
|
38
|
+
el.removeAttribute('data-processed');
|
|
39
|
+
el.innerHTML = el.dataset.source;
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
mermaid.run();
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
observer.observe(document.documentElement, { attributes: true, attributeFilter: ['data-theme'] });
|
|
47
|
+
})();
|
|
16
48
|
</script>
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
</ul>
|
|
9
9
|
<ul>
|
|
10
10
|
<li>
|
|
11
|
-
<span id="sse-status" data-connected="false" aria-label="SSE connection status"></span>
|
|
11
|
+
<span id="sse-status" data-connected="false" aria-label="SSE connection status" title="Live updates: disconnected"></span>
|
|
12
12
|
</li>
|
|
13
13
|
<li>
|
|
14
14
|
<button id="theme-toggle" class="theme-toggle" aria-label="Toggle theme">🌙</button>
|
|
@@ -3,7 +3,11 @@
|
|
|
3
3
|
|
|
4
4
|
<% if (active.length > 0) { %>
|
|
5
5
|
<h2>Active</h2>
|
|
6
|
-
<% active.forEach(function(m) {
|
|
6
|
+
<% active.forEach(function(m) {
|
|
7
|
+
var msPhases = (typeof phases !== 'undefined' && phases) ? phases.filter(function(p) { return p.id >= m.startPhase && p.id <= m.endPhase; }) : [];
|
|
8
|
+
var totalPhases = msPhases.length;
|
|
9
|
+
var completedPhases = msPhases.filter(function(p) { return p.status === 'complete'; }).length;
|
|
10
|
+
%>
|
|
7
11
|
<article>
|
|
8
12
|
<header>
|
|
9
13
|
<strong><%= m.name %></strong>
|
|
@@ -12,6 +16,10 @@
|
|
|
12
16
|
<p>
|
|
13
17
|
Phases <%= m.startPhase %> – <%= m.endPhase %>
|
|
14
18
|
</p>
|
|
19
|
+
<% if (totalPhases > 0) { %>
|
|
20
|
+
<progress value="<%= completedPhases %>" max="<%= totalPhases %>"></progress>
|
|
21
|
+
<p><small><%= completedPhases %> of <%= totalPhases %> phases complete</small></p>
|
|
22
|
+
<% } %>
|
|
15
23
|
</article>
|
|
16
24
|
<% }); %>
|
|
17
25
|
<% } %>
|
|
@@ -0,0 +1,23 @@
|
|
|
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
|
+
<article>
|
|
9
|
+
<header>
|
|
10
|
+
<strong><%= note.title %></strong>
|
|
11
|
+
<% if (note.promoted) { %>
|
|
12
|
+
<span class="status-badge" data-status="complete">promoted</span>
|
|
13
|
+
<% } %>
|
|
14
|
+
<% if (note.date) { %>
|
|
15
|
+
<small style="float:right"><%= note.date %></small>
|
|
16
|
+
<% } %>
|
|
17
|
+
</header>
|
|
18
|
+
<div class="markdown-body">
|
|
19
|
+
<%- note.html %>
|
|
20
|
+
</div>
|
|
21
|
+
</article>
|
|
22
|
+
<% }); %>
|
|
23
|
+
<% } %>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<%- include('breadcrumbs', { breadcrumbs: typeof breadcrumbs !== 'undefined' ? breadcrumbs : [] }) %>
|
|
2
2
|
<h1>Phase <%= phaseId %>: <%= phaseName %></h1>
|
|
3
3
|
|
|
4
|
-
<p><a href="/">← Back to
|
|
4
|
+
<p><a href="/phases" hx-get="/phases" hx-target="#main-content" hx-push-url="true">← Back to Phases</a></p>
|
|
5
5
|
|
|
6
6
|
<% if (verification) { %>
|
|
7
7
|
<!-- Verification Summary Card -->
|
|
@@ -73,6 +73,9 @@
|
|
|
73
73
|
<% if (plan.summary) { %>
|
|
74
74
|
| <a href="/phases/<%= phaseId %>/<%= plan.planId %>/summary">View Summary</a>
|
|
75
75
|
<% } %>
|
|
76
|
+
<% if (verification) { %>
|
|
77
|
+
| <a href="/phases/<%= phaseId %>/<%= plan.planId %>/verification">View Verification</a>
|
|
78
|
+
<% } %>
|
|
76
79
|
</p>
|
|
77
80
|
|
|
78
81
|
<% if (plan.summary) { %>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<%
|
|
2
|
-
var docLabel = docType === 'plan' ? 'Plan' : 'Summary';
|
|
2
|
+
var docLabel = docType === 'plan' ? 'Plan' : docType === 'verification' ? 'Verification' : 'Summary';
|
|
3
3
|
%>
|
|
4
4
|
<%- include('breadcrumbs', { breadcrumbs: typeof breadcrumbs !== 'undefined' ? breadcrumbs : [] }) %>
|
|
5
5
|
<h1><%= docLabel %> <%= planId %></h1>
|
|
@@ -61,7 +61,10 @@
|
|
|
61
61
|
<tr>
|
|
62
62
|
<td><%= String(phase.id).padStart(2, '0') %></td>
|
|
63
63
|
<td>
|
|
64
|
-
<a href="/phases/<%= String(phase.id).padStart(2, '0') %>"
|
|
64
|
+
<a href="/phases/<%= String(phase.id).padStart(2, '0') %>"
|
|
65
|
+
hx-get="/phases/<%= String(phase.id).padStart(2, '0') %>"
|
|
66
|
+
hx-target="#main-content"
|
|
67
|
+
hx-push-url="true">
|
|
65
68
|
<%= phase.name %>
|
|
66
69
|
</a>
|
|
67
70
|
</td>
|
|
@@ -97,7 +100,10 @@
|
|
|
97
100
|
<tr>
|
|
98
101
|
<td><%= String(phase.id).padStart(2, '0') %></td>
|
|
99
102
|
<td>
|
|
100
|
-
<a href="/phases/<%= String(phase.id).padStart(2, '0') %>"
|
|
103
|
+
<a href="/phases/<%= String(phase.id).padStart(2, '0') %>"
|
|
104
|
+
hx-get="/phases/<%= String(phase.id).padStart(2, '0') %>"
|
|
105
|
+
hx-target="#main-content"
|
|
106
|
+
hx-push-url="true">
|
|
101
107
|
<%= phase.name %>
|
|
102
108
|
</a>
|
|
103
109
|
</td>
|
|
@@ -67,7 +67,10 @@
|
|
|
67
67
|
<tr>
|
|
68
68
|
<td><%= String(phase.id).padStart(2, '0') %></td>
|
|
69
69
|
<td>
|
|
70
|
-
<a href="/phases/<%= String(phase.id).padStart(2, '0') %>"
|
|
70
|
+
<a href="/phases/<%= String(phase.id).padStart(2, '0') %>"
|
|
71
|
+
hx-get="/phases/<%= String(phase.id).padStart(2, '0') %>"
|
|
72
|
+
hx-target="#main-content"
|
|
73
|
+
hx-push-url="true">
|
|
71
74
|
<%= phase.name %>
|
|
72
75
|
</a>
|
|
73
76
|
</td>
|
|
@@ -80,7 +83,10 @@
|
|
|
80
83
|
<td>
|
|
81
84
|
<% if (phase.dependencies && phase.dependencies.length > 0) { %>
|
|
82
85
|
<% phase.dependencies.forEach(function(dep, idx) { %>
|
|
83
|
-
<a href="/phases/<%= String(dep).padStart(2, '0') %>"
|
|
86
|
+
<a href="/phases/<%= String(dep).padStart(2, '0') %>"
|
|
87
|
+
hx-get="/phases/<%= String(dep).padStart(2, '0') %>"
|
|
88
|
+
hx-target="#main-content"
|
|
89
|
+
hx-push-url="true"><%= String(dep).padStart(2, '0') %></a><% if (idx < phase.dependencies.length - 1) { %>, <% } %>
|
|
84
90
|
<% }); %>
|
|
85
91
|
<% } else { %>
|
|
86
92
|
<small>None</small>
|
|
@@ -113,7 +119,10 @@
|
|
|
113
119
|
<tr>
|
|
114
120
|
<td><%= String(phase.id).padStart(2, '0') %></td>
|
|
115
121
|
<td>
|
|
116
|
-
<a href="/phases/<%= String(phase.id).padStart(2, '0') %>"
|
|
122
|
+
<a href="/phases/<%= String(phase.id).padStart(2, '0') %>"
|
|
123
|
+
hx-get="/phases/<%= String(phase.id).padStart(2, '0') %>"
|
|
124
|
+
hx-target="#main-content"
|
|
125
|
+
hx-push-url="true">
|
|
117
126
|
<%= phase.name %>
|
|
118
127
|
</a>
|
|
119
128
|
</td>
|
|
@@ -126,7 +135,10 @@
|
|
|
126
135
|
<td>
|
|
127
136
|
<% if (phase.dependencies && phase.dependencies.length > 0) { %>
|
|
128
137
|
<% phase.dependencies.forEach(function(dep, idx) { %>
|
|
129
|
-
<a href="/phases/<%= String(dep).padStart(2, '0') %>"
|
|
138
|
+
<a href="/phases/<%= String(dep).padStart(2, '0') %>"
|
|
139
|
+
hx-get="/phases/<%= String(dep).padStart(2, '0') %>"
|
|
140
|
+
hx-target="#main-content"
|
|
141
|
+
hx-push-url="true"><%= String(dep).padStart(2, '0') %></a><% if (idx < phase.dependencies.length - 1) { %>, <% } %>
|
|
130
142
|
<% }); %>
|
|
131
143
|
<% } else { %>
|
|
132
144
|
<small>None</small>
|
|
@@ -71,6 +71,14 @@
|
|
|
71
71
|
Todos
|
|
72
72
|
</a>
|
|
73
73
|
</li>
|
|
74
|
+
<li>
|
|
75
|
+
<a href="/notes"
|
|
76
|
+
hx-get="/notes"
|
|
77
|
+
hx-target="#main-content"
|
|
78
|
+
hx-push-url="true"<%= typeof activePage !== 'undefined' && activePage === 'notes' ? ' aria-current="page"' : '' %>>
|
|
79
|
+
Notes
|
|
80
|
+
</a>
|
|
81
|
+
</li>
|
|
74
82
|
</ul>
|
|
75
83
|
</details>
|
|
76
84
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pbr",
|
|
3
3
|
"displayName": "Plan-Build-Run",
|
|
4
|
-
"version": "2.
|
|
4
|
+
"version": "2.18.1",
|
|
5
5
|
"description": "Plan-Build-Run — Structured development workflow for GitHub Copilot CLI. Solves context rot through disciplined agent delegation, structured planning, atomic execution, and goal-backward verification.",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "SienkLogic",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pbr",
|
|
3
3
|
"displayName": "Plan-Build-Run",
|
|
4
|
-
"version": "2.
|
|
4
|
+
"version": "2.18.1",
|
|
5
5
|
"description": "Plan-Build-Run — Structured development workflow for Cursor. Solves context rot through disciplined subagent delegation, structured planning, atomic execution, and goal-backward verification.",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "SienkLogic",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pbr",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.18.1",
|
|
4
4
|
"description": "Plan-Build-Run — Structured development workflow for Claude Code. Solves context rot through disciplined subagent delegation, structured planning, atomic execution, and goal-backward verification.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "SienkLogic",
|
|
@@ -190,28 +190,28 @@ function updateStatePosition(content, updates) {
|
|
|
190
190
|
const body = content.substring(fmEnd + 3);
|
|
191
191
|
|
|
192
192
|
if (updates.fmCurrentPhase !== undefined) {
|
|
193
|
-
fm = fm.replace(/^(current_phase:\s*).*/m, `$
|
|
193
|
+
fm = fm.replace(/^(current_phase:\s*).*/m, (_, p) => `${p}${updates.fmCurrentPhase}`);
|
|
194
194
|
}
|
|
195
195
|
if (updates.fmTotalPhases !== undefined) {
|
|
196
|
-
fm = fm.replace(/^(total_phases:\s*).*/m, `$
|
|
196
|
+
fm = fm.replace(/^(total_phases:\s*).*/m, (_, p) => `${p}${updates.fmTotalPhases}`);
|
|
197
197
|
}
|
|
198
198
|
if (updates.fmPhaseSlug !== undefined) {
|
|
199
|
-
fm = fm.replace(/^(phase_slug:\s*).*/m, `$
|
|
199
|
+
fm = fm.replace(/^(phase_slug:\s*).*/m, (_, p) => `${p}"${updates.fmPhaseSlug}"`);
|
|
200
200
|
}
|
|
201
201
|
if (updates.fmPhaseName !== undefined) {
|
|
202
|
-
fm = fm.replace(/^(phase_name:\s*).*/m, `$
|
|
202
|
+
fm = fm.replace(/^(phase_name:\s*).*/m, (_, p) => `${p}"${updates.fmPhaseName}"`);
|
|
203
203
|
}
|
|
204
204
|
if (updates.fmPlansComplete !== undefined) {
|
|
205
|
-
fm = fm.replace(/^(plans_complete:\s*).*/m, `$
|
|
205
|
+
fm = fm.replace(/^(plans_complete:\s*).*/m, (_, p) => `${p}${updates.fmPlansComplete}`);
|
|
206
206
|
}
|
|
207
207
|
if (updates.fmStatus !== undefined) {
|
|
208
|
-
fm = fm.replace(/^(status:\s*).*/m, `$
|
|
208
|
+
fm = fm.replace(/^(status:\s*).*/m, (_, p) => `${p}"${updates.fmStatus}"`);
|
|
209
209
|
}
|
|
210
210
|
if (updates.fmLastActivity !== undefined) {
|
|
211
|
-
fm = fm.replace(/^(last_activity:\s*).*/m, `$
|
|
211
|
+
fm = fm.replace(/^(last_activity:\s*).*/m, (_, p) => `${p}"${updates.fmLastActivity}"`);
|
|
212
212
|
}
|
|
213
213
|
if (updates.fmProgressPct !== undefined) {
|
|
214
|
-
fm = fm.replace(/^(progress_percent:\s*).*/m, `$
|
|
214
|
+
fm = fm.replace(/^(progress_percent:\s*).*/m, (_, p) => `${p}${updates.fmProgressPct}`);
|
|
215
215
|
}
|
|
216
216
|
|
|
217
217
|
// Reconstruct with updated frontmatter + body with line updates
|
|
@@ -80,7 +80,7 @@ function main() {
|
|
|
80
80
|
// Replace existing section
|
|
81
81
|
content = content.replace(
|
|
82
82
|
/## Session Continuity[\s\S]*?(?=\n## |\n---|\s*$)/,
|
|
83
|
-
`${continuityHeader}\n${continuityContent}\n`
|
|
83
|
+
() => `${continuityHeader}\n${continuityContent}\n`
|
|
84
84
|
);
|
|
85
85
|
} else {
|
|
86
86
|
// Append section
|
|
@@ -823,7 +823,7 @@ function updateLegacyStateField(content, field, value) {
|
|
|
823
823
|
case 'current_phase': {
|
|
824
824
|
const idx = lines.findIndex(l => /Phase:\s*\d+\s+of\s+\d+/.test(l));
|
|
825
825
|
if (idx !== -1) {
|
|
826
|
-
lines[idx] = lines[idx].replace(/(Phase:\s*)\d+/, `$
|
|
826
|
+
lines[idx] = lines[idx].replace(/(Phase:\s*)\d+/, (_, prefix) => `${prefix}${value}`);
|
|
827
827
|
}
|
|
828
828
|
break;
|
|
829
829
|
}
|
|
@@ -844,7 +844,7 @@ function updateLegacyStateField(content, field, value) {
|
|
|
844
844
|
case 'plans_complete': {
|
|
845
845
|
const idx = lines.findIndex(l => /Plan:\s*\d+\s+of\s+\d+/.test(l));
|
|
846
846
|
if (idx !== -1) {
|
|
847
|
-
lines[idx] = lines[idx].replace(/(Plan:\s*)\d+/, `$
|
|
847
|
+
lines[idx] = lines[idx].replace(/(Plan:\s*)\d+/, (_, prefix) => `${prefix}${value}`);
|
|
848
848
|
}
|
|
849
849
|
break;
|
|
850
850
|
}
|
|
@@ -886,7 +886,7 @@ function updateFrontmatterField(content, field, value) {
|
|
|
886
886
|
|
|
887
887
|
const fieldRegex = new RegExp(`^(${field})\\s*:.*$`, 'm');
|
|
888
888
|
if (fieldRegex.test(yaml)) {
|
|
889
|
-
yaml = yaml.replace(fieldRegex, `${field}: ${formatted}`);
|
|
889
|
+
yaml = yaml.replace(fieldRegex, () => `${field}: ${formatted}`);
|
|
890
890
|
} else {
|
|
891
891
|
yaml = yaml + `\n${field}: ${formatted}`;
|
|
892
892
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: build
|
|
3
3
|
description: "Execute all plans in a phase. Spawns agents to build in parallel, commits atomically."
|
|
4
|
-
allowed-tools: Read, Write, Edit, Bash, Glob, Grep, Task, AskUserQuestion
|
|
4
|
+
allowed-tools: Read, Write, Edit, Bash, Glob, Grep, Task, AskUserQuestion, Skill
|
|
5
5
|
argument-hint: "<phase-number> [--gaps-only] [--team]"
|
|
6
6
|
---
|
|
7
7
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: continue
|
|
3
3
|
description: "Execute the next logical step automatically. No prompts, no decisions — just do it."
|
|
4
|
-
allowed-tools: Read, Write, Bash, Glob, Grep, Task
|
|
4
|
+
allowed-tools: Read, Write, Bash, Glob, Grep, Task, Skill
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
**STOP — DO NOT READ THIS FILE. You are already reading it. This prompt was injected into your context by Claude Code's plugin system. Using the Read tool on this SKILL.md file wastes ~7,600 tokens. Begin executing Step 1 immediately.**
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: plan
|
|
3
3
|
description: "Create a detailed plan for a phase. Research, plan, and verify before building."
|
|
4
|
-
allowed-tools: Read, Write, Bash, Glob, Grep, WebFetch, WebSearch, Task, AskUserQuestion
|
|
4
|
+
allowed-tools: Read, Write, Bash, Glob, Grep, WebFetch, WebSearch, Task, AskUserQuestion, Skill
|
|
5
5
|
argument-hint: "<phase-number> [--skip-research] [--assumptions] [--gaps] | add | insert <N> | remove <N>"
|
|
6
6
|
---
|
|
7
7
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: review
|
|
3
3
|
description: "Verify the build matched the plan. Automated checks + walkthrough with you."
|
|
4
|
-
allowed-tools: Read, Write, Bash, Glob, Grep, Task, AskUserQuestion
|
|
4
|
+
allowed-tools: Read, Write, Bash, Glob, Grep, Task, AskUserQuestion, Skill
|
|
5
5
|
argument-hint: "<phase-number> [--auto-fix] [--teams]"
|
|
6
6
|
---
|
|
7
7
|
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
<%# This file is deprecated. See partials/layout-top.ejs and partials/layout-bottom.ejs %>
|