commitgpt 0.3.5 → 0.3.6

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: b47c936f4c64c0d4cb2fb5214d1bf28b188b1c14525d5984be2fb2a0f84fb287
4
- data.tar.gz: 540d6f74110a1d86e1a34a366456224f56b78888ddbafc070fb5ef0594697e59
3
+ metadata.gz: a1e1fcc474c778dff2c10f370b5a291483793d0ded5c8d43beaab1b7e377b4ff
4
+ data.tar.gz: 17ec9d1daaf3ad085adbd8566173411db284995188b4c97d5eaf4d69a634763d
5
5
  SHA512:
6
- metadata.gz: 60c0ea07f58567bee161b6037fe6db60d92344b1098c88e33c9e4d4908ca08b36b66f722d29cc743308c1bc1f4d4c16f70fb5a7e2a65db1d8c3c0a8adafd5ef2
7
- data.tar.gz: 664fa8dc1665d983485c97d064e440e434b2645ff578954d2b762ceec4a741649bb20af3c32dbd180560cd67838e6a4eee1662ef75c56b53c790216d9738f2af
6
+ metadata.gz: 91ab99f89df2861a5731a3692e88d989b733a5e88edbd55ccdb654e109778b778f02dfc2af547ee6d12dcb8da8858ade012fd3150df381a6a771eecc036b04f1
7
+ data.tar.gz: aee42f5617f25c523aa9ed3c134eaae5b92c3e9d1e7d19cb9e7de089d79c010d792c4eb12c836db138160076f787dbe30e4219c104476e0c806f2de8135085bb
data/README.md CHANGED
@@ -216,6 +216,7 @@ $ gem update commitgpt
216
216
  We support any OpenAI-compatible API. Presets available for:
217
217
  - **Cerebras** (Fast & Recommended)
218
218
  - **OpenAI** (Official)
219
+ - **Apple** (Local via apple-to-openai)
219
220
  - **Ollama** (Local)
220
221
  - **Groq**
221
222
  - **DeepSeek**
@@ -246,6 +247,10 @@ llama-3.3-70b-versatile
246
247
  llama-3.1-8b-instant
