pan-wizard 3.21.1 → 3.24.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (136) hide show
  1. package/README.md +38 -24
  2. package/agents/pan-conductor.md +3 -3
  3. package/agents/pan-design-checker.md +83 -0
  4. package/agents/pan-designer.md +67 -0
  5. package/agents/pan-document_code.md +3 -2
  6. package/agents/pan-executor.md +2 -2
  7. package/agents/pan-plan-checker.md +19 -6
  8. package/agents/pan-planner.md +4 -1
  9. package/agents/pan-previewer.md +1 -1
  10. package/agents/pan-roadmapper.md +3 -1
  11. package/agents/pan-verifier.md +10 -10
  12. package/bin/install-lib.cjs +128 -17
  13. package/bin/install.js +480 -74
  14. package/commands/pan/army.md +20 -14
  15. package/commands/pan/audit-deployment.md +17 -14
  16. package/commands/pan/cost.md +2 -2
  17. package/commands/pan/debug.md +1 -1
  18. package/commands/pan/design-phase.md +77 -0
  19. package/commands/pan/exec-phase.md +2 -2
  20. package/commands/pan/experiment.md +18 -14
  21. package/commands/pan/focus-auto.md +5 -5
  22. package/commands/pan/focus-design.md +13 -1
  23. package/commands/pan/focus-exec.md +1 -1
  24. package/commands/pan/learn.md +1 -1
  25. package/commands/pan/links.md +3 -1
  26. package/commands/pan/map-codebase.md +4 -4
  27. package/commands/pan/mcp-bridge.md +1 -1
  28. package/commands/pan/milestone-new.md +3 -1
  29. package/commands/pan/optimize.md +2 -2
  30. package/commands/pan/patches.md +1 -1
  31. package/commands/pan/plan-phase.md +3 -1
  32. package/commands/pan/preview.md +3 -3
  33. package/commands/pan/profile.md +3 -3
  34. package/commands/pan/research-phase.md +1 -1
  35. package/commands/pan/retro.md +4 -1
  36. package/commands/pan/review-deep.md +1 -1
  37. package/commands/pan/settings.md +2 -2
  38. package/commands/pan/what-if.md +1 -1
  39. package/hooks/dist/pan-check-update.js +149 -38
  40. package/hooks/dist/pan-context-monitor.js +155 -81
  41. package/hooks/dist/pan-cost-logger.js +288 -13
  42. package/hooks/dist/pan-statusline.js +24 -8
  43. package/hooks/dist/pan-stop-guard.js +160 -0
  44. package/hooks/dist/pan-trace-logger.js +261 -19
  45. package/package.json +2 -2
  46. package/pan-wizard-core/bin/lib/bridge.cjs +1 -1
  47. package/pan-wizard-core/bin/lib/bus.cjs +19 -4
  48. package/pan-wizard-core/bin/lib/campaign.cjs +8 -3
  49. package/pan-wizard-core/bin/lib/codebase.cjs +15 -5
  50. package/pan-wizard-core/bin/lib/commands.cjs +88 -6
  51. package/pan-wizard-core/bin/lib/config.cjs +27 -2
  52. package/pan-wizard-core/bin/lib/constants.cjs +1 -0
  53. package/pan-wizard-core/bin/lib/core.cjs +113 -8
  54. package/pan-wizard-core/bin/lib/cost.cjs +61 -24
  55. package/pan-wizard-core/bin/lib/distill.cjs +22 -4
  56. package/pan-wizard-core/bin/lib/doc-lint.cjs +25 -7
  57. package/pan-wizard-core/bin/lib/experiment.cjs +25 -1
  58. package/pan-wizard-core/bin/lib/focus.cjs +42 -7
  59. package/pan-wizard-core/bin/lib/frontmatter.cjs +62 -31
  60. package/pan-wizard-core/bin/lib/git.cjs +51 -16
  61. package/pan-wizard-core/bin/lib/hud.cjs +17 -2
  62. package/pan-wizard-core/bin/lib/init.cjs +2 -5
  63. package/pan-wizard-core/bin/lib/knowledge.cjs +20 -1
  64. package/pan-wizard-core/bin/lib/learn-lint.cjs +50 -0
  65. package/pan-wizard-core/bin/lib/links.cjs +9 -6
  66. package/pan-wizard-core/bin/lib/lock.cjs +23 -4
  67. package/pan-wizard-core/bin/lib/memory-optimize.cjs +101 -17
  68. package/pan-wizard-core/bin/lib/memory-rebuild.cjs +26 -2
  69. package/pan-wizard-core/bin/lib/milestone.cjs +11 -5
  70. package/pan-wizard-core/bin/lib/optimize.cjs +18 -7
  71. package/pan-wizard-core/bin/lib/phase-remove.cjs +41 -2
  72. package/pan-wizard-core/bin/lib/phase.cjs +82 -11
  73. package/pan-wizard-core/bin/lib/preview.cjs +23 -4
  74. package/pan-wizard-core/bin/lib/review-deep.cjs +14 -6
  75. package/pan-wizard-core/bin/lib/roadmap.cjs +6 -2
  76. package/pan-wizard-core/bin/lib/runner.cjs +17 -7
  77. package/pan-wizard-core/bin/lib/squads.cjs +43 -11
  78. package/pan-wizard-core/bin/lib/state.cjs +32 -11
  79. package/pan-wizard-core/bin/lib/template.cjs +8 -3
  80. package/pan-wizard-core/bin/lib/verify-deploy.cjs +14 -2
  81. package/pan-wizard-core/bin/lib/verify-drift.cjs +15 -1
  82. package/pan-wizard-core/bin/lib/verify-preflight.cjs +9 -3
  83. package/pan-wizard-core/bin/lib/verify.cjs +84 -39
  84. package/pan-wizard-core/bin/lib/whatif.cjs +8 -1
  85. package/pan-wizard-core/bin/pan-tools.cjs +42 -19
  86. package/pan-wizard-core/learnings/index.json +7 -7
  87. package/pan-wizard-core/learnings/universal/autonomous-loop.md +5 -3
  88. package/pan-wizard-core/learnings/universal/concurrency.md +1 -1
  89. package/pan-wizard-core/references/design-methodology.md +94 -0
  90. package/pan-wizard-core/references/git-integration.md +9 -9
  91. package/pan-wizard-core/references/guardrails.md +4 -1
  92. package/pan-wizard-core/references/model-profile-resolution.md +1 -1
  93. package/pan-wizard-core/references/model-profiles.md +44 -34
  94. package/pan-wizard-core/references/planning-config.md +5 -5
  95. package/pan-wizard-core/references/verification-patterns.md +67 -0
  96. package/pan-wizard-core/templates/codebase/architecture.md +1 -1
  97. package/pan-wizard-core/templates/codebase/concerns.md +1 -1
  98. package/pan-wizard-core/templates/codebase/conventions.md +1 -1
  99. package/pan-wizard-core/templates/codebase/integrations.md +1 -1
  100. package/pan-wizard-core/templates/codebase/stack.md +1 -1
  101. package/pan-wizard-core/templates/codebase/structure.md +1 -1
  102. package/pan-wizard-core/templates/codebase/testing.md +1 -1
  103. package/pan-wizard-core/templates/design.md +146 -0
  104. package/pan-wizard-core/templates/discovery.md +3 -3
  105. package/pan-wizard-core/templates/milestone-archive.md +2 -2
  106. package/pan-wizard-core/templates/playbook.md +1 -1
  107. package/pan-wizard-core/templates/uat.md +1 -1
  108. package/pan-wizard-core/workflows/diagnose-issues.md +9 -7
  109. package/pan-wizard-core/workflows/exec-phase.md +28 -8
  110. package/pan-wizard-core/workflows/execute-plan.md +2 -3
  111. package/pan-wizard-core/workflows/health.md +24 -1
  112. package/pan-wizard-core/workflows/help.md +5 -4
  113. package/pan-wizard-core/workflows/learn.md +5 -5
  114. package/pan-wizard-core/workflows/map-codebase.md +1 -1
  115. package/pan-wizard-core/workflows/milestone-audit.md +1 -1
  116. package/pan-wizard-core/workflows/milestone-new.md +4 -4
  117. package/pan-wizard-core/workflows/new-project.md +47 -32
  118. package/pan-wizard-core/workflows/optimize.md +8 -8
  119. package/pan-wizard-core/workflows/pause.md +1 -1
  120. package/pan-wizard-core/workflows/phase-tests.md +2 -2
  121. package/pan-wizard-core/workflows/plan-phase.md +4 -0
  122. package/pan-wizard-core/workflows/profile.md +13 -10
  123. package/pan-wizard-core/workflows/research-phase.md +1 -1
  124. package/pan-wizard-core/workflows/resume-project.md +1 -1
  125. package/pan-wizard-core/workflows/settings.md +9 -9
  126. package/pan-wizard-core/workflows/transition.md +24 -8
  127. package/pan-wizard-core/workflows/update.md +23 -20
  128. package/pan-wizard-core/workflows/verify-phase.md +7 -2
  129. package/pan-zcode/KNOWN-BETA-RISKS.md +8 -0
  130. package/pan-zcode/README.md +14 -2
  131. package/pan-zcode/bin/install-zcode.js +1 -1
  132. package/pan-zcode/mcp/native-tools.cjs +1 -1
  133. package/pan-zcode/mcp/server.cjs +76 -14
  134. package/scripts/build-hooks.js +2 -1
  135. package/scripts/generate-skills-docs.py +0 -4
  136. package/scripts/run-tests.cjs +11 -4
