#!/usr/bin/env python3
"""
Generate MkDocs skill pages from source SKILL.md + TEMPLATE.md + EXAMPLE.md.

Reads skills/ directory, generates docs/skills/{phase}/{name}.md pages,
phase index pages, a main skills index, and a commands reference page.
Also prints a nav YAML snippet for mkdocs.yml.

Usage:
    python scripts/generate-skill-pages.py

No external dependencies . uses only the Python standard library.
"""

import os
import re
import sys
from pathlib import Path

ROOT = Path(__file__).resolve().parent.parent
SKILLS_DIR = ROOT / "skills"
COMMANDS_DIR = ROOT / "commands"
SAMPLES_DIR = ROOT / "library" / "skill-output-samples"
OUTPUT_DIR = ROOT / "docs" / "skills"

# Phase/classification ordering
PHASE_ORDER = [
    "discover", "define", "develop", "deliver",
    "measure", "iterate", "foundation", "utility",
]
PHASE_DISPLAY = {
    "discover": "Discover",
    "define": "Define",
    "develop": "Develop",
    "deliver": "Deliver",
    "measure": "Measure",
    "iterate": "Iterate",
    "foundation": "Foundation",
    "utility": "Utility",
}

# Known phase and classification prefixes
NAMESPACE_PREFIXES = [
    "discover", "define", "develop", "deliver",
    "measure", "iterate", "foundation", "utility",
]


def parse_frontmatter(content: str) -> tuple:
    """Parse YAML frontmatter from markdown content.
    Returns (metadata_dict, body_without_frontmatter).
    """
    # Handle HTML comment before frontmatter
    text = content.lstrip()
    if text.startswith("<!--"):
        end = text.find("-->")
        if end != -1:
            text = text[end + 3:].lstrip()

    match = re.match(r"^---\s*\n(.*?)\n---\s*\n?(.*)", text, re.DOTALL)
    if not match:
        return {}, content

    fm_text = match.group(1)
    body = match.group(2)

    # Also strip a leading HTML comment from the body (post-frontmatter convention,
    # standardized by commit 0ab0a60 for plugin-validation compatibility).
    body_stripped = body.lstrip()
    if body_stripped.startswith("<!--"):
        end = body_stripped.find("-->")
        if end != -1:
            body = body_stripped[end + 3:].lstrip()

    metadata = {}
    current_key = None
    in_metadata_block = False
    metadata_block_key = None

    for line in fm_text.split("\n"):
        stripped = line.rstrip()

        # Nested block (like metadata:)
        if re.match(r"^\w[\w-]*:\s*$", stripped):
            metadata_block_key = stripped.rstrip(":").strip()
            metadata[metadata_block_key] = {}
            in_metadata_block = True
            continue

        if in_metadata_block and stripped.startswith("  "):
            nested_match = re.match(r"^\s+([\w-]+):\s*(.+)", stripped)
            if nested_match:
                key = nested_match.group(1)
                val = nested_match.group(2).strip()
                val = val.strip("\"'")
                # Handle lists
                if val.startswith("[") and val.endswith("]"):
                    val = [v.strip().strip("\"'") for v in val[1:-1].split(",")]
                metadata[metadata_block_key][key] = val
            continue
        else:
            in_metadata_block = False

        # Top-level key: value
        kv_match = re.match(r"^([\w-]+):\s*(.+)", stripped)
        if kv_match:
            key = kv_match.group(1)
            val = kv_match.group(2).strip()
            val = val.strip("\"'")
            if val.startswith("[") and val.endswith("]"):
                val = [v.strip().strip("\"'") for v in val[1:-1].split(",")]
            metadata[key] = val

    return metadata, body


