@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.
- package/CHANGELOG.md +39 -0
- package/package.json +1 -1
- package/plugins/codex-pbr/commands/profile.md +7 -0
- package/plugins/codex-pbr/references/model-profiles.md +47 -3
- package/plugins/codex-pbr/references/model-selection.md +1 -1
- package/plugins/codex-pbr/skills/begin/SKILL.md +10 -7
- package/plugins/codex-pbr/skills/build/SKILL.md +8 -4
- package/plugins/codex-pbr/skills/debug/SKILL.md +2 -2
- package/plugins/codex-pbr/skills/discuss/SKILL.md +2 -2
- package/plugins/codex-pbr/skills/health/SKILL.md +2 -2
- package/plugins/codex-pbr/skills/milestone/SKILL.md +8 -8
- package/plugins/codex-pbr/skills/pause/SKILL.md +1 -1
- package/plugins/codex-pbr/skills/plan/SKILL.md +9 -7
- package/plugins/codex-pbr/skills/profile/SKILL.md +171 -0
- package/plugins/codex-pbr/skills/review/SKILL.md +5 -3
- package/plugins/codex-pbr/skills/scan/SKILL.md +2 -2
- package/plugins/codex-pbr/skills/setup/SKILL.md +68 -2
- package/plugins/copilot-pbr/commands/profile.md +7 -0
- package/plugins/copilot-pbr/hooks/hooks.json +39 -0
- package/plugins/copilot-pbr/plugin.json +1 -1
- package/plugins/copilot-pbr/references/model-profiles.md +47 -3
- package/plugins/copilot-pbr/references/model-selection.md +1 -1
- package/plugins/copilot-pbr/skills/begin/SKILL.md +10 -7
- package/plugins/copilot-pbr/skills/build/SKILL.md +8 -4
- package/plugins/copilot-pbr/skills/debug/SKILL.md +2 -2
- package/plugins/copilot-pbr/skills/discuss/SKILL.md +2 -2
- package/plugins/copilot-pbr/skills/health/SKILL.md +2 -2
- package/plugins/copilot-pbr/skills/milestone/SKILL.md +8 -8
- package/plugins/copilot-pbr/skills/pause/SKILL.md +1 -1
- package/plugins/copilot-pbr/skills/plan/SKILL.md +9 -7
- package/plugins/copilot-pbr/skills/profile/SKILL.md +171 -0
- package/plugins/copilot-pbr/skills/review/SKILL.md +5 -3
- package/plugins/copilot-pbr/skills/scan/SKILL.md +2 -2
- package/plugins/copilot-pbr/skills/setup/SKILL.md +68 -2
- package/plugins/cursor-pbr/.cursor-plugin/plugin.json +1 -1
- package/plugins/cursor-pbr/commands/profile.md +7 -0
- package/plugins/cursor-pbr/hooks/hooks.json +33 -0
- package/plugins/cursor-pbr/references/model-profiles.md +47 -3
- package/plugins/cursor-pbr/references/model-selection.md +1 -1
- package/plugins/cursor-pbr/skills/begin/SKILL.md +10 -7
- package/plugins/cursor-pbr/skills/build/SKILL.md +9 -5
- package/plugins/cursor-pbr/skills/debug/SKILL.md +2 -2
- package/plugins/cursor-pbr/skills/discuss/SKILL.md +2 -2
- package/plugins/cursor-pbr/skills/health/SKILL.md +2 -2
- package/plugins/cursor-pbr/skills/milestone/SKILL.md +8 -8
- package/plugins/cursor-pbr/skills/pause/SKILL.md +1 -1
- package/plugins/cursor-pbr/skills/plan/SKILL.md +10 -8
- package/plugins/cursor-pbr/skills/profile/SKILL.md +172 -0
- package/plugins/cursor-pbr/skills/review/SKILL.md +6 -4
- package/plugins/cursor-pbr/skills/scan/SKILL.md +2 -2
- package/plugins/cursor-pbr/skills/setup/SKILL.md +68 -2
- package/plugins/pbr/.claude-plugin/plugin.json +1 -1
- package/plugins/pbr/agents/audit.md +0 -1
- package/plugins/pbr/agents/codebase-mapper.md +0 -1
- package/plugins/pbr/agents/debugger.md +0 -1
- package/plugins/pbr/agents/dev-sync.md +0 -1
- package/plugins/pbr/agents/executor.md +0 -1
- package/plugins/pbr/agents/general.md +0 -1
- package/plugins/pbr/agents/integration-checker.md +0 -1
- package/plugins/pbr/agents/plan-checker.md +0 -1
- package/plugins/pbr/agents/planner.md +0 -1
- package/plugins/pbr/agents/researcher.md +0 -1
- package/plugins/pbr/agents/synthesizer.md +0 -1
- package/plugins/pbr/agents/verifier.md +0 -1
- package/plugins/pbr/commands/profile.md +7 -0
- package/plugins/pbr/hooks/hooks.json +33 -0
- package/plugins/pbr/references/model-profiles.md +47 -3
- package/plugins/pbr/references/model-selection.md +1 -1
- package/plugins/pbr/scripts/check-subagent-output.js +1 -1
- package/plugins/pbr/scripts/config-schema.json +18 -0
- package/plugins/pbr/scripts/hooks-schema.json +4 -1
- package/plugins/pbr/scripts/instructions-loaded.js +69 -0
- package/plugins/pbr/scripts/lib/gates/helpers.js +7 -0
- package/plugins/pbr/scripts/lib/init.js +10 -7
- package/plugins/pbr/scripts/pbr-tools.js +6 -6
- package/plugins/pbr/scripts/task-completed.js +89 -1
- package/plugins/pbr/scripts/worktree-create.js +93 -0
- package/plugins/pbr/scripts/worktree-remove.js +83 -0
- package/plugins/pbr/skills/begin/SKILL.md +10 -7
- package/plugins/pbr/skills/build/SKILL.md +9 -5
- package/plugins/pbr/skills/debug/SKILL.md +2 -2
- package/plugins/pbr/skills/discuss/SKILL.md +2 -2
- package/plugins/pbr/skills/health/SKILL.md +2 -2
- package/plugins/pbr/skills/milestone/SKILL.md +8 -8
- package/plugins/pbr/skills/pause/SKILL.md +1 -1
- package/plugins/pbr/skills/plan/SKILL.md +10 -8
- package/plugins/pbr/skills/profile/SKILL.md +173 -0
- package/plugins/pbr/skills/review/SKILL.md +6 -4
- package/plugins/pbr/skills/scan/SKILL.md +2 -2
- 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:
|
|
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
|
|
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.
|
|
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",
|
|
@@ -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
|
|
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
|
|
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 —
|
|
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() {
|