"""Pydantic model layer for tool request/response contracts."""

from __future__ import annotations

from enum import StrEnum
from pathlib import Path
from typing import Annotated
from typing import Literal

from pydantic import AnyHttpUrl
from pydantic import BaseModel
from pydantic import ConfigDict
from pydantic import Field
from pydantic import StringConstraints
from pydantic import field_validator
from pydantic import model_validator
from pydantic_core import PydanticCustomError

from .domain.contracts import AuthoringPrimitive
from .domain.contracts import FrameworkName
from .domain.contracts import PrimitiveTranslationGuidance
from .domain.contracts import SupportLevel
from .domain.copilot_artifact_spec import (
    CopilotArtifactFamily,  # noqa: TC001 - Runtime Pydantic type resolution.
)
from .domain.copilot_artifact_spec import (
    CopilotArtifactPack,  # noqa: TC001 - Runtime Pydantic type resolution.
)
from .domain.copilot_artifact_spec import (
    CopilotHookTarget,  # noqa: TC001 - Runtime Pydantic type resolution.
)


type ToolStatus = Literal["success", "warning", "error"]


class ShellScriptType(StrEnum):
    """Supported shell script artifact types generated by init workflows."""

    BASH = "bash"
    ZSH = "zsh"
    POWERSHELL = "powershell"


class DocsDeployProvider(StrEnum):
    """Supported docs deploy providers for generated CI/CD artifacts."""

    GITHUB_PAGES = "github-pages"
    NETLIFY = "netlify"
    VERCEL = "vercel"
    CLOUDFLARE_PAGES = "cloudflare-pages"
    SELF_HOSTED = "self-hosted"
    CUSTOM = "custom"


class BoilerplateGenerationErrorCode(StrEnum):
    """Error codes for gated doc boilerplate generation failures."""

    GATE_NOT_CONFIRMED = "gate-not-confirmed"
    INIT_NOT_COMPLETE = "init-not-complete"
    PROJECT_ROOT_INVALID = "project-root-invalid"


class ReadinessLevel(StrEnum):
    """Project readiness level for docs infrastructure."""

    NONE = "none"
    PARTIAL = "partial"
    FULL = "full"


class ModelBase(BaseModel):
    """Shared base model with strict contracts."""

    model_config = ConfigDict(extra="forbid", str_strip_whitespace=True, frozen=True)


class PipelineContext(ModelBase):
    """Shared context passed between pipeline tools for chaining.

    Each tool in the pipeline (detect → plan → scaffold → compose → enrich →
    validate → score) populates relevant fields so the next tool can
    auto-configure without requiring the user to re-specify parameters.
    """

    framework: FrameworkName | None = Field(
        default=None, description="Detected or specified documentation framework."
    )
    project_root: Path | None = Field(
        default=None, description="Root path of the project being documented."
    )
    docs_root: Path | None = Field(
        default=None, description="Root path of the documentation directory."
    )
    doc_paths: list[str] = Field(
        default_factory=list,
        description="Paths to documentation files created or processed by previous tools.",
    )
    last_tool: str | None = Field(
        default=None, description="Name of the last tool that produced this context."
    )


class ToolSignature(ModelBase):
    """Tool name and argument signature."""

    name: str = Field(description="Tool name exposed by the MCP server.")
    params: list[str] = Field(description="Ordered argument names accepted by the tool.")


class LinkIssue(ModelBase):
    """Issue found while validating documentation links."""

    type: str = Field(description="Issue type identifier.")
    file: str = Field(description="File path where the issue was detected.")
    target: str | None = Field(
        default=None, description="Link target related to the issue when available."
    )


class StructureIssue(ModelBase):
    """Issue found while validating document structure."""

    type: str = Field(description="Issue type identifier.")
    file: str = Field(description="File path where the issue was detected.")
    detail: str = Field(description="Human-readable detail for the issue.")


class QualityIssue(ModelBase):
    """Issue raised during documentation quality scoring."""

    category: Literal["structure", "frontmatter", "primitive-usage", "code-hygiene"] = Field(
        description="Quality category for the issue."
    )
    severity: Literal["low", "medium", "high"] = Field(description="Issue severity.")
    file: str = Field(description="File path where the issue was detected.")
    detail: str = Field(description="Human-readable detail for the issue.")
    suggestion: str | None = Field(default=None, description="Suggested remediation action.")


class GenerateCliDocsRequest(ModelBase):
    """Input contract for CLI documentation generation."""

    cli_command: str = Field(description="CLI command to execute for help extraction.")
    output_file: Path | None = Field(
        default=None, description="Optional output Markdown file path."
    )
    timeout_seconds: int = Field(default=10, ge=1, description="Execution timeout in seconds.")


class GenerateCliDocsResponse(ModelBase):
    """Output contract for CLI documentation generation."""

    status: ToolStatus = Field(description="Tool execution status.")
    tool: str = Field(default="generate_cli_docs", description="Tool identifier.")
    command: str | None = Field(default=None, description="Resolved command executed by the tool.")
    exit_code: int | None = Field(
        default=None, description="Process exit code when a command was run."
    )
    output_file: Path | None = Field(
        default=None, description="Output Markdown file path when provided."
    )
    markdown: str | None = Field(default=None, description="Generated Markdown content.")
    message: str | None = Field(default=None, description="Error or warning details when present.")


class GenerateMcpToolsDocsRequest(ModelBase):
    """Input contract for MCP tool docs generation."""

    target: Path | None = Field(
        default=None, description="Python module path to scan for MCP tools."
    )
    output_file: Path | None = Field(
        default=None, description="Optional output Markdown file path."
    )


class GenerateMcpToolsDocsResponse(ModelBase):
    """Output contract for MCP tool docs generation."""

    status: ToolStatus = Field(description="Tool execution status.")
    tool: str = Field(default="generate_mcp_tools_docs", description="Tool identifier.")
    target: Path | None = Field(default=None, description="Target module path scanned for tools.")
    count: int | None = Field(
        default=None, ge=0, description="Number of tools discovered in the target."
    )
    tools: list[ToolSignature] = Field(
        default_factory=list, description="Discovered tool signatures."
    )
    output_file: Path | None = Field(
        default=None, description="Output Markdown file path when provided."
    )
    markdown: str | None = Field(default=None, description="Generated Markdown content.")
    message: str | None = Field(default=None, description="Error or warning details when present.")


class CheckDocsLinksRequest(ModelBase):
    """Input contract for link validation."""

    docs_root: Path = Field(
        default=Path("docs"), description="Root directory containing Markdown documentation."
    )
    external_mode: Literal["report", "ignore"] = Field(
        default="report",
        description="How external links should be handled during validation.",
    )


class CheckDocsLinksResponse(ModelBase):
    """Output contract for link validation."""

    status: ToolStatus = Field(description="Tool execution status.")
    tool: str = Field(default="check_docs_links", description="Tool identifier.")
    docs_root: Path = Field(description="Validated documentation root directory.")
    issues: list[LinkIssue] = Field(
        default_factory=list, description="Detected link validation issues."
    )
    missing_internal_count: int = Field(
        default=0, ge=0, description="Count of missing internal link issues."
    )
    message: str | None = Field(default=None, description="Error or warning details when present.")


class CheckOrphanDocsRequest(ModelBase):
    """Input contract for orphan document validation."""

    docs_root: Path = Field(
        default=Path("docs"), description="Root directory containing Markdown documentation."
    )
    mkdocs_file: Path = Field(
        default=Path("mkdocs.yml"), description="MkDocs configuration file path."
    )


class CheckOrphanDocsResponse(ModelBase):
    """Output contract for orphan document validation."""

    status: ToolStatus = Field(description="Tool execution status.")
    tool: str = Field(default="check_orphan_docs", description="Tool identifier.")
    docs_root: Path = Field(description="Validated documentation root directory.")
    mkdocs_file: Path = Field(description="MkDocs configuration file path used for nav analysis.")
    orphans: list[str] = Field(
        default_factory=list, description="Markdown paths not referenced in nav."
    )
    message: str | None = Field(default=None, description="Error or warning details when present.")


class CheckLanguageStructureRequest(ModelBase):
    """Input contract for structure validation."""

    docs_root: Path = Field(
        default=Path("docs"), description="Root directory containing Markdown documentation."
    )
    required_headers: list[str] | None = Field(
        default=None,
        description="Required heading strings expected in each Markdown file.",
    )
    required_frontmatter: list[str] | None = Field(
        default=None,
        description="Required frontmatter keys expected in each Markdown file.",
    )


class CheckLanguageStructureResponse(ModelBase):
    """Output contract for structure validation."""

    status: ToolStatus = Field(description="Tool execution status.")
    tool: str = Field(default="check_language_structure", description="Tool identifier.")
    docs_root: Path = Field(description="Validated documentation root directory.")
    required_headers: list[str] = Field(
        default_factory=list, description="Required header checks applied."
    )
    required_frontmatter: list[str] = Field(
        default_factory=list, description="Required frontmatter checks applied."
    )
    issues: list[StructureIssue] = Field(
        default_factory=list, description="Detected structure validation issues."
    )
    message: str | None = Field(default=None, description="Error or warning details when present.")


class ScaffoldDocRequest(ModelBase):
    """Input contract for documentation scaffold generation."""

    doc_path: Path = Field(description="Markdown path to create.")
    title: str = Field(description="Document title used in frontmatter and heading.")
    add_to_nav: bool = Field(
        default=True, description="Whether to append the document to MkDocs nav."
    )
    mkdocs_file: Path = Field(
        default=Path("mkdocs.yml"), description="MkDocs configuration file path."
    )
    description: str = Field(
        default="", description="Optional document description for frontmatter."
    )
    overwrite: bool = Field(
        default=False, description="Whether to overwrite an existing target document."
    )
    framework: FrameworkName | None = Field(
        default=None,
        description="Target documentation framework. Auto-detected when omitted.",
    )


class ScaffoldDocResponse(ModelBase):
    """Output contract for documentation scaffold generation."""

    status: ToolStatus = Field(description="Tool execution status.")
    tool: str = Field(default="scaffold_doc", description="Tool identifier.")
    doc_path: Path = Field(description="Created or targeted Markdown path.")
    title: str | None = Field(default=None, description="Document title used for scaffolding.")
    add_to_nav: bool | None = Field(default=None, description="Whether nav updates were requested.")
    nav_updated: bool | None = Field(default=None, description="Whether MkDocs nav was modified.")
    framework: FrameworkName | None = Field(
        default=None,
        description="Documentation framework used for scaffolding.",
    )
    message: str | None = Field(default=None, description="Error or warning details when present.")
    pipeline_context: PipelineContext | None = Field(
        default=None,
        description="Pipeline context for downstream tool chaining.",
    )


class EnrichDocRequest(ModelBase):
    """Input contract for documentation enrichment."""

    model_config = ConfigDict(extra="forbid", str_strip_whitespace=True, frozen=True)

    doc_path: Path = Field(description="Path to the existing document to enrich.")
    content: str = Field(
        default="", description="Additional content to incorporate into the document."
    )
    framework: FrameworkName | None = Field(
        default=None, description="Target framework. Auto-detected when omitted."
    )
    sections_to_enrich: list[str] = Field(
        default_factory=list,
        description="Specific section headings to enrich. If empty, enriches all TODO sections.",
    )
    overwrite: bool = Field(
        default=False, description="Whether to overwrite existing non-TODO content."
    )


class EnrichDocResponse(ModelBase):
    """Output contract for documentation enrichment."""

    model_config = ConfigDict(extra="forbid", str_strip_whitespace=True, frozen=True)

    status: ToolStatus = Field(description="Tool execution status.")
    doc_path: str = Field(description="Path to the enriched document.")
    sections_enriched: list[str] = Field(
        default_factory=list, description="Section headings that were enriched."
    )
    sections_skipped: list[str] = Field(
        default_factory=list, description="Section headings that were skipped."
    )
    content_added: bool = Field(
        default=False, description="Whether any content was actually added."
    )
    message: str | None = Field(default=None, description="Optional status message.")
    pipeline_context: PipelineContext | None = Field(
        default=None,
        description="Pipeline context for downstream tool chaining.",
    )


class MaterialSnippetRequest(ModelBase):
    """Input contract for Material snippet retrieval."""

    topic: str | None = Field(default=None, description="Specific snippet topic to retrieve.")


class MaterialSnippetResponse(ModelBase):
    """Output contract for Material snippet retrieval."""

    status: ToolStatus = Field(description="Tool execution status.")
    tool: str = Field(
        default="generate_material_reference_snippets", description="Tool identifier."
    )
    topic: str | None = Field(default=None, description="Requested snippet topic.")
    snippet: str | None = Field(default=None, description="Snippet content for a specific topic.")
    available_topics: list[str] = Field(
        default_factory=list, description="Available snippet topic names."
    )
    snippets: dict[str, str] | None = Field(
        default=None, description="Full topic-to-snippet mapping when requested."
    )
    recommended_sections: list[str] = Field(
        default_factory=list, description="Suggested section names for docs pages."
    )
    message: str | None = Field(default=None, description="Error or warning details when present.")


class SourceLineLink(ModelBase):
    """One line-anchored source link rendered for a host-specific URL format."""

    model_config = ConfigDict(extra="forbid", str_strip_whitespace=True, frozen=True)

    host: SourceCodeHost = Field(description="Source-code host used for anchor formatting.")
    repository_url: str = Field(description="Repository base URL without trailing slash.")
    file_path: str = Field(description="Repository-relative file path.")
    line_start: int = Field(ge=1, description="Inclusive starting line number.")
    line_end: int = Field(ge=1, description="Inclusive ending line number.")
    url: str = Field(description="Fully rendered host-specific line anchor URL.")


class ReferenceAuthoringPackResponse(ModelBase):
    """Generated code-block and linking guidance for docs authoring workflows."""

    model_config = ConfigDict(extra="forbid", str_strip_whitespace=True, frozen=True)

    status: ToolStatus = Field(description="Tool execution status.")
    tool: str = Field(default="generate_reference_docs", description="Tool identifier.")
    zensical_mkdocstrings_setup: str = Field(
        description="mkdocstrings[python] setup snippet for Zensical/MkDocs workflows."
    )
    shell_code_blocks: dict[str, str] = Field(
        default_factory=dict,
        description="Standardized shell code blocks keyed by shell name.",
    )
    api_code_blocks: dict[str, str] = Field(
        default_factory=dict,
        description="Standardized API documentation code blocks by scenario.",
    )
    storyteller_mode_blocks: dict[str, str] = Field(
        default_factory=dict,
        description="Interactive storyteller code blocks for deterministic runtime loops.",
    )
    markup_examples: dict[MarkupDialect, str] = Field(
        default_factory=dict,
        description="Markup dialect examples for markdown/mdx/markdoc outputs.",
    )
    source_line_links: list[SourceLineLink] = Field(
        default_factory=list,
        description="Rendered source links with GitHub/GitLab line anchors.",
    )
    message: str | None = Field(default=None, description="Error or warning details when present.")


class CapabilityStrategy(StrEnum):
    """Strategy used to realize a capability in a target framework."""

    NATIVE = "native"
    PLUGIN = "plugin"
    FALLBACK = "fallback"
    UNSUPPORTED = "unsupported"


class InteractionQuestionType(StrEnum):
    """Question archetypes used for deterministic interaction planning."""

    CLARIFICATION = "clarification"
    CONFIRMATION = "confirmation"
    CHOICE = "choice"
    CONSTRAINT = "constraint"


class AnswerSlotType(StrEnum):
    """Supported value encodings for answer slots."""

    TEXT = "text"
    BOOLEAN = "boolean"
    SINGLE_CHOICE = "single-choice"
    MULTI_CHOICE = "multi-choice"


class TurnPlanAction(StrEnum):
    """Deterministic turn actions for multi-turn orchestration."""

    ASK_QUESTIONS = "ask-questions"
    WAIT_FOR_ANSWERS = "wait-for-answers"
    EXECUTE_MODULES = "execute-modules"
    FINALIZE_STORY = "finalize-story"


class StorySessionStatus(StrEnum):
    """Lifecycle state for deterministic multi-turn story sessions."""

    INITIALIZED = "initialized"
    IN_PROGRESS = "in-progress"
    WAITING_FOR_ANSWERS = "waiting-for-answers"
    READY_TO_FINALIZE = "ready-to-finalize"
    COMPLETED = "completed"
    ERROR = "error"


class StoryExplorationStage(StrEnum):
    """Exploration stages used while profiling docs-story context gaps."""

    DISCOVERY = "discovery"
    GAP_ANALYSIS = "gap-analysis"
    FEEDBACK_LOOP = "feedback-loop"
    READY_FOR_GENERATION = "ready-for-generation"


class ExploreStoryStage(StrEnum):
    """Staged storyteller phases used by the explore module orchestrator."""

    MOTIVATION = "motivation"
    API_STORY = "api-story"
    IMPLEMENTATION_STORY = "implementation-story"
    CONSTRAINTS = "constraints"
    VERIFICATION = "verification"


