@sienklogic/plan-build-run 2.11.0 → 2.13.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 +21 -0
- package/package.json +1 -1
- package/plugins/copilot-pbr/README.md +7 -0
- 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/plugins/pbr/scripts/check-roadmap-sync.js +9 -1
- package/plugins/pbr/scripts/check-subagent-output.js +32 -2
- package/plugins/pbr/scripts/log-tool-failure.js +1 -1
- package/plugins/pbr/scripts/progress-tracker.js +31 -0
- package/plugins/pbr/scripts/track-context-budget.js +10 -1
- package/plugins/pbr/scripts/validate-task.js +12 -0
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.13.0](https://github.com/SienkLogic/plan-build-run/compare/plan-build-run-v2.12.0...plan-build-run-v2.13.0) (2026-02-22)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* **tools:** add stale active-skill session-start warning and copilot hook limitation docs ([158a78d](https://github.com/SienkLogic/plan-build-run/commit/158a78d03b482f56ab6f09e89bf9ef67b81fb409))
|
|
14
|
+
|
|
15
|
+
## [2.12.0](https://github.com/SienkLogic/plan-build-run/compare/plan-build-run-v2.11.0...plan-build-run-v2.12.0) (2026-02-21)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
### Features
|
|
19
|
+
|
|
20
|
+
* **tools:** add review verifier post-check and stale active-skill detection ([dbd2eb8](https://github.com/SienkLogic/plan-build-run/commit/dbd2eb899c2b15cc2ac61913b29ca1aaab6b0b1f))
|
|
21
|
+
* **tools:** add scan mapper area validation and stale Building status detection ([8d8a438](https://github.com/SienkLogic/plan-build-run/commit/8d8a43809095722ae9d00242f90f29d32bef4d1f))
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
### Bug Fixes
|
|
25
|
+
|
|
26
|
+
* **tools:** extend executor commit check to quick skill and add .catch() to log-tool-failure ([197efc7](https://github.com/SienkLogic/plan-build-run/commit/197efc70cb5e2a36f014ee2c5c7d653b4b1898f4))
|
|
27
|
+
* **tools:** warn on context budget tracker reset and roadmap sync parse failures ([f5aef28](https://github.com/SienkLogic/plan-build-run/commit/f5aef2804e42a934d3e7a480e3045ac6c0e0fc9b))
|
|
28
|
+
|
|
8
29
|
## [2.11.0](https://github.com/SienkLogic/plan-build-run/compare/plan-build-run-v2.10.0...plan-build-run-v2.11.0) (2026-02-21)
|
|
9
30
|
|
|
10
31
|
|
package/package.json
CHANGED
|
@@ -119,6 +119,13 @@ Copilot CLI supports 6 hook events vs Claude Code's full set. The following hook
|
|
|
119
119
|
|
|
120
120
|
**Not available** in Copilot CLI (present in Claude Code/Cursor ports): `SubagentStart`, `SubagentStop`, `TaskCompleted`, `PostToolUseFailure`, `PreCompact`, `Stop`.
|
|
121
121
|
|
|
122
|
+
**Impact of missing hooks:**
|
|
123
|
+
|
|
124
|
+
- No auto-continue between skills (`Stop` hook) — you must manually run the next command
|
|
125
|
+
- No tool failure logging (`PostToolUseFailure`) — silent failures won't be recorded to `.planning/logs/`
|
|
126
|
+
- No context budget preservation on compaction (`PreCompact`) — STATE.md won't be auto-preserved when context is compressed
|
|
127
|
+
- No subagent lifecycle tracking (`SubagentStart`/`SubagentStop`/`TaskCompleted`) — agent spawn/completion events aren't logged
|
|
128
|
+
|
|
122
129
|
## Cross-Plugin Compatibility
|
|
123
130
|
|
|
124
131
|
This plugin works alongside the Claude Code and Cursor versions of Plan-Build-Run. All three plugins share the same `.planning/` directory and file formats, so you can switch between tools without losing state. Hook scripts under `plugins/pbr/scripts/` are shared between all plugins via relative paths.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pbr",
|
|
3
3
|
"displayName": "Plan-Build-Run",
|
|
4
|
-
"version": "2.
|
|
4
|
+
"version": "2.13.0",
|
|
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.13.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",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pbr",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.13.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",
|
|
@@ -48,6 +48,10 @@ function main() {
|
|
|
48
48
|
const stateInfo = parseState(stateContent);
|
|
49
49
|
if (!stateInfo || !stateInfo.phase || !stateInfo.status) {
|
|
50
50
|
logHook('check-roadmap-sync', 'PostToolUse', 'skip', { reason: 'could not parse STATE.md' });
|
|
51
|
+
const output = {
|
|
52
|
+
additionalContext: '[Roadmap Sync] Could not parse phase/status from STATE.md — ensure it contains **Phase**: and **Status**: fields.'
|
|
53
|
+
};
|
|
54
|
+
process.stdout.write(JSON.stringify(output));
|
|
51
55
|
process.exit(0);
|
|
52
56
|
}
|
|
53
57
|
|
|
@@ -220,7 +224,11 @@ function checkSync(data) {
|
|
|
220
224
|
const stateInfo = parseState(stateContent);
|
|
221
225
|
if (!stateInfo || !stateInfo.phase || !stateInfo.status) {
|
|
222
226
|
logHook('check-roadmap-sync', 'PostToolUse', 'skip', { reason: 'could not parse STATE.md' });
|
|
223
|
-
return
|
|
227
|
+
return {
|
|
228
|
+
output: {
|
|
229
|
+
additionalContext: '[Roadmap Sync] Could not parse phase/status from STATE.md — ensure it contains **Phase**: and **Status**: fields.'
|
|
230
|
+
}
|
|
231
|
+
};
|
|
224
232
|
}
|
|
225
233
|
|
|
226
234
|
if (!LIFECYCLE_STATUSES.includes(stateInfo.status)) {
|
|
@@ -312,8 +312,38 @@ function main() {
|
|
|
312
312
|
}
|
|
313
313
|
}
|
|
314
314
|
|
|
315
|
-
// GAP-
|
|
316
|
-
if (activeSkill === '
|
|
315
|
+
// GAP-08: Scan codebase-mapper should produce all 4 focus areas
|
|
316
|
+
if (activeSkill === 'scan' && agentType === 'pbr:codebase-mapper') {
|
|
317
|
+
const expectedAreas = ['tech', 'arch', 'quality', 'concerns'];
|
|
318
|
+
const codebaseDir = path.join(planningDir, 'codebase');
|
|
319
|
+
if (fs.existsSync(codebaseDir)) {
|
|
320
|
+
try {
|
|
321
|
+
const files = fs.readdirSync(codebaseDir).map(f => f.toLowerCase());
|
|
322
|
+
for (const area of expectedAreas) {
|
|
323
|
+
if (!files.some(f => f.includes(area))) {
|
|
324
|
+
skillWarnings.push(`Scan mapper: No output file containing "${area}" found in .planning/codebase/. One of the 4 mappers may have failed.`);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
} catch (_e) { /* best-effort */ }
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// GAP-07: Review verifier should produce meaningful VERIFICATION.md status
|
|
332
|
+
if (activeSkill === 'review' && agentType === 'pbr:verifier') {
|
|
333
|
+
const verFiles = findInPhaseDir(planningDir, /^VERIFICATION\.md$/i);
|
|
334
|
+
for (const vf of verFiles) {
|
|
335
|
+
try {
|
|
336
|
+
const content = fs.readFileSync(vf, 'utf8');
|
|
337
|
+
const statusMatch = content.match(/^status:\s*(\S+)/mi);
|
|
338
|
+
if (statusMatch && statusMatch[1] === 'gaps_found') {
|
|
339
|
+
skillWarnings.push('Review verifier: VERIFICATION.md has status "gaps_found" — ensure gaps are surfaced to the user.');
|
|
340
|
+
}
|
|
341
|
+
} catch (_e) { /* best-effort */ }
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
// GAP-06: Build/quick executor SUMMARY should have commits
|
|
346
|
+
if ((activeSkill === 'build' || activeSkill === 'quick') && agentType === 'pbr:executor') {
|
|
317
347
|
checkSummaryCommits(planningDir, found, skillWarnings);
|
|
318
348
|
}
|
|
319
349
|
|
|
@@ -91,6 +91,20 @@ function buildContext(planningDir, stateFile) {
|
|
|
91
91
|
if (continuity) {
|
|
92
92
|
parts.push(`\nLast Session:\n${continuity}`);
|
|
93
93
|
}
|
|
94
|
+
|
|
95
|
+
// Detect stale "Building" status — likely a crashed executor
|
|
96
|
+
const statusMatch = state.match(/\*{0,2}(?:Phase\s+)?Status\*{0,2}:\s*["']?(\w+)["']?/i);
|
|
97
|
+
if (statusMatch && statusMatch[1].toLowerCase() === 'building') {
|
|
98
|
+
try {
|
|
99
|
+
const stateStat = fs.statSync(stateFile);
|
|
100
|
+
const ageMs = Date.now() - stateStat.mtimeMs;
|
|
101
|
+
const ageMinutes = Math.round(ageMs / 60000);
|
|
102
|
+
if (ageMinutes > 30) {
|
|
103
|
+
parts.push(`\nWarning: STATE.md shows status "Building" but was last modified ${ageMinutes} minutes ago. This may indicate a crashed executor. Run /pbr:health to diagnose.`);
|
|
104
|
+
logHook('progress-tracker', 'SessionStart', 'stale-building', { ageMinutes });
|
|
105
|
+
}
|
|
106
|
+
} catch (_e) { /* best-effort */ }
|
|
107
|
+
}
|
|
94
108
|
} else {
|
|
95
109
|
parts.push('\nNo STATE.md found. Run /pbr:begin to initialize or /pbr:status to check.');
|
|
96
110
|
}
|
|
@@ -168,6 +182,23 @@ function buildContext(planningDir, stateFile) {
|
|
|
168
182
|
}
|
|
169
183
|
}
|
|
170
184
|
|
|
185
|
+
// Check for stale .active-skill (multi-session conflict detection)
|
|
186
|
+
const activeSkillFile = path.join(planningDir, '.active-skill');
|
|
187
|
+
if (fs.existsSync(activeSkillFile)) {
|
|
188
|
+
try {
|
|
189
|
+
const stats = fs.statSync(activeSkillFile);
|
|
190
|
+
const ageMs = Date.now() - stats.mtimeMs;
|
|
191
|
+
const ageMinutes = Math.floor(ageMs / 60000);
|
|
192
|
+
if (ageMinutes > 60) {
|
|
193
|
+
const skill = fs.readFileSync(activeSkillFile, 'utf8').trim();
|
|
194
|
+
parts.push(`\nWarning: .active-skill is ${ageMinutes} minutes old (skill: "${skill}"). This may be a stale lock from a crashed session or concurrent session conflict. Run /pbr:health to auto-fix, or delete .planning/.active-skill manually.`);
|
|
195
|
+
logHook('progress-tracker', 'SessionStart', 'stale-active-skill', { ageMinutes, skill });
|
|
196
|
+
}
|
|
197
|
+
} catch (_e) {
|
|
198
|
+
// Ignore errors
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
171
202
|
// Check for stale .auto-next signal (S>M-9)
|
|
172
203
|
const autoNextFile = path.join(planningDir, '.auto-next');
|
|
173
204
|
if (fs.existsSync(autoNextFile)) {
|
|
@@ -67,8 +67,17 @@ function main() {
|
|
|
67
67
|
const currentSkill = readFileSafe(skillPath);
|
|
68
68
|
let tracker = loadTracker(trackerPath);
|
|
69
69
|
|
|
70
|
-
if (tracker.skill !== currentSkill
|
|
70
|
+
if (tracker.skill !== currentSkill) {
|
|
71
71
|
tracker = { skill: currentSkill, reads: 0, total_chars: 0, files: [] };
|
|
72
|
+
} else if (tracker.files.length > 200) {
|
|
73
|
+
logHook('track-context-budget', 'PostToolUse', 'warn', {
|
|
74
|
+
reason: 'tracker reset at 200 files',
|
|
75
|
+
reads: tracker.reads,
|
|
76
|
+
total_chars: tracker.total_chars,
|
|
77
|
+
unique_files: tracker.files.length,
|
|
78
|
+
});
|
|
79
|
+
const prevCharsTotal = tracker.total_chars;
|
|
80
|
+
tracker = { skill: currentSkill, reads: 0, total_chars: prevCharsTotal, files: [] };
|
|
72
81
|
}
|
|
73
82
|
|
|
74
83
|
// Update tracker
|
|
@@ -677,6 +677,18 @@ function checkActiveSkillIntegrity(data) {
|
|
|
677
677
|
return 'Active-skill integrity: .planning/.active-skill not found. Skill-specific enforcement is disabled. The invoking skill should write this file. To fix: Wait for the current skill to finish, or delete .planning/.active-skill if stale.';
|
|
678
678
|
}
|
|
679
679
|
|
|
680
|
+
// Stale lock detection: warn if .active-skill is older than 2 hours
|
|
681
|
+
try {
|
|
682
|
+
const stat = fs.statSync(activeSkillFile);
|
|
683
|
+
const ageMs = Date.now() - stat.mtimeMs;
|
|
684
|
+
const TWO_HOURS = 2 * 60 * 60 * 1000;
|
|
685
|
+
if (ageMs > TWO_HOURS) {
|
|
686
|
+
const ageHours = Math.round(ageMs / (60 * 60 * 1000));
|
|
687
|
+
const skill = fs.readFileSync(activeSkillFile, 'utf8').trim();
|
|
688
|
+
return `Active-skill integrity: .planning/.active-skill is ${ageHours}h old (skill: "${skill}"). This may be a stale lock from a crashed session. Run /pbr:health to diagnose, or delete .planning/.active-skill if the previous session is no longer running.`;
|
|
689
|
+
}
|
|
690
|
+
} catch (_e) { /* best-effort */ }
|
|
691
|
+
|
|
680
692
|
return null;
|
|
681
693
|
}
|
|
682
694
|
|