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 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,5 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require_relative '../lib/commit_msg_ai/cli'
5
+ CommitMsgAi::Cli.run
@@ -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,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CommitMsgAi
4
+ VERSION = '0.1.0'
5
+ 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: []