from __future__ import annotations import argparse import re import sys from pathlib import Path from . import __version__ from .calibration_profile import available_policy_names, load_calibration_profile, load_calibration_profile_file from .policy_intent import derive_policy_intent, simulate_policy_outcome from .render import write_outputs from .scanner import audit_repository _LEVEL_MAP = { 1: ("brief", 1), 2: ("detailed", 5), 3: ("detailed", 8), } _FORMAT_CHOICES = ["json", "md", "html", "pdf", "all"] _SUMMARY_CHOICES = ["full", "compact", "off"] _ADVISORY_CHOICES = ["none", "validate", "packet", "call"] _TIER_CHOICES = ["T0", "T1", "T2", "T3", "T4"] _TIER_ORDER = {"T0": 0, "T1": 1, "T2": 2, "T3": 3, "T4": 4} def _policy_choices() -> list[str]: names = available_policy_names() return names or ["default"] def _build_parser() -> argparse.ArgumentParser: parser = argparse.ArgumentParser( prog="stem", description=( "STEM BIO-AI local evidence-surface scanner for bio/medical AI repositories.\n\n" "Workflow-oriented commands:\n" " scan Generate JSON / Markdown / PDF artifacts for a local repository\n" " gate Enforce a minimum triage tier for CI/CD pipelines\n" " advisory Run provider-neutral advisory validation and packet workflows\n\n" "Time-to-value shortcut:\n" " stem behaves the same as stem scan " ), epilog=( "Common examples:\n" " stem /path/to/repo\n" " stem scan /path/to/repo --level 3 --format all --explain\n" " stem gate /path/to/repo --min-tier T2\n" " stem advisory packet /path/to/repo --output advisory_out\n" " stem advisory check-response /path/to/repo --response provider_advisory.json\n" ), formatter_class=argparse.RawDescriptionHelpFormatter, ) parser.add_argument("--version", action="version", version=f"STEM BIO-AI {__version__}") subparsers = parser.add_subparsers(dest="command") scan = subparsers.add_parser( "scan", help="Run a local scan and generate report artifacts", description="Scan a local repository and generate deterministic STEM BIO-AI artifacts.", ) _add_scan_arguments(scan, default_format="all", default_summary="full") audit = subparsers.add_parser( "audit", help="Backward-compatible alias for `scan`", description="Alias for `scan`. Kept for backward compatibility.", ) _add_scan_arguments(audit, default_format="all", default_summary="full") gate = subparsers.add_parser( "gate", help="Run a scan and fail if the repository tier is below the required threshold", description="CI/CD-oriented workflow: run the scan and enforce a minimum triage tier.", ) _add_shared_arguments(gate, default_format="json", default_summary="compact") gate.add_argument("target", help="Path to a local repository") gate.add_argument( "--min-tier", "--tier-gate", dest="min_tier", choices=_TIER_CHOICES, required=True, help="Minimum required tier; exit code 1 if the result is below this threshold", ) advisory = subparsers.add_parser( "advisory", help="Provider-neutral advisory workflows", description="Prepare, validate, or execute advisory workflows without letting advisory output override deterministic scores.", ) advisory_subparsers = advisory.add_subparsers(dest="advisory_command") advisory_subparsers.required = True advisory_validate = advisory_subparsers.add_parser( "validate", help="Run offline advisory validation against the deterministic result", description="Run the audit and attach offline advisory-validation metadata with no provider call.", ) _add_shared_arguments(advisory_validate, default_format="json", default_summary="compact") advisory_validate.add_argument("target", help="Path to a local repository") advisory_packet = advisory_subparsers.add_parser( "packet", help="Export a provider-neutral advisory packet", description="Run the audit and export a sanitized advisory input packet for downstream provider review.", ) _add_shared_arguments(advisory_packet, default_format="json", default_summary="compact") advisory_packet.add_argument("target", help="Path to a local repository") advisory_call = advisory_subparsers.add_parser( "call", help="Enter the explicit provider-call advisory workflow", description="Run the audit and execute the explicit provider-call advisory workflow.", ) _add_shared_arguments(advisory_call, default_format="json", default_summary="compact") advisory_call.add_argument("target", help="Path to a local repository") advisory_response = advisory_subparsers.add_parser( "check-response", help="Validate a provider-produced advisory response JSON", description="Run the audit and validate a provider-produced advisory response file against the evidence ledger.", ) _add_shared_arguments(advisory_response, default_format="json", default_summary="compact") advisory_response.add_argument("target", help="Path to a local repository") advisory_response.add_argument( "--response", "--advisory-response", dest="response", required=True, help="Path to a provider-produced advisory JSON response file", ) policy = subparsers.add_parser( "policy", help="Inspect available calibration profiles", description="List or explain named calibration profiles used by STEM BIO-AI policy surfaces.", ) policy_subparsers = policy.add_subparsers(dest="policy_command") policy_subparsers.required = True policy_subparsers.add_parser( "list", help="List available calibration profiles", description="Show the named calibration profiles that can be selected with --policy.", ) policy_explain = policy_subparsers.add_parser( "explain", help="Explain one calibration profile", description="Show the current contents and bounded differences of a named calibration profile.", ) policy_explain.add_argument("profile_name", choices=_policy_choices(), help="Calibration profile name") policy_derive = policy_subparsers.add_parser( "derive", help="Translate researcher intent into a named profile or preview-only delta", description="Apply the documented top-down rule table to a 1-5 researcher intent surface.", ) _add_intent_arguments(policy_derive) policy_simulate = policy_subparsers.add_parser( "simulate", help="Preview how a derived posture would change the current repository outcome", description="Run the deterministic scan, then simulate a named-profile or preview-only policy outcome without changing the stored scan contract.", ) _add_intent_arguments(policy_simulate, required=False) policy_simulate.add_argument("target", help="Path to a local repository") policy_simulate.add_argument( "--profile-file", default=None, help="Path to a local calibration profile JSON for simulation-only use", ) return parser def _add_scan_arguments(parser: argparse.ArgumentParser, *, default_format: str, default_summary: str) -> None: _add_shared_arguments(parser, default_format=default_format, default_summary=default_summary) parser.add_argument("target", help="Path to a local repository") parser.add_argument( "--advisory", choices=_ADVISORY_CHOICES, default="none", help="Compatibility path for advisory workflows; prefer `stem advisory validate|packet|call`", ) parser.add_argument( "--advisory-response", default=None, help="Compatibility path for provider response validation; prefer `stem advisory check-response ... --response FILE`", ) parser.add_argument( "--tier-gate", choices=_TIER_CHOICES, default=None, help="Compatibility path for CI gating; prefer `stem gate --min-tier ...`", ) def _add_shared_arguments(parser: argparse.ArgumentParser, *, default_format: str, default_summary: str) -> None: parser.set_defaults(default_summary=default_summary) parser.add_argument( "--level", type=int, choices=[1, 2, 3], default=3, help=( "Report depth: " "1=brief 1-page executive summary, " "2=standard 5-page review packet, " "3=full 8-page evidence packet (default)" ), ) parser.add_argument( "--format", choices=_FORMAT_CHOICES, default=default_format, help="Artifact format to write", ) parser.add_argument( "--out", "--output", dest="out", default="stem_output", help="Output directory for generated artifacts", ) parser.add_argument( "--policy", choices=_policy_choices(), default="default", help="Named calibration profile to surface in the result (1.8.0 keeps scan-time policy selection mirror-only)", ) parser.add_argument( "--explain", action="store_true", default=False, help="Write a {stem}_explain.txt proof trace with file, line, snippet, and reason", ) parser.add_argument( "--summary", choices=_SUMMARY_CHOICES, default=None, help="stdout summary mode: full, compact, or off", ) parser.add_argument( "--quiet", action="store_true", default=False, help="Alias for `--summary off`", ) def _add_intent_arguments(parser: argparse.ArgumentParser, *, required: bool = True) -> None: parser.add_argument("--baseline", choices=_policy_choices(), default="default", help="Baseline named profile") parser.add_argument("--clinical-strictness", type=int, choices=range(1, 6), required=required, help="1-5 scale") parser.add_argument("--code-integrity-priority", type=int, choices=range(1, 6), required=required, help="1-5 scale") parser.add_argument("--reproducibility-priority", type=int, choices=range(1, 6), required=required, help="1-5 scale") parser.add_argument("--structured-limitations-requirement", type=int, choices=range(1, 6), required=required, help="1-5 scale") def _normalize_argv(argv: list[str]) -> list[str]: if not argv: return argv if argv[0] in {"scan", "audit", "gate", "advisory", "policy", "-h", "--help", "--version"}: return argv if argv[0].startswith("-"): return argv return ["scan", *argv] def _validate_target(raw_target: str) -> Path: if raw_target.startswith(("http://", "https://")): raise SystemExit( "GitHub URL auditing is not enabled in the local CLI yet. " "Clone the repository first, then run: stem scan " ) target = Path(raw_target).expanduser().resolve() if not target.exists(): raise SystemExit(f"Target path does not exist: {target}") if not target.is_dir(): raise SystemExit(f"Target must be a directory: {target}") return target def _validate_optional_file(raw_path: str | None) -> Path | None: if raw_path is None: return None path = Path(raw_path).expanduser().resolve() if not path.exists(): raise SystemExit(f"Advisory response file does not exist: {path}") if not path.is_file(): raise SystemExit(f"Advisory response path must be a file: {path}") return path def _safe_output_segment(name: str) -> str: slug = re.sub(r"[^A-Za-z0-9._-]+", "-", name.strip()).strip("-") return slug or "scan_target" def _resolve_output_dir(raw_out_dir: str, target: Path) -> Path: output_dir = Path(raw_out_dir).expanduser().resolve() if output_dir.name != "stem_output": return output_dir return output_dir / _safe_output_segment(target.name) def _resolve_summary_mode(args: argparse.Namespace) -> str: if getattr(args, "quiet", False): return "off" if getattr(args, "summary", None) is not None: return args.summary return getattr(args, "default_summary", "full") def _bio_summary_line(result: dict) -> str | None: by_detector = result.get("detector_summary", {}).get("by_detector", {}) hits = [key.replace("BIO_", "") for key, value in by_detector.items() if key.startswith("BIO_") and value.get("detected")] if not hits: return None return ", ".join(hits[:4]) + (f" (+{len(hits) - 4} more)" if len(hits) > 4 else "") def _integrity_summary_line(result: dict) -> str | None: integrity = result.get("code_integrity", {}) warnings = [key for key, value in integrity.items() if value.get("status") not in ("PASS", None)] if not warnings: return None return ", ".join(item.split("_", 1)[0] for item in warnings) def _ast_scope_summary_line(result: dict) -> str | None: ast = result.get("ast_signal_summary", {}) if not ast or not ast.get("file_limit_exceeded"): return None return ( f"capped at {ast.get('files_considered', 'unknown')} / " f"{ast.get('files_total', 'unknown')} python files" ) def _workflow_label(command: str, advisory_command: str | None = None) -> str: if command == "gate": return "gate" if command == "advisory" and advisory_command is not None: return f"advisory {advisory_command}" return "scan" def _mirror_only_notice(calibration: dict) -> str | None: if calibration.get("profile_read_mode") != "mirror_only": return None if calibration.get("profile_name") == "default": return None return ( "mirror-only preview; scan scoring still follows authoritative runtime constants. " "Use `stem policy simulate` for score deltas." ) def _evaluate_tier_gate(result: dict, required_tier: str | None) -> tuple[bool, str | None]: if required_tier is None: return True, None formal_tier = result["score"]["formal_tier"] actual_tier_key = formal_tier.split()[0] actual_rank = _TIER_ORDER.get(actual_tier_key, 0) required_rank = _TIER_ORDER[required_tier] passed = actual_rank >= required_rank verdict = "PASS" if passed else "FAIL" return passed, f"{verdict} ({formal_tier} vs required {required_tier})" def _print_full_summary( result: dict, *, workflow: str, level: int, mode: str, pages: int, output_dir: Path, created: list[Path], advisory_mode: str, advisory_response: Path | None, gate_message: str | None, ) -> None: score = result["score"] classification = result.get("classification", {}) print("STEM BIO-AI scan complete") print(f"Workflow: {workflow}") print(f"Target: {result['target']['name']}") print(f"Level: {level} ({mode}, {pages}p)") calibration = result.get("calibration_profile", {}) print( "Policy: " f"{calibration.get('profile_name', 'unknown')} " f"({calibration.get('profile_status', 'unknown')}, {calibration.get('profile_read_mode', 'unknown')})" ) policy_notice = _mirror_only_notice(calibration) if policy_notice: print(f"Policy Mode: {policy_notice}") print() print(f"Score: {score['final_score']} / 100 ({score['formal_tier']})") print(f" Stage 1: {score['stage_1_readme_intent']} / 100 (README evidence)") print(f" Stage 2R: {score['stage_2_repo_local_consistency']} / 100 (repo consistency)") print(f" Stage 3: {score['stage_3_code_bio']} / 100 (code + bio)") rep_score = result.get("replication_score") rep_tier = result.get("replication_tier") if rep_score is not None: print(f" Stage 4: {rep_score} / 100 (replication {rep_tier})") ca = classification.get("ca_severity", "none") if ca != "none": clinical_line = f"Clinical: {ca}" if classification.get("t0_hard_floor"): clinical_line += " [T0 HARD FLOOR]" elif classification.get("score_cap") is not None: clinical_line += f" [cap={classification['score_cap']}]" print(clinical_line) integrity_line = _integrity_summary_line(result) if integrity_line: print(f"Integrity: WARN ({integrity_line})") bio_line = _bio_summary_line(result) if bio_line: print(f"Bio: {bio_line}") ast_line = _ast_scope_summary_line(result) if ast_line: print(f"AST Scope: {ast_line}") regulatory = result.get("regulatory_basis", {}) if regulatory.get("review_required"): reasons = ", ".join(regulatory.get("review_reasons", [])) print(f"Regulatory: review required ({reasons})") if advisory_mode == "none" and advisory_response is None: print("AI: not used (deterministic only)") else: ai_status = result.get("ai_advisory", {}) provider = ai_status.get("provider", "none") status = "packet_ready" if advisory_mode == "packet" else ai_status.get("status", "not_run") print(f"AI: {status} (provider={provider})") if gate_message is not None: print(f"Gate: {gate_message}") risks = result.get("notable_risks", []) if risks: print() limit = min(len(risks), 3) print(f"Action items ({len(risks)} total):") for risk in risks[:limit]: print(f" - {risk}") if len(risks) > limit: print(f" ... +{len(risks) - limit} more (see report)") print() print(f"Output: {output_dir}") for path in created: print(f" {path.name}") def _print_compact_summary( result: dict, *, workflow: str, output_dir: Path, created: list[Path], gate_message: str | None, ) -> None: score = result["score"] print(f"STEM BIO-AI | {workflow} | {score['formal_tier']} | {score['final_score']}/100") print(f"Target: {result['target']['name']}") calibration = result.get("calibration_profile", {}) print( f"Policy: {calibration.get('profile_name', 'unknown')} " f"[{calibration.get('profile_status', 'unknown')}; {calibration.get('profile_read_mode', 'unknown')}]" ) policy_notice = _mirror_only_notice(calibration) if policy_notice: print(f"Policy Mode: {policy_notice}") classification = result.get("classification", {}) if classification.get("ca_severity", "none") != "none": suffix = "" if classification.get("t0_hard_floor"): suffix = " [T0 HARD FLOOR]" elif classification.get("score_cap") is not None: suffix = f" [cap={classification['score_cap']}]" print(f"Clinical: {classification['ca_severity']}{suffix}") if gate_message is not None: print(f"Gate: {gate_message}") ast_line = _ast_scope_summary_line(result) if ast_line: print(f"AST: {ast_line}") print(f"Artifacts: {len(created)} -> {output_dir}") def _print_summary( result: dict, *, workflow: str, level: int, mode: str, pages: int, output_dir: Path, created: list[Path], advisory_mode: str, advisory_response: Path | None, summary_mode: str, gate_message: str | None, ) -> None: if summary_mode == "off": return if summary_mode == "compact": _print_compact_summary( result, workflow=workflow, output_dir=output_dir, created=created, gate_message=gate_message, ) return _print_full_summary( result, workflow=workflow, level=level, mode=mode, pages=pages, output_dir=output_dir, created=created, advisory_mode=advisory_mode, advisory_response=advisory_response, gate_message=gate_message, ) def _execute_scan( *, target: Path, level: int, fmt: str, out_dir: str, explain: bool, advisory_mode: str, advisory_response: Path | None, policy_name: str, tier_gate: str | None, summary_mode: str, workflow: str, ) -> int: mode, pages = _LEVEL_MAP[level] result = audit_repository( target, policy_name=policy_name, advisory=advisory_mode, advisory_response_path=advisory_response, ) output_dir = _resolve_output_dir(out_dir, target) created = write_outputs(result, output_dir, mode, pages, fmt, explain=explain) gate_passed, gate_message = _evaluate_tier_gate(result, tier_gate) _print_summary( result, workflow=workflow, level=level, mode=mode, pages=pages, output_dir=output_dir, created=created, advisory_mode=advisory_mode, advisory_response=advisory_response, summary_mode=summary_mode, gate_message=gate_message, ) return 0 if gate_passed else 1 def _format_tier_boundaries(profile: dict) -> str: boundaries = profile["tier_policy"]["tier_boundaries"] return ( f"T0 < {boundaries[0]}, " f"T1 {boundaries[0]}-{boundaries[1] - 1}, " f"T2 {boundaries[1]}-{boundaries[2] - 1}, " f"T3 {boundaries[2]}-{boundaries[3] - 1}, " f"T4 >= {boundaries[3]}" ) def _print_policy_list() -> int: print("STEM BIO-AI calibration profiles") for name in _policy_choices(): profile = load_calibration_profile(name) print( f"- {profile['profile_name']}: {profile['profile_status']} | " f"{profile['profile_read_mode']} | {profile['policy_version']}" ) return 0 def _print_policy_explain(profile_name: str) -> int: profile = load_calibration_profile(profile_name) default_profile = load_calibration_profile("default") weights = profile["weights"] clinical = profile["clinical_policy"] stage_3 = profile["stage_3_policy"] stage_4 = profile["stage_4_policy"] print(f"STEM BIO-AI policy: {profile['profile_name']}") print(f"Policy Version: {profile['policy_version']}") print(f"Status: {profile['profile_status']}") print(f"Read Mode: {profile['profile_read_mode']}") print( "Scoring Effect: " "mirror-only in 1.8.0; selection is surfaced in artifacts but does not yet reweight score computation" ) print() print( "Weights: " f"S1={weights['stage_1_percent']}% | " f"S2R={weights['stage_2r_percent']}% | " f"S3={weights['stage_3_percent']}%" ) print(f"Tier Policy: {_format_tier_boundaries(profile)}") print( "Clinical Caps: " f"no_disclaimer_cap={clinical['ca_no_disclaimer_cap']} | " f"t0_hard_floor_cap={clinical['t0_hard_floor_cap']}" ) normalization = stage_3["normalization"] print( "Stage 3: " f"normalization={normalization['kind']} " f"(raw_max={normalization['raw_max']}, target_max={normalization['target_max']}, rounding={normalization['rounding']})" ) print(f"B2 Posture: {stage_3['b2_partial_credit_mode']}") print(f"Stage 4: emphasis={stage_4.get('emphasis', 'unknown')}") print( "Reasoning: " f"{profile['reasoning_policy'].get('status', 'unknown')} " f"(score integration: {profile['reasoning_policy'].get('score_integration', 'unknown')})" ) if profile_name != "default": diffs: list[str] = [] if profile["clinical_policy"]["ca_no_disclaimer_cap"] != default_profile["clinical_policy"]["ca_no_disclaimer_cap"]: diffs.append( "ca_no_disclaimer_cap " f"{default_profile['clinical_policy']['ca_no_disclaimer_cap']} -> {profile['clinical_policy']['ca_no_disclaimer_cap']}" ) if profile["clinical_policy"]["t0_hard_floor_cap"] != default_profile["clinical_policy"]["t0_hard_floor_cap"]: diffs.append( "t0_hard_floor_cap " f"{default_profile['clinical_policy']['t0_hard_floor_cap']} -> {profile['clinical_policy']['t0_hard_floor_cap']}" ) if profile["profile_status"] != default_profile["profile_status"]: diffs.append(f"profile_status {default_profile['profile_status']} -> {profile['profile_status']}") if profile["policy_version"] != default_profile["policy_version"]: diffs.append(f"policy_version {default_profile['policy_version']} -> {profile['policy_version']}") if diffs: print() print("Default Diff:") for diff in diffs: print(f"- {diff}") return 0 def _intent_answers_from_args(args: argparse.Namespace) -> dict[str, int]: return { "clinical_strictness": args.clinical_strictness, "code_integrity_priority": args.code_integrity_priority, "reproducibility_priority": args.reproducibility_priority, "structured_limitations_requirement": args.structured_limitations_requirement, } def _has_intent_answers(args: argparse.Namespace) -> bool: return all( getattr(args, name) is not None for name in ( "clinical_strictness", "code_integrity_priority", "reproducibility_priority", "structured_limitations_requirement", ) ) def _print_derived_policy_tail(derived: dict, notes: list | None = None) -> None: if derived["triggered_rules"]: print("Rules: " + "; ".join(derived["triggered_rules"])) if derived["preview_only_deltas"]: print("Preview Deltas:") for section, values in derived["preview_only_deltas"].items(): print(f"- {section}: {values}") effective_notes = notes if notes is not None else derived["notes"] if effective_notes: print("Notes:") for note in effective_notes: print(f"- {note}") def _format_optional_cap(value: int | None) -> str: return "none" if value is None else str(value) def _print_simulation_tail(simulation: dict) -> None: print(f"Outcome Type: {simulation['outcome_type']}") print( "Profile Mode: " f"{simulation['effective_profile_status']} | " f"{simulation['effective_profile_read_mode']} | " f"{simulation['effective_profile_source']}" ) if simulation.get("effective_profile_path"): print(f"Profile File: {simulation['effective_profile_path']}") print(f"Profile Hash: {simulation['effective_policy_sha256']}") print( "Replication: " f"baseline={simulation['baseline_stage_4_emphasis']} | " f"simulation={simulation['effective_stage_4_emphasis']}" ) print( "Cap Effect: " f"baseline={_format_optional_cap(simulation['baseline_score_cap'])} | " f"simulation={_format_optional_cap(simulation['score_cap'])}" ) print(f"Score Delta: {simulation['score_delta']:+d}") print(f"Raw Delta: {simulation['raw_score_delta']:+d}") if simulation["replication_posture_changed"] and not simulation["formal_score_changed"]: print("Formal Score: unchanged; Stage 4 remains a separate replication lane in 1.8.0") def _print_policy_derive(args: argparse.Namespace) -> int: derived = derive_policy_intent(_intent_answers_from_args(args), baseline_profile_name=args.baseline) print("STEM BIO-AI policy derive") print(f"Baseline: {derived['baseline_profile']}") print(f"Outcome: {derived['outcome_type']}") print(f"Recommendation:{' '}{derived['recommended_profile']}") print("Answers: " + ", ".join(f"{k}={v}" for k, v in derived["answers"].items())) _print_derived_policy_tail(derived) return 0 def _print_policy_simulate(args: argparse.Namespace) -> int: target = _validate_target(args.target) result = audit_repository(target, policy_name=args.baseline) using_profile_file = args.profile_file is not None has_answers = _has_intent_answers(args) if using_profile_file and has_answers: raise SystemExit("Use either intent answers or --profile-file for policy simulate, not both.") if not using_profile_file and not has_answers: raise SystemExit("Provide either all 1-5 intent answers or --profile-file for policy simulate.") print("STEM BIO-AI policy simulation") print(f"Target: {result['target']['name']}") print("Mode: preview only; baseline scan scoring remains authoritative") print(f"Baseline: {args.baseline} -> {result['score']['final_score']}/100 ({result['score']['formal_tier']})") if using_profile_file: profile = load_calibration_profile_file(args.profile_file) if profile["profile_read_mode"] != "mirror_only": raise SystemExit("External profile files for simulation must remain mirror_only.") simulation = simulate_policy_outcome( result, None, baseline_profile_name=args.baseline, external_profile=profile, ) print( f"Simulation: {simulation['effective_profile']} [local_file] -> " f"{simulation['final_score']}/100 ({simulation['formal_tier']})" ) _print_simulation_tail(simulation) if simulation["notes"]: print("Notes:") for note in simulation["notes"]: print(f"- {note}") return 0 derived = derive_policy_intent(_intent_answers_from_args(args), baseline_profile_name=args.baseline) simulation = simulate_policy_outcome(result, derived, baseline_profile_name=args.baseline) print( f"Simulation: {simulation['effective_profile']} -> " f"{simulation['final_score']}/100 ({simulation['formal_tier']})" ) _print_simulation_tail(simulation) _print_derived_policy_tail(derived, notes=simulation["notes"]) return 0 def _handle_scan_cmd(parsed: argparse.Namespace, summary_mode: str) -> int: target = _validate_target(parsed.target) advisory_response = _validate_optional_file(parsed.advisory_response) return _execute_scan( target=target, level=parsed.level, fmt=parsed.format, out_dir=parsed.out, explain=parsed.explain, advisory_mode=parsed.advisory, advisory_response=advisory_response, policy_name=parsed.policy, tier_gate=parsed.tier_gate, summary_mode=summary_mode, workflow="scan", ) def _handle_gate_cmd(parsed: argparse.Namespace, summary_mode: str) -> int: target = _validate_target(parsed.target) return _execute_scan( target=target, level=parsed.level, fmt=parsed.format, out_dir=parsed.out, explain=parsed.explain, advisory_mode="none", advisory_response=None, policy_name=parsed.policy, tier_gate=parsed.min_tier, summary_mode=summary_mode, workflow="gate", ) def _resolve_advisory_params(parsed: argparse.Namespace) -> tuple[str, Path | None]: cmd = parsed.advisory_command if cmd == "validate": return "validate", None if cmd == "packet": return "packet", None if cmd == "call": return "call", None if cmd == "check-response": return "none", _validate_optional_file(parsed.response) return "none", None def _handle_advisory_cmd(parsed: argparse.Namespace, summary_mode: str) -> int: target = _validate_target(parsed.target) advisory_mode, advisory_response = _resolve_advisory_params(parsed) return _execute_scan( target=target, level=parsed.level, fmt=parsed.format, out_dir=parsed.out, explain=parsed.explain, advisory_mode=advisory_mode, advisory_response=advisory_response, policy_name=parsed.policy, tier_gate=None, summary_mode=summary_mode, workflow=_workflow_label(parsed.command, parsed.advisory_command), ) def _handle_policy_cmd(parsed: argparse.Namespace) -> int: if parsed.policy_command == "list": return _print_policy_list() if parsed.policy_command == "explain": return _print_policy_explain(parsed.profile_name) if parsed.policy_command == "derive": return _print_policy_derive(parsed) if parsed.policy_command == "simulate": return _print_policy_simulate(parsed) return 2 def main(argv: list[str] | None = None) -> int: parser = _build_parser() parsed = parser.parse_args(_normalize_argv(list(sys.argv[1:] if argv is None else argv))) if parsed.command is None: parser.print_help() return 2 summary_mode = _resolve_summary_mode(parsed) if parsed.command in {"scan", "audit"}: return _handle_scan_cmd(parsed, summary_mode) if parsed.command == "gate": return _handle_gate_cmd(parsed, summary_mode) if parsed.command == "advisory": return _handle_advisory_cmd(parsed, summary_mode) if parsed.command == "policy": return _handle_policy_cmd(parsed) parser.print_help() return 2 if __name__ == "__main__": raise SystemExit(main())