Skip to content

lilabrooks/python-cli-template

Repository files navigation

python-cli-template

Code quality Tests Coverage Python License uv

A walking skeleton for Python 3.12+ command-line tools: a minimal working CLI (skeleton-cli) that already passes a production-grade quality gate, so a new project starts at "green" instead of at "empty directory".

Project creation is supported on macOS, Linux, and Windows through WSL because the setup path uses Bash and Make. The generated Python package is otherwise OS-independent, but native Windows project creation is not currently tested.

This is a stack template — it carries toolchain decisions, not process. It pairs with (but does not require) claude-okf-repo-kit, which layers the operating contract — goal file, specs/ADRs, source-to-knowledge mapping, session hooks — on top of whatever stack exists. Template first, kit second, then the goal loop builds your actual tool.

What you get

  • A real CLI, not stubs. skeleton-cli hello, ask, and providers work out of the box; the template's own CI keeps every piece green, so the chassis cannot rot silently.
  • The quality gate. ruff (lint + format), mypy strict, pytest with a 90% branch-coverage floor, docs validation, and an environment preflight that catches file-sync damage (iCloud-style hidden flags and conflict duplicates) — one make check, mirrored by three GitHub Actions workflows across Python 3.12–3.14. Keep working checkouts outside synced folders when possible. A .venv.nosync directory with a .venv symlink may reduce environment churn with some sync tools, but remains a best-effort fallback.
  • Repository-health tests. The repo tests itself: the version must agree across pyproject.toml, the package, CHANGELOG.md, and docs; no tracked file may match .gitignore; requirements.txt must mirror the optional extras; a second agent's config mirror (if you add one) must stay in parity.
  • An optional model-provider layer (deletable, see below): the one-method LanguageModel protocol, a registry with echo/anthropic/openai adapters, environment-only settings, and safe-by-default file writes. Zero runtime dependencies — vendor SDKs are opt-in extras with deferred imports, so the CLI works offline and credential-free by default.

The chassis was extracted one time from spec-agent-cli, where each piece shipped and was hardened in real use; this template's own CI keeps it honest from here.

Using the template

If you used the GitHub "Use this template" button, your first command is bash scripts/rename-project your-tool-name. The button only copies the tree — until you run the rename, every project keeps the skeleton-cli name and the template maintainer's contact details in pyproject.toml, SECURITY.md, and CODE_OF_CONDUCT.md. The preferred path below does this for you; the manual path makes it step 2.

Preferred path — one command from a template checkout. Deterministic and agent-neutral (plain bash; equally runnable by a human, Claude Code, or Codex), so no session re-derives the sequence:

bash scripts/create-project /path/to/your-tool your-tool-name

It extracts this template's committed tree into the target (a fresh directory, or an existing repo that only carries goal-shaped content like docs/GOAL.md — colliding files are refused, an existing .gitignore is merged), renames the skeleton, git-inits if needed, uses uv to create the environment and a project-specific uv.lock, and runs make check. When uv is unavailable, the script uses the venv/pip fallback; pass --pip to select it explicitly. It installs claude-okf-repo-kit when a clone is available (--kit /path, auto-detected as a sibling, or --no-kit), removes the template-side tooling from the target, and prints the kit installer's created/updated/review summary plus the judgment steps that remain: project README and CHANGELOG, numbered candidate review, the goal, and the playbook brackets. Its mechanics are guarded by tests/test_create_project.py; CI also runs the complete uv and pip setup paths on Python 3.14, including a bracketed target path. Target paths containing # are refused before files are created because Python console-script shebangs cannot portably use them.

Manual path (the same sequence, step by step):

  1. Create your repo from this one (GitHub "Use this template", or clone and re-init).

  2. Rename the skeleton, then remove the template's single-use generator files:

    bash scripts/rename-project your-tool-name
    rm scripts/rename-project scripts/create-project \
      tests/test_create_project.py .github/workflows/template-chassis.yml

    rename-project rewrites the package (skeleton_cli), distribution (skeleton-cli), and env-var prefix (SKELETON_CLI) everywhere, moves the source directory, substitutes your git config user.name and user.email as owner and contact in the docs and community files, and resets the project to its own 0.1.0 with a fresh CHANGELOG.md and docs/log.md. The rm drops the two generator scripts, their smoke test, and the template-chassis workflow — template tooling that has no role in a downstream project, and whose CI jobs run those scripts and assert the template's own version, so they only fail once the scripts are gone. (The preferred path removes these same four files for you.)

    The rename step prunes Git internals, virtual environments, and tool caches by directory name, so checkout paths containing spaces or square brackets do not weaken those exclusions.

  3. Create the environment and verify the gate with uv:

    uv sync --all-extras
    uv run make check

    The generated uv.lock belongs to the new project and should be committed. The portable fallback is:

    python3 -m venv .venv
    .venv/bin/pip install -e ".[dev,anthropic,openai]"
    make check
  4. Optionally install the kit on top for the goal loop and knowledge discipline:

    bash /path/to/claude-okf-repo-kit/scripts/update-existing-repo .

    The safe updater preserves same-name Markdown files. Because this template already has CLAUDE.md, the kit normally writes its proposed playbook as CLAUDE.2.md and lists it under Needs review.

  5. Replace the hello command with your tool's real surface and start building. The README you are reading describes the template — rewrite it for your project.

