commit_msg_ai 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 +7 -0
- data/bin/commit_msg_ai +5 -0
- data/lib/commit_msg_ai/cli.rb +84 -0
- data/lib/commit_msg_ai/version.rb +5 -0
- data/lib/commit_msg_ai.rb +84 -0
- metadata +150 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 2542791ddb6bec173a486478c5eda34c727dc9f74fb048dd3dd879c987801e5a
|
4
|
+
data.tar.gz: d4248da19ff04e1c1ebeccbe9d0a664c7c0069f9fb57e3436220a57252e1704d
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 34cbf1afa69b5729d4fa977528eb47b4d36d82ac8c32da1638a2406afdcb9e89ccdebe6a306c239fd15516ee31d96a37750ea884f39e7fef2ebc20e856822a6f
|
7
|
+
data.tar.gz: 42d31537fddd5189b4efd114782d93316ef41559fc898aa7aadbc3e07b5169460b9970ead364d780b76f0bd13ce8440576641f1fc4522b7117e4648c6d881a54
|
data/bin/commit_msg_ai
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'open3'
|
4
|
+
require 'shellwords'
|
5
|
+
require 'commit_msg_ai'
|
6
|
+
|
7
|
+
module CommitMsgAi
|
8
|
+
# ================================================================================
|
9
|
+
# The `CommitMsgAi::Cli` class provides a command-line interface for generating
|
10
|
+
# commit messages using the Commit Message AI service. It fetches the staged git
|
11
|
+
# diff, communicates with the AI service to generate a commit message, and allows
|
12
|
+
# the user to confirm or edit the message before committing the changes.
|
13
|
+
# ================================================================================
|
14
|
+
class Cli
|
15
|
+
def self.run
|
16
|
+
api_token = ENV.fetch('OPENAI_ACCESS_TOKEN', nil)
|
17
|
+
|
18
|
+
unless api_token
|
19
|
+
puts 'Error: Missing OpenAI API token. Please set OPENAI_ACCESS_TOKEN in your environment variables.'
|
20
|
+
exit 1
|
21
|
+
end
|
22
|
+
|
23
|
+
diff = git_diff
|
24
|
+
if diff.empty?
|
25
|
+
puts 'No staged changes to commit.'
|
26
|
+
exit 0
|
27
|
+
end
|
28
|
+
|
29
|
+
commit_ai = CommitMsgAi::Client.new(api_token)
|
30
|
+
commit_message = commit_ai.generate_commit_message(diff)
|
31
|
+
|
32
|
+
final_message = ask_user_to_confirm_or_edit(commit_message)
|
33
|
+
subject, body = split_commit_message(final_message)
|
34
|
+
|
35
|
+
puts "\nFinal Commit Message:\n\n#{final_message}"
|
36
|
+
puts "\nRunning git commit..."
|
37
|
+
system("git commit -m #{Shellwords.escape(subject)} -m #{Shellwords.escape(body)}")
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.git_diff
|
41
|
+
stdout, stderr, status = Open3.capture3('git diff --staged')
|
42
|
+
raise "Error getting git diff: #{stderr}" unless status.success?
|
43
|
+
|
44
|
+
stdout.strip
|
45
|
+
rescue StandardError => e
|
46
|
+
puts e.message
|
47
|
+
exit 1
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.ask_user_to_confirm_or_edit(commit_message)
|
51
|
+
puts "\nSuggested Commit Message:\n\n#{commit_message}"
|
52
|
+
loop do
|
53
|
+
puts "\nDo you want to:"
|
54
|
+
puts '1. Use this message as-is.'
|
55
|
+
puts '2. Edit this message.'
|
56
|
+
puts '3. Abort the commit process.'
|
57
|
+
print "\nEnter your choice (1, 2, or 3): "
|
58
|
+
|
59
|
+
case gets.strip
|
60
|
+
when '1'
|
61
|
+
return commit_message
|
62
|
+
when '2'
|
63
|
+
puts "\nEnter your custom commit message (leave blank to keep the original):"
|
64
|
+
puts commit_message
|
65
|
+
print '> '
|
66
|
+
edited_message = $stdin.read.strip
|
67
|
+
return edited_message.empty? ? commit_message : edited_message
|
68
|
+
when '3'
|
69
|
+
puts "\nCommit process aborted."
|
70
|
+
exit 0
|
71
|
+
else
|
72
|
+
puts "\nInvalid choice. Please try again."
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def self.split_commit_message(message)
|
78
|
+
lines = message.strip.split("\n", 2)
|
79
|
+
subject = lines[0]
|
80
|
+
body = lines[1..]&.join("\n") || ''
|
81
|
+
[subject, body]
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'openai'
|
4
|
+
require_relative 'commit_msg_ai/version'
|
5
|
+
|
6
|
+
module CommitMsgAi
|
7
|
+
# ================================================================================
|
8
|
+
# This class interacts with the Commit Message AI service to process and generate
|
9
|
+
# meaningful commit messages based on staged changes in the repository.
|
10
|
+
# ================================================================================
|
11
|
+
class Client
|
12
|
+
def initialize(api_token)
|
13
|
+
@client = OpenAI::Client.new(
|
14
|
+
access_token: api_token,
|
15
|
+
log_errors: true
|
16
|
+
)
|
17
|
+
end
|
18
|
+
|
19
|
+
def generate_commit_message(diff)
|
20
|
+
prompt = generate_prompt(diff)
|
21
|
+
response = @client.chat(
|
22
|
+
parameters: {
|
23
|
+
model: 'gpt-3.5-turbo-0125',
|
24
|
+
messages: [
|
25
|
+
{ role: 'system', content: 'You are an assistant generating Git commit messages.' },
|
26
|
+
{ role: 'user', content: prompt }
|
27
|
+
]
|
28
|
+
}
|
29
|
+
)
|
30
|
+
response.dig('choices', 0, 'message', 'content') || 'Error: Unable to generate commit message.'
|
31
|
+
rescue StandardError => e
|
32
|
+
"Error communicating with OpenAI API: #{e.message}"
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def generate_prompt(diff)
|
38
|
+
<<~PROMPT
|
39
|
+
Act as a professional Ruby on Rails developer who follows "Conventional Commits", please answer the questions.
|
40
|
+
In case you do not know, "Conventional Commits" is as follows:
|
41
|
+
Summary starts here:
|
42
|
+
The Conventional Commits specification is a lightweight convention on top of commit messages. It provides an easy set of rules for creating an explicit commit history; which makes it easier to write automated tools on top of. This convention dovetails with SemVer, by describing the features, fixes, and breaking changes made in commit messages.
|
43
|
+
The commit message should be structured as follows:
|
44
|
+
|
45
|
+
<type>[optional scope]: <description>
|
46
|
+
|
47
|
+
[optional body]
|
48
|
+
|
49
|
+
[optional footer(s)]
|
50
|
+
The commit contains the following structural elements, to communicate intent to the consumers of your library:
|
51
|
+
|
52
|
+
fix: a commit of the type fix patches a bug in your codebase (this correlates with PATCH in Semantic Versioning).
|
53
|
+
feat: a commit of the type feat introduces a new feature to the codebase (this correlates with MINOR in Semantic Versioning).
|
54
|
+
BREAKING CHANGE: a commit that has a footer BREAKING CHANGE:, or appends a ! after the type/scope, introduces a breaking API change (correlating with MAJOR in Semantic Versioning). A BREAKING CHANGE can be part of commits of any type.
|
55
|
+
types other than fix: and feat: are allowed, for example @commitlint/config-conventional (based on the Angular convention) recommends build:, chore:, ci:, docs:, style:, refactor:, perf:, test:, and others.
|
56
|
+
footers other than BREAKING CHANGE: <description> may be provided and follow a convention similar to git trailer format.
|
57
|
+
Additional types are not mandated by the Conventional Commits specification, and have no implicit effect in Semantic Versioning (unless they include a BREAKING CHANGE). A scope may be provided to a commit’s type, to provide additional contextual information and is contained within parenthesis, e.g., feat(parser): add ability to parse arrays.
|
58
|
+
|
59
|
+
Examples
|
60
|
+
Commit message with description and breaking change footer
|
61
|
+
feat: allow provided config object to extend other configs
|
62
|
+
|
63
|
+
BREAKING CHANGE: `extends` key in config file is now used for extending other config files
|
64
|
+
Commit message with ! to draw attention to breaking change
|
65
|
+
feat!: send an email to the customer when a product is shipped
|
66
|
+
Commit message with scope and ! to draw attention to breaking change
|
67
|
+
feat(api)!: send an email to the customer when a product is shipped
|
68
|
+
Commit message with both ! and BREAKING CHANGE footer
|
69
|
+
chore!: drop support for Node 6
|
70
|
+
|
71
|
+
BREAKING CHANGE: use JavaScript features not available in Node 6.
|
72
|
+
Commit message with no body
|
73
|
+
docs: correct spelling of CHANGELOG
|
74
|
+
Commit message with scope
|
75
|
+
feat(lang): add Polish language
|
76
|
+
|
77
|
+
Summary ends here.
|
78
|
+
|
79
|
+
Now, create a concise, professional Git commit message using the "Conventonal Commit" without body and footer based on the following Git diff:
|
80
|
+
#{diff}
|
81
|
+
PROMPT
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
metadata
ADDED
@@ -0,0 +1,150 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: commit_msg_ai
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Michiharu Ono
|
8
|
+
bindir: bin
|
9
|
+
cert_chain: []
|
10
|
+
date: 2025-01-04 00:00:00.000000000 Z
|
11
|
+
dependencies:
|
12
|
+
- !ruby/object:Gem::Dependency
|
13
|
+
name: faraday
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
15
|
+
requirements:
|
16
|
+
- - "~>"
|
17
|
+
- !ruby/object:Gem::Version
|
18
|
+
version: '2.1'
|
19
|
+
type: :runtime
|
20
|
+
prerelease: false
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
22
|
+
requirements:
|
23
|
+
- - "~>"
|
24
|
+
- !ruby/object:Gem::Version
|
25
|
+
version: '2.1'
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: ruby-openai
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
29
|
+
requirements:
|
30
|
+
- - "~>"
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '7.3'
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - "~>"
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '7.3'
|
40
|
+
- !ruby/object:Gem::Dependency
|
41
|
+
name: bundler-audit
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - "~>"
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '0.9'
|
47
|
+
type: :development
|
48
|
+
prerelease: false
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - "~>"
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0.9'
|
54
|
+
- !ruby/object:Gem::Dependency
|
55
|
+
name: github_changelog_generator
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - "~>"
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '1.16'
|
61
|
+
type: :development
|
62
|
+
prerelease: false
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - "~>"
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '1.16'
|
68
|
+
- !ruby/object:Gem::Dependency
|
69
|
+
name: pry
|
70
|
+
requirement: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - "~>"
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: 0.14.0
|
75
|
+
type: :development
|
76
|
+
prerelease: false
|
77
|
+
version_requirements: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - "~>"
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: 0.14.0
|
82
|
+
- !ruby/object:Gem::Dependency
|
83
|
+
name: rspec
|
84
|
+
requirement: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - "~>"
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '3.13'
|
89
|
+
type: :development
|
90
|
+
prerelease: false
|
91
|
+
version_requirements: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - "~>"
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: '3.13'
|
96
|
+
- !ruby/object:Gem::Dependency
|
97
|
+
name: rubocop
|
98
|
+
requirement: !ruby/object:Gem::Requirement
|
99
|
+
requirements:
|
100
|
+
- - "~>"
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: '1.6'
|
103
|
+
type: :development
|
104
|
+
prerelease: false
|
105
|
+
version_requirements: !ruby/object:Gem::Requirement
|
106
|
+
requirements:
|
107
|
+
- - "~>"
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '1.6'
|
110
|
+
description: CLI tool that leverages OpenAI to generate commit messages following
|
111
|
+
the Conventional Commits standard.
|
112
|
+
email: michiharuono77@gmail.com
|
113
|
+
executables:
|
114
|
+
- commit_msg_ai
|
115
|
+
extensions: []
|
116
|
+
extra_rdoc_files: []
|
117
|
+
files:
|
118
|
+
- bin/commit_msg_ai
|
119
|
+
- lib/commit_msg_ai.rb
|
120
|
+
- lib/commit_msg_ai/cli.rb
|
121
|
+
- lib/commit_msg_ai/version.rb
|
122
|
+
licenses:
|
123
|
+
- MIT
|
124
|
+
metadata:
|
125
|
+
rubygems_mfa_required: 'true'
|
126
|
+
post_install_message: |
|
127
|
+
After installing the gem, please run the following command to install Node.js dependencies:
|
128
|
+
npm install -g standard-version
|
129
|
+
|
130
|
+
You can then add a changelog by running:
|
131
|
+
npx standard-version --no-tag
|
132
|
+
This refers the version defined in the package.json file.
|
133
|
+
rdoc_options: []
|
134
|
+
require_paths:
|
135
|
+
- lib
|
136
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
137
|
+
requirements:
|
138
|
+
- - ">="
|
139
|
+
- !ruby/object:Gem::Version
|
140
|
+
version: '3.3'
|
141
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - ">="
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0'
|
146
|
+
requirements: []
|
147
|
+
rubygems_version: 3.6.2
|
148
|
+
specification_version: 4
|
149
|
+
summary: Generate commit messages effortlessly using OpenAI.
|
150
|
+
test_files: []
|