AI-powered git commit message generator with multi-LLM support
Project description
Hunknote
Transform messy working trees into clean, atomic commit stacks with AI.
Features
- Compose mode: Split working tree changes into a clean stack of atomic commits automatically — the killer feature
- Automatic commit message generation from staged git changes
- Smart caching: Reuses generated messages for the same staged changes (no redundant API calls)
- Multi-LLM support: Anthropic, OpenAI, Google Gemini, Mistral, Cohere, Groq, and OpenRouter
- Commit style profiles: Default, Blueprint (structured sections), Conventional Commits, Ticket-prefixed, and Kernel-style
- Smart scope inference: Automatically detect scope from file paths (monorepo, path-prefix, mapping)
- Intelligent type selection: Automatically selects the correct commit type (feat, fix, docs, test, merge, etc.)
- Intent channel: Provide explicit motivation/context with
--intentto guide commit message framing - Merge state detection: Automatically detects merge commits and conflict resolutions
- Structured output: Title line + bullet-point body following git best practices
- Raw JSON debugging: Inspect the raw LLM response with
--jsonflag - Intelligent context: Distinguishes between new files and modified files for accurate descriptions
- Editor integration: Review and edit generated messages before committing
- One-command commits: Generate and commit in a single step with confirmation prompt
- Configurable ignore patterns: Exclude lock files, build artifacts, etc. from diff analysis
- Debug mode: Inspect cache metadata, token usage, scope inference, and file change details
- Comprehensive test suite: 818+ unit tests covering all modules
Installation
Option 1: Install from PyPI (Recommended)
# Using pipx (recommended - installs in isolated environment)
pipx install hunknote
# Or using pip
pip install hunknote
Option 2: Install from Source
# Clone the repository
git clone <repo-url>
cd hunknote
# Install with Poetry (requires Python 3.12+)
poetry install
# Or install in development mode with test dependencies
poetry install --with dev
Verify Installation
# Check that hunknote is available
hunknote --help
# Check git subcommand works
git hunknote --help
Quick Start
# Initialize configuration (interactive setup)
hunknote init
# This will prompt you to:
# 1. Select an LLM provider (Anthropic, OpenAI, Google, etc.)
# 2. Choose a model
# 3. Enter your API key
# Stage your changes
git add <files>
# Generate a commit message
hunknote
# Edit the message (optional)
hunknote -e
# Commit with the generated message
hunknote commit
# Or commit immediately without confirmation
hunknote commit -y
Configuration
Initial Setup
Run the interactive configuration wizard:
hunknote init
This creates a global configuration at ~/.hunknote/ with:
config.yaml- Provider, model, and preference settingscredentials- Securely stored API keys (read-only permissions)
Managing Configuration
View current configuration:
hunknote config show
Change provider or model:
# Interactive model selection
hunknote config set-provider google
# Or specify model directly
hunknote config set-provider anthropic --model claude-sonnet-4-20250514
Update API keys:
hunknote config set-key google
hunknote config set-key anthropic
List available providers and models:
hunknote config list-providers
hunknote config list-models google
hunknote config list-models # Show all providers and models
Manual Configuration
Alternatively, you can manually edit ~/.hunknote/config.yaml:
provider: google
model: gemini-2.0-flash
max_tokens: 1500
temperature: 0.3
editor: gedit # Optional: preferred editor for -e flag
default_ignore: # Optional: patterns to ignore in all repos
- poetry.lock
- package-lock.json
- "*.min.js"
And add API keys to ~/.hunknote/credentials:
GOOGLE_API_KEY=your_key_here
ANTHROPIC_API_KEY=your_anthropic_key
OPENAI_API_KEY=your_openai_key
Setting Up API Keys (Alternative Methods)
API keys are checked in this order:
- Environment variables (highest priority - useful for CI/CD)
~/.hunknote/credentialsfile (recommended for local development)- Project
.envfile (lowest priority)
Set via environment variable:
# Anthropic
export ANTHROPIC_API_KEY=your_key_here
# OpenAI
export OPENAI_API_KEY=your_key_here
# Google Gemini
export GOOGLE_API_KEY=your_key_here
# Mistral
export MISTRAL_API_KEY=your_key_here
# Cohere
export COHERE_API_KEY=your_key_here
# Groq
export GROQ_API_KEY=your_key_here
# OpenRouter (access to 200+ models)
export OPENROUTER_API_KEY=your_key_here
Or create a .env file in your project root.
Supported Providers and Models
| Provider | Models | API Key Variable |
|---|---|---|
| Anthropic | claude-sonnet-4-20250514, claude-3-5-sonnet-latest, claude-3-5-haiku-latest, claude-3-opus-latest | ANTHROPIC_API_KEY |
| OpenAI | gpt-4.1, gpt-4.1-mini, gpt-4.1-nano, gpt-4o, gpt-4o-mini, gpt-4-turbo | OPENAI_API_KEY |
| gemini-3-pro-preview, gemini-2.5-pro, gemini-3-flash-preview, gemini-2.5-flash, gemini-2.5-flash-lite, gemini-2.0-flash, gemini-2.0-flash-lite | GOOGLE_API_KEY |
|
| Mistral | mistral-large-latest, mistral-medium-latest, mistral-small-latest, codestral-latest | MISTRAL_API_KEY |
| Cohere | command-r-plus, command-r, command | COHERE_API_KEY |
| Groq | llama-3.3-70b-versatile, llama-3.1-8b-instant, mixtral-8x7b-32768 | GROQ_API_KEY |
| OpenRouter | 200+ models (anthropic/claude-sonnet-4, openai/gpt-4o, google/gemini-2.0-flash-exp, meta-llama/llama-3.3-70b-instruct, deepseek/deepseek-chat, qwen/qwen-2.5-72b-instruct, etc.) | OPENROUTER_API_KEY |
Usage
Basic Usage
Stage your changes and generate a commit message:
git add <files>
hunknote
Command Options
| Flag | Description |
|---|---|
-e, --edit |
Open the generated message in an editor for manual edits |
-r, --regenerate |
Force regenerate, ignoring cached message |
-d, --debug |
Show full cache metadata (staged files, tokens, diff preview, scope inference) |
-j, --json |
Show the raw JSON response from the LLM for debugging |
-i, --intent |
Provide explicit intent/motivation to guide commit message framing |
--intent-file |
Load intent text from a file |
--style |
Override commit style profile (default, blueprint, conventional, ticket, kernel) |
--scope |
Force a scope for the commit message (use 'auto' for inference) |
--no-scope |
Disable scope even if profile supports it |
--scope-strategy |
Scope inference strategy (auto, monorepo, path-prefix, mapping, none) |
--ticket |
Force a ticket key (e.g., PROJ-123) for ticket-style commits |
--max-diff-chars |
Maximum characters for staged diff (default: 50000) |
Commit Subcommand
Use hunknote commit to commit using the generated message:
# Commit with the cached message (prompts for confirmation)
hunknote commit
# Commit immediately without confirmation (for CI/scripts)
hunknote commit -y
hunknote commit --yes
Scope Inference
Hunknote automatically infers scope from your staged files for consistent commit messages:
# Automatic scope inference (default)
hunknote --style conventional # feat(api): Add endpoint
# Force a specific scope
hunknote --scope auth --style conventional
# Disable scope
hunknote --no-scope --style conventional
# Choose inference strategy
hunknote --scope-strategy monorepo --style conventional
hunknote --scope-strategy path-prefix --style conventional
Supported strategies:
- auto (default): Tries all strategies in order
- monorepo: Infer from
packages/,apps/,libs/directories - path-prefix: Use the most common path segment
- mapping: Use explicit path-to-scope mapping in config
- none: Disable scope inference
Intent Channel
When the diff alone doesn't convey the "why" behind your changes, use the intent channel to provide context:
# Provide intent directly
hunknote --intent "Fix race condition in session handling"
# Load intent from a file
hunknote --intent-file ./commit-intent.txt
# Combine both (concatenated with blank line)
hunknote --intent "Primary motivation" --intent-file ./additional-context.txt
The intent guides the LLM's framing of the commit message while keeping technical claims constrained to what's actually in the diff. Useful for:
- Explaining non-obvious changes
- Providing business context
- Guiding the narrative when the diff is ambiguous
Commit Style Profiles
Hunknote supports multiple commit message formats to match your team's conventions:
# List available profiles
hunknote style list
# Show details about a profile
hunknote style show blueprint
# Set default style globally
hunknote style set blueprint
# Set style for current repo only
hunknote style set ticket --repo
# Override style for a single run
hunknote --style blueprint --scope api
hunknote --style conventional --scope api
hunknote --style ticket --ticket PROJ-123 -e -c
Available Profiles
| Profile | Format | Description |
|---|---|---|
| default | <Title>\n\n- <bullet> |
Simple title + bullet points |
| blueprint | <type>(<scope>): <title>\n\n<summary>\n\nChanges:\n- ... |
Structured sections (Changes, Implementation, Testing, Documentation, Notes) |
| conventional | <type>(<scope>): <subject> |
Conventional Commits format |
| ticket | <KEY-123> <subject> |
Ticket-prefixed format |
| kernel | <subsystem>: <subject> |
Linux kernel style |
Ignore Pattern Management
Manage which files are excluded from the diff sent to the LLM:
# List all ignore patterns
hunknote ignore list
# Add a new pattern
hunknote ignore add "*.log"
hunknote ignore add "build/*"
hunknote ignore add "dist/*"
# Remove a pattern
hunknote ignore remove "*.log"
Configuration Commands
Manage global configuration stored in ~/.hunknote/:
# View current configuration
hunknote config show
# Set or update API key for a provider
hunknote config set-key google
hunknote config set-key anthropic
# Change provider and model
hunknote config set-provider google
hunknote config set-provider anthropic --model claude-sonnet-4-20250514
# List available providers
hunknote config list-providers
# List models for a specific provider
hunknote config list-models google
# List all providers and their models
hunknote config list-models
Examples
# Generate commit message (print only, cached for reuse)
hunknote
# Generate and open in editor
hunknote -e
# Generate commit message
hunknote
# Commit with the generated message (prompts for confirmation)
hunknote commit
# Commit immediately without confirmation (for CI/scripts)
hunknote commit -y
# Edit message before committing
hunknote -e
hunknote commit
# Force regeneration (ignore cache)
hunknote -r
# Debug: view cache metadata, token usage, and scope inference
hunknote -d
# View raw JSON response from LLM
hunknote -j
# Force regenerate and view raw JSON
hunknote -r -j
# Use conventional commits style with scope
hunknote --style conventional --scope api
# Use blueprint style for detailed commit messages
hunknote --style blueprint
# Use ticket-prefixed style
hunknote --style ticket --ticket PROJ-6767
hunknote commit
# Force kernel style for this commit
hunknote --style kernel --scope auth
# Provide intent to guide the commit message
hunknote --intent "Fix memory leak in connection pool"
# Load intent from a file
hunknote --intent-file ./intent.txt
hunknote commit -y
Git Subcommand
You can also use it as a git subcommand:
git hunknote
git hunknote -e
git hunknote commit
git hunknote commit -y
Compose Mode (Commit Stacking)
Split messy working tree changes into a clean stack of atomic commits:
# Preview the proposed commit stack (plan-only, no changes)
hunknote compose
# Execute the plan and create commits
hunknote compose --commit
# Skip confirmation prompt
hunknote compose --commit --yes
# Limit to 3 commits
hunknote compose --max-commits 3
# Use a specific style
hunknote compose --style conventional
# Force regenerate (ignore cache)
hunknote compose -r
# Show cached compose plan JSON
hunknote compose -j
# Load plan from external file
hunknote compose --from-plan my_plan.json --commit
# Debug mode: show inventory and patch details
hunknote compose --debug
What Compose Does
- Collects all tracked changes (staged + unstaged) from
git diff HEAD - Parses the diff into files and hunks with stable IDs
- Asks the LLM to split changes into logical, atomic commits
- Validates the plan (no duplicate hunks, all hunks assigned, etc.)
- Optionally executes by applying patches and creating commits
Safety Features
- Plan-only by default: Does not modify git state unless
--commitis passed - Pre-execution snapshot: Saves staged state before committing for recovery
- Best-effort restore: Attempts to restore previous state on failure
- Binary file handling: Skips binary files with warnings
- Untracked file warnings: Reminds you to stage untracked files
Compose Options
| Flag | Description |
|---|---|
--max-commits |
Maximum commits in the plan (default: 6) |
--style |
Override commit style profile |
-c, --commit |
Execute the plan and create commits |
-y, --yes |
Skip confirmation prompt |
--dry-run |
Force plan-only even if --commit present |
-r, --regenerate |
Force regenerate the plan, ignoring cache |
-j, --json |
Show the cached compose plan JSON for debugging |
--from-plan |
Load plan from external JSON file (skip LLM) |
--debug |
Print diagnostics |
Caching
Compose uses smart caching similar to the main command:
- Cache key is computed from: diff content + style + max_commits
- Cached files are stored in
.hunknote/:hunknote_compose_plan.json- The full compose planhunknote_compose_metadata.json- Generation metadatahunknote_hunk_ids.json- All hunks with their diffs and commit assignments
- Use
-rto force regeneration - Use
-jto inspect the cached plan - Cache is automatically invalidated after successful commit execution
How It Works
- Collects git context: branch name, file changes (new vs modified), last 5 commits, and staged diff
- Computes a hash of the context to check cache validity
- Checks cache: If valid, uses cached message; otherwise calls the configured LLM
- Parses the response: Extracts structured JSON (title + bullet points) from LLM response
- Renders the message: Formats into standard git commit message format
- Optionally opens editor and/or commits
Intelligent File Change Detection
The tool distinguishes between:
- New files (did not exist before this commit)
- Modified files (already existed, now changed)
- Deleted files
- Renamed files
This context helps the LLM generate accurate descriptions - for example, it won't say "implement caching" when you're just adding tests for existing caching functionality.
Caching Behavior
The tool caches generated commit messages to avoid redundant API calls:
- Same staged changes → Uses cached message (no API call)
- Different staged changes → Regenerates automatically
- After commit → Cache is invalidated
- Use
-rflag → Force regeneration
Cache files are stored in <repo>/.hunknote/:
hunknote_message.txt- The cached commit messagehunknote_context_hash.txt- Hash of the git contexthunknote_metadata.json- Full metadata (tokens, model, timestamp)hunknote_llm_response.json- Raw JSON response from LLM (for debugging with-j)config.yaml- Repository-specific configuration
Gitignore recommendation: Add these to your .gitignore:
# hunknote cache files (but keep config.yaml for shared settings)
.hunknote/hunknote_*.txt
.hunknote/hunknote_*.json
Repository Configuration
Each repository can have its own .hunknote/config.yaml file for customization.
The file is auto-created with defaults on first run.
Ignore Patterns
The ignore section lists file patterns to exclude from the diff sent to the LLM.
This reduces token usage and focuses the commit message on actual code changes.
ignore:
# Lock files (auto-generated)
- poetry.lock
- package-lock.json
- yarn.lock
- pnpm-lock.yaml
- Cargo.lock
- Gemfile.lock
- composer.lock
- go.sum
# Build artifacts
- "*.min.js"
- "*.min.css"
- "*.map"
# Binary and generated files
- "*.pyc"
- "*.pyo"
- "*.so"
- "*.dll"
- "*.exe"
# IDE files
- ".idea/*"
- ".vscode/*"
- "*.swp"
- "*.swo"
You can add custom patterns using glob syntax (e.g., build/*, *.generated.ts).
Output Format
Generated messages follow git best practices:
Add user authentication feature
- Implement login and logout endpoints
- Add session management middleware
- Create user model with password hashing
Development
Running Tests
The project includes a comprehensive test suite with 553 tests:
# Run all tests
pytest tests/
# Run with verbose output
pytest tests/ -v
# Run specific test file
pytest tests/test_formatters.py
# Run specific test
pytest tests/test_cache.py::TestSaveCache::test_saves_all_files
Test Coverage
| Module | Tests | Description |
|---|---|---|
cache.py |
52 | Caching utilities, metadata, raw JSON storage |
cli.py |
59 | CLI commands and subcommands |
compose.py |
51 | Compose feature (diff parsing, plan validation, caching, execution) |
config.py |
24 | Configuration constants and enums |
formatters.py |
21 | Commit message formatting and validation |
git_ctx.py |
47 | Git context collection, filtering, merge detection |
global_config.py |
26 | Global user configuration (~/.hunknote/) |
scope.py |
54 | Scope inference from file paths |
styles.py |
102 | Commit style profiles and rendering |
llm/base.py |
66 | JSON parsing, schema validation, style prompts |
llm/*.py providers |
31 | All LLM provider classes |
user_config.py |
20 | Repository YAML config file management |
Project Structure
hunknote/
├── __init__.py
├── cli.py # CLI entry point and commands
├── config.py # LLM provider configuration
├── cache.py # Caching utilities
├── formatters.py # Commit message formatting
├── styles.py # Commit style profiles (default, blueprint, conventional, ticket, kernel)
├── scope.py # Scope inference (monorepo, path-prefix, mapping)
├── git_ctx.py # Git context collection
├── user_config.py # Repository config management
├── global_config.py # Global user config (~/.hunknote/)
└── llm/
├── __init__.py # Provider factory
├── base.py # Base classes and prompts
├── anthropic_provider.py
├── openai_provider.py
├── google_provider.py
├── mistral_provider.py
├── cohere_provider.py
├── groq_provider.py
└── openrouter_provider.py
Requirements
- Python 3.12+
- Git
- API key for at least one supported LLM provider
Dependencies
typer(>=0.21.0) - CLI frameworkpydantic(>=2.5.0) - Data validationpython-dotenv- Environment variable managementpyyaml- YAML configuration- LLM SDKs:
anthropic,openai,google-genai,mistralai,cohere,groq
License
MIT
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file hunknote-1.5.0.tar.gz.
File metadata
- Download URL: hunknote-1.5.0.tar.gz
- Upload date:
- Size: 83.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/2.3.2 CPython/3.10.12 Linux/6.8.0-100-generic
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
50b857cc0c1921850381cf7282608676d56bca2325893a8353c5224381c069b8
|
|
| MD5 |
095e4b86acbd2f5b1d318030db750347
|
|
| BLAKE2b-256 |
0cd03f18309e7c6d7d6d92975fdee18e673118e91b46f88c2b0e9e218bfc6183
|
File details
Details for the file hunknote-1.5.0-py3-none-any.whl.
File metadata
- Download URL: hunknote-1.5.0-py3-none-any.whl
- Upload date:
- Size: 116.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/2.3.2 CPython/3.10.12 Linux/6.8.0-100-generic
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ac06818a4d79019a2f7d85e9a73ec1cf89bbaeceb46a9bf9f443f4be36a19dff
|
|
| MD5 |
ef51c0dd05b228be97e59413ffe87947
|
|
| BLAKE2b-256 |
74660ce8de486fdc4c2b7fe651b252553bff92b318430ae878e82f6c314e1e4d
|