247
248
  ```
248
249
 
250
+ **Apple Local Models** (via [apple-to-openai](https://github.com/ZPVIP/apple-to-openai))
251
+
252
+ > **Note**: Due to the context window limits of Apple's local models, it is highly recommended to set your max diff length (`diff_len`) to `10000` during setup. When prompted for large diffs, select the **Smart chunked mode** to avoid errors.
253
+
249
254
  ## How It Works
250
255
  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/system instructions for generating conventional commit messages.
251
256
 
@@ -139,7 +139,7 @@ module CommitGpt
139
139
  exit(1) unless welcome
140
140
  diff = git_diff || exit(1)
141
141
  if verbose
142
- puts " Git diff (#{diff.length} chars):".cyan
142
+ puts " Git diff (#{diff.length} chars):".cyan
143
143
  puts diff
144
144
  puts "\n"
145
145
  end
@@ -151,7 +151,7 @@ module CommitGpt
151
151
  case action
152
152
  when :commit
153
153
  commit_command = "git commit -m \"#{ai_commit_message}\""
154
- puts "\n Executing: #{commit_command}".yellow
154
+ puts "\n Executing: #{commit_command}".yellow
155
155
  system(commit_command)
156
156
  puts "\n\n"
157
157
  puts `git log -1`
@@ -168,11 +168,11 @@ module CommitGpt
168
168
  puts "\n"
169
169
  puts `git log -1`
170
170
  else
171
- puts ' Commit aborted (empty message).'.red
171
+ puts ' Commit aborted (empty message).'.red
172
172
  end
173
173
  break
174
174
  when :exit
175
- puts ' Exit without commit.'.yellow
175
+ puts ' Exit without commit.'.yellow
176
176
  break
177
177
  end
178
178
  end
@@ -190,7 +190,7 @@ module CommitGpt
190
190
  models = response['data'] || []
191
191
  models.each { |m| puts m['id'] }
192
192
  rescue StandardError => e
193
- puts " Failed to list models: #{e.message}".red
193
+ puts " Failed to list models: #{e.message}".red
194
194
  end
195
195
  end
196
196
 
@@ -206,7 +206,7 @@ module CommitGpt
206
206
  models = response['data'] || []
207
207
  models.each { |m| puts m['id'] }
208
208
  rescue StandardError => e
209
- puts " Failed to list models: #{e.message}".red
209
+ puts " Failed to list models: #{e.message}".red
210
210
  end
211
211
  end
212
212
 
@@ -228,42 +228,56 @@ module CommitGpt
228
228
  end
229
229
 
230
230
  def message(diff = nil)
231
- generate_commit(diff)
231
+ return generate_commit(diff) unless @chunked_mode
232
+
233
+ # Reserve space for system prompt overhead (~20% of diff_len)
234
+ chunk_size = [(@diff_len * 0.8).to_i, 4000].max
235
+ chunks = split_diff_by_length(diff, chunk_size)
236
+ segment_messages = []
237
+
238
+ puts "→ Splitting into #{chunks.length} segments (#{chunk_size} chars each)...".cyan
239
+
240
+ chunks.each_with_index do |chunk, idx|
241
+ puts "\n◆ Generating message for segment #{idx + 1}/#{chunks.length}...".magenta
242
+ msg = generate_commit(chunk, chunk_label: "Segment #{idx + 1}/#{chunks.length}")
243
+ return nil if msg.nil?
244
+
245
+ segment_messages << msg
246
+ puts ''
247
+ end
248
+
249
+ puts "\n→ Synthesizing final commit message from #{segment_messages.length} segments...".cyan
250
+ synthesize_commit(segment_messages)
232
251
  end
233
252
 
234
253
  def welcome
235
- puts "\n Welcome to AI Commits!".green
254
+ puts "\n Welcome to AI Commits!".green
236
255
 
237
256
  # Check if config exists
238
257
  unless ConfigManager.config_exists?
239
- puts ' Configuration not found. Generating default config...'.yellow
258
+ puts ' Configuration not found. Generating default config...'.yellow
240
259
  ConfigManager.generate_default_configs
241
- puts " Please run 'aicm setup' to configure your provider.".red
260
+ puts " Please run 'aicm setup' to configure your provider.".red
242
261
  return false
243
262
  end
244
263
 
245
264
  # Check if active provider is configured
246
- if @api_key.nil? || @api_key.empty?
247
- puts "▲ No active provider configured. Please run 'aicm setup'.".red
248
- return false
249
- end
250
-
251
265
  if @model.nil? || @model.empty?
252
- puts " No model selected. Please run 'aicm setup'.".red
266
+ puts " No model selected. Please run 'aicm setup'.".red
253
267
  return false
254
268
  end
255
269
 
256
270
  begin
257
271
  `git rev-parse --is-inside-work-tree`
258
272
  rescue StandardError
259
- puts ' This is not a git repository'.red
273
+ puts ' This is not a git repository'.red
260
274
  return false
261
275
  end
262
276
  true
263
277
  end
264
278
 
265
- # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
266
- def generate_commit(diff = '')
279
+ # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity, Metrics/BlockLength
280
+ def generate_commit(diff = '', chunk_label: nil)
267
281
  # Build format-specific prompt
268
282
  base_prompt = '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.'
269
283
 
@@ -323,7 +337,9 @@ module CommitGpt
323
337
  end
324
338
 
325
339
  # Initial UI feedback (only on first try)
326
- puts '....... Generating your AI commit message ......'.gray unless defined?(@is_retrying) && @is_retrying
340
+ total_chars = system_content.length + diff.to_s.length
341
+ puts " ....... System prompt: #{system_content.length} chars, Diff chunk: #{diff.to_s.length} chars, Total: #{total_chars} chars".gray
342
+ puts ' ....... Generating your AI commit message'.gray unless defined?(@is_retrying) && @is_retrying
327
343
 
328
344
  full_content = ''
329
345
  full_reasoning = ''
@@ -364,12 +380,12 @@ module CommitGpt
364
380
  end
365
381
 
366
382
  if can_disable_reasoning && (error_msg =~ /parameter|reasoning|unsupported/i || response.code == '400')
367
- puts " Provider does not support 'disable_reasoning'. Updating config and retrying...".yellow
383
+ puts " Provider does not support 'disable_reasoning'. Updating config and retrying...".yellow
368
384
  ConfigManager.update_provider(provider_config['name'], { 'can_disable_reasoning' => false })
369
385
  @is_retrying = true
370
386
  return generate_commit(diff)
371
387
  else
372
- puts " API Error: #{error_msg}".red
388
+ puts " API Error: #{error_msg}".red
373
389
  return nil
374
390
  end
375
391
  end
@@ -411,7 +427,11 @@ module CommitGpt
411
427
  end
412
428
 
413
429
  unless printed_content_prefix
414
- print "\n▲ Commit message: git commit -am \"".green
430
+ if chunk_label
431
+ print "◆ #{chunk_label}: ".magenta
432
+ else
433
+ print '✦ Commit message: git commit -am "'.green
434
+ end
415
435
  printed_content_prefix = true
416
436
  end
417
437
 
@@ -421,7 +441,7 @@ module CommitGpt
421
441
  break
422
442
  end
423
443
 
424
- print content_chunk.green
444
+ print chunk_label ? content_chunk.magenta : content_chunk.green
425
445
  full_content += content_chunk
426
446
  $stdout.flush
427
447
  end
@@ -435,22 +455,22 @@ module CommitGpt
435
455
  end
436
456
  end
437
457
  rescue StandardError => e
438
- puts " Error: #{e.message}".red
458
+ puts " Error: #{e.message}".red
439
459
  return nil
440
460
  end
441
461
 
442
462
  # Close the quote
443
- puts '"'.green if printed_content_prefix
463
+ puts(chunk_label ? '' : '"'.green) if printed_content_prefix
444
464
 
445
465
  # Post-processing Logic (Retry if empty content)
446
466
  if (full_content.nil? || full_content.strip.empty?) && (full_reasoning && !full_reasoning.strip.empty?)
447
467
  if can_disable_reasoning
448
- puts "\n Model returned reasoning despite 'disable_reasoning: true'. Updating config and retrying...".yellow
468
+ puts "\n Model returned reasoning despite 'disable_reasoning: true'. Updating config and retrying...".yellow
449
469
  ConfigManager.update_provider(provider_config['name'], { 'can_disable_reasoning' => false })
450
470
  @is_retrying = true
451
471
  return generate_commit(diff)
452
472
  else
453
- puts "\n Model output truncated (Reasoning consumed all #{configured_max_tokens} tokens).".red
473
+ puts "\n Model output truncated (Reasoning consumed all #{configured_max_tokens} tokens).".red
454
474
  prompt = TTY::Prompt.new
455
475
  choice = prompt.select('Choose an action:') do |menu|
456
476
  menu.choice "Double max_tokens to #{configured_max_tokens * 2}", :double
@@ -468,7 +488,7 @@ module CommitGpt
468
488
  end
469
489
 
470
490
  if new_max
471
- puts " Updating max_tokens to #{new_max} and retrying...".yellow
491
+ puts " Updating max_tokens to #{new_max} and retrying...".yellow
472
492
  ConfigManager.update_provider(provider_config['name'], { 'max_tokens' => new_max })
473
493
  @is_retrying = true
474
494
  return generate_commit(diff)
@@ -478,13 +498,13 @@ module CommitGpt
478
498
  end
479
499
 
480
500
  if full_content.empty? && full_reasoning.empty?
481
- puts ' No response from AI.'.red
501
+ puts ' No response from AI.'.red
482
502
  return nil
483
503
  end
484
504
 
485
505
  # Print usage info if available (saved from stream or approximated)
486
506
  if defined?(@last_usage) && @last_usage
487
- puts "\n...... Tokens: #{@last_usage['total_tokens']} (Prompt: #{@last_usage['prompt_tokens']}, Completion: #{@last_usage['completion_tokens']})\n\n".gray
507
+ puts " ....... Tokens: #{@last_usage['total_tokens']} (Prompt: #{@last_usage['prompt_tokens']}, Completion: #{@last_usage['completion_tokens']})".gray
488
508
  @last_usage = nil
489
509
  end
490
510
 
@@ -495,6 +515,130 @@ module CommitGpt
495
515
  first_line = full_content.split("\n").map(&:strip).reject(&:empty?).first
496
516
  first_line&.gsub(/\A["']|["']\z/, '') || ''
497
517
  end
498
- # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
518
+ # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity, Metrics/BlockLength
519
+
520
+ # Synthesize a final commit message from multiple segment messages
521
+ def synthesize_commit(segment_messages)
522
+ numbered = segment_messages.each_with_index.map { |msg, i| "#{i + 1}. #{msg}" }.join("\n")
523
+
524
+ format_instruction = case @commit_format
525
+ when 'conventional'
526
+ "The output must follow Conventional Commits format:\n#{COMMIT_FORMATS['conventional']}"
527
+ when 'gitmoji'
528
+ "The output must use Gitmoji format:\n#{COMMIT_FORMATS['gitmoji']}"
529
+ else
530
+ ''
531
+ end
532
+
533
+ system_content = [
534
+ 'You are given multiple commit messages generated from different segments of a single large git diff.',
535
+ 'Synthesize them into ONE concise, unified git commit message that captures the overall change.',
536
+ 'Rules:',
537
+ '- Maximum 100 characters.',
538
+ '- Present tense.',
539
+ '- Be specific: include concrete details rather than generic statements.',
540
+ '- Return ONLY the commit message, nothing else. No quotes, no explanations.',
541
+ format_instruction
542
+ ].reject(&:empty?).join("\n")
543
+
544
+ messages = [
545
+ { role: 'system', content: system_content },
546
+ { role: 'user', content: "Synthesize these segment commit messages into one:\n\n#{numbered}" }
547
+ ]
548
+
549
+ provider_config = ConfigManager.get_active_provider_config
550
+ can_disable_reasoning = provider_config.key?('can_disable_reasoning') ? provider_config['can_disable_reasoning'] : true
551
+
552
+ payload = {
553
+ model: @model,
554
+ messages: messages,
555
+ temperature: 0.5,
556
+ stream: true
557
+ }
558
+
559
+ if can_disable_reasoning
560
+ payload[:disable_reasoning] = true
561
+ payload[:max_tokens] = 300
562
+ else
563
+ payload[:max_tokens] = provider_config['max_tokens'] || 2000
564
+ end
565
+
566
+ full_content = ''
567
+ printed_content_prefix = false
568
+ stop_stream = false
569
+
570
+ uri = URI("#{@base_url}/chat/completions")
571
+ http = Net::HTTP.new(uri.host, uri.port)
572
+ http.use_ssl = (uri.scheme == 'https')
573
+ http.read_timeout = 120
574
+
575
+ request = Net::HTTP::Post.new(uri)
576
+ request['Content-Type'] = 'application/json'
577
+ request['Authorization'] = "Bearer #{@api_key}" if @api_key
578
+ request.body = payload.to_json
579
+
580
+ begin
581
+ http.request(request) do |response|
582
+ if response.code != '200'
583
+ error_body = response.read_body
584
+ puts "✖ API Error: #{error_body}".red
585
+ return nil
586
+ end
587
+
588
+ buffer = ''
589
+ response.read_body do |chunk|
590
+ break if stop_stream
591
+
592
+ buffer += chunk
593
+ while (line_end = buffer.index("\n"))
594
+ line = buffer.slice!(0, line_end + 1).strip
595
+ next if line.empty?
596
+ next unless line.start_with?('data: ')
597
+
598
+ data_str = line[6..]
599
+ next if data_str == '[DONE]'
600
+
601
+ begin
602
+ data = JSON.parse(data_str)
603
+ delta = data.dig('choices', 0, 'delta')
604
+ next unless delta
605
+
606
+ content_chunk = delta['content']
607
+ if content_chunk && !content_chunk.empty?
608
+ unless printed_content_prefix
609
+ print "\n✦ Commit message: git commit -am \"".green
610
+ printed_content_prefix = true
611
+ end
612
+
613
+ if full_content.length + content_chunk.length > 300
614
+ stop_stream = true
615
+ break
616
+ end
617
+
618
+ print content_chunk.green
619
+ full_content += content_chunk
620
+ $stdout.flush
621
+ end
622
+ rescue JSON::ParserError
623
+ # Partial JSON, wait for more data
624
+ end
625
+ end
626
+ end
627
+ end
628
+ rescue StandardError => e
629
+ puts "✖ Error: #{e.message}".red
630
+ return nil
631
+ end
632
+
633
+ puts '"'.green if printed_content_prefix
634
+
635
+ if full_content.strip.empty?
636
+ puts '✖ No response from AI during synthesis.'.red
637
+ return nil
638
+ end
639
+
640
+ first_line = full_content.split("\n").map(&:strip).reject(&:empty?).first
641
+ first_line&.gsub(/\A["']|["']\z/, '') || ''
642
+ end
499
643
  end
500
644
  end
@@ -102,8 +102,8 @@ module CommitGpt
102
102
  save_local_config(local_config)
103
103
 
104
104
  # Remind user to add config.local.yml to .gitignore
105
- puts ' Generated default configuration files.'.green
106
- puts ' Remember to add ~/.config/commitgpt/config.local.yml to your .gitignore'.yellow
105
+ puts ' Generated default configuration files.'.green
106
+ puts ' Remember to add ~/.config/commitgpt/config.local.yml to your .gitignore'.yellow
107
107
  end
108
108
 
109
109
  # Get list of configured providers (with API keys)
@@ -21,11 +21,11 @@ module CommitGpt
21
21
  choice = prompt_no_staged_changes
22
22
  case choice
23
23
  when :add_all
24
- puts ' Running git add .'.yellow
24
+ puts ' Running git add .'.yellow
25
25
  system('git add .')
26
26
  diff_cached = `git diff --cached . #{exclusions}`.chomp
