@sienklogic/plan-build-run 2.56.0 → 2.56.2

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 (43) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +76 -28
  3. package/package.json +1 -1
  4. package/plugins/codex-pbr/references/pbr-tools-cli.md +2 -2
  5. package/plugins/codex-pbr/skills/audit/SKILL.md +2 -2
  6. package/plugins/codex-pbr/skills/explore/SKILL.md +28 -1
  7. package/plugins/codex-pbr/skills/health/SKILL.md +1 -1
  8. package/plugins/codex-pbr/skills/shared/context-loader-task.md +1 -0
  9. package/plugins/codex-pbr/skills/shared/state-update.md +1 -1
  10. package/plugins/codex-pbr/skills/shared/universal-anti-patterns.md +1 -0
  11. package/plugins/copilot-pbr/plugin.json +1 -1
  12. package/plugins/copilot-pbr/references/pbr-tools-cli.md +2 -2
  13. package/plugins/copilot-pbr/skills/audit/SKILL.md +2 -2
  14. package/plugins/copilot-pbr/skills/begin/templates/STATE.md.tmpl +0 -1
  15. package/plugins/copilot-pbr/skills/explore/SKILL.md +28 -1
  16. package/plugins/copilot-pbr/skills/health/SKILL.md +1 -1
  17. package/plugins/copilot-pbr/skills/shared/context-loader-task.md +1 -0
  18. package/plugins/copilot-pbr/skills/shared/state-update.md +1 -1
  19. package/plugins/copilot-pbr/skills/shared/universal-anti-patterns.md +1 -0
  20. package/plugins/cursor-pbr/.cursor-plugin/plugin.json +1 -1
  21. package/plugins/cursor-pbr/references/pbr-tools-cli.md +2 -2
  22. package/plugins/cursor-pbr/skills/audit/SKILL.md +2 -2
  23. package/plugins/cursor-pbr/skills/begin/templates/STATE.md.tmpl +0 -1
  24. package/plugins/cursor-pbr/skills/explore/SKILL.md +28 -1
  25. package/plugins/cursor-pbr/skills/health/SKILL.md +1 -1
  26. package/plugins/cursor-pbr/skills/shared/context-loader-task.md +1 -0
  27. package/plugins/cursor-pbr/skills/shared/state-update.md +1 -1
  28. package/plugins/cursor-pbr/skills/shared/universal-anti-patterns.md +1 -0
  29. package/plugins/pbr/.claude-plugin/plugin.json +1 -1
  30. package/plugins/pbr/references/pbr-tools-cli.md +2 -2
  31. package/plugins/pbr/scripts/check-plan-format.js +5 -6
  32. package/plugins/pbr/scripts/check-state-sync.js +0 -5
  33. package/plugins/pbr/scripts/enforce-pbr-workflow.js +31 -5
  34. package/plugins/pbr/scripts/lib/state.js +1 -3
  35. package/plugins/pbr/scripts/pbr-tools.js +1 -1
  36. package/plugins/pbr/scripts/status-line.js +3 -4
  37. package/plugins/pbr/skills/audit/SKILL.md +2 -2
  38. package/plugins/pbr/skills/begin/templates/STATE.md.tmpl +0 -1
  39. package/plugins/pbr/skills/explore/SKILL.md +28 -1
  40. package/plugins/pbr/skills/health/SKILL.md +1 -1
  41. package/plugins/pbr/skills/shared/context-loader-task.md +1 -0
  42. package/plugins/pbr/skills/shared/state-update.md +1 -1
  43. package/plugins/pbr/skills/shared/universal-anti-patterns.md +1 -0