def extract_sections(body: str) -> dict:
    """Split markdown body by ## headings.
    Returns {heading_text: content_below_heading}.
    """
    sections = {}
    current_heading = None
    current_lines = []

    for line in body.split("\n"):
        heading_match = re.match(r"^##\s+(.+)", line)
        if heading_match:
            if current_heading is not None:
                sections[current_heading] = "\n".join(current_lines).strip()
            current_heading = heading_match.group(1).strip()
            current_lines = []
        else:
            current_lines.append(line)

    if current_heading is not None:
        sections[current_heading] = "\n".join(current_lines).strip()

    # Capture intro (content before first ##)
    intro_lines = []
    for line in body.split("\n"):
        if re.match(r"^##\s+", line):
            break
        intro_lines.append(line)
    sections["_intro"] = "\n".join(intro_lines).strip()

    return sections


def classify_skill(metadata: dict, dirname: str) -> tuple:
    """Determine phase/group and display name.
    Returns (group_key, display_name).
    """
    # v2.17.0: phase/classification live under metadata; fall back to top-level for transition.
    meta = metadata.get("metadata", {}) if isinstance(metadata.get("metadata"), dict) else {}
    phase = meta.get("phase") or metadata.get("phase")
    classification = meta.get("classification") or metadata.get("classification")
    if phase:
        return phase, PHASE_DISPLAY.get(phase, phase)
    if classification:
        return classification, PHASE_DISPLAY.get(classification, classification)
    # Fallback: derive from directory name prefix
    for prefix in NAMESPACE_PREFIXES:
        if dirname.startswith(f"{prefix}-"):
            return prefix, PHASE_DISPLAY.get(prefix, prefix)
    return "other", "Other"


def derive_command_name(dirname: str) -> str:
    """Strip the phase/classification prefix to get the command name."""
    for prefix in NAMESPACE_PREFIXES:
        if dirname.startswith(f"{prefix}-"):
            return dirname[len(prefix) + 1:]
    return dirname


def find_command_file(command_name: str) -> str | None:
    """Find the command file for a skill."""
    cmd_file = COMMANDS_DIR / f"{command_name}.md"
    if cmd_file.exists():
        return command_name
    return None


def read_file_content(filepath: Path) -> str:
    """Read file, strip frontmatter for template/example files."""
    if not filepath.exists():
        return ""
    content = filepath.read_text(encoding="utf-8")
    return content


def strip_frontmatter(content: str) -> str:
    """Remove YAML frontmatter from content for embedding."""
    text = content.lstrip()
    if text.startswith("<!--"):
        end = text.find("-->")
        if end != -1:
            text = text[end + 3:].lstrip()
    match = re.match(r"^---\s*\n.*?\n---\s*\n?(.*)", text, re.DOTALL)
    if match:
        return match.group(1)
    return content


def indent_content(content: str, spaces: int = 4) -> str:
    """Indent every line of content by N spaces."""
    prefix = " " * spaces
    return "\n".join(prefix + line if line.strip() else "" for line in content.split("\n"))


def rewrite_internal_paths(text: str) -> str:
    """Rewrite '../../docs/' to '../../' in markdown link targets.

    Source SKILL.md files at skills/{name}/SKILL.md use '../../docs/...'
    which resolves correctly from the source location (going up 2 dirs to
    repo root, then into docs/). When the generator copies that content to
    docs/skills/{phase}/{name}.md, the same '../../docs/...' resolves to
    docs/docs/... which does not exist (the generated file is already
    inside docs/, so going up 2 lands at docs/, not at repo root).

    This function rewrites markdown link targets matching '](../../docs/'
    to '](../../', so paths resolve correctly from the generated file's
    location. Only markdown link form ']( ... )' is touched; inline
    code, prose mentions, and frontmatter are not affected.

    Closes the W13 B3.5 P1.1 follow-up (FU6 in v2.14 cycle): Codex PR.2
    review surfaced 5 doubled-docs-prefix broken links in foundation-
    meeting-* and foundation-stakeholder-update generator output. The
    source SKILL.md paths are CORRECT for the source location; the
    generator just needed a translation step at the copy boundary.
    """
    return re.sub(r'\]\(\.\./\.\./docs/', '](../../', text)