@@ -12,28 +12,31 @@ Read all files referenced by the invoking prompt's execution_context before star
12
12
  Detect whether PAN is installed locally or globally by checking both locations and validating install integrity:
13
13
 
14
14
  ```bash
15
- # Check local first (takes priority only if valid)
16
- # Paths templated at install time for runtime compatibility
17
- LOCAL_VERSION_FILE="./.claude/pan-wizard-core/VERSION"
18
- LOCAL_MARKER_FILE="./.claude/pan-wizard-core/workflows/update.md"
19
- GLOBAL_VERSION_FILE="$HOME/.claude/pan-wizard-core/VERSION"
20
- GLOBAL_MARKER_FILE="$HOME/.claude/pan-wizard-core/workflows/update.md"
21
-
22
- if [ -f "$LOCAL_VERSION_FILE" ] && [ -f "$LOCAL_MARKER_FILE" ] && grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+' "$LOCAL_VERSION_FILE"; then
23
- cat "$LOCAL_VERSION_FILE"
24
- echo "LOCAL"
25
- elif [ -f "$GLOBAL_VERSION_FILE" ] && [ -f "$GLOBAL_MARKER_FILE" ] && grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+' "$GLOBAL_VERSION_FILE"; then
26
- cat "$GLOBAL_VERSION_FILE"
27
- echo "GLOBAL"
15
+ # There is ONE install location per install. The installer templates the
16
+ # canonical ~/.claude/ prefix to this install's real path, and the SHAPE of that
17
+ # templated prefix reveals the scope: a --local install is cwd-relative ("./…"),
18
+ # a --global install is an absolute path. Deriving scope from the single
19
+ # templated path avoids the collapsed dual-path detection that mis-ran every
20
+ # global update as --local (N2 regression fix, ADR audit 2026-08).
21
+ VERSION_FILE="~/.claude/pan-wizard-core/VERSION"
22
+ MARKER_FILE="~/.claude/pan-wizard-core/workflows/update.md"
23
+ case "$VERSION_FILE" in
24
+ ./*) SCOPE="LOCAL" ;;
25
+ *) SCOPE="GLOBAL" ;;
26
+ esac
27
+
28
+ if [ -f "$VERSION_FILE" ] && [ -f "$MARKER_FILE" ] && grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+' "$VERSION_FILE"; then
29
+ cat "$VERSION_FILE"
30
+ echo "$SCOPE"
28
31
  else
29
32
  echo "UNKNOWN"
30
33
  fi
31
34
  ```
