commitgpt 0.3.1 → 0.3.4

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.
@@ -1,11 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "tty-prompt"
4
- require "httparty"
5
- require "timeout"
6
- require_relative "config_manager"
7
- require_relative "provider_presets"
8
- require_relative "string"
3
+ require 'tty-prompt'
4
+ require 'httparty'
5
+ require 'timeout'
6
+ require_relative 'config_manager'
7
+ require_relative 'provider_presets'
8
+ require_relative 'string'
9
9
 
10
10
  module CommitGpt
11
11
  # Interactive setup wizard for configuring AI providers
@@ -26,34 +26,34 @@ module CommitGpt
26
26
  # Switch to a different configured provider
27
27
  def switch_provider
28
28
  configured = ConfigManager.configured_providers
29
-
29
+
30
30
  if configured.empty?
31
31
  puts "▲ No providers configured. Please run 'aicm setup' first.".red
32
32
  return
33
33
  end
34
34
 
35
35
  choices = configured.map do |p|
36
- preset = PROVIDER_PRESETS.find { |pr| pr[:value] == p["name"] }
37
- { name: preset ? preset[:label] : p["name"], value: p["name"] }
36
+ preset = PROVIDER_PRESETS.find { |pr| pr[:value] == p['name'] }
37
+ { name: preset ? preset[:label] : p['name'], value: p['name'] }
38
38
  end
39
39
 
40
- selected = @prompt.select("Choose your provider:", choices)
41
-
40
+ selected = @prompt.select('Choose your provider:', choices)
41
+
42
42
  # Get current config for this provider
43
43
  config = ConfigManager.load_config
44
- provider = config["providers"].find { |p| p["name"] == selected }
44
+ provider = config['providers'].find { |p| p['name'] == selected }
45
45
 
46
46
  # Fetch models and let user select
47
- models = fetch_models_with_timeout(provider["base_url"], provider["api_key"])
47
+ models = fetch_models_with_timeout(provider['base_url'], provider['api_key'])
48
48
  return if models.nil?
49
49
 
50
- model = select_model(models, provider["model"])
51
-
50
+ model = select_model(models, provider['model'])
51
+
52
52
  # Prompt for diff length
53
- diff_len = prompt_diff_len(provider["diff_len"] || 32768)
54
-
53
+ diff_len = prompt_diff_len(provider['diff_len'] || 32_768)
54
+
55
55
  # Update config
56
- ConfigManager.update_provider(selected, { "model" => model, "diff_len" => diff_len })
56
+ ConfigManager.update_provider(selected, { 'model' => model, 'diff_len' => diff_len })
57
57
  reset_provider_inference_params(selected)
58
58
  ConfigManager.set_active_provider(selected)
59
59
 
@@ -67,22 +67,22 @@ module CommitGpt
67
67
  # Change model for the active provider
68
68
  def change_model
69
69
  provider_config = ConfigManager.get_active_provider_config
70
-
71
- if provider_config.nil? || provider_config["api_key"].nil? || provider_config["api_key"].empty?
70
+
71
+ if provider_config.nil? || provider_config['api_key'].nil? || provider_config['api_key'].empty?
72
72
  puts "▲ No active provider configured. Please run 'aicm setup'.".red
73
73
  return
74
74
  end
75
75
 
76
76
  # Fetch models and let user select
77
- models = fetch_models_with_timeout(provider_config["base_url"], provider_config["api_key"])
77
+ models = fetch_models_with_timeout(provider_config['base_url'], provider_config['api_key'])
78
78
  return if models.nil?
79
79
 
80
- model = select_model(models, provider_config["model"])
81
-
80
+ model = select_model(models, provider_config['model'])
81
+
82
82
  # Update config
83
- ConfigManager.update_provider(provider_config["name"], { "model" => model })
84
- reset_provider_inference_params(provider_config["name"])
85
-
83
+ ConfigManager.update_provider(provider_config['name'], { 'model' => model })
84
+ reset_provider_inference_params(provider_config['name'])
85
+
86
86
  puts "\nModel selected: #{model}".green
