cfui
A small, dependency-free toolkit for Python command-line tools. It gives every tool the same look (modelled on Cloudflare's cf CLI), the same common commands, and the same behavior when an option is missing.
- PyPI package:
cfuicli. Import name:cfui. - Python 3.12 to 3.15, standard library only.
Install
uv add cfuicli # in a project
pip install cfuicli # anywhere else
A complete tool in a few lines
from cfui import App, Arg, ui
app = App("tool", repo="https://github.com/me/tool", pypi="tool", description="Does things.")
@app.command("greet", "Say hi", args=[Arg("--name", required=True, prompt="Your name")])
def greet(a):
ui.success(f"hi {a.name}")
def main():
app.main()
$ tool greet --name Sara # runs
$ tool greet # on a terminal: asks "Your name"
$ tool greet </dev/null # in a pipe or CI: --name is required. Pass --name <value> or run interactively.
$ tool help greet
$ tool version
What every tool gets
| Command or flag | What it does |
|---|---|
help [command], -h |
cf-style help: usage, description, options by section, global flags, docs link |
version, -v |
tool name and version, git repository, Python version |
update [--check] |
self update through uv, pipx or pip (override with App(upgrade=...)) |
-q, --quiet |
no banner, notes or success markers (also <TOOL>_QUIET=1) |
Behavior you do not have to write:
- A required option or argument is asked for on a terminal and reported as an error when the input is piped or the
CIvariable is set. The error exits with code 2. - Errors are drawn as a red box on stderr and exit non-zero.
raise Fail(message, hint)orsys.exit("message")both work. Ctrl-C exits 130. - Color follows the rules: only on a TTY,
NO_COLORturns it off,FORCE_COLOR=0turns it off, and the palette drops to 256 or 16 colors when the terminal needs it. - Structured results go to stdout as JSON (
ui.data), decoration goes to stderr.
Building commands
app = App("tool", repo=..., pypi=..., description=..., default="count", global_args=[Arg("-c", "--config")])
grp = app.group("config", "Manage settings") # tool config show
@grp.command("show", "Show settings")
def show(a): ...
@app.command("wp", "Pass everything through", raw=True) # a.args is the untouched argv tail
def wp(a): ...
Arg takes the usual argparse.add_argument keywords plus required=True (prompt when missing), prompt="Question", secret=True (hidden input) and group="Section" (help section). Positional arguments work the same way.
Output helpers (cfui.ui)
success, info, warning, error, hint, heading, banner, error_box, data (JSON), table, out, and the prompts ask, confirm, need, plus progress for a one-line counter.
Shared pieces (cfui.common)
| Name | Use |
|---|---|
YES, JSON, FORCE, DRY_RUN, JOBS, TIMEOUT, BACKGROUND |
the same flags with the same help text in every tool |
confirm(message, yes) |
ask, or require --yes when not on a terminal |
parse_value(text) |
turn true, 5, a,b, none into Python values |
run_parallel(items, worker, jobs) |
thread pool with one status line per item |
add_config(app, items=, get=, set=, reset=, path=, validate=) |
registers config show/get/set/reset/path/validate |
The agent skill
The package carries the cf-cli-ui skill, which tells coding agents how to build and restyle CLIs this way.
cfui skill # print where it is
cfui skill --install # copy it to ~/.agents/skills/cf-cli-ui
Tools built on cfui
aitc, thronetools, ollama-tool-cli, pem, linkcovery, pulsecheck, wpguard. dokploytools is written in Go and follows the same conventions with its own code.
Development
uv sync
uv run pytest
uv run ruff check . && uv run ruff format --check .
Commits follow Conventional Commits. A feat: or fix: merged into main runs the Release workflow, which bumps the version, tags it, and publishes to PyPI through a trusted publisher.
License
MIT
Metadata
Release files for cfuicli 1.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| cfuicli-1.1.0.tar.gz | 73.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| cfuicli-1.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 92.3 kB
Release files / cfuicli-1.1.0.tar.gz
| Download URL | cfuicli-1.1.0.tar.gz |
|---|---|
| Size | 73.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
83640ac7e8aaa35c057a5d5c404c7b262c75f5e51abe68dcfa78c35545e96d1e
|
|
BLAKE2b-256 checksum How to use checksums |
6544b248297499a4bb345a8f7497274a278c1e938135227f0e0f96ec1471852f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.13.0 {"installer":{"name":"uv","version":"0.13.0","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / cfuicli-1.1.0-py3-none-any.whl
| Download URL | cfuicli-1.1.0-py3-none-any.whl |
|---|---|
| Size | 19.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
ecf7dfdd1012697a10d1fbf41b6ec7f66858f42d3118055ac4310ed58d7cde69
|
|
BLAKE2b-256 checksum How to use checksums |
030cbdf441eba3267d8be1b0c03ef39cf04efb57ec45571eaaf263728008eef8
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.13.0 {"installer":{"name":"uv","version":"0.13.0","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|