def parse_sample_sections(filepath: Path) -> dict:
    """Parse a sample file into scenario, prompt, and output sections."""
    content = filepath.read_text(encoding="utf-8")
    text = content.lstrip()
    if text.startswith("<!--"):
        end = text.find("-->")
        if end != -1:
            text = text[end + 3:].lstrip()
    match = re.match(r"^---\s*\n(.*?)\n---\s*\n?(.*)", text, re.DOTALL)
    if match:
        fm_text = match.group(1)
        body = match.group(2)
    else:
        fm_text = ""
        body = text

    context = ""
    thread = ""
    for line in fm_text.split("\n"):
        if line.startswith("context:"):
            context = line[len("context:"):].strip()
        if line.startswith("thread:"):
            thread = line[len("thread:"):].strip()

    sections = {}
    current = None
    lines = []
    for line in body.split("\n"):
        m = re.match(r"^##\s+(.+)", line)
        if m:
            if current:
                sections[current] = "\n".join(lines).strip()
            current = m.group(1).strip()
            lines = []
        else:
            lines.append(line)
    if current:
        sections[current] = "\n".join(lines).strip()

    return {
        "context": context,
        "thread": thread,
        "scenario": sections.get("Scenario", ""),
        "prompt": sections.get("Prompt", ""),
        "output": sections.get("Output", ""),
    }


THREAD_DISPLAY = {
    "storevine": "Storevine (B2B)",
    "brainshelf": "Brainshelf (Consumer)",
    "workbench": "Workbench (Enterprise)",
}


def load_skill_samples(skill_name: str) -> list:
    """Load all samples for a skill from the sample library."""
    sample_dir = SAMPLES_DIR / skill_name
    if not sample_dir.exists():
        return []

    results = []
    for thread in ["storevine", "brainshelf", "workbench"]:
        files = sorted(sample_dir.glob(f"sample_{skill_name}_{thread}_*.md"))
        if files:
            # Use first match per thread (skip persona variants for simplicity)
            parsed = parse_sample_sections(files[0])
            parsed["thread_display"] = THREAD_DISPLAY.get(thread, thread)
            if parsed["output"]:
                results.append(parsed)

    return results


