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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a73fe2965f7f3fedb44a5173fcd2dfb888c909c3ea275bed72ae1474f6bfe7cc
4
- data.tar.gz: 22388d33280bd6c90a4febff1964eab35b558befc777465374b420567d080e35
3
+ metadata.gz: 550cd13670cec1d792df268d54b8c511ca528bb498377a9789bb265acb8a55a5
4
+ data.tar.gz: 6cbf6bb2c24376cca893409b6af23879225e61c02111c2cba656eac361b60da9
5
5
  SHA512:
6
- metadata.gz: 5d0af4dacb622f9c893bafadf5bafe45a48fdd7664a29c804e122ae9cddafea13d7d8a61a0345215cffe6552e15ba4e8e6d0888515a12527b7e79f7305a3e238
7
- data.tar.gz: bc785e338d404f12f8d6e5b94659ae4aa8dc9301507722ec78b73f7f85ea7824e2e8ce718563fa5a1cf69c1c56d62e0ad8cb8f66646418b56865ebc9a061be4a
6
+ metadata.gz: e835710e43929f73e491ec963c37e37763f0d620e5711d746db4ea066110e012edbeef6ff8d2dd849e0dcd443a131dfcb31654aeb2438f7386e2d4568ec80fba
7
+ data.tar.gz: 34c20bf93a7fe6553b74706fb32b7be50c875471a535d1abebaffd768dd14ddfb235dc58887b5028f500e65a560486aa8309e87c93e4424a3ee86c76fd04b876
data/Gemfile CHANGED
@@ -5,6 +5,9 @@ source "https://rubygems.org"
5
5
  # Specify your gem's dependencies in commitgpt.gemspec
6
6
  gemspec
7
7
 
8
+ gem "base64"
9
+ gem "bigdecimal"
10
+ gem "csv"
8
11
  gem "httparty"
9
12
 
10
13
  gem "rake", "~> 13.0"
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- commitgpt (0.1.2)
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
- ### API key
17
- grab your [OpenAI key](https://openai.com/api/) and add it as an env variable.
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
- $ export OPENAI_API_KEY=sk-xxxxxxxxxxxxxxxx
44
+ $ aicm -m
45
+ llama3.1-8b
46
+ llama-3.3-70b
47
+ gpt-4o-mini
20
48
  ```
21
- It's recommended to add the first line to your `.zshrc` or `.bashrc` so it persists instead of having to define it in each terminal session.
22
-
49
+
23
50
  ### aicm
24
- `aicm` is an abbreviation for `AI commits`, after `git add .` add your file to stage, then use `aicm` to commit with an AI generated commit message.
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
- ## Special Thanks
41
- I used chatGPT to convert `AICommits`(JS) to `CommitGPT`(Ruby). Thanks to [https://github.com/Nutlope/aicommits](https://github.com/Nutlope/aicommits)
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
- ## How it works
44
- This CLI tool runs a git diff command to grab all the latest changes, sends this to OpenAI's GPT-3, then returns the AI generated commit message. I also want to note that it does cost money since GPT-3 generations aren't free. However, OpenAI gives folks $18 of free credits and commit message generations are cheap so it should be free for a long time.
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 of up to 200 lines of code for now
48
- The generated commit message can't be edited yet, but you can choose `n` and copy the commit command and edit it manually.
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
- ## Contributing
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
- CommitGpt::CommitAi.new.aicm
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
@@ -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
- OPENAI_API_KEY = ENV.fetch("OPENAI_API_KEY", nil)
13
- def aicm
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(prompt)
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
- # Accounting for GPT-3's input req of 4k tokens (approx 8k chars)
55
- if diff.length > 8000
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 OPENAI_API_KEY.nil?
67
- puts "▲ Please save your OpenAI API key as an env variable by doing 'export OPENAI_API_KEY=YOUR_API_KEY'".red
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(prompt = "")
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: "text-davinci-003", prompt: prompt, temperature: 0.7, top_p: 1,
84
- frequency_penalty: 0, presence_penalty: 0, max_tokens: 200, stream: false, n: 1
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
- response = HTTParty.post("https://api.openai.com/v1/completions",
89
- headers: { "Authorization" => "Bearer #{OPENAI_API_KEY}",
90
- "Content-Type" => "application/json", "User-Agent" => "Ruby/#{RUBY_VERSION}" },
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
- puts "#{response.inspect}\n"
94
-
95
- ai_commit = response["choices"][0]["text"]
96
- rescue StandardError
97
- puts "▲ There was an error with the OpenAI API. Please try again later.".red
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
@@ -17,4 +17,8 @@ class String
17
17
  def magenta
18
18
  "\e[35m#{self}\e[0m"
19
19
  end
20
+
21
+ def cyan
22
+ "\e[36m#{self}\e[0m"
23
+ end
20
24
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CommitGpt
4
- VERSION = "0.1.2"
4
+ VERSION = "0.2.0"
5
5
  end
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.1.2
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: 2023-02-15 00:00:00.000000000 Z
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.1.6
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: []