commitgpt 0.3.10 → 0.3.12

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: bc50a5ed7cfb5cc59703014b3b6f58370028dfe2c1bc5c7186e3c6d43a755dd7
4
- data.tar.gz: e9dd5c01f7c07062fc803710c79a31ce40ab9a9625918c2753bf59d25b4f9d4b
3
+ metadata.gz: ab9b9d8c6894ba37e2e16881e28fc2772336f5056f53330f91451818c5a28e36
4
+ data.tar.gz: e4a4d6a33c7a0270725af5e79f1da44bac5111c9810bb799231631336a5aa4c5
5
5
  SHA512:
6
- metadata.gz: 2005518993b77ff65ab764075016d6c1a8bb1259efff645017ab3f0a23a83f03e1b18fc7df257f497a8ecf5a705d4a0c91c29a5e044d00e9896437faed7cf675
7
- data.tar.gz: d290dac27335967df2b7dfdc8309509f7f228ede0d50c7e0b86e99c28cb16b1551d2bb5004ce3d846a183f1fb85beda193d5fec2873ad9e397211fa6e93ceb26
6
+ metadata.gz: 7610f6291cffa1b70c7d9735096962a7e1a25f62d5b6e512c6688d0d80d1cd71961798d5867e77c0bc94dd58db027cd76168ecf833ab07fb886818b370d88616
7
+ data.tar.gz: fbf38cd92931dddfdb1acdf9d7a8ec290d33376fab4fd769a480f6e45a4da8a495624e8143dd58592a51294ecd5c959316143744e9ee3eb6fce395b09cfd09fc
data/README.md CHANGED
@@ -17,9 +17,12 @@ The easiest way to install and keep CommitGPT updated.
17
17
 
18
18
  ```bash
19
19
  brew tap ZPVIP/commitgpt https://github.com/ZPVIP/commitgpt
20
+ brew trust zpvip/commitgpt
20
21
  brew install commitgpt
21
22
  ```
22
23
 
24
+ > **Note**: Recent Homebrew versions refuse to load formulae from third-party taps until you trust them, so `brew trust` is required before installing.
25
+
23
26
  **Upgrade:**
24
27
 
25
28
  ```bash
@@ -194,8 +197,41 @@ docs: update API documentation
194
197
  šŸ“ update API documentation
195
198
  ```
196
199
 
200
+ ### Choose Commit Message Detail Level
201
+ Select how much the commit message says:
202
+ ```bash
203
+ $ aicm -d
204
+ # or
205
+ $ aicm --detail
206
+ ```
207
+
208
+ - **Concise** - Subject line only (default)
209
+ - **Detailed** - Subject line, an explanatory paragraph, and a bullet list of notable changes
210
+
211
+ This is independent of the format above: the format decides how the subject line is written, the detail level decides how much follows it. Your selection is saved as `commit_detail` in `~/.config/commitgpt/config.yml`.
212
+
213
+ #### Detail Examples
214
+
215
+ **Concise:**
216
+ ```
217
+ feat(dfb): store and expand DFB event and speed logs
218
+ ```
219
+
220
+ **Detailed:**
221
+ ```
222
+ feat(dfb): store and expand DFB event and speed logs
223
+
224
+ Persist DFB device logs ingested from the proactive `evt` (event log) and
225
+ `lge` (speed stats) topics, one record per message.
226
+
227
+ - Add `dfb_event_logs` and `dfb_speed_stats` tables and models, each with an
228
+ idempotent unique index so re-delivery and overlapping backfills are safe.
229
+ - Associate both log tables on `Product`.
230
+ - Update `README_DFB.md` to document the cloud tables.
231
+ ```
232
+
197
233
  ### Check Configuration
198
- View your current configuration (Provider, Model, Format, Base URL, Diff Len):
234
+ View your current configuration (Provider, Model, Format, Detail, Base URL, Diff Len):
199
235
  ```bash
200
236
  $ aicm help
201
237
  ```
data/lib/commitgpt/cli.rb CHANGED
@@ -14,6 +14,7 @@ module CommitGpt
14
14
  method_option :verbose, aliases: '-v', type: :boolean, desc: 'Show git diff being sent to AI'
