@sienklogic/plan-build-run 2.61.0 → 2.61.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +7 -0
- package/package.json +1 -1
- package/plugins/copilot-pbr/hooks/hooks.json +0 -24
- package/plugins/copilot-pbr/plugin.json +1 -1
- package/plugins/cursor-pbr/.cursor-plugin/plugin.json +1 -1
- package/plugins/cursor-pbr/hooks/hooks.json +0 -20
- package/plugins/pbr/.claude-plugin/plugin.json +1 -1
- package/plugins/pbr/hooks/hooks.json +0 -20
- package/plugins/pbr/scripts/context-bridge.js +15 -0
- package/plugins/pbr/scripts/enforce-pbr-workflow.js +2 -1
- package/plugins/pbr/scripts/pre-bash-dispatch.js +7 -0
- package/plugins/pbr/scripts/run-hook.js +45 -8
- package/plugins/pbr/scripts/suggest-compact.js +3 -1
- package/plugins/pbr/scripts/track-context-budget.js +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,13 @@ All notable changes to Plan-Build-Run will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [2.61.1](https://github.com/SienkLogic/plan-build-run/compare/plan-build-run-v2.61.0...plan-build-run-v2.61.1) (2026-03-06)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Bug Fixes
|
|
12
|
+
|
|
13
|
+
* **hooks:** resolve script path before .planning/ early-exit check ([06487f7](https://github.com/SienkLogic/plan-build-run/commit/06487f78799a521f6d9041dc1323d36bfdf4a1d8))
|
|
14
|
+
|
|
8
15
|
## [2.61.0](https://github.com/SienkLogic/plan-build-run/compare/plan-build-run-v2.60.0...plan-build-run-v2.61.0) (2026-03-06)
|
|
9
16
|
|
|
10
17
|
|
package/package.json
CHANGED
|
@@ -79,18 +79,6 @@
|
|
|
79
79
|
}
|
|
80
80
|
]
|
|
81
81
|
},
|
|
82
|
-
{
|
|
83
|
-
"matcher": "Write|Edit",
|
|
84
|
-
"hooks": [
|
|
85
|
-
{
|
|
86
|
-
"type": "command",
|
|
87
|
-
"bash": "node \"$(cd \"$(dirname \"$0\")\" && pwd)/../../pbr/scripts/run-hook.js\" suggest-compact.js",
|
|
88
|
-
"powershell": "node (Join-Path (Split-Path -Parent $PSScriptRoot) 'pbr\\scripts\\run-hook.js') suggest-compact.js",
|
|
89
|
-
"cwd": ".",
|
|
90
|
-
"timeoutSec": 15
|
|
91
|
-
}
|
|
92
|
-
]
|
|
93
|
-
},
|
|
94
82
|
{
|
|
95
83
|
"matcher": "Bash",
|
|
96
84
|
"hooks": [
|
|
@@ -166,18 +154,6 @@
|
|
|
166
154
|
}
|
|
167
155
|
]
|
|
168
156
|
},
|
|
169
|
-
{
|
|
170
|
-
"matcher": "Bash",
|
|
171
|
-
"hooks": [
|
|
172
|
-
{
|
|
173
|
-
"type": "command",
|
|
174
|
-
"bash": "node \"$(cd \"$(dirname \"$0\")\" && pwd)/../../pbr/scripts/run-hook.js\" check-cross-plugin-sync.js",
|
|
175
|
-
"powershell": "node (Join-Path (Split-Path -Parent $PSScriptRoot) 'pbr\\scripts\\run-hook.js') check-cross-plugin-sync.js",
|
|
176
|
-
"cwd": ".",
|
|
177
|
-
"timeoutSec": 15
|
|
178
|
-
}
|
|
179
|
-
]
|
|
180
|
-
},
|
|
181
157
|
{
|
|
182
158
|
"matcher": "Write|Edit",
|
|
183
159
|
"hooks": [
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pbr",
|
|
3
3
|
"displayName": "Plan-Build-Run",
|
|
4
|
-
"version": "2.61.
|
|
4
|
+
"version": "2.61.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.61.
|
|
4
|
+
"version": "2.61.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",
|
|
@@ -67,16 +67,6 @@
|
|
|
67
67
|
}
|
|
68
68
|
]
|
|
69
69
|
},
|
|
70
|
-
{
|
|
71
|
-
"matcher": "Write|Edit",
|
|
72
|
-
"hooks": [
|
|
73
|
-
{
|
|
74
|
-
"type": "command",
|
|
75
|
-
"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'))\" suggest-compact.js",
|
|
76
|
-
"statusMessage": "Checking context budget..."
|
|
77
|
-
}
|
|
78
|
-
]
|
|
79
|
-
},
|
|
80
70
|
{
|
|
81
71
|
"matcher": "Bash",
|
|
82
72
|
"hooks": [
|
|
@@ -140,16 +130,6 @@
|
|
|
140
130
|
}
|
|
141
131
|
]
|
|
142
132
|
},
|
|
143
|
-
{
|
|
144
|
-
"matcher": "Bash",
|
|
145
|
-
"hooks": [
|
|
146
|
-
{
|
|
147
|
-
"type": "command",
|
|
148
|
-
"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'))\" check-cross-plugin-sync.js",
|
|
149
|
-
"statusMessage": "Checking cross-plugin sync..."
|
|
150
|
-
}
|
|
151
|
-
]
|
|
152
|
-
},
|
|
153
133
|
{
|
|
154
134
|
"matcher": "Write|Edit",
|
|
155
135
|
"hooks": [
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pbr",
|
|
3
|
-
"version": "2.61.
|
|
3
|
+
"version": "2.61.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",
|
|
@@ -72,16 +72,6 @@
|
|
|
72
72
|
}
|
|
73
73
|
]
|
|
74
74
|
},
|
|
75
|
-
{
|
|
76
|
-
"matcher": "Write|Edit",
|
|
77
|
-
"hooks": [
|
|
78
|
-
{
|
|
79
|
-
"type": "command",
|
|
80
|
-
"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'))\" suggest-compact.js",
|
|
81
|
-
"statusMessage": "Checking context budget..."
|
|
82
|
-
}
|
|
83
|
-
]
|
|
84
|
-
},
|
|
85
75
|
{
|
|
86
76
|
"matcher": "Bash",
|
|
87
77
|
"hooks": [
|
|
@@ -145,16 +135,6 @@
|
|
|
145
135
|
}
|
|
146
136
|
]
|
|
147
137
|
},
|
|
148
|
-
{
|
|
149
|
-
"matcher": "Bash",
|
|
150
|
-
"hooks": [
|
|
151
|
-
{
|
|
152
|
-
"type": "command",
|
|
153
|
-
"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'))\" check-cross-plugin-sync.js",
|
|
154
|
-
"statusMessage": "Checking cross-plugin sync..."
|
|
155
|
-
}
|
|
156
|
-
]
|
|
157
|
-
},
|
|
158
138
|
{
|
|
159
139
|
"matcher": "Write|Edit",
|
|
160
140
|
"hooks": [
|
|
@@ -239,6 +239,21 @@ function main() {
|
|
|
239
239
|
source: 'bridge'
|
|
240
240
|
});
|
|
241
241
|
process.stdout.write(JSON.stringify(output));
|
|
242
|
+
process.exit(0);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// For Write|Edit tools, also run suggest-compact check in-process
|
|
246
|
+
// (eliminates a separate Node process spawn per Write/Edit).
|
|
247
|
+
// Detect Write|Edit by presence of file_path in tool_input.
|
|
248
|
+
const toolInput = data.tool_input || {};
|
|
249
|
+
if (toolInput.file_path || toolInput.path) {
|
|
250
|
+
try {
|
|
251
|
+
const { checkCompaction } = require('./suggest-compact');
|
|
252
|
+
const compactResult = checkCompaction(planningDir, cwd);
|
|
253
|
+
if (compactResult) {
|
|
254
|
+
process.stdout.write(JSON.stringify(compactResult));
|
|
255
|
+
}
|
|
256
|
+
} catch (_e) { /* best-effort — never block on compact check */ }
|
|
242
257
|
}
|
|
243
258
|
|
|
244
259
|
process.exit(0);
|
|
@@ -21,7 +21,6 @@
|
|
|
21
21
|
const fs = require('fs');
|
|
22
22
|
const path = require('path');
|
|
23
23
|
const { logHook } = require('./hook-logger');
|
|
24
|
-
const { sessionLoad } = require('./pbr-tools');
|
|
25
24
|
|
|
26
25
|
/**
|
|
27
26
|
* Load the enforcement configuration from .planning/config.json.
|
|
@@ -75,6 +74,7 @@ function checkUnmanagedSourceWrite(data) {
|
|
|
75
74
|
if (!fs.existsSync(planningDir)) return null;
|
|
76
75
|
|
|
77
76
|
// Skip if a PBR skill is active — try .session.json first, fall back to legacy .active-skill
|
|
77
|
+
const { sessionLoad } = require('./pbr-tools');
|
|
78
78
|
let activeSkill = sessionLoad(planningDir).activeSkill || '';
|
|
79
79
|
if (!activeSkill) {
|
|
80
80
|
try { activeSkill = fs.readFileSync(path.join(planningDir, '.active-skill'), 'utf8').trim(); } catch (_) { /* legacy file missing */ }
|
|
@@ -240,6 +240,7 @@ function checkUnmanagedCommit(data) {
|
|
|
240
240
|
if (!fs.existsSync(planningDir)) return null;
|
|
241
241
|
|
|
242
242
|
// Skip if a PBR skill is active — try .session.json first, fall back to legacy .active-skill
|
|
243
|
+
const { sessionLoad } = require('./pbr-tools');
|
|
243
244
|
let activeSkillCommit = sessionLoad(planningDir).activeSkill || '';
|
|
244
245
|
if (!activeSkillCommit) {
|
|
245
246
|
try { activeSkillCommit = fs.readFileSync(path.join(planningDir, '.active-skill'), 'utf8').trim(); } catch (_) { /* legacy file missing */ }
|
|
@@ -50,6 +50,7 @@ const { logHook } = require('./hook-logger');
|
|
|
50
50
|
const { checkDangerous } = require('./check-dangerous-commands');
|
|
51
51
|
const { checkCommit, enrichCommitLlm } = require('./validate-commit');
|
|
52
52
|
const { checkUnmanagedCommit } = require('./enforce-pbr-workflow');
|
|
53
|
+
const { checkCrossPluginSync } = require('./check-cross-plugin-sync');
|
|
53
54
|
|
|
54
55
|
function main() {
|
|
55
56
|
let input = '';
|
|
@@ -104,6 +105,12 @@ function main() {
|
|
|
104
105
|
warnings.push(unmanagedCommitResult.output.additionalContext);
|
|
105
106
|
}
|
|
106
107
|
|
|
108
|
+
// Cross-plugin sync advisory — warn when pbr changes lack cursor/copilot counterparts
|
|
109
|
+
const syncResult = checkCrossPluginSync(data);
|
|
110
|
+
if (syncResult) {
|
|
111
|
+
warnings.push(syncResult.additionalContext);
|
|
112
|
+
}
|
|
113
|
+
|
|
107
114
|
// LLM commit semantic classification — advisory only
|
|
108
115
|
const llmAdvisory = await enrichCommitLlm(data);
|
|
109
116
|
if (llmAdvisory) {
|
|
@@ -21,8 +21,22 @@
|
|
|
21
21
|
|
|
22
22
|
'use strict';
|
|
23
23
|
|
|
24
|
+
const fs = require('fs');
|
|
24
25
|
const path = require('path');
|
|
25
26
|
|
|
27
|
+
// Scripts that must run even without .planning/ (lifecycle hooks).
|
|
28
|
+
// All per-tool-call hooks (PreToolUse, PostToolUse, etc.) are PBR-specific
|
|
29
|
+
// and can early-exit when .planning/ doesn't exist, skipping their entire
|
|
30
|
+
// require() chain (~10-20ms saved per hook invocation on non-PBR projects).
|
|
31
|
+
const ALWAYS_RUN = new Set([
|
|
32
|
+
'progress-tracker.js', // SessionStart — reports non-PBR status
|
|
33
|
+
'session-cleanup.js', // SessionEnd — cleanup
|
|
34
|
+
'worktree-create.js', // WorktreeCreate — creates .planning/ in worktrees
|
|
35
|
+
'worktree-remove.js', // WorktreeRemove — cleans up worktrees
|
|
36
|
+
'instructions-loaded.js', // InstructionsLoaded — detects instruction changes
|
|
37
|
+
'check-config-change.js', // ConfigChange — monitors config
|
|
38
|
+
]);
|
|
39
|
+
|
|
26
40
|
/**
|
|
27
41
|
* Fix MSYS-style paths on Windows.
|
|
28
42
|
* Converts /d/Repos/... to D:\Repos\...
|
|
@@ -73,23 +87,46 @@ if (scriptName) {
|
|
|
73
87
|
|
|
74
88
|
function runScript(name, args) {
|
|
75
89
|
args = args || [];
|
|
90
|
+
|
|
76
91
|
// Try __dirname first, then pluginRoot
|
|
77
92
|
const candidates = [
|
|
78
93
|
path.resolve(__dirname, name),
|
|
79
94
|
pluginRoot ? path.resolve(pluginRoot, 'scripts', name) : null
|
|
80
95
|
].filter(Boolean);
|
|
81
96
|
|
|
97
|
+
// Phase 1: Resolve the script path (cheap — just fs.statSync, no require).
|
|
98
|
+
// This must happen before the .planning/ check so missing scripts still
|
|
99
|
+
// get proper error messages regardless of project type.
|
|
100
|
+
let resolvedPath = null;
|
|
82
101
|
for (const candidate of candidates) {
|
|
83
102
|
try {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
} catch (
|
|
88
|
-
|
|
103
|
+
fs.statSync(candidate);
|
|
104
|
+
resolvedPath = candidate;
|
|
105
|
+
break;
|
|
106
|
+
} catch (_e) {
|
|
107
|
+
// Not found at this path
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (!resolvedPath) {
|
|
112
|
+
process.stderr.write(`run-hook: cannot find script: ${name}\n`);
|
|
113
|
+
process.stderr.write(` searched: ${candidates.join(', ')}\n`);
|
|
114
|
+
process.exit(1);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Phase 2: Early-exit for non-PBR projects. Script exists but .planning/
|
|
118
|
+
// doesn't — skip loading the module entirely. This avoids the full
|
|
119
|
+
// require() chain (~10-20ms) for every hook invocation in non-PBR projects.
|
|
120
|
+
if (!ALWAYS_RUN.has(name)) {
|
|
121
|
+
const cwd = process.env.PBR_PROJECT_ROOT || process.cwd();
|
|
122
|
+
try {
|
|
123
|
+
fs.statSync(path.join(cwd, '.planning'));
|
|
124
|
+
} catch (_e) {
|
|
125
|
+
process.exit(0);
|
|
89
126
|
}
|
|
90
127
|
}
|
|
91
128
|
|
|
92
|
-
|
|
93
|
-
process.
|
|
94
|
-
|
|
129
|
+
// Phase 3: Load and execute the script.
|
|
130
|
+
process.argv = [process.argv[0], resolvedPath, ...args];
|
|
131
|
+
require(resolvedPath);
|
|
95
132
|
}
|
|
@@ -20,7 +20,6 @@
|
|
|
20
20
|
const fs = require('fs');
|
|
21
21
|
const path = require('path');
|
|
22
22
|
const { logHook } = require('./hook-logger');
|
|
23
|
-
const { configLoad, sessionSave } = require('./pbr-tools');
|
|
24
23
|
const { loadBridge, TIER_MESSAGES } = require('./context-bridge');
|
|
25
24
|
|
|
26
25
|
const DEFAULT_THRESHOLD = 50;
|
|
@@ -159,12 +158,14 @@ function saveCounter(counterPath, counter) {
|
|
|
159
158
|
// Derive planningDir from counterPath (counterPath is planningDir/.compact-counter)
|
|
160
159
|
try {
|
|
161
160
|
const planningDir = path.dirname(counterPath);
|
|
161
|
+
const { sessionSave } = require('./pbr-tools');
|
|
162
162
|
sessionSave(planningDir, { compactCounter: counter });
|
|
163
163
|
} catch (_e) { /* non-fatal mirror */ }
|
|
164
164
|
}
|
|
165
165
|
|
|
166
166
|
function getThreshold(cwd) {
|
|
167
167
|
const planningDir = path.join(cwd, '.planning');
|
|
168
|
+
const { configLoad } = require('./pbr-tools');
|
|
168
169
|
const config = configLoad(planningDir);
|
|
169
170
|
if (!config) return DEFAULT_THRESHOLD;
|
|
170
171
|
return config.hooks?.compactThreshold || DEFAULT_THRESHOLD;
|
|
@@ -173,6 +174,7 @@ function getThreshold(cwd) {
|
|
|
173
174
|
function resetCounter(planningDir) {
|
|
174
175
|
// Primary: reset compactCounter in .session.json to 0
|
|
175
176
|
try {
|
|
177
|
+
const { sessionSave } = require('./pbr-tools');
|
|
176
178
|
sessionSave(planningDir, { compactCounter: { count: 0, lastSuggested: 0 } });
|
|
177
179
|
} catch (_e) { /* best-effort */ }
|
|
178
180
|
|
|
@@ -194,8 +194,8 @@ function loadTracker(trackerPath) {
|
|
|
194
194
|
function checkBridge(planningDir) {
|
|
195
195
|
const bridgePath = path.join(planningDir, '.context-budget.json');
|
|
196
196
|
try {
|
|
197
|
-
|
|
198
|
-
|
|
197
|
+
// Single statSync replaces existsSync + statSync (2 syscalls → 1).
|
|
198
|
+
// If the file doesn't exist, statSync throws and we catch below.
|
|
199
199
|
const stats = fs.statSync(bridgePath);
|
|
200
200
|
const ageMs = Date.now() - stats.mtimeMs;
|
|
201
201
|
if (ageMs > BRIDGE_STALENESS_MS) return null;
|