32
35
 
33
- Parse output:
34
- - If last line is "LOCAL": local install is valid; installed version is first line; use `--local`
35
- - If last line is "GLOBAL": local missing/invalid, global install is valid; installed version is first line; use `--global`
36
- - If "UNKNOWN": proceed to install step (treat as version 0.0.0)
36
+ Parse output (there is ONE install; scope is derived from the shape of the single templated prefix):
37
+ - If last line is "LOCAL": this install is local-scoped (templated prefix is `./`-relative); installed version is first line; use `--local`
38
+ - If last line is "GLOBAL": this install is global-scoped (templated prefix is an absolute path); installed version is first line; use `--global`
39
+ - If "UNKNOWN": the VERSION/marker files are missing or invalid; proceed to install step (treat as version 0.0.0)
37
40
 
38
41
  **If VERSION file missing:**
39
42
  ```
@@ -166,7 +169,7 @@ Clear the update cache so statusline indicator disappears:
166
169
 
167
170
  **If LOCAL install:**
168
171
  ```bash
169
- rm -f ./.claude/cache/pan-update-check.json
172
+ rm -f ~/.claude/cache/pan-update-check.json
170
173
  ```
171
174
 
172
175
  **If GLOBAL install:**
@@ -184,9 +187,9 @@ Format completion message (changelog was already shown in confirmation step):
184
187
  ║ PAN Updated: v1.5.10 → v1.5.15 ║
185
188
  ╚═══════════════════════════════════════════════════════════╝
186
189
 
187
- ⚠️ Restart Claude Code to pick up the new commands.
190
+ ⚠️ Restart your AI coding tool to pick up the new commands.
188
191
 
189
- [View full changelog](https://github.com/pan-wizard-core/blob/main/CHANGELOG.md)
192
+ [View full changelog](https://github.com/oharms/PanWizard/blob/main/CHANGELOG.md)
190
193
  ```
