pan-wizard 3.32.0 → 3.34.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 (209) hide show
  1. package/README.md +23 -20
  2. package/agents/pan-conductor.md +18 -17
  3. package/agents/pan-counterfactual.md +2 -2
  4. package/agents/pan-debugger.md +27 -8
  5. package/agents/pan-design-checker.md +1 -1
  6. package/agents/pan-designer.md +2 -2
  7. package/agents/pan-distiller.md +3 -3
  8. package/agents/pan-document_code.md +16 -16
  9. package/agents/pan-executor.md +15 -11
  10. package/agents/pan-experiment-runner.md +15 -17
  11. package/agents/pan-hardener.md +15 -15
  12. package/agents/pan-integration-checker.md +2 -2
  13. package/agents/pan-knowledge.md +2 -2
  14. package/agents/pan-meta-reviewer.md +5 -5
  15. package/agents/pan-optimizer.md +40 -44
  16. package/agents/pan-phase-researcher.md +10 -10
  17. package/agents/pan-plan-checker.md +36 -30
  18. package/agents/pan-planner.md +77 -53
  19. package/agents/pan-previewer.md +1 -1
  20. package/agents/pan-project-researcher.md +21 -78
  21. package/agents/pan-release.md +1 -1
  22. package/agents/pan-research-synthesizer.md +2 -2
  23. package/agents/pan-reviewer.md +24 -2
  24. package/agents/pan-roadmapper.md +20 -62
  25. package/agents/pan-verifier.md +53 -30
  26. package/bin/install-lib.cjs +228 -38
  27. package/bin/install.js +86 -15
  28. package/commands/pan/army.md +16 -12
  29. package/commands/pan/assumptions.md +2 -2
  30. package/commands/pan/audit-deployment.md +11 -12
  31. package/commands/pan/cost.md +8 -4
  32. package/commands/pan/debug.md +10 -7
  33. package/commands/pan/design-phase.md +3 -3
  34. package/commands/pan/discuss-phase.md +2 -0
  35. package/commands/pan/exec-phase.md +21 -37
  36. package/commands/pan/experiment.md +11 -10
  37. package/commands/pan/focus-auto.md +33 -27
  38. package/commands/pan/focus-design.md +9 -6
  39. package/commands/pan/focus-doc-audit.md +4 -3
  40. package/commands/pan/focus-drift-walking.md +4 -3
  41. package/commands/pan/focus-exec.md +7 -7
  42. package/commands/pan/focus-plan.md +12 -11
  43. package/commands/pan/focus-scan.md +7 -10
  44. package/commands/pan/focus-sync.md +3 -2
  45. package/commands/pan/git.md +8 -8
  46. package/commands/pan/health.md +2 -2
  47. package/commands/pan/hud.md +3 -2
  48. package/commands/pan/hygiene.md +14 -7
  49. package/commands/pan/knowledge.md +9 -9
  50. package/commands/pan/learn.md +10 -7
  51. package/commands/pan/links.md +4 -1
  52. package/commands/pan/map-codebase.md +5 -5
  53. package/commands/pan/mcp-bridge.md +18 -18
  54. package/commands/pan/milestone-done.md +7 -5
  55. package/commands/pan/milestone-new.md +1 -1
  56. package/commands/pan/new-project.md +10 -10
  57. package/commands/pan/optimize.md +16 -14
  58. package/commands/pan/patches.md +2 -6
  59. package/commands/pan/pause.md +1 -1
  60. package/commands/pan/phase-tests.md +4 -3
  61. package/commands/pan/plan-phase.md +9 -19
  62. package/commands/pan/preview.md +1 -1
  63. package/commands/pan/profile.md +4 -4
  64. package/commands/pan/quick.md +1 -1
  65. package/commands/pan/remove-phase.md +1 -1
  66. package/commands/pan/report.md +2 -2
  67. package/commands/pan/research-phase.md +14 -15
  68. package/commands/pan/resume.md +9 -11
  69. package/commands/pan/retro.md +5 -3
  70. package/commands/pan/review-deep.md +10 -10
  71. package/commands/pan/settings.md +1 -1
  72. package/commands/pan/todo-add.md +2 -2
  73. package/commands/pan/todo-check.md +2 -2
  74. package/commands/pan/verify-phase.md +0 -11
  75. package/commands/pan/what-if.md +2 -2
  76. package/hooks/dist/pan-context-monitor.js +343 -44
  77. package/hooks/dist/pan-cost-logger.js +30 -0
  78. package/hooks/dist/pan-state-reinject.js +140 -15
  79. package/hooks/dist/pan-statusline.js +68 -22
  80. package/hooks/dist/pan-stop-guard.js +7 -2
  81. package/package.json +1 -1
  82. package/pan-wizard-core/bin/lib/agents-md.cjs +48 -7
  83. package/pan-wizard-core/bin/lib/bridge.cjs +6 -5
  84. package/pan-wizard-core/bin/lib/campaign.cjs +4 -2
  85. package/pan-wizard-core/bin/lib/commands-learnings.cjs +19 -2
  86. package/pan-wizard-core/bin/lib/commands.cjs +126 -5
  87. package/pan-wizard-core/bin/lib/config.cjs +112 -1
  88. package/pan-wizard-core/bin/lib/constants.cjs +29 -15
  89. package/pan-wizard-core/bin/lib/context-budget.cjs +12 -9
  90. package/pan-wizard-core/bin/lib/core.cjs +77 -23
  91. package/pan-wizard-core/bin/lib/cost-rebuild.cjs +13 -1
  92. package/pan-wizard-core/bin/lib/cost.cjs +149 -10
  93. package/pan-wizard-core/bin/lib/distill.cjs +6 -3
  94. package/pan-wizard-core/bin/lib/doc-lint.cjs +30 -11
  95. package/pan-wizard-core/bin/lib/findings.cjs +15 -4
  96. package/pan-wizard-core/bin/lib/focus.cjs +13 -4
  97. package/pan-wizard-core/bin/lib/frontmatter.cjs +105 -26
  98. package/pan-wizard-core/bin/lib/hygiene.cjs +178 -17
  99. package/pan-wizard-core/bin/lib/init.cjs +12 -5
  100. package/pan-wizard-core/bin/lib/knowledge.cjs +17 -1
  101. package/pan-wizard-core/bin/lib/learn-index.cjs +111 -10
  102. package/pan-wizard-core/bin/lib/learn-lint.cjs +30 -3
  103. package/pan-wizard-core/bin/lib/memory-optimize.cjs +8 -4
  104. package/pan-wizard-core/bin/lib/memory.cjs +508 -54
  105. package/pan-wizard-core/bin/lib/milestone.cjs +8 -3
  106. package/pan-wizard-core/bin/lib/optimize.cjs +84 -16
  107. package/pan-wizard-core/bin/lib/phase-remove.cjs +85 -21
  108. package/pan-wizard-core/bin/lib/phase-report.cjs +8 -2
  109. package/pan-wizard-core/bin/lib/phase.cjs +67 -15
  110. package/pan-wizard-core/bin/lib/preview.cjs +3 -1
  111. package/pan-wizard-core/bin/lib/review-deep.cjs +2 -2
  112. package/pan-wizard-core/bin/lib/roadmap-compact.cjs +267 -0
  113. package/pan-wizard-core/bin/lib/roadmap.cjs +243 -9
  114. package/pan-wizard-core/bin/lib/runner.cjs +2 -1
  115. package/pan-wizard-core/bin/lib/state-compact.cjs +11 -6
  116. package/pan-wizard-core/bin/lib/state.cjs +47 -31
  117. package/pan-wizard-core/bin/lib/template.cjs +11 -4
  118. package/pan-wizard-core/bin/lib/verdict.cjs +54 -4
  119. package/pan-wizard-core/bin/lib/verify-drift.cjs +14 -5
  120. package/pan-wizard-core/bin/lib/verify-preflight.cjs +4 -13
  121. package/pan-wizard-core/bin/lib/verify-retro.cjs +5 -3
  122. package/pan-wizard-core/bin/lib/verify-scope.cjs +74 -4
  123. package/pan-wizard-core/bin/lib/verify.cjs +117 -35
  124. package/pan-wizard-core/bin/lib/whatif.cjs +5 -3
  125. package/pan-wizard-core/bin/pan-tools.cjs +111 -17
  126. package/pan-wizard-core/learnings/index.json +5 -5
  127. package/pan-wizard-core/mcp/server.cjs +18 -9
  128. package/pan-wizard-core/opencode/pan-wizard.js +111 -0
  129. package/pan-wizard-core/references/checkpoints.md +7 -5
  130. package/pan-wizard-core/references/continuation-format.md +3 -3
  131. package/pan-wizard-core/references/decimal-phase-calculation.md +2 -3
  132. package/pan-wizard-core/references/design-methodology.md +1 -1
  133. package/pan-wizard-core/references/git-integration.md +4 -6
  134. package/pan-wizard-core/references/git-planning-commit.md +2 -2
  135. package/pan-wizard-core/references/guardrails.md +2 -2
  136. package/pan-wizard-core/references/handoff-decisions.md +3 -3
  137. package/pan-wizard-core/references/model-profile-resolution.md +5 -5
  138. package/pan-wizard-core/references/model-profiles.md +14 -9
  139. package/pan-wizard-core/references/phase-argument-parsing.md +7 -7
  140. package/pan-wizard-core/references/planning-config.md +4 -4
  141. package/pan-wizard-core/references/tdd.md +10 -1
  142. package/pan-wizard-core/references/verdict-contract.md +2 -1
  143. package/pan-wizard-core/references/verification-patterns.md +5 -5
  144. package/pan-wizard-core/templates/codebase/architecture.md +2 -2
  145. package/pan-wizard-core/templates/codebase/best-practices.md +2 -2
  146. package/pan-wizard-core/templates/codebase/concerns.md +3 -3
  147. package/pan-wizard-core/templates/codebase/integrations.md +6 -6
  148. package/pan-wizard-core/templates/codebase/relationships.md +2 -2
  149. package/pan-wizard-core/templates/codebase/stack.md +2 -2
  150. package/pan-wizard-core/templates/codebase/structure.md +4 -4
  151. package/pan-wizard-core/templates/codebase/testing.md +4 -4
  152. package/pan-wizard-core/templates/continue-here.md +1 -1
  153. package/pan-wizard-core/templates/debug-subagent-prompt.md +3 -2
  154. package/pan-wizard-core/templates/debug.md +15 -1
  155. package/pan-wizard-core/templates/discovery.md +1 -1
  156. package/pan-wizard-core/templates/milestone-archive.md +6 -6
  157. package/pan-wizard-core/templates/milestone.md +1 -1
  158. package/pan-wizard-core/templates/phase-prompt.md +31 -18
  159. package/pan-wizard-core/templates/planner-subagent-prompt.md +3 -5
  160. package/pan-wizard-core/templates/research-project/architecture.md +3 -3
  161. package/pan-wizard-core/templates/research-project/features.md +1 -1
  162. package/pan-wizard-core/templates/research-project/pitfalls.md +1 -1
  163. package/pan-wizard-core/templates/research-project/stack.md +3 -3
  164. package/pan-wizard-core/templates/research.md +5 -5
  165. package/pan-wizard-core/templates/retrospective.md +1 -1
  166. package/pan-wizard-core/templates/roadmap.md +15 -15
  167. package/pan-wizard-core/templates/summary-complex.md +1 -0
  168. package/pan-wizard-core/templates/summary-minimal.md +1 -0
  169. package/pan-wizard-core/templates/summary-standard.md +2 -1
  170. package/pan-wizard-core/templates/summary.md +18 -2
  171. package/pan-wizard-core/templates/uat.md +3 -3
  172. package/pan-wizard-core/templates/user-setup.md +8 -8
  173. package/pan-wizard-core/templates/verification-report.md +54 -4
  174. package/pan-wizard-core/workflows/add-phase.md +1 -1
  175. package/pan-wizard-core/workflows/assumptions.md +1 -1
  176. package/pan-wizard-core/workflows/discuss-phase.md +9 -8
  177. package/pan-wizard-core/workflows/exec-phase.md +34 -60
  178. package/pan-wizard-core/workflows/execute-plan.md +8 -7
  179. package/pan-wizard-core/workflows/health.md +13 -11
  180. package/pan-wizard-core/workflows/help.md +8 -9
  181. package/pan-wizard-core/workflows/learn.md +13 -11
  182. package/pan-wizard-core/workflows/map-codebase.md +10 -10
  183. package/pan-wizard-core/workflows/milestone-audit.md +10 -10
  184. package/pan-wizard-core/workflows/milestone-gaps.md +17 -24
  185. package/pan-wizard-core/workflows/milestone-new.md +7 -6
  186. package/pan-wizard-core/workflows/new-project.md +10 -10
  187. package/pan-wizard-core/workflows/optimize.md +11 -9
  188. package/pan-wizard-core/workflows/pause.md +6 -1
  189. package/pan-wizard-core/workflows/phase-tests.md +4 -4
  190. package/pan-wizard-core/workflows/plan-phase.md +31 -34
  191. package/pan-wizard-core/workflows/profile.md +2 -2
  192. package/pan-wizard-core/workflows/progress.md +5 -7
  193. package/pan-wizard-core/workflows/quick.md +10 -8
  194. package/pan-wizard-core/workflows/remove-phase.md +1 -1
  195. package/pan-wizard-core/workflows/research-phase.md +9 -5
  196. package/pan-wizard-core/workflows/resume-project.md +4 -6
  197. package/pan-wizard-core/workflows/retro.md +3 -1
  198. package/pan-wizard-core/workflows/settings.md +16 -24
  199. package/pan-wizard-core/workflows/todo-check.md +1 -1
  200. package/pan-wizard-core/workflows/transition.md +12 -12
  201. package/pan-wizard-core/workflows/update.md +25 -17
  202. package/pan-wizard-core/workflows/verify-phase.md +24 -17
  203. package/pan-zcode/KNOWN-BETA-RISKS.md +2 -2
  204. package/pan-zcode/README.md +7 -7
  205. package/pan-zcode/bin/install-zcode.js +1 -1
  206. package/scripts/build-agent-plugin.js +19 -5
  207. package/scripts/build-plugin.js +3 -1
  208. package/scripts/generate-skills-docs.py +13 -4
  209. package/scripts/test-surface.cjs +4 -0
