commiti 1.3.0 → 1.3.1

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: 751893cc1501fbd4723682605f17bcc6f0617fafe28997b398affd8e33a20d1e
4
- data.tar.gz: 6b05cf37e1617655547c5e2de646dd75d07cde47d0883b70ee8eb7114242719b
3
+ metadata.gz: 026566db469248936e47f40faa446650926b7bb893089246a6c23a45dfaa432a
4
+ data.tar.gz: 251995106b1883d61cb12c00f911a4e3158d6d47e9b9d1b434d2026234dd3c2f
5
5
  SHA512:
6
- metadata.gz: 777e2d6ddc6e40f0599abbe0ee191b819993926c5e0ca5bed79abb81407071356fa97807de031ba21dc3221a96bfa8b526765ee35a80aeee95cf9410c952214f
7
- data.tar.gz: 739b300910794916477e8222b1ba2ea4f08698c337b1af10273c126a8fe8f3c619c6ed8171134cf545550e10767746b3b8d0da29f4cd203627ac7e26108edabf
6
+ metadata.gz: c0d857c40f016686ee37b6b7f2d1109c5d7f79a7037c0ed41d0e49e1d3556ff57ce0db9f1da3f0f26e4956840e5a7ab0376a39799f359673068542e89a11480c
7
+ data.tar.gz: 677c08e9d0d5ae62723f7bb3fdea97c3c3799dd5a3775b133ca5520af32a102d924863c5f733794e005b3ff82c79a22d30a7aaee578985118ec0af31cd13c8ce
data/lib/commiti.rb CHANGED
@@ -10,6 +10,7 @@ require_relative 'services/helpers/prompt_builder'
10
10
  require_relative 'services/helpers/interactive_prompt'
11
11
  require_relative 'services/git/pr/pr_opener'
12
12
  require_relative 'services/helpers/clipboard'
13
+ require_relative 'services/helpers/terminal_ui'
13
14
  require_relative 'services/helpers/spinner'
14
15
  require_relative 'services/flow_context_builder'
15
16
  require_relative 'services/message_generator'
@@ -51,7 +51,8 @@ module Commiti
51
51
  end
52
52
 
53
53
  def run_single_group_context(context:, client:, model:)
54
- puts "\nAuto-split found a single connected change group. Falling back to single commit flow."
54
+ message = 'Auto-split found a single connected change group. Falling back to single commit flow.'
55
+ puts "\n#{Commiti::TerminalUI.status(:info, message)}"
55
56
  Commiti::MessagePresenter.print_summarization_notice(context[:summarized_result])
56
57
 
57
58
  message = generate_message_for_context(context:, client:, model:)
@@ -63,7 +64,7 @@ module Commiti
63
64
  groups = context[:change_groups]
64
65
  run_stage('Unstaging current index for grouped commit execution') { Commiti::GitWriter.unstage_all! }
65
66
 
66
- puts "\nAuto-split detected #{groups.length} connected change groups."
67
+ puts "\n#{Commiti::TerminalUI.status(:info, "Auto-split detected #{groups.length} connected change groups.")}"
67
68
 
68
69
  groups.each_with_index do |group, index|
69
70
  break if process_group(group:, index:, total: groups.length, client:, model:) == :stop
@@ -74,7 +75,7 @@ module Commiti
74
75
  run_stage("Staging files for group #{index + 1}/#{total}") { Commiti::GitWriter.stage_files!(group[:files]) }
75
76
  return :continue unless run_stage('Checking staged changes') { Commiti::GitWriter.staged_changes? }
76
77
 
77
- puts "\nGroup #{index + 1}/#{total} files:"
78
+ puts "\n#{Commiti::TerminalUI.header("Group #{index + 1}/#{total} files")}:"
78
79
  group[:files].each { |path| puts "- #{path}" }
79
80
 
80
81
  group_context = build_context(diff: group_diff(group), client:, model:)
@@ -84,7 +85,8 @@ module Commiti
84
85
  maybe_copy_to_clipboard(message)
85
86
  return :continue if finalize(message) == :committed
86
87
 
