Stop shipping fix stuff commits.
commit-critic is an LLM-powered terminal tool that reviews Git commit message quality and helps you write clearer commits from staged changes. It is built for the workflow developers already use: inspect the current repo, analyze a remote repo when needed, and turn git diff --staged into a clean Conventional Commit.
- Reviews recent commits with AI-generated critique
- Highlights weak messages and well-written messages
- Reports commit quality stats such as vague and one-word commits
- Suggests a commit message from staged changes
- Runs against OpenAI-compatible
/v1endpoints or a local model server - Falls back to deterministic
--no-llmmode for offline checks
- Bun 1.3.9+
- Git in
PATH - An OpenAI-compatible endpoint, or a local OpenAI-compatible server
Install Bun if needed:
curl -fsSL https://bun.sh/install | bashgit clone <repo-url> commit-critic
cd commit-critic
bun install --frozen-lockfile
export AI_PROVIDER=openai
export AI_BASE_URL=https://provider.example/v1
export AI_API_KEY=<api-key>
export AI_MODEL=<model-name>
bun ./src/cli.ts doctor
bun ./src/cli.ts --analyzeFor OpenAI directly, you can omit AI_BASE_URL:
export AI_PROVIDER=openai
export AI_API_KEY=<openai-api-key>
export AI_MODEL=<openai-model>
bun ./src/cli.ts doctor
bun ./src/cli.ts --analyze --count=5For a local server at localhost:8081:
export AI_PROVIDER=local
export AI_BASE_URL=http://localhost:8081/v1
export AI_MODEL=qwen3.6
bun ./src/cli.ts doctor
bun ./src/cli.ts --analyze --count=5From a source checkout, run the Bun entry point directly:
bun ./src/cli.ts --analyzeBy default this reviews the last 50 commits in the current Git repository.
Equivalent subcommand form:
bun ./src/cli.ts analyzeUseful variants:
# Analyze fewer commits
bun ./src/cli.ts --analyze --count=10
# Analyze a remote repository
bun ./src/cli.ts --analyze --url="https://github.com/steel-dev/steel-browser"
# Force machine-readable output
bun ./src/cli.ts --analyze --count=10 --json
# Deterministic offline check, no provider call
bun ./src/cli.ts --analyze --count=3 --no-llmExample terminal output:
Analyzing last 50 commits...
━━━━━━━━━━━━━━━━━━━━━━━━━━━━
💩 COMMITS THAT NEED WORK
━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Commit: "fixed bug"
Score: 2/10
Issue: Too vague - which bug? What was the impact?
Better: "fix(auth): resolve token expiration handling"
Commit: "wip"
Score: 1/10
Issue: No information about what's in progress
Better: "feat: describe the work in progress"
━━━━━━━━━━━━━━━━━━━━━━━━━━━━
💎 WELL-WRITTEN COMMITS
━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Commit: "feat(api): add Redis caching layer"
Score: 9/10
Why it's good: Clear scope, specific change, and useful context.
━━━━━━━━━━━━━━━━━━━━━━━━━━━━
📊 YOUR STATS
━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Average score: 4.2/10
Vague commits: 34 (68%)
One-word commits: 12 (24%)
When stdout is piped, JSON output is enabled automatically. Top-level JSON fields are version, command, repo, commitCount, overallScore, summary, commits, stats, topIssues, and durationMs.
bun ./src/cli.ts --write--write reads git diff --staged, summarizes the staged changes, suggests a Conventional Commit message, and prompts you to accept, edit, regenerate, cancel, or type your own message.
It does not run git commit unless you pass --commit.
Equivalent subcommand form:
bun ./src/cli.ts writeCommon workflow:
git add <files>
bun ./src/cli.ts --writePrefill the prompt when you already know the intent:
bun ./src/cli.ts --write --type=refactor --scope=config
bun ./src/cli.ts --write --type=docs --scope=readme --description="clarify setup"
bun ./src/cli.ts --write --commitExample flow:
Analyzing staged changes... (3 files changed, +82 -19 lines)
Changes detected:
• Modified provider configuration
• Added setup validation
• Updated README examples
Suggested commit message:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━
refactor(config): simplify provider setup
- Prefer AI_BASE_URL and AI_API_KEY for compatible endpoints
- Keep local model setup explicit
- Update tests for provider validation
━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Press Enter to accept, type a custom message, or /e=edit /r=regenerate /c=cancel:
bun ./src/cli.ts doctordoctor checks Git, repository detection, provider configuration, and provider connectivity. Git and repository failures are hard failures; provider config and connectivity failures are warnings so doctor can still help you diagnose setup. API keys are masked in diagnostic output.
bun ./src/cli.ts setup
bun ./src/cli.ts setup --quick
bun ./src/cli.ts setup --non-interactivesetup prints the environment values commit-critic needs. It writes .env only after explicit confirmation and uses private file permissions. setup --quick validates the current provider config and exits immediately; invalid config exits with code 3.
LLM analysis is the default. Use --no-llm only when you want an offline deterministic check.
Use this for OpenAI or any provider that implements the OpenAI /v1 API:
export AI_PROVIDER=openai
export AI_BASE_URL=https://provider.example/v1
export AI_API_KEY=<api-key>
export AI_MODEL=<model-name>
bun ./src/cli.ts doctor
bun ./src/cli.ts --analyze --count=5For OpenAI's hosted API, omit AI_BASE_URL:
export AI_PROVIDER=openai
export AI_API_KEY=<openai-api-key>
export AI_MODEL=<openai-model>OPENAI_API_KEY and OPENAI_BASE_URL also work. Provider-specific values take precedence over generic AI_API_KEY and AI_BASE_URL.
Use a local OpenAI-compatible server when you want to run without hosted provider calls:
export AI_PROVIDER=local
export AI_BASE_URL=http://localhost:8081/v1
export AI_MODEL=qwen3.6
bun ./src/cli.ts doctor
bun ./src/cli.ts --analyze --count=5For local servers that require auth, set AI_API_KEY or LOCAL_API_KEY.
Additional provider presets are available in the CLI for common OpenAI-compatible servers (llamacpp, lmstudio, vllm, ollama, and openrouter), but the generic AI_BASE_URL path is usually enough.
Advanced tuning variables: AI_TEMPERATURE, AI_MAX_TOKENS, AI_MAX_RETRIES, AI_TIMEOUT_MS, and AI_STRICT_MODE. Set AI_STRICT_MODE=true when you want LLM failures to stop analysis instead of falling back to deterministic scoring.
bun ./src/cli.ts --help
bun ./src/cli.ts doctor
bun ./src/cli.ts --analyze --count=5From another project:
bun add file:/path/to/commit-critic
bunx --bun commit-critic --help
bunx --bun commit-critic --analyze --count=5The package is Bun-native, so use bunx --bun.
bun run compile
./dist/commit-critic --help
./dist/commit-critic --analyze --count=5Cross-platform targets are also available:
bun run compile:linux
bun run compile:mac-arm
bun run compile:mac-intel
bun run compile:windowsCompiled binaries use the same commands and environment variables as the Bun entry point.
Bun compiled binaries embed the runtime. On Linux, the local standalone binary is about 85-92 MB; measure your target with wc -c dist/commit-critic*.
| Flag | Description |
|---|---|
--count <n> |
Number of commits to analyze. Default: 50 |
--url <url> |
Analyze https://, git@, file://, or an absolute path |
--provider <name> |
Override AI_PROVIDER for one run |
--model <name> |
Override AI_MODEL for one run |
--no-llm |
Use deterministic scoring only |
--no-merges |
Exclude merge commits |
--json |
Force JSON output |
--verbose |
Show detailed statistics in terminal output |
| Flag | Description |
|---|---|
--type <type> |
Preselect commit type |
--scope <scope> |
Pre-fill optional scope |
--description <text> |
Pre-fill the short description prompt |
--provider <name> |
Override AI_PROVIDER for one run |
--model <name> |
Override AI_MODEL for one run |
--no-llm |
Use a deterministic template |
--commit |
Ask to run git commit after accepting the message |
bun run typecheck
bun test
bun run build
bun run compile
bun pm pack --dry-run- Do not commit
.envfiles or API keys. doctormasks configured API keys before printing them.setupwrites.envwith private permissions.- Git and subprocess calls use argument arrays instead of shell string interpolation.
- Remote analysis clones into a temporary directory and cleans it up after the run.
| Code | Meaning |
|---|---|
0 |
Success. Analysis exits 0 even when commits need work |
1 |
Operational error such as Git, I/O, or unexpected failure |
3 |
Provider auth or config error |
4 |
Reserved for network errors |
5 |
Reserved for provider client errors |
6 |
Reserved for provider server errors |
10 |
Invalid input such as bad --count, invalid --type, or no staged changes |
Set the generic compatible endpoint variables:
export AI_PROVIDER=openai
export AI_BASE_URL=https://provider.example/v1
export AI_API_KEY=<api-key>
export AI_MODEL=<model-name>
bun ./src/cli.ts doctorFor OpenAI directly:
export AI_PROVIDER=openai
export AI_API_KEY=<openai-api-key>
export AI_MODEL=<openai-model>
bun ./src/cli.ts doctorCheck that the base URL includes /v1, the model name exists on that provider, and the server is reachable:
bun ./src/cli.ts doctor
bun ./src/cli.ts --analyze --count=1 --jsonStage files first:
git add <files>
bun ./src/cli.ts --writeRun inside a repo or pass a remote URL:
bun ./src/cli.ts --analyze --url="https://github.com/user/repo"MIT