Skip to content

CLI reference

Installing cliffy adds two identical entry points, cli and cliffy. This page uses cli. It is generated from the command definitions in cliffy/cli.py and cliffy/ai.py, so the arguments, options and defaults below are the ones the installed version accepts.

cli [OPTIONS] COMMAND [ARGS]...

Commands at a glance

Command Description
cli ai AI-powered commands. generate and ask require the ai extra: pip install 'cliffy[ai]'
cli blueprint Work with blueprints: custom YAML specs that map onto a CLI
cli build Build a CLI manifest or a loaded CLI into a single file to hand to someone else
cli dev Start hot-reloader for a manifest for active development.
cli docs Generate documentation for a CLI
cli info Display CLI info
cli init Generate a CLI manifest template
cli list List all CLIs loaded
cli load Load CLI for given manifest(s)
cli remove Remove a loaded CLI by name
cli remove-all Remove all loaded CLIs
cli render Render the CLI manifest generation as code
cli run Run CLI for a manifest
cli test Run tests defined in a manifest
cli update Reloads CLI by name
cli validate Validate a CLI manifest: its structure, its requirements and the code generated from it

Global options

Option Description
--version Print the version and exit.
--aliases List the command aliases and exit.
-h, --help Show help. Every command accepts this option.

Aliases

Aliases are hidden from cli --help. cli --aliases prints this list.

Alias Command
cli ls cli list
cli add cli load
cli reload cli update
cli rm cli remove
cli rm-all cli remove-all
cli rmall cli remove-all
cli blueprints cli blueprint list

cli ai

AI-powered commands. generate and ask require the ai extra: pip install 'cliffy[ai]'

cli ai [OPTIONS] COMMAND [ARGS]...

generate and ask need the ai extra (pip install "cliffy[ai]"). Both send the manifest JSON schema to the model as part of the prompt and print token usage when they finish. llms-txt needs no extra and calls no model.

Model names use the pydantic-ai provider:model form, and the API key of that provider must be set in the environment. Without --model, the CLIFFY_AI_MODEL environment variable is used, then a default for the first provider that has an API key set. See AI commands.

cli ai ask

Ask a question about cliffy or a specific CLI manifest.

cli ai ask [OPTIONS] PROMPT
Argument or option Type Required Default Description
PROMPT text yes Your question.
--preview flag no Print the prompt instead of sending it. Needs no model.
--max-tokens integer range no Maximum number of tokens in a model response.
-m, --model text no Model to use, as provider:model. See https://pydantic.dev/docs/ai/models/overview/. Defaults to a model of the first provider with an API key set, else google:gemini-flash-latest.
--cli file no Loaded CLI or manifest to include in the prompt as reference.

--preview prints the prompt and exits without calling a model.

cli ai generate

Generate a CLI manifest based on a description.

cli ai generate [OPTIONS] CLI_NAME DESCRIPTION
Argument or option Type Required Default Description
CLI_NAME text yes Name of the CLI. The manifest is written to <cli_name>.yaml.
DESCRIPTION text yes Plain-language description of the CLI you want.
--preview flag no Print the prompt instead of sending it. Needs no model.
--max-tokens integer range no Maximum number of tokens in a model response.
-m, --model text no Model to use, as provider:model. See https://pydantic.dev/docs/ai/models/overview/. Defaults to a model of the first provider with an API key set, else google:gemini-flash-latest.
--retries integer range no 2 How often to send an invalid manifest back to the model to be fixed.
-f, --force flag no Overwrite the manifest file if it exists.
-o, --output-dir directory no . Output directory

The reply of the model is validated before the manifest is written, and sent back with the reason when it is not a manifest cliffy can load. --preview prints the prompt and exits without calling a model.

cli ai llms-txt

Print the bundled cliffy documentation as llms.txt. Needs no model.

cli ai llms-txt [OPTIONS]

Prints the documentation that is bundled with the installed cliffy as one text file, in the llms.txt layout. It needs no model, no API key and no ai extra, so it works offline and always matches the installed version.