87
- puts "Stopping auto-split flow at group #{index + 1} because commit was skipped."
88
+ stop_message = "Stopping auto-split flow at group #{index + 1} because commit was skipped."
89
+ puts Commiti::TerminalUI.status(:warn, stop_message)
88
90
  run_stage('Restaging remaining uncommitted changes') { Commiti::GitWriter.stage_all! }
89
91
  :stop
90
92
  end
@@ -12,13 +12,13 @@ module Commiti
12
12
  when :yes
13
13
  errors = Commiti::InteractivePrompt.commit_message_errors(working_message)
14
14
  unless errors.empty?
15
- puts "\nCurrent message needs fixes before commit:"
15
+ puts "\n#{Commiti::TerminalUI.status(:warn, 'Current message needs fixes before commit:')}"
16
16
  errors.each { |error| puts "- #{error}" }
17
17
 
18
18
  if Commiti::InteractivePrompt.ask_yes_no('Open editor to fix now?', default: :yes)
19
19
  edited = edit_message_until_valid(working_message)
20
20
  if edited.nil?
21
- puts "\nEditor did not exit successfully. Commit skipped.\n\n"
21
+ puts "\n#{Commiti::TerminalUI.status(:fail, 'Editor did not exit successfully. Commit skipped.')}\n\n"
22
22
  return :skipped
23
23
  end
24
24
 
@@ -27,25 +27,25 @@ module Commiti
27
27
  next
28
28
  end
29
29
 
30
- puts "\nCommit skipped.\n\n"
30
+ puts "\n#{Commiti::TerminalUI.status(:warn, 'Commit skipped.')}\n\n"
31
31
  return :skipped
32
32
  end
33
33
 
34
34
  output = run_stage.call('Writing commit') { Commiti::GitWriter.commit_with_message_file(working_message) }
35
35
  puts output unless output.to_s.strip.empty?
36
- puts "\nCommit created.\n\n"
36
+ puts "\n#{Commiti::TerminalUI.status(:success, 'Commit created.')}\n\n"
37
37
  return :committed
38
38
  when :edit
39
39
  edited = edit_message_until_valid(working_message)
40
40
  if edited.nil?
41
- puts "\nEditor did not exit successfully.\n\n"
41
+ puts "\n#{Commiti::TerminalUI.status(:fail, 'Editor did not exit successfully.')}\n\n"
42
42
  next
43
43
  end
44
44
 
45
45
  working_message = edited
46
46
  print_message.call(working_message)
47
47
  else
48
- puts "\nCommit skipped.\n\n"
48
+ puts "\n#{Commiti::TerminalUI.status(:warn, 'Commit skipped.')}\n\n"
49
49
  return :skipped
50
50
  end
51
51
  end
@@ -59,7 +59,7 @@ module Commiti
59
59
  return nil if edited.nil?
60
60
 
61
61
  if edited == working.to_s.strip
62
- puts "\nNo changes detected in editor."
62
+ puts "\n#{Commiti::TerminalUI.status(:info, 'No changes detected in editor.')}"
63
63
  return edited unless Commiti::InteractivePrompt.ask_yes_no('Re-open editor now?', default: :yes)
64
64
 
65
65
  next
@@ -68,7 +68,7 @@ module Commiti
68
68
  errors = Commiti::InteractivePrompt.commit_message_errors(edited)
69
69
  return edited if errors.empty?
70
70
 
71
- puts "\nEdited message needs fixes:"
71
+ puts "\n#{Commiti::TerminalUI.status(:warn, 'Edited message needs fixes:')}"
72
72
  errors.each { |error| puts "- #{error}" }
73
73
  return edited unless Commiti::InteractivePrompt.ask_yes_no('Re-open editor now?', default: :yes)
74
74
 
@@ -11,11 +11,11 @@ module Commiti
11
11
  status = run_stage.call('Reading git status') { Commiti::GitWriter.status_short }
12
12
  raise 'No changes found in working tree.' if status.strip.empty?
13
13
 
14
- puts "\nCurrent git status:\n\n#{status}"
14
+ puts "\n#{Commiti::TerminalUI.header('Current git status')}\n\n#{status}"
15
15
  return unless Commiti::InteractivePrompt.ask_yes_no('Run git add -A now?', default: :no)