27
27
  if diff_cached.empty?
28
- puts ' Still no changes to commit.'.red
28
+ puts ' Still no changes to commit.'.red
29
29
  return nil
30
30
  end
31
31
  when :exit
@@ -33,7 +33,7 @@ module CommitGpt
33
33
  end
34
34
  else
35
35
  # Scenario: Mixed state (some staged, some not)
36
- puts ' You have both staged and unstaged changes:'.yellow
36
+ puts ' You have both staged and unstaged changes:'.yellow
37
37
 
38
38
  staged_files = `git diff --cached --name-status . #{exclusions}`.chomp
39
39
  unstaged_files = `git diff --name-status . #{exclusions}`.chomp
@@ -54,7 +54,7 @@ module CommitGpt
54
54
 
55
55
  case choice
56
56
  when :add_all
57
- puts ' Running git add .'.yellow
57
+ puts ' Running git add .'.yellow
58
58
  system('git add .')
59
59
  diff_cached = `git diff --cached . #{exclusions}`.chomp
60
60
  when :exit
@@ -67,7 +67,7 @@ module CommitGpt
67
67
  # git status --porcelain includes untracked files
68
68
  git_status = `git status --porcelain`.chomp
69
69
  if git_status.empty?
70
- puts ' No changes to commit. Working tree clean.'.yellow
70
+ puts ' No changes to commit. Working tree clean.'.yellow
71
71
  return nil