def generate_skill_page(skill_dir: Path) -> dict | None:
    """Generate a merged skill page. Returns skill info dict or None."""
    dirname = skill_dir.name
    skill_file = skill_dir / "SKILL.md"
    template_file = skill_dir / "references" / "TEMPLATE.md"
    example_file = skill_dir / "references" / "EXAMPLE.md"

    if not skill_file.exists():
        return None

    content = skill_file.read_text(encoding="utf-8")
    metadata, body = parse_frontmatter(content)
    body = rewrite_internal_paths(body)
    sections = extract_sections(body)

    group, group_display = classify_skill(metadata, dirname)
    command_name = derive_command_name(dirname)
    cmd = find_command_file(command_name)
    name = metadata.get("name", dirname)
    description = metadata.get("description", "")
    # v2.17.0: version/category live under metadata; fall back to top-level for transition.
    meta = metadata.get("metadata", {}) if isinstance(metadata.get("metadata"), dict) else {}
    version = meta.get("version", metadata.get("version", "1.0.0"))
    category = meta.get("category", "")

    # Compute display title from source SKILL.md H1 (Codex P2 fix:
    # was emitting `name` slug as title; now uses the proper H1).
    page_title = sections.get("_intro", "").split("\n")[0].lstrip("# ").strip()
    if not page_title:
        page_title = name

    # Build the page
    lines = []

    # Frontmatter (Pattern 5C: generated-content marker)
    lines.append("---")
    lines.append(f'title: "{page_title}"')
    lines.append(f'description: "{description}"')
    lines.append("generated: true")
    lines.append("source: scripts/generate-skill-pages.py")
    lines.append("tags:")
    lines.append(f"  - {group_display}")
    if category:
        lines.append(f"  - {category}")
    lines.append("---")
    lines.append("")

    # No body H1: Starlight renders the frontmatter title as the page heading.
    # Emitting `# {page_title}` here would duplicate the title (v2.14.x V15 fix).

    # Generated-content marker: frontmatter `generated: true` + `source:` fields (set above)
    # are the contributor / validator marker (Pattern 5C). The user-facing visible aside
    # was removed in v2.14.x V10 (2026-05-10) because the warning was contributor-noise on
    # the rendered docs site; users do not care how the page is generated. Contributors
    # viewing raw markdown still see the frontmatter; check-generated-content-untouched.sh
    # validator still uses the frontmatter marker.

    # Quick facts aside
    lines.append(':::note[Quick facts]')
    phase_label = f"**Phase:** {group_display}" if group in PHASE_ORDER[:6] else f"**Classification:** {group_display}"
    lines.append(f"{phase_label} | **Version:** {version} | **Category:** {category} | **License:** Apache-2.0")
    lines.append(':::')
    lines.append("")

    # Quick-try snippet (E-03; .md-button attr_list dropped per W5.5; renders as inline code link)
    if cmd:
        lines.append(f"**Try it:** `/{cmd} \"Your context here\"`")
        lines.append("")

    # Intro paragraph (skip the title line)
    intro_lines = sections.get("_intro", "").split("\n")
    intro_text = "\n".join(intro_lines[1:]).strip()
    if intro_text:
        lines.append(intro_text)
        lines.append("")

    # When to Use
    if "When to Use" in sections:
        lines.append("## When to Use")
        lines.append("")
        lines.append(sections["When to Use"])
        lines.append("")

    # When NOT to Use (utility skills)
    if "When NOT to Use" in sections:
        lines.append("## When NOT to Use")
        lines.append("")
        lines.append(sections["When NOT to Use"])
        lines.append("")

    # How to Use
    if cmd:
        lines.append("## How to Use")
        lines.append("")
        lines.append(f"Use the `/{cmd}` slash command:")
        lines.append("")
        lines.append("```")
        lines.append(f'/{cmd} "Your context here"')
        lines.append("```")
        lines.append("")
        lines.append(f"Or reference the skill file directly: `skills/{dirname}/SKILL.md`")
        lines.append("")

    # Instructions
    if "Instructions" in sections:
        lines.append("## Instructions")
        lines.append("")
        lines.append(sections["Instructions"])
        lines.append("")

    # Output Template
    template_content = read_file_content(template_file)
    if template_content:
        lines.append("## Output Template")
        lines.append("")
        template_body = rewrite_internal_paths(strip_frontmatter(template_content))
        lines.append(template_body.strip())
        lines.append("")

    # Example Output
    example_content = read_file_content(example_file)
    if example_content:
        example_body = rewrite_internal_paths(strip_frontmatter(example_content))
        example_title = example_body.split("\n")[0].lstrip("# ").strip()
        if not example_title:
            example_title = "Complete example"
        lines.append("## Example Output")
        lines.append("")
        lines.append('<details>')
        lines.append(f'<summary>{example_title}</summary>')
        lines.append("")
        lines.append(example_body.strip())
        lines.append("")
        lines.append('</details>')
        lines.append("")

    # Real-world samples from sample library (E-10)
    samples = load_skill_samples(dirname)
    if samples:
        lines.append("## Real-World Examples")
        lines.append("")
        lines.append("See this skill applied to three different product contexts:")
        lines.append("")
        for sample in samples:
            context_label = sample["context"] if sample["context"] else sample["thread_display"]
            lines.append('<details>')
            lines.append(f'<summary>{sample["thread_display"]}: {context_label}</summary>')
            lines.append("")
            if sample["prompt"]:
                lines.append(f"**Prompt:**\n\n{sample['prompt']}")
                lines.append("")
            lines.append(f"**Output:**\n\n{sample['output']}")
            lines.append("")
            lines.append('</details>')
            lines.append("")

    # Quality Checklist
    for key in ["Quality Checklist", "Quality checklist"]:
        if key in sections:
            lines.append("## Quality Checklist")
            lines.append("")
            lines.append(sections[key])
            lines.append("")
            break

    # Output Format (if separate from template)
    for key in ["Output Format", "Output Contract"]:
        if key in sections:
            lines.append(f"## {key}")
            lines.append("")
            lines.append(sections[key])
            lines.append("")
            break

    # Write the file
    output_path = OUTPUT_DIR / group / f"{name}.md"
    output_path.parent.mkdir(parents=True, exist_ok=True)
    output_path.write_text("\n".join(lines), encoding="utf-8")

    return {
        "name": name,
        "dirname": dirname,
        "group": group,
        "group_display": group_display,
        "description": description,
        "version": version,
        "category": category,
        "command": cmd,
        "output_path": output_path.relative_to(ROOT / "docs"),
    }


