ai_git_commit 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.rspec +3 -0
- data/.rubocop.yml +18 -0
- data/CHANGELOG.md +5 -0
- data/LICENSE.txt +21 -0
- data/README.md +42 -0
- data/Rakefile +12 -0
- data/exe/ai_git_commit +3 -0
- data/lib/ai_git_commit/cli.rb +68 -0
- data/lib/ai_git_commit/config.rb +38 -0
- data/lib/ai_git_commit/generator.rb +83 -0
- data/lib/ai_git_commit/version.rb +5 -0
- data/lib/ai_git_commit.rb +7 -0
- data/lib/templates/ai_git_commit.rb +25 -0
- data/sig/ai_git_commit.rbs +4 -0
- metadata +103 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: b14c761a09946ac7dccecc9a2b30c3926d4b8607de3cc030faefaf3c39341abf
|
4
|
+
data.tar.gz: 389310ce83fe9f84928c8fe6cfd25e89cd79f84b73825fadca71f3f39c800ba0
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 6ec4e82fd7e052996c45d1f9066cef2f7d3f0e54c8d8809bd0e89841026acc34e7b622cc6d11315aea333c61976da8a3f6f804634787ce03abeac58717ac6745
|
7
|
+
data.tar.gz: f24abb2d10dd6453c921d86de3b802de709e77cb01f66a51220db80d8d6c11ba86783cf4e2f1053f9d0e9cf7e4ad359c0615930b1c8e430635db77272d5d6b57
|
data/.rspec
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
AllCops:
|
2
|
+
TargetRubyVersion: 3.1
|
3
|
+
Exclude:
|
4
|
+
- 'exe/**/*'
|
5
|
+
|
6
|
+
Style/Documentation:
|
7
|
+
Enabled: false
|
8
|
+
|
9
|
+
Style/StringLiterals:
|
10
|
+
EnforcedStyle: double_quotes
|
11
|
+
|
12
|
+
Style/StringLiteralsInInterpolation:
|
13
|
+
EnforcedStyle: double_quotes
|
14
|
+
|
15
|
+
Metrics/BlockLength:
|
16
|
+
Exclude:
|
17
|
+
- 'spec/**/*'
|
18
|
+
- 'ai_git_commit.gemspec'
|
data/CHANGELOG.md
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2025 Mike Belyaev
|
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,42 @@
|
|
1
|
+
# AiGitCommit
|
2
|
+
|
3
|
+
AiGitCommit is a Ruby gem designed to help automate and enhance your git commit workflow using AI.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Install the gem and add to the application's Gemfile by executing:
|
8
|
+
|
9
|
+
```bash
|
10
|
+
bundle add ai_git_commit
|
11
|
+
```
|
12
|
+
|
13
|
+
If bundler is not being used to manage dependencies, install the gem by executing:
|
14
|
+
|
15
|
+
```bash
|
16
|
+
gem install ai_git_commit
|
17
|
+
```
|
18
|
+
|
19
|
+
then run to install it:
|
20
|
+
|
21
|
+
```bash
|
22
|
+
ai_git_commit install
|
23
|
+
```
|
24
|
+
|
25
|
+
## Usage
|
26
|
+
|
27
|
+
All you have to do is to have a `OPENAI_API_KEY` environment variable set with your OpenAI API key.
|
28
|
+
|
29
|
+
Then, just:
|
30
|
+
```bash
|
31
|
+
git add <files>
|
32
|
+
git commit
|
33
|
+
```
|
34
|
+
That's it! Your commit message will be generated by AI.
|
35
|
+
|
36
|
+
## Contributing
|
37
|
+
|
38
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/drkmen/ai_git_commit.
|
39
|
+
|
40
|
+
## License
|
41
|
+
|
42
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
data/exe/ai_git_commit
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "thor"
|
4
|
+
require "fileutils"
|
5
|
+
|
6
|
+
module AiGitCommit
|
7
|
+
# CLI class provides command-line interface for AI Git Commit tool.
|
8
|
+
# It uses Thor for command parsing and execution.
|
9
|
+
class CLI < Thor
|
10
|
+
HOOK_PATH = ".git/hooks/prepare-commit-msg"
|
11
|
+
|
12
|
+
# Installs the prepare-commit-msg hook.
|
13
|
+
# If the hook already exists, appends the script; otherwise, creates a new hook file.
|
14
|
+
# Makes the hook executable and prints a confirmation message.
|
15
|
+
desc "install", "Set up prepare-commit-msg hook"
|
16
|
+
def install
|
17
|
+
create_hook
|
18
|
+
copy_initializer
|
19
|
+
puts "AI Git Commit prepare-commit-msg hook set up."
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
# Creates or updates a Git hook file with a given script.
|
25
|
+
# If the hook file already exists, it appends the script to it.
|
26
|
+
# Otherwise, it creates a new file with the script content.
|
27
|
+
# Finally, it sets the file's permissions to be executable.
|
28
|
+
# @return [void]
|
29
|
+
def create_hook
|
30
|
+
if hook_file_exists?
|
31
|
+
File.open(HOOK_PATH, "a") { _1.puts script }
|
32
|
+
else
|
33
|
+
File.write(HOOK_PATH, script)
|
34
|
+
end
|
35
|
+
FileUtils.chmod("+x", HOOK_PATH)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Copies the ai_git_commit initializer template into the application's config directory.
|
39
|
+
# This ensures that:
|
40
|
+
# * The destination directory (config/initializers) exists
|
41
|
+
# * The initializer file (ai_git_commit.rb) is copied from the templates directory
|
42
|
+
# @return [void]
|
43
|
+
def copy_initializer
|
44
|
+
source = File.expand_path("../templates/ai_git_commit.rb", __dir__)
|
45
|
+
destination_dir = File.expand_path("config/initializers", Dir.pwd)
|
46
|
+
destination = File.join(destination_dir, "ai_git_commit.rb")
|
47
|
+
FileUtils.mkdir_p(destination_dir)
|
48
|
+
FileUtils.cp(source, destination)
|
49
|
+
end
|
50
|
+
|
51
|
+
# The script content for the prepare-commit-msg hook.
|
52
|
+
# Adds a shebang line if the hook file does not exist.
|
53
|
+
# The script runs a Ruby command to generate a commit message using AiGitCommit::Generator.
|
54
|
+
# @return [String] the script content
|
55
|
+
def script
|
56
|
+
<<~SCRIPT
|
57
|
+
#{"#!/bin/bash" unless hook_file_exists?}
|
58
|
+
ruby -r 'ai_git_commit' -e 'puts AiGitCommit::Generator.commit_message' >> .git/COMMIT_EDITMSG
|
59
|
+
SCRIPT
|
60
|
+
end
|
61
|
+
|
62
|
+
# Checks if the prepare-commit-msg hook file exists.
|
63
|
+
# @return [Boolean] true if the hook file exists, false otherwise.
|
64
|
+
def hook_file_exists?
|
65
|
+
File.exist?(HOOK_PATH)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module AiGitCommit
|
4
|
+
# Configuration class for AiGitCommit
|
5
|
+
class Config
|
6
|
+
attr_accessor :openai_api_key, :model, :program_language,
|
7
|
+
:max_tokens, :temperature, :system_role_message
|
8
|
+
|
9
|
+
# Initializes configuration with default values
|
10
|
+
def initialize
|
11
|
+
@openai_api_key = ENV["OPENAI_API_KEY"]
|
12
|
+
@model = "gpt-3.5-turbo"
|
13
|
+
@program_language = "Ruby"
|
14
|
+
@max_tokens = 300
|
15
|
+
@temperature = 0.7
|
16
|
+
@system_role_message = <<~MESSAGE
|
17
|
+
You are a senior #{@program_language} developer. Your task is to write a concise, clear commit
|
18
|
+
message and a detailed description based on the provided code changes. The message
|
19
|
+
should be in the imperative mood. The body should be wrapped at 72 characters.
|
20
|
+
MESSAGE
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# Module-level accessor for configuration
|
25
|
+
class << self
|
26
|
+
# Returns current configuration or initializes it
|
27
|
+
# @return [Config]
|
28
|
+
def config
|
29
|
+
@config ||= Config.new
|
30
|
+
end
|
31
|
+
|
32
|
+
# Yields the configuration for modification
|
33
|
+
# @yield [Config] config
|
34
|
+
def configure
|
35
|
+
yield(config)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "openai"
|
4
|
+
|
5
|
+
module AiGitCommit
|
6
|
+
# Generator class responsible for creating AI-generated git commit messages
|
7
|
+
class Generator
|
8
|
+
class << self
|
9
|
+
# Generates a commit message using OpenAI based on staged git diff
|
10
|
+
# @return [String] the generated commit message or an error message
|
11
|
+
def commit_message
|
12
|
+
return "# AI skipped: OPENAI_API_KEY is not set." unless openai_api_key
|
13
|
+
|
14
|
+
diff = staged_diff
|
15
|
+
return "# No staged changes found." if diff.strip.empty?
|
16
|
+
|
17
|
+
fetch_message(diff)
|
18
|
+
rescue StandardError => e
|
19
|
+
"# Error: #{e.message}"
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
# Sends the staged diff to OpenAI and returns the generated message
|
25
|
+
# @param diff [String] the staged git diff
|
26
|
+
# @return [String] the commit message from OpenAI
|
27
|
+
def fetch_message(diff)
|
28
|
+
response = openai.chat.completions.create(completion_payload(diff))
|
29
|
+
response.choices.first.message.content
|
30
|
+
end
|
31
|
+
|
32
|
+
# Builds the payload for the OpenAI API request
|
33
|
+
# @param diff [String] the staged git diff
|
34
|
+
# @return [Hash] the payload for OpenAI API
|
35
|
+
def completion_payload(diff)
|
36
|
+
{
|
37
|
+
model: config.model,
|
38
|
+
messages: [
|
39
|
+
{ role: "system", content: config.system_role_message },
|
40
|
+
{ role: "user", content: user_role_message(diff) }
|
41
|
+
],
|
42
|
+
max_tokens: config.max_tokens,
|
43
|
+
temperature: config.temperature
|
44
|
+
}
|
45
|
+
end
|
46
|
+
|
47
|
+
# Formatted message instructing the AI to create a Git commit message and description.
|
48
|
+
# @param diff [String] The diff content representing changes in the repository.
|
49
|
+
# @return [String] A formatted message ready to be sent to the AI.
|
50
|
+
def user_role_message(diff)
|
51
|
+
<<~CONTENT
|
52
|
+
Generate a git commit message and description for the following changes.
|
53
|
+
Do not include control chars. Keep description under 200-300 chars.\n
|
54
|
+
Changes:\n(#{diff})
|
55
|
+
CONTENT
|
56
|
+
end
|
57
|
+
|
58
|
+
# Returns an instance of OpenAI::Client
|
59
|
+
# @return [OpenAI::Client]
|
60
|
+
def openai
|
61
|
+
@openai ||= OpenAI::Client.new(api_key: openai_api_key)
|
62
|
+
end
|
63
|
+
|
64
|
+
# Returns the configuration for AiGitCommit
|
65
|
+
# @return [AiGitCommit::Config]
|
66
|
+
def config
|
67
|
+
@config ||= AiGitCommit.config
|
68
|
+
end
|
69
|
+
|
70
|
+
# Returns the OpenAI API key from configuration
|
71
|
+
# @return [String, nil] the API key or nil if not set
|
72
|
+
def openai_api_key
|
73
|
+
AiGitCommit.config.openai_api_key
|
74
|
+
end
|
75
|
+
|
76
|
+
# Gets the staged git diff
|
77
|
+
# @return [String] the output of `git diff --cached`
|
78
|
+
def staged_diff
|
79
|
+
`git diff --cached`
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
AiGitCommit.configure do |config|
|
4
|
+
# OpenAI API key
|
5
|
+
config.api_key = ENV["OPENAI_API_KEY"]
|
6
|
+
|
7
|
+
# Specifies the OpenAI model to use for generating commit messages.
|
8
|
+
config.model = "gpt-3.5-turbo"
|
9
|
+
|
10
|
+
# Defines the programming language context for commit message generation.
|
11
|
+
config.program_language = "Ruby"
|
12
|
+
|
13
|
+
# Limits the maximum number of tokens in the generated response.
|
14
|
+
config.max_tokens = 300
|
15
|
+
|
16
|
+
# Controls the randomness of the AI's output (0.0 = deterministic, 1.0 = creative).
|
17
|
+
config.temperature = 0.7
|
18
|
+
|
19
|
+
# Sets the system prompt for the AI
|
20
|
+
config.system_role_message = <<~MESSAGE
|
21
|
+
You are a senior #{config.program_language} developer. Your task is to write a concise, clear commit
|
22
|
+
message and a detailed description based on the provided code changes. The message
|
23
|
+
should be in the imperative mood. The body should be wrapped at 72 characters.
|
24
|
+
MESSAGE
|
25
|
+
end
|
metadata
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ai_git_commit
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Mike Belyaev
|
8
|
+
bindir: exe
|
9
|
+
cert_chain: []
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
11
|
+
dependencies:
|
12
|
+
- !ruby/object:Gem::Dependency
|
13
|
+
name: base64
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
15
|
+
requirements:
|
16
|
+
- - "~>"
|
17
|
+
- !ruby/object:Gem::Version
|
18
|
+
version: 0.1.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.1.0
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: openai
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
29
|
+
requirements:
|
30
|
+
- - "~>"
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 0.22.0
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - "~>"
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: 0.22.0
|
40
|
+
- !ruby/object:Gem::Dependency
|
41
|
+
name: thor
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - "~>"
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '1.4'
|
47
|
+
type: :runtime
|
48
|
+
prerelease: false
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - "~>"
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '1.4'
|
54
|
+
description: |
|
55
|
+
A gem that leverages OpenAI's API to automatically generate
|
56
|
+
Git commit messages based on staged changes.
|
57
|
+
email:
|
58
|
+
- pair.dro@gmail.com
|
59
|
+
executables:
|
60
|
+
- ai_git_commit
|
61
|
+
extensions: []
|
62
|
+
extra_rdoc_files: []
|
63
|
+
files:
|
64
|
+
- ".rspec"
|
65
|
+
- ".rubocop.yml"
|
66
|
+
- CHANGELOG.md
|
67
|
+
- LICENSE.txt
|
68
|
+
- README.md
|
69
|
+
- Rakefile
|
70
|
+
- exe/ai_git_commit
|
71
|
+
- lib/ai_git_commit.rb
|
72
|
+
- lib/ai_git_commit/cli.rb
|
73
|
+
- lib/ai_git_commit/config.rb
|
74
|
+
- lib/ai_git_commit/generator.rb
|
75
|
+
- lib/ai_git_commit/version.rb
|
76
|
+
- lib/templates/ai_git_commit.rb
|
77
|
+
- sig/ai_git_commit.rbs
|
78
|
+
homepage: https://github.com/drkmen/ai_git_commit
|
79
|
+
licenses:
|
80
|
+
- MIT
|
81
|
+
metadata:
|
82
|
+
allowed_push_host: https://rubygems.org
|
83
|
+
homepage_uri: https://github.com/drkmen/ai_git_commit
|
84
|
+
source_code_uri: https://github.com/drkmen/ai_git_commit
|
85
|
+
changelog_uri: https://github.com/drkmen/ai_git_commit/blob/main/CHANGELOG.md
|
86
|
+
rdoc_options: []
|
87
|
+
require_paths:
|
88
|
+
- lib
|
89
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
90
|
+
requirements:
|
91
|
+
- - ">="
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: 3.1.0
|
94
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
95
|
+
requirements:
|
96
|
+
- - ">="
|
97
|
+
- !ruby/object:Gem::Version
|
98
|
+
version: '0'
|
99
|
+
requirements: []
|
100
|
+
rubygems_version: 3.6.9
|
101
|
+
specification_version: 4
|
102
|
+
summary: AI Git Commit generates Git commit messages using OpenAI.
|
103
|
+
test_files: []
|