返回 ppt-master
cli.py
1 #!/usr/bin/env python3
2 """PPT Master SVG quality-check CLI implementation.
3
4 Parses the legacy command-line contract and delegates validation to the checker.
5
6 Usage:
7 python3 scripts/svg_quality_checker.py <svg_file_or_project> [options]
8
9 Examples:
10 python3 scripts/svg_quality_checker.py projects/demo --stage final --json
11
12 Dependencies:
13 Standard library plus local PPT Master validation modules.
14 """
15
16 import sys
17 from pathlib import Path
18
19 from attribution_guard import require_skill_integrity
20 from slide_roster import discover_slide_svgs
21
22 from .checker import SVGQualityChecker
23
24
25 def _first_page_target(target: str) -> str:
26 """Resolve a project/directory target to its first authored SVG page."""
27 path = Path(target)
28 if path.is_file():
29 return str(path)
30 svg_root = path / "svg_output" if (path / "svg_output").is_dir() else path
31 svg_files = discover_slide_svgs(svg_root) if svg_root.is_dir() else []
32 return str(svg_files[0]) if svg_files else target
33
34
35 def _default_json_report_path(
36 checker: SVGQualityChecker,
37 target: str,
38 stage: str,
39 ) -> Path:
40 """Choose a stage-specific report path without overwriting the final gate."""
41 target_path = Path(target)
42 project_path = checker._resolve_project_path(target_path)
43 report_name = (
44 "svg_quality_report.json"
45 if stage == "final"
46 else "svg_quality_first_page_report.json"
47 )
48 if (
49 (project_path / "svg_output").is_dir()
50 or (project_path / "design_spec.md").is_file()
51 ):
52 return project_path / "validation" / report_name
53 base = target_path if target_path.is_dir() else target_path.parent
54 return base / report_name
55
56
57 def print_usage() -> None:
58 """Print CLI usage information."""
59 print("PPT Master - SVG Quality Check Tool\n")
60 print("Usage:")
61 print(" python3 scripts/svg_quality_checker.py <svg_file>")
62 print(" python3 scripts/svg_quality_checker.py <directory>")
63 print(" python3 scripts/svg_quality_checker.py <workspace>/templates --template-mode")
64 print(" python3 scripts/svg_quality_checker.py --all examples")
65 print("\nExamples:")
66 print(" python3 scripts/svg_quality_checker.py examples/project/svg_output/slide_01.svg")
67 print(" python3 scripts/svg_quality_checker.py examples/project/svg_output")
68 print(" python3 scripts/svg_quality_checker.py examples/project")
69 print(" python3 scripts/svg_quality_checker.py templates/layouts/presentation_core/templates --template-mode")
70 print(" python3 scripts/svg_quality_checker.py templates/decks/中国电信/templates --template-mode")
71 print("\nOptions:")
72 print(" --format <ppt169|ppt43|...> Expected canvas format")
73 print(" --stage <first-page|final> first-page checks only the first authored SVG")
74 print(" with a partial structure roster; final (default)")
75 print(" requires the complete declared page roster.")
76 print(" --json Write a machine-readable quality report")
77 print(" --json-output <path> Override the JSON report path")
78 print(" --quick-generate Validate lockless flat Quick Generate SVGs;")
79 print(" ignore design_spec.md and spec_lock.md.")
80 print(" --template-mode Validate a template workspace's templates/ directory:")
81 print(" Brand/Style validate their portable workspace contracts;")
82 print(" Layout/Deck glob *.svg directly, skip spec_lock checks,")
83 print(" enforce roster consistency, and emit placeholder hints.")
84 print(" native_structure_mode: structured also enables complete")
85 print(" per-file and cross-page structure validation. Legacy")
86 print(" native_structure_mode: template fails and must be")
87 print(" re-created through create-template before validation.")
88 print(" Warnings are advisory: they require no modification and do not affect exit status;")
89 print(" only errors make the command exit with status 1.")
90
91
92 def main() -> None:
93 """Run the CLI entry point."""
94 require_skill_integrity()
95 if len(sys.argv) < 2:
96 print_usage()
97 sys.exit(0)
98
99 if sys.argv[1] in {"-h", "--help", "help"}:
100 print_usage()
101 sys.exit(0)
102
103 if sys.argv[1].startswith("--") and sys.argv[1] not in {"--all"}:
104 print(f"[ERROR] Missing target before option: {sys.argv[1]}")
105 print_usage()
106 sys.exit(1)
107
108 template_mode = "--template-mode" in sys.argv
109 quick_generate = "--quick-generate" in sys.argv
110 if template_mode and quick_generate:
111 print("[ERROR] --template-mode cannot be combined with --quick-generate")
112 sys.exit(1)
113 checker = SVGQualityChecker(
114 template_mode=template_mode,
115 quick_generate=quick_generate,
116 )
117
118 target = sys.argv[1]
119 expected_format = None
120 stage = "final"
121
122 if "--format" in sys.argv:
123 idx = sys.argv.index("--format")
124 if idx + 1 < len(sys.argv):
125 expected_format = sys.argv[idx + 1]
126 if "--stage" in sys.argv:
127 idx = sys.argv.index("--stage")
128 if idx + 1 >= len(sys.argv):
129 print("[ERROR] --stage requires first-page or final")
130 sys.exit(1)
131 stage = sys.argv[idx + 1]
132 if stage not in {"first-page", "final"}:
133 print(f"[ERROR] Unsupported quality-check stage: {stage}")
134 sys.exit(1)
135
136 if target == "--all":
137 if quick_generate:
138 print("[ERROR] --quick-generate does not support --all")
139 sys.exit(1)
140 if stage != "final":
141 print("[ERROR] --stage first-page does not support --all")
142 sys.exit(1)
143 base_dir = sys.argv[2] if len(sys.argv) > 2 else "examples"
144 from project_utils import find_all_projects
145
146 projects = find_all_projects(base_dir)
147
148 for project in projects:
149 print(f"\n{'=' * 80}")
150 print(f"Checking project: {project.name}")
151 print("=" * 80)
152 checker.check_directory(str(project))
153 else:
154 check_target = _first_page_target(target) if stage == "first-page" else target
155 checker.check_directory(check_target, expected_format)
156
157 if stage == "final" and Path(target).is_dir():
158 if checker._has_incomplete_page_roster:
159 print(
160 "[TIP] This final-stage run found an incomplete page roster. "
161 "During serial authoring, use --stage first-page for the first-page "
162 "gate; keep --stage final for the complete deck."
163 )
164
165 checker.print_summary()
166
167 if "--export" in sys.argv:
168 output_file = "svg_quality_report.txt"
169 if "--output" in sys.argv:
170 idx = sys.argv.index("--output")
171 if idx + 1 < len(sys.argv):
172 output_file = sys.argv[idx + 1]
173 checker.export_report(output_file)
174
175 if "--json" in sys.argv or "--json-output" in sys.argv:
176 if "--json-output" in sys.argv:
177 idx = sys.argv.index("--json-output")
178 if idx + 1 >= len(sys.argv):
179 print("[ERROR] --json-output requires a path")
180 sys.exit(1)
181 json_output = Path(sys.argv[idx + 1])
182 else:
183 json_output = _default_json_report_path(checker, target, stage)
184 checker.export_json_report(
185 str(json_output),
186 target=target,
187 stage=stage,
188 )
189
190 if checker.summary["errors"] > 0:
191 sys.exit(1)
192 else:
193 sys.exit(0)
194
194 lines PYTHON