AI-powered git commit messages from your staged diff. Single file, zero dependencies, and it never turns a CHANGELOG edit into a fake feat:.
Project description
commitclerk
Write better git commit messages in one command — powered by your staged diff and an LLM.
Quick start · Usage · Why it exists · Configuration · Contributing · Português
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 zero dependencies, small enough that you can read the whole thing before letting it near your source code.
$ 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
Highlights
| 🪶 Zero dependencies | Standard library only (urllib, subprocess, argparse). Drop the file in and run it. |
| ✍️ You can own the title | -m "feat: add X" uses your title verbatim and lets the AI write only the body. |
| 📄 Doc-aware | Detects documentation-only commits and refuses to describe already-shipped features as new work. See Why it exists. |
| 🧾 Conventional Commits | Emits feat: / fix: / docs: / chore: / refactor: / test: / build: / perf: prefixes. |
| 👀 Dry run | --dry-run prints the message and commits nothing. |
| 🔧 Model agnostic | Any OpenAI Chat Completions model via --model or $OPENAI_MODEL. |
Requirements
- Python 3.8+ — no third-party packages
- git on your
PATH - An OpenAI API key in
OPENAI_API_KEY
Quick start
1. Install
pipx install commitclerk # recommended
# or
pip install commitclerk
Or skip installing entirely — it is one file with no dependencies, so this works just as well:
curl -O https://raw.githubusercontent.com/alegauss/commitclerk/main/commitclerk.py
python commitclerk.py --help
2. Set your API key
# macOS / Linux
export OPENAI_API_KEY="sk-..."
# Windows (PowerShell, persisted for future sessions)
setx OPENAI_API_KEY "sk-..."
3. Stage and commit
git add .
clerk --dry-run # look before you leap
clerk
Usage
clerk [-m TITLE] [--dry-run] [--model MODEL] [--max-chars N] [--version]
Installing gives you two identical commands, clerk and commitclerk. If you
run the file directly instead, replace clerk with python commitclerk.py in
every example below.
| 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. |
--model MODEL |
gpt-4o-mini (or $OPENAI_MODEL) |
Chat Completions model to call. |
--max-chars N |
60000 |
Truncate the diff to N characters before sending it to the API. |
--version |
— | Print the version and exit. |
Examples
# Let the AI write the whole message
clerk
# You choose the title, the AI writes the body — the most reliable mode
clerk -m "refactor: extract retry policy into its own module"
# Preview only, never commits
clerk --dry-run
# Use a stronger model for a large or subtle change
clerk --model gpt-4o
# Very large diff: send more context
clerk --max-chars 120000
Exit codes
| Code | Meaning |
|---|---|
0 |
Committed (or --dry-run printed the message). |
1 |
Nothing staged — run git add first. |
2 |
OPENAI_API_KEY is not set. |
| other | Passed through from git commit. |
Windows wrapper
run-commit.cmd is a convenience wrapper for Windows: it checks the API key, runs git add *, then calls commitclerk.py with whatever arguments you pass through.
run-commit.cmd -m "feat: add CSV export to the reports page"
Put the repo directory (or a copy of both files) on your PATH to call it from any repo:
run-commit.cmd
Heads up: the wrapper stages everything with
git add *. If you prefer to curate what goes into the commit, stage it yourself and callpython commitclerk.pydirectly. The Python script never stages anything on its own.
On macOS and Linux, a shell alias does the same job:
alias ac='git add -A && clerk'
Why it exists
Most commit-message generators only see the diff, and that is a real blind spot. When a commit adds prose to a CHANGELOG, ROADMAP, or README describing a feature that shipped three commits ago, a naive generator reads that prose and writes:
feat: implement real-time collaboration
…for a commit that changed nothing but Markdown. Your history is now lying to you, and git log --grep and release tooling inherit the lie.
commitclerk handles this in two ways:
-
Documentation-only detection. If every staged file is documentation —
.md,.mdx,.rst,.txt,.adoc, anything underdocs/, or a known name likeCHANGELOG/README/ROADMAP/CONTRIBUTING— the prompt switches to a docs-only framing: use thedocs:prefix and describe the documentation change itself ("record X in the changelog"), never "implement X". -
-mas an override. You know what your change is.-m "<title>"pins the title and reduces the model's job to summarizing the diff underneath it. This is the recommended default for any commit whose intent isn't obvious from the diff alone.
The same rule set also keeps titles imperative and under 72 characters, keeps bodies to 2–6 bullets about why rather than a file-by-file replay, and bans emojis, headers, and code fences.
How it works
git diff --staged ──▶ truncate to --max-chars ──▶ doc-only? ──▶ build prompt
│
Chat Completions API ◀─────────┘
│
message ──▶ print ──▶ git commit -F -
The whole thing is ~230 lines in commitclerk.py. It's meant to be read, forked, and adapted to your team's conventions — start with the _RULES string.
Privacy and cost
- Your staged diff is sent to the OpenAI API. Do not run this on repositories whose contents cannot leave your machine. 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-miniand a typical diff, that is a fraction of a cent.
Troubleshooting
"No staged changes. Run git add <files> first."
Nothing is staged. commitclerk.py deliberately never stages for you — run git add (or use run-commit.cmd, which stages everything).
"Error: OPENAI_API_KEY is not set."
Export the key in the shell you are actually using. On Windows, setx only affects new terminals — reopen yours after running it.
"OpenAI API error 401 / 429"
401 means the key is invalid or revoked. 429 means rate-limited or out of quota — check your usage at the OpenAI dashboard, or retry with a smaller --max-chars.
The message describes the wrong thing
Use -m "<your title>". The AI then writes only the body, and the framing of the commit is yours.
The diff got truncated
Diffs are cut at 60 000 characters by default. Raise it with --max-chars, or — better — split the change into smaller commits.
Roadmap
Ideas that would make good first contributions:
- A POSIX
run-commit.shwrapper to matchrun-commit.cmd -
prepare-commit-msggit hook installer - Support for additional providers (Anthropic, Azure OpenAI, Ollama / local models)
- Interactive
--editmode that opens the message in$EDITORbefore committing - A configuration file for project-specific commit rules
Grab one, or propose your own in an issue.
Contributing
Contributions are very welcome. Read CONTRIBUTING.md for the ground rules — the short version is: keep it dependency-free, keep it one file, and open an issue before a large change.
Also see the Code of Conduct and the security policy.
License
MIT © Alexandre Oliveira
git commit -m "fix stuff", consider leaving a ⭐.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file commitclerk-0.2.1.tar.gz.
File metadata
- Download URL: commitclerk-0.2.1.tar.gz
- Upload date:
- Size: 11.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
99e1bdefdce1627e28e161c8879612172a15a9a5a1d75afd08c7168217bc2426
|
|
| MD5 |
5f0fe06406e7ac7b1e69d885c19e1acd
|
|
| BLAKE2b-256 |
e7948da92bce1ad06756ce50381dd6dc55c23df7621d965fde48a9ac61575c4f
|
Provenance
The following attestation bundles were made for commitclerk-0.2.1.tar.gz:
Publisher:
publish.yml on alegauss/commitclerk
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
commitclerk-0.2.1.tar.gz -
Subject digest:
99e1bdefdce1627e28e161c8879612172a15a9a5a1d75afd08c7168217bc2426 - Sigstore transparency entry: 2263362389
- Sigstore integration time:
-
Permalink:
alegauss/commitclerk@2e2c496b618752e83d4725125c2324ffab50db9c -
Branch / Tag:
refs/heads/main - Owner: https://github.com/alegauss
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@2e2c496b618752e83d4725125c2324ffab50db9c -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file commitclerk-0.2.1-py3-none-any.whl.
File metadata
- Download URL: commitclerk-0.2.1-py3-none-any.whl
- Upload date:
- Size: 11.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5e49e39ef412c9add5f22d3c8c20a46e760520cc0a7b2c4d2e4f0350dac3600d
|
|
| MD5 |
67793fc64fca79c17e1c72355c81bb72
|
|
| BLAKE2b-256 |
882f979c93118564353bd981e2cb8c3bb2a1d5ae363df39c43846b18546427ed
|
Provenance
The following attestation bundles were made for commitclerk-0.2.1-py3-none-any.whl:
Publisher:
publish.yml on alegauss/commitclerk
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
commitclerk-0.2.1-py3-none-any.whl -
Subject digest:
5e49e39ef412c9add5f22d3c8c20a46e760520cc0a7b2c4d2e4f0350dac3600d - Sigstore transparency entry: 2263362518
- Sigstore integration time:
-
Permalink:
alegauss/commitclerk@2e2c496b618752e83d4725125c2324ffab50db9c -
Branch / Tag:
refs/heads/main - Owner: https://github.com/alegauss
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@2e2c496b618752e83d4725125c2324ffab50db9c -
Trigger Event:
workflow_dispatch
-
Statement type: