git_auto 0.1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 18b1522d4ba03cddfe1536bb6ee06cab7b12c7b3368c1883ebda20825c89673f
4
+ data.tar.gz: 62e9d5d3a7f0b89292ed993ab601a63fa91134abb49ecda8bbbc349384a3aff8
5
+ SHA512:
6
+ metadata.gz: e3ee6ea8c6e9de921c13bb63b0c5d0e9232754b3cca4c39d1bc2de4b6cabb54ad1f0800ced7cafbaa60a13f22d5b5cd0ebff85541912c5b7dd670fd8d0982f86
7
+ data.tar.gz: a9f369e2008ed297e21fa88f049ba36ed3446ad72ecdc845537bcd20529c947bc31df69fc7195b554ff7b094ce139ff8717735b9b3b5124099ab86292b3a7bce
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ ## [Unreleased]
2
+
3
+ ## [0.1.0] - 2024-12-12
4
+
5
+ - Initial release
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2024 Guillermo Diaz
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,113 @@
1
+ # GitAuto šŸ¤–āœØ
2
+
3
+ > AI-powered commit messages that make sense
4
+
5
+ GitAuto is a Ruby gem that streamlines your git workflow by automatically generating meaningful commit messages using AI. Say goodbye to generic commit messages and hello to clear, consistent, and informative descriptions of your changes.
6
+
7
+ ## Features šŸš€
8
+
9
+ - šŸŽÆ **Intelligent Analysis**: Automatically analyzes your staged changes
10
+ - šŸ’” **Smart Generation**: Creates conventional commit messages that actually make sense
11
+ - šŸŽØ **Beautiful CLI**: Interactive and colorful command-line interface
12
+ - šŸ“‹ **Clipboard Integration**: Easy copying of generated messages
13
+ - šŸ” **Diff Preview**: Optional preview of changes before message generation
14
+ - šŸ“ **Message History**: Keeps track of your commit messages
15
+ - šŸ¤– **AI Providers**: Supports multiple AI providers:
16
+ - OpenAI (GPT-4o, GPT-4o mini)
17
+ - Anthropic (Claude 3.5 Sonnet, Claude 3.5 Haiku)
18
+ - šŸ”’ **Secure Storage**: Your API keys are encrypted using AES-256-CBC and stored securely
19
+
20
+ ## Installation šŸ’Ž
21
+
22
+ ```bash
23
+ gem install git_auto
24
+ ```
25
+
26
+ Or add to your Gemfile:
27
+
28
+ ```ruby
29
+ gem 'git_auto'
30
+ ```
31
+
32
+ ## Usage šŸ› ļø
33
+
34
+ 1. Stage your changes as usual:
35
+ ```bash
36
+ git add .
37
+ ```
38
+
39
+ 2. Generate a commit message:
40
+ ```bash
41
+ git-auto commit
42
+ ```
43
+
44
+ 3. Review, edit if needed, and confirm!
45
+
46
+ ## Setup and Configuration šŸ”§
47
+
48
+ ### Initial Setup
49
+
50
+ Run the setup wizard to configure GitAuto:
51
+ ```bash
52
+ git-auto setup
53
+ ```
54
+ This will guide you through:
55
+ - Setting up your preferred AI provider
56
+ - Configuring your API keys (stored securely with AES-256-CBC encryption)
57
+ - Setting default preferences
58
+
59
+ ### Configuration Management
60
+
61
+ Manage your settings anytime with:
62
+ ```bash
63
+ git-auto config
64
+ ```
65
+
66
+ This allows you to:
67
+ - Update API keys
68
+ - Change AI provider settings
69
+ - Modify default behaviors
70
+ - View current configuration
71
+
72
+ ### Security šŸ”
73
+
74
+ GitAuto takes security seriously:
75
+ - API keys are encrypted using AES-256-CBC encryption
76
+ - Keys are stored in your system's user directory (`~/.git_auto/credentials.yml`)
77
+ - You can set a custom encryption key via `GIT_AUTO_SECRET` environment variable
78
+
79
+ ### Environment Variables
80
+
81
+ GitAuto can also be configured through environment variables:
82
+
83
+ - `OPENAI_API_KEY`: Your OpenAI API key for message generation
84
+ - `GIT_AUTO_MODEL`: OpenAI model to use (default: gpt-3.5-turbo)
85
+ - `GIT_AUTO_SECRET`: Custom encryption key for storing API keys (optional)
86
+
87
+ ## Requirements āš™ļø
88
+
89
+ - Ruby >= 3.0.0
90
+ - Git repository with staged changes
91
+
92
+ ## Screenshots šŸ“ø
93
+
94
+ *Coming soon...*
95
+
96
+ ## Roadmap šŸ—ŗļø
97
+
98
+ Here's what we're planning for future releases:
99
+
100
+ - šŸ¤– Support for Google Gemini AI
101
+ - šŸ“ Automatic PR description generation
102
+ - More exciting features coming soon!
103
+
104
+ ## Contributing šŸ¤
105
+
106
+ Bug reports and pull requests are welcome on GitHub at https://github.com/diazgdev/git_auto.
107
+
108
+ ## License šŸ“„
109
+
110
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
111
+
112
+ ---
113
+ Made with ā¤ļø by [Guillermo Diaz](https://github.com/diazgdev)
data/exe/git_auto ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "git_auto"
5
+ require "git_auto/cli"
6
+
7
+ GitAuto::CLI.start(ARGV)
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "thor"
4
+ require "tty-prompt"
5
+ require "colorize"
6
+
7
+ module GitAuto
8
+ class CLI < Thor
9
+ def self.exit_on_failure?
10
+ true
11
+ end
12
+
13
+ # Add version information
14
+ map ["--version", "-v"] => :version
15
+ desc "--version, -v", "Print version"
16
+ def version
17
+ puts "git_auto version #{GitAuto::VERSION}"
18
+ end
19
+
20
+ desc "setup", "Configure GitAuto settings and API keys"
21
+ long_desc <<-LONGDESC
22
+ Interactive setup wizard for GitAuto.
23
+
24
+ This will guide you through:
25
+ * Selecting an AI provider (Claude or OpenAI)
26
+ * Configuring your API key
27
+ * Setting commit message preferences
28
+ * Configuring other options
29
+ LONGDESC
30
+ def setup
31
+ Commands::SetupCommand.new.execute
32
+ end
33
+
34
+ desc "config [get|set] [key] [value]", "View or update configuration"
35
+ long_desc <<-LONGDESC
36
+ Manage GitAuto configuration.
37
+
38
+ Examples:
39
+ git_auto config # Interactive configuration
40
+ git_auto config get # Show all settings
41
+ git_auto config get ai_provider # Show specific setting
42
+ git_auto config set ai_model gpt-4 # Update specific setting
43
+ LONGDESC
44
+ def config(*args)
45
+ Commands::ConfigCommand.new.execute(args)
46
+ end
47
+
48
+ desc "commit", "Generate AI-powered commit message"
49
+ method_option :style, type: :string, desc: "Commit message style (conventional, simple, detailed)"
50
+ method_option :scope, type: :string, desc: "Commit scope for conventional style"
51
+ method_option :preview, type: :boolean, desc: "Show diff preview before committing"
52
+ long_desc <<-LONGDESC
53
+ Generate an AI-powered commit message for your staged changes.
54
+
55
+ Examples:
56
+ git_auto commit # Use default style
57
+ git_auto commit --style simple # Use simple style
58
+ git_auto commit --style conventional --scope api # Specify scope
59
+ LONGDESC
60
+ def commit
61
+ Commands::CommitMessageCommand.new(options).execute
62
+ end
63
+
64
+ desc "analyze", "Analyze commit history patterns"
65
+ method_option :limit, type: :numeric, default: 10, desc: "Number of commits to analyze"
66
+ method_option :save, type: :boolean, desc: "Save patterns for future commits"
67
+ long_desc <<-LONGDESC
68
+ Analyze your repository's commit history to learn patterns and improve future commit messages.
69
+
70
+ Examples:
71
+ git_auto analyze # Analyze last 10 commits
72
+ git_auto analyze --limit 50 # Analyze last 50 commits
73
+ git_auto analyze --save # Save patterns for future use
74
+ LONGDESC
75
+ def analyze
76
+ Commands::HistoryAnalysisCommand.new(options).execute
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,315 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "tty-prompt"
4
+ require "tty-spinner"
5
+ require "colorize"
6
+ require "clipboard"
7
+ require_relative "../formatters/diff_formatter"
8
+ require_relative "../formatters/message_formatter"
9
+ require_relative "../validators/commit_message_validator"
10
+
11
+ module GitAuto
12
+ module Commands
13
+ class CommitMessageCommand
14
+ def initialize(options = {})
15
+ @options = options.dup
16
+ @prompt = TTY::Prompt.new
17
+ @spinner = TTY::Spinner.new("[:spinner] :message...")
18
+ @settings = Config::Settings.new
19
+ @git_service = Services::GitService.new
20
+ @ai_service = Services::AIService.new(@settings)
21
+ @history_service = Services::HistoryService.new
22
+ @retry_count = 0
23
+ end
24
+
25
+ def execute
26
+ # Get repository status
27
+ status = @git_service.repository_status
28
+ validate_repository(status)
29
+
30
+ # Get and validate changes
31
+ diff = @git_service.get_staged_diff
32
+ validate_changes(diff)
33
+
34
+ # Show diff preview if requested
35
+ show_diff_preview(diff) if show_preview?
36
+
37
+ # Generate commit message
38
+ message = generate_commit_message(diff)
39
+
40
+ # Handle the generated message
41
+ handle_message(message, diff)
42
+ rescue StandardError => e
43
+ puts "\nāŒ Error: #{e.message}".red
44
+ exit 1
45
+ end
46
+
47
+ private
48
+
49
+ # Repository and Change Validation Methods
50
+ def validate_repository(status)
51
+ return if status[:is_clean] || status[:has_staged_changes]
52
+
53
+ puts "ā„¹ļø Status:".blue
54
+ puts " Branch: #{status[:branch]}"
55
+ puts " Staged files: #{status[:staged_files].join(", ")}"
56
+ puts "\nāŒ No changes staged for commit. Use 'git add' to stage changes.".red
57
+ exit 1
58
+ end
59
+
60
+ def validate_changes(diff)
61
+ return unless diff.empty?
62
+
63
+ puts "āŒ No staged changes found. Use 'git add' first.".red
64
+ exit 1
65
+ end
66
+
67
+ # Preview Methods
68
+ def show_preview?
69
+ @options[:preview] || @settings.get(:show_diff)
70
+ end
71
+
72
+ def show_diff_preview(diff)
73
+ puts "\nšŸ“„ Changes to be committed:".blue
74
+ puts Formatters::DiffFormatter.new.format(diff)
75
+
76
+ return if @prompt.yes?("Continue with these changes?")
77
+
78
+ puts "Operation cancelled.".yellow
79
+ exit 0
80
+ end
81
+
82
+ # Message Generation Methods
83
+ def generate_commit_message(diff, options = @options)
84
+ style = get_commit_style
85
+ scope = options[:scope]
86
+
87
+ if style == "conventional" && scope.nil?
88
+ # Show recent scopes for reference
89
+ patterns = @history_service.analyze_patterns(20)
90
+ if patterns && patterns[:scopes]&.any?
91
+ puts "\nšŸ“Š Recently used scopes:".blue
92
+ patterns[:scopes].each do |scope, count|
93
+ puts " #{scope}: #{count} times"
94
+ end
95
+ end
96
+
97
+ generate_message_with_style(diff, style, nil)
98
+
99
+ else
100
+ generate_message_with_style(diff, style, scope)
101
+ end
102
+ end
103
+
104
+ def get_commit_style
105
+ @options[:style] || @settings.get(:commit_style)
106
+ end
107
+
108
+ def generate_message_with_style(diff, style, scope)
109
+ @spinner.update(message: "Generating commit message...")
110
+ @spinner.auto_spin
111
+ message = @ai_service.generate_commit_message(diff, style: style, scope: scope)
112
+ @spinner.success("āœ“ Message generated".green)
113
+ message
114
+ end
115
+
116
+ # Message Handling Methods
117
+ def handle_message(message, diff)
118
+ formatted = Formatters::MessageFormatter.new.format(message)
119
+ validation = validate_message(message)
120
+ display_message_and_validation(formatted, validation)
121
+
122
+ loop do
123
+ choice = prompt_user_action
124
+ break if handle_user_choice(choice, message, diff, validation)
125
+ end
126
+ end
127
+
128
+ def validate_message(message)
129
+ validator = Validators::CommitMessageValidator.new
130
+ validator.validate(message)
131
+ end
132
+
133
+ def display_message_and_validation(formatted_message, validation)
134
+ puts "\nšŸ“ Generated commit message:".blue
135
+ puts formatted_message
136
+
137
+ display_validation_errors(validation[:errors]) if validation[:errors].any?
138
+
139
+ return unless validation[:warnings].any?
140
+
141
+ display_validation_warnings(validation[:warnings])
142
+ end
143
+
144
+ def display_validation_errors(errors)
145
+ puts "\nāŒ Validation errors:".red
146
+ validator = Validators::CommitMessageValidator.new
147
+ errors.each { |error| puts validator.format_error(error) }
148
+ puts "\nPlease edit the message to fix these errors.".yellow
149
+ end
150
+
151
+ def display_validation_warnings(warnings)
152
+ puts "\nāš ļø Suggestions:".yellow
153
+ validator = Validators::CommitMessageValidator.new
154
+ warnings.each { |warning| puts validator.format_warning(warning) }
155
+ end
156
+
157
+ def prompt_user_action
158
+ @prompt.select("Choose an action:", {
159
+ "āœ… Accept and commit" => :accept,
160
+ "āœļø Edit message" => :edit,
161
+ "šŸ“‹ Copy to clipboard" => :copy,
162
+ "šŸ‘€ Show diff" => :diff,
163
+ "šŸ“Š Show patterns" => :patterns,
164
+ "šŸ”„ Generate new message" => :retry,
165
+ "āŒ Cancel" => :cancel
166
+ })
167
+ end
168
+
169
+ # User Action Handling Methods
170
+ def handle_user_choice(choice, message, diff, validation)
171
+ case choice
172
+ when :accept
173
+ handle_accept_action(message, diff, validation)
174
+ when :edit
175
+ handle_edit_action(message)
176
+ false
177
+ when :copy
178
+ handle_copy_action(message)
179
+ false
180
+ when :diff
181
+ show_diff_preview(diff)
182
+ false
183
+ when :patterns
184
+ show_commit_patterns
185
+ false
186
+ when :retry
187
+ handle_retry_action(diff)
188
+ false
189
+ when :cancel
190
+ cancel_operation
191
+ end
192
+ end
193
+
194
+ def handle_accept_action(message, diff, validation)
195
+ if validation[:errors].any?
196
+ puts "\nāŒ Cannot commit: Please fix validation errors first.".red
197
+ new_message = edit_message(message)
198
+ handle_message(new_message, diff)
199
+ return true
200
+ end
201
+ accept_message(message, diff)
202
+ true
203
+ end
204
+
205
+ def handle_edit_action(message)
206
+ new_message = edit_message(message)
207
+ puts "\nšŸ“ Updated message:".blue
208
+ puts Formatters::MessageFormatter.new.format(new_message)
209
+ end
210
+
211
+ def handle_copy_action(message)
212
+ Clipboard.copy(message)
213
+ puts "āœ“ Copied to clipboard".green
214
+ end
215
+
216
+ def handle_retry_action(diff)
217
+ @retry_count += 1
218
+
219
+ # Create a new options hash for this retry
220
+ retry_options = @options.transform_keys(&:to_s)
221
+ retry_options["creativity"] = [(retry_options["creativity"].to_f + 0.1), 1.0].min if retry_options["creativity"]
222
+ retry_options["retry_attempt"] = @retry_count
223
+
224
+ new_message = generate_commit_message(diff, retry_options)
225
+ puts "\nšŸ“ New message (Attempt #{@retry_count}):".blue
226
+ formatted = Formatters::MessageFormatter.new.format(new_message)
227
+ puts formatted
228
+
229
+ # Validate the message
230
+ validation = validate_message(new_message)
231
+ display_message_and_validation(formatted, validation) if validation[:errors].any? || validation[:warnings].any?
232
+
233
+ handle_message(new_message, diff)
234
+ end
235
+
236
+ # Pattern Analysis Methods
237
+ def show_commit_patterns
238
+ puts "\nšŸ“Š Analyzing commit patterns...".blue
239
+ patterns = @history_service.analyze_patterns
240
+ return unless patterns
241
+
242
+ display_pattern_section(patterns[:styles], "Commit Styles")
243
+ display_pattern_section(patterns[:types], "Common Types")
244
+ display_pattern_section(patterns[:scopes], "Frequent Scopes", count_format: true)
245
+ display_pattern_section(patterns[:common_phrases], "Common Phrases", count_format: true)
246
+
247
+ wait_for_user_input
248
+ end
249
+
250
+ def display_pattern_section(data, title, count_format: false)
251
+ return unless data&.any?
252
+
253
+ puts "\n#{title}:".cyan
254
+ display_pattern_items(data, count_format)
255
+ end
256
+
257
+ def display_pattern_items(items, count_format)
258
+ items.each do |key, value|
259
+ formatted_value = format_pattern_value(value, count_format)
260
+ puts " #{key}: #{formatted_value}"
261
+ end
262
+ end
263
+
264
+ def format_pattern_value(value, count_format)
265
+ if count_format
266
+ "#{value} times"
267
+ else
268
+ "#{value}%"
269
+ end
270
+ end
271
+
272
+ def wait_for_user_input
273
+ puts "\nPress any key to continue..."
274
+ @prompt.keypress
275
+ end
276
+
277
+ # Utility Methods
278
+ def edit_message(message)
279
+ puts "\nEdit your commit message:"
280
+ puts "Enter your commit message (press Ctrl+D or Ctrl+Z to finish)".light_black
281
+ editor_input = $stdin.gets
282
+ editor_input&.strip || message
283
+ end
284
+
285
+ def accept_message(message, diff)
286
+ @spinner.update(message: "Creating commit...")
287
+ @spinner.auto_spin
288
+ # Ensure we only use the first line
289
+ message = message.split("\n").first.strip
290
+ @git_service.commit(message)
291
+ @spinner.success("āœ“ Commit created successfully!".green)
292
+
293
+ # Save to history if enabled
294
+ save_to_history(message, diff)
295
+ end
296
+
297
+ def save_to_history(message, diff)
298
+ return unless @settings.get(:save_history)
299
+
300
+ metadata = {
301
+ files: @git_service.get_staged_files,
302
+ diff_size: diff.length,
303
+ style: @options[:style] || @settings.get(:commit_style)
304
+ }
305
+
306
+ @history_service.save_commit(message, metadata)
307
+ end
308
+
309
+ def cancel_operation
310
+ puts "Operation cancelled.".yellow
311
+ exit 0
312
+ end
313
+ end
314
+ end
315
+ end