from typing import Dict, Tuple, List, Any import json from pathlib import Path from scripts.mindmodel import loader from scripts.mindmodel import checks def validate_manifest(path: str, base_dir: str = None) -> Tuple[int, Dict[str, Any]]: """Validate a manifest file at `path`. Returns a tuple (exit_code, report). exit codes: 0 - ok (no issues) 1 - warnings (only truncated snippets found) 2 - critical (missing files, secrets, or parse error) """ report: Dict[str, Any] = { "path": path, "secrets": [], "missing_files": [], "truncated": 0, "constraints": [], } p = Path(path) try: raw_text = p.read_text(encoding="utf-8") except Exception as exc: report["load_error"] = f"Manifest file not readable: {exc}" return 2, report # scan for secrets in the manifest text secrets = checks.find_potential_secrets(raw_text) report["secrets"] = secrets try: manifest = loader.load_manifest(path) except loader.ManifestLoadError as exc: report["load_error"] = str(exc) # treat parse/load errors as critical return 2, report constraints = manifest.get("constraints") or [] for constraint in constraints: c_rep: Dict[str, Any] = {"constraint": constraint, "evidence": []} for ev in ( constraint.get("evidence", []) if isinstance(constraint.get("evidence", []), list) else [] ): text = ev.get("text") if isinstance(ev, dict) else None file_ref = ev.get("file") if isinstance(ev, dict) else None exists = True if file_ref: if not checks.file_exists(base_dir or "", file_ref): exists = False report["missing_files"].append(file_ref) truncated = False if text: truncated = checks.detect_truncated(text) if truncated: report["truncated"] += 1 c_rep["evidence"].append( { "text": text, "file": file_ref, "exists": exists, "truncated": truncated, } ) report["constraints"].append(c_rep) # decide exit code if report["secrets"]: return 2, report if report["missing_files"]: return 2, report if report["truncated"] > 0: return 1, report return 0, report def main(argv: List[str]) -> int: import sys if len(argv) < 2: print(json.dumps({"error": "manifest path required"})) return 2 path = argv[1] base_dir = argv[2] if len(argv) > 2 else None code, report = validate_manifest(path, base_dir=base_dir) print(json.dumps(report)) return code # no execution at import time