commitclerk logo
MIT · zero dependencies · one file you can read

Stop writing
“fix stuff”.

A clerk records what actually happened. commitclerk reads your staged diff, asks an LLM for a Conventional Commits message, shows it to you, and commits — in a single Python file with no third-party packages.

📦 pipx install commitclerk 🐍 Python 3.8+, stdlib only 🔗 Works as git clerk
~1 100 lines · one file Conventional Commits 1.0.0 Any OpenAI chat model MIT
~/your-repo
$ git add .
$ clerk

--- commit message ---
fix: prevent duplicate webhook deliveries on retry

- Deduplicate by delivery id before enqueueing, so a provider retry no
  longer fans out into multiple downstream jobs.
- Store the id in the existing idempotency table instead of a new one,
  keeping the retention policy in a single place.
- Log a debug line on the dedupe path to make retry storms visible.
----------------------
[main a1b2c3d] fix: prevent duplicate webhook deliveries on retry

Titles stay imperative and under 72 characters. Bodies stay 2–6 bullets about why — never a file-by-file replay. No emojis, no headers, no code fences.

Why you'll love it

Small enough to trust, opinionated enough to be useful

One downloadable file, no dependencies — you can read the whole thing before letting it near your source code. (The source is a small package; the file is a build of it.)

🪶

Zero dependencies

Standard library only — urllib, subprocess, argparse. Drop the file in and run it, or install it properly. Your call.

🔗

Git-native

Installs as git clerk too, so it lives where the rest of your git muscle memory already is: git add -A && git clerk.

✍️

You can own the title

-m "feat: add X" uses your title verbatim and reduces the model's job to writing the body. The framing of the commit stays yours.

📄

Doc-aware

Detects documentation-only commits and refuses to describe already-shipped features as new work — the blind spot every diff-only generator has.

👀

Dry run

--dry-run prints the message and commits nothing. Look before you leap, every time, at no risk to your history.

🔧

Model agnostic

OpenAI, Anthropic or a local Ollama model via --provider, any model via --model, and any OpenAI-compatible endpoint via --base-url — LM Studio, vLLM, OpenRouter, Groq, Azure. Cheap by default, stronger when the change is subtle, local when the diff must stay put.

The blind spot

Most generators only see the diff

When a commit adds prose to a CHANGELOG, ROADMAP or README describing a feature that shipped three commits ago, a diff-only generator reads that prose and announces the feature — for a commit that changed nothing but Markdown. Your history is now lying to you, and git log --grep and your release tooling inherit the lie.

Diff-only generator
feat: implement real-time collaboration

…for a commit that only added a paragraph to the changelog. Nothing was implemented. The next release notes will claim otherwise.

commitclerk
docs: record real-time collaboration in the changelog

Every staged file is documentation, so the prompt switches to a docs-only framing: describe the documentation change itself, never “implement X”.

Two safeguards

Detection, plus an override you control

  • 1Documentation detection. If every staged file is docs — .md, .mdx, .rst, .txt, .adoc, anything under docs/, or a known name like CHANGELOG / README / ROADMAP — the framing switches to docs:. When docs are mixed with code, the type comes only from the non-documentation lines, so a docstring tweak beside a big changelog entry is still docs:.
  • 2-m as an override. You know what your change is. Pinning the title reduces the model's job to summarizing the diff underneath it — the recommended default for any commit whose intent isn't obvious from the diff alone.
How it works

Four steps, no magic

git diff --staged ├─▶ truncate to --max-chars ├─▶ doc-only? → switch framing ├─▶ build prompt → Chat Completions └─▶ print git commit -F -

The whole thing is ~230 lines. It's meant to be read, forked, and adapted to your team's conventions — start with the _RULES string.

Command line

Five flags, and you'll mostly use one

Installing gives you three identical entry points: clerk, commitclerk, and git clerk.