package/CHANGELOG.md CHANGED
@@ -5,6 +5,20 @@ 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.56.2](https://github.com/SienkLogic/plan-build-run/compare/plan-build-run-v2.56.1...plan-build-run-v2.56.2) (2026-03-03)
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * **tools:** block non-PBR agent types by default with [native] bypass ([0256189](https://github.com/SienkLogic/plan-build-run/commit/02561890e49dba88df03d3977f9a8597740bd389))
14
+
15
+ ## [2.56.1](https://github.com/SienkLogic/plan-build-run/compare/plan-build-run-v2.56.0...plan-build-run-v2.56.1) (2026-03-03)
16
+
17
+
18
+ ### Documentation
19
+
20
+ * **quick-020:** comprehensively update documentation for v2.56.0 ([82f9177](https://github.com/SienkLogic/plan-build-run/commit/82f917792dd573192c4ce69df8633f13b1050b0a))
21
+
8
22
  ## [2.56.0](https://github.com/SienkLogic/plan-build-run/compare/plan-build-run-v2.55.0...plan-build-run-v2.56.0) (2026-03-02)
9
23
 
10
24
 
package/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  </p>
4
4
 
5
5
  <p align="center">
6
- <strong>Context-engineered development workflow for Claude Code, Cursor, and GitHub Copilot CLI.</strong>
6
+ <strong>Context-engineered development workflow for Claude Code, Cursor, GitHub Copilot CLI, OpenAI Codex, and Google Jules.</strong>
7
7
  <br />
8
8
  Build ambitious multi-phase software without quality degradation.
9
9
  <br />
@@ -27,7 +27,7 @@
27
27
  <img src="https://img.shields.io/badge/Node.js-18%2B-339933?style=for-the-badge&logo=node.js&logoColor=white" alt="Node.js 18+" />
28
28
  <a href="LICENSE"><img src="https://img.shields.io/github/license/SienkLogic/plan-build-run?style=for-the-badge" alt="License" /></a>
29
29
  <a href="https://www.npmjs.com/package/@sienklogic/plan-build-run"><img src="https://img.shields.io/npm/v/@sienklogic/plan-build-run?style=for-the-badge&logo=npm&logoColor=white" alt="npm" /></a>
30
- <img src="https://img.shields.io/badge/Tests-2153_passing-brightgreen?style=for-the-badge" alt="2153 Tests" />
30
+ <img src="https://img.shields.io/badge/Tests-9283_passing-brightgreen?style=for-the-badge" alt="9283 Tests" />
31
31
  </p>
32
32
 
33
33
  ---
@@ -132,6 +132,48 @@ All three plugins share the same `.planning/` directory — start in any tool, c
132
132
 
133
133
  </details>
134
134
 
135
+ <details>
136
+ <summary><strong>Install for OpenAI Codex CLI</strong></summary>
137
+
138
+ Plan-Build-Run also works in OpenAI Codex CLI. Skills and agents are available via the `plugins/codex-pbr/` plugin. No hooks (Codex CLI does not support lifecycle hooks).
139
+
140
+ **macOS / Linux:**
141
+ ```bash
142
+ cd /path/to/your/project
143
+ bash /path/to/plan-build-run/plugins/codex-pbr/setup.sh
144
+ ```
145
+
146
+ **Windows (PowerShell):**
147
+ ```powershell
148
+ cd C:\path\to\your\project
149
+ powershell -ExecutionPolicy Bypass -File C:\path\to\plan-build-run\plugins\codex-pbr\setup.ps1
150
+ ```
151
+
152
+ All five plugins share the same `.planning/` directory. See [`plugins/codex-pbr/README.md`](plugins/codex-pbr/README.md) for full details.
153
+
154
+ </details>
155
+
156
+ <details>
157
+ <summary><strong>Install for Google Jules</strong></summary>
158
+
159
+ Plan-Build-Run also works in Google Jules. Skills and agents are available via the `plugins/jules-pbr/` plugin. No hooks (Jules does not support lifecycle hooks).
160
+
161
+ **macOS / Linux:**
162
+ ```bash
163
+ cd /path/to/your/project
164
+ bash /path/to/plan-build-run/plugins/jules-pbr/setup.sh
165
+ ```
166
+
167
+ **Windows (PowerShell):**
168
+ ```powershell
169
+ cd C:\path\to\your\project
170
+ powershell -ExecutionPolicy Bypass -File C:\path\to\plan-build-run\plugins\jules-pbr\setup.ps1
171
+ ```
172
+
173
+ All five plugins share the same `.planning/` directory. See [`plugins/jules-pbr/README.md`](plugins/jules-pbr/README.md) for full details.
174
+
175
+ </details>
176
+
135
177
  <details>
136
178
  <summary><strong>Dashboard (Optional)</strong></summary>
137
179
 
@@ -219,7 +261,7 @@ Set `depth: quick` in `/pbr:config` to reduce agent spawns across all workflows.
219
261
  | `/pbr:build <N>` | Build a phase: parallel execution in waves, atomic commits | 2-4 (quick: 1-2) |
220
262
  | `/pbr:review <N>` | Verify a phase: automated 3-layer checks + conversational UAT | 1 |
221
263
 
222
- See **[Commands](https://github.com/SienkLogic/plan-build-run/wiki/Commands)** for all 26 commands with flags, cost-by-depth tables, and detailed descriptions.
264
+ See **[Commands](https://github.com/SienkLogic/plan-build-run/wiki/Commands)** for all 27 commands with flags, cost-by-depth tables, and detailed descriptions.
223
265
 
224
266
  ---
225
267
 
@@ -292,8 +334,8 @@ Requires a GPU with 6+ GB VRAM for best performance. CPU-only works but adds lat
292
334
  | Topic | Description |
293
335
  |-------|-------------|
294
336
  | **[Agents](https://github.com/SienkLogic/plan-build-run/wiki/Agents)** | 12 specialized agents with configurable model profiles and file-based communication |
295
- | **[Configuration](https://github.com/SienkLogic/plan-build-run/wiki/Configuration)** | 13 config keys, depth/model profiles, 16+ feature toggles, local LLM settings |
296
- | **[Hooks](https://github.com/SienkLogic/plan-build-run/wiki/Hooks)** | 38 hook scripts that enforce discipline at zero token cost |
337
+ | **[Configuration](https://github.com/SienkLogic/plan-build-run/wiki/Configuration)** | 12 config keys, depth/model profiles, 14+ feature toggles, local LLM settings |
338
+ | **[Hooks](https://github.com/SienkLogic/plan-build-run/wiki/Hooks)** | 41 hook scripts that enforce discipline at zero token cost |
297
339
  | **[Local LLM](https://github.com/SienkLogic/plan-build-run/wiki/Configuration#local_llm)** | Offload hook-level inference to Ollama for token savings with automatic fallback |
298
340
  | **[Project Structure](https://github.com/SienkLogic/plan-build-run/wiki/Project-Structure)** | The `.planning/` directory layout, key files, and file ownership |
299
341
  | **[Dashboard](https://github.com/SienkLogic/plan-build-run/wiki/Dashboard)** | Web UI with live updates for browsing project state |
@@ -306,28 +348,29 @@ Requires a GPU with 6+ GB VRAM for best performance. CPU-only works but adds lat
306
348
 
307
349
  ## Platform Compatibility
308
350
 
309
- Plan-Build-Run works across three platforms with varying levels of hook support. Hooks are the mechanism that fires validation scripts on every tool call — they power local LLM offloading, commit format enforcement, context budget tracking, and workflow gates.
310
-
311
- | Feature | Claude Code | Copilot CLI | Cursor IDE |
312
- |---------|:-----------:|:-----------:|:----------:|
313
- | Skills (slash commands) | All 26 | All 26 | All 26 |
314
- | Agents (subagent delegation) | All 12 | All 12 | All 12 |
315
- | `.planning/` state management | Full | Full | Full |
316
- | **Hook support** | **Full (14 events)** | **Partial (4 events)** | **Unverified** |
317
- | Commit format enforcement | Hook-enforced | Hook-enforced | Manual |
318
- | PLAN/SUMMARY quality classification | Hook + skill fallback | Hook + skill fallback | Skill fallback only |
319
- | Test failure triage | Automatic (hook) | Automatic (hook) | Not available |
320
- | Context budget tracking | Automatic (hook) | Not available | Not available |
321
- | Auto-continue between skills | Automatic (hook) | Not available | Not available |
322
- | Subagent lifecycle logging | Automatic (hook) | Not available | Not available |
323
- | **Local LLM offloading** | **Full (8 operations)** | **Mostly (6-7 operations)** | **CLI only** |
324
- | `pbr-tools.js llm` CLI commands | Full | Full | Full |
351
+ Plan-Build-Run works across five platforms with varying levels of hook support. Hooks are the mechanism that fires validation scripts on every tool call — they power local LLM offloading, commit format enforcement, context budget tracking, and workflow gates.
352
+
353
+ | Feature | Claude Code | Copilot CLI | Cursor IDE | Codex CLI | Jules |
354
+ |---------|:-----------:|:-----------:|:----------:|:---------:|:-----:|
355
+ | Skills (slash commands) | All 27 | All 27 | All 27 | All 27 | All 27 |
356
+ | Agents (subagent delegation) | All 12 | All 12 | All 12 | All 12 | All 12 |
357
+ | `.planning/` state management | Full | Full | Full | Full | Full |
358
+ | **Hook support** | **Full (14 events)** | **Partial (4 events)** | **Unverified** | **None** | **None** |
359
+ | Commit format enforcement | Hook-enforced | Hook-enforced | Manual | Manual | Manual |
360
+ | PLAN/SUMMARY quality classification | Hook + skill fallback | Hook + skill fallback | Skill fallback only | Skill fallback only | Skill fallback only |
361
+ | Test failure triage | Automatic (hook) | Automatic (hook) | Not available | Not available | Not available |
362
+ | Context budget tracking | Automatic (hook) | Not available | Not available | Not available | Not available |
363
+ | Auto-continue between skills | Automatic (hook) | Not available | Not available | Not available | Not available |
364
+ | Subagent lifecycle logging | Automatic (hook) | Not available | Not available | Not available | Not available |
365
+ | **Local LLM offloading** | **Full (8 operations)** | **Mostly (6-7 operations)** | **CLI only** | **CLI only** | **CLI only** |
366
+ | `pbr-tools.js llm` CLI commands | Full | Full | Full | Full | Full |
325
367
 
326
368
  **Key differences:**
327
369
 
328
370
  - **Claude Code** has full hook support — all local LLM operations fire automatically on every tool call
329
371
  - **Copilot CLI** supports `sessionStart`, `preToolUse`, `postToolUse`, and `sessionEnd` — covers most validation hooks but misses lifecycle events (`SubagentStop`, `PreCompact`, `Stop`)
330
372
  - **Cursor IDE** hook support is unverified — hooks.json is configured but whether Cursor actually fires them is unknown. Skills include `pbr-tools.js llm` fallback calls for key operations (plan quality, verification quality) so local LLM classification is available even without hooks
373
+ - **OpenAI Codex CLI** and **Google Jules** do not support hooks. All skills and agents are available via `plugins/codex-pbr/` and `plugins/jules-pbr/` respectively. Agent definitions use AGENTS.md format.
331
374
 
332
375
  All platforms share the same scripts via relative paths — no code duplication. See the [Copilot CLI](plugins/copilot-pbr/README.md) and [Cursor IDE](plugins/cursor-pbr/README.md) READMEs for platform-specific details.
333
376
 
@@ -341,7 +384,7 @@ git clone https://github.com/SienkLogic/plan-build-run.git
341
384
  cd plan-build-run
342
385
  npm install
343
386
 
344
- # Run tests (2153 tests, 73 suites)
387
+ # Run tests (9283 tests, 281 suites)
345
388
  npm test
346
389
 
347
390
  # Lint
@@ -362,14 +405,19 @@ CI runs on Node 18/20/22 across Windows, macOS, and Linux. See [CONTRIBUTING.md]
362
405
 
363
406
  | Metric | Count |
364
407
  |--------|-------|
365
- | Skills (slash commands) | 26 |
408
+ | Skills (slash commands) | 27 |
366
409
  | Specialized agents | 12 |
367
- | Hook scripts | 38 |
410
+ | Hook scripts | 41 |
368
411
  | Local LLM operations | 8 |
369
- | Supported platforms | 3 (Claude Code, Cursor, Copilot CLI) |
370
- | Tests | 2153 |
371
- | Test suites | 73 |
372
- | Config keys | 13 top-level (62+ properties) |
412
+ | Supported platforms | 5 (Claude Code, Cursor, Copilot CLI, Codex, Jules) |
413
+ | Tests | 9283 |
414
+ | Test suites | 281 |
415
+ | Config keys | 12 top-level (62+ properties) |
416
+ | Feature toggles | 14 |
417
+ | Lib modules | 19 |
418
+ | Shared fragments | 12 |
419
+ | Templates | 10 |
420
+ | References | 19 |
373
421
 
374
422
  ---
375
423
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sienklogic/plan-build-run",
3
- "version": "2.56.0",
3
+ "version": "2.56.2",
4
4
  "description": "Plan it, Build it, Run it — structured development workflow for Claude Code",
5
5
  "keywords": [
6
6
  "claude-code",
@@ -361,7 +361,7 @@ All phases with status and completion data.
361
361
  node ${CLAUDE_PLUGIN_ROOT}/scripts/pbr-tools.js init progress
362
362
  ```
363
363
 
364
- **Output:** `current_phase`, `total_phases`, `status`, `phases` array, `total_plans`, `completed_plans`, `percentage`.
364
+ **Output:** `current_phase`, `phase_count`, `status`, `phases` array, `total_plans`, `completed_plans`, `percentage`.
365
365
 
366
366
  ---
367
367
 
@@ -375,7 +375,7 @@ Multi-field atomic STATE.md update. Updates all fields in a single pass.
375
375
  node ${CLAUDE_PLUGIN_ROOT}/scripts/pbr-tools.js state patch '{"status":"executing","last_activity":"now"}'
376
376
  ```
377
377
 
378
- **Valid fields:** `current_phase`, `status`, `plans_complete`, `last_activity`, `progress_percent`, `phase_slug`, `total_phases`, `last_command`, `blockers`
378
+ **Valid fields:** `current_phase`, `status`, `plans_complete`, `last_activity`, `progress_percent`, `phase_slug`, `last_command`, `blockers`
379
379
 
380
380
  **Output:** `{ "success": true, "updated": ["status", "last_activity"] }`
381
381
 
@@ -153,11 +153,11 @@ Task({
153
153
  })
154
154
  ```
155
155
 
156
- Also spawn a git analysis agent (can use a Bash agent or general-purpose):
156
+ Also spawn a git analysis agent:
157
157
 
158
158
  ```
159
159
  Task({
160
- subagent_type: "Bash",
160
+ subagent_type: "pbr:general",
161
161
  model: "haiku",
162
162
  prompt: "Run these git commands in {project_dir}:
163
163
  1. git log --since='{from}' --until='{to}' --format='%h|%s|%an|%ai' --all
@@ -21,7 +21,9 @@ Then proceed to Step 1.
21
21
 
22
22
  You are running the **explore** skill. Your job is to help the user think through ideas that might become a todo, requirement, phase, decision, or nothing yet. This is Socratic conversation, not requirements gathering. No phase number is needed.
23
23
 
24
- This skill runs **inline** (no Task delegation), with optional Task() spawns for context loading and mid-conversation research.
24
+ This skill runs **inline** (no Task delegation), with optional Task() spawns for context loading, upfront research, and mid-conversation research.
25
+
26
+ **CRITICAL: Agent type rule** — When spawning ANY research or analysis Task(), ALWAYS use `subagent_type: "pbr:researcher"`. NEVER use `general-purpose`, `Explore`, or other non-PBR agent types. The PreToolUse hook will block non-PBR agents.
25
27
 
26
28
  ---
27
29
 
@@ -103,6 +105,31 @@ Reference `skills/shared/domain-probes.md` for technology-specific follow-up que
103
105
 
104
106
  ---
105
107
 
108
+ ## Upfront Research Delegation
109
+
110
+ When the user's initial request is research-heavy (e.g., "explore best practices for X", "research how other projects do Y", "compare approaches to Z"), delegate immediately to `pbr:researcher` agents rather than doing inline research.
111
+
112
+ **Detection**: If `$ARGUMENTS` contains words like "research", "compare", "explore examples", "best practices", "how do others", or describes gathering external information — this is an upfront research task.
113
+
114
+ **Pattern**: Spawn one or more `pbr:researcher` agents in parallel, then synthesize their findings inline:
115
+
116
+ ```
117
+ Task({
118
+ subagent_type: "pbr:researcher",
119
+ prompt: "<research_assignment>
120
+ Topic: {specific research question from user's request}
121
+ Output: Return findings as structured markdown in your response.
122
+ Mode: external-research
123
+
124
+ {detailed research instructions}
125
+ </research_assignment>"
126
+ })
127
+ ```
128
+
129
+ After researchers complete, synthesize findings inline and continue the Socratic conversation with the user about what was discovered.
130
+
131
+ ---
132
+
106
133
  ## Mid-Conversation Research
107
134
 
108
135
  When a knowledge gap emerges during the conversation — you're unsure about a library, pattern, or approach — surface it explicitly.
@@ -172,7 +172,7 @@ After running all 10 checks and collecting results, if any of the following auto
172
172
 
173
173
  | Pattern | Detection | Fix Action |
174
174
  |---------|-----------|------------|
175
- | Missing STATE.md frontmatter | Check 5 finds STATE.md without `---` block | Regenerate frontmatter from ROADMAP.md phase data (current_phase, total_phases, status) |
175
+ | Missing STATE.md frontmatter | Check 5 finds STATE.md without `---` block | Regenerate frontmatter from ROADMAP.md phase data (current_phase, phase_slug, status) |
176
176
  | STATE.md phase_slug mismatch | Check 5/7 finds phase_slug doesn't match current phase directory name | Correct phase_slug to match the actual directory name in `.planning/phases/` |
177
177
  | Missing config.json | Check 1/2 finds no `.planning/config.json` | Create with default config template (same as `$pbr-setup` defaults) |
178
178
  | Orphaned .active-skill file | Check 10 or general scan finds `.planning/.active-skill` older than 1 hour | Delete the stale `.active-skill` file |
@@ -44,6 +44,7 @@ Task({
44
44
  2. **Budget: ~500 tokens.** The briefing must be concise. The subagent reads full files in its own context; the orchestrator receives only the summary.
45
45
  3. **No suggestions.** The briefing reports state, it does not recommend actions. The skill logic decides what to do.
46
46
  4. **Read-only.** The briefing task must not write any files.
47
+ 5. **No subagent_type needed.** This is the ONE exception to the "always use `pbr:*` agents" rule. Bare `Task()` without `subagent_type` is intentional here — these are lightweight read-only briefings that don't need agent-specific prompts. Do NOT generalize this pattern to other Task() calls — all research, execution, and analysis Task() calls MUST use `subagent_type: "pbr:{agent}"`.
47
48
 
48
49
  ### Using the Briefing
49
50
 
@@ -53,7 +53,7 @@ Progress: [{progress_bar}] {percent}%
53
53
  Phase 3 of 10 = 20% → [████░░░░░░░░░░░░░░░░] 20%
54
54
  Phase 7 of 10 = 70% → [██████████████░░░░░░] 70%
55
55
  ```
56
- Calculation: `filled = Math.round((completed_phases / total_phases) * 20)`
56
+ Calculation: `filled = Math.round((completed_phases / phase_count) * 20)` where `phase_count` is derived from ROADMAP.md (available via `stateLoad` as `phase_count`)
57
57
 
58
58
  ### 3. Accumulated Context (lines 16-25)
59
59
  ```
@@ -26,6 +26,7 @@ These rules prevent context rot -- quality degradation as the context window fil
26
26
  ## Task/Subagent Rules (apply to every skill)
27
27
 
28
28
  10. **Never** invoke `Skill()` inside a `Task()` subagent -- the Skill tool is not available in subagent contexts. Agents spawned by `Task()` cannot resolve `$pbr-*` skill prefixes, so `Skill({ skill: "pbr:plan" })` will silently fail. Instead, chain skills at the orchestrator level (return control to the orchestrator, then call `Skill()` from there). For subagent work, use `subagent_type: "pbr:{agent}"` which auto-loads agent definitions.
29
+ 11. **NEVER** use non-PBR agent types (`general-purpose`, `Explore`, `Plan`, `Bash`, `feature-dev`, etc.) -- ALWAYS use `subagent_type: "pbr:{agent}"` (e.g., `pbr:researcher`, `pbr:executor`, `pbr:general`). PBR agents have project-aware prompts, audit logging, and workflow context. Generic agents bypass all of this. A PreToolUse hook **blocks** non-PBR agent spawns by default. Exceptions: (a) bare `Task()` with no `subagent_type` for lightweight read-only briefings (see context-loader-task pattern), (b) if the user says "use native agents", add `[native]` to the Task description to bypass the block for that call.
29
30
 
30
31
  ## Behavioral Rules (apply to every skill)
31
32
 
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pbr",
3
3
  "displayName": "Plan-Build-Run",
4
- "version": "2.56.0",
4
+ "version": "2.56.2",
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",
@@ -361,7 +361,7 @@ All phases with status and completion data.
361
361
  node ${CLAUDE_PLUGIN_ROOT}/scripts/pbr-tools.js init progress
362
362
  ```
363
363
 
364
- **Output:** `current_phase`, `total_phases`, `status`, `phases` array, `total_plans`, `completed_plans`, `percentage`.
364
+ **Output:** `current_phase`, `phase_count`, `status`, `phases` array, `total_plans`, `completed_plans`, `percentage`.
365
365
 
366
366
  ---
367
367
 
@@ -375,7 +375,7 @@ Multi-field atomic STATE.md update. Updates all fields in a single pass.
375
375
  node ${CLAUDE_PLUGIN_ROOT}/scripts/pbr-tools.js state patch '{"status":"executing","last_activity":"now"}'
376
376
  ```
377
377
 
378
- **Valid fields:** `current_phase`, `status`, `plans_complete`, `last_activity`, `progress_percent`, `phase_slug`, `total_phases`, `last_command`, `blockers`
378
+ **Valid fields:** `current_phase`, `status`, `plans_complete`, `last_activity`, `progress_percent`, `phase_slug`, `last_command`, `blockers`
379
379
 
380
380
  **Output:** `{ "success": true, "updated": ["status", "last_activity"] }`
381
381
 
@@ -153,11 +153,11 @@ Task({
153
153
  })
154
154
  ```
155
155
 
156
- Also spawn a git analysis agent (can use a Bash agent or general-purpose):
156
+ Also spawn a git analysis agent:
157
157
 
158
158
  ```
159
159
  Task({
160
- subagent_type: "Bash",
160
+ subagent_type: "pbr:general",
161
161
  model: "haiku",
162
162
  prompt: "Run these git commands in {project_dir}:
163
163
  1. git log --since='{from}' --until='{to}' --format='%h|%s|%an|%ai' --all
@@ -2,7 +2,6 @@
2
2
  ---
3
3
  version: 2
4
4
  current_phase: 1
5
- total_phases: {total}
6
5
  phase_slug: "{phase_1_slug}"
7
6
  phase_name: "{Phase 1 name}"
8
7
  status: "ready_to_plan"
@@ -21,7 +21,9 @@ Then proceed to Step 1.
21
21
 
22
22
  You are running the **explore** skill. Your job is to help the user think through ideas that might become a todo, requirement, phase, decision, or nothing yet. This is Socratic conversation, not requirements gathering. No phase number is needed.
23
23
 
24
- This skill runs **inline** (no Task delegation), with optional Task() spawns for context loading and mid-conversation research.
24
+ This skill runs **inline** (no Task delegation), with optional Task() spawns for context loading, upfront research, and mid-conversation research.
25
+
26
+ **CRITICAL: Agent type rule** — When spawning ANY research or analysis Task(), ALWAYS use `subagent_type: "pbr:researcher"`. NEVER use `general-purpose`, `Explore`, or other non-PBR agent types. The PreToolUse hook will block non-PBR agents.
25
27
 
26
28
  ---
27
29
 
@@ -103,6 +105,31 @@ Reference `skills/shared/domain-probes.md` for technology-specific follow-up que
103
105
 
104
106
  ---
105
107
 
108
+ ## Upfront Research Delegation
109
+
110
+ When the user's initial request is research-heavy (e.g., "explore best practices for X", "research how other projects do Y", "compare approaches to Z"), delegate immediately to `pbr:researcher` agents rather than doing inline research.
111
+
112
+ **Detection**: If `$ARGUMENTS` contains words like "research", "compare", "explore examples", "best practices", "how do others", or describes gathering external information — this is an upfront research task.
113
+
114
+ **Pattern**: Spawn one or more `pbr:researcher` agents in parallel, then synthesize their findings inline:
115
+
116
+ ```
117
+ Task({
118
+ subagent_type: "pbr:researcher",
119
+ prompt: "<research_assignment>
120
+ Topic: {specific research question from user's request}
121
+ Output: Return findings as structured markdown in your response.
122
+ Mode: external-research
123
+
124
+ {detailed research instructions}
125
+ </research_assignment>"
126
+ })
127
+ ```
128
+
129
+ After researchers complete, synthesize findings inline and continue the Socratic conversation with the user about what was discovered.
130
+
131
+ ---
132
+
106
133
  ## Mid-Conversation Research
107
134
 
108
135
  When a knowledge gap emerges during the conversation — you're unsure about a library, pattern, or approach — surface it explicitly.
@@ -172,7 +172,7 @@ After running all 10 checks and collecting results, if any of the following auto
172
172
 
173
173
  | Pattern | Detection | Fix Action |
174
174
  |---------|-----------|------------|
175
- | Missing STATE.md frontmatter | Check 5 finds STATE.md without `---` block | Regenerate frontmatter from ROADMAP.md phase data (current_phase, total_phases, status) |
175
+ | Missing STATE.md frontmatter | Check 5 finds STATE.md without `---` block | Regenerate frontmatter from ROADMAP.md phase data (current_phase, phase_slug, status) |
176
176
  | STATE.md phase_slug mismatch | Check 5/7 finds phase_slug doesn't match current phase directory name | Correct phase_slug to match the actual directory name in `.planning/phases/` |
177
177
  | Missing config.json | Check 1/2 finds no `.planning/config.json` | Create with default config template (same as `/pbr:setup` defaults) |
178
178
  | Orphaned .active-skill file | Check 10 or general scan finds `.planning/.active-skill` older than 1 hour | Delete the stale `.active-skill` file |
@@ -44,6 +44,7 @@ Task({
44
44
  2. **Budget: ~500 tokens.** The briefing must be concise. The subagent reads full files in its own context; the orchestrator receives only the summary.
45
45
  3. **No suggestions.** The briefing reports state, it does not recommend actions. The skill logic decides what to do.
46
46
  4. **Read-only.** The briefing task must not write any files.
47
+ 5. **No subagent_type needed.** This is the ONE exception to the "always use `pbr:*` agents" rule. Bare `Task()` without `subagent_type` is intentional here — these are lightweight read-only briefings that don't need agent-specific prompts. Do NOT generalize this pattern to other Task() calls — all research, execution, and analysis Task() calls MUST use `subagent_type: "pbr:{agent}"`.
47
48
 
48
49
  ### Using the Briefing
49
50
 
@@ -53,7 +53,7 @@ Progress: [{progress_bar}] {percent}%
53
53
  Phase 3 of 10 = 20% → [████░░░░░░░░░░░░░░░░] 20%
54
54
  Phase 7 of 10 = 70% → [██████████████░░░░░░] 70%
55
55
  ```
56
- Calculation: `filled = Math.round((completed_phases / total_phases) * 20)`
56
+ Calculation: `filled = Math.round((completed_phases / phase_count) * 20)` where `phase_count` is derived from ROADMAP.md (available via `stateLoad` as `phase_count`)
57
57
 
58
58
  ### 3. Accumulated Context (lines 16-25)
59
59
  ```
@@ -26,6 +26,7 @@ These rules prevent context rot -- quality degradation as the context window fil
26
26
  ## Task/Subagent Rules (apply to every skill)
27
27
 
28
28
  10. **Never** invoke `Skill()` inside a `Task()` subagent -- the Skill tool is not available in subagent contexts. Agents spawned by `Task()` cannot resolve `/pbr:*` skill prefixes, so `Skill({ skill: "pbr:plan" })` will silently fail. Instead, chain skills at the orchestrator level (return control to the orchestrator, then call `Skill()` from there). For subagent work, use `subagent_type: "pbr:{agent}"` which auto-loads agent definitions.
29
+ 11. **NEVER** use non-PBR agent types (`general-purpose`, `Explore`, `Plan`, `Bash`, `feature-dev`, etc.) -- ALWAYS use `subagent_type: "pbr:{agent}"` (e.g., `pbr:researcher`, `pbr:executor`, `pbr:general`). PBR agents have project-aware prompts, audit logging, and workflow context. Generic agents bypass all of this. A PreToolUse hook **blocks** non-PBR agent spawns by default. Exceptions: (a) bare `Task()` with no `subagent_type` for lightweight read-only briefings (see context-loader-task pattern), (b) if the user says "use native agents", add `[native]` to the Task description to bypass the block for that call.
29
30
 
30
31
  ## Behavioral Rules (apply to every skill)
31
32
 
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pbr",
3
3
  "displayName": "Plan-Build-Run",
4
- "version": "2.56.0",
4
+ "version": "2.56.2",
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",
@@ -361,7 +361,7 @@ All phases with status and completion data.
361
361
  node ${CLAUDE_PLUGIN_ROOT}/scripts/pbr-tools.js init progress
362
362
  ```
363
363
 
364
- **Output:** `current_phase`, `total_phases`, `status`, `phases` array, `total_plans`, `completed_plans`, `percentage`.
364
+ **Output:** `current_phase`, `phase_count`, `status`, `phases` array, `total_plans`, `completed_plans`, `percentage`.
365
365
 
366
366
  ---
367
367
 
@@ -375,7 +375,7 @@ Multi-field atomic STATE.md update. Updates all fields in a single pass.
375
375
  node ${CLAUDE_PLUGIN_ROOT}/scripts/pbr-tools.js state patch '{"status":"executing","last_activity":"now"}'
376
376
  ```
377
377
 
378
- **Valid fields:** `current_phase`, `status`, `plans_complete`, `last_activity`, `progress_percent`, `phase_slug`, `total_phases`, `last_command`, `blockers`
378
+ **Valid fields:** `current_phase`, `status`, `plans_complete`, `last_activity`, `progress_percent`, `phase_slug`, `last_command`, `blockers`
379
379
 
380
380
  **Output:** `{ "success": true, "updated": ["status", "last_activity"] }`
381
381
 
@@ -154,11 +154,11 @@ Task({
154
154
  })
155
155
  ```
156
156
 
157
- Also spawn a git analysis agent (can use a Bash agent or general-purpose):
157
+ Also spawn a git analysis agent:
158
158
 
159
159
  ```
160
160
  Task({
161
- subagent_type: "Bash",
161
+ subagent_type: "pbr:general",
162
162
  model: "haiku",
163
163
  prompt: "Run these git commands in {project_dir}:
164
164
  1. git log --since='{from}' --until='{to}' --format='%h|%s|%an|%ai' --all
@@ -2,7 +2,6 @@
2
2
  ---
3
3
  version: 2
4
4
  current_phase: 1
5
- total_phases: {total}
6
5
  phase_slug: "{phase_1_slug}"
7
6
  phase_name: "{Phase 1 name}"
8
7
  status: "ready_to_plan"
@@ -22,7 +22,9 @@ Then proceed to Step 1.
22
22
 
23
23
  You are running the **explore** skill. Your job is to help the user think through ideas that might become a todo, requirement, phase, decision, or nothing yet. This is Socratic conversation, not requirements gathering. No phase number is needed.
24
24
 
25
- This skill runs **inline** (no Task delegation), with optional Task() spawns for context loading and mid-conversation research.
25
+ This skill runs **inline** (no Task delegation), with optional Task() spawns for context loading, upfront research, and mid-conversation research.
26
+
27
+ **CRITICAL: Agent type rule** — When spawning ANY research or analysis Task(), ALWAYS use `subagent_type: "pbr:researcher"`. NEVER use `general-purpose`, `Explore`, or other non-PBR agent types. The PreToolUse hook will block non-PBR agents.
26
28
 
27
29
  ---
28
30
 
@@ -104,6 +106,31 @@ Reference `skills/shared/domain-probes.md` for technology-specific follow-up que
104
106
 
105
107
  ---
106
108
 
109
+ ## Upfront Research Delegation
110
+
111
+ When the user's initial request is research-heavy (e.g., "explore best practices for X", "research how other projects do Y", "compare approaches to Z"), delegate immediately to `pbr:researcher` agents rather than doing inline research.
112
+
113
+ **Detection**: If `$ARGUMENTS` contains words like "research", "compare", "explore examples", "best practices", "how do others", or describes gathering external information — this is an upfront research task.
114
+
115
+ **Pattern**: Spawn one or more `pbr:researcher` agents in parallel, then synthesize their findings inline:
116
+
117
+ ```
118
+ Task({
119
+ subagent_type: "pbr:researcher",
120
+ prompt: "<research_assignment>
121
+ Topic: {specific research question from user's request}
122
+ Output: Return findings as structured markdown in your response.
123
+ Mode: external-research
124
+
125
+ {detailed research instructions}
126
+ </research_assignment>"
127
+ })
128
+ ```
129
+
130
+ After researchers complete, synthesize findings inline and continue the Socratic conversation with the user about what was discovered.
131
+
132
+ ---
133
+
107
134
  ## Mid-Conversation Research
108
135
 
109
136
  When a knowledge gap emerges during the conversation — you're unsure about a library, pattern, or approach — surface it explicitly.
@@ -173,7 +173,7 @@ After running all 10 checks and collecting results, if any of the following auto
173
173
 
174
174
  | Pattern | Detection | Fix Action |
175
175
  |---------|-----------|------------|
176
- | Missing STATE.md frontmatter | Check 5 finds STATE.md without `---` block | Regenerate frontmatter from ROADMAP.md phase data (current_phase, total_phases, status) |
176
+ | Missing STATE.md frontmatter | Check 5 finds STATE.md without `---` block | Regenerate frontmatter from ROADMAP.md phase data (current_phase, phase_slug, status) |
177
177
  | STATE.md phase_slug mismatch | Check 5/7 finds phase_slug doesn't match current phase directory name | Correct phase_slug to match the actual directory name in `.planning/phases/` |
178
178
  | Missing config.json | Check 1/2 finds no `.planning/config.json` | Create with default config template (same as `/pbr:setup` defaults) |
179
179
  | Orphaned .active-skill file | Check 10 or general scan finds `.planning/.active-skill` older than 1 hour | Delete the stale `.active-skill` file |
@@ -44,6 +44,7 @@ Task({
44
44
  2. **Budget: ~500 tokens.** The briefing must be concise. The subagent reads full files in its own context; the orchestrator receives only the summary.
45
45
  3. **No suggestions.** The briefing reports state, it does not recommend actions. The skill logic decides what to do.
46
46
  4. **Read-only.** The briefing task must not write any files.
47
+ 5. **No subagent_type needed.** This is the ONE exception to the "always use `pbr:*` agents" rule. Bare `Task()` without `subagent_type` is intentional here — these are lightweight read-only briefings that don't need agent-specific prompts. Do NOT generalize this pattern to other Task() calls — all research, execution, and analysis Task() calls MUST use `subagent_type: "pbr:{agent}"`.
47
48
 
48
49
  ### Using the Briefing
49
50
 
@@ -53,7 +53,7 @@ Progress: [{progress_bar}] {percent}%
53
53
  Phase 3 of 10 = 20% → [████░░░░░░░░░░░░░░░░] 20%
54
54
  Phase 7 of 10 = 70% → [██████████████░░░░░░] 70%
55
55
  ```
56
- Calculation: `filled = Math.round((completed_phases / total_phases) * 20)`
56
+ Calculation: `filled = Math.round((completed_phases / phase_count) * 20)` where `phase_count` is derived from ROADMAP.md (available via `stateLoad` as `phase_count`)
57
57
 
58
58
  ### 3. Accumulated Context (lines 16-25)
59
59
  ```
@@ -26,6 +26,7 @@ These rules prevent context rot -- quality degradation as the context window fil
26
26
  ## Task/Subagent Rules (apply to every skill)
27
27
 
28
28
  10. **Never** invoke `Skill()` inside a `Task()` subagent -- the Skill tool is not available in subagent contexts. Agents spawned by `Task()` cannot resolve `/pbr:*` skill prefixes, so `Skill({ skill: "pbr:plan" })` will silently fail. Instead, chain skills at the orchestrator level (return control to the orchestrator, then call `Skill()` from there). For subagent work, use `subagent_type: "pbr:{agent}"` which auto-loads agent definitions.
29
+ 11. **NEVER** use non-PBR agent types (`general-purpose`, `Explore`, `Plan`, `Bash`, `feature-dev`, etc.) -- ALWAYS use `subagent_type: "pbr:{agent}"` (e.g., `pbr:researcher`, `pbr:executor`, `pbr:general`). PBR agents have project-aware prompts, audit logging, and workflow context. Generic agents bypass all of this. A PreToolUse hook **blocks** non-PBR agent spawns by default. Exceptions: (a) bare `Task()` with no `subagent_type` for lightweight read-only briefings (see context-loader-task pattern), (b) if the user says "use native agents", add `[native]` to the Task description to bypass the block for that call.
29
30
 
30
31
  ## Behavioral Rules (apply to every skill)
31
32
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pbr",
3
- "version": "2.56.0",
3
+ "version": "2.56.2",
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",
@@ -361,7 +361,7 @@ All phases with status and completion data.
361
361
  node ${CLAUDE_PLUGIN_ROOT}/scripts/pbr-tools.js init progress
362
362
  ```
363
363
 
364
- **Output:** `current_phase`, `total_phases`, `status`, `phases` array, `total_plans`, `completed_plans`, `percentage`.
364
+ **Output:** `current_phase`, `phase_count`, `status`, `phases` array, `total_plans`, `completed_plans`, `percentage`.
365
365
 
366
366
  ---
367
367
 
@@ -375,7 +375,7 @@ Multi-field atomic STATE.md update. Updates all fields in a single pass.
375
375
  node ${CLAUDE_PLUGIN_ROOT}/scripts/pbr-tools.js state patch '{"status":"executing","last_activity":"now"}'
376
376
  ```
377
377
 
378
- **Valid fields:** `current_phase`, `status`, `plans_complete`, `last_activity`, `progress_percent`, `phase_slug`, `total_phases`, `last_command`, `blockers`
378
+ **Valid fields:** `current_phase`, `status`, `plans_complete`, `last_activity`, `progress_percent`, `phase_slug`, `last_command`, `blockers`
379
379
 
380
380
  **Output:** `{ "success": true, "updated": ["status", "last_activity"] }`
381
381
 
@@ -347,7 +347,7 @@ function validateState(content, _filePath) {
347
347
  warnings.push('Unclosed YAML frontmatter');
348
348
  } else {
349
349
  const frontmatter = content.substring(3, frontmatterEnd);
350
- const requiredFields = ['version', 'current_phase', 'total_phases', 'phase_slug', 'status'];
350
+ const requiredFields = ['version', 'current_phase', 'phase_slug', 'status'];
351
351
  for (const field of requiredFields) {
352
352
  if (!frontmatter.includes(`${field}:`)) {
353
353
  warnings.push(`Frontmatter missing "${field}" field`);
@@ -440,14 +440,12 @@ function syncStateBody(content, filePath) {
440
440
 
441
441
  const fm = content.substring(3, fmEnd);
442
442
  const phaseMatch = fm.match(/^current_phase:\s*(\d+)/m);
443
- const totalMatch = fm.match(/^total_phases:\s*(\d+)/m);
444
443
  const slugMatch = fm.match(/^phase_name:\s*"?([^"\r\n]+)"?/m);
445
444
  const statusMatch = fm.match(/^status:\s*"?([^"\r\n]+)"?/m);
446
445
 
447
446
  if (!phaseMatch) return null;
448
447
 
449
448
  const fmPhase = phaseMatch[1];
450
- const fmTotal = totalMatch ? totalMatch[1] : null;
451
449
  const fmName = slugMatch ? slugMatch[1] : null;
452
450
  const fmStatus = statusMatch ? statusMatch[1] : null;
453
451
 
@@ -459,9 +457,10 @@ function syncStateBody(content, filePath) {
459
457
 
460
458
  // Fix phase line drift
461
459
  if (bodyPhaseMatch && bodyPhaseMatch[1] !== fmPhase) {
462
- const newPhaseLine = fmTotal
463
- ? (fmName ? `Phase: ${fmPhase} of ${fmTotal} (${fmName})` : `Phase: ${fmPhase} of ${fmTotal}`)
464
- : `Phase: ${fmPhase} of ${bodyPhaseMatch[2]}`;
460
+ const bodyTotal = bodyPhaseMatch ? bodyPhaseMatch[2] : null;
461
+ const newPhaseLine = bodyTotal
462
+ ? (fmName ? `Phase: ${fmPhase} of ${bodyTotal} (${fmName})` : `Phase: ${fmPhase} of ${bodyTotal}`)
463
+ : `Phase: ${fmPhase}`;
465
464
  updated = updated.replace(/^Phase:\s*\d+\s*of\s*\d+.*/m, newPhaseLine);
466
465
  needsFix = true;
467
466
  }
@@ -210,9 +210,6 @@ function updateStatePosition(content, updates) {
210
210
  if (updates.fmCurrentPhase !== undefined) {
211
211
  fm = fm.replace(/^(current_phase:\s*).*/m, (_, p) => `${p}${updates.fmCurrentPhase}`);
212
212
  }
213
- if (updates.fmTotalPhases !== undefined) {
214
- fm = fm.replace(/^(total_phases:\s*).*/m, (_, p) => `${p}${updates.fmTotalPhases}`);
215
- }
216
213
  if (updates.fmPhaseSlug !== undefined) {
217
214
  fm = fm.replace(/^(phase_slug:\s*).*/m, (_, p) => `${p}"${updates.fmPhaseSlug}"`);
218
215
  }
@@ -420,7 +417,6 @@ function checkStateSync(data) {
420
417
  if (currentPhase !== null && currentPhase !== phaseNumInt) {
421
418
  stateUpdates.phaseLine = `${phaseNumInt} of ${totalPhases} (${phaseName})`;
422
419
  stateUpdates.fmCurrentPhase = phaseNumInt;
423
- stateUpdates.fmTotalPhases = totalPhases;
424
420
  stateUpdates.fmPhaseSlug = phaseSlug;
425
421
  stateUpdates.fmPhaseName = phaseName;
426
422
  messages.push(`STATE.md: Phase ${currentPhase} → ${phaseNumInt}`);
@@ -503,7 +499,6 @@ function checkStateSync(data) {
503
499
  if (currentPhase !== null && currentPhase !== phaseNumInt) {
504
500
  stateUpdates.phaseLine = `${phaseNumInt} of ${totalPhases} (${phaseName})`;
505
501
  stateUpdates.fmCurrentPhase = phaseNumInt;
506
- stateUpdates.fmTotalPhases = totalPhases;
507
502
  stateUpdates.fmPhaseSlug = phaseSlug;
508
503
  stateUpdates.fmPhaseName = phaseName;
509
504
  messages.push(`STATE.md: Phase ${currentPhase} → ${phaseNumInt}`);
@@ -13,7 +13,7 @@
13
13
  * checkUnmanagedCommit(data) — PreToolUse Bash: advises git commits to use /pbr:quick
14
14
  *
15
15
  * All functions return null for pass, or { exitCode, output } for action.
16
- * checkNonPbrAgent always returns advisory (exitCode 0) — never blocks Task().
16
+ * checkNonPbrAgent defaults to blocking (exitCode 2) — blocks non-PBR agents.
17
17
  */
18
18
 
19
19
  'use strict';
@@ -136,8 +136,9 @@ const AGENT_MAPPING = {
136
136
  * - subagent_type is missing/empty (can't determine type)
137
137
  * - Enforcement level is "off"
138
138
  *
139
- * In "advisory" mode (default): returns exitCode 0 with additionalContext.
140
- * In "block" mode: returns exitCode 2 with decision/reason to hard-block the agent spawn.
139
+ * Defaults to **blocking** (exitCode 2) unless config overrides to "advisory".
140
+ * This is stricter than checkUnmanagedSourceWrite/checkUnmanagedCommit because
141
+ * non-PBR agents bypass audit logging, workflow context, and project-aware prompts.
141
142
  *
142
143
  * @param {Object} data - parsed hook input from Claude Code
143
144
  * @returns {null|{ exitCode: number, output: Object }}
@@ -149,6 +150,10 @@ function checkNonPbrAgent(data) {
149
150
  // Already using a PBR agent
150
151
  if (subagentType.startsWith('pbr:')) return null;
151
152
 
153
+ // Per-call bypass: user says "use native agents" and the LLM includes [native] in description
154
+ const description = (data.tool_input && data.tool_input.description) || '';
155
+ if (/\[native\]/i.test(description)) return null;
156
+
152
157
  const cwd = process.cwd();
153
158
  const planningDir = path.join(cwd, '.planning');
154
159
 
@@ -158,14 +163,35 @@ function checkNonPbrAgent(data) {
158
163
  const config = loadEnforcementConfig(planningDir);
159
164
  if (config.level === 'off') return null;
160
165
 
166
+ // For agent enforcement, determine the effective level.
167
+ // Priority: workflow.enforce_pbr_agents > workflow.enforce_pbr_skills > default "block"
168
+ // Agent enforcement is stricter than source write/commit checks — blocks by default.
169
+ let agentLevel = 'block'; // default: block non-PBR agents
170
+ try {
171
+ const configPath = path.join(planningDir, 'config.json');
172
+ const parsed = JSON.parse(fs.readFileSync(configPath, 'utf8'));
173
+ const agentExplicit = parsed.workflow && parsed.workflow.enforce_pbr_agents;
174
+ const skillExplicit = parsed.workflow && parsed.workflow.enforce_pbr_skills;
175
+ if (agentExplicit === 'advisory' || agentExplicit === 'block' || agentExplicit === 'off') {
176
+ agentLevel = agentExplicit;
177
+ } else if (skillExplicit === 'advisory' || skillExplicit === 'block' || skillExplicit === 'off') {
178
+ agentLevel = skillExplicit;
179
+ }
180
+ } catch (_e) {
181
+ // No config — keep default (block)
182
+ }
183
+
184
+ if (agentLevel === 'off') return null;
185
+
161
186
  const suggestion = AGENT_MAPPING[subagentType] || 'a pbr:* agent (e.g., pbr:researcher, pbr:general, pbr:executor)';
162
187
 
163
- if (config.level === 'block') {
188
+ // Block non-PBR agents unless explicitly set to advisory
189
+ if (agentLevel !== 'advisory') {
164
190
  const blockMessage =
165
191
  `PBR workflow violation: spawning generic agent "${subagentType}" is blocked. ` +
166
192
  `Use ${suggestion} instead. ` +
167
193
  'PBR agents are auto-loaded via subagent_type — just change the type, no extra setup needed. ' +
168
- 'Set workflow.enforce_pbr_skills: "advisory" in config to allow with warnings.';
194
+ 'If the user explicitly requested a native agent, add [native] to the Task description to bypass this check.';
169
195
 
170
196
  logHook('enforce-pbr-workflow', 'PreToolUse', 'block', { agentType: subagentType, suggestion });
171
197
  return {
@@ -31,7 +31,6 @@ function parseStateMd(content) {
31
31
  if (frontmatter.version === 2 || frontmatter.current_phase !== undefined) {
32
32
  result.format = 'frontmatter';
33
33
  result.current_phase = frontmatter.current_phase || null;
34
- result.total_phases = frontmatter.total_phases || null;
35
34
  result.phase_name = frontmatter.phase_slug || frontmatter.phase_name || null;
36
35
  result.status = frontmatter.status || null;
37
36
  result.progress = frontmatter.progress_percent !== undefined ? frontmatter.progress_percent : null;
@@ -301,7 +300,6 @@ function stateUpdate(field, value, planningDir) {
301
300
  'last_activity',
302
301
  'progress_percent',
303
302
  'phase_slug',
304
- 'total_phases',
305
303
  'last_command',
306
304
  'blockers'
307
305
  ];
@@ -340,7 +338,7 @@ function statePatch(jsonStr, planningDir) {
340
338
  if (!fs.existsSync(statePath)) return { success: false, error: "STATE.md not found" };
341
339
  let fields;
342
340
  try { fields = JSON.parse(jsonStr); } catch (_e) { return { success: false, error: "Invalid JSON" }; }
343
- const validFields = ["current_phase", "status", "plans_complete", "last_activity", "progress_percent", "phase_slug", "total_phases", "last_command", "blockers"];
341
+ const validFields = ["current_phase", "status", "plans_complete", "last_activity", "progress_percent", "phase_slug", "last_command", "blockers"];
344
342
  const updates = [], errors = [];
345
343
  for (const [field, value] of Object.entries(fields)) {
346
344
  if (!validFields.includes(field)) { errors.push("Unknown field: " + field); continue; }
@@ -539,7 +539,7 @@ async function main() {
539
539
  const field = args[2];
540
540
  const value = args[3];
541
541
  if (!field || value === undefined) {
542
- error('Usage: pbr-tools.js state update <field> <value>\nFields: current_phase, status, plans_complete, last_activity, progress_percent, phase_slug, total_phases, last_command, blockers');
542
+ error('Usage: pbr-tools.js state update <field> <value>\nFields: current_phase, status, plans_complete, last_activity, progress_percent, phase_slug, last_command, blockers');
543
543
  }
544
544
  output(stateUpdate(field, value));
545
545
  } else if (command === 'config' && subcommand === 'validate') {
@@ -242,12 +242,11 @@ function buildStatusLine(content, ctxPercent, cfg, stdinData, planningDir) {
242
242
  // Phase section (always includes brand text)
243
243
  if (sections.includes('phase')) {
244
244
  const fmPhase = fm && fm.current_phase;
245
- const fmTotal = fm && fm.total_phases;
246
245
  const fmName = fm && fm.phase_name;
247
246
  const phaseMatch = content.match(/Phase:\s*(\d+)\s*of\s*(\d+)\s*(?:\(([^)]+)\))?/);
248
247
 
249
248
  const phaseNum = fmPhase || (phaseMatch && phaseMatch[1]);
250
- const phaseTotal = fmTotal || (phaseMatch && phaseMatch[2]);
249
+ const phaseTotal = phaseMatch && phaseMatch[2];
251
250
  const phaseName = fmName || (phaseMatch && phaseMatch[3]);
252
251
 
253
252
  if (phaseNum && phaseTotal) {
@@ -263,11 +262,11 @@ function buildStatusLine(content, ctxPercent, cfg, stdinData, planningDir) {
263
262
  // Plan section
264
263
  if (sections.includes('plan')) {
265
264
  const fmComplete = fm && fm.plans_complete;
266
- const fmTotal = fm && fm.plans_total;
265
+ const fmPlansTotal = fm && fm.plans_total;
267
266
  const planMatch = content.match(/Plan:\s*(\d+)\s*of\s*(\d+)/);
268
267
 
269
268
  const done = fmComplete != null ? parseInt(fmComplete, 10) : (planMatch ? parseInt(planMatch[1], 10) : null);
270
- const total = fmTotal != null ? parseInt(fmTotal, 10) : (planMatch ? parseInt(planMatch[2], 10) : null);
269
+ const total = fmPlansTotal != null ? parseInt(fmPlansTotal, 10) : (planMatch ? parseInt(planMatch[2], 10) : null);
271
270
 
272
271
  if (done != null && total != null && total > 0) {
273
272
  const planColor = done === total ? c.green : c.white;
@@ -155,11 +155,11 @@ Task({
155
155
  })
156
156
  ```
157
157
 
158
- Also spawn a git analysis agent (can use a Bash agent or general-purpose):
158
+ Also spawn a git analysis agent:
159
159
 
160
160
  ```
161
161
  Task({
162
- subagent_type: "Bash",
162
+ subagent_type: "pbr:general",
163
163
  model: "haiku",
164
164
  prompt: "Run these git commands in {project_dir}:
165
165
  1. git log --since='{from}' --until='{to}' --format='%h|%s|%an|%ai' --all
@@ -1,7 +1,6 @@
1
1
  ---
2
2
  version: 2
3
3
  current_phase: 1
4
- total_phases: {total}
5
4
  phase_slug: "{phase_1_slug}"
6
5
  phase_name: "{Phase 1 name}"
7
6
  status: "ready_to_plan"
@@ -23,7 +23,9 @@ Then proceed to Step 1.
23
23
 
24
24
  You are running the **explore** skill. Your job is to help the user think through ideas that might become a todo, requirement, phase, decision, or nothing yet. This is Socratic conversation, not requirements gathering. No phase number is needed.
25
25
 
26
- This skill runs **inline** (no Task delegation), with optional Task() spawns for context loading and mid-conversation research.
26
+ This skill runs **inline** (no Task delegation), with optional Task() spawns for context loading, upfront research, and mid-conversation research.
27
+
28
+ **CRITICAL: Agent type rule** — When spawning ANY research or analysis Task(), ALWAYS use `subagent_type: "pbr:researcher"`. NEVER use `general-purpose`, `Explore`, or other non-PBR agent types. The PreToolUse hook will block non-PBR agents.
27
29
 
28
30
  ---
29
31
 
@@ -105,6 +107,31 @@ Reference `skills/shared/domain-probes.md` for technology-specific follow-up que
105
107
 
106
108
  ---
107
109
 
110
+ ## Upfront Research Delegation
111
+
112
+ When the user's initial request is research-heavy (e.g., "explore best practices for X", "research how other projects do Y", "compare approaches to Z"), delegate immediately to `pbr:researcher` agents rather than doing inline research.
113
+
114
+ **Detection**: If `$ARGUMENTS` contains words like "research", "compare", "explore examples", "best practices", "how do others", or describes gathering external information — this is an upfront research task.
115
+
116
+ **Pattern**: Spawn one or more `pbr:researcher` agents in parallel, then synthesize their findings inline:
117
+
118
+ ```
119
+ Task({
120
+ subagent_type: "pbr:researcher",
121
+ prompt: "<research_assignment>
122
+ Topic: {specific research question from user's request}
123
+ Output: Return findings as structured markdown in your response.
124
+ Mode: external-research
125
+
126
+ {detailed research instructions}
127
+ </research_assignment>"
128
+ })
129
+ ```
130
+
131
+ After researchers complete, synthesize findings inline and continue the Socratic conversation with the user about what was discovered.
132
+
133
+ ---
134
+
108
135
  ## Mid-Conversation Research
109
136
 
110
137
  When a knowledge gap emerges during the conversation — you're unsure about a library, pattern, or approach — surface it explicitly.
@@ -174,7 +174,7 @@ After running all 10 checks and collecting results, if any of the following auto
174
174
 
175
175
  | Pattern | Detection | Fix Action |
176
176
  |---------|-----------|------------|
177
- | Missing STATE.md frontmatter | Check 5 finds STATE.md without `---` block | Regenerate frontmatter from ROADMAP.md phase data (current_phase, total_phases, status) |
177
+ | Missing STATE.md frontmatter | Check 5 finds STATE.md without `---` block | Regenerate frontmatter from ROADMAP.md phase data (current_phase, phase_slug, status) |
178
178
  | STATE.md phase_slug mismatch | Check 5/7 finds phase_slug doesn't match current phase directory name | Correct phase_slug to match the actual directory name in `.planning/phases/` |
179
179
  | Missing config.json | Check 1/2 finds no `.planning/config.json` | Create with default config template (same as `/pbr:setup` defaults) |
180
180
  | Orphaned .active-skill file | Check 10 or general scan finds `.planning/.active-skill` older than 1 hour | Delete the stale `.active-skill` file |
@@ -44,6 +44,7 @@ Task({
44
44
  2. **Budget: ~500 tokens.** The briefing must be concise. The subagent reads full files in its own context; the orchestrator receives only the summary.
45
45
  3. **No suggestions.** The briefing reports state, it does not recommend actions. The skill logic decides what to do.
46
46
  4. **Read-only.** The briefing task must not write any files.
47
+ 5. **No subagent_type needed.** This is the ONE exception to the "always use `pbr:*` agents" rule. Bare `Task()` without `subagent_type` is intentional here — these are lightweight read-only briefings that don't need agent-specific prompts. Do NOT generalize this pattern to other Task() calls — all research, execution, and analysis Task() calls MUST use `subagent_type: "pbr:{agent}"`.
47
48
 
48
49
  ### Using the Briefing
49
50
 
@@ -53,7 +53,7 @@ Progress: [{progress_bar}] {percent}%
53
53
  Phase 3 of 10 = 20% → [████░░░░░░░░░░░░░░░░] 20%
54
54
  Phase 7 of 10 = 70% → [██████████████░░░░░░] 70%
55
55
  ```
56
- Calculation: `filled = Math.round((completed_phases / total_phases) * 20)`
56
+ Calculation: `filled = Math.round((completed_phases / phase_count) * 20)` where `phase_count` is derived from ROADMAP.md (available via `stateLoad` as `phase_count`)
57
57
 
58
58
  ### 3. Accumulated Context (lines 16-25)
59
59
  ```
@@ -26,6 +26,7 @@ These rules prevent context rot -- quality degradation as the context window fil
26
26
  ## Task/Subagent Rules (apply to every skill)
27
27
 
28
28
  10. **Never** invoke `Skill()` inside a `Task()` subagent -- the Skill tool is not available in subagent contexts. Subagents spawned by `Task()` cannot resolve `/pbr:*` skill prefixes, so `Skill({ skill: "pbr:plan" })` will silently fail. Instead, chain skills at the orchestrator level (return control to the orchestrator, then call `Skill()` from there). For subagent work, use `subagent_type: "pbr:{agent}"` which auto-loads agent definitions.
29
+ 11. **NEVER** use non-PBR agent types (`general-purpose`, `Explore`, `Plan`, `Bash`, `feature-dev`, etc.) -- ALWAYS use `subagent_type: "pbr:{agent}"` (e.g., `pbr:researcher`, `pbr:executor`, `pbr:general`). PBR agents have project-aware prompts, audit logging, and workflow context. Generic agents bypass all of this. A PreToolUse hook **blocks** non-PBR agent spawns by default. Exceptions: (a) bare `Task()` with no `subagent_type` for lightweight read-only briefings (see context-loader-task pattern), (b) if the user says "use native agents", add `[native]` to the Task description to bypass the block for that call.
29
30
 
30
31
  ## Behavioral Rules (apply to every skill)
31
32