#!/usr/bin/env python3
"""
Unified Skill Builder

Generates final skill structure from merged multi-source data:
- SKILL.md with merged APIs and conflict warnings
- references/ with organized content by source
- Inline conflict markers (⚠️)
- Separate conflicts summary section

Supports mixed sources (documentation, GitHub, PDF) and highlights
discrepancies transparently.
"""

import json
import logging
import os
import re
import shutil
from pathlib import Path

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)


class UnifiedSkillBuilder:
    """
    Builds unified skill from multi-source data.

    Builder strategy invoked by UnifiedScraper.build_skill() (the registered
    SkillConverter for "config"). Not a SkillConverter itself: it has no
    extract() phase — it only synthesizes the final skill from the in-memory
    scraped_data/merged_data/conflicts that UnifiedScraper's phases 1-3
    produced, so it sits outside the run() template (extract → build_skill)
    by design.
    """

    def __init__(
        self,
        config: dict,
        scraped_data: dict,
        merged_data: dict | None = None,
        conflicts: list | None = None,
        cache_dir: str | None = None,
    ):
        """
        Initialize skill builder.

        Args:
            config: Unified config dict
            scraped_data: Dict of scraped data by source type
            merged_data: Merged API data (if conflicts were resolved)
            conflicts: List of detected conflicts
            cache_dir: Optional cache directory for intermediate files
        """
        self.config = config
        self.scraped_data = scraped_data
        self.merged_data = merged_data
        self.conflicts = conflicts or []
        self.cache_dir = cache_dir

        self.name = config["name"]
        self.description = config["description"]
        # Honor --output (config["output_dir"]) for the final skill.
        from skill_seekers.cli.skill_converter import SkillConverter

        self.skill_dir = SkillConverter.resolve_skill_dir(config, self.name)

        # Create directories
        os.makedirs(self.skill_dir, exist_ok=True)
        os.makedirs(f"{self.skill_dir}/references", exist_ok=True)
        os.makedirs(f"{self.skill_dir}/scripts", exist_ok=True)
        os.makedirs(f"{self.skill_dir}/assets", exist_ok=True)

    def build(self):
        """Build complete skill structure."""
        logger.info(f"Building unified skill: {self.name}")

        # Generate main SKILL.md
        self._generate_skill_md()

        # Generate reference files by source
        self._generate_references()

        # Generate conflicts report (if any)
        if self.conflicts:
            self._generate_conflicts_report()

        logger.info(f"✅ Unified skill built: {self.skill_dir}/")

    def _load_source_skill_mds(self) -> dict[str, str]:
        """Load standalone SKILL.md files from each source.

        Returns:
            Dict mapping source type to SKILL.md content
            e.g., {'documentation': '...', 'github': '...', 'pdf': '...'}
        """
        skill_mds = {}

        # Determine base directory for source SKILL.md files
        sources_dir = Path(self.cache_dir) / "sources" if self.cache_dir else Path("output")

        # Load documentation SKILL.md
        docs_skill_path = sources_dir / f"{self.name}_docs" / "SKILL.md"
        if docs_skill_path.exists():
            try:
                skill_mds["documentation"] = docs_skill_path.read_text(encoding="utf-8")
                logger.debug(
                    f"Loaded documentation SKILL.md ({len(skill_mds['documentation'])} chars)"
                )
            except OSError as e:
                logger.warning(f"Failed to read documentation SKILL.md: {e}")

        # Load ALL GitHub sources (multi-source support)
        github_sources = []
        for github_dir in sources_dir.glob(f"{self.name}_github_*"):
            github_skill_path = github_dir / "SKILL.md"
            if github_skill_path.exists():
                try:
                    content = github_skill_path.read_text(encoding="utf-8")
                    github_sources.append(content)
                    logger.debug(
                        f"Loaded GitHub SKILL.md from {github_dir.name} ({len(content)} chars)"
                    )
                except OSError as e:
                    logger.warning(f"Failed to read GitHub SKILL.md from {github_dir.name}: {e}")

        if github_sources:
            # Concatenate all GitHub sources with separator
            skill_mds["github"] = "\n\n---\n\n".join(github_sources)
            logger.debug(f"Combined {len(github_sources)} GitHub SKILL.md files")

        # Load ALL PDF sources (multi-source support)
        pdf_sources = []
        for pdf_dir in sources_dir.glob(f"{self.name}_pdf_*"):
            pdf_skill_path = pdf_dir / "SKILL.md"
            if pdf_skill_path.exists():
                try:
                    content = pdf_skill_path.read_text(encoding="utf-8")
                    pdf_sources.append(content)
                    logger.debug(f"Loaded PDF SKILL.md from {pdf_dir.name} ({len(content)} chars)")
                except OSError as e:
                    logger.warning(f"Failed to read PDF SKILL.md from {pdf_dir.name}: {e}")

        if pdf_sources:
            # Concatenate all PDF sources with separator
            skill_mds["pdf"] = "\n\n---\n\n".join(pdf_sources)
            logger.debug(f"Combined {len(pdf_sources)} PDF SKILL.md files")

        # Load additional source types using generic glob pattern
        # Each source type uses: {name}_{type}_{idx}_*/ or {name}_{type}_*/
        _extra_types = [
            "word",
            "epub",
            "video",
            "jupyter",
            "html",
            "openapi",
            "asciidoc",
            "pptx",
            "confluence",
            "notion",
            "rss",
            "manpage",
            "chat",
        ]
        for source_type in _extra_types:
            type_sources = []
            for type_dir in sources_dir.glob(f"{self.name}_{source_type}_*"):
                type_skill_path = type_dir / "SKILL.md"
                if type_skill_path.exists():
                    try:
                        content = type_skill_path.read_text(encoding="utf-8")
                        type_sources.append(content)
                        logger.debug(
                            f"Loaded {source_type} SKILL.md from {type_dir.name} "
                            f"({len(content)} chars)"
                        )
                    except OSError as e:
                        logger.warning(
                            f"Failed to read {source_type} SKILL.md from {type_dir.name}: {e}"
                        )

            if type_sources:
                skill_mds[source_type] = "\n\n---\n\n".join(type_sources)
                logger.debug(f"Combined {len(type_sources)} {source_type} SKILL.md files")

        logger.info(f"Loaded {len(skill_mds)} source SKILL.md files")
        return skill_mds

    def _parse_skill_md_sections(self, skill_md: str) -> dict[str, str]:
        """Parse SKILL.md into sections by ## headers.

        Args:
            skill_md: Full SKILL.md content

        Returns:
            Dict mapping section name to content
            e.g., {'When to Use': '...', 'Quick Reference': '...'}
        """
        sections = {}
        current_section = None
        current_content = []

        lines = skill_md.split("\n")

        for line in lines:
            # Detect section header (## Header)
            if line.startswith("## "):
                # Save previous section
                if current_section:
                    sections[current_section] = "\n".join(current_content).strip()

                # Start new section
                current_section = line[3:].strip()
                # Remove emoji and markdown formatting
                current_section = current_section.split("](")[0]  # Remove links
                for emoji in [
                    "📚",
                    "🏗️",
                    "⚠️",
                    "🔧",
                    "📖",
                    "💡",
                    "🎯",
                    "📊",
                    "🔍",
                    "⚙️",
                    "🧪",
                    "📝",
                    "🗂️",
                    "📐",
                    "⚡",
                ]:
                    current_section = current_section.replace(emoji, "").strip()
                current_content = []
            elif current_section:
                # Accumulate content for current section
                current_content.append(line)

        # Save last section
        if current_section and current_content:
            sections[current_section] = "\n".join(current_content).strip()

        logger.debug(f"Parsed {len(sections)} sections from SKILL.md")
        return sections

    def _synthesize_docs_github(self, skill_mds: dict[str, str]) -> str:
        """Synthesize documentation + GitHub sources with weighted merge.

        Strategy:
        - Start with docs frontmatter and intro
        - Add GitHub metadata (stars, topics, language stats)
        - Merge "When to Use" from both sources
        - Merge "Quick Reference" from both sources
        - Include GitHub-specific sections (patterns, architecture)
        - Merge code examples (prioritize GitHub real usage)
        - Include Known Issues from GitHub
        - Fix placeholder text (httpx_docs → httpx)

        Args:
            skill_mds: Dict with 'documentation' and 'github' keys

        Returns:
            Synthesized SKILL.md content
        """
        docs_sections = self._parse_skill_md_sections(skill_mds.get("documentation", ""))
        github_sections = self._parse_skill_md_sections(skill_mds.get("github", ""))

        # Extract GitHub metadata from full content
        _github_full = skill_mds.get("github", "")

        # Start with YAML frontmatter
        skill_name = self.name.lower().replace("_", "-").replace(" ", "-")[:64]
        desc = self.description[:1024] if len(self.description) > 1024 else self.description

        sources = self.config.get("sources", [])
        doc_src = next((s for s in sources if s.get("type") == "documentation"), None)
        gh_src = next((s for s in sources if s.get("type") == "github"), None)
        doc_url = doc_src.get("base_url", "N/A") if doc_src else "N/A"
        gh_repo = gh_src.get("repo", "N/A") if gh_src else "N/A"

        content = f"""---
name: {skill_name}
description: {desc}
---

# {self.name.title()}

{self.description}

## 📚 Sources

This skill synthesizes knowledge from multiple sources:

- ✅ **Official Documentation**: {doc_url}
- ✅ **GitHub Repository**: {gh_repo}

"""

        # Add GitHub Description and Metadata if present
        if "Description" in github_sections:
            content += "## 📦 About\n\n"
            content += github_sections["Description"] + "\n\n"

        # Add Repository Info from GitHub
        if "Repository Info" in github_sections:
            content += "### Repository Info\n\n"
            content += github_sections["Repository Info"] + "\n\n"

        # Add Language stats from GitHub
        if "Languages" in github_sections:
            content += "### Languages\n\n"
            content += github_sections["Languages"] + "\n\n"

        content += "## 💡 When to Use This Skill\n\n"

        # Merge "When to Use" sections - Fix placeholder text
        when_to_use_added = False
        for key in ["When to Use This Skill", "When to Use"]:
            if key in docs_sections:
                # Fix placeholder text: httpx_docs → httpx
                when_content = docs_sections[key].replace("httpx_docs", self.name)
                when_content = when_content.replace("httpx_github", self.name)
                content += when_content + "\n\n"
                when_to_use_added = True
                break

        if "When to Use This Skill" in github_sections:
            if when_to_use_added:
                content += "**From repository analysis:**\n\n"
            content += github_sections["When to Use This Skill"] + "\n\n"

        # Quick Reference: Merge from both sources
        content += "## 🎯 Quick Reference\n\n"

        if "Quick Reference" in docs_sections:
            content += "**From Documentation:**\n\n"
            content += docs_sections["Quick Reference"] + "\n\n"

        if "Quick Reference" in github_sections:
            # Include GitHub's Quick Reference (contains design patterns summary)
            logger.debug(
                "Including GitHub Quick Reference (%d chars)",
                len(github_sections["Quick Reference"]),
            )
            content += github_sections["Quick Reference"] + "\n\n"
        else:
            logger.debug("GitHub Quick Reference section not present in source SKILL.md")

        # Design Patterns (GitHub only - C3.1 analysis)
        if "Design Patterns Detected" in github_sections:
            content += "### Design Patterns Detected\n\n"
            content += "*From C3.1 codebase analysis (confidence > 0.7)*\n\n"
            content += github_sections["Design Patterns Detected"] + "\n\n"

        # Code Examples: Prefer GitHub (real usage)
        content += "## 🧪 Code Examples\n\n"

        if "Code Examples" in github_sections:
            content += "**From Repository Tests:**\n\n"
            # Note: GitHub section already includes "*High-quality examples from codebase (C3.2)*" label
            content += github_sections["Code Examples"] + "\n\n"
        elif "Usage Examples" in github_sections:
            content += "**From Repository:**\n\n"
            content += github_sections["Usage Examples"] + "\n\n"

        if "Example Code Patterns" in docs_sections:
            content += "**From Documentation:**\n\n"
            content += docs_sections["Example Code Patterns"] + "\n\n"

        # API Reference: Include from both sources
        if "API Reference" in docs_sections or "API Reference" in github_sections:
            content += "## 🔧 API Reference\n\n"

            if "API Reference" in github_sections:
                # Note: GitHub section already includes "*Extracted from codebase analysis (C2.5)*" label
                content += github_sections["API Reference"] + "\n\n"

            if "API Reference" in docs_sections:
                content += "**Official API Documentation:**\n\n"
                content += docs_sections["API Reference"] + "\n\n"

        # Known Issues: GitHub only
        if "Known Issues" in github_sections:
            content += "## ⚠️ Known Issues\n\n"
            content += "*Recent issues from GitHub*\n\n"
            content += github_sections["Known Issues"] + "\n\n"

        # Recent Releases: GitHub only (include subsection if present)
        if "Recent Releases" in github_sections:
            # Recent Releases might be a subsection within Known Issues
            # Check if it's standalone
            releases_content = github_sections["Recent Releases"]
            if releases_content.strip() and not releases_content.startswith("###"):
                content += "### Recent Releases\n"
            content += releases_content + "\n\n"

        # Reference documentation
        content += "## 📖 Reference Documentation\n\n"
        content += "Organized by source:\n\n"
        content += "- [Documentation](references/documentation/)\n"
        content += "- [GitHub](references/github/)\n"
        content += "- [Codebase Analysis](references/codebase_analysis/index.md)\n\n"

        # Footer
        content += "---\n\n"
        content += (
            "*Synthesized from official documentation and codebase analysis by Skill Seekers*\n"
        )

        return content

    def _synthesize_docs_github_pdf(self, skill_mds: dict[str, str]) -> str:
        """Synthesize all three sources: documentation + GitHub + PDF.

        Strategy:
        - Start with docs+github synthesis
        - Insert PDF chapters after Quick Reference
        - Add PDF key concepts as supplementary section

        Args:
            skill_mds: Dict with 'documentation', 'github', and 'pdf' keys

        Returns:
            Synthesized SKILL.md content
        """
        # Start with docs+github synthesis
        base_content = self._synthesize_docs_github(skill_mds)
        pdf_sections = self._parse_skill_md_sections(skill_mds.get("pdf", ""))

        # Find insertion point after Quick Reference
        lines = base_content.split("\n")
        insertion_index = -1

        for i, line in enumerate(lines):
            if line.startswith("## 🧪 Code Examples") or line.startswith("## 🔧 API Reference"):
                insertion_index = i
                break

        if insertion_index == -1:
            # Fallback: insert before Reference Documentation
            for i, line in enumerate(lines):
                if line.startswith("## 📖 Reference Documentation"):
                    insertion_index = i
                    break

        # Build PDF section
        pdf_content_lines = []

        # Add Chapter Overview
        if "Chapter Overview" in pdf_sections:
            pdf_content_lines.append("## 📚 PDF Documentation Structure\n")
            pdf_content_lines.append("*From PDF analysis*\n")
            pdf_content_lines.append(pdf_sections["Chapter Overview"])
            pdf_content_lines.append("\n")

        # Add Key Concepts
        if "Key Concepts" in pdf_sections:
            pdf_content_lines.append("## 🔍 Key Concepts\n")
            pdf_content_lines.append("*Extracted from PDF headings*\n")
            pdf_content_lines.append(pdf_sections["Key Concepts"])
            pdf_content_lines.append("\n")

        # Insert PDF content
        if pdf_content_lines and insertion_index != -1:
            lines[insertion_index:insertion_index] = pdf_content_lines
        elif pdf_content_lines:
            # Append before the trailing footer if present, otherwise at the very
            # end. ENH-17: when the base SKILL.md had no Code Examples / API
            # Reference / Reference Documentation heading AND no trailing footer,
            # the assembled PDF content was silently discarded.
            footer_index = -1
            for i, line in enumerate(lines):
                if line.startswith("---") and i > len(lines) - 5:
                    footer_index = i
                    break
            if footer_index != -1:
                lines[footer_index:footer_index] = pdf_content_lines
            else:
                lines.extend(pdf_content_lines)

        # Update reference documentation to include PDF
        final_content = "\n".join(lines)
        final_content = final_content.replace(
            "- [Codebase Analysis](references/codebase_analysis/index.md)\n",
            "- [Codebase Analysis](references/codebase_analysis/index.md)\n- [PDF Documentation](references/pdf/)\n",
        )

        return final_content

    def _generate_skill_md(self):
        """Generate main SKILL.md file using synthesis formulas.

        Strategy:
        1. Try to load standalone SKILL.md from each source
        2. If found, use synthesis formulas for rich content
        3. If not found, fall back to legacy minimal generation
        """
        skill_path = os.path.join(self.skill_dir, "SKILL.md")

        # Try to load source SKILL.md files
        skill_mds = self._load_source_skill_mds()

        # Determine synthesis strategy based on available sources
        has_docs = "documentation" in skill_mds
        has_github = "github" in skill_mds
        has_pdf = "pdf" in skill_mds

        content = None

        # Apply appropriate synthesis formula
        if has_docs and has_github and has_pdf:
            logger.info("Synthesizing: documentation + GitHub + PDF")
            content = self._synthesize_docs_github_pdf(skill_mds)

        elif has_docs and has_github:
            logger.info("Synthesizing: documentation + GitHub")
            content = self._synthesize_docs_github(skill_mds)

        elif has_docs and has_pdf:
            logger.info("Synthesizing: documentation + PDF")
            content = self._synthesize_docs_pdf(skill_mds)

        elif has_github and has_pdf:
            logger.info("Synthesizing: GitHub + PDF")
            content = self._synthesize_github_pdf(skill_mds)

        elif has_docs:
            logger.info("Using documentation SKILL.md as-is")
            content = skill_mds["documentation"]

        elif has_github:
            logger.info("Using GitHub SKILL.md as-is")
            content = skill_mds["github"]

        elif has_pdf:
            logger.info("Using PDF SKILL.md as-is")
            content = skill_mds["pdf"]

        # Generic merge for additional source types not covered by pairwise methods
        if not content and skill_mds:
            # At least one source SKILL.md exists but not docs/github/pdf
            logger.info(f"Generic merge for source types: {list(skill_mds.keys())}")
            content = self._generic_merge(skill_mds)
        elif content and len(skill_mds) > (int(has_docs) + int(has_github) + int(has_pdf)):
            # Pairwise synthesis handled the core types; append additional sources
            extra_types = set(skill_mds.keys()) - {"documentation", "github", "pdf"}
            if extra_types:
                logger.info(f"Appending additional sources: {extra_types}")
                content = self._append_extra_sources(content, skill_mds, extra_types)

        # Fallback: generate minimal SKILL.md (legacy behavior)
        if not content:
            logger.warning("No source SKILL.md files found, generating minimal SKILL.md (legacy)")
            content = self._generate_minimal_skill_md()

        # Ensure frontmatter uses config name/description, not auto-generated slugs
        content = self._normalize_frontmatter(content)

        # Write final content
        with open(skill_path, "w", encoding="utf-8") as f:
            f.write(content)

        logger.info(f"Created SKILL.md ({len(content)} chars, ~{len(content.split())} words)")

    def _normalize_frontmatter(self, content: str) -> str:
        """Ensure SKILL.md frontmatter uses the config name and description.

        Standalone source SKILL.md files may have auto-generated slugs
        (e.g., 'primetween-github-0-kyrylokuzyk-primetween'). This replaces
        the name and description with the canonical values from the config.
        """
        if not content.startswith("---"):
            return content

        end = content.find("---", 3)
        if end == -1:
            return content

        frontmatter = content[3:end]
        body = content[end + 3 :]

        canonical_name = self.name.lower().replace("_", "-").replace(" ", "-")[:64]
        frontmatter = re.sub(
            r"^name:.*$", f"name: {canonical_name}", frontmatter, count=1, flags=re.MULTILINE
        )

        # Handle both single-line and multiline YAML description values
        desc = self.description[:1024] if len(self.description) > 1024 else self.description
        frontmatter = re.sub(
            r"^description:.*(?:\n[ \t]+.*)*$",
            f"description: {desc}",
            frontmatter,
            count=1,
            flags=re.MULTILINE,
        )

        return f"---{frontmatter}---{body}"

    def _synthesize_docs_pdf(self, skill_mds: dict[str, str]) -> str:
        """Synthesize documentation + PDF sources.

        Strategy:
        - Start with docs SKILL.md
        - Insert PDF chapters and key concepts as supplementary sections

        Args:
            skill_mds: Dict with 'documentation' and 'pdf' keys

        Returns:
            Synthesized SKILL.md content
        """
        docs_content = skill_mds["documentation"]
        pdf_sections = self._parse_skill_md_sections(skill_mds["pdf"])

        lines = docs_content.split("\n")
        insertion_index = -1

        # Find insertion point before Reference Documentation
        for i, line in enumerate(lines):
            if line.startswith("## 📖 Reference") or line.startswith("## Reference"):
                insertion_index = i
                break

        # Build PDF sections
        pdf_content_lines = []

        if "Chapter Overview" in pdf_sections:
            pdf_content_lines.append("## 📚 PDF Documentation Structure\n")
            pdf_content_lines.append("*From PDF analysis*\n")
            pdf_content_lines.append(pdf_sections["Chapter Overview"])
            pdf_content_lines.append("\n")

        if "Key Concepts" in pdf_sections:
            pdf_content_lines.append("## 🔍 Key Concepts\n")
            pdf_content_lines.append("*Extracted from PDF headings*\n")
            pdf_content_lines.append(pdf_sections["Key Concepts"])
            pdf_content_lines.append("\n")

        # Insert PDF content
        if pdf_content_lines and insertion_index != -1:
            lines[insertion_index:insertion_index] = pdf_content_lines

        return "\n".join(lines)

    def _synthesize_github_pdf(self, skill_mds: dict[str, str]) -> str:
        """Synthesize GitHub + PDF sources.

        Strategy:
        - Start with GitHub SKILL.md (has C3.x analysis)
        - Add PDF documentation structure as supplementary section

        Args:
            skill_mds: Dict with 'github' and 'pdf' keys

        Returns:
            Synthesized SKILL.md content
        """
        github_content = skill_mds["github"]
        pdf_sections = self._parse_skill_md_sections(skill_mds["pdf"])

        lines = github_content.split("\n")
        insertion_index = -1

        # Find insertion point before Reference Documentation
        for i, line in enumerate(lines):
            if line.startswith("## 📖 Reference") or line.startswith("## Reference"):
                insertion_index = i
                break

        # Build PDF sections
        pdf_content_lines = []

        if "Chapter Overview" in pdf_sections:
            pdf_content_lines.append("## 📚 PDF Documentation Structure\n")
            pdf_content_lines.append("*From PDF analysis*\n")
            pdf_content_lines.append(pdf_sections["Chapter Overview"])
            pdf_content_lines.append("\n")

        # Insert PDF content
        if pdf_content_lines and insertion_index != -1:
            lines[insertion_index:insertion_index] = pdf_content_lines

        return "\n".join(lines)

    # ------------------------------------------------------------------
    # Generic merge system for any combination of source types (v3.2.0+)
    # ------------------------------------------------------------------

    # Human-readable labels for source types
    _SOURCE_LABELS: dict[str, str] = {
        "documentation": "Documentation",
        "github": "GitHub Repository",
        "pdf": "PDF Document",
        "word": "Word Document",
        "epub": "EPUB E-book",
        "video": "Video",
        "local": "Local Codebase",
        "jupyter": "Jupyter Notebook",
        "html": "HTML Document",
        "openapi": "OpenAPI/Swagger Spec",
        "asciidoc": "AsciiDoc Document",
        "pptx": "PowerPoint Presentation",
        "confluence": "Confluence Wiki",
        "notion": "Notion Page",
        "rss": "RSS/Atom Feed",
        "manpage": "Man Page",
        "chat": "Chat Export",
    }

    def _generic_merge(self, skill_mds: dict[str, str]) -> str:
        """Generic merge for any combination of source types.

        Uses a priority-based section ordering approach:
        1. Parse all source SKILL.md files into sections
        2. Collect unique sections across all sources
        3. Merge matching sections with source attribution
        4. Produce a unified SKILL.md

        This preserves the existing pairwise synthesis for docs+github, docs+pdf, etc.
        and handles any other combination generically.

        Args:
            skill_mds: Dict mapping source type to SKILL.md content

        Returns:
            Merged SKILL.md content string
        """
        skill_name = self.name.lower().replace("_", "-").replace(" ", "-")[:64]
        desc = self.description[:1024] if len(self.description) > 1024 else self.description

        # Parse all source SKILL.md files into sections
        all_sections: dict[str, dict[str, str]] = {}
        for source_type, content in skill_mds.items():
            all_sections[source_type] = self._parse_skill_md_sections(content)

        # Determine all unique section names in priority order
        # Sections that appear earlier in sources have higher priority
        seen_sections: list[str] = []
        for _source_type, sections in all_sections.items():
            for section_name in sections:
                if section_name not in seen_sections:
                    seen_sections.append(section_name)

        # Build merged content
        source_labels = ", ".join(self._SOURCE_LABELS.get(t, t.title()) for t in skill_mds)
        lines = [
            "---",
            f"name: {skill_name}",
            f"description: {desc}",
            "---",
            "",
            f"# {self.name.replace('_', ' ').title()}",
            "",
            f"{self.description}",
            "",
            f"*Merged from: {source_labels}*",
            "",
        ]

        # Emit each section, merging content from all sources that have it
        for section_name in seen_sections:
            contributing_sources = [
                (stype, sections[section_name])
                for stype, sections in all_sections.items()
                if section_name in sections
            ]

            if len(contributing_sources) == 1:
                # Single source for this section — emit as-is
                stype, content = contributing_sources[0]
                label = self._SOURCE_LABELS.get(stype, stype.title())
                lines.append(f"## {section_name}")
                lines.append("")
                lines.append(f"*From {label}*")
                lines.append("")
                lines.append(content)
                lines.append("")
            else:
                # Multiple sources — merge with attribution
                lines.append(f"## {section_name}")
                lines.append("")
                for stype, content in contributing_sources:
                    label = self._SOURCE_LABELS.get(stype, stype.title())
                    lines.append(f"### From {label}")
                    lines.append("")
                    lines.append(content)
                    lines.append("")

        lines.append("---")
        lines.append("")
        lines.append("*Generated by Skill Seeker's unified multi-source scraper*")

        return "\n".join(lines)

    def _append_extra_sources(
        self,
        base_content: str,
        skill_mds: dict[str, str],
        extra_types: set[str],
    ) -> str:
        """Append additional source content to existing pairwise-synthesized SKILL.md.

        Used when the core docs+github+pdf synthesis has run, but there are
        additional source types (epub, jupyter, etc.) that need to be included.

        Args:
            base_content: Already-synthesized SKILL.md content
            skill_mds: All source SKILL.md files
            extra_types: Set of extra source type keys to append

        Returns:
            Extended SKILL.md content
        """
        lines = base_content.split("\n")

        # Find the final separator (---) or end of file
        insertion_index = len(lines)
        for i in range(len(lines) - 1, -1, -1):
            if lines[i].strip() == "---":
                insertion_index = i
                break

        # Build extra content
        extra_lines = [""]
        for source_type in sorted(extra_types):
            if source_type not in skill_mds:
                continue
            label = self._SOURCE_LABELS.get(source_type, source_type.title())
            sections = self._parse_skill_md_sections(skill_mds[source_type])

            extra_lines.append(f"## {label} Content")
            extra_lines.append("")

            for section_name, content in sections.items():
                extra_lines.append(f"### {section_name}")
                extra_lines.append("")
                extra_lines.append(content)
                extra_lines.append("")

        lines[insertion_index:insertion_index] = extra_lines

        return "\n".join(lines)

    def _generate_minimal_skill_md(self) -> str:
        """Generate minimal SKILL.md (legacy fallback behavior).

        Used when no source SKILL.md files are available.
        """
        skill_name = self.name.lower().replace("_", "-").replace(" ", "-")[:64]
        desc = self.description[:1024] if len(self.description) > 1024 else self.description

        content = f"""---
name: {skill_name}
description: {desc}
---

# {self.name.title()}

{self.description}

## 📚 Sources

This skill combines knowledge from multiple sources:

"""

        # Source type display keys: type -> (label, primary_key, extra_keys)
        _source_detail_map = {
            "documentation": ("Documentation", "base_url", [("Pages", "max_pages", "unlimited")]),
            "github": (
                "GitHub Repository",
                "repo",
                [("Code Analysis", "code_analysis_depth", "surface"), ("Issues", "max_issues", 0)],
            ),
            "pdf": ("PDF Document", "path", []),
            "word": ("Word Document", "path", []),
            "epub": ("EPUB E-book", "path", []),
            "video": ("Video", "url", []),
            "local": ("Local Codebase", "path", [("Analysis Depth", "analysis_depth", "surface")]),
            "jupyter": ("Jupyter Notebook", "path", []),
            "html": ("HTML Document", "path", []),
            "openapi": ("OpenAPI Spec", "path", []),
            "asciidoc": ("AsciiDoc Document", "path", []),
            "pptx": ("PowerPoint", "path", []),
            "confluence": ("Confluence Wiki", "base_url", []),
            "notion": ("Notion Page", "page_id", []),
            "rss": ("RSS/Atom Feed", "url", []),
            "manpage": ("Man Page", "names", []),
            "chat": ("Chat Export", "path", []),
        }

        # List sources
        for source in self.config.get("sources", []):
            source_type = source["type"]
            display = _source_detail_map.get(source_type, (source_type.title(), "path", []))
            label, primary_key, extras = display
            primary_val = source.get(primary_key, "N/A")
            if isinstance(primary_val, list):
                primary_val = ", ".join(str(v) for v in primary_val)
            content += f"- ✅ **{label}**: {primary_val}\n"
            for extra_label, extra_key, extra_default in extras:
                content += f"  - {extra_label}: {source.get(extra_key, extra_default)}\n"

        # C3.x Architecture & Code Analysis section (if available)
        c3_payloads = self._collect_c3_payloads()
        if c3_payloads:
            content += self._format_c3_summary_section(c3_payloads)

        # Data quality section
        if self.conflicts:
            content += "\n## ⚠️ Data Quality\n\n"
            content += f"**{len(self.conflicts)} conflicts detected** between sources.\n\n"

            # Count by type
            by_type = {}
            for conflict in self.conflicts:
                ctype = (
                    conflict.type if hasattr(conflict, "type") else conflict.get("type", "unknown")
                )
                by_type[ctype] = by_type.get(ctype, 0) + 1

            content += "**Conflict Breakdown:**\n"
            for ctype, count in by_type.items():
                content += f"- {ctype}: {count}\n"

            content += "\nSee `references/conflicts.md` for detailed conflict information.\n"

        # Merged API section (if available)
        if self.merged_data:
            content += self._format_merged_apis()

        # Quick reference from each source
        content += "\n## 📖 Reference Documentation\n\n"
        content += "Organized by source:\n\n"

        for source in self.config.get("sources", []):
            source_type = source["type"]
            content += f"- [{source_type.title()}](references/{source_type}/)\n"

        # When to use this skill
        content += "\n## 💡 When to Use This Skill\n\n"
        content += "Use this skill when you need to:\n"
        content += f"- Understand how to use {self.name}\n"
        content += "- Look up API documentation\n"
        content += "- Find usage examples\n"

        if "github" in self.scraped_data:
            content += "- Check for known issues or recent changes\n"
            content += "- Review release history\n"

        content += "\n---\n\n"
        content += "*Generated by Skill Seeker's unified multi-source scraper*\n"

        return content

    def _format_merged_apis(self) -> str:
        """Format merged APIs section with inline conflict warnings."""
        if not self.merged_data:
            return ""

        content = "\n## 🔧 API Reference\n\n"
        content += "*Merged from documentation and code analysis*\n\n"

        apis = self.merged_data.get("apis", {})

        if not apis:
            return content + "*No APIs to display*\n"

        # Group APIs by status
        matched = {k: v for k, v in apis.items() if v.get("status") == "matched"}
        conflicts = {k: v for k, v in apis.items() if v.get("status") == "conflict"}
        docs_only = {k: v for k, v in apis.items() if v.get("status") == "docs_only"}
        code_only = {k: v for k, v in apis.items() if v.get("status") == "code_only"}

        # Show matched APIs first
        if matched:
            content += "### ✅ Verified APIs\n\n"
            content += "*Documentation and code agree*\n\n"
            for _api_name, api_data in list(matched.items())[:10]:  # Limit to first 10
                content += self._format_api_entry(api_data, inline_conflict=False)

        # Show conflicting APIs with warnings
        if conflicts:
            content += "\n### ⚠️ APIs with Conflicts\n\n"
            content += "*Documentation and code differ*\n\n"
            for _api_name, api_data in list(conflicts.items())[:10]:
                content += self._format_api_entry(api_data, inline_conflict=True)

        # Show undocumented APIs
        if code_only:
            content += "\n### 💻 Undocumented APIs\n\n"
            content += f"*Found in code but not in documentation ({len(code_only)} total)*\n\n"
            for _api_name, api_data in list(code_only.items())[:5]:
                content += self._format_api_entry(api_data, inline_conflict=False)

        # Show removed/missing APIs
        if docs_only:
            content += "\n### 📖 Documentation-Only APIs\n\n"
            content += f"*Documented but not found in code ({len(docs_only)} total)*\n\n"
            for _api_name, api_data in list(docs_only.items())[:5]:
                content += self._format_api_entry(api_data, inline_conflict=False)

        content += "\n*See references/api/ for complete API documentation*\n"

        return content

    def _format_api_entry(self, api_data: dict, inline_conflict: bool = False) -> str:
        """Format a single API entry."""
        name = api_data.get("name", "Unknown")
        signature = api_data.get("merged_signature", api_data.get("signature", name))
        description = api_data.get("merged_description", api_data.get("description", ""))
        warning = api_data.get("warning", "")

        entry = f"#### `{signature}`\n\n"

        if description:
            entry += f"{description}\n\n"

        # Add inline conflict warning
        if inline_conflict and warning:
            entry += f"⚠️ **Conflict**: {warning}\n\n"

            # Show both versions if available
            conflict = api_data.get("conflict", {})
            if conflict:
                docs_info = conflict.get("docs_info")
                code_info = conflict.get("code_info")

                if docs_info and code_info:
                    entry += "**Documentation says:**\n"
                    entry += f"```\n{docs_info.get('raw_signature', 'N/A')}\n```\n\n"
                    entry += "**Code implementation:**\n"
                    entry += f"```\n{self._format_code_signature(code_info)}\n```\n\n"

        # Add source info
        source = api_data.get("source", "unknown")
        entry += f"*Source: {source}*\n\n"

        entry += "---\n\n"

        return entry

    def _format_code_signature(self, code_info: dict) -> str:
        """Format code signature for display."""
        name = code_info.get("name", "")
        params = code_info.get("parameters", [])
        return_type = code_info.get("return_type")

        param_strs = []
        for param in params:
            param_str = param.get("name", "")
            if param.get("type_hint"):
                param_str += f": {param['type_hint']}"
            if param.get("default"):
                param_str += f" = {param['default']}"
            param_strs.append(param_str)

        sig = f"{name}({', '.join(param_strs)})"
        if return_type:
            sig += f" -> {return_type}"

        return sig

    def _generate_references(self):
        """Generate reference files organized by source."""
        logger.info("Generating reference files...")

        # Generate references for each source type (now lists)
        docs_list = self.scraped_data.get("documentation", [])
        if docs_list:
            self._generate_docs_references(docs_list)

        github_list = self.scraped_data.get("github", [])
        if github_list:
            self._generate_github_references(github_list)

        pdf_list = self.scraped_data.get("pdf", [])
        if pdf_list:
            self._generate_pdf_references(pdf_list)

        # Generate references for all additional source types
        _extra_source_types = [
            "word",
            "epub",
            "video",
            "jupyter",
            "html",
            "openapi",
            "asciidoc",
            "pptx",
            "confluence",
            "notion",
            "rss",
            "manpage",
            "chat",
        ]
        for source_type in _extra_source_types:
            source_list = self.scraped_data.get(source_type, [])
            if source_list:
                self._generate_generic_references(source_type, source_list)

        # Generate merged API reference if available
        if self.merged_data:
            self._generate_merged_api_reference()

        # Generate C3.x codebase analysis references if available (multi-source)
        github_list = self.scraped_data.get("github", [])
        for github_source in github_list:
            github_data = github_source.get("data", {})
            if github_data.get("c3_analysis"):
                repo_id = github_source.get("repo_id", "unknown")
                self._generate_c3_analysis_references(repo_id=repo_id)

        # Local sources also carry C3.x output — fixes #363
        self._generate_local_codebase_analysis_references()

        # Top-level index linking each per-source ARCHITECTURE.md (#362).
        # SKILL.md links into `references/codebase_analysis/index.md` instead of
        # the historical `references/codebase_analysis/ARCHITECTURE.md`, which
        # never existed once outputs became per-source-namespaced.
        self._generate_codebase_analysis_index()

    def _generate_docs_references(self, docs_list: list[dict]):
        """Generate references from multiple documentation sources."""
        # Skip if no documentation sources
        if not docs_list:
            return

        docs_dir = os.path.join(self.skill_dir, "references", "documentation")
        os.makedirs(docs_dir, exist_ok=True)

        all_copied_files: list[str] = []

        # Process each documentation source
        for i, doc_source in enumerate(docs_list):
            source_id = doc_source.get("source_id", f"source_{i}")
            base_url = doc_source.get("base_url", "Unknown")
            refs_dir = doc_source.get("refs_dir", "")

            # Create subdirectory for this source
            source_dir = os.path.join(docs_dir, source_id)
            os.makedirs(source_dir, exist_ok=True)

            copied_files: list[str] = []

            if refs_dir and os.path.isdir(refs_dir):
                for entry in sorted(os.listdir(refs_dir)):
                    src_path = os.path.join(refs_dir, entry)
                    dst_path = os.path.join(source_dir, entry)
                    if not os.path.isfile(src_path):
                        continue
                    shutil.copy2(src_path, dst_path)
                    copied_files.append(entry)

            # Create index for this source
            source_index_path = os.path.join(source_dir, "index.md")
            with open(source_index_path, "w", encoding="utf-8") as f:
                f.write(f"# Documentation: {source_id}\n\n")
                f.write(f"**Source**: {base_url}\n\n")
                f.write(f"**Pages**: {doc_source.get('total_pages', 'N/A')}\n\n")

                if copied_files:
                    files_no_index = [p for p in copied_files if p.lower() != "index.md"]
                    f.write("## Files\n\n")
                    for filename in files_no_index:
                        f.write(f"- [{filename}]({filename})\n")
                else:
                    f.write("No reference files available.\n")

            all_copied_files.extend(copied_files)

        # Create main index
        index_path = os.path.join(docs_dir, "index.md")
        with open(index_path, "w", encoding="utf-8") as f:
            f.write("# Documentation References\n\n")
            f.write(f"Combined from {len(docs_list)} documentation sources.\n\n")

            f.write("## Sources\n\n")
            for doc_source in docs_list:
                source_id = doc_source.get("source_id", "unknown")
                base_url = doc_source.get("base_url", "Unknown")
                total_pages = doc_source.get("total_pages", "N/A")
                f.write(
                    f"- [{source_id}]({source_id}/index.md) - {base_url} ({total_pages} pages)\n"
                )

        # Compatibility: if this unified skill contains only one documentation source,
        # also expose the reference files at references/*.md so docs-only SKILL.md files
        # remain accurate and easy to browse.
        non_empty_sources = [
            source_type for source_type, items in self.scraped_data.items() if items
        ]
        if len(docs_list) == 1 and non_empty_sources == ["documentation"]:
            source_id = docs_list[0].get("source_id", "source_0")
            source_dir = os.path.join(docs_dir, source_id)
            root_refs_dir = os.path.join(self.skill_dir, "references")
            for entry in sorted(os.listdir(source_dir)):
                if entry.lower() == "index.md":
                    continue
                src_path = os.path.join(source_dir, entry)
                dst_path = os.path.join(root_refs_dir, entry)
                if os.path.isfile(src_path):
                    shutil.copy2(src_path, dst_path)

        logger.info(f"Created documentation references ({len(docs_list)} sources)")

    def _generate_github_references(self, github_list: list[dict]):
        """Generate references from multiple GitHub sources."""
        # Skip if no GitHub sources
        if not github_list:
            return

        github_dir = os.path.join(self.skill_dir, "references", "github")
        os.makedirs(github_dir, exist_ok=True)

        # Process each GitHub source
        for i, github_source in enumerate(github_list):
            repo = github_source.get("repo", f"repo_{i}")
            repo_id = github_source.get("repo_id", repo.replace("/", "_"))
            github_data = github_source.get("data", {})

            # Create subdirectory for this repo
            repo_dir = os.path.join(github_dir, repo_id)
            os.makedirs(repo_dir, exist_ok=True)

            # Create README reference
            if github_data.get("readme"):
                readme_path = os.path.join(repo_dir, "README.md")
                with open(readme_path, "w", encoding="utf-8") as f:
                    f.write(f"# Repository README: {repo}\n\n")
                    f.write(github_data["readme"])

            # Create issues reference
            if github_data.get("issues"):
                issues_path = os.path.join(repo_dir, "issues.md")
                with open(issues_path, "w", encoding="utf-8") as f:
                    f.write(f"# GitHub Issues: {repo}\n\n")
                    f.write(f"{len(github_data['issues'])} recent issues.\n\n")

                    for issue in github_data["issues"]:  # All issues, no arbitrary limit
                        f.write(f"## #{issue['number']}: {issue['title']}\n\n")
                        f.write(f"**State**: {issue['state']}\n")
                        if issue.get("labels"):
                            f.write(f"**Labels**: {', '.join(issue['labels'])}\n")
                        f.write(f"**URL**: {issue.get('url', 'N/A')}\n\n")

            # Create releases reference
            if github_data.get("releases"):
                releases_path = os.path.join(repo_dir, "releases.md")
                with open(releases_path, "w", encoding="utf-8") as f:
                    f.write(f"# Releases: {repo}\n\n")

                    for release in github_data["releases"]:  # All releases, no arbitrary limit
                        f.write(f"## {release['tag_name']}: {release.get('name', 'N/A')}\n\n")
                        f.write(f"**Published**: {release.get('published_at', 'N/A')[:10]}\n\n")
                        if release.get("body"):
                            f.write(release["body"])  # Full release notes
                            f.write("\n\n")

            # Create index for this repo
            repo_index_path = os.path.join(repo_dir, "index.md")
            repo_info = github_data.get("repo_info", {})
            with open(repo_index_path, "w", encoding="utf-8") as f:
                f.write(f"# GitHub: {repo}\n\n")
                f.write(f"**Stars**: {repo_info.get('stars', 'N/A')}\n")
                f.write(f"**Language**: {repo_info.get('language', 'N/A')}\n")
                f.write(f"**Issues**: {len(github_data.get('issues', []))}\n")
                f.write(f"**Releases**: {len(github_data.get('releases', []))}\n\n")
                f.write("## Files\n\n")
                f.write("- [README.md](README.md)\n")
                if github_data.get("issues"):
                    f.write("- [issues.md](issues.md)\n")
                if github_data.get("releases"):
                    f.write("- [releases.md](releases.md)\n")

        # Create main index
        index_path = os.path.join(github_dir, "index.md")
        with open(index_path, "w", encoding="utf-8") as f:
            f.write("# GitHub References\n\n")
            f.write(f"Combined from {len(github_list)} GitHub repositories.\n\n")

            f.write("## Repositories\n\n")
            for github_source in github_list:
                repo = github_source.get("repo", "unknown")
                repo_id = github_source.get("repo_id", repo.replace("/", "_"))
                github_data = github_source.get("data", {})
                repo_info = github_data.get("repo_info", {})
                stars = repo_info.get("stars", "N/A")
                f.write(f"- [{repo}]({repo_id}/index.md) - {stars} stars\n")

        logger.info(f"Created GitHub references ({len(github_list)} repos)")

    def _generate_pdf_references(self, pdf_list: list[dict]):
        """Generate references from PDF sources."""
        # Skip if no PDF sources
        if not pdf_list:
            return

        pdf_dir = os.path.join(self.skill_dir, "references", "pdf")
        os.makedirs(pdf_dir, exist_ok=True)

        # Create index
        index_path = os.path.join(pdf_dir, "index.md")
        with open(index_path, "w", encoding="utf-8") as f:
            f.write("# PDF Documentation\n\n")
            f.write(f"Reference from {len(pdf_list)} PDF document(s).\n\n")

        logger.info(f"Created PDF references ({len(pdf_list)} sources)")

    def _generate_generic_references(self, source_type: str, source_list: list[dict]):
        """Generate references for any source type using a generic approach.

        Creates a references/<source_type>/ directory with an index and
        copies any data files from the source list.

        Args:
            source_type: The source type key (e.g., 'epub', 'jupyter')
            source_list: List of scraped source dicts for this type
        """
        if not source_list:
            return

        label = self._SOURCE_LABELS.get(source_type, source_type.title())
        type_dir = os.path.join(self.skill_dir, "references", source_type)
        os.makedirs(type_dir, exist_ok=True)

        # Create index
        index_path = os.path.join(type_dir, "index.md")
        with open(index_path, "w", encoding="utf-8") as f:
            f.write(f"# {label} References\n\n")
            f.write(f"Reference from {len(source_list)} {label} source(s).\n\n")

            for i, source_data in enumerate(source_list):
                # Try common ID fields
                source_id = (
                    source_data.get("source_id")
                    or source_data.get(f"{source_type}_id")
                    or source_data.get("notebook_id")
                    or source_data.get("spec_id")
                    or source_data.get("feed_id")
                    or source_data.get("man_id")
                    or source_data.get("chat_id")
                    or f"source_{i}"
                )
                f.write(f"## {source_id}\n\n")

                # Write summary of extracted data
                data = source_data.get("data", {})
                if isinstance(data, dict):
                    for key in ["title", "description", "metadata"]:
                        if key in data:
                            val = data[key]
                            if isinstance(val, str) and val:
                                f.write(f"**{key.title()}:** {val}\n\n")

                # Copy data file if available
                data_file = source_data.get("data_file")
                if data_file and os.path.isfile(data_file):
                    dest = os.path.join(type_dir, f"{source_id}_data.json")
                    import contextlib

                    with contextlib.suppress(OSError):
                        shutil.copy(data_file, dest)

        logger.info(f"Created {label} references ({len(source_list)} sources)")

    def _generate_merged_api_reference(self):
        """Generate merged API reference file."""
        api_dir = os.path.join(self.skill_dir, "references", "api")
        os.makedirs(api_dir, exist_ok=True)

        api_path = os.path.join(api_dir, "merged_api.md")

        with open(api_path, "w") as f:
            f.write("# Merged API Reference\n\n")
            f.write("*Combined from documentation and code analysis*\n\n")

            apis = self.merged_data.get("apis", {})

            for api_name in sorted(apis.keys()):
                api_data = {**apis[api_name], "name": api_name}
                entry = self._format_api_entry(api_data, inline_conflict=True)
                f.write(entry)

        logger.info(f"Created merged API reference ({len(apis)} APIs)")

    def _generate_c3_analysis_references(self, repo_id: str = "github"):
        """Generate codebase analysis references (C3.5) for a specific GitHub source.

        Args:
            repo_id: Repository identifier (e.g., 'encode_httpx') for multi-source support
        """
        # Find the correct github_source from the list
        github_list = self.scraped_data.get("github", [])
        github_source = None
        for source in github_list:
            if source.get("repo_id") == repo_id:
                github_source = source
                break

        if not github_source:
            logger.warning(f"GitHub source with repo_id '{repo_id}' not found")
            return

        github_data = github_source.get("data", {})
        c3_data = github_data.get("c3_analysis")

        if not c3_data:
            return

        self._write_codebase_analysis_references(
            c3_data=c3_data, source_id=repo_id, github_data=github_data
        )

    def _generate_local_codebase_analysis_references(self):
        """Generate codebase analysis references for each local source (#363).

        Local sources store C3.x output (patterns, test_examples, how_to_guides,
        config_patterns, architecture, ...) directly on the source dict. They were
        previously dropped by the builder, which only consumed GitHub sources.
        """
        local_list = self.scraped_data.get("local", [])
        if not local_list:
            return

        c3_keys = (
            "patterns",
            "test_examples",
            "how_to_guides",
            "config_patterns",
            "architecture",
            "api_reference",
            "dependency_graph",
        )
        for local_source in local_list:
            if not any(local_source.get(k) for k in c3_keys):
                continue

            source_id = self._sanitize_source_id(
                local_source.get("source_id") or local_source.get("name") or "local"
            )
            self._write_codebase_analysis_references(
                c3_data=local_source, source_id=source_id, github_data=None
            )

    @staticmethod
    def _sanitize_source_id(raw: str) -> str:
        """Normalize a source identifier to a filesystem-safe directory name."""
        cleaned = re.sub(r"[^\w\-]", "_", raw or "").strip("_")
        return cleaned or "local"

    def _generate_codebase_analysis_index(self):
        """Write `references/codebase_analysis/index.md` (#362).

        Scans the codebase_analysis directory after per-source references have
        been written and produces a single landing page that links to each
        source's ARCHITECTURE.md and any populated subsections (patterns,
        examples, guides, configuration). SKILL.md uses this as a stable link
        target so the path resolves whether the build has one source or many.
        """
        base = os.path.join(self.skill_dir, "references", "codebase_analysis")
        if not os.path.isdir(base):
            return

        sources = []
        for entry in sorted(os.listdir(base)):
            entry_path = os.path.join(base, entry)
            if not os.path.isdir(entry_path):
                continue
            if not os.path.isfile(os.path.join(entry_path, "ARCHITECTURE.md")):
                continue
            sources.append(entry)

        if not sources:
            return

        index_path = os.path.join(base, "index.md")
        subsections = (
            ("patterns", "Design patterns"),
            ("examples", "Usage examples"),
            ("guides", "How-to guides"),
            ("configuration", "Configuration"),
            ("api_reference", "API reference"),
            ("dependencies", "Dependencies"),
        )

        with open(index_path, "w", encoding="utf-8") as f:
            f.write("# Codebase Analysis\n\n")
            f.write(
                "Architecture and code-analysis output from the C3.x pipeline, "
                "organized by source. Each entry links to the per-source "
                "ARCHITECTURE.md and any populated subsections.\n\n"
            )

            for source in sources:
                f.write(f"## {source}\n\n")
                f.write(f"- [Architecture overview]({source}/ARCHITECTURE.md)\n")
                for sub_dir, label in subsections:
                    if os.path.isdir(os.path.join(base, source, sub_dir)):
                        f.write(f"- [{label}]({source}/{sub_dir}/)\n")
                f.write("\n")

        logger.info(f"📚 Created codebase analysis index: {index_path}")

    def _write_codebase_analysis_references(
        self,
        c3_data: dict,
        source_id: str,
        github_data: dict | None = None,
    ):
        """Shared C3.x reference writer used by both GitHub and local sources."""
        c3_dir = os.path.join(self.skill_dir, "references", "codebase_analysis", source_id)
        os.makedirs(c3_dir, exist_ok=True)

        logger.info(f"Generating C3.x codebase analysis references for '{source_id}'...")

        # Generate ARCHITECTURE.md (main deliverable)
        self._generate_architecture_overview(c3_dir, c3_data, github_data or {})

        # Generate subdirectories for each C3.x component
        self._generate_pattern_references(c3_dir, c3_data.get("patterns"))
        self._generate_example_references(c3_dir, c3_data.get("test_examples"))
        self._generate_guide_references(c3_dir, c3_data.get("how_to_guides"))
        self._generate_config_references(c3_dir, c3_data.get("config_patterns"))
        self._copy_architecture_details(c3_dir, c3_data.get("architecture"))
        self._generate_api_reference_references(c3_dir, c3_data.get("api_reference"))
        self._generate_dependency_references(c3_dir, c3_data.get("dependency_graph"))

        logger.info("✅ Created codebase analysis references")

    def _generate_architecture_overview(self, c3_dir: str, c3_data: dict, github_data: dict):
        """Generate comprehensive ARCHITECTURE.md (C3.5 main deliverable)."""
        arch_path = os.path.join(c3_dir, "ARCHITECTURE.md")

        with open(arch_path, "w", encoding="utf-8") as f:
            f.write(f"# {self.name.title()} Architecture Overview\n\n")
            f.write("*Generated from C3.x automated codebase analysis*\n\n")

            # Section 1: Overview
            f.write("## 1. Overview\n\n")
            f.write(f"{self.description}\n\n")

            # Section 2: Architectural Patterns (C3.7)
            if c3_data.get("architecture"):
                arch = c3_data["architecture"]
                patterns = arch.get("patterns", [])
                if patterns:
                    f.write("## 2. Architectural Patterns\n\n")
                    f.write("*Detected architectural patterns from codebase structure*\n\n")
                    for pattern in patterns[:5]:  # Top 5 patterns
                        f.write(f"### {pattern['pattern_name']}\n\n")
                        f.write(f"- **Confidence**: {pattern['confidence']:.2f}\n")
                        if pattern.get("framework"):
                            f.write(f"- **Framework**: {pattern['framework']}\n")
                        if pattern.get("evidence"):
                            f.write(f"- **Evidence**: {', '.join(pattern['evidence'][:3])}\n")
                        f.write("\n")

            # Section 3: Technology Stack
            f.write("## 3. Technology Stack\n\n")

            # Try to get languages from C3.7 architecture analysis first
            languages = {}
            if c3_data.get("architecture"):
                languages = c3_data["architecture"].get("languages", {})

            # If no languages from C3.7, try to get from code_analysis or GitHub data
            if not languages:
                code_analysis = github_data.get("code_analysis", {})
                if code_analysis.get("files_analyzed") and code_analysis.get("languages_analyzed"):
                    # Use code_analysis file counts per language
                    files = code_analysis.get("files", [])
                    lang_counts = {}
                    for file_info in files:
                        lang = file_info.get("language", "Unknown")
                        lang_counts[lang] = lang_counts.get(lang, 0) + 1
                    if lang_counts:
                        languages = lang_counts
                    else:
                        # Fallback: total count attributed to primary language
                        for lang in code_analysis["languages_analyzed"]:
                            languages[lang] = code_analysis["files_analyzed"]
                elif github_data.get("languages"):
                    gh_langs = github_data["languages"]
                    if isinstance(gh_langs, dict):
                        languages = dict.fromkeys(gh_langs, 0)
                    elif isinstance(gh_langs, list):
                        languages = dict.fromkeys(gh_langs, 0)

            if languages:
                f.write("**Languages Detected**:\n")
                for lang, count in sorted(languages.items(), key=lambda x: x[1], reverse=True)[:5]:
                    if isinstance(count, int) and count > 0:
                        f.write(f"- {lang}: {count} files\n")
                    else:
                        f.write(f"- {lang}\n")
                f.write("\n")

            # Add frameworks if available
            if c3_data.get("architecture"):
                frameworks = c3_data["architecture"].get("frameworks_detected", [])
                if frameworks:
                    f.write("**Frameworks & Libraries**:\n")
                    for fw in frameworks[:10]:
                        f.write(f"- {fw}\n")
                    f.write("\n")

            if not languages and not (
                c3_data.get("architecture") and c3_data["architecture"].get("frameworks_detected")
            ):
                f.write("*Technology stack analysis not available*\n\n")

            # Section 4: Design Patterns (C3.1)
            if c3_data.get("patterns"):
                f.write("## 4. Design Patterns\n\n")
                f.write("*Classic design patterns identified in the codebase*\n\n")

                # Summarize pattern types
                pattern_summary = {}
                for file_data in c3_data["patterns"]:
                    for pattern in file_data.get("patterns", []):
                        ptype = pattern["pattern_type"]
                        pattern_summary[ptype] = pattern_summary.get(ptype, 0) + 1

                if pattern_summary:
                    for ptype, count in sorted(
                        pattern_summary.items(), key=lambda x: x[1], reverse=True
                    ):
                        f.write(f"- **{ptype}**: {count} instance(s)\n")
                    f.write(
                        "\n📁 See `references/codebase_analysis/patterns/` for detailed analysis.\n\n"
                    )
                else:
                    f.write("*No design patterns detected.*\n\n")

            # Section 5: Configuration Overview (C3.4)
            if c3_data.get("config_patterns"):
                f.write("## 5. Configuration Overview\n\n")
                config = c3_data["config_patterns"]
                config_files = config.get("config_files", [])

                if config_files:
                    f.write(f"**{len(config_files)} configuration file(s) detected**:\n\n")
                    for cf in config_files[:10]:  # Top 10
                        f.write(f"- **`{cf['relative_path']}`**: {cf['type']}\n")
                        if cf.get("purpose"):
                            f.write(f"  - Purpose: {cf['purpose']}\n")

                    # Add security warnings if available
                    if config.get("ai_enhancements"):
                        insights = config["ai_enhancements"].get("overall_insights", {})
                        security_issues = insights.get("security_issues_found", 0)
                        if security_issues > 0:
                            f.write(
                                f"\n🔐 **Security Alert**: {security_issues} potential security issue(s) found in configurations.\n"
                            )
                            if insights.get("recommended_actions"):
                                f.write("\n**Recommended Actions**:\n")
                                for action in insights["recommended_actions"][:5]:
                                    f.write(f"- {action}\n")
                    f.write(
                        "\n📁 See `references/codebase_analysis/configuration/` for details.\n\n"
                    )
                else:
                    f.write("*No configuration files detected.*\n\n")

            # Section 6: Common Workflows (C3.3)
            if c3_data.get("how_to_guides"):
                f.write("## 6. Common Workflows\n\n")
                guides = c3_data["how_to_guides"].get("guides", [])

                if guides:
                    f.write(f"**{len(guides)} how-to guide(s) extracted from codebase**:\n\n")
                    for guide in guides[:10]:  # Top 10
                        f.write(f"- {guide.get('title', 'Untitled Guide')}\n")
                    f.write(
                        "\n📁 See `references/codebase_analysis/guides/` for detailed tutorials.\n\n"
                    )
                else:
                    f.write("*No workflow guides extracted.*\n\n")

            # Section 7: Usage Examples (C3.2)
            if c3_data.get("test_examples"):
                f.write("## 7. Usage Examples\n\n")
                examples = c3_data["test_examples"]
                total = examples.get("total_examples", 0)
                high_value = examples.get("high_value_count", 0)

                if total > 0:
                    f.write(f"**{total} usage example(s) extracted from tests**:\n")
                    f.write(f"- High-value examples: {high_value}\n")

                    # Category breakdown
                    if examples.get("examples_by_category"):
                        f.write("\n**By Category**:\n")
                        for cat, count in sorted(
                            examples["examples_by_category"].items(),
                            key=lambda x: x[1],
                            reverse=True,
                        ):
                            f.write(f"- {cat}: {count}\n")

                    f.write(
                        "\n📁 See `references/codebase_analysis/examples/` for code samples.\n\n"
                    )
                else:
                    f.write("*No test examples extracted.*\n\n")

            # Section 8: Entry Points & Directory Structure
            f.write("## 8. Entry Points & Directory Structure\n\n")
            f.write("*Analysis based on codebase organization*\n\n")

            if c3_data.get("architecture"):
                dir_struct = c3_data["architecture"].get("directory_structure", {})
                if dir_struct:
                    f.write("**Main Directories**:\n")
                    for dir_name, file_count in sorted(
                        dir_struct.items(), key=lambda x: x[1], reverse=True
                    )[:15]:
                        f.write(f"- `{dir_name}/`: {file_count} file(s)\n")
                    f.write("\n")

            # Footer
            f.write("---\n\n")
            f.write(
                "*This architecture overview was automatically generated by C3.x codebase analysis.*\n"
            )
            f.write("*Last updated: skill build time*\n")

        logger.info("📐 Created ARCHITECTURE.md")

    @staticmethod
    def _make_path_relative(file_path: str) -> str:
        """Strip absolute path prefixes, keeping only the repo-relative path."""
        # Strip .skillseeker-cache repo clone paths
        if ".skillseeker-cache" in file_path:
            # Pattern: ...repos/{idx}_{owner}_{repo}/relative/path
            parts = file_path.split("/repos/")
            if len(parts) > 1:
                # Skip the repo dir name (e.g., '0_Owner_Repo/')
                remainder = parts[1]
                slash_idx = remainder.find("/")
                if slash_idx != -1:
                    return remainder[slash_idx + 1 :]
        # Generic: if it looks absolute, try to make it relative
        if os.path.isabs(file_path):
            return os.path.basename(file_path)
        return file_path

    def _generate_pattern_references(self, c3_dir: str, patterns_data: dict):
        """Generate design pattern references (C3.1)."""
        if not patterns_data:
            return

        patterns_dir = os.path.join(c3_dir, "patterns")
        os.makedirs(patterns_dir, exist_ok=True)

        # Save JSON data
        json_path = os.path.join(patterns_dir, "detected_patterns.json")
        with open(json_path, "w", encoding="utf-8") as f:
            json.dump(patterns_data, f, indent=2, ensure_ascii=False)

        # Create summary markdown
        md_path = os.path.join(patterns_dir, "index.md")
        with open(md_path, "w", encoding="utf-8") as f:
            f.write("# Design Patterns\n\n")
            f.write("*Detected patterns from C3.1 analysis*\n\n")

            for file_data in patterns_data:
                patterns = file_data.get("patterns", [])
                if patterns:
                    display_path = self._make_path_relative(file_data["file_path"])
                    f.write(f"## {display_path}\n\n")
                    for p in patterns:
                        f.write(f"### {p['pattern_type']}\n\n")
                        if p.get("class_name"):
                            f.write(f"- **Class**: `{p['class_name']}`\n")
                        if p.get("confidence"):
                            f.write(f"- **Confidence**: {p['confidence']:.2f}\n")
                        if p.get("indicators"):
                            f.write(f"- **Indicators**: {', '.join(p['indicators'][:3])}\n")
                        f.write("\n")

        logger.info(f"   ✓ Design patterns: {len(patterns_data)} files")

    def _generate_example_references(self, c3_dir: str, examples_data: dict):
        """Generate test example references (C3.2)."""
        if not examples_data:
            return

        examples_dir = os.path.join(c3_dir, "examples")
        os.makedirs(examples_dir, exist_ok=True)

        # Save JSON data
        json_path = os.path.join(examples_dir, "test_examples.json")
        with open(json_path, "w", encoding="utf-8") as f:
            json.dump(examples_data, f, indent=2, ensure_ascii=False)

        # Create summary markdown
        md_path = os.path.join(examples_dir, "index.md")
        with open(md_path, "w", encoding="utf-8") as f:
            f.write("# Usage Examples\n\n")
            f.write("*Extracted from test files (C3.2)*\n\n")

            total = examples_data.get("total_examples", 0)
            high_value = examples_data.get("high_value_count", 0)

            f.write(f"**Total Examples**: {total}\n")
            f.write(f"**High-Value Examples**: {high_value}\n\n")

            # List high-value examples
            examples = examples_data.get("examples", [])
            high_value_examples = [e for e in examples if e.get("confidence", 0) > 0.7]

            if high_value_examples:
                f.write("## High-Value Examples\n\n")
                for ex in high_value_examples[:20]:  # Top 20
                    f.write(f"### {ex.get('description', 'Example')}\n\n")
                    f.write(f"- **Category**: {ex.get('category', 'unknown')}\n")
                    f.write(f"- **Confidence**: {ex.get('confidence', 0):.2f}\n")
                    f.write(f"- **File**: `{ex.get('file_path', 'N/A')}`\n")
                    if ex.get("code_snippet"):
                        lang = ex.get("language", "text")
                        f.write(
                            f"\n```{lang}\n{ex['code_snippet']}\n```\n"
                        )  # Full code, no truncation
                    f.write("\n")

        logger.info(f"   ✓ Test examples: {total} total, {high_value} high-value")

    def _generate_guide_references(self, c3_dir: str, guides_data: dict):
        """Generate how-to guide references (C3.3)."""
        if not guides_data:
            return

        guides_dir = os.path.join(c3_dir, "guides")
        os.makedirs(guides_dir, exist_ok=True)

        # Save JSON collection data
        json_path = os.path.join(guides_dir, "guide_collection.json")
        with open(json_path, "w", encoding="utf-8") as f:
            json.dump(guides_data, f, indent=2, ensure_ascii=False)

        guides = guides_data.get("guides", [])

        # Create index
        md_path = os.path.join(guides_dir, "index.md")
        with open(md_path, "w", encoding="utf-8") as f:
            f.write("# How-To Guides\n\n")
            f.write("*Workflow tutorials extracted from codebase (C3.3)*\n\n")

            f.write(f"**Total Guides**: {len(guides)}\n\n")

            if guides:
                f.write("## Available Guides\n\n")
                for guide in guides:
                    f.write(
                        f"- [{guide.get('title', 'Untitled')}](guide_{guide.get('id', 'unknown')}.md)\n"
                    )
                f.write("\n")

        # Save individual guide markdown files
        for guide in guides:
            guide_id = guide.get("id", "unknown")
            guide_path = os.path.join(guides_dir, f"guide_{guide_id}.md")

            with open(guide_path, "w", encoding="utf-8") as f:
                f.write(f"# {guide.get('title', 'Untitled Guide')}\n\n")

                if guide.get("description"):
                    f.write(f"{guide['description']}\n\n")

                steps = guide.get("steps", [])
                if steps:
                    f.write("## Steps\n\n")
                    for i, step in enumerate(steps, 1):
                        f.write(f"### {i}. {step.get('action', 'Step')}\n\n")
                        if step.get("code_example"):
                            lang = step.get("language", "python")
                            f.write(f"```{lang}\n{step['code_example']}\n```\n\n")
                        if step.get("explanation"):
                            f.write(f"{step['explanation']}\n\n")

        logger.info(f"   ✓ How-to guides: {len(guides)}")

    def _generate_config_references(self, c3_dir: str, config_data: dict):
        """Generate configuration pattern references (C3.4)."""
        if not config_data:
            return

        config_dir = os.path.join(c3_dir, "configuration")
        os.makedirs(config_dir, exist_ok=True)

        # Save JSON data
        json_path = os.path.join(config_dir, "config_patterns.json")
        with open(json_path, "w", encoding="utf-8") as f:
            json.dump(config_data, f, indent=2, ensure_ascii=False)

        # Create summary markdown
        md_path = os.path.join(config_dir, "index.md")
        config_files = config_data.get("config_files", [])

        with open(md_path, "w", encoding="utf-8") as f:
            f.write("# Configuration Patterns\n\n")
            f.write("*Detected configuration files (C3.4)*\n\n")

            f.write(f"**Total Config Files**: {len(config_files)}\n\n")

            if config_files:
                f.write("## Configuration Files\n\n")
                for cf in config_files:
                    f.write(f"### `{cf['relative_path']}`\n\n")
                    f.write(f"- **Type**: {cf['type']}\n")
                    f.write(f"- **Purpose**: {cf.get('purpose', 'N/A')}\n")
                    f.write(f"- **Settings**: {len(cf.get('settings', []))}\n")

                    # Show AI enhancements if available
                    if cf.get("ai_enhancement"):
                        enh = cf["ai_enhancement"]
                        if enh.get("security_concern"):
                            f.write(f"- **Security**: {enh['security_concern']}\n")
                        if enh.get("best_practice"):
                            f.write(f"- **Best Practice**: {enh['best_practice']}\n")

                    f.write("\n")

                # Overall insights
                if config_data.get("ai_enhancements"):
                    insights = config_data["ai_enhancements"].get("overall_insights", {})
                    if insights:
                        f.write("## Overall Insights\n\n")
                        if insights.get("security_issues_found"):
                            f.write(
                                f"🔐 **Security Issues**: {insights['security_issues_found']}\n\n"
                            )
                        if insights.get("recommended_actions"):
                            f.write("**Recommended Actions**:\n")
                            for action in insights["recommended_actions"]:
                                f.write(f"- {action}\n")
                            f.write("\n")

        logger.info(f"   ✓ Configuration files: {len(config_files)}")

    def _copy_architecture_details(self, c3_dir: str, arch_data: dict):
        """Copy architectural pattern JSON details (C3.7)."""
        if not arch_data:
            return

        arch_dir = os.path.join(c3_dir, "architecture_details")
        os.makedirs(arch_dir, exist_ok=True)

        # Save full JSON data
        json_path = os.path.join(arch_dir, "architectural_patterns.json")
        with open(json_path, "w", encoding="utf-8") as f:
            json.dump(arch_data, f, indent=2, ensure_ascii=False)

        # Create summary markdown
        md_path = os.path.join(arch_dir, "index.md")
        with open(md_path, "w", encoding="utf-8") as f:
            f.write("# Architectural Patterns (Detailed)\n\n")
            f.write("*Comprehensive architectural analysis (C3.7)*\n\n")

            patterns = arch_data.get("patterns", [])
            if patterns:
                f.write("## Detected Patterns\n\n")
                for p in patterns:
                    f.write(f"### {p['pattern_name']}\n\n")
                    f.write(f"- **Confidence**: {p['confidence']:.2f}\n")
                    if p.get("framework"):
                        f.write(f"- **Framework**: {p['framework']}\n")
                    if p.get("evidence"):
                        f.write("- **Evidence**:\n")
                        for e in p["evidence"][:5]:
                            f.write(f"  - {e}\n")
                    f.write("\n")

        logger.info(f"   ✓ Architectural details: {len(patterns)} patterns")

    def _generate_api_reference_references(self, c3_dir: str, api_data: dict):
        """Write per-module API reference markdown (C2.5).

        ``api_data`` maps module name -> markdown content (as loaded by
        ``UnifiedScraper._load_api_reference``). Previously this payload was
        carried on the source dict but never written out, so the packaged skill
        lost its API reference entirely.
        """
        if not api_data:
            return

        api_dir = os.path.join(c3_dir, "api_reference")
        os.makedirs(api_dir, exist_ok=True)

        for module_name, content in api_data.items():
            # basename() guards against any path separators in the module key.
            stem = os.path.basename(str(module_name)) or "module"
            with open(os.path.join(api_dir, f"{stem}.md"), "w", encoding="utf-8") as f:
                f.write(content)

        logger.info(f"   ✓ API reference: {len(api_data)} module(s)")

    def _generate_dependency_references(self, c3_dir: str, dep_data: dict):
        """Write the dependency graph (C2.6) JSON plus a short summary index."""
        if not dep_data:
            return

        dep_dir = os.path.join(c3_dir, "dependencies")
        os.makedirs(dep_dir, exist_ok=True)

        json_path = os.path.join(dep_dir, "dependency_graph.json")
        with open(json_path, "w", encoding="utf-8") as f:
            json.dump(dep_data, f, indent=2, ensure_ascii=False)

        nodes = dep_data.get("nodes") or []
        edges = dep_data.get("edges") or []
        md_path = os.path.join(dep_dir, "index.md")
        with open(md_path, "w", encoding="utf-8") as f:
            f.write("# Dependency Graph\n\n")
            f.write("*Module dependency graph from codebase analysis (C2.6)*\n\n")
            f.write(f"- **Modules**: {len(nodes)}\n")
            f.write(f"- **Dependencies**: {len(edges)}\n\n")
            f.write("See `dependency_graph.json` for the full graph.\n")

        logger.info(f"   ✓ Dependency graph: {len(nodes)} module(s), {len(edges)} edge(s)")

    def _collect_c3_payloads(self) -> list[dict]:
        """Gather C3.x analysis payloads across GitHub and local sources (#363).

        Returns:
            List of c3_data dicts, one per source that produced analysis.
        """
        payloads: list[dict] = []

        github_data = self.scraped_data.get("github", {})
        # Handle both dict and list cases
        if isinstance(github_data, list):
            for item in github_data:
                if not isinstance(item, dict):
                    continue
                inner = item.get("data", {})
                if isinstance(inner, dict) and inner.get("c3_analysis"):
                    payloads.append(inner["c3_analysis"])
        elif isinstance(github_data, dict):
            inner = github_data.get("data", {})
            if isinstance(inner, dict) and inner.get("c3_analysis"):
                payloads.append(inner["c3_analysis"])

        for local_source in self.scraped_data.get("local", []) or []:
            if not isinstance(local_source, dict):
                continue
            if any(
                local_source.get(k)
                for k in (
                    "patterns",
                    "test_examples",
                    "how_to_guides",
                    "config_patterns",
                    "architecture",
                )
            ):
                payloads.append(local_source)

        return payloads

    def _format_c3_summary_section(self, c3_payloads) -> str:
        """Format C3.x analysis summary for SKILL.md.

        Accepts either a single c3_data dict (legacy) or a list of payloads
        (post #363, when GitHub + local sources both contribute).
        """
        # Backward-compatible: allow callers passing a single dict
        payloads = [c3_payloads] if isinstance(c3_payloads, dict) else list(c3_payloads or [])

        if not payloads:
            return ""

        content = "\n## 🏗️ Architecture & Code Analysis\n\n"
        content += "*This skill includes comprehensive codebase analysis*\n\n"

        # Primary architecture: take from the first payload that has one
        for payload in payloads:
            arch = payload.get("architecture") or {}
            arch_patterns = arch.get("patterns", []) if isinstance(arch, dict) else []
            if arch_patterns:
                top_pattern = arch_patterns[0]
                content += f"**Primary Architecture**: {top_pattern['pattern_name']}"
                if top_pattern.get("framework"):
                    content += f" ({top_pattern['framework']})"
                content += f" - Confidence: {top_pattern['confidence']:.0%}\n\n"
                break

        # Design patterns: aggregate across payloads
        total_patterns = 0
        pattern_summary: dict[str, int] = {}
        for payload in payloads:
            for file_data in payload.get("patterns") or []:
                for pattern in file_data.get("patterns", []):
                    total_patterns += 1
                    ptype = pattern.get("pattern_type", "Unknown")
                    pattern_summary[ptype] = pattern_summary.get(ptype, 0) + 1
        if total_patterns > 0:
            content += f"**Design Patterns**: {total_patterns} detected\n"
            top_patterns = sorted(pattern_summary.items(), key=lambda x: x[1], reverse=True)[:3]
            if top_patterns:
                content += (
                    f"- Top patterns: {', '.join([f'{p[0]} ({p[1]})' for p in top_patterns])}\n"
                )
            content += "\n"

        # Test examples: sum totals across payloads
        total_examples = 0
        total_high_value = 0
        for payload in payloads:
            examples = payload.get("test_examples") or {}
            if isinstance(examples, dict):
                total_examples += examples.get("total_examples", 0) or 0
                total_high_value += examples.get("high_value_count", 0) or 0
        if total_examples > 0:
            content += (
                f"**Usage Examples**: {total_examples} extracted from tests "
                f"({total_high_value} high-value)\n\n"
            )

        # How-to guides: sum guide counts
        total_guides = 0
        for payload in payloads:
            guides = payload.get("how_to_guides") or {}
            if isinstance(guides, dict):
                total_guides += len(guides.get("guides", []) or [])
        if total_guides > 0:
            content += f"**How-To Guides**: {total_guides} workflow tutorials\n\n"

        # Configuration: sum config files + propagate any security alerts
        total_config_files = 0
        total_security_issues = 0
        for payload in payloads:
            config = payload.get("config_patterns") or {}
            if not isinstance(config, dict):
                continue
            total_config_files += len(config.get("config_files", []) or [])
            ai_block = config.get("ai_enhancements") or {}
            if isinstance(ai_block, dict):
                insights = ai_block.get("overall_insights") or {}
                if isinstance(insights, dict):
                    total_security_issues += insights.get("security_issues_found", 0) or 0
        if total_config_files > 0:
            content += f"**Configuration Files**: {total_config_files} analyzed\n"
            if total_security_issues > 0:
                content += f"- 🔐 **Security Alert**: {total_security_issues} issue(s) detected\n"
            content += "\n"

        # Add link to the codebase-analysis landing page (#362)
        content += (
            "📖 **See** `references/codebase_analysis/index.md` for the per-source "
            "architecture overviews and detailed pattern, example, guide, and "
            "configuration references.\n\n"
        )

        return content

    def _generate_conflicts_report(self):
        """Generate detailed conflicts report."""
        conflicts_path = os.path.join(self.skill_dir, "references", "conflicts.md")

        with open(conflicts_path, "w") as f:
            f.write("# Conflict Report\n\n")
            f.write(f"Found **{len(self.conflicts)}** conflicts between sources.\n\n")

            # Group by severity
            high = [
                c
                for c in self.conflicts
                if (hasattr(c, "severity") and c.severity == "high") or c.get("severity") == "high"
            ]
            medium = [
                c
                for c in self.conflicts
                if (hasattr(c, "severity") and c.severity == "medium")
                or c.get("severity") == "medium"
            ]
            low = [
                c
                for c in self.conflicts
                if (hasattr(c, "severity") and c.severity == "low") or c.get("severity") == "low"
            ]

            f.write("## Severity Breakdown\n\n")
            f.write(f"- 🔴 **High**: {len(high)} (action required)\n")
            f.write(f"- 🟡 **Medium**: {len(medium)} (review recommended)\n")
            f.write(f"- 🟢 **Low**: {len(low)} (informational)\n\n")

            # List high severity conflicts
            if high:
                f.write("## 🔴 High Severity\n\n")
                f.write("*These conflicts require immediate attention*\n\n")

                for conflict in high:
                    api_name = (
                        conflict.api_name
                        if hasattr(conflict, "api_name")
                        else conflict.get("api_name", "Unknown")
                    )
                    diff = (
                        conflict.difference
                        if hasattr(conflict, "difference")
                        else conflict.get("difference", "N/A")
                    )

                    f.write(f"### {api_name}\n\n")
                    f.write(f"**Issue**: {diff}\n\n")

            # List medium severity
            if medium:
                f.write("## 🟡 Medium Severity\n\n")

                for conflict in medium[:20]:  # Limit to 20
                    api_name = (
                        conflict.api_name
                        if hasattr(conflict, "api_name")
                        else conflict.get("api_name", "Unknown")
                    )
                    diff = (
                        conflict.difference
                        if hasattr(conflict, "difference")
                        else conflict.get("difference", "N/A")
                    )

                    f.write(f"### {api_name}\n\n")
                    f.write(f"{diff}\n\n")

        logger.info("Created conflicts report")


if __name__ == "__main__":
    # Test with mock data
    import sys

    if len(sys.argv) < 2:
        print("Usage: python unified_skill_builder.py <config.json>")
        sys.exit(1)

    config_path = sys.argv[1]

    with open(config_path) as f:
        config = json.load(f)

    # Mock scraped data
    scraped_data = {
        "github": {"data": {"readme": "# Test Repository", "issues": [], "releases": []}}
    }

    builder = UnifiedSkillBuilder(config, scraped_data)
    builder.build()

    print(f"\n✅ Test skill built in: output/{config['name']}/")
