Git commit message generator using Claude AI (or other LLMs) via OpenAI-compatible API. Generates conventional commit messages with concise summaries (≤72 chars) and structured detail points from git diffs.
Two-phase generation:
- Analysis phase: Extract 0-6 detail points from diff using Sonnet/Opus
- Summary phase: Generate commit summary from details using Haiku
- Rust (latest stable): Install from rustup.rs
- Git: Required for repository operations
- API Access: One of the following:
- Anthropic API key (recommended)
- OpenAI API key
- OpenRouter API key
- Local LiteLLM proxy (for development)
git clone https://github.com/can1357/llm-git.git
cd llm-git
cargo install --path .Once published:
cargo install llm-gitOption A: LiteLLM (Recommended for local development)
# Start LiteLLM proxy (handles API keys and routing)
pip install litellm
export ANTHROPIC_API_KEY=your_key_here
litellm --port 4000 --model claude-sonnet-4.5
# llm-git uses localhost:4000 by default
llm-git # Ready to use!Option B: Direct API Access
# Set API URL and key via environment variables
export LLM_GIT_API_URL=https://api.anthropic.com/v1
export LLM_GIT_API_KEY=your_api_key_here
llm-gitOption C: OpenRouter
export LLM_GIT_API_URL=https://openrouter.ai/api/v1
export LLM_GIT_API_KEY=your_openrouter_key
llm-gitOption D: Configuration File
# Create ~/.config/llm-git/config.toml (see Configuration section)
mkdir -p ~/.config/llm-git
# Edit config.toml with your preferences
llm-git# Stage your changes
git add .
# Generate and commit
llm-git # Analyze & commit staged changes
llm-git --dry-run # Preview without committing
llm-git --mode=unstaged # Analyze unstaged (no commit)
llm-git --copy # Copy message to clipboard
llm-git Fixed regression from PR #123 # Add context (trailing text)Basic Usage:
llm-git # Analyze & commit staged changes (default)
llm-git --dry-run # Preview without committing
llm-git --mode=unstaged # Analyze unstaged (no commit)
llm-git --mode=commit --target=HEAD~1 # Analyze specific commit
llm-git --copy # Copy message to clipboard
llm-git -m opus # Use Opus model (more powerful)
llm-git -m sonnet # Use Sonnet model (default)
llm-git Fixed regression from PR #123 # Add context (trailing text)Compose Mode (Multi-commit generation):
llm-git --compose # Compose changes into multiple atomic commits
llm-git --compose --compose-preview # Preview proposed splits without committing
llm-git --compose --compose-max-commits 5 # Limit number of commits
llm-git --compose --compose-test-after-each # Run tests after each commitRewrite Mode (History rewrite to conventional commits):
llm-git --rewrite # Rewrite full history (creates backup)
llm-git --rewrite --rewrite-preview 10 # Preview first 10 commits
llm-git --rewrite --rewrite-dry-run # Preview all without applying
llm-git --rewrite --rewrite-start main~50 # Rewrite last 50 commits only
llm-git --rewrite --rewrite-parallel 20 # Use 20 parallel API calls
llm-git --rewrite --rewrite-hide-old-types # Hide old type/scope tagsAll configuration options can be overridden via environment variables:
LLM_GIT_API_URL- API endpoint URL (default:http://localhost:4000)LLM_GIT_API_KEY- API authentication key (default: none)LLM_GIT_CONFIG- Custom config file path (default:~/.config/llm-git/config.toml)LLM_GIT_VERBOSE- Enable verbose output with JSON structure
Examples:
# Use OpenAI instead of Claude
export LLM_GIT_API_URL=https://api.openai.com/v1
export LLM_GIT_API_KEY=sk-...
llm-git --analysis-model=gpt-4o --summary-model=gpt-4o-mini
# Use custom config location
export LLM_GIT_CONFIG=~/my-project/.llm-git-config.toml
llm-git
# Enable verbose debugging
export LLM_GIT_VERBOSE=1
llm-gitcargo test # Run all unit tests
cargo test --lib # Library tests onlyModular library structure:
src/lib.rs- Public API exportssrc/main.rs- CLI entry pointsrc/analysis.rs- Scope candidate extractionsrc/api/- OpenRouter/LiteLLM integration with retry logicsrc/config.rs- Configuration and prompt templatessrc/diff.rs- Smart diff truncation with priority scoringsrc/error.rs- Error typessrc/git.rs- Git command wrapperssrc/normalization.rs- Unicode normalization, formattingsrc/types.rs- Type-safe commit types, scopes, summariessrc/validation.rs- Commit message validation
Core workflow:
get_git_diff()+get_git_stat()- Extract staged/unstaged/commit changessmart_truncate_diff()- Priority-based diff truncation when >100KB:- Parse into
FileDiffstructs with priority scoring - Source files (rs/py/js) > config > tests > binaries > lock files
- Excluded files:
Cargo.lock,package-lock.json, etc. (seeEXCLUDED_FILES) - Preserve headers for all files, truncate content proportionally
- Parse into
generate_conventional_analysis()- Call Sonnet/Opus withCONVENTIONAL_ANALYSIS_PROMPTusing function callinggenerate_summary_from_analysis()- Call Haiku withSUMMARY_PROMPT_TEMPLATE+ detail pointspost_process_commit_message()- Enforce length limits, punctuation, capitalizationvalidate_commit_message()- Check past-tense verbs, length, punctuation
Prompts:
CONVENTIONAL_ANALYSIS_PROMPT- Extracts 0-6 past-tense detail statements from diffSUMMARY_PROMPT_TEMPLATE- Creates ≤72 char summary from details + stat- Both enforce past-tense verbs:
added,fixed,updated,refactored, etc.
Smart truncation strategy (src/diff.rs):
- Shows ALL file headers even under length pressure
- Distributes remaining space proportionally by priority
- Keeps diff context (first 15 + last 10 lines per file)
- Annotates omitted files
Dependencies:
clap- CLI parsing with derive macrosreqwest(blocking) - OpenRouter API via LiteLLM localhost:4000serde+serde_json- Function calling schema + response parsingarboard- Clipboard support for--copyanyhow+thiserror- Error handling
API integration:
- Uses OpenRouter's function calling API with structured output
- Two tools:
create_conventional_analysis(detail extraction),create_commit_summary(summary creation) - Supports trailing text arguments as user context to analysis phase
- Fallback to
fallback_summary()if model calls fail - Retry logic with exponential backoff for transient failures
Validation rules:
- Summary: ≤72 chars (guideline), ≤96 (soft limit), ≤128 (hard limit), past-tense verb, no trailing period
- Body: Past-tense verbs preferred, ends with periods
- Warns on present-tense usage but doesn't block
Models:
- Default: Sonnet 4.5 (
claude-sonnet-4.5) - Optional: Opus 4.1 (
claude-opus-4.1) via--opus - Summary creation: Haiku 4.5 (
claude-haiku-4-5-20251001) hardcoded
Rewrite mode converts entire git histories to conventional commits format:
Workflow:
- Extracts commit list via
git rev-list --reverse - For each commit: analyzes diff and generates conventional message
- Parallel API calls for faster processing
- Rebuilds history with
git commit-tree:- Preserves trees, authors, dates
- Updates messages only
- Maintains parent relationships
- Updates branch ref to new head
Safety:
- Auto-creates timestamped backup branch
--rewrite-preview N/--rewrite-dry-runmodes- Checks working tree is clean
- Preserves all commit metadata except message
Cost: ~$0.001-0.005/commit depending on model and diff size
Create ~/.config/llm-git/config.toml to customize behavior:
# API Configuration
api_base_url = "http://localhost:4000" # Override with LLM_GIT_API_URL
api_key = "your-api-key" # Optional, override with LLM_GIT_API_KEY
# HTTP Timeouts
request_timeout_secs = 120 # Request timeout (default: 120s)
connect_timeout_secs = 30 # Connection timeout (default: 30s)
# Models (supports any OpenAI-compatible API)
analysis_model = "claude-sonnet-4.5" # Model for analysis phase
summary_model = "claude-haiku-4-5" # Model for summary phase
# Commit Message Limits
summary_guideline = 72 # Target length (conventional commits)
summary_soft_limit = 96 # Triggers retry if exceeded
summary_hard_limit = 128 # Absolute maximum
# Retry Configuration
max_retries = 3 # API retry attempts
initial_backoff_ms = 1000 # Initial backoff delay
# Diff Processing
max_diff_length = 100000 # Max diff size before truncation
wide_change_threshold = 0.50 # Threshold for omitting scope (50%)
# Compose Mode
compose_max_rounds = 5 # Max rounds for multi-commit generation
# Model Temperature
temperature = 0.2 # Low for consistency (0.0-1.0)
# File Exclusions
excluded_files = [ # Files to exclude from diff
"Cargo.lock",
"package-lock.json",
"yarn.lock",
# ... add more
]
low_priority_extensions = [ # Low-priority file extensions
".lock", ".toml", ".yaml", ".json", ".md",
# ... add more
]
# Prompt Variants (advanced)
analysis_prompt_variant = "default" # Prompt template variant
summary_prompt_variant = "default" # Prompt template variant
exclude_old_message = true # Exclude old message in rewrite modeLiteLLM (localhost):
api_base_url = "http://localhost:4000"
# No api_key needed - LiteLLM handles authentication
analysis_model = "claude-sonnet-4.5"
summary_model = "claude-haiku-4-5"Anthropic Direct:
api_base_url = "https://api.anthropic.com/v1"
api_key = "sk-ant-..." # Or use LLM_GIT_API_KEY env var
analysis_model = "claude-sonnet-4.5-20250514"
summary_model = "claude-haiku-4-5-20250514"OpenRouter:
api_base_url = "https://openrouter.ai/api/v1"
api_key = "sk-or-..."
analysis_model = "anthropic/claude-sonnet-4.5"
summary_model = "anthropic/claude-haiku-4-5"OpenAI:
api_base_url = "https://api.openai.com/v1"
api_key = "sk-..."
analysis_model = "gpt-4o"
summary_model = "gpt-4o-mini"
temperature = 0.3 # OpenAI models may benefit from slightly higher tempMIT