clerk [-m TITLE] [--dry-run] [--provider NAME] [--base-url URL] [--model MODEL] [--timeout S] [--max-chars N] [--version]
FlagDefaultWhat it does
-m, --message TITLEUse TITLE verbatim as the commit title; the AI writes only the body bullets.
--dry-runoffPrint the generated message and exit without committing.
--provider NAMEopenaiopenai, anthropic, or ollama (local, no key) — or set $CLERK_PROVIDER.
--base-url URLhttps://api.openai.com/v1Any OpenAI-compatible endpoint — Ollama, LM Studio, vLLM, OpenRouter, Groq, Azure — or set $OPENAI_BASE_URL.
--model MODELgpt-4o-miniModel to call — the provider's default, or set $OPENAI_MODEL.
--timeout S60Seconds to wait for each API request. Raise it for a slow local model; transient failures are retried twice with backoff.
--max-chars N60000Character budget for the diff. A larger diff is trimmed per file, so every changed file still reaches the model; lockfiles and vendored files collapse to one line first.
--versionPrint the version and exit.

Examples

# the AI writes the whole message clerk # you choose the title — the most reliable mode clerk -m "refactor: extract retry policy" # preview only, never commits clerk --dry-run # a stronger model for a subtle change clerk --model gpt-4o # a local model — no API key, nothing leaves your machine clerk --provider ollama # Anthropic instead of OpenAI clerk --provider anthropic

Exit codes

0Committed (or --dry-run printed the message).
1Nothing staged — run git add first.
2Configuration problem — the provider's API key is not set, or the provider does not exist.
otherPassed through from git commit.

Scriptable on purpose: the exit code tells your wrapper exactly what happened.

Get started

Up and running in one minute

Three steps, and the third one is just git add.

1

Install it

pipx install commitclerk — or download the single file and run python commitclerk.py. Both work identically.

2

Set your API key

export OPENAI_API_KEY="sk-…" (on Windows, setx — then reopen the terminal).

3

Stage and commit

git add . then clerk --dry-run to look, clerk to commit.

Install from PyPI:

pipx install commitclerk

Or skip installing entirely — it's one file with no dependencies:

curl -O https://raw.githubusercontent.com/alegauss/commitclerk/main/dist/commitclerk.py
📦 commitclerk on PyPI
Optional

Stage-and-commit in one word

Two wrappers ship with the repo — run-commit.cmd (Windows) and run-commit.sh (macOS / Linux). Both check the API key, stage everything with git add -A, then pass your arguments straight through.

Heads up: they stage everything, including new, deleted and dot-prefixed files. The Python script itself never stages anything on its own.

REM Windows run-commit.cmd -m "feat: add CSV export" # macOS / Linux ./run-commit.sh -m "feat: add CSV export" # or just an alias alias ac='git add -A && clerk'
🔒

Know what leaves your machine

Your staged diff is sent to the API you configuredapi.openai.com by default, or api.anthropic.com with --provider anthropic. That is the whole point, and it is the one thing to be deliberate about. On a repository whose contents cannot leave your machine, run --provider ollama — a local model, no API key, nothing over the network — or don't run the tool there at all. Check your employer's policy first.

Nothing else is transmitted, stored, or logged by this tool — no telemetry, no analytics, no remote config. The API key is read from the environment and never written to disk. Cost is a single Chat Completions call per commit: with the default gpt-4o-mini and a typical diff, a fraction of a cent.

Open source

Good first contributions are on the roadmap

Keep it dependency-free, keep it one file, and open an issue before a large change. Grab one of these, or propose your own.

🪝

prepare-commit-msg hook

An installer so the message is drafted by git itself, wherever you commit from.

🧩

More providers

Anthropic, Azure OpenAI, and Ollama / local models — without breaking the zero-dependency rule.

📝

Interactive --edit

Open the generated message in $EDITOR before the commit lands.

⚙️

Config file

Project-specific commit rules, so a team's conventions travel with the repo.

clerk --lint

Validate an existing message with no API call — usable as a commit-msg hook.

🎬

A demo cast

An asciinema recording or GIF for the top of the README. Worth a thousand words.

Read CONTRIBUTING.md