class StoryGapSeverity(StrEnum):
    """Severity levels for identified story-context gaps."""

    LOW = "low"
    MEDIUM = "medium"
    HIGH = "high"
    CRITICAL = "critical"


class StoryFeedbackLoopState(StrEnum):
    """Feedback loop states for interactive story exploration."""

    IDLE = "idle"
    AWAITING_FEEDBACK = "awaiting-feedback"
    INCORPORATING = "incorporating"
    RESOLVED = "resolved"


class StoryMigrationMode(StrEnum):
    """Migration mode used when adapting stories between frameworks."""

    SAME_TARGET = "same-target"
    CROSS_TARGET = "cross-target"


class VisualAssetKind(StrEnum):
    """Kind of visual asset to generate or transform for docs UX surfaces."""

    HEADER = "header"
    SOCIAL_CARD = "social-card"
    ICONS = "icons"
    FAVICON = "favicon"
    BADGE = "badge"
    TOC = "toc"


class VisualAssetOperation(StrEnum):
    """Operation to perform on a visual asset request."""

    RENDER = "render"
    PROMPT_SPEC = "prompt_spec"
    GENERATE_SCRIPTS = "generate_scripts"
    CONVERT_TO_PNG = "convert_to_png"


class VisualAssetFormat(StrEnum):
    """Output format for visual asset conversion."""

    SVG = "svg"
    PNG = "png"
    WEBP = "webp"
    ICO = "ico"


class VisualAssetBackend(StrEnum):
    """Backend runtime used to render or convert visual assets."""

    CAIROSVG = "cairosvg"
    PILLOW = "pillow"
    IMAGEMAGICK = "imagemagick"


class StoryNextQuestionContract(ModelBase):
    """Contract for the next exploration question emitted by the runtime loop."""

    model_config = ConfigDict(extra="forbid", str_strip_whitespace=True, frozen=True)

    question_id: str = Field(description="Stable unique identifier for the next question.")
    question: str = Field(description="User-facing question text to collect missing context.")
    stage: StoryExplorationStage = Field(
        description="Exploration stage that produced this question."
    )
    gap_severity: StoryGapSeverity = Field(
        default=StoryGapSeverity.MEDIUM,
        description="Severity of the context gap this question resolves.",
    )
    feedback_state: StoryFeedbackLoopState = Field(
        default=StoryFeedbackLoopState.IDLE,
        description="Current feedback loop state for this question.",
    )
    rationale: str | None = Field(
        default=None,
        description="Optional rationale explaining why this question is being asked now.",
    )
    required: bool = Field(
        default=True,
        description="Whether this question must be answered before story generation proceeds.",
    )


class ExploreStageContract(ModelBase):
    """Single staged checkpoint used by the explore orchestrator."""

    model_config = ConfigDict(extra="forbid", str_strip_whitespace=True, frozen=True)

    stage: ExploreStoryStage = Field(description="Explore stage identifier.")
    title: str = Field(description="Human-readable title for the stage.")
    objective: str = Field(description="Why this stage matters for story quality.")
    context_key: str = Field(description="Context key expected to satisfy this stage.")
    question_id: str = Field(description="Stable question identifier for this stage.")
    answer_slot_id: str = Field(description="Stable answer-slot identifier for this stage.")
    required: bool = Field(
        default=True,
        description="Whether the stage must be completed before final narrative synthesis.",
    )


class MigrationQualityEnhancementToggles(ModelBase):
    """Quality enhancement toggles applied during migration workflows."""

    model_config = ConfigDict(extra="forbid", str_strip_whitespace=True, frozen=True)

    improve_clarity: bool = Field(
        default=True,
        description="Whether migration should improve clarity and readability.",
    )
    strengthen_structure: bool = Field(
        default=True,
        description="Whether migration should improve section structure and flow.",
    )
    enrich_examples: bool = Field(
        default=False,
        description="Whether migration should add or refine practical examples.",
    )


class MigrationModeContract(ModelBase):
    """Contract describing migration mode, framework endpoints, and quality toggles."""

    model_config = ConfigDict(extra="forbid", str_strip_whitespace=True, frozen=True)

    mode: StoryMigrationMode = Field(
        description="Migration mode indicating same-target or cross-target adaptation."
    )
    source_framework: FrameworkName = Field(description="Framework being migrated from.")
    target_framework: FrameworkName = Field(description="Framework being migrated to.")
    quality_enhancements: MigrationQualityEnhancementToggles = Field(
        default_factory=MigrationQualityEnhancementToggles,
        description="Quality enhancement toggles applied to migration output.",
    )


class SvgPromptRequest(ModelBase):
    """Input contract for generating an SVG-focused creative prompt."""

    model_config = ConfigDict(extra="forbid", str_strip_whitespace=True, frozen=True)

    asset_kind: VisualAssetKind = Field(description="Visual asset kind to generate a prompt for.")
    prompt: str = Field(description="Primary creative prompt for SVG generation.")
    style_notes: str | None = Field(
        default=None,
        description="Optional style notes such as tone, palette, or composition cues.",
    )
    target_size_hint: str | None = Field(
        default=None,
        description="Optional target size hint such as 1200x630 or 64x64.",
    )


class SvgPromptResponse(ModelBase):
    """Output contract containing an SVG generation prompt and metadata."""

    model_config = ConfigDict(extra="forbid", str_strip_whitespace=True, frozen=True)

    status: ToolStatus = Field(description="Prompt generation status.")
    asset_kind: VisualAssetKind = Field(description="Visual asset kind represented by this prompt.")
    svg_prompt: str = Field(description="Generated SVG-focused prompt text.")
    message: str | None = Field(default=None, description="Error or warning details when present.")


class VisualAssetSpec(ModelBase):
    """Deterministic visual asset specification used by prompt tooling."""

    model_config = ConfigDict(extra="forbid", str_strip_whitespace=True, frozen=True)

    asset_kind: VisualAssetKind = Field(description="Visual asset kind represented by this spec.")
    canvas_width: int = Field(description="Recommended canvas width in pixels.", ge=1)
    canvas_height: int = Field(description="Recommended canvas height in pixels.", ge=1)
    view_box: str = Field(description="SVG viewBox value aligned with the recommended canvas size.")
    safe_margin_px: int = Field(
        default=0,
        ge=0,
        description="Recommended safe margin in pixels for text and focal elements.",
    )
    file_stem: str = Field(description="Recommended deterministic output file stem.")
    required_elements: list[str] = Field(
        default_factory=list,
        description="Required composition elements for this asset kind.",
    )
    accessibility_notes: list[str] = Field(
        default_factory=list,
        description="Accessibility guidance for semantic labeling and visual contrast.",
    )
    mcp_surface_notes: list[str] = Field(
        default_factory=list,
        description="MCP-first usage notes for the generated asset and docs surfaces.",
    )
    export_formats: list[VisualAssetFormat] = Field(
        default_factory=list,
        description="Preferred output formats for this asset kind.",
    )


class SvgPromptToolkitResponse(ModelBase):
    """Output contract for deterministic SVG prompt plus visual asset specification."""

    model_config = ConfigDict(extra="forbid", str_strip_whitespace=True, frozen=True)

    status: ToolStatus = Field(description="Prompt/spec generation status.")
    asset_kind: VisualAssetKind = Field(
        description="Visual asset kind represented by this payload."
    )
    svg_prompt: str = Field(description="Generated deterministic SVG-focused prompt text.")
    spec: VisualAssetSpec = Field(description="Deterministic asset specification for the request.")
    deterministic_fingerprint: str = Field(
        description="Stable fingerprint for deterministic prompt/spec generation output."
    )
    message: str | None = Field(default=None, description="Error or warning details when present.")


class VisualAssetConversionRequest(ModelBase):
    """Input contract for converting visual assets across output formats."""

    model_config = ConfigDict(extra="forbid", str_strip_whitespace=True, frozen=True)

    asset_kind: VisualAssetKind = Field(description="Visual asset kind being converted.")
    source_svg: str = Field(description="Source SVG markup to convert.")
    output_format: VisualAssetFormat = Field(description="Desired output format.")
    output_path: Path | None = Field(
        default=None,
        description="Optional destination path for the converted asset.",
    )


class VisualAssetBackendMetadata(ModelBase):
    """Metadata about the backend used for visual asset conversion."""

    model_config = ConfigDict(extra="forbid", str_strip_whitespace=True, frozen=True)

    backend: VisualAssetBackend = Field(description="Backend used for conversion.")
    backend_version: str | None = Field(
        default=None,
        description="Backend version when discoverable at runtime.",
    )
    notes: str | None = Field(
        default=None,
        description="Optional backend or conversion notes for diagnostics.",
    )


class VisualAssetConversionResponse(ModelBase):
    """Output contract for visual asset conversion operations."""

    model_config = ConfigDict(extra="forbid", str_strip_whitespace=True, frozen=True)

    status: ToolStatus = Field(description="Conversion execution status.")
    asset_kind: VisualAssetKind = Field(description="Visual asset kind converted.")
    output_format: VisualAssetFormat = Field(description="Output format produced.")
    backend_metadata: VisualAssetBackendMetadata = Field(
        description="Backend metadata for the conversion operation."
    )
    output_path: Path | None = Field(
        default=None,
        description="Resolved output path when conversion output is written to disk.",
    )
    message: str | None = Field(default=None, description="Error or warning details when present.")


class CreateSvgAssetRequest(ModelBase):
    """Input contract for persisting arbitrary SVG markup to a file."""

    model_config = ConfigDict(extra="forbid", str_strip_whitespace=True, frozen=True)

    svg_markup: str = Field(
        description=(
            "Raw SVG markup string (must begin with <svg)."
            " Typically LLM-generated or user-provided."
        )
    )
    output_path: Path = Field(description="Destination path where the SVG file will be written.")
    asset_kind: VisualAssetKind = Field(
        default=VisualAssetKind.ICONS,
        description="Visual asset kind for metadata classification.",
    )
    convert_to_png: bool = Field(
        default=False,
        description=(
            "Whether to attempt PNG conversion after writing the SVG (requires mmdc or inkscape)."
        ),
    )
    png_output_path: Path | None = Field(
        default=None,
        description=(
            "Explicit PNG output path. Defaults to output_path with .png suffix when omitted."
        ),
    )
    overwrite: bool = Field(
        default=False,
        description="Whether to overwrite an existing file at output_path.",
    )


class CreateSvgAssetResponse(ModelBase):
    """Output contract for SVG asset persistence."""

    model_config = ConfigDict(extra="forbid", str_strip_whitespace=True, frozen=True)

    status: ToolStatus = Field(description="Tool execution status.")
    tool: str = Field(default="create_svg_asset", description="Tool identifier.")
    asset_kind: VisualAssetKind = Field(description="Visual asset kind written.")
    output_path: Path = Field(description="Path where the SVG file was written.")
    file_size_bytes: int = Field(default=0, description="File size in bytes after write.", ge=0)
    canvas_width: int | None = Field(
        default=None,
        description="Parsed canvas width in pixels from SVG width/viewBox attribute.",
    )
    canvas_height: int | None = Field(
        default=None,
        description="Parsed canvas height in pixels from SVG height/viewBox attribute.",
    )
    png_output_path: Path | None = Field(
        default=None,
        description="Path to the PNG file when convert_to_png succeeded.",
    )
    message: str | None = Field(default=None, description="Error or warning details when present.")


class QuestionItemContract(ModelBase):
    """Structured interaction question emitted by orchestration and modules."""

    model_config = ConfigDict(extra="forbid", str_strip_whitespace=True, frozen=True)

    question_id: str = Field(description="Stable unique question identifier.")
    question: str = Field(description="User-facing question text.")
    question_type: InteractionQuestionType = Field(
        default=InteractionQuestionType.CLARIFICATION,
        description="Question archetype used for deterministic turn planning.",
    )
    module_name: str | None = Field(
        default=None,
        description="Module identifier responsible for requesting this question.",
    )
    required: bool = Field(
        default=True,
        description="Whether answering this question is required before finalizing the story.",
    )
    answer_slot_ids: list[str] = Field(
        default_factory=list,
        description="Identifiers of answer slots this question resolves.",
    )


class AnswerSlotContract(ModelBase):
    """Structured user answer slot tied to deterministic interaction planning."""

    model_config = ConfigDict(extra="forbid", str_strip_whitespace=True, frozen=True)

    slot_id: str = Field(description="Stable unique answer slot identifier.")
    slot_type: AnswerSlotType = Field(description="Expected value encoding for this answer slot.")
    prompt: str = Field(
        description="Prompt describing the information expected for this answer slot."
    )
    required: bool = Field(
        default=True,
        description="Whether this slot is required to proceed with deterministic planning.",
    )
    module_name: str | None = Field(
        default=None,
        description="Module identifier that consumes this slot.",
    )
    choices: list[str] = Field(
        default_factory=list,
        description="Available values for choice-based slot types.",
    )
    value: str | bool | list[str] | None = Field(
        default=None,
        description="Resolved value when provided by the user.",
    )


class DeterministicTurnStep(ModelBase):
    """One deterministic turn step in a story interaction plan."""

    model_config = ConfigDict(extra="forbid", str_strip_whitespace=True, frozen=True)

    step_id: str = Field(description="Stable unique step identifier.")
    action: TurnPlanAction = Field(description="Deterministic action for this turn step.")
    module_names: list[str] = Field(
        default_factory=list,
        description="Modules participating in this step.",
    )
    question_ids: list[str] = Field(
        default_factory=list,
        description="Question identifiers expected to be addressed in this step.",
    )
    description: str = Field(description="Human-readable summary of the step intent.")


class DeterministicTurnPlan(ModelBase):
    """Deterministic multi-turn interaction plan for story generation orchestration."""

    model_config = ConfigDict(extra="forbid", str_strip_whitespace=True, frozen=True)

    plan_id: str = Field(description="Stable deterministic plan identifier.")
    current_step_index: int = Field(
        default=0,
        ge=0,
        description="Index of the next step to execute in the plan.",
    )
    steps: list[DeterministicTurnStep] = Field(
        default_factory=list,
        description="Ordered deterministic steps in the interaction plan.",
    )


class ModuleIntentProfile(ModelBase):
    """Intent profile describing how a module participates in interaction planning."""

    model_config = ConfigDict(extra="forbid", str_strip_whitespace=True, frozen=True)

    module_name: str = Field(description="Module identifier represented by this profile.")
    intent_summary: str = Field(
        description="Short statement of the module intent in the story flow."
    )
    required_context_keys: list[str] = Field(
        default_factory=list,
        description="Context keys required by the module before execution.",
    )
    preferred_question_ids: list[str] = Field(
        default_factory=list,
        description="Question identifiers prioritized by this module.",
    )
    consumes_answer_slot_ids: list[str] = Field(
        default_factory=list,
        description="Answer slot identifiers consumed by this module.",
    )


class CapabilitySupportDetail(ModelBase):
    """Framework-specific capability support detail."""

    framework: FrameworkName = Field(description="Framework name this support detail belongs to.")
    strategy: CapabilityStrategy = Field(
        description="Support strategy used in the target framework."
    )
    plugin: str | None = Field(
        default=None, description="Plugin/integration used when strategy is plugin."
    )
    notes: str = Field(description="Implementation note for this framework capability.")


class CapabilityMatrixItem(ModelBase):
    """Capability matrix record with per-framework support details."""

    capability: str = Field(description="Capability identifier.")
    description: str = Field(description="Human-readable description of the capability.")
    details: list[CapabilitySupportDetail] = Field(
        default_factory=list,
        description="Framework-specific support details for this capability.",
    )
    migration_hint: str | None = Field(default=None, description="Actionable migration guidance.")


class CapabilityMatrixV2Response(ModelBase):
    """Output contract for framework capability matrix v2."""

    status: ToolStatus = Field(description="Tool execution status.")
    tool: str = Field(default="get_framework_capability_matrix_v2", description="Tool identifier.")
    items: list[CapabilityMatrixItem] = Field(
        default_factory=list,
        description="Capability matrix entries with plugin/fallback semantics.",
    )
    follow_up_questions: list[str] = Field(
        default_factory=list,
        description="Questions used to tailor capability strategy to project constraints.",
    )
    message: str | None = Field(default=None, description="Error or warning details when present.")


class RuntimeTrack(ModelBase):
    """Deterministic onboarding track for one runtime ecosystem."""

    runtime: str = Field(description="Runtime identifier for the onboarding track.")
    setup_steps: list[str] = Field(default_factory=list, description="Ordered setup commands.")
    verification_commands: list[str] = Field(
        default_factory=list,
        description="Commands used to verify the runtime track.",
    )
    notes: list[str] = Field(
        default_factory=list, description="Additional runtime-specific guidance notes."
    )


# ---------------------------------------------------------------------------
# Deployment URL configuration with constructive validation
# ---------------------------------------------------------------------------

_PLACEHOLDER_DOMAINS = frozenset(
    {
        "example.com",
        "dev.example.com",
        "staging.example.com",
        "localhost",
    }
)


