@sienklogic/plan-build-run 2.57.0 → 2.59.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.
Files changed (90) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/package.json +1 -1
  3. package/plugins/codex-pbr/commands/profile.md +7 -0
  4. package/plugins/codex-pbr/references/model-profiles.md +47 -3
  5. package/plugins/codex-pbr/references/model-selection.md +1 -1
  6. package/plugins/codex-pbr/skills/begin/SKILL.md +10 -7
  7. package/plugins/codex-pbr/skills/build/SKILL.md +8 -4
  8. package/plugins/codex-pbr/skills/debug/SKILL.md +2 -2
  9. package/plugins/codex-pbr/skills/discuss/SKILL.md +2 -2
  10. package/plugins/codex-pbr/skills/health/SKILL.md +2 -2
  11. package/plugins/codex-pbr/skills/milestone/SKILL.md +8 -8
  12. package/plugins/codex-pbr/skills/pause/SKILL.md +1 -1
  13. package/plugins/codex-pbr/skills/plan/SKILL.md +9 -7
  14. package/plugins/codex-pbr/skills/profile/SKILL.md +171 -0
  15. package/plugins/codex-pbr/skills/review/SKILL.md +5 -3
  16. package/plugins/codex-pbr/skills/scan/SKILL.md +2 -2
  17. package/plugins/codex-pbr/skills/setup/SKILL.md +68 -2
  18. package/plugins/copilot-pbr/commands/profile.md +7 -0
  19. package/plugins/copilot-pbr/hooks/hooks.json +39 -0
  20. package/plugins/copilot-pbr/plugin.json +1 -1
  21. package/plugins/copilot-pbr/references/model-profiles.md +47 -3
  22. package/plugins/copilot-pbr/references/model-selection.md +1 -1
  23. package/plugins/copilot-pbr/skills/begin/SKILL.md +10 -7
  24. package/plugins/copilot-pbr/skills/build/SKILL.md +8 -4
  25. package/plugins/copilot-pbr/skills/debug/SKILL.md +2 -2
  26. package/plugins/copilot-pbr/skills/discuss/SKILL.md +2 -2
  27. package/plugins/copilot-pbr/skills/health/SKILL.md +2 -2
  28. package/plugins/copilot-pbr/skills/milestone/SKILL.md +8 -8
  29. package/plugins/copilot-pbr/skills/pause/SKILL.md +1 -1
  30. package/plugins/copilot-pbr/skills/plan/SKILL.md +9 -7
  31. package/plugins/copilot-pbr/skills/profile/SKILL.md +171 -0
  32. package/plugins/copilot-pbr/skills/review/SKILL.md +5 -3
  33. package/plugins/copilot-pbr/skills/scan/SKILL.md +2 -2
  34. package/plugins/copilot-pbr/skills/setup/SKILL.md +68 -2
  35. package/plugins/cursor-pbr/.cursor-plugin/plugin.json +1 -1
  36. package/plugins/cursor-pbr/commands/profile.md +7 -0
  37. package/plugins/cursor-pbr/hooks/hooks.json +33 -0
  38. package/plugins/cursor-pbr/references/model-profiles.md +47 -3
  39. package/plugins/cursor-pbr/references/model-selection.md +1 -1
  40. package/plugins/cursor-pbr/skills/begin/SKILL.md +10 -7
  41. package/plugins/cursor-pbr/skills/build/SKILL.md +9 -5
  42. package/plugins/cursor-pbr/skills/debug/SKILL.md +2 -2
  43. package/plugins/cursor-pbr/skills/discuss/SKILL.md +2 -2
  44. package/plugins/cursor-pbr/skills/health/SKILL.md +2 -2
  45. package/plugins/cursor-pbr/skills/milestone/SKILL.md +8 -8
  46. package/plugins/cursor-pbr/skills/pause/SKILL.md +1 -1
  47. package/plugins/cursor-pbr/skills/plan/SKILL.md +10 -8
  48. package/plugins/cursor-pbr/skills/profile/SKILL.md +172 -0
  49. package/plugins/cursor-pbr/skills/review/SKILL.md +6 -4
  50. package/plugins/cursor-pbr/skills/scan/SKILL.md +2 -2
  51. package/plugins/cursor-pbr/skills/setup/SKILL.md +68 -2
  52. package/plugins/pbr/.claude-plugin/plugin.json +1 -1
  53. package/plugins/pbr/agents/audit.md +0 -1
  54. package/plugins/pbr/agents/codebase-mapper.md +0 -1
  55. package/plugins/pbr/agents/debugger.md +0 -1
  56. package/plugins/pbr/agents/dev-sync.md +0 -1
  57. package/plugins/pbr/agents/executor.md +0 -1
  58. package/plugins/pbr/agents/general.md +0 -1
  59. package/plugins/pbr/agents/integration-checker.md +0 -1
  60. package/plugins/pbr/agents/plan-checker.md +0 -1
  61. package/plugins/pbr/agents/planner.md +0 -1
  62. package/plugins/pbr/agents/researcher.md +0 -1
  63. package/plugins/pbr/agents/synthesizer.md +0 -1
  64. package/plugins/pbr/agents/verifier.md +0 -1
  65. package/plugins/pbr/commands/profile.md +7 -0
  66. package/plugins/pbr/hooks/hooks.json +33 -0
  67. package/plugins/pbr/references/model-profiles.md +47 -3
  68. package/plugins/pbr/references/model-selection.md +1 -1
  69. package/plugins/pbr/scripts/check-subagent-output.js +1 -1
  70. package/plugins/pbr/scripts/config-schema.json +18 -0
  71. package/plugins/pbr/scripts/hooks-schema.json +4 -1
  72. package/plugins/pbr/scripts/instructions-loaded.js +69 -0
  73. package/plugins/pbr/scripts/lib/gates/helpers.js +7 -0
  74. package/plugins/pbr/scripts/lib/init.js +10 -7
  75. package/plugins/pbr/scripts/pbr-tools.js +6 -6
  76. package/plugins/pbr/scripts/task-completed.js +89 -1
  77. package/plugins/pbr/scripts/worktree-create.js +93 -0
  78. package/plugins/pbr/scripts/worktree-remove.js +83 -0
  79. package/plugins/pbr/skills/begin/SKILL.md +10 -7
  80. package/plugins/pbr/skills/build/SKILL.md +9 -5
  81. package/plugins/pbr/skills/debug/SKILL.md +2 -2
  82. package/plugins/pbr/skills/discuss/SKILL.md +2 -2
  83. package/plugins/pbr/skills/health/SKILL.md +2 -2
  84. package/plugins/pbr/skills/milestone/SKILL.md +8 -8
  85. package/plugins/pbr/skills/pause/SKILL.md +1 -1
  86. package/plugins/pbr/skills/plan/SKILL.md +10 -8
  87. package/plugins/pbr/skills/profile/SKILL.md +173 -0
  88. package/plugins/pbr/skills/review/SKILL.md +6 -4
  89. package/plugins/pbr/skills/scan/SKILL.md +2 -2
  90. package/plugins/pbr/skills/setup/SKILL.md +68 -2
