CLI for parsed commands and generated help,
or a script CLI when the program should parse its own arguments.
Shell builtins and CLIs keep separate catalogs and dispatch. Both use
CommandSpec for argument parsing.
Define a typed command
Each handler receives oneCLIInvocation. The example below shows the
registration and parsing contract; the runnable pager example
adds a mutable incident service and shared cross-language coverage.
type="path". Read flags through FlagView(inv.flags, inv.spec)
so a read of an undeclared option fails rather than looking like an omitted flag.
Choose the command shape
A typed node is a leaf or a group. Names and aliases are single words;
child names and aliases share one namespace. Options cannot collide across
ancestors and descendants. Construction validates the tree and compiles the
grammar; do not repeat parser validation in every handler.
CommandSpec.arguments holds options and positional arguments: a dashed name
such as --by declares an option; a bare name declares a positional.
CLI.handlers maps canonical paths below the root to CLIHandler bindings,
such as "ack", "incident ack", or "" for a single-command program.
Declare config_model on the account CLI. Registration validates
each installation, and inv.config holds that account’s settings. Without a
config model, a typed CLI accepts no config and receives None.
Mark mutating CLIHandler bindings write=True. This classifies the command for policy and
cache invalidation; it does not grant permission or perform the mutation.
Descriptions, options and operands supply --help and man at every level.
Register the program
Installing a CLI directly is the shortest path:cli: may instead point directly at module:ATTR or ./file.py:ATTR. Relative
files resolve next to the YAML file. A packaged Python CLI can publish the same
CLI without import side effects:
install or uninstall command, so an agent can use a CLI it was given
but cannot remove it. A deployment that must pin the head word should enforce
that with policy; shell functions can otherwise shadow it like they do in bash.
Use workspace files
Build the service client frominv.config. A CLI should not find a matching
VFS to obtain credentials or a backend accessor. Reuse core service functions
between CLI and VFS implementations where their behavior is the same.
For file arguments such as uploads or attachments, use inv.view:
Declare file options and operands with
type="path"; preserve the resulting
PathSpec values when dispatching. Use PathSpec.from_str_path(value, cwd=inv.cwd) for a path string from
config, and derive paths with join and parent. Do not convert them to plain
strings before filesystem access. Direct handler callers may omit the view;
a handler requiring files must refuse when the needed entry point is absent.
Account CLI writes, including failed writes that may have reached the service,
expire mounted caches. Config-free filesystem CLIs rely on the dispatcher’s
per-path invalidation. A script marked as a write also expires caches because
its config and service access are opaque.
Snapshots and credentials
A named typed CLI is saved by spec name. Fields declared as Pydantic secrets inconfig_model are redacted. Loading that snapshot must provide fresh install
config through clis=; Mirage refuses to restore redacted credentials as if
they were usable values. The loading process must also make the spec name
resolvable, or pass (cli, config) as that install’s clis= override. A tree
installed directly in live code survives Workspace.copy, which carries the
live CLI and revealed config together.
A script CLI is different: the script source is embedded in the snapshot and
its opaque config is serialized verbatim because there is no config model to
identify secrets. Keep credentials in the environment, or provide fresh script
config through clis= when loading.
Script CLIs
Usescript: when the program should own its grammar or should have no Mirage
imports:
MIRAGE_CLI_CONFIG. The
program’s stdout, stderr, and exit status become the shell result. A script CLI
cannot declare config_model, subcommands, or function handlers; it parses and
documents its own options, including --help.
Slot 0 is the installed name on monty and quickjs, so two installs can identify
themselves and user-facing errors can name the invoked program. Wasi and local
run CPython with -c, which owns sys.argv[0]; arguments still begin at index
- A YAML script declares no Mirage grammar, so every option reaches the
program unchanged. A script CLI constructed in code may supply a
CommandSpecto opt into Mirage parsing and generated help.
The sandboxed runtimes provide files plus compute, not arbitrary networking or
third-party packages.
runtime: local selects the host interpreter when that
broader authority is intentional.
Authoring reference
The public constructors document exact types and defaults:CLI and CLIHandler,
and CommandSpec and Argument.
CLI and CLIHandler
CommandSpec
Argument
This follows argparse’s naming conventions and supports a subset of its
features. Positional defaults and variadic options are unsupported;
append
collects single option values, while extend flattens fixed groups.