72
72
  else
73
73
  # Only untracked files? Or ignored files?
@@ -76,7 +76,7 @@ module CommitGpt
76
76
  choice = prompt_no_staged_changes
77
77
  case choice
78
78
  when :add_all
79
- puts ' Running git add .'.yellow
79
+ puts ' Running git add .'.yellow
80
80
  system('git add .')
81
81
  diff_cached = `git diff --cached . #{exclusions}`.chomp
82
82
  when :exit
@@ -93,11 +93,14 @@ module CommitGpt
93
93
  if diff.length > diff_len
94
94
  choice = prompt_diff_handling(diff.length, diff_len)
95
95
  case choice
96
+ when :chunked
97
+ @chunked_mode = true
98
+ puts "→ Smart chunked mode: splitting #{diff.length} chars into ~#{(diff.length.to_f / diff_len).ceil} segments...".yellow
96
99
  when :truncate
97
- puts " Truncating diff to #{diff_len} chars...".yellow
100
+ puts " Truncating diff to #{diff_len} chars...".yellow
98
101
  diff = diff[0...diff_len]
99
102
  when :unlimited
100
- puts " Using full diff (#{diff.length} chars)...".yellow
103
+ puts " Using full diff (#{diff.length} chars)...".yellow
101
104
  when :exit
