pan-wizard 3.34.0 → 3.35.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/README.md +6 -6
- package/agents/pan-conductor.md +2 -2
- package/agents/pan-debugger.md +3 -1
- package/agents/pan-design-checker.md +1 -0
- package/agents/pan-executor.md +18 -1
- package/agents/pan-experiment-runner.md +1 -1
- package/agents/pan-hardener.md +1 -1
- package/agents/pan-meta-reviewer.md +1 -1
- package/agents/pan-phase-researcher.md +2 -2
- package/agents/pan-plan-checker.md +9 -8
- package/agents/pan-planner.md +3 -1
- package/agents/pan-project-researcher.md +1 -1
- package/agents/pan-research-synthesizer.md +1 -1
- package/agents/pan-reviewer.md +3 -2
- package/agents/pan-verifier.md +1 -1
- package/bin/install-lib.cjs +119 -16
- package/bin/install.js +34 -62
- package/commands/pan/cost.md +0 -4
- package/commands/pan/dashboard.md +0 -3
- package/commands/pan/design-phase.md +2 -2
- package/commands/pan/hud.md +1 -5
- package/commands/pan/knowledge.md +0 -1
- package/commands/pan/mcp-bridge.md +1 -3
- package/commands/pan/optimize.md +2 -0
- package/commands/pan/phase-tests.md +1 -1
- package/commands/pan/preview.md +1 -2
- package/commands/pan/report.md +1 -5
- package/commands/pan/research-phase.md +4 -4
- package/commands/pan/review-deep.md +0 -2
- package/commands/pan/what-if.md +1 -2
- package/hooks/dist/pan-context-monitor.js +12 -3
- package/hooks/dist/pan-guard.js +244 -0
- package/hooks/dist/pan-statusline.js +5 -2
- package/package.json +1 -1
- package/pan-wizard-core/bin/lib/commands.cjs +24 -2
- package/pan-wizard-core/bin/lib/config.cjs +5 -0
- package/pan-wizard-core/bin/lib/findings.cjs +126 -19
- package/pan-wizard-core/bin/lib/hygiene.cjs +11 -5
- package/pan-wizard-core/bin/lib/init.cjs +6 -0
- package/pan-wizard-core/bin/lib/optimize.cjs +13 -1
- package/pan-wizard-core/bin/lib/phase-gate.cjs +174 -0
- package/pan-wizard-core/bin/lib/phase.cjs +24 -0
- package/pan-wizard-core/bin/lib/preview.cjs +32 -31
- package/pan-wizard-core/bin/lib/verdict.cjs +140 -10
- package/pan-wizard-core/bin/lib/verify-integrity.cjs +181 -0
- package/pan-wizard-core/bin/lib/verify-plan-set.cjs +230 -0
- package/pan-wizard-core/bin/lib/verify-scope.cjs +6 -5
- package/pan-wizard-core/bin/lib/whatif.cjs +48 -6
- package/pan-wizard-core/bin/pan-tools.cjs +13 -4
- package/pan-wizard-core/mcp/merge-gate.cjs +14 -3
- package/pan-wizard-core/mcp/native-tools.cjs +3 -3
- package/pan-wizard-core/opencode/pan-wizard.js +36 -8
- package/pan-wizard-core/references/model-profiles.md +1 -1
- package/pan-wizard-core/references/planning-config.md +3 -3
- package/pan-wizard-core/references/verdict-contract.md +6 -1
- package/pan-wizard-core/templates/config.json +2 -6
- package/pan-wizard-core/templates/debug-subagent-prompt.md +2 -1
- package/pan-wizard-core/templates/phase-prompt.md +2 -0
- package/pan-wizard-core/templates/planner-subagent-prompt.md +2 -0
- package/pan-wizard-core/templates/preview-report.md +1 -1
- package/pan-wizard-core/templates/state.md +4 -4
- package/pan-wizard-core/workflows/diagnose-issues.md +4 -4
- package/pan-wizard-core/workflows/discuss-phase.md +2 -1
- package/pan-wizard-core/workflows/exec-phase.md +51 -40
- package/pan-wizard-core/workflows/milestone-gaps.md +2 -2
- package/pan-wizard-core/workflows/new-project.md +8 -16
- package/pan-wizard-core/workflows/plan-phase.md +22 -8
- package/pan-wizard-core/workflows/progress.md +1 -1
- package/pan-wizard-core/workflows/quick.md +2 -2
- package/pan-wizard-core/workflows/todo-check.md +3 -2
- package/pan-wizard-core/workflows/transition.md +4 -0
- package/pan-wizard-core/workflows/verify-phase.md +6 -6
- package/scripts/build-hooks.js +1 -0
- package/scripts/generate-skills-docs.py +2 -2
- package/scripts/prompt-lint.cjs +167 -0
- package/scripts/test-surface.cjs +1 -0
package/README.md
CHANGED
|
@@ -101,7 +101,7 @@ PAN is the context engineering layer that makes Claude Code reliable. It breaks
|
|
|
101
101
|
|
|
102
102
|
- **A human merges.** The Release squad prepares a squash-merge and surfaces an `always-ask` approval instead of merging; pair it with branch protection on your repo, which is what makes that unbypassable rather than merely instructed. Recovery is `git revert` or the previous tag, never a force-push.
|
|
103
103
|
- **Isolated builders.** Each Build agent forks its own `army/<task>` branch + git worktree, so parallel agents never share a file.
|
|
104
|
-
- **Caps.** Delegation-depth cap, per-cycle spawn + budget ceilings, and a `.planning/orchestration/abort` kill-switch, re-checked before every spawn by Mission Control's protocol (prompt-enforced; the MCP `pan_next_action` path
|
|
104
|
+
- **Caps.** Delegation-depth cap, per-cycle spawn + budget ceilings, and a `.planning/orchestration/abort` kill-switch, re-checked before every spawn by Mission Control's protocol (prompt-enforced; the MCP `pan_next_action` path applies the cycle cap, the budget cap (when `caps.enforceBudget` is set) and an `aborted` flag to the counts and caps the caller passes in — it tracks none of them and does not read the abort file; only the merge is gated independently of the caller, by `pan_confirm_merge`'s human approval token — delegation depth is bounded by which agents hold the `Task` tool) — the same harness as hierarchical exec, at campaign scale.
|
|
105
105
|
|
|
106
106
|
**Run it over days.** `--schedule` arms a resumable campaign with a per-day budget that burns the backlog down across sessions (the per-day budget is advisory unless the schedule is armed with `--enforce-budget`, which pauses the day's run once spent — `campaign status` shows the day's spend — and an external scheduler triggers each `--continue`) — and *still* waits for you at every merge. **Autonomy runs up to the irreversible step; a human is at the step.**
|
|
107
107
|
|
|
@@ -415,7 +415,7 @@ Automated verification checks that code exists and tests pass. `/pan:verify-phas
|
|
|
415
415
|
|
|
416
416
|
The system:
|
|
417
417
|
|
|
418
|
-
1. **Gates on the test suite** —
|
|
418
|
+
1. **Gates on the test suite** — the verifier's protocol sets the verdict to `gaps_found` when the suite fails or cannot run, and the must-have checks still run so the report is complete; the recorder flags a failed gate filed under `passed` (`test_gate_failed_under_pass`) but does not overwrite the status
|
|
419
419
|
2. **Checks every must-have** — each phase promise is verified against the code, not the task list
|
|
420
420
|
3. **Reports the gaps** — a verification report with a per-truth status and the recommended fix plans
|
|
421
421
|
4. **Hands the gaps to the planner** — `/pan:plan-phase N --gaps` writes the fix plans (`gap_closure: true`); `/pan:exec-phase N --gaps-only` runs just those
|
|
@@ -528,7 +528,7 @@ Every stage uses the same pattern: a thin orchestrator spawns specialized agents
|
|
|
528
528
|
|
|
529
529
|
The orchestrator never does heavy lifting. It spawns agents, waits, integrates results.
|
|
530
530
|
|
|
531
|
-
**The result:** You can run an entire phase — deep research, multiple plans created and verified, thousands of lines of code written across parallel executors, automated verification against goals — and your main context window stays
|
|
531
|
+
**The result:** You can run an entire phase — deep research, multiple plans created and verified, thousands of lines of code written across parallel executors, automated verification against goals — and your main context window stays small, because the work happens in fresh subagent contexts. Your session stays fast and responsive.
|
|
532
532
|
|
|
533
533
|
### Reasoning-Trace Handoff
|
|
534
534
|
|
|
@@ -582,7 +582,7 @@ You're never locked in. The system adapts.
|
|
|
582
582
|
| **Context rot prevention** | Phase-scoped fresh context windows (native sub-agents on Claude Code) | No — context degrades over time | No (Cline: condensing) | No |
|
|
583
583
|
| **Multi-agent** | Specialized agents, parallel waves | Parallel agents in worktrees (Cursor) | Single agent (Cline: subagents) | Custom agents as subagents |
|
|
584
584
|
| **Plan → Verify loop** | Research → plan → verify with iteration | Agent generates plan | Plan mode (Cline) | Plan step |
|
|
585
|
-
| **Post-execution verification** | Auto verifier
|
|
585
|
+
| **Post-execution verification** | Auto verifier with a test-suite gate (UAT is a template no command runs) | Iterative error-fix | Manual test runs | Auto-fix loop |
|
|
586
586
|
| **Session persistence** | state.md + pause/resume + handoff | Memory tool / transcripts | None / checkpoints (Cline) | Copilot Memory, CLI session recovery |
|
|
587
587
|
| **Runtime support** | Claude Code, OpenCode, Gemini CLI, Codex, Copilot CLI | IDE-locked | Terminal / VS Code | VS Code + CLI |
|
|
588
588
|
| **Zero dependencies** | Yes (Node builtins only) | No (Electron) | No (Python / Node) | No |
|
|
@@ -630,7 +630,7 @@ PAN is not a replacement for your IDE or AI agent — it's the orchestration lay
|
|
|
630
630
|
|
|
631
631
|
| Command | What it does |
|
|
632
632
|
|---------|--------------|
|
|
633
|
-
| `/pan:army "<goal>"` | Campaign-scale delivery: Mission Control delegates a whole-project goal to architecture/build/quality/release squads with branch-per-agent worktrees, behind CI + a human merge gate; `--schedule`/`--continue` run it over time |
|
|
633
|
+
| `/pan:army "<goal>"` | Campaign-scale delivery: Mission Control delegates a whole-project goal to architecture/build/quality/release squads with branch-per-agent worktrees, behind CI + a human merge gate (prompt-enforced on `/pan:army`; enforced in code on the MCP path); `--schedule`/`--continue` run it over time |
|
|
634
634
|
|
|
635
635
|
### Phase Management
|
|
636
636
|
|
|
@@ -662,7 +662,7 @@ PAN is not a replacement for your IDE or AI agent — it's the orchestration lay
|
|
|
662
662
|
| `/pan:health [--repair]` | Validate `.planning/` directory integrity; `--repair` fixes the issues it marks repairable |
|
|
663
663
|
| `/pan:hygiene [--apply] [--trace-age-days N] [--all-tracks]` | Scan for PAN version drift and stale project artifacts (legacy filenames, .tmp orphans, memory bloat and stale or expired memory entries, poisoned cost ledgers, trace and report debris, cached-context bloat, fragment planning dirs) and Claude Code's own memory index (reported, never edited); `--apply` executes the safe fixes — poisoned ledgers are quarantined by rename (only the newest quarantine copy is kept), and settled `state.md` history, shipped roadmap phases and stale memory entries are archived rather than dropped |
|
|
664
664
|
| `/pan:links [--strict] [--doc-root <dir>] [--source-root <dir>]` | Validate the doc-code link graph: inline `[[<id>]]` refs, `// @pan:` source anchors, `require-code-mention` contracts (ADR-0027). The default roots are PAN's own source layout, so pass your project's roots |
|
|
665
|
-
| `/pan:phase-tests <N> [instructions]` | Generate tests for a completed phase
|
|
665
|
+
| `/pan:phase-tests <N> [instructions]` | Generate tests for a completed phase from its summaries, context decisions and verification report |
|
|
666
666
|
| `/pan:milestone-cleanup` | Archive accumulated phase directories from completed milestones |
|
|
667
667
|
| `/pan:retro` | Milestone retrospective — estimation accuracy, verification patterns, gap analysis |
|
|
668
668
|
| `/pan:patches` | Restore local modifications after a PAN update |
|
package/agents/pan-conductor.md
CHANGED
|
@@ -51,7 +51,7 @@ Given a phase plan, decompose into **sub-tasks** that correspond to sub-agents:
|
|
|
51
51
|
- **Executor sub-agents (up to 6):** one per `-plan.md` file that's marked `autonomous: true` in frontmatter. Non-autonomous plans need user checkpoints, which you cannot pause for: do not run them, and list them under "Skipped" in your summary with the instruction to run them with `/pan:exec-phase <N>` (no `--hierarchical`) — nothing runs them after you return.
|
|
52
52
|
- **Reviewer (1):** spawn a `pan-reviewer` after all executors complete, unless `--skip-review` or `--fast` was passed.
|
|
53
53
|
- **Verifier (1):** spawn a `pan-verifier` after the reviewer, unless `workflow.verifier` is false.
|
|
54
|
-
- Optional hardener + meta-reviewer (2): only if `--deep-review` was also passed.
|
|
54
|
+
- Optional hardener + meta-reviewer (2): only if `--deep-review` was also passed and the reviewer ran — like exec-phase, skip the deep review under `--skip-review` or `--fast`, since its merge needs the reviewer's report.
|
|
55
55
|
|
|
56
56
|
3. **Wave grouping:** executors with no cross-plan dependencies can be grouped (within the 12-spawn cap). Sequential executors when `depends_on:` frontmatter indicates.
|
|
57
57
|
|
|
@@ -108,7 +108,7 @@ For each phase execution:
|
|
|
108
108
|
a. Spawn pan-reviewer (always, unless --skip-review or --fast)
|
|
109
109
|
b. Save its returned report verbatim to {phase_dir}/{phase_number}-review.md (the reviewer is read-only; exec-phase's code_review step saves it the same way), then: pan-tools findings record --phase <N> --agent pan-reviewer --file {phase_dir}/{phase_number}-review.md
|
|
110
110
|
5. After reviewer:
|
|
111
|
-
a. If --deep-review: spawn pan-hardener (<output_path> .planning/reviews/<N>/hardener.md), then pan-meta-reviewer (<output_path> .planning/reviews/<N>/meta.md)
|
|
111
|
+
a. If --deep-review and the reviewer ran (not --skip-review/--fast): spawn pan-hardener (<output_path> .planning/reviews/<N>/hardener.md), then pan-meta-reviewer (<output_path> .planning/reviews/<N>/meta.md)
|
|
112
112
|
b. Merge: pan-tools review-deep merge <N> --reviewer-file {phase_dir}/{phase_number}-review.md --hardener-file .planning/reviews/<N>/hardener.md --meta-file .planning/reviews/<N>/meta.md
|
|
113
113
|
6. Spawn pan-verifier — unless `workflow.verifier` is false: then report "verification skipped (workflow.verifier is off)", as exec-phase does
|
|
114
114
|
7. Emit final orchestration summary
|
package/agents/pan-debugger.md
CHANGED
|
@@ -830,6 +830,8 @@ gathering -> investigating -> fixing -> verifying -> awaiting_human_verify -> re
|
|
|
830
830
|
^ | | |
|
|
831
831
|
|____________|___________|_________________|
|
|
832
832
|
(if verification fails or user reports issue)
|
|
833
|
+
|
|
834
|
+
goal: find_root_cause_only → gathering -> investigating -> diagnosed (stops there)
|
|
833
835
|
```
|
|
834
836
|
|
|
835
837
|
## Resume Behavior
|
|
@@ -1060,7 +1062,7 @@ mv .planning/debug/{slug}.md .planning/debug/resolved/
|
|
|
1060
1062
|
|
|
1061
1063
|
```bash
|
|
1062
1064
|
INIT=$(node ~/.claude/pan-wizard-core/bin/pan-tools.cjs state load)
|
|
1063
|
-
# commit_docs is in the JSON output
|
|
1065
|
+
# commit_docs is at .config.commit_docs in the JSON output
|
|
1064
1066
|
```
|
|
1065
1067
|
|
|
1066
1068
|
**Commit the fix:**
|
|
@@ -90,6 +90,7 @@ contract is in `~/.claude/pan-wizard-core/references/verdict-contract.md`. How t
|
|
|
90
90
|
`unrequested`, every other dimension → `partial`.
|
|
91
91
|
- Each gap is `high`. A gap you report as a caveat on the final iteration is `medium`.
|
|
92
92
|
- `where` is the design section.
|
|
93
|
+
- **On a re-check you are given `<open_findings>`:** your earlier open findings on this phase, each with its `id`. Answer every one in a `resolutions` list in the block: `{"id":"f_…","status":"resolved","evidence":"what you checked and saw"}` when it is fixed, `still_present` when it is not, `cannot_tell` when you could not check it. A finding you leave out stays open: silence never closes a finding, and `resolved` without evidence closes nothing.
|
|
93
94
|
|
|
94
95
|
The orchestrator records the block, so it must say what your verdict says.
|
|
95
96
|
|
package/agents/pan-executor.md
CHANGED
|
@@ -183,6 +183,23 @@ Only auto-fix issues DIRECTLY caused by the current task's changes. Pre-existing
|
|
|
183
183
|
- Do NOT fix them
|
|
184
184
|
- Do NOT re-run builds hoping they resolve themselves
|
|
185
185
|
|
|
186
|
+
**TESTS ARE THE SPEC, NOT AN OBSTACLE:**
|
|
187
|
+
Test files your plan does not list in `files_modified` are read-only. In any test file, never delete or rename a test, add a skip or only marker (`.skip`, `.only`, `xit`, `{ skip: true }`, `@pytest.mark.skip`, `t.Skip`, `#[ignore]`, `@Disabled`), remove an assertion, rewrite an expectation or a snapshot to match what the code now does, or add an assertion that cannot fail. `pan-tools verify test-integrity <phase>` checks the phase's commits for these, and a violation fails the phase gate: exec-phase will not auto-advance, and under `workflow.phase_gate: enforce` the phase cannot be completed without an override.
|
|
188
|
+
|
|
189
|
+
When a test and your task cannot both be satisfied (the test asserts behaviour the plan changes, or the task is impossible as specified), stop. Do not edit the test. Return:
|
|
190
|
+
|
|
191
|
+
```markdown
|
|
192
|
+
## BLOCKED_SPEC_CONFLICT
|
|
193
|
+
|
|
194
|
+
**Plan:** {phase}-{plan}
|
|
195
|
+
**Test:** {file}:{line} — what it asserts
|
|
196
|
+
**Requirement:** the task or requirement it conflicts with
|
|
197
|
+
**Why both cannot hold:** one or two sentences
|
|
198
|
+
**Options:** change the test (needs a plan that declares it) · change the requirement · something else
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
A person rules on it. Commit the work you finished before stopping.
|
|
202
|
+
|
|
186
203
|
**FIX ATTEMPT LIMIT:**
|
|
187
204
|
Track auto-fix attempts per task. After 3 auto-fix attempts on a single task:
|
|
188
205
|
- STOP fixing — document remaining issues in summary.md under "Deferred Issues"
|
|
@@ -453,7 +470,7 @@ node ~/.claude/pan-wizard-core/bin/pan-tools.cjs requirements mark-complete ${RE
|
|
|
453
470
|
**State command behaviors:**
|
|
454
471
|
- `state advance-plan`: Increments Current Plan, detects last-plan edge case, sets status
|
|
455
472
|
- `state update-progress`: Recalculates progress bar from summary.md counts on disk
|
|
456
|
-
- `state record-metric`: Appends to Performance Metrics table
|
|
473
|
+
- `state record-metric`: Appends a per-plan row to the Performance Metrics table
|
|
457
474
|
- `state add-decision`: Adds to Decisions section, removes placeholders
|
|
458
475
|
- `state record-session`: Updates Last session timestamp and Stopped At fields
|
|
459
476
|
- `roadmap update-plan-progress`: Updates roadmap.md progress table row with PLAN vs SUMMARY counts
|
|
@@ -105,7 +105,7 @@ After the run completes, produce a concise structured summary:
|
|
|
105
105
|
- <e.g., "External session ran a tight 14-cycle focus-auto loop">
|
|
106
106
|
|
|
107
107
|
### Recommendation for /pan:learn
|
|
108
|
-
- Run `/pan:learn --experiment <slug>` to extract patterns from the trace.
|
|
108
|
+
- Run `/pan:experiment harvest <slug>`, then `/pan:learn --experiment <slug>` to extract patterns from the trace (learn reads the harvest folder, not the experiment folder).
|
|
109
109
|
- Especially check: <areas of high event density / repeated failures>
|
|
110
110
|
```
|
|
111
111
|
|
package/agents/pan-hardener.md
CHANGED
|
@@ -12,7 +12,7 @@ You are the PAN hardener. You perform focused security review on files changed d
|
|
|
12
12
|
|
|
13
13
|
This is **authorized, defensive** secure-coding review of the user's own codebase — the goal is to find and fix weaknesses before shipping. You report findings for the user to remediate; you never write exploit code, attack tooling, or step-by-step intrusion instructions.
|
|
14
14
|
|
|
15
|
-
You are spawned by `/pan:review-deep <phase
|
|
15
|
+
You are spawned by `/pan:review-deep <phase>`, `/pan:exec-phase --deep-review`, `/pan:focus-exec --deep-review` (per item, output under `.planning/reviews/<item-id>/`) or `/pan:focus-auto` Pass 3. Your output is read by `pan-meta-reviewer` (cross-checks you) and merged by `review-deep.cjs` into `.planning/reviews/<phase>/deep-review.md`.
|
|
16
16
|
|
|
17
17
|
**You NEVER modify source files.** Your one write is the report at the output path your prompt gives. You report findings; the user fixes them.
|
|
18
18
|
|
|
@@ -15,7 +15,7 @@ You are the PAN meta-reviewer. Your job is to check the first-pass reviewers (`p
|
|
|
15
15
|
3. **Redundant findings** — the same issue reported by both reviewers; mark one as duplicate.
|
|
16
16
|
4. **Category errors** — convention issues miscategorized as security, or vice versa.
|
|
17
17
|
|
|
18
|
-
You are spawned by `/pan:review-deep <phase>` after both the reviewer and hardener have written their reports. Your output is merged with theirs by `review-deep.cjs`.
|
|
18
|
+
You are spawned by `/pan:review-deep <phase>` after both the reviewer and hardener have written their reports, or by `/pan:focus-exec --deep-review` with the hardener's report alone (an item has no first-pass review: check the hardener against the diff, and skip the reviewer-side coverage and duplicate checks). Your output is merged with theirs by `review-deep.cjs`.
|
|
19
19
|
|
|
20
20
|
**You NEVER modify source code.** You produce one findings file. This is authorized, defensive review of the user's own codebase — you adjudicate security findings for remediation; never produce exploit code.
|
|
21
21
|
|
|
@@ -142,7 +142,7 @@ node ~/.claude/pan-wizard-core/bin/pan-tools.cjs websearch "your query" --limit
|
|
|
142
142
|
|
|
143
143
|
**Options:**
|
|
144
144
|
- `--limit N` — Number of results (default: 10)
|
|
145
|
-
- `--freshness day|week|month` — Restrict to recent content
|
|
145
|
+
- `--freshness day|week|month|year` — Restrict to recent content
|
|
146
146
|
|
|
147
147
|
If `brave_search: false` (or not set), use built-in WebSearch tool instead.
|
|
148
148
|
|
|
@@ -411,7 +411,7 @@ Also read `.planning/config.json` — if `workflow.nyquist_validation` is `true`
|
|
|
411
411
|
|
|
412
412
|
Then read context.md if exists:
|
|
413
413
|
```bash
|
|
414
|
-
cat "$
|
|
414
|
+
cat "$PHASE_DIR"/*-context.md 2>/dev/null
|
|
415
415
|
```
|
|
416
416
|
|
|
417
417
|
**If context.md exists**, it constrains research:
|
|
@@ -246,8 +246,8 @@ issue:
|
|
|
246
246
|
```yaml
|
|
247
247
|
issue:
|
|
248
248
|
dimension: scope_sanity
|
|
249
|
-
severity:
|
|
250
|
-
description: "Plan 01 has 5 tasks - split
|
|
249
|
+
severity: blocker
|
|
250
|
+
description: "Plan 01 has 5 tasks - split required"
|
|
251
251
|
plan: "01"
|
|
252
252
|
metrics:
|
|
253
253
|
tasks: 5
|
|
@@ -541,11 +541,11 @@ Extract from init JSON: `phase_dir`, `phase_number`, `has_plans`, `plan_count`.
|
|
|
541
541
|
Orchestrator provides context.md content in the verification prompt. If provided, parse for locked decisions, discretion areas, deferred ideas.
|
|
542
542
|
|
|
543
543
|
```bash
|
|
544
|
-
ls "$
|
|
544
|
+
ls "$PHASE_DIR"/*-plan.md 2>/dev/null
|
|
545
545
|
# Read research for Nyquist validation data
|
|
546
|
-
cat "$
|
|
547
|
-
node ~/.claude/pan-wizard-core/bin/pan-tools.cjs roadmap get-phase "$
|
|
548
|
-
cat "$
|
|
546
|
+
cat "$PHASE_DIR"/*-research.md 2>/dev/null
|
|
547
|
+
node ~/.claude/pan-wizard-core/bin/pan-tools.cjs roadmap get-phase "$PHASE"
|
|
548
|
+
cat "$PHASE_DIR"/*-design.md 2>/dev/null # approved design from /pan:design-phase (Design Conformance)
|
|
549
549
|
```
|
|
550
550
|
|
|
551
551
|
**Extract:** Phase goal, requirements (decompose goal), locked decisions, deferred ideas.
|
|
@@ -660,7 +660,7 @@ Missing: No mention of fetch/API call → Issue: Key link not planned
|
|
|
660
660
|
## Step 8: Assess Scope
|
|
661
661
|
|
|
662
662
|
```bash
|
|
663
|
-
for plan in "$
|
|
663
|
+
for plan in "$PHASE_DIR"/*-plan.md; do
|
|
664
664
|
grep -cE "<task[ >]" "$plan" # "<task" alone also counts the <tasks> wrapper; Step 2's task_count is the same number
|
|
665
665
|
grep "files_modified:" "$plan"
|
|
666
666
|
done
|
|
@@ -766,6 +766,7 @@ Return all issues as a structured `issues:` YAML list (see dimension examples fo
|
|
|
766
766
|
- `verdict` is `passed` or `issues_found` (the status from Step 10), and `outcome` is `pass` or `fail`.
|
|
767
767
|
- Add one finding per issue you list. Blocker → `high`, warning → `medium`, info → `info`. The class follows the dimension, as mapped in the reference.
|
|
768
768
|
- `where` is the plan file, plus `#task-N` when the issue is about one task.
|
|
769
|
+
- **On a re-check you are given `<open_findings>`:** your earlier open findings on this phase, each with its `id`. Answer every one in a `resolutions` list in the block: `{"id":"f_…","status":"resolved","evidence":"what you checked and saw"}` when it is fixed, `still_present` when it is not, `cannot_tell` when you could not check it. A finding you leave out stays open: silence never closes a finding, and `resolved` without evidence closes nothing.
|
|
769
770
|
|
|
770
771
|
The orchestrator records the block, and the record is what it branches on. The block must say the same thing as your heading.
|
|
771
772
|
|
|
@@ -838,7 +839,7 @@ End it with one finding per listed issue:
|
|
|
838
839
|
|
|
839
840
|
````markdown
|
|
840
841
|
```pan-verdict
|
|
841
|
-
{"contract":"1.0","agent":"pan-plan-checker","phase":"{phase}","verdict":"issues_found","outcome":"fail","findings":[{"class":"missing","severity":"high","where":"{phase}-01-plan.md","summary":"requirement AUTH-02 (password reset) has no covering task"},{"class":"risk","severity":"
|
|
842
|
+
{"contract":"1.0","agent":"pan-plan-checker","phase":"{phase}","verdict":"issues_found","outcome":"fail","findings":[{"class":"missing","severity":"high","where":"{phase}-01-plan.md","summary":"requirement AUTH-02 (password reset) has no covering task"},{"class":"risk","severity":"high","where":"{phase}-02-plan.md","summary":"plan 02 modifies 12 files across 5 tasks; split it"}]}
|
|
842
843
|
```
|
|
843
844
|
````
|
|
844
845
|
|
package/agents/pan-planner.md
CHANGED
|
@@ -422,6 +422,7 @@ files_modified: []
|
|
|
422
422
|
autonomous: true
|
|
423
423
|
requirements: []
|
|
424
424
|
user_setup: []
|
|
425
|
+
planned_commit: <sha>
|
|
425
426
|
|
|
426
427
|
# Goal-backward verification; the Frontmatter Fields table below explains each field
|
|
427
428
|
must_haves:
|
|
@@ -505,10 +506,11 @@ After completion, create `.planning/phases/XX-name/{phase}-{plan}-summary.md`
|
|
|
505
506
|
| `files_modified` | Yes | Files this plan touches |
|
|
506
507
|
| `autonomous` | Yes | `true` if no checkpoints |
|
|
507
508
|
| `requirements` | Yes | **MUST** list requirement IDs from ROADMAP. Every roadmap requirement ID MUST appear in at least one plan. |
|
|
509
|
+
| `planned_commit` | Yes | The output of `git rev-parse HEAD` when you write the plan (omit it outside a git repository) |
|
|
508
510
|
| `user_setup` | No | Human-required setup items |
|
|
509
511
|
| `must_haves` | Yes | Goal-backward verification criteria |
|
|
510
512
|
|
|
511
|
-
Wave numbers are pre-computed during planning. Execute-phase reads `wave` directly from frontmatter.
|
|
513
|
+
Wave numbers are pre-computed during planning. Execute-phase reads `wave` directly from frontmatter. `pan-tools verify plan-set <phase>` checks the arithmetic before the plan checker runs: every roadmap requirement covered, no two plans in one wave modifying the same file, every `depends_on` naming a plan of this phase in an earlier wave, and no cycles.
|
|
512
514
|
|
|
513
515
|
## Interface Context for Executors
|
|
514
516
|
|
|
@@ -111,7 +111,7 @@ node ~/.claude/pan-wizard-core/bin/pan-tools.cjs websearch "your query" --limit
|
|
|
111
111
|
|
|
112
112
|
**Options:**
|
|
113
113
|
- `--limit N` — Number of results (default: 10)
|
|
114
|
-
- `--freshness day|week|month` — Restrict to recent content
|
|
114
|
+
- `--freshness day|week|month|year` — Restrict to recent content
|
|
115
115
|
|
|
116
116
|
If `brave_search: false` (or not set), use built-in WebSearch tool instead.
|
|
117
117
|
|
|
@@ -59,7 +59,7 @@ cat .planning/research/features.md
|
|
|
59
59
|
cat .planning/research/architecture.md
|
|
60
60
|
cat .planning/research/pitfalls.md
|
|
61
61
|
|
|
62
|
-
#
|
|
62
|
+
# The commit step loads the planning config through pan-tools
|
|
63
63
|
```
|
|
64
64
|
|
|
65
65
|
Parse each file to extract:
|
package/agents/pan-reviewer.md
CHANGED
|
@@ -72,7 +72,7 @@ If a changed file path starts with any of these prefixes, **skip it silently**.
|
|
|
72
72
|
## Design smells (baseline)
|
|
73
73
|
|
|
74
74
|
A fixed baseline that applies even when the project documents no standards (Fowler, *Refactoring*, ch. 3). Rules:
|
|
75
|
-
- Each is a judgement call: always **INFO**, written as "possible <smell>", never a reason for a non-PASS verdict on its own
|
|
75
|
+
- Each is a judgement call: always **INFO**, written as "possible <smell>", never a reason for a non-PASS verdict on its own; its Category is `Design smell`.
|
|
76
76
|
- **The project wins.** Where a documented project standard, or an established pattern in the code, endorses what a smell would flag, do not report it. Skip anything a linter or formatter already enforces.
|
|
77
77
|
- Only for code this phase changed, and only with a file:line and the hunk that shows it.
|
|
78
78
|
|
|
@@ -136,9 +136,10 @@ Return a structured review report:
|
|
|
136
136
|
**Machine verdict (required).** End the report with a `pan-verdict` block. The contract is in `~/.claude/pan-wizard-core/references/verdict-contract.md`. How to fill it:
|
|
137
137
|
- `verdict` is the word under `### Verdict`; `outcome` is `fail` for NEEDS_FIXES and `pass` otherwise.
|
|
138
138
|
- Add one finding per table row. ERROR → `high`, WARNING → `medium`, INFO → `info`.
|
|
139
|
-
- Category maps to class: Convention → `quality`, Security → `risk`, Code Quality → `defect`, Unrequested → `unrequested`.
|
|
139
|
+
- Category maps to class: Convention → `quality`, Security → `risk`, Code Quality → `defect`, Design smell → `quality`, Unrequested → `unrequested`.
|
|
140
140
|
- `where` is `file:line`.
|
|
141
141
|
- A clean review has `"findings": []`.
|
|
142
|
+
- **On a re-check you are given `<open_findings>`:** your earlier open findings on this phase, each with its `id`. Answer every one in a `resolutions` list in the block: `{"id":"f_…","status":"resolved","evidence":"what you checked and saw"}` when it is fixed, `still_present` when it is not, `cannot_tell` when you could not check it. A finding you leave out stays open: silence never closes a finding, and `resolved` without evidence closes nothing.
|
|
142
143
|
|
|
143
144
|
The orchestrator records this block. It is how your findings reach the phase's findings ledger and `/pan:review-deep`.
|
|
144
145
|
|
package/agents/pan-verifier.md
CHANGED
|
@@ -59,7 +59,7 @@ cat "$PHASE_DIR"/*-verification.md 2>/dev/null
|
|
|
59
59
|
1. Parse previous verification.md frontmatter
|
|
60
60
|
2. Take `must_haves` (truths, artifacts, key_links) from the plans' frontmatter (Step 2, Option A): the verification frontmatter does not store them
|
|
61
61
|
3. Extract `gaps` (items that failed)
|
|
62
|
-
4. Set `is_re_verification = true`
|
|
62
|
+
4. Set `is_re_verification = true`. In the frontmatter's `re_verification` block, list every earlier gap you re-checked and found closed under `gaps_closed`, by its truth (add `evidence:` under a truth when one line says what you saw), and every gap still failing under `gaps_remaining`. A gap you leave out of both stays open in the findings ledger: silence never closes a gap
|
|
63
63
|
5. **Skip to Step 3** with optimization:
|
|
64
64
|
- **Failed items:** Full 3-level verification (exists, substantive, wired)
|
|
65
65
|
- **Passed items:** Quick regression check (existence + basic sanity only)
|
package/bin/install-lib.cjs
CHANGED
|
@@ -1160,11 +1160,16 @@ function stripThinkingFrontmatter(content, runtime) {
|
|
|
1160
1160
|
*/
|
|
1161
1161
|
function buildCopilotHooksConfig(commands) {
|
|
1162
1162
|
const { updateCheckCommand, contextMonitorCommand, costLoggerCommand, traceLoggerCommand, stopGuardCommand,
|
|
1163
|
-
stateReinjectMarkCommand, stateReinjectInjectCommand } = commands || {};
|
|
1163
|
+
stateReinjectMarkCommand, stateReinjectInjectCommand, guardCommand } = commands || {};
|
|
1164
1164
|
const config = { version: 1, hooks: {} };
|
|
1165
1165
|
if (updateCheckCommand) {
|
|
1166
1166
|
config.hooks.sessionStart = [{ type: 'command', command: updateCheckCommand }];
|
|
1167
1167
|
}
|
|
1168
|
+
// preToolUse: the PAN guard (A7). Copilot denies a call on the JSON
|
|
1169
|
+
// `permissionDecision: "deny"` output, which pan-guard.js prints under `--host copilot`.
|
|
1170
|
+
if (guardCommand) {
|
|
1171
|
+
config.hooks.preToolUse = [{ type: 'command', command: guardCommand }];
|
|
1172
|
+
}
|
|
1168
1173
|
const postToolUse = [];
|
|
1169
1174
|
if (contextMonitorCommand) postToolUse.push({ type: 'command', command: contextMonitorCommand });
|
|
1170
1175
|
// State re-injection after a compaction (M33): preCompact leaves the marker, the
|
|
@@ -1238,10 +1243,10 @@ function buildCopilotHooksConfig(commands) {
|
|
|
1238
1243
|
* 2026-10-03, and live on Copilot CLI 1.0.88 the same day).
|
|
1239
1244
|
*/
|
|
1240
1245
|
const HOOK_EVENT_MAP = Object.freeze({
|
|
1241
|
-
claude: { surface: 'settings.json', sessionStart: 'SessionStart', postToolUse: 'PostToolUse', subagentStop: 'SubagentStop', stop: 'Stop', compact: 'SessionStart', compactMark: null, compactInject: null },
|
|
1242
|
-
gemini: { surface: 'settings.json', sessionStart: 'SessionStart', postToolUse: null, subagentStop: null, stop: 'AfterAgent', compact: null, compactMark: 'PreCompress', compactInject: 'AfterTool' },
|
|
1243
|
-
codex: { surface: 'hooks.json', sessionStart: 'SessionStart', postToolUse: 'PostToolUse', subagentStop: 'SubagentStop', stop: 'Stop', compact: 'SessionStart', compactMark: null, compactInject: null },
|
|
1244
|
-
copilot: { surface: 'hooks/pan.json', sessionStart: 'sessionStart', postToolUse: 'postToolUse', subagentStop: 'subagentStop', stop: 'agentStop', compact: null, compactMark: 'preCompact', compactInject: 'postToolUse' },
|
|
1246
|
+
claude: { surface: 'settings.json', sessionStart: 'SessionStart', preToolUse: 'PreToolUse', postToolUse: 'PostToolUse', subagentStop: 'SubagentStop', stop: 'Stop', compact: 'SessionStart', compactMark: null, compactInject: null },
|
|
1247
|
+
gemini: { surface: 'settings.json', sessionStart: 'SessionStart', preToolUse: 'BeforeTool', postToolUse: null, subagentStop: null, stop: 'AfterAgent', compact: null, compactMark: 'PreCompress', compactInject: 'AfterTool' },
|
|
1248
|
+
codex: { surface: 'hooks.json', sessionStart: 'SessionStart', preToolUse: 'PreToolUse', postToolUse: 'PostToolUse', subagentStop: 'SubagentStop', stop: 'Stop', compact: 'SessionStart', compactMark: null, compactInject: null },
|
|
1249
|
+
copilot: { surface: 'hooks/pan.json', sessionStart: 'sessionStart', preToolUse: 'preToolUse', postToolUse: 'postToolUse', subagentStop: 'subagentStop', stop: 'agentStop', compact: null, compactMark: 'preCompact', compactInject: 'postToolUse' },
|
|
1245
1250
|
opencode: null,
|
|
1246
1251
|
});
|
|
1247
1252
|
|
|
@@ -1253,7 +1258,7 @@ const HOOK_EVENT_MAP = Object.freeze({
|
|
|
1253
1258
|
const HOOK_SLOT_MATCHERS = Object.freeze({ compact: 'compact' });
|
|
1254
1259
|
|
|
1255
1260
|
/** The hook scripts PAN registers in a Claude-shaped settings.json `hooks` block. */
|
|
1256
|
-
const PAN_SETTINGS_HOOKS = Object.freeze(['pan-check-update', 'pan-context-monitor', 'pan-cost-logger', 'pan-trace-logger', 'pan-stop-guard', 'pan-state-reinject']);
|
|
1261
|
+
const PAN_SETTINGS_HOOKS = Object.freeze(['pan-check-update', 'pan-guard', 'pan-context-monitor', 'pan-cost-logger', 'pan-trace-logger', 'pan-stop-guard', 'pan-state-reinject']);
|
|
1257
1262
|
|
|
1258
1263
|
/**
|
|
1259
1264
|
* Remove, from every event array of a Claude-shaped `hooks` object except
|
|
@@ -1384,10 +1389,78 @@ function mergeMcpRegistration(existing, runtime, entry, serverName = 'pan') {
|
|
|
1384
1389
|
const config = (existing && typeof existing === 'object' && !Array.isArray(existing)) ? existing : {};
|
|
1385
1390
|
const key = spec.key;
|
|
1386
1391
|
if (!config[key] || typeof config[key] !== 'object' || Array.isArray(config[key])) config[key] = {};
|
|
1392
|
+
// OpenCode 2 nests servers under `mcp.servers` (R47). PAN keeps writing the v1
|
|
1393
|
+
// shape, but in a file the user already converted it updates the native entry and
|
|
1394
|
+
// never leaves a legacy `mcp.pan` beside it: the native one would win, stale.
|
|
1395
|
+
if (runtime === 'opencode' && isOpencodeV2Servers(config[key].servers)) {
|
|
1396
|
+
config[key].servers[serverName] = entry;
|
|
1397
|
+
delete config[key][serverName];
|
|
1398
|
+
return config;
|
|
1399
|
+
}
|
|
1387
1400
|
config[key][serverName] = entry;
|
|
1388
1401
|
return config;
|
|
1389
1402
|
}
|
|
1390
1403
|
|
|
1404
|
+
/** OpenCode 2's `mcp.servers` container: an object of server entries, not a v1 server named "servers". */
|
|
1405
|
+
function isOpencodeV2Servers(v) {
|
|
1406
|
+
return !!v && typeof v === 'object' && !Array.isArray(v) && !('type' in v) && !('command' in v);
|
|
1407
|
+
}
|
|
1408
|
+
|
|
1409
|
+
/**
|
|
1410
|
+
* Add PAN's OpenCode read and external_directory allowances for `panPath`.
|
|
1411
|
+
* v1 (or a new file): the `permission` object PAN has always written. v2 (the file
|
|
1412
|
+
* already has an ordered `permissions[]` of {action, resource, effect}): PAN's rules
|
|
1413
|
+
* go into that array, and no legacy `permission` object is added beside it (R47).
|
|
1414
|
+
* @returns {{config: object, modified: boolean, shape: 'v1'|'v2'}}
|
|
1415
|
+
*/
|
|
1416
|
+
function addOpencodePermissions(existing, panPath) {
|
|
1417
|
+
const config = (existing && typeof existing === 'object' && !Array.isArray(existing)) ? existing : {};
|
|
1418
|
+
let modified = false;
|
|
1419
|
+
if (Array.isArray(config.permissions)) {
|
|
1420
|
+
for (const action of ['read', 'external_directory']) {
|
|
1421
|
+
const has = config.permissions.some((r) => r && r.action === action && r.resource === panPath && r.effect === 'allow');
|
|
1422
|
+
if (!has) { config.permissions.push({ action, resource: panPath, effect: 'allow' }); modified = true; }
|
|
1423
|
+
}
|
|
1424
|
+
return { config, modified, shape: 'v2' };
|
|
1425
|
+
}
|
|
1426
|
+
if (!config.permission || typeof config.permission !== 'object' || Array.isArray(config.permission)) config.permission = {};
|
|
1427
|
+
for (const type of ['read', 'external_directory']) {
|
|
1428
|
+
if (!config.permission[type] || typeof config.permission[type] !== 'object') config.permission[type] = {};
|
|
1429
|
+
if (config.permission[type][panPath] !== 'allow') { config.permission[type][panPath] = 'allow'; modified = true; }
|
|
1430
|
+
}
|
|
1431
|
+
return { config, modified, shape: 'v1' };
|
|
1432
|
+
}
|
|
1433
|
+
|
|
1434
|
+
/**
|
|
1435
|
+
* Remove every PAN allowance from an OpenCode config, in either shape: keys of the
|
|
1436
|
+
* v1 `permission.read` / `permission.external_directory` objects and v2
|
|
1437
|
+
* `permissions[]` rules whose path names `pan-wizard` (which also catches the
|
|
1438
|
+
* malformed `~/.config/opencode/pan-wizard/*` key early versions wrote). Containers
|
|
1439
|
+
* PAN emptied are removed.
|
|
1440
|
+
* @returns {{config: object, modified: boolean}}
|
|
1441
|
+
*/
|
|
1442
|
+
function stripOpencodePermissions(existing) {
|
|
1443
|
+
const config = (existing && typeof existing === 'object' && !Array.isArray(existing)) ? existing : {};
|
|
1444
|
+
let modified = false;
|
|
1445
|
+
if (config.permission && typeof config.permission === 'object' && !Array.isArray(config.permission)) {
|
|
1446
|
+
for (const type of ['read', 'external_directory']) {
|
|
1447
|
+
const bag = config.permission[type];
|
|
1448
|
+
if (!bag || typeof bag !== 'object') continue;
|
|
1449
|
+
for (const key of Object.keys(bag)) if (key.includes('pan-wizard')) { delete bag[key]; modified = true; }
|
|
1450
|
+
if (Object.keys(bag).length === 0) delete config.permission[type];
|
|
1451
|
+
}
|
|
1452
|
+
if (Object.keys(config.permission).length === 0) delete config.permission;
|
|
1453
|
+
}
|
|
1454
|
+
if (Array.isArray(config.permissions)) {
|
|
1455
|
+
const kept = config.permissions.filter((r) => !(r && typeof r.resource === 'string' && r.resource.includes('pan-wizard')));
|
|
1456
|
+
if (kept.length !== config.permissions.length) {
|
|
1457
|
+
modified = true;
|
|
1458
|
+
if (kept.length) config.permissions = kept; else delete config.permissions;
|
|
1459
|
+
}
|
|
1460
|
+
}
|
|
1461
|
+
return { config, modified };
|
|
1462
|
+
}
|
|
1463
|
+
|
|
1391
1464
|
/**
|
|
1392
1465
|
* Remove PAN's MCP server from a config object, preserving foreign entries.
|
|
1393
1466
|
* Empties the container key when PAN was its only member, so uninstall does not
|
|
@@ -1400,10 +1473,17 @@ function stripMcpRegistration(existing, runtime, serverName = 'pan') {
|
|
|
1400
1473
|
if (!spec) throw new Error(`stripMcpRegistration: unknown runtime "${runtime}"`);
|
|
1401
1474
|
const config = (existing && typeof existing === 'object' && !Array.isArray(existing)) ? existing : {};
|
|
1402
1475
|
const bag = config[spec.key];
|
|
1403
|
-
if (!bag || typeof bag !== 'object'
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1476
|
+
if (!bag || typeof bag !== 'object') return { config, removed: false };
|
|
1477
|
+
let removed = false;
|
|
1478
|
+
// OpenCode 2's `mcp.servers.pan` as well as the v1 `mcp.pan` (R47).
|
|
1479
|
+
if (runtime === 'opencode' && isOpencodeV2Servers(bag.servers) && serverName in bag.servers) {
|
|
1480
|
+
delete bag.servers[serverName];
|
|
1481
|
+
if (Object.keys(bag.servers).length === 0) delete bag.servers;
|
|
1482
|
+
removed = true;
|
|
1483
|
+
}
|
|
1484
|
+
if (serverName in bag) { delete bag[serverName]; removed = true; }
|
|
1485
|
+
if (removed && Object.keys(bag).length === 0) delete config[spec.key];
|
|
1486
|
+
return { config, removed };
|
|
1407
1487
|
}
|
|
1408
1488
|
|
|
1409
1489
|
/**
|
|
@@ -1439,7 +1519,7 @@ function buildCodexMcpSnippet(serverPath, panToolsPath, projectRoot, serverName
|
|
|
1439
1519
|
* @returns {object} Merged config object to serialize back to hooks.json
|
|
1440
1520
|
*/
|
|
1441
1521
|
function mergeCodexHooksConfig(existing, commands) {
|
|
1442
|
-
const { updateCheckCommand, contextMonitorCommand, costLoggerCommand, traceLoggerCommand, stopGuardCommand, stateReinjectCommand } = commands || {};
|
|
1522
|
+
const { updateCheckCommand, contextMonitorCommand, costLoggerCommand, traceLoggerCommand, stopGuardCommand, stateReinjectCommand, guardCommand } = commands || {};
|
|
1443
1523
|
const config = (existing && typeof existing === 'object') ? existing : {};
|
|
1444
1524
|
if (!config.hooks || typeof config.hooks !== 'object') config.hooks = {};
|
|
1445
1525
|
|
|
@@ -1461,6 +1541,8 @@ function mergeCodexHooksConfig(existing, commands) {
|
|
|
1461
1541
|
// builders without reading their docs first.
|
|
1462
1542
|
const wanted = [
|
|
1463
1543
|
['SessionStart', updateCheckCommand, 'pan-check-update', true],
|
|
1544
|
+
// The guard decides whether a call may run, which an async handler cannot (A7).
|
|
1545
|
+
['PreToolUse', guardCommand, 'pan-guard', false],
|
|
1464
1546
|
['PostToolUse', contextMonitorCommand, 'pan-context-monitor', false],
|
|
1465
1547
|
['SubagentStop', costLoggerCommand, 'pan-cost-logger', true],
|
|
1466
1548
|
['SubagentStop', traceLoggerCommand, 'pan-trace-logger', true],
|
|
@@ -1504,7 +1586,7 @@ function removeCodexPanHooks(existing) {
|
|
|
1504
1586
|
for (const event of Object.keys(existing.hooks)) {
|
|
1505
1587
|
if (!Array.isArray(existing.hooks[event])) continue;
|
|
1506
1588
|
existing.hooks[event] = existing.hooks[event].filter(group =>
|
|
1507
|
-
!(Array.isArray(group.hooks) && group.hooks.some(h => h.command && /pan-(check-update|context-monitor|cost-logger|trace-logger|stop-guard|state-reinject)/.test(h.command))));
|
|
1589
|
+
!(Array.isArray(group.hooks) && group.hooks.some(h => h.command && /pan-(check-update|guard|context-monitor|cost-logger|trace-logger|stop-guard|state-reinject)/.test(h.command))));
|
|
1508
1590
|
if (existing.hooks[event].length === 0) delete existing.hooks[event];
|
|
1509
1591
|
}
|
|
1510
1592
|
if (Object.keys(existing.hooks).length === 0) delete existing.hooks;
|
|
@@ -1929,15 +2011,16 @@ function buildPluginManifest(pkg) {
|
|
|
1929
2011
|
* @returns {object} hooks.json object
|
|
1930
2012
|
*/
|
|
1931
2013
|
function buildPluginHooksConfig() {
|
|
1932
|
-
const hook = (script, matcher) => ({
|
|
2014
|
+
const hook = (script, matcher, args = '') => ({
|
|
1933
2015
|
...(matcher ? { matcher } : {}),
|
|
1934
|
-
hooks: [{ type: 'command', command: `node "\${CLAUDE_PLUGIN_ROOT}/hooks/${script}"` }],
|
|
2016
|
+
hooks: [{ type: 'command', command: `node "\${CLAUDE_PLUGIN_ROOT}/hooks/${script}"${args}` }],
|
|
1935
2017
|
});
|
|
1936
2018
|
const events = HOOK_EVENT_MAP.claude;
|
|
1937
2019
|
return {
|
|
1938
2020
|
hooks: {
|
|
1939
2021
|
// compact shares SessionStart with the update check, narrowed by its matcher (M10).
|
|
1940
2022
|
[events.sessionStart]: [hook('pan-check-update.js'), hook('pan-state-reinject.js', HOOK_SLOT_MATCHERS.compact)],
|
|
2023
|
+
[events.preToolUse]: [hook('pan-guard.js', null, ' --host claude')],
|
|
1941
2024
|
[events.postToolUse]: [hook('pan-context-monitor.js')],
|
|
1942
2025
|
[events.subagentStop]: [hook('pan-cost-logger.js'), hook('pan-trace-logger.js')],
|
|
1943
2026
|
[events.stop]: [hook('pan-stop-guard.js')],
|
|
@@ -2147,7 +2230,7 @@ const FINDINGS = {
|
|
|
2147
2230
|
properties: {
|
|
2148
2231
|
title: { type: 'string' },
|
|
2149
2232
|
file: { type: 'string' },
|
|
2150
|
-
severity: { type: 'string', enum: ['critical', '
|
|
2233
|
+
severity: { type: 'string', enum: ['critical', 'high', 'medium', 'low', 'info'] }, // PAN's one ladder (verdict.cjs, review-deep.cjs)
|
|
2151
2234
|
detail: { type: 'string' },
|
|
2152
2235
|
},
|
|
2153
2236
|
required: ['title', 'severity', 'detail'],
|
|
@@ -2256,6 +2339,7 @@ return { areas_mapped: maps.filter(Boolean).length, synthesis }
|
|
|
2256
2339
|
{ title: 'Index', detail: 'plan inventory with wave grouping, from the PAN engine' },
|
|
2257
2340
|
{ title: 'Execute', detail: 'one executor per plan; waves in order, plans within a wave in parallel' },
|
|
2258
2341
|
{ title: 'Verify', detail: 'the phase verifier over the completed plans' },
|
|
2342
|
+
{ title: 'Gate', detail: 'the PAN phase gate: stops when the phase may not be completed' },
|
|
2259
2343
|
],
|
|
2260
2344
|
}
|
|
2261
2345
|
// twin: pan-wizard-core/workflows/exec-phase.md
|
|
@@ -2377,7 +2461,24 @@ const verdict = await agent(
|
|
|
2377
2461
|
'Verify phase ' + phaseArg + ' following the PAN verify-phase protocol (PAN core: workflows/verify-phase.md — ' + PAN_TOOLS + '). Check the phase goals against what the plans delivered, write the verification file the protocol prescribes, and report status (passed | gaps_found | human_needed | failed), the verification file path, any gaps, and a summary. Do not mark the phase complete or advance state — that decision stays with the user.',
|
|
2378
2462
|
{ agentType: 'pan-verifier', label: 'verify', phase: 'Verify', schema: VERIFY })
|
|
2379
2463
|
|
|
2380
|
-
|
|
2464
|
+
phase('Gate')
|
|
2465
|
+
const GATE = {
|
|
2466
|
+
type: 'object',
|
|
2467
|
+
properties: {
|
|
2468
|
+
ok: { type: 'boolean' },
|
|
2469
|
+
mode: { type: 'string' },
|
|
2470
|
+
reasons: { type: 'array', items: { type: 'object', properties: { code: { type: 'string' }, detail: { type: 'string' } }, required: ['code'] } },
|
|
2471
|
+
},
|
|
2472
|
+
required: ['ok', 'reasons'],
|
|
2473
|
+
}
|
|
2474
|
+
const gate = await agent(
|
|
2475
|
+
'Run the PAN phase gate for phase ' + phaseArg + ' with the ' + PAN_TOOLS + ' Run exactly: phase gate ' + phaseArg + ' (it exits 1 when it refuses; that is a result, not an error). Return its JSON fields ok, mode and reasons (each code and detail) unchanged. Do not fix anything and do not mark the phase complete.',
|
|
2476
|
+
{ label: 'gate', phase: 'Gate', schema: GATE })
|
|
2477
|
+
const gateReasons = gate && Array.isArray(gate.reasons) ? gate.reasons : []
|
|
2478
|
+
if (!gate || gate.ok !== true) {
|
|
2479
|
+
return { phase: phaseArg, waves_run: waveNumbers.length, plans_complete: executed.length, verification: verdict, gate, halted: 'phase_gate', next: 'The phase gate refused: ' + (gateReasons.map((r) => r.code).join(', ') || 'the gate step returned no verdict') + '. Fix each reason (or close the gaps with /pan:plan-phase ' + phaseArg + ' --gaps), then re-run /pan:exec-phase ' + phaseArg + '. Do not mark the phase complete until the gate passes.' }
|
|
2480
|
+
}
|
|
2481
|
+
return { phase: phaseArg, waves_run: waveNumbers.length, plans_complete: executed.length, verification: verdict, gate, next: 'The phase gate passes. Continue with /pan:exec-phase ' + phaseArg + ' (transition) or /pan:plan-phase for the next phase' }
|
|
2381
2482
|
`;
|
|
2382
2483
|
|
|
2383
2484
|
const diagnoseIssues = `export const meta = {
|
|
@@ -2559,6 +2660,8 @@ module.exports = {
|
|
|
2559
2660
|
buildMcpServerEntry,
|
|
2560
2661
|
mergeMcpRegistration,
|
|
2561
2662
|
stripMcpRegistration,
|
|
2663
|
+
addOpencodePermissions,
|
|
2664
|
+
stripOpencodePermissions,
|
|
2562
2665
|
buildCodexMcpSnippet,
|
|
2563
2666
|
removeCodexPanHooks,
|
|
2564
2667
|
buildNativeWorkflowScripts,
|