0388751c6e
- main(argv) builds top-level argparse(prog="nova") with required subparsers. - Auto-discovers nova/<module>.py via pkgutil.iter_modules(nova.__path__), skipping `cli`; each module exports add_parser(subparsers) + run(args) -> int. - Before dispatch: resolve_mode_from_env() → emit cli.invocation audit event (INV-12) as a stderr JSON line stub with mode, selection_reason, credential_type, command, args. Real outbox wiring comes later. - Dispatch: args._run(args); exit code via sys.exit(main()). - nova/__init__.py empty package marker. ---ci--- project: acdl phase: 1 milestone: v1.28 status: execute persona: cli-engineer ---/ci---
60 lines
1.7 KiB
Python
60 lines
1.7 KiB
Python
"""Nova CLI entry point — dispatch + audit (P1, REQ-324, INV-12).
|
|
|
|
Auto-discovers nova/<module>.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()) |