class DeploymentUrlConfig(ModelBase):
    """Project-specific deployment environment URLs.

    Validators reject placeholder domains (``example.com``, ``localhost``) and
    return a ``PydanticCustomError`` with *constructive* guidance that tells the
    caller exactly what is expected.
    """

    model_config = ConfigDict(extra="forbid", str_strip_whitespace=True, frozen=True)

    dev_url: AnyHttpUrl | None = Field(
        default=None,
        description=(
            "Development environment URL (e.g. https://dev.myproject.io). "
            "Leave empty to be prompted during onboarding."
        ),
    )
    staging_url: AnyHttpUrl | None = Field(
        default=None,
        description=(
            "Staging environment URL (e.g. https://staging.myproject.io). "
            "Leave empty to be prompted during onboarding."
        ),
    )
    production_url: AnyHttpUrl | None = Field(
        default=None,
        description=(
            "Production environment URL (e.g. https://myproject.io). "
            "Leave empty to be prompted during onboarding."
        ),
    )

    @field_validator("dev_url", "staging_url", "production_url", mode="after")
    @classmethod
    def reject_placeholder_urls(cls, v: AnyHttpUrl | None) -> AnyHttpUrl | None:
        """Reject ``example.com`` and other placeholder domains with constructive feedback."""
        if v is None:
            return v
        host = str(v.host or "").lower()
        if host in _PLACEHOLDER_DOMAINS:
            _err_type = "placeholder_url_rejected"
            _err_msg = (
                "'{url}' looks like a placeholder. "
                "Please provide your real deployment URL "
                "(e.g. https://docs.your-project.io). "
                "Leave the field empty/null if the URL is not yet known "
                "— the onboarding tool will prompt you interactively."
            )
            raise PydanticCustomError(_err_type, _err_msg, {"url": str(v)})
        return v


class EphemeralInstallRequest(ModelBase):
    """Input contract for ephemeral uvx/npx install-in-tmp-and-copy runs."""

    model_config = ConfigDict(extra="forbid", str_strip_whitespace=True, frozen=True)

    package: str = Field(
        default="mcp-zen-of-docs",
        description="Package name to install ephemerally via uvx or npx.",
    )
    command: str = Field(
        default="mcp-zen-of-docs",
        description="Executable command to invoke after ephemeral install.",
    )
    args: list[str] = Field(
        default_factory=list,
        description="Additional arguments passed to the ephemeral command.",
    )
    copy_artifacts: list[str] = Field(
        default_factory=lambda: ["docs/"],
        description=(
            "Glob patterns or directory names whose generated outputs "
            "are copied from the temp directory back to the project root."
        ),
    )
    project_root: Path = Field(
        default=Path(),
        description="Destination project root for copied artifacts.",
    )
    installer: str = Field(
        default="uvx",
        description="Installer to use: 'uvx' (Python) or 'npx' (Node).",
    )
    source_subdir: str | None = Field(
        default=None,
        description=(
            "If the init command creates a named subdirectory"
            " (e.g. 'site' for Docusaurus/Starlight), "
            "artifacts are copied from tmp_dir/source_subdir rather than tmp_dir directly."
        ),
    )
    stdin_input: Annotated[str, StringConstraints(strip_whitespace=False)] | None = Field(
        default=None,
        description=(
            "Text to pipe to stdin for non-interactive CLIs "
            "(e.g. VitePress init accepts newlines to confirm all prompts)."
        ),
    )

    @field_validator("installer", mode="after")
    @classmethod
    def validate_installer(cls, v: str) -> str:
        """Only allow supported ephemeral installers."""
        allowed = {"uvx", "npx"}
        if v not in allowed:
            _err_type = "unsupported_installer"
            _err_msg = (
                "'{installer}' is not a supported ephemeral installer. Use one of: {allowed}."
            )
            raise PydanticCustomError(
                _err_type, _err_msg, {"installer": v, "allowed": ", ".join(sorted(allowed))}
            )
        return v


class EphemeralInstallResponse(ModelBase):
    """Output contract for ephemeral uvx/npx install-in-tmp-and-copy runs."""

    model_config = ConfigDict(extra="forbid", str_strip_whitespace=True, frozen=True)

    status: ToolStatus = Field(description="Execution status.")
    installer: str = Field(description="Installer that was used (uvx or npx).")
    package: str = Field(description="Package that was installed.")
    tmp_dir: Path | None = Field(
        default=None,
        description="Temporary directory where the package was installed (cleaned up on success).",
    )
    copied_artifacts: list[Path] = Field(
        default_factory=list,
        description="Artifacts copied back to the project root.",
    )
    message: str | None = Field(default=None, description="Error or status details.")


class FrameworkInitSpec(ModelBase):
    """Specification for running a framework's native CLI init in a temporary directory."""

    model_config = ConfigDict(extra="forbid", str_strip_whitespace=True, frozen=True)

    framework: FrameworkName = Field(description="Documentation framework this spec applies to.")
    installer: str = Field(
        description="Installer to use: 'uvx' for Python tools, 'npx' for Node tools.",
    )
    package: str = Field(description="Package name passed to the installer.")
    command: str = Field(
        description=(
            "Executable command to invoke"
            " (e.g. 'zensical' for uvx, 'create-docusaurus@latest' for npx)."
        ),
    )
    init_args: list[str] = Field(
        description="Arguments passed to the init command (e.g. ['new', '.'] for Zensical).",
    )
    source_subdir: str | None = Field(
        default=None,
        description=(
            "If the init command creates a named subdirectory"
            " (e.g. 'site' for Docusaurus/Starlight), "
            "artifacts are copied from that subdirectory rather than the tmp root."
        ),
    )
    copy_artifacts: list[str] = Field(
        description=(
            "Relative paths (within source_subdir or tmp root) to copy to the project root."
        ),
    )
    docs_root: Path = Field(
        description="Final location of the docs content relative to the project root.",
    )
    stdin_input: Annotated[str, StringConstraints(strip_whitespace=False)] | None = Field(
        default=None,
        description=(
            "Text piped to stdin for non-interactive CLIs "
            "(e.g. VitePress init accepts newlines to confirm all prompts)."
        ),
    )


class InitFrameworkStructureRequest(ModelBase):
    """Input contract for initialising a framework's canonical folder structure."""

    model_config = ConfigDict(extra="forbid", str_strip_whitespace=True, frozen=True)

    framework: FrameworkName = Field(
        description="Documentation framework whose native CLI init will be run.",
    )
    project_root: Path = Field(
        default=Path(),
        description="Project root where the generated structure will be written.",
    )
    overwrite: bool = Field(
        default=False,
        description="When True, existing files may be overwritten by the init command.",
    )


class InitFrameworkStructureResponse(ModelBase):
    """Output contract for framework structure initialisation."""

    model_config = ConfigDict(extra="forbid", str_strip_whitespace=True, frozen=True)

    status: ToolStatus = Field(description="Execution status.")
    tool: str = Field(default="init_framework_structure", description="Tool identifier.")
    framework: FrameworkName = Field(description="Framework whose init was executed.")
    docs_root: Path = Field(description="Docs content root relative to project_root.")
    copied_artifacts: list[Path] = Field(
        default_factory=list,
        description="Files and directories copied to the project root.",
    )
    cli_command: str = Field(description="The CLI command that was executed.")
    message: str | None = Field(default=None, description="Error or status details.")


class RuntimeOnboardingMatrixResponse(ModelBase):
    """Output contract for runtime onboarding matrix guidance."""

    status: ToolStatus = Field(description="Tool execution status.")
    tool: str = Field(default="get_runtime_onboarding_matrix", description="Tool identifier.")
    python_tracks: list[RuntimeTrack] = Field(
        default_factory=list,
        description="Python runtime onboarding tracks (uv/uvx/poetry/.venv).",
    )
    js_tracks: list[RuntimeTrack] = Field(
        default_factory=list,
        description="JS/TS documentation framework onboarding tracks.",
    )
    follow_up_questions: list[str] = Field(
        default_factory=list,
        description="Questions used to select the best onboarding track.",
    )
    message: str | None = Field(default=None, description="Error or warning details when present.")


class OnboardingSkeletonRequest(ModelBase):
    """Input contract for onboarding skeleton generation."""

    project_name: str = Field(description="Project name used in generated onboarding content.")
    output_file: Path | None = Field(
        default=None, description="Optional output Markdown file path."
    )
    include_checklist: bool = Field(
        default=True, description="Whether to include contribution checklist content."
    )
    channel: Literal["cli", "mcp"] = Field(
        default="mcp",
        description="Consumer channel requesting onboarding guidance.",
    )


class OnboardingSkeletonResponse(ModelBase):
    """Output contract for onboarding skeleton generation."""

    status: ToolStatus = Field(description="Tool execution status.")
    tool: str = Field(default="generate_onboarding_skeleton", description="Tool identifier.")
    project_name: str = Field(description="Project name used in generated onboarding content.")
    output_file: Path | None = Field(
        default=None, description="Output Markdown file path when provided."
    )
    markdown: str = Field(description="Generated onboarding Markdown content.")
    guidance: OnboardingGuidanceContract | None = Field(
        default=None,
        description="Structured onboarding guidance shared across CLI and MCP flows.",
    )
    message: str | None = Field(default=None, description="Error or warning details when present.")


class ShellScriptArtifactMetadata(ModelBase):
    """Metadata describing a generated shell script artifact."""

    model_config = ConfigDict(extra="forbid", str_strip_whitespace=True, frozen=True)

    shell: ShellScriptType = Field(description="Shell target associated with the generated script.")
    script_path: Path = Field(description="Filesystem path to the generated script artifact.")
    executable: bool = Field(
        description="Whether executable permissions were applied to the generated script."
    )
    generated: bool = Field(
        description="Whether the script artifact was generated in this operation."
    )


class CopilotInitArtifactMetadata(ModelBase):
    """Metadata describing one generated Copilot init artifact."""

    model_config = ConfigDict(extra="forbid", str_strip_whitespace=True, frozen=True)

    artifact_id: str = Field(description="Stable artifact identifier from the domain spec.")
    family: CopilotArtifactFamily = Field(
        description="Artifact family classification from the domain spec."
    )
    pack: CopilotArtifactPack = Field(description="Artifact pack name from the domain spec.")
    file_path: Path = Field(description="Filesystem path to the generated Copilot artifact.")
    required: bool = Field(
        description="Whether the artifact is required for init baseline support."
    )
    hook_target: CopilotHookTarget | None = Field(
        default=None,
        description="Optional hook integration target when the artifact is a hook contract.",
    )
    generated: bool = Field(
        description="Whether the Copilot artifact was generated in this operation."
    )


class DocsDeployPipelineArtifactMetadata(ModelBase):
    """Metadata describing one generated docs deploy pipeline artifact."""

    model_config = ConfigDict(extra="forbid", str_strip_whitespace=True, frozen=True)

    provider: DocsDeployProvider = Field(
        description="Deploy provider used by the workflow artifact."
    )
    workflow_path: Path = Field(
        description="Filesystem path to the generated docs deploy workflow."
    )
    required: bool = Field(
        description="Whether the deploy pipeline artifact is required for init baseline support."
    )
    generated: bool = Field(
        description="Whether the deploy pipeline artifact was generated in this operation."
    )


class InitProjectRequest(ModelBase):
    """Input contract for project initialization workflows."""

    project_root: Path = Field(default=Path(), description="Project root path for initialization.")
    overwrite: bool = Field(
        default=False,
        description="Whether initialization may overwrite existing generated artifacts.",
    )
    include_shell_scripts: bool = Field(
        default=True,
        description="Whether shell setup scripts should be generated during initialization.",
    )
    deploy_provider: DocsDeployProvider = Field(
        default=DocsDeployProvider.GITHUB_PAGES,
        description="Docs deploy provider used for generated CI/CD pipeline artifacts.",
    )


class InitProjectResponse(ModelBase):
    """Output contract for project initialization workflows."""

    model_config = ConfigDict(extra="forbid", str_strip_whitespace=True, frozen=True)

    status: ToolStatus = Field(description="Tool execution status.")
    tool: str = Field(default="init_project", description="Tool identifier.")
    project_root: Path = Field(description="Project root path that was initialized.")
    initialized: bool = Field(description="Whether initialization completed successfully.")
    created_files: list[Path] = Field(
        default_factory=list,
        description="Filesystem paths to files created during initialization.",
    )
    shell_scripts: list[ShellScriptArtifactMetadata] = Field(
        default_factory=list,
        description="Generated shell script artifacts and their metadata.",
    )
    copilot_assets: list[CopilotInitArtifactMetadata] = Field(
        default_factory=list,
        description="Generated Copilot artifacts and their metadata.",
    )
    deploy_pipelines: list[DocsDeployPipelineArtifactMetadata] = Field(
        default_factory=list,
        description="Generated docs deploy pipeline artifacts and their metadata.",
    )
    message: str | None = Field(default=None, description="Error or warning details when present.")


class CheckInitStatusRequest(ModelBase):
    """Input contract for initialization status checks."""

    project_root: Path = Field(
        default=Path(),
        description="Project root path inspected for initialization artifacts.",
    )


class CheckInitStatusResponse(ModelBase):
    """Output contract for initialization status checks."""

    model_config = ConfigDict(extra="forbid", str_strip_whitespace=True, frozen=True)

    status: ToolStatus = Field(description="Tool execution status.")
    tool: str = Field(default="check_init_status", description="Tool identifier.")
    project_root: Path = Field(
        description="Project root path inspected for initialization artifacts."
    )
    initialized: bool = Field(description="Whether the project appears fully initialized.")
    readiness_level: ReadinessLevel = Field(
        default=ReadinessLevel.NONE,
        description="Project readiness level: none, partial, or full.",
    )
    existing_copilot_files: list[Path] = Field(
        default_factory=list,
        description="Pre-existing Copilot artifact files discovered outside the spec.",
    )
    missing_artifacts: list[Path] = Field(
        default_factory=list,
        description="Required initialization artifacts that are currently missing.",
    )
    shell_scripts: list[ShellScriptArtifactMetadata] = Field(
        default_factory=list,
        description="Discovered shell script artifact metadata for initialization.",
    )
    copilot_assets: list[CopilotInitArtifactMetadata] = Field(
        default_factory=list,
        description="Discovered Copilot artifact metadata for initialization.",
    )
    deploy_pipelines: list[DocsDeployPipelineArtifactMetadata] = Field(
        default_factory=list,
        description="Discovered docs deploy pipeline metadata for initialization.",
    )
    message: str | None = Field(default=None, description="Error or warning details when present.")


class GatedBoilerplateGenerationRequest(ModelBase):
    """Input contract for gated boilerplate generation workflows."""

    project_root: Path = Field(
        default=Path(),
        description="Project root path where gated boilerplate should be generated.",
    )
    gate_confirmed: bool = Field(
        default=False,
        description="Whether the caller has confirmed the gate to allow boilerplate generation.",
    )
    overwrite: bool = Field(
        default=False,
        description="Whether boilerplate generation may overwrite existing files.",
    )
    shell_targets: list[ShellScriptType] = Field(
        default_factory=lambda: [
            ShellScriptType.BASH,
            ShellScriptType.ZSH,
            ShellScriptType.POWERSHELL,
        ],
        description="Shell script targets to include during gated boilerplate generation.",
    )


class GatedBoilerplateGenerationResponse(ModelBase):
    """Output contract for gated boilerplate generation workflows."""

    model_config = ConfigDict(extra="forbid", str_strip_whitespace=True, frozen=True)

    status: ToolStatus = Field(description="Tool execution status.")
    tool: str = Field(default="generate_doc_boilerplate", description="Tool identifier.")
    project_root: Path = Field(
        description="Project root path where boilerplate generation was attempted."
    )
    gate_confirmed: bool = Field(
        description="Whether gate confirmation was supplied by the caller."
    )
    boilerplate_generated: bool = Field(
        description="Whether boilerplate artifacts were generated for the request."
    )
    generated_files: list[Path] = Field(
        default_factory=list,
        description="Filesystem paths to generated boilerplate artifacts.",
    )
    shell_scripts: list[ShellScriptArtifactMetadata] = Field(
        default_factory=list,
        description="Generated shell script artifact metadata for the gated flow.",
    )
    missing_init_artifacts: list[Path] = Field(
        default_factory=list,
        description=(
            "Required initialization artifacts missing when gate enforcement blocks generation."
        ),
    )
    error_code: BoilerplateGenerationErrorCode | None = Field(
        default=None,
        description="Typed error code for hard-gate failures, if applicable.",
    )
    message: str | None = Field(default=None, description="Error or warning details when present.")