$ cli ai llms-txt > cliffy-llms.txt
$ cli ai llms-txt | pbcopy

Give the output to a coding agent, or add it to a project's context, so that it can write manifests without looking anything up.

cli blueprint

Work with blueprints: custom YAML specs that map onto a CLI

cli blueprint [OPTIONS] COMMAND [ARGS]...

A blueprint lets a YAML file use its own keys instead of commands and params, and maps them onto a CLI. See Blueprints.

cli blueprint list

List the blueprints provided by installed packages

cli blueprint list [OPTIONS]

Alias: cli blueprints

Lists the blueprints that installed packages register under the cliffy.blueprints entry point group. A blueprint in a .py file or referenced as module:Class works without being listed.

$ cli blueprint list
No blueprints installed. Use a .py file or module:Class reference, or install a blueprint package.

cli blueprint schema

Print the JSON schema of a blueprint's spec, for validation and editor completion

cli blueprint schema [OPTIONS] BLUEPRINT
Argument or option Type Required Default Description
BLUEPRINT text yes Blueprint reference: a .py file, module:Class or the name of an installed blueprint.
-o, --output file no Write the schema to a file
$ cli blueprint schema examples/blueprints/tasks.py -o tasks.schema.json
+ tasks.schema.json

Point a # yaml-language-server: $schema= comment at the file to get completion and validation for files written in the blueprint's spec.

cli build

Build a CLI manifest or a loaded CLI into a single file to hand to someone else

cli build [OPTIONS] [CLI_OR_MANIFESTS]...
Argument or option Type Required Default Description
[CLI_OR_MANIFESTS]... file no Manifest files (paths ending in yaml) or names of loaded CLIs.
-t, --target zipapp | script | native no zipapp What to build: zipapp is a zipapp with its dependencies bundled, which needs a compatible Python; script is a .py script that declares its dependencies inline, which needs uv or an environment with them; native is a compiled executable that brings its own Python and needs nothing installed (built with cargo and uv).
-o, --output-dir directory no Output dir
-p, --python text no Interpreter for the #! line of a zipapp or a script, added after the #!. [default: /usr/bin/env python3 for a zipapp, /usr/bin/env -S uv run --script for a script]
-b, --blueprint text no Blueprint that maps a custom YAML spec onto a CLI: a .py file, module:Class or an installed blueprint name. Overrides the blueprint: key of the file.

Builds one file to hand to someone else. --target chooses what kind:

Target What you get Needs on the other machine
zipapp hello, a zipapp built with shiv that contains the dependencies A compatible Python
script hello.py, a script with its dependencies declared inline uv, or Python with the dependencies installed
native hello, a compiled executable with its own Python Nothing

Every target ships the generated CLI, the framework it imports (Typer, or rich-click with use_click) and every package listed under requires. Without --output-dir the file is written to the current directory.

$ cli build hello.yaml -o dist
+ hello built ๐Ÿ“ฆ
  dist/hello (single file, needs Python >=3.10, 1.8 MB)
  run it with: ./dist/hello
$ ./dist/hello greet Ada
Hello, Ada!
$ cli build hello.yaml -o dist --target script
+ hello built ๐Ÿ“ฆ
  dist/hello.py (single file, needs uv, or Python >=3.11 with typer==0.27.3 installed, 1.3 kB)
  run it with: ./dist/hello.py (or: uv run dist/hello.py)
  uv downloads the dependencies the first time the script runs

The native target needs cargo and uv on the machine that builds. When a build fails, the reason is printed and the exit code is 1. With --blueprint, or a blueprint: key in the file, the blueprint is applied at build time and is not needed to run the result.

See Building executables.

cli dev

Start hot-reloader for a manifest for active development.

Examples:

  • cli dev examples/hello.yaml

  • cli dev examples/hello.yaml --run-cli -- -h

  • cli dev examples/hello.yaml --run-cli python