# Phase flow diagrams (E-08)
PHASE_FLOWS = {
    "discover": """```mermaid
flowchart LR
    A["/competitive-analysis"] --> D["/problem-statement"]
    B["/interview-synthesis"] --> D
    C["/stakeholder-summary"] --> D
```""",
    "define": """```mermaid
flowchart LR
    A["/problem-statement"] --> B["/hypothesis"]
    A --> C["/jtbd-canvas"]
    C --> D["/opportunity-tree"]
    D --> B
```""",
    "develop": """```mermaid
flowchart LR
    A["/solution-brief"] --> B["/spike-summary"]
    B --> C["/adr"]
    A --> D["/design-rationale"]
    C --> E["/prd"]
    D --> E
```""",
    "deliver": """```mermaid
flowchart LR
    A["/prd"] --> B["/user-stories"]
    B --> C["/acceptance-criteria"]
    A --> D["/edge-cases"]
    C --> E["/launch-checklist"]
    E --> F["/release-notes"]
```""",
    "measure": """```mermaid
flowchart LR
    A["/experiment-design"] --> B["/instrumentation-spec"]
    B --> C["/dashboard-requirements"]
    B --> D["/experiment-results"]
```""",
    "iterate": """```mermaid
flowchart LR
    A["/retrospective"] --> B["/lessons-log"]
    B --> C["/refinement-notes"]
    A --> D["/pivot-decision"]
```""",
}


def generate_phase_index(group: str, skills: list) -> None:
    """Generate a phase/classification index page."""
    display = PHASE_DISPLAY.get(group, group.title())
    output_path = OUTPUT_DIR / group / "index.md"
    output_path.parent.mkdir(parents=True, exist_ok=True)

    lines = []
    lines.append("---")
    lines.append(f'title: "{display} Skills"')
    lines.append(f'description: "PM skills in the {display} phase."')
    lines.append("generated: true")
    lines.append("source: scripts/generate-skill-pages.py")
    lines.append("tags:")
    lines.append(f"  - {display}")
    lines.append("---")
    lines.append("")
    # No body H1: Starlight renders frontmatter title (v2.14.x V15 fix).

    # Generated-content marker: frontmatter (set above). Visible aside removed in V10.

    # Phase flow diagram (E-08)
    if group in PHASE_FLOWS:
        lines.append("## How these skills connect")
        lines.append("")
        lines.append(PHASE_FLOWS[group])
        lines.append("")

    lines.append("## Skills in this phase")
    lines.append("")
    lines.append(f"| Skill | Description | Command |")
    lines.append(f"|-------|-------------|---------|")

    for s in sorted(skills, key=lambda x: x["name"]):
        cmd_str = f"`/{s['command']}`" if s["command"] else "."
        desc = s["description"][:80] + "..." if len(s["description"]) > 80 else s["description"]
        lines.append(f"| [{s['name']}]({s['name']}.md) | {desc} | {cmd_str} |")

    lines.append("")

    output_path.write_text("\n".join(lines), encoding="utf-8")