class StoryGenerationRequest(ModelBase):
    """Input contract for story generation and module composition flows."""

    prompt: str = Field(description="Primary story generation prompt or intent.")
    audience: str | None = Field(
        default=None, description="Optional target audience for story tone and depth."
    )
    modules: list[str] = Field(
        default_factory=list,
        description="Ordered module identifiers to compose into the final story output.",
    )
    context: dict[str, str] = Field(
        default_factory=dict,
        description="Optional context map shared with module composers.",
    )
    include_onboarding_guidance: bool = Field(
        default=False,
        description="Whether onboarding guidance should be included in the response contract.",
    )
    answer_slots: list[AnswerSlotContract] = Field(
        default_factory=list,
        description=(
            "Structured answer slots supplied by the caller for deterministic interaction planning."
        ),
    )
    enable_runtime_loop: bool | None = Field(
        default=None,
        description="Optional runtime loop toggle for iterative session advancement.",
    )
    runtime_max_turns: int | None = Field(
        default=None,
        ge=1,
        description="Optional maximum runtime turns allowed during iterative advancement.",
    )
    auto_advance: bool | None = Field(
        default=None,
        description="Optional auto-advance toggle for deterministic runtime progression.",
    )


class StoryTurnTransition(ModelBase):
    """Single transition record emitted while advancing story session state."""

    model_config = ConfigDict(extra="forbid", str_strip_whitespace=True, frozen=True)

    from_status: StorySessionStatus = Field(
        description="Session status before applying the transition."
    )
    to_status: StorySessionStatus = Field(
        description="Session status after applying the transition."
    )
    action: TurnPlanAction = Field(description="Turn-plan action that triggered this transition.")
    applied_answer_slot_ids: list[str] = Field(
        default_factory=list,
        description="Answer slot identifiers consumed while applying the transition.",
    )
    previous_step_index: int | None = Field(
        default=None,
        ge=0,
        description="Turn-plan step index before applying the transition, when available.",
    )
    next_step_index: int | None = Field(
        default=None,
        ge=0,
        description="Turn-plan step index after applying the transition, when available.",
    )
    reason: str | None = Field(
        default=None,
        description="Optional machine-readable reason for this transition.",
    )
    message: str | None = Field(
        default=None,
        description="Optional human-readable transition details.",
    )


class StorySessionStateContract(ModelBase):
    """Snapshot of iterative story session state across deterministic turns."""

    model_config = ConfigDict(extra="forbid", str_strip_whitespace=True, frozen=True)

    session_id: str = Field(description="Stable unique identifier for the iterative story session.")
    story_id: str | None = Field(
        default=None, description="Stable story identifier associated with this session."
    )
    status: StorySessionStatus = Field(
        description="Current lifecycle status for the story session."
    )
    turn_plan: DeterministicTurnPlan | None = Field(
        default=None,
        description="Current deterministic turn plan governing multi-turn progression.",
    )
    current_step_index: int = Field(
        default=0,
        ge=0,
        description="Current turn-plan step index for the next transition.",
    )
    question_items: list[QuestionItemContract] = Field(
        default_factory=list,
        description="Structured question contracts still relevant for this session state.",
    )
    answer_slots: list[AnswerSlotContract] = Field(
        default_factory=list,
        description="All known answer slots and resolved values for this session.",
    )
    pending_required_slot_ids: list[str] = Field(
        default_factory=list,
        description="Required answer slot identifiers that still need values.",
    )
    transition_history: list[StoryTurnTransition] = Field(
        default_factory=list,
        description="Ordered transition records applied to this session state.",
    )
    last_message: str | None = Field(
        default=None,
        description="Most recent summary message associated with this session snapshot.",
    )


class ModuleOutputContract(ModelBase):
    """Standardized output emitted by each story composition module."""

    module_name: str = Field(description="Module identifier that produced this output.")
    status: ToolStatus = Field(description="Execution status for the module output.")
    content: str | None = Field(
        default=None, description="Primary generated content from the module."
    )
    summary: str | None = Field(default=None, description="Short summary of the module result.")
    metadata: dict[str, str] = Field(
        default_factory=dict, description="Module-specific metadata payload."
    )
    warnings: list[str] = Field(
        default_factory=list, description="Warnings raised while producing the output."
    )
    follow_up_questions: list[str] = Field(
        default_factory=list,
        description=(
            "Explicit follow-up questions for missing context required to finalize this"
            " module output."
        ),
    )
    question_items: list[QuestionItemContract] = Field(
        default_factory=list,
        description="Structured questions emitted by this module.",
    )
    answer_slots: list[AnswerSlotContract] = Field(
        default_factory=list,
        description="Answer slots requested or resolved by this module.",
    )
    intent_profile: ModuleIntentProfile | None = Field(
        default=None,
        description="Optional module intent profile used for deterministic turn planning.",
    )


class OnboardingGuidanceContract(ModelBase):
    """Shared onboarding guidance payload for both CLI and MCP consumers."""

    channel: Literal["cli", "mcp"] = Field(
        default="mcp",
        description="Target consumer for the onboarding guidance payload.",
    )
    project_name: str = Field(description="Project name the guidance applies to.")
    summary: str = Field(description="Short onboarding summary for new contributors.")
    setup_steps: list[str] = Field(
        default_factory=list, description="Ordered environment setup steps."
    )
    verification_commands: list[str] = Field(
        default_factory=list,
        description="Commands used to validate local setup.",
    )
    next_actions: list[str] = Field(
        default_factory=list,
        description="Suggested follow-up actions after completing setup.",
    )
    metadata: dict[str, str] = Field(
        default_factory=dict,
        description="Additional ecosystem-specific guidance metadata.",
    )
    follow_up_questions: list[str] = Field(
        default_factory=list,
        description="Optional prompts to tailor setup for alternate ecosystems/frameworks.",
    )


class StoryGenerationResponse(ModelBase):
    """Output contract for composed story generation responses."""

    status: ToolStatus = Field(description="Story generation status.")
    tool: str = Field(
        default="generate_story", description="Logical tool identifier for story generation."
    )
    story_id: str | None = Field(default=None, description="Optional unique story identifier.")
    title: str | None = Field(default=None, description="Story title.")
    narrative: str | None = Field(
        default=None, description="Combined narrative generated by module composition."
    )
    module_outputs: list[ModuleOutputContract] = Field(
        default_factory=list,
        description="Per-module outputs included in the composed story.",
    )
    onboarding_guidance: OnboardingGuidanceContract | None = Field(
        default=None,
        description="Optional shared onboarding guidance contract.",
    )
    follow_up_questions: list[str] = Field(
        default_factory=list,
        description=(
            "Aggregated follow-up questions for the user when composition context is incomplete."
        ),
    )
    question_items: list[QuestionItemContract] = Field(
        default_factory=list,
        description="Aggregated structured question items for object-driven interaction.",
    )
    answer_slots: list[AnswerSlotContract] = Field(
        default_factory=list,
        description="Aggregated answer slots for deterministic multi-turn interaction.",
    )
    turn_plan: DeterministicTurnPlan | None = Field(
        default=None,
        description="Deterministic interaction plan used to orchestrate follow-up turns.",
    )
    module_intent_profiles: list[ModuleIntentProfile] = Field(
        default_factory=list,
        description="Per-module intent profiles for object-driven orchestration.",
    )
    message: str | None = Field(default=None, description="Error or warning details when present.")


class OrchestratorResultContract(ModelBase):
    """Top-level orchestration result payload for story composition flows."""

    status: ToolStatus = Field(description="Overall orchestrator status.")
    orchestrator: str = Field(default="story-orchestrator", description="Orchestrator identifier.")
    request: StoryGenerationRequest = Field(description="Validated story generation request.")
    response: StoryGenerationResponse = Field(
        description="Resolved story generation response payload."
    )
    completed_modules: list[str] = Field(
        default_factory=list,
        description="Modules completed successfully during orchestration.",
    )
    failed_modules: list[str] = Field(
        default_factory=list,
        description="Modules that failed during orchestration.",
    )
    duration_ms: int | None = Field(
        default=None, ge=0, description="Optional orchestration duration in milliseconds."
    )
    message: str | None = Field(default=None, description="Error or warning details when present.")


class PrimitiveCatalogResponse(ModelBase):
    """Output contract for authoring primitive catalog and support matrix."""

    status: ToolStatus = Field(description="Tool execution status.")
    tool: str = Field(default="list_authoring_primitives", description="Tool identifier.")
    primitives: list[AuthoringPrimitive] = Field(description="All supported authoring primitives.")
    frameworks: list[FrameworkName] = Field(
        description="Frameworks currently available for dispatch."
    )
    support_matrix: dict[str, dict[str, SupportLevel]] = Field(
        default_factory=dict,
        description="Support-level lookup keyed by framework then primitive.",
    )


class PrimitiveSupportLookupRequest(ModelBase):
    """Input contract for support-level lookup requests."""

    framework: FrameworkName = Field(description="Framework to inspect.")
    primitive: AuthoringPrimitive = Field(description="Authoring primitive to inspect.")


class PrimitiveSupportLookupResponse(ModelBase):
    """Output contract for support-level lookup requests."""

    status: ToolStatus = Field(description="Tool execution status.")
    tool: str = Field(default="lookup_primitive_support", description="Tool identifier.")
    framework: FrameworkName = Field(description="Framework inspected.")
    primitive: AuthoringPrimitive = Field(description="Primitive inspected.")
    support_level: SupportLevel = Field(
        description="Support level for the framework/primitive pair."
    )
    message: str | None = Field(default=None, description="Error or warning details when present.")


class RenderPrimitiveSnippetRequest(ModelBase):
    """Input contract for framework primitive snippet rendering."""

    framework: FrameworkName = Field(description="Framework to render for.")
    primitive: AuthoringPrimitive = Field(description="Primitive to render.")
    topic: str | None = Field(
        default=None, description="Optional topic to interpolate into snippets."
    )


class RenderPrimitiveSnippetResponse(ModelBase):
    """Output contract for framework primitive snippet rendering."""

    status: ToolStatus = Field(description="Tool execution status.")
    tool: str = Field(default="render_framework_primitive", description="Tool identifier.")
    framework: FrameworkName = Field(description="Framework rendered.")
    primitive: AuthoringPrimitive = Field(description="Primitive rendered.")
    support_level: SupportLevel = Field(
        description="Support level for the framework/primitive pair."
    )
    snippet: str | None = Field(default=None, description="Rendered framework-specific snippet.")
    message: str | None = Field(default=None, description="Error or warning details when present.")


class TranslatePrimitiveSyntaxRequest(ModelBase):
    """Input contract for primitive syntax translation guidance."""

    source_framework: FrameworkName = Field(description="Framework being migrated from.")
    target_framework: FrameworkName = Field(description="Framework being migrated to.")
    primitive: AuthoringPrimitive = Field(description="Primitive to translate.")
    topic: str | None = Field(
        default=None, description="Optional topic to interpolate into snippets."
    )


class TranslatePrimitiveSyntaxResponse(ModelBase):
    """Output contract for primitive syntax translation guidance."""

    status: ToolStatus = Field(description="Tool execution status.")
    tool: str = Field(default="translate_primitive_syntax", description="Tool identifier.")
    source_framework: FrameworkName = Field(description="Framework being migrated from.")
    target_framework: FrameworkName = Field(description="Framework being migrated to.")
    primitive: AuthoringPrimitive = Field(description="Primitive being translated.")
    source_support_level: SupportLevel = Field(description="Support in the source framework.")
    target_support_level: SupportLevel = Field(description="Support in the target framework.")
    source_snippet: str | None = Field(
        default=None, description="Rendered source framework snippet."
    )
    target_snippet: str | None = Field(
        default=None, description="Rendered target framework snippet."
    )
    hints: list[str] = Field(default_factory=list, description="Actionable migration guidance.")
    message: str | None = Field(default=None, description="Error or warning details when present.")


class QualityScore(ModelBase):
    """Quality scoring dimensions for generated documentation artifacts."""

    readability: int = Field(ge=0, le=100, description="Readability score from 0 to 100.")
    completeness: int = Field(
        ge=0, le=100, description="Coverage/completeness score from 0 to 100."
    )
    consistency: int = Field(ge=0, le=100, description="Consistency score from 0 to 100.")
    overall: int = Field(ge=0, le=100, description="Aggregate quality score from 0 to 100.")


class FrameworkDetectionResult(ModelBase):
    """Result payload for framework detection and compatibility scoring."""

    framework: FrameworkName = Field(description="Detected documentation framework.")
    support_level: SupportLevel = Field(
        description="How well the framework is currently supported."
    )
    confidence: float = Field(
        ge=0.0, le=1.0, description="Detection confidence score from 0.0 to 1.0."
    )
    authoring_primitives: list[AuthoringPrimitive] = Field(
        default_factory=list,
        description="Detected authoring primitives in project documentation.",
    )
    matched_signals: list[str] = Field(
        default_factory=list, description="Signals that matched the detected framework."
    )
    missing_signals: list[str] = Field(
        default_factory=list, description="Expected signals that were not detected."
    )
    quality_score: QualityScore | None = Field(
        default=None, description="Optional quality scoring metadata."
    )


class PrimitiveResolutionMode(StrEnum):
    """Resolution mode for primitive consolidation workflows."""

    SUPPORT = "support"
    RENDER = "render"


class DocsValidationCheck(StrEnum):
    """Validation dimensions available via consolidated validate_docs."""

    LINKS = "links"
    ORPHANS = "orphans"
    STRUCTURE = "structure"


class OnboardProjectMode(StrEnum):
    """Execution mode for consolidated onboarding workflows."""

    SKELETON = "skeleton"
    INIT = "init"
    BOILERPLATE = "boilerplate"
    FULL = "full"


class GenerateReferenceDocsKind(StrEnum):
    """Reference-doc generation kind for consolidated docs tooling."""

    CLI = "cli"
    MCP_TOOLS = "mcp-tools"
    MATERIAL_SNIPPETS = "material-snippets"
    AUTHORING_PACK = "authoring-pack"
    DOCSTRING_PACK = "docstring-pack"
    PROJECT_MANIFEST = "project-manifest"


class SourceCodeHost(StrEnum):
    """Supported source-code hosts for line-anchor link generation.

    Each value maps to a distinct URL scheme for file/line-range deep links:

    - ``github``      — github.com and GitHub Enterprise (``blob/main/f#L10-L20``)
    - ``gitlab``      — gitlab.com and self-hosted GitLab (``-/blob/main/f#L10-20``)
    - ``bitbucket``   — bitbucket.org (``src/HEAD/f#lines-10:20``)
    - ``azure-devops``— dev.azure.com / Azure Repos (query-string anchors)
    - ``gitea``       — Gitea and Forgejo instances (``src/branch/main/f#L10-L20``)
    - ``codeberg``    — codeberg.org (Forgejo-based, same scheme as Gitea)
    - ``self-hosted`` — generic fallback; uses GitHub-style anchors
    """

    GITHUB = "github"
    GITLAB = "gitlab"
    BITBUCKET = "bitbucket"
    AZURE_DEVOPS = "azure-devops"
    GITEA = "gitea"
    CODEBERG = "codeberg"
    SELF_HOSTED = "self-hosted"


class MarkupDialect(StrEnum):
    """Supported rich-markup dialects used in generated authoring packs."""

    MARKDOWN = "markdown"
    MDX = "mdx"
    MARKDOC = "markdoc"


class DocstringLanguage(StrEnum):
    """Source-code languages supported by docstring audit and optimization tools."""

    PYTHON = "python"
    TYPESCRIPT = "typescript"
    GO = "go"
    RUST = "rust"
    JAVA = "java"
    CSHARP = "csharp"


class DocstringStyle(StrEnum):
    """Canonical docstring style per programming language."""

    GOOGLE = "google"
    NUMPY = "numpy"
    TSDOC = "tsdoc"
    JSDOC = "jsdoc"
    GODOC = "godoc"
    RUSTDOC = "rustdoc"
    JAVADOC = "javadoc"
    XMLDOC = "xmldoc"


class DocstringSymbol(ModelBase):
    """A public symbol that is missing or has an incomplete docstring."""

    model_config = ConfigDict(extra="forbid", str_strip_whitespace=True, frozen=True)

    name: str = Field(description="Symbol name as it appears in the source file.")
    kind: str = Field(description="Symbol kind: function, method, class, struct, or enum.")
    line: int = Field(description="1-based line number of the symbol declaration.")
    existing_comment: str | None = Field(
        default=None,
        description="Existing comment text found immediately after the declaration, if any.",
    )
    suggested_docstring: str = Field(
        description="Canonical docstring stub to insert in the target language/style."
    )
    language: DocstringLanguage = Field(description="Language of the source file.")


class DocstringAuditRequest(ModelBase):
    """Input contract for the audit_docstrings tool."""

    model_config = ConfigDict(extra="forbid", str_strip_whitespace=True, frozen=True)

    source_path: Path = Field(
        description="Path to a source file or directory. Directories are scanned recursively."
    )
    language: DocstringLanguage | None = Field(
        default=None,
        description="Source language. Auto-detected from file extension when omitted.",
    )
    include_private: bool = Field(
        default=False,
        description="When True, include private/unexported symbols in the audit.",
    )
    min_coverage: float = Field(
        default=0.8,
        ge=0.0,
        le=1.0,
        description="Minimum acceptable documented-symbol ratio (0.0-1.0). "
        "Response status is 'warning' when coverage falls below this threshold.",
    )


