← Files ClaraARCHIVED FILE

skills/html-deck/scripts/init_html_deck.py

4.73 KB · Oct 2, 2026 · 00:29 UTC

↓ Download file

#!/usr/bin/env python3
"""Initialize editable sources for a Clara standalone HTML deck."""

from __future__ import annotations

import argparse
import json
import shutil
import sys
from pathlib import Path

SCHEMA_VERSION = "clara.html_deck_work.v1"
LEDGER_SCHEMA_VERSION = "clara.html_deck_ledger.v1"


def parse_args() -> argparse.Namespace:
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("--work-dir", required=True, type=Path)
    parser.add_argument("--title", required=True)
    parser.add_argument("--subtitle", default="")
    parser.add_argument("--author", default="")
    parser.add_argument("--eyebrow", default="Presentation")
    parser.add_argument("--language", default="it")
    parser.add_argument("--description", default="")
    parser.add_argument("--theme-color", default="#141817")
    return parser.parse_args()


def require_empty_target(path: Path) -> None:
    if path.exists():
        if not path.is_dir():
            raise ValueError(f"Work path is not a directory: {path}")
        if any(path.iterdir()):
            raise ValueError(f"Work directory is not empty: {path}")
    path.mkdir(parents=True, exist_ok=True)


def main() -> int:
    args = parse_args()
    try:
        work_dir = args.work_dir.expanduser().resolve()
        require_empty_target(work_dir)
        engine_dir = Path(__file__).resolve().parents[1] / "assets" / "deck-engine"
        starter = engine_dir / "starter-slides.html"
        starter_plan = (
            Path(__file__).resolve().parents[1]
            / "assets"
            / "layout-library"
            / "starter-deck-plan.json"
        )
        if not starter.is_file():
            raise FileNotFoundError(f"Missing starter slides: {starter}")
        if not starter_plan.is_file():
            raise FileNotFoundError(f"Missing starter deck plan: {starter_plan}")

        metadata = {
            "schema_version": SCHEMA_VERSION,
            "title": args.title.strip(),
            "subtitle": args.subtitle.strip(),
            "author": args.author.strip(),
            "eyebrow": args.eyebrow.strip(),
            "language": args.language.strip() or "it",
            "description": args.description.strip()
            or args.subtitle.strip()
            or args.title.strip(),
            "robots": "noindex,nofollow,noarchive",
            "theme_color": args.theme_color.strip() or "#141817",
        }
        if not metadata["title"]:
            raise ValueError("Deck title cannot be empty")

        (work_dir / "deck.json").write_text(
            json.dumps(metadata, ensure_ascii=False, indent=2, sort_keys=True) + "\n",
            encoding="utf-8",
        )
        shutil.copyfile(starter, work_dir / "slides.html")
        shutil.copyfile(starter_plan, work_dir / "deck-plan.json")
        plan_payload = json.loads(starter_plan.read_text(encoding="utf-8"))
        slide_ids = [
            str(slide.get("id", "")).strip()
            for slide in plan_payload.get("slides", [])
            if isinstance(slide, dict)
        ]
        if not slide_ids:
            raise ValueError("Starter deck plan has no stable slide IDs")
        ledger = {
            "schema_version": LEDGER_SCHEMA_VERSION,
            "sources": [],
            "slides": [
                {
                    "slide_id": slide_id,
                    "basis_status": "not-applicable",
                    "basis_note": "Template example; replace with the actual evidence basis before delivery.",
                    "claims": [],
                }
                for slide_id in slide_ids
            ],
        }
        (work_dir / "content-ledger.json").write_text(
            json.dumps(ledger, ensure_ascii=False, indent=2, sort_keys=True) + "\n",
            encoding="utf-8",
        )
        (work_dir / "custom.css").write_text(
            "/* Add only deck-specific semantic layouts here. Keep the shared engine intact. */\n",
            encoding="utf-8",
        )
        result = {
            "schema_version": SCHEMA_VERSION,
            "work_dir": str(work_dir),
            "metadata": str(work_dir / "deck.json"),
            "deck_plan": str(work_dir / "deck-plan.json"),
            "slides": str(work_dir / "slides.html"),
            "custom_css": str(work_dir / "custom.css"),
            "content_ledger": str(work_dir / "content-ledger.json"),
            "next": (
                "Edit deck-plan.json and content-ledger.json, compose with "
                "compose_html_deck.py --force, then build."
            ),
        }
        print(json.dumps(result, ensure_ascii=False, indent=2, sort_keys=True))
        return 0
    except (OSError, ValueError) as exc:
        print(f"error: {exc}", file=sys.stderr)
        return 2


if __name__ == "__main__":
    raise SystemExit(main())

SHA-256: 5c47bd53b7aac2f64844f377aac475179109392b672a4c934707c7a74865e94c