committer 0.1.1 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a7e89bde257ed6c9de9a4ad0e8bdff105f5200143ef1fe0938ee086fc2ac51fa
4
- data.tar.gz: 251035e7d4b188dc7589ea2cbfc01733496d70bfc28ed348d01647c0f955fb12
3
+ metadata.gz: 131648bca91f63a2fd32dc1dde7048f70ce3e3d0bc919d6737ff2ec098914fc2
4
+ data.tar.gz: dd1734f574490501b34589c8494eae3edf8db202c97372b2b961088100280855
5
5
  SHA512:
6
- metadata.gz: 77ab47d1217c616a19de223748c9399ff90a04a9561a1fd38a716beddf56829914263926feb78d0c80a699e7c328fca15ecc7022b11dd6bb0ce78f03201cef81
7
- data.tar.gz: dd29f94532dafa9791cddf4868b4cb1bd0f214ad888b862b4924ea0f147bc7ee8327ee93721c054fb3e084a65d60109e6b4a3942bc6bad564c3ecff68bcf7c2f
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-20240229
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 execute_git_diff_staged
31
- stdout, stderr, status = Open3.capture3('git diff --staged')
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
- if status.success?
34
- if stdout.empty?
35
- puts 'No changes are staged for commit.'
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
- 'model': @config['model'],
25
- 'max_tokens': 4096,
26
- 'messages': [
27
- { 'role': 'user', 'content': message }
25
+ model: @config['model'],
26
+ max_tokens: 4096,
27
+ messages: [
28
+ { role: 'user', content: message }
28
29
  ]
29
30
  }
30
- options = {
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
- response = HTTParty.post(
39
- 'https://api.anthropic.com/v1/messages', options
40
- )
41
- if response['type'] == 'error'
42
- error = response['error']
43
- raise OverloadError if error['type'] == 'overloaded_error'
44
-
45
- puts response
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
@@ -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-20240229'
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) unless Dir.exist?(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-20240229'
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Committer
4
- VERSION = '0.1.1'
4
+ VERSION = '0.2.1'
5
5
  end
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.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-05 00:00:00.000000000 Z
11
+ date: 2025-03-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: httparty
@@ -39,11 +39,13 @@ files:
39
39
  - bin/git-smart-commit
40
40
  - lib/clients/claude_client.rb
41
41
  - lib/committer/config.rb
42
+ - lib/committer/prompt_templates.rb
42
43
  - lib/committer/version.rb
43
44
  homepage: https://github.com/Hyper-Unearthing/committer
44
45
  licenses:
45
46
  - MIT
46
- metadata: {}
47
+ metadata:
48
+ rubygems_mfa_required: 'true'
47
49
  post_install_message:
48
50
  rdoc_options: []
49
51
  require_paths: