open-design/skills/html-to-design-spec/scripts/create_run_dir.py

134 lines
4.3 KiB
Python
Executable file

#!/usr/bin/env python3
"""Create a html-to-design-spec run directory skeleton."""
from __future__ import annotations
import argparse
import json
import re
from datetime import datetime
from pathlib import Path
def slugify(value: str) -> str:
value = value.strip().lower()
value = re.sub(r"\.[a-z0-9]+$", "", value)
value = re.sub(r"[^a-z0-9]+", "-", value)
value = value.strip("-")
return value or "prototype"
def timestamp_to_iso(timestamp: str, local_timezone) -> str | None:
for fmt in ("%Y%m%d-%H%M%S", "%Y%m%d%H%M%S"):
try:
parsed = datetime.strptime(timestamp, fmt)
except ValueError:
continue
return parsed.replace(tzinfo=local_timezone).isoformat(timespec="seconds")
return None
def main() -> int:
parser = argparse.ArgumentParser()
parser.add_argument("project_dir", help="Directory where the analysis result should be created.")
parser.add_argument("--name", help="HTML file stem, route, app name, or other run label.")
parser.add_argument("--target", help="Prototype URL or file path being analyzed.")
parser.add_argument("--timestamp", help="Timestamp suffix. Defaults to local YYYYMMDD-HHMMSS.")
parser.add_argument(
"-p",
"--prompt-constraint",
action="append",
default=[],
help="Runtime constraint from the user prompt. May be provided multiple times.",
)
args = parser.parse_args()
project_dir = Path(args.project_dir).expanduser().resolve()
now = datetime.now().astimezone()
timestamp = args.timestamp or now.strftime("%Y%m%d-%H%M%S")
created_at_iso = timestamp_to_iso(timestamp, now.tzinfo) if args.timestamp else now.isoformat(timespec="seconds")
base_name = args.name or (Path(args.target).name if args.target else project_dir.name)
run_dir = project_dir / f"{slugify(base_name)}-{timestamp}"
run_dir.mkdir(parents=True, exist_ok=False)
for child in ("screenshots", "prompts", "specs"):
(run_dir / child).mkdir()
pages = {
"schemaVersion": "html-to-design-spec/v1",
"metadata": {
"target": args.target,
"capturedAt": None,
"viewports": [],
"tooling": [],
"runConstraints": args.prompt_constraint,
},
"discoveredActions": [],
"dedupeLog": [],
"pages": [],
"states": [],
}
style = {
"schemaVersion": "html-to-design-spec/v1",
"designTokens": {
"colors": [],
"backgroundTokens": [],
"typography": [],
"spacing": [],
"radii": [],
"shadows": [],
"borders": [],
},
"componentStyles": [],
"responsiveStyle": [],
"visualReferences": [],
}
(run_dir / "pages.json").write_text(
json.dumps(pages, indent=2) + "\n",
encoding="utf-8",
)
(run_dir / "flows.json").write_text(
json.dumps({"schemaVersion": "html-to-design-spec/v1", "flows": []}, indent=2) + "\n",
encoding="utf-8",
)
(run_dir / "graph.json").write_text(
json.dumps({"schemaVersion": "html-to-design-spec/v1", "nodes": [], "edges": []}, indent=2) + "\n",
encoding="utf-8",
)
(run_dir / "style.json").write_text(
json.dumps(style, indent=2) + "\n",
encoding="utf-8",
)
metadata = {
"schemaVersion": "html-to-design-spec/v1",
"target": args.target,
"createdAt": timestamp,
"createdAtIso": created_at_iso,
"runDirectory": str(run_dir),
"runConstraints": args.prompt_constraint,
"viewports": [],
"tooling": [],
"defaultOutputs": ["pages.json", "style.json", "screenshots/"],
"conditionalOutputs": ["flows.json", "graph.json", "prompts/", "specs/"],
"generatedOutputs": [],
"placeholderOutputs": [
"pages.json",
"style.json",
"screenshots/",
"flows.json",
"graph.json",
"prompts/",
"specs/",
],
"omittedOutputs": [],
"validationStatus": "pending",
}
(run_dir / "manifest.json").write_text(json.dumps(metadata, indent=2) + "\n", encoding="utf-8")
print(run_dir)
return 0
if __name__ == "__main__":
raise SystemExit(main())