@@ -438,7 +438,7 @@ function convertClaudeCommandToCodexSkill(content, skillName) {
438
438
  description = toSingleLine(description);
439
439
  const shortDescription = description.length > 180 ? `${description.slice(0, 177)}...` : description;
440
440
  const adapter = getCodexSkillAdapterHeader(skillName);
441
- return `---\nname: ${yamlQuote(skillName)}\ndescription: ${yamlQuote(description)}\nmetadata:\n short-description: ${yamlQuote(shortDescription)}\n---\n\n${adapter}\n\n${body.trimStart()}`;
441
+ return `---\nname: ${yamlQuote(skillName)}\ndescription: ${yamlQuote(description)}\nlicense: ${yamlQuote(SKILL_LICENSE)}\nmetadata:\n short-description: ${yamlQuote(shortDescription)}\n---\n\n${adapter}\n\n${body.trimStart()}`;
442
442
  }
443
443
 
444
444
  /**
@@ -474,6 +474,12 @@ ${note ? `\n${note}\n` : ''}</pan_skill_adapter>`;
474
474
  * for environment requirements (max 500 chars). Kept short and factual: these
475
475
  * are the two things a host cannot infer and that every PAN skill depends on.
476
476
  */
477
+ // The Agent Skills `license` field: optional in the spec, and GitHub's validator
478
+ // (`gh skill publish --dry-run`) warns on every skill without it (market-ideas M30).
479
+ // Read from package.json so the skills always carry the package's own licence.
480
+ const SKILL_LICENSE = (() => {
481
+ try { return require('../package.json').license || 'MIT'; } catch { return 'MIT'; }
482
+ })();
477
483
  const SKILL_COMPATIBILITY = 'Requires Node.js (skills invoke the bundled pan-tools CLI) and a project with a .planning/ directory, created by /pan-new-project or /pan-map-codebase.';