87
87
  end
88
88
 
@@ -91,29 +91,29 @@ module CommitGpt
91
91
  # Select provider from list
92
92
  def select_provider
93
93
  config = ConfigManager.load_config
94
- configured = config ? (config["providers"] || []) : []
94
+ configured = config ? (config['providers'] || []) : []
95
95
 
96
96
  choices = PROVIDER_PRESETS.map do |preset|
97
97
  # Check if this provider already has an API key
98
- provider_config = configured.find { |p| p["name"] == preset[:value] }
99
- has_key = provider_config && provider_config["api_key"] && !provider_config["api_key"].empty?
100
-
98
+ provider_config = configured.find { |p| p['name'] == preset[:value] }
99
+ has_key = provider_config && provider_config['api_key'] && !provider_config['api_key'].empty?
100
+
101
101
  label = preset[:label]
102
102
  label = "✅ #{label}" if has_key
103
- label = "#{label} (recommended)" if preset[:value] == "cerebras"
103
+ label = "#{label} (recommended)" if preset[:value] == 'cerebras'
104
104
  label = "#{label} (local)" if %w[ollama llamacpp lmstudio llamafile].include?(preset[:value])
105
105
 
106
106
  { name: label, value: preset[:value] }
107
107
  end
108
108
 
109
- choices << { name: "Custom (OpenAI-compatible)", value: "custom" }
109
+ choices << { name: 'Custom (OpenAI-compatible)', value: 'custom' }
110
110
 
111
- @prompt.select("Choose your AI provider:", choices, per_page: 15)
111
+ @prompt.select('Choose your AI provider:', choices, per_page: 15)
112
112
  end
113
113
 
114
114
  # Configure selected provider
115
115
  def configure_provider(provider_name)
116
- if provider_name == "custom"
116
+ if provider_name == 'custom'
117
117
  configure_custom_provider
118
118
  return
119
119
  end
@@ -124,10 +124,10 @@ module CommitGpt
124
124
 
125
125
  # Get existing API key if any
126
126
  config = ConfigManager.load_config
127
- existing_provider = config["providers"].find { |p| p["name"] == provider_name } if config
127
+ existing_provider = config['providers'].find { |p| p['name'] == provider_name } if config
128
128
 
129
129
  # Prompt for API key
130
- api_key = prompt_api_key(provider_label, existing_provider&.dig("api_key"))
130
+ api_key = prompt_api_key(provider_label, existing_provider&.dig('api_key'))
131
131
  return if api_key.nil? # User cancelled
132
132
 
133
133
  # Fetch models with timeout
@@ -135,16 +135,16 @@ module CommitGpt
135
135
  return if models.nil?
136
136
 
137
137
  # Let user select model
138
- model = select_model(models, existing_provider&.dig("model"))
138
+ model = select_model(models, existing_provider&.dig('model'))
139
139
 
140
140
  # Prompt for diff length
141
- diff_len = prompt_diff_len(existing_provider&.dig("diff_len") || 32768)
141
+ diff_len = prompt_diff_len(existing_provider&.dig('diff_len') || 32_768)
142
142
 
143
143
  # Save configuration
144
144
  ConfigManager.update_provider(
145
145
  provider_name,
146
- { "model" => model, "diff_len" => diff_len },
147
- { "api_key" => api_key }
146
+ { 'model' => model, 'diff_len' => diff_len },
147
+ { 'api_key' => api_key }
148
148
  )
149
149
  ConfigManager.set_active_provider(provider_name)
150
150
 
@@ -154,53 +154,56 @@ module CommitGpt
154
154
 
155
155
  # Configure custom provider
156
156
  def configure_custom_provider
157
- provider_name = @prompt.ask("Enter provider name:") do |q|
157
+ provider_name = @prompt.ask('Enter provider name:') do |q|
158
158
  q.required true
