from __future__ import annotations import html import json from collections import defaultdict from pathlib import Path from typing import Any ROOT = Path(__file__).resolve().parent.parent CATALOG_MD = ROOT / "docs" / "CapabilityCatalog.md" CATALOG_HTML_DIR = ROOT / "docs" / "catalog" CATALOG_HTML = CATALOG_HTML_DIR / "index.html" CATALOG_JSON = CATALOG_HTML_DIR / "registry.json" CATALOG_SEARCH = CATALOG_HTML_DIR / "search.html" GRAPH_HTML = ROOT / "docs" / "graph" / "index.html" def _grouped_capabilities( indexed_entries: list[tuple[dict[str, Any], dict[str, Any]]], ) -> dict[str, list[tuple[dict[str, Any], dict[str, Any]]]]: grouped: dict[str, list[tuple[dict[str, Any], dict[str, Any]]]] = defaultdict( list ) for index_item, entry in indexed_entries: domain = index_item.get("domain", "unknown") grouped[domain].append((index_item, entry)) return dict(sorted(grouped.items())) def render_markdown( index: dict[str, Any], indexed_entries: list[tuple[dict[str, Any], dict[str, Any]]], ) -> str: lines = [ "# Capability Catalog", "", f"**Domain:** {index.get('domain', 'unknown')} ", f"**Updated:** {index.get('updated', 'unknown')} ", f"**Entries:** {len(indexed_entries)}", "", "Generated by `reuse-surface catalog`. Do not edit manually.", "", ] for domain, items in _grouped_capabilities(indexed_entries).items(): lines.extend([f"## {domain}", ""]) for index_item, entry in sorted(items, key=lambda pair: pair[0]["id"]): lines.extend( [ f"### {index_item['name']}", "", f"- **ID:** `{index_item['id']}`", f"- **Vector:** {index_item['vector']}", f"- **Owner:** {index_item.get('owner', 'unknown')}", f"- **Path:** `{index_item['path']}`", f"- **Summary:** {index_item['summary']}", "", ] ) guidance = entry.get("consumer_guidance") or {} limitations = guidance.get("known_limitations") or [] if limitations: lines.append("**Known limitations:**") lines.extend(f"- {item}" for item in limitations) lines.append("") return "\n".join(lines).rstrip() + "\n" def render_html( index: dict[str, Any], indexed_entries: list[tuple[dict[str, Any], dict[str, Any]]], ) -> str: sections: list[str] = [] for domain, items in _grouped_capabilities(indexed_entries).items(): cards: list[str] = [] for index_item, entry in sorted(items, key=lambda pair: pair[0]["id"]): name = html.escape(index_item["name"]) summary = html.escape(index_item["summary"]) cap_id = html.escape(index_item["id"]) vector = html.escape(index_item["vector"]) path = html.escape(index_item["path"]) cards.append( f"""

{name}

{cap_id} · {vector}

{summary}

{path}

""" ) sections.append( f"

{html.escape(domain)}

\n" + "\n".join(cards) + "
" ) body = "\n".join(sections) title = html.escape(f"Capability Catalog — {index.get('domain', 'unknown')}") return f""" {title}

Capability Catalog

Updated {html.escape(str(index.get('updated', 'unknown')))} · {len(indexed_entries)} entries

{body} """ def render_search_html() -> str: return """ Capability Catalog Search

Capability Catalog

Client-side search over registry.json. Generated by reuse-surface catalog.

""" def render_graph_explorer(mermaid_source: str) -> str: escaped = json.dumps(mermaid_source) return f""" Capability Relation Graph

Capability Relation Graph

Generated from entry relations fields. Regenerate with reuse-surface graph.


  


"""


def write_catalog(
    index: dict[str, Any],
    indexed_entries: list[tuple[dict[str, Any], dict[str, Any]]],
    *,
    mermaid_source: str | None = None,
) -> list[Path]:
    CATALOG_HTML_DIR.mkdir(parents=True, exist_ok=True)
    written: list[Path] = []
    CATALOG_MD.write_text(render_markdown(index, indexed_entries), encoding="utf-8")
    written.append(CATALOG_MD)
    CATALOG_HTML.write_text(render_html(index, indexed_entries), encoding="utf-8")
    written.append(CATALOG_HTML)
    payload = {
        "domain": index.get("domain"),
        "updated": index.get("updated"),
        "capabilities": [item for item, _ in indexed_entries],
    }
    CATALOG_JSON.write_text(json.dumps(payload, indent=2), encoding="utf-8")
    written.append(CATALOG_JSON)
    CATALOG_SEARCH.write_text(render_search_html(), encoding="utf-8")
    written.append(CATALOG_SEARCH)
    if mermaid_source is not None:
        GRAPH_HTML.parent.mkdir(parents=True, exist_ok=True)
        GRAPH_HTML.write_text(render_graph_explorer(mermaid_source), encoding="utf-8")
        written.append(GRAPH_HTML)
    return written