"""Nova CLI entry point — dispatch + audit (P1, REQ-324, INV-12). Auto-discovers nova/.py subcommands; each exports add_parser(subparsers) + run(args) -> int. Resolves the client mode via core.mode_resolver and emits a cli.invocation audit event (stderr JSON line stub) before dispatching. """ from __future__ import annotations import argparse import importlib import json import pkgutil import sys from typing import Optional from core.mode_resolver import resolve_mode_from_env def _emit_invocation(mode, reason, cred_type, command, args): """INV-12: emit cli.invocation audit event to stderr (stub).""" event = { "event": "cli.invocation", "mode": mode, "selection_reason": reason, "credential_type": cred_type, "command": command, "args": args, } sys.stderr.write(json.dumps(event, sort_keys=True) + "\n") import nova def _build_parser(): parser = argparse.ArgumentParser(prog="nova", description="Nova platform CLI") parser.add_argument("--mode", choices=["agent", "interactive"], default=None) sub = parser.add_subparsers(dest="command", required=True) for mod_info in pkgutil.iter_modules(nova.__path__): name = mod_info.name if name == "cli": continue mod = importlib.import_module(f"nova.{name}") mod.add_parser(sub) return parser def main(argv: Optional[list] = None) -> int: parser = _build_parser() args = parser.parse_args(argv) mode, reason = resolve_mode_from_env() arg_dict = {k: v for k, v in vars(args).items() if k != "_run"} _emit_invocation(mode, reason, None, args.command, arg_dict) return args._run(args) if __name__ == "__main__": sys.exit(main())