aigcm 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.envrc +4 -0
- data/CHANGELOG.md +11 -0
- data/LICENSE.txt +21 -0
- data/README.md +128 -0
- data/Rakefile +8 -0
- data/bin/aigcm +5 -0
- data/lib/aigcm/commit_message_generator.rb +113 -0
- data/lib/aigcm/git_diff.rb +44 -0
- data/lib/aigcm/style_guide.rb +50 -0
- data/lib/aigcm/version.rb +5 -0
- data/lib/aigcm.rb +146 -0
- data/sig/aicommit.rbs +4 -0
- metadata +136 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 1f0e2c18c6c1c820563cc9f640a319709b1cbb752fdee4464617759cd2e6f963
|
4
|
+
data.tar.gz: 3a74e5f33183449a690f5ae8892bd55d438cff57193eb90af1b358458f215c77
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: e203e403e07b9865c879d6bee4cedb9545fc460a8cbaa990d9257e00e49a7bf735ac5ee4bd403f2e7dd1ad4ee399734e194d49e55c723d7f184684cee8c5a548
|
7
|
+
data.tar.gz: 22ff6406f1aba1ab8defeac098bc1dab5b0aece00c04801c3aa4b888399a23db974ed89b0387e373a5f05e77a0a99b7834c1da8ffb95c240f926429330d6b1ba
|
data/.envrc
ADDED
data/CHANGELOG.md
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2025 Dewayne VanHoozer
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,128 @@
|
|
1
|
+
# aigcm
|
2
|
+
|
3
|
+
The **AI git commit message** generator.
|
4
|
+
ƒ
|
5
|
+
## Overview
|
6
|
+
|
7
|
+
**aigcm** is a Ruby gem designed to generate high-quality commit messages for git diffs. It leverages AI to analyze changes in your codebase and create concise, meaningful commit messages following best practices.
|
8
|
+
|
9
|
+
## Features
|
10
|
+
|
11
|
+
- **Automatic Commit Message Generation**: Automatically generate commit messages based on code diffs.
|
12
|
+
- **Security-Aware Provider Selection**: Detects execution in a private repository and ensures non-local providers are not used for security reasons. This means that if you are working within a private repository, the gem will default to local providers unless explicitly forced otherwise.
|
13
|
+
- **Configurable Style Guide**: Allows using a specific style guide for commit messages, either from a default location or specified by the user.
|
14
|
+
- **AI Model Integration**: Integration with various AI models for enhanced message generation.
|
15
|
+
|
16
|
+
## Installation
|
17
|
+
|
18
|
+
Add this line to your application's Gemfile:
|
19
|
+
|
20
|
+
```ruby
|
21
|
+
gem 'aigcm'
|
22
|
+
```
|
23
|
+
|
24
|
+
And then execute:
|
25
|
+
|
26
|
+
```shell
|
27
|
+
bundle install
|
28
|
+
```
|
29
|
+
|
30
|
+
Or install it yourself as:
|
31
|
+
|
32
|
+
```shell
|
33
|
+
gem install aigcm
|
34
|
+
```
|
35
|
+
|
36
|
+
## Usage
|
37
|
+
|
38
|
+
To generate a commit message:
|
39
|
+
|
40
|
+
```shell
|
41
|
+
aigcm [options] [ref]
|
42
|
+
```
|
43
|
+
|
44
|
+
### Options
|
45
|
+
|
46
|
+
- `-a, --amend`: Amend the last commit.
|
47
|
+
- `-c, --context=CONTEXT`: Extra context beyond the diff.
|
48
|
+
- `-d, --dry`: Dry run the command without making any changes.
|
49
|
+
- `-m, --model=MODEL`: Specify the AI model to use.
|
50
|
+
- `--provider=PROVIDER`: Specify the provider (ollama, openai, anthropic, etc). Note: This only needs to be used when the specified model is available from multiple providers; otherwise, the owner of the model is used by default.
|
51
|
+
- `--force-external`: Force using external AI provider even for private repos.
|
52
|
+
- `-s, --style=STYLE`: Path to the style guide file. If not provided, the system looks for `COMMITS.md` in the repo root or uses the default style guide.
|
53
|
+
- `--default`: Print the default style guide and exit the application.
|
54
|
+
- `--version`: Show version.
|
55
|
+
|
56
|
+
### Examples
|
57
|
+
|
58
|
+
If your commit involves refactoring a function to improve its performance, you might provide context like:
|
59
|
+
```shell
|
60
|
+
aigcm -m MODEL -c "Refactored to improve performance by using algorithm X"
|
61
|
+
```
|
62
|
+
|
63
|
+
This context helps the AI craft a more informative commit message.
|
64
|
+
|
65
|
+
When your commit is related to a specific JIRA ticket:
|
66
|
+
```shell
|
67
|
+
aigcm -m MODEL -c "Resolved issues as per JIRA ticket JIRA-1234"
|
68
|
+
```
|
69
|
+
|
70
|
+
Including the JIRA ticket helps relate the commit to external tracking systems.
|
71
|
+
|
72
|
+
Including multiple context strings:
|
73
|
+
```shell
|
74
|
+
aigcm -m MODEL -c "Refactored for performance" -c "JIRA-1234"
|
75
|
+
```
|
76
|
+
|
77
|
+
Multiple context strings can be added by repeating the `-c` option.
|
78
|
+
|
79
|
+
Using environment variables in context:
|
80
|
+
```shell
|
81
|
+
aigcm -c "Put the work ticket as the first entry on the subject line" -c "Ticket: $TICKET"
|
82
|
+
```
|
83
|
+
|
84
|
+
This allows you to dynamically include environment variables in your commit message.
|
85
|
+
|
86
|
+
### Style Guide Example
|
87
|
+
|
88
|
+
The style guide is used as part of the generative AI prompt that instructs the large language model (LLM) how to craft its summary of the `git diff` results. The see the default style guide use the `--default` option.
|
89
|
+
|
90
|
+
You can create your own style guide named `COMMITS.md` in the root directory of your repository. You can also use the `--style` option to point `aigcm` to your style guide if you choose to keep it in a different place. This is handy when you want to have consistent commit messages across several different projects.
|
91
|
+
|
92
|
+
This would be a simple style guide:
|
93
|
+
|
94
|
+
```
|
95
|
+
- Use conventional commits format (type: description)
|
96
|
+
- Keep first line under 72 characters
|
97
|
+
- Use present tense ("add" not "added")
|
98
|
+
- Be descriptive but concise
|
99
|
+
- Have fun. Be creative. Add ASCII art if you feel like it.
|
100
|
+
```
|
101
|
+
|
102
|
+
## Last Thoughts
|
103
|
+
|
104
|
+
This gem saves its commit message in the file `.aigcm_msg` at the root directory of the repository. Its there even if you do a `--dry` run. This could be handy if you want to incorporate `aigcm` into some larger workflow.
|
105
|
+
|
106
|
+
Remember that the style guide can be extended using one or more `--context` strings. For example you could create a shell alias like this:
|
107
|
+
|
108
|
+
```
|
109
|
+
alias gc='aigcm -c "JIRA $JIRA_TICKET"'
|
110
|
+
```
|
111
|
+
|
112
|
+
## Development
|
113
|
+
|
114
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests.
|
115
|
+
|
116
|
+
To install this gem onto your local machine, run `bundle exec rake install`.
|
117
|
+
|
118
|
+
## Contributing
|
119
|
+
|
120
|
+
1. Fork it (<https://github.com/your_username/aigcm/fork>)
|
121
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
122
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
123
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
124
|
+
5. Create a new Pull Request
|
125
|
+
|
126
|
+
## License
|
127
|
+
|
128
|
+
The gem is available as open-source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
data/bin/aigcm
ADDED
@@ -0,0 +1,113 @@
|
|
1
|
+
|
2
|
+
require "net/http"
|
3
|
+
require "json"
|
4
|
+
require "open3"
|
5
|
+
require "ai_client"
|
6
|
+
|
7
|
+
module Aigcm
|
8
|
+
class CommitMessageGenerator
|
9
|
+
class Error < StandardError; end
|
10
|
+
|
11
|
+
MAX_DIFF_SIZE = 4000 # Characters
|
12
|
+
|
13
|
+
def initialize(model:, provider:, max_tokens:, force_external:)
|
14
|
+
@force_external = force_external
|
15
|
+
validate_model_provider_combination(model)
|
16
|
+
check_provider_availability
|
17
|
+
|
18
|
+
@client = AiClient.new(model, provider: provider)
|
19
|
+
rescue StandardError => e
|
20
|
+
raise Error, "Failed to initialize AI client: #{e.message}"
|
21
|
+
end
|
22
|
+
|
23
|
+
def generate(diff, style_guide, context = [])
|
24
|
+
return "No changes to commit" if diff.strip.empty?
|
25
|
+
|
26
|
+
check_repository_privacy unless @force_external
|
27
|
+
|
28
|
+
# Truncate diff if too large
|
29
|
+
if diff.length > MAX_DIFF_SIZE
|
30
|
+
diff = diff[0...MAX_DIFF_SIZE] + "\n...[diff truncated]"
|
31
|
+
end
|
32
|
+
|
33
|
+
processed_context = process_context(context)
|
34
|
+
prompt = build_prompt(diff, style_guide, processed_context)
|
35
|
+
|
36
|
+
begin
|
37
|
+
response = @client.chat(prompt)
|
38
|
+
response.to_s.strip
|
39
|
+
rescue StandardError => e
|
40
|
+
"Error generating commit message: #{e.message}"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def validate_model_provider_combination(model)
|
47
|
+
client = AiClient.new(model)
|
48
|
+
@provider = client.provider
|
49
|
+
rescue ArgumentError => e
|
50
|
+
raise Error, "Invalid model/provider combination: #{e.message}"
|
51
|
+
end
|
52
|
+
|
53
|
+
def check_provider_availability
|
54
|
+
case @provider
|
55
|
+
when :ollama
|
56
|
+
check_ollama_running
|
57
|
+
when :localai
|
58
|
+
check_localai_running
|
59
|
+
end
|
60
|
+
rescue StandardError => e
|
61
|
+
raise Error, "Provider not available: #{e.message}"
|
62
|
+
end
|
63
|
+
|
64
|
+
def check_ollama_running
|
65
|
+
Net::HTTP.get(URI("http://localhost:11434/api/version"))
|
66
|
+
rescue StandardError
|
67
|
+
raise Error, "Ollama is not running. Please start ollama first."
|
68
|
+
end
|
69
|
+
|
70
|
+
def check_localai_running
|
71
|
+
Net::HTTP.get(URI("http://localhost:8080/v1/models"))
|
72
|
+
rescue StandardError
|
73
|
+
raise Error, "LocalAI is not running. Please start localai first."
|
74
|
+
end
|
75
|
+
|
76
|
+
def process_context(context_array)
|
77
|
+
context_array.map do |ctx|
|
78
|
+
if ctx.start_with?("@")
|
79
|
+
filename = ctx[1..]
|
80
|
+
File.read(filename) rescue "Could not read #{filename}"
|
81
|
+
else
|
82
|
+
ctx
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def check_repository_privacy
|
88
|
+
return if @provider == :ollama # Local provider is always safe
|
89
|
+
|
90
|
+
stdout, _, status = Open3.capture3("gh repo view --json isPrivate -q '.isPrivate'")
|
91
|
+
|
92
|
+
if status.success? && stdout.strip == "true"
|
93
|
+
raise Error, "This is a private repository. Use a local model (ollama) or run with --force-external flag"
|
94
|
+
end
|
95
|
+
rescue Errno::ENOENT
|
96
|
+
puts "Warning: Unable to check repository privacy status (gh command not found)"
|
97
|
+
end
|
98
|
+
|
99
|
+
def build_prompt(diff, style_guide, context)
|
100
|
+
<<~PROMPT
|
101
|
+
Generate a commit message for the git diff that follows these instructions.
|
102
|
+
Do not wrap your response in a code block.
|
103
|
+
Follow these style guidelines when constructing your response:
|
104
|
+
#{style_guide}
|
105
|
+
|
106
|
+
#{context.join("\n") unless context.empty?}
|
107
|
+
|
108
|
+
Git diff:
|
109
|
+
#{diff}
|
110
|
+
PROMPT
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require "open3"
|
2
|
+
|
3
|
+
module Aigcm
|
4
|
+
class GitDiff
|
5
|
+
class Error < StandardError; end
|
6
|
+
|
7
|
+
def initialize(dir:, commit_hash: nil, amend: false)
|
8
|
+
@dir = dir
|
9
|
+
@commit_hash = commit_hash
|
10
|
+
@amend = amend
|
11
|
+
validate_git_repo
|
12
|
+
end
|
13
|
+
|
14
|
+
def generate_diff
|
15
|
+
Dir.chdir(@dir) do
|
16
|
+
cmd = if @amend
|
17
|
+
"git diff --cached HEAD^ 2>/dev/null || git diff --cached"
|
18
|
+
elsif @commit_hash
|
19
|
+
"git diff #{@commit_hash}^..#{@commit_hash}"
|
20
|
+
else
|
21
|
+
"git diff --cached"
|
22
|
+
end
|
23
|
+
|
24
|
+
stdout, _, status = Open3.capture3(cmd)
|
25
|
+
|
26
|
+
raise Error, "Git command failed" unless status.success?
|
27
|
+
raise Error, "No changes detected" if stdout.strip.empty?
|
28
|
+
|
29
|
+
stdout
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def validate_git_repo
|
36
|
+
Dir.chdir(@dir) do
|
37
|
+
_, _, status = Open3.capture3("git rev-parse --git-dir")
|
38
|
+
raise Error, "Not a git repository" unless status.success?
|
39
|
+
end
|
40
|
+
rescue Errno::ENOENT
|
41
|
+
raise Error, "Directory not found: #{@dir}"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'erb'
|
2
|
+
|
3
|
+
module Aigcm
|
4
|
+
class StyleGuide
|
5
|
+
LINE_MAX = 72
|
6
|
+
DEFAULT_GUIDE = ERB.new(
|
7
|
+
File.read(__FILE__)
|
8
|
+
.split("__END__")
|
9
|
+
.last
|
10
|
+
.strip
|
11
|
+
).result
|
12
|
+
|
13
|
+
def self.load(dir, custom_path = nil)
|
14
|
+
# If a custom path is provided, use it
|
15
|
+
if custom_path
|
16
|
+
return load_from_file(custom_path)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Check for COMMITS.md in the repository root
|
20
|
+
config_file = File.join(dir, "COMMITS.md")
|
21
|
+
return load_from_file(config_file) if File.exist?(config_file)
|
22
|
+
|
23
|
+
# Fallback to the default style guide
|
24
|
+
DEFAULT_GUIDE
|
25
|
+
rescue StandardError => e
|
26
|
+
puts "Warning: Error reading style guide: #{e.message}"
|
27
|
+
DEFAULT_GUIDE
|
28
|
+
end
|
29
|
+
|
30
|
+
private_class_method def self.load_from_file(path)
|
31
|
+
File.read(path)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
__END__
|
37
|
+
|
38
|
+
1. Craft a Clear Subject Line:
|
39
|
+
• Summarize Concisely: Begin with a brief summary (<%= Aigcm::StyleGuide::LINE_MAX %> characters max).
|
40
|
+
• Capitalize the Subject: Start the subject line with a capital letter.
|
41
|
+
• Omit Periods in Subject Line: Avoid ending with a period to save space.
|
42
|
+
• Use Imperative Mood: Phrase commands as direct actions (e.g., "Add feature" instead of "Added feature").
|
43
|
+
|
44
|
+
2. Provide a Detailed Body:
|
45
|
+
• Seperate the body from the subject line with a blank line.
|
46
|
+
• Explain the Reason: Clearly articulate the rationale for the change rather than just summarizing the modification.
|
47
|
+
• Wrap Body Text at <%= Aigcm::StyleGuide::LINE_MAX %> Characters: Ensure that the body text wraps at <%= Aigcm::StyleGuide::LINE_MAX %> characters per line.
|
48
|
+
|
49
|
+
3. Reference Issues/Tickets:
|
50
|
+
• Include relevant issue numbers, ticket IDs and/or references when they are provided. Dp not invent your own reference. Use what has been provided.
|
data/lib/aigcm.rb
ADDED
@@ -0,0 +1,146 @@
|
|
1
|
+
# lib/aigcm.rb
|
2
|
+
|
3
|
+
require 'debug_me'
|
4
|
+
include DebugMe
|
5
|
+
|
6
|
+
require 'optparse'
|
7
|
+
require 'ai_client'
|
8
|
+
require 'time'
|
9
|
+
|
10
|
+
require_relative 'aigcm/version'
|
11
|
+
require_relative 'aigcm/git_diff'
|
12
|
+
require_relative 'aigcm/commit_message_generator'
|
13
|
+
require_relative 'aigcm/style_guide'
|
14
|
+
|
15
|
+
module Aigcm
|
16
|
+
COMMIT_MESSAGE_FILE = '.aigcm_msg'
|
17
|
+
RECENT_THRESHOLD = 60 # seconds (1 minute)
|
18
|
+
|
19
|
+
class Error < StandardError; end
|
20
|
+
|
21
|
+
def self.run(test_mode: false)
|
22
|
+
dir = Dir.pwd
|
23
|
+
options = parse_options
|
24
|
+
|
25
|
+
if options[:amend]
|
26
|
+
system('git commit --amend')
|
27
|
+
return
|
28
|
+
end
|
29
|
+
|
30
|
+
commit_message = check_recent_commit(dir)
|
31
|
+
|
32
|
+
# Generate a new commit message if not reusing an existing one
|
33
|
+
commit_message ||= generate_commit_message(dir, options)
|
34
|
+
|
35
|
+
perform_commit(dir, commit_message, options)
|
36
|
+
|
37
|
+
rescue OptionParser::InvalidOption => e
|
38
|
+
STDERR.puts "Error: '#{e.message}'"
|
39
|
+
exit 1
|
40
|
+
rescue GitDiff::Error => e
|
41
|
+
puts "Git error: #{e.message}"
|
42
|
+
exit 1
|
43
|
+
rescue StandardError => e
|
44
|
+
puts "Error: #{e.message}"
|
45
|
+
exit 1
|
46
|
+
end
|
47
|
+
|
48
|
+
private_class_method def self.parse_options
|
49
|
+
options = {
|
50
|
+
amend: false,
|
51
|
+
context: [],
|
52
|
+
dry: false,
|
53
|
+
model: 'gpt-4o-mini',
|
54
|
+
provider: nil,
|
55
|
+
force_external: false,
|
56
|
+
style: nil
|
57
|
+
}
|
58
|
+
|
59
|
+
OptionParser.new do |opts|
|
60
|
+
opts.banner = "Usage: aigcm [options] [ref]"
|
61
|
+
|
62
|
+
opts.on("-a", "--amend", "Amend the last commit") { options[:amend] = true }
|
63
|
+
|
64
|
+
opts.on("-cCONTEXT", "--context=CONTEXT", "Extra context beyond the diff") do |context|
|
65
|
+
options[:context] << context
|
66
|
+
end
|
67
|
+
|
68
|
+
opts.on("-d", "--dry", "Dry run the command") { options[:dry] = true }
|
69
|
+
|
70
|
+
opts.on("-mMODEL", "--model=MODEL", "The model to use") { |model| options[:model] = model }
|
71
|
+
|
72
|
+
opts.on("--provider=PROVIDER", "Specify the provider (ollama, openai, anthropic, etc)") do |provider|
|
73
|
+
provider = provider.to_sym
|
74
|
+
unless [:ollama, :openai, :anthropic, :google, :mistral].include?(provider)
|
75
|
+
puts "Invalid provider specified. Valid providers are: ollama, openai, anthropic, google, mistral"
|
76
|
+
exit 1
|
77
|
+
end
|
78
|
+
options[:provider] = provider
|
79
|
+
end
|
80
|
+
|
81
|
+
opts.on("--force-external", "Force using external AI provider even for private repos") {
|
82
|
+
options[:force_external] = true
|
83
|
+
}
|
84
|
+
|
85
|
+
opts.on("-sSTYLE", "--style=STYLE", "Path to the style guide file") { |style| options[:style] = style }
|
86
|
+
|
87
|
+
opts.on("--default", "Print the default style guide and exit") do
|
88
|
+
puts "\nDefault Style Guide:"
|
89
|
+
puts "-------------------"
|
90
|
+
puts StyleGuide::DEFAULT_GUIDE
|
91
|
+
exit
|
92
|
+
end
|
93
|
+
|
94
|
+
opts.on("--version", "Show version") { puts Aigcm::VERSION; exit }
|
95
|
+
|
96
|
+
end.parse!
|
97
|
+
|
98
|
+
options
|
99
|
+
end
|
100
|
+
|
101
|
+
private_class_method def self.check_recent_commit(dir)
|
102
|
+
commit_file_path = File.join(dir, COMMIT_MESSAGE_FILE)
|
103
|
+
|
104
|
+
if File.exist?(commit_file_path)
|
105
|
+
file_mod_time = File.mtime(commit_file_path)
|
106
|
+
current_time = Time.now
|
107
|
+
if (current_time - file_mod_time).to_i < RECENT_THRESHOLD
|
108
|
+
return File.read(commit_file_path)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
nil
|
113
|
+
end
|
114
|
+
|
115
|
+
private_class_method def self.generate_commit_message(dir, options)
|
116
|
+
diff_generator = GitDiff.new(dir: dir, commit_hash: ARGV.shift, amend: options[:amend])
|
117
|
+
diff = diff_generator.generate_diff
|
118
|
+
|
119
|
+
style_guide = StyleGuide.load(dir, options[:style])
|
120
|
+
generator = CommitMessageGenerator.new(
|
121
|
+
model: options[:model],
|
122
|
+
provider: options[:provider],
|
123
|
+
max_tokens: 1000,
|
124
|
+
force_external: options[:force_external]
|
125
|
+
)
|
126
|
+
|
127
|
+
commit_message = generator.generate(diff, style_guide, options[:context])
|
128
|
+
File.write(File.join(dir, COMMIT_MESSAGE_FILE), commit_message)
|
129
|
+
commit_message
|
130
|
+
end
|
131
|
+
|
132
|
+
private_class_method def self.perform_commit(dir, commit_message, options)
|
133
|
+
commit_file_path = File.join(dir, COMMIT_MESSAGE_FILE)
|
134
|
+
|
135
|
+
if options[:dry]
|
136
|
+
puts "\nDry run - would generate commit message:"
|
137
|
+
puts "-"*StyleGuide::LINE_MAX
|
138
|
+
puts commit_message
|
139
|
+
puts "-"*StyleGuide::LINE_MAX
|
140
|
+
puts
|
141
|
+
else
|
142
|
+
File.write(commit_file_path, commit_message)
|
143
|
+
system("git commit --edit -F #{commit_file_path}")
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
data/sig/aicommit.rbs
ADDED
metadata
ADDED
@@ -0,0 +1,136 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: aigcm
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Dewayne VanHoozer
|
8
|
+
bindir: bin
|
9
|
+
cert_chain: []
|
10
|
+
date: 2025-02-09 00:00:00.000000000 Z
|
11
|
+
dependencies:
|
12
|
+
- !ruby/object:Gem::Dependency
|
13
|
+
name: ai_client
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
15
|
+
requirements:
|
16
|
+
- - "~>"
|
17
|
+
- !ruby/object:Gem::Version
|
18
|
+
version: 0.4.0
|
19
|
+
type: :runtime
|
20
|
+
prerelease: false
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
22
|
+
requirements:
|
23
|
+
- - "~>"
|
24
|
+
- !ruby/object:Gem::Version
|
25
|
+
version: 0.4.0
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: minitest
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
29
|
+
requirements:
|
30
|
+
- - "~>"
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '5.16'
|
33
|
+
type: :development
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - "~>"
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '5.16'
|
40
|
+
- !ruby/object:Gem::Dependency
|
41
|
+
name: mocha
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '0'
|
47
|
+
type: :development
|
48
|
+
prerelease: false
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
- !ruby/object:Gem::Dependency
|
55
|
+
name: rake
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - "~>"
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '13.0'
|
61
|
+
type: :development
|
62
|
+
prerelease: false
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - "~>"
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '13.0'
|
68
|
+
- !ruby/object:Gem::Dependency
|
69
|
+
name: rubocop
|
70
|
+
requirement: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - "~>"
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '1.21'
|
75
|
+
type: :development
|
76
|
+
prerelease: false
|
77
|
+
version_requirements: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - "~>"
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '1.21'
|
82
|
+
description: |
|
83
|
+
`aigcm` generates meaningful git commit messages using artificial
|
84
|
+
intelligence. It supports multiple AI providers including OpenAI,
|
85
|
+
Anthropic, Google, and local models via Ollama. The gem
|
86
|
+
automatically detects private repositories and defaults to using
|
87
|
+
local models for security. It integrates seamlessly with git
|
88
|
+
workflows and supports various commit scenarios including amending
|
89
|
+
commits and handling staged changes. The gem follows conventional
|
90
|
+
commit message formats and allows customization of commit message
|
91
|
+
styles through configuration.
|
92
|
+
email:
|
93
|
+
- dvanhoozer@gmail.com
|
94
|
+
executables:
|
95
|
+
- aigcm
|
96
|
+
extensions: []
|
97
|
+
extra_rdoc_files: []
|
98
|
+
files:
|
99
|
+
- ".envrc"
|
100
|
+
- CHANGELOG.md
|
101
|
+
- LICENSE.txt
|
102
|
+
- README.md
|
103
|
+
- Rakefile
|
104
|
+
- bin/aigcm
|
105
|
+
- lib/aigcm.rb
|
106
|
+
- lib/aigcm/commit_message_generator.rb
|
107
|
+
- lib/aigcm/git_diff.rb
|
108
|
+
- lib/aigcm/style_guide.rb
|
109
|
+
- lib/aigcm/version.rb
|
110
|
+
- sig/aicommit.rbs
|
111
|
+
homepage: https://github.com/MadBomber/aigcm
|
112
|
+
licenses:
|
113
|
+
- MIT
|
114
|
+
metadata:
|
115
|
+
allowed_push_host: https://rubygems.org
|
116
|
+
homepage_uri: https://github.com/MadBomber/aigcm
|
117
|
+
source_code_uri: https://github.com/MadBomber/aigcm
|
118
|
+
changelog_uri: https://github.com/MadBomber/aigcm/blob/main/CHANGELOG.md
|
119
|
+
rdoc_options: []
|
120
|
+
require_paths:
|
121
|
+
- lib
|
122
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
123
|
+
requirements:
|
124
|
+
- - ">="
|
125
|
+
- !ruby/object:Gem::Version
|
126
|
+
version: 3.1.0
|
127
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
requirements: []
|
133
|
+
rubygems_version: 3.6.3
|
134
|
+
specification_version: 4
|
135
|
+
summary: AI-powered git commit message generator
|
136
|
+
test_files: []
|