aigc 0.6.0 ā 0.8.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 +4 -4
- data/CHANGELOG.md +17 -0
- data/lib/ai/commit/claude_client.rb +26 -30
- data/lib/ai/commit/cli.rb +80 -35
- data/lib/ai/commit/git.rb +42 -0
- data/lib/ai/commit/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 77df6281418a993b1d1a4575403579a734b80e26bae4d25782e55d51cd32b56e
|
|
4
|
+
data.tar.gz: 03ee82eae23ed408a3887e770125129eed42d46d39b956a8139db60d2b213bef
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f27de3cef28a0f21abed1b1d17d0891ec2740c5ba9b502f814bd6fae540535954382ceb3670ccba8f3234c2db7c2da59b37cf48e05eb97bf1c9a8fe09434e0f5
|
|
7
|
+
data.tar.gz: e763ed357ac9e15d96fdc23694175c895680ffa75fe9e5e2096a900217274d1714a63ffae5631b297d7a64bd0ff604d8648aef8b05e570ae2379946f806e0074
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [0.8.0] - 2025-01-27
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- Smart ticket number extraction from branch names
|
|
7
|
+
- Automatically detects ticket numbers like ABC-123, PROJ-456 from branch names
|
|
8
|
+
- Supports common branch naming patterns: feature/ABC-123, bugfix/PROJ-456, etc.
|
|
9
|
+
- Includes ticket numbers in commit message scope when relevant
|
|
10
|
+
- Refactored commit guidelines into a constant for DRY code
|
|
11
|
+
|
|
12
|
+
## [0.7.0] - 2025-01-27
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
- `--hash` option to generate commit messages from diff between HEAD and specified commit
|
|
16
|
+
- Users can now run `aigc --hash abc123` to generate a message for changes between commit abc123 and HEAD
|
|
17
|
+
- Useful for analyzing existing commits or generating messages for uncommitted changes since a specific commit
|
|
18
|
+
- Added `Git.commit_diff` method to get diff between two commits
|
|
19
|
+
|
|
3
20
|
## [0.6.0] - 2025-01-27
|
|
4
21
|
|
|
5
22
|
### Changed
|
|
@@ -29,30 +29,36 @@ module Ai
|
|
|
29
29
|
|
|
30
30
|
private
|
|
31
31
|
|
|
32
|
+
COMMIT_GUIDELINES = <<~GUIDELINES
|
|
33
|
+
1. Use conventional commit format: type(scope): description
|
|
34
|
+
2. Keep subject line under 50 characters
|
|
35
|
+
3. Use imperative mood ("add" not "added")
|
|
36
|
+
4. Separate subject from body with blank line
|
|
37
|
+
5. Use 'feat' for new features, 'fix' for bug fixes, 'docs' for documentation, 'style' for formatting, 'refactor' for code refactoring, 'test' for tests, 'chore' for build/tooling
|
|
38
|
+
6. Be clear and specific about what changed, not just "update"
|
|
39
|
+
7. Explain the impact/benefit when relevant
|
|
40
|
+
8. Include scope based on file structure (e.g., auth, bank, api, components)
|
|
41
|
+
9. Reference any related components or services affected
|
|
42
|
+
10. For breaking changes, start with 'BREAKING CHANGE:'
|
|
43
|
+
11. Use bullet points in body for multiple changes
|
|
44
|
+
12. Include ticket numbers in scope when available (e.g., auth(ABC-123): description)
|
|
45
|
+
13. Match existing project commit style
|
|
46
|
+
GUIDELINES
|
|
47
|
+
|
|
32
48
|
def build_prompt(diff)
|
|
33
49
|
custom_prompts = Config.custom_prompts
|
|
34
50
|
custom_prompt_text = custom_prompts.empty? ? '' : "\n\nAdditional context and preferences:\n#{custom_prompts.join("\n")}"
|
|
35
51
|
|
|
52
|
+
ticket_number = Git.extract_ticket_from_branch
|
|
53
|
+
ticket_context = ticket_number ? "\n\nTicket number from branch: #{ticket_number} (include in scope if relevant)" : ""
|
|
54
|
+
|
|
36
55
|
<<~PROMPT
|
|
37
56
|
You are a helpful assistant that generates conventional commit messages.
|
|
38
57
|
|
|
39
58
|
Based on the following git diff, generate a conventional commit message that:
|
|
40
|
-
|
|
41
|
-
2. Keep subject line under 50 characters
|
|
42
|
-
3. Use imperative mood ("add" not "added")
|
|
43
|
-
4. Separate subject from body with blank line
|
|
44
|
-
5. Wrap body at 72 characters
|
|
45
|
-
6. Use 'feat' for new features, 'fix' for bug fixes, 'docs' for documentation, 'style' for formatting, 'refactor' for code refactoring, 'test' for tests, 'chore' for build/tooling
|
|
46
|
-
7. Be clear and specific about what changed, not just "update"
|
|
47
|
-
8. Explain the impact/benefit when relevant
|
|
48
|
-
9. Include scope based on file structure (e.g., auth, bank, api, components)
|
|
49
|
-
10. Reference any related components or services affected
|
|
50
|
-
11. For breaking changes, start with 'BREAKING CHANGE:'
|
|
51
|
-
12. Use bullet points in body for multiple changes
|
|
52
|
-
13. Reference ticket numbers when applicable
|
|
53
|
-
14. Match existing project commit style
|
|
59
|
+
#{COMMIT_GUIDELINES}
|
|
54
60
|
|
|
55
|
-
#{custom_prompt_text}
|
|
61
|
+
#{custom_prompt_text}#{ticket_context}
|
|
56
62
|
|
|
57
63
|
Git diff:
|
|
58
64
|
#{diff}
|
|
@@ -65,28 +71,18 @@ module Ai
|
|
|
65
71
|
custom_prompts = Config.custom_prompts
|
|
66
72
|
custom_prompt_text = custom_prompts.empty? ? '' : "\n\nAdditional context and preferences:\n#{custom_prompts.join("\n")}"
|
|
67
73
|
|
|
74
|
+
ticket_number = Git.extract_ticket_from_branch
|
|
75
|
+
ticket_context = ticket_number ? "\n\nTicket number from branch: #{ticket_number} (include in scope if relevant)" : ""
|
|
76
|
+
|
|
68
77
|
feedback_text = feedback_array.map.with_index { |feedback, i| "#{i + 1}. #{feedback}" }.join("\n")
|
|
69
78
|
|
|
70
79
|
<<~PROMPT
|
|
71
80
|
You are a helpful assistant that generates conventional commit messages.
|
|
72
81
|
|
|
73
82
|
Based on the following git diff, generate a conventional commit message that:
|
|
74
|
-
|
|
75
|
-
2. Keep subject line under 50 characters
|
|
76
|
-
3. Use imperative mood ("add" not "added")
|
|
77
|
-
4. Separate subject from body with blank line
|
|
78
|
-
5. Wrap body at 72 characters
|
|
79
|
-
6. Use 'feat' for new features, 'fix' for bug fixes, 'docs' for documentation, 'style' for formatting, 'refactor' for code refactoring, 'test' for tests, 'chore' for build/tooling
|
|
80
|
-
7. Be clear and specific about what changed, not just "update"
|
|
81
|
-
8. Explain the impact/benefit when relevant
|
|
82
|
-
9. Include scope based on file structure (e.g., auth, bank, api, components)
|
|
83
|
-
10. Reference any related components or services affected
|
|
84
|
-
11. For breaking changes, start with 'BREAKING CHANGE:'
|
|
85
|
-
12. Use bullet points in body for multiple changes
|
|
86
|
-
13. Reference ticket numbers when applicable
|
|
87
|
-
14. Match existing project commit style
|
|
83
|
+
#{COMMIT_GUIDELINES}
|
|
88
84
|
|
|
89
|
-
#{custom_prompt_text}
|
|
85
|
+
#{custom_prompt_text}#{ticket_context}
|
|
90
86
|
|
|
91
87
|
IMPORTANT: The user has provided feedback on previous attempts. Please address ALL of the following feedback:
|
|
92
88
|
#{feedback_text}
|
data/lib/ai/commit/cli.rb
CHANGED
|
@@ -70,6 +70,7 @@ module Ai
|
|
|
70
70
|
end
|
|
71
71
|
|
|
72
72
|
desc "commit", "Generate a commit message and commit with confirmation"
|
|
73
|
+
option :hash, :type => :string, :desc => "Generate message from diff between HEAD and specified commit hash"
|
|
73
74
|
def commit
|
|
74
75
|
unless Config.api_key_configured?
|
|
75
76
|
puts "ā No API key configured. Please run 'aigc setup' first."
|
|
@@ -81,15 +82,32 @@ module Ai
|
|
|
81
82
|
exit 1
|
|
82
83
|
end
|
|
83
84
|
|
|
84
|
-
|
|
85
|
-
puts "
|
|
86
|
-
|
|
85
|
+
if options[:hash]
|
|
86
|
+
puts "š¤ Generating commit message from diff between HEAD and #{options[:hash]}..."
|
|
87
|
+
|
|
88
|
+
begin
|
|
89
|
+
diff = Git.commit_diff(options[:hash])
|
|
90
|
+
rescue => e
|
|
91
|
+
puts "ā Error getting commit diff: #{e.message}"
|
|
92
|
+
exit 1
|
|
93
|
+
end
|
|
94
|
+
else
|
|
95
|
+
unless Git.has_staged_changes?
|
|
96
|
+
puts "ā No staged changes found. Stage your changes with 'git add' first."
|
|
97
|
+
exit 1
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
puts "š¤ Generating commit message..."
|
|
101
|
+
|
|
102
|
+
begin
|
|
103
|
+
diff = Git.staged_diff
|
|
104
|
+
rescue => e
|
|
105
|
+
puts "ā Error getting staged diff: #{e.message}"
|
|
106
|
+
exit 1
|
|
107
|
+
end
|
|
87
108
|
end
|
|
88
109
|
|
|
89
|
-
puts "š¤ Generating commit message..."
|
|
90
|
-
|
|
91
110
|
begin
|
|
92
|
-
diff = Git.staged_diff
|
|
93
111
|
client = ClaudeClient.new(Config.api_key)
|
|
94
112
|
message = client.generate_commit_message(diff)
|
|
95
113
|
|
|
@@ -97,25 +115,21 @@ module Ai
|
|
|
97
115
|
puts "=" * 50
|
|
98
116
|
puts message
|
|
99
117
|
puts "=" * 50
|
|
100
|
-
|
|
101
|
-
print "\nCommit with this message? (y/N/e to edit/r to regenerate): "
|
|
102
|
-
response = STDIN.gets.chomp.downcase
|
|
103
|
-
|
|
104
|
-
if response == 'y' || response == 'yes'
|
|
105
|
-
puts "\nš Committing..."
|
|
106
|
-
result = system("git", "commit", "-m", message)
|
|
107
118
|
|
|
108
|
-
if
|
|
109
|
-
|
|
119
|
+
if options[:hash]
|
|
120
|
+
print "\nCopy this message? (y/N/e to edit/r to regenerate): "
|
|
110
121
|
else
|
|
111
|
-
|
|
112
|
-
exit 1
|
|
122
|
+
print "\nCommit with this message? (y/N/e to edit/r to regenerate): "
|
|
113
123
|
end
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
124
|
+
response = STDIN.gets.chomp.downcase
|
|
125
|
+
|
|
126
|
+
if response == 'y' || response == 'yes'
|
|
127
|
+
if options[:hash]
|
|
128
|
+
puts "\nš Message ready to copy:"
|
|
129
|
+
puts message
|
|
130
|
+
else
|
|
131
|
+
puts "\nš Committing..."
|
|
132
|
+
result = system("git", "commit", "-m", message)
|
|
119
133
|
|
|
120
134
|
if result
|
|
121
135
|
puts "ā
Successfully committed!"
|
|
@@ -123,14 +137,36 @@ module Ai
|
|
|
123
137
|
puts "ā Failed to commit"
|
|
124
138
|
exit 1
|
|
125
139
|
end
|
|
140
|
+
end
|
|
141
|
+
elsif response == 'e' || response == 'edit'
|
|
142
|
+
edited_message = edit_message(message)
|
|
143
|
+
if edited_message && !edited_message.strip.empty?
|
|
144
|
+
if options[:hash]
|
|
145
|
+
puts "\nš Edited message ready to copy:"
|
|
146
|
+
puts edited_message
|
|
147
|
+
else
|
|
148
|
+
puts "\nš Committing with edited message..."
|
|
149
|
+
result = system("git", "commit", "-m", edited_message)
|
|
150
|
+
|
|
151
|
+
if result
|
|
152
|
+
puts "ā
Successfully committed!"
|
|
153
|
+
else
|
|
154
|
+
puts "ā Failed to commit"
|
|
155
|
+
exit 1
|
|
156
|
+
end
|
|
157
|
+
end
|
|
126
158
|
else
|
|
127
|
-
puts "ā
|
|
159
|
+
puts "ā Operation cancelled."
|
|
128
160
|
exit 0
|
|
129
161
|
end
|
|
130
162
|
elsif response == 'r' || response == 'regenerate'
|
|
131
|
-
regenerate_with_feedback(diff, client, 1, [])
|
|
163
|
+
regenerate_with_feedback(diff, client, 1, [], options[:hash])
|
|
132
164
|
else
|
|
133
|
-
|
|
165
|
+
if options[:hash]
|
|
166
|
+
puts "ā Operation cancelled."
|
|
167
|
+
else
|
|
168
|
+
puts "ā Commit cancelled."
|
|
169
|
+
end
|
|
134
170
|
exit 0
|
|
135
171
|
end
|
|
136
172
|
|
|
@@ -219,7 +255,7 @@ module Ai
|
|
|
219
255
|
edited_content
|
|
220
256
|
end
|
|
221
257
|
|
|
222
|
-
def regenerate_with_feedback(diff, client, attempt = 1, accumulated_feedback = [])
|
|
258
|
+
def regenerate_with_feedback(diff, client, attempt = 1, accumulated_feedback = [], is_hash_mode = false)
|
|
223
259
|
if attempt > 3
|
|
224
260
|
puts "\nā ļø Maximum regenerations (3) reached. Please commit with the current message or edit it manually."
|
|
225
261
|
return
|
|
@@ -251,18 +287,27 @@ module Ai
|
|
|
251
287
|
puts new_message
|
|
252
288
|
puts "=" * 50
|
|
253
289
|
|
|
254
|
-
|
|
290
|
+
if is_hash_mode
|
|
291
|
+
print "\nCopy this message? (y/N/e to edit/r to regenerate again): "
|
|
292
|
+
else
|
|
293
|
+
print "\nCommit with this message? (y/N/e to edit/r to regenerate again): "
|
|
294
|
+
end
|
|
255
295
|
response = STDIN.gets.chomp.downcase
|
|
256
296
|
|
|
257
297
|
if response == 'y' || response == 'yes'
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
if result
|
|
262
|
-
puts "ā
Successfully committed!"
|
|
298
|
+
if is_hash_mode
|
|
299
|
+
puts "\nš Message ready to copy:"
|
|
300
|
+
puts new_message
|
|
263
301
|
else
|
|
264
|
-
puts "
|
|
265
|
-
|
|
302
|
+
puts "\nš Committing..."
|
|
303
|
+
result = system("git", "commit", "-m", new_message)
|
|
304
|
+
|
|
305
|
+
if result
|
|
306
|
+
puts "ā
Successfully committed!"
|
|
307
|
+
else
|
|
308
|
+
puts "ā Failed to commit"
|
|
309
|
+
exit 1
|
|
310
|
+
end
|
|
266
311
|
end
|
|
267
312
|
elsif response == 'e' || response == 'edit'
|
|
268
313
|
edited_message = edit_message(new_message)
|
|
@@ -281,7 +326,7 @@ module Ai
|
|
|
281
326
|
exit 0
|
|
282
327
|
end
|
|
283
328
|
elsif response == 'r' || response == 'regenerate'
|
|
284
|
-
regenerate_with_feedback(diff, client, attempt + 1, all_feedback)
|
|
329
|
+
regenerate_with_feedback(diff, client, attempt + 1, all_feedback, is_hash_mode)
|
|
285
330
|
else
|
|
286
331
|
puts "ā Commit cancelled."
|
|
287
332
|
exit 0
|
data/lib/ai/commit/git.rb
CHANGED
|
@@ -24,6 +24,48 @@ module Ai
|
|
|
24
24
|
def self.has_staged_changes?
|
|
25
25
|
!`git diff --cached --name-only`.strip.empty?
|
|
26
26
|
end
|
|
27
|
+
|
|
28
|
+
def self.current_branch
|
|
29
|
+
result = `git branch --show-current`
|
|
30
|
+
result.strip if $?.exitstatus == 0
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def self.extract_ticket_from_branch
|
|
34
|
+
branch_name = current_branch
|
|
35
|
+
return nil unless branch_name
|
|
36
|
+
|
|
37
|
+
# Common patterns: ABC-123, PROJ-456, JIRA-789, etc.
|
|
38
|
+
# Look for patterns like: feature/ABC-123, bugfix/PROJ-456, hotfix/JIRA-789
|
|
39
|
+
ticket_match = branch_name.match(/(?:feature|bugfix|hotfix|fix|feat|chore|docs|style|refactor|test|perf|revert)\/([A-Z]+-\d+)/i)
|
|
40
|
+
|
|
41
|
+
if ticket_match
|
|
42
|
+
return ticket_match[1]
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Also check for ticket numbers without prefixes
|
|
46
|
+
ticket_match = branch_name.match(/([A-Z]+-\d+)/)
|
|
47
|
+
ticket_match ? ticket_match[1] : nil
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def self.commit_diff(commit_hash)
|
|
51
|
+
# Validate the commit hash exists
|
|
52
|
+
unless system("git rev-parse --verify #{commit_hash}", out: File::NULL, err: File::NULL)
|
|
53
|
+
raise Error, "Commit hash '#{commit_hash}' not found"
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Get the diff between HEAD and the specified commit
|
|
57
|
+
result = `git diff #{commit_hash}..HEAD`
|
|
58
|
+
|
|
59
|
+
if $?.exitstatus != 0
|
|
60
|
+
raise Error, "Failed to get diff between #{commit_hash} and HEAD"
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
if result.empty?
|
|
64
|
+
raise Error, "No differences found between #{commit_hash} and HEAD"
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
result
|
|
68
|
+
end
|
|
27
69
|
end
|
|
28
70
|
end
|
|
29
71
|
end
|
data/lib/ai/commit/version.rb
CHANGED