cli.registry

The CLI command registry: how commands — first- and third-party — join otto.

A CommandSpec describes one top-level command or group: its name, a loader (a live Typer app, a plain/async function, or a lazy "pkg.mod:attr" string imported only on dispatch), the help line shown by otto --help without importing the module, and dispatch metadata (lab_free, output_dir, gate). First-party subcommands and third-party plugins register through the same register_cli_command().

class otto.cli.registry.CommandSpec(name: str, loader: Any, help: str | None = None, lab_free: bool = False, output_dir: bool = True, gate: bool = True, origin: str = '')

Bases: object

One registered top-level CLI command or group.

name : str

CLI name as the user types it (e.g. "run", "flash").

loader : Any

A typer.Typer app, a plain/async function, or a lazy "pkg.mod:attr" string.

help : str | None = None

One-line help for otto --help — rendered without importing the module.

lab_free : bool = False

True when the command never needs the lab (e.g. schema).

output_dir : bool = True

Whether invocations create a per-command output directory.

gate : bool = True

Whether invocations run the reservation gate (ignored when lab_free).

origin : str = ''

Module that registered the command (auto-captured) — used in collisions.

otto.cli.registry.CLI_COMMANDS : Registry[CommandSpec] = <otto.registry.Registry object>

Every registered top-level otto command or group, keyed by CLI name.

otto.cli.registry.register_cli_command(name: str, loader: Any, *, help: str | None = None, lab_free: bool = False, output_dir: bool = True, gate: bool = True) None

Register a top-level otto command or group.

loader is a typer.Typer app (group), a plain/async function (leaf command), or a "pkg.mod:attr" string resolved lazily on dispatch. Name collisions raise immediately, naming both registering modules — there is deliberately no overwrite escape hatch for CLI commands.

otto.cli.registry.cli_command(*, options: type | None = None, name: str | None = None, help: str | None = None, lab_free: bool = False, output_dir: bool = True, gate: bool = True) Callable[[...], Any]

Register an async function as a top-level otto command.

The ergonomics match @instruction: an OttoContext-annotated parameter is injected (hidden from the CLI), and options= expands a pydantic-dataclass into flags. The command name defaults to the function name with underscores dashed.

otto.cli.registry.resolve_spec_command(spec: CommandSpec) Any

Return the vendored-click command/group for spec, importing lazily.

A "pkg.mod:attr" loader imports its module only now; a function loader is wrapped in a throwaway Typer (the expose._synthesize_command pattern); a Typer app converts via Typer’s own app→click converter.