pan-wizard 3.36.0 → 3.37.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 (314) hide show
  1. package/CHANGELOG.md +3645 -0
  2. package/README.md +36 -48
  3. package/agents/pan-conductor.md +2 -3
  4. package/agents/pan-counterfactual.md +1 -2
  5. package/agents/pan-debugger.md +14 -3
  6. package/agents/pan-design-checker.md +1 -2
  7. package/agents/pan-designer.md +1 -2
  8. package/agents/pan-document_code.md +1 -2
  9. package/agents/pan-hardener.md +2 -3
  10. package/agents/pan-integration-checker.md +1 -2
  11. package/agents/pan-knowledge.md +1 -2
  12. package/agents/pan-meta-reviewer.md +3 -3
  13. package/agents/pan-optimizer.md +4 -4
  14. package/agents/pan-phase-researcher.md +79 -488
  15. package/agents/pan-plan-checker.md +12 -2
  16. package/agents/pan-planner.md +10 -6
  17. package/agents/pan-previewer.md +1 -2
  18. package/agents/pan-project-researcher.md +70 -499
  19. package/agents/pan-release.md +1 -1
  20. package/agents/pan-reviewer.md +9 -3
  21. package/agents/pan-roadmapper.md +12 -7
  22. package/agents/pan-verifier.md +7 -2
  23. package/bin/install-lib.cjs +134 -14
  24. package/bin/install.js +59 -12
  25. package/commands/pan/army.md +1 -1
  26. package/commands/pan/autodev.md +76 -0
  27. package/commands/pan/exec-phase.md +3 -4
  28. package/commands/pan/focus-auto.md +14 -7
  29. package/commands/pan/focus-design.md +4 -5
  30. package/commands/pan/focus-docs.md +24 -0
  31. package/commands/pan/learn.md +13 -6
  32. package/commands/pan/milestone-audit.md +8 -1
  33. package/commands/pan/milestone-done.md +10 -5
  34. package/commands/pan/milestone-new.md +2 -2
  35. package/commands/pan/new-project.md +15 -12
  36. package/commands/pan/plan-phase.md +15 -9
  37. package/commands/pan/quick.md +1 -1
  38. package/commands/pan/review-deep.md +11 -5
  39. package/commands/pan/settings.md +6 -0
  40. package/commands/pan/verify-phase.md +6 -2
  41. package/docs/AGENTS.md +917 -0
  42. package/docs/ARCHITECTURE.md +1054 -0
  43. package/docs/CLI-REFERENCE.md +4582 -0
  44. package/docs/COMPARISON.md +116 -0
  45. package/docs/DEVELOPMENT.md +490 -0
  46. package/docs/ECOSYSTEM-REVIEW-2026-06.md +107 -0
  47. package/docs/ECOSYSTEM-REVIEW-2026-08.md +349 -0
  48. package/docs/EXAMPLES.md +584 -0
  49. package/docs/FAQ.md +254 -0
  50. package/docs/FIELD-HARVEST-2026-07.md +88 -0
  51. package/docs/FIELD-REPORT-army-2026-06.md +121 -0
  52. package/docs/HOOKS.md +638 -0
  53. package/docs/IMPROVEMENT-TODO.md +246 -0
  54. package/docs/INTERNALS.md +485 -0
  55. package/docs/MIGRATION-v2-to-v3.md +258 -0
  56. package/docs/SECURITY-HARDENING.md +230 -0
  57. package/docs/SKILLS-ENHANCEMENT-PLAN-V2.md +122 -0
  58. package/docs/SKILLS-FULL-TEXT.md +9685 -0
  59. package/docs/SKILLS-REFERENCE.md +1037 -0
  60. package/docs/TROUBLESHOOTING.md +1084 -0
  61. package/docs/USER-GUIDE.md +1437 -0
  62. package/docs/archive/SKILLS-ENHANCEMENT-PLAN.md +230 -0
  63. package/docs/audits/AUDIT-2026-08-e2e-ROADMAP.md +117 -0
  64. package/docs/audits/AUDIT-2026-08-e2e-VERIFICATION.md +693 -0
  65. package/docs/audits/AUDIT-2026-08-e2e.md +1546 -0
  66. package/docs/audits/OUTSTANDING-2026-08.md +275 -0
  67. package/docs/audits/STABILITY-TEST-2026-08.md +640 -0
  68. package/docs/branding/README-brand-draft.md +119 -0
  69. package/docs/branding/handoff/IMPLEMENTATION.md +205 -0
  70. package/docs/branding/handoff/README-brand-draft.md +119 -0
  71. package/docs/branding/handoff/image-prompts.md +46 -0
  72. package/docs/branding/image-prompts.md +46 -0
  73. package/docs/branding/nano-banana-prompts.txt +60 -0
  74. package/docs/context-monitor.md +13 -0
  75. package/docs/decisions/ADR-0001-strategic-feature-prioritization.md +83 -0
  76. package/docs/decisions/ADR-0002-copilot-cli-runtime.md +71 -0
  77. package/docs/decisions/ADR-0003-smart-execution.md +84 -0
  78. package/docs/decisions/ADR-0004-copilot-cli-interaction.md +89 -0
  79. package/docs/decisions/ADR-0005-command-naming.md +80 -0
  80. package/docs/decisions/ADR-0006-focus-commands.md +101 -0
  81. package/docs/decisions/ADR-0007-ship-v1.md +73 -0
  82. package/docs/decisions/ADR-0008-map-codebase-mermaid-togaf.md +80 -0
  83. package/docs/decisions/ADR-0009-production-deployment-checklist.md +68 -0
  84. package/docs/decisions/ADR-0010-standards-integration.md +79 -0
  85. package/docs/decisions/ADR-0011-help-commands-rewrite.md +64 -0
  86. package/docs/decisions/ADR-0012-contributors-attribution-index.md +42 -0
  87. package/docs/decisions/ADR-0013-strategic-feature-additions.md +53 -0
  88. package/docs/decisions/ADR-0014-internal-cleanup-code-quality.md +53 -0
  89. package/docs/decisions/ADR-0015-focus-auto-runner.md +296 -0
  90. package/docs/decisions/ADR-0016-enhanced-doc-sync.md +54 -0
  91. package/docs/decisions/ADR-0017-enhanced-focus-design.md +69 -0
  92. package/docs/decisions/ADR-0018-user-reality-testing.md +84 -0
  93. package/docs/decisions/ADR-0019-end-to-end-user-acceptance-testing.md +113 -0
  94. package/docs/decisions/ADR-0020-ai-drift-prevention.md +55 -0
  95. package/docs/decisions/ADR-0021-codebase-mapper-v2.md +76 -0
  96. package/docs/decisions/ADR-0022-lifecycle-completeness.md +63 -0
  97. package/docs/decisions/ADR-0023-opus-4-7-adoption.md +89 -0
  98. package/docs/decisions/ADR-0024-spec-b-v2-completion.md +137 -0
  99. package/docs/decisions/ADR-0025-behavioral-guardrails-layer.md +129 -0
  100. package/docs/decisions/ADR-0026-self-improvement-loop.md +171 -0
  101. package/docs/decisions/ADR-0027-doc-code-link-graph.md +164 -0
  102. package/docs/decisions/ADR-0028-unified-agents-skills-target.md +46 -0
  103. package/docs/decisions/ADR-0029-experiment-evaluator.md +76 -0
  104. package/docs/decisions/ADR-0030-planning-concurrency.md +37 -0
  105. package/docs/decisions/ADR-0031-focus-auto-backlog-source.md +47 -0
  106. package/docs/decisions/ADR-0032-squad-model.md +64 -0
  107. package/docs/decisions/ADR-0033-army-campaign.md +40 -0
  108. package/docs/decisions/ADR-0034-scheduled-campaigns.md +57 -0
  109. package/docs/decisions/ADR-0035-army-hud-dashboard.md +40 -0
  110. package/docs/decisions/ADR-0036-agentic-memory-architecture.md +106 -0
  111. package/docs/decisions/ADR-0037-pin-security-agents-to-opus.md +51 -0
  112. package/docs/decisions/ADR-0038-skill-aligned-decomposition.md +41 -0
  113. package/docs/decisions/ADR-0039-worktree-as-project-variant.md +37 -0
  114. package/docs/decisions/ADR-0040-memory-injection-defense.md +55 -0
  115. package/docs/decisions/ADR-0041-mcp-dual-era-bridge.md +47 -0
  116. package/docs/decisions/ADR-0042-design-parity-both-flows.md +45 -0
  117. package/docs/decisions/ADR-0043-addressable-planning-roots.md +56 -0
  118. package/docs/decisions/ADR-0044-bound-the-cached-context.md +104 -0
  119. package/docs/decisions/ADR-0045-agent-plugins-bundle.md +76 -0
  120. package/docs/decisions/ADR-0046-subagent-frontmatter-decisions.md +39 -0
  121. package/docs/decisions/ADR-0047-pan-harness.md +76 -0
  122. package/docs/decisions/ADR-0048-one-core-per-project.md +43 -0
  123. package/docs/decisions/ADR-0049-machine-verdicts-and-findings-ledger.md +116 -0
  124. package/docs/specs/ai_drift_prevention_featureai.md +286 -0
  125. package/docs/specs/codebase_mapper_v2_featureai.md +930 -0
  126. package/docs/specs/command_naming_optimization_featureai.md +930 -0
  127. package/docs/specs/comp_analysis_markdown_v1.md +149 -0
  128. package/docs/specs/continuous_autonomous_execution_featureai.md +304 -0
  129. package/docs/specs/contributors_attribution_index_featureai.md +476 -0
  130. package/docs/specs/copilot_cli_interaction_featureai.md +868 -0
  131. package/docs/specs/copilot_cli_runtime_featureai.md +647 -0
  132. package/docs/specs/custom-skills-assessment.md +461 -0
  133. package/docs/specs/deployment_audit_findings_featureai.md +243 -0
  134. package/docs/specs/doc_code_link_graph_featureai.md +253 -0
  135. package/docs/specs/documentation_alignment_cleanup_featureai.md +69 -0
  136. package/docs/specs/e2e_visual_testing_featureai.md +615 -0
  137. package/docs/specs/end_to_end_user_acceptance_testing_featureai.md +810 -0
  138. package/docs/specs/evidence-loop-plan.md +76 -0
  139. package/docs/specs/evidence_loop_featureai.md +497 -0
  140. package/docs/specs/focus-auto-optimize-category.md +449 -0
  141. package/docs/specs/focus-auto-optimize-superplan.md +188 -0
  142. package/docs/specs/focus_auto_runner_featureai.md +1102 -0
  143. package/docs/specs/focus_design_v2_featureai.md +197 -0
  144. package/docs/specs/focus_sync_v2_featureai.md +202 -0
  145. package/docs/specs/git_integration_overhaul_featureai.md +326 -0
  146. package/docs/specs/glm_zai_provider_featureai.md +200 -0
  147. package/docs/specs/googlecli_adoption_featureai.md +661 -0
  148. package/docs/specs/help_commands_rewrite_featureai.md +860 -0
  149. package/docs/specs/industry_analysis_strategic_additions_featureai.md +231 -0
  150. package/docs/specs/industry_comparison_and_feature_gaps_featureai.md +674 -0
  151. package/docs/specs/internal_cleanup_code_quality_featureai.md +486 -0
  152. package/docs/specs/investigations/README.md +31 -0
  153. package/docs/specs/investigations/digests/2026-09-28.md +47 -0
  154. package/docs/specs/investigations/digests/2026-10-08.md +62 -0
  155. package/docs/specs/investigations/digests/2026-10-10.md +61 -0
  156. package/docs/specs/investigations/features/debugger-red-loop-first.md +55 -0
  157. package/docs/specs/investigations/features/harness-cross-host-behaviour.md +93 -0
  158. package/docs/specs/investigations/features/optimize-apply-regression-gate.md +66 -0
  159. package/docs/specs/investigations/features/reviewer-smell-baseline.md +57 -0
  160. package/docs/specs/investigations/features/trace-failure-capture.md +69 -0
  161. package/docs/specs/lifecycle_completeness_featureai.md +754 -0
  162. package/docs/specs/local-first-ci.md +42 -0
  163. package/docs/specs/map_codebase_mermaid_togaf_featureai.md +805 -0
  164. package/docs/specs/market-delta-2026-09-superplan.md +310 -0
  165. package/docs/specs/market-ideas-2026-09.md +131 -0
  166. package/docs/specs/market-ideas-2026-10.md +428 -0
  167. package/docs/specs/market-ideas-ledger.md +177 -0
  168. package/docs/specs/memory-optimization-2026-10.md +285 -0
  169. package/docs/specs/multi-track-planning-roots.md +143 -0
  170. package/docs/specs/multi_model_routing_featureai.md +687 -0
  171. package/docs/specs/multi_model_routing_superplan.md +197 -0
  172. package/docs/specs/opus_47_existing_enhancements_featureai.md +247 -0
  173. package/docs/specs/opus_47_extended_features_featureai.md +363 -0
  174. package/docs/specs/opus_47_extended_features_v2_featureai.md +397 -0
  175. package/docs/specs/pan_focus_commands_featureai.md +2336 -0
  176. package/docs/specs/pan_zcode_mcp_bridge_featureai.md +120 -0
  177. package/docs/specs/product-excellence-superplan.md +315 -0
  178. package/docs/specs/product_excellence_program_2026_q4_featureai.md +365 -0
  179. package/docs/specs/production_deployment_readiness_featureai.md +433 -0
  180. package/docs/specs/production_readiness_ship_v1_featureai.md +698 -0
  181. package/docs/specs/real_test_enforcement_featureai.md +213 -0
  182. package/docs/specs/reality-check-2026-09-superplan.md +362 -0
  183. package/docs/specs/right-sized-ceremony.md +74 -0
  184. package/docs/specs/roadmap_and_competition_2026_q4_featureai.md +390 -0
  185. package/docs/specs/self_improvement_loop_featureai.md +875 -0
  186. package/docs/specs/skill-aligned-decomposition.md +155 -0
  187. package/docs/specs/standards_integration_featureai.md +833 -0
  188. package/docs/specs/testing-system-redesign-2026-09.md +267 -0
  189. package/docs/specs/user_reality_testing_system_featureai.md +788 -0
  190. package/docs/specs/watch-triggers.json +22 -0
  191. package/docs/specs/workflow-integration-redesign.md +742 -0
  192. package/docs/specs/workflow_state_consistency_featureai.md +183 -0
  193. package/hooks/dist/pan-context-monitor.js +9 -2
  194. package/hooks/dist/pan-cost-logger.js +5 -0
  195. package/hooks/dist/pan-guard.js +28 -5
  196. package/hooks/dist/pan-stop-guard.js +9 -9
  197. package/hooks/dist/pan-trace-logger.js +5 -0
  198. package/package.json +10 -2
  199. package/pan-wizard-core/bin/lib/agents-md.cjs +57 -8
  200. package/pan-wizard-core/bin/lib/autodev-report.cjs +180 -0
  201. package/pan-wizard-core/bin/lib/autodev.cjs +642 -0
  202. package/pan-wizard-core/bin/lib/ci.cjs +143 -0
  203. package/pan-wizard-core/bin/lib/commands.cjs +1 -1
  204. package/pan-wizard-core/bin/lib/config.cjs +31 -2
  205. package/pan-wizard-core/bin/lib/constants.cjs +27 -1
  206. package/pan-wizard-core/bin/lib/core.cjs +22 -10
  207. package/pan-wizard-core/bin/lib/cost.cjs +14 -4
  208. package/pan-wizard-core/bin/lib/cross-family.cjs +164 -0
  209. package/pan-wizard-core/bin/lib/debug-cases.cjs +104 -0
  210. package/pan-wizard-core/bin/lib/failure-class.cjs +81 -0
  211. package/pan-wizard-core/bin/lib/findings.cjs +34 -4
  212. package/pan-wizard-core/bin/lib/focus.cjs +1 -1
  213. package/pan-wizard-core/bin/lib/hosts.cjs +88 -0
  214. package/pan-wizard-core/bin/lib/init.cjs +7 -2
  215. package/pan-wizard-core/bin/lib/knowledge.cjs +2 -0
  216. package/pan-wizard-core/bin/lib/merge.cjs +50 -0
  217. package/pan-wizard-core/bin/lib/mutate.cjs +16 -5
  218. package/pan-wizard-core/bin/lib/next-action.cjs +112 -0
  219. package/pan-wizard-core/bin/lib/optimize.cjs +2 -2
  220. package/pan-wizard-core/bin/lib/phase.cjs +48 -4
  221. package/pan-wizard-core/bin/lib/plan-lint.cjs +150 -1
  222. package/pan-wizard-core/bin/lib/preview.cjs +23 -4
  223. package/pan-wizard-core/bin/lib/requirements-lint.cjs +130 -0
  224. package/pan-wizard-core/bin/lib/research-lint.cjs +150 -3
  225. package/pan-wizard-core/bin/lib/review-deep.cjs +14 -22
  226. package/pan-wizard-core/bin/lib/review-vote.cjs +105 -0
  227. package/pan-wizard-core/bin/lib/runner.cjs +2 -16
  228. package/pan-wizard-core/bin/lib/sizing.cjs +356 -0
  229. package/pan-wizard-core/bin/lib/squads.cjs +1 -1
  230. package/pan-wizard-core/bin/lib/state-write.cjs +247 -0
  231. package/pan-wizard-core/bin/lib/uat.cjs +149 -0
  232. package/pan-wizard-core/bin/lib/verdict.cjs +26 -3
  233. package/pan-wizard-core/bin/lib/verify-plan-set.cjs +30 -1
  234. package/pan-wizard-core/bin/lib/verify-preflight.cjs +2 -2
  235. package/pan-wizard-core/bin/lib/verify-red.cjs +37 -1
  236. package/pan-wizard-core/bin/lib/verify-retro.cjs +3 -2
  237. package/pan-wizard-core/bin/lib/verify.cjs +59 -4
  238. package/pan-wizard-core/bin/lib/worktree.cjs +70 -0
  239. package/pan-wizard-core/bin/pan-tools.cjs +168 -27
  240. package/pan-wizard-core/learnings/universal/autonomous-loop.md +2 -2
  241. package/pan-wizard-core/references/continuation-format.md +3 -3
  242. package/pan-wizard-core/references/guardrails.md +1 -1
  243. package/pan-wizard-core/references/model-profiles.md +2 -3
  244. package/pan-wizard-core/references/questioning.md +24 -0
  245. package/pan-wizard-core/references/ui-brand.md +4 -6
  246. package/pan-wizard-core/references/verdict-contract.md +2 -1
  247. package/pan-wizard-core/templates/config.json +5 -1
  248. package/pan-wizard-core/templates/debug.md +1 -0
  249. package/pan-wizard-core/templates/discovery.md +2 -2
  250. package/pan-wizard-core/templates/requirements.md +9 -0
  251. package/pan-wizard-core/workflows/add-phase.md +4 -6
  252. package/pan-wizard-core/workflows/assumptions.md +19 -1
  253. package/pan-wizard-core/workflows/diagnose-issues.md +2 -0
  254. package/pan-wizard-core/workflows/discuss-phase.md +4 -2
  255. package/pan-wizard-core/workflows/exec-lean.md +69 -0
  256. package/pan-wizard-core/workflows/exec-phase.md +36 -8
  257. package/{commands/pan → pan-wizard-core/workflows}/focus-doc-audit.md +13 -29
  258. package/{commands/pan → pan-wizard-core/workflows}/focus-drift-walking.md +3 -18
  259. package/{commands/pan → pan-wizard-core/workflows}/focus-exec.md +8 -23
  260. package/{commands/pan → pan-wizard-core/workflows}/focus-plan.md +6 -20
  261. package/{commands/pan → pan-wizard-core/workflows}/focus-scan.md +3 -16
  262. package/{commands/pan → pan-wizard-core/workflows}/focus-sync.md +7 -21
  263. package/pan-wizard-core/workflows/health.md +6 -0
  264. package/pan-wizard-core/workflows/help.md +31 -53
  265. package/pan-wizard-core/workflows/insert-phase.md +4 -6
  266. package/pan-wizard-core/workflows/learn.md +3 -3
  267. package/pan-wizard-core/workflows/milestone-audit.md +2 -2
  268. package/pan-wizard-core/workflows/milestone-new.md +38 -95
  269. package/pan-wizard-core/workflows/new-project.md +96 -258
  270. package/pan-wizard-core/workflows/optimize.md +107 -5
  271. package/pan-wizard-core/workflows/phase-tests.md +10 -0
  272. package/pan-wizard-core/workflows/plan-phase.md +86 -43
  273. package/pan-wizard-core/workflows/profile.md +50 -0
  274. package/pan-wizard-core/workflows/progress.md +10 -0
  275. package/pan-wizard-core/workflows/quick.md +34 -43
  276. package/pan-wizard-core/workflows/research-phase.md +118 -40
  277. package/pan-wizard-core/workflows/resume-project.md +18 -2
  278. package/pan-wizard-core/workflows/settings.md +21 -24
  279. package/pan-wizard-core/workflows/todo-check.md +6 -2
  280. package/pan-wizard-core/workflows/transition.md +22 -4
  281. package/pan-wizard-core/workflows/uat-walkthrough.md +90 -0
  282. package/pan-wizard-core/workflows/update.md +1 -1
  283. package/pan-wizard-core/workflows/verify-phase.md +2 -2
  284. package/scripts/build-agent-plugin.js +35 -15
  285. package/scripts/build-plugin.js +10 -1
  286. package/scripts/deprecate-old-versions.js +70 -3
  287. package/scripts/generate-skills-docs.py +1 -1
  288. package/scripts/git-hooks/pre-push +27 -0
  289. package/scripts/install-git-hooks.js +7 -5
  290. package/scripts/install-pre-push.cjs +38 -0
  291. package/scripts/land.cjs +151 -0
  292. package/scripts/local-ci.cjs +501 -0
  293. package/scripts/local-schedule.cjs +277 -0
  294. package/scripts/prompt-lint.cjs +32 -0
  295. package/scripts/prompt-rules.cjs +156 -0
  296. package/scripts/run-tests.cjs +7 -1
  297. package/scripts/test-quality-lint.cjs +3 -3
  298. package/scripts/test-surface.cjs +9 -2
  299. package/agents/pan-research-synthesizer.md +0 -246
  300. package/commands/pan/audit-deployment.md +0 -386
  301. package/commands/pan/dashboard.md +0 -22
  302. package/commands/pan/discord.md +0 -19
  303. package/commands/pan/mcp-bridge.md +0 -143
  304. package/commands/pan/milestone-cleanup.md +0 -19
  305. package/commands/pan/milestone-gaps.md +0 -35
  306. package/commands/pan/optimize.md +0 -115
  307. package/commands/pan/profile.md +0 -76
  308. package/commands/pan/research-phase.md +0 -189
  309. package/pan-wizard-core/bin/lib/bridge.cjs +0 -269
  310. package/pan-wizard-core/templates/research-project/architecture.md +0 -210
  311. package/pan-wizard-core/templates/research-project/features.md +0 -153
  312. package/pan-wizard-core/templates/research-project/pitfalls.md +0 -206
  313. package/pan-wizard-core/templates/research-project/stack.md +0 -126
  314. package/pan-wizard-core/templates/research-project/summary.md +0 -170
