Skip to content

CLI System

pyrig provides a fully inheritable, automatically extensible CLI that every project built on pyrig gets for free. The system is built on Typer but adds a dynamic command-discovery layer on top, so projects never need to touch pyrig's source code to gain or override CLI commands.


How the CLI is Inherited

When a project declares pyrig as a dependency it gets a working CLI entry point automatically. The only thing required is registering a console-script in pyproject.toml that points to the same main function pyrig uses. This is also handled automatically by pyrig via its PyprojectConfigFile and pyrig mkroot:

[project.scripts]
my-project = "pyrig.rig.cli.main:main"

From that point on, uv run my-project <command> delegates to pyrig's entry point, which discovers and registers the right commands for the calling project automatically at runtime.


The Typer App

A RigDependencySubclass named CLI is defined in pyrig.rig.cli.cli.cli. Its app() method builds a fully configured typer.Typer application that registers every function defined in my_project.rig.cli.subcommands as a command. Functions from shared_subcommands modules across all packages in the dependency chain are also registered. Because CLI is a RigDependencySubclass, a dependent project can override any step of the build by subclassing it.


Command Discovery

When the CLI is invoked, pyrig discovers every function defined in my_project.rig.cli.subcommands and registers them as CLI commands. Additionally, it discovers functions from shared_subcommands modules across all packages in the dependency chain (pyrig and all packages that depend on it). This means that to add a new command, simply define a new function in one of those modules, and it will be automatically available as a CLI command the next time the CLI is run. Simply run pyrig mkcmd <command-name> to append a new command function skeleton to my_project.rig.cli.subcommands (creating the file if it does not exist).

my_project.rig.cli.shared_subcommands is a bit special: it is intended for commands that should be shared across multiple projects. If a project defines a function in shared_subcommands, that command will also be available in every other pyrig-based project that depends on it (directly or transitively). An example of this is pyrig's own version command, which is defined in pyrig.rig.cli.shared_subcommands so that it is available in every project that uses pyrig. So your project has already one command from the start that you can run:

uv run my-project version
# This will print smth like: my-project version 1.2.3

But if you run pyrig's version command directly, you get the version of pyrig instead:

uv run pyrig version
# This will print smth like: pyrig version 12.3.4

To add a shared command, simply run pyrig mkcmd <command-name> --shared and it will append the command skeleton to shared_subcommands (creating the file if it does not exist).