committer 0.5.0 → 0.5.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 +4 -4
- data/README.md +66 -0
- data/lib/committer/commit_generator.rb +4 -5
- data/lib/committer/config/accessor.rb +26 -34
- data/lib/committer/config/constants.rb +5 -3
- data/lib/committer/config/defaults/commit_message_and_body.prompt +52 -0
- data/lib/committer/config/defaults/commit_message_only.prompt +49 -0
- data/lib/committer/config/writer.rb +0 -10
- data/lib/committer/git_helper.rb +1 -1
- data/lib/committer/prompt_templates.rb +16 -49
- data/lib/committer/version.rb +1 -1
- metadata +5 -3
- /data/lib/committer/{formatting_rules.txt → config/defaults/formatting_rules.txt} +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 15d7ba36e033d0d50db617ab0e02673cead10eea1fa8a86e95180bed40ea3734
|
4
|
+
data.tar.gz: b7e3e8ddb14cf500b1bce8bec83ad1f4ce9f345bf950945365b400550e6a6851
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5a180861a3641814d2f517194639f57178c8b4470331a21c1b8a7e404ea437c1c2b4b1ab750c8b1588aee56e0bc2b5837ee6a95e92c72088c84d385268046c2c
|
7
|
+
data.tar.gz: 3a9efb4acca99255a025eea8caa25f9d4583b8a75583f82db363769f64d250f6b195dc83a9fc74428e81c93aa17ae9256c46e66c4f24394fff9d8ae87d71e5e4
|
data/README.md
CHANGED
@@ -96,6 +96,7 @@ committer setup-git-hook
|
|
96
96
|
```
|
97
97
|
|
98
98
|
This command will:
|
99
|
+
|
99
100
|
1. Verify you're in the root of a git repository
|
100
101
|
2. Install the prepare-commit-msg hook in your `.git/hooks` directory
|
101
102
|
3. Make the hook executable
|
@@ -103,6 +104,7 @@ This command will:
|
|
103
104
|
### How the Git Hook Works
|
104
105
|
|
105
106
|
Once installed, the git hook will:
|
107
|
+
|
106
108
|
1. Automatically run whenever you execute `git commit`
|
107
109
|
2. Generate an AI-powered commit message based on your staged changes
|
108
110
|
3. Pre-fill your commit message editor with the generated message
|
@@ -127,6 +129,70 @@ bundle install
|
|
127
129
|
bundle exec rake spec
|
128
130
|
```
|
129
131
|
|
132
|
+
## Evaluation Tools
|
133
|
+
|
134
|
+
The `eval` directory contains tools for benchmarking and testing commit message generation across different AI models. These tools help evaluate the quality and consistency of generated commit messages.
|
135
|
+
|
136
|
+
### Usage
|
137
|
+
|
138
|
+
1. **Install dependencies**:
|
139
|
+
|
140
|
+
```bash
|
141
|
+
npm install
|
142
|
+
npm install -g promptfoo
|
143
|
+
```
|
144
|
+
|
145
|
+
2. **Set environment variables**:
|
146
|
+
|
147
|
+
```bash
|
148
|
+
export ANTHROPIC_API_KEY=your_anthropic_api_key_here
|
149
|
+
export OPENAI_API_KEY=your_openai_api_key_here
|
150
|
+
```
|
151
|
+
|
152
|
+
3. **Dump commits** from a repository to a database:
|
153
|
+
|
154
|
+
```bash
|
155
|
+
node eval.mjs dump-commits --eval-data-dir ./path --repo ./repo-path
|
156
|
+
```
|
157
|
+
|
158
|
+
4. **Process commits** for evaluation:
|
159
|
+
|
160
|
+
```bash
|
161
|
+
node eval.mjs process-commits --eval-data-dir ./path
|
162
|
+
```
|
163
|
+
|
164
|
+
5. **Run evaluations** against different AI models:
|
165
|
+
```bash
|
166
|
+
node eval.mjs run-evaluation --eval-data-dir ./path [--sha commit_sha] [--limit number]
|
167
|
+
```
|
168
|
+
|
169
|
+
The system compares multiple AI models (Claude 3.7 Sonnet, Claude 3.5 Haiku, GPT-4o, GPT-4o Mini) and tests their ability to generate properly formatted conventional commit messages based on commit diffs.
|
170
|
+
|
171
|
+
### Viewing Evaluation Results prompt versions
|
172
|
+
|
173
|
+
select your verison from https://drive.google.com/drive/folders/1xHpOkNSss2PM-cnLKGaCHLNfWvxl9hAX
|
174
|
+
|
175
|
+
download .promptfoo file and move it to ~/.promptfoo promptfoo.sqlite
|
176
|
+
|
177
|
+
```bash
|
178
|
+
npm install -g promptfoo
|
179
|
+
promptfoo view -y
|
180
|
+
```
|
181
|
+
|
182
|
+
### Running Evaluation for prompt versions
|
183
|
+
|
184
|
+
select your verison from https://drive.google.com/drive/folders/1xHpOkNSss2PM-cnLKGaCHLNfWvxl9hAX
|
185
|
+
|
186
|
+
download .electron-eval folder and move it the root fo this project
|
187
|
+
|
188
|
+
```bash
|
189
|
+
npm install
|
190
|
+
npm install -g promptfoo
|
191
|
+
export ANTHROPIC_API_KEY=your_anthropic_api_key_here
|
192
|
+
export OPENAI_API_KEY=your_openai_api_key_here
|
193
|
+
node eval.mjs run-evaluation --eval-data-dir ./electron-eval
|
194
|
+
```
|
195
|
+
|
130
196
|
## License
|
131
197
|
|
132
198
|
MIT
|
@@ -16,9 +16,8 @@ module Committer
|
|
16
16
|
end
|
17
17
|
|
18
18
|
def build_commit_prompt
|
19
|
-
|
20
|
-
|
21
|
-
commit_context: @commit_context)
|
19
|
+
scopes = Committer::Config::Accessor.instance[:scopes] || []
|
20
|
+
Committer::PromptTemplates.build_prompt(diff, scopes, commit_context)
|
22
21
|
end
|
23
22
|
|
24
23
|
def template
|
@@ -55,8 +54,8 @@ module Committer
|
|
55
54
|
end
|
56
55
|
end
|
57
56
|
|
58
|
-
def prepare_commit_message
|
59
|
-
client =
|
57
|
+
def prepare_commit_message(client_class = Clients::ClaudeClient)
|
58
|
+
client = client_class.new
|
60
59
|
|
61
60
|
prompt = build_commit_prompt
|
62
61
|
response = client.post(prompt)
|
@@ -28,60 +28,52 @@ module Committer
|
|
28
28
|
|
29
29
|
def load_config
|
30
30
|
# Load configs from both locations and merge them
|
31
|
-
|
32
|
-
|
31
|
+
|
32
|
+
home_config = load_yml_file(read_file_from_home(Committer::Config::Constants::CONFIG_FILE_NAME))
|
33
|
+
git_root_config = load_yml_file(read_file_from_git_root(Committer::Config::Constants::CONFIG_FILE_NAME))
|
33
34
|
raise Committer::ConfigErrors::NotSetup if home_config.empty? && git_root_config.empty?
|
34
35
|
|
36
|
+
unless home_config.is_a?(Hash) && git_root_config.is_a?(Hash)
|
37
|
+
raise Committer::ConfigErrors::FormatError,
|
38
|
+
'Config file must be a YAML hash'
|
39
|
+
end
|
40
|
+
|
35
41
|
# Merge configs with git root taking precedence
|
36
42
|
home_config.merge(git_root_config)
|
37
43
|
end
|
38
44
|
|
39
|
-
def
|
40
|
-
|
41
|
-
|
42
|
-
result = YAML.load_file(path)
|
43
|
-
raise Committer::ConfigErrors::FormatError, 'Config file must be a YAML hash' unless result.is_a?(Hash)
|
44
|
-
|
45
|
-
result
|
45
|
+
def load_yml_file(contents)
|
46
|
+
YAML.safe_load(contents, permitted_classes: [Symbol, NilClass, String, Array]) || {}
|
46
47
|
end
|
47
48
|
|
48
|
-
def
|
49
|
+
def read_file_from_path(path)
|
49
50
|
return '' unless File.exist?(path)
|
50
51
|
|
51
52
|
File.read(path)
|
52
53
|
end
|
53
54
|
|
54
|
-
def
|
55
|
-
|
56
|
-
|
57
|
-
formatting_rules_git_path = File.join(git_root, '.committer',
|
58
|
-
Committer::Config::Constants::FORMATTING_RULES_FILE_NAME)
|
59
|
-
end
|
60
|
-
|
61
|
-
git_path_contents = load_file_from_path(formatting_rules_git_path) if formatting_rules_git_path
|
55
|
+
def read_file_from_git_root(file_name)
|
56
|
+
read_file_from_path(File.join(Committer::GitHelper.repo_root, '.committer', file_name))
|
57
|
+
end
|
62
58
|
|
63
|
-
|
59
|
+
def read_file_from_home(file_name)
|
60
|
+
read_file_from_path(File.join(Committer::Config::Constants::CONFIG_DIR, file_name))
|
61
|
+
end
|
64
62
|
|
65
|
-
|
66
|
-
|
63
|
+
def load_default_file(file_name)
|
64
|
+
read_file_from_path(File.join(Committer::Config::Constants::DEFAULTS_PATH, file_name))
|
65
|
+
end
|
67
66
|
|
68
|
-
|
67
|
+
def read_path_prioritized_file(file_name)
|
68
|
+
git_path_contents = read_file_from_git_root(file_name)
|
69
69
|
|
70
|
-
return
|
70
|
+
return git_path_contents unless git_path_contents.empty?
|
71
71
|
|
72
|
-
|
73
|
-
Committer::Config::Constants::FORMATTING_RULES_FILE_NAME)
|
74
|
-
load_file_from_path(default_path)
|
75
|
-
end
|
72
|
+
home_path_contents = read_file_from_home(file_name)
|
76
73
|
|
77
|
-
|
78
|
-
git_root = Committer::GitHelper.repo_root
|
79
|
-
return {} if git_root.empty?
|
74
|
+
return home_path_contents unless home_path_contents.empty?
|
80
75
|
|
81
|
-
|
82
|
-
load_config_from_path(git_config_file)
|
83
|
-
rescue StandardError
|
84
|
-
{}
|
76
|
+
load_default_file(file_name)
|
85
77
|
end
|
86
78
|
|
87
79
|
# Force reload configuration (useful for testing)
|
@@ -4,10 +4,12 @@ module Committer
|
|
4
4
|
module Config
|
5
5
|
module Constants
|
6
6
|
CONFIG_DIR = File.join(Dir.home, '.committer')
|
7
|
-
|
8
|
-
|
9
|
-
|
7
|
+
DEFAULTS_PATH = File.join(File.dirname(__FILE__), './defaults')
|
8
|
+
|
9
|
+
COMMIT_MESSAGE_ONLY_PROMPT_FILE_NAME = 'commit_message_only.prompt'
|
10
|
+
COMMIT_MESSAGE_AND_BODY_PROMPT_FILE_NAME = 'commit_message_and_body.prompt'
|
10
11
|
CONFIG_FILE_NAME = 'config.yml'
|
12
|
+
|
11
13
|
DEFAULT_CONFIG = {
|
12
14
|
'api_key' => nil,
|
13
15
|
'model' => 'claude-3-7-sonnet-20250219',
|
@@ -0,0 +1,52 @@
|
|
1
|
+
You are an experienced software developer tasked with creating a commit message based on a git diff. Your goal is to produce a clear, concise, and informative commit message.
|
2
|
+
|
3
|
+
First, carefully analyze the following git diff:
|
4
|
+
|
5
|
+
<git_diff>
|
6
|
+
{{DIFF}}
|
7
|
+
</git_diff>
|
8
|
+
|
9
|
+
Here are the available scopes (if any):
|
10
|
+
|
11
|
+
<scopes>
|
12
|
+
{{SCOPES}}
|
13
|
+
</scopes>
|
14
|
+
|
15
|
+
<user_context>
|
16
|
+
{{CONTEXT}}
|
17
|
+
</user_context>
|
18
|
+
|
19
|
+
Please follow these instructions to generate the commit message:
|
20
|
+
|
21
|
+
1. Analyze the git diff and determine the most appropriate commit type from the following options:
|
22
|
+
- feat: A new feature
|
23
|
+
- fix: A bug fix
|
24
|
+
- docs: Documentation only changes
|
25
|
+
- style: Changes that do not affect the meaning of the code
|
26
|
+
- refactor: A code change that neither fixes a bug nor adds a feature
|
27
|
+
- perf: A code change that improves performance
|
28
|
+
- test: Adding missing tests or correcting existing tests
|
29
|
+
- chore: Changes to the build process or auxiliary tools and libraries
|
30
|
+
|
31
|
+
|
32
|
+
2. Adhere to these message guidelines:
|
33
|
+
- Keep the summary under 70 characters
|
34
|
+
- Use imperative, present tense (e.g., "add" not "added" or "adds")
|
35
|
+
- Do not end the summary with a period
|
36
|
+
- Be concise but descriptive
|
37
|
+
|
38
|
+
3. Format the commit message as follows:
|
39
|
+
- If a scope is available: <type>(<scope>): <description>
|
40
|
+
- If no scope is available: <type>: <description>
|
41
|
+
|
42
|
+
4 Body Guidelines:
|
43
|
+
- Add a blank line between summary and body
|
44
|
+
- Use the body to explain why the change was made, incorporating the user's context, defined in <user_context>
|
45
|
+
- Wrap each line in the body at 80 characters maximum
|
46
|
+
- Break the body into multiple paragraphs if needed
|
47
|
+
|
48
|
+
[Your concise commit message in the specified format]
|
49
|
+
[blank line]
|
50
|
+
[Your detailed commit message body]
|
51
|
+
|
52
|
+
Respond ONLY with the commit message text (message and body), nothing else.
|
@@ -0,0 +1,49 @@
|
|
1
|
+
You are an experienced software developer tasked with creating a commit message based on a git diff. Your goal is to produce a clear, concise, and informative commit message.
|
2
|
+
|
3
|
+
First, carefully analyze the following git diff:
|
4
|
+
|
5
|
+
<git_diff>
|
6
|
+
{{DIFF}}
|
7
|
+
</git_diff>
|
8
|
+
|
9
|
+
Here are the available scopes (if any):
|
10
|
+
|
11
|
+
<scopes>
|
12
|
+
{{SCOPES}}
|
13
|
+
</scopes>
|
14
|
+
|
15
|
+
|
16
|
+
|
17
|
+
2. Adhere to these message guidelines:
|
18
|
+
- Keep the summary under 70 characters
|
19
|
+
- Use imperative, present tense (e.g., "add" not "added" or "adds")
|
20
|
+
- Do not end the summary with a period
|
21
|
+
- Be concise but descriptive
|
22
|
+
|
23
|
+
3. Format the commit message as follows:
|
24
|
+
- If a scope is available: <type>(<scope>): <description>
|
25
|
+
- If no scope is available: <type>: <description>
|
26
|
+
|
27
|
+
|
28
|
+
Please follow these instructions to generate the commit message:
|
29
|
+
|
30
|
+
if such and such changes its docs b ut if commit also has such then its a feat
|
31
|
+
|
32
|
+
1. Analyze the git diff and determine the most appropriate commit type from the following options:
|
33
|
+
- feat: A new feature these changes ARE NOT in the docs directory
|
34
|
+
- fix: A bug fix these changes ARE NOT in the docs directory
|
35
|
+
- docs: Documentation only changes for example changes that happen in docs directory
|
36
|
+
- style: Changes that do not affect the code execution (e.g., white-space, formatting, missing semi-colons, etc.) usually done by linters
|
37
|
+
- refactor:
|
38
|
+
- A code change that does not change the behaviour of the code at all, just how the code is written executed,
|
39
|
+
- modifing error messages is not a refactor
|
40
|
+
- if parts of the code is removed and no alternative in place IT IS NOT A REFACTOR
|
41
|
+
- perf: A code change that improves performance
|
42
|
+
- test: Adding missing tests or correcting existing tests
|
43
|
+
- build: Changes that affect the build system or external dependencies
|
44
|
+
- ci:
|
45
|
+
- Changes to the CI configuration files and scripts
|
46
|
+
- these changes should not change the way that the code is built otherwise its build
|
47
|
+
|
48
|
+
|
49
|
+
Respond ONLY with the commit message line, nothing else.
|
@@ -20,7 +20,6 @@ module Committer
|
|
20
20
|
|
21
21
|
def setup
|
22
22
|
create_default_config
|
23
|
-
create_sample_formatting_rules
|
24
23
|
end
|
25
24
|
|
26
25
|
def write_config_file(file_path, contents)
|
@@ -34,15 +33,6 @@ module Committer
|
|
34
33
|
end
|
35
34
|
end
|
36
35
|
|
37
|
-
def create_sample_formatting_rules
|
38
|
-
default_formatting_rules = File.read(File.join(Committer::Config::Constants::DEFAULT_PROMPT_PATH,
|
39
|
-
Committer::Config::Constants::FORMATTING_RULES_FILE_NAME))
|
40
|
-
formatting_rules_file = File.join(@config_dir,
|
41
|
-
"#{Committer::Config::Constants::FORMATTING_RULES_FILE_NAME}.sample")
|
42
|
-
wrote_file = write_config_file(formatting_rules_file, default_formatting_rules)
|
43
|
-
nil unless wrote_file
|
44
|
-
end
|
45
|
-
|
46
36
|
def create_default_config
|
47
37
|
wrote_file = write_config_file(config_file, Committer::Config::Constants::DEFAULT_CONFIG.to_yaml)
|
48
38
|
return unless wrote_file
|
data/lib/committer/git_helper.rb
CHANGED
@@ -2,12 +2,19 @@
|
|
2
2
|
|
3
3
|
module Committer
|
4
4
|
module PromptTemplates
|
5
|
-
def self.
|
6
|
-
|
5
|
+
def self.build_prompt(diff, scopes, commit_context)
|
6
|
+
prompt_template = if commit_context.nil? || commit_context.empty?
|
7
|
+
Committer::PromptTemplates.build_prompt_summary_only
|
8
|
+
else
|
9
|
+
Committer::PromptTemplates.build_prompt_summary_and_body
|
10
|
+
end
|
11
|
+
prompt_template
|
12
|
+
.gsub('{{DIFF}}', diff)
|
13
|
+
.gsub('{{SCOPES}}', build_scopes_list(scopes))
|
14
|
+
.gsub('{{CONTEXT}}', commit_context || '')
|
7
15
|
end
|
8
16
|
|
9
|
-
def self.
|
10
|
-
scopes = Committer::Config::Accessor.instance[:scopes] || []
|
17
|
+
def self.build_scopes_list(scopes)
|
11
18
|
return 'DO NOT include a scope in your commit message' if scopes.empty?
|
12
19
|
|
13
20
|
scope_list = "\nScopes:\n#{scopes.map { |s| "- #{s}" }.join("\n")}"
|
@@ -15,56 +22,16 @@ module Committer
|
|
15
22
|
"- Choose an appropriate scope from the list above if relevant to the change \n#{scope_list}"
|
16
23
|
end
|
17
24
|
|
18
|
-
def self.commit_message_guidelines
|
19
|
-
<<~PROMPT
|
20
|
-
#{load_formatting_rules}
|
21
|
-
|
22
|
-
# Formatting rules with body:
|
23
|
-
<message>
|
24
|
-
|
25
|
-
<blank line>
|
26
|
-
<body with more detailed explanation>
|
27
|
-
|
28
|
-
#{load_scopes}
|
29
|
-
|
30
|
-
# Message Guidelines:
|
31
|
-
- Keep the summary under 70 characters
|
32
|
-
- Use imperative, present tense (e.g., "add" not "added" or "adds")
|
33
|
-
- Do not end the summary with a period
|
34
|
-
- Be concise but descriptive in the summary
|
35
|
-
|
36
|
-
# Body Guidelines:
|
37
|
-
- Add a blank line between summary and body
|
38
|
-
- Use the body to explain why the change was made, incorporating the user's context
|
39
|
-
- Wrap each line in the body at 80 characters maximum
|
40
|
-
- Break the body into multiple paragraphs if needed
|
41
|
-
|
42
|
-
Git Diff:
|
43
|
-
```
|
44
|
-
%<diff>s
|
45
|
-
```
|
46
|
-
PROMPT
|
47
|
-
end
|
48
|
-
|
49
25
|
def self.build_prompt_summary_only
|
50
|
-
|
51
|
-
Below is a git diff of staged changes. Please analyze it and create a commit message following the formatting rules format with ONLY a message line (NO body):
|
52
|
-
|
53
|
-
#{commit_message_guidelines}
|
54
|
-
|
55
|
-
Respond ONLY with the commit message line, nothing else.
|
56
|
-
PROMPT
|
26
|
+
load_prompt(Committer::Config::Constants::COMMIT_MESSAGE_ONLY_PROMPT_FILE_NAME)
|
57
27
|
end
|
58
28
|
|
59
29
|
def self.build_prompt_summary_and_body
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
#{commit_message_guidelines}
|
64
|
-
User's context for this change: %<commit_context>s
|
30
|
+
load_prompt(Committer::Config::Constants::COMMIT_MESSAGE_AND_BODY_PROMPT_FILE_NAME)
|
31
|
+
end
|
65
32
|
|
66
|
-
|
67
|
-
|
33
|
+
def self.load_prompt(file_name)
|
34
|
+
Committer::Config::Accessor.instance.read_path_prioritized_file(file_name)
|
68
35
|
end
|
69
36
|
end
|
70
37
|
end
|
data/lib/committer/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: committer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.5.
|
4
|
+
version: 0.5.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sebastien Stettler
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-03-
|
11
|
+
date: 2025-03-26 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: A tool that uses Claude API to generate conventional commit messages
|
14
14
|
based on staged changes
|
@@ -32,8 +32,10 @@ files:
|
|
32
32
|
- lib/committer/committer_errors.rb
|
33
33
|
- lib/committer/config/accessor.rb
|
34
34
|
- lib/committer/config/constants.rb
|
35
|
+
- lib/committer/config/defaults/commit_message_and_body.prompt
|
36
|
+
- lib/committer/config/defaults/commit_message_only.prompt
|
37
|
+
- lib/committer/config/defaults/formatting_rules.txt
|
35
38
|
- lib/committer/config/writer.rb
|
36
|
-
- lib/committer/formatting_rules.txt
|
37
39
|
- lib/committer/git_helper.rb
|
38
40
|
- lib/committer/prepare-commit-msg
|
39
41
|
- lib/committer/prompt_templates.rb
|
File without changes
|