15
15
  method_option :provider, aliases: '-p', type: :boolean, desc: 'Switch active provider'
16
16
  method_option :format, aliases: '-f', type: :boolean, desc: 'Choose commit message format'
17
+ method_option :detail, aliases: '-d', type: :boolean, desc: 'Choose commit message detail level'
17
18
  def generate
18
19
  if options[:provider]
19
20
  CommitGpt::SetupWizard.new.switch_provider
@@ -21,6 +22,8 @@ module CommitGpt
21
22
  CommitGpt::SetupWizard.new.change_model
22
23
  elsif options[:format]
23
24
  CommitGpt::SetupWizard.new.choose_format
25
+ elsif options[:detail]
26
+ CommitGpt::SetupWizard.new.choose_detail
24
27
  else
25
28
  CommitGpt::CommitAi.new.aicm(verbose: options[:verbose])
26
29
  end
@@ -42,6 +45,7 @@ module CommitGpt
42
45
  shell.say ' -m, --models # Interactive model selection'
43
46
  shell.say ' -p, --provider # Switch active provider'
44
47
  shell.say ' -f, --format # Choose commit message format'
48
+ shell.say ' -d, --detail # Choose commit message detail level'
45
49
  shell.say ' -v, --verbose # Show git diff being sent to AI'
46
50
  shell.say ''
47
51
 
@@ -61,6 +65,7 @@ module CommitGpt
61
65
  shell.say " Provider: #{config['name'].green}"
62
66
  shell.say " Model: #{config['model'].cyan}"
63
67
  shell.say " Format: #{format.capitalize.yellow}"
68
+ shell.say " Detail: #{CommitGpt::ConfigManager.get_commit_detail.capitalize.yellow}"
64
69
  shell.say " Base URL: #{config['base_url']}"
65
70
  shell.say " Diff Len: #{config['diff_len']}"
66
71
  shell.say " Timeout: #{CommitGpt::ConfigManager.get_model_fetch_timeout}s"
@@ -25,6 +25,26 @@ module CommitGpt
25
25
  'gitmoji' => ':emoji: <commit message>'
26
26
  }.freeze
27
27
 
28
+ # Worked example of the detailed shape. Models follow a sample far more
29
+ # reliably than a description of one.
30
+ DETAILED_EXAMPLE = <<~EXAMPLE
31
+ Here is an example of the expected output, showing the exact shape only.
32
+ Your subject line must still follow the format above:
33
+
34
+ feat(dfb): store and expand DFB event and speed logs
35
+
36
+ Persist DFB device logs ingested from the proactive evt (event log) and
37
+ lge (speed stats) topics, one record per message.
38
+
39
+ - Add dfb_event_logs and dfb_speed_stats tables and models, each with an
40
+ idempotent unique index so re-delivery and overlapping backfills are safe.
41
+ - Add the canonical event-code table and the loader that expands a device
42
+ event byte into a human label and tags; unknown codes fall back to
43
+ "Unknown Event".
44
+ - Associate both log tables on Product.
45
+ - Update README_DFB.md to document the new cloud tables.
46
+ EXAMPLE
47
+
28
48
  # Conventional commit types based on aicommits implementation