cli dev [OPTIONS] MANIFEST [RUN_CLI_ARGS]...
Argument or option Type Required Default Description
MANIFEST file yes Manifest file to watch.
[RUN_CLI_ARGS]... text no Arguments for the generated CLI when --run-cli is set.
--run-cli flag no If passed, runs CLI on each reload. Useful for syntax checks or testing command execution on each reload
-b, --blueprint text no Blueprint that maps a custom YAML spec onto a CLI: a .py file, module:Class or an installed blueprint name. Overrides the blueprint: key of the file.

Nothing is loaded when the command starts. The CLI is loaded the first time the file changes and again on every later change. The manifests it includes and the Python file of its blueprint are watched too. Stop with Ctrl+C.

With --run-cli, the remaining arguments are passed to the freshly generated CLI after each reload. Put -- before them when they include options.

See Hot-reload dev loop.

cli docs

Generate documentation for a CLI

cli docs [OPTIONS] CLI_OR_MANIFEST
Argument or option Type Required Default Description
CLI_OR_MANIFEST file yes Manifest file (a path ending in yaml) or the name of a loaded CLI.
-f, --format md | rst | html no md Output format.
-o, --output-dir directory no Output directory
-b, --blueprint text no Blueprint that maps a custom YAML spec onto a CLI: a .py file, module:Class or an installed blueprint name. Overrides the blueprint: key of the file.

Writes <name>.md, <name>.rst or <name>.html to the output directory, or to the current directory when --output-dir is not given. The output directory must exist.

A loaded CLI is documented from the manifest path recorded by cli load.

See Generating docs.

cli info

Display CLI info

cli info [OPTIONS] CLI_NAME
Argument or option Type Required Default Description
CLI_NAME text yes Name of a loaded CLI.

Prints the name, version, requirements, blueprint (when one was used), age and stored manifest of a loaded CLI.

cli init

Generate a CLI manifest template

cli init [OPTIONS] [CLI_NAME]
Argument or option Type Required Default Description
[CLI_NAME] text no mycli Name of the CLI. The manifest is written to <cli_name>.yaml.
--render flag no Print template to terminal directly
--raw flag no Raw template without boilerplate helpers and examples.
--json-schema flag no Write JSON schema (cliffy_schema.json) file to the current directory. Useful for development.
$ cli init mycli --raw
+ mycli.yaml

--raw writes a skeleton with every top-level key, one hello command and one test. Without --raw the manifest is a commented tour of the format with example commands. Both run and pass cli test as written.

--json-schema also writes cliffy_schema.json and adds a # yaml-language-server comment that points at it. See Editor setup.

cli list

List all CLIs loaded

cli list [OPTIONS]

Alias: cli ls

$ cli list
โ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”“
โ”ƒ Name  โ”ƒ Version โ”ƒ Age โ”ƒ Manifest                โ”ƒ
โ”กโ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ฉ
โ”‚ hello โ”‚ 0.1.0   โ”‚ 5s  โ”‚ /home/ada/hello.yaml    โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

cli load

Load CLI for given manifest(s)

cli load [OPTIONS] [MANIFESTS]...

Alias: cli add

Argument or option Type Required Default Description
[MANIFESTS]... file no One or more manifest files to load.
-b, --blueprint text no Blueprint that maps a custom YAML spec onto a CLI: a .py file, module:Class or an installed blueprint name. Overrides the blueprint: key of the file.

Loading does three things:

  1. generates the CLI code and writes it to cliffy/clis/<name>.py inside the installed cliffy package,
  2. writes a launcher script called <name> into the bin directory of the Python environment cliffy is installed in,
  3. records the manifest path and contents so that cli list, cli info and cli update can find it.

The CLI is therefore available wherever that environment is active. Packages listed under requires must already be installed, otherwise loading stops with missing requirement.

A file written in a blueprint's spec is loaded the same way. The blueprint comes from --blueprint or from the file's blueprint: key and is remembered, so cli update applies it again.

$ cli load hello.yaml
โœจ Generated hello CLI v0.1.0 โœจ
$ hello -h

See Hot-reload dev loop.

cli remove

Remove a loaded CLI by name

cli remove [OPTIONS] [CLI_NAMES]...

Alias: cli rm