Resolving kit review candidates

Numbered files such as CLAUDE.2.md, AGENTS.2.md, or docs/okf-map.2.yml are review candidates. They are deliberately trackable, and the live tools do not load them automatically. In particular, Claude Code loads CLAUDE.md; it ignores CLAUDE.2.md until its content is merged into a live instruction file.

Before the first project commit:

  1. Read the kit installer's Needs review list and compare each candidate with its live file.
  2. Move shared repository rules into AGENTS.md. Keep Claude-specific @ imports and installed-skill references in CLAUDE.md.
  3. Delete each numbered candidate after adopting or rejecting its content.
  4. Run the kit's scripts/verify-install from the kit checkout, then run the generated project's make check.
  5. Check git status and commit only the resolved live files. An unresolved CLAUDE.2.md should not remain in the project history.

scripts/create-project prints the kit installer summary and repeats this candidate-review requirement in its final next steps. CI uses a fake kit to guard that reporting contract. A release check with a local real-kit clone confirms the two repositories still compose as expected.

The build metadata exists so this chassis can prove its wheel and source archive. This repository is distributed as a GitHub template; the example skeleton-cli package is not the project generator.

Design rationale

Three deliberate decisions shape this repo:

  • Stack and process are separate layers. This template carries toolchain decisions plus a small AGENTS.md for setup, verification, and repository safety. The operating contract — goal file, source-to-knowledge mapping, session hooks, the milestone loop — arrives by running claude-okf-repo-kit's installer on top, per project. That separation keeps the template's release cadence (Python ecosystem churn) independent of the kit's (process changes).
  • A walking skeleton, not file stubs. The template is itself a working CLI passing its own gate in its own CI, so it cannot rot silently: a stub bag decays invisibly, a skeleton fails its CI the day it breaks.
  • Scripts for what must never vary. Extraction, rename, and layering are deterministic mechanics encoded in scripts/create-project and guarded by smoke tests — re-derived by no one, human or agent. Judgment work (your README, your goal) is printed as next steps, never attempted.

The chassis was seeded one time from spec-agent-cli and is maintained here.

Working with Claude Code and Codex

Everything an agent runs here is uv, bash, and make, so Claude Code and Codex use the same mechanical path: run scripts/create-project, then verify with uv run make check. The root AGENTS.md gives Codex the stack commands and safety rules immediately. After the kit is installed on a created project, it ships Claude Code's .claude/ hooks, settings, and skills. If the live CLAUDE.md already exists, the kit preserves it and stages its proposed playbook as a numbered review candidate; resolve that candidate before expecting the full goal-loop instructions to load. The owner can mirror workflow-specific hooks and skills into .codex/hooks/ and .agents/skills/ (see spec-drift or spec-agent-cli for a worked pattern, and the kit README's second-agent section for the rules). When a Codex mirror exists, repository health tests require the complete hook set with byte-identical contents and a paired skill set.

The walking skeleton

$ skeleton-cli hello
Hello, world!

$ skeleton-cli ask "ping"          # echo provider: offline, no credentials
Echo provider received: ping

$ skeleton-cli providers
anthropic
echo
openai

$ skeleton-cli ask "ping" -p anthropic   # real vendor: needs the extra + key

Deleting the model-provider layer

If your CLI does not call language models, remove in one commit: the agents/, config/, core/, providers/, and runtime/ packages under src/, their tests (test_agent, test_factory, test_settings, test_fileset, test_*_provider), the ask/providers commands in cli.py, the anthropic/openai extras in pyproject.toml and their mirror lines in requirements.txt, the extras in the workflows and Makefile check-all, and .env.example. Run make check; the gate tells you what you missed.

Layout

├── AGENTS.md            # Codex setup, verification, and safety instructions
├── .github/workflows/   # quality, tests, coverage (3.12–3.14); template-chassis self-test, dropped on generate
├── docs/                # knowledge bundle indexes and documentation log
├── scripts/
│   ├── check-env.py        # file-sync damage preflight, first step of make check
│   ├── check-okf-docs.py   # stdlib-only docs validator, wired into make check
│   ├── create-project      # preferred one-command project generator
│   └── rename-project      # single-use parameterization, delete after use
├── src/skeleton_cli/    # the walking skeleton
└── tests/               # CLI, provider-layer, and repository-health tests

About

Python CLI template with uv setup, strict typing, 90% coverage, offline-safe provider stubs, and optional claude-okf-repo-kit layering.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Releases

Packages

Used by

Contributors

Languages