Skip to content

Latest commit

 

History

History
171 lines (117 loc) · 3.88 KB

File metadata and controls

171 lines (117 loc) · 3.88 KB

Python CLI Bootstrap

A Python CLI reference project built with Click and a plugin-style command loader. It is designed as a starting point for CLIs that need dynamic command discovery, user-extensible commands, and a practical developer toolchain.

Purpose

This project demonstrates how to:

  • build a nested CLI using Click
  • discover commands from both built-in and user command trees
  • lazily load command modules at runtime
  • scaffold and rebrand commands using built-in admin utilities
  • develop and ship with uv, Ruff, Pyright, pytest, Bandit, and Make

Requirements

  • Python 3.12+
  • uv

Installation

Create/sync the environment and install dependencies:

make setup

Install the package in editable mode (optional, but useful when developing command entry points):

make install

Usage

Show top-level help:

uv run mcli --help

Run sample commands:

uv run mcli samples add 1 2
uv run mcli samples sub 5 3
uv run mcli samples ping 1.1.1.1 --count 1

Run admin utilities:

uv run mcli admin new-command mul --short-help "Multiply two integers."
uv run mcli admin rm-command mul
uv run mcli admin rebrand --name "My CLI" --cli mycli

Module-entry equivalent:

uv run python -m cli.main --help

Built-in command groups

  • samples — demo commands (add, sub, ping)
  • admin — project utilities (new-command, rm-command, rebrand)

Plugin discovery contract

Commands are discovered from both trees:

src/cli/commands/<...nested command path...>/
$HOME/.<package-name>/commands/<...nested command path...>/

<package-name> is derived from project metadata (see src/cli/utils/metadata.py).

If the same command path exists in both trees, the user command wins.

Each command directory should provide:

  • entry.py exporting cli
  • meta.yaml with a non-empty short_help

Supported meta.yaml keys:

  • short_help (required)
  • help_group (optional, default Commands)
  • enabled (optional, default true)
  • hidden (optional, default false; forced true when disabled)
  • packaged (optional; used by packaging workflows)
  • no_args_is_help (optional, default false)

Legacy aliases (shortHelp, HelpSummary, HelpGroup) are accepted.

Scaffolding and rebranding

Create nested commands using dot-notation parents:

uv run mcli admin new-command issue --parent github.repo --short-help "Manage repository issues."

Use --user with admin new-command or admin rm-command to target the per-user command tree at $HOME/.<package-name>/commands.

Without --user, admin new-command respects MCLI_COMMANDS_DIR when it is set and otherwise falls back to the per-user tree. admin rm-command respects MCLI_COMMANDS_DIR when it is set and otherwise targets the packaged command tree. You can override paths with environment variables using the configured prefix from pyproject.toml:

  • MCLI_COMMANDS_DIR
  • MCLI_REBRAND_PROJECT_ROOT

Development workflow

Common tasks:

make help
make format
make lint
make test
make coverage
make build
make scan

Environment loading for Make targets (later files override earlier):

  • .env-build
  • .env-build.local
  • .env-build.$(ENV)

Example:

ENV=prod make lint

Contributing

uv run pre-commit install
  • Before opening a PR, run formatting, linting, tests, and build checks:
make format lint test build

Project structure

.
├── src/cli/
│   ├── commands/      # Built-in command plugins
│   ├── loader.py      # Discovery + lazy loading
│   ├── main.py        # CLI entry point
│   └── utils/
├── tests/
├── pyproject.toml
├── Makefile
└── README.md

License

MIT