Argument or option Type Required Default Description
[CLI_NAMES]... text no Names of loaded CLIs to remove.

Deletes the launcher script, the generated module and the stored metadata.

$ cli remove hello
~ hello removed ๐Ÿ’ฅ

cli remove-all

Remove all loaded CLIs

cli remove-all [OPTIONS]

Aliases: cli rm-all, cli rmall

cli render

Render the CLI manifest generation as code

cli render [OPTIONS] MANIFEST
Argument or option Type Required Default Description
MANIFEST file yes Manifest file to render.
-b, --blueprint text no Blueprint that maps a custom YAML spec onto a CLI: a .py file, module:Class or an installed blueprint name. Overrides the blueprint: key of the file.

Prints the Python module cliffy generates, without loading or running it. Use it to see exactly how params, shell lines and templates are translated.

$ cli render hello.yaml

cli run

Run CLI for a manifest

cli run [OPTIONS] MANIFEST [CLI_ARGS]...
Argument or option Type Required Default Description
MANIFEST file yes Manifest file to run.
[CLI_ARGS]... text no Arguments passed to the generated CLI. Put -- first when they include options.
-b, --blueprint text no Blueprint that maps a custom YAML spec onto a CLI: a .py file, module:Class or an installed blueprint name. Overrides the blueprint: key of the file.

Generates the CLI in a temporary file and runs it once, without loading it. Options meant for your CLI must come after --, otherwise cli run tries to parse them itself:

$ cli run hello.yaml greet Ada
Hello, Ada!
$ cli run hello.yaml -- greet Ada --shout
HELLO, ADA!
$ cli run hello.yaml -- --help

Help output shows the temporary file name instead of the CLI name.

cli test

Run tests defined in a manifest

cli test [OPTIONS] MANIFEST
Argument or option Type Required Default Description
MANIFEST file yes Manifest file whose tests section should run.
-x, --exitfirst flag no exit instantly on first error or failed test.
--traceback flag no Also print the full Python traceback of a command that raises.
-b, --blueprint text no Blueprint that maps a custom YAML spec onto a CLI: a .py file, module:Class or an installed blueprint name. Overrides the blueprint: key of the file.

Runs the tests section of a manifest and prints each command, its output and the result. The exit code is 0 only when every test passes. It is 1 when a test fails or when the manifest has no tests.

See Testing manifests.

cli update

Reloads CLI by name

cli update [OPTIONS] [CLI_NAMES]...

Alias: cli reload

Argument or option Type Required Default Description
[CLI_NAMES]... text no Names of loaded CLIs to regenerate from their manifests.

update re-reads the manifest from the path recorded by cli load, so it only works while the manifest is still at that location.

$ cli update hello
โœจ Reloaded hello CLI v0.1.0 โœจ
$ hello -h

cli validate

Validate a CLI manifest: its structure, its requirements and the code generated from it

cli validate [OPTIONS] MANIFEST
Argument or option Type Required Default Description
MANIFEST file yes Manifest file to validate.
-b, --blueprint text no Blueprint that maps a custom YAML spec onto a CLI: a .py file, module:Class or an installed blueprint name. Overrides the blueprint: key of the file.

Checks that the file is valid YAML, matches the manifest schema, that its requires are installed and that the generated code works. Scripts are checked while the code is generated, as they are by every other command. validate then compiles and imports the generated code, which defines the commands without running any of them. The code in imports and functions and the expressions in vars do run.

$ cli validate hello.yaml
Manifest hello.yaml is valid

An error in a script is reported with its command, line and column. A problem that only shows when the code is imported, such as a type that does not exist, is reported against the command it comes from:

$ cli validate broken.yaml
~ error in broken.yaml: command `db.create`, run, line 2, column 7: not valid Python: '(' was never closed
    print((
          ^ ๐Ÿ’”
$ cli validate other.yaml
Manifest other.yaml is invalid: the generated code does not work
  command `db.create`: NameError: name 'Table' is not defined
    def db_create(table: Table = typer.Argument(...)): ๐Ÿ’”