@@ -111,7 +111,14 @@ function main() {
111
111
  );
112
112
 
113
113
  // 2. Commands (Claude flavor, plugin-root-relative paths)
114
- copyTree(path.join(ROOT, 'commands', 'pan'), path.join(OUT, 'commands', 'pan'), rewriteContent);
114
+ // Commands are user-invoked only on Claude Code (M49): out of the model's listing.
115
+ copyTree(path.join(ROOT, 'commands', 'pan'), path.join(OUT, 'commands', 'pan'), (c) => lib.withClaudeUserInvokedOnly(rewriteContent(c)));
116
+ // Renamed commands keep their old names as aliases, as the installer writes them (L7).
117
+ const { COMMAND_ALIASES } = require(path.join(ROOT, 'pan-wizard-core', 'bin', 'lib', 'constants.cjs'));
118
+ for (const [oldName, alias] of Object.entries(COMMAND_ALIASES)) {
119
+ const target = fs.readFileSync(path.join(ROOT, 'commands', 'pan', `${alias.to}.md`), 'utf8');
120
+ fs.writeFileSync(path.join(OUT, 'commands', 'pan', `${oldName}.md`), lib.withClaudeUserInvokedOnly(rewriteContent(lib.renderCommandAlias(target, oldName, alias))));
121
+ }
115
122
 