159
159
  q.modify :strip, :down
160
160
  end
161
161
 
162
- base_url = @prompt.ask("Enter base URL:") do |q|
162
+ base_url = @prompt.ask('Enter base URL:') do |q|
163
163
  q.required true
164
- q.default "http://localhost:8080/v1"
164
+ q.default 'http://localhost:8080/v1'
165
165
  end
166
166
 
167
- api_key = @prompt.mask("Enter your API key (optional):") { |q| q.echo false }
167
+ api_key = @prompt.mask('Enter your API key (optional):') { |q| q.echo false }
168
168
 
169
169
  # Fetch models
170
170
  models = fetch_models_with_timeout(base_url, api_key)
171
171
  return if models.nil?
172
172
 
173
173
  model = select_model(models)
174
- diff_len = prompt_diff_len(32768)
174
+ diff_len = prompt_diff_len(32_768)
175
175
 
176
176
  # Add to presets dynamically (just for this session)
177
177
  # Save to config
178
- config = ConfigManager.load_config || { "providers" => [], "active_provider" => "" }
179
-
178
+ config = ConfigManager.load_config || { 'providers' => [], 'active_provider' => '' }
179
+
180
180
  # Add or update provider in main config
181
- existing = config["providers"].find { |p| p["name"] == provider_name }
181
+ existing = config['providers'].find { |p| p['name'] == provider_name }
182
182
  if existing
183
- existing.merge!({ "model" => model, "diff_len" => diff_len, "base_url" => base_url })
183
+ existing.merge!({ 'model' => model, 'diff_len' => diff_len, 'base_url' => base_url })
184
184
  else