def generate_commands_reference(all_skills: list) -> None:
    """Generate a commands reference page."""
    output_path = ROOT / "docs" / "reference" / "commands.md"
    output_path.parent.mkdir(parents=True, exist_ok=True)

    # Count actual commands dynamically. total_cmd_count derives from the
    # filesystem so it stays accurate even when commands ship that do not
    # 1-to-1 correspond to a skill (e.g., v2.16.0 added 4 sub-agent companion
    # commands: /audit-repo, /critic, /draft-changelog, /release). The
    # subtraction below isolates sub-agent (or other non-skill, non-workflow)
    # commands so the narrative tracks each commit class.
    total_cmd_count = len(list(COMMANDS_DIR.glob("*.md")))
    skill_cmd_count = sum(1 for s in all_skills if s["command"])
    workflow_cmd_count = len(list(COMMANDS_DIR.glob("workflow-*.md")))
    other_cmd_count = total_cmd_count - skill_cmd_count - workflow_cmd_count
    # Defensive guard: negative other_cmd_count signals an arithmetic bug
    # (skill+workflow counts overcounted vs the filesystem total). Fail loud
    # rather than silently emit internally inconsistent narrative.
    assert other_cmd_count >= 0, (
        f"other_cmd_count={other_cmd_count} is negative; "
        f"total={total_cmd_count}, skill={skill_cmd_count}, workflow={workflow_cmd_count}. "
        f"Check COMMANDS_DIR counting + workflow-*.md glob."
    )

    lines = []
    lines.append("---")
    lines.append('title: "Commands Reference"')
    lines.append('description: "All slash commands available in PM Skills."')
    lines.append("generated: true")
    lines.append("source: scripts/generate-skill-pages.py")
    lines.append("---")
    lines.append("")
    # No body H1: Starlight renders frontmatter title (v2.14.x V15 fix).
    # Generated-content marker: frontmatter (set above). Visible aside removed in V10.
    if other_cmd_count > 0:
        lines.append(
            f"PM Skills ships {total_cmd_count} slash commands: "
            f"{skill_cmd_count} skill commands, {workflow_cmd_count} workflow commands, "
            f"and {other_cmd_count} sub-agent companion commands."
        )
    else:
        lines.append(f"PM Skills ships {total_cmd_count} slash commands: {skill_cmd_count} skill commands plus {workflow_cmd_count} workflow commands.")
    lines.append("")
    lines.append("| Command | Skill | Phase | Description |")
    lines.append("|---------|-------|-------|-------------|")

    for s in sorted(all_skills, key=lambda x: x.get("command", "") or ""):
        if s["command"]:
            desc = s["description"][:60] + "..." if len(s["description"]) > 60 else s["description"]
            lines.append(
                f"| `/{s['command']}` | [{s['name']}](../skills/{s['group']}/{s['name']}.md) "
                f"| {s['group_display']} | {desc} |"
            )

    # Add workflow commands
    lines.append("| `/workflow-customer-discovery` | [Customer Discovery](../workflows/customer-discovery.md) | Workflow | Run the Customer Discovery workflow |")
    lines.append("| `/workflow-feature-kickoff` | [Feature Kickoff](../workflows/feature-kickoff.md) | Workflow | Run the Feature Kickoff workflow |")
    lines.append("| `/workflow-post-launch-learning` | [Post-Launch Learning](../workflows/post-launch-learning.md) | Workflow | Run the Post-Launch Learning workflow |")
    lines.append("| `/workflow-product-strategy` | [Product Strategy](../workflows/product-strategy.md) | Workflow | Run the Product Strategy workflow |")
    lines.append("| `/workflow-sprint-planning` | [Sprint Planning](../workflows/sprint-planning.md) | Workflow | Run the Sprint Planning workflow |")
    lines.append("| `/workflow-stakeholder-alignment` | [Stakeholder Alignment](../workflows/stakeholder-alignment.md) | Workflow | Run the Stakeholder Alignment workflow |")
    lines.append("| `/workflow-technical-discovery` | [Technical Discovery](../workflows/technical-discovery.md) | Workflow | Run the Technical Discovery workflow |")
    lines.append("| `/workflow-foundation-sprint` | [Foundation Sprint](../workflows/foundation-sprint.md) | Workflow | Run the Foundation Sprint workflow |")
    lines.append("| `/workflow-design-sprint` | [Design Sprint](../workflows/design-sprint.md) | Workflow | Run the Design Sprint workflow |")
    lines.append("| `/workflow-foundation-to-design` | [Foundation to Design](../workflows/foundation-to-design.md) | Workflow | Run the Foundation Sprint to Design Sprint workflow |")

    # Add orphan commands (commands present on filesystem but not matched to a
    # 1-1 skill via derive_command_name + find_command_file). v2.16.0 introduced
    # verb-shaped sub-agent companion commands (/pm-audit-repo, /pm-draft-changelog,
    # /pm-release) whose stems intentionally do not match their parent utility
    # skills (utility-pm-skill-auditor, utility-pm-changelog-curator,
    # utility-pm-release-conductor). The previous render path silently dropped
    # these rows even though the narrative claimed "N sub-agent companion
    # commands" via the subtraction count. This block surfaces them in the
    # table so the count and rows agree, and stays robust for any future
    # commands-without-1-1-skill-match.
    if other_cmd_count > 0:
        skill_command_names = {s["command"] for s in all_skills if s["command"]}
        workflow_command_names = {p.stem for p in COMMANDS_DIR.glob("workflow-*.md")}
        for cmd_path in sorted(COMMANDS_DIR.glob("*.md")):
            cmd_name = cmd_path.stem
            if cmd_name in skill_command_names or cmd_name in workflow_command_names:
                continue
            cmd_content = cmd_path.read_text(encoding="utf-8")
            cmd_meta, _ = parse_frontmatter(cmd_content)
            cmd_desc = cmd_meta.get("description", "")
            if len(cmd_desc) > 60:
                cmd_desc = cmd_desc[:60] + "..."
            lines.append(
                f"| `/{cmd_name}` | _([sub-agent companion](sub-agent-compatibility.md))_ | Utility | {cmd_desc} |"
            )

    lines.append("")

    output_path.write_text("\n".join(lines), encoding="utf-8")


