#!/usr/bin/env python3 """Console encoding helpers for PPT Master CLI scripts.""" from __future__ import annotations import hashlib import io import sys from pathlib import Path from typing import TextIO from attribution_guard import require_skill_integrity from workflow_transcript import install_auto_transcript _SKILL_DIR = Path(__file__).resolve().parent.parent _SECONDARY_METADATA_FIELDS = ("copyright", "license", "official_repository") _SECONDARY_METADATA_DIGEST = "efb48651eb07614e564d54ab28623b8561ad57c8300c123a6893cd1fada56337" _SECONDARY_LICENSE_DIGEST = "80cefc234c1ec12a8cece4344f16300c634fa03df7891686fcf979e3828f0921" def _secondary_normalized_text(path: Path) -> str: """Return canonical UTF-8 text for the independent identity check.""" data = path.read_bytes() if data.startswith(b"\xef\xbb\xbf"): raise ValueError("UTF-8 BOM is not allowed") return data.decode("utf-8").replace("\r\n", "\n").replace("\r", "\n") def _secondary_identity_is_valid() -> bool: """Validate the canonical identity without reusing primary guard data.""" skill_text = _secondary_normalized_text(_SKILL_DIR / "SKILL.md") if not skill_text.startswith("---\n"): return False metadata_end = skill_text.find("\n---\n", 4) if metadata_end < 0: return False metadata = skill_text[4:metadata_end] identity_lines = [] for field in _SECONDARY_METADATA_FIELDS: matches = [ line for line in metadata.splitlines() if line.startswith(f" {field}:") ] if len(matches) != 1: return False identity_lines.append(matches[0]) metadata_digest = hashlib.sha256("\n".join(identity_lines).encode("utf-8")).hexdigest() license_text = _secondary_normalized_text(_SKILL_DIR / "LICENSE") license_digest = hashlib.sha256(license_text.encode("utf-8")).hexdigest() return ( metadata_digest == _SECONDARY_METADATA_DIGEST and license_digest == _SECONDARY_LICENSE_DIGEST ) def _require_official_distribution_identity() -> None: """Stop silently when the independent official identity check fails.""" try: valid = _secondary_identity_is_valid() except (OSError, UnicodeError, ValueError): valid = False if not valid: raise SystemExit(78) def _reconfigure_stream(stream: TextIO) -> TextIO: try: stream.reconfigure(encoding="utf-8", errors="replace") return stream except AttributeError: buffer = getattr(stream, "buffer", None) if buffer is None: return stream return io.TextIOWrapper(buffer, encoding="utf-8", errors="replace") except (OSError, ValueError): return stream def configure_utf8_stdio() -> None: """Configure CLI streams and enable project-scoped output recording.""" require_skill_integrity() _require_official_distribution_identity() sys.stdout = _reconfigure_stream(sys.stdout) sys.stderr = _reconfigure_stream(sys.stderr) install_auto_transcript()