@@ -57,6 +57,8 @@ Stop. Do not proceed further.
57
57
 
58
58
  ## Step 2: Model Selection
59
59
 
60
+ Note: These profiles control agent models, not the orchestrator. The orchestrator uses your Claude Code session model. For cost optimization, consider running your session on Sonnet -- agents with `inherit` will follow. See `references/model-profiles.md` for details.
61
+
60
62
  Use AskUserQuestion:
61
63
  question: "Which model profile should agents use?"
62
64
  header: "Models"
@@ -133,7 +135,69 @@ This project uses [Plan-Build-Run](https://github.com/SienkLogic/plan-build-run)
133
135
 
134
136
  ---
135
137
 
136
- ## Step 5: Write Updated Config
138
+ ## Step 5: Platform Settings
139
+
140
+ Manage Claude Code platform settings that PBR recommends for optimal token usage.
141
+
142
+ **Read `.planning/config.json`** (already loaded in Step 1). Check for a `platform_settings` block:
143
+ - If absent, treat it as `{}` (no managed settings).
144
+
145
+ **Check for existing `.claude/settings.json`**:
146
+ - If it exists, read its contents. Parse as JSON (or `{}` if empty/missing).
147
+ - If it does not exist, start from `{}`.
148
+
149
+ **Compute the merge**:
150
+ - Start with the existing `.claude/settings.json` content.
151
+ - Apply each key from `platform_settings` in config.json on top.
152
+ - If `platform_settings` is absent or empty, skip the write entirely.
153
+
154
+ **Report drift** (compare before/after):
155
+ - List keys being ADDED (were absent in .claude/settings.json)
156
+ - List keys being CHANGED (differ from existing .claude/settings.json value)
157
+ - List keys that MATCH (already correct — no change needed)
158
+
159
+ Display the report:
160
+
161
+ ```
162
+ Platform Settings:
163
+ Keys added: includeGitInstructions → false
164
+ Keys matched: (none)
165
+ Keys changed: (none)
166
+
167
+ Writing .claude/settings.json...
168
+ ```
169
+
170
+ If all keys already match, display:
171
+
172
+ ```
173
+ Platform Settings: .claude/settings.json is already in sync — no changes needed.
174
+ ```
175
+
176
+ **Conflict detection**: If an existing `.claude/settings.json` has a key from `platform_settings` set to the OPPOSITE value (e.g., `"includeGitInstructions": true` when PBR wants `false`), display a WARNING before overwriting:
177
+
178
+ ```
179
+ WARNING: .claude/settings.json has includeGitInstructions: true
180
+ PBR recommends false (removes redundant built-in git instructions).
181
+ Overwriting with PBR recommended value.
182
+ ```
183
+
184
+ **Write the merged settings.json**:
185
+ - Write to `.claude/settings.json` in the project root (alongside `.planning/`).
186
+ - Ensure the directory `.claude/` exists (create it if needed with Bash `mkdir -p .claude`).
187
+ - Write with 2-space indentation. Include a `$schema` key at the top:
188
+ ```json
189
+ {
190
+ "$schema": "https://json.schemastore.org/claude-code-settings.json",
191
+ "includeGitInstructions": false
192
+ }
193
+ ```
194
+ (Merge in any pre-existing keys from .claude/settings.json that are NOT managed by PBR — pass them through unchanged at the bottom of the object.)
195
+
196
+ **Drift detection note**: On subsequent /pbr:setup runs, if `.claude/settings.json` has drifted from `platform_settings` (e.g., user manually changed it), this step will detect and re-apply the PBR values with a drift warning. This is by design — platform_settings in config.json is the source of truth.
197
+
198
+ ---
199
+
200
+ ## Step 6: Write Updated Config
137
201
 
138
202
  **CRITICAL: Write `.planning/config.json` NOW with all changes from Steps 2-3. Do NOT skip this step.**
139
203
 
@@ -141,13 +205,14 @@ Write the updated config.json to disk with all applied changes.
141
205
 
142
206
  ---
143
207
 
144
- ## Step 6: Verification
208
+ ## Step 7: Verification
145
209
 
146
210
  Run a quick check:
147
211
  1. Verify `.planning/config.json` is valid JSON (read it back)
148
212
  2. Display updated settings summary
149
213
 
150
214
  Display:
215
+
151
216
  ```
152
217
  ╔══════════════════════════════════════════════════════════════╗
153
218
  ║ PLAN-BUILD-RUN ► RECONFIGURE COMPLETE ✓ ║
@@ -157,6 +222,7 @@ Updated:
157
222
  - Model profile: {new profile}
158
223
  - Features changed: {list or "none"}
159
224
  - CLAUDE.md: {updated/skipped}
225
+ - Platform settings: {".claude/settings.json updated" or "already in sync" or "no managed settings"}
160
226
 
161
227
  Run /pbr:status to see current project state.
162
228
  ```
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pbr",
3
- "version": "2.57.0",
3
+ "version": "2.59.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",
@@ -1,7 +1,6 @@
1
1
  ---
2
2
  name: audit
3
3
  description: "Analyzes Claude Code session logs for PBR workflow compliance, hook firing, state file hygiene, and user experience quality."
4
- model: sonnet
5
4
  memory: none
6
5
  tools:
7
6
  - Read
@@ -1,7 +1,6 @@
1
1
  ---
2
2
  name: codebase-mapper
3
3
  description: "Explores existing codebases and writes structured analysis documents. Four focus areas: tech, arch, quality, concerns."
4
- model: sonnet
5
4
  memory: none
6
5
  tools:
7
6
  - Read
@@ -1,7 +1,6 @@
1
1
  ---
2
2
  name: debugger
3
3
  description: "Systematic debugging using scientific method. Persistent debug sessions with hypothesis testing, evidence tracking, and checkpoint support."
4
- model: sonnet
5
4
  memory: project
6
5
  tools:
7
6
  - Read
@@ -1,7 +1,6 @@
1
1
  ---
2
2
  name: dev-sync
3
3
  description: "Syncs PBR plugin changes to cursor-pbr and copilot-pbr derivatives with format adjustments."
4
- model: sonnet
5
4
  memory: none
6
5
  tools:
7
6
  - Read
@@ -1,7 +1,6 @@
1
1
  ---
2
2
  name: executor
3
3
  description: "Executes plan tasks with atomic commits, deviation handling, checkpoint protocols, TDD support, and self-verification."
4
- model: sonnet
5
4
  memory: project
6
5
  tools:
7
6
  - Read
@@ -1,7 +1,6 @@
1
1
  ---
2
2
  name: general
3
3
  description: "Lightweight Plan-Build-Run-aware agent for ad-hoc tasks that don't fit specialized roles."
4
- model: sonnet
5
4
  memory: none
6
5
  tools:
7
6
  - Read
@@ -1,7 +1,6 @@
1
1
  ---
2
2
  name: integration-checker
3
3
  description: "Cross-phase integration and E2E flow verification. Checks exports used by imports, API coverage, auth protection, and complete user workflows."
4
- model: sonnet
5
4
  memory: none
6
5
  tools:
7
6
  - Read
@@ -1,7 +1,6 @@
1
1
  ---
2
2
  name: plan-checker
3
3
  description: "Verifies plans will achieve phase goals before execution. Goal-backward analysis of plan quality across 10 dimensions."
4
- model: sonnet
5
4
  memory: none
6
5
  tools:
7
6
  - Read
@@ -1,7 +1,6 @@
1
1
  ---
2
2
  name: planner
3
3
  description: "Creates executable phase plans with task breakdown, dependency analysis, wave assignment, and goal-backward verification. Also creates roadmaps."
4
- model: sonnet
5
4
  memory: project
6
5
  tools:
7
6
  - Read
@@ -1,7 +1,6 @@
1
1
  ---
2
2
  name: researcher
3
3
  description: "Unified research agent for project domains, phase implementation approaches, and synthesis. Follows source-hierarchy methodology with confidence levels."
4
- model: sonnet
5
4
  memory: user
6
5
  tools:
7
6
  - Read
@@ -1,7 +1,6 @@
1
1
  ---
2
2
  name: synthesizer
3
3
  description: "Fast synthesis of multiple research outputs into coherent recommendations. Resolves contradictions between sources."
4
- model: sonnet
5
4
  memory: none
6
5
  tools:
7
6
  - Read
@@ -1,7 +1,6 @@
1
1
  ---
2
2
  name: verifier
3
3
  description: "Goal-backward phase verification. Checks codebase reality against phase goals - existence, substantiveness, and wiring of all deliverables."
4
- model: sonnet
5
4
  memory: none
6
5
  isolation: worktree
7
6
  tools:
@@ -0,0 +1,7 @@
1
+ ---
2
+ name: profile
3
+ description: "Switch the active model profile. Presets: quality, balanced, budget, adaptive. Supports custom profiles from config.json model_profiles."
4
+ skill: profile
5
+ ---
6
+
7
+ This command is provided by the `pbr:profile` skill.
@@ -18,6 +18,39 @@
18
18
  ]