102
105
  return nil
103
106
  end
@@ -107,7 +110,7 @@ module CommitGpt
107
110
  end
108
111
 
109
112
  def prompt_no_staged_changes
110
- puts ' No staged changes found (but unstaged/untracked files exist).'.yellow
113
+ puts ' No staged changes found (but unstaged/untracked files exist).'.yellow
111
114
  prompt = TTY::Prompt.new
112
115
  begin
113
116
  prompt.select('Choose an option:') do |menu|
@@ -120,10 +123,11 @@ module CommitGpt
120
123
  end
121
124
 
122
125
  def prompt_diff_handling(current_len, max_len)
123
- puts " The diff is too large (#{current_len} chars, max #{max_len}).".yellow
126
+ puts " The diff is too large (#{current_len} chars, max #{max_len}).".yellow
124
127
  prompt = TTY::Prompt.new
125
128
  begin
126
129
  prompt.select('Choose an option:') do |menu|
130
+ menu.choice 'Smart chunked: split into segments and synthesize (recommended)', :chunked
127
131
  menu.choice "Use first #{max_len} characters to generate commit message", :truncate
128
132
  menu.choice 'Use unlimited characters (may fail or be slow)', :unlimited
129
133
  menu.choice 'Exit', :exit
@@ -133,6 +137,21 @@ module CommitGpt
133
137
  end