116
123
  // 3. Agents
117
124
  copyTree(path.join(ROOT, 'agents'), path.join(OUT, 'agents'), rewriteContent);
@@ -152,6 +159,8 @@ function main() {
152
159
  copyTree(path.join(ROOT, 'pan-wizard-core'), path.join(OUT, 'pan-wizard-core'), rewriteContent);
153
160
  fs.rmSync(path.join(OUT, 'pan-wizard-core', 'learnings', 'internal'), { recursive: true, force: true });
154
161
  fs.writeFileSync(path.join(OUT, 'pan-wizard-core', 'VERSION'), pkg.version);
162
+ // `/pan:update` points at the installed changelog, as a loose-file install carries it.
163
+ fs.copyFileSync(path.join(ROOT, 'CHANGELOG.md'), path.join(OUT, 'pan-wizard-core', 'CHANGELOG.md'));
155
164
 
156
165
  // 6. Native workflows. A plugin loads `workflows/` at its root and exposes each
157
166
  // script as `/<plugin>:<meta.name>`. Until 2026-09 the builder never wrote this
@@ -156,6 +156,58 @@ function npmJson(args, fallback = null) {
156
156
  return JSON.parse(out);
157
157
  }
158
158
 
159
+ /**
160
+ * Turn npm's stderr into the error that matters.
161
+ *
162
+ * WHY. npm writes warnings before the error, and on a GitHub runner the first
163
+ * line is setup-node's `npm warn Unknown user config "always-auth"` — present on
164
+ * every call, unrelated to any failure. Reporting only the first line made all 69
165
+ * failures across the 3.35.0 and 3.36.0 releases say that, hiding the cause. The
166
+ * real error is the `npm error code X` line plus the first other `npm error` line,
167
+ * skipping npm's closing "A complete log of this run..." pointer.
168
+ */
169
+ function summarizeNpmError(stderr) {
170
+ const lines = String(stderr || '').split(/\r?\n/).map((l) => l.trim()).filter(Boolean);
171
+ let code = null;
172
+ let detail = null;
173
+ for (const l of lines) {
174
+ const m = /^npm (?:error|ERR!)\s*(.*)$/.exec(l);
175
+ if (!m) continue;
176
+ const rest = m[1].trim();
177
+ const c = /^code\s+(\S+)$/.exec(rest);
178
+ if (c) { code = code || c[1]; continue; }
179
+ if (!detail && rest && !/^A complete log of this run/i.test(rest)) detail = rest;
180
+ }
181
+ if (!detail) detail = lines.find((l) => !/^npm (warn|notice)\b/i.test(l)) || null;
182
+ return { code, detail };
183
+ }
184
+
185
+ /** Auth refusals: every further call with the same credential fails the same way. */
186
+ const AUTH_CODES = new Set(['E401', 'E403', 'EOTP', 'ENEEDAUTH']);
187
+ function isAuthFailure(code) {
188
+ return AUTH_CODES.has(code);
189
+ }
190
+
191
+ /**
192
+ * What the owner has to change, per auth failure. `npm deprecate` cannot use the
193
+ * OIDC trusted-publishing credential (npm grants that for `npm publish` only), so
194
+ * it runs on the NPM_TOKEN secret, and each hint points at that secret.
195
+ */
196
+ const AUTH_HINTS = {
197
+ E401: 'NPM_TOKEN is missing, expired or revoked — write-enabled granular tokens expire (90 days at most); create a new one and update the secret',
198
+ ENEEDAUTH: 'NPM_TOKEN is missing or empty — add it as a repository secret',
199
+ EOTP: 'NPM_TOKEN cannot get past 2FA — create the granular token with "Bypass 2FA" enabled',
200
+ E403: 'NPM_TOKEN has no write access to pan-wizard — give the granular token Read and write on the pan-wizard package',
201
+ };
202
+
203
+ /** One line for the log: `CODE: detail (hint)`, falling back to what npm or node said. */
204
+ function formatNpmError(stderr, fallback = '') {
205
+ const { code, detail } = summarizeNpmError(stderr);
206
+ const text = detail || String(fallback || '').split('\n')[0] || 'unknown error';
207
+ const head = code ? `${code}: ${text}` : text;
208
+ return AUTH_HINTS[code] ? `${head} (${AUTH_HINTS[code]})` : head;
209
+ }
210
+
159
211
  function main() {
160
212
  const args = process.argv.slice(2);
161
213
  const apply = args.includes('--apply');
@@ -185,7 +237,7 @@ function main() {
185
237
  // npm returns a bare string for a single version, or {version: message}.
186
238
  deprecatedAlready = (map && typeof map === 'object') ? Object.keys(map) : [];
187
239
  } catch (e) {
188
- console.error(`deprecate: could not read the registry (${String(e.message).split('\n')[0]}). Nothing changed.`);
240
+ console.error(`deprecate: could not read the registry (${formatNpmError(e.stderr, e.message)}). Nothing changed.`);
189
241
  return; // never fail the build over housekeeping
190
242
  }
191
243
 
@@ -210,16 +262,31 @@ function main() {
210
262
  console.log(` ✓ ${v}`);
211
263
  } catch (e) {
212
264
  failed++;
213
- console.error(` ✗ ${v}: ${String(e.stderr || e.message).split('\n')[0]}`);
265
+ console.error(` ✗ ${v}: ${formatNpmError(e.stderr, e.message)}`);
266
+ // An auth refusal repeats identically for every remaining version — say it
267
+ // once and stop rather than printing the same line 35 times.
268
+ if (isAuthFailure(summarizeNpmError(e.stderr).code)) {
269
+ const skipped = deprecate.length - deprecate.indexOf(v) - 1;
270
+ if (skipped > 0) console.error(` stopping: authentication refused — ${skipped} remaining version(s) not attempted.`);
271
+ break;
272
+ }
214
273
  }
215
274
  }
216
275
  if (failed) {
217
276
  // Reported, not fatal. The publish already succeeded; a red build here would
218
277
  // imply the release failed, which is false and worse than the omission.
219
278
  console.error(`deprecate: ${failed} of ${deprecate.length} failed — release is unaffected.`);
279
+ // continue-on-error hides a failed step inside the log; an annotation puts it
280
+ // on the run's summary page where the owner actually looks.
281
+ if (process.env.GITHUB_ACTIONS === 'true') {
282
+ console.log(`::warning title=Deprecation sweep failed::${failed} of ${deprecate.length} old versions were not deprecated — see the "Deprecate superseded versions" step log`);
283
+ }
220
284
  }
221
285
  }