16
16
 
17
17
  run_stage.call('Staging changes (git add -A)') { Commiti::GitWriter.stage_all! }
18
- puts "\nStaged changes with git add -A.\n"
18
+ puts "\n#{Commiti::TerminalUI.status(:success, 'Staged changes with git add -A.')}\n"
19
19
  end
20
20
  private_class_method :maybe_stage_changes
21
21
 
@@ -26,7 +26,7 @@ module Commiti
26
26
  if Commiti::InteractivePrompt.ask_yes_no('No staged changes found. Stage all changes now with git add -A?',
27
27
  default: :yes)
28
28
  run_stage.call('Staging changes (git add -A)') { Commiti::GitWriter.stage_all! }
29
- puts "\nStaged changes with git add -A.\n"
29
+ puts "\n#{Commiti::TerminalUI.status(:success, 'Staged changes with git add -A.')}\n"
30
30
  end
31
31
 
32
32
  staged = run_stage.call('Checking staged changes') { Commiti::GitWriter.staged_changes? }
@@ -5,13 +5,8 @@ module Commiti
5
5
  FRAMES = ['|', '/', '-', '\\'].freeze
6
6
  INTERVAL_SECONDS = 0.1
7
7
 
8
- def self.run(message)
9
- unless $stdout.tty?
10
- puts "#{message}..."
11
- result = yield
12
- puts "[done] #{message}"
13
- return result
14
- end
8
+ def self.run(message, &block)
9
+ return run_without_spinner(message, &block) unless $stdout.tty?
15
10
 
16
11
  done = false
17
12
  error = nil
@@ -20,7 +15,7 @@ module Commiti
20
15
  spinner_thread = Thread.new do
21
16
  index = 0
22
17
  until done
23
- frame = FRAMES[index % FRAMES.length]
18
+ frame = Commiti::TerminalUI.color(FRAMES[index % FRAMES.length], :cyan)
24
19
  print "\r#{frame} #{message}"
25
20
  $stdout.flush
26
21
  index += 1
@@ -29,15 +24,14 @@ module Commiti
29
24
  end
30
25
 
31
26
  begin
32
- result = yield
27
+ result = block.call
33
28
  rescue StandardError => e
34
29
  error = e
35
30
  ensure
36
31
  done = true
37
32
  spinner_thread.join
38
33
 
39
- status = error.nil? ? '[done]' : '[fail]'
40
- print "\r#{status} #{message}\n"
34
+ print "\r#{final_status_line(error, message)}\n"
41
35
  $stdout.flush
42
36
  end
43
37
 
@@ -45,5 +39,19 @@ module Commiti
45
39
 
46
40
  result
47
41
  end
42
+
43
+ def self.run_without_spinner(message, &block)
44
+ puts Commiti::TerminalUI.status(:info, "#{message}...")
45
+ result = block.call
46
+ puts Commiti::TerminalUI.status(:success, message)
47
+ result
48
+ end
49
+ private_class_method :run_without_spinner
50
+
51
+ def self.final_status_line(error, message)
52
+ kind = error.nil? ? :success : :fail
53
+ Commiti::TerminalUI.status(kind, message)
54
+ end
55
+ private_class_method :final_status_line
48
56
  end
49
57
  end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Commiti