29
49
  CONVENTIONAL_TYPES = {
30
50
  'docs' => 'Documentation only changes',
@@ -133,6 +153,72 @@ module CommitGpt
133
153
  end
134
154
 
135
155
  @commit_format = ConfigManager.get_commit_format
156
+ @commit_detail = ConfigManager.get_commit_detail
157
+ end
158
+
159
+ # True when the message should carry a body, not just a subject line
160
+ def detailed?
161
+ @commit_detail == 'detailed'
162
+ end
163
+
164
+ # Streaming cutoff. Generous because models that ignore disable_reasoning
165
+ # spend it on inline <think> text before the message itself arrives.
166
+ def max_content_chars
167
+ detailed? ? 6000 : 1500
168
+ end
169
+
170
+ def reasoning_disabled_max_tokens
171
+ detailed? ? 1200 : 300
172
+ end
173
+
174
+ # Some models stream their chain of thought inside content instead of the
175
+ # reasoning_content field, either wrapped in <think>...</think> or with only
176
+ # the closing tag when the chat template pre-fills the opener. Keep what
177
+ # comes after the thinking, never the thinking itself.
178
+ def strip_reasoning(content)
179
+ text = content.gsub(%r{<think(?:ing)?>.*?</think(?:ing)?>}m, '')
180
+ text = text.rpartition(%r{</think(?:ing)?>}).last if text.match?(%r{</think(?:ing)?>})
181
+ # Opener with no closer: the model never came back from thinking
182
+ text.sub(/<think(?:ing)?>.*\z/m, '')
183
+ end
184
+
185
+ # Header printed before the streamed message starts arriving
186
+ def content_prefix
187
+ detailed? ? "✦ Commit message:\n" : '✦ Commit message: git commit -am "'
188
+ end
189
+
190
+ # Closing quote that matches content_prefix; a body has no quote to close
191
+ def content_suffix
192
+ detailed? ? '' : '"'
193
+ end
194
+
195
+ # Editor git will actually use, resolved through its own precedence chain
196
+ def git_editor
197
+ editor = `git var GIT_EDITOR 2>/dev/null`.strip
198
+ editor.empty? ? 'your editor' : editor
199
+ end
200
+
201
+ # Models like to wrap the whole message in a fence despite being told not to
202
+ def strip_code_fence(text)
203
+ return text unless text.start_with?('```')
204
+
205
+ text.sub(/\A```[a-zA-Z]*\n?/, '').sub(/\n?```\s*\z/, '')
206
+ end
207
+
208
+ # Trim the model output down to what git should receive
209
+ def finalize_message(full_content)
210
+ content = strip_reasoning(full_content)
211
+
212
+ unless detailed?
213
+ # Take only the first non-empty line to avoid repetition or multi-line garbage
214
+ first_line = content.split("\n").map(&:strip).reject(&:empty?).first
215
+ return first_line&.gsub(/\A["']|["']\z/, '') || ''
216
+ end
217
+
218
+ body = strip_code_fence(content.strip).gsub(/\A["']|["']\z/, '').strip
219
+ # Keep the subject/body/bullets structure but drop trailing spaces and
220
+ # collapse the extra blank lines models like to emit between sections
221
+ body.split("\n").map(&:rstrip).join("\n").gsub(/\n{3,}/, "\n\n")
136
222
  end
137
223
 
138
224
  def aicm(verbose: false)
@@ -150,9 +236,10 @@ module CommitGpt
150
236
 
151
237
  case action
152
238
  when :commit
153
- commit_command = "git commit -m \"#{ai_commit_message}\""
154
- puts "\n→ Executing: #{commit_command}".yellow
155
- system(commit_command)
239
+ # Pass argv directly: a body with quotes, backticks or newlines must
240
+ # not be re-interpreted by the shell
241
+ puts "\n→ Executing: git commit -m <message>".yellow
242
+ system('git', 'commit', '-m', ai_commit_message)
156
243
  puts "\n\n"
157
244
  puts `git log -1`
158
245
  break
@@ -160,15 +247,15 @@ module CommitGpt
160
247
  puts "\n"
161
248
  next
162
249
  when :edit
163
- prompt = TTY::Prompt.new
164
- new_message = prompt.ask('Enter your commit message:')
165
- if new_message && !new_message.strip.empty?
166
- commit_command = "git commit -m \"#{new_message}\""
167
- system(commit_command)
168
- puts "\n"
169
- puts `git log -1`
250
+ # Let git open the message in the user's editor: it honours the
251
+ # GIT_EDITOR/core.editor/VISUAL/EDITOR chain, handles multi-line
252
+ # bodies, and aborts by itself if the message is left empty
253
+ puts "\n→ Opening #{git_editor} with the generated message...".yellow
254
+ if system('git', 'commit', '-e', '-m', ai_commit_message)
255
+ puts "\n"
256
+ puts `git log -1`
170
257
  else
171
- puts 'āœ– Commit aborted (empty message).'.red
258
+ puts 'āœ– Commit aborted.'.red
172
259
  end
173
260
  break
174
261
  when :exit
@@ -219,7 +306,7 @@ module CommitGpt
219
306
  prompt.select('Action:') do |menu|
220
307
  menu.choice 'Commit', :commit
221
308
  menu.choice 'Regenerate', :regenerate
222
- menu.choice 'Edit', :edit
309
+ menu.choice 'Edit in editor', :edit
223
310
  menu.choice 'Exit without commit', :exit
224
311
  end
225
312
  rescue TTY::Reader::InputInterrupt, Interrupt
@@ -279,7 +366,11 @@ module CommitGpt
279
366
  # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity, Metrics/BlockLength
280
367
  def generate_commit(diff = '', chunk_label: nil)
281
368
  # Build format-specific prompt
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.'
369
+ base_prompt = if detailed?
370
+ 'Generate a git commit message in present tense that precisely describes the key changes in the following code diff. Focus on what was changed and why, not just file names.'
371
+ else
372
+ '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.'
373
+ end
283
374
 
284
375
  format_instruction = case @commit_format
285
376
  when 'conventional'
@@ -290,17 +381,42 @@ module CommitGpt
290
381
  ''
291
382
  end
292
383
 
293
- format_spec = "The output response must be in format:\n#{COMMIT_FORMATS[@commit_format]}"
384
+ format_spec = if detailed?
385
+ "The subject line must be in format:\n#{COMMIT_FORMATS[@commit_format]}\n\n#{DETAILED_EXAMPLE}"
386
+ else
387
+ "The output response must be in format:\n#{COMMIT_FORMATS[@commit_format]}"
388
+ end
389
+
390
+ rules = if detailed?
391
+ [
392
+ '- The first line is the subject: a maximum of 100 characters, no trailing period.',
393
+ '- Then one blank line, then a body paragraph of 1-3 sentences explaining what changed and why.',
394
+ '- Then one blank line, then a bullet list where each bullet starts with "- " and describes one notable change.',
395
+ '- Wrap body and bullet text at 72 characters.',
396
+ '- Cover the whole diff in 3-6 bullets. Group related files into one bullet instead of listing every file.',
397
+ '- Be specific: include concrete details (file, table, method, package names) rather than generic statements.',
398
+ '- Do not use markdown headings, bold, or code fences. Plain text only.',
399
+ '- Write the message directly. Do not think out loud, do not draft and revise, do not explain your reasoning, do not mention the diff or these instructions.',
400
+ '- Do not emit <think> tags or any text before the subject line.',
401
+ '- Stop immediately after the last bullet. Do not add a summary, a closing remark, or a second version of the message.',
402
+ '- Exclude anything unnecessary such as translation. Your entire response will be passed directly into git commit.',
403
+ '- Return ONLY the commit message, nothing else. No explanations, no introductions, no surrounding quotes.'
404
+ ]
405
+ else
406
+ [
407
+ '- Commit message must be a maximum of 100 characters.',
408
+ '- Exclude anything unnecessary such as translation. Your entire response will be passed directly into git commit.',
409
+ '- 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.',
410
+ '- Be specific: include concrete details (package names, versions, functionality) rather than generic statements.',
411
+ '- Return ONLY the commit message, nothing else.'
412
+ ]
413
+ end
294
414
 
295
415
  system_content = [
296
416
  base_prompt,
297
417
  'Message language: English.',
298
418
  'Rules:',
299
- '- Commit message must be a maximum of 100 characters.',
300
- '- Exclude anything unnecessary such as translation. Your entire response will be passed directly into git commit.',
301
- '- 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.',
302
- '- Be specific: include concrete details (package names, versions, functionality) rather than generic statements.',
303
- '- Return ONLY the commit message, nothing else.',
419
+ *rules,
304
420
  format_instruction,
305
421
  format_spec
306
422
  ].reject(&:empty?).join("\n")
@@ -331,7 +447,9 @@ module CommitGpt
331
447
 
332
448
  if can_disable_reasoning
333
449
  payload[:disable_reasoning] = true
334
- payload[:max_tokens] = 300
450
+ # SGLang/vLLM back ends honour this where disable_reasoning is ignored
451
+ payload[:chat_template_kwargs] = { enable_thinking: false }
452
+ payload[:max_tokens] = reasoning_disabled_max_tokens
335
453
  else
336
454
  payload[:max_tokens] = configured_max_tokens
337
455
  end
@@ -379,7 +497,7 @@ module CommitGpt
379
497
  error_msg += " Raw: #{error_body}" unless error_body.to_s.strip.empty?
380
498
  end
381
499
 
382
- if can_disable_reasoning && (error_msg =~ /parameter|reasoning|unsupported/i || response.code == '400')
500
+ if can_disable_reasoning && (error_msg =~ /parameter|reasoning|template|unsupported/i || response.code == '400')
383
501
  puts "⚠ Provider does not support 'disable_reasoning'. Updating config and retrying...".yellow
384
502
  ConfigManager.update_provider(provider_config['name'], { 'can_disable_reasoning' => false })
385
503
  @is_retrying = true
@@ -430,13 +548,13 @@ module CommitGpt
430
548
  if chunk_label
431
549
  print "ā—† #{chunk_label}: ".magenta
432
550
  else
433
- print '✦ Commit message: git commit -am "'.green
551
+ print content_prefix.green
434
552
  end
435
553
  printed_content_prefix = true
436
554
  end
437
555
 
438
556
  # Prevent infinite loops/repetitive garbage
439
- if full_content.length + content_chunk.length > 300
557
+ if full_content.length + content_chunk.length > max_content_chars
440
558
  stop_stream = true
441
559
  break
442
560
  end
@@ -460,7 +578,7 @@ module CommitGpt
460
578
  end
461
579
 
462
580
  # Close the quote
463
- puts(chunk_label ? '' : '"'.green) if printed_content_prefix
581
+ puts(chunk_label ? '' : content_suffix.green) if printed_content_prefix
464
582
 
465
583
  # Post-processing Logic (Retry if empty content)
466
584
  if (full_content.nil? || full_content.strip.empty?) && (full_reasoning && !full_reasoning.strip.empty?)
@@ -511,9 +629,7 @@ module CommitGpt
511
629
  # Reset retrying flag
512
630
  @is_retrying = false
513
631
 
514
- # Take only the first non-empty line to avoid repetition or multi-line garbage
515
- first_line = full_content.split("\n").map(&:strip).reject(&:empty?).first
516
- first_line&.gsub(/\A["']|["']\z/, '') || ''
632
+ finalize_message(full_content)
517
633
  end
518
634
  # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity, Metrics/BlockLength
519
635
 
@@ -530,14 +646,31 @@ module CommitGpt
530
646
  ''
531
647
  end
532
648
 
649
+ rules = if detailed?
650
+ [
651
+ '- The first line is the subject: a maximum of 100 characters, no trailing period.',
652
+ '- Then one blank line, then a body paragraph of 1-3 sentences explaining what changed and why.',
653
+ '- Then one blank line, then a bullet list where each bullet starts with "- " and describes one notable change.',
654
+ '- Wrap body and bullet text at 72 characters.',
655
+ '- Present tense.',
656
+ '- Be specific: include concrete details rather than generic statements.',
657
+ '- Do not use markdown headings, bold, or code fences. Plain text only.',
658
+ '- Return ONLY the commit message, nothing else. No quotes, no explanations.'
659
+ ]
660
+ else
661
+ [
662
+ '- Maximum 100 characters.',
663
+ '- Present tense.',
664
+ '- Be specific: include concrete details rather than generic statements.',
665
+ '- Return ONLY the commit message, nothing else. No quotes, no explanations.'
666
+ ]
667
+ end
668
+
533
669
  system_content = [
534
670
  '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.',
671
+ "Synthesize them into ONE unified git commit message that captures the overall change.#{' Keep it concise.' unless detailed?}",
536
672
  '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.',
673
+ *rules,
541
674
  format_instruction
542
675
  ].reject(&:empty?).join("\n")
543
676
 
@@ -558,7 +691,9 @@ module CommitGpt
558
691
 
559
692
  if can_disable_reasoning
560
693
  payload[:disable_reasoning] = true
561
- payload[:max_tokens] = 300
694
+ # SGLang/vLLM back ends honour this where disable_reasoning is ignored
695
+ payload[:chat_template_kwargs] = { enable_thinking: false }
696
+ payload[:max_tokens] = reasoning_disabled_max_tokens
562
697
  else
563
698
  payload[:max_tokens] = provider_config['max_tokens'] || 2000
564
699
  end
@@ -606,11 +741,11 @@ module CommitGpt
606
741
  content_chunk = delta['content']
607
742
  if content_chunk && !content_chunk.empty?
608
743
  unless printed_content_prefix
609
- print "\n✦ Commit message: git commit -am \"".green
744
+ print "\n#{content_prefix}".green
610
745
  printed_content_prefix = true
611
746
  end
612
747
 
613
- if full_content.length + content_chunk.length > 300
748
+ if full_content.length + content_chunk.length > max_content_chars
614
749
  stop_stream = true
615
750
  break
616
751
  end
@@ -630,15 +765,14 @@ module CommitGpt
630
765
  return nil
631
766
  end
632
767
 
633
- puts '"'.green if printed_content_prefix
768
+ puts content_suffix.green if printed_content_prefix
634
769
 
635
770
  if full_content.strip.empty?
636
771
  puts 'āœ– No response from AI during synthesis.'.red
637
772
  return nil
638
773
  end
639
774
 
640
- first_line = full_content.split("\n").map(&:strip).reject(&:empty?).first
641
- first_line&.gsub(/\A["']|["']\z/, '') || ''
775
+ finalize_message(full_content)
642
776
  end
643
777
  end
644
778
  end
@@ -11,6 +11,11 @@ module CommitGpt
11
11
  # Seconds to wait when fetching a provider's model list
12
12
  DEFAULT_MODEL_FETCH_TIMEOUT = 30
13
13
 
14
+ # How much the commit message says: a subject line only, or a subject
15
+ # followed by an explanatory body
16
+ COMMIT_DETAILS = %w[concise detailed].freeze
17
+ DEFAULT_COMMIT_DETAIL = 'concise'
18
+
14
19
  class << self
15
20
  # Get the config directory path
16
21
  def config_dir
@@ -168,6 +173,23 @@ module CommitGpt
168
173
  save_main_config(main_config)
169
174
  end
170
175
 
176
+ # Get commit detail level ('concise' or 'detailed')
177
+ def get_commit_detail
178
+ return DEFAULT_COMMIT_DETAIL unless config_exists?
179
+
180
+ main_config = YAML.load_file(main_config_path)
181
+ detail = main_config['commit_detail']
182
+ COMMIT_DETAILS.include?(detail) ? detail : DEFAULT_COMMIT_DETAIL
183
+ end
184
+
185
+ # Set commit detail level
186
+ def set_commit_detail(detail)
187
+ ensure_config_dir
188
+ main_config = config_exists? ? YAML.load_file(main_config_path) : { 'providers' => [], 'active_provider' => '' }
189
+ main_config['commit_detail'] = detail
190
+ save_main_config(main_config)
191
+ end
192
+
171
193
  # Get the model list fetch timeout (seconds)
172
194
  def get_model_fetch_timeout
173
195
  return DEFAULT_MODEL_FETCH_TIMEOUT unless config_exists?
@@ -116,8 +116,26 @@ module CommitGpt
116
116
  puts "\nāœ” Commit format set to: #{format}".green
117
117
  end
118
118
 
119
+ # Choose how much the commit message says
120
+ def choose_detail
121
+ puts "\n→ Choose git commit message detail level:\n".green
122
+
123
+ detail = @prompt.select('Select detail level:', default: default_detail_index) do |menu|
124
+ menu.choice 'Concise - Subject line only', 'concise'
125
+ menu.choice 'Detailed - Subject line, explanatory paragraph, and bullet list of changes', 'detailed'
126
+ end
127
+
128
+ ConfigManager.set_commit_detail(detail)
129
+ puts "\nāœ” Commit detail set to: #{detail}".green
130
+ end
131
+
119
132
  private
120
133
 
134
+ # Menu position of the currently configured detail level (1-based)
135
+ def default_detail_index
136
+ ConfigManager::COMMIT_DETAILS.index(ConfigManager.get_commit_detail).to_i + 1
137
+ end
138
+
121
139
  # Select provider from list
122
140
  def select_provider
123
141
  config = ConfigManager.load_config
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CommitGpt
4
- VERSION = '0.3.10'
4
+ VERSION = '0.3.12'
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.10
4
+ version: 0.3.12
5
5
  platform: ruby
6
6
  authors:
7
7
  - Peng Zhang