committer 0.1.1 → 0.2.2

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: 19704907042388baa2b8ede51ebe80710b9cbe5f6095fd09b0fb7cb997496382
4
+ data.tar.gz: ecaab24d0b58301c41a9677cf09fd1ae5ef23c3930c911c6cdf9820abacc52f9
5
5
  SHA512:
6
- metadata.gz: 77ab47d1217c616a19de223748c9399ff90a04a9561a1fd38a716beddf56829914263926feb78d0c80a699e7c328fca15ecc7022b11dd6bb0ce78f03201cef81
7
- data.tar.gz: dd29f94532dafa9791cddf4868b4cb1bd0f214ad888b862b4924ea0f147bc7ee8327ee93721c054fb3e084a65d60109e6b4a3942bc6bad564c3ecff68bcf7c2f
6
+ metadata.gz: 1a6eb3eaabe0723082df95e113c7cbeb47521a16839ae04cdf6bfa146a3747b68b77aeac2462954220eeefd2cf709d73a8e2df9ab63b6b265d3c9f4304706357
7
+ data.tar.gz: 1c98170b2c2b194bb739c96278e3150e005d1f00df9c69c1c21219b26e7f2511d710894eb1001dfe973103e23efa22e91aabaa0f27fd06e53387aeca1b10e626
data/README.md CHANGED
@@ -4,8 +4,25 @@ An AI-powered git commit message generator using Claude.
4
4
 
5
5
  ## Overview
6
6
 
7
+ The goal of committer is to make it easier to write beautiful commits.
8
+
7
9
  Committer uses Claude AI to analyze your staged git changes and generate conventional commit messages for you. It detects the type of changes (feature, fix, refactor, etc.) and creates a well-formatted commit message that follows best practices.
8
10
 
11
+ ## What Makes a Good Commit
12
+
13
+ A good commit should:
14
+ - Have a summary that clearly describes the change
15
+ - Explain WHY the change was made, not just what changed
16
+
17
+ When a future developer uses git blame to understand a line of code, they should immediately understand why the change was made. This context is invaluable for maintaining and evolving the codebase effectively.
18
+
19
+ ## How Committer Helps
20
+
21
+ Committer analyzes your code changes and generates commit messages that:
22
+ 1. Provide a clean, descriptive summary of the change
23
+ 2. Include context about why the change was necessary
24
+ 3. Follow conventional commit format for consistency
25
+
9
26
  ## Installation
10
27
 
11
28
  ### Install from RubyGems
@@ -23,6 +40,7 @@ gem install committer
23
40
  ```
24
41
 
25
42
  2. Install the project dependencies:
43
+
26
44
  ```sh
27
45
  bundle install
28
46
  ```
@@ -43,13 +61,19 @@ committer setup
43
61
 
44
62
  This will create a template config file at `~/.committer/config.yml`.
45
63
 
46
- Next, edit this file to add your Anthropic API key and optionally change the model:
64
+ Next, edit this file to add your Anthropic API key and optionally change the model or configure commit scopes:
47
65
 
48
66
  ```yaml
49
67
  api_key: your_anthropic_api_key_here
50
- model: claude-3-sonnet-20240229
68
+ model: claude-3-7-sonnet-20250219
69
+ scopes:
70
+ - feature
71
+ - api
72
+ - ui
51
73
  ```
52
74
 
75
+ The `scopes` configuration is optional. When provided, Committer will generate conventional commit messages with scopes (like `feat(api): add new endpoint`). If left as `null` or omitted, commit messages will be generated without scopes (like `feat: add new endpoint`).
76
+
53
77
  You only need to do this setup once.
54
78
 
55
79
  ## Usage
@@ -61,11 +85,13 @@ committer
61
85
  ```
62
86
 
63
87
  This will:
88
+
64
89
  1. Get the diff of your staged changes
65
- 2. Send it to Claude for analysis
66
- 3. Generate a commit message in conventional format
67
- 4. Open your default git editor with the suggested message
68
- 5. Allow you to edit the message if needed or simply save to confirm
90
+ 2. Ask you for optional context about why you're making the change
91
+ 3. Send the diff and context to Claude for analysis
92
+ 4. Generate a commit message in conventional format (with scope if configured)
93
+ 5. Open your default git editor with the suggested message
94
+ 6. Allow you to edit the message if needed or simply save to confirm
69
95
 
70
96
  ## Commands
71
97
 
@@ -73,12 +99,6 @@ This will:
73
99
  - `committer setup` - Create the config file template
74
100
  - `committer help` - Display help information
75
101
 
76
- You can also run it directly through git:
77
-
78
- ```bash
79
- git smart-commit
80
- ```
81
-
82
102
  ## License
83
103
 
84
104
  MIT
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.2'
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.2
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
@@ -29,21 +29,21 @@ description: A tool that uses Claude API to generate conventional commit message
29
29
  email:
30
30
  - sebastien@managerbot.dev
31
31
  executables:
32
- - git-smart-commit
33
32
  - committer
34
33
  extensions: []
35
34
  extra_rdoc_files: []
36
35
  files:
37
36
  - README.md
38
37
  - bin/committer
39
- - bin/git-smart-commit
40
38
  - lib/clients/claude_client.rb
41
39
  - lib/committer/config.rb
40
+ - lib/committer/prompt_templates.rb
42
41
  - lib/committer/version.rb
43
42
  homepage: https://github.com/Hyper-Unearthing/committer
44
43
  licenses:
45
44
  - MIT
46
- metadata: {}
45
+ metadata:
46
+ rubygems_mfa_required: 'true'
47
47
  post_install_message:
48
48
  rdoc_options: []
49
49
  require_paths:
data/bin/git-smart-commit DELETED
@@ -1,8 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- # This is a git integration script that allows users to run:
5
- # git smart-commit
6
- # which will forward to the committer command
7
-
8
- require_relative 'committer'