nitpicker-code-review 1.0.0 → 1.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 +4 -4
- data/LICENSE +1 -1
- data/README.md +35 -4
- data/config/prompt +8 -2
- data/lib/nitpicker/version.rb +2 -2
- data/lib/nitpicker.rb +97 -18
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f0515786e9e4de04268cb3972eb9a88c72f6fbe004feba7f99c57f9cac8f7af3
|
4
|
+
data.tar.gz: 855c02aa8daeb96b26ea6d9e4ef65a339a60b20be6f4caaedf7c4c3f80a2091e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: be5e14b952451be9208553b2066132a81ea4585e64a2de3bf231ff8c0e2591608ba4ecbfa71a0e8090485c9423d9cd2771e08587a01297b7c82686f3133782af
|
7
|
+
data.tar.gz: 988e0441dbe173d17aaede678d76d9c635fe0f527ec3eab997e1b334af410ec5e55f4ee1013b913550600685bed6199f6275979f6e8e9edfc1fa205583620613
|
data/LICENSE
CHANGED
data/README.md
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
# NitPicker
|
2
2
|
|
3
|
-
NitPicker is a command-line tool that provides AI-powered code reviews for your Git changes. It analyzes your staged changes
|
3
|
+
NitPicker is a command-line tool that provides AI-powered code reviews for your Git changes. It analyzes your staged changes by default, but can also review any diff piped to it. It offers constructive feedback to help improve code quality, security, performance, and maintainability.
|
4
4
|
|
5
5
|
## Features
|
6
6
|
|
7
|
-
- AI-powered code review of staged Git changes
|
7
|
+
- AI-powered code review of staged Git changes or any piped diff
|
8
8
|
- Comprehensive analysis covering code quality, security, performance, and best practices
|
9
9
|
- Customizable AI prompts for tailored review feedback
|
10
10
|
- Support for repository-specific prompts
|
11
|
-
- Choose any AI model (
|
11
|
+
- Choose any AI model (OpenAI GPT-4.1-mini by default)
|
12
12
|
- Easy integration into your Git workflow
|
13
13
|
|
14
14
|
## Installation
|
@@ -81,7 +81,7 @@ Optionally, you can specify a different model by setting:
|
|
81
81
|
export GIT_REVIEW_MODEL="openai/gpt-4o"
|
82
82
|
```
|
83
83
|
|
84
|
-
The default model is `
|
84
|
+
The default model is `openai/gpt-4.1-mini` if not specified.
|
85
85
|
|
86
86
|
### Prompt Configuration
|
87
87
|
|
@@ -100,18 +100,31 @@ The special string `{{DIFF}}` in your prompt will be replaced with the current g
|
|
100
100
|
|
101
101
|
## Usage
|
102
102
|
|
103
|
+
### Review Staged Changes (Default)
|
104
|
+
|
103
105
|
Navigate to your Git repository, stage your changes, and run:
|
104
106
|
|
105
107
|
```bash
|
106
108
|
nitpicker [options]
|
107
109
|
```
|
108
110
|
|
111
|
+
### Review Any Diff via Pipe
|
112
|
+
|
113
|
+
You can pipe any diff into nitpicker for review:
|
114
|
+
|
115
|
+
```bash
|
116
|
+
git show | nitpicker # Review a specific commit
|
117
|
+
git diff HEAD~1 | nitpicker # Review changes from previous commit
|
118
|
+
git diff main..feature | nitpicker # Review differences between branches
|
119
|
+
```
|
120
|
+
|
109
121
|
Options:
|
110
122
|
- `--version`: Show version information
|
111
123
|
- `-h, --help`: Show help message
|
112
124
|
|
113
125
|
## Examples
|
114
126
|
|
127
|
+
### Review Staged Changes
|
115
128
|
```bash
|
116
129
|
# Stage some changes first
|
117
130
|
git add .
|
@@ -120,6 +133,24 @@ git add .
|
|
120
133
|
nitpicker
|
121
134
|
```
|
122
135
|
|
136
|
+
### Review Specific Commits or Diffs
|
137
|
+
```bash
|
138
|
+
# Review the last commit
|
139
|
+
git show | nitpicker
|
140
|
+
|
141
|
+
# Review changes from 2 commits ago
|
142
|
+
git show HEAD~2 | nitpicker
|
143
|
+
|
144
|
+
# Review uncommitted changes (both staged and unstaged)
|
145
|
+
git diff HEAD | nitpicker
|
146
|
+
|
147
|
+
# Review differences between branches
|
148
|
+
git diff main..feature-branch | nitpicker
|
149
|
+
|
150
|
+
# Review a range of commits
|
151
|
+
git diff HEAD~3..HEAD | nitpicker
|
152
|
+
```
|
153
|
+
|
123
154
|
Example output:
|
124
155
|
```
|
125
156
|
## Code Review Summary
|
data/config/prompt
CHANGED
@@ -1,4 +1,8 @@
|
|
1
|
-
You are an expert software engineer conducting a thorough code review.
|
1
|
+
You are an expert software engineer conducting a thorough code review.
|
2
|
+
|
3
|
+
IMPORTANT: Do NOT reproduce, echo, or reformat the code. Your job is to ANALYZE and provide FEEDBACK only. Do NOT simply summarize what was done.
|
4
|
+
|
5
|
+
Analyze the following git diff and provide constructive feedback focusing on:
|
2
6
|
|
3
7
|
**Code Quality & Best Practices:**
|
4
8
|
- Code readability, maintainability, and organization
|
@@ -35,5 +39,7 @@ Provide your feedback in a clear, constructive manner. For each issue identified
|
|
35
39
|
|
36
40
|
If the code looks good overall, highlight what was done well and provide any minor suggestions for improvement.
|
37
41
|
|
42
|
+
Start your response with "## Code Review" and provide analysis in prose, NOT code blocks or diffs.
|
43
|
+
|
38
44
|
DIFF:
|
39
|
-
{{DIFF}}
|
45
|
+
{{DIFF}}
|
data/lib/nitpicker/version.rb
CHANGED
@@ -1,3 +1,3 @@
|
|
1
1
|
module NitPicker
|
2
|
-
VERSION = '1.
|
3
|
-
end
|
2
|
+
VERSION = '1.2'
|
3
|
+
end
|
data/lib/nitpicker.rb
CHANGED
@@ -20,7 +20,10 @@ module NitPicker
|
|
20
20
|
require 'optparse'
|
21
21
|
|
22
22
|
OptionParser.new do |opts|
|
23
|
-
opts.banner = 'Usage: nitpicker [options]
|
23
|
+
opts.banner = 'Usage: nitpicker [options]
|
24
|
+
git show | nitpicker # Review a specific commit
|
25
|
+
git diff HEAD~1 | nitpicker # Review changes from previous commit
|
26
|
+
nitpicker # Review staged changes (default)'
|
24
27
|
opts.version = VERSION
|
25
28
|
|
26
29
|
opts.on('-h', '--help', 'Show this help message') do
|
@@ -33,12 +36,25 @@ module NitPicker
|
|
33
36
|
def run
|
34
37
|
parse_options
|
35
38
|
|
36
|
-
#
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
39
|
+
# Check if we have piped input
|
40
|
+
if STDIN.tty?
|
41
|
+
# Interactive terminal - use git diff of staged changes
|
42
|
+
diff = `git diff --staged`
|
43
|
+
if diff.empty?
|
44
|
+
puts "No changes staged for review."
|
45
|
+
exit 1
|
46
|
+
end
|
47
|
+
else
|
48
|
+
# STDIN is redirected - try to read from it
|
49
|
+
diff = STDIN.read
|
50
|
+
if diff.empty?
|
51
|
+
# Empty piped input, fall back to git diff
|
52
|
+
diff = `git diff --staged`
|
53
|
+
if diff.empty?
|
54
|
+
puts "No changes staged for review."
|
55
|
+
exit 1
|
56
|
+
end
|
57
|
+
end
|
42
58
|
end
|
43
59
|
|
44
60
|
# Get AI-generated code review
|
@@ -85,13 +101,24 @@ module NitPicker
|
|
85
101
|
def generate_code_review(diff)
|
86
102
|
api_key = ENV['OPENROUTER_API_KEY']
|
87
103
|
if api_key.nil? || api_key.empty?
|
88
|
-
puts "Error: OPENROUTER_API_KEY environment variable not set"
|
89
|
-
|
104
|
+
puts "❌ Error: OPENROUTER_API_KEY environment variable not set"
|
105
|
+
puts ""
|
106
|
+
puts "To get AI code reviews, you need an OpenRouter API key:"
|
107
|
+
puts "1. Sign up at https://openrouter.ai/"
|
108
|
+
puts "2. Get your API key from the dashboard"
|
109
|
+
puts "3. Set it as an environment variable:"
|
110
|
+
puts " export OPENROUTER_API_KEY=\"sk-or-v1-your-key-here\""
|
111
|
+
puts ""
|
112
|
+
puts "For now, showing the raw diff instead:"
|
113
|
+
puts "=" * 50
|
114
|
+
return diff
|
90
115
|
end
|
91
116
|
|
92
|
-
model = ENV['GIT_REVIEW_MODEL'] || '
|
117
|
+
model = ENV['GIT_REVIEW_MODEL'] || 'openai/gpt-4.1-mini'
|
118
|
+
puts "Using model: #{model}" if ENV['DEBUG']
|
93
119
|
|
94
120
|
prompt = get_prompt.gsub('{{DIFF}}', diff)
|
121
|
+
puts "Prompt length: #{prompt.length} characters" if ENV['DEBUG']
|
95
122
|
|
96
123
|
uri = URI.parse('https://openrouter.ai/api/v1/chat/completions')
|
97
124
|
http = Net::HTTP.new(uri.host, uri.port)
|
@@ -102,28 +129,80 @@ module NitPicker
|
|
102
129
|
request['Authorization'] = "Bearer #{api_key}"
|
103
130
|
request['HTTP-Referer'] = 'https://github.com/nitpicker-code-review'
|
104
131
|
|
105
|
-
|
132
|
+
request_payload = {
|
106
133
|
model: model,
|
107
134
|
messages: [
|
108
135
|
{ role: 'user', content: prompt }
|
109
136
|
],
|
110
137
|
max_tokens: 2000
|
111
|
-
}
|
138
|
+
}
|
139
|
+
|
140
|
+
request.body = request_payload.to_json
|
141
|
+
puts "Making API request to OpenRouter..." if ENV['DEBUG']
|
112
142
|
|
113
143
|
begin
|
114
144
|
response = http.request(request)
|
145
|
+
puts "API Response code: #{response.code}" if ENV['DEBUG']
|
115
146
|
|
116
147
|
if response.code == '200'
|
117
148
|
result = JSON.parse(response.body)
|
118
|
-
|
119
|
-
|
149
|
+
puts "API Response parsed successfully" if ENV['DEBUG']
|
150
|
+
|
151
|
+
if result['choices'] && result['choices'][0] && result['choices'][0]['message']
|
152
|
+
review = result['choices'][0]['message']['content'].strip
|
153
|
+
puts "Review length: #{review.length} characters" if ENV['DEBUG']
|
154
|
+
puts "Review content preview: #{review[0..200]}..." if ENV['DEBUG']
|
155
|
+
return review
|
156
|
+
else
|
157
|
+
puts "❌ Error: Unexpected API response structure"
|
158
|
+
puts "Response: #{response.body}" if ENV['DEBUG']
|
159
|
+
puts ""
|
160
|
+
puts "Showing raw diff instead:"
|
161
|
+
puts "=" * 50
|
162
|
+
return diff
|
163
|
+
end
|
120
164
|
else
|
121
|
-
|
122
|
-
|
165
|
+
error_body = JSON.parse(response.body) rescue response.body
|
166
|
+
error_message = error_body.is_a?(Hash) && error_body['error'] ? error_body['error']['message'] || error_body['error'] : response.body
|
167
|
+
|
168
|
+
puts "❌ API Error (#{response.code}): #{error_message}"
|
169
|
+
|
170
|
+
if response.code == '401'
|
171
|
+
puts ""
|
172
|
+
puts "This usually means:"
|
173
|
+
puts "• Your API key is invalid or expired"
|
174
|
+
puts "• You haven't set the OPENROUTER_API_KEY environment variable"
|
175
|
+
puts "• Your API key doesn't have sufficient credits"
|
176
|
+
puts ""
|
177
|
+
puts "Please check your API key at https://openrouter.ai/"
|
178
|
+
elsif response.code == '429'
|
179
|
+
puts ""
|
180
|
+
puts "You've hit the rate limit. Please wait a moment and try again."
|
181
|
+
end
|
182
|
+
|
183
|
+
puts ""
|
184
|
+
puts "Showing raw diff instead:"
|
185
|
+
puts "=" * 50
|
186
|
+
return diff
|
123
187
|
end
|
188
|
+
rescue JSON::ParserError => e
|
189
|
+
puts "❌ Error parsing API response: #{e.message}"
|
190
|
+
puts "Raw response: #{response.body}" if ENV['DEBUG']
|
191
|
+
puts ""
|
192
|
+
puts "Showing raw diff instead:"
|
193
|
+
puts "=" * 50
|
194
|
+
return diff
|
124
195
|
rescue => e
|
125
|
-
puts "Error: #{e.message}"
|
126
|
-
|
196
|
+
puts "❌ Error making API request: #{e.message}"
|
197
|
+
puts ""
|
198
|
+
puts "This could be due to:"
|
199
|
+
puts "• Network connectivity issues"
|
200
|
+
puts "• OpenRouter API service being temporarily unavailable"
|
201
|
+
puts "• Invalid API configuration"
|
202
|
+
puts ""
|
203
|
+
puts "Showing raw diff instead:"
|
204
|
+
puts "=" * 50
|
205
|
+
return diff
|
127
206
|
end
|
128
207
|
end
|
129
208
|
end
|