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