134
138
  end
135
139
 
140
+ def split_diff_by_length(diff, max_len)
141
+ chunks = []
142
+ current_chunk = ''
143
+
144
+ diff.each_line do |line|
145
+ if current_chunk.length + line.length > max_len && !current_chunk.empty?
146
+ chunks << current_chunk
147
+ current_chunk = ''
148
+ end
149
+ current_chunk += line
150
+ end
151
+ chunks << current_chunk unless current_chunk.empty?
152
+ chunks
153
+ end
154
+
136
155
  def detect_lock_file_changes
137
156
  # Check both staged and unstaged changes for lock files
138
157
  staged_files = `git diff --cached --name-only`.chomp.split("\n")
@@ -28,7 +28,7 @@ module CommitGpt
28
28
  configured = ConfigManager.configured_providers
29
29
 
30
30
  if configured.empty?
31
- puts " No providers configured. Please run 'aicm setup' first.".red
31
+ puts " No providers configured. Please run 'aicm setup' first.".red
32
32
  return
33
33
  end
34
34
 
@@ -69,7 +69,7 @@ module CommitGpt
69
69
  provider_config = ConfigManager.get_active_provider_config
70
70
 
71
71
  if provider_config.nil? || provider_config['api_key'].nil? || provider_config['api_key'].empty?
72
- puts " No active provider configured. Please run 'aicm setup'.".red
72
+ puts " No active provider configured. Please run 'aicm setup'.".red
73
73
  return
74
74
  end
75
75
 
@@ -90,7 +90,7 @@ module CommitGpt
90
90
  def choose_format
91
91
  prompt = TTY::Prompt.new
92
92
 
93
- puts "\n Choose git commit message format:\n".green
93
+ puts "\n Choose git commit message format:\n".green
94
94
 
95
95
  format = prompt.select('Select format:') do |menu|
96
96
  menu.choice 'Simple - Concise commit message', 'simple'
@@ -99,7 +99,7 @@ module CommitGpt
99
99
  end
100
100
 
101
101
  ConfigManager.set_commit_format(format)
102
- puts "\n Commit format set to: #{format}".green
102
+ puts "\n Commit format set to: #{format}".green
103
103
  end
104
104
 
105
105
  private
@@ -264,20 +264,20 @@ module CommitGpt
264
264
  models = response['data'] || []
265
265
  models = models.map { |m| m['id'] }.compact.sort
266
266
  else
267
- puts " Failed to fetch models: HTTP #{response.code}".red
267
+ puts " Failed to fetch models: HTTP #{response.code}".red
268
268
  return nil
269
269
  end
270
270
  end
271
271
  rescue Timeout::Error
272
- puts ' Connection timeout (5s). Please check your network, base_url, and api_key.'.red
272
+ puts ' Connection timeout (5s). Please check your network, base_url, and api_key.'.red
273
273
  exit(0)
274
274
  rescue StandardError => e
275
- puts " Error fetching models: #{e.message}".red
275
+ puts " Error fetching models: #{e.message}".red
276
276
  exit(0)
277
277
  end
278
278
 
279
279
  if models.nil? || models.empty?
280
- puts ' No models found. Please check your configuration.'.red
280
+ puts ' No models found. Please check your configuration.'.red
281
281
  exit(0)
282
282
  end
283
283
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CommitGpt
4
- VERSION = '0.3.5'
4
+ VERSION = '0.3.6'
5
5
  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.5
4
+ version: 0.3.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Peng Zhang