185
- config["providers"] << {
186
- "name" => provider_name,
187
- "model" => model,
188
- "diff_len" => diff_len,
189
- "base_url" => base_url
185
+ config['providers'] << {
186
+ 'name' => provider_name,
187
+ 'model' => model,
188
+ 'diff_len' => diff_len,
189
+ 'base_url' => base_url
190
190
  }
191
191
  end
192
- config["active_provider"] = provider_name
192
+ config['active_provider'] = provider_name
193
193
  ConfigManager.save_main_config(config)
194
194
 
195
195
  # Update local config
196
- local_config = File.exist?(ConfigManager.local_config_path) ?
197
- YAML.load_file(ConfigManager.local_config_path) : { "providers" => [] }
198
-
199
- local_existing = local_config["providers"].find { |p| p["name"] == provider_name }
196
+ local_config = if File.exist?(ConfigManager.local_config_path)
197
+ YAML.load_file(ConfigManager.local_config_path)
198
+ else
199
+ { 'providers' => [] }
200
+ end
201
+
202
+ local_existing = local_config['providers'].find { |p| p['name'] == provider_name }
200
203
  if local_existing
201
- local_existing["api_key"] = api_key
204
+ local_existing['api_key'] = api_key
202
205
  else
203
- local_config["providers"] << { "name" => provider_name, "api_key" => api_key }
206
+ local_config['providers'] << { 'name' => provider_name, 'api_key' => api_key }
204
207
  end
205
208
  ConfigManager.save_local_config(local_config)
206
209
 
@@ -209,15 +212,15 @@ module CommitGpt
209
212
  end
210
213
 
211
214
  # Prompt for API key
212
- def prompt_api_key(provider_name, existing_key)
215
+ def prompt_api_key(_provider_name, existing_key)
213
216
  message = if existing_key && !existing_key.empty?
214
- "Enter your API key (press Enter to keep existing):"
217
+ 'Enter your API key (press Enter to keep existing):'
215
218
  else
216
- "Enter your API key:"
219
+ 'Enter your API key:'
217
220
  end
218
221
 
219
222
  key = @prompt.mask(message) { |q| q.echo false }
220
-
223
+
221
224
  # If user pressed Enter and there's an existing key, use it
222
225
  if key.empty? && existing_key && !existing_key.empty?
223
226
  existing_key
@@ -228,29 +231,29 @@ module CommitGpt
228
231
 
229
232
  # Fetch models from provider with timeout
230
233
  def fetch_models_with_timeout(base_url, api_key)
231
- puts "Fetching available models...".gray
234
+ puts 'Fetching available models...'.gray
232
235
 
233
236
  models = nil
234
237
  begin
235
238
  Timeout.timeout(5) do
236
239
  headers = {
237
- "Content-Type" => "application/json",
238
- "User-Agent" => "Ruby/#{RUBY_VERSION}"
240
+ 'Content-Type' => 'application/json',
241
+ 'User-Agent' => "Ruby/#{RUBY_VERSION}"
239
242
  }
240
- headers["Authorization"] = "Bearer #{api_key}" if api_key && !api_key.empty?
243
+ headers['Authorization'] = "Bearer #{api_key}" if api_key && !api_key.empty?
241
244
 
242
245
  response = HTTParty.get("#{base_url}/models", headers: headers)
243
-
246
+
244
247
  if response.code == 200
245
- models = response["data"] || []
246
- models = models.map { |m| m["id"] }.compact.sort
248
+ models = response['data'] || []
249
+ models = models.map { |m| m['id'] }.compact.sort
247
250
  else
248
251
  puts "▲ Failed to fetch models: HTTP #{response.code}".red
249
252
  return nil
250
253
  end
251
254
  end
252
255
  rescue Timeout::Error
253
- puts "▲ Connection timeout (5s). Please check your network, base_url, and api_key.".red
256
+ puts '▲ Connection timeout (5s). Please check your network, base_url, and api_key.'.red
254
257
  exit(0)
255
258
  rescue StandardError => e
256
259
  puts "▲ Error fetching models: #{e.message}".red
@@ -258,7 +261,7 @@ module CommitGpt
258
261
  end
259
262
 
260
263
  if models.nil? || models.empty?
261
- puts "▲ No models found. Please check your configuration.".red
264
+ puts '▲ No models found. Please check your configuration.'.red
262
265
  exit(0)
263
266
  end
264
267
 
@@ -268,7 +271,7 @@ module CommitGpt
268
271
  # Let user select a model
269
272
  def select_model(models, current_model = nil)
270
273
  choices = models.map { |m| { name: m, value: m } }
271
- choices << { name: "Custom model name...", value: :custom }
274
+ choices << { name: 'Custom model name...', value: :custom }
272
275
 
273
276
  # Set default to current model if it exists
274
277
  default_index = if current_model && models.include?(current_model)
@@ -277,10 +280,10 @@ module CommitGpt
277
280
  1
278
281
  end
279
282
 
280
- selected = @prompt.select("Choose your model:", choices, per_page: 15, default: default_index)
283
+ selected = @prompt.select('Choose your model:', choices, per_page: 15, default: default_index)
281
284
 
282
285
  if selected == :custom
283
- @prompt.ask("Enter custom model name:") do |q|
286
+ @prompt.ask('Enter custom model name:') do |q|
284
287
  q.required true
285
288
  q.modify :strip
286
289
  end
@@ -290,8 +293,8 @@ module CommitGpt
290
293
  end
291
294
 
292
295
  # Prompt for diff length
293
- def prompt_diff_len(default = 32768)
294
- answer = @prompt.ask("Set the maximum diff length (Bytes) for generating commit message:") do |q|
296
+ def prompt_diff_len(default = 32_768)
297
+ answer = @prompt.ask('Set the maximum diff length (Bytes) for generating commit message:') do |q|
295
298
  q.default default.to_s
296
299
  q.convert :int
297
300
  end
@@ -301,12 +304,12 @@ module CommitGpt
301
304
 
302
305
  def reset_provider_inference_params(provider_name)
303
306
  config = YAML.load_file(ConfigManager.main_config_path)
304
- return unless config && config["providers"]
305
-
306
- provider = config["providers"].find { |p| p["name"] == provider_name }
307
+ return unless config && config['providers']
308
+
309
+ provider = config['providers'].find { |p| p['name'] == provider_name }
307
310
  if provider
308
- provider.delete("can_disable_reasoning")
309
- provider.delete("max_tokens")
311
+ provider.delete('can_disable_reasoning')
312
+ provider.delete('max_tokens')
310
313
  ConfigManager.save_main_config(config)
311
314
  end
312
315
  end
@@ -1,5 +1,24 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'tty-prompt'
4
+
5
+ # Monkey patch TTY::Prompt::List to add padding at the bottom of menus
6
+ module TTY
7
+ class Prompt
8
+ # Monkey patch to add padding
9
+ class List
10
+ alias original_render_menu render_menu
11
+
12
+ # Override render_menu to add empty lines at the bottom
13
+ def render_menu
14
+ output = original_render_menu
15
+ # Add 2 empty lines at the bottom so menu doesn't stick to terminal edge
16
+ "#{output}\n\n"
17
+ end
18
+ end
19
+ end
20
+ end
21
+
3
22
  # Open String to add color
4
23
  class String
5
24
  def red
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CommitGpt
4
- VERSION = "0.3.1"
4
+ VERSION = '0.3.4'
5
5
  end
data/lib/commitgpt.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "commitgpt/version"
4
- require_relative "commitgpt/commit_ai"
3
+ require_relative 'commitgpt/version'
4
+ require_relative 'commitgpt/commit_ai'
5
5
 
6
6
  module CommitGpt
7
7
  class Error < StandardError; end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: commitgpt
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 0.3.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Peng Zhang
@@ -15,28 +15,28 @@ dependencies:
15
15
  requirements:
16
16
  - - "~>"
17
17
  - !ruby/object:Gem::Version
18
- version: '0.18'
18
+ version: '0.24'
19
19
  type: :runtime
20
20
  prerelease: false
21
21
  version_requirements: !ruby/object:Gem::Requirement
22
22
  requirements:
23
23
  - - "~>"
24
24
  - !ruby/object:Gem::Version
25
- version: '0.18'
25
+ version: '0.24'
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: thor
28
28
  requirement: !ruby/object:Gem::Requirement
29
29
  requirements:
30
30
  - - "~>"
31
31
  - !ruby/object:Gem::Version
32
- version: '1.2'
32
+ version: '1.4'
33
33
  type: :runtime
34
34
  prerelease: false
35
35
  version_requirements: !ruby/object:Gem::Requirement
36
36
  requirements:
37
37
  - - "~>"
38
38
  - !ruby/object:Gem::Version
39
- version: '1.2'
39
+ version: '1.4'
40
40
  - !ruby/object:Gem::Dependency
41
41
  name: tty-prompt
42
42
  requirement: !ruby/object:Gem::Requirement
@@ -68,6 +68,7 @@ files:
68
68
  - lib/commitgpt/cli.rb
69
69
  - lib/commitgpt/commit_ai.rb
70
70
  - lib/commitgpt/config_manager.rb
71
+ - lib/commitgpt/diff_helpers.rb
71
72
  - lib/commitgpt/provider_presets.rb
72
73
  - lib/commitgpt/setup_wizard.rb
73
74
  - lib/commitgpt/string.rb
@@ -79,6 +80,7 @@ metadata:
79
80
  homepage_uri: https://github.com/ZPVIP/commitgpt
80
81
  source_code_uri: https://github.com/ZPVIP/commitgpt
81
82
  changelog_uri: https://github.com/ZPVIP/commitgpt/blob/master/CHANGELOG.md
83
+ rubygems_mfa_required: 'true'
82
84
  rdoc_options: []
83
85
  require_paths:
84
86
  - lib