def print_nav_snippet(groups: dict) -> None:
    """Print the nav YAML snippet for mkdocs.yml."""
    print("\n# === NAV SNIPPET FOR mkdocs.yml ===")
    print("  - Skills:")
    print("    - skills/index.md")

    for group in PHASE_ORDER:
        if group not in groups:
            continue
        display = PHASE_DISPLAY[group]
        print(f"    - {display}:")
        print(f"      - skills/{group}/index.md")
        for s in sorted(groups[group], key=lambda x: x["name"]):
            print(f"      - skills/{group}/{s['name']}.md")

    print("# === END NAV SNIPPET ===")


def main():
    if not SKILLS_DIR.exists():
        print(f"Error: skills directory not found at {SKILLS_DIR}", file=sys.stderr)
        sys.exit(1)

    all_skills = []
    groups = {}

    # Generate individual skill pages
    for skill_dir in sorted(SKILLS_DIR.iterdir()):
        if not skill_dir.is_dir():
            continue
        info = generate_skill_page(skill_dir)
        if info:
            all_skills.append(info)
            groups.setdefault(info["group"], []).append(info)

    print(f"Generated {len(all_skills)} skill pages")

    # Generate phase index pages
    for group, skills in groups.items():
        generate_phase_index(group, skills)
        print(f"  {PHASE_DISPLAY.get(group, group)}: {len(skills)} skills")

    # Generate commands reference
    generate_commands_reference(all_skills)
    print(f"Generated commands reference page")

    # Print nav snippet
    print_nav_snippet(groups)


if __name__ == "__main__":
    main()