class DocstringAuditResponse(ModelBase):
    """Response from the audit_docstrings tool."""

    model_config = ConfigDict(extra="forbid", str_strip_whitespace=True, frozen=True)

    status: ToolStatus = Field(description="Tool execution status.")
    tool: str = Field(default="audit_docstrings", description="Tool name.")
    source_path: Path = Field(description="Audited path (file or directory).")
    language: DocstringLanguage = Field(description="Detected or supplied source language.")
    style: DocstringStyle = Field(description="Canonical docstring style for the language.")
    total_symbols: int = Field(description="Total public symbols scanned.")
    documented_symbols: int = Field(description="Symbols with an existing docstring.")
    coverage: float = Field(
        ge=0.0, le=1.0, description="Fraction of symbols that are documented (0.0-1.0)."
    )
    missing: list[DocstringSymbol] = Field(
        description="Symbols that are undocumented or have incomplete docstrings."
    )
    message: str | None = Field(default=None, description="Human-readable summary or error.")


class DocstringOptimizerRequest(ModelBase):
    """Input contract for the optimize_docstrings tool."""

    model_config = ConfigDict(extra="forbid", str_strip_whitespace=True, frozen=True)

    source_path: Path = Field(description="Path to a source file or directory to optimize.")
    language: DocstringLanguage | None = Field(
        default=None,
        description="Source language. Auto-detected from file extension when omitted.",
    )
    style: DocstringStyle | None = Field(
        default=None,
        description="Target docstring style. Defaults to the canonical style for the language.",
    )
    overwrite: bool = Field(
        default=False,
        description="When True, write the patched content back to source_path. "
        "When False (default), return the patched text as output without modifying any file.",
    )
    include_private: bool = Field(
        default=False,
        description="When True, also generate stubs for private/unexported symbols.",
    )
    context_hint: str | None = Field(
        default=None,
        description="Optional one-line description of the module used to enrich generated stubs.",
    )


class DocstringOptimizerResponse(ModelBase):
    """Response from the optimize_docstrings tool."""

    model_config = ConfigDict(extra="forbid", str_strip_whitespace=True, frozen=True)

    status: ToolStatus = Field(description="Tool execution status.")
    tool: str = Field(default="optimize_docstrings", description="Tool name.")
    source_path: Path = Field(description="Source file that was analysed.")
    language: DocstringLanguage = Field(description="Detected or supplied source language.")
    style: DocstringStyle = Field(description="Docstring style applied to generated stubs.")
    symbols_optimized: int = Field(description="Number of symbols that received a new stub.")
    symbols_skipped: int = Field(
        description="Number of symbols skipped (already documented or unrecognised)."
    )
    output: str | None = Field(
        default=None,
        description="Patched file content when overwrite=False. None when overwrite=True.",
    )
    written_path: Path | None = Field(
        default=None,
        description="Path of the file written when overwrite=True. None when overwrite=False.",
    )
    message: str | None = Field(default=None, description="Human-readable summary or error.")

    @model_validator(mode="after")
    def _validate_output_xor_written_path(self) -> DocstringOptimizerResponse:
        """Ensure output and written_path are mutually exclusive."""
        if self.output is not None and self.written_path is not None:
            msg = "output and written_path are mutually exclusive: only one may be set at a time"
            raise ValueError(msg)
        return self


class DetectDocsContextRequest(ModelBase):
    """Input contract for consolidated docs context detection."""

    model_config = ConfigDict(extra="forbid", str_strip_whitespace=True, frozen=True)

    project_root: Path = Field(
        default=Path(),
        description="Project root path inspected for framework and onboarding context.",
    )


class DetectDocsContextResponse(ModelBase):
    """Output contract for consolidated docs context detection."""

    model_config = ConfigDict(extra="forbid", str_strip_whitespace=True, frozen=True)

    status: ToolStatus = Field(description="Tool execution status.")
    tool: str = Field(default="detect_docs_context", description="Tool identifier.")
    project_root: Path = Field(description="Project root path inspected.")
    framework_detection: DetectFrameworkResponse = Field(
        description="Framework detection payload for the inspected project."
    )
    runtime_onboarding: RuntimeOnboardingMatrixResponse = Field(
        description="Runtime onboarding guidance matrix paired with detected context."
    )
    message: str | None = Field(default=None, description="Error or warning details when present.")
    pipeline_context: PipelineContext | None = Field(
        default=None,
        description="Pipeline context for downstream tool chaining.",
    )


class DetectProjectReadinessRequest(ModelBase):
    """Input contract for consolidated project readiness checks."""

    model_config = ConfigDict(extra="forbid", str_strip_whitespace=True, frozen=True)

    project_root: Path = Field(
        default=Path(),
        description="Project root path inspected for readiness and init artifacts.",
    )


class DetectProjectReadinessResponse(ModelBase):
    """Output contract for consolidated project readiness checks."""

    model_config = ConfigDict(extra="forbid", str_strip_whitespace=True, frozen=True)

    status: ToolStatus = Field(description="Tool execution status.")
    tool: str = Field(default="detect_project_readiness", description="Tool identifier.")
    project_root: Path = Field(description="Project root path inspected.")
    initialized: bool = Field(description="Whether required initialization artifacts are present.")
    readiness_level: ReadinessLevel = Field(
        default=ReadinessLevel.NONE,
        description="Project readiness level: none, partial, or full.",
    )
    init_status: CheckInitStatusResponse = Field(
        description="Detailed initialization status payload."
    )
    runtime_onboarding: RuntimeOnboardingMatrixResponse = Field(
        description="Runtime onboarding guidance matrix for readiness recommendations."
    )
    message: str | None = Field(default=None, description="Error or warning details when present.")


class FrameworkAdvantageReference(ModelBase):
    """Reference link supporting one framework advantage claim."""

    model_config = ConfigDict(extra="forbid", str_strip_whitespace=True, frozen=True)

    title: str = Field(description="Human-readable reference title.")
    url: str = Field(description="Reference URL for framework-specific guidance.")


class GeneralDocsReference(ModelBase):
    """Industry best-practice documentation reference."""

    model_config = ConfigDict(extra="forbid", str_strip_whitespace=True, frozen=True)

    title: str = Field(description="Human-readable reference title.")
    url: str = Field(description="Reference URL for docs best-practice guidance.")
    scope: str = Field(description="What aspect of docs practice this reference covers.")


class FrameworkAdvantage(ModelBase):
    """Framework-specific strengths and references for authoring guidance."""

    model_config = ConfigDict(extra="forbid", str_strip_whitespace=True, frozen=True)

    framework: FrameworkName = Field(description="Framework the advantage profile applies to.")
    summary: str = Field(description="Short summary of where this framework is strongest.")
    strengths: list[str] = Field(
        default_factory=list,
        description="Concrete framework strengths that can be leveraged during authoring.",
    )
    references: list[FrameworkAdvantageReference] = Field(
        default_factory=list,
        description="Curated references for deeper framework-specific usage patterns.",
    )


class GetAuthoringProfileResponse(ModelBase):
    """Output contract for consolidated authoring profile retrieval."""

    model_config = ConfigDict(extra="forbid", str_strip_whitespace=True, frozen=True)

    status: ToolStatus = Field(description="Tool execution status.")
    tool: str = Field(default="get_authoring_profile", description="Tool identifier.")
    primitive_catalog: PrimitiveCatalogResponse = Field(
        description="Authoring primitive catalog and support matrix payload."
    )
    capability_matrix: CapabilityMatrixV2Response = Field(
        description="Framework capability matrix payload."
    )
    framework_advantages: list[FrameworkAdvantage] = Field(
        default_factory=list,
        description="Framework-specific strengths and references for feature-aware authoring.",
    )
    general_references: list[GeneralDocsReference] = Field(
        default_factory=list,
        description="Industry best-practice documentation references.",
    )
    message: str | None = Field(default=None, description="Error or warning details when present.")


class ResolvePrimitiveRequest(ModelBase):
    """Input contract for consolidated primitive resolution."""

    model_config = ConfigDict(extra="forbid", str_strip_whitespace=True, frozen=True)

    framework: FrameworkName = Field(description="Framework to resolve against.")
    primitive: AuthoringPrimitive = Field(description="Primitive to resolve.")
    mode: PrimitiveResolutionMode = Field(
        default=PrimitiveResolutionMode.SUPPORT,
        description="Primitive resolution mode to execute.",
    )
    topic: str | None = Field(
        default=None,
        description="Optional topic to interpolate into snippet rendering.",
    )


class ResolvePrimitiveResponse(ModelBase):
    """Output contract for consolidated primitive resolution."""

    model_config = ConfigDict(extra="forbid", str_strip_whitespace=True, frozen=True)

    status: ToolStatus = Field(description="Tool execution status.")
    tool: str = Field(default="resolve_primitive", description="Tool identifier.")
    framework: FrameworkName = Field(description="Framework resolved.")
    primitive: AuthoringPrimitive = Field(description="Primitive resolved.")
    mode: PrimitiveResolutionMode = Field(description="Resolution mode executed.")
    support_lookup: PrimitiveSupportLookupResponse | None = Field(
        default=None,
        description="Primitive support-level lookup payload when requested.",
    )
    render_result: RenderPrimitiveSnippetResponse | None = Field(
        default=None,
        description="Primitive rendering payload when requested.",
    )
    message: str | None = Field(default=None, description="Error or warning details when present.")


class TranslatePrimitivesRequest(ModelBase):
    """Input contract for consolidated primitive translation."""

    model_config = ConfigDict(extra="forbid", str_strip_whitespace=True, frozen=True)

    source_framework: FrameworkName = Field(description="Framework being migrated from.")
    target_framework: FrameworkName = Field(description="Framework being migrated to.")
    primitive: AuthoringPrimitive = Field(description="Primitive being translated.")
    topic: str | None = Field(
        default=None,
        description="Optional topic to interpolate into translation snippets.",
    )


class TranslatePrimitivesResponse(ModelBase):
    """Output contract for consolidated primitive translation."""

    model_config = ConfigDict(extra="forbid", str_strip_whitespace=True, frozen=True)

    status: ToolStatus = Field(description="Tool execution status.")
    tool: str = Field(default="translate_primitives", description="Tool identifier.")
    translation: TranslatePrimitiveSyntaxResponse = Field(
        description="Detailed primitive translation guidance payload."
    )
    message: str | None = Field(default=None, description="Error or warning details when present.")


class ComposeDocsStoryRequest(ModelBase):
    """Input contract for consolidated docs-story composition."""

    model_config = ConfigDict(extra="forbid", str_strip_whitespace=True, frozen=True)

    prompt: str = Field(description="Primary story generation prompt or intent.")
    audience: str | None = Field(
        default=None, description="Optional target audience for story tone and depth."
    )
    modules: list[str] = Field(
        default_factory=list,
        description="Ordered module identifiers to compose into the final story output.",
    )
    context: dict[str, str] = Field(
        default_factory=dict,
        description="Optional context map shared with module composers.",
    )
    include_onboarding_guidance: bool = Field(
        default=False,
        description="Whether onboarding guidance should be included in the response contract.",
    )
    enable_runtime_loop: bool | None = Field(
        default=None,
        description="Optional runtime loop toggle for iterative session advancement.",
    )
    runtime_max_turns: int | None = Field(
        default=None,
        ge=1,
        description="Optional maximum runtime turns allowed during iterative advancement.",
    )
    auto_advance: bool | None = Field(
        default=None,
        description="Optional auto-advance toggle for deterministic runtime progression.",
    )
    migration_mode: StoryMigrationMode | None = Field(
        default=None,
        description="Optional migration mode for same-target or cross-target adaptation.",
    )
    migration_source_framework: FrameworkName | None = Field(
        default=None,
        description="Optional source framework used when migration mode is enabled.",
    )
    migration_target_framework: FrameworkName | None = Field(
        default=None,
        description="Optional target framework used when migration mode is enabled.",
    )
    migration_improve_clarity: bool = Field(
        default=True,
        description="Whether migration mode should improve output clarity.",
    )
    migration_strengthen_structure: bool = Field(
        default=True,
        description="Whether migration mode should strengthen section structure.",
    )
    migration_enrich_examples: bool = Field(
        default=False,
        description="Whether migration mode should enrich output with examples.",
    )


class ComposeDocsStoryResponse(ModelBase):
    """Output contract for consolidated docs-story composition."""

    model_config = ConfigDict(extra="forbid", str_strip_whitespace=True, frozen=True)

    status: ToolStatus = Field(description="Tool execution status.")
    tool: str = Field(default="compose_docs_story", description="Tool identifier.")
    story: StoryGenerationResponse = Field(description="Composed story generation payload.")
    message: str | None = Field(default=None, description="Error or warning details when present.")
    pipeline_context: PipelineContext | None = Field(
        default=None,
        description="Pipeline context for downstream tool chaining.",
    )


class WriteDocRequest(ModelBase):
    """Input contract for writing a complete, ready-to-publish documentation page."""

    model_config = ConfigDict(extra="forbid", str_strip_whitespace=True, frozen=True)

    topic: str = Field(description="Subject or title of the documentation page to write.")
    framework: FrameworkName = Field(
        default=FrameworkName.ZENSICAL,
        description="Target documentation framework for native primitive rendering.",
    )
    output_path: Path = Field(
        description="Destination path where the Markdown file will be written."
    )
    audience: str | None = Field(
        default=None,
        description=(
            "Intended reader audience (e.g. 'backend developers'). Used in frontmatter description."
        ),
    )
    content_hints: str | None = Field(
        default=None,
        description="Prose hints or bullet points to guide section content generation.",
    )
    sections: list[str] = Field(
        default_factory=list,
        description=(
            "Section heading names to include. "
            "If empty, defaults to ['Overview', 'Usage', 'References']."
        ),
    )
    overwrite: bool = Field(
        default=False,
        description="Whether to overwrite an existing file at output_path.",
    )


class WriteDocResponse(ModelBase):
    """Output contract for documentation page writing."""

    model_config = ConfigDict(extra="forbid", str_strip_whitespace=True, frozen=True)

    status: ToolStatus = Field(description="Tool execution status.")
    tool: str = Field(default="write_doc", description="Tool identifier.")
    output_path: Path = Field(description="Path where the documentation file was written.")
    framework: FrameworkName = Field(description="Framework used for rendering primitives.")
    word_count: int = Field(
        default=0, description="Approximate word count of the written page.", ge=0
    )
    section_count: int = Field(default=0, description="Number of sections written.", ge=0)
    message: str | None = Field(default=None, description="Error or warning details when present.")


class ValidateDocsRequest(ModelBase):
    """Input contract for consolidated docs validation."""

    model_config = ConfigDict(extra="forbid", str_strip_whitespace=True, frozen=True)

    docs_root: Path = Field(
        default=Path("docs"), description="Root directory containing Markdown documentation."
    )
    mkdocs_file: Path = Field(
        default=Path("mkdocs.yml"), description="MkDocs configuration file path."
    )
    external_mode: Literal["report", "ignore"] = Field(
        default="report",
        description="How external links should be handled during validation.",
    )
    required_headers: list[str] | None = Field(
        default=None,
        description="Required heading strings expected in each Markdown file.",
    )
    required_frontmatter: list[str] | None = Field(
        default=None,
        description="Required frontmatter keys expected in each Markdown file.",
    )
    checks: list[DocsValidationCheck] = Field(
        default_factory=lambda: [
            DocsValidationCheck.LINKS,
            DocsValidationCheck.ORPHANS,
            DocsValidationCheck.STRUCTURE,
        ],
        description="Validation dimensions to execute for this run.",
    )


class ValidateDocsResponse(ModelBase):
    """Output contract for consolidated docs validation."""

    model_config = ConfigDict(extra="forbid", str_strip_whitespace=True, frozen=True)

    status: ToolStatus = Field(description="Tool execution status.")
    tool: str = Field(default="validate_docs", description="Tool identifier.")
    docs_root: Path = Field(description="Validated documentation root directory.")
    mkdocs_file: Path = Field(description="MkDocs configuration file path used for nav analysis.")
    checks: list[DocsValidationCheck] = Field(
        default_factory=list,
        description="Validation dimensions executed for this run.",
    )
    links: CheckDocsLinksResponse | None = Field(
        default=None,
        description="Link-validation payload when links checks are executed.",
    )
    orphans: CheckOrphanDocsResponse | None = Field(
        default=None,
        description="Orphan-doc validation payload when orphan checks are executed.",
    )
    structure: CheckLanguageStructureResponse | None = Field(
        default=None,
        description="Language-structure validation payload when structure checks are executed.",
    )
    total_issue_count: int = Field(
        default=0,
        ge=0,
        description="Total number of issues discovered across executed checks.",
    )
    message: str | None = Field(default=None, description="Error or warning details when present.")
    pipeline_context: PipelineContext | None = Field(
        default=None,
        description="Pipeline context for downstream tool chaining.",
    )