191
194
  </step>
192
195
 
@@ -50,7 +50,7 @@ Extract from init JSON: `phase_dir`, `phase_number`, `phase_name`, `has_plans`,
50
50
  Then load phase details and list plans/summaries:
51
51
  ```bash
52
52
  node ~/.claude/pan-wizard-core/bin/pan-tools.cjs roadmap get-phase "${phase_number}"
53
- grep -E "^| ${phase_number}" .planning/requirements.md 2>/dev/null
53
+ grep -E "^\|[^|]*\| *Phase +${phase_number} " .planning/requirements.md 2>/dev/null
54
54
  ls "$phase_dir"/*-summary.md "$phase_dir"/*-plan.md 2>/dev/null
55
55
  ```
56
56
 
@@ -63,7 +63,12 @@ Extract **phase goal** from roadmap.md (the outcome to verify, not tasks) and **
63
63
  If `phase_number` > 1:
64
64
  ```bash
65
65
  PREV=$((phase_number - 1))
66
- PREV_VERIF=$(ls .planning/phase-${PREV}*/*-verification.md 2>/dev/null | head -1)
66
+ # Resolve the previous phase's directory via find-phase (handles zero-padding
67
+ # and the .planning/phases/ layout), like exec-phase does — do NOT glob
68
+ # .planning/phase-N*/ (wrong dir, unpadded, matches nothing). With --raw,
69
+ # find-phase prints the resolved directory path (empty if not found).
70
+ PREV_DIR=$(node ~/.claude/pan-wizard-core/bin/pan-tools.cjs find-phase "${PREV}" --raw 2>/dev/null)
71
+ PREV_VERIF=$([ -n "$PREV_DIR" ] && ls "$PREV_DIR"/*-verification.md 2>/dev/null | head -1)
67
72
  ```
68
73
 
69
74
  If `PREV_VERIF` is empty:
@@ -27,6 +27,14 @@ hard-depend on, and the go/no-go facts that can only be settled on a real ZCode
27
27
  - **User-global subagents only.** No repo-scoped rosters or per-project model profiles.
28
28
  - **Genuinely lost:** scheduled self-resuming multi-day campaigns (no headless/daemon),
29
29
  background execution, committable permissions, and custom slash-commands.
30
+ - **Bridge scope is tool/resource exposure ONLY — never agent session state.** The MCP
31
+ server exposes `pan-tools` verbs as `tools/call` + `resources/read`; it deliberately
32
+ does not represent rich agent session state (diffs, streaming, live thread lifecycle).
33
+ This is by design, not an omission: MCP cannot faithfully carry that state — a point
34
+ OpenAI made explicitly when it built the Codex "harness" as a native Rust core rather
35
+ than over MCP (2026). Do NOT extend the bridge to stream diffs or proxy session state
36
+ through it; keep the CLI's JSON contract as the tool contract. (Codex-adapter audit,
37
+ 2026-07-30.)
30
38
 
31
39
  ## Format-drift policy
32
40
 
@@ -21,13 +21,18 @@ pan-zcode/mcp (this subsystem) a thin, zero-dep bridge — verbs → MCP tools
21
21
  pan-wizard-core (reused as-is) the deterministic engine; .planning/ stays the state store