478
484
 
479
485
  function convertClaudeCommandToUnifiedSkill(content, skillName, opts = {}) {
@@ -500,7 +506,7 @@ function convertClaudeCommandToUnifiedSkill(content, skillName, opts = {}) {
500
506
  // Deliberately NOT emitting `allowed-tools`: it is marked experimental in the
501
507
  // spec, and ADR-0028's frontmatter rule is that anything unverified stays out
502
508
  // until a live per-runtime check confirms no parser rejects it.
503
- return `---\nname: ${yamlQuote(skillName)}\ndescription: ${yamlQuote(description)}\ncompatibility: ${yamlQuote(SKILL_COMPATIBILITY)}\nmetadata:\n short-description: ${yamlQuote(shortDescription)}\n---\n\n${adapter}\n\n${body.trimStart()}`;
509
+ return `---\nname: ${yamlQuote(skillName)}\ndescription: ${yamlQuote(description)}\nlicense: ${yamlQuote(SKILL_LICENSE)}\ncompatibility: ${yamlQuote(SKILL_COMPATIBILITY)}\nmetadata:\n short-description: ${yamlQuote(shortDescription)}\n---\n\n${adapter}\n\n${body.trimStart()}`;
504
510
  }
505
511
 
506
512
  // ─── Unified-skill content rewrites (extracted from bin/install.js, 2026-09) ──
@@ -558,6 +564,8 @@ function rewriteSharedCoreMarkdown(content, { corePrefix, pathPrefix, projectDir
558
564
  content = content.replace(/\.\/\.claude\/agents\//g, `${corePrefix}pan-wizard-core/agents/`);
559
565
  content = content.replace(/~\/\.claude\//g, pathPrefix);
560
566
  content = content.replace(/\.\/\.claude\//g, projectDirPrefix);
567
+ // No `pan-tools` bin is on PATH: invoke it via node, as the skill rewrite does.
568
+ content = content.replace(/\bpan-tools\b(?=\s+[a-z])/g, `node ${corePrefix}pan-wizard-core/bin/pan-tools.cjs`);
561
569
  content = processAttribution(content, attribution);
562
570
  return convertSlashCommandsToCopilotSkillMentions(content);
563
571
  }
@@ -567,9 +575,29 @@ function rewriteSharedCoreMarkdown(content, { corePrefix, pathPrefix, projectDir
567
575
  * under `<shared core>/agents/` — reading material for agents, not a runtime
568
576
  * registration (ADR-0028 agent-ref canonicalization).
569
577
  */
578
+ /**
579
+ * Name the agent file this runtime actually installs. A prompt that says "read
580
+ * ~/.claude/agents/pan-planner.md for your role" becomes `<prefix>agents/pan-planner.md`
581
+ * after the prefix rewrite, but Codex installs agents as `pan-planner.toml` and Copilot
582
+ * as `pan-planner.agent.md`, so the spawned agent read nothing and ran with no role.
583
+ * Idempotent; other runtimes keep `.md`.
584
+ * @param {string} content
585
+ * @param {string} runtime - claude | codex | gemini | opencode | copilot
586
+ * @param {string} pathPrefix - the runtime's rewritten config-dir prefix (ends in `/`)
587
+ * @returns {string}
588
+ */
589
+ function rewriteAgentFileRefs(content, runtime, pathPrefix) {
590
+ const ext = runtime === 'codex' ? '.toml' : runtime === 'copilot' ? '.agent.md' : null;
591
+ if (!ext || !pathPrefix) return content;
592
+ const prefix = pathPrefix.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
593
+ return content.replace(new RegExp(`(${prefix}agents/pan-[a-z0-9_-]+)\\.md\\b`, 'g'), (_, base) => base + ext);
594
+ }
595
+
570
596
  function rewriteAgentReferenceCopy(content, corePrefix) {
571
597
  content = content.replace(/~\/\.claude\/pan-wizard-core\//g, `${corePrefix}pan-wizard-core/`);
572
598
  content = content.replace(/\.\/\.claude\/pan-wizard-core\//g, `${corePrefix}pan-wizard-core/`);
599
+ // No `pan-tools` bin is on PATH: invoke it via node, as the skill rewrite does.
600
+ content = content.replace(/\bpan-tools\b(?=\s+[a-z])/g, `node ${corePrefix}pan-wizard-core/bin/pan-tools.cjs`);
573
601
  return convertSlashCommandsToCopilotSkillMentions(content);
574
602
  }
575
603
 
@@ -707,6 +735,65 @@ function agentPluginSkillAdapterNote(kind = 'skill') {
707
735
  - \`${AGENT_PLUGIN_RUNTIME_HOME_TOKEN}\` is your runtime's user-level configuration directory (for example \`~/.claude\`, \`~/.codex\`, \`~/.gemini\`, \`~/.config/opencode\`, \`~/.copilot\`) and \`${AGENT_PLUGIN_RUNTIME_DIR_TOKEN}\` its project-level directory (\`.claude\`, \`.codex\`, \`.gemini\`, \`.opencode\`, \`.github\`). Substitute the one that applies to the runtime you are.`;
708
736
  }
709
737
 
738
+ // ─── Agent Plugin skills over Codex's prompt cut (market-ideas S8) ──────────
739
+ // Codex injects at most the first 8,000 bytes of a skill that came from an Agent
740
+ // Plugin (codex-rs/ext/skills/src/render.rs `MAX_SKILL_PROMPT_BYTES`, host_prompt.rs).
741
+ // Measured on Codex CLI 0.157.1 on 2026-10-03: a 56,540-byte PAN skill reached the
742
+ // model as its first 8,000 bytes, cut mid-sentence; the same skill from an install's
743
+ // .agents/skills/ arrived whole, so only the bundle is affected. A bundle skill over
744
+ // the cut ships as a short SKILL.md that keeps its frontmatter and adapter block and
745
+ // points at the full instructions in references/instructions.md, the Agent Skills
746
+ // directory for files a skill loads on demand.
747
+ const AGENT_PLUGIN_SKILL_MAX_BYTES = 8000;
748
+ const AGENT_PLUGIN_SKILL_REFERENCE = 'references/instructions.md';
749
+ const SKILL_SUMMARY_MIN_CHARS = 40;
750
+
751
+ /**
752
+ * Split a converted bundle skill that is longer than Codex loads into a pointer
753
+ * SKILL.md and a reference file. Pure. A skill within the limit comes back as is.
754
+ * @param {string} skillMd - the unified converter's output (frontmatter + adapter + body)
755
+ * @param {string} skillName
756
+ * @param {number} [maxBytes]
757
+ * @returns {{skill: string, reference: string|null}}
758
+ */
759
+ function splitOversizedSkill(skillMd, skillName, maxBytes = AGENT_PLUGIN_SKILL_MAX_BYTES) {
760
+ if (Buffer.byteLength(skillMd, 'utf8') <= maxBytes) return { skill: skillMd, reference: null };
761
+ const close = '</pan_skill_adapter>';
762
+ const end = skillMd.indexOf(close);
763
+ if (end === -1) throw new Error(`splitOversizedSkill: ${skillName} has no adapter block to keep`);
764
+ const head = skillMd.slice(0, end + close.length);
765
+ const body = skillMd.slice(end + close.length).replace(/^\s+/, '');
766
+ const pointer = (summary) => [
767
+ head,
768
+ '',
769
+ '## Instructions',
770
+ '',
771
+ `This skill's full instructions are in \`${AGENT_PLUGIN_SKILL_REFERENCE}\`, in the same directory as this SKILL.md: they are longer than some runtimes load into a prompt. Before you do anything else, read that file in full with your file-reading tool, then follow it exactly. It is the complete procedure, and nothing on this page replaces it.`,
772
+ ...(summary ? ['', '## What this skill does', '', summary] : []),
773
+ '',
774
+ ].join('\n');
775
+ // The command's own <objective> is the summary, else its first plain prose
776
+ // paragraph; trimmed at word boundaries until the page fits, and dropped when
777
+ // too little of it would be left.
778
+ let summary = ((body.match(/<objective>\s*([\s\S]*?)\s*<\/objective>/) || [])[1] || '').trim();
779
+ if (!summary) {
780
+ summary = body.split(/\n\s*\n/).map((p) => p.trim())
781
+ .find((p) => p.length >= SKILL_SUMMARY_MIN_CHARS && !/^[#<`|>\-*]/.test(p) && !/^\d+\./.test(p)) || '';
782
+ }
783
+ let skill = pointer(summary);
784
+ while (summary && Buffer.byteLength(skill, 'utf8') > maxBytes) {
785
+ summary = summary.slice(0, Math.floor(summary.length * 0.8)).replace(/\s+\S*$/, '');
786
+ if (summary.length < SKILL_SUMMARY_MIN_CHARS) summary = '';
787
+ else summary += ' …';
788
+ skill = pointer(summary);
789
+ }
790
+ if (Buffer.byteLength(skill, 'utf8') > maxBytes) {
791
+ throw new Error(`splitOversizedSkill: ${skillName}'s frontmatter and adapter alone exceed ${maxBytes} bytes`);
792
+ }
793
+ const reference = `# ${skillName}: full instructions\n\nRead from \`${skillName}/SKILL.md\`, whose adapter block defines \`${AGENT_PLUGIN_ROOT_TOKEN}\`, \`{{PAN_ARGS}}\` and the runtime-directory tokens used below.\n\n${body}`;
794
+ return { skill, reference };
795
+ }
796
+
710
797
  /** Generate Copilot CLI skill adapter header */
711
798
  function getCopilotSkillAdapterHeader(skillName) {
712
799
  const invocation = `/pan-${skillName.replace(/^pan-/, '')}`;
@@ -743,7 +830,7 @@ function convertClaudeCommandToCopilotSkill(content, skillName) {
743
830
  description = toSingleLine(description);
744
831
  const shortDescription = description.length > 180 ? `${description.slice(0, 177)}...` : description;
745
832
  const adapter = getCopilotSkillAdapterHeader(skillName);
746
- return `---\nname: ${yamlQuote(skillName)}\ndescription: ${yamlQuote(description)}\nmetadata:\n short-description: ${yamlQuote(shortDescription)}\n---\n\n${adapter}\n\n${body.trimStart()}`;
833
+ return `---\nname: ${yamlQuote(skillName)}\ndescription: ${yamlQuote(description)}\nlicense: ${yamlQuote(SKILL_LICENSE)}\nmetadata:\n short-description: ${yamlQuote(shortDescription)}\n---\n\n${adapter}\n\n${body.trimStart()}`;
747
834
  }
748
835
 
749
836
  /** Claude agent → Copilot .agent.md */
@@ -838,6 +925,31 @@ function convertClaudeToCopilotAgent(content, opts = {}) {
838
925
 
839
926
  // ─── Attribution Processing ─────────────────────────────────────────────────
840
927
 
928
+ /**
929
+ * The commit attribution Claude Code's settings ask for, in processAttribution's
930
+ * terms: undefined keeps PAN's default `Co-Authored-By` line, null removes it, a
931
+ * string replaces it. Claude Code's settings reference (read 2026-10-03):
932
+ * `attribution: false` hides all attribution (2.1.281 and later); an empty
933
+ * `attribution.commit` hides the commit trailer and a non-empty one replaces it; once
934
+ * `commit` or `pr` is set the deprecated `includeCoAuthoredBy` is ignored, and before
935
+ * that `includeCoAuthoredBy: false` still hides the trailer. Until 2026-10-03 PAN
936
+ * read only `attribution.commit`, so `false` (an explicit request to hide) kept the
937
+ * default line.
938
+ * @param {object|null} settings - a parsed Claude settings.json
939
+ * @returns {string|null|undefined}
940
+ */
941
+ function commitAttributionFromSettings(settings) {
942
+ const s = settings && typeof settings === 'object' ? settings : {};
943
+ const a = s.attribution;
944
+ if (a === false) return null;
945
+ if (a && typeof a === 'object') {
946
+ if (typeof a.commit === 'string') return a.commit === '' ? null : a.commit;
947
+ if (a.pr !== undefined) return undefined; // attribution set: includeCoAuthoredBy no longer applies
948
+ }
949
+ if (s.includeCoAuthoredBy === false) return null;
950
+ return undefined;
951
+ }
952
+
841
953
  /**
842
954
  * Process Co-Authored-By lines based on attribution setting.
843
955
  * @param {string} content - File content
@@ -1042,16 +1154,25 @@ function stripThinkingFrontmatter(content, runtime) {
1042
1154
  * @param {Object} commands
1043
1155
  * @param {string} commands.updateCheckCommand - node invocation for pan-check-update.js
1044
1156
  * @param {string} commands.contextMonitorCommand - node invocation for pan-context-monitor.js
1157
+ * @param {string} [commands.stateReinjectMarkCommand] - pan-state-reinject.js `--mark` (preCompact)
1158
+ * @param {string} [commands.stateReinjectInjectCommand] - pan-state-reinject.js `--inject copilot` (postToolUse)
1045
1159
  * @returns {Object} A `.github/hooks/pan.json` config object
1046
1160
  */
1047
1161
  function buildCopilotHooksConfig(commands) {
1048
- const { updateCheckCommand, contextMonitorCommand, costLoggerCommand, traceLoggerCommand, stopGuardCommand } = commands || {};
1162
+ const { updateCheckCommand, contextMonitorCommand, costLoggerCommand, traceLoggerCommand, stopGuardCommand,
1163
+ stateReinjectMarkCommand, stateReinjectInjectCommand } = commands || {};
1049
1164
  const config = { version: 1, hooks: {} };
1050
1165
  if (updateCheckCommand) {
1051
1166
  config.hooks.sessionStart = [{ type: 'command', command: updateCheckCommand }];
1052
1167
  }
1053
- if (contextMonitorCommand) {
1054
- config.hooks.postToolUse = [{ type: 'command', command: contextMonitorCommand }];
1168
+ const postToolUse = [];
1169
+ if (contextMonitorCommand) postToolUse.push({ type: 'command', command: contextMonitorCommand });
1170
+ // State re-injection after a compaction (M33): preCompact leaves the marker, the
1171
+ // next postToolUse returns the block once (hooks/pan-state-reinject.js).
1172
+ if (stateReinjectInjectCommand) postToolUse.push({ type: 'command', command: stateReinjectInjectCommand });
1173
+ if (postToolUse.length > 0) config.hooks.postToolUse = postToolUse;
1174
+ if (stateReinjectMarkCommand) {
1175
+ config.hooks.preCompact = [{ type: 'command', command: stateReinjectMarkCommand }];
1055
1176
  }
1056
1177
  // subagentStop is Copilot's SubagentStop equivalent (verified docs.github.com
1057
1178
  // 2026-06) — carries the cost + trace loggers, same as Claude and Codex (Gemini registers neither).
@@ -1107,15 +1228,20 @@ function buildCopilotHooksConfig(commands) {
1107
1228
  * `compact` is where the state re-injection hook registers (M10): SessionStart with
1108
1229
  * the `compact` matcher (HOOK_SLOT_MATCHERS), the one event after a compaction whose
1109
1230
  * `hookSpecificOutput.additionalContext` reaches the model on Claude Code and Codex —
1110
- * both hosts' PostCompact discard it (hooks references, read 2026-09-26). Gemini's
1111
- * PreCompress fires before the summary and Copilot's preCompact is notification-only,
1112
- * so neither gets it.
1231
+ * both hosts' PostCompact discard it (hooks references, read 2026-09-26).
1232
+ *
1233
+ * `compactMark` / `compactInject` (market-ideas M33) carry it where no start event
1234
+ * follows a compaction. Gemini's PreCompress and Copilot's preCompact are advisory,
1235
+ * so the hook runs in two steps: a marker before the compaction, the block on the
1236
+ * next tool result, the first point after it where both hosts take added context
1237
+ * (gemini-cli v0.61.0 docs/hooks/reference.md; Copilot hooks reference; read
1238
+ * 2026-10-03, and live on Copilot CLI 1.0.88 the same day).
1113
1239
  */
1114
1240
  const HOOK_EVENT_MAP = Object.freeze({
1115
- claude: { surface: 'settings.json', sessionStart: 'SessionStart', postToolUse: 'PostToolUse', subagentStop: 'SubagentStop', stop: 'Stop', compact: 'SessionStart' },
1116
- gemini: { surface: 'settings.json', sessionStart: 'SessionStart', postToolUse: null, subagentStop: null, stop: 'AfterAgent', compact: null },
1117
- codex: { surface: 'hooks.json', sessionStart: 'SessionStart', postToolUse: 'PostToolUse', subagentStop: 'SubagentStop', stop: 'Stop', compact: 'SessionStart' },
1118
- copilot: { surface: 'hooks/pan.json', sessionStart: 'sessionStart', postToolUse: 'postToolUse', subagentStop: 'subagentStop', stop: 'agentStop', compact: null },
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' },
1119
1245
  opencode: null,
1120
1246
  });
1121
1247
 
@@ -1140,14 +1266,16 @@ const PAN_SETTINGS_HOOKS = Object.freeze(['pan-check-update', 'pan-context-monit
1140
1266
  *
1141
1267
  * @param {object} hooks - settings.hooks
1142
1268
  * @param {string[]} hookNames - script basenames without `.js` (e.g. 'pan-stop-guard')
1143
- * @param {string|null} [keepEvent] - the event the hook now belongs to, left alone
1269
+ * @param {string|string[]|null} [keepEvent] - the event (or events: a hook such as
1270
+ * pan-state-reinject registers under two on Gemini) the hook now belongs to, left alone
1144
1271
  * @returns {string[]}
1145
1272
  */
1146
1273
  function stripPanHookEntries(hooks, hookNames, keepEvent = null) {
1147
1274
  const touched = [];
1148
1275
  if (!hooks || typeof hooks !== 'object' || Array.isArray(hooks)) return touched;
1276
+ const keep = new Set([].concat(keepEvent || []));
1149
1277
  for (const event of Object.keys(hooks)) {
1150
- if (event === keepEvent || !Array.isArray(hooks[event])) continue;
1278
+ if (keep.has(event) || !Array.isArray(hooks[event])) continue;
1151
1279
  const before = hooks[event].length;
1152
1280
  hooks[event] = hooks[event].filter(entry => !(entry && Array.isArray(entry.hooks)
1153
1281
  && entry.hooks.some(h => h && typeof h.command === 'string' && hookNames.some(n => h.command.includes(n)))));
@@ -1548,6 +1676,12 @@ function detectModelCapabilities(modelName) {
1548
1676
  if (n.includes('sonnet-5')) {
1549
1677
  return { has_1m_ctx: true, has_thinking: true, has_cache: true, tier: 'mid' };
1550
1678
  }
1679
+ // Haiku 5 family (Haiku 5.5): 1M context and adaptive thinking (pricing page and
1680
+ // Claude Code 2.1.293 changelog, read 2026-10-10). It is a forward release, so
1681
+ // without this it took the fallback's Haiku profile, which is Haiku 4's.
1682
+ if (n.includes('haiku-5')) {
1683
+ return { has_1m_ctx: true, has_thinking: true, has_cache: true, tier: 'fast' };
1684
+ }
1551
1685
  if (n.includes('opus-4-8') || n.includes('opus-4.8')
1552
1686
  || n.includes('opus-4-7') || n.includes('opus-4.7')
1553
1687
  || n.includes('opus-4-6') || n.includes('opus-4.6')) {
@@ -1567,10 +1701,9 @@ function detectModelCapabilities(modelName) {
1567
1701
  if (n.includes('sonnet-4') && !isForwardRelease) {
1568
1702
  return { has_1m_ctx: false, has_thinking: true, has_cache: true, tier: 'mid' };
1569
1703
  }
1570
- // Haiku 4.x — fast tier, no extended thinking. Guarded for the same reason,
1571
- // though today it is behavior-neutral: the fallback's Haiku profile is
1572
- // identical to this one, so a forward Haiku release lands on the same answer
1573
- // either way. The guard is here so that stops being true only deliberately.
1704
+ // Haiku 4.x — fast tier, no extended thinking. Guarded for the same reason: a
1705
+ // Haiku release newer than 4.5 takes the fallback's modern Haiku profile, which
1706
+ // since Haiku 5.5 has 1M context and thinking.
1574
1707
  if ((n.includes('haiku-4-5') || n.includes('haiku-4.5') || n.includes('haiku-4')) && !isForwardRelease) {
1575
1708
  return { has_1m_ctx: false, has_thinking: false, has_cache: true, tier: 'fast' };
1576
1709
  }
@@ -1643,7 +1776,8 @@ function detectModelCapabilities(modelName) {
1643
1776
  const MODERN = {
1644
1777
  opus: { has_1m_ctx: true, has_thinking: true, has_cache: true, tier: 'reasoning' },
1645
1778
  sonnet: { has_1m_ctx: true, has_thinking: true, has_cache: true, tier: 'mid' },
1646
- haiku: { has_1m_ctx: false, has_thinking: false, has_cache: true, tier: 'fast' },
1779
+ // Haiku 5.5 (the branch above) is the newest Haiku with known capabilities.
1780
+ haiku: { has_1m_ctx: true, has_thinking: true, has_cache: true, tier: 'fast' },
1647
1781
  };
1648
1782
  return { ...MODERN[family] };
1649
1783
  }
@@ -1719,6 +1853,49 @@ function verifyInstall(configDir, manifest) {
1719
1853
  return { ok: missing.length === 0 && empty.length === 0, missing, empty, warnings };
1720
1854
  }
1721
1855
 
1856
+ /**
1857
+ * The script a PAN hook command runs, as written in the command: `node "<path>"`
1858
+ * (global installs, buildHookCommand) or `node <dir>/hooks/<hook>.js` (local
1859
+ * installs, relative to the project root). Null for any other command shape.
1860
+ * @param {string} command
1861
+ * @returns {string|null}
1862
+ */
1863
+ function hookCommandScript(command) {
1864
+ // Plain arguments may follow the script (`--mark`, `--inject gemini`, M33).
1865
+ const m = /^node\s+(?:"([^"]+)"|([^\s"]+))(?:\s+[^\s"]+)*$/.exec(String(command || '').trim());
1866
+ return m ? (m[1] || m[2]) : null;
1867
+ }
1868
+
1869
+ /**
1870
+ * Check that every hook command an install is about to register runs a script
1871
+ * that exists and has content (market-ideas M24). Read-only: nothing is executed
1872
+ * or written. A dev install from a clone without `npm run build:hooks` copied no
1873
+ * hook scripts and registered every hook anyway, so each session event ran a
1874
+ * missing file. A relative script resolves against `projectRoot`, the directory a
1875
+ * local install's hook commands run from.
1876
+ *
1877
+ * @param {string[]} commands - the hook commands to register
1878
+ * @param {string} projectRoot
1879
+ * @returns {{ok: boolean, missing: string[]}} each missing entry names the script and why
1880
+ */
1881
+ function verifyHookEntrypoints(commands, projectRoot) {
1882
+ const missing = [];
1883
+ for (const command of commands) {
1884
+ const script = hookCommandScript(command);
1885
+ if (!script) {
1886
+ missing.push(`${command} (not a "node <script>" command)`);
1887
+ continue;
1888
+ }
1889
+ const abs = path_v.isAbsolute(script) ? script : path_v.join(projectRoot, script);
1890
+ try {
1891
+ if (fs_v.statSync(abs).size === 0) missing.push(`${script} (empty)`);
1892
+ } catch {
1893
+ missing.push(`${script} (missing)`);
1894
+ }
1895
+ }
1896
+ return { ok: missing.length === 0, missing };
1897
+ }
1898
+
1722
1899
  // ─── Claude Code plugin packaging (2026-06) ─────────────────────────────────
1723
1900
 
1724
1901
  /**
@@ -1886,11 +2063,11 @@ so the result is greppable out of the transcript.
1886
2063
  /**
1887
2064
  * Build the plugin's MCP registration (`.mcp.json` at the plugin root).
1888
2065
  *
1889
- * Plugins may declare MCP servers in a plugin-root `.mcp.json`, and unlike
1890
- * command markdown — where `${CLAUDE_PLUGIN_ROOT}` expansion is unverified and
1891
- * is why marketplace publishing is still gated — hook and MCP *configs* are the
1892
- * documented place the variable is substituted. So the same form
1893
- * `buildPluginHooksConfig()` relies on is correct here.
2066
+ * Plugins may declare MCP servers in a plugin-root `.mcp.json`. Hook and MCP
2067
+ * *configs* are the documented place `${CLAUDE_PLUGIN_ROOT}` is substituted (it
2068
+ * expands in plugin command markdown too: measured 2026-08-14, which lifted the
2069
+ * marketplace-publishing gate). So the same form `buildPluginHooksConfig()` relies
2070
+ * on is correct here.
1894
2071
  *
1895
2072
  * No `env` block: a plugin serves whatever project the session is in, so pinning
1896
2073
  * PAN_PROJECT_ROOT would be wrong, and the server resolves its engine from its
@@ -1948,7 +2125,7 @@ function buildNativeWorkflowScripts() {
1948
2125
  const reviewPipeline = `export const meta = {
1949
2126
  name: 'pan-review-pipeline',
1950
2127
  description: 'PAN deep review: reviewer + hardener fan-out, meta-reviewer merge',
1951
- whenToUse: 'Deterministic version of the /pan-review-deep fan-out. Pass the phase number or a description of the change set as args.',
2128
+ whenToUse: 'Deterministic version of the /pan:review-deep fan-out. Pass the phase number or a description of the change set as args.',
1952
2129
  phases: [
1953
2130
  { title: 'Find', detail: 'reviewer + security hardener in parallel' },
1954
2131
  { title: 'Merge', detail: 'meta-reviewer dedupes, disputes, and issues the verdict' },
@@ -2013,7 +2190,7 @@ return merged
2013
2190
  const mapCodebase = `export const meta = {
2014
2191
  name: 'pan-map-codebase',
2015
2192
  description: 'PAN codebase mapping: shard fan-out per top-level area, then synthesis',
2016
- whenToUse: 'Deterministic version of the /pan-map-codebase shard pattern for repositories too large for a single pass.',
2193
+ whenToUse: 'Deterministic version of the /pan:map-codebase shard pattern for repositories too large for a single pass.',
2017
2194
  phases: [
2018
2195
  { title: 'Scan', detail: 'discover top-level areas worth documenting' },
2019
2196
  { title: 'Map', detail: 'one documenter per area, in parallel' },
@@ -2074,7 +2251,7 @@ return { areas_mapped: maps.filter(Boolean).length, synthesis }
2074
2251
  const execWaves = `export const meta = {
2075
2252
  name: 'pan-exec-waves',
2076
2253
  description: 'PAN phase execution: wave-grouped executor fan-out for a checkpoint-free phase, then verification',
2077
- whenToUse: 'Deterministic version of the /pan-exec-phase wave dispatch. Pass the phase number as args. Refuses a phase that contains checkpoint plans (a workflow cannot pause for a human) — run /pan-exec-phase for those.',
2254
+ whenToUse: 'Deterministic version of the /pan:exec-phase wave dispatch. Pass the phase number as args. Refuses a phase that contains checkpoint plans (a workflow cannot pause for a human) — run /pan:exec-phase for those.',
2078
2255
  phases: [
2079
2256
  { title: 'Index', detail: 'plan inventory with wave grouping, from the PAN engine' },
2080
2257
  { title: 'Execute', detail: 'one executor per plan; waves in order, plans within a wave in parallel' },
@@ -2100,6 +2277,7 @@ const INDEX = {
2100
2277
  phase_name: { type: 'string' },
2101
2278
  phase_dir: { type: 'string' },
2102
2279
  parallelization: { type: 'boolean' },
2280
+ verifier_enabled: { type: 'boolean' },
2103
2281
  has_checkpoints: { type: 'boolean' },
2104
2282
  plans: {
2105
2283
  type: 'array',
@@ -2120,11 +2298,11 @@ const INDEX = {
2120
2298
  required: ['phase_found', 'has_checkpoints', 'plans'],
2121
2299
  }
2122
2300
  const index = await agent(
2123
- 'Index phase ' + phaseArg + ' for execution using the ' + PAN_TOOLS + ' Run two verbs and merge their JSON: (1) init execute-phase ' + phaseArg + ' — take phase_found, phase_number, phase_name, phase_dir, parallelization; (2) phase-plan-index ' + phaseArg + ' — take has_checkpoints and plans[] (id, wave, autonomous, has_summary, objective; include each plan file path as file). Do not execute anything; return only the merged index.',
2301
+ 'Index phase ' + phaseArg + ' for execution using the ' + PAN_TOOLS + ' Run two verbs and merge their JSON: (1) init execute-phase ' + phaseArg + ' — take phase_found, phase_number, phase_name, phase_dir, parallelization, verifier_enabled; (2) phase-plan-index ' + phaseArg + ' — take has_checkpoints and plans[] (id, wave, autonomous, has_summary, objective; include each plan file path as file). Do not execute anything; return only the merged index.',
2124
2302
  { label: 'index', phase: 'Index', schema: INDEX })
2125
2303
  if (!index || !index.phase_found) return { error: 'phase ' + phaseArg + ' not found' }
2126
2304
  if (index.has_checkpoints) {
2127
- return { error: 'phase ' + phaseArg + ' contains checkpoint plans (autonomous: false). A workflow cannot pause for a human — run /pan-exec-phase ' + phaseArg + ' instead.', plans: index.plans.map(p => p.id) }
2305
+ return { error: 'phase ' + phaseArg + ' contains checkpoint plans (autonomous: false). A workflow cannot pause for a human — run /pan:exec-phase ' + phaseArg + ' instead.', plans: index.plans.map(p => p.id) }
2128
2306
  }
2129
2307
  const pending = index.plans.filter(p => !p.has_summary)
2130
2308
  if (pending.length === 0) return { phase: phaseArg, done: true, message: 'every plan already has a summary — nothing to execute' }
@@ -2148,7 +2326,7 @@ const EXEC_RESULT = {
2148
2326
  const executorPrompt = (p) =>
2149
2327
  'Execute plan ' + p.id + ' of phase ' + (index.phase_number || phaseArg) + (index.phase_name ? '-' + index.phase_name : '') + '. Commit each task atomically. Create summary.md. Update state.md and roadmap.md (via roadmap update-plan-progress).\\n\\n'
2150
2328
  + 'Read first, in this order: ' + CORE_DOCS + '\\n\\n'
2151
- + 'Then read: ' + (p.file || (index.phase_dir + '/' + p.id)) + ' (the plan), .planning/state.md, .planning/config.json (if present), ./CLAUDE.md (if present — follow its conventions), .agents/skills/ (if present — follow relevant skills), and every .planning/memory/*.md (apply every rule without exception).\\n\\n'
2329
+ + 'Then read: ' + (p.file || (index.phase_dir + '/' + p.id)) + ' (the plan), .planning/state.md, .planning/config.json (if present), ./CLAUDE.md (if present — follow its conventions), and .agents/skills/ (if present — follow relevant skills).\\n\\n'
2152
2330
  + 'Report plan_id, status (complete | failed | checkpoint), summary_path, the number of commits you made, and self_check (passed if your summary carries no "Self-Check: FAILED" marker).'
2153
2331
  const executed = []
2154
2332
  let halted = null
@@ -2177,7 +2355,11 @@ for (const w of waveNumbers) {
2177
2355
  }
2178
2356
  }
2179
2357
  if (halted) {
2180
- return { phase: phaseArg, halted, executed, next: 'Inspect the failed plan(s), then re-run /pan-exec-waves ' + phaseArg + ' (completed plans are skipped) or fall back to /pan-exec-phase ' + phaseArg }
2358
+ return { phase: phaseArg, halted, executed, next: 'Inspect the failed plan(s), then re-run /pan-exec-waves ' + phaseArg + ' (completed plans are skipped) or fall back to /pan:exec-phase ' + phaseArg }
2359
+ }
2360
+ // workflow.verifier: false (/pan:settings) turns the phase verifier off, as in exec-phase.
2361
+ if (index.verifier_enabled === false) {
2362
+ return { phase: phaseArg, waves_run: waveNumbers.length, plans_complete: executed.length, verification: null, next: 'Verification skipped: workflow.verifier is off. /pan:verify-phase ' + phaseArg + ' verifies on demand; then continue with /pan:exec-phase ' + phaseArg + ' (transition)' }
2181
2363
  }
2182
2364
 
2183
2365
  phase('Verify')
@@ -2195,13 +2377,13 @@ const verdict = await agent(
2195
2377
  '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.',
2196
2378
  { agentType: 'pan-verifier', label: 'verify', phase: 'Verify', schema: VERIFY })
2197
2379
 
2198
- return { phase: phaseArg, waves_run: waveNumbers.length, plans_complete: executed.length, verification: verdict, next: 'Review the verification, then continue with /pan-exec-phase ' + phaseArg + ' (transition) or /pan-plan-phase for the next phase' }
2380
+ return { phase: phaseArg, waves_run: waveNumbers.length, plans_complete: executed.length, verification: verdict, next: 'Review the verification, then continue with /pan:exec-phase ' + phaseArg + ' (transition) or /pan:plan-phase for the next phase' }
2199
2381
  `;
2200
2382
 
2201
2383
  const diagnoseIssues = `export const meta = {
2202
2384
  name: 'pan-diagnose-issues',
2203
2385
  description: 'PAN UAT diagnosis: one debugger per failed UAT truth, in parallel, then root causes written back',
2204
- whenToUse: 'Deterministic version of /pan-diagnose-issues. Pass the phase number as args. Investigates only — fixes come from /pan-plan-phase --gaps.',
2386
+ whenToUse: 'Deterministic version of the diagnose-issues workflow. Pass the phase number as args. Investigates only — fixes come from /pan:plan-phase --gaps.',
2205
2387
  phases: [
2206
2388
  { title: 'Gaps', detail: 'read the phase UAT file and list the failed truths' },
2207
2389
  { title: 'Diagnose', detail: 'one pan-debugger per gap, in parallel, root cause only' },
@@ -2255,6 +2437,7 @@ const DIAGNOSIS = {
2255
2437
  issue_id: { type: 'string' },
2256
2438
  status: { type: 'string', enum: ['root_cause_found', 'inconclusive'] },
2257
2439
  root_cause: { type: 'string' },
2440
+ reproduction: { type: 'string' },
2258
2441
  evidence: { type: 'array', items: { type: 'string' } },
2259
2442
  files: { type: 'array', items: { type: 'string' } },
2260
2443
  suggested_fix: { type: 'string' },
@@ -2264,9 +2447,9 @@ const DIAGNOSIS = {
2264
2447
  required: ['issue_id', 'status'],
2265
2448
  }
2266
2449
  const diagnoses = await parallel(gaps.map(g => () => agent(
2267
- 'Debug issue UAT-' + g.test_num + ' for phase ' + phaseArg + ' — root cause ONLY, do not fix (fixes come from /pan-plan-phase --gaps).\\n\\n'
2450
+ 'Debug issue UAT-' + g.test_num + ' for phase ' + phaseArg + ' — root cause ONLY, do not fix (fixes come from /pan:plan-phase --gaps).\\n\\n'
2268
2451
  + 'Symptoms (pre-filled from UAT, treat as given): expected: ' + (g.expected || g.truth) + '. actual: ' + (g.reason || 'not recorded') + '. reproduction: test ' + g.test_num + ' in ' + found.uat_path + '. severity: ' + g.severity + '.\\n\\n'
2269
- + 'Follow the PAN debugger protocol: create the debug session file under .planning/debug/ named from the issue, investigate autonomously (read code, form hypotheses, test them), and report issue_id, status (root_cause_found | inconclusive), root_cause with evidence, files involved, a suggested fix direction, and the debug session path. If inconclusive, list the remaining possibilities. Also read ' + found.uat_path + ' and .planning/state.md for context.',
2452
+ + 'Follow the PAN debugger protocol: create the debug session file under .planning/debug/ named from the issue, build and run a command that fails on the symptom before forming any hypothesis, investigate autonomously (read code, form hypotheses, test them), and report issue_id, status (root_cause_found | inconclusive), the reproduction command (or none, with what you tried), root_cause with evidence, files involved, a suggested fix direction, and the debug session path. If inconclusive, list the remaining possibilities. Also read ' + found.uat_path + ' and .planning/state.md for context.',
2270
2453
  { agentType: 'pan-debugger', label: 'debug:UAT-' + g.test_num, phase: 'Diagnose', schema: DIAGNOSIS })))
2271
2454
  const results = diagnoses.filter(Boolean)
2272
2455
  log(results.filter(r => r.status === 'root_cause_found').length + ' root cause(s) found, ' + results.filter(r => r.status === 'inconclusive').length + ' inconclusive')
@@ -2278,10 +2461,10 @@ const RECORDED = {
2278
2461
  required: ['uat_path', 'gaps_updated'],
2279
2462
  }
2280
2463
  const recorded = await agent(
2281
- 'Update the Gaps section of ' + found.uat_path + ' with these diagnoses, following the PAN diagnose-issues protocol: for each gap add root_cause, artifacts (the debug session path), the files involved, and the suggested fix direction; mark inconclusive ones as needing manual review with their remaining possibilities. Edit in place — do not rewrite unrelated content. Report the path and how many gaps you updated.\\n\\nDiagnoses:\\n' + JSON.stringify(results, null, 2),
2464
+ 'Update the Gaps section of ' + found.uat_path + ' with these diagnoses, following the PAN diagnose-issues protocol: for each gap add root_cause, artifacts (each file involved, with its issue), missing (the suggested fix direction) and debug_session (the debug session path); mark inconclusive ones as needing manual review with their remaining possibilities. Edit the Gaps section in place and set the frontmatter status to diagnosed (the --gaps mode of pan-planner reads UAT gaps only from a diagnosed file); do not rewrite other content. Report the path and how many gaps you updated.\\n\\nDiagnoses:\\n' + JSON.stringify(results, null, 2),
2282
2465
  { label: 'record', phase: 'Record', schema: RECORDED })
2283
2466
 
2284
- return { phase: phaseArg, uat_path: found.uat_path, gaps: gaps.length, root_causes_found: results.filter(r => r.status === 'root_cause_found').length, inconclusive: results.filter(r => r.status === 'inconclusive').length, recorded, next: 'Run /pan-plan-phase ' + phaseArg + ' --gaps to plan the fixes' }
2467
+ return { phase: phaseArg, uat_path: found.uat_path, gaps: gaps.length, root_causes_found: results.filter(r => r.status === 'root_cause_found').length, inconclusive: results.filter(r => r.status === 'inconclusive').length, recorded, next: 'Run /pan:plan-phase ' + phaseArg + ' --gaps to plan the fixes' }
2285
2468
  `;
2286
2469
 
2287
2470
  return [
@@ -2353,6 +2536,7 @@ module.exports = {
2353
2536
  convertClaudeToCopilotAgent,
2354
2537
  // Attribution
2355
2538
  processAttribution,
2539
+ commitAttributionFromSettings,
2356
2540
  // JSONC
2357
2541
  parseJsonc,
2358
2542
  // Opus 4.7 capabilities
@@ -2382,6 +2566,7 @@ module.exports = {
2382
2566
  rewriteUnifiedSkillCommandContent,
2383
2567
  rewriteSharedCoreMarkdown,
2384
2568
  rewriteAgentReferenceCopy,
2569
+ rewriteAgentFileRefs,
2385
2570
  stripInternalLearningsTopics,
2386
2571
  AGENT_PLUGINS_VERSION,
2387
2572
  AGENT_PLUGIN_MANIFEST_SCHEMA,
@@ -2392,6 +2577,9 @@ module.exports = {
2392
2577
  buildAgentPluginManifest,
2393
2578
  buildAgentPluginMcpConfig,
2394
2579
  agentPluginSkillAdapterNote,
2580
+ AGENT_PLUGIN_SKILL_MAX_BYTES,
2581
+ AGENT_PLUGIN_SKILL_REFERENCE,
2582
+ splitOversizedSkill,
2395
2583
  buildCopilotPluginHooksConfig,
2396
2584
  COPILOT_PLUGIN_NAMESPACE,
2397
2585
  buildPluginManifest,
@@ -2400,6 +2588,8 @@ module.exports = {
2400
2588
  buildPluginSelfTestCommand,
2401
2589
  // Install verification (v3.7.10)
2402
2590
  verifyInstall,
2591
+ hookCommandScript,
2592
+ verifyHookEntrypoints,
2403
2593
  // AGENTS.md universal rules layer (ADR-0028 Phase 3)
2404
2594
  buildAgentsMdSection,
2405
2595
  upsertAgentsMdSection,