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.
$ 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.
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.)
Standard library only — urllib, subprocess, argparse. Drop the file in and run it, or install it properly. Your call.
Installs as git clerk too, so it lives where the rest of your git muscle memory already is: git add -A && git clerk.
-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.
Detects documentation-only commits and refuses to describe already-shipped features as new work — the blind spot every diff-only generator has.
--dry-run prints the message and commits nothing. Look before you leap, every time, at no risk to your history.
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.
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.
…for a commit that only added a paragraph to the changelog. Nothing was implemented. The next release notes will claim otherwise.
Every staged file is documentation, so the prompt switches to a docs-only framing: describe the documentation change itself, never “implement X”.
.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:.-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.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.
Installing gives you three identical entry points: clerk, commitclerk, and git clerk.
| Flag | Default | What it does |
|---|---|---|
-m, --message TITLE | — | Use TITLE verbatim as the commit title; the AI writes only the body bullets. |
--dry-run | off | Print the generated message and exit without committing. |
--provider NAME | openai | openai, anthropic, or ollama (local, no key) — or set $CLERK_PROVIDER. |
--base-url URL | https://api.openai.com/v1 | Any OpenAI-compatible endpoint — Ollama, LM Studio, vLLM, OpenRouter, Groq, Azure — or set $OPENAI_BASE_URL. |
--model MODEL | gpt-4o-mini | Model to call — the provider's default, or set $OPENAI_MODEL. |
--timeout S | 60 | Seconds to wait for each API request. Raise it for a slow local model; transient failures are retried twice with backoff. |
--max-chars N | 60000 | Character 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. |
--version | — | Print the version and exit. |
0 | Committed (or --dry-run printed the message). |
1 | Nothing staged — run git add first. |
2 | Configuration problem — the provider's API key is not set, or the provider does not exist. |
| other | Passed through from git commit. |
Scriptable on purpose: the exit code tells your wrapper exactly what happened.
Three steps, and the third one is just git add.
pipx install commitclerk — or download the single file and run python commitclerk.py. Both work identically.
export OPENAI_API_KEY="sk-…" (on Windows, setx — then reopen the terminal).
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
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.
Your staged diff is sent to the API you configured — api.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.
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 hookAn installer so the message is drafted by git itself, wherever you commit from.
Anthropic, Azure OpenAI, and Ollama / local models — without breaking the zero-dependency rule.
--editOpen the generated message in $EDITOR before the commit lands.
Project-specific commit rules, so a team's conventions travel with the repo.
clerk --lintValidate an existing message with no API call — usable as a commit-msg hook.
An asciinema recording or GIF for the top of the README. Worth a thousand words.