Metadata-Version: 2.4
Name: commitclerk
Version: 0.2.1
Summary: AI-powered git commit messages from your staged diff. Single file, zero dependencies, and it never turns a CHANGELOG edit into a fake feat:.
Author: Alexandre Oliveira
License: MIT License
        
        Copyright (c) 2026 Alexandre Oliveira
        
        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to deal
        in the Software without restriction, including without limitation the rights
        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        copies of the Software, and to permit persons to whom the Software is
        furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
        
Project-URL: Homepage, https://github.com/alegauss/commitclerk
Project-URL: Repository, https://github.com/alegauss/commitclerk
Project-URL: Issues, https://github.com/alegauss/commitclerk/issues
Project-URL: Changelog, https://github.com/alegauss/commitclerk/blob/main/CHANGELOG.md
Keywords: git,commit,commit-message,conventional-commits,openai,llm,ai,cli,developer-tools
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Version Control :: Git
Classifier: Topic :: Utilities
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE
Dynamic: license-file

<div align="center">

# commitclerk

**Write better git commit messages in one command — powered by your staged diff and an LLM.**

[![PyPI](https://img.shields.io/pypi/v/commitclerk.svg)](https://pypi.org/project/commitclerk/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
[![Python 3.8+](https://img.shields.io/badge/python-3.8%2B-blue.svg)](https://www.python.org/downloads/)
[![Zero dependencies](https://img.shields.io/badge/dependencies-zero-brightgreen.svg)](#requirements)
[![CI](https://github.com/alegauss/commitclerk/actions/workflows/ci.yml/badge.svg)](https://github.com/alegauss/commitclerk/actions/workflows/ci.yml)
[![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-fe5196.svg)](https://www.conventionalcommits.org/)
[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](CONTRIBUTING.md)

[Quick start](#quick-start) · [Usage](#usage) · [Why it exists](#why-it-exists) · [Configuration](#configuration) · [Contributing](CONTRIBUTING.md) · [Português](README.pt-BR.md)

</div>

---

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.

```console
$ 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](#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**

```bash
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:

```bash
curl -O https://raw.githubusercontent.com/alegauss/commitclerk/main/commitclerk.py
python commitclerk.py --help
```

**2. Set your API key**

```bash
# macOS / Linux
export OPENAI_API_KEY="sk-..."
```

```powershell
# Windows (PowerShell, persisted for future sessions)
setx OPENAI_API_KEY "sk-..."
```

**3. Stage and commit**

```bash
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

```bash
# 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.

```bat
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:

```bat
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 call `python commitclerk.py` directly. The Python script never stages anything on its own.

On macOS and Linux, a shell alias does the same job:

```bash
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:

1. **Documentation-only detection.** If every staged file is documentation — `.md`, `.mdx`, `.rst`, `.txt`, `.adoc`, anything under `docs/`, or a known name like `CHANGELOG`/`README`/`ROADMAP`/`CONTRIBUTING` — the prompt switches to a docs-only framing: use the `docs:` prefix and describe *the documentation change itself* ("record X in the changelog"), never "implement X".

2. **`-m` as 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`](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-mini` and a typical diff, that is a fraction of a cent.

## Troubleshooting

<details>
<summary><strong>"No staged changes. Run <code>git add &lt;files&gt;</code> first."</strong></summary>

Nothing is staged. `commitclerk.py` deliberately never stages for you — run `git add` (or use `run-commit.cmd`, which stages everything).
</details>

<details>
<summary><strong>"Error: OPENAI_API_KEY is not set."</strong></summary>

Export the key in the shell you are actually using. On Windows, `setx` only affects **new** terminals — reopen yours after running it.
</details>

<details>
<summary><strong>"OpenAI API error 401 / 429"</strong></summary>

`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`.
</details>

<details>
<summary><strong>The message describes the wrong thing</strong></summary>

Use `-m "<your title>"`. The AI then writes only the body, and the framing of the commit is yours.
</details>

<details>
<summary><strong>The diff got truncated</strong></summary>

Diffs are cut at 60 000 characters by default. Raise it with `--max-chars`, or — better — split the change into smaller commits.
</details>

## Roadmap

Ideas that would make good first contributions:

- [ ] A POSIX `run-commit.sh` wrapper to match `run-commit.cmd`
- [ ] `prepare-commit-msg` git hook installer
- [ ] Support for additional providers (Anthropic, Azure OpenAI, Ollama / local models)
- [ ] Interactive `--edit` mode that opens the message in `$EDITOR` before committing
- [ ] A configuration file for project-specific commit rules

Grab one, or propose your own in an [issue](https://github.com/alegauss/commitclerk/issues).

## Contributing

Contributions are very welcome. Read [CONTRIBUTING.md](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](CODE_OF_CONDUCT.md) and the [security policy](SECURITY.md).

## License

[MIT](LICENSE) © Alexandre Oliveira

---

<div align="center">
If this saves you from one more <code>git commit -m "fix stuff"</code>, consider leaving a ⭐.
</div>