class OnboardProjectRequest(ModelBase):
    """Input contract for consolidated project onboarding workflows."""

    model_config = ConfigDict(extra="forbid", str_strip_whitespace=True, frozen=True)

    project_root: Path = Field(default=Path(), description="Project root path for onboarding.")
    project_name: str = Field(
        default="Project", description="Project name used in generated onboarding content."
    )
    output_file: Path | None = Field(
        default=None, description="Optional output Markdown file path for onboarding skeleton."
    )
    mode: OnboardProjectMode = Field(
        default=OnboardProjectMode.SKELETON,
        description="Onboarding workflow mode to execute.",
    )
    include_checklist: bool = Field(
        default=True, description="Whether to include contribution checklist content."
    )
    overwrite: bool = Field(
        default=False,
        description="Whether onboarding artifact generation may overwrite existing files.",
    )
    include_shell_scripts: bool = Field(
        default=True,
        description="Whether shell setup scripts should be generated during initialization.",
    )
    deploy_provider: DocsDeployProvider = Field(
        default=DocsDeployProvider.GITHUB_PAGES,
        description="Docs deploy provider used for generated CI/CD pipeline artifacts.",
    )
    gate_confirmed: bool = Field(
        default=False,
        description="Whether the boilerplate gate has been explicitly confirmed.",
    )
    shell_targets: list[ShellScriptType] = Field(
        default_factory=lambda: [
            ShellScriptType.BASH,
            ShellScriptType.ZSH,
            ShellScriptType.POWERSHELL,
        ],
        description="Shell script targets to include in gated onboarding flows.",
    )


class OnboardProjectWarningMetadata(ModelBase):
    """Compatibility warning metadata emitted by onboard_project."""

    model_config = ConfigDict(extra="forbid", str_strip_whitespace=True, frozen=True)

    ignored_keys: list[str] = Field(
        default_factory=list,
        description="Compatibility keys that were accepted but intentionally ignored.",
    )
    detail: str = Field(description="Human-readable warning detail for compatibility behavior.")


class OnboardProjectResponse(ModelBase):
    """Output contract for consolidated project onboarding workflows."""

    model_config = ConfigDict(extra="forbid", str_strip_whitespace=True, frozen=True)

    status: ToolStatus = Field(description="Tool execution status.")
    tool: str = Field(default="onboard_project", description="Tool identifier.")
    mode: OnboardProjectMode = Field(description="Onboarding workflow mode executed.")
    project_root: Path = Field(description="Project root path used for onboarding operations.")
    project_name: str = Field(description="Project name used in onboarding content.")
    skeleton: OnboardingSkeletonResponse | None = Field(
        default=None, description="Onboarding skeleton payload when generated."
    )
    init_result: InitProjectResponse | None = Field(
        default=None, description="Initialization payload when init mode is executed."
    )
    init_status: CheckInitStatusResponse | None = Field(
        default=None, description="Initialization status payload for readiness checks."
    )
    boilerplate_result: GatedBoilerplateGenerationResponse | None = Field(
        default=None,
        description="Gated boilerplate generation payload when boilerplate mode is executed.",
    )
    deploy_pipelines: list[DocsDeployPipelineArtifactMetadata] | None = Field(
        default=None,
        description=(
            "Docs deploy pipeline metadata collected during onboarding initialization checks."
        ),
    )
    warning_metadata: OnboardProjectWarningMetadata | None = Field(
        default=None,
        description="Compatibility warning metadata for accepted-but-ignored input keys.",
    )
    deployment_urls: DeploymentUrlConfig | None = Field(
        default=None,
        description="Elicited deployment environment URLs when available.",
    )
    framework_init_result: InitFrameworkStructureResponse | None = Field(
        default=None,
        description="Framework-native scaffold result when scaffold_docs=True was requested.",
    )
    message: str | None = Field(default=None, description="Error or warning details when present.")


class GenerateReferenceDocsRequest(ModelBase):
    """Input contract for consolidated reference-doc generation."""

    model_config = ConfigDict(extra="forbid", str_strip_whitespace=True, frozen=True)

    kind: GenerateReferenceDocsKind = Field(
        default=GenerateReferenceDocsKind.MCP_TOOLS,
        description="Reference-doc generation kind to execute.",
    )
    cli_command: str | None = Field(
        default=None, description="CLI command to execute for help extraction."
    )
    target: Path | None = Field(
        default=None, description="Python module path to scan for MCP tools."
    )
    output_file: Path | None = Field(
        default=None, description="Optional output Markdown file path."
    )
    timeout_seconds: int = Field(default=10, ge=1, description="Execution timeout in seconds.")
    topic: str | None = Field(default=None, description="Specific snippet topic to retrieve.")
    source_host: SourceCodeHost | None = Field(
        default=None,
        description="Optional source host used to render line-link anchors in authoring packs.",
    )
    repository_url: str | None = Field(
        default=None,
        description="Optional repository URL used to build source line links.",
    )
    source_file: str | None = Field(
        default=None,
        description="Optional repository-relative source file path for line-link examples.",
    )
    docstring_source_path: Path | None = Field(
        default=None,
        description=(
            "Source file or directory to scan for docstring coverage "
            "(DOCSTRING_PACK kind only). Defaults to '.' when omitted."
        ),
    )
    line_start: int | None = Field(
        default=None,
        ge=1,
        description="Optional starting line number for source-link examples.",
    )
    line_end: int | None = Field(
        default=None,
        ge=1,
        description="Optional ending line number for source-link examples.",
    )
    asset_kind: VisualAssetKind | None = Field(
        default=None,
        description=(
            "Deprecated: use generate_visual_asset with operation='convert_to_png' instead."
        ),
    )
    asset_prompt: str | None = Field(
        default=None,
        description=("Deprecated: use generate_visual_asset with operation='prompt_spec' instead."),
    )
    style_notes: str | None = Field(
        default=None,
        description="Deprecated: use generate_visual_asset instead.",
    )
    target_size_hint: str | None = Field(
        default=None,
        description="Deprecated: use generate_visual_asset instead.",
    )
    source_svg: str | None = Field(
        default=None,
        description=(
            "Deprecated: use generate_visual_asset with operation='convert_to_png' instead."
        ),
    )


class SvgPngScriptsResponse(ModelBase):
    """Output contract for deterministic SVG-to-PNG wrapper script generation."""

    model_config = ConfigDict(extra="forbid", str_strip_whitespace=True, frozen=True)

    status: ToolStatus = Field(description="Script generation status.")
    scripts: dict[ShellScriptType, str] = Field(
        default_factory=dict,
        description="Deterministic shell scripts keyed by shell type.",
    )
    message: str | None = Field(default=None, description="Error or warning details when present.")


class ManifestType(StrEnum):
    """Detected project manifest file type."""

    PYTHON = "python"
    NODEJS = "nodejs"
    RUST = "rust"
    GO = "go"
    RUBY = "ruby"
    UNKNOWN = "unknown"


class GenerateProjectManifestDocsResponse(ModelBase):
    """Output contract for project manifest reference documentation generation.

    Supports Python (pyproject.toml), Node.js (package.json), Rust (Cargo.toml),
    Go (go.mod), Ruby (Gemfile/gemspec), and any project layout that contains one
    of these standard manifest files.
    """

    status: ToolStatus = Field(description="Tool execution status.")
    tool: str = Field(default="generate_reference_docs", description="Tool identifier.")
    manifest_type: ManifestType | None = Field(
        default=None,
        description="Detected project manifest type (python, nodejs, rust, go, ruby, unknown).",
    )
    manifest_file: str | None = Field(
        default=None, description="Name of the manifest file that was read."
    )
    project_name: str | None = Field(default=None, description="Project name from manifest.")
    project_version: str | None = Field(default=None, description="Project version.")
    output_file: Path | None = Field(default=None, description="Path to generated markdown file.")
    markdown: str | None = Field(default=None, description="Generated markdown content.")
    message: str | None = Field(default=None, description="Error or warning details when present.")


class GenerateReferenceDocsResponse(ModelBase):
    """Output contract for consolidated reference-doc generation."""

    model_config = ConfigDict(extra="forbid", str_strip_whitespace=True, frozen=True)

    status: ToolStatus = Field(description="Tool execution status.")
    tool: str = Field(default="generate_reference_docs", description="Tool identifier.")
    kind: GenerateReferenceDocsKind = Field(
        description="Reference-doc generation kind that was executed."
    )
    cli_docs: GenerateCliDocsResponse | None = Field(
        default=None,
        description="CLI documentation generation payload when CLI mode is executed.",
    )
    mcp_tools_docs: GenerateMcpToolsDocsResponse | None = Field(
        default=None,
        description="MCP tool documentation payload when MCP tools mode is executed.",
    )
    material_snippets: MaterialSnippetResponse | None = Field(
        default=None,
        description="Material snippet payload when snippets mode is executed.",
    )
    authoring_pack: ReferenceAuthoringPackResponse | None = Field(
        default=None,
        description="Authoring pack payload for standardized code blocks and source links.",
    )
    docstring_pack: DocstringAuditResponse | None = Field(
        default=None,
        description="Docstring coverage audit payload when docstring-pack mode is executed.",
    )
    project_manifest_docs: GenerateProjectManifestDocsResponse | None = Field(
        default=None,
        description="Project manifest reference payload when project-manifest mode is executed. "
        "Supports pyproject.toml (Python), package.json (Node.js), Cargo.toml (Rust), "
        "go.mod (Go), and Gemfile/gemspec (Ruby).",
    )
    message: str | None = Field(default=None, description="Error or warning details when present.")


class DetectFrameworkRequest(ModelBase):
    """Input contract for framework detection across project signals."""

    project_root: Path = Field(
        default=Path(), description="Project root path to inspect for framework signals."
    )


class DetectFrameworkResponse(ModelBase):
    """Output contract for framework detection."""

    status: ToolStatus = Field(description="Tool execution status.")
    tool: str = Field(default="detect_framework", description="Tool identifier.")
    project_root: Path = Field(description="Project root path inspected.")
    best_match: FrameworkDetectionResult | None = Field(
        default=None,
        description="Highest confidence framework detection result.",
    )
    candidates: list[FrameworkDetectionResult] = Field(
        default_factory=list,
        description="All framework detection candidates sorted by confidence.",
    )
    message: str | None = Field(default=None, description="Error or warning details when present.")


class ScoreDocsQualityRequest(ModelBase):
    """Input contract for documentation quality scoring."""

    docs_root: Path = Field(
        default=Path("docs"), description="Root directory containing Markdown documentation."
    )


class ScoreDocsQualityResponse(ModelBase):
    """Output contract for documentation quality scoring."""

    status: ToolStatus = Field(description="Tool execution status.")
    tool: str = Field(default="score_docs_quality", description="Tool identifier.")
    docs_root: Path = Field(description="Validated documentation root directory.")
    framework: FrameworkName | None = Field(
        default=None,
        description="Framework selected for scoring context when detected.",
    )
    quality_score: QualityScore | None = Field(
        default=None, description="Aggregated quality scoring metadata."
    )
    component_scores: dict[str, int] = Field(
        default_factory=dict,
        description="Category-level quality scores from 0 to 100.",
    )
    detected_primitives: list[AuthoringPrimitive] = Field(
        default_factory=list,
        description="Authoring primitives detected while evaluating quality.",
    )
    issues: list[QualityIssue] = Field(default_factory=list, description="Detected quality issues.")
    suggestions: list[str] = Field(
        default_factory=list, description="Actionable quality improvement suggestions."
    )
    message: str | None = Field(default=None, description="Error or warning details when present.")
    pipeline_context: PipelineContext | None = Field(
        default=None,
        description="Pipeline context for downstream tool chaining.",
    )


# ---------------------------------------------------------------------------
# Copilot Artifact Frontmatter Models
# ---------------------------------------------------------------------------


class CopilotAgentMode(StrEnum):
    """Supported VS Code Copilot agent modes for prompt/agent frontmatter."""

    ASK = "ask"
    EDIT = "edit"
    AGENT = "agent"


# Backward-compatible alias.
CopilotPromptMode = CopilotAgentMode

# Standard tool set for Copilot prompts and agents in this project.
COPILOT_DEFAULT_TOOLS: list[str] = [
    "vscode",
    "execute/testFailure",
    "execute/getTerminalOutput",
    "execute/awaitTerminal",
    "execute/killTerminal",
    "execute/createAndRunTask",
    "execute/runInTerminal",
    "execute/runTests",
    "read",
    "agent",
    "edit",
    "search",
    "web",
    "browser",
    "zen-of-docs/*",
    "ai-agent-guidelines/gap-frameworks-analyzers",
    "ai-agent-guidelines/l9-distinguished-engineer-prompt-builder",
    "context7/*",
    "serena/*",
    "zen-of-languages/*",
    "todo",
]


class InstructionFrontmatter(ModelBase):
    """YAML frontmatter for ``*.instructions.md`` files."""

    apply_to: str = Field(
        default="**",
        description="Glob pattern specifying which files this instruction applies to.",
    )


class PromptFrontmatter(ModelBase):
    """YAML frontmatter for ``*.prompt.md`` files."""

    agent: CopilotAgentMode = Field(
        default=CopilotAgentMode.AGENT,
        description="Copilot agent mode (ask/edit/agent) per VS Code spec.",
    )
    tools: list[str] = Field(
        default_factory=lambda: list(COPILOT_DEFAULT_TOOLS),
        description="MCP/VS Code tools available to this prompt.",
    )
    description: str = Field(description="Short human-readable description of the prompt.")


class AgentFrontmatter(ModelBase):
    """YAML frontmatter for ``*.agent.md`` files."""

    name: str = Field(description="Agent identifier name.")
    description: str = Field(description="Short human-readable description of the agent.")
    agents: list[str] = Field(
        default_factory=lambda: ["*"],
        description="Scope list controlling which agents can invoke this agent.",
    )
    tools: list[str] = Field(
        default_factory=lambda: list(COPILOT_DEFAULT_TOOLS),
        description="Tools available to this agent.",
    )


# ---------------------------------------------------------------------------
# Create Instruction / Prompt / Agent — Request & Response Models
# ---------------------------------------------------------------------------


class CreateInstructionRequest(ModelBase):
    """Request model for ``create_instruction`` tool."""

    file_stem: str = Field(
        description=(
            "Stem of the output file (e.g. 'python-testing' → python-testing.instructions.md)."
        ),
    )
    apply_to: str = Field(
        default="**",
        description="Glob pattern for applyTo frontmatter.",
    )
    title: str = Field(description="Title heading for the instruction file.")
    content: str = Field(description="Markdown body content after the title.")
    project_root: Path = Field(
        default=Path(),
        description="Repository root where .github/instructions/ is located.",
    )
    overwrite: bool = Field(
        default=False,
        description="Whether to overwrite an existing file.",
    )


class CreateInstructionResponse(ModelBase):
    """Response model for ``create_instruction`` tool."""

    status: ToolStatus = Field(description="Tool execution status.")
    file_path: Path = Field(description="Absolute path to the created file.")
    file_stem: str = Field(description="Stem identifier of the created instruction.")
    apply_to: str = Field(description="The applyTo glob written to frontmatter.")
    message: str | None = Field(default=None, description="Additional result details.")


class CreatePromptRequest(ModelBase):
    """Request model for ``create_prompt`` tool."""

    file_stem: str = Field(
        description=("Stem of the output file (e.g. 'init-checklist' → init-checklist.prompt.md)."),
    )
    agent: CopilotAgentMode = Field(
        default=CopilotAgentMode.AGENT,
        description="Copilot agent mode (ask/edit/agent) per VS Code spec.",
    )
    tools: list[str] = Field(
        default_factory=lambda: list(COPILOT_DEFAULT_TOOLS),
        description="Tools available to this prompt.",
    )
    description: str = Field(description="Short description for frontmatter.")
    content: str = Field(description="Markdown body content.")
    project_root: Path = Field(
        default=Path(),
        description="Repository root where .github/prompts/ is located.",
    )
    overwrite: bool = Field(
        default=False,
        description="Whether to overwrite an existing file.",
    )


class CreatePromptResponse(ModelBase):
    """Response model for ``create_prompt`` tool."""

    status: ToolStatus = Field(description="Tool execution status.")
    file_path: Path = Field(description="Absolute path to the created file.")
    file_stem: str = Field(description="Stem identifier of the created prompt.")
    agent: CopilotAgentMode = Field(description="The agent mode written to frontmatter.")
    message: str | None = Field(default=None, description="Additional result details.")


class CreateAgentRequest(ModelBase):
    """Request model for ``create_agent`` tool."""

    agent_name: str = Field(
        description="Agent identifier (e.g. 'docs-reviewer' → docs-reviewer.agent.md).",
    )
    description: str = Field(description="Short description for frontmatter.")
    agents: list[str] = Field(
        default_factory=lambda: ["*"],
        description="Scope list for the agents: frontmatter field.",
    )
    tools: list[str] = Field(
        default_factory=lambda: list(COPILOT_DEFAULT_TOOLS),
        description="Tools available to this agent.",
    )
    content: str = Field(description="Markdown system prompt body content.")
    project_root: Path = Field(
        default=Path(),
        description="Repository root where .github/agents/ is located.",
    )
    overwrite: bool = Field(
        default=False,
        description="Whether to overwrite an existing file.",
    )


class CreateAgentResponse(ModelBase):
    """Response model for ``create_agent`` tool."""

    status: ToolStatus = Field(description="Tool execution status.")
    file_path: Path = Field(description="Absolute path to the created file.")
    agent_name: str = Field(description="Agent identifier name.")
    message: str | None = Field(default=None, description="Additional result details.")


class CopilotArtifactKind(StrEnum):
    """Kind of Copilot artifact to create via create_copilot_artifact."""

    INSTRUCTION = "instruction"
    PROMPT = "prompt"
    AGENT = "agent"


class CreateCopilotArtifactRequest(ModelBase):
    """Input contract for the consolidated ``create_copilot_artifact`` tool."""

    kind: CopilotArtifactKind = Field(
        description="Kind of artifact to create: instruction, prompt, or agent."
    )
    file_stem: str = Field(
        description=(
            "Stem of the output file, e.g. 'python-rules' → python-rules.instructions.md."
        ),
    )
    title: str | None = Field(
        default=None,
        description="Title heading (used for instruction files; omit for prompt/agent).",
    )
    description: str | None = Field(
        default=None,
        description="Short description written to frontmatter (used for prompt and agent kinds).",
    )
    content: str = Field(description="Markdown body content for the artifact.")
    project_root: Path = Field(
        default=Path(),
        description="Repository root where .github/ subdirectory is located.",
    )
    overwrite: bool = Field(default=False, description="Whether to overwrite an existing file.")
    # instruction-specific
    apply_to: str = Field(
        default="**",
        description="Glob pattern for applyTo frontmatter (instruction kind only).",
    )
    # prompt-specific
    agent: CopilotAgentMode = Field(
        default=CopilotAgentMode.AGENT,
        description="Copilot agent mode written to frontmatter (prompt kind only).",
    )
    # agent-specific
    agents: list[str] = Field(
        default_factory=lambda: ["*"],
        description="Scope list for the agents: frontmatter field (agent kind only).",
    )
    tools: list[str] = Field(
        default_factory=lambda: list(COPILOT_DEFAULT_TOOLS),
        description="Tools list written to frontmatter (prompt and agent kinds).",
    )


class CreateCopilotArtifactResponse(ModelBase):
    """Output contract for the consolidated ``create_copilot_artifact`` tool."""

    status: ToolStatus = Field(description="Tool execution status.")
    kind: CopilotArtifactKind = Field(description="Artifact kind that was created.")
    file_path: Path = Field(description="Absolute path to the created file.")
    file_stem: str = Field(description="Stem identifier of the created artifact.")
    message: str | None = Field(default=None, description="Additional result details.")


class PlannedPage(ModelBase):
    """A single planned documentation page."""

    path: str = Field(
        description="Relative path for the planned page (e.g., 'docs/quickstart.md')."
    )
    title: str = Field(description="Page title.")
    description: str = Field(description="Brief description of page content and purpose.")
    suggested_primitives: list[AuthoringPrimitive] = Field(
        default_factory=list,
        description="Authoring primitives recommended for this page.",
    )
    dependencies: list[str] = Field(
        default_factory=list,
        description="Paths of pages that should be written before this one.",
    )
    priority: str = Field(default="medium", description="Page priority: high, medium, low.")
    exists: bool = Field(default=False, description="Whether the file already exists.")


class PlanDocsRequest(ModelBase):
    """Request model for plan_docs tool."""

    project_root: Path = Field(default=Path(), description="Project root directory.")
    framework: FrameworkName | None = Field(
        default=None, description="Target framework. Auto-detected when omitted."
    )
    scope: str = Field(default="full", description="Planning scope: full, api-only, quickstart.")
    docs_root: Path = Field(default=Path("docs"), description="Documentation root directory.")


class PlanDocsResponse(ModelBase):
    """Response model for plan_docs tool."""

    status: ToolStatus = Field(description="Tool execution status.")
    framework: FrameworkName | None = Field(
        default=None, description="Detected or specified framework."
    )
    pages: list[PlannedPage] = Field(
        default_factory=list, description="Ordered list of planned pages."
    )
    total_pages: int = Field(default=0, description="Total number of planned pages.")
    existing_pages: int = Field(default=0, description="Number of pages that already exist.")
    new_pages: int = Field(default=0, description="Number of new pages to create.")
    message: str | None = Field(default=None, description="Optional status message.")
    pipeline_context: PipelineContext | None = Field(
        default=None,
        description="Pipeline context for downstream tool chaining.",
    )


class BatchScaffoldRequest(ModelBase):
    """Input contract for batch documentation scaffold generation."""

    pages: list[ScaffoldDocRequest] = Field(description="List of pages to scaffold.")
    docs_root: Path = Field(
        default=Path("docs"),
        description="Base docs directory.",
    )
    framework: FrameworkName | None = Field(
        default=None,
        description="Optional framework override for all pages.",
    )


class BatchScaffoldResponse(ModelBase):
    """Output contract for batch documentation scaffold generation."""

    status: ToolStatus = Field(description="Tool execution status.")
    created: list[ScaffoldDocResponse] = Field(
        default_factory=list,
        description="Successfully created pages.",
    )
    skipped: list[str] = Field(
        default_factory=list,
        description="Pages that already exist.",
    )
    total: int = Field(default=0, description="Total pages attempted.")
    pipeline_context: PipelineContext | None = Field(
        default=None,
        description="Pipeline context for chaining.",
    )
    message: str | None = Field(
        default=None,
        description="Optional status message.",
    )


class AgentPlatform(StrEnum):
    """Supported AI agent platforms."""

    COPILOT = "copilot"
    CURSOR = "cursor"
    WINDSURF = "windsurf"
    CLAUDE_CODE = "claude-code"
    GENERIC = "generic"


class AgentConfigRequest(ModelBase):
    """Request to generate agent config for a platform."""

    platform: AgentPlatform = Field(description="Target agent platform.")
    project_root: str = Field(default=".", description="Project root directory.")
    include_tools: bool = Field(default=True, description="Include MCP tool references.")


class AgentConfigResponse(ModelBase):
    """Generated agent configuration for a platform."""

    model_config = ConfigDict(frozen=True)

    status: ToolStatus = Field(description="Generation status.")
    platform: AgentPlatform = Field(description="Target platform.")
    config_files: list[dict[str, str]] = Field(
        default_factory=list,
        description="Generated config file paths and contents.",
    )
    message: str = Field(default="", description="Status message.")
    pipeline_context: PipelineContext | None = Field(
        default=None,
        description="Pipeline context for chaining.",
    )


class PipelinePhase(StrEnum):
    """Pipeline execution phases."""

    CONSTITUTION = "constitution"
    SPECIFY = "specify"
    PLAN = "plan"
    TASKS = "tasks"
    IMPLEMENT = "implement"


class PipelinePhaseStatus(StrEnum):
    """Status of a pipeline phase execution."""

    READY = "ready"
    DONE = "done"
    NEEDS_INPUT = "needs_input"
    BLOCKED = "blocked"


class PhaseArtifact(ModelBase):
    """An artifact produced by a pipeline phase."""

    model_config = ConfigDict(frozen=True)

    phase: PipelinePhase = Field(description="Phase that produced this artifact.")
    path: str = Field(description="Relative path to the artifact file.")
    content_hash: str = Field(default="", description="SHA256 of content for change detection.")


class PipelinePhaseRequest(ModelBase):
    """Request to execute a pipeline phase."""

    phase: PipelinePhase = Field(description="Phase to execute.")
    project_root: str = Field(default=".", description="Project root directory.")
    artifacts_dir: str = Field(default=".zen-of-docs", description="Pipeline artifacts directory.")
    force: bool = Field(default=False, description="Re-run even if artifacts exist.")


class PipelinePhaseResponse(ModelBase):
    """Response from a pipeline phase execution."""

    model_config = ConfigDict(frozen=True)

    status: ToolStatus = Field(description="Phase execution status.")
    phase: PipelinePhase = Field(description="Executed phase.")
    phase_status: PipelinePhaseStatus = Field(description="Phase-specific status.")
    artifacts: list[PhaseArtifact] = Field(default_factory=list, description="Produced artifacts.")
    next_phase: PipelinePhase | None = Field(default=None, description="Suggested next phase.")
    message: str = Field(default="", description="Human-readable status message.")
    pipeline_context: PipelineContext | None = Field(default=None, description="Pipeline context.")


# ---------------------------------------------------------------------------
# sync_nav models
# ---------------------------------------------------------------------------


class SyncNavMode(StrEnum):
    """Execution mode for sync_nav."""

    AUDIT = "audit"
    GENERATE = "generate"
    REPAIR = "repair"


class NavIssueKind(StrEnum):
    """Classification of a navigation consistency issue."""

    NAV_MISSING_FILE = "nav-missing-file"
    FILE_MISSING_FROM_NAV = "file-missing-from-nav"


class NavIssue(ModelBase):
    """A single navigation consistency issue."""

    path: str = Field(description="Relative path of the affected file.")
    issue_kind: NavIssueKind = Field(description="Type of navigation issue.")
    detail: str = Field(default="", description="Additional context.")


class SyncNavRequest(ModelBase):
    """Input contract for the sync_nav tool."""

    project_root: Path = Field(default=Path(), description="Root of the docs project.")
    framework: FrameworkName | None = Field(
        default=None, description="Framework override; auto-detected when omitted."
    )
    mode: SyncNavMode = Field(default=SyncNavMode.AUDIT, description="Sync mode to execute.")


class SyncNavResponse(ModelBase):
    """Output contract for the sync_nav tool."""

    status: ToolStatus = Field(description="Tool run status.")
    tool: str = Field(default="sync_nav", description="Tool identifier.")
    framework: FrameworkName | None = Field(
        default=None, description="Detected or specified framework."
    )
    mode: SyncNavMode = Field(description="Sync mode that was executed.")
    nav_file: str | None = Field(default=None, description="Path to the detected nav config file.")
    issues: list[NavIssue] = Field(default_factory=list, description="Navigation issues found.")
    nav_generated: str | None = Field(
        default=None, description="Generated nav config content (generate/repair mode)."
    )
    files_repaired: int = Field(default=0, description="Number of nav entries repaired.")
    pipeline_context: PipelineContext | None = Field(default=None, description="Pipeline context.")


# ---------------------------------------------------------------------------
# audit_frontmatter models
# ---------------------------------------------------------------------------


class FrontmatterFileAudit(ModelBase):
    """Frontmatter audit result for a single file."""

    file_path: str = Field(description="Relative path of the audited file.")
    has_frontmatter: bool = Field(description="Whether the file contains a YAML frontmatter block.")
    present_keys: list[str] = Field(default_factory=list, description="Keys found in frontmatter.")
    missing_keys: list[str] = Field(
        default_factory=list, description="Required keys absent from frontmatter."
    )
    repaired: bool = Field(
        default=False, description="Whether missing keys were written to the file."
    )


class FrontmatterAuditRequest(ModelBase):
    """Input contract for audit_frontmatter."""

    docs_root: Path = Field(
        default=Path("docs"), description="Root directory of the documentation."
    )
    required_keys: list[str] = Field(
        default_factory=lambda: ["title", "description"],
        description="Frontmatter keys that must be present in every file.",
    )
    fix: bool = Field(
        default=False,
        description="When True, insert placeholder values for missing required keys.",
    )


class FrontmatterAuditResponse(ModelBase):
    """Output contract for audit_frontmatter."""

    status: ToolStatus = Field(description="Tool run status.")
    tool: str = Field(default="audit_frontmatter", description="Tool identifier.")
    docs_root: str = Field(description="Root directory that was audited.")
    files_audited: int = Field(description="Total number of Markdown files examined.")
    files_with_gaps: int = Field(description="Files with one or more missing required keys.")
    files_repaired: int = Field(default=0, description="Files where missing keys were written.")
    audits: list[FrontmatterFileAudit] = Field(
        default_factory=list, description="Per-file audit results."
    )
    pipeline_context: PipelineContext | None = Field(default=None, description="Pipeline context.")


# ---------------------------------------------------------------------------
# generate_visual_asset models
# ---------------------------------------------------------------------------


class GenerateVisualAssetRequest(ModelBase):
    """Input contract for generate_visual_asset."""

    kind: VisualAssetKind = Field(description="Type of visual asset to generate.")
    operation: VisualAssetOperation = Field(
        default=VisualAssetOperation.RENDER,
        description=(
            "Operation to perform: 'render' generates SVG markup (default); "
            "'prompt_spec' returns an AI design-prompt spec; "
            "'generate_scripts' returns SVG-to-PNG shell scripts; "
            "'convert_to_png' converts SVG to PNG output."
        ),
    )
    title: str | None = Field(default=None, description="Primary title text embedded in the SVG.")
    subtitle: str | None = Field(default=None, description="Secondary subtitle or tagline text.")
    primary_color: str = Field(
        default="#5C6BC0", description="Primary brand color as a CSS hex value."
    )
    background_color: str = Field(
        default="#F8F9FA", description="Background fill color as a CSS hex value."
    )
    output_path: Path | None = Field(
        default=None, description="Write the SVG to this path when provided."
    )
    # For operation='prompt_spec'
    asset_prompt: str | None = Field(
        default=None,
        description="Design intent prompt used when operation='prompt_spec'.",
    )
    style_notes: str | None = Field(
        default=None,
        description="Optional style notes used when operation='prompt_spec'.",
    )
    target_size_hint: str | None = Field(
        default=None,
        description="Optional explicit size hint used when operation='prompt_spec'.",
    )
    # For operation='convert_to_png'
    source_svg: str | None = Field(
        default=None,
        description="Inline SVG markup to convert when operation='convert_to_png'.",
    )
    source_file: Path | None = Field(
        default=None,
        description=(
            "Path to SVG file to read when source_svg is absent and operation='convert_to_png'."
        ),
    )


class GenerateVisualAssetResponse(ModelBase):
    """Output contract for generate_visual_asset."""

    status: ToolStatus = Field(description="Tool run status.")
    tool: str = Field(default="generate_visual_asset", description="Tool identifier.")
    kind: VisualAssetKind = Field(description="Asset kind that was generated.")
    operation: VisualAssetOperation = Field(
        default=VisualAssetOperation.RENDER,
        description="Operation that was executed.",
    )
    svg_content: str | None = Field(
        default=None, description="Generated SVG markup (set when operation='render')."
    )
    canvas_width: int | None = Field(
        default=None, description="SVG canvas width in pixels (set when operation='render')."
    )
    canvas_height: int | None = Field(
        default=None, description="SVG canvas height in pixels (set when operation='render')."
    )
    output_path: str | None = Field(default=None, description="Path the SVG was written to.")
    pipeline_context: PipelineContext | None = Field(default=None, description="Pipeline context.")
    svg_prompt_toolkit: SvgPromptToolkitResponse | None = Field(
        default=None,
        description="SVG design-prompt spec (set when operation='prompt_spec').",
    )
    svg_png_scripts: SvgPngScriptsResponse | None = Field(
        default=None,
        description="SVG-to-PNG shell scripts (set when operation='generate_scripts').",
    )
    svg_to_png: VisualAssetConversionResponse | None = Field(
        default=None,
        description="SVG-to-PNG conversion result (set when operation='convert_to_png').",
    )
    message: str | None = Field(default=None, description="Error or warning details when present.")


# ---------------------------------------------------------------------------
# generate_diagram / render_diagram models
# ---------------------------------------------------------------------------


class DiagramType(StrEnum):
    """Mermaid diagram type identifiers."""

    FLOWCHART = "flowchart"
    SEQUENCE = "sequence"
    CLASS = "class"
    ER = "er"
    C4_CONTEXT = "c4-context"
    GANTT = "gantt"
    MINDMAP = "mindmap"
    STATE = "state"


class GenerateDiagramRequest(ModelBase):
    """Input contract for generate_diagram."""

    description: str = Field(description="Plain-language description of what to diagram.")
    diagram_type: DiagramType = Field(
        default=DiagramType.FLOWCHART, description="Mermaid diagram type."
    )
    framework: FrameworkName | None = Field(
        default=None, description="Target framework for syntax wrapping."
    )
    title: str | None = Field(default=None, description="Optional diagram title.")


class GenerateDiagramResponse(ModelBase):
    """Output contract for generate_diagram."""

    status: ToolStatus = Field(description="Tool run status.")
    tool: str = Field(default="generate_diagram", description="Tool identifier.")
    diagram_type: DiagramType = Field(description="Mermaid diagram type generated.")
    mermaid_source: str = Field(description="Raw Mermaid diagram source (no code fence).")
    framework_snippet: str = Field(
        description="Mermaid source wrapped in a framework-native code fence."
    )
    framework: FrameworkName | None = Field(
        default=None, description="Framework used for snippet wrapping."
    )
    pipeline_context: PipelineContext | None = Field(default=None, description="Pipeline context.")


