Generate high-quality git commit messages with Claude - using your Claude Code subscription, not an API key.
claude-commit reads your staged diff, has a strong model summarize it, and a fast
model turn that summary into a well-formed commit message. It handles diffs of any
size (including ones too large to fit in a single context window), and supports
Conventional Commits, gitmoji, first-line templates, custom instructions, and an
interactive mode for choosing between several options.
$ cco -c
✔ Committed
feat(auth): add error handling and refresh token rotation to loginWant more details? See WALKTHROUGH.md.
staged diff ──split──▶ [chunk, …] ──sonnet[1m]──▶ summaries ──haiku──▶ commit message
- Summarize - the diff is split into chunks that fit the context window and
each chunk is summarized by a strong model (
sonnet[1m], Sonnet with a 1M-token context). Diffs larger than 1M tokens simply produce more chunks. - Write - the summaries are handed to a fast model (
haiku) that writes the final commit message according to your formatting rules.
Both stages run through the Claude Agent SDK.
Requires Bun.
bun install
bun link # makes `cco` and `claude-commit` available on your PATHOr run it directly without linking:
bun run bin/cco.ts --helpclaude-commit uses the Claude Agent SDK and, by default, always authenticates
with your Claude Code subscription session (run claude login once). Usage is
bundled with your Claude Code usage - no separate API bill.
To protect you from surprise pay-as-you-go charges, API credentials in your
environment (ANTHROPIC_API_KEY / ANTHROPIC_AUTH_TOKEN) are ignored by
default: they are stripped from the environment passed to the model
subprocess, and a one-line notice is printed to stderr. To bill an API key
instead (pay-as-you-go), opt in explicitly in your configuration:
{ "allowApiKey": true }cco [options]By default cco summarizes your staged changes, generates a message, shows it,
and asks for confirmation before committing. Pass -y to skip the prompt, or
--dry-run to print the message without committing.
| Flag | Description |
|---|---|
-i, --interactive / --no-interactive |
Choose between several options in an interactive TUI, or skip it when enabled in config |
-n, --count <n> |
Number of options to generate in interactive mode (default 3) |
-a, --all |
Stage all changes (git add -A) before committing |
-c, --conventional |
Format as a Conventional Commit |
-g, --gitmoji |
Prefix the subject with a gitmoji |
-m, --multiline / --no-multiline |
Write a multi-line commit (subject + body), or force a single line |
-t, --template <tpl> |
Template for the first line, e.g. "[PROJ-1] {message}" |
-p, --prompt <text> |
Extra instructions appended to the prompt |
--model-summary <model> |
Model used to summarize the diff (default sonnet[1m]) |
--model-final <model> |
Model used to write the message (default haiku) |
-d, --dry-run |
Print the message to stdout without committing |
-y, --yes |
Commit without asking for confirmation |
--no-spinner |
Disable the progress spinner |
--config <path> |
Path to a config file |
-v, --verbose |
Print summaries, cost and debug output |
cco # generate, confirm, and commit staged changes
cco -a -c # stage everything and write a Conventional Commit
cco -c -g -m # conventional + gitmoji + a body
cco -i -n 5 # pick from 5 options interactively
cco --dry-run | cat # print a message without committing (TUI-free, pipe-safe)
git commit -F <(cco -d) # use the message with your own git invocationIn a pipe (no TTY) there is no spinner and no confirmation prompt - cco just
generates and commits (or prints, with --dry-run).
cco -i opens a TUI listing several candidate messages to choose from. The
options are generated with a higher temperature (interactiveTemperature) for
more variety. Use the arrow keys to move between options, Enter to commit the
highlighted option, e to edit it in your $EDITOR first, and q/Esc to
cancel.
To make interactive mode the default without typing -i every time, set
"interactive": true in your config (see below); opt out of a single run with
--no-interactive. When there is no interactive terminal - in a pipe, a CI job,
or with --dry-run - cco ignores the setting and falls back to the
non-interactive flow rather than failing.
Configuration is layered, from lowest to highest precedence:
- Built-in defaults.
- A global user config at
~/.config/claude-commit/config.json(or$XDG_CONFIG_HOME/claude-commit/config.json) - your personal defaults across every project. The.claude-commit.json/.claude-commitrc.json/.claude-commitrcnames are also accepted in that directory. - A
claude-commitkey in the repo'spackage.json. - The nearest
.claude-commit.json/.claude-commitrc.json/.claude-commitrc, searched from the current directory up to the repo root. - CLI flags.
So a global config sets your personal defaults and any project further down the
tree can override them. Note that the config.json name is recognised only in
the global directory; inside a project, use one of the dotted filenames. The same
keys are valid at every level:
{
"conventionalCommits": true,
"gitmoji": true,
"multiline": true,
"template": null,
"customPrompt": "Reference the ticket id from the branch name when present.",
"interactive": true,
"interactiveCount": 3,
"interactiveTemperature": 1,
"models": {
"summary": "sonnet[1m]",
"final": "haiku"
},
"maxChunkTokens": 600000,
"charsPerToken": 3.5,
"allowApiKey": false
}bun test # run the test suite
bun run typecheck # tsc --noEmitI distinguish vibe coding and AI-assisted development by where you live as the developer. If you live in the code, it's AI-assisted dev. If you just shout at a chat and hope for the best, that's vibe coding.
This was AI-assisted development.
Thanks for coming to my TED talk.