222
286
 
223
287
  if (require.main === module) main();
224
288
 
225
- module.exports = { selectVersionsToDeprecate, buildMessage, parse, compare, assertQuotable };
289
+ module.exports = {
290
+ selectVersionsToDeprecate, buildMessage, parse, compare, assertQuotable,
291
+ summarizeNpmError, formatNpmError, isAuthFailure,
292
+ };
@@ -517,7 +517,7 @@ def generate_reference(shipped: list[Skill], dev: list[Skill], version: str) ->
517
517
  w("| Continuous improvement | `focus-auto --category <cat>` |")
518
518
  w("| Execute micro-prompts | `focus-auto --category prompts` |")
519
519
  w("| Feature investigation | `focus-design \"description\"` |")
520
- w("| Doc hygiene | `focus-drift-walking` or `focus-doc-audit` |")
520
+ w("| Doc hygiene | `focus-auto --step drift` or `focus-docs` |")
521
521
  w("")
522
522
 
523
523
  return "\n".join(lines)
@@ -0,0 +1,27 @@
1
+ #!/bin/sh
2
+ # PAN Wizard pre-push hook — the fast subset of the local CI (scripts/local-ci.cjs --fast):
3
+ # the suite on the current Node, the harness at tier 0, and gitleaks.
4
+ #
5
+ # Opt-in. core.hooksPath points every clone at this directory, so the hook is always
6
+ # present; it does nothing until you turn it on for this clone:
7
+ # npm run hooks:pre-push (git config pan.prePush true)
8
+ # npm run hooks:pre-push -- --off (turn it off again)
9
+ #
10
+ # Bypass once: SKIP_PREPUSH=1 git push or git push --no-verify
11
+ #
12
+ # Exit codes:
13
+ # 0 off, bypassed, or every gate passed (a skipped gate is listed, not passed)
14
+ # 1 a gate failed — read the summary and .ci-local/logs/ before pushing
15
+
16
+ if [ "$(git config --bool pan.prePush 2>/dev/null)" != "true" ]; then
17
+ exit 0
18
+ fi
19
+
20
+ if [ "${SKIP_PREPUSH:-}" = "1" ]; then
21
+ echo "[pre-push] SKIP_PREPUSH=1 — local CI bypassed." >&2
22
+ exit 0
23
+ fi
24
+
25
+ REPO_ROOT="$(git rev-parse --show-toplevel)"
26
+ echo "[pre-push] local CI, fast subset (SKIP_PREPUSH=1 to bypass)…" >&2
27
+ exec node "$REPO_ROOT/scripts/local-ci.cjs" --fast
@@ -57,12 +57,14 @@ try {
57
57
  // through `prepare`, every Linux and macOS checkout was left with a one-bit dirty
58
58
  // tree that nothing looked at until CI began asserting the tree is unchanged
59
59
  // (2026-09-17).
60
- const hookFile = path.join(REPO_ROOT, HOOKS_DIR, 'pre-commit');
60
+ // pre-push is tracked here too but stays inert until `npm run hooks:pre-push` opts in.
61
61
  if (process.platform !== 'win32') {
62
- try {
63
- fs.chmodSync(hookFile, 0o755);
64
- } catch {
65
- // Best effort.
62
+ for (const name of ['pre-commit', 'pre-push']) {
63
+ try {
64
+ fs.chmodSync(path.join(REPO_ROOT, HOOKS_DIR, name), 0o755);
65
+ } catch {
66
+ // Best effort.
67
+ }
66
68
  }
67
69
  }
68
70
 
@@ -0,0 +1,38 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * install-pre-push.cjs — turn the local-CI pre-push hook on or off for this clone
4
+ * (`npm run hooks:pre-push`, `npm run hooks:pre-push -- --off`).
5
+ *
6
+ * The hook itself is tracked at scripts/git-hooks/pre-push, where core.hooksPath already
7
+ * points (scripts/install-git-hooks.js). It is inert until `pan.prePush` is true in this
8
+ * clone's git config, so a fresh clone never runs the local CI on push unless its owner
9
+ * asked for it. This script sets or clears that key, and makes sure core.hooksPath is set.
10
+ *
11
+ * Exit 0 on success, 1 when git refuses.
12
+ */
13
+
14
+ 'use strict';
15
+
16
+ const { spawnSync } = require('child_process');
17
+ const path = require('path');
18
+
19
+ const REPO_ROOT = path.resolve(__dirname, '..');
20
+ const off = process.argv.includes('--off');
21
+
22
+ function git(args) {
23
+ const r = spawnSync('git', args, { cwd: REPO_ROOT, encoding: 'utf8' });
24
+ if (r.status !== 0 && !(off && args.includes('--unset'))) {
25
+ console.error(`[pre-push] git ${args.join(' ')} failed: ${(r.stderr || r.error && r.error.message || '').trim()}`);
26
+ process.exit(1);
27
+ }
28
+ return r;
29
+ }
30
+
31
+ if (off) {
32
+ git(['config', '--local', '--unset', 'pan.prePush']);
33
+ console.error('[pre-push] off — git push no longer runs the local CI.');
34
+ } else {
35
+ git(['config', '--local', 'core.hooksPath', 'scripts/git-hooks']);
36
+ git(['config', '--local', 'pan.prePush', 'true']);
37
+ console.error('[pre-push] on — git push runs `node scripts/local-ci.cjs --fast` first (SKIP_PREPUSH=1 to bypass once; --off to remove).');
38
+ }
@@ -0,0 +1,151 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * land.cjs — land a branch on main without a pull request (`npm run land -- <branch>`;
4
+ * local-first plan, phase 3). GitHub stays the mirror: main is pushed there, never forced.
5
+ *
6
+ * Refuses unless:
7
+ * - a full `npm run ci:local` passed on the branch's current HEAD, from a tree with no
8
+ * uncommitted changes, with no gate skipped (scripts/local-ci.cjs records the verdict in
9
+ * the git directory, pan-ci-local/<sha>.json, shared by every worktree);
10
+ * - the branch contains origin/main, so the squash commit's tree is exactly the tree
11
+ * ci:local passed (merge or rebase main into the branch and run ci:local again if not);
12
+ * - the checkout that has main (or a temporary one) is clean and main can fast-forward
13
+ * to origin/main.
14
+ *
15
+ * Then: fetch origin, fast-forward main to origin/main, `git merge --squash <branch>`, commit
16
+ * with the branch's summary (the title is its first commit's subject, or --title; the body
17
+ * lists every commit), and `git push origin main`. A rejected push leaves the commit on the
18
+ * local main and says so; nothing is forced, and the branch itself is left alone.
19
+ *
20
+ * Usage:
21
+ * node scripts/land.cjs <branch> [--title "..."] [--dry-run] [--no-push] [--allow-skips]
22
+ * [--remote origin] [--main main]
23
+ * Exit 0 landed (or the dry run would land), 1 refused or failed, 2 usage.
24
+ */
25
+
26
+ 'use strict';
27
+
28
+ const { spawnSync } = require('child_process');
29
+ const fs = require('fs');
30
+ const os = require('os');
31
+ const path = require('path');
32
+
33
+ function git(cwd, args, opts = {}) {
34
+ const r = spawnSync('git', args, { cwd, encoding: 'utf8', maxBuffer: 64 * 1024 * 1024, ...opts });
35
+ return { ok: r.status === 0, out: (r.stdout || '').trim(), err: (r.stderr || '').trim() || (r.error ? r.error.message : '') };
36
+ }
37
+
38
+ function parseArgs(argv) {
39
+ const a = { branch: null, title: null, dryRun: false, push: true, allowSkips: false, remote: 'origin', main: 'main' };
40
+ for (let i = 0; i < argv.length; i++) {
41
+ const k = argv[i];
42
+ if (k === '--title') a.title = argv[++i];
43
+ else if (k === '--dry-run') a.dryRun = true;
44
+ else if (k === '--no-push') a.push = false;
45
+ else if (k === '--allow-skips') a.allowSkips = true;
46
+ else if (k === '--remote') a.remote = argv[++i];
47
+ else if (k === '--main') a.main = argv[++i];
48
+ else if (k.startsWith('--')) throw new Error(`unknown option: ${k}`);
49
+ else if (!a.branch) a.branch = k;
50
+ else throw new Error(`one branch at a time (got ${a.branch} and ${k})`);
51
+ }
52
+ if (!a.branch) throw new Error('usage: land.cjs <branch> [--title T] [--dry-run] [--no-push] [--allow-skips]');
53
+ return a;
54
+ }
55
+
56
+ /** Why ci:local's verdict does not clear `sha` for landing, or null when it does. Pure. */
57
+ function verdictProblem(verdict, sha, { allowSkips = false } = {}) {
58
+ if (!verdict) return `no ci:local verdict for ${sha.slice(0, 9)} — run \`npm run ci:local\` on the branch at that commit`;
59
+ if (verdict.headFull !== sha) return `the verdict is for ${String(verdict.headFull).slice(0, 9)}, not ${sha.slice(0, 9)}`;
60
+ if (verdict.options && (verdict.options.only || verdict.options.fast)) return 'the verdict is from a partial run (--only or --fast); run the full ci:local';
61
+ if (verdict.dirtyAtStart !== false) return 'ci:local ran with uncommitted changes, so it did not test this commit alone; commit or stash them and run it again';
62
+ if (verdict.status !== 'pass') return `ci:local failed on ${sha.slice(0, 9)}: ${(verdict.gates || []).filter(g => g.status === 'fail').map(g => g.id).join(', ') || 'see its report'}`;
63
+ const skipped = (verdict.gates || []).filter(g => g.status === 'skip').map(g => g.id);
64
+ if (skipped.length && !allowSkips) return `ci:local skipped ${skipped.join(', ')}; install what they need, or pass --allow-skips`;
65
+ return null;
66
+ }
67
+
68
+ /** The squash commit's message: a title, and the branch's commits as the body. Pure. */
69
+ function commitMessage(branch, subjects, title) {
70
+ const head = title || subjects[0] || `Land ${branch}`;
71
+ const body = subjects.length > 1 || (title && subjects.length) ? `\n\nSquashed from ${branch}:\n${subjects.map(s => `- ${s}`).join('\n')}` : '';
72
+ return `${head}${body}\n`;
73
+ }
74
+
75
+ // The worktree that has `main` checked out, if any.
76
+ function mainCheckout(cwd, main) {
77
+ const list = git(cwd, ['worktree', 'list', '--porcelain']).out;
78
+ for (const block of list.split(/\n\n+/)) {
79
+ const wt = (block.match(/^worktree (.+)$/m) || [])[1];
80
+ if (wt && new RegExp(`^branch refs/heads/${main.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}$`, 'm').test(block)) return path.resolve(wt);
81
+ }
82
+ return null;
83
+ }
84
+
85
+ function land(argv, cwd = process.cwd(), log = console.log) {
86
+ let o;
87
+ try { o = parseArgs(argv); } catch (e) { log(`land: ${e.message}`); return 2; }
88
+ const refuse = (why) => { log(`land: refused — ${why}`); return 1; };
89
+
90
+ const sha = git(cwd, ['rev-parse', '--verify', `refs/heads/${o.branch}^{commit}`]);
91
+ if (!sha.ok) return refuse(`no local branch ${o.branch}`);
92
+ if (o.branch === o.main) return refuse(`${o.branch} is the branch it would land on`);
93
+
94
+ const common = path.resolve(cwd, git(cwd, ['rev-parse', '--git-common-dir']).out);
95
+ let verdict = null;
96
+ try { verdict = JSON.parse(fs.readFileSync(path.join(common, 'pan-ci-local', `${sha.out}.json`), 'utf8')); } catch { /* none */ }
97
+ const problem = verdictProblem(verdict, sha.out, o);
98
+ if (problem) return refuse(problem);
99
+
100
+ const fetched = git(cwd, ['fetch', o.remote, o.main]);
101
+ if (!fetched.ok) return refuse(`git fetch ${o.remote} ${o.main}: ${fetched.err}`);
102
+ const upstream = `refs/remotes/${o.remote}/${o.main}`;
103
+ if (!git(cwd, ['merge-base', '--is-ancestor', upstream, sha.out]).ok) {
104
+ return refuse(`${o.branch} does not contain ${o.remote}/${o.main}; merge or rebase it onto ${o.remote}/${o.main}, then run ci:local again`);
105
+ }
106
+ const subjects = git(cwd, ['log', '--reverse', '--format=%s', `${upstream}..${sha.out}`]).out.split('\n').filter(Boolean);
107
+ if (!subjects.length) return refuse(`${o.branch} has nothing that ${o.remote}/${o.main} lacks`);
108
+ const message = commitMessage(o.branch, subjects, o.title);
109
+
110
+ let where = mainCheckout(cwd, o.main);
111
+ if (where && git(where, ['status', '--porcelain']).out) return refuse(`the checkout with ${o.main} (${where}) has uncommitted changes`);
112
+ if (o.dryRun) {
113
+ log(`land: would squash ${subjects.length} commit(s) of ${o.branch} (${sha.out.slice(0, 9)}) onto ${o.remote}/${o.main} in ${where || 'a temporary worktree'}${o.push ? ` and push ${o.main} to ${o.remote}` : ''}:\n\n${message}`);
114
+ return 0;
115
+ }
116
+
117
+ let temp = null;
118
+ if (!where) {
119
+ temp = fs.mkdtempSync(path.join(os.tmpdir(), 'pan-land-'));
120
+ const hasMain = git(cwd, ['rev-parse', '--verify', `refs/heads/${o.main}`]).ok;
121
+ const add = git(cwd, hasMain ? ['worktree', 'add', temp, o.main] : ['worktree', 'add', '-b', o.main, temp, upstream]);
122
+ if (!add.ok) { fs.rmSync(temp, { recursive: true, force: true }); return refuse(`could not check out ${o.main}: ${add.err}`); }
123
+ where = temp;
124
+ }
125
+ try {
126
+ const ff = git(where, ['merge', '--ff-only', upstream]);
127
+ if (!ff.ok) return refuse(`local ${o.main} cannot fast-forward to ${o.remote}/${o.main} (it has commits ${o.remote} lacks): ${ff.err}`);
128
+ const sq = git(where, ['merge', '--squash', sha.out]);
129
+ if (!sq.ok) { git(where, ['reset', '--merge']); return refuse(`the squash merge failed: ${sq.err}`); }
130
+ // The branch contains main, so the result is the branch's tree: anything else is a bug here.
131
+ const tree = git(where, ['write-tree']).out;
132
+ const want = git(cwd, ['rev-parse', `${sha.out}^{tree}`]).out;
133
+ if (tree !== want) { git(where, ['reset', '--hard', upstream]); return refuse(`the squashed tree ${tree.slice(0, 9)} is not the tree ci:local passed (${want.slice(0, 9)})`); }
134
+ const commit = git(where, ['commit', '--no-verify', '-F', '-'], { input: message });
135
+ if (!commit.ok) { git(where, ['reset', '--merge']); return refuse(`git commit: ${commit.err}`); }
136
+ const landed = git(where, ['rev-parse', 'HEAD']).out;
137
+ log(`land: ${o.branch} squashed onto ${o.main} as ${landed.slice(0, 9)}`);
138
+ if (!o.push) { log(`land: not pushed (--no-push); push with \`git push ${o.remote} ${o.main}\``); return 0; }
139
+ // Never forced: a push the remote rejects (it moved, or branch protection) stops here.
140
+ const push = git(where, ['push', o.remote, `refs/heads/${o.main}:refs/heads/${o.main}`]);
141
+ if (!push.ok) { log(`land: the push to ${o.remote} was rejected; ${landed.slice(0, 9)} is on the local ${o.main} only:\n${push.err}`); return 1; }
142
+ log(`land: pushed ${o.main} to ${o.remote}`);
143
+ return 0;
144
+ } finally {
145
+ if (temp) { git(cwd, ['worktree', 'remove', '--force', temp]); fs.rmSync(temp, { recursive: true, force: true }); }
146
+ }
147
+ }
148
+
149
+ module.exports = { parseArgs, verdictProblem, commitMessage, mainCheckout, land };
150
+
151
+ if (require.main === module) process.exit(land(process.argv.slice(2)));