@sienklogic/plan-build-run 2.61.1 → 2.62.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 CHANGED
@@ -5,6 +5,18 @@ 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.62.0](https://github.com/SienkLogic/plan-build-run/compare/plan-build-run-v2.61.1...plan-build-run-v2.62.0) (2026-03-06)
9
+
10
+
11
+ ### Features
12
+
13
+ * **hook-server:** add GET /context endpoint and SessionStart enrichment ([802253d](https://github.com/SienkLogic/plan-build-run/commit/802253da7af9087a10c8218f762593a2d048cfe1))
14
+ * **hook-server:** add handleHttp to 5 remaining medium-frequency hooks ([41f0cd7](https://github.com/SienkLogic/plan-build-run/commit/41f0cd7e98cf3c1019b42c27afed500b19fe4dd2))
15
+ * **hook-server:** add handleHttp to post-write-dispatch and post-bash-triage ([b4d794f](https://github.com/SienkLogic/plan-build-run/commit/b4d794faff1a588a9016a19ebeb354dfd57f5caa))
16
+ * **hook-server:** add handleHttp to track-context-budget and context-bridge ([8212131](https://github.com/SienkLogic/plan-build-run/commit/8212131a6b057a487a6ee837cb999eeee5d10ebc))
17
+ * **hook-server:** add HTTP hook server and client with JSONL event log ([bf1a15b](https://github.com/SienkLogic/plan-build-run/commit/bf1a15bf8993211db2801af429e9680dd220a521))
18
+ * **hook-server:** complete hook migration — all enrichment hooks export handleHttp ([647561f](https://github.com/SienkLogic/plan-build-run/commit/647561f7c6c863d1ef177b7a3ddc88c0f7c3489f))
19
+
8
20
  ## [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
21
 
10
22
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sienklogic/plan-build-run",
3
- "version": "2.61.1",
3
+ "version": "2.62.0",
4
4
  "description": "Plan it, Build it, Run it — structured development workflow for Claude Code",
5
5
  "keywords": [
6
6
  "claude-code",
@@ -50,7 +50,7 @@
50
50
  "coverageThreshold": {
51
51
  "global": {
52
52
  "statements": 70,
53
- "branches": 67,
53
+ "branches": 65,
54
54
  "functions": 70,
55
55
  "lines": 70
56
56
  }
@@ -20,8 +20,8 @@
20
20
  "hooks": [
21
21
  {
22
22
  "type": "command",
23
- "bash": "node \"$(cd \"$(dirname \"$0\")\" && pwd)/../../pbr/scripts/run-hook.js\" instructions-loaded.js",
24
- "powershell": "node (Join-Path (Split-Path -Parent $PSScriptRoot) 'pbr\\scripts\\run-hook.js') instructions-loaded.js",
23
+ "bash": "node \"$(cd \"$(dirname \"$0\")\" && pwd)/../../pbr/scripts/run-hook.js\" hook-server-client.js instructions-loaded",
24
+ "powershell": "node (Join-Path (Split-Path -Parent $PSScriptRoot) 'pbr\\scripts\\run-hook.js') hook-server-client.js instructions-loaded",
25
25
  "cwd": ".",
26
26
  "timeoutSec": 15
27
27
  }
@@ -33,8 +33,8 @@
33
33
  "hooks": [
34
34
  {
35
35
  "type": "command",
36
- "bash": "node \"$(cd \"$(dirname \"$0\")\" && pwd)/../../pbr/scripts/run-hook.js\" worktree-create.js",
37
- "powershell": "node (Join-Path (Split-Path -Parent $PSScriptRoot) 'pbr\\scripts\\run-hook.js') worktree-create.js",
36
+ "bash": "node \"$(cd \"$(dirname \"$0\")\" && pwd)/../../pbr/scripts/run-hook.js\" hook-server-client.js worktree-create",
37
+ "powershell": "node (Join-Path (Split-Path -Parent $PSScriptRoot) 'pbr\\scripts\\run-hook.js') hook-server-client.js worktree-create",
38
38
  "cwd": ".",
39
39
  "timeoutSec": 15
40
40
  }
@@ -46,8 +46,8 @@
46
46
  "hooks": [
47
47
  {
48
48
  "type": "command",
49
- "bash": "node \"$(cd \"$(dirname \"$0\")\" && pwd)/../../pbr/scripts/run-hook.js\" worktree-remove.js",
50
- "powershell": "node (Join-Path (Split-Path -Parent $PSScriptRoot) 'pbr\\scripts\\run-hook.js') worktree-remove.js",
49
+ "bash": "node \"$(cd \"$(dirname \"$0\")\" && pwd)/../../pbr/scripts/run-hook.js\" hook-server-client.js worktree-remove",
50
+ "powershell": "node (Join-Path (Split-Path -Parent $PSScriptRoot) 'pbr\\scripts\\run-hook.js') hook-server-client.js worktree-remove",
51
51
  "cwd": ".",
52
52
  "timeoutSec": 15
53
53
  }
@@ -72,8 +72,8 @@
72
72
  "hooks": [
73
73
  {
74
74
  "type": "command",
75
- "bash": "node \"$(cd \"$(dirname \"$0\")\" && pwd)/../../pbr/scripts/run-hook.js\" check-subagent-output.js",
76
- "powershell": "node (Join-Path (Split-Path -Parent $PSScriptRoot) 'pbr\\scripts\\run-hook.js') check-subagent-output.js",
75
+ "bash": "node \"$(cd \"$(dirname \"$0\")\" && pwd)/../../pbr/scripts/run-hook.js\" hook-server-client.js check-subagent-output",
76
+ "powershell": "node (Join-Path (Split-Path -Parent $PSScriptRoot) 'pbr\\scripts\\run-hook.js') hook-server-client.js check-subagent-output",
77
77
  "cwd": ".",
78
78
  "timeoutSec": 15
79
79
  }
@@ -121,8 +121,8 @@
121
121
  "hooks": [
122
122
  {
123
123
  "type": "command",
124
- "bash": "node \"$(cd \"$(dirname \"$0\")\" && pwd)/../../pbr/scripts/run-hook.js\" log-tool-failure.js",
125
- "powershell": "node (Join-Path (Split-Path -Parent $PSScriptRoot) 'pbr\\scripts\\run-hook.js') log-tool-failure.js",
124
+ "bash": "node \"$(cd \"$(dirname \"$0\")\" && pwd)/../../pbr/scripts/run-hook.js\" hook-server-client.js log-tool-failure",
125
+ "powershell": "node (Join-Path (Split-Path -Parent $PSScriptRoot) 'pbr\\scripts\\run-hook.js') hook-server-client.js log-tool-failure",
126
126
  "cwd": ".",
127
127
  "timeoutSec": 15
128
128
  }
@@ -208,8 +208,8 @@
208
208
  "hooks": [
209
209
  {
210
210
  "type": "command",
211
- "bash": "node \"$(cd \"$(dirname \"$0\")\" && pwd)/../../pbr/scripts/run-hook.js\" context-budget-check.js",
212
- "powershell": "node (Join-Path (Split-Path -Parent $PSScriptRoot) 'pbr\\scripts\\run-hook.js') context-budget-check.js",
211
+ "bash": "node \"$(cd \"$(dirname \"$0\")\" && pwd)/../../pbr/scripts/run-hook.js\" hook-server-client.js context-budget-check",
212
+ "powershell": "node (Join-Path (Split-Path -Parent $PSScriptRoot) 'pbr\\scripts\\run-hook.js') hook-server-client.js context-budget-check",
213
213
  "cwd": ".",
214
214
  "timeoutSec": 15
215
215
  }
@@ -234,8 +234,8 @@
234
234
  "hooks": [
235
235
  {
236
236
  "type": "command",
237
- "bash": "node \"$(cd \"$(dirname \"$0\")\" && pwd)/../../pbr/scripts/run-hook.js\" log-subagent.js start",
238
- "powershell": "node (Join-Path (Split-Path -Parent $PSScriptRoot) 'pbr\\scripts\\run-hook.js') log-subagent.js start",
237
+ "bash": "node \"$(cd \"$(dirname \"$0\")\" && pwd)/../../pbr/scripts/run-hook.js\" hook-server-client.js log-subagent-start",
238
+ "powershell": "node (Join-Path (Split-Path -Parent $PSScriptRoot) 'pbr\\scripts\\run-hook.js') hook-server-client.js log-subagent-start",
239
239
  "cwd": ".",
240
240
  "timeoutSec": 15
241
241
  }
@@ -247,8 +247,8 @@
247
247
  "hooks": [
248
248
  {
249
249
  "type": "command",
250
- "bash": "node \"$(cd \"$(dirname \"$0\")\" && pwd)/../../pbr/scripts/run-hook.js\" log-subagent.js stop",
251
- "powershell": "node (Join-Path (Split-Path -Parent $PSScriptRoot) 'pbr\\scripts\\run-hook.js') log-subagent.js stop",
250
+ "bash": "node \"$(cd \"$(dirname \"$0\")\" && pwd)/../../pbr/scripts/run-hook.js\" hook-server-client.js log-subagent",
251
+ "powershell": "node (Join-Path (Split-Path -Parent $PSScriptRoot) 'pbr\\scripts\\run-hook.js') hook-server-client.js log-subagent",
252
252
  "cwd": ".",
253
253
  "timeoutSec": 15
254
254
  }
@@ -258,8 +258,8 @@
258
258
  "hooks": [
259
259
  {
260
260
  "type": "command",
261
- "bash": "node \"$(cd \"$(dirname \"$0\")\" && pwd)/../../pbr/scripts/run-hook.js\" event-handler.js",
262
- "powershell": "node (Join-Path (Split-Path -Parent $PSScriptRoot) 'pbr\\scripts\\run-hook.js') event-handler.js",
261
+ "bash": "node \"$(cd \"$(dirname \"$0\")\" && pwd)/../../pbr/scripts/run-hook.js\" hook-server-client.js event-handler",
262
+ "powershell": "node (Join-Path (Split-Path -Parent $PSScriptRoot) 'pbr\\scripts\\run-hook.js') hook-server-client.js event-handler",
263
263
  "cwd": ".",
264
264
  "timeoutSec": 15
265
265
  }
@@ -271,8 +271,8 @@
271
271
  "hooks": [
272
272
  {
273
273
  "type": "command",
274
- "bash": "node \"$(cd \"$(dirname \"$0\")\" && pwd)/../../pbr/scripts/run-hook.js\" task-completed.js",
275
- "powershell": "node (Join-Path (Split-Path -Parent $PSScriptRoot) 'pbr\\scripts\\run-hook.js') task-completed.js",
274
+ "bash": "node \"$(cd \"$(dirname \"$0\")\" && pwd)/../../pbr/scripts/run-hook.js\" hook-server-client.js task-completed",
275
+ "powershell": "node (Join-Path (Split-Path -Parent $PSScriptRoot) 'pbr\\scripts\\run-hook.js') hook-server-client.js task-completed",
276
276
  "cwd": ".",
277
277
  "timeoutSec": 15
278
278
  }
@@ -284,8 +284,8 @@
284
284
  "hooks": [
285
285
  {
286
286
  "type": "command",
287
- "bash": "node \"$(cd \"$(dirname \"$0\")\" && pwd)/../../pbr/scripts/run-hook.js\" check-config-change.js",
288
- "powershell": "node (Join-Path (Split-Path -Parent $PSScriptRoot) 'pbr\\scripts\\run-hook.js') check-config-change.js",
287
+ "bash": "node \"$(cd \"$(dirname \"$0\")\" && pwd)/../../pbr/scripts/run-hook.js\" hook-server-client.js check-config-change",
288
+ "powershell": "node (Join-Path (Split-Path -Parent $PSScriptRoot) 'pbr\\scripts\\run-hook.js') hook-server-client.js check-config-change",
289
289
  "cwd": ".",
290
290
  "timeoutSec": 15
291
291
  }
@@ -297,8 +297,8 @@
297
297
  "hooks": [
298
298
  {
299
299
  "type": "command",
300
- "bash": "node \"$(cd \"$(dirname \"$0\")\" && pwd)/../../pbr/scripts/run-hook.js\" session-cleanup.js",
301
- "powershell": "node (Join-Path (Split-Path -Parent $PSScriptRoot) 'pbr\\scripts\\run-hook.js') session-cleanup.js",
300
+ "bash": "node \"$(cd \"$(dirname \"$0\")\" && pwd)/../../pbr/scripts/run-hook.js\" hook-server-client.js session-cleanup",
301
+ "powershell": "node (Join-Path (Split-Path -Parent $PSScriptRoot) 'pbr\\scripts\\run-hook.js') hook-server-client.js session-cleanup",
302
302
  "cwd": ".",
303
303
  "timeoutSec": 30
304
304
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pbr",
3
3
  "displayName": "Plan-Build-Run",
4
- "version": "2.61.1",
4
+ "version": "2.62.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.61.1",
4
+ "version": "2.62.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",
@@ -18,7 +18,7 @@
18
18
  "hooks": [
19
19
  {
20
20
  "type": "command",
21
- "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'))\" instructions-loaded.js",
21
+ "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'))\" hook-server-client.js instructions-loaded",
22
22
  "statusMessage": "Detecting instruction reload..."
23
23
  }
24
24
  ]
@@ -29,7 +29,7 @@
29
29
  "hooks": [
30
30
  {
31
31
  "type": "command",
32
- "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'))\" worktree-create.js",
32
+ "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'))\" hook-server-client.js worktree-create",
33
33
  "statusMessage": "Initializing worktree .planning/..."
34
34
  }
35
35
  ]
@@ -40,7 +40,7 @@
40
40
  "hooks": [
41
41
  {
42
42
  "type": "command",
43
- "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'))\" worktree-remove.js",
43
+ "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'))\" hook-server-client.js worktree-remove",
44
44
  "statusMessage": "Cleaning up worktree state..."
45
45
  }
46
46
  ]
@@ -62,7 +62,7 @@
62
62
  "hooks": [
63
63
  {
64
64
  "type": "command",
65
- "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-subagent-output.js",
65
+ "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'))\" hook-server-client.js check-subagent-output",
66
66
  "statusMessage": "Validating agent output..."
67
67
  }
68
68
  ]
@@ -103,7 +103,7 @@
103
103
  "hooks": [
104
104
  {
105
105
  "type": "command",
106
- "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'))\" log-tool-failure.js",
106
+ "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'))\" hook-server-client.js log-tool-failure",
107
107
  "statusMessage": "Logging tool failure..."
108
108
  }
109
109
  ]
@@ -176,7 +176,7 @@
176
176
  "hooks": [
177
177
  {
178
178
  "type": "command",
179
- "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'))\" context-budget-check.js",
179
+ "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'))\" hook-server-client.js context-budget-check",
180
180
  "statusMessage": "Preserving state before compaction..."
181
181
  }
182
182
  ]
@@ -198,7 +198,7 @@
198
198
  "hooks": [
199
199
  {
200
200
  "type": "command",
201
- "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'))\" log-subagent.js start",
201
+ "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'))\" hook-server-client.js log-subagent-start",
202
202
  "statusMessage": "Logging agent spawn..."
203
203
  }
204
204
  ]
@@ -209,7 +209,7 @@
209
209
  "hooks": [
210
210
  {
211
211
  "type": "command",
212
- "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'))\" log-subagent.js stop",
212
+ "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'))\" hook-server-client.js log-subagent",
213
213
  "statusMessage": "Logging agent completion..."
214
214
  }
215
215
  ]
@@ -218,7 +218,7 @@
218
218
  "hooks": [
219
219
  {
220
220
  "type": "command",
221
- "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'))\" event-handler.js",
221
+ "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'))\" hook-server-client.js event-handler",
222
222
  "statusMessage": "Checking for auto-verification..."
223
223
  }
224
224
  ]
@@ -229,7 +229,7 @@
229
229
  "hooks": [
230
230
  {
231
231
  "type": "command",
232
- "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'))\" task-completed.js",
232
+ "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'))\" hook-server-client.js task-completed",
233
233
  "statusMessage": "Processing task completion..."
234
234
  }
235
235
  ]
@@ -240,7 +240,7 @@
240
240
  "hooks": [
241
241
  {
242
242
  "type": "command",
243
- "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-config-change.js",
243
+ "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'))\" hook-server-client.js check-config-change",
244
244
  "statusMessage": "Validating configuration..."
245
245
  }
246
246
  ]
@@ -251,7 +251,7 @@
251
251
  "hooks": [
252
252
  {
253
253
  "type": "command",
254
- "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'))\" session-cleanup.js",
254
+ "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'))\" hook-server-client.js session-cleanup",
255
255
  "statusMessage": "Cleaning up session..."
256
256
  }
257
257
  ]
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pbr",
3
- "version": "2.61.1",
3
+ "version": "2.62.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",
@@ -23,7 +23,7 @@
23
23
  "hooks": [
24
24
  {
25
25
  "type": "command",
26
- "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'))\" instructions-loaded.js",
26
+ "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'))\" hook-server-client.js instructions-loaded",
27
27
  "statusMessage": "Detecting instruction reload..."
28
28
  }
29
29
  ]
@@ -34,7 +34,7 @@
34
34
  "hooks": [
35
35
  {
36
36
  "type": "command",
37
- "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'))\" worktree-create.js",
37
+ "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'))\" hook-server-client.js worktree-create",
38
38
  "statusMessage": "Initializing worktree .planning/..."
39
39
  }
40
40
  ]
@@ -45,7 +45,7 @@
45
45
  "hooks": [
46
46
  {
47
47
  "type": "command",
48
- "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'))\" worktree-remove.js",
48
+ "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'))\" hook-server-client.js worktree-remove",
49
49
  "statusMessage": "Cleaning up worktree state..."
50
50
  }
51
51
  ]
@@ -67,7 +67,7 @@
67
67
  "hooks": [
68
68
  {
69
69
  "type": "command",
70
- "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-subagent-output.js",
70
+ "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'))\" hook-server-client.js check-subagent-output",
71
71
  "statusMessage": "Validating agent output..."
72
72
  }
73
73
  ]
@@ -108,7 +108,7 @@
108
108
  "hooks": [
109
109
  {
110
110
  "type": "command",
111
- "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'))\" log-tool-failure.js",
111
+ "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'))\" hook-server-client.js log-tool-failure",
112
112
  "statusMessage": "Logging tool failure..."
113
113
  }
114
114
  ]
@@ -181,7 +181,7 @@
181
181
  "hooks": [
182
182
  {
183
183
  "type": "command",
184
- "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'))\" context-budget-check.js",
184
+ "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'))\" hook-server-client.js context-budget-check",
185
185
  "statusMessage": "Preserving state before compaction..."
186
186
  }
187
187
  ]
@@ -203,7 +203,7 @@
203
203
  "hooks": [
204
204
  {
205
205
  "type": "command",
206
- "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'))\" log-subagent.js start",
206
+ "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'))\" hook-server-client.js log-subagent-start",
207
207
  "statusMessage": "Logging agent spawn..."
208
208
  }
209
209
  ]
@@ -214,7 +214,7 @@
214
214
  "hooks": [
215
215
  {
216
216
  "type": "command",
217
- "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'))\" log-subagent.js stop",
217
+ "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'))\" hook-server-client.js log-subagent",
218
218
  "statusMessage": "Logging agent completion..."
219
219
  }
220
220
  ]
@@ -223,7 +223,7 @@
223
223
  "hooks": [
224
224
  {
225
225
  "type": "command",
226
- "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'))\" event-handler.js",
226
+ "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'))\" hook-server-client.js event-handler",
227
227
  "statusMessage": "Checking for auto-verification..."
228
228
  }
229
229
  ]
@@ -234,7 +234,7 @@
234
234
  "hooks": [
235
235
  {
236
236
  "type": "command",
237
- "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'))\" task-completed.js",
237
+ "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'))\" hook-server-client.js task-completed",
238
238
  "statusMessage": "Processing task completion..."
239
239
  }
240
240
  ]
@@ -245,7 +245,7 @@
245
245
  "hooks": [
246
246
  {
247
247
  "type": "command",
248
- "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-config-change.js",
248
+ "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'))\" hook-server-client.js check-config-change",
249
249
  "statusMessage": "Validating configuration..."
250
250
  }
251
251
  ]
@@ -256,7 +256,7 @@
256
256
  "hooks": [
257
257
  {
258
258
  "type": "command",
259
- "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'))\" session-cleanup.js",
259
+ "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'))\" hook-server-client.js session-cleanup",
260
260
  "statusMessage": "Cleaning up session..."
261
261
  }
262
262
  ]
@@ -159,5 +159,30 @@ async function main() {
159
159
  process.exit(0);
160
160
  }
161
161
 
162
+ /**
163
+ * handleHttp — hook-server.js interface.
164
+ * reqBody = { event, tool, data, planningDir, cache }
165
+ * Returns { additionalContext: "..." } or null. Never calls process.exit().
166
+ */
167
+ function handleHttp(reqBody) {
168
+ const planningDir = (reqBody && reqBody.planningDir) || findPlanningDir();
169
+ if (!planningDir) return null;
170
+
171
+ const configPath = path.join(planningDir, 'config.json');
172
+ if (!fs.existsSync(configPath)) return null;
173
+
174
+ const warnings = validateConfig(configPath);
175
+ if (warnings.length > 0) {
176
+ const msg = `\u26a0\ufe0f Config validation (${warnings.length} issue${warnings.length > 1 ? 's' : ''}):\n${warnings.map(w => ` - ${w}`).join('\n')}`;
177
+ logHook('check-config-change', 'ConfigChange', 'warn', { warnings });
178
+ logEvent('workflow', 'config-change', { warnings });
179
+ return { additionalContext: msg };
180
+ }
181
+
182
+ logHook('check-config-change', 'ConfigChange', 'ok', {});
183
+ logEvent('workflow', 'config-change', { status: 'valid' });
184
+ return null;
185
+ }
186
+
162
187
  if (require.main === module || process.argv[1] === __filename) main();
163
- module.exports = { validateConfig, findPlanningDir };
188
+ module.exports = { validateConfig, findPlanningDir, handleHttp };
@@ -549,5 +549,93 @@ async function main() {
549
549
  process.exit(0);
550
550
  }
551
551
 
552
- module.exports = { AGENT_OUTPUTS, SKILL_CHECKS, findInPhaseDir, findInQuickDir, checkSummaryCommits, isRecent, getCurrentPhase, checkRoadmapStaleness };
552
+ /**
553
+ * HTTP handler for hook-server.js integration.
554
+ * Called as handleHttp(reqBody, cache) where reqBody = { event, tool, data, planningDir, cache }.
555
+ * Must NOT call process.exit().
556
+ * @param {{ data: object, planningDir: string }} reqBody
557
+ * @returns {Promise<{ additionalContext: string }|null>}
558
+ */
559
+ async function handleHttp(reqBody) {
560
+ const data = reqBody.data || {};
561
+ const planningDir = reqBody.planningDir;
562
+ if (!planningDir || !fs.existsSync(planningDir)) return null;
563
+
564
+ const agentType = data.agent_type || data.tool_input?.subagent_type || data.subagent_type || '';
565
+ const outputSpec = AGENT_OUTPUTS[agentType];
566
+ if (!outputSpec) {
567
+ const shortName = agentType.startsWith('pbr:') ? agentType.slice(4) : agentType;
568
+ if (KNOWN_AGENTS && KNOWN_AGENTS.includes && KNOWN_AGENTS.includes(shortName)) {
569
+ logHook('check-subagent-output', 'PostToolUse', 'missing-output-spec', {
570
+ agent_type: agentType,
571
+ message: `Agent ${agentType} is in KNOWN_AGENTS but has no AGENT_OUTPUTS entry. Add one to check-subagent-output.js.`
572
+ });
573
+ }
574
+ return null;
575
+ }
576
+
577
+ let activeSkill = sessionLoad(planningDir).activeSkill || '';
578
+ if (!activeSkill) {
579
+ try { activeSkill = fs.readFileSync(path.join(planningDir, '.active-skill'), 'utf8').trim(); } catch (_) { /* legacy file missing */ }
580
+ }
581
+
582
+ const found = outputSpec.check(planningDir);
583
+ const genericMissing = found.length === 0 && !outputSpec.noFileExpected;
584
+ const skillWarnings = [];
585
+
586
+ if (!activeSkill && agentType !== 'pbr:general' && agentType !== 'pbr:plan-checker' && agentType !== 'pbr:integration-checker') {
587
+ skillWarnings.push('.active-skill file is missing — the orchestrating skill never wrote it. This means skill-workflow guards were inactive for this entire operation. CRITICAL: Write the skill name to .planning/.active-skill BEFORE spawning agents.');
588
+ }
589
+
590
+ if (agentType === 'pbr:executor' || agentType === 'pbr:verifier') {
591
+ const roadmapWarning = checkRoadmapStaleness(planningDir);
592
+ if (roadmapWarning) skillWarnings.push(roadmapWarning);
593
+ }
594
+
595
+ if (found._stale && (agentType === 'pbr:researcher' || agentType === 'pbr:synthesizer')) {
596
+ const label = agentType === 'pbr:researcher' ? 'Researcher' : 'Synthesizer';
597
+ skillWarnings.push(`${label} output may be stale — no recent output files detected.`);
598
+ }
599
+
600
+ const skillCheckKey = `${activeSkill}:${agentType}`;
601
+ const skillCheck = SKILL_CHECKS[skillCheckKey];
602
+ if (skillCheck) skillCheck.check(planningDir, found, skillWarnings);
603
+
604
+ // LLM classification helper (advisory, never throws)
605
+ async function getLlmNote() {
606
+ try {
607
+ const cwd = process.env.PBR_PROJECT_ROOT || process.cwd();
608
+ const llmConfig = loadLocalLlmConfig(cwd);
609
+ const errorText = (data.tool_output || '').substring(0, 500);
610
+ if (!errorText) return '';
611
+ const llmResult = await classifyError(llmConfig, planningDir, errorText, agentType, data.session_id);
612
+ if (llmResult && llmResult.category) {
613
+ return `\nLLM error category: ${llmResult.category} (confidence: ${(llmResult.confidence * 100).toFixed(0)}%)`;
614
+ }
615
+ } catch (_e) { /* never propagate */ }
616
+ return '';
617
+ }
618
+
619
+ if (genericMissing && skillWarnings.length > 0) {
620
+ logHook('check-subagent-output', 'PostToolUse', 'skill-warning', { skill: activeSkill, agent_type: agentType, warnings: skillWarnings });
621
+ const llmCategoryNote = await getLlmNote();
622
+ const msg = `Warning: Agent ${agentType} completed but no ${outputSpec.description} was found.\nSkill-specific warnings:\n` +
623
+ skillWarnings.map(w => `- ${w}`).join('\n') + llmCategoryNote;
624
+ return { additionalContext: msg };
625
+ } else if (genericMissing) {
626
+ logHook('check-subagent-output', 'PostToolUse', 'warning', { agent_type: agentType, expected: outputSpec.description, found: 'none' });
627
+ const llmCategoryNote = await getLlmNote();
628
+ return {
629
+ additionalContext: `[WARN] Agent ${agentType} completed but no ${outputSpec.description} was found. Likely causes: (1) agent hit an error mid-run, (2) wrong working directory. To fix: re-run the parent skill — the executor gate will block until the output is present. Check the Task() output above for error details.` + llmCategoryNote
630
+ };
631
+ } else if (skillWarnings.length > 0) {
632
+ logHook('check-subagent-output', 'PostToolUse', 'skill-warning', { skill: activeSkill, agent_type: agentType, warnings: skillWarnings });
633
+ return { additionalContext: 'Skill-specific warnings:\n' + skillWarnings.map(w => `- ${w}`).join('\n') };
634
+ } else {
635
+ logHook('check-subagent-output', 'PostToolUse', 'verified', { agent_type: agentType, found: found });
636
+ return null;
637
+ }
638
+ }
639
+
640
+ module.exports = { AGENT_OUTPUTS, SKILL_CHECKS, findInPhaseDir, findInQuickDir, checkSummaryCommits, isRecent, getCurrentPhase, checkRoadmapStaleness, handleHttp };
553
641
  if (require.main === module || process.argv[1] === __filename) { main(); }
@@ -311,6 +311,30 @@
311
311
  },
312
312
  "additionalProperties": false
313
313
  },
314
+ "hook_server": {
315
+ "type": "object",
316
+ "description": "Persistent HTTP hook server settings. When enabled, hooks POST to the server instead of spawning per-hook processes.",
317
+ "properties": {
318
+ "enabled": {
319
+ "type": "boolean",
320
+ "default": false,
321
+ "description": "When true, hook-server-client.js routes hook events to the persistent hook-server.js process."
322
+ },
323
+ "port": {
324
+ "type": "integer",
325
+ "minimum": 1024,
326
+ "maximum": 65535,
327
+ "default": 19836,
328
+ "description": "TCP port the hook server listens on (127.0.0.1 only)."
329
+ },
330
+ "event_log": {
331
+ "type": "boolean",
332
+ "default": true,
333
+ "description": "When true, all hook events are appended to .planning/.hook-events.jsonl."
334
+ }
335
+ },
336
+ "additionalProperties": false
337
+ },
314
338
  "local_llm": {
315
339
  "type": "object",
316
340
  "properties": {