19
19
  }
20
20
  ],
21
+ "InstructionsLoaded": [
22
+ {
23
+ "hooks": [
24
+ {
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",
27
+ "statusMessage": "Detecting instruction reload..."
28
+ }
29
+ ]
30
+ }
31
+ ],
32
+ "WorktreeCreate": [
33
+ {
34
+ "hooks": [
35
+ {
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",
38
+ "statusMessage": "Initializing worktree .planning/..."
39
+ }
40
+ ]
41
+ }
42
+ ],
43
+ "WorktreeRemove": [
44
+ {
45
+ "hooks": [
46
+ {
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",
49
+ "statusMessage": "Cleaning up worktree state..."
50
+ }
51
+ ]
52
+ }
53
+ ],
21
54
  "PostToolUse": [
22
55
  {
23
56
  "matcher": "Write|Edit",
@@ -4,9 +4,29 @@ How Plan-Build-Run maps agents to models and how to configure model selection.
4
4
 
5
5
  ---
6
6
 
7
+ ## Session Model (Orchestrator)
8
+
9
+ The orchestrator is your main Claude Code session -- it reads skills, manages state, and routes work to agents. Its model is whatever you selected in Claude Code (not controlled by PBR's `config.json`).
10
+
11
+ **Cost impact:** The orchestrator accounts for roughly 40% of total session cost. Agents set to `inherit` (planner, executor, debugger, general in the `balanced` profile) also use this model. Switching your session from Opus to Sonnet reduces cost for both the orchestrator and all `inherit` agents.
12
+
13
+ **Recommendations:**
14
+
15
+ | Goal | Session Model | Config Adjustment |
16
+ |------|---------------|-------------------|
17
+ | Maximum quality | Opus | None needed -- `inherit` agents get Opus |
18
+ | Balanced cost/quality | Sonnet | None needed -- `inherit` agents get Sonnet, which handles most tasks well |
19
+ | Cheap orchestration, Opus agents | Sonnet | Set `planner: "opus"`, `executor: "opus"`, `debugger: "opus"` to override inherit |
20
+
21
+ Sonnet 4.6 handles orchestration tasks (state reading, routing decisions, context assembly) effectively. Reserve Opus for agents doing complex reasoning, architecture, or novel code generation.
22
+
23
+ ---
24
+
7
25
  ## Agent-to-Model Mapping
8
26
 
9
- Each Plan-Build-Run agent has a default model specified in its agent definition frontmatter (`model:` field). These defaults are overridden by the `models` section of `config.json`.
27
+ Each Plan-Build-Run agent's model is controlled by `config.json models.*`. When no config entry exists for an agent, the agent inherits the session model.
28
+
29
+ The `model:` frontmatter field has been removed from PBR agent definitions — `config.json` is now the sole source of truth for agent model assignment.
10
30
 
11
31
  ### Default Agent Models
12
32
 
@@ -22,8 +42,10 @@ Each Plan-Build-Run agent has a default model specified in its agent definition
22
42
  | `codebase-mapper` | `sonnet` | Codebase analysis requires thorough reasoning |
23
43
  | `synthesizer` | `haiku` | Synthesis is mechanical combination; speed over depth |
24
44
  | `general` | `inherit` | Lightweight utility; inherits session model |
45
+ | `audit` | `inherit` | Session log analysis inherits session model |
46
+ | `dev-sync` | `inherit` | Cross-plugin sync is mechanical; inherits session model |
25
47
 
26
- The `inherit` value means the agent uses whatever model the parent session is running (typically the user's configured Claude model).
48
+ The `inherit` value means the agent uses whatever model the parent session is running (typically the user's configured Claude model). Defaults listed above are the effective values from the `balanced` profile in `config.json`.
27
49
 
28
50
  ---
29
51
 
@@ -88,6 +110,28 @@ Note: Claude Code 2.1.45+ supports Sonnet 4.6. Model values are abstract names
88
110
 
89
111
  ---
90
112
 
113
+ ## Custom Profiles
114
+
115
+ The `model_profiles` key in `config.json` lets you define named profiles beyond the four built-in presets. Each profile is a partial map of agent names to model strings — omitted agents fall back to the active profile defaults.
116
+
117
+ ```json
118
+ {
119
+ "model_profiles": {
120
+ "my-profile": {
121
+ "executor": "opus",
122
+ "planner": "opus",
123
+ "synthesizer": "sonnet"
124
+ }
125
+ }
126
+ }
127
+ ```
128
+
129
+ Activate a custom profile with `/pbr:config model-profile my-profile`. The profile merges with the `balanced` preset: any agent not listed in your custom profile uses its `balanced` default.
130
+
131
+ Partial profiles are allowed — you can override just the agents you care about. This is useful for temporarily upgrading a single agent without specifying the rest.
132
+
133
+ ---
134
+
91
135
  ## Model Selection in Skill Orchestration
92
136
 
93
137
  Skills that spawn subagents use the `model` parameter in `Task()` calls. Some skills hardcode a lighter model for specific tasks:
@@ -96,4 +140,4 @@ Skills that spawn subagents use the `model` parameter in `Task()` calls. Some sk
96
140
  - **Build skill**: Spawns codebase mapper updates with `model: "haiku"` for incremental map refreshes
97
141
  - **Plan skill**: Uses the configured `planner` model for main planning work
98
142
 
99
- The `subagent_type` parameter automatically loads the agent definition, and the model from `config.json` takes precedence over the agent's default `model:` frontmatter field.
143
+ The `subagent_type` parameter automatically loads the agent definition, and the model from `config.json` takes precedence over any agent-level model setting.
@@ -14,7 +14,7 @@ Plan-Build-Run uses adaptive model selection to balance cost and capability.
14
14
  |-----------|-------|-----------|
15
15
  | simple | haiku | Fast, cheap — sufficient for mechanical changes |
16
16
  | medium | sonnet | Good balance for standard feature work |
17
- | complex | inherit | Uses session model — typically Opus for critical work |
17
+ | complex | inherit | Uses session model — see `model-profiles.md` Session Model section for cost implications |
18
18
 
19
19
  ## Override Mechanisms
20
20
 
@@ -421,7 +421,7 @@ async function main() {
421
421
  }
422
422
 
423
423
  // Extract agent type from the Task completion data
424
- const agentType = data.tool_input?.subagent_type || data.subagent_type || '';
424
+ const agentType = data.agent_type || data.tool_input?.subagent_type || data.subagent_type || '';
425
425
 
426
426
  // Only check known Plan-Build-Run agent types
427
427
  const outputSpec = AGENT_OUTPUTS[agentType];
@@ -52,6 +52,24 @@
52
52
  },
53
53
  "additionalProperties": false
54
54
  },
55
+ "model_profiles": {
56
+ "type": "object",
57
+ "description": "User-defined custom model profiles. Each key is a profile name; value is an object mapping agent names to model strings (sonnet, opus, haiku, inherit). Partial profiles allowed — omitted agents fall back to the active profile defaults.",
58
+ "additionalProperties": {
59
+ "type": "object",
60
+ "properties": {
61
+ "researcher": { "type": "string" },
62
+ "planner": { "type": "string" },
63
+ "executor": { "type": "string" },
64
+ "verifier": { "type": "string" },
65
+ "integration_checker": { "type": "string" },
66
+ "debugger": { "type": "string" },
67
+ "mapper": { "type": "string" },
68
+ "synthesizer": { "type": "string" }
69
+ },
70
+ "additionalProperties": false
71
+ }
72
+ },
55
73
  "parallelization": {
56
74
  "type": "object",
57
75
  "properties": {
@@ -10,6 +10,7 @@
10
10
  "type": "object",
11
11
  "properties": {
12
12
  "SessionStart": { "$ref": "#/definitions/hookEntryList" },
13
+ "InstructionsLoaded": { "$ref": "#/definitions/hookEntryList" },
13
14
  "PreToolUse": { "$ref": "#/definitions/hookEntryList" },
14
15
  "PostToolUse": { "$ref": "#/definitions/hookEntryList" },
15
16
  "PostToolUseFailure": { "$ref": "#/definitions/hookEntryList" },
@@ -19,7 +20,9 @@
19
20
  "SubagentStop": { "$ref": "#/definitions/hookEntryList" },
20
21
  "TaskCompleted": { "$ref": "#/definitions/hookEntryList" },
21
22
  "ConfigChange": { "$ref": "#/definitions/hookEntryList" },
22
- "SessionEnd": { "$ref": "#/definitions/hookEntryList" }
23
+ "SessionEnd": { "$ref": "#/definitions/hookEntryList" },
24
+ "WorktreeCreate": { "$ref": "#/definitions/hookEntryList" },
25
+ "WorktreeRemove": { "$ref": "#/definitions/hookEntryList" }
23
26
  },
24
27
  "additionalProperties": false
25
28
  }
@@ -0,0 +1,69 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * InstructionsLoaded hook: Lightweight PBR state reminder after CLAUDE.md loads.
5
+ *
6
+ * Fires each time Claude Code loads CLAUDE.md or .claude/rules/*.md files.
7
+ * For PBR projects: injects a brief reminder that PBR workflow is active.
8
+ * Debounces mid-session re-fires using .session.json load timestamp.
9
+ *
10
+ * Non-blocking — always exits 0.
11
+ */
12
+
13
+ 'use strict';
14
+
15
+ const fs = require('fs');
16
+ const path = require('path');
17
+ const { logHook } = require('./hook-logger');
18
+
19
+ function readStdin() {
20
+ try {
21
+ const input = fs.readFileSync(0, 'utf8').trim();
22
+ if (input) return JSON.parse(input);
23
+ } catch (_e) { /* empty or non-JSON stdin */ }
24
+ return {};
25
+ }
26
+
27
+ function main() {
28
+ const data = readStdin();
29
+ const cwd = process.env.PBR_PROJECT_ROOT || process.cwd();
30
+ const planningDir = path.join(cwd, '.planning');
31
+
32
+ // Only relevant for PBR projects
33
+ if (!fs.existsSync(planningDir)) {
34
+ process.exit(0);
35
+ }
36
+
37
+ // Debounce: only emit additionalContext on first fire per session.
38
+ // .session.json is written by progress-tracker.js at SessionStart.
39
+ // If it exists with a sessionStart timestamp, this is a mid-session reload.
40
+ let isMidSession = false;
41
+ try {
42
+ const sessionFile = path.join(planningDir, '.session.json');
43
+ if (fs.existsSync(sessionFile)) {
44
+ const session = JSON.parse(fs.readFileSync(sessionFile, 'utf8'));
45
+ if (session.sessionStart) {
46
+ isMidSession = true;
47
+ }
48
+ }
49
+ } catch (_e) { /* non-fatal */ }
50
+
51
+ logHook('instructions-loaded', 'InstructionsLoaded', isMidSession ? 'mid-session-reload' : 'initial-load', {
52
+ instructions_file: data.instructions_file || null
53
+ });
54
+
55
+ // Mid-session: note that instructions reloaded (CLAUDE.md may have changed)
56
+ if (isMidSession) {
57
+ process.stdout.write(JSON.stringify({
58
+ additionalContext: '[Plan-Build-Run] Project CLAUDE.md was reloaded mid-session. If PBR workflow rules changed, current skill invocation may be affected. Check .planning/STATE.md for current position.'
59
+ }));
60
+ process.exit(0);
61
+ }
62
+
63
+ // Initial load: no additionalContext needed — progress-tracker.js already
64
+ // injected full state at SessionStart. Avoid double-injection.
65
+ process.exit(0);
66
+ }
67
+
68
+ if (require.main === module || process.argv[1] === __filename) { main(); }
69
+ module.exports = { main };
@@ -15,6 +15,13 @@ const path = require('path');
15
15
  * @returns {string|null}
16
16
  */
17
17
  function readActiveSkill(planningDir) {
18
+ // Check .session.json first (consistent with check-subagent-output.js and log-subagent.js)
19
+ try {
20
+ const { sessionLoad } = require('../../pbr-tools');
21
+ const session = sessionLoad(planningDir);
22
+ if (session && session.activeSkill) return session.activeSkill;
23
+ } catch (_e) { /* pbr-tools unavailable */ }
24
+ // Fall back to legacy .active-skill file
18
25
  try {
19
26
  return fs.readFileSync(path.join(planningDir, '.active-skill'), 'utf8').trim();
20
27
  } catch (_e) {
@@ -17,8 +17,9 @@ const { phaseInfo, planIndex } = require('./phase');
17
17
  *
18
18
  * @param {string} phaseNum - Phase number
19
19
  * @param {string} [planningDir] - Path to .planning directory
20
+ * @param {string} [overrideModel] - Optional model override from --model CLI flag (sonnet|opus|haiku|inherit)
20
21
  */
21
- function initExecutePhase(phaseNum, planningDir) {
22
+ function initExecutePhase(phaseNum, planningDir, overrideModel) {
22
23
  const dir = planningDir || path.join(process.env.PBR_PROJECT_ROOT || process.cwd(), '.planning');
23
24
  const state = stateLoad(dir);
24
25
  if (!state.exists) return { error: "No .planning/ directory found" };
@@ -36,8 +37,8 @@ function initExecutePhase(phaseNum, planningDir) {
36
37
  gitState = { branch, clean: status === "" };
37
38
  } catch (_e) { /* not a git repo */ }
38
39
  return {
39
- executor_model: models.executor || "sonnet",
40
- verifier_model: models.verifier || "sonnet",
40
+ executor_model: overrideModel || models.executor || "sonnet",
41
+ verifier_model: overrideModel || models.verifier || "sonnet",
41
42
  config: { depth: depthProfile.depth, mode: config.mode || "interactive", parallelization: config.parallelization || { enabled: false }, planning: config.planning || {}, gates: config.gates || {}, features: config.features || {} },
42
43
  phase: { num: phaseNum, dir: phase.phase, name: phase.name, goal: phase.goal, has_context: phase.has_context, status: phase.filesystem_status, plan_count: phase.plan_count, completed: phase.completed },
43
44
  plans: (plans.plans || []).map(p => ({ file: p.file, plan_id: p.plan_id, wave: p.wave, autonomous: p.autonomous, has_summary: p.has_summary, must_haves_count: p.must_haves_count, depends_on: p.depends_on })),
@@ -51,8 +52,9 @@ function initExecutePhase(phaseNum, planningDir) {
51
52
  *
52
53
  * @param {string} phaseNum - Phase number
53
54
  * @param {string} [planningDir] - Path to .planning directory
55
+ * @param {string} [overrideModel] - Optional model override from --model CLI flag (sonnet|opus|haiku|inherit)
54
56
  */
55
- function initPlanPhase(phaseNum, planningDir) {
57
+ function initPlanPhase(phaseNum, planningDir, overrideModel) {
56
58
  const dir = planningDir || path.join(process.env.PBR_PROJECT_ROOT || process.cwd(), '.planning');
57
59
  const state = stateLoad(dir);
58
60
  if (!state.exists) return { error: "No .planning/ directory found" };
@@ -72,7 +74,7 @@ function initPlanPhase(phaseNum, planningDir) {
72
74
  if (rp) { phaseGoal = rp.goal; phaseDeps = rp.depends_on; }
73
75
  }
74
76
  return {
75
- researcher_model: models.researcher || "sonnet", planner_model: models.planner || "sonnet", checker_model: models.planner || "sonnet",
77
+ researcher_model: overrideModel || models.researcher || "sonnet", planner_model: overrideModel || models.planner || "sonnet", checker_model: overrideModel || models.planner || "sonnet",
76
78
  config: { depth: depthProfile.depth, profile: depthProfile.profile, features: config.features || {}, planning: config.planning || {} },
77
79
  phase: { num: phaseNum, dir: phaseDirName, goal: phaseGoal, depends_on: phaseDeps },
78
80
  existing_artifacts: existingArtifacts,
@@ -109,8 +111,9 @@ function initQuick(description, planningDir) {
109
111
  *
110
112
  * @param {string} phaseNum - Phase number
111
113
  * @param {string} [planningDir] - Path to .planning directory
114
+ * @param {string} [overrideModel] - Optional model override from --model CLI flag (sonnet|opus|haiku|inherit)
112
115
  */
113
- function initVerifyWork(phaseNum, planningDir) {
116
+ function initVerifyWork(phaseNum, planningDir, overrideModel) {
114
117
  const dir = planningDir || path.join(process.env.PBR_PROJECT_ROOT || process.cwd(), '.planning');
115
118
  const phase = phaseInfo(phaseNum, dir);
116
119
  if (phase.error) return { error: phase.error };
@@ -119,7 +122,7 @@ function initVerifyWork(phaseNum, planningDir) {
119
122
  let priorAttempts = 0;
120
123
  if (phase.verification) { priorAttempts = parseInt(phase.verification.attempt, 10) || 0; }
121
124
  return {
122
- verifier_model: models.verifier || "sonnet",
125
+ verifier_model: overrideModel || models.verifier || "sonnet",
123
126
  phase: { num: phaseNum, dir: phase.phase, name: phase.name, goal: phase.goal, plan_count: phase.plan_count, completed: phase.completed },
124
127
  has_verification: !!phase.verification, prior_attempts: priorAttempts,
125
128
  prior_status: phase.verification ? (phase.verification.status || "unknown") : null,
@@ -297,20 +297,20 @@ function milestoneStats(version) {
297
297
  return _milestoneStats(version, planningDir);
298
298
  }
299
299
 
300
- function initExecutePhase(phaseNum) {
301
- return _initExecutePhase(phaseNum, planningDir);
300
+ function initExecutePhase(phaseNum, overridePlanningDir, overrideModel) {
301
+ return _initExecutePhase(phaseNum, overridePlanningDir || planningDir, overrideModel);
302
302
  }
303
303
 
304
- function initPlanPhase(phaseNum) {
305
- return _initPlanPhase(phaseNum, planningDir);
304
+ function initPlanPhase(phaseNum, overridePlanningDir, overrideModel) {
305
+ return _initPlanPhase(phaseNum, overridePlanningDir || planningDir, overrideModel);
306
306
  }
307
307
 
308
308
  function initQuick(description) {
309
309
  return _initQuick(description, planningDir);
310
310
  }
311
311
 
312
- function initVerifyWork(phaseNum) {
313
- return _initVerifyWork(phaseNum, planningDir);
312
+ function initVerifyWork(phaseNum, overridePlanningDir, overrideModel) {
313
+ return _initVerifyWork(phaseNum, overridePlanningDir || planningDir, overrideModel);
314
314
  }
315
315
 
316
316
  function initResume() {