CLI Commands¶
Experimental
Command is a small, experimental CLI surface for prototypes and internal
tools. It is not a replacement for typer or
click, and the API may still change.
Command is xnano's process-argument surface: options, subcommands, and help for small tools. It is not a TUI host.
Use it when a project needs a real argv entrypoint. Pair it with Terminal only when the same project also runs an interactive session.
A Command can:
- Register a root callback (
@cliorregister_callback) - Declare options and flags (
@Command.option) - Nest subcommands (
@cli.command()oradd_subcommand) - Coerce values from type annotations (optionally
strict=True) - Emit help (
--help/-h)
A root command¶
from xnano.cli import Command
cli = Command(name="tool", description="A small utility")
@cli # (1)!
@Command.option("--name", default="world", help="Who to greet")
def greet(name: str = "world") -> None:
print(f"hello, {name}")
if __name__ == "__main__":
cli.run() # (2)!
@cliregisters the function as the main callback. Also valid:cli(greet)orcli.register_callback(greet).run()parsessys.argv[1:]by default. Pass a list in tests:cli.run(["--name", "hammad"]).
uv run python tool.py
# hello, world
uv run python tool.py --name hammad
# hello, hammad
uv run python tool.py --help
Types on the signature drive coercion when values are parseable. Set
Command(strict=True) to raise on bad values instead of leaving the raw string.
Options and flags¶
from xnano.cli import Command
cli = Command(name="build", description="Compile the project")
@cli
@Command.option("--count", default=1, help="How many times")
@Command.option(["--verbose", "-v"], is_flag=True, help="Verbose output")
def main(count: int = 1, verbose: bool = False) -> None: # (1)!
mode = "verbose" if verbose else "quiet"
print(f"building ×{count} ({mode})")
--counttakes a value;-v/--verboseis a bare flag. Parameter names match the long flag form (count,verbose).
Parameters without an explicit @Command.option still appear as
--param-name flags derived from the signature.
Flags vs. values
is_flag=True(or aboolannotation / default) treats presence asTrue.- Otherwise the next argument is the value (
--count 3or--count=3).
Subcommands¶
from xnano.cli import Command
cli = Command(name="ship", description="Release helpers")
@cli.command(name="greet", description="Print a greeting")
@Command.option("--name", default="world", help="Who to greet")
def greet(name: str = "world") -> None:
print(f"hello, {name}")
@cli.command(name="bump")
@Command.option("--major", is_flag=True, help="Bump the major version")
def bump(major: bool = False) -> None:
kind = "major" if major else "patch"
print(f"bumping {kind}")
if __name__ == "__main__":
cli.run()
uv run python ship.py greet --name crew
# hello, crew
uv run python ship.py bump --major
# bumping major
uv run python ship.py --help
uv run python ship.py bump --help
Subcommand names default from the function name (dry_run → dry-run) when
name= is omitted. You can also use add_subcommand(Command(...)).
Help¶
With help=True (the default), --help / -h print usage and exit.
get_help() returns the same text as a string.
from xnano.cli import Command
cli = Command(name="tool", description="A small utility")
@cli
@Command.option("--name", default="world", help="Who to greet")
def greet(name: str = "world") -> None:
print(f"hello, {name}")
print(cli.get_help())
Usage: tool [OPTIONS]
A small utility
Options:
--name Who to greet [default: world]
--help, -h Show this message and exit.
Strict mode¶
from xnano.cli import Command
cli = Command(name="count", strict=True)
@cli
def main(n: int) -> None:
print(n * 2)
cli.run(["--n", "21"]) # prints 42
# cli.run(["--n", "nope"]) # Error: Invalid value for parameter 'n': ...
Parsing without running¶
from xnano.cli import Command
cli = Command(name="tool")
@cli
@Command.option("--name", default="world")
def greet(name: str = "world") -> None:
...
target, values = cli.parse_arguments(["--name", "hammad"])
assert values["name"] == "hammad"
assert target is cli
Next¶
- Command API
- Terminal when you need a live UI session