#!/usr/bin/env python3 """PPT Master SVG quality-check CLI implementation. Parses the legacy command-line contract and delegates validation to the checker. Usage: python3 scripts/svg_quality_checker.py [options] Examples: python3 scripts/svg_quality_checker.py projects/demo --stage final --json Dependencies: Standard library plus local PPT Master validation modules. """ import sys from pathlib import Path from attribution_guard import require_skill_integrity from slide_roster import discover_slide_svgs from .checker import SVGQualityChecker def _first_page_target(target: str) -> str: """Resolve a project/directory target to its first authored SVG page.""" path = Path(target) if path.is_file(): return str(path) svg_root = path / "svg_output" if (path / "svg_output").is_dir() else path svg_files = discover_slide_svgs(svg_root) if svg_root.is_dir() else [] return str(svg_files[0]) if svg_files else target def _default_json_report_path( checker: SVGQualityChecker, target: str, stage: str, ) -> Path: """Choose a stage-specific report path without overwriting the final gate.""" target_path = Path(target) project_path = checker._resolve_project_path(target_path) report_name = ( "svg_quality_report.json" if stage == "final" else "svg_quality_first_page_report.json" ) if ( (project_path / "svg_output").is_dir() or (project_path / "design_spec.md").is_file() ): return project_path / "validation" / report_name base = target_path if target_path.is_dir() else target_path.parent return base / report_name def print_usage() -> None: """Print CLI usage information.""" print("PPT Master - SVG Quality Check Tool\n") print("Usage:") print(" python3 scripts/svg_quality_checker.py ") print(" python3 scripts/svg_quality_checker.py ") print(" python3 scripts/svg_quality_checker.py /templates --template-mode") print(" python3 scripts/svg_quality_checker.py --all examples") print("\nExamples:") print(" python3 scripts/svg_quality_checker.py examples/project/svg_output/slide_01.svg") print(" python3 scripts/svg_quality_checker.py examples/project/svg_output") print(" python3 scripts/svg_quality_checker.py examples/project") print(" python3 scripts/svg_quality_checker.py templates/layouts/presentation_core/templates --template-mode") print(" python3 scripts/svg_quality_checker.py templates/decks/中国电信/templates --template-mode") print("\nOptions:") print(" --format Expected canvas format") print(" --stage first-page checks only the first authored SVG") print(" with a partial structure roster; final (default)") print(" requires the complete declared page roster.") print(" --json Write a machine-readable quality report") print(" --json-output Override the JSON report path") print(" --quick-generate Validate lockless flat Quick Generate SVGs;") print(" ignore design_spec.md and spec_lock.md.") print(" --template-mode Validate a template workspace's templates/ directory:") print(" Brand/Style validate their portable workspace contracts;") print(" Layout/Deck glob *.svg directly, skip spec_lock checks,") print(" enforce roster consistency, and emit placeholder hints.") print(" native_structure_mode: structured also enables complete") print(" per-file and cross-page structure validation. Legacy") print(" native_structure_mode: template fails and must be") print(" re-created through create-template before validation.") print(" Warnings are advisory: they require no modification and do not affect exit status;") print(" only errors make the command exit with status 1.") def main() -> None: """Run the CLI entry point.""" require_skill_integrity() if len(sys.argv) < 2: print_usage() sys.exit(0) if sys.argv[1] in {"-h", "--help", "help"}: print_usage() sys.exit(0) if sys.argv[1].startswith("--") and sys.argv[1] not in {"--all"}: print(f"[ERROR] Missing target before option: {sys.argv[1]}") print_usage() sys.exit(1) template_mode = "--template-mode" in sys.argv quick_generate = "--quick-generate" in sys.argv if template_mode and quick_generate: print("[ERROR] --template-mode cannot be combined with --quick-generate") sys.exit(1) checker = SVGQualityChecker( template_mode=template_mode, quick_generate=quick_generate, ) target = sys.argv[1] expected_format = None stage = "final" if "--format" in sys.argv: idx = sys.argv.index("--format") if idx + 1 < len(sys.argv): expected_format = sys.argv[idx + 1] if "--stage" in sys.argv: idx = sys.argv.index("--stage") if idx + 1 >= len(sys.argv): print("[ERROR] --stage requires first-page or final") sys.exit(1) stage = sys.argv[idx + 1] if stage not in {"first-page", "final"}: print(f"[ERROR] Unsupported quality-check stage: {stage}") sys.exit(1) if target == "--all": if quick_generate: print("[ERROR] --quick-generate does not support --all") sys.exit(1) if stage != "final": print("[ERROR] --stage first-page does not support --all") sys.exit(1) base_dir = sys.argv[2] if len(sys.argv) > 2 else "examples" from project_utils import find_all_projects projects = find_all_projects(base_dir) for project in projects: print(f"\n{'=' * 80}") print(f"Checking project: {project.name}") print("=" * 80) checker.check_directory(str(project)) else: check_target = _first_page_target(target) if stage == "first-page" else target checker.check_directory(check_target, expected_format) if stage == "final" and Path(target).is_dir(): if checker._has_incomplete_page_roster: print( "[TIP] This final-stage run found an incomplete page roster. " "During serial authoring, use --stage first-page for the first-page " "gate; keep --stage final for the complete deck." ) checker.print_summary() if "--export" in sys.argv: output_file = "svg_quality_report.txt" if "--output" in sys.argv: idx = sys.argv.index("--output") if idx + 1 < len(sys.argv): output_file = sys.argv[idx + 1] checker.export_report(output_file) if "--json" in sys.argv or "--json-output" in sys.argv: if "--json-output" in sys.argv: idx = sys.argv.index("--json-output") if idx + 1 >= len(sys.argv): print("[ERROR] --json-output requires a path") sys.exit(1) json_output = Path(sys.argv[idx + 1]) else: json_output = _default_json_report_path(checker, target, stage) checker.export_json_report( str(json_output), target=target, stage=stage, ) if checker.summary["errors"] > 0: sys.exit(1) else: sys.exit(0)