pan-wizard 3.35.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.
- package/CHANGELOG.md +3645 -0
- package/README.md +46 -52
- package/agents/pan-conductor.md +2 -3
- package/agents/pan-counterfactual.md +1 -2
- package/agents/pan-debugger.md +14 -3
- package/agents/pan-design-checker.md +1 -2
- package/agents/pan-designer.md +2 -3
- package/agents/pan-document_code.md +1 -2
- package/agents/pan-executor.md +25 -0
- package/agents/pan-hardener.md +4 -3
- package/agents/pan-integration-checker.md +1 -2
- package/agents/pan-knowledge.md +1 -2
- package/agents/pan-meta-reviewer.md +3 -3
- package/agents/pan-optimizer.md +4 -4
- package/agents/pan-phase-researcher.md +80 -475
- package/agents/pan-plan-checker.md +40 -3
- package/agents/pan-planner.md +21 -7
- package/agents/pan-previewer.md +3 -2
- package/agents/pan-project-researcher.md +72 -493
- package/agents/pan-release.md +1 -1
- package/agents/pan-reviewer.md +14 -3
- package/agents/pan-roadmapper.md +12 -7
- package/agents/pan-verifier.md +9 -4
- package/bin/install-lib.cjs +134 -14
- package/bin/install.js +59 -12
- package/commands/pan/army.md +1 -1
- package/commands/pan/autodev.md +76 -0
- package/commands/pan/cost.md +21 -1
- package/commands/pan/exec-phase.md +3 -4
- package/commands/pan/focus-auto.md +14 -7
- package/commands/pan/focus-design.md +4 -5
- package/commands/pan/focus-docs.md +24 -0
- package/commands/pan/learn.md +13 -6
- package/commands/pan/milestone-audit.md +8 -1
- package/commands/pan/milestone-done.md +10 -5
- package/commands/pan/milestone-new.md +2 -2
- package/commands/pan/new-project.md +15 -12
- package/commands/pan/plan-phase.md +15 -9
- package/commands/pan/preview.md +1 -1
- package/commands/pan/quick.md +1 -1
- package/commands/pan/review-deep.md +11 -5
- package/commands/pan/settings.md +6 -0
- package/commands/pan/verify-phase.md +6 -2
- package/docs/AGENTS.md +917 -0
- package/docs/ARCHITECTURE.md +1054 -0
- package/docs/CLI-REFERENCE.md +4582 -0
- package/docs/COMPARISON.md +116 -0
- package/docs/DEVELOPMENT.md +490 -0
- package/docs/ECOSYSTEM-REVIEW-2026-06.md +107 -0
- package/docs/ECOSYSTEM-REVIEW-2026-08.md +349 -0
- package/docs/EXAMPLES.md +584 -0
- package/docs/FAQ.md +254 -0
- package/docs/FIELD-HARVEST-2026-07.md +88 -0
- package/docs/FIELD-REPORT-army-2026-06.md +121 -0
- package/docs/HOOKS.md +638 -0
- package/docs/IMPROVEMENT-TODO.md +246 -0
- package/docs/INTERNALS.md +485 -0
- package/docs/MIGRATION-v2-to-v3.md +258 -0
- package/docs/SECURITY-HARDENING.md +230 -0
- package/docs/SKILLS-ENHANCEMENT-PLAN-V2.md +122 -0
- package/docs/SKILLS-FULL-TEXT.md +9685 -0
- package/docs/SKILLS-REFERENCE.md +1037 -0
- package/docs/TROUBLESHOOTING.md +1084 -0
- package/docs/USER-GUIDE.md +1437 -0
- package/docs/archive/SKILLS-ENHANCEMENT-PLAN.md +230 -0
- package/docs/audits/AUDIT-2026-08-e2e-ROADMAP.md +117 -0
- package/docs/audits/AUDIT-2026-08-e2e-VERIFICATION.md +693 -0
- package/docs/audits/AUDIT-2026-08-e2e.md +1546 -0
- package/docs/audits/OUTSTANDING-2026-08.md +275 -0
- package/docs/audits/STABILITY-TEST-2026-08.md +640 -0
- package/docs/branding/README-brand-draft.md +119 -0
- package/docs/branding/handoff/IMPLEMENTATION.md +205 -0
- package/docs/branding/handoff/README-brand-draft.md +119 -0
- package/docs/branding/handoff/image-prompts.md +46 -0
- package/docs/branding/image-prompts.md +46 -0
- package/docs/branding/nano-banana-prompts.txt +60 -0
- package/docs/context-monitor.md +13 -0
- package/docs/decisions/ADR-0001-strategic-feature-prioritization.md +83 -0
- package/docs/decisions/ADR-0002-copilot-cli-runtime.md +71 -0
- package/docs/decisions/ADR-0003-smart-execution.md +84 -0
- package/docs/decisions/ADR-0004-copilot-cli-interaction.md +89 -0
- package/docs/decisions/ADR-0005-command-naming.md +80 -0
- package/docs/decisions/ADR-0006-focus-commands.md +101 -0
- package/docs/decisions/ADR-0007-ship-v1.md +73 -0
- package/docs/decisions/ADR-0008-map-codebase-mermaid-togaf.md +80 -0
- package/docs/decisions/ADR-0009-production-deployment-checklist.md +68 -0
- package/docs/decisions/ADR-0010-standards-integration.md +79 -0
- package/docs/decisions/ADR-0011-help-commands-rewrite.md +64 -0
- package/docs/decisions/ADR-0012-contributors-attribution-index.md +42 -0
- package/docs/decisions/ADR-0013-strategic-feature-additions.md +53 -0
- package/docs/decisions/ADR-0014-internal-cleanup-code-quality.md +53 -0
- package/docs/decisions/ADR-0015-focus-auto-runner.md +296 -0
- package/docs/decisions/ADR-0016-enhanced-doc-sync.md +54 -0
- package/docs/decisions/ADR-0017-enhanced-focus-design.md +69 -0
- package/docs/decisions/ADR-0018-user-reality-testing.md +84 -0
- package/docs/decisions/ADR-0019-end-to-end-user-acceptance-testing.md +113 -0
- package/docs/decisions/ADR-0020-ai-drift-prevention.md +55 -0
- package/docs/decisions/ADR-0021-codebase-mapper-v2.md +76 -0
- package/docs/decisions/ADR-0022-lifecycle-completeness.md +63 -0
- package/docs/decisions/ADR-0023-opus-4-7-adoption.md +89 -0
- package/docs/decisions/ADR-0024-spec-b-v2-completion.md +137 -0
- package/docs/decisions/ADR-0025-behavioral-guardrails-layer.md +129 -0
- package/docs/decisions/ADR-0026-self-improvement-loop.md +171 -0
- package/docs/decisions/ADR-0027-doc-code-link-graph.md +164 -0
- package/docs/decisions/ADR-0028-unified-agents-skills-target.md +46 -0
- package/docs/decisions/ADR-0029-experiment-evaluator.md +76 -0
- package/docs/decisions/ADR-0030-planning-concurrency.md +37 -0
- package/docs/decisions/ADR-0031-focus-auto-backlog-source.md +47 -0
- package/docs/decisions/ADR-0032-squad-model.md +64 -0
- package/docs/decisions/ADR-0033-army-campaign.md +40 -0
- package/docs/decisions/ADR-0034-scheduled-campaigns.md +57 -0
- package/docs/decisions/ADR-0035-army-hud-dashboard.md +40 -0
- package/docs/decisions/ADR-0036-agentic-memory-architecture.md +106 -0
- package/docs/decisions/ADR-0037-pin-security-agents-to-opus.md +51 -0
- package/docs/decisions/ADR-0038-skill-aligned-decomposition.md +41 -0
- package/docs/decisions/ADR-0039-worktree-as-project-variant.md +37 -0
- package/docs/decisions/ADR-0040-memory-injection-defense.md +55 -0
- package/docs/decisions/ADR-0041-mcp-dual-era-bridge.md +47 -0
- package/docs/decisions/ADR-0042-design-parity-both-flows.md +45 -0
- package/docs/decisions/ADR-0043-addressable-planning-roots.md +56 -0
- package/docs/decisions/ADR-0044-bound-the-cached-context.md +104 -0
- package/docs/decisions/ADR-0045-agent-plugins-bundle.md +76 -0
- package/docs/decisions/ADR-0046-subagent-frontmatter-decisions.md +39 -0
- package/docs/decisions/ADR-0047-pan-harness.md +76 -0
- package/docs/decisions/ADR-0048-one-core-per-project.md +43 -0
- package/docs/decisions/ADR-0049-machine-verdicts-and-findings-ledger.md +116 -0
- package/docs/specs/ai_drift_prevention_featureai.md +286 -0
- package/docs/specs/codebase_mapper_v2_featureai.md +930 -0
- package/docs/specs/command_naming_optimization_featureai.md +930 -0
- package/docs/specs/comp_analysis_markdown_v1.md +149 -0
- package/docs/specs/continuous_autonomous_execution_featureai.md +304 -0
- package/docs/specs/contributors_attribution_index_featureai.md +476 -0
- package/docs/specs/copilot_cli_interaction_featureai.md +868 -0
- package/docs/specs/copilot_cli_runtime_featureai.md +647 -0
- package/docs/specs/custom-skills-assessment.md +461 -0
- package/docs/specs/deployment_audit_findings_featureai.md +243 -0
- package/docs/specs/doc_code_link_graph_featureai.md +253 -0
- package/docs/specs/documentation_alignment_cleanup_featureai.md +69 -0
- package/docs/specs/e2e_visual_testing_featureai.md +615 -0
- package/docs/specs/end_to_end_user_acceptance_testing_featureai.md +810 -0
- package/docs/specs/evidence-loop-plan.md +76 -0
- package/docs/specs/evidence_loop_featureai.md +497 -0
- package/docs/specs/focus-auto-optimize-category.md +449 -0
- package/docs/specs/focus-auto-optimize-superplan.md +188 -0
- package/docs/specs/focus_auto_runner_featureai.md +1102 -0
- package/docs/specs/focus_design_v2_featureai.md +197 -0
- package/docs/specs/focus_sync_v2_featureai.md +202 -0
- package/docs/specs/git_integration_overhaul_featureai.md +326 -0
- package/docs/specs/glm_zai_provider_featureai.md +200 -0
- package/docs/specs/googlecli_adoption_featureai.md +661 -0
- package/docs/specs/help_commands_rewrite_featureai.md +860 -0
- package/docs/specs/industry_analysis_strategic_additions_featureai.md +231 -0
- package/docs/specs/industry_comparison_and_feature_gaps_featureai.md +674 -0
- package/docs/specs/internal_cleanup_code_quality_featureai.md +486 -0
- package/docs/specs/investigations/README.md +31 -0
- package/docs/specs/investigations/digests/2026-09-28.md +47 -0
- package/docs/specs/investigations/digests/2026-10-08.md +62 -0
- package/docs/specs/investigations/digests/2026-10-10.md +61 -0
- package/docs/specs/investigations/features/debugger-red-loop-first.md +55 -0
- package/docs/specs/investigations/features/harness-cross-host-behaviour.md +93 -0
- package/docs/specs/investigations/features/optimize-apply-regression-gate.md +66 -0
- package/docs/specs/investigations/features/reviewer-smell-baseline.md +57 -0
- package/docs/specs/investigations/features/trace-failure-capture.md +69 -0
- package/docs/specs/lifecycle_completeness_featureai.md +754 -0
- package/docs/specs/local-first-ci.md +42 -0
- package/docs/specs/map_codebase_mermaid_togaf_featureai.md +805 -0
- package/docs/specs/market-delta-2026-09-superplan.md +310 -0
- package/docs/specs/market-ideas-2026-09.md +131 -0
- package/docs/specs/market-ideas-2026-10.md +428 -0
- package/docs/specs/market-ideas-ledger.md +177 -0
- package/docs/specs/memory-optimization-2026-10.md +285 -0
- package/docs/specs/multi-track-planning-roots.md +143 -0
- package/docs/specs/multi_model_routing_featureai.md +687 -0
- package/docs/specs/multi_model_routing_superplan.md +197 -0
- package/docs/specs/opus_47_existing_enhancements_featureai.md +247 -0
- package/docs/specs/opus_47_extended_features_featureai.md +363 -0
- package/docs/specs/opus_47_extended_features_v2_featureai.md +397 -0
- package/docs/specs/pan_focus_commands_featureai.md +2336 -0
- package/docs/specs/pan_zcode_mcp_bridge_featureai.md +120 -0
- package/docs/specs/product-excellence-superplan.md +315 -0
- package/docs/specs/product_excellence_program_2026_q4_featureai.md +365 -0
- package/docs/specs/production_deployment_readiness_featureai.md +433 -0
- package/docs/specs/production_readiness_ship_v1_featureai.md +698 -0
- package/docs/specs/real_test_enforcement_featureai.md +213 -0
- package/docs/specs/reality-check-2026-09-superplan.md +362 -0
- package/docs/specs/right-sized-ceremony.md +74 -0
- package/docs/specs/roadmap_and_competition_2026_q4_featureai.md +390 -0
- package/docs/specs/self_improvement_loop_featureai.md +875 -0
- package/docs/specs/skill-aligned-decomposition.md +155 -0
- package/docs/specs/standards_integration_featureai.md +833 -0
- package/docs/specs/testing-system-redesign-2026-09.md +267 -0
- package/docs/specs/user_reality_testing_system_featureai.md +788 -0
- package/docs/specs/watch-triggers.json +22 -0
- package/docs/specs/workflow-integration-redesign.md +742 -0
- package/docs/specs/workflow_state_consistency_featureai.md +183 -0
- package/hooks/dist/pan-context-monitor.js +9 -2
- package/hooks/dist/pan-cost-logger.js +5 -0
- package/hooks/dist/pan-guard.js +28 -5
- package/hooks/dist/pan-stop-guard.js +9 -9
- package/hooks/dist/pan-trace-logger.js +5 -0
- package/package.json +10 -2
- package/pan-wizard-core/bin/lib/agents-md.cjs +57 -8
- package/pan-wizard-core/bin/lib/autodev-report.cjs +180 -0
- package/pan-wizard-core/bin/lib/autodev.cjs +642 -0
- package/pan-wizard-core/bin/lib/ci.cjs +143 -0
- package/pan-wizard-core/bin/lib/commands.cjs +1 -1
- package/pan-wizard-core/bin/lib/config.cjs +41 -5
- package/pan-wizard-core/bin/lib/constants.cjs +27 -1
- package/pan-wizard-core/bin/lib/core.cjs +31 -14
- package/pan-wizard-core/bin/lib/cost.cjs +160 -4
- package/pan-wizard-core/bin/lib/cross-family.cjs +164 -0
- package/pan-wizard-core/bin/lib/debug-cases.cjs +104 -0
- package/pan-wizard-core/bin/lib/doc-lint.cjs +5 -3
- package/pan-wizard-core/bin/lib/failure-class.cjs +81 -0
- package/pan-wizard-core/bin/lib/findings.cjs +148 -9
- package/pan-wizard-core/bin/lib/focus.cjs +1 -1
- package/pan-wizard-core/bin/lib/git.cjs +1 -1
- package/pan-wizard-core/bin/lib/hosts.cjs +88 -0
- package/pan-wizard-core/bin/lib/init.cjs +7 -2
- package/pan-wizard-core/bin/lib/knowledge.cjs +2 -0
- package/pan-wizard-core/bin/lib/merge.cjs +50 -0
- package/pan-wizard-core/bin/lib/mutate.cjs +268 -0
- package/pan-wizard-core/bin/lib/next-action.cjs +112 -0
- package/pan-wizard-core/bin/lib/optimize.cjs +2 -2
- package/pan-wizard-core/bin/lib/phase-gate.cjs +15 -9
- package/pan-wizard-core/bin/lib/phase-remove.cjs +91 -11
- package/pan-wizard-core/bin/lib/phase.cjs +85 -13
- package/pan-wizard-core/bin/lib/plan-lint.cjs +310 -0
- package/pan-wizard-core/bin/lib/preview.cjs +26 -4
- package/pan-wizard-core/bin/lib/requirements-lint.cjs +130 -0
- package/pan-wizard-core/bin/lib/research-lint.cjs +268 -0
- package/pan-wizard-core/bin/lib/review-deep.cjs +14 -22
- package/pan-wizard-core/bin/lib/review-vote.cjs +105 -0
- package/pan-wizard-core/bin/lib/roadmap.cjs +1 -0
- package/pan-wizard-core/bin/lib/runner.cjs +2 -16
- package/pan-wizard-core/bin/lib/sizing.cjs +356 -0
- package/pan-wizard-core/bin/lib/squads.cjs +1 -1
- package/pan-wizard-core/bin/lib/state-write.cjs +247 -0
- package/pan-wizard-core/bin/lib/test-gate.cjs +433 -0
- package/pan-wizard-core/bin/lib/uat.cjs +149 -0
- package/pan-wizard-core/bin/lib/usage.cjs +104 -0
- package/pan-wizard-core/bin/lib/verdict.cjs +26 -3
- package/pan-wizard-core/bin/lib/verify-plan-set.cjs +30 -1
- package/pan-wizard-core/bin/lib/verify-preflight.cjs +2 -2
- package/pan-wizard-core/bin/lib/verify-red.cjs +174 -0
- package/pan-wizard-core/bin/lib/verify-retro.cjs +3 -2
- package/pan-wizard-core/bin/lib/verify-scope.cjs +143 -2
- package/pan-wizard-core/bin/lib/verify.cjs +86 -31
- package/pan-wizard-core/bin/lib/worktree.cjs +70 -0
- package/pan-wizard-core/bin/pan-tools.cjs +257 -28
- package/pan-wizard-core/learnings/universal/autonomous-loop.md +2 -2
- package/pan-wizard-core/mcp/orchestrator.cjs +4 -1
- package/pan-wizard-core/references/continuation-format.md +3 -3
- package/pan-wizard-core/references/design-methodology.md +1 -1
- package/pan-wizard-core/references/guardrails.md +1 -1
- package/pan-wizard-core/references/handoff-decisions.md +1 -1
- package/pan-wizard-core/references/model-profiles.md +2 -3
- package/pan-wizard-core/references/questioning.md +24 -0
- package/pan-wizard-core/references/ui-brand.md +4 -6
- package/pan-wizard-core/references/verdict-contract.md +2 -1
- package/pan-wizard-core/templates/config.json +10 -2
- package/pan-wizard-core/templates/debug.md +1 -0
- package/pan-wizard-core/templates/design.md +5 -4
- package/pan-wizard-core/templates/discovery.md +2 -2
- package/pan-wizard-core/templates/phase-prompt.md +7 -0
- package/pan-wizard-core/templates/requirements.md +9 -0
- package/pan-wizard-core/templates/research.md +10 -0
- package/pan-wizard-core/templates/summary-complex.md +5 -1
- package/pan-wizard-core/templates/summary-minimal.md +4 -0
- package/pan-wizard-core/templates/summary-standard.md +5 -1
- package/pan-wizard-core/templates/summary.md +8 -1
- package/pan-wizard-core/workflows/add-phase.md +4 -6
- package/pan-wizard-core/workflows/assumptions.md +19 -1
- package/pan-wizard-core/workflows/diagnose-issues.md +2 -0
- package/pan-wizard-core/workflows/discuss-phase.md +4 -2
- package/pan-wizard-core/workflows/exec-lean.md +69 -0
- package/pan-wizard-core/workflows/exec-phase.md +79 -15
- package/pan-wizard-core/workflows/execute-plan.md +3 -1
- package/{commands/pan → pan-wizard-core/workflows}/focus-doc-audit.md +13 -29
- package/{commands/pan → pan-wizard-core/workflows}/focus-drift-walking.md +3 -18
- package/{commands/pan → pan-wizard-core/workflows}/focus-exec.md +8 -23
- package/{commands/pan → pan-wizard-core/workflows}/focus-plan.md +6 -20
- package/{commands/pan → pan-wizard-core/workflows}/focus-scan.md +3 -16
- package/{commands/pan → pan-wizard-core/workflows}/focus-sync.md +7 -21
- package/pan-wizard-core/workflows/health.md +6 -0
- package/pan-wizard-core/workflows/help.md +114 -77
- package/pan-wizard-core/workflows/insert-phase.md +4 -6
- package/pan-wizard-core/workflows/learn.md +3 -3
- package/pan-wizard-core/workflows/milestone-audit.md +2 -2
- package/pan-wizard-core/workflows/milestone-new.md +38 -95
- package/pan-wizard-core/workflows/new-project.md +96 -258
- package/pan-wizard-core/workflows/optimize.md +107 -5
- package/pan-wizard-core/workflows/phase-tests.md +10 -0
- package/pan-wizard-core/workflows/plan-phase.md +106 -45
- package/pan-wizard-core/workflows/profile.md +50 -0
- package/pan-wizard-core/workflows/progress.md +10 -0
- package/pan-wizard-core/workflows/quick.md +34 -43
- package/pan-wizard-core/workflows/remove-phase.md +11 -1
- package/pan-wizard-core/workflows/research-phase.md +118 -40
- package/pan-wizard-core/workflows/resume-project.md +18 -2
- package/pan-wizard-core/workflows/settings.md +33 -19
- package/pan-wizard-core/workflows/todo-check.md +6 -2
- package/pan-wizard-core/workflows/transition.md +22 -4
- package/pan-wizard-core/workflows/uat-walkthrough.md +90 -0
- package/pan-wizard-core/workflows/update.md +1 -1
- package/pan-wizard-core/workflows/verify-phase.md +39 -46
- package/scripts/build-agent-plugin.js +35 -15
- package/scripts/build-help.cjs +106 -0
- package/scripts/build-plugin.js +10 -1
- package/scripts/deprecate-old-versions.js +70 -3
- package/scripts/generate-skills-docs.py +1 -1
- package/scripts/git-hooks/pre-push +27 -0
- package/scripts/install-git-hooks.js +7 -5
- package/scripts/install-pre-push.cjs +38 -0
- package/scripts/land.cjs +151 -0
- package/scripts/local-ci.cjs +501 -0
- package/scripts/local-schedule.cjs +277 -0
- package/scripts/mutation-probe.cjs +7 -80
- package/scripts/prompt-lint.cjs +32 -0
- package/scripts/prompt-rules.cjs +156 -0
- package/scripts/run-tests.cjs +7 -1
- package/scripts/test-quality-lint.cjs +3 -3
- package/scripts/test-surface.cjs +175 -6
- package/agents/pan-research-synthesizer.md +0 -246
- package/commands/pan/audit-deployment.md +0 -386
- package/commands/pan/dashboard.md +0 -22
- package/commands/pan/discord.md +0 -19
- package/commands/pan/mcp-bridge.md +0 -143
- package/commands/pan/milestone-cleanup.md +0 -19
- package/commands/pan/milestone-gaps.md +0 -35
- package/commands/pan/optimize.md +0 -115
- package/commands/pan/profile.md +0 -76
- package/commands/pan/research-phase.md +0 -189
- package/pan-wizard-core/bin/lib/bridge.cjs +0 -269
- package/pan-wizard-core/templates/research-project/architecture.md +0 -204
- package/pan-wizard-core/templates/research-project/features.md +0 -147
- package/pan-wizard-core/templates/research-project/pitfalls.md +0 -200
- package/pan-wizard-core/templates/research-project/stack.md +0 -120
- package/pan-wizard-core/templates/research-project/summary.md +0 -170
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
<purpose>
|
|
2
|
+
Walk the user through user acceptance testing for one phase, one test at a time, on any runtime. Record each answer through the engine, so every runtime leaves the same `{phase_num}-uat.md`. When the walkthrough finds issues, diagnose them, then hand the gaps to `/pan:plan-phase {phase_num} --gaps`.
|
|
3
|
+
</purpose>
|
|
4
|
+
|
|
5
|
+
<core_principle>
|
|
6
|
+
The user is the oracle here. You present what they should be able to observe, they say whether they observed it, and the engine writes it down. Never mark a test passed on their behalf, and never rephrase their report: an issue is recorded in their words.
|
|
7
|
+
</core_principle>
|
|
8
|
+
|
|
9
|
+
<process>
|
|
10
|
+
|
|
11
|
+
<step name="initialize">
|
|
12
|
+
```bash
|
|
13
|
+
INIT=$(node ~/.claude/pan-wizard-core/bin/pan-tools.cjs init phase-op "${PHASE_ARG}")
|
|
14
|
+
```
|
|
15
|
+
Take `phase_dir`, `phase_number` and `phase_name` from the JSON. Look for an existing session:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
node ~/.claude/pan-wizard-core/bin/pan-tools.cjs uat status "${PHASE_NUMBER}"
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
- **A session exists with tests pending:** resume at the first pending test.
|
|
22
|
+
- **A session exists and is complete:** show its summary. If it has gaps, go to `diagnose`; otherwise go to `done`.
|
|
23
|
+
- **No session (`uat_not_found`):** go to `build_tests`.
|
|
24
|
+
</step>
|
|
25
|
+
|
|
26
|
+
<step name="build_tests">
|
|
27
|
+
Create the file, then fill in its tests:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
node ~/.claude/pan-wizard-core/bin/pan-tools.cjs scaffold uat --phase "${PHASE_NUMBER}"
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Read the phase's summaries (`{phase_dir}/*-summary.md`) and its plans' `must_haves`. Write one test per user-observable truth into the file's `## Tests` section, each as:
|
|
34
|
+
|
|
35
|
+
```markdown
|
|
36
|
+
### N. <short name>
|
|
37
|
+
expected: <what the user should be able to see or do, concretely: the command, the screen, the output>
|
|
38
|
+
result: [pending]
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Keep to what a person can observe without reading the code. Write the test list once, in full, then leave every result to the engine.
|
|
42
|
+
</step>
|
|
43
|
+
|
|
44
|
+
<step name="walk">
|
|
45
|
+
For each pending test, in order:
|
|
46
|
+
|
|
47
|
+
1. Show the test: its name and its `expected` behaviour, with the exact steps to observe it.
|
|
48
|
+
2. Ask for the result:
|
|
49
|
+
```
|
|
50
|
+
AskUserQuestion([
|
|
51
|
+
{ question: "Test N: <name>. Did you observe the expected behaviour?", header: "UAT", multiSelect: false, options: [
|
|
52
|
+
{ label: "Pass", description: "It behaves as expected" },
|
|
53
|
+
{ label: "Issue", description: "Something differs; describe what you saw" },
|
|
54
|
+
{ label: "Skip", description: "Cannot test this now" }
|
|
55
|
+
] }
|
|
56
|
+
])
|
|
57
|
+
```
|
|
58
|
+
Runtimes without AskUserQuestion ask the same question in plain text and take the answer from the reply.
|
|
59
|
+
3. Record it, in the user's own words for an issue:
|
|
60
|
+
```bash
|
|
61
|
+
node ~/.claude/pan-wizard-core/bin/pan-tools.cjs uat record "${PHASE_NUMBER}" --test N --result pass
|
|
62
|
+
node ~/.claude/pan-wizard-core/bin/pan-tools.cjs uat record "${PHASE_NUMBER}" --test N --result issue --reported "<their words>" --severity major
|
|
63
|
+
node ~/.claude/pan-wizard-core/bin/pan-tools.cjs uat record "${PHASE_NUMBER}" --test N --result skipped --reason "<why>"
|
|
64
|
+
```
|
|
65
|
+
Severity: `blocker` (cannot use the feature), `major` (wrong behaviour), `minor` (works, but rough), `cosmetic`.
|
|
66
|
+
|
|
67
|
+
When no test is pending, the engine marks the session `complete`.
|
|
68
|
+
</step>
|
|
69
|
+
|
|
70
|
+
<step name="diagnose">
|
|
71
|
+
```bash
|
|
72
|
+
node ~/.claude/pan-wizard-core/bin/pan-tools.cjs uat status "${PHASE_NUMBER}"
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
If `gaps` is empty, go to `done`. Otherwise follow the diagnose-issues workflow (@~/.claude/pan-wizard-core/workflows/diagnose-issues.md). On Claude Code the native `/pan-diagnose-issues {phase_num}` script does the same in parallel. Diagnosis fills each gap's `root_cause`, `artifacts`, `missing` and `debug_session`, and the engine keeps those fields across any later re-record.
|
|
76
|
+
</step>
|
|
77
|
+
|
|
78
|
+
<step name="done">
|
|
79
|
+
Report the summary (`passed`, `issues`, `skipped`). With gaps:
|
|
80
|
+
|
|
81
|
+
```
|
|
82
|
+
UAT found {issues} issue(s), diagnosed in {phase_num}-uat.md.
|
|
83
|
+
|
|
84
|
+
Next: /pan:plan-phase {phase_num} --gaps
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Without gaps: "UAT passed. Continue with `/pan:exec-phase {phase_num}` (transition) or the next phase."
|
|
88
|
+
</step>
|
|
89
|
+
|
|
90
|
+
</process>
|
|
@@ -197,7 +197,7 @@ Format completion message (changelog was already shown in confirmation step):
|
|
|
197
197
|
|
|
198
198
|
⚠️ Restart your AI coding tool to pick up the new commands.
|
|
199
199
|
|
|
200
|
-
|
|
200
|
+
Full changelog: ~/.claude/pan-wizard-core/CHANGELOG.md
|
|
201
201
|
```
|
|
202
202
|
</step>
|
|
203
203
|
|
|
@@ -85,65 +85,58 @@ This step provides awareness; the hard gate is enforced by exec-phase.
|
|
|
85
85
|
</step>
|
|
86
86
|
|
|
87
87
|
<step name="run_test_suite">
|
|
88
|
-
**Run the project's test suite as a verification gate.**
|
|
88
|
+
**Run the project's test suite as a verification gate, through the engine.**
|
|
89
89
|
|
|
90
|
-
This step catches test regressions that goal-backward analysis cannot detect.
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
90
|
+
This step catches test regressions that goal-backward analysis cannot detect. The engine
|
|
91
|
+
runs the suite, not you: it finds the project's test command in any language
|
|
92
|
+
(`workflow.test_command` in config.json, else package.json's test script, pytest, `go test
|
|
93
|
+
./...`, cargo, Maven, Gradle or `make test`), reads the runner's own output, compares it with
|
|
94
|
+
the baseline exec-phase recorded before the first wave, and writes the result to the phase
|
|
95
|
+
directory as `{NN}-test-run.json`.
|
|
96
96
|
|
|
97
97
|
```bash
|
|
98
|
-
|
|
98
|
+
TEST_RUN=$(node ~/.claude/pan-wizard-core/bin/pan-tools.cjs test run --phase "${phase_number}")
|
|
99
99
|
```
|
|
100
100
|
|
|
101
|
-
|
|
102
|
-
|
|
101
|
+
Read from the JSON: `status`, `reason`, `command`, `total`, `passed`, `failed`,
|
|
102
|
+
`regressions`, `preexisting_failures` and `record`.
|
|
103
103
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
TEST_COUNT=$(echo "$TEST_OUTPUT" | grep -E "^(ℹ|#) tests" | awk '{print $NF}')
|
|
109
|
-
TEST_PASS=$(echo "$TEST_OUTPUT" | grep -E "^(ℹ|#) pass" | awk '{print $NF}')
|
|
110
|
-
TEST_FAIL=$(echo "$TEST_OUTPUT" | grep -E "^(ℹ|#) fail" | awk '{print $NF}')
|
|
111
|
-
```
|
|
104
|
+
**Copy the status; do not judge it.** Write `test_gate: <status>` into the verification.md
|
|
105
|
+
frontmatter exactly as the record says. Reconcile compares the two, so a verification whose
|
|
106
|
+
`test_gate` disagrees with the record is refused at the phase gate, and a failed record
|
|
107
|
+
lowers a `passed` verification to `gaps_found` whatever the frontmatter says.
|
|
112
108
|
|
|
113
|
-
|
|
109
|
+
| `status` | What it means | What you do |
|
|
110
|
+
|----------|---------------|-------------|
|
|
111
|
+
| `passed` | The suite ran and nothing that passed at the baseline fails now | Record the counts. If `preexisting_failures` is not empty, list them as known failures the phase inherited, not as gaps |
|
|
112
|
+
| `failed` | A test that passed at the baseline fails now (`regressions`), or the suite did not run at all (a crash, a build error, a timeout: `reason` says `suite did not run`) | Set the overall status to `gaps_found` and list each regression, or the reason, as a gap |
|
|
113
|
+
| `skipped` | There was nothing to run: no test command, or one that ran no tests | Add a `not_checked` entry: `check: "tests"`, `reason:` the record's `reason` |
|
|
114
114
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
115
|
+
A suite that exists and did not run is `failed`, never `skipped`: the engine decides that
|
|
116
|
+
from the exit code and the runner's output, and you copy it.
|
|
117
|
+
|
|
118
|
+
**If test gate FAILED:** Continue verification (gather full picture) but set overall status to `gaps_found` regardless of goal-backward results. Include test failures as gaps in the report.
|
|
119
|
+
</step>
|
|
120
120
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
| `TEST_EXIT` = 0 **and** `TEST_FAIL` = 0 (a real number) | Record counts, continue to must-haves |
|
|
124
|
-
| `TEST_EXIT` = 0 **and** no `ℹ`/`#` count lines (a runner other than Node's built-in one) | Set `test_gate_status: passed`; record the runner's own summary line as the counts |
|
|
125
|
-
| `TEST_EXIT` ≠ 0 **and** failures were reported | Set `test_gate_status: failed`, include failure details |
|
|
126
|
-
| `TEST_EXIT` ≠ 0 **and** `TEST_FAIL` is empty/absent — the suite CRASHED or could not run (syntax error, missing module, bad import, no runner) | Set `test_gate_status: failed`, and record the reason as `suite did not run`. **Never `skipped`, never `passed`.** A suite that cannot execute is stronger evidence of a broken phase than one that runs and fails |
|
|
127
|
-
| `HAS_TEST` = `no` (handled in step 1) | Record as `test_gate_status: skipped`, continue. This is the ONLY legitimate route to `skipped` |
|
|
121
|
+
<step name="mutation_report">
|
|
122
|
+
**Measure whether the tests pin the code the phase wrote** (a report, unless the project chose gate mode).
|
|
128
123
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
project. They must never share a verdict; the first is `skipped`, the second is
|
|
132
|
-
`failed`.
|
|
124
|
+
Only when the test gate passed: a mutant cannot be read against a suite that already fails.
|
|
125
|
+
With `workflow.mutation_gate: off` the command runs nothing and says so.
|
|
133
126
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
TEST_GATE_STATUS: passed | failed | skipped
|
|
137
|
-
TEST_TOTAL: ${TEST_COUNT}
|
|
138
|
-
TEST_PASSED: ${TEST_PASS}
|
|
139
|
-
TEST_FAILED: ${TEST_FAIL}
|
|
127
|
+
```bash
|
|
128
|
+
MUTATION=$(node ~/.claude/pan-wizard-core/bin/pan-tools.cjs verify mutate --phase "${phase_number}" --max 5)
|
|
140
129
|
```
|
|
141
|
-
Write `test_gate: ${TEST_GATE_STATUS}` into the verification.md frontmatter. When it is
|
|
142
|
-
`skipped`, also add a `not_checked` entry (`check: "tests"`, `reason: "no test script in
|
|
143
|
-
package.json"`). The recorder reads both: a pass whose tests never ran is recorded as a
|
|
144
|
-
pass with tests not checked, never as a plain pass.
|
|
145
130
|
|
|
146
|
-
|
|
131
|
+
The engine flips one comparison, boolean or `+ 1` at a time on the lines the phase's plan
|
|
132
|
+
commits changed, in a throwaway worktree at HEAD, and runs the suite against each mutant. Read
|
|
133
|
+
`checked`, `sampled`, `killed`, `survivors` (file, line, the swap) and `aborted`.
|
|
134
|
+
|
|
135
|
+
Write a **Test strength** section in verification.md: `killed/sampled`, and each survivor as
|
|
136
|
+
`file:line — before → after`, framed as a test the phase could add. A survivor is a lead, not a
|
|
137
|
+
gap: in `report` mode it never changes the status. In `gate` mode (`workflow.mutation_gate: gate`) the result carries `gate`: when `gate.failed` is true (the score is below `workflow.mutation_floor`), set the status to `gaps_found` and list each survivor as a gap; `verify reconcile` refuses a passed verification over a failed gate. `aborted: flaky` (the unmutated suite passed once and failed
|
|
138
|
+
once) is worth reporting as a finding of its own; `checked: false` with its `reason` goes in
|
|
139
|
+
`not_checked` as `check: "mutation"`.
|
|
147
140
|
</step>
|
|
148
141
|
|
|
149
142
|
<step name="establish_must_haves">
|
|
@@ -15,9 +15,17 @@
|
|
|
15
15
|
* canonical agent reference copies under agents/
|
|
16
16
|
*
|
|
17
17
|
* hooks/pan-*.js PAN's hook scripts (pure Node), shared by every vendor
|
|
18
|
-
* hooks/hooks.json Codex
|
|
18
|
+
* hooks/hooks.json Codex's default plugin hooks location; matcher-group
|
|
19
19
|
* shape with `${PLUGIN_ROOT}` paths and `async` observers
|
|
20
|
-
* (developers.openai.com/plugins/build/plugins, 2026-09-10)
|
|
20
|
+
* (developers.openai.com/plugins/build/plugins, 2026-09-10).
|
|
21
|
+
* NOT LOADED BY CODEX for this bundle: a root plugin.json
|
|
22
|
+
* with the agent-plugins.org schema makes Codex treat the
|
|
23
|
+
* plugin as an Agent Plugin, and its loader then skips
|
|
24
|
+
* hooks entirely (codex-rs/core-plugins/src/loader.rs,
|
|
25
|
+
* "Enforce Agent Plugin runtime boundaries" #37027,
|
|
26
|
+
* 2026-08-05; same at rust-v0.163.0; read 2026-10-19, R55).
|
|
27
|
+
* Codex users get PAN's hooks from a `--codex` install
|
|
28
|
+
* (.codex/hooks.json). Kept for the other consumers.
|
|
21
29
|
* com.github.copilot/ Copilot's reverse-domain namespace (ADR-0045 D5):
|
|
22
30
|
* agents/pan-*.agent.md agents in Copilot's format
|
|
23
31
|
* hooks/hooks.json flat PascalCase format, `${CLAUDE_PLUGIN_ROOT}` paths
|
|
@@ -90,25 +98,35 @@ function emitSkills(srcDir, skillsDir, prefix) {
|
|
|
90
98
|
if (entry.isDirectory()) { recurse(srcPath, `${currentPrefix}-${entry.name}`); continue; }
|
|
91
99
|
if (!entry.name.endsWith('.md')) continue;
|
|
92
100
|
const skillName = `${currentPrefix}-${entry.name.replace(/\.md$/, '')}`;
|
|
93
|
-
|
|
94
|
-
fs.mkdirSync(skillDir, { recursive: true });
|
|
95
|
-
let content = fs.readFileSync(srcPath, 'utf8');
|
|
96
|
-
content = lib.rewriteUnifiedSkillCommandContent(content, REWRITE);
|
|
97
|
-
content = lib.convertClaudeCommandToUnifiedSkill(content, skillName, { adapterNote: lib.agentPluginSkillAdapterNote() });
|
|
98
|
-
const { skill, reference } = lib.splitOversizedSkill(content, skillName);
|
|
99
|
-
fs.writeFileSync(path.join(skillDir, 'SKILL.md'), skill);
|
|
100
|
-
if (reference) {
|
|
101
|
-
const refPath = path.join(skillDir, ...lib.AGENT_PLUGIN_SKILL_REFERENCE.split('/'));
|
|
102
|
-
fs.mkdirSync(path.dirname(refPath), { recursive: true });
|
|
103
|
-
fs.writeFileSync(refPath, reference);
|
|
104
|
-
split++;
|
|
105
|
-
}
|
|
101
|
+
if (emitSkill(fs.readFileSync(srcPath, 'utf8'), skillsDir, skillName)) split++;
|
|
106
102
|
count++;
|
|
107
103
|
}
|
|
108
104
|
})(srcDir, prefix);
|
|
105
|
+
// Renamed commands keep their old names as aliases, as the installer writes them (L7).
|
|
106
|
+
const { COMMAND_ALIASES } = require(path.join(ROOT, 'pan-wizard-core', 'bin', 'lib', 'constants.cjs'));
|
|
107
|
+
for (const [oldName, alias] of Object.entries(COMMAND_ALIASES)) {
|
|
108
|
+
const target = fs.readFileSync(path.join(srcDir, `${alias.to}.md`), 'utf8');
|
|
109
|
+
if (emitSkill(lib.renderCommandAlias(target, oldName, alias), skillsDir, `${prefix}-${oldName}`)) split++;
|
|
110
|
+
count++;
|
|
111
|
+
}
|
|
109
112
|
return { count, split };
|
|
110
113
|
}
|
|
111
114
|
|
|
115
|
+
/** One command → skills/<name>/SKILL.md (plus a reference file when split). Returns true when split. */
|
|
116
|
+
function emitSkill(content, skillsDir, skillName) {
|
|
117
|
+
const skillDir = path.join(skillsDir, skillName);
|
|
118
|
+
fs.mkdirSync(skillDir, { recursive: true });
|
|
119
|
+
content = lib.rewriteUnifiedSkillCommandContent(content, REWRITE);
|
|
120
|
+
content = lib.convertClaudeCommandToUnifiedSkill(content, skillName, { adapterNote: lib.agentPluginSkillAdapterNote() });
|
|
121
|
+
const { skill, reference } = lib.splitOversizedSkill(content, skillName);
|
|
122
|
+
fs.writeFileSync(path.join(skillDir, 'SKILL.md'), skill);
|
|
123
|
+
if (!reference) return false;
|
|
124
|
+
const refPath = path.join(skillDir, ...lib.AGENT_PLUGIN_SKILL_REFERENCE.split('/'));
|
|
125
|
+
fs.mkdirSync(path.dirname(refPath), { recursive: true });
|
|
126
|
+
fs.writeFileSync(refPath, reference);
|
|
127
|
+
return true;
|
|
128
|
+
}
|
|
129
|
+
|
|
112
130
|
/** pan-wizard-core → bundle, markdown rewritten, everything else verbatim. */
|
|
113
131
|
function emitCore(srcDir, destDir) {
|
|
114
132
|
(function recurse(currentSrc, currentDest) {
|
|
@@ -133,6 +151,8 @@ function emitCore(srcDir, destDir) {
|
|
|
133
151
|
if (err.code !== 'ENOENT') throw err;
|
|
134
152
|
}
|
|
135
153
|
fs.writeFileSync(path.join(destDir, 'VERSION'), pkg.version);
|
|
154
|
+
// `/pan:update` points at the installed changelog, as a loose-file install carries it.
|
|
155
|
+
fs.copyFileSync(path.join(ROOT, 'CHANGELOG.md'), path.join(destDir, 'CHANGELOG.md'));
|
|
136
156
|
}
|
|
137
157
|
|
|
138
158
|
/** Canonical agent reference copies under <core>/agents/ (ADR-0028). */
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
/**
|
|
4
|
+
* Generate /pan:help's command index from command frontmatter (program L5).
|
|
5
|
+
*
|
|
6
|
+
* node scripts/build-help.cjs # print the block
|
|
7
|
+
* node scripts/build-help.cjs --write # rewrite the block in pan-wizard-core/workflows/help.md
|
|
8
|
+
* node scripts/build-help.cjs --check # exit 1 when the committed block is stale
|
|
9
|
+
*
|
|
10
|
+
* The block between GENERATED_START and GENERATED_END in help.md lists every
|
|
11
|
+
* commands/pan/*.md by its frontmatter `group` and `description`, so help cannot leave
|
|
12
|
+
* a command out. tests/help-generated.test.cjs fails when it is stale.
|
|
13
|
+
*/
|
|
14
|
+
const fs = require('fs');
|
|
15
|
+
const path = require('path');
|
|
16
|
+
const { extractFrontmatter } = require('../pan-wizard-core/bin/lib/frontmatter.cjs');
|
|
17
|
+
|
|
18
|
+
const ROOT = path.join(__dirname, '..');
|
|
19
|
+
const HELP_REL = path.join('pan-wizard-core', 'workflows', 'help.md');
|
|
20
|
+
const GENERATED_START = '<!-- generated:command-index:start (scripts/build-help.cjs --write) -->';
|
|
21
|
+
const GENERATED_END = '<!-- generated:command-index:end -->';
|
|
22
|
+
|
|
23
|
+
// The order a new user reads the groups in; groups not listed follow, alphabetically.
|
|
24
|
+
const GROUP_ORDER = [
|
|
25
|
+
'Getting Started', 'Phase Lifecycle', 'Phase Management', 'Session & Progress',
|
|
26
|
+
'Milestone', 'Milestone Lifecycle', 'Focus', 'Foresight', 'Review', 'Validation',
|
|
27
|
+
'Observability', 'Knowledge', 'Self-Improvement', 'Git Workflow', 'Army',
|
|
28
|
+
'External tools', 'System', 'Community',
|
|
29
|
+
];
|
|
30
|
+
|
|
31
|
+
/** Every shipped command as {name, group, description}, sorted by name. */
|
|
32
|
+
function readCommands(root = ROOT) {
|
|
33
|
+
const dir = path.join(root, 'commands', 'pan');
|
|
34
|
+
return fs.readdirSync(dir).filter((f) => f.endsWith('.md')).sort().map((f) => {
|
|
35
|
+
const fm = extractFrontmatter(fs.readFileSync(path.join(dir, f), 'utf-8')) || {};
|
|
36
|
+
return {
|
|
37
|
+
// The file name is the command (`/pan:<file>`); some frontmatter names omit the prefix.
|
|
38
|
+
name: `pan:${f.replace(/\.md$/, '')}`,
|
|
39
|
+
group: String(fm.group || '').trim(),
|
|
40
|
+
description: String(fm.description || '').replace(/\s+/g, ' ').trim(),
|
|
41
|
+
};
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Backslashes first, so one in a description cannot turn the escaped pipe after it back into a cell break.
|
|
46
|
+
const cell = (s) => s.replace(/\\/g, '\\\\').replace(/\|/g, '\\|');
|
|
47
|
+
|
|
48
|
+
/** The markdown command index, grouped. Throws on a command with no group or description. */
|
|
49
|
+
function renderCommandIndex(commands) {
|
|
50
|
+
const groups = new Map();
|
|
51
|
+
for (const c of commands) {
|
|
52
|
+
if (!c.group) throw new Error(`${c.name} has no group in its frontmatter`);
|
|
53
|
+
if (!c.description) throw new Error(`${c.name} has no description in its frontmatter`);
|
|
54
|
+
if (!groups.has(c.group)) groups.set(c.group, []);
|
|
55
|
+
groups.get(c.group).push(c);
|
|
56
|
+
}
|
|
57
|
+
const rank = (g) => { const i = GROUP_ORDER.indexOf(g); return i === -1 ? GROUP_ORDER.length : i; };
|
|
58
|
+
const order = [...groups.keys()].sort((a, b) => rank(a) - rank(b) || a.localeCompare(b));
|
|
59
|
+
const out = [];
|
|
60
|
+
for (const g of order) {
|
|
61
|
+
out.push(`### ${g}`, '| Command | Description |', '|---------|-------------|');
|
|
62
|
+
for (const c of groups.get(g).sort((a, b) => a.name.localeCompare(b.name))) out.push(`| \`/${c.name}\` | ${cell(c.description)} |`);
|
|
63
|
+
out.push('');
|
|
64
|
+
}
|
|
65
|
+
return out.join('\n').trimEnd();
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** The generated block's content in a help.md text, or null when the markers are missing. */
|
|
69
|
+
function extractBlock(text) {
|
|
70
|
+
const t = String(text).replace(/\r\n/g, '\n');
|
|
71
|
+
const a = t.indexOf(GENERATED_START);
|
|
72
|
+
const b = t.indexOf(GENERATED_END);
|
|
73
|
+
if (a === -1 || b === -1 || b < a) return null;
|
|
74
|
+
return t.slice(a + GENERATED_START.length, b).trim();
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** help.md with its block replaced by `block`. Throws when the markers are missing. */
|
|
78
|
+
function replaceBlock(text, block) {
|
|
79
|
+
const t = String(text).replace(/\r\n/g, '\n');
|
|
80
|
+
const a = t.indexOf(GENERATED_START);
|
|
81
|
+
const b = t.indexOf(GENERATED_END);
|
|
82
|
+
if (a === -1 || b === -1 || b < a) throw new Error(`${HELP_REL} has no generated command-index markers`);
|
|
83
|
+
return t.slice(0, a + GENERATED_START.length) + '\n' + block + '\n' + t.slice(b);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function main(argv) {
|
|
87
|
+
const file = path.join(ROOT, HELP_REL);
|
|
88
|
+
const block = renderCommandIndex(readCommands(ROOT));
|
|
89
|
+
if (argv.includes('--write')) {
|
|
90
|
+
fs.writeFileSync(file, replaceBlock(fs.readFileSync(file, 'utf-8'), block));
|
|
91
|
+
process.stdout.write(`wrote the command index into ${HELP_REL}\n`);
|
|
92
|
+
} else if (argv.includes('--check')) {
|
|
93
|
+
if (extractBlock(fs.readFileSync(file, 'utf-8')) !== block) {
|
|
94
|
+
process.stderr.write(`${HELP_REL}'s command index is stale: run node scripts/build-help.cjs --write\n`);
|
|
95
|
+
process.exitCode = 1;
|
|
96
|
+
} else {
|
|
97
|
+
process.stdout.write(`${HELP_REL}'s command index is up to date\n`);
|
|
98
|
+
}
|
|
99
|
+
} else {
|
|
100
|
+
process.stdout.write(block + '\n');
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (require.main === module) main(process.argv.slice(2));
|
|
105
|
+
|
|
106
|
+
module.exports = { GROUP_ORDER, GENERATED_START, GENERATED_END, readCommands, renderCommandIndex, extractBlock, replaceBlock };
|
package/scripts/build-plugin.js
CHANGED
|
@@ -111,7 +111,14 @@ function main() {
|
|
|
111
111
|
);
|
|
112
112
|
|
|
113
113
|
// 2. Commands (Claude flavor, plugin-root-relative paths)
|
|
114
|
-
|
|
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 (${
|
|
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}: ${
|
|
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 = {
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
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
|
+
}
|