claude-git-hooks 1.5.0 → 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 +59 -4
- package/README.md +163 -414
- package/bin/claude-hooks +339 -374
- package/package.json +1 -1
- package/templates/pre-commit +97 -103
- 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,32 +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
|
-
sed -i "s|{{ANALYSIS_MODE}}|sonar|g" "$RESOLUTION_FILE"
|
|
113
112
|
|
|
114
|
-
#
|
|
113
|
+
# Create temporary file for formatted issues
|
|
115
114
|
local TEMP_ISSUES_FILE=$(mktemp)
|
|
116
115
|
echo "$ISSUES_FORMATTED" > "$TEMP_ISSUES_FILE"
|
|
117
116
|
|
|
118
|
-
#
|
|
117
|
+
# Replace {{BLOCKING_ISSUES}} with content
|
|
119
118
|
if [ -n "$ISSUES_FORMATTED" ]; then
|
|
120
119
|
sed -i "/{{BLOCKING_ISSUES}}/r $TEMP_ISSUES_FILE" "$RESOLUTION_FILE"
|
|
121
120
|
fi
|
|
122
121
|
sed -i "s|{{BLOCKING_ISSUES}}||g" "$RESOLUTION_FILE"
|
|
123
122
|
rm -f "$TEMP_ISSUES_FILE"
|
|
124
123
|
|
|
125
|
-
#
|
|
124
|
+
# Add content from affected files
|
|
126
125
|
local TEMP_FILES=$(mktemp)
|
|
127
126
|
echo "$JSON_RESPONSE" | jq -r '.blockingIssues[].file' 2>/dev/null | sort -u | while IFS= read -r file; do
|
|
128
127
|
if [ -f "$file" ]; then
|
|
@@ -135,7 +134,7 @@ generate_resolution_prompt() {
|
|
|
135
134
|
fi
|
|
136
135
|
done
|
|
137
136
|
|
|
138
|
-
#
|
|
137
|
+
# Replace {{FILE_CONTENTS}} with content
|
|
139
138
|
if [ -s "$TEMP_FILES" ]; then
|
|
140
139
|
sed -i "/{{FILE_CONTENTS}}/r $TEMP_FILES" "$RESOLUTION_FILE"
|
|
141
140
|
fi
|
|
@@ -143,70 +142,62 @@ generate_resolution_prompt() {
|
|
|
143
142
|
rm -f "$TEMP_FILES"
|
|
144
143
|
|
|
145
144
|
echo
|
|
146
|
-
echo -e "${YELLOW}===
|
|
147
|
-
echo -e "${GREEN}
|
|
148
|
-
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}"
|
|
149
148
|
echo
|
|
150
149
|
}
|
|
151
150
|
|
|
152
|
-
#
|
|
153
|
-
ANALYSIS_MODE="sonar"
|
|
154
|
-
|
|
155
|
-
# Configurar archivos para modo SonarQube
|
|
151
|
+
# Configure files for SonarQube mode
|
|
156
152
|
GUIDELINES_FILE=".claude/CLAUDE_PRE_COMMIT_SONAR.md"
|
|
157
153
|
PROMPT_TEMPLATE=".claude/CLAUDE_ANALYSIS_PROMPT_SONAR.md"
|
|
158
|
-
log "Usando modo de análisis: SonarQube"
|
|
159
154
|
|
|
160
|
-
# Guardar preferencia si no existe
|
|
161
|
-
if [ ! -f ".claude-analysis-mode" ]; then
|
|
162
|
-
echo "sonar" > .claude-analysis-mode
|
|
163
|
-
fi
|
|
164
155
|
|
|
165
|
-
#
|
|
156
|
+
# Check that the prompt template exists
|
|
166
157
|
if [ ! -f "$PROMPT_TEMPLATE" ]; then
|
|
167
|
-
error "
|
|
168
|
-
error "
|
|
169
|
-
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:"
|
|
170
161
|
error " claude-hooks install --force"
|
|
171
162
|
exit 1
|
|
172
163
|
fi
|
|
173
|
-
#
|
|
164
|
+
# Function to clean temporary files
|
|
174
165
|
cleanup() {
|
|
175
166
|
rm -rf "$TEMP_DIR"
|
|
176
167
|
}
|
|
177
168
|
|
|
178
|
-
#
|
|
169
|
+
# Configure cleanup on exit
|
|
179
170
|
trap cleanup EXIT
|
|
180
171
|
|
|
181
|
-
#
|
|
172
|
+
# Create temporary directory
|
|
182
173
|
mkdir -p "$TEMP_DIR"
|
|
183
174
|
|
|
184
|
-
#
|
|
175
|
+
# Check if Claude CLI is installed (only for code analysis)
|
|
185
176
|
if ! command -v "$CLAUDE_CLI" &> /dev/null; then
|
|
186
|
-
error "Claude CLI
|
|
187
|
-
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"
|
|
188
179
|
exit 1
|
|
189
180
|
fi
|
|
190
181
|
|
|
191
|
-
#
|
|
182
|
+
# Now check if there are Java files to analyze
|
|
192
183
|
JAVA_FILES=$(git diff --cached --name-only --diff-filter=ACM | grep -E '\.(java|xml|properties|yml|yaml)$' || true)
|
|
193
184
|
|
|
194
185
|
if [ -z "$JAVA_FILES" ]; then
|
|
195
|
-
log "No
|
|
186
|
+
log "No Java/configuration files to review"
|
|
196
187
|
exit 0
|
|
197
188
|
fi
|
|
198
189
|
|
|
199
|
-
#
|
|
190
|
+
# Check if the guidelines file exists
|
|
200
191
|
if [ ! -f "$GUIDELINES_FILE" ]; then
|
|
201
|
-
error "
|
|
202
|
-
error "
|
|
192
|
+
error "Guidelines file not found: $GUIDELINES_FILE"
|
|
193
|
+
error "Please reinstall claude-git-hooks by running:"
|
|
203
194
|
error " claude-hooks install --force"
|
|
204
195
|
exit 1
|
|
205
196
|
fi
|
|
206
197
|
|
|
207
|
-
log "
|
|
198
|
+
log "Java/config files to review: $(echo "$JAVA_FILES" | wc -l)"
|
|
208
199
|
|
|
209
|
-
#
|
|
200
|
+
# Function to filter content with SKIP-ANALYSIS
|
|
210
201
|
filter_skip_analysis() {
|
|
211
202
|
local file_content="$1"
|
|
212
203
|
local filtered_content=""
|
|
@@ -214,73 +205,77 @@ filter_skip_analysis() {
|
|
|
214
205
|
local inside_skip_block=false
|
|
215
206
|
|
|
216
207
|
while IFS= read -r line; do
|
|
217
|
-
#
|
|
208
|
+
# Detect SKIP-ANALYSIS for single line
|
|
218
209
|
if echo "$line" | grep -q "// SKIP-ANALYSIS"; then
|
|
210
|
+
skip_next_line=true
|
|
211
|
+
continue
|
|
212
|
+
fi
|
|
213
|
+
|
|
214
|
+
# Detect start/end of SKIP_ANALYSIS_BLOCK
|
|
215
|
+
if echo "$line" | grep -q "// SKIP_ANALYSIS_BLOCK"; then
|
|
219
216
|
if [ "$inside_skip_block" = true ]; then
|
|
220
|
-
#
|
|
217
|
+
# End of block
|
|
221
218
|
inside_skip_block=false
|
|
222
219
|
else
|
|
223
|
-
#
|
|
220
|
+
# Start of block
|
|
224
221
|
inside_skip_block=true
|
|
225
|
-
skip_next_line=true
|
|
226
222
|
fi
|
|
227
223
|
continue
|
|
228
224
|
fi
|
|
229
225
|
|
|
230
|
-
#
|
|
226
|
+
# If we're inside a block, skip the line
|
|
231
227
|
if [ "$inside_skip_block" = true ]; then
|
|
232
228
|
continue
|
|
233
229
|
fi
|
|
234
230
|
|
|
235
|
-
#
|
|
231
|
+
# If we should skip the next line (single comment)
|
|
236
232
|
if [ "$skip_next_line" = true ]; then
|
|
237
233
|
skip_next_line=false
|
|
238
|
-
inside_skip_block=false
|
|
239
234
|
continue
|
|
240
235
|
fi
|
|
241
236
|
|
|
242
|
-
#
|
|
237
|
+
# Add line to filtered content
|
|
243
238
|
filtered_content="${filtered_content}${line}"$'\n'
|
|
244
239
|
done <<< "$file_content"
|
|
245
240
|
|
|
246
241
|
echo "$filtered_content"
|
|
247
242
|
}
|
|
248
243
|
|
|
249
|
-
#
|
|
244
|
+
# Build the prompt for code analysis
|
|
250
245
|
PROMPT_FILE="$TEMP_DIR/code_review_prompt.txt"
|
|
251
246
|
|
|
252
|
-
#
|
|
247
|
+
# Copy the prompt template
|
|
253
248
|
cat "$PROMPT_TEMPLATE" > "$PROMPT_FILE"
|
|
254
249
|
|
|
255
|
-
#
|
|
256
|
-
echo "===
|
|
250
|
+
# Add the guidelines
|
|
251
|
+
echo "=== EVALUATION GUIDELINES ===" >> "$PROMPT_FILE"
|
|
257
252
|
cat "$GUIDELINES_FILE" >> "$PROMPT_FILE"
|
|
258
|
-
echo -e "\n\n===
|
|
253
|
+
echo -e "\n\n=== CHANGES TO REVIEW ===\n" >> "$PROMPT_FILE"
|
|
259
254
|
|
|
260
|
-
#
|
|
255
|
+
# Process each Java file
|
|
261
256
|
FILE_COUNT=0
|
|
262
257
|
for FILE in $JAVA_FILES; do
|
|
263
258
|
if [ -f "$FILE" ]; then
|
|
264
259
|
FILE_SIZE=$(stat -c%s "$FILE" 2>/dev/null || stat -f%z "$FILE" 2>/dev/null || echo "0")
|
|
265
260
|
|
|
266
261
|
if [ "$FILE_SIZE" -gt "$MAX_FILE_SIZE" ]; then
|
|
267
|
-
warning "
|
|
262
|
+
warning "File $FILE too large ($FILE_SIZE bytes), skipping..."
|
|
268
263
|
continue
|
|
269
264
|
fi
|
|
270
265
|
|
|
271
266
|
echo -e "\n--- Archivo: $FILE ---" >> "$PROMPT_FILE"
|
|
272
267
|
|
|
273
|
-
#
|
|
274
|
-
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")
|
|
275
270
|
FILTERED_DIFF=$(filter_skip_analysis "$DIFF_CONTENT")
|
|
276
271
|
|
|
277
|
-
#
|
|
272
|
+
# Show the filtered diff of the file
|
|
278
273
|
echo -e "\nDiff:" >> "$PROMPT_FILE"
|
|
279
274
|
echo "$FILTERED_DIFF" >> "$PROMPT_FILE"
|
|
280
275
|
|
|
281
|
-
#
|
|
276
|
+
# If it's a new file, show complete filtered content
|
|
282
277
|
if git diff --cached --name-status | grep "^A.*$FILE" > /dev/null; then
|
|
283
|
-
echo -e "\
|
|
278
|
+
echo -e "\nComplete content (new file):" >> "$PROMPT_FILE"
|
|
284
279
|
FILE_CONTENT=$(git show ":$FILE" 2>/dev/null || cat "$FILE")
|
|
285
280
|
FILTERED_CONTENT=$(filter_skip_analysis "$FILE_CONTENT")
|
|
286
281
|
echo "$FILTERED_CONTENT" >> "$PROMPT_FILE"
|
|
@@ -291,45 +286,45 @@ for FILE in $JAVA_FILES; do
|
|
|
291
286
|
done
|
|
292
287
|
|
|
293
288
|
if [ "$FILE_COUNT" -eq 0 ]; then
|
|
294
|
-
log "No
|
|
289
|
+
log "No valid files found to review"
|
|
295
290
|
exit 0
|
|
296
291
|
fi
|
|
297
292
|
|
|
298
293
|
if [ "$FILE_COUNT" -gt 10 ]; then
|
|
299
|
-
warning "
|
|
300
|
-
warning "
|
|
294
|
+
warning "Too many files to review ($FILE_COUNT)"
|
|
295
|
+
warning "Consider splitting the commit into smaller parts"
|
|
301
296
|
exit 0
|
|
302
297
|
fi
|
|
303
298
|
|
|
304
|
-
log "
|
|
299
|
+
log "Sending $FILE_COUNT files for review with Claude..."
|
|
305
300
|
|
|
306
|
-
#
|
|
301
|
+
# Send to Claude and capture response
|
|
307
302
|
RESPONSE_FILE="$TEMP_DIR/code_review_response.txt"
|
|
308
303
|
|
|
309
|
-
#
|
|
304
|
+
# Execute Claude CLI and capture the response
|
|
310
305
|
if $CLAUDE_CLI < "$PROMPT_FILE" > "$RESPONSE_FILE" 2>&1; then
|
|
311
|
-
#
|
|
306
|
+
# Extract the JSON from the response
|
|
312
307
|
JSON_RESPONSE=$(sed -n '/^{/,/^}/p' "$RESPONSE_FILE" | head -n 1000)
|
|
313
308
|
|
|
314
309
|
if [ -z "$JSON_RESPONSE" ]; then
|
|
315
|
-
error "
|
|
316
|
-
error "
|
|
310
|
+
error "Did not receive a valid JSON response from Claude"
|
|
311
|
+
error "Complete response:"
|
|
317
312
|
cat "$RESPONSE_FILE"
|
|
318
313
|
exit 1
|
|
319
314
|
fi
|
|
320
315
|
|
|
321
|
-
#
|
|
316
|
+
# Save JSON for debug if activated
|
|
322
317
|
if [ -n "$DEBUG" ]; then
|
|
323
318
|
echo "$JSON_RESPONSE" > ./debug-claude-response.json
|
|
324
|
-
log "
|
|
319
|
+
log "Response saved to debug-claude-response.json"
|
|
325
320
|
fi
|
|
326
321
|
|
|
327
|
-
#
|
|
322
|
+
# Parse the response using jq
|
|
328
323
|
APPROVED=$(echo "$JSON_RESPONSE" | jq -r '.approved // false')
|
|
329
324
|
SCORE=$(echo "$JSON_RESPONSE" | jq -r '.score // 0')
|
|
330
325
|
RECOMMENDATIONS=$(echo "$JSON_RESPONSE" | jq -r '.recommendations[]?' 2>/dev/null | sed '/^$/d')
|
|
331
326
|
|
|
332
|
-
#
|
|
327
|
+
# Parse blockingIssues as objects and extract descriptions
|
|
333
328
|
BLOCKING_ISSUES=""
|
|
334
329
|
BLOCKING_COUNT=$(echo "$JSON_RESPONSE" | jq '.blockingIssues | length' 2>/dev/null || echo "0")
|
|
335
330
|
|
|
@@ -337,11 +332,10 @@ if $CLAUDE_CLI < "$PROMPT_FILE" > "$RESPONSE_FILE" 2>&1; then
|
|
|
337
332
|
BLOCKING_ISSUES=$(echo "$JSON_RESPONSE" | jq -r '.blockingIssues[].description' 2>/dev/null | sed '/^$/d')
|
|
338
333
|
fi
|
|
339
334
|
|
|
340
|
-
# Verificar si estamos en modo SonarQube
|
|
341
335
|
QUALITY_GATE=$(echo "$JSON_RESPONSE" | jq -r '.QUALITY_GATE // ""' 2>/dev/null)
|
|
342
336
|
|
|
343
|
-
if [
|
|
344
|
-
#
|
|
337
|
+
if [ -n "$QUALITY_GATE" ] && [ "$QUALITY_GATE" != "null" ]; then
|
|
338
|
+
# Show SonarQube style results
|
|
345
339
|
echo
|
|
346
340
|
echo "╔════════════════════════════════════════════════════════════════════╗"
|
|
347
341
|
echo "║ CODE QUALITY ANALYSIS ║"
|
|
@@ -416,51 +410,51 @@ if $CLAUDE_CLI < "$PROMPT_FILE" > "$RESPONSE_FILE" 2>&1; then
|
|
|
416
410
|
echo
|
|
417
411
|
log "✅ Code analysis completed. Quality gate passed."
|
|
418
412
|
else
|
|
419
|
-
#
|
|
413
|
+
# Show results in classic format
|
|
420
414
|
echo
|
|
421
|
-
echo "===
|
|
415
|
+
echo "=== REVIEW RESULTS ==="
|
|
422
416
|
echo "Score: $SCORE/10"
|
|
423
417
|
|
|
424
418
|
if [ -n "$RECOMMENDATIONS" ] && [ "$RECOMMENDATIONS" != "null" ]; then
|
|
425
419
|
echo
|
|
426
|
-
echo "===
|
|
420
|
+
echo "=== RECOMMENDATIONS ==="
|
|
427
421
|
echo "$RECOMMENDATIONS" | sed 's/^/- /'
|
|
428
422
|
fi
|
|
429
423
|
|
|
430
|
-
#
|
|
424
|
+
# Check if the commit should be blocked
|
|
431
425
|
if [ "$APPROVED" = "false" ]; then
|
|
432
|
-
error "❌ Commit
|
|
426
|
+
error "❌ Commit rejected due to critical issues"
|
|
433
427
|
if [ -n "$BLOCKING_ISSUES" ] && [ "$BLOCKING_ISSUES" != "null" ]; then
|
|
434
428
|
echo
|
|
435
|
-
echo "===
|
|
429
|
+
echo "=== CRITICAL ISSUES ==="
|
|
436
430
|
echo "$BLOCKING_ISSUES" | sed 's/^/- /'
|
|
437
431
|
|
|
438
|
-
#
|
|
432
|
+
# Generate AI-friendly resolution prompt
|
|
439
433
|
generate_resolution_prompt
|
|
440
434
|
fi
|
|
441
435
|
exit 1
|
|
442
436
|
fi
|
|
443
437
|
|
|
444
|
-
#
|
|
438
|
+
# Show additional details if they exist
|
|
445
439
|
DETAILS=$(echo "$JSON_RESPONSE" | jq -r '.details // null')
|
|
446
440
|
if [ -n "$DETAILS" ] && [ "$DETAILS" != "null" ]; then
|
|
447
441
|
echo
|
|
448
|
-
echo "===
|
|
449
|
-
#
|
|
442
|
+
echo "=== ADDITIONAL DETAILS ==="
|
|
443
|
+
# If details is a string, print it directly
|
|
450
444
|
if echo "$DETAILS" | jq -e 'type == "string"' >/dev/null 2>&1; then
|
|
451
445
|
echo "$DETAILS" | jq -r '.'
|
|
452
|
-
#
|
|
446
|
+
# If it's an object or array, format it
|
|
453
447
|
else
|
|
454
448
|
echo "$DETAILS" | jq '.'
|
|
455
449
|
fi
|
|
456
450
|
fi
|
|
457
451
|
|
|
458
|
-
log "✅
|
|
452
|
+
log "✅ Review completed. Commit approved (Score: $SCORE/10)"
|
|
459
453
|
fi
|
|
460
454
|
|
|
461
455
|
else
|
|
462
|
-
error "Error
|
|
463
|
-
error "
|
|
456
|
+
error "Error executing Claude CLI"
|
|
457
|
+
error "Check that Claude CLI is configured correctly"
|
|
464
458
|
cat "$RESPONSE_FILE"
|
|
465
459
|
exit 1
|
|
466
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
|