4
+ module TerminalUI
5
+ COLORS = {
6
+ green: 32,
7
+ red: 31,
8
+ yellow: 33,
9
+ blue: 34,
10
+ cyan: 36,
11
+ gray: 90,
12
+ bold: 1
13
+ }.freeze
14
+
15
+ SYMBOLS = {
16
+ success: '✅',
17
+ fail: '❌',
18
+ info: 'ℹ',
19
+ warn: '⚠'
20
+ }.freeze
21
+
22
+ def self.supports_ansi?
23
+ return false unless $stdout.tty?
24
+ return false if ENV.key?('NO_COLOR')
25
+
26
+ term = ENV.fetch('TERM', '').downcase
27
+ term != 'dumb'
28
+ end
29
+
30
+ def self.color(text, *styles)
31
+ return text unless supports_ansi?
32
+
33
+ codes = styles.filter_map { |style| COLORS[style] }
34
+ return text if codes.empty?
35
+
36
+ "\e[#{codes.join(';')}m#{text}\e[0m"
37
+ end
38
+
39
+ def self.status(kind, text)
40
+ symbol = SYMBOLS.fetch(kind, '*')
41
+ color_style = case kind
42
+ when :success then :green
43
+ when :fail then :red
44
+ when :warn then :yellow
45
+ else :blue
46
+ end
47
+ "#{color(symbol, color_style)} #{text}"
48
+ end
49
+
50
+ def self.separator(length = 60)
51
+ color('─' * length, :gray)
52
+ end
53
+
54
+ def self.header(text)
55
+ color(text, :bold, :cyan)
56
+ end
57
+ end
58
+ end
@@ -4,9 +4,9 @@ module Commiti
4
4
  module MessagePresenter
5
5
  def self.print_summarization_notice(summarized_result)
6
6
  if summarized_result[:fallback_reason]
7
- puts "\n#{summarized_result[:fallback_reason]}\n"
7
+ puts "\n#{Commiti::TerminalUI.status(:warn, summarized_result[:fallback_reason])}\n"
8
8
  elsif summarized_result[:summarized]
9
- puts "\nDiff is large - summarizing first to preserve system prompt focus...\n"
9
+ puts "\n#{Commiti::TerminalUI.status(:info, 'Diff is large summarizing first to preserve prompt focus.')}\n"
10
10
  end
11
11
  end
12
12
 
@@ -19,7 +19,7 @@ module Commiti
19
19
  print_candidates(candidates)
20
20
  selected_index = Commiti::InteractivePrompt.ask_candidate_selection(candidates.length)
21
21
  selected_message = candidates[selected_index]
22
- puts "\nUsing candidate #{selected_index + 1}."
22
+ puts "\n#{Commiti::TerminalUI.status(:info, "Using candidate #{selected_index + 1}.")}"
23
23
  print_message(selected_message)
24
24
  selected_message
25
25
  end
@@ -29,21 +29,23 @@ module Commiti
29
29
 
30
30
  copied = run_stage.call('Copying output to clipboard') { Commiti::Clipboard.copy(message) }
31
31
  if copied
32
- puts "Copied to clipboard!\n\n"
32
+ puts "#{Commiti::TerminalUI.status(:success, 'Copied output to clipboard!')}\n\n"
33
33
  else
34
- puts "Clipboard not available. Install xclip: sudo apt install xclip\n\n"
34
+ puts "#{Commiti::TerminalUI.status(:warn, 'Clipboard unavailable. Install xclip: sudo apt install xclip')}\n\n"
35
35
  end
36
36
  end
37
37
 
38
38
  def self.print_message(message)
39
- puts "\n#{'─' * 60}"
39
+ puts "\n#{Commiti::TerminalUI.separator}"
40
+ puts Commiti::TerminalUI.header('Generated output')
41
+ puts Commiti::TerminalUI.separator
40
42
  puts message
41
- puts "#{'─' * 60}\n"
43
+ puts "#{Commiti::TerminalUI.separator}\n"
42
44
  end
43
45
 
44
46
  def self.print_candidates(candidates)
45
47
  candidates.each_with_index do |candidate, index|
46
- puts "\nCandidate #{index + 1}:"
48
+ puts "\n#{Commiti::TerminalUI.header("Candidate #{index + 1}")}"
47
49
  print_message(candidate)
48
50
  end
49
51
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: commiti
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.0
4
+ version: 1.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Setoju
@@ -71,6 +71,7 @@ files:
71
71
  - lib/services/helpers/interactive_prompt.rb
72
72
  - lib/services/helpers/prompt_builder.rb
73
73
  - lib/services/helpers/spinner.rb
74
+ - lib/services/helpers/terminal_ui.rb
74
75
  - lib/services/message_generator.rb
75
76
  - lib/services/message_presenter.rb
76
77
  homepage: https://github.com/setoju/commiti