commitgpt 0.1.2 → 0.2.0
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 +4 -4
- data/Gemfile +3 -0
- data/Gemfile.lock +8 -1
- data/README.md +95 -15
- data/lib/commitgpt/cli.rb +7 -1
- data/lib/commitgpt/commit_ai.rb +80 -24
- data/lib/commitgpt/string.rb +4 -0
- data/lib/commitgpt/version.rb +1 -1
- metadata +3 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 550cd13670cec1d792df268d54b8c511ca528bb498377a9789bb265acb8a55a5
|
|
4
|
+
data.tar.gz: 6cbf6bb2c24376cca893409b6af23879225e61c02111c2cba656eac361b60da9
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e835710e43929f73e491ec963c37e37763f0d620e5711d746db4ea066110e012edbeef6ff8d2dd849e0dcd443a131dfcb31654aeb2438f7386e2d4568ec80fba
|
|
7
|
+
data.tar.gz: 34c20bf93a7fe6553b74706fb32b7be50c875471a535d1abebaffd768dd14ddfb235dc58887b5028f500e65a560486aa8309e87c93e4424a3ee86c76fd04b876
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
commitgpt (0.
|
|
4
|
+
commitgpt (0.2.0)
|
|
5
5
|
httparty (~> 0.18)
|
|
6
6
|
thor (~> 1.2)
|
|
7
7
|
|
|
@@ -9,6 +9,9 @@ GEM
|
|
|
9
9
|
remote: https://rubygems.org/
|
|
10
10
|
specs:
|
|
11
11
|
ast (2.4.2)
|
|
12
|
+
base64 (0.3.0)
|
|
13
|
+
bigdecimal (4.0.1)
|
|
14
|
+
csv (3.3.5)
|
|
12
15
|
diff-lcs (1.5.0)
|
|
13
16
|
httparty (0.21.0)
|
|
14
17
|
mini_mime (>= 1.0.0)
|
|
@@ -52,10 +55,14 @@ GEM
|
|
|
52
55
|
|
|
53
56
|
PLATFORMS
|
|
54
57
|
arm64-darwin-21
|
|
58
|
+
arm64-darwin-25
|
|
55
59
|
x86_64-darwin-22
|
|
56
60
|
|
|
57
61
|
DEPENDENCIES
|
|
62
|
+
base64
|
|
63
|
+
bigdecimal
|
|
58
64
|
commitgpt!
|
|
65
|
+
csv
|
|
59
66
|
httparty
|
|
60
67
|
rake (~> 13.0)
|
|
61
68
|
rspec (~> 3.0)
|
data/README.md
CHANGED
|
@@ -13,15 +13,42 @@ $ gem install commitgpt
|
|
|
13
13
|
```
|
|
14
14
|
|
|
15
15
|
## Usage
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
|
|
17
|
+
### API Key
|
|
18
|
+
Grab your API key and add it as an env variable.
|
|
19
|
+
```bash
|
|
20
|
+
$ export AICM_KEY=sk-xxxxxxxxxxxxxxxx
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
It's recommended to add this to your `.zshrc` or `.bashrc` so it persists across terminal sessions.
|
|
24
|
+
|
|
25
|
+
### Custom API Endpoint (Optional)
|
|
26
|
+
You can use any OpenAI-compatible API provider by setting `AICM_LINK`:
|
|
27
|
+
```bash
|
|
28
|
+
# Use a local proxy
|
|
29
|
+
$ export AICM_LINK=http://127.0.0.1:8045/v1
|
|
30
|
+
|
|
31
|
+
# Or use another provider
|
|
32
|
+
# Cerebras
|
|
33
|
+
$ export AICM_LINK=https://api.cerebras.ai/v1
|
|
34
|
+
|
|
35
|
+
# Groq
|
|
36
|
+
$ export AICM_LINK=https://api.groq.com/openai/v1
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
> **Note**: If you're using a local proxy that doesn't require authentication, you can leave `AICM_KEY` empty.
|
|
40
|
+
|
|
41
|
+
### List Models
|
|
42
|
+
Use `-m` to list all available models from your API provider:
|
|
18
43
|
```bash
|
|
19
|
-
$
|
|
44
|
+
$ aicm -m
|
|
45
|
+
llama3.1-8b
|
|
46
|
+
llama-3.3-70b
|
|
47
|
+
gpt-4o-mini
|
|
20
48
|
```
|
|
21
|
-
|
|
22
|
-
|
|
49
|
+
|
|
23
50
|
### aicm
|
|
24
|
-
`aicm` is an abbreviation for `AI commits
|
|
51
|
+
`aicm` is an abbreviation for `AI commits`. After staging your changes with `git add .`, use `aicm` to commit with an AI-generated message.
|
|
25
52
|
```bash
|
|
26
53
|
$ cd /path/to/your/repo
|
|
27
54
|
$ git add .
|
|
@@ -37,24 +64,77 @@ $ aicm
|
|
|
37
64
|
4 files changed, 24 insertions(+), 19 deletions(-)
|
|
38
65
|
```
|
|
39
66
|
|
|
40
|
-
|
|
41
|
-
|
|
67
|
+
### Update
|
|
68
|
+
To update to the latest version:
|
|
69
|
+
```bash
|
|
70
|
+
$ gem update commitgpt
|
|
71
|
+
$ gem cleanup commitgpt
|
|
72
|
+
$ gem info commitgpt
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Configuration
|
|
76
|
+
|
|
77
|
+
| Environment Variable | Required | Default | Description |
|
|
78
|
+
|---------------------|----------|---------|-------------|
|
|
79
|
+
| `AICM_KEY` | No* | `nil` | Your API key. Required when using official OpenAI API. |
|
|
80
|
+
| `AICM_LINK` | No | `https://api.openai.com/v1` | Custom API endpoint for OpenAI-compatible services. |
|
|
81
|
+
| `AICM_MODEL` | Yes | `gpt-4o-mini` | Model to use for generating commit messages. |
|
|
82
|
+
| `AICM_DIFF_LEN` | No | `32768` | Maximum diff length in characters. Increase if you have large diffs. |
|
|
83
|
+
|
|
84
|
+
\* Required when using the default OpenAI endpoint.
|
|
42
85
|
|
|
43
|
-
|
|
44
|
-
|
|
86
|
+
### Available Models
|
|
87
|
+
|
|
88
|
+
Use `aicm -m` to list models from your provider, or set `AICM_MODEL` directly:
|
|
89
|
+
|
|
90
|
+
**OpenAI** ([https://api.openai.com/v1](https://platform.openai.com))
|
|
91
|
+
```
|
|
92
|
+
gpt-5.2
|
|
93
|
+
gpt-5-mini
|
|
94
|
+
gpt-5-nano
|
|
95
|
+
gpt-4o-mini
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
**Cerebras** ([https://api.cerebras.ai/v1](https://cloud.cerebras.ai))
|
|
99
|
+
```
|
|
100
|
+
zai-glm-4.6
|
|
101
|
+
zai-glm-4.7
|
|
102
|
+
gpt-oss-120b
|
|
103
|
+
llama3.1-8b
|
|
104
|
+
llama-3.3-70b
|
|
105
|
+
qwen-3-32b
|
|
106
|
+
qwen-3-235b-a22b-instruct-2507
|
|
107
|
+
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
**Groq** ([https://api.groq.com/openai/v1](https://console.groq.com))
|
|
111
|
+
```
|
|
112
|
+
llama-3.3-70b-versatile
|
|
113
|
+
llama-3.1-8b-instant
|
|
114
|
+
meta-llama/llama-4-maverick-17b-128e-instruct
|
|
115
|
+
meta-llama/llama-4-scout-17b-16e-instruct
|
|
116
|
+
qwen/qwen3-32b
|
|
117
|
+
moonshotai/kimi-k2-instruct-0905
|
|
118
|
+
openai/gpt-oss-120b
|
|
119
|
+
groq/compound
|
|
120
|
+
groq/compound-mini
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## How It Works
|
|
124
|
+
This CLI tool runs a `git diff` command to grab all staged changes, sends this to OpenAI's GPT API (or compatible endpoint), and returns an AI-generated commit message. The tool uses the `/v1/chat/completions` endpoint with optimized prompts for generating conventional commit messages.
|
|
45
125
|
|
|
46
126
|
## Limitations
|
|
47
|
-
Only supports git diffs
|
|
48
|
-
The generated commit message can't be edited
|
|
127
|
+
- Only supports git diffs up to `AICM_DIFF_LEN` characters (default 32K)
|
|
128
|
+
- The generated commit message can't be edited interactively, but you can choose `n` and copy the command to edit manually
|
|
49
129
|
|
|
50
|
-
##
|
|
130
|
+
## Special Thanks
|
|
131
|
+
I used ChatGPT to convert `AICommits` from TypeScript to Ruby. Special thanks to [https://github.com/Nutlope/aicommits](https://github.com/Nutlope/aicommits)
|
|
51
132
|
|
|
133
|
+
## Contributing
|
|
52
134
|
Bug reports and pull requests are welcome on GitHub at https://github.com/ZPVIP/commitgpt. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/ZPVIP/commitgpt/blob/main/CODE_OF_CONDUCT.md).
|
|
53
135
|
|
|
54
136
|
## License
|
|
55
|
-
|
|
56
137
|
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
|
57
138
|
|
|
58
139
|
## Code of Conduct
|
|
59
|
-
|
|
60
140
|
Everyone interacting in the CommitGpt project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/ZPVIP/commitgpt/blob/master/CODE_OF_CONDUCT.md).
|
data/lib/commitgpt/cli.rb
CHANGED
|
@@ -9,8 +9,14 @@ module CommitGpt
|
|
|
9
9
|
default_task :aicm
|
|
10
10
|
|
|
11
11
|
desc "aicm", "AI commits for you!"
|
|
12
|
+
method_option :models, aliases: "-m", type: :boolean, desc: "List available models"
|
|
13
|
+
method_option :verbose, aliases: "-v", type: :boolean, desc: "Show git diff being sent to AI"
|
|
12
14
|
def aicm
|
|
13
|
-
|
|
15
|
+
if options[:models]
|
|
16
|
+
CommitGpt::CommitAi.new.list_models
|
|
17
|
+
else
|
|
18
|
+
CommitGpt::CommitAi.new.aicm(verbose: options[:verbose])
|
|
19
|
+
end
|
|
14
20
|
end
|
|
15
21
|
end
|
|
16
22
|
end
|
data/lib/commitgpt/commit_ai.rb
CHANGED
|
@@ -9,14 +9,39 @@ require_relative "string"
|
|
|
9
9
|
module CommitGpt
|
|
10
10
|
# Commit AI roboter based on GPT-3
|
|
11
11
|
class CommitAi
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
AICM_KEY = ENV.fetch("AICM_KEY", nil)
|
|
13
|
+
AICM_LINK = ENV.fetch("AICM_LINK", "https://api.openai.com/v1")
|
|
14
|
+
AICM_DIFF_LEN = ENV.fetch("AICM_DIFF_LEN", "32768").to_i
|
|
15
|
+
AICM_MODEL = ENV.fetch("AICM_MODEL", "gpt-4o-mini")
|
|
16
|
+
|
|
17
|
+
def aicm(verbose: false)
|
|
14
18
|
exit(1) unless welcome
|
|
15
19
|
diff = git_diff || exit(1)
|
|
20
|
+
if verbose
|
|
21
|
+
puts "▲ Git diff (#{diff.length} chars):".cyan
|
|
22
|
+
puts diff
|
|
23
|
+
puts "\n"
|
|
24
|
+
end
|
|
16
25
|
ai_commit_message = message(diff) || exit(1)
|
|
17
26
|
puts `git commit -m "#{ai_commit_message}" && echo && echo && git log -1 && echo` if confirmed
|
|
18
27
|
end
|
|
19
28
|
|
|
29
|
+
def list_models
|
|
30
|
+
headers = {
|
|
31
|
+
"Content-Type" => "application/json",
|
|
32
|
+
"User-Agent" => "Ruby/#{RUBY_VERSION}"
|
|
33
|
+
}
|
|
34
|
+
headers["Authorization"] = "Bearer #{AICM_KEY}" if AICM_KEY
|
|
35
|
+
|
|
36
|
+
begin
|
|
37
|
+
response = HTTParty.get("#{AICM_LINK}/models", headers: headers)
|
|
38
|
+
models = response["data"] || []
|
|
39
|
+
models.each { |m| puts m["id"] }
|
|
40
|
+
rescue StandardError => e
|
|
41
|
+
puts "▲ Failed to list models: #{e.message}".red
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
20
45
|
private
|
|
21
46
|
|
|
22
47
|
def confirmed
|
|
@@ -31,12 +56,8 @@ module CommitGpt
|
|
|
31
56
|
end
|
|
32
57
|
|
|
33
58
|
def message(diff = nil)
|
|
34
|
-
prompt = "I want you to act like a git commit message writer. I will input a git diff and your job is to convert it into a useful " \
|
|
35
|
-
"commit message. Do not preface the commit with anything, use the present tense, return a complete sentence, " \
|
|
36
|
-
"and do not repeat yourself: #{diff}"
|
|
37
|
-
|
|
38
59
|
puts "▲ Generating your AI commit message...\n".gray
|
|
39
|
-
ai_commit_message = generate_commit(
|
|
60
|
+
ai_commit_message = generate_commit(diff)
|
|
40
61
|
return nil if ai_commit_message.nil?
|
|
41
62
|
|
|
42
63
|
puts "#{"▲ Commit message: ".green}git commit -am \"#{ai_commit_message}\"\n\n"
|
|
@@ -51,9 +72,8 @@ module CommitGpt
|
|
|
51
72
|
return nil
|
|
52
73
|
end
|
|
53
74
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
puts "▲ The diff is too large to write a commit message.".red
|
|
75
|
+
if diff.length > AICM_DIFF_LEN
|
|
76
|
+
puts "▲ The diff is too large (#{diff.length} chars, max #{AICM_DIFF_LEN}). Set AICM_DIFF_LEN to increase limit.".red
|
|
57
77
|
return nil
|
|
58
78
|
end
|
|
59
79
|
|
|
@@ -63,8 +83,8 @@ module CommitGpt
|
|
|
63
83
|
def welcome
|
|
64
84
|
puts "\n▲ Welcome to AI Commits!".green
|
|
65
85
|
|
|
66
|
-
if
|
|
67
|
-
puts "▲ Please save your
|
|
86
|
+
if AICM_KEY.nil? && AICM_LINK == "https://api.openai.com/v1"
|
|
87
|
+
puts "▲ Please save your API key as an env variable by doing 'export AICM_KEY=YOUR_API_KEY'".red
|
|
68
88
|
return false
|
|
69
89
|
end
|
|
70
90
|
|
|
@@ -78,27 +98,63 @@ module CommitGpt
|
|
|
78
98
|
true
|
|
79
99
|
end
|
|
80
100
|
|
|
81
|
-
def generate_commit(
|
|
101
|
+
def generate_commit(diff = "")
|
|
102
|
+
messages = [
|
|
103
|
+
{
|
|
104
|
+
role: "system",
|
|
105
|
+
content: "Generate a concise git commit message title in present tense that precisely describes the key changes in the following code diff. Focus on what was changed, not just file names. Provide only the title, no description or body. " \
|
|
106
|
+
"Message language: English. Rules:\n" \
|
|
107
|
+
"- Commit message must be a maximum of 100 characters.\n" \
|
|
108
|
+
"- Exclude anything unnecessary such as translation. Your entire response will be passed directly into git commit.\n" \
|
|
109
|
+
"- IMPORTANT: Do not include any explanations, introductions, or additional text. Do not wrap the commit message in quotes or any other formatting. The commit message must not exceed 100 characters. Respond with ONLY the commit message text. \n" \
|
|
110
|
+
"- Be specific: include concrete details (package names, versions, functionality) rather than generic statements. \n" \
|
|
111
|
+
"- Return ONLY the commit message, nothing else."
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
role: "user",
|
|
115
|
+
content: "Generate a commit message for the following git diff:\n\n#{diff}"
|
|
116
|
+
}
|
|
117
|
+
]
|
|
118
|
+
|
|
82
119
|
payload = {
|
|
83
|
-
model:
|
|
84
|
-
|
|
120
|
+
model: AICM_MODEL,
|
|
121
|
+
messages: messages,
|
|
122
|
+
temperature: 0.7,
|
|
123
|
+
max_tokens: 300,
|
|
124
|
+
disable_reasoning: true
|
|
85
125
|
}
|
|
86
126
|
|
|
87
127
|
begin
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
128
|
+
headers = {
|
|
129
|
+
"Content-Type" => "application/json",
|
|
130
|
+
"User-Agent" => "Ruby/#{RUBY_VERSION}"
|
|
131
|
+
}
|
|
132
|
+
headers["Authorization"] = "Bearer #{AICM_KEY}" if AICM_KEY
|
|
133
|
+
|
|
134
|
+
response = HTTParty.post("#{AICM_LINK}/chat/completions",
|
|
135
|
+
headers: headers,
|
|
91
136
|
body: payload.to_json)
|
|
92
137
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
138
|
+
# Check for API error response
|
|
139
|
+
if response["error"]
|
|
140
|
+
puts "▲ API Error: #{response['error']['message']}".red
|
|
141
|
+
return nil
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
message = response.dig("choices", 0, "message")
|
|
145
|
+
# Some models (like zai-glm) use 'reasoning' instead of 'content'
|
|
146
|
+
ai_commit = message&.dig("content") || message&.dig("reasoning")
|
|
147
|
+
if ai_commit.nil?
|
|
148
|
+
puts "▲ Unexpected API response format:".red
|
|
149
|
+
puts response.inspect
|
|
150
|
+
return nil
|
|
151
|
+
end
|
|
152
|
+
rescue StandardError => e
|
|
153
|
+
puts "▲ Error: #{e.message}".red
|
|
98
154
|
return nil
|
|
99
155
|
end
|
|
100
156
|
|
|
101
|
-
ai_commit.gsub(/(\r\n|\n|\r)/, "")
|
|
157
|
+
ai_commit.gsub(/(\r\n|\n|\r)/, "").gsub(/\A["']|["']\z/, "")
|
|
102
158
|
end
|
|
103
159
|
end
|
|
104
160
|
end
|
data/lib/commitgpt/string.rb
CHANGED
data/lib/commitgpt/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: commitgpt
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Peng Zhang
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: bin
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
13
12
|
- !ruby/object:Gem::Dependency
|
|
14
13
|
name: httparty
|
|
@@ -70,7 +69,6 @@ metadata:
|
|
|
70
69
|
homepage_uri: https://github.com/ZPVIP/commitgpt
|
|
71
70
|
source_code_uri: https://github.com/ZPVIP/commitgpt
|
|
72
71
|
changelog_uri: https://github.com/ZPVIP/commitgpt/blob/master/CHANGELOG.md
|
|
73
|
-
post_install_message:
|
|
74
72
|
rdoc_options: []
|
|
75
73
|
require_paths:
|
|
76
74
|
- lib
|
|
@@ -85,8 +83,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
85
83
|
- !ruby/object:Gem::Version
|
|
86
84
|
version: '0'
|
|
87
85
|
requirements: []
|
|
88
|
-
rubygems_version: 3.
|
|
89
|
-
signing_key:
|
|
86
|
+
rubygems_version: 3.6.9
|
|
90
87
|
specification_version: 4
|
|
91
88
|
summary: A CLI AI that writes git commit messages for you.
|
|
92
89
|
test_files: []
|