22
22
  ```
23
23
 
24
+ **Scope boundary (by design):** the bridge exposes `pan-tools` verbs as MCP tools/resources and nothing more. It intentionally does **not** carry rich agent *session state* — diffs, streaming, live thread lifecycle — because MCP can't faithfully represent it (the reason OpenAI built the Codex harness as a native Rust core rather than over MCP). Keep the bridge to tool/resource exposure; the CLI's JSON contract is the tool contract. See `KNOWN-BETA-RISKS.md`.
25
+
24
26
  ## Status — M1–M5 built (M0 is the human verify spike)
25
27
 
26
28
  - **M1 — bridge core.** `mcp/tool-registry.cjs` (pure verb→tool/resource map, with a hard
27
29
  guardrail against exposing a force/reset/rebase/push verb) + `mcp/server.cjs` (a
28
30
  **zero-dependency** JSON-RPC 2.0 stdio MCP server; reads → resources, actions → tools with
29
- accurate hints; shell-less `execFile` spawn; `@file:` overflow protocol; protocol-version
30
- negotiation; strict per-arg validation).
31
+ accurate hints; shell-less `execFile` spawn; `@file:` overflow protocol; strict per-arg
32
+ validation). **Dual-era** per the MCP 2026-07-28 stateless spec (ADR-0041): legacy clients
33
+ use the `initialize` handshake; modern clients declare their protocol version in each
34
+ request's `_meta`, probe `server/discover`, and get `UnsupportedProtocolVersionError`
35
+ (`-32022`) on a version mismatch.
31
36
  - **M2 — determinism grafts.** `mcp/merge-gate.cjs` (two-step, model-proof merge: a human-origin
32
37
  env token that ignores agent-supplied approval; never force/reset/push) + `mcp/orchestrator.cjs`
33
38
  (the deterministic `next-action` state machine with safety caps + regression circuit-breaker),
@@ -50,6 +55,13 @@ Two go/no-go facts can only be settled empirically: **can a subagent call MCP to
50
55
  local stdio MCP calls metered?** Both have folded-in fallbacks (see `KNOWN-BETA-RISKS.md`), so the
51
56
  design holds either way — but confirm them before relying on the richer paths.
52
57
 
58
+ A third M0 checkpoint (added 2026-08): **which protocol era does the real ZCode client speak?**
59
+ The bridge is now dual-era (ADR-0041), so it answers both a legacy `initialize` handshake and a
60
+ modern `server/discover` probe. Confirm on a real install which path ZCode takes and that the
61
+ version it declares is in our supported list; if ZCode ever declares a revision newer than
62
+ `2026-07-28`, add it to `SUPPORTED_VERSIONS_LIST` in `mcp/server.cjs` once its method shapes are
63
+ implemented.
64
+
53
65
  ## Zero dependencies
54
66
 
55
67
  Like the rest of PAN, this subsystem ships **no runtime dependencies**. The MCP protocol is
@@ -48,7 +48,7 @@ PAN-Z bridges PAN's engine to ZCode over MCP. Finish setup inside ZCode:
48
48
  3. **Protect your branches.** The merge gate (\`pan_confirm_merge\`) is one lock; the
49
49
  real, non-bypassable one is **server-side branch protection** on your remote. Enable
50
50
  required reviews so a raw push under Full Access cannot merge. Approve a staged merge
51
- by setting \`PAN_MERGE_APPROVAL=<request-id>\` in the MCP server's environment.
51
+ by setting \`PAN_MERGE_APPROVAL=<approval_token>\` in the MCP server's environment.
52
52
 
53
53
  4. **Never run the install/import step in ZCode Full Access mode.**
54
54
 
@@ -43,7 +43,7 @@ const NATIVE_TOOLS = [
43
43
  {
44
44
  name: 'pan_confirm_merge',
45
45
  title: 'Confirm a human-approved merge',
46
- description: 'Perform a squash-merge ONLY if CI is green, verify passed, and a human-origin approval token (env PAN_MERGE_APPROVAL equal to the request id) is present. Any agent-supplied approval is ignored; never force-pushes or rewrites history.',
46
+ description: 'Perform a squash-merge ONLY if CI is green, verify passed, and a human-origin approval token (env PAN_MERGE_APPROVAL equal to the request\'s approval_token) is present. Any agent-supplied approval is ignored; never force-pushes or rewrites history.',
47
47
  readOnly: false, destructive: true,
48
48
  inputSchema: {
49
49
  type: 'object', additionalProperties: false, required: ['branch'],
@@ -4,8 +4,12 @@
4
4
  * PAN-Z MCP bridge server (M1).
5
5
  *
6
6
  * A dependency-free JSON-RPC 2.0 server over stdio implementing the small MCP
7
- * surface ZCode needs: initialize / tools/list / tools/call / resources/list /
8
- * resources/read / ping. Each pan-tools verb is reached by spawning
7
+ * surface ZCode needs: server/discover / initialize / tools/list / tools/call /
8
+ * resources/list / resources/read / ping. It is a DUAL-ERA server (see the MCP
9
+ * 2026-07-28 versioning spec): legacy clients open with the `initialize`
10
+ * handshake; modern clients (2026-07-28+) declare their protocol version in each
11
+ * request's `_meta` and MAY probe `server/discover` first. Each pan-tools verb is
12
+ * reached by spawning
9
13
  * node <pan-tools.cjs> <verb> [args] --raw --cwd <root>
10
14
  * and returning its JSON — the CLI's JSON contract IS the tool contract, so the
11
15
  * PAN engine (pan-wizard-core) is reused byte-for-byte with no refactor.
@@ -26,11 +30,21 @@ const os = require('os');
26
30
  const path = require('path');
27
31
  const reg = require('./tool-registry.cjs');
28
32
 
33
+ // Legacy `initialize` default: when a handshake-era client omits protocolVersion
34
+ // we answer with this (never a modern version — a legacy client can't speak it).
29
35
  const PROTOCOL_VERSION = '2025-06-18';
30
- // Versions whose method shapes this server actually implements. During
31
- // `initialize` we echo the client's requested version only if it's one of these,
32
- // otherwise we answer with our latest — never claim to speak a version we don't.
33
- const SUPPORTED_PROTOCOL_VERSIONS = new Set(['2025-06-18', '2025-03-26', '2024-11-05']);
36
+ // The modern (per-request `_meta`, stateless) revision this bridge speaks.
37
+ const MODERN_PROTOCOL_VERSION = '2026-07-28';
38
+ // `_meta` key a modern client uses to declare its protocol version per request.
39
+ const META_PROTOCOL_VERSION_KEY = 'io.modelcontextprotocol/protocolVersion';
40
+ // `_meta` key under which a server identifies itself in modern results.
41
+ const META_SERVER_INFO_KEY = 'io.modelcontextprotocol/serverInfo';
42
+ // Every version whose method shapes this server actually implements — the modern
43
+ // revision plus the legacy handshake revisions. Used both for the legacy
44
+ // `initialize` echo and for modern per-request version negotiation; we never
45
+ // claim to speak a version we don't. Newest first (the `server/discover` order).
46
+ const SUPPORTED_VERSIONS_LIST = [MODERN_PROTOCOL_VERSION, '2025-06-18', '2025-03-26', '2024-11-05'];
47
+ const SUPPORTED_PROTOCOL_VERSIONS = new Set(SUPPORTED_VERSIONS_LIST);
34
48
  const SERVER_INFO = { name: 'pan-mcp', version: '0.1.0' };
35
49
 
36
50
  /** Default engine location: pan-wizard-core is a sibling of pan-zcode/. */
@@ -77,7 +91,11 @@ function resolveOverflow(stdout) {
77
91
  }
78
92
 
79
93
  function rpcResult(id, result) { return { jsonrpc: '2.0', id, result }; }
80
- function rpcError(id, code, message) { return { jsonrpc: '2.0', id, error: { code, message } }; }
94
+ function rpcError(id, code, message, data) {
95
+ const error = { code, message };
96
+ if (data !== undefined) error.data = data;
97
+ return { jsonrpc: '2.0', id, error };
98
+ }
81
99
 
82
100
  function toMcpTool(t) {
83
101
  return {
@@ -173,26 +191,67 @@ function createServer(opts = {}) {
173
191
  // the dispatch — this also stops any request-method sent id-less from emitting
174
192
  // an id-less response frame.
175
193
  if (id === undefined || id === null) return null;
194
+
195
+ // Era detection: a modern client (2026-07-28+) declares its protocol version
196
+ // in `_meta` on every request; a legacy client uses the `initialize` handshake
197
+ // and carries no such field. Gating modern behavior on this presence keeps the
198
+ // legacy path byte-for-byte unchanged.
199
+ const requestedVersion = params && params._meta && params._meta[META_PROTOCOL_VERSION_KEY];
200
+ const isModern = typeof requestedVersion === 'string';
201
+
202
+ // Stateless per-request version negotiation: if a modern client asks for a
203
+ // version we don't implement, answer with UnsupportedProtocolVersionError
204
+ // listing what we do support, so it can retry on a mutually-supported version.
205
+ if (isModern && !SUPPORTED_PROTOCOL_VERSIONS.has(requestedVersion)) {
206
+ return rpcError(id, -32022, 'Unsupported protocol version',
207
+ { supported: SUPPORTED_VERSIONS_LIST, requested: requestedVersion });
208
+ }
209
+
210
+ // Modern results MUST carry a `resultType`; legacy results MUST NOT change
211
+ // shape (clients treat an absent resultType as "complete"). Stamp it only on
212
+ // the modern path.
213
+ const reply = (result) => rpcResult(id,
214
+ isModern && result && typeof result === 'object' && result.resultType === undefined
215
+ ? { resultType: 'complete', ...result }
216
+ : result);
217
+
176
218
  switch (method) {
219
+ // Modern stateless discovery probe (MUST be implemented). Also the stdio
220
+ // backward-compat probe: a dual-era client sends this first; a real
221
+ // DiscoverResult identifies us as modern-capable, and its supportedVersions
222
+ // let the client pick a version before issuing any tools/resources call.
223
+ case 'server/discover':
224
+ return rpcResult(id, {
225
+ resultType: 'complete',
226
+ supportedVersions: SUPPORTED_VERSIONS_LIST,
227
+ capabilities: { tools: {}, resources: {} },
228
+ instructions: 'PAN Wizard engine bridge: planning, verification, and orchestration tools backed by the pan-tools CLI. All tools are read-only except the gated pan_confirm_merge.',
229
+ ttlMs: 3600000,
230
+ cacheScope: 'public',
231
+ _meta: { [META_SERVER_INFO_KEY]: SERVER_INFO },
232
+ });
177
233
  case 'initialize': {
178
234
  const requested = params && params.protocolVersion;
179
- const negotiated = SUPPORTED_PROTOCOL_VERSIONS.has(requested) ? requested : PROTOCOL_VERSION;
235
+ // A legacy handshake must not negotiate a modern (per-request `_meta`)
236
+ // revision, so only echo legacy versions; anything else falls back.
237
+ const negotiated = (requested && requested !== MODERN_PROTOCOL_VERSION
238
+ && SUPPORTED_PROTOCOL_VERSIONS.has(requested)) ? requested : PROTOCOL_VERSION;
180
239
  return rpcResult(id, { protocolVersion: negotiated, capabilities: { tools: {}, resources: {} }, serverInfo: SERVER_INFO });
181
240
  }
182
241
  case 'ping':
183
- return rpcResult(id, {});
242
+ return reply({});
184
243
  case 'tools/list':
185
- return rpcResult(id, { tools: reg.TOOLS.map(toMcpTool) });
244
+ return reply({ tools: reg.TOOLS.map(toMcpTool) });
186
245
  case 'resources/list':
187
- return rpcResult(id, { resources: reg.RESOURCES.map(toMcpResource) });
246
+ return reply({ resources: reg.RESOURCES.map(toMcpResource) });
188
247
  case 'tools/call': {
189
248
  const out = callTool(params && params.name, params && params.arguments);
190
- return out.error ? rpcError(id, out.error.code, out.error.message) : rpcResult(id, out.result);
249
+ return out.error ? rpcError(id, out.error.code, out.error.message) : reply(out.result);
191
250
  }
192
251
  case 'resources/read': {
193
252
  const out = readResource(params && params.uri);
194
253
  if (out.unknown) return rpcError(id, -32602, `Unknown resource: ${params && params.uri}`);
195
- return out.error ? rpcError(id, out.error.code, out.error.message) : rpcResult(id, out.result);
254
+ return out.error ? rpcError(id, out.error.code, out.error.message) : reply(out.result);
196
255
  }
197
256
  default:
198
257
  return rpcError(id, -32601, `Method not found: ${method}`);
@@ -226,4 +285,7 @@ function main() {
226
285
 
227
286
  if (require.main === module) main();
228
287
 
229
- module.exports = { createServer, defaultPanToolsPath, defaultSpawn, PROTOCOL_VERSION, SERVER_INFO, toMcpTool, toMcpResource };
288
+ module.exports = {
289
+ createServer, defaultPanToolsPath, defaultSpawn, SERVER_INFO, toMcpTool, toMcpResource,
290
+ PROTOCOL_VERSION, MODERN_PROTOCOL_VERSION, SUPPORTED_VERSIONS_LIST, META_PROTOCOL_VERSION_KEY,
291
+ };
@@ -21,7 +21,8 @@ const HOOKS_TO_COPY = [
21
21
  'pan-context-monitor.js',
22
22
  'pan-statusline.js',
23
23
  'pan-cost-logger.js',
24
- 'pan-trace-logger.js'
24
+ 'pan-trace-logger.js',
25
+ 'pan-stop-guard.js'
25
26
  ];
26
27
 
27
28
  function build() {
@@ -232,8 +232,6 @@ def generate_full_text(shipped: list[Skill], dev: list[Skill], version: str) ->
232
232
  w("Every skill (slash command) available in PAN Wizard, reproduced in full.")
233
233
  w("This is the actual prompt text that Claude receives when a skill is invoked.")
234
234
  w("")
235
- w(f"**Version:** {version}")
236
- w("")
237
235
  w("> Auto-generated by `scripts/generate-skills-docs.py` — do not edit manually.")
238
236
  w("> For canonical counts (commands / agents / modules / etc.), see `CLAUDE.md`.")
239
237
  w("")
@@ -330,8 +328,6 @@ def generate_reference(shipped: list[Skill], dev: list[Skill], version: str) ->
330
328
  w("Complete catalog of every skill (slash command) available in PAN Wizard, organized by purpose.")
331
329
  w("Each entry shows the command, what it does, what tools it uses, and when to reach for it.")
332
330
  w("")
333
- w(f"**Version:** {version}")
334
- w("")
335
331
  w("> Auto-generated by `scripts/generate-skills-docs.py` — do not edit manually.")
336
332
  w("> For canonical counts (commands / agents / modules / etc.), see `CLAUDE.md`.")
337
333
  w("")
@@ -6,18 +6,24 @@
6
6
  * literal pattern "tests/*.test.cjs" matches no file and the run exits 1.
7
7
  * This script expands the pattern deterministically on every platform.
8
8
  *
9
- * Usage: node scripts/run-tests.cjs <dir> [<dir> ...]
9
+ * Usage: node scripts/run-tests.cjs [--watch] <dir> [<dir> ...]
10
10
  * Runs every *.test.cjs DIRECTLY inside each listed directory (no recursion,
11
11
  * so `tests` and `tests/scenarios` stay separately addressable).
12
+ * --watch re-runs on change. It must go through this script too: since Node 21
13
+ * a bare `node --test --watch tests/` treats `tests/` as a glob pattern, fails
14
+ * to match the directory as a file, and crashes having run nothing (audit L36 /
15
+ * N8) — so the same deterministic expansion has to feed --watch.
12
16
  */
13
17
 
14
18
  const { spawnSync } = require('child_process');
15
19
  const fs = require('fs');
16
20
  const path = require('path');
17
21
 
18
- const dirs = process.argv.slice(2);
22
+ const argv = process.argv.slice(2);
23
+ const watch = argv[0] === '--watch';
24
+ const dirs = watch ? argv.slice(1) : argv;
19
25
  if (dirs.length === 0) {
20
- console.error('Usage: node scripts/run-tests.cjs <dir> [<dir> ...]');
26
+ console.error('Usage: node scripts/run-tests.cjs [--watch] <dir> [<dir> ...]');
21
27
  process.exit(1);
22
28
  }
23
29
 
@@ -40,5 +46,6 @@ if (files.length === 0) {
40
46
  process.exit(1);
41
47
  }
42
48
 
43
- const result = spawnSync(process.execPath, ['--test', ...files], { stdio: 'inherit' });
49
+ const nodeArgs = watch ? ['--test', '--watch', ...files] : ['--test', ...files];
50
+ const result = spawnSync(process.execPath, nodeArgs, { stdio: 'inherit' });
44
51
  process.exit(result.status ?? 1);