class RenderDiagramRequest(ModelBase):
    """Input contract for render_diagram."""

    mermaid_source: str = Field(description="Mermaid diagram source to render.")
    output_format: Literal["svg", "png"] = Field(
        default="svg", description="Output format: svg or png."
    )
    output_path: Path | None = Field(
        default=None, description="Write rendered output to this path."
    )


class RenderDiagramResponse(ModelBase):
    """Output contract for render_diagram."""

    status: ToolStatus = Field(description="Tool run status.")
    tool: str = Field(default="render_diagram", description="Tool identifier.")
    output_format: str = Field(description="Requested output format.")
    output_path: str | None = Field(
        default=None, description="Path the rendered file was written to."
    )
    svg_content: str | None = Field(
        default=None, description="Inline SVG content when format=svg and no output path."
    )
    backend_used: str | None = Field(default=None, description="Rendering backend that was used.")
    backend_available: bool = Field(
        default=False, description="Whether a rendering backend was available."
    )
    install_hint: str | None = Field(
        default=None, description="Installation guidance when no backend is available."
    )
    pipeline_context: PipelineContext | None = Field(default=None, description="Pipeline context.")


# ---------------------------------------------------------------------------
# generate_changelog models
# ---------------------------------------------------------------------------


class ChangelogEntryFormat(StrEnum):
    """Output format for generated changelog content."""

    KEEP_A_CHANGELOG = "keep-a-changelog"
    GITHUB_RELEASE = "github-release"


class ChangelogCategoryEntry(ModelBase):
    """A single changelog category with its change items."""

    category: str = Field(description="Change category label (Added, Fixed, Changed, etc.).")
    items: list[str] = Field(default_factory=list, description="Change items in this category.")


class GenerateChangelogRequest(ModelBase):
    """Input contract for generate_changelog."""

    version: str = Field(description="Release version string (e.g. '1.2.0').")
    project_root: Path = Field(
        default=Path(), description="Git repository root used for git log parsing."
    )
    since_tag: str | None = Field(
        default=None, description="Restrict commits to those after this git tag."
    )
    format: ChangelogEntryFormat = Field(
        default=ChangelogEntryFormat.KEEP_A_CHANGELOG,
        description="Output format: keep-a-changelog or github-release.",
    )


class GenerateChangelogResponse(ModelBase):
    """Output contract for generate_changelog."""

    status: ToolStatus = Field(description="Tool run status.")
    tool: str = Field(default="generate_changelog", description="Tool identifier.")
    version: str = Field(description="Release version that was processed.")
    format: ChangelogEntryFormat = Field(description="Output format used.")
    changelog_text: str = Field(description="Generated changelog entry or release notes text.")
    categories: list[ChangelogCategoryEntry] = Field(
        default_factory=list, description="Structured category entries."
    )
    commits_parsed: int = Field(default=0, description="Number of commits processed.")
    pipeline_context: PipelineContext | None = Field(default=None, description="Pipeline context.")


# ---------------------------------------------------------------------------
# Custom theme generation
# ---------------------------------------------------------------------------


class CustomThemeTarget(StrEnum):
    """Scope of files to generate for a custom theme."""

    CSS_ONLY = "css-only"
    CSS_AND_JS = "css-and-js"
    FULL = "full"


class GenerateCustomThemeRequest(ModelBase):
    """Input contract for generate_custom_theme."""

    framework: FrameworkName = Field(description="Target documentation framework.")
    theme_name: str = Field(
        default="custom",
        description="Theme identifier used in scheme names and file prefixes.",
    )
    primary_color: str = Field(
        default="#1de9b6",
        description="Primary brand color as a hex string (e.g. '#1de9b6').",
    )
    accent_color: str = Field(
        default="#7c4dff",
        description="Accent / secondary color as a hex string (e.g. '#7c4dff').",
    )
    output_dir: Path = Field(
        default=Path("docs/stylesheets"),
        description="Directory to write generated theme files into.",
    )
    target: CustomThemeTarget = Field(
        default=CustomThemeTarget.CSS_AND_JS,
        description="Scope of files to generate.",
    )
    dark_mode: bool = Field(
        default=True,
        description="Whether to generate a dark-mode variant alongside the light theme.",
    )
    font_body: str | None = Field(
        default=None,
        description="Body font family name (e.g. 'Inter'). Uses framework default when omitted.",
    )
    font_code: str | None = Field(
        default=None,
        description="Monospace font family (e.g. 'JetBrains Mono'). Uses framework default when omitted.",  # noqa: E501
    )


class GenerateCustomThemeFile(ModelBase):
    """A single file produced by the custom theme generator."""

    path: Path = Field(description="Relative path of the generated file.")
    content: str = Field(description="Full file content as a string.")
    description: str = Field(description="Human-readable description of the file's role.")


class GenerateCustomThemeResponse(ModelBase):
    """Output contract for generate_custom_theme."""

    status: ToolStatus = Field(description="Tool run status.")
    tool: str = Field(default="generate_custom_theme", description="Tool identifier.")
    framework: FrameworkName = Field(description="Framework the theme was generated for.")
    theme_name: str = Field(description="Theme identifier used.")
    files: list[GenerateCustomThemeFile] = Field(
        default_factory=list,
        description="Generated files (path + content + description).",
    )
    config_snippet: str = Field(
        description="Config file snippet showing how to wire the theme into the framework.",
    )
    message: str | None = Field(default=None, description="Optional status message.")


class ZensicalExtension(StrEnum):
    """Python Markdown Extensions (pymdownx) and standard extensions supported by Zensical."""

    # Core math
    ARITHMATEX = "pymdownx.arithmatex"
    # Inline formatting
    CARET = "pymdownx.caret"
    MARK = "pymdownx.mark"
    TILDE = "pymdownx.tilde"
    # Content blocks
    DETAILS = "pymdownx.details"
    SUPERFENCES = "pymdownx.superfences"
    TABBED = "pymdownx.tabbed"
    TASKLIST = "pymdownx.tasklist"
    # Code highlighting
    HIGHLIGHT = "pymdownx.highlight"
    INLINEHILITE = "pymdownx.inlinehilite"
    # Icons and emoji
    EMOJI = "pymdownx.emoji"
    # Keys and symbols
    KEYS = "pymdownx.keys"
    SMARTSYMBOLS = "pymdownx.smartsymbols"
    # Content reuse
    SNIPPETS = "pymdownx.snippets"
    # Blocks caption (Zensical-specific)
    BLOCKS_CAPTION = "pymdownx.blocks.caption"
    # Beta Blocks system (generic containers)
    BLOCKS = "pymdownx.blocks"
    BLOCKS_ADMONITION = "pymdownx.blocks.admonition"
    BLOCKS_DETAILS = "pymdownx.blocks.details"
    BLOCKS_HTML = "pymdownx.blocks.html"
    BLOCKS_TAB = "pymdownx.blocks.tab"
    # Links
    MAGICLINK = "pymdownx.magiclink"
    # Track changes
    CRITIC = "pymdownx.critic"
    # Emphasis helpers
    BETTEREM = "pymdownx.betterem"
    # Lists
    FANCYLISTS = "pymdownx.fancylists"
    # Misc
    ESCAPEALL = "pymdownx.escapeall"
    SANEHEADERS = "pymdownx.saneheaders"
    PROGRESSBAR = "pymdownx.progressbar"
    STRIPHTML = "pymdownx.striphtml"
    PATHCONVERTER = "pymdownx.pathconverter"
    # Standard Python-Markdown extensions
    ADMONITION = "admonition"
    ATTR_LIST = "attr_list"
    DEF_LIST = "def_list"
    FOOTNOTES = "footnotes"
    MD_IN_HTML = "md_in_html"
    TOC = "toc"


class ZensicalExtensionConfig(ModelBase):
    """Configuration details for a single Zensical extension."""

    extension: ZensicalExtension = Field(description="Extension identifier.")
    description: str = Field(description="What the extension enables.")
    toml_snippet: str = Field(description="zensical.toml TOML configuration block.")
    yaml_snippet: str = Field(description="mkdocs.yml YAML configuration block.")
    authoring_guides: list[str] = Field(
        default_factory=list,
        description="Links or descriptions of relevant authoring capabilities.",
    )
    requires: list[ZensicalExtension] = Field(
        default_factory=list,
        description="Other extensions that must also be enabled.",
    )
    extra_js: list[str] = Field(
        default_factory=list,
        description="Extra JavaScript assets required (e.g. MathJax CDN for arithmatex).",
    )


class ConfigureZensicalExtensionsRequest(ModelBase):
    """Input contract for configure_zensical_extensions."""

    extensions: list[ZensicalExtension] = Field(
        description="Extensions to configure. Returns TOML and YAML config blocks for each.",
    )
    output_format: Literal["toml", "yaml", "both"] = Field(
        default="toml",
        description="Config format to return: 'toml' (zensical.toml), 'yaml' (mkdocs.yml), or 'both'.",  # noqa: E501
    )
    include_authoring_examples: bool = Field(
        default=True,
        description="Whether to include Markdown authoring usage examples for each extension.",
    )


class ConfigureZensicalExtensionsResponse(ModelBase):
    """Output contract for configure_zensical_extensions."""

    status: ToolStatus = Field(description="Tool run status.")
    tool: str = Field(default="configure_zensical_extensions", description="Tool identifier.")
    extensions: list[ZensicalExtensionConfig] = Field(
        description="Per-extension configuration details.",
    )
    combined_toml: str = Field(
        description="Combined zensical.toml block for all requested extensions.",
    )
    combined_yaml: str = Field(
        description="Combined mkdocs.yml block for all requested extensions.",
    )
    extra_js: list[str] = Field(
        default_factory=list,
        description="Deduplicated list of extra_javascript entries required.",
    )
    message: str | None = Field(default=None, description="Optional status message.")


__all__ = [
    "COPILOT_DEFAULT_TOOLS",
    "AgentConfigRequest",
    "AgentConfigResponse",
    "AgentFrontmatter",
    "AgentPlatform",
    "AnswerSlotContract",
    "AnswerSlotType",
    "AuthoringPrimitive",
    "BatchScaffoldRequest",
    "BatchScaffoldResponse",
    "BoilerplateGenerationErrorCode",
    "CapabilityMatrixItem",
    "CapabilityMatrixV2Response",
    "CapabilityStrategy",
    "CapabilitySupportDetail",
    "ChangelogCategoryEntry",
    "ChangelogEntryFormat",
    "CheckDocsLinksRequest",
    "CheckDocsLinksResponse",
    "CheckInitStatusRequest",
    "CheckInitStatusResponse",
    "CheckLanguageStructureRequest",
    "CheckLanguageStructureResponse",
    "CheckOrphanDocsRequest",
    "CheckOrphanDocsResponse",
    "ComposeDocsStoryRequest",
    "ComposeDocsStoryResponse",
    "ConfigureZensicalExtensionsRequest",
    "ConfigureZensicalExtensionsResponse",
    "CopilotAgentMode",
    "CopilotArtifactKind",
    "CopilotInitArtifactMetadata",
    "CopilotPromptMode",
    "CreateAgentRequest",
    "CreateAgentResponse",
    "CreateCopilotArtifactRequest",
    "CreateCopilotArtifactResponse",
    "CreateInstructionRequest",
    "CreateInstructionResponse",
    "CreatePromptRequest",
    "CreatePromptResponse",
    "CreateSvgAssetRequest",
    "CreateSvgAssetResponse",
    "CustomThemeTarget",
    "DeploymentUrlConfig",
    "DetectDocsContextRequest",
    "DetectDocsContextResponse",
    "DetectFrameworkRequest",
    "DetectFrameworkResponse",
    "DetectProjectReadinessRequest",
    "DetectProjectReadinessResponse",
    "DeterministicTurnPlan",
    "DeterministicTurnStep",
    "DiagramType",
    "DocsDeployPipelineArtifactMetadata",
    "DocsDeployProvider",
    "DocsValidationCheck",
    "DocstringAuditRequest",
    "DocstringAuditResponse",
    "DocstringLanguage",
    "DocstringOptimizerRequest",
    "DocstringOptimizerResponse",
    "DocstringStyle",
    "DocstringSymbol",
    "EnrichDocRequest",
    "EnrichDocResponse",
    "EphemeralInstallRequest",
    "EphemeralInstallResponse",
    "ExploreStageContract",
    "ExploreStoryStage",
    "FrameworkAdvantage",
    "FrameworkAdvantageReference",
    "FrameworkDetectionResult",
    "FrameworkInitSpec",
    "FrameworkName",
    "FrontmatterAuditRequest",
    "FrontmatterAuditResponse",
    "FrontmatterFileAudit",
    "GatedBoilerplateGenerationRequest",
    "GatedBoilerplateGenerationResponse",
    "GeneralDocsReference",
    "GenerateChangelogRequest",
    "GenerateChangelogResponse",
    "GenerateCliDocsRequest",
    "GenerateCliDocsResponse",
    "GenerateCustomThemeFile",
    "GenerateCustomThemeRequest",
    "GenerateCustomThemeResponse",
    "GenerateDiagramRequest",
    "GenerateDiagramResponse",
    "GenerateMcpToolsDocsRequest",
    "GenerateMcpToolsDocsResponse",
    "GenerateProjectManifestDocsResponse",
    "GenerateReferenceDocsKind",
    "GenerateReferenceDocsRequest",
    "GenerateReferenceDocsResponse",
    "GenerateVisualAssetRequest",
    "GenerateVisualAssetResponse",
    "GetAuthoringProfileResponse",
    "InitFrameworkStructureRequest",
    "InitFrameworkStructureResponse",
    "InitProjectRequest",
    "InitProjectResponse",
    "InstructionFrontmatter",
    "InteractionQuestionType",
    "LinkIssue",
    "ManifestType",
    "MaterialSnippetRequest",
    "MaterialSnippetResponse",
    "MigrationModeContract",
    "MigrationQualityEnhancementToggles",
    "ModuleIntentProfile",
    "ModuleOutputContract",
    "NavIssue",
    "NavIssueKind",
    "OnboardProjectMode",
    "OnboardProjectRequest",
    "OnboardProjectResponse",
    "OnboardProjectWarningMetadata",
    "OnboardingGuidanceContract",
    "OnboardingSkeletonRequest",
    "OnboardingSkeletonResponse",
    "OrchestratorResultContract",
    "PhaseArtifact",
    "PipelineContext",
    "PipelinePhase",
    "PipelinePhaseRequest",
    "PipelinePhaseResponse",
    "PipelinePhaseStatus",
    "PlanDocsRequest",
    "PlanDocsResponse",
    "PlannedPage",
    "PrimitiveCatalogResponse",
    "PrimitiveResolutionMode",
    "PrimitiveSupportLookupRequest",
    "PrimitiveSupportLookupResponse",
    "PrimitiveTranslationGuidance",
    "PromptFrontmatter",
    "QualityIssue",
    "QualityScore",
    "QuestionItemContract",
    "ReadinessLevel",
    "RenderDiagramRequest",
    "RenderDiagramResponse",
    "RenderPrimitiveSnippetRequest",
    "RenderPrimitiveSnippetResponse",
    "ResolvePrimitiveRequest",
    "ResolvePrimitiveResponse",
    "RuntimeOnboardingMatrixResponse",
    "RuntimeTrack",
    "ScaffoldDocRequest",
    "ScaffoldDocResponse",
    "ScoreDocsQualityRequest",
    "ScoreDocsQualityResponse",
    "ShellScriptArtifactMetadata",
    "ShellScriptType",
    "StoryExplorationStage",
    "StoryFeedbackLoopState",
    "StoryGapSeverity",
    "StoryGenerationRequest",
    "StoryGenerationResponse",
    "StoryMigrationMode",
    "StoryNextQuestionContract",
    "StorySessionStateContract",
    "StorySessionStatus",
    "StoryTurnTransition",
    "StructureIssue",
    "SupportLevel",
    "SvgPngScriptsResponse",
    "SvgPromptRequest",
    "SvgPromptResponse",
    "SvgPromptToolkitResponse",
    "SyncNavMode",
    "SyncNavRequest",
    "SyncNavResponse",
    "ToolSignature",
    "TranslatePrimitiveSyntaxRequest",
    "TranslatePrimitiveSyntaxResponse",
    "TranslatePrimitivesRequest",
    "TranslatePrimitivesResponse",
    "TurnPlanAction",
    "ValidateDocsRequest",
    "ValidateDocsResponse",
    "VisualAssetBackend",
    "VisualAssetBackendMetadata",
    "VisualAssetConversionRequest",
    "VisualAssetConversionResponse",
    "VisualAssetFormat",
    "VisualAssetKind",
    "VisualAssetOperation",
    "VisualAssetSpec",
    "WriteDocRequest",
    "WriteDocResponse",
    "ZensicalExtension",
    "ZensicalExtensionConfig",
]
