Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
b452402
[#maintenance] [Suggester] Switch to OpenAI-compatible chat completio…
AfonsoNeto Sep 26, 2026
e7b1c81
[#maintenance] [Suggester] Add HTTP timeouts, status checks and error…
AfonsoNeto Sep 26, 2026
4549f3a
[#maintenance] [Suggester] Clean model output and enforce message length
AfonsoNeto Sep 26, 2026
d9e6d05
[#maintenance] [Suggester] Support conventional commits format and om…
AfonsoNeto Sep 26, 2026
34715a4
[#maintenance] [CLI] Skip LLM call when nothing is staged
AfonsoNeto Sep 26, 2026
b7cb65c
[#maintenance] [CLI] Cap diff size sent to the model
AfonsoNeto Sep 26, 2026
5b45270
[#maintenance] [CLI] Fix option resolution to respect explicit flags
AfonsoNeto Sep 26, 2026
fab2ad0
[#maintenance] [CLI] Change default model to qwen3:8b
AfonsoNeto Sep 26, 2026
91068ac
[#maintenance] [CLI] Add conventional, length and apply flags to comm…
AfonsoNeto Sep 26, 2026
940ece9
[#maintenance] [Suggester] Add list_models to query available models …
AfonsoNeto Sep 26, 2026
8f96209
[#maintenance] [CLI] Add models command and config model picker
AfonsoNeto Sep 26, 2026
501f8b5
[#maintenance] [Gemspec] Bump version to 0.2.0, add MIT license, drop…
AfonsoNeto Sep 26, 2026
5938de8
[#maintenance] [Tests] Add test suite for suggester and config manager
AfonsoNeto Sep 26, 2026
f09a01d
[#maintenance] [CI] Add GitHub Actions test workflow
AfonsoNeto Sep 26, 2026
a77f286
[#maintenance] [Suggester] Raise token budget for thinking models and…
AfonsoNeto Sep 26, 2026
1c7604b
[#maintenance] [CLI] Exit with failure status on errors
AfonsoNeto Sep 26, 2026
77a3eaa
[#maintenance] [Suggester] Raise token budget to 2048 so thinking mod…
AfonsoNeto Sep 26, 2026
27b4dec
[#maintenance] [CLI] Fix options.length collision returning hash size…
AfonsoNeto Sep 26, 2026
8b41c76
[#maintenance] [Docs] Add terminal demo gif and reproducible generato…
AfonsoNeto Sep 26, 2026
352b9a1
[#maintenance] [README] Rewrite docs for v0.2 with OpenAI-compatible …
AfonsoNeto Sep 26, 2026
531d871
[#maintenance] [Docs] Speed up typing in demo gif
AfonsoNeto Sep 26, 2026
edc05e5
[#maintenance] [CLI] Commit by default after confirmation, add --mess…
AfonsoNeto Sep 27, 2026
4dfbe2c
[#maintenance] [README] Document commit-by-default flow and --message…
AfonsoNeto Sep 27, 2026
ba44c6e
[#maintenance] [Docs] Update demo gif to default commit flow
AfonsoNeto Sep 27, 2026
fb35998
[#maintenance] [Docs] Show message-only flag on first command in demo…
AfonsoNeto Sep 27, 2026
6eb77d8
[#maintenance] [Tests] Close mock server before joining thread to avo…
AfonsoNeto Sep 27, 2026
76b5ac7
[#maintenance] [Suggester] Enforce TLS with certificate verification …
AfonsoNeto Sep 27, 2026
f80d481
[#maintenance] [Suggester] Strip ANSI escape sequences from model output
AfonsoNeto Sep 27, 2026
5419dc7
[#maintenance] [ConfigManager] Write config file with owner-only perm…
AfonsoNeto Sep 27, 2026
5c0e135
[#maintenance] [CI] Add bundler-audit dependency scan
AfonsoNeto Sep 27, 2026
7a8d865
[#maintenance] [Tests] Read full HTTP request in mock server before p…
AfonsoNeto Sep 27, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
name: Tests

on:
push:
branches: [master, main, maintenance]
pull_request:

jobs:
test:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
ruby: ['3.1', '3.2', '3.3', '3.4']
steps:
- uses: actions/checkout@v4
- uses: ruby/setup-ruby@v1
with:
ruby-version: ${{ matrix.ruby }}
bundler-cache: true
- name: Run tests
run: bundle exec rake test
- name: Scan dependencies for vulnerabilities
run: |
gem install bundler-audit --no-document
bundler-audit check
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ build-iPhoneSimulator/

# for a library or gem, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# Gemfile.lock
Gemfile.lock
# .ruby-version
# .ruby-gemset

Expand Down
3 changes: 3 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
source 'https://rubygems.org'

gemspec
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2025 Afonso Neto

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
78 changes: 58 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# AI git commit message

A Ruby gem that automatically generates concise and meaningful git commit messages using AI models via [Ollama](https://github.com/ollama/ollama) or any OpenAI-compatible API.
A Ruby gem that automatically generates concise and meaningful git commit messages using a local LLM via [Ollama](https://github.com/ollama/ollama), LM Studio, llama.cpp `llama-server`, or any other OpenAI-compatible API.

![Terminal demo](docs/demo.gif)

## Installation

Expand All @@ -26,13 +28,13 @@ bundle install

- Ruby 3.0 or newer
- Git repository
- [Ollama](https://github.com/ollama/ollama) model running locally or other OpenAI-compatible API endpoint
- A local LLM server: [Ollama](https://github.com/ollama/ollama), [LM Studio](https://lmstudio.ai/), [llama.cpp `llama-server`](https://github.com/ggml-org/llama.cpp), or any other OpenAI-compatible endpoint

## Usage

### Basic Usage

Generate a commit message for your staged changes:
Generate a commit message for your staged changes and commit with it after a confirmation:

```bash
ai-commit-message commit
Expand All @@ -43,18 +45,39 @@ This will:
2. Review your recent commit history for style consistency
3. Consider your current branch name
4. Generate an appropriate commit message
5. Ask for confirmation and create the commit (answering "no" keeps the printed message so you can edit or copy it)

### Message only

If you just want the suggested message without the confirmation prompt — e.g. to pipe it somewhere or review it yourself — use `--message-only`:

```bash
ai-commit-message commit --message-only
```

### Conventional Commits

Ask the model to follow the [Conventional Commits](https://www.conventionalcommits.org/) format (`feat:`, `fix:`, `chore:`, ...):

```bash
ai-commit-message commit --conventional
```

### Configuration

You can configure the gem using the built-in configuration command:
Configure the API URL and model interactively:

```bash
ai-commit-message config
```

This interactive prompt allows you to set:
- **API URL**: The endpoint for your AI model (defaults to `http://localhost:11434`)
- **Model name**: The AI model to use (defaults to `qwen2.5-coder:7b`, which I personally recommend)
If your server is reachable, the model prompt becomes a picker populated with the models available on that server.

List the models available on your server:

```bash
ai-commit-message models
```

### Command Line Options

Expand All @@ -64,44 +87,59 @@ Override configuration settings directly:
ai-commit-message commit --url=http://your-api-endpoint --model=your-model-name
```

All options:

| Option | Description | Default |
| --- | --- | --- |
| `--url` | Base URL of the OpenAI-compatible API | `http://localhost:11434` |
| `--model` | Model name | `qwen3:8b` |
| `--length` | Maximum message length in characters | `72` |
| `--conventional` | Use Conventional Commits format | off |
| `--message-only` | Print the message without the commit confirmation prompt | off |

## How It Works

The gem:
1. Collects your staged changes using `git diff --cached`
1. Collects your staged changes using `git diff --cached` (capped at 12,000 characters so large diffs don't overwhelm a local model's context)
2. Retrieves your recent commit history for context
3. Identifies your current branch name
4. Sends this information to the specified AI model
5. Returns a concise, contextual commit message (limited to 250 characters)
4. Sends a chat request to `{url}/v1/chat/completions` — the standard OpenAI-compatible endpoint
5. Cleans the model output (strips code fences, quotes and reasoning blocks) and prints a single-line commit message (limited to 72 characters by default)

The URL accepts base addresses with or without a trailing `/v1` and `/`, so `http://localhost:11434` (Ollama), `http://localhost:1234/v1` (LM Studio) and `http://localhost:8080` (llama.cpp `llama-server`) all work as-is.

## Configuration File

The gem stores your configuration in `~/.ai-commit-message.conf`. You can manually edit this file if needed. Example:

```bash
url=http://localhost:11434
model=qwen2.5-coder:7b
model=qwen3:8b
```

## Models

The default configuration uses Ollama with the `qwen2.5-coder:7b` model, but you can use any model available through your API endpoint.
The default configuration uses Ollama with the `qwen3:8b` model, but you can use any model available through your API endpoint.

### Recommended Models

- **qwen2.5-coder:7b**: Good balance of quality and speed (default)
- **llama3:8b**: Excellent for general commit messages
- **codellama:7b**: Specialized for code-related commits
- **qwen3:8b**: Good balance of quality and speed (default). Note that it is a thinking model, so expect a few seconds of reasoning before the message appears
- **qwen2.5-coder:7b**: Faster, non-thinking coding model
- **qwen3.5:4b**: Current-generation small model for modest hardware
- **gemma4:e4b**: Compact general-purpose option
- **llama3.1:8b**: Solid general-purpose choice

## Contributing

1. Fork the repository
2. Create your feature branch: `git checkout -b my-new-feature`
3. Install dependencies: `bundle install`
4. Make your changes and add tests if applicable
5. Commit your changes: `git commit -m 'Add some feature'`
6. Push to the branch: `git push origin my-new-feature`
7. Submit a pull request
4. Run the tests: `bundle exec rake test`
5. Make your changes and add tests if applicable
6. Commit your changes: `git commit -m 'Add some feature'`
7. Push to the branch: `git push origin my-new-feature`
8. Submit a pull request

## License

This gem is available as open source free to use/alter without restrictions.
This gem is available as open source under the terms of the [MIT License](LICENSE).
9 changes: 9 additions & 0 deletions Rakefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
require 'rake/testtask'

Rake::TestTask.new do |t|
t.libs << 'lib' << 'test'
t.test_files = FileList['test/**/*_test.rb']
t.warning = false
end

task default: :test
21 changes: 12 additions & 9 deletions ai-commit-message.gemspec
Original file line number Diff line number Diff line change
@@ -1,21 +1,24 @@
Gem::Specification.new do |gem|
gem.name = 'ai-commit-message'
gem.version = '0.1.2'
gem.version = '0.2.0'
gem.authors = ['Afonso Neto']
gem.email = ['afonso.pontesneto@gmail.com']

gem.summary = 'Git commit messages suggested by AI'
gem.description = 'Git commit messages suggested by AI'
gem.description = 'Git commit messages suggested by AI using a local LLM via any OpenAI-compatible API'
gem.homepage = 'https://github.com/AfonsoNeto/ai-commit-message'
gem.license = nil

gem.files = Dir.glob('{bin/*,lib/**/*,README.md}')
gem.license = 'MIT'

gem.metadata['rubygems_mfa_required'] = 'true'

gem.files = Dir.glob('{bin/*,lib/**/*,README.md,LICENSE}')
gem.require_paths = ['lib']
gem.executables = ['ai-commit-message']
gem.required_ruby_version = '>= 3.0'

gem.add_dependency 'thor'
gem.add_runtime_dependency 'tty-prompt'
gem.add_runtime_dependency 'net-http'
gem.add_runtime_dependency 'json'
end
gem.add_dependency 'tty-prompt'

gem.add_development_dependency 'rake'
gem.add_development_dependency 'minitest'
end
39 changes: 39 additions & 0 deletions docs/demo.cast
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
{"version": 3, "term": {"cols": 84, "rows": 13}, "timestamp": 1790494557, "command": "ai-commit-message"}
[0.8, "o", ""]
[0.84, "o", "\u001b[2m# ai-commit-message: AI commit messages from a local LLM\u001b[0m\r\n\r\n"]
[0.89, "o", "\u001b[1;32m$\u001b[0m "]
[0.94, "o", "ai-c"]
[0.99, "o", "ommi"]
[1.04, "o", "t-me"]
[1.09, "o", "ssag"]
[1.14, "o", "e co"]
[1.19, "o", "mmit"]
[1.24, "o", " --m"]
[1.29, "o", "essa"]
[1.34, "o", "ge-o"]
[1.46, "o", "nly"]
[2.36, "o", "\r\n"]
[2.96, "o", ""]
[3.0, "o", "docs: add type check and docstring to add function\r\n\r\n"]
[3.05, "o", "\u001b[1;32m$\u001b[0m "]
[3.1, "o", "ai-c"]
[3.15, "o", "ommi"]
[3.2, "o", "t-me"]
[3.25, "o", "ssag"]
[3.3, "o", "e co"]
[3.42, "o", "mmit"]
[4.32, "o", "\r\n"]
[4.72, "o", ""]
[5.22, "o", "docs: add type check and docstring to add function\r\n"]
[5.62, "o", "Commit with this message? (\u001b[1mY\u001b[0m/n) "]
[6.22, "o", "y\r\n"]
[6.26, "o", "\u001b[32m3f7159a docs: add docstring and type check for add function\u001b[0m\r\n\r\n"]
[6.31, "o", "\u001b[1;32m$\u001b[0m "]
[6.36, "o", "git "]
[6.41, "o", "log "]
[6.46, "o", "--on"]
[6.51, "o", "elin"]
[6.63, "o", "e -2"]
[7.03, "o", "\r\n"]
[7.53, "o", ""]
[7.53, "o", "\u001b[33m3f7159a docs: add docstring and type check for add function\r\nb445970 chore: initial setup\u001b[0m\r\n\r\n"]
Binary file added docs/demo.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading