commitstash
AI-powered git commit message generator — reads your staged diff, writes the commit for you
The Problem
Writing good commit messages is tedious. Most developers either:
- Write vague messages like
fix bugorupdate stuff - Spend more time on the message than the actual change
- Skip conventions entirely under time pressure
The Solution
commitstash reads your staged diff and generates a precise, conventional commit message using Claude or GPT — in under 3 seconds. No API key? Run it fully offline with --no-ai and it builds a message straight from the diff, or point it at a local model with Ollama.
It does more than messages:
- Blocks secrets — every commit is scanned for API keys, tokens, and credentials in your staged changes. Leaks are stopped before they land.
- Reviews your diff —
commitstash reviewflags bugs and issues before you commit. - Writes your PR —
commitstash prdrafts a title and description from your branch's commits and diff.
git add orders/views.py orders/serializers.py
commitstash
Staged (2 files):
· orders/views.py
· orders/serializers.py
╭─ Suggested commit message ────────────────────────────────────╮
│ feat(orders): add bulk export endpoint with date range filters │
╰───────────────────────────────────────────────────────────────╯
[Enter] commit e edit r regenerate q quit
>
✓ Committed successfully
Installation
pip install commitstash
Setup
# Anthropic Claude (default)
export ANTHROPIC_API_KEY=sk-ant-...
# Or OpenAI
export OPENAI_API_KEY=sk-...
Add the export to your ~/.zshrc or ~/.bashrc so it persists.
No API key? Skip setup entirely and use offline mode — see No-AI mode below.
Quick Start
# Stage your changes
git add <files>
# Generate and commit
commitstash
That's it. Press Enter to accept, e to edit, r to regenerate, q to quit.
Usage
# Stage everything, then generate
commitstash -a
# Auto-accept without prompting (CI / hooks)
commitstash -a -y
# Change style for one commit
commitstash --style simple
commitstash --style angular
# Add emoji prefix (✨ feat, 🐛 fix, ♻️ refactor...)
commitstash --emoji
# Include a commit body explaining WHY
commitstash --body
# Switch provider for one commit
commitstash --provider openai
# No API key — generate offline from the diff
commitstash --no-ai
No-AI (offline) mode
Don't have an API key, working offline, or just want zero-cost commits? Add --no-ai
and commitstash builds the message locally by analyzing your staged diff — no network,
no key, no SDK required.
commitstash --no-ai # generate offline
commitstash --no-ai -a -y # stage all, offline, auto-accept
It inspects the diff to pick a sensible message:
| What it detects | Example output |
|---|---|
| Brand-new file(s) | feat(auth): add login |
| Docs / README changes | docs: update README |
| Test files | test(tests): add test_llm |
| Config / build files | chore: update pyproject |
| Mostly deletions | refactor(api): remove legacy client |
| File removals | chore: remove old_helper |
The type (feat/fix/docs/test/chore/refactor), scope (derived from the common
directory), and emoji all respect your configured style. It's a heuristic, not a mind
reader — press e to tweak anything before committing.
Make it the default so you never pass the flag:
commitstash configure # choose "local" when prompted for provider
Secret Scanning
Before every commit, commitstash scans your staged changes for secrets — AWS keys,
GitHub tokens, Anthropic/OpenAI keys, Slack/Stripe/Google keys, private key blocks, JWTs,
and hardcoded password/api_key/token assignments. If it finds one, the commit is
blocked and the finding is shown with the secret redacted:
╭──────────────── Secrets detected (1) ────────────────╮
│ AWS access key ID config.py:12 │
│ AKIAIOSF…MPLE │
╰──────────────────────────────────────────────────────╯
Only added lines are scanned, so pre-existing secrets in unrelated files don't block you.
Obvious placeholders (your_key_here, xxxx, ${VAR}, changeme) are ignored.
Run the scan on its own — it exits non-zero when anything is found, so it drops straight into a pre-commit hook or CI step:
commitstash scan
Turn the automatic commit-time gate off in commitstash configure (or set "scan_secrets": false
in your config).
Review
Get a review of your staged diff before you commit:
commitstash review # AI review with your configured provider
commitstash review --no-ai # offline pattern checks only
With an AI provider it looks for bugs, security issues, and clear mistakes in the changed
lines. Offline mode is deterministic — it flags leftover debug statements (print,
console.log, breakpoint()), merge-conflict markers, and new TODO/FIXME comments —
and tells you it isn't a correctness review.
Pull Requests
Draft a PR title and description from the commits and diff on your current branch:
commitstash pr # base branch autodetected (origin/HEAD, then main/master)
commitstash pr --base develop # compare against a specific branch
commitstash pr --no-ai # assemble from commit subjects, no API key
Output is a title plus a ## Summary / ## Changes / ## Testing markdown body — paste it
straight into GitHub.
Commit Styles
| Style | Example output |
|---|---|
conventional (default) |
feat(auth): add JWT refresh token rotation |
angular |
fix(orders): handle null warehouse on bulk export |
simple |
Fix null check in order serializer |
Configure
Run the interactive setup to save your preferences:
commitstash configure
Preferences are saved to ~/.commitstash/config.json.
API keys are never written to disk — always read from environment variables.
Manual config (~/.commitstash/config.json)
{
"provider": "anthropic",
"style": "conventional",
"include_scope": true,
"include_body": false,
"emoji": false,
"max_diff_lines": 500,
"scan_secrets": true,
"ollama_model": "llama3.2",
"ollama_host": "http://localhost:11434"
}
Providers
| Provider | Default Model | Env Var |
|---|---|---|
anthropic (default) |
claude-opus-4-8 |
ANTHROPIC_API_KEY |
openai |
gpt-4o-mini |
OPENAI_API_KEY |
ollama |
llama3.2 (local LLM) |
none |
local |
— (offline heuristic) | none |
Switch permanently:
commitstash configure # select openai when prompted
Switch for one commit:
commitstash -p openai
Ollama (local LLM)
Run a real model on your own machine — no API key, no network calls off-box:
ollama serve
ollama pull llama3.2
commitstash -p ollama # one commit
commitstash configure # choose "ollama"; set model + host
Model and host are configurable (ollama_model, ollama_host).
Git Hook
Install commitstash as a prepare-commit-msg hook so every git commit auto-generates a message:
commitstash install-hook
To uninstall:
rm .git/hooks/prepare-commit-msg
Commit Splitting
Staged everything at once? commitstash split clusters the staged files into
logical commits — source changes by scope, then tests, docs, and config — and
commits each group with its own generated message:
git add .
commitstash split
Proposed split (3 commits, AI grouping)
Commit 1 — auth refactor
· auth/views.py
· auth/models.py
Commit 2 — test changes
· tests/test_auth.py
Commit 3 — documentation
· README.md
Create these commits? [y/N] > y
✓ 1/3 refactor(auth): move JWT validation into middleware
✓ 2/3 test(auth): cover middleware token validation
✓ 3/3 docs: document the new auth flow
AI providers propose the grouping from the diff; the response is strictly
validated (every staged file in exactly one group) and falls back to
deterministic grouping otherwise — --no-ai uses it directly. Splitting is
file-level, so a single file's hunks never end up divided across commits.
Files with both staged and unstaged edits abort the split rather than
silently dragging unstaged work into a commit.
Explain & Changelog
# Plain-language explanation of the staged diff — what, why, impact, risk
commitstash explain
# Changelog section from conventional commits since the last tag
commitstash changelog
# ...or a labelled release, prepended to CHANGELOG.md
commitstash changelog --label v0.3.0 --write
changelog is deliberately deterministic — the same history always produces
the same changelog, so it needs no API key and works in CI.
commitstash also reads your recent commit history when generating messages,
so suggestions match the tone and scope conventions your repo already uses.
Custom Providers
Backends are pluggable. Anything that can complete a prompt can drive every feature — subclass, register, done:
from commitstash.providers import LLMProvider, register
class GroqProvider(LLMProvider):
name = "groq"
def complete(self, prompt, config, max_tokens=1024):
... # call any API you like
register(GroqProvider())
Commands
| Command | Description |
|---|---|
commitstash |
Generate from staged diff (interactive) |
commitstash -a |
Stage all changes, then generate |
commitstash -y |
Auto-accept first suggestion |
commitstash --no-ai |
Generate offline, no API key needed |
commitstash scan |
Scan staged changes for secrets (exits 1 on findings) |
commitstash review |
Review the staged diff for bugs and issues |
commitstash pr |
Draft a PR title and description for the branch |
commitstash split |
Split staged changes into a series of atomic commits |
commitstash explain |
Explain the staged diff: what, why, impact, risk |
commitstash changelog |
Generate a changelog from conventional commit history |
commitstash configure |
Interactive setup |
commitstash install-hook |
Install as git hook in current repo |
commitstash version |
Show version |
License
MIT
Metadata
Release files for commitstash 0.5.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| commitstash-0.5.0.tar.gz | 36.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| commitstash-0.5.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 67.5 kB
Release files / commitstash-0.5.0.tar.gz
| Download URL | commitstash-0.5.0.tar.gz |
|---|---|
| Size | 36.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
7d9a22fa6ae3984698ef53203c3e273a2fdf5cf5e6ccdc0672e6f099fbd5f4d6
|
|
BLAKE2b-256 checksum How to use checksums |
43cc59709ce4c332f5bbc1f3ef6830c99166751da2206ba22e33247e6eccbfd6
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 6, 2026.
Transparency logRelease files / commitstash-0.5.0-py3-none-any.whl
| Download URL | commitstash-0.5.0-py3-none-any.whl |
|---|---|
| Size | 31.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
e88f6eae66170a2fde7ca792bf30f8b5faf6faf1d6d13216fae6bbad201dd758
|
|
BLAKE2b-256 checksum How to use checksums |
f37e47b3ee123edb677feef2777e3b18383c5be96140ce834bc4587dd0ef5ae4
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 6, 2026.
Transparency log