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.
- A real CLI, not stubs.
skeleton-cli hello,ask, andproviderswork 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) — onemake check, mirrored by three GitHub Actions workflows across Python 3.12–3.14. Keep working checkouts outside synced folders when possible. A.venv.nosyncdirectory with a.venvsymlink 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.txtmust 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
LanguageModelprotocol, a registry withecho/anthropic/openaiadapters, 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.
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 theskeleton-cliname and the template maintainer's contact details inpyproject.toml,SECURITY.md, andCODE_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-nameIt 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):
-
Create your repo from this one (GitHub "Use this template", or clone and re-init).
-
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-projectrewrites the package (skeleton_cli), distribution (skeleton-cli), and env-var prefix (SKELETON_CLI) everywhere, moves the source directory, substitutes yourgit config user.nameanduser.emailas owner and contact in the docs and community files, and resets the project to its own0.1.0with a freshCHANGELOG.mdanddocs/log.md. Thermdrops the two generator scripts, their smoke test, and thetemplate-chassisworkflow — 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.
-
Create the environment and verify the gate with uv:
uv sync --all-extras uv run make check
The generated
uv.lockbelongs 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 -
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 asCLAUDE.2.mdand lists it under Needs review. -
Replace the
hellocommand with your tool's real surface and start building. The README you are reading describes the template — rewrite it for your project.
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:
- Read the kit installer's Needs review list and compare each candidate with its live file.
- Move shared repository rules into
AGENTS.md. Keep Claude-specific@imports and installed-skill references inCLAUDE.md. - Delete each numbered candidate after adopting or rejecting its content.
- Run the kit's
scripts/verify-installfrom the kit checkout, then run the generated project'smake check. - Check
git statusand commit only the resolved live files. An unresolvedCLAUDE.2.mdshould 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.
Three deliberate decisions shape this repo:
- Stack and process are separate layers. This template carries toolchain
decisions plus a small
AGENTS.mdfor 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-projectand 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.
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.
$ 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 + keyIf 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.
├── 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