@sienklogic/plan-build-run 2.1.0 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +30 -0
- package/README.md +25 -6
- package/dashboard/public/css/layout.css +66 -0
- package/dashboard/src/repositories/planning.repository.js +2 -0
- package/dashboard/src/routes/pages.routes.js +56 -0
- package/dashboard/src/services/milestone.service.js +103 -0
- package/dashboard/src/views/milestone-detail.ejs +5 -0
- package/dashboard/src/views/milestones.ejs +5 -0
- package/dashboard/src/views/partials/head.ejs +10 -4
- package/dashboard/src/views/partials/milestone-detail-content.ejs +19 -0
- package/dashboard/src/views/partials/milestones-content.ejs +44 -0
- package/dashboard/src/views/partials/sidebar.ejs +8 -0
- package/package.json +1 -1
- package/plugins/cursor-pbr/.cursor-plugin/plugin.json +1 -1
- package/plugins/cursor-pbr/hooks/hooks.json +10 -0
- package/plugins/cursor-pbr/skills/dashboard/SKILL.md +22 -0
- package/plugins/cursor-pbr/skills/plan/SKILL.md +4 -2
- package/plugins/cursor-pbr/skills/todo/SKILL.md +42 -3
- package/plugins/pbr/.claude-plugin/plugin.json +1 -1
- package/plugins/pbr/agents/planner.md +2 -0
- package/plugins/pbr/commands/dashboard.md +5 -0
- package/plugins/pbr/hooks/hooks.json +10 -0
- package/plugins/pbr/references/ui-formatting.md +1 -1
- package/plugins/pbr/scripts/config-schema.json +8 -0
- package/plugins/pbr/scripts/progress-tracker.js +46 -1
- package/plugins/pbr/scripts/validate-skill-args.js +106 -0
- package/plugins/pbr/skills/begin/SKILL.md +4 -2
- package/plugins/pbr/skills/begin/templates/roadmap-prompt.md.tmpl +2 -0
- package/plugins/pbr/skills/build/SKILL.md +6 -4
- package/plugins/pbr/skills/continue/SKILL.md +2 -1
- package/plugins/pbr/skills/dashboard/SKILL.md +34 -0
- package/plugins/pbr/skills/plan/SKILL.md +4 -2
- package/plugins/pbr/skills/review/SKILL.md +4 -3
- package/plugins/pbr/skills/status/SKILL.md +5 -2
- package/plugins/pbr/skills/todo/SKILL.md +43 -4
- package/plugins/pbr/templates/ROADMAP.md.tmpl +6 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,36 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [2.2.0] - 2026-02-19
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- Milestone integration into workflow lifecycle — milestones are now created automatically during `/pbr:begin` roadmap generation
|
|
14
|
+
- Planner agent generates milestone-grouped roadmaps (single milestone for standard projects, multiple for 8+ phase comprehensive projects)
|
|
15
|
+
- Milestone-aware boundary detection in `/pbr:build` and `/pbr:review` — reads ROADMAP.md phase ranges instead of just "last phase overall"
|
|
16
|
+
- Between-milestones state handling in `/pbr:continue`
|
|
17
|
+
- Audit report detection in `/pbr:status` — suggests the correct next action based on whether an audit exists and its result
|
|
18
|
+
|
|
19
|
+
### Fixed
|
|
20
|
+
- "Skip audit" option in build/review completion banners now correctly says "archive milestone after audit passes" (consistent with milestone anti-pattern rules)
|
|
21
|
+
- `auto_advance` hard-stop message at milestone boundaries now explains why it paused
|
|
22
|
+
|
|
23
|
+
## [2.1.0] - 2026-02-19
|
|
24
|
+
|
|
25
|
+
### Added
|
|
26
|
+
- **Cursor IDE plugin** (`plugins/cursor-pbr/`) — complete port with 21 skills, 10 agents, and `.mdc` workflow rules
|
|
27
|
+
- Cross-plugin compatibility: shared `.planning/` state between Claude Code and Cursor plugins
|
|
28
|
+
- Setup scripts (`setup.sh` for macOS/Linux, `setup.ps1` for Windows) for easy Cursor plugin installation
|
|
29
|
+
- Summary gate hook (`check-summary-gate.js`) — enforces SUMMARY file before phase state can advance
|
|
30
|
+
- Cross-plugin compatibility test suite (7 tests)
|
|
31
|
+
- Cursor plugin validation test suite (92 tests)
|
|
32
|
+
- Companion web dashboard improvements: service and route enhancements
|
|
33
|
+
|
|
34
|
+
### Changed
|
|
35
|
+
- Agent definitions optimized — 48% average size reduction across all 10 agents
|
|
36
|
+
- Hook scripts improved with better error handling and dispatch logic
|
|
37
|
+
- Test count increased from 758 to 1008 across 42 suites
|
|
38
|
+
- Removed `.gitkeep` placeholder files from `cursor-pbr/` (replaced by real content)
|
|
39
|
+
|
|
10
40
|
## [2.0.0] - 2026-02-17
|
|
11
41
|
|
|
12
42
|
### Added
|
package/README.md
CHANGED
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
</p>
|
|
4
4
|
|
|
5
5
|
<p align="center">
|
|
6
|
-
<strong>Context-engineered development workflow for Claude Code.</strong>
|
|
6
|
+
<strong>Context-engineered development workflow for Claude Code and Cursor.</strong>
|
|
7
7
|
<br />
|
|
8
8
|
Build ambitious multi-phase software without quality degradation.
|
|
9
9
|
<br />
|
|
10
|
-
Works with any Claude Code plan. Shines on Max.
|
|
10
|
+
Works with any Claude Code plan. Shines on Max. Now available for Cursor IDE.
|
|
11
11
|
<br />
|
|
12
12
|
<br />
|
|
13
13
|
<a href="#why-plan-build-run">Why Plan-Build-Run?</a> •
|
|
@@ -21,10 +21,11 @@
|
|
|
21
21
|
<p align="center">
|
|
22
22
|
<a href="https://github.com/SienkLogic/plan-build-run/actions"><img src="https://img.shields.io/github/actions/workflow/status/SienkLogic/plan-build-run/ci.yml?style=for-the-badge&label=CI&logo=github" alt="CI Status" /></a>
|
|
23
23
|
<img src="https://img.shields.io/badge/Claude_Code-Plugin-7C3AED?style=for-the-badge&logo=anthropic&logoColor=white" alt="Claude Code Plugin" />
|
|
24
|
+
<img src="https://img.shields.io/badge/Cursor-Plugin-00A67E?style=for-the-badge&logo=cursor&logoColor=white" alt="Cursor Plugin" />
|
|
24
25
|
<img src="https://img.shields.io/badge/Node.js-18%2B-339933?style=for-the-badge&logo=node.js&logoColor=white" alt="Node.js 18+" />
|
|
25
26
|
<a href="LICENSE"><img src="https://img.shields.io/github/license/SienkLogic/plan-build-run?style=for-the-badge" alt="License" /></a>
|
|
26
27
|
<a href="https://www.npmjs.com/package/@sienklogic/plan-build-run"><img src="https://img.shields.io/npm/v/@sienklogic/plan-build-run?style=for-the-badge&logo=npm&logoColor=white" alt="npm" /></a>
|
|
27
|
-
<img src="https://img.shields.io/badge/Tests-
|
|
28
|
+
<img src="https://img.shields.io/badge/Tests-1008_passing-brightgreen?style=for-the-badge" alt="1008 Tests" />
|
|
28
29
|
</p>
|
|
29
30
|
|
|
30
31
|
---
|
|
@@ -87,6 +88,24 @@ All `/pbr:*` commands are now available globally.
|
|
|
87
88
|
|
|
88
89
|
</details>
|
|
89
90
|
|
|
91
|
+
### Install for Cursor IDE
|
|
92
|
+
|
|
93
|
+
Plan-Build-Run also works in Cursor. The setup script symlinks rules and agents into your project's `.cursor/` directory.
|
|
94
|
+
|
|
95
|
+
**macOS / Linux:**
|
|
96
|
+
```bash
|
|
97
|
+
cd /path/to/your/project
|
|
98
|
+
bash /path/to/plan-build-run/plugins/cursor-pbr/setup.sh
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
**Windows (PowerShell):**
|
|
102
|
+
```powershell
|
|
103
|
+
cd C:\path\to\your\project
|
|
104
|
+
powershell -ExecutionPolicy Bypass -File C:\path\to\plan-build-run\plugins\cursor-pbr\setup.ps1
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Both plugins share the same `.planning/` directory — start a project in Claude Code, continue in Cursor, or vice versa. See [`plugins/cursor-pbr/README.md`](plugins/cursor-pbr/README.md) for full details.
|
|
108
|
+
|
|
90
109
|
### Dashboard (Optional)
|
|
91
110
|
|
|
92
111
|
Plan-Build-Run ships with a companion web dashboard for browsing your project's planning state in a browser. To set it up:
|
|
@@ -212,7 +231,7 @@ git clone https://github.com/SienkLogic/plan-build-run.git
|
|
|
212
231
|
cd plan-build-run
|
|
213
232
|
npm install
|
|
214
233
|
|
|
215
|
-
# Run tests (
|
|
234
|
+
# Run tests (1008 tests, 42 suites)
|
|
216
235
|
npm test
|
|
217
236
|
|
|
218
237
|
# Lint
|
|
@@ -236,8 +255,8 @@ CI runs on Node 18/20/22 across Windows, macOS, and Linux. See [CONTRIBUTING.md]
|
|
|
236
255
|
| Skills (slash commands) | 21 |
|
|
237
256
|
| Specialized agents | 10 |
|
|
238
257
|
| Hook scripts | 28 |
|
|
239
|
-
| Tests |
|
|
240
|
-
| Test suites |
|
|
258
|
+
| Tests | 1008 |
|
|
259
|
+
| Test suites | 42 |
|
|
241
260
|
| Config toggles | 12 top-level keys |
|
|
242
261
|
|
|
243
262
|
---
|
|
@@ -267,6 +267,72 @@ details li {
|
|
|
267
267
|
background: rgba(255, 255, 255, 0.06);
|
|
268
268
|
}
|
|
269
269
|
|
|
270
|
+
/* --- Markdown Body (rendered markdown content) --- */
|
|
271
|
+
.markdown-body h1 { font-size: 1.5rem; margin-top: var(--space-xl); margin-bottom: var(--space-sm); }
|
|
272
|
+
.markdown-body h2 { font-size: 1.25rem; margin-top: var(--space-xl); margin-bottom: var(--space-sm); }
|
|
273
|
+
.markdown-body h3 { font-size: 1.05rem; margin-top: var(--space-lg); margin-bottom: var(--space-xs); }
|
|
274
|
+
|
|
275
|
+
.markdown-body table {
|
|
276
|
+
border-collapse: collapse;
|
|
277
|
+
width: 100%;
|
|
278
|
+
margin: var(--space-md) 0;
|
|
279
|
+
font-size: 0.875rem;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
.markdown-body th,
|
|
283
|
+
.markdown-body td {
|
|
284
|
+
border: 1px solid var(--border-subtle);
|
|
285
|
+
padding: var(--space-xs) var(--space-sm);
|
|
286
|
+
text-align: left;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
.markdown-body th {
|
|
290
|
+
background: rgba(255, 255, 255, 0.04);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
.markdown-body pre {
|
|
294
|
+
background: rgba(0, 0, 0, 0.3);
|
|
295
|
+
border-radius: var(--radius-sm);
|
|
296
|
+
padding: var(--space-md);
|
|
297
|
+
overflow-x: auto;
|
|
298
|
+
margin: var(--space-md) 0;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
.markdown-body pre code {
|
|
302
|
+
background: none;
|
|
303
|
+
padding: 0;
|
|
304
|
+
font-size: 0.85em;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
.markdown-body blockquote {
|
|
308
|
+
border-left: 3px solid var(--pico-primary);
|
|
309
|
+
background: rgba(255, 255, 255, 0.02);
|
|
310
|
+
margin: var(--space-md) 0;
|
|
311
|
+
padding: var(--space-sm) var(--space-md);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
.markdown-body ul.contains-task-list {
|
|
315
|
+
list-style: none;
|
|
316
|
+
padding-left: var(--space-sm);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
.markdown-body ul.contains-task-list li {
|
|
320
|
+
position: relative;
|
|
321
|
+
padding-left: var(--space-xs);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
.markdown-body img {
|
|
325
|
+
max-width: 100%;
|
|
326
|
+
height: auto;
|
|
327
|
+
border-radius: var(--radius-sm);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
.markdown-body hr {
|
|
331
|
+
border: none;
|
|
332
|
+
border-top: 1px solid var(--border-subtle);
|
|
333
|
+
margin: var(--space-lg) 0;
|
|
334
|
+
}
|
|
335
|
+
|
|
270
336
|
/* --- Back Link --- */
|
|
271
337
|
main > p:first-of-type > a[href="/"] {
|
|
272
338
|
font-size: 0.875rem;
|
|
@@ -3,6 +3,8 @@ import { join, resolve, relative, normalize } from 'node:path';
|
|
|
3
3
|
import matter from 'gray-matter';
|
|
4
4
|
import { marked } from 'marked';
|
|
5
5
|
|
|
6
|
+
marked.setOptions({ gfm: true, breaks: false });
|
|
7
|
+
|
|
6
8
|
/**
|
|
7
9
|
* Strip UTF-8 BOM (Byte Order Mark) if present.
|
|
8
10
|
* Windows editors (Notepad, older VS Code) may prepend BOM to UTF-8 files.
|
|
@@ -3,6 +3,7 @@ import { getPhaseDetail, getPhaseDocument } from '../services/phase.service.js';
|
|
|
3
3
|
import { getRoadmapData } from '../services/roadmap.service.js';
|
|
4
4
|
import { parseStateFile, derivePhaseStatuses } from '../services/dashboard.service.js';
|
|
5
5
|
import { listPendingTodos, getTodoDetail, createTodo, completeTodo } from '../services/todo.service.js';
|
|
6
|
+
import { getAllMilestones, getMilestoneDetail } from '../services/milestone.service.js';
|
|
6
7
|
|
|
7
8
|
const router = Router();
|
|
8
9
|
|
|
@@ -225,6 +226,61 @@ router.post('/todos/:id/done', async (req, res) => {
|
|
|
225
226
|
}
|
|
226
227
|
});
|
|
227
228
|
|
|
229
|
+
router.get('/milestones', async (req, res) => {
|
|
230
|
+
const projectDir = req.app.locals.projectDir;
|
|
231
|
+
const milestoneData = await getAllMilestones(projectDir);
|
|
232
|
+
|
|
233
|
+
const templateData = {
|
|
234
|
+
title: 'Milestones',
|
|
235
|
+
activePage: 'milestones',
|
|
236
|
+
currentPath: '/milestones',
|
|
237
|
+
...milestoneData
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
res.setHeader('Vary', 'HX-Request');
|
|
241
|
+
|
|
242
|
+
if (req.get('HX-Request') === 'true') {
|
|
243
|
+
res.render('partials/milestones-content', templateData);
|
|
244
|
+
} else {
|
|
245
|
+
res.render('milestones', templateData);
|
|
246
|
+
}
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
router.get('/milestones/:version', async (req, res) => {
|
|
250
|
+
const { version } = req.params;
|
|
251
|
+
|
|
252
|
+
// Validate version: alphanumeric with dots and dashes
|
|
253
|
+
if (!/^[\w.-]+$/.test(version)) {
|
|
254
|
+
const err = new Error('Invalid milestone version format');
|
|
255
|
+
err.status = 404;
|
|
256
|
+
throw err;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
const projectDir = req.app.locals.projectDir;
|
|
260
|
+
const detail = await getMilestoneDetail(projectDir, version);
|
|
261
|
+
|
|
262
|
+
if (detail.sections.length === 0) {
|
|
263
|
+
const err = new Error(`No archived files found for milestone v${version}`);
|
|
264
|
+
err.status = 404;
|
|
265
|
+
throw err;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
const templateData = {
|
|
269
|
+
title: `Milestone v${version}`,
|
|
270
|
+
activePage: 'milestones',
|
|
271
|
+
currentPath: '/milestones/' + version,
|
|
272
|
+
...detail
|
|
273
|
+
};
|
|
274
|
+
|
|
275
|
+
res.setHeader('Vary', 'HX-Request');
|
|
276
|
+
|
|
277
|
+
if (req.get('HX-Request') === 'true') {
|
|
278
|
+
res.render('partials/milestone-detail-content', templateData);
|
|
279
|
+
} else {
|
|
280
|
+
res.render('milestone-detail', templateData);
|
|
281
|
+
}
|
|
282
|
+
});
|
|
283
|
+
|
|
228
284
|
router.get('/roadmap', async (req, res) => {
|
|
229
285
|
const projectDir = req.app.locals.projectDir;
|
|
230
286
|
const [roadmapData, stateData] = await Promise.all([
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { readdir } from 'node:fs/promises';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { readMarkdownFile } from '../repositories/planning.repository.js';
|
|
4
|
+
import { getRoadmapData } from './roadmap.service.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Scan .planning/milestones/ for archived milestone files.
|
|
8
|
+
* Groups files by version prefix (e.g., v1.0-ROADMAP.md, v1.0-STATS.md).
|
|
9
|
+
*
|
|
10
|
+
* @param {string} projectDir - Absolute path to the project root
|
|
11
|
+
* @returns {Promise<Array<{version: string, name: string, date: string, duration: string, files: string[]}>>}
|
|
12
|
+
*/
|
|
13
|
+
export async function listArchivedMilestones(projectDir) {
|
|
14
|
+
const milestonesDir = join(projectDir, '.planning', 'milestones');
|
|
15
|
+
|
|
16
|
+
let entries;
|
|
17
|
+
try {
|
|
18
|
+
entries = await readdir(milestonesDir);
|
|
19
|
+
} catch (err) {
|
|
20
|
+
if (err.code === 'ENOENT') return [];
|
|
21
|
+
throw err;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Group files by version prefix: v1.0-ROADMAP.md -> version "1.0"
|
|
25
|
+
const versionMap = new Map();
|
|
26
|
+
const versionPattern = /^v([\w.-]+)-(\w+)\.md$/;
|
|
27
|
+
|
|
28
|
+
for (const file of entries) {
|
|
29
|
+
const match = file.match(versionPattern);
|
|
30
|
+
if (!match) continue;
|
|
31
|
+
|
|
32
|
+
const version = match[1];
|
|
33
|
+
if (!versionMap.has(version)) {
|
|
34
|
+
versionMap.set(version, { version, name: '', date: '', duration: '', files: [] });
|
|
35
|
+
}
|
|
36
|
+
versionMap.get(version).files.push(file);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Try to parse STATS.md for each version to get name/date/duration
|
|
40
|
+
for (const [version, milestone] of versionMap) {
|
|
41
|
+
const statsFile = `v${version}-STATS.md`;
|
|
42
|
+
if (milestone.files.includes(statsFile)) {
|
|
43
|
+
try {
|
|
44
|
+
const { frontmatter } = await readMarkdownFile(join(milestonesDir, statsFile));
|
|
45
|
+
milestone.name = frontmatter.milestone || frontmatter.name || `v${version}`;
|
|
46
|
+
milestone.date = frontmatter.completed || frontmatter.date || '';
|
|
47
|
+
milestone.duration = frontmatter.duration || '';
|
|
48
|
+
} catch (_e) {
|
|
49
|
+
milestone.name = `v${version}`;
|
|
50
|
+
}
|
|
51
|
+
} else {
|
|
52
|
+
milestone.name = `v${version}`;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Sort by version descending (newest first)
|
|
57
|
+
return [...versionMap.values()].sort((a, b) => b.version.localeCompare(a.version, undefined, { numeric: true }));
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Combine active milestones from ROADMAP.md with archived milestones.
|
|
62
|
+
*
|
|
63
|
+
* @param {string} projectDir - Absolute path to the project root
|
|
64
|
+
* @returns {Promise<{active: Array, archived: Array}>}
|
|
65
|
+
*/
|
|
66
|
+
export async function getAllMilestones(projectDir) {
|
|
67
|
+
const [roadmapData, archived] = await Promise.all([
|
|
68
|
+
getRoadmapData(projectDir),
|
|
69
|
+
listArchivedMilestones(projectDir)
|
|
70
|
+
]);
|
|
71
|
+
|
|
72
|
+
return {
|
|
73
|
+
active: roadmapData.milestones || [],
|
|
74
|
+
archived
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Read all archived files for a specific milestone version.
|
|
80
|
+
* Returns rendered HTML for each file type (ROADMAP, STATS, REQUIREMENTS).
|
|
81
|
+
*
|
|
82
|
+
* @param {string} projectDir - Absolute path to the project root
|
|
83
|
+
* @param {string} version - Milestone version (e.g., "1.0")
|
|
84
|
+
* @returns {Promise<{version: string, sections: Array<{type: string, frontmatter: object, html: string}>}>}
|
|
85
|
+
*/
|
|
86
|
+
export async function getMilestoneDetail(projectDir, version) {
|
|
87
|
+
const milestonesDir = join(projectDir, '.planning', 'milestones');
|
|
88
|
+
const fileTypes = ['ROADMAP', 'STATS', 'REQUIREMENTS'];
|
|
89
|
+
|
|
90
|
+
const sections = [];
|
|
91
|
+
for (const type of fileTypes) {
|
|
92
|
+
const filePath = join(milestonesDir, `v${version}-${type}.md`);
|
|
93
|
+
try {
|
|
94
|
+
const result = await readMarkdownFile(filePath);
|
|
95
|
+
sections.push({ type, frontmatter: result.frontmatter, html: result.html });
|
|
96
|
+
} catch (err) {
|
|
97
|
+
if (err.code !== 'ENOENT') throw err;
|
|
98
|
+
// File doesn't exist for this type — skip
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return { version, sections };
|
|
103
|
+
}
|
|
@@ -4,10 +4,16 @@
|
|
|
4
4
|
<title><%= typeof title !== 'undefined' ? title : 'Plan-Build-Run' %> - Plan-Build-Run</title>
|
|
5
5
|
<link rel="preconnect" href="https://cdn.jsdelivr.net" crossorigin>
|
|
6
6
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.min.css">
|
|
7
|
-
<link rel="stylesheet" href="https://cdn.jsdelivr.net/fontsource/fonts/inter@latest/latin-400-normal.min.css">
|
|
8
|
-
<link rel="stylesheet" href="https://cdn.jsdelivr.net/fontsource/fonts/inter@latest/latin-600-normal.min.css">
|
|
9
|
-
<link rel="stylesheet" href="https://cdn.jsdelivr.net/fontsource/fonts/inter@latest/latin-700-normal.min.css">
|
|
10
|
-
<link rel="stylesheet" href="https://cdn.jsdelivr.net/fontsource/fonts/jetbrains-mono@latest/latin-400-normal.min.css">
|
|
7
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/fontsource/fonts/inter@latest/latin-400-normal.min.css" media="print" onload="this.media='all'">
|
|
8
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/fontsource/fonts/inter@latest/latin-600-normal.min.css" media="print" onload="this.media='all'">
|
|
9
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/fontsource/fonts/inter@latest/latin-700-normal.min.css" media="print" onload="this.media='all'">
|
|
10
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/fontsource/fonts/jetbrains-mono@latest/latin-400-normal.min.css" media="print" onload="this.media='all'">
|
|
11
|
+
<noscript>
|
|
12
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/fontsource/fonts/inter@latest/latin-400-normal.min.css">
|
|
13
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/fontsource/fonts/inter@latest/latin-600-normal.min.css">
|
|
14
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/fontsource/fonts/inter@latest/latin-700-normal.min.css">
|
|
15
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/fontsource/fonts/jetbrains-mono@latest/latin-400-normal.min.css">
|
|
16
|
+
</noscript>
|
|
11
17
|
<link rel="stylesheet" href="/css/layout.css">
|
|
12
18
|
<link rel="stylesheet" href="/css/status-colors.css">
|
|
13
19
|
<script
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<h1>Milestone v<%= version %></h1>
|
|
2
|
+
|
|
3
|
+
<p>
|
|
4
|
+
<a href="/milestones"
|
|
5
|
+
hx-get="/milestones"
|
|
6
|
+
hx-target="#main-content"
|
|
7
|
+
hx-push-url="true">← Back to Milestones</a>
|
|
8
|
+
</p>
|
|
9
|
+
|
|
10
|
+
<% sections.forEach(function(section) { %>
|
|
11
|
+
<article>
|
|
12
|
+
<header>
|
|
13
|
+
<strong><%= section.type %></strong>
|
|
14
|
+
</header>
|
|
15
|
+
<div class="markdown-body">
|
|
16
|
+
<%- section.html %>
|
|
17
|
+
</div>
|
|
18
|
+
</article>
|
|
19
|
+
<% }); %>
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
<h1>Milestones</h1>
|
|
2
|
+
|
|
3
|
+
<% if (active.length > 0) { %>
|
|
4
|
+
<h2>Active</h2>
|
|
5
|
+
<% active.forEach(function(m) { %>
|
|
6
|
+
<article>
|
|
7
|
+
<header>
|
|
8
|
+
<strong><%= m.name %></strong>
|
|
9
|
+
<% if (m.goal) { %><small> — <%= m.goal %></small><% } %>
|
|
10
|
+
</header>
|
|
11
|
+
<p>
|
|
12
|
+
Phases <%= m.startPhase %> – <%= m.endPhase %>
|
|
13
|
+
</p>
|
|
14
|
+
</article>
|
|
15
|
+
<% }); %>
|
|
16
|
+
<% } %>
|
|
17
|
+
|
|
18
|
+
<% if (archived.length > 0) { %>
|
|
19
|
+
<h2>Archived</h2>
|
|
20
|
+
<% archived.forEach(function(m) { %>
|
|
21
|
+
<article>
|
|
22
|
+
<header>
|
|
23
|
+
<strong>
|
|
24
|
+
<a href="/milestones/<%= m.version %>"
|
|
25
|
+
hx-get="/milestones/<%= m.version %>"
|
|
26
|
+
hx-target="#main-content"
|
|
27
|
+
hx-push-url="true">
|
|
28
|
+
v<%= m.version %> — <%= m.name %>
|
|
29
|
+
</a>
|
|
30
|
+
</strong>
|
|
31
|
+
</header>
|
|
32
|
+
<p>
|
|
33
|
+
<% if (m.date) { %><small>Completed: <%= m.date %></small><% } %>
|
|
34
|
+
<% if (m.duration) { %><small> • Duration: <%= m.duration %></small><% } %>
|
|
35
|
+
<br>
|
|
36
|
+
<small><%= m.files.length %> archived file<%= m.files.length !== 1 ? 's' : '' %></small>
|
|
37
|
+
</p>
|
|
38
|
+
</article>
|
|
39
|
+
<% }); %>
|
|
40
|
+
<% } %>
|
|
41
|
+
|
|
42
|
+
<% if (active.length === 0 && archived.length === 0) { %>
|
|
43
|
+
<p>No milestones found. Use <code>/pbr:milestone new</code> to create one.</p>
|
|
44
|
+
<% } %>
|
|
@@ -33,6 +33,14 @@
|
|
|
33
33
|
Roadmap
|
|
34
34
|
</a>
|
|
35
35
|
</li>
|
|
36
|
+
<li>
|
|
37
|
+
<a href="/milestones"
|
|
38
|
+
hx-get="/milestones"
|
|
39
|
+
hx-target="#main-content"
|
|
40
|
+
hx-push-url="true"<%= typeof activePage !== 'undefined' && activePage === 'milestones' ? ' aria-current="page"' : '' %>>
|
|
41
|
+
Milestones
|
|
42
|
+
</a>
|
|
43
|
+
</li>
|
|
36
44
|
</ul>
|
|
37
45
|
</nav>
|
|
38
46
|
</aside>
|
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.3.0",
|
|
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",
|
|
@@ -106,6 +106,16 @@
|
|
|
106
106
|
"statusMessage": "Validating Task() call..."
|
|
107
107
|
}
|
|
108
108
|
]
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
"matcher": "Skill",
|
|
112
|
+
"hooks": [
|
|
113
|
+
{
|
|
114
|
+
"type": "command",
|
|
115
|
+
"command": "node -e \"var r=process.env.CLAUDE_PLUGIN_ROOT||'',m=r.match(/^\\/([a-zA-Z])\\/(.*)/);if(m)r=m[1]+String.fromCharCode(58)+String.fromCharCode(92)+m[2];require(require('path').resolve(r,'..','pbr','scripts','run-hook.js'))\" validate-skill-args.js",
|
|
116
|
+
"statusMessage": "Validating skill arguments..."
|
|
117
|
+
}
|
|
118
|
+
]
|
|
109
119
|
}
|
|
110
120
|
],
|
|
111
121
|
"PreCompact": [
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: dashboard
|
|
3
|
+
description: "Launch the PBR web dashboard for the current project."
|
|
4
|
+
argument-hint: "[--port N]"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Behavior
|
|
8
|
+
|
|
9
|
+
1. **Parse arguments**: Extract `--port N` from the user's input. Default to `3000`.
|
|
10
|
+
|
|
11
|
+
2. **Check dependencies**: Check if the dashboard `node_modules/` exists in the plugin's dashboard directory. If not, run `npm install` in that directory.
|
|
12
|
+
|
|
13
|
+
3. **Launch dashboard**: Run in background:
|
|
14
|
+
```
|
|
15
|
+
node <plugin-root>/dashboard/bin/cli.js --dir <cwd> --port <port> &
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
4. **Output to user**:
|
|
19
|
+
```
|
|
20
|
+
Dashboard running at http://localhost:<port>
|
|
21
|
+
Open this URL in your browser to view your project's planning state.
|
|
22
|
+
```
|
|
@@ -63,9 +63,9 @@ Parse the phase number and optional flags:
|
|
|
63
63
|
| `insert <N>` | Insert a new phase at position N (uses decimal numbering) |
|
|
64
64
|
| `remove <N>` | Remove phase N from the roadmap |
|
|
65
65
|
|
|
66
|
-
### Freeform Text Guard
|
|
66
|
+
### Freeform Text Guard — CRITICAL
|
|
67
67
|
|
|
68
|
-
**Before
|
|
68
|
+
**STOP. Before ANY context loading or Step 1 work**, you MUST check whether `$ARGUMENTS` looks like freeform text rather than a valid invocation. This check is non-negotiable. Valid patterns are:
|
|
69
69
|
|
|
70
70
|
- Empty (no arguments)
|
|
71
71
|
- A phase number: integer (`3`, `03`) or decimal (`3.1`)
|
|
@@ -97,6 +97,8 @@ Then suggest the appropriate skill based on the text content:
|
|
|
97
97
|
|
|
98
98
|
Do NOT proceed with planning. The user needs to use the correct skill.
|
|
99
99
|
|
|
100
|
+
**Self-check**: If you reach Step 1 without having matched a valid argument pattern above, you have a bug. Stop immediately and show the usage block.
|
|
101
|
+
|
|
100
102
|
---
|
|
101
103
|
|
|
102
104
|
## Orchestration Flow: Standard Planning
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: todo
|
|
3
3
|
description: "File-based persistent todos. Add, list, complete — survives sessions."
|
|
4
|
-
argument-hint: "add <description> | list [theme] | done <NNN>"
|
|
4
|
+
argument-hint: "add <description> | list [theme] | done <NNN> | work <NNN>"
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
## Step 0 — Immediate Output
|
|
@@ -121,12 +121,12 @@ Pending Todos:
|
|
|
121
121
|
|
|
122
122
|
**Pick a todo** — mark one done or start working
|
|
123
123
|
|
|
124
|
-
`/pbr:todo
|
|
124
|
+
`/pbr:todo work <NNN>` — start working on a todo
|
|
125
|
+
`/pbr:todo done <NNN>` — mark a todo as complete
|
|
125
126
|
|
|
126
127
|
───────────────────────────────────────────────────────────────
|
|
127
128
|
|
|
128
129
|
**Also available:**
|
|
129
|
-
- `/pbr:quick` — work on one now
|
|
130
130
|
- `/pbr:status` — see project status
|
|
131
131
|
|
|
132
132
|
───────────────────────────────────────────────────────────────
|
|
@@ -178,6 +178,45 @@ Todo {NNN} not found in pending todos.
|
|
|
178
178
|
───────────────────────────────────────────────────────────────
|
|
179
179
|
```
|
|
180
180
|
|
|
181
|
+
### `work <NNN>`
|
|
182
|
+
|
|
183
|
+
1. Find `.planning/todos/pending/{NNN}-*.md` (match by number prefix)
|
|
184
|
+
2. If not found, display the same error block as `done` — suggest `/pbr:todo list`
|
|
185
|
+
3. Read the todo file content (frontmatter + body)
|
|
186
|
+
4. Extract the `title` from frontmatter and the full body (Goal, Scope, Acceptance Criteria sections)
|
|
187
|
+
5. Display branded output:
|
|
188
|
+
```
|
|
189
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
190
|
+
PLAN-BUILD-RUN ► WORKING ON TODO {NNN}
|
|
191
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
192
|
+
|
|
193
|
+
**Todo {NNN}:** {title}
|
|
194
|
+
|
|
195
|
+
Launching /pbr:quick with todo context...
|
|
196
|
+
```
|
|
197
|
+
6. Invoke the Skill tool: `skill: "pbr:quick"` with `args` set to the todo title followed by the body content. Format the args as:
|
|
198
|
+
|
|
199
|
+
```
|
|
200
|
+
{title}
|
|
201
|
+
|
|
202
|
+
Context from todo {NNN}:
|
|
203
|
+
{body content — Goal, Scope, Acceptance Criteria sections}
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
This hands off execution to `/pbr:quick`, which will spawn an executor agent, make atomic commits, and track the work. When quick completes, remind the user:
|
|
207
|
+
|
|
208
|
+
```
|
|
209
|
+
───────────────────────────────────────────────────────────────
|
|
210
|
+
|
|
211
|
+
## ▶ Next Up
|
|
212
|
+
|
|
213
|
+
**Mark this todo as done if the work is complete**
|
|
214
|
+
|
|
215
|
+
`/pbr:todo done {NNN}`
|
|
216
|
+
|
|
217
|
+
───────────────────────────────────────────────────────────────
|
|
218
|
+
```
|
|
219
|
+
|
|
181
220
|
### No arguments
|
|
182
221
|
|
|
183
222
|
Show a brief summary: count of pending todos, grouped by theme, plus usage hint.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pbr",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.0",
|
|
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",
|
|
@@ -38,6 +38,8 @@ Invoked with plan-checker feedback containing issues. Revise flagged plan(s) to
|
|
|
38
38
|
### Mode 4: Roadmap Mode
|
|
39
39
|
Invoked with a request to create/update the project roadmap. Produce `.planning/ROADMAP.md` using the template at `${CLAUDE_PLUGIN_ROOT}/templates/ROADMAP.md.tmpl`.
|
|
40
40
|
|
|
41
|
+
**Milestone grouping:** All phases in the initial roadmap MUST be wrapped in a `## Milestone: {project name} v1.0` section. This section includes `**Goal:**` and `**Phases:** 1 - {N}`, followed by the `### Phase NN:` details. For comprehensive-depth projects (8+ phases), consider splitting into multiple milestones if there are natural delivery boundaries (e.g., "Core Platform" phases 1-5, "Advanced Features" phases 6-10). Each milestone section follows the format defined in the roadmap template.
|
|
42
|
+
|
|
41
43
|
---
|
|
42
44
|
|
|
43
45
|
## Goal-Backward Methodology
|
|
@@ -106,6 +106,16 @@
|
|
|
106
106
|
"statusMessage": "Validating Task() call..."
|
|
107
107
|
}
|
|
108
108
|
]
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
"matcher": "Skill",
|
|
112
|
+
"hooks": [
|
|
113
|
+
{
|
|
114
|
+
"type": "command",
|
|
115
|
+
"command": "node -e \"var r=process.env.CLAUDE_PLUGIN_ROOT||'',m=r.match(/^\\/([a-zA-Z])\\/(.*)/);if(m)r=m[1]+String.fromCharCode(58)+String.fromCharCode(92)+m[2];require(require('path').resolve(r,'scripts','run-hook.js'))\" validate-skill-args.js",
|
|
116
|
+
"statusMessage": "Validating skill arguments..."
|
|
117
|
+
}
|
|
118
|
+
]
|
|
109
119
|
}
|
|
110
120
|
],
|
|
111
121
|
"PreCompact": [
|
|
@@ -387,7 +387,7 @@ All phase goals verified ✓
|
|
|
387
387
|
|
|
388
388
|
**Also available:**
|
|
389
389
|
- /pbr:review — manual acceptance testing
|
|
390
|
-
- /pbr:milestone complete —
|
|
390
|
+
- /pbr:milestone complete — archive milestone after audit passes
|
|
391
391
|
|
|
392
392
|
───────────────────────────────────────────────────────────────
|
|
393
393
|
```
|
|
@@ -211,6 +211,14 @@
|
|
|
211
211
|
},
|
|
212
212
|
"additionalProperties": false
|
|
213
213
|
},
|
|
214
|
+
"dashboard": {
|
|
215
|
+
"type": "object",
|
|
216
|
+
"properties": {
|
|
217
|
+
"auto_launch": { "type": "boolean" },
|
|
218
|
+
"port": { "type": "integer", "minimum": 1024, "maximum": 65535 }
|
|
219
|
+
},
|
|
220
|
+
"additionalProperties": false
|
|
221
|
+
},
|
|
214
222
|
"status_line": {
|
|
215
223
|
"type": "object",
|
|
216
224
|
"properties": {
|
|
@@ -32,6 +32,12 @@ function main() {
|
|
|
32
32
|
|
|
33
33
|
const context = buildContext(planningDir, stateFile);
|
|
34
34
|
|
|
35
|
+
// Auto-launch dashboard if configured
|
|
36
|
+
const config = configLoad(planningDir);
|
|
37
|
+
if (config && config.dashboard && config.dashboard.auto_launch) {
|
|
38
|
+
tryLaunchDashboard(config.dashboard.port || 3000, planningDir, cwd);
|
|
39
|
+
}
|
|
40
|
+
|
|
35
41
|
if (context) {
|
|
36
42
|
const output = {
|
|
37
43
|
additionalContext: context
|
|
@@ -275,7 +281,46 @@ function getHookHealthSummary(planningDir) {
|
|
|
275
281
|
}
|
|
276
282
|
}
|
|
277
283
|
|
|
284
|
+
/**
|
|
285
|
+
* Attempt to launch the dashboard in a detached background process.
|
|
286
|
+
* Checks if the port is already in use before spawning.
|
|
287
|
+
*/
|
|
288
|
+
function tryLaunchDashboard(port, _planningDir, projectDir) {
|
|
289
|
+
const net = require('net');
|
|
290
|
+
const { spawn } = require('child_process');
|
|
291
|
+
|
|
292
|
+
// Quick port probe — if something is already listening, skip launch
|
|
293
|
+
const probe = net.createConnection({ port, host: '127.0.0.1' });
|
|
294
|
+
probe.on('connect', () => {
|
|
295
|
+
probe.destroy();
|
|
296
|
+
logHook('progress-tracker', 'SessionStart', 'dashboard-already-running', { port });
|
|
297
|
+
});
|
|
298
|
+
probe.on('error', () => {
|
|
299
|
+
// Port is free — launch dashboard
|
|
300
|
+
const cliPath = path.join(__dirname, '..', 'dashboard', 'bin', 'cli.js');
|
|
301
|
+
if (!fs.existsSync(cliPath)) {
|
|
302
|
+
logHook('progress-tracker', 'SessionStart', 'dashboard-cli-missing', { cliPath });
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
try {
|
|
307
|
+
const child = spawn(process.execPath, [cliPath, '--dir', projectDir, '--port', String(port)], {
|
|
308
|
+
detached: true,
|
|
309
|
+
stdio: 'ignore',
|
|
310
|
+
cwd: projectDir
|
|
311
|
+
});
|
|
312
|
+
child.unref();
|
|
313
|
+
logHook('progress-tracker', 'SessionStart', 'dashboard-launched', { port, pid: child.pid });
|
|
314
|
+
} catch (e) {
|
|
315
|
+
logHook('progress-tracker', 'SessionStart', 'dashboard-launch-error', { error: e.message });
|
|
316
|
+
}
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
// Don't let the probe keep the process alive
|
|
320
|
+
probe.unref();
|
|
321
|
+
}
|
|
322
|
+
|
|
278
323
|
// Exported for testing
|
|
279
|
-
module.exports = { getHookHealthSummary, FAILURE_DECISIONS, HOOK_HEALTH_MAX_ENTRIES };
|
|
324
|
+
module.exports = { getHookHealthSummary, FAILURE_DECISIONS, HOOK_HEALTH_MAX_ENTRIES, tryLaunchDashboard };
|
|
280
325
|
|
|
281
326
|
main();
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* PreToolUse hook: Validates Skill tool arguments before execution.
|
|
5
|
+
*
|
|
6
|
+
* Currently validates:
|
|
7
|
+
* - /pbr:plan — blocks freeform text arguments that don't match
|
|
8
|
+
* valid patterns (phase number, subcommand, flags).
|
|
9
|
+
*
|
|
10
|
+
* This provides structural enforcement that catches cases where the
|
|
11
|
+
* LLM ignores the prompt-based freeform text guard in SKILL.md.
|
|
12
|
+
*
|
|
13
|
+
* Exit codes:
|
|
14
|
+
* 0 = allowed (valid args or non-plan skill)
|
|
15
|
+
* 2 = blocked (freeform text detected for /pbr:plan)
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
const { logHook } = require('./hook-logger');
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Valid argument patterns for /pbr:plan.
|
|
22
|
+
*
|
|
23
|
+
* Matches:
|
|
24
|
+
* - Empty / whitespace only
|
|
25
|
+
* - Phase number: "3", "03", "3.1"
|
|
26
|
+
* - Phase number + flags: "3 --skip-research", "3 --assumptions", "3 --gaps", "3 --teams"
|
|
27
|
+
* - Subcommands: "add", "insert 3", "remove 3"
|
|
28
|
+
* - Legacy: "check"
|
|
29
|
+
*/
|
|
30
|
+
const PLAN_VALID_PATTERN = /^\s*$|^\s*\d+(\.\d+)?\s*(--(?:skip-research|assumptions|gaps|teams)\s*)*$|^\s*(?:add|check)\s*$|^\s*(?:insert|remove)\s+\d+(\.\d+)?\s*$/i;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Check whether a Skill tool call has valid arguments.
|
|
34
|
+
* Returns null if valid, or { output, exitCode } if blocked.
|
|
35
|
+
*/
|
|
36
|
+
function checkSkillArgs(data) {
|
|
37
|
+
const toolInput = data.tool_input || {};
|
|
38
|
+
const skill = toolInput.skill || '';
|
|
39
|
+
const args = toolInput.args || '';
|
|
40
|
+
|
|
41
|
+
// Only validate /pbr:plan for now
|
|
42
|
+
if (skill !== 'pbr:plan') {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Test against valid patterns
|
|
47
|
+
if (PLAN_VALID_PATTERN.test(args)) {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Freeform text detected — block
|
|
52
|
+
logHook('validate-skill-args', 'PreToolUse', 'blocked', {
|
|
53
|
+
skill,
|
|
54
|
+
args: args.substring(0, 100),
|
|
55
|
+
reason: 'freeform-text'
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
return {
|
|
59
|
+
output: {
|
|
60
|
+
additionalContext: [
|
|
61
|
+
'BLOCKED: /pbr:plan received freeform text instead of a phase number.',
|
|
62
|
+
'',
|
|
63
|
+
'The arguments "' + args.substring(0, 80) + (args.length > 80 ? '...' : '') + '" do not match any valid pattern.',
|
|
64
|
+
'',
|
|
65
|
+
'Valid usage:',
|
|
66
|
+
' /pbr:plan <N> Plan phase N',
|
|
67
|
+
' /pbr:plan <N> --gaps Create gap-closure plans',
|
|
68
|
+
' /pbr:plan add Add a new phase',
|
|
69
|
+
' /pbr:plan insert <N> Insert a phase at position N',
|
|
70
|
+
' /pbr:plan remove <N> Remove phase N',
|
|
71
|
+
'',
|
|
72
|
+
'For freeform tasks, use:',
|
|
73
|
+
' /pbr:todo add <description> Capture as a todo',
|
|
74
|
+
' /pbr:quick <description> Execute immediately',
|
|
75
|
+
' /pbr:explore <topic> Investigate an idea'
|
|
76
|
+
].join('\n')
|
|
77
|
+
},
|
|
78
|
+
exitCode: 2
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function main() {
|
|
83
|
+
let input = '';
|
|
84
|
+
|
|
85
|
+
process.stdin.setEncoding('utf8');
|
|
86
|
+
process.stdin.on('data', (chunk) => { input += chunk; });
|
|
87
|
+
process.stdin.on('end', () => {
|
|
88
|
+
try {
|
|
89
|
+
const data = JSON.parse(input);
|
|
90
|
+
const result = checkSkillArgs(data);
|
|
91
|
+
|
|
92
|
+
if (result) {
|
|
93
|
+
process.stdout.write(JSON.stringify(result.output));
|
|
94
|
+
process.exit(result.exitCode);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
process.exit(0);
|
|
98
|
+
} catch (_e) {
|
|
99
|
+
// Don't block on errors
|
|
100
|
+
process.exit(0);
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
module.exports = { checkSkillArgs, PLAN_VALID_PATTERN };
|
|
106
|
+
if (require.main === module || process.argv[1] === __filename) { main(); }
|
|
@@ -379,10 +379,11 @@ Read `skills/begin/templates/roadmap-prompt.md.tmpl` for the prompt structure.
|
|
|
379
379
|
|
|
380
380
|
**After the planner completes:**
|
|
381
381
|
- Read `.planning/ROADMAP.md`
|
|
382
|
-
- Count the phases
|
|
382
|
+
- Count the phases from the roadmap content
|
|
383
|
+
- Verify the roadmap contains a `## Milestone:` section wrapping the phases (the planner should generate this). If not, the initial set of phases constitutes the first milestone — add the section header yourself.
|
|
383
384
|
- Display:
|
|
384
385
|
```
|
|
385
|
-
✓ Roadmap created — {N} phases
|
|
386
|
+
✓ Roadmap created — {N} phases in milestone "{name}"
|
|
386
387
|
```
|
|
387
388
|
- If `gates.confirm_roadmap` is true in config, use the **approve-revise-abort** pattern from `skills/shared/gate-prompts.md`:
|
|
388
389
|
question: "Approve this roadmap?"
|
|
@@ -544,6 +545,7 @@ Then use the "Next Up" routing block:
|
|
|
544
545
|
**Also available:**
|
|
545
546
|
- `/pbr:explore` — open-ended exploration before planning
|
|
546
547
|
- `/pbr:plan 1` — jump straight to planning Phase 1
|
|
548
|
+
- `/pbr:milestone new` — add a second milestone with new phases
|
|
547
549
|
- `/pbr:config` — adjust workflow settings
|
|
548
550
|
|
|
549
551
|
───────────────────────────────────────────────────────────────
|
|
@@ -24,6 +24,8 @@ Each phase should:
|
|
|
24
24
|
3. Build on prior phases logically
|
|
25
25
|
4. Be independently verifiable
|
|
26
26
|
|
|
27
|
+
**Milestone structure:** Wrap all phases in a `## Milestone:` section. For comprehensive-depth projects with 8+ phases, consider splitting into multiple milestones at natural delivery boundaries. Use the milestone section format from the roadmap template (`## Milestone: {name}` with `**Goal:**` and `**Phases:** start - end`).
|
|
28
|
+
|
|
27
29
|
Write the roadmap to .planning/ROADMAP.md using the roadmap format from your agent definition.
|
|
28
30
|
</roadmap_instructions>
|
|
29
31
|
|
|
@@ -744,7 +744,7 @@ Chain to the next skill directly within this session. This eliminates manual pha
|
|
|
744
744
|
| Verification passed, more phases | Plan next phase | `Skill({ skill: "pbr:plan", args: "{N+1}" })` |
|
|
745
745
|
| Verification skipped | Run review | `Skill({ skill: "pbr:review", args: "{N}" })` |
|
|
746
746
|
| Verification gaps found | **HARD STOP** — present gaps to user | Do NOT auto-advance past failures |
|
|
747
|
-
| Last phase
|
|
747
|
+
| Last phase in current milestone | **HARD STOP** — milestone boundary | Suggest `/pbr:milestone audit`. Explain: "auto_advance pauses at milestone boundaries — your sign-off is required." |
|
|
748
748
|
| Build errors occurred | **HARD STOP** — errors need human review | Do NOT auto-advance past errors |
|
|
749
749
|
|
|
750
750
|
After invoking the chained skill, it runs within the same session. When it completes, the chained skill may itself chain further (review→plan, plan→build) if auto_advance remains true. This creates the full cycle: build→review→plan→build→...
|
|
@@ -759,8 +759,10 @@ Use the branded output templates from `references/ui-formatting.md`. Route based
|
|
|
759
759
|
|
|
760
760
|
| Status | Template |
|
|
761
761
|
|--------|----------|
|
|
762
|
-
| `passed` + more phases | "Phase Complete" template |
|
|
763
|
-
| `passed` + last phase | "Milestone Complete" template |
|
|
762
|
+
| `passed` + more phases in current milestone | "Phase Complete" template |
|
|
763
|
+
| `passed` + last phase in current milestone | "Milestone Complete" template |
|
|
764
|
+
|
|
765
|
+
**Milestone boundary detection:** To determine "last phase in current milestone", read ROADMAP.md and find the `## Milestone:` section containing the current phase. Check its `**Phases:** start - end` range. If the current phase equals `end`, this is the last phase in the milestone. For projects with a single milestone or no explicit milestone sections, "last phase in ROADMAP" is equivalent.
|
|
764
766
|
| `gaps_found` | "Gaps Found" template |
|
|
765
767
|
|
|
766
768
|
Before the branded banner, include the results table:
|
|
@@ -838,7 +840,7 @@ All phase goals verified ✓
|
|
|
838
840
|
|
|
839
841
|
**Also available:**
|
|
840
842
|
- `/pbr:review` — manual acceptance testing
|
|
841
|
-
- `/pbr:milestone complete` —
|
|
843
|
+
- `/pbr:milestone complete` — archive milestone after audit passes
|
|
842
844
|
|
|
843
845
|
───────────────────────────────────────────────────────────────
|
|
844
846
|
```
|
|
@@ -95,7 +95,8 @@ Check the resumption priority hierarchy (same as /pbr:resume):
|
|
|
95
95
|
4. **Incomplete build**: PLAN.md files without SUMMARY.md → Execute `/pbr:build {N}`
|
|
96
96
|
5. **Unverified phase**: All plans complete, no VERIFICATION.md → Execute `/pbr:review {N}`
|
|
97
97
|
6. **Phase complete, more phases exist**: Verification passed → Execute `/pbr:plan {N+1}`
|
|
98
|
-
7. **
|
|
98
|
+
7. **Last phase in current milestone complete**: Verification passed on the last phase of the current milestone's phase range → Stop. Display: "Milestone complete! Run `/pbr:milestone audit` to verify cross-phase integration, then `/pbr:milestone complete` to archive."
|
|
99
|
+
8. **Between milestones**: Current milestone is marked complete in STATE.md, but more milestones exist or user needs to create the next one → Stop. Display: "Current milestone is complete. Run `/pbr:milestone new` to start the next milestone, or `/pbr:milestone audit` if not yet audited."
|
|
99
100
|
|
|
100
101
|
### Step 3: Execute
|
|
101
102
|
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: dashboard
|
|
3
|
+
description: "Launch the PBR web dashboard for the current project."
|
|
4
|
+
allowed-tools: Bash, Read
|
|
5
|
+
argument-hint: "[--port N]"
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
**STOP — DO NOT READ THIS FILE. You are already reading it. This prompt was injected into your context by Claude Code's plugin system. Begin executing immediately.**
|
|
9
|
+
|
|
10
|
+
## Behavior
|
|
11
|
+
|
|
12
|
+
1. **Parse arguments**: Extract `--port N` from the user's input. Default to `3000`.
|
|
13
|
+
|
|
14
|
+
2. **Check dependencies**: Check if `${CLAUDE_PLUGIN_ROOT}/dashboard/node_modules/` exists. If not, run:
|
|
15
|
+
```
|
|
16
|
+
npm install --prefix ${CLAUDE_PLUGIN_ROOT}/dashboard
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
3. **Launch dashboard**: Run in background via Bash:
|
|
20
|
+
```
|
|
21
|
+
node ${CLAUDE_PLUGIN_ROOT}/dashboard/bin/cli.js --dir <cwd> --port <port> &
|
|
22
|
+
```
|
|
23
|
+
Use `&` to background the process so it doesn't block the session.
|
|
24
|
+
|
|
25
|
+
4. **Output to user**:
|
|
26
|
+
```
|
|
27
|
+
Dashboard running at http://localhost:<port>
|
|
28
|
+
Open this URL in your browser to view your project's planning state.
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Notes
|
|
32
|
+
|
|
33
|
+
- If the port is already in use, the dashboard will fail to start — suggest the user try a different port with `--port`.
|
|
34
|
+
- The dashboard watches `.planning/` for live updates via SSE.
|
|
@@ -66,9 +66,9 @@ Parse the phase number and optional flags:
|
|
|
66
66
|
| `insert <N>` | Insert a new phase at position N (uses decimal numbering) |
|
|
67
67
|
| `remove <N>` | Remove phase N from the roadmap |
|
|
68
68
|
|
|
69
|
-
### Freeform Text Guard
|
|
69
|
+
### Freeform Text Guard — CRITICAL
|
|
70
70
|
|
|
71
|
-
**Before
|
|
71
|
+
**STOP. Before ANY context loading or Step 1 work**, you MUST check whether `$ARGUMENTS` looks like freeform text rather than a valid invocation. This check is non-negotiable. Valid patterns are:
|
|
72
72
|
|
|
73
73
|
- Empty (no arguments)
|
|
74
74
|
- A phase number: integer (`3`, `03`) or decimal (`3.1`)
|
|
@@ -100,6 +100,8 @@ Then suggest the appropriate skill based on the text content:
|
|
|
100
100
|
|
|
101
101
|
Do NOT proceed with planning. The user needs to use the correct skill.
|
|
102
102
|
|
|
103
|
+
**Self-check**: If you reach Step 1 without having matched a valid argument pattern above, you have a bug. Stop immediately and show the usage block.
|
|
104
|
+
|
|
103
105
|
---
|
|
104
106
|
|
|
105
107
|
## Orchestration Flow: Standard Planning
|
|
@@ -340,7 +340,8 @@ If all automated checks and UAT items passed:
|
|
|
340
340
|
|
|
341
341
|
Use the branded output from `references/ui-formatting.md`:
|
|
342
342
|
- If more phases remain: use the "Phase Complete" banner template
|
|
343
|
-
- If this was the last phase: use the "Milestone Complete" banner template
|
|
343
|
+
- If this was the last phase in the current milestone: use the "Milestone Complete" banner template
|
|
344
|
+
- **Milestone boundary detection:** Read ROADMAP.md and find the `## Milestone:` section containing the current phase. Check its `**Phases:** start - end` range. If the current phase equals `end`, this is the last phase in the milestone.
|
|
344
345
|
- Always include the "Next Up" routing block
|
|
345
346
|
|
|
346
347
|
4. If `gates.confirm_transition` is true in config AND `features.auto_advance` is NOT true:
|
|
@@ -356,7 +357,7 @@ Use the branded output from `references/ui-formatting.md`:
|
|
|
356
357
|
5. **If `features.auto_advance` is `true` AND `mode` is `autonomous` AND more phases remain:**
|
|
357
358
|
- Chain directly to plan: `Skill({ skill: "pbr:plan", args: "{N+1}" })`
|
|
358
359
|
- This continues the build→review→plan cycle automatically
|
|
359
|
-
- **If this is the last phase:** HARD STOP — do NOT auto-advance past milestone boundaries
|
|
360
|
+
- **If this is the last phase in the current milestone:** HARD STOP — do NOT auto-advance past milestone boundaries. Display: "auto_advance pauses at milestone boundaries — your sign-off is required."
|
|
360
361
|
|
|
361
362
|
#### Gaps Found WITH `--auto-fix`
|
|
362
363
|
|
|
@@ -666,7 +667,7 @@ Then:
|
|
|
666
667
|
───────────────────────────────────────────────────────────────
|
|
667
668
|
|
|
668
669
|
**Also available:**
|
|
669
|
-
- `/pbr:milestone complete` — archive
|
|
670
|
+
- `/pbr:milestone complete` — archive milestone after audit passes
|
|
670
671
|
- `/pbr:milestone new` — start planning next features
|
|
671
672
|
- `/pbr:status` — see final project status
|
|
672
673
|
|
|
@@ -251,7 +251,7 @@ Based on the project state, suggest the single most logical next action:
|
|
|
251
251
|
YES → "Re-plan with updated context: `/pbr:plan {N+1}`"
|
|
252
252
|
NO → "Build the next phase: `/pbr:build {N+1}`"
|
|
253
253
|
NO → "Plan the next phase: `/pbr:plan {N+1}`"
|
|
254
|
-
NO → "All phases complete
|
|
254
|
+
NO → Check for existing `*-MILESTONE-AUDIT.md` in `.planning/`:\n IF audit passed → "All phases complete and audited! `/pbr:milestone complete` to archive and tag."\n IF audit has gaps → "Audit found gaps. `/pbr:milestone gaps` to address them."\n IF no audit → "All phases complete! `/pbr:milestone audit` to verify cross-phase integration (recommended), then `/pbr:milestone complete`."
|
|
255
255
|
|
|
256
256
|
6. Is the current phase not started?
|
|
257
257
|
YES → Has it been discussed?
|
|
@@ -317,7 +317,10 @@ Build options dynamically from the decision tree results. Always include "Someth
|
|
|
317
317
|
|
|
318
318
|
### All phases complete
|
|
319
319
|
- Celebrate briefly: "All phases complete!"
|
|
320
|
-
-
|
|
320
|
+
- Check for existing audit report: look for `*-MILESTONE-AUDIT.md` in `.planning/`
|
|
321
|
+
- **If audit exists and passed:** Suggest `/pbr:milestone complete` to archive (audit already done)
|
|
322
|
+
- **If audit exists with gaps:** Suggest `/pbr:milestone gaps` to address issues
|
|
323
|
+
- **If no audit exists:** Suggest `/pbr:milestone audit` to verify cross-phase integration (recommended first)
|
|
321
324
|
- Then: `/pbr:milestone complete` to archive the milestone and tag it
|
|
322
325
|
- Or: `/pbr:milestone new` to start the next set of features
|
|
323
326
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: todo
|
|
3
3
|
description: "File-based persistent todos. Add, list, complete — survives sessions."
|
|
4
|
-
allowed-tools: Read, Write, Bash, Glob, Grep
|
|
5
|
-
argument-hint: "add <description> | list [theme] | done <NNN>"
|
|
4
|
+
allowed-tools: Read, Write, Bash, Glob, Grep, Skill
|
|
5
|
+
argument-hint: "add <description> | list [theme] | done <NNN> | work <NNN>"
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
**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.**
|
|
@@ -124,12 +124,12 @@ Pending Todos:
|
|
|
124
124
|
|
|
125
125
|
**Pick a todo** — mark one done or start working
|
|
126
126
|
|
|
127
|
-
`/pbr:todo
|
|
127
|
+
`/pbr:todo work <NNN>` — start working on a todo
|
|
128
|
+
`/pbr:todo done <NNN>` — mark a todo as complete
|
|
128
129
|
|
|
129
130
|
───────────────────────────────────────────────────────────────
|
|
130
131
|
|
|
131
132
|
**Also available:**
|
|
132
|
-
- `/pbr:quick` — work on one now
|
|
133
133
|
- `/pbr:status` — see project status
|
|
134
134
|
|
|
135
135
|
───────────────────────────────────────────────────────────────
|
|
@@ -181,6 +181,45 @@ Todo {NNN} not found in pending todos.
|
|
|
181
181
|
───────────────────────────────────────────────────────────────
|
|
182
182
|
```
|
|
183
183
|
|
|
184
|
+
### `work <NNN>`
|
|
185
|
+
|
|
186
|
+
1. Find `.planning/todos/pending/{NNN}-*.md` (match by number prefix)
|
|
187
|
+
2. If not found, display the same error block as `done` — suggest `/pbr:todo list`
|
|
188
|
+
3. Read the todo file content (frontmatter + body)
|
|
189
|
+
4. Extract the `title` from frontmatter and the full body (Goal, Scope, Acceptance Criteria sections)
|
|
190
|
+
5. Display branded output:
|
|
191
|
+
```
|
|
192
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
193
|
+
PLAN-BUILD-RUN ► WORKING ON TODO {NNN}
|
|
194
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
195
|
+
|
|
196
|
+
**Todo {NNN}:** {title}
|
|
197
|
+
|
|
198
|
+
Launching /pbr:quick with todo context...
|
|
199
|
+
```
|
|
200
|
+
6. Invoke the Skill tool: `skill: "pbr:quick"` with `args` set to the todo title followed by the body content. Format the args as:
|
|
201
|
+
|
|
202
|
+
```
|
|
203
|
+
{title}
|
|
204
|
+
|
|
205
|
+
Context from todo {NNN}:
|
|
206
|
+
{body content — Goal, Scope, Acceptance Criteria sections}
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
This hands off execution to `/pbr:quick`, which will spawn an executor agent, make atomic commits, and track the work. When quick completes, remind the user:
|
|
210
|
+
|
|
211
|
+
```
|
|
212
|
+
───────────────────────────────────────────────────────────────
|
|
213
|
+
|
|
214
|
+
## ▶ Next Up
|
|
215
|
+
|
|
216
|
+
**Mark this todo as done if the work is complete**
|
|
217
|
+
|
|
218
|
+
`/pbr:todo done {NNN}`
|
|
219
|
+
|
|
220
|
+
───────────────────────────────────────────────────────────────
|
|
221
|
+
```
|
|
222
|
+
|
|
184
223
|
### No arguments
|
|
185
224
|
|
|
186
225
|
Show a brief summary: count of pending todos, grouped by theme, plus usage hint.
|