committer 0.1.0 → 0.2.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 +3 -1
- data/bin/committer +84 -59
- data/lib/clients/claude_client.rb +29 -16
- data/lib/committer/config.rb +9 -3
- data/lib/committer/prompt_templates.rb +83 -0
- data/lib/committer/version.rb +1 -1
- metadata +6 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 131648bca91f63a2fd32dc1dde7048f70ce3e3d0bc919d6737ff2ec098914fc2
|
4
|
+
data.tar.gz: dd1734f574490501b34589c8494eae3edf8db202c97372b2b961088100280855
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 03fbecc6488b914a31d8793015a4a0268ffd1c961bde13ff9df8440a2aa950ea904379c46262922439ac61d2976cdb5f2322747133b2bee0ce5371ad50aa45d5
|
7
|
+
data.tar.gz: 3e1dc9c66848b9039b233ed44ed46d16e6ed48c5c8f66129d25a23ee420cb00613d6164b4a4a0ea4ab3b9e18ac7357b8efd5e6e08e8b69bc9c4ab1279c9671e0
|
data/README.md
CHANGED
@@ -23,6 +23,7 @@ gem install committer
|
|
23
23
|
```
|
24
24
|
|
25
25
|
2. Install the project dependencies:
|
26
|
+
|
26
27
|
```sh
|
27
28
|
bundle install
|
28
29
|
```
|
@@ -47,7 +48,7 @@ Next, edit this file to add your Anthropic API key and optionally change the mod
|
|
47
48
|
|
48
49
|
```yaml
|
49
50
|
api_key: your_anthropic_api_key_here
|
50
|
-
model: claude-3-sonnet-
|
51
|
+
model: claude-3-7-sonnet-20250219
|
51
52
|
```
|
52
53
|
|
53
54
|
You only need to do this setup once.
|
@@ -61,6 +62,7 @@ committer
|
|
61
62
|
```
|
62
63
|
|
63
64
|
This will:
|
65
|
+
|
64
66
|
1. Get the diff of your staged changes
|
65
67
|
2. Send it to Claude for analysis
|
66
68
|
3. Generate a commit message in conventional format
|
data/bin/committer
CHANGED
@@ -6,6 +6,7 @@ require 'open3'
|
|
6
6
|
require 'httparty'
|
7
7
|
require 'yaml'
|
8
8
|
require_relative '../lib/committer/config'
|
9
|
+
require_relative '../lib/committer/prompt_templates'
|
9
10
|
require_relative '../lib/clients/claude_client'
|
10
11
|
|
11
12
|
# Handle command line arguments
|
@@ -27,71 +28,95 @@ when 'help', '--help', '-h'
|
|
27
28
|
end
|
28
29
|
|
29
30
|
# Default behavior: generate commit message
|
30
|
-
def
|
31
|
-
|
31
|
+
def build_commit_prompt(diff, commit_context = nil)
|
32
|
+
scopes = Committer::Config.load
|
33
|
+
scope_section = scopes.empty? ? '' : "\nScopes:\n#{scopes.map { |s| "- #{s}" }.join("\n")}"
|
34
|
+
scope_instruction = if scopes.empty?
|
35
|
+
'- DO NOT include a scope in your commit message'
|
36
|
+
else
|
37
|
+
'- Choose an appropriate scope from the list above if relevant to the change'
|
38
|
+
end
|
39
|
+
format(template(commit_context),
|
40
|
+
diff: diff,
|
41
|
+
scopes_section: scope_section,
|
42
|
+
scope_instruction: scope_instruction,
|
43
|
+
commit_context: commit_context)
|
44
|
+
end
|
32
45
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
exit 0
|
37
|
-
else
|
38
|
-
begin
|
39
|
-
client = Clients::ClaudeClient.new
|
40
|
-
puts 'Sending diff to Claude...'
|
41
|
-
|
42
|
-
prompt = <<~PROMPT
|
43
|
-
Below is a git diff of staged changes. Please analyze it and create a commit message following the Conventional Commits format:
|
44
|
-
|
45
|
-
Format: <type>(<optional scope>): <description>
|
46
|
-
|
47
|
-
Types:
|
48
|
-
- feat: A new feature
|
49
|
-
- fix: A bug fix
|
50
|
-
- docs: Documentation only changes
|
51
|
-
- style: Changes that do not affect the meaning of the code
|
52
|
-
- refactor: A code change that neither fixes a bug nor adds a feature
|
53
|
-
- perf: A code change that improves performance
|
54
|
-
- test: Adding missing tests or correcting existing tests
|
55
|
-
- chore: Changes to the build process or auxiliary tools
|
56
|
-
|
57
|
-
Guidelines:
|
58
|
-
- Keep the first line under 70 characters
|
59
|
-
- Use imperative, present tense (e.g., "add" not "added" or "adds")
|
60
|
-
- Do not end with a period
|
61
|
-
- Be concise but descriptive
|
62
|
-
|
63
|
-
Git Diff:
|
64
|
-
```
|
65
|
-
#{stdout}
|
66
|
-
```
|
67
|
-
|
68
|
-
Respond ONLY with the commit message text, nothing else.
|
69
|
-
PROMPT
|
70
|
-
|
71
|
-
response = client.post(prompt)
|
72
|
-
commit_message = begin
|
73
|
-
response.dig('content', 0, 'text')
|
74
|
-
rescue StandardError
|
75
|
-
response.inspect
|
76
|
-
end
|
77
|
-
|
78
|
-
puts "\nOpening git commit with the suggested message..."
|
79
|
-
|
80
|
-
# Create git commit with the suggested message and open in editor
|
81
|
-
system('git', 'commit', '-m', commit_message, '-e')
|
82
|
-
rescue Clients::ClaudeClient::ConfigError => e
|
83
|
-
puts "Error: #{e.message}"
|
84
|
-
exit 1
|
85
|
-
rescue StandardError => e
|
86
|
-
puts "Error: #{e.message}"
|
87
|
-
exit 1
|
88
|
-
end
|
89
|
-
end
|
46
|
+
def template(commit_context)
|
47
|
+
if commit_context.nil? || commit_context.empty?
|
48
|
+
Committer::PromptTemplates::SUMMARY_ONLY
|
90
49
|
else
|
50
|
+
Committer::PromptTemplates::SUMMARY_AND_BODY
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def check_git_status
|
55
|
+
stdout, stderr, status = Open3.capture3('git diff --staged')
|
56
|
+
|
57
|
+
unless status.success?
|
91
58
|
puts 'Error executing git diff --staged:'
|
92
59
|
puts stderr
|
93
60
|
exit 1
|
94
61
|
end
|
62
|
+
|
63
|
+
if stdout.empty?
|
64
|
+
puts 'No changes are staged for commit.'
|
65
|
+
exit 0
|
66
|
+
end
|
67
|
+
|
68
|
+
stdout
|
69
|
+
end
|
70
|
+
|
71
|
+
def parse_response(response, commit_context)
|
72
|
+
text = response.dig('content', 0, 'text')
|
73
|
+
|
74
|
+
# If user didn't provide context, response should only be a summary line
|
75
|
+
if commit_context.nil? || commit_context.empty?
|
76
|
+
{ summary: text.strip, body: nil }
|
77
|
+
else
|
78
|
+
# Split the response into summary and body
|
79
|
+
message_parts = text.split("\n\n", 2)
|
80
|
+
summary = message_parts[0].strip
|
81
|
+
body = message_parts[1]&.strip
|
82
|
+
|
83
|
+
# Wrap body text at 80 characters
|
84
|
+
body = body.gsub(/(.{1,80})(\s+|$)/, "\\1\n").strip if body
|
85
|
+
|
86
|
+
{ summary: summary, body: body }
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def prepare_commit_message(diff, commit_context = nil)
|
91
|
+
client = Clients::ClaudeClient.new
|
92
|
+
puts 'Sending diff to Claude...'
|
93
|
+
|
94
|
+
prompt = build_commit_prompt(diff, commit_context)
|
95
|
+
response = client.post(prompt)
|
96
|
+
parse_response(response, commit_context)
|
97
|
+
end
|
98
|
+
|
99
|
+
def execute_git_diff_staged
|
100
|
+
diff = check_git_status
|
101
|
+
|
102
|
+
# Prompt user for commit context
|
103
|
+
puts 'Why are you making this change? (Press Enter to skip)'
|
104
|
+
commit_context = gets.chomp
|
105
|
+
|
106
|
+
commit_message = prepare_commit_message(diff, commit_context)
|
107
|
+
|
108
|
+
summary = commit_message[:summary]
|
109
|
+
body = commit_message[:body]
|
110
|
+
|
111
|
+
# Create git commit with the suggested message and open in editor
|
112
|
+
if body
|
113
|
+
system('git', 'commit', '-m', summary, '-m', body, '-e')
|
114
|
+
else
|
115
|
+
system('git', 'commit', '-m', summary, '-e')
|
116
|
+
end
|
117
|
+
rescue Clients::ClaudeClient::ConfigError, StandardError => e
|
118
|
+
puts "Error: #{e.message}"
|
119
|
+
exit 1
|
95
120
|
end
|
96
121
|
|
97
122
|
# Execute the function if no specific command was given
|
@@ -5,6 +5,7 @@ require 'httparty'
|
|
5
5
|
require_relative '../committer/config'
|
6
6
|
|
7
7
|
module Clients
|
8
|
+
# Claude API client for communicating with Anthropic's Claude model
|
8
9
|
class ClaudeClient
|
9
10
|
class OverloadError < StandardError; end
|
10
11
|
class UnknownError < StandardError; end
|
@@ -21,13 +22,28 @@ module Clients
|
|
21
22
|
|
22
23
|
def post(message)
|
23
24
|
body = {
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
{
|
25
|
+
model: @config['model'],
|
26
|
+
max_tokens: 4096,
|
27
|
+
messages: [
|
28
|
+
{ role: 'user', content: message }
|
28
29
|
]
|
29
30
|
}
|
30
|
-
|
31
|
+
|
32
|
+
response = send_request(body)
|
33
|
+
handle_error_response(response) if response['type'] == 'error'
|
34
|
+
|
35
|
+
response
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def send_request(body)
|
41
|
+
options = build_request_options(body)
|
42
|
+
HTTParty.post('https://api.anthropic.com/v1/messages', options)
|
43
|
+
end
|
44
|
+
|
45
|
+
def build_request_options(body)
|
46
|
+
{
|
31
47
|
headers: {
|
32
48
|
'anthropic-version': '2023-06-01',
|
33
49
|
'content-type': 'application/json',
|
@@ -35,17 +51,14 @@ module Clients
|
|
35
51
|
},
|
36
52
|
body: body.to_json
|
37
53
|
}
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
raise UnknownError, "Claude API error: #{response.inspect}"
|
47
|
-
end
|
48
|
-
response
|
54
|
+
end
|
55
|
+
|
56
|
+
def handle_error_response(response)
|
57
|
+
error = response['error']
|
58
|
+
raise OverloadError if error['type'] == 'overloaded_error'
|
59
|
+
|
60
|
+
puts response
|
61
|
+
raise UnknownError, "Claude API error: #{response.inspect}"
|
49
62
|
end
|
50
63
|
end
|
51
64
|
end
|
data/lib/committer/config.rb
CHANGED
@@ -4,12 +4,14 @@ require 'yaml'
|
|
4
4
|
require 'fileutils'
|
5
5
|
|
6
6
|
module Committer
|
7
|
+
# Configuration management for the Committer gem
|
7
8
|
class Config
|
8
9
|
CONFIG_DIR = File.join(Dir.home, '.committer')
|
9
10
|
CONFIG_FILE = File.join(CONFIG_DIR, 'config.yml')
|
10
11
|
DEFAULT_CONFIG = {
|
11
12
|
'api_key' => nil,
|
12
|
-
'model' => 'claude-3-sonnet-
|
13
|
+
'model' => 'claude-3-7-sonnet-20250219',
|
14
|
+
'scopes' => nil
|
13
15
|
}.freeze
|
14
16
|
|
15
17
|
def self.load
|
@@ -23,7 +25,7 @@ module Committer
|
|
23
25
|
end
|
24
26
|
|
25
27
|
def self.create_default_config
|
26
|
-
FileUtils.mkdir_p(CONFIG_DIR)
|
28
|
+
FileUtils.mkdir_p(CONFIG_DIR)
|
27
29
|
File.write(CONFIG_FILE, DEFAULT_CONFIG.to_yaml)
|
28
30
|
end
|
29
31
|
|
@@ -35,7 +37,11 @@ module Committer
|
|
35
37
|
puts 'Example config format:'
|
36
38
|
puts '---'
|
37
39
|
puts 'api_key: your_api_key_here'
|
38
|
-
puts 'model: claude-3-sonnet-
|
40
|
+
puts 'model: claude-3-7-sonnet-20250219'
|
41
|
+
puts 'scopes:'
|
42
|
+
puts ' - feature'
|
43
|
+
puts ' - api'
|
44
|
+
puts ' - ui'
|
39
45
|
end
|
40
46
|
end
|
41
47
|
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Committer
|
4
|
+
module PromptTemplates
|
5
|
+
SUMMARY_ONLY = <<~PROMPT
|
6
|
+
Below is a git diff of staged changes. Please analyze it and create a commit message following the Conventional Commits format with ONLY a summary line (NO body):
|
7
|
+
|
8
|
+
Format when scopes are available: <type>(<scope>): <description>
|
9
|
+
Format when no scopes are available: <type>: <description>
|
10
|
+
|
11
|
+
Types:
|
12
|
+
- feat: A new feature
|
13
|
+
- fix: A bug fix
|
14
|
+
- docs: Documentation only changes
|
15
|
+
- style: Changes that do not affect the meaning of the code
|
16
|
+
- refactor: A code change that neither fixes a bug nor adds a feature
|
17
|
+
- perf: A code change that improves performance
|
18
|
+
- test: Adding missing tests or correcting existing tests
|
19
|
+
- chore: Changes to the build process or auxiliary tools
|
20
|
+
%<scopes_section>s
|
21
|
+
Guidelines:
|
22
|
+
- Keep the summary under 70 characters
|
23
|
+
- Use imperative, present tense (e.g., "add" not "added" or "adds")
|
24
|
+
- Do not end the summary with a period
|
25
|
+
- Be concise but descriptive in the summary
|
26
|
+
%<scope_instruction>s
|
27
|
+
|
28
|
+
Git Diff:
|
29
|
+
```
|
30
|
+
%<diff>s
|
31
|
+
```
|
32
|
+
|
33
|
+
Respond ONLY with the commit message summary line, nothing else.
|
34
|
+
PROMPT
|
35
|
+
|
36
|
+
SUMMARY_AND_BODY = <<~PROMPT
|
37
|
+
Below is a git diff of staged changes. Please analyze it and create a commit message following the Conventional Commits format with a summary line and a detailed body:
|
38
|
+
|
39
|
+
Format when scopes are available:
|
40
|
+
<type>(<scope>): <description>
|
41
|
+
|
42
|
+
<blank line>
|
43
|
+
<body with more detailed explanation>
|
44
|
+
|
45
|
+
Format when no scopes are available:
|
46
|
+
<type>: <description>
|
47
|
+
|
48
|
+
<blank line>
|
49
|
+
<body with more detailed explanation>
|
50
|
+
|
51
|
+
|
52
|
+
Types:
|
53
|
+
- feat: A new feature
|
54
|
+
- fix: A bug fix
|
55
|
+
- docs: Documentation only changes
|
56
|
+
- style: Changes that do not affect the meaning of the code
|
57
|
+
- refactor: A code change that neither fixes a bug nor adds a feature
|
58
|
+
- perf: A code change that improves performance
|
59
|
+
- test: Adding missing tests or correcting existing tests
|
60
|
+
- chore: Changes to the build process or auxiliary tools
|
61
|
+
%<scopes_section>s
|
62
|
+
Guidelines:
|
63
|
+
- Keep the first line (summary) under 70 characters
|
64
|
+
- Use imperative, present tense (e.g., "add" not "added" or "adds")
|
65
|
+
- Do not end the summary with a period
|
66
|
+
- Be concise but descriptive in the summary
|
67
|
+
- Add a blank line between summary and body
|
68
|
+
- Use the body to explain why the change was made, incorporating the user's context
|
69
|
+
- Wrap each line in the body at 80 characters maximum
|
70
|
+
- Break the body into multiple paragraphs if needed
|
71
|
+
%<scope_instruction>s
|
72
|
+
|
73
|
+
User's context for this change: %<commit_context>s
|
74
|
+
|
75
|
+
Git Diff:
|
76
|
+
```
|
77
|
+
%<diff>s
|
78
|
+
```
|
79
|
+
|
80
|
+
Respond ONLY with the commit message text (summary and body), nothing else.
|
81
|
+
PROMPT
|
82
|
+
end
|
83
|
+
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.1
|
4
|
+
version: 0.2.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-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: httparty
|
@@ -30,6 +30,7 @@ email:
|
|
30
30
|
- sebastien@managerbot.dev
|
31
31
|
executables:
|
32
32
|
- git-smart-commit
|
33
|
+
- committer
|
33
34
|
extensions: []
|
34
35
|
extra_rdoc_files: []
|
35
36
|
files:
|
@@ -38,11 +39,13 @@ files:
|
|
38
39
|
- bin/git-smart-commit
|
39
40
|
- lib/clients/claude_client.rb
|
40
41
|
- lib/committer/config.rb
|
42
|
+
- lib/committer/prompt_templates.rb
|
41
43
|
- lib/committer/version.rb
|
42
44
|
homepage: https://github.com/Hyper-Unearthing/committer
|
43
45
|
licenses:
|
44
46
|
- MIT
|
45
|
-
metadata:
|
47
|
+
metadata:
|
48
|
+
rubygems_mfa_required: 'true'
|
46
49
|
post_install_message:
|
47
50
|
rdoc_options: []
|
48
51
|
require_paths:
|