claude-git-hooks 1.5.1 → 1.5.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.
- package/CHANGELOG.md +49 -3
- package/README.md +1 -1
- package/bin/claude-hooks +334 -334
- package/package.json +1 -1
- package/templates/pre-commit +91 -91
- package/templates/prepare-commit-msg +32 -32
package/package.json
CHANGED
package/templates/pre-commit
CHANGED
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
2
|
|
|
3
|
-
# Git Pre-commit Hook
|
|
3
|
+
# Git Pre-commit Hook for code evaluation with Claude CLI
|
|
4
4
|
# Archivo: .git/hooks/pre-commit
|
|
5
5
|
|
|
6
6
|
set -e
|
|
7
7
|
|
|
8
|
-
#
|
|
8
|
+
# Configuration
|
|
9
9
|
CLAUDE_CLI="claude"
|
|
10
10
|
TEMP_DIR="/tmp/code-review-$$"
|
|
11
|
-
MAX_FILE_SIZE=100000 # 100KB
|
|
11
|
+
MAX_FILE_SIZE=100000 # 100KB maximum per file
|
|
12
12
|
|
|
13
|
-
#
|
|
13
|
+
# Colors for output
|
|
14
14
|
RED='\033[0;31m'
|
|
15
15
|
GREEN='\033[0;32m'
|
|
16
16
|
YELLOW='\033[1;33m'
|
|
17
17
|
BLUE='\033[0;34m'
|
|
18
18
|
NC='\033[0m' # No Color
|
|
19
19
|
|
|
20
|
-
#
|
|
20
|
+
# Function for logging
|
|
21
21
|
log() {
|
|
22
22
|
echo -e "${GREEN}[PRE-COMMIT]${NC} $1"
|
|
23
23
|
}
|
|
@@ -30,10 +30,10 @@ warning() {
|
|
|
30
30
|
echo -e "${YELLOW}[WARNING]${NC} $1"
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
#
|
|
34
|
-
#
|
|
33
|
+
# Check version at start (before any analysis)
|
|
34
|
+
# Try to find the check-version.sh script
|
|
35
35
|
CHECK_VERSION_SCRIPT=""
|
|
36
|
-
#
|
|
36
|
+
# Search in multiple possible locations
|
|
37
37
|
SCRIPT_PATHS=(
|
|
38
38
|
"$(dirname "$0")/check-version.sh"
|
|
39
39
|
"/usr/local/lib/node_modules/claude-git-hooks/templates/check-version.sh"
|
|
@@ -49,37 +49,37 @@ for path in "${SCRIPT_PATHS[@]}"; do
|
|
|
49
49
|
fi
|
|
50
50
|
done
|
|
51
51
|
|
|
52
|
-
#
|
|
52
|
+
# If we find the script, execute it
|
|
53
53
|
if [ -n "$CHECK_VERSION_SCRIPT" ]; then
|
|
54
|
-
# Source
|
|
54
|
+
# Source the script to have access to the function
|
|
55
55
|
source "$CHECK_VERSION_SCRIPT"
|
|
56
56
|
check_version
|
|
57
57
|
fi
|
|
58
58
|
|
|
59
|
-
#
|
|
59
|
+
# Function to generate AI-friendly resolution prompt
|
|
60
60
|
generate_resolution_prompt() {
|
|
61
61
|
local RESOLUTION_FILE="./claude_resolution_prompt.md"
|
|
62
62
|
local RESOLUTION_TEMPLATE=".claude/CLAUDE_RESOLUTION_PROMPT.md"
|
|
63
63
|
|
|
64
64
|
if [ ! -f "$RESOLUTION_TEMPLATE" ]; then
|
|
65
|
-
warning "
|
|
65
|
+
warning "Resolution template not found: $RESOLUTION_TEMPLATE"
|
|
66
66
|
return
|
|
67
67
|
fi
|
|
68
68
|
|
|
69
|
-
#
|
|
69
|
+
# Get context information
|
|
70
70
|
local REPO_NAME=$(basename $(git rev-parse --show-toplevel))
|
|
71
71
|
local BRANCH_NAME=$(git rev-parse --abbrev-ref HEAD)
|
|
72
72
|
local COMMIT_SHA="pending"
|
|
73
73
|
local FILE_COUNT=$(echo "$JAVA_FILES" | wc -l)
|
|
74
74
|
|
|
75
|
-
#
|
|
75
|
+
# Format the blocking issues for the resolution prompt
|
|
76
76
|
local ISSUES_FORMATTED=""
|
|
77
77
|
local issue_num=1
|
|
78
78
|
|
|
79
|
-
#
|
|
79
|
+
# Use a temporary file to accumulate the issues
|
|
80
80
|
local TEMP_ISSUES=$(mktemp)
|
|
81
81
|
|
|
82
|
-
#
|
|
82
|
+
# Parse each blocking issue as JSON object
|
|
83
83
|
echo "$JSON_RESPONSE" | jq -c '.blockingIssues[]?' 2>/dev/null | while IFS= read -r issue; do
|
|
84
84
|
if [ -n "$issue" ]; then
|
|
85
85
|
local desc=$(echo "$issue" | jq -r '.description')
|
|
@@ -97,31 +97,31 @@ generate_resolution_prompt() {
|
|
|
97
97
|
fi
|
|
98
98
|
done
|
|
99
99
|
|
|
100
|
-
#
|
|
100
|
+
# Read the accumulated content
|
|
101
101
|
ISSUES_FORMATTED=$(cat "$TEMP_ISSUES")
|
|
102
102
|
rm -f "$TEMP_ISSUES"
|
|
103
103
|
|
|
104
|
-
#
|
|
104
|
+
# Generate the prompt from the template
|
|
105
105
|
cp "$RESOLUTION_TEMPLATE" "$RESOLUTION_FILE"
|
|
106
106
|
|
|
107
|
-
#
|
|
107
|
+
# Replace placeholders - use double quotes and escape special characters
|
|
108
108
|
sed -i "s|{{REPO_NAME}}|${REPO_NAME}|g" "$RESOLUTION_FILE"
|
|
109
109
|
sed -i "s|{{BRANCH_NAME}}|${BRANCH_NAME}|g" "$RESOLUTION_FILE"
|
|
110
110
|
sed -i "s|{{COMMIT_SHA}}|${COMMIT_SHA}|g" "$RESOLUTION_FILE"
|
|
111
111
|
sed -i "s|{{FILE_COUNT}}|${FILE_COUNT}|g" "$RESOLUTION_FILE"
|
|
112
112
|
|
|
113
|
-
#
|
|
113
|
+
# Create temporary file for formatted issues
|
|
114
114
|
local TEMP_ISSUES_FILE=$(mktemp)
|
|
115
115
|
echo "$ISSUES_FORMATTED" > "$TEMP_ISSUES_FILE"
|
|
116
116
|
|
|
117
|
-
#
|
|
117
|
+
# Replace {{BLOCKING_ISSUES}} with content
|
|
118
118
|
if [ -n "$ISSUES_FORMATTED" ]; then
|
|
119
119
|
sed -i "/{{BLOCKING_ISSUES}}/r $TEMP_ISSUES_FILE" "$RESOLUTION_FILE"
|
|
120
120
|
fi
|
|
121
121
|
sed -i "s|{{BLOCKING_ISSUES}}||g" "$RESOLUTION_FILE"
|
|
122
122
|
rm -f "$TEMP_ISSUES_FILE"
|
|
123
123
|
|
|
124
|
-
#
|
|
124
|
+
# Add content from affected files
|
|
125
125
|
local TEMP_FILES=$(mktemp)
|
|
126
126
|
echo "$JSON_RESPONSE" | jq -r '.blockingIssues[].file' 2>/dev/null | sort -u | while IFS= read -r file; do
|
|
127
127
|
if [ -f "$file" ]; then
|
|
@@ -134,7 +134,7 @@ generate_resolution_prompt() {
|
|
|
134
134
|
fi
|
|
135
135
|
done
|
|
136
136
|
|
|
137
|
-
#
|
|
137
|
+
# Replace {{FILE_CONTENTS}} with content
|
|
138
138
|
if [ -s "$TEMP_FILES" ]; then
|
|
139
139
|
sed -i "/{{FILE_CONTENTS}}/r $TEMP_FILES" "$RESOLUTION_FILE"
|
|
140
140
|
fi
|
|
@@ -142,62 +142,62 @@ generate_resolution_prompt() {
|
|
|
142
142
|
rm -f "$TEMP_FILES"
|
|
143
143
|
|
|
144
144
|
echo
|
|
145
|
-
echo -e "${YELLOW}===
|
|
146
|
-
echo -e "${GREEN}
|
|
147
|
-
echo -e "${YELLOW}
|
|
145
|
+
echo -e "${YELLOW}=== AI RESOLUTION PROMPT GENERATED ===${NC}"
|
|
146
|
+
echo -e "${GREEN}An AI-friendly prompt has been generated at: ${BLUE}$RESOLUTION_FILE${NC}"
|
|
147
|
+
echo -e "${YELLOW}Copy this file to a new Claude instance to resolve problems automatically.${NC}"
|
|
148
148
|
echo
|
|
149
149
|
}
|
|
150
150
|
|
|
151
|
-
#
|
|
151
|
+
# Configure files for SonarQube mode
|
|
152
152
|
GUIDELINES_FILE=".claude/CLAUDE_PRE_COMMIT_SONAR.md"
|
|
153
153
|
PROMPT_TEMPLATE=".claude/CLAUDE_ANALYSIS_PROMPT_SONAR.md"
|
|
154
154
|
|
|
155
155
|
|
|
156
|
-
#
|
|
156
|
+
# Check that the prompt template exists
|
|
157
157
|
if [ ! -f "$PROMPT_TEMPLATE" ]; then
|
|
158
|
-
error "
|
|
159
|
-
error "
|
|
160
|
-
error "
|
|
158
|
+
error "Prompt template not found: $PROMPT_TEMPLATE"
|
|
159
|
+
error "Claude configuration files appear to be incomplete."
|
|
160
|
+
error "Please reinstall claude-git-hooks by running:"
|
|
161
161
|
error " claude-hooks install --force"
|
|
162
162
|
exit 1
|
|
163
163
|
fi
|
|
164
|
-
#
|
|
164
|
+
# Function to clean temporary files
|
|
165
165
|
cleanup() {
|
|
166
166
|
rm -rf "$TEMP_DIR"
|
|
167
167
|
}
|
|
168
168
|
|
|
169
|
-
#
|
|
169
|
+
# Configure cleanup on exit
|
|
170
170
|
trap cleanup EXIT
|
|
171
171
|
|
|
172
|
-
#
|
|
172
|
+
# Create temporary directory
|
|
173
173
|
mkdir -p "$TEMP_DIR"
|
|
174
174
|
|
|
175
|
-
#
|
|
175
|
+
# Check if Claude CLI is installed (only for code analysis)
|
|
176
176
|
if ! command -v "$CLAUDE_CLI" &> /dev/null; then
|
|
177
|
-
error "Claude CLI
|
|
178
|
-
error "
|
|
177
|
+
error "Claude CLI is not installed or not found in PATH"
|
|
178
|
+
error "Install Claude CLI from: https://github.com/anthropics/claude-cli"
|
|
179
179
|
exit 1
|
|
180
180
|
fi
|
|
181
181
|
|
|
182
|
-
#
|
|
182
|
+
# Now check if there are Java files to analyze
|
|
183
183
|
JAVA_FILES=$(git diff --cached --name-only --diff-filter=ACM | grep -E '\.(java|xml|properties|yml|yaml)$' || true)
|
|
184
184
|
|
|
185
185
|
if [ -z "$JAVA_FILES" ]; then
|
|
186
|
-
log "No
|
|
186
|
+
log "No Java/configuration files to review"
|
|
187
187
|
exit 0
|
|
188
188
|
fi
|
|
189
189
|
|
|
190
|
-
#
|
|
190
|
+
# Check if the guidelines file exists
|
|
191
191
|
if [ ! -f "$GUIDELINES_FILE" ]; then
|
|
192
|
-
error "
|
|
193
|
-
error "
|
|
192
|
+
error "Guidelines file not found: $GUIDELINES_FILE"
|
|
193
|
+
error "Please reinstall claude-git-hooks by running:"
|
|
194
194
|
error " claude-hooks install --force"
|
|
195
195
|
exit 1
|
|
196
196
|
fi
|
|
197
197
|
|
|
198
|
-
log "
|
|
198
|
+
log "Java/config files to review: $(echo "$JAVA_FILES" | wc -l)"
|
|
199
199
|
|
|
200
|
-
#
|
|
200
|
+
# Function to filter content with SKIP-ANALYSIS
|
|
201
201
|
filter_skip_analysis() {
|
|
202
202
|
local file_content="$1"
|
|
203
203
|
local filtered_content=""
|
|
@@ -205,77 +205,77 @@ filter_skip_analysis() {
|
|
|
205
205
|
local inside_skip_block=false
|
|
206
206
|
|
|
207
207
|
while IFS= read -r line; do
|
|
208
|
-
#
|
|
208
|
+
# Detect SKIP-ANALYSIS for single line
|
|
209
209
|
if echo "$line" | grep -q "// SKIP-ANALYSIS"; then
|
|
210
210
|
skip_next_line=true
|
|
211
211
|
continue
|
|
212
212
|
fi
|
|
213
213
|
|
|
214
|
-
#
|
|
214
|
+
# Detect start/end of SKIP_ANALYSIS_BLOCK
|
|
215
215
|
if echo "$line" | grep -q "// SKIP_ANALYSIS_BLOCK"; then
|
|
216
216
|
if [ "$inside_skip_block" = true ]; then
|
|
217
|
-
#
|
|
217
|
+
# End of block
|
|
218
218
|
inside_skip_block=false
|
|
219
219
|
else
|
|
220
|
-
#
|
|
220
|
+
# Start of block
|
|
221
221
|
inside_skip_block=true
|
|
222
222
|
fi
|
|
223
223
|
continue
|
|
224
224
|
fi
|
|
225
225
|
|
|
226
|
-
#
|
|
226
|
+
# If we're inside a block, skip the line
|
|
227
227
|
if [ "$inside_skip_block" = true ]; then
|
|
228
228
|
continue
|
|
229
229
|
fi
|
|
230
230
|
|
|
231
|
-
#
|
|
231
|
+
# If we should skip the next line (single comment)
|
|
232
232
|
if [ "$skip_next_line" = true ]; then
|
|
233
233
|
skip_next_line=false
|
|
234
234
|
continue
|
|
235
235
|
fi
|
|
236
236
|
|
|
237
|
-
#
|
|
237
|
+
# Add line to filtered content
|
|
238
238
|
filtered_content="${filtered_content}${line}"$'\n'
|
|
239
239
|
done <<< "$file_content"
|
|
240
240
|
|
|
241
241
|
echo "$filtered_content"
|
|
242
242
|
}
|
|
243
243
|
|
|
244
|
-
#
|
|
244
|
+
# Build the prompt for code analysis
|
|
245
245
|
PROMPT_FILE="$TEMP_DIR/code_review_prompt.txt"
|
|
246
246
|
|
|
247
|
-
#
|
|
247
|
+
# Copy the prompt template
|
|
248
248
|
cat "$PROMPT_TEMPLATE" > "$PROMPT_FILE"
|
|
249
249
|
|
|
250
|
-
#
|
|
251
|
-
echo "===
|
|
250
|
+
# Add the guidelines
|
|
251
|
+
echo "=== EVALUATION GUIDELINES ===" >> "$PROMPT_FILE"
|
|
252
252
|
cat "$GUIDELINES_FILE" >> "$PROMPT_FILE"
|
|
253
|
-
echo -e "\n\n===
|
|
253
|
+
echo -e "\n\n=== CHANGES TO REVIEW ===\n" >> "$PROMPT_FILE"
|
|
254
254
|
|
|
255
|
-
#
|
|
255
|
+
# Process each Java file
|
|
256
256
|
FILE_COUNT=0
|
|
257
257
|
for FILE in $JAVA_FILES; do
|
|
258
258
|
if [ -f "$FILE" ]; then
|
|
259
259
|
FILE_SIZE=$(stat -c%s "$FILE" 2>/dev/null || stat -f%z "$FILE" 2>/dev/null || echo "0")
|
|
260
260
|
|
|
261
261
|
if [ "$FILE_SIZE" -gt "$MAX_FILE_SIZE" ]; then
|
|
262
|
-
warning "
|
|
262
|
+
warning "File $FILE too large ($FILE_SIZE bytes), skipping..."
|
|
263
263
|
continue
|
|
264
264
|
fi
|
|
265
265
|
|
|
266
266
|
echo -e "\n--- Archivo: $FILE ---" >> "$PROMPT_FILE"
|
|
267
267
|
|
|
268
|
-
#
|
|
269
|
-
DIFF_CONTENT=$(git diff --cached "$FILE" 2>/dev/null || echo "
|
|
268
|
+
# Get the diff and filter it
|
|
269
|
+
DIFF_CONTENT=$(git diff --cached "$FILE" 2>/dev/null || echo "Could not get diff")
|
|
270
270
|
FILTERED_DIFF=$(filter_skip_analysis "$DIFF_CONTENT")
|
|
271
271
|
|
|
272
|
-
#
|
|
272
|
+
# Show the filtered diff of the file
|
|
273
273
|
echo -e "\nDiff:" >> "$PROMPT_FILE"
|
|
274
274
|
echo "$FILTERED_DIFF" >> "$PROMPT_FILE"
|
|
275
275
|
|
|
276
|
-
#
|
|
276
|
+
# If it's a new file, show complete filtered content
|
|
277
277
|
if git diff --cached --name-status | grep "^A.*$FILE" > /dev/null; then
|
|
278
|
-
echo -e "\
|
|
278
|
+
echo -e "\nComplete content (new file):" >> "$PROMPT_FILE"
|
|
279
279
|
FILE_CONTENT=$(git show ":$FILE" 2>/dev/null || cat "$FILE")
|
|
280
280
|
FILTERED_CONTENT=$(filter_skip_analysis "$FILE_CONTENT")
|
|
281
281
|
echo "$FILTERED_CONTENT" >> "$PROMPT_FILE"
|
|
@@ -286,45 +286,45 @@ for FILE in $JAVA_FILES; do
|
|
|
286
286
|
done
|
|
287
287
|
|
|
288
288
|
if [ "$FILE_COUNT" -eq 0 ]; then
|
|
289
|
-
log "No
|
|
289
|
+
log "No valid files found to review"
|
|
290
290
|
exit 0
|
|
291
291
|
fi
|
|
292
292
|
|
|
293
293
|
if [ "$FILE_COUNT" -gt 10 ]; then
|
|
294
|
-
warning "
|
|
295
|
-
warning "
|
|
294
|
+
warning "Too many files to review ($FILE_COUNT)"
|
|
295
|
+
warning "Consider splitting the commit into smaller parts"
|
|
296
296
|
exit 0
|
|
297
297
|
fi
|
|
298
298
|
|
|
299
|
-
log "
|
|
299
|
+
log "Sending $FILE_COUNT files for review with Claude..."
|
|
300
300
|
|
|
301
|
-
#
|
|
301
|
+
# Send to Claude and capture response
|
|
302
302
|
RESPONSE_FILE="$TEMP_DIR/code_review_response.txt"
|
|
303
303
|
|
|
304
|
-
#
|
|
304
|
+
# Execute Claude CLI and capture the response
|
|
305
305
|
if $CLAUDE_CLI < "$PROMPT_FILE" > "$RESPONSE_FILE" 2>&1; then
|
|
306
|
-
#
|
|
306
|
+
# Extract the JSON from the response
|
|
307
307
|
JSON_RESPONSE=$(sed -n '/^{/,/^}/p' "$RESPONSE_FILE" | head -n 1000)
|
|
308
308
|
|
|
309
309
|
if [ -z "$JSON_RESPONSE" ]; then
|
|
310
|
-
error "
|
|
311
|
-
error "
|
|
310
|
+
error "Did not receive a valid JSON response from Claude"
|
|
311
|
+
error "Complete response:"
|
|
312
312
|
cat "$RESPONSE_FILE"
|
|
313
313
|
exit 1
|
|
314
314
|
fi
|
|
315
315
|
|
|
316
|
-
#
|
|
316
|
+
# Save JSON for debug if activated
|
|
317
317
|
if [ -n "$DEBUG" ]; then
|
|
318
318
|
echo "$JSON_RESPONSE" > ./debug-claude-response.json
|
|
319
|
-
log "
|
|
319
|
+
log "Response saved to debug-claude-response.json"
|
|
320
320
|
fi
|
|
321
321
|
|
|
322
|
-
#
|
|
322
|
+
# Parse the response using jq
|
|
323
323
|
APPROVED=$(echo "$JSON_RESPONSE" | jq -r '.approved // false')
|
|
324
324
|
SCORE=$(echo "$JSON_RESPONSE" | jq -r '.score // 0')
|
|
325
325
|
RECOMMENDATIONS=$(echo "$JSON_RESPONSE" | jq -r '.recommendations[]?' 2>/dev/null | sed '/^$/d')
|
|
326
326
|
|
|
327
|
-
#
|
|
327
|
+
# Parse blockingIssues as objects and extract descriptions
|
|
328
328
|
BLOCKING_ISSUES=""
|
|
329
329
|
BLOCKING_COUNT=$(echo "$JSON_RESPONSE" | jq '.blockingIssues | length' 2>/dev/null || echo "0")
|
|
330
330
|
|
|
@@ -335,7 +335,7 @@ if $CLAUDE_CLI < "$PROMPT_FILE" > "$RESPONSE_FILE" 2>&1; then
|
|
|
335
335
|
QUALITY_GATE=$(echo "$JSON_RESPONSE" | jq -r '.QUALITY_GATE // ""' 2>/dev/null)
|
|
336
336
|
|
|
337
337
|
if [ -n "$QUALITY_GATE" ] && [ "$QUALITY_GATE" != "null" ]; then
|
|
338
|
-
#
|
|
338
|
+
# Show SonarQube style results
|
|
339
339
|
echo
|
|
340
340
|
echo "╔════════════════════════════════════════════════════════════════════╗"
|
|
341
341
|
echo "║ CODE QUALITY ANALYSIS ║"
|
|
@@ -410,51 +410,51 @@ if $CLAUDE_CLI < "$PROMPT_FILE" > "$RESPONSE_FILE" 2>&1; then
|
|
|
410
410
|
echo
|
|
411
411
|
log "✅ Code analysis completed. Quality gate passed."
|
|
412
412
|
else
|
|
413
|
-
#
|
|
413
|
+
# Show results in classic format
|
|
414
414
|
echo
|
|
415
|
-
echo "===
|
|
415
|
+
echo "=== REVIEW RESULTS ==="
|
|
416
416
|
echo "Score: $SCORE/10"
|
|
417
417
|
|
|
418
418
|
if [ -n "$RECOMMENDATIONS" ] && [ "$RECOMMENDATIONS" != "null" ]; then
|
|
419
419
|
echo
|
|
420
|
-
echo "===
|
|
420
|
+
echo "=== RECOMMENDATIONS ==="
|
|
421
421
|
echo "$RECOMMENDATIONS" | sed 's/^/- /'
|
|
422
422
|
fi
|
|
423
423
|
|
|
424
|
-
#
|
|
424
|
+
# Check if the commit should be blocked
|
|
425
425
|
if [ "$APPROVED" = "false" ]; then
|
|
426
|
-
error "❌ Commit
|
|
426
|
+
error "❌ Commit rejected due to critical issues"
|
|
427
427
|
if [ -n "$BLOCKING_ISSUES" ] && [ "$BLOCKING_ISSUES" != "null" ]; then
|
|
428
428
|
echo
|
|
429
|
-
echo "===
|
|
429
|
+
echo "=== CRITICAL ISSUES ==="
|
|
430
430
|
echo "$BLOCKING_ISSUES" | sed 's/^/- /'
|
|
431
431
|
|
|
432
|
-
#
|
|
432
|
+
# Generate AI-friendly resolution prompt
|
|
433
433
|
generate_resolution_prompt
|
|
434
434
|
fi
|
|
435
435
|
exit 1
|
|
436
436
|
fi
|
|
437
437
|
|
|
438
|
-
#
|
|
438
|
+
# Show additional details if they exist
|
|
439
439
|
DETAILS=$(echo "$JSON_RESPONSE" | jq -r '.details // null')
|
|
440
440
|
if [ -n "$DETAILS" ] && [ "$DETAILS" != "null" ]; then
|
|
441
441
|
echo
|
|
442
|
-
echo "===
|
|
443
|
-
#
|
|
442
|
+
echo "=== ADDITIONAL DETAILS ==="
|
|
443
|
+
# If details is a string, print it directly
|
|
444
444
|
if echo "$DETAILS" | jq -e 'type == "string"' >/dev/null 2>&1; then
|
|
445
445
|
echo "$DETAILS" | jq -r '.'
|
|
446
|
-
#
|
|
446
|
+
# If it's an object or array, format it
|
|
447
447
|
else
|
|
448
448
|
echo "$DETAILS" | jq '.'
|
|
449
449
|
fi
|
|
450
450
|
fi
|
|
451
451
|
|
|
452
|
-
log "✅
|
|
452
|
+
log "✅ Review completed. Commit approved (Score: $SCORE/10)"
|
|
453
453
|
fi
|
|
454
454
|
|
|
455
455
|
else
|
|
456
|
-
error "Error
|
|
457
|
-
error "
|
|
456
|
+
error "Error executing Claude CLI"
|
|
457
|
+
error "Check that Claude CLI is configured correctly"
|
|
458
458
|
cat "$RESPONSE_FILE"
|
|
459
459
|
exit 1
|
|
460
460
|
fi
|
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
2
|
|
|
3
|
-
# Git Prepare-commit-msg Hook
|
|
3
|
+
# Git Prepare-commit-msg Hook to generate automatic messages with Claude
|
|
4
4
|
# Archivo: .git/hooks/prepare-commit-msg
|
|
5
5
|
|
|
6
6
|
set -e
|
|
7
7
|
|
|
8
|
-
#
|
|
8
|
+
# Configuration
|
|
9
9
|
CLAUDE_CLI="claude"
|
|
10
10
|
TEMP_DIR="/tmp/commit-msg-$$"
|
|
11
11
|
MAX_FILE_SIZE=100000
|
|
12
12
|
AUTO_COMMIT_ENABLED=true
|
|
13
13
|
|
|
14
|
-
#
|
|
14
|
+
# Colors for output
|
|
15
15
|
RED='\033[0;31m'
|
|
16
16
|
GREEN='\033[0;32m'
|
|
17
17
|
YELLOW='\033[1;33m'
|
|
18
18
|
NC='\033[0m'
|
|
19
19
|
|
|
20
|
-
#
|
|
20
|
+
# Function for logging
|
|
21
21
|
log() {
|
|
22
22
|
printf "${GREEN}[PREPARE-MSG]${NC} %s\n" "$1" >&2
|
|
23
23
|
}
|
|
@@ -26,35 +26,35 @@ warning() {
|
|
|
26
26
|
printf "${YELLOW}[WARNING]${NC} %s\n" "$1" >&2
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
#
|
|
29
|
+
# Function to clean temporary files
|
|
30
30
|
cleanup() {
|
|
31
31
|
rm -rf "$TEMP_DIR"
|
|
32
32
|
}
|
|
33
33
|
trap cleanup EXIT
|
|
34
34
|
|
|
35
|
-
#
|
|
35
|
+
# Hook arguments
|
|
36
36
|
COMMIT_MSG_FILE="$1"
|
|
37
37
|
COMMIT_SOURCE="$2"
|
|
38
38
|
|
|
39
|
-
#
|
|
39
|
+
# Only process if it's a normal commit
|
|
40
40
|
if [ "$COMMIT_SOURCE" != "" ] && [ "$COMMIT_SOURCE" != "message" ]; then
|
|
41
41
|
exit 0
|
|
42
42
|
fi
|
|
43
43
|
|
|
44
|
-
#
|
|
44
|
+
# Read the current message
|
|
45
45
|
CURRENT_MSG=$(head -n 1 "$COMMIT_MSG_FILE" 2>/dev/null || echo "")
|
|
46
46
|
|
|
47
|
-
#
|
|
47
|
+
# Check if we need to generate a message
|
|
48
48
|
if [ "$CURRENT_MSG" = "auto" ]; then
|
|
49
|
-
log "
|
|
49
|
+
log "Trying to generate message..."
|
|
50
50
|
else
|
|
51
51
|
exit 0
|
|
52
52
|
fi
|
|
53
53
|
|
|
54
|
-
#
|
|
54
|
+
# Check if Claude CLI is installed
|
|
55
55
|
if ! command -v "$CLAUDE_CLI" &> /dev/null; then
|
|
56
|
-
warning "Claude CLI
|
|
57
|
-
warning "Commit
|
|
56
|
+
warning "Claude CLI is not installed"
|
|
57
|
+
warning "Commit canceled. Run again without 'auto' to write manual message"
|
|
58
58
|
exit 1
|
|
59
59
|
fi
|
|
60
60
|
|
|
@@ -62,49 +62,49 @@ mkdir -p "$TEMP_DIR"
|
|
|
62
62
|
PROMPT_FILE="$TEMP_DIR/commit_msg_prompt.txt"
|
|
63
63
|
|
|
64
64
|
cat > "$PROMPT_FILE" << 'EOF'
|
|
65
|
-
|
|
65
|
+
Analyze the following changes and generate a commit message following the Conventional Commits format.
|
|
66
66
|
|
|
67
|
-
|
|
67
|
+
Respond ONLY with a valid JSON:
|
|
68
68
|
|
|
69
69
|
{
|
|
70
70
|
"type": "feat|fix|docs|style|refactor|test|chore|ci|perf",
|
|
71
|
-
"scope": "
|
|
72
|
-
"title": "
|
|
73
|
-
"body": "
|
|
71
|
+
"scope": "optional scope (e.g.: api, frontend, db)",
|
|
72
|
+
"title": "short description in present tense (max 50 chars)",
|
|
73
|
+
"body": "optional detailed description"
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
-
|
|
76
|
+
CHANGES TO ANALYZE:
|
|
77
77
|
EOF
|
|
78
78
|
|
|
79
|
-
#
|
|
79
|
+
# Get staged files
|
|
80
80
|
ALL_STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACM 2>/dev/null || echo "")
|
|
81
81
|
|
|
82
82
|
if [ -z "$ALL_STAGED_FILES" ]; then
|
|
83
|
-
warning "No
|
|
84
|
-
warning "Commit
|
|
83
|
+
warning "No staged files"
|
|
84
|
+
warning "Commit canceled. Run again without 'auto' to write manual message"
|
|
85
85
|
exit 1
|
|
86
86
|
fi
|
|
87
87
|
|
|
88
|
-
printf "\
|
|
88
|
+
printf "\nModified files:\n" >> "$PROMPT_FILE"
|
|
89
89
|
echo "$ALL_STAGED_FILES" >> "$PROMPT_FILE"
|
|
90
90
|
|
|
91
|
-
printf "\
|
|
92
|
-
git diff --cached --stat >> "$PROMPT_FILE" 2>/dev/null || echo "
|
|
91
|
+
printf "\nSummary of changes:\n" >> "$PROMPT_FILE"
|
|
92
|
+
git diff --cached --stat >> "$PROMPT_FILE" 2>/dev/null || echo "Could not get statistics"
|
|
93
93
|
|
|
94
|
-
#
|
|
94
|
+
# Show diffs of small files
|
|
95
95
|
for FILE in $ALL_STAGED_FILES; do
|
|
96
96
|
if [ -f "$FILE" ]; then
|
|
97
97
|
FILE_SIZE=$(stat -c%s "$FILE" 2>/dev/null || stat -f%z "$FILE" 2>/dev/null || echo "0")
|
|
98
98
|
|
|
99
99
|
if [ "$FILE_SIZE" -lt "$MAX_FILE_SIZE" ]; then
|
|
100
|
-
printf "\n--- Diff
|
|
101
|
-
git diff --cached "$FILE" >> "$PROMPT_FILE" 2>/dev/null || echo "
|
|
100
|
+
printf "\n--- Diff of %s ---\n" "$FILE" >> "$PROMPT_FILE"
|
|
101
|
+
git diff --cached "$FILE" >> "$PROMPT_FILE" 2>/dev/null || echo "Could not get diff"
|
|
102
102
|
fi
|
|
103
103
|
fi
|
|
104
104
|
done
|
|
105
105
|
|
|
106
106
|
RESPONSE_FILE="$TEMP_DIR/commit_msg_response.txt"
|
|
107
|
-
log "
|
|
107
|
+
log "Generating commit message with Claude..."
|
|
108
108
|
|
|
109
109
|
if $CLAUDE_CLI < "$PROMPT_FILE" > "$RESPONSE_FILE" 2>&1; then
|
|
110
110
|
JSON_MSG=$(sed -n '/^{/,/^}/p' "$RESPONSE_FILE" | head -n 1000)
|
|
@@ -127,12 +127,12 @@ if $CLAUDE_CLI < "$PROMPT_FILE" > "$RESPONSE_FILE" 2>&1; then
|
|
|
127
127
|
fi
|
|
128
128
|
|
|
129
129
|
printf "%s\n" "$FULL_MESSAGE" > "$COMMIT_MSG_FILE"
|
|
130
|
-
log "📝
|
|
130
|
+
log "📝 Message generated: $(echo "$FULL_MESSAGE" | head -n 1)"
|
|
131
131
|
exit 0
|
|
132
132
|
fi
|
|
133
133
|
fi
|
|
134
134
|
fi
|
|
135
135
|
|
|
136
|
-
warning "
|
|
137
|
-
warning "Commit
|
|
136
|
+
warning "Could not generate message automatically with Claude"
|
|
137
|
+
warning "Commit canceled. Run again without 'auto' to write manual message"
|
|
138
138
|
exit 1
|