claude-git-hooks 1.5.5 → 2.0.0

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.
@@ -1,466 +1,79 @@
1
1
  #!/bin/bash
2
2
 
3
- # Git Pre-commit Hook for code evaluation with Claude CLI
4
- # Archivo: .git/hooks/pre-commit
3
+ # Git Pre-commit Hook - Node.js Wrapper
4
+ # This is a minimal wrapper that calls the actual Node.js implementation
5
+ # Why: Allows the Node.js script to use relative imports from its installed location
5
6
 
6
7
  set -e
7
8
 
8
- # Configuration
9
- CLAUDE_CLI="claude"
10
- TEMP_DIR="/tmp/code-review-$$"
11
- MAX_FILE_SIZE=100000 # 100KB maximum per file
12
- USE_SUBAGENTS="${CLAUDE_USE_SUBAGENTS:-false}" # Use subagents for parallel analysis
13
- SUBAGENT_MODEL="${CLAUDE_SUBAGENT_MODEL:-haiku}" # Model for subagents (haiku/sonnet/opus)
14
- SUBAGENT_BATCH_SIZE="${CLAUDE_SUBAGENT_BATCH_SIZE:-3}" # Number of parallel subagents per batch
15
-
16
- # Validate batch size (must be >= 1)
17
- if [ "$SUBAGENT_BATCH_SIZE" -le 0 ] 2>/dev/null; then
18
- SUBAGENT_BATCH_SIZE=1
19
- fi
20
-
21
9
  # Colors for output
22
10
  RED='\033[0;31m'
23
- GREEN='\033[0;32m'
24
- YELLOW='\033[1;33m'
25
- BLUE='\033[0;34m'
26
- NC='\033[0m' # No Color
27
-
28
- # Function for logging
29
- log() {
30
- echo -e "${GREEN}[PRE-COMMIT]${NC} $1"
31
- }
32
-
33
- error() {
34
- echo -e "${RED}[ERROR]${NC} $1"
35
- }
36
-
37
- warning() {
38
- echo -e "${YELLOW}[WARNING]${NC} $1"
39
- }
40
-
41
- # Function to show elapsed time
42
- show_elapsed_time() {
43
- local start_ms=$1
44
- local end_ms=$(date +%s%3N 2>/dev/null || echo $(($(date +%s) * 1000)))
45
- local elapsed=$((end_ms - start_ms))
46
- local seconds=$((elapsed / 1000))
47
- local ms=$((elapsed % 1000))
48
- echo -e "${BLUE}⏱️ Analysis completed in ${seconds}.${ms}s${NC}"
11
+ NC='\033[0m'
12
+
13
+ # Convert Windows path to Git Bash/WSL path
14
+ # Why: npm prefix -g in Git Bash returns Windows paths (C:\...) that need conversion
15
+ convert_windows_path() {
16
+ local path="$1"
17
+
18
+ # Check if it's a Windows path (contains :\ or starts with C:)
19
+ if [[ "$path" =~ ^[A-Za-z]:\\ ]] || [[ "$path" =~ ^[A-Za-z]: ]]; then
20
+ # Convert C:\path\to\file to /c/path/to/file
21
+ # First, extract drive letter
22
+ local drive=$(echo "$path" | sed 's/^\([A-Za-z]\):.*/\1/' | tr '[:upper:]' '[:lower:]')
23
+ # Remove drive letter and colon, replace backslashes with forward slashes
24
+ local rest=$(echo "$path" | sed 's/^[A-Za-z]://' | sed 's/\\/\//g')
25
+ echo "/$drive$rest"
26
+ else
27
+ echo "$path"
28
+ fi
49
29
  }
50
30
 
51
- # Check version at start (before any analysis)
52
- # Try to find the check-version.sh script
53
- CHECK_VERSION_SCRIPT=""
54
- # Search in multiple possible locations
55
- SCRIPT_PATHS=(
56
- "$(dirname "$0")/check-version.sh"
57
- "/usr/local/lib/node_modules/claude-git-hooks/templates/check-version.sh"
58
- "/usr/lib/node_modules/claude-git-hooks/templates/check-version.sh"
59
- "$HOME/.npm-global/lib/node_modules/claude-git-hooks/templates/check-version.sh"
60
- "$(npm prefix -g 2>/dev/null)/lib/node_modules/claude-git-hooks/templates/check-version.sh"
61
- )
31
+ # Function to find the Node.js script
32
+ find_hook_script() {
33
+ # Why: Try multiple locations to find where npm installed the package
34
+ # Checks: global npm, local node_modules, npm prefix
62
35
 
63
- for path in "${SCRIPT_PATHS[@]}"; do
64
- if [ -f "$path" ]; then
65
- CHECK_VERSION_SCRIPT="$path"
66
- break
36
+ # Get npm global prefix and convert if it's a Windows path
37
+ local npm_prefix=$(npm prefix -g 2>/dev/null || echo "")
38
+ if [ -n "$npm_prefix" ]; then
39
+ npm_prefix=$(convert_windows_path "$npm_prefix")
67
40
  fi
68
- done
69
-
70
- # If we find the script, execute it
71
- if [ -n "$CHECK_VERSION_SCRIPT" ]; then
72
- # Source the script to have access to the function
73
- source "$CHECK_VERSION_SCRIPT"
74
- check_version
75
- fi
76
41
 
77
- # Function to generate AI-friendly resolution prompt
78
- generate_resolution_prompt() {
79
- local RESOLUTION_FILE="./claude_resolution_prompt.md"
80
- local RESOLUTION_TEMPLATE=".claude/CLAUDE_RESOLUTION_PROMPT.md"
81
-
82
- if [ ! -f "$RESOLUTION_TEMPLATE" ]; then
83
- warning "Resolution template not found: $RESOLUTION_TEMPLATE"
84
- return
85
- fi
86
-
87
- # Get context information
88
- local REPO_NAME=$(basename $(git rev-parse --show-toplevel))
89
- local BRANCH_NAME=$(git rev-parse --abbrev-ref HEAD)
90
- local COMMIT_SHA="pending"
91
- local FILE_COUNT=$(echo "$JAVA_FILES" | wc -l)
92
-
93
- # Format the blocking issues for the resolution prompt
94
- local ISSUES_FORMATTED=""
95
- local issue_num=1
96
-
97
- # Use a temporary file to accumulate the issues
98
- local TEMP_ISSUES=$(mktemp)
99
-
100
- # Parse each blocking issue as JSON object
101
- echo "$JSON_RESPONSE" | jq -c '.blockingIssues[]?' 2>/dev/null | while IFS= read -r issue; do
102
- if [ -n "$issue" ]; then
103
- local desc=$(echo "$issue" | jq -r '.description')
104
- local file=$(echo "$issue" | jq -r '.file')
105
- local line=$(echo "$issue" | jq -r '.line')
106
- local method=$(echo "$issue" | jq -r '.method')
107
- local severity=$(echo "$issue" | jq -r '.severity')
108
-
109
- echo "### Issue #${issue_num} [${severity^^}]" >> "$TEMP_ISSUES"
110
- echo "**Description:** ${desc}" >> "$TEMP_ISSUES"
111
- echo "**Location:** ${file}:${line}" >> "$TEMP_ISSUES"
112
- echo "**Method/Class:** ${method}" >> "$TEMP_ISSUES"
113
- echo "" >> "$TEMP_ISSUES"
114
- issue_num=$((issue_num + 1))
115
- fi
116
- done
117
-
118
- # Read the accumulated content
119
- ISSUES_FORMATTED=$(cat "$TEMP_ISSUES")
120
- rm -f "$TEMP_ISSUES"
121
-
122
- # Generate the prompt from the template
123
- cp "$RESOLUTION_TEMPLATE" "$RESOLUTION_FILE"
124
-
125
- # Replace placeholders - use double quotes and escape special characters
126
- sed -i "s|{{REPO_NAME}}|${REPO_NAME}|g" "$RESOLUTION_FILE"
127
- sed -i "s|{{BRANCH_NAME}}|${BRANCH_NAME}|g" "$RESOLUTION_FILE"
128
- sed -i "s|{{COMMIT_SHA}}|${COMMIT_SHA}|g" "$RESOLUTION_FILE"
129
- sed -i "s|{{FILE_COUNT}}|${FILE_COUNT}|g" "$RESOLUTION_FILE"
130
-
131
- # Create temporary file for formatted issues
132
- local TEMP_ISSUES_FILE=$(mktemp)
133
- echo "$ISSUES_FORMATTED" > "$TEMP_ISSUES_FILE"
134
-
135
- # Replace {{BLOCKING_ISSUES}} with content
136
- if [ -n "$ISSUES_FORMATTED" ]; then
137
- sed -i "/{{BLOCKING_ISSUES}}/r $TEMP_ISSUES_FILE" "$RESOLUTION_FILE"
138
- fi
139
- sed -i "s|{{BLOCKING_ISSUES}}||g" "$RESOLUTION_FILE"
140
- rm -f "$TEMP_ISSUES_FILE"
141
-
142
- # Add content from affected files
143
- local TEMP_FILES=$(mktemp)
144
- echo "$JSON_RESPONSE" | jq -r '.blockingIssues[].file' 2>/dev/null | sort -u | while IFS= read -r file; do
145
- if [ -f "$file" ]; then
146
- echo "### File: $file" >> "$TEMP_FILES"
147
- echo "" >> "$TEMP_FILES"
148
- echo '```' >> "$TEMP_FILES"
149
- cat "$file" >> "$TEMP_FILES"
150
- echo '```' >> "$TEMP_FILES"
151
- echo "" >> "$TEMP_FILES"
42
+ local SCRIPT_PATHS=(
43
+ # Global npm installation (Linux/Mac)
44
+ "/usr/local/lib/node_modules/claude-git-hooks/lib/hooks/pre-commit.js"
45
+ "/usr/lib/node_modules/claude-git-hooks/lib/hooks/pre-commit.js"
46
+ # Global npm installation (Windows) - node_modules directly under prefix
47
+ "${npm_prefix}/node_modules/claude-git-hooks/lib/hooks/pre-commit.js"
48
+ # Global npm installation (Unix/WSL) - lib/node_modules under prefix
49
+ "${npm_prefix}/lib/node_modules/claude-git-hooks/lib/hooks/pre-commit.js"
50
+ # Home directory npm global
51
+ "$HOME/.npm-global/lib/node_modules/claude-git-hooks/lib/hooks/pre-commit.js"
52
+ # Local node_modules (if linked or installed locally)
53
+ "./node_modules/claude-git-hooks/lib/hooks/pre-commit.js"
54
+ "../node_modules/claude-git-hooks/lib/hooks/pre-commit.js"
55
+ )
56
+
57
+ for path in "${SCRIPT_PATHS[@]}"; do
58
+ if [ -f "$path" ]; then
59
+ echo "$path"
60
+ return 0
152
61
  fi
153
62
  done
154
-
155
- # Replace {{FILE_CONTENTS}} with content
156
- if [ -s "$TEMP_FILES" ]; then
157
- sed -i "/{{FILE_CONTENTS}}/r $TEMP_FILES" "$RESOLUTION_FILE"
158
- fi
159
- sed -i "s|{{FILE_CONTENTS}}||g" "$RESOLUTION_FILE"
160
- rm -f "$TEMP_FILES"
161
-
162
- echo
163
- echo -e "${YELLOW}=== AI RESOLUTION PROMPT GENERATED ===${NC}"
164
- echo -e "${GREEN}An AI-friendly prompt has been generated at: ${BLUE}$RESOLUTION_FILE${NC}"
165
- echo -e "${YELLOW}Copy this file to a new Claude instance to resolve problems automatically.${NC}"
166
- echo
167
- }
168
-
169
- # Configure files for SonarQube mode
170
- GUIDELINES_FILE=".claude/CLAUDE_PRE_COMMIT_SONAR.md"
171
- PROMPT_TEMPLATE=".claude/CLAUDE_ANALYSIS_PROMPT_SONAR.md"
172
63
 
173
-
174
- # Check that the prompt template exists
175
- if [ ! -f "$PROMPT_TEMPLATE" ]; then
176
- error "Prompt template not found: $PROMPT_TEMPLATE"
177
- error "Claude configuration files appear to be incomplete."
178
- error "Please reinstall claude-git-hooks by running:"
179
- error " claude-hooks install --force"
180
- exit 1
181
- fi
182
- # Function to clean temporary files
183
- cleanup() {
184
- rm -rf "$TEMP_DIR"
185
- }
186
-
187
- # Function to inject subagent instruction for parallel analysis
188
- inject_subagent_instruction() {
189
- if [ "$USE_SUBAGENTS" = "true" ]; then
190
- echo ""
191
- echo "IMPORTANT PARALLEL PROCESSING: If analyzing 3+ files, process them in batches of ${SUBAGENT_BATCH_SIZE}. For EACH batch, create that many subagents in parallel using Task tool (send single message with multiple Task calls). Each subagent analyzes one assigned file following OUTPUT_SCHEMA. After ALL batches complete, consolidate results into SINGLE JSON: (1) merge blockingIssues arrays, (2) merge details arrays, (3) sum issue counts, (4) worst-case metrics (lowest rating), (5) QUALITY_GATE=FAILED if ANY subagent found blockers/criticals, (6) approved=false if any disapproved. Model: ${SUBAGENT_MODEL}. Example: 4 files with BATCH_SIZE=1 → 4 sequential batches of 1 subagent each. Example: 4 files with BATCH_SIZE=3 → batch 1 has 3 parallel subagents (files 1-3), batch 2 has 1 subagent (file 4)."
192
- echo ""
193
- fi
194
- }
195
-
196
- # Configure cleanup on exit
197
- trap cleanup EXIT
198
-
199
- # Create temporary directory
200
- mkdir -p "$TEMP_DIR"
201
-
202
- # Check if Claude CLI is installed (only for code analysis)
203
- if ! command -v "$CLAUDE_CLI" &> /dev/null; then
204
- error "Claude CLI is not installed or not found in PATH"
205
- error "Install Claude CLI from: https://github.com/anthropics/claude-cli"
206
- exit 1
207
- fi
208
-
209
- # Now check if there are Java files to analyze
210
- JAVA_FILES=$(git diff --cached --name-only --diff-filter=ACM | grep -E '\.(java|xml|properties|yml|yaml)$' || true)
211
-
212
- if [ -z "$JAVA_FILES" ]; then
213
- log "No Java/configuration files to review"
214
- exit 0
215
- fi
216
-
217
- # Check if the guidelines file exists
218
- if [ ! -f "$GUIDELINES_FILE" ]; then
219
- error "Guidelines file not found: $GUIDELINES_FILE"
220
- error "Please reinstall claude-git-hooks by running:"
221
- error " claude-hooks install --force"
222
- exit 1
223
- fi
224
-
225
- log "Java/config files to review: $(echo "$JAVA_FILES" | wc -l)"
226
-
227
- # Function to filter content with SKIP-ANALYSIS
228
- filter_skip_analysis() {
229
- local file_content="$1"
230
- local filtered_content=""
231
- local skip_next_line=false
232
- local inside_skip_block=false
233
-
234
- while IFS= read -r line; do
235
- # Detect SKIP-ANALYSIS for single line
236
- if echo "$line" | grep -q "// SKIP-ANALYSIS"; then
237
- skip_next_line=true
238
- continue
239
- fi
240
-
241
- # Detect start/end of SKIP_ANALYSIS_BLOCK
242
- if echo "$line" | grep -q "// SKIP_ANALYSIS_BLOCK"; then
243
- if [ "$inside_skip_block" = true ]; then
244
- # End of block
245
- inside_skip_block=false
246
- else
247
- # Start of block
248
- inside_skip_block=true
249
- fi
250
- continue
251
- fi
252
-
253
- # If we're inside a block, skip the line
254
- if [ "$inside_skip_block" = true ]; then
255
- continue
256
- fi
257
-
258
- # If we should skip the next line (single comment)
259
- if [ "$skip_next_line" = true ]; then
260
- skip_next_line=false
261
- continue
262
- fi
263
-
264
- # Add line to filtered content
265
- filtered_content="${filtered_content}${line}"$'\n'
266
- done <<< "$file_content"
267
-
268
- echo "$filtered_content"
64
+ return 1
269
65
  }
270
66
 
271
- # Build the prompt for code analysis
272
- PROMPT_FILE="$TEMP_DIR/code_review_prompt.txt"
273
-
274
- # Copy the prompt template
275
- cat "$PROMPT_TEMPLATE" > "$PROMPT_FILE"
276
-
277
- # Add the guidelines
278
- echo "=== EVALUATION GUIDELINES ===" >> "$PROMPT_FILE"
279
- cat "$GUIDELINES_FILE" >> "$PROMPT_FILE"
280
-
281
- # Inject subagent instruction if enabled
282
- inject_subagent_instruction >> "$PROMPT_FILE"
283
-
284
- echo -e "\n\n=== CHANGES TO REVIEW ===\n" >> "$PROMPT_FILE"
285
-
286
- # Process each Java file
287
- FILE_COUNT=0
288
- for FILE in $JAVA_FILES; do
289
- if [ -f "$FILE" ]; then
290
- FILE_SIZE=$(stat -c%s "$FILE" 2>/dev/null || stat -f%z "$FILE" 2>/dev/null || echo "0")
291
-
292
- if [ "$FILE_SIZE" -gt "$MAX_FILE_SIZE" ]; then
293
- warning "File $FILE too large ($FILE_SIZE bytes), skipping..."
294
- continue
295
- fi
296
-
297
- echo -e "\n--- Archivo: $FILE ---" >> "$PROMPT_FILE"
298
-
299
- # Get the diff and filter it
300
- DIFF_CONTENT=$(git diff --cached "$FILE" 2>/dev/null || echo "Could not get diff")
301
- FILTERED_DIFF=$(filter_skip_analysis "$DIFF_CONTENT")
302
-
303
- # Show the filtered diff of the file
304
- echo -e "\nDiff:" >> "$PROMPT_FILE"
305
- echo "$FILTERED_DIFF" >> "$PROMPT_FILE"
306
-
307
- # If it's a new file, show complete filtered content
308
- if git diff --cached --name-status | grep "^A.*$FILE" > /dev/null; then
309
- echo -e "\nComplete content (new file):" >> "$PROMPT_FILE"
310
- FILE_CONTENT=$(git show ":$FILE" 2>/dev/null || cat "$FILE")
311
- FILTERED_CONTENT=$(filter_skip_analysis "$FILE_CONTENT")
312
- echo "$FILTERED_CONTENT" >> "$PROMPT_FILE"
313
- fi
314
-
315
- FILE_COUNT=$((FILE_COUNT + 1))
316
- fi
317
- done
318
-
319
- if [ "$FILE_COUNT" -eq 0 ]; then
320
- log "No valid files found to review"
321
- exit 0
322
- fi
323
-
324
- if [ "$FILE_COUNT" -gt 10 ]; then
325
- warning "Too many files to review ($FILE_COUNT)"
326
- warning "Consider splitting the commit into smaller parts"
327
- exit 0
328
- fi
329
-
330
- log "Sending $FILE_COUNT files for review with Claude..."
331
-
332
- # Send to Claude and capture response
333
- RESPONSE_FILE="$TEMP_DIR/code_review_response.txt"
334
- START_TIME=$(date +%s%3N 2>/dev/null || echo $(($(date +%s) * 1000)))
335
-
336
- # Execute Claude CLI and capture the response
337
- if $CLAUDE_CLI < "$PROMPT_FILE" > "$RESPONSE_FILE" 2>&1; then
338
- # Extract the JSON from the response
339
- JSON_RESPONSE=$(sed -n '/^{/,/^}/p' "$RESPONSE_FILE" | head -n 1000)
340
-
341
- if [ -z "$JSON_RESPONSE" ]; then
342
- error "Did not receive a valid JSON response from Claude"
343
- error "Complete response:"
344
- cat "$RESPONSE_FILE"
345
- exit 1
346
- fi
347
-
348
- # Save JSON for debug if activated
349
- if [ -n "$DEBUG" ]; then
350
- echo "$JSON_RESPONSE" > ./debug-claude-response.json
351
- log "Response saved to debug-claude-response.json"
352
- fi
353
-
354
- # Parse the response using jq
355
- APPROVED=$(echo "$JSON_RESPONSE" | jq -r '.approved // false')
356
- SCORE=$(echo "$JSON_RESPONSE" | jq -r '.score // 0')
357
- RECOMMENDATIONS=$(echo "$JSON_RESPONSE" | jq -r '.recommendations[]?' 2>/dev/null | sed '/^$/d')
358
-
359
- # Parse blockingIssues as objects and extract descriptions
360
- BLOCKING_ISSUES=""
361
- BLOCKING_COUNT=$(echo "$JSON_RESPONSE" | jq '.blockingIssues | length' 2>/dev/null || echo "0")
362
-
363
- if [ "$BLOCKING_COUNT" -gt 0 ]; then
364
- BLOCKING_ISSUES=$(echo "$JSON_RESPONSE" | jq -r '.blockingIssues[].description' 2>/dev/null | sed '/^$/d')
365
- fi
366
-
367
- # Always use SonarQube mode (as per v1.4.1)
368
- QUALITY_GATE=$(echo "$JSON_RESPONSE" | jq -r '.QUALITY_GATE // ""' 2>/dev/null)
369
-
370
- # Show SonarQube style results
371
- echo
372
- echo "╔════════════════════════════════════════════════════════════════════╗"
373
- echo "║ CODE QUALITY ANALYSIS ║"
374
- echo "╚════════════════════════════════════════════════════════════════════╝"
375
- echo
376
-
377
- # Quality Gate Status
378
- if [ "$QUALITY_GATE" = "PASSED" ]; then
379
- echo -e "${GREEN}✓ Quality Gate: PASSED${NC}"
380
- else
381
- echo -e "${RED}✗ Quality Gate: FAILED${NC}"
382
- fi
383
- echo
384
-
385
- # Metrics
386
- METRICS=$(echo "$JSON_RESPONSE" | jq -r '.metrics // {}' 2>/dev/null)
387
- if [ "$METRICS" != "{}" ] && [ "$METRICS" != "null" ]; then
388
- echo "📊 METRICS"
389
- echo "├─ Reliability: $(echo "$METRICS" | jq -r '.reliability // "?"' 2>/dev/null)"
390
- echo "├─ Security: $(echo "$METRICS" | jq -r '.security // "?"' 2>/dev/null)"
391
- echo "├─ Maintainability: $(echo "$METRICS" | jq -r '.maintainability // "?"' 2>/dev/null)"
392
- echo "├─ Coverage: $(echo "$METRICS" | jq -r '.coverage // "?"' 2>/dev/null)%"
393
- echo "├─ Duplications: $(echo "$METRICS" | jq -r '.duplications // "?"' 2>/dev/null)%"
394
- echo "└─ Complexity: $(echo "$METRICS" | jq -r '.complexity // "?"' 2>/dev/null)"
395
- echo
396
- fi
397
-
398
- # Issues Summary
399
- ISSUES=$(echo "$JSON_RESPONSE" | jq -r '.issues // {}' 2>/dev/null)
400
- if [ "$ISSUES" != "{}" ] && [ "$ISSUES" != "null" ]; then
401
- echo "📋 ISSUES SUMMARY"
402
- BLOCKER=$(echo "$ISSUES" | jq -r '.blocker // 0' 2>/dev/null)
403
- CRITICAL=$(echo "$ISSUES" | jq -r '.critical // 0' 2>/dev/null)
404
- MAJOR=$(echo "$ISSUES" | jq -r '.major // 0' 2>/dev/null)
405
- MINOR=$(echo "$ISSUES" | jq -r '.minor // 0' 2>/dev/null)
406
- INFO=$(echo "$ISSUES" | jq -r '.info // 0' 2>/dev/null)
407
- TOTAL=$((BLOCKER + CRITICAL + MAJOR + MINOR + INFO))
408
-
409
- echo "Total: $TOTAL issues found"
410
- [ "$BLOCKER" -gt 0 ] && echo -e " ${RED}🔴 Blocker: $BLOCKER${NC}"
411
- [ "$CRITICAL" -gt 0 ] && echo -e " ${RED}🟠 Critical: $CRITICAL${NC}"
412
- [ "$MAJOR" -gt 0 ] && echo -e " ${YELLOW}🟡 Major: $MAJOR${NC}"
413
- [ "$MINOR" -gt 0 ] && echo " 🔵 Minor: $MINOR"
414
- [ "$INFO" -gt 0 ] && echo " ⚪ Info: $INFO"
415
- echo
416
- fi
417
-
418
- # Detailed Issues
419
- DETAILS_COUNT=$(echo "$JSON_RESPONSE" | jq -r '.details | length' 2>/dev/null)
420
- if [ "$DETAILS_COUNT" -gt 0 ] 2>/dev/null; then
421
- echo "🔍 DETAILED ISSUES"
422
- echo "$JSON_RESPONSE" | jq -r '.details[]? |
423
- "[\(.severity)] \(.type) in \(.file):\(.line // "?")\n \(.message)\n"' 2>/dev/null
424
- fi
425
-
426
- # Security Hotspots
427
- HOTSPOTS=$(echo "$JSON_RESPONSE" | jq -r '.securityHotspots // 0' 2>/dev/null)
428
- if [ "$HOTSPOTS" -gt 0 ] 2>/dev/null; then
429
- echo "🔥 SECURITY HOTSPOTS: $HOTSPOTS found"
430
- echo " Review security-sensitive code carefully"
431
- echo
432
- fi
433
-
434
- # Check if commit should be blocked
435
- if [ "$QUALITY_GATE" = "FAILED" ] || [ "$APPROVED" = "false" ]; then
436
- echo
437
- show_elapsed_time "$START_TIME"
438
- error "❌ Commit blocked due to quality gate failure"
439
-
440
- # Show blocking issues if they exist
441
- if [ -n "$BLOCKING_ISSUES" ] && [ "$BLOCKING_ISSUES" != "null" ]; then
442
- echo
443
- echo "=== CRITICAL ISSUES ==="
444
- echo "$BLOCKING_ISSUES" | sed 's/^/- /'
445
- fi
446
-
447
- # Generate AI-friendly resolution prompt if there are blocking issues
448
- if [ "$BLOCKING_COUNT" -gt 0 ]; then
449
- generate_resolution_prompt
450
- fi
451
-
452
- exit 1
453
- fi
454
-
455
- echo
456
- show_elapsed_time "$START_TIME"
457
- log "✅ Code analysis completed. Quality gate passed."
67
+ # Find the Node.js script
68
+ NODE_HOOK=$(find_hook_script)
458
69
 
459
- else
460
- error "Error executing Claude CLI"
461
- error "Check that Claude CLI is configured correctly"
462
- cat "$RESPONSE_FILE"
70
+ if [ -z "$NODE_HOOK" ]; then
71
+ echo -e "${RED}Error: Could not find pre-commit.js${NC}" >&2
72
+ echo "Claude Git Hooks may not be properly installed." >&2
73
+ echo "Try running: claude-hooks install --force" >&2
463
74
  exit 1
464
75
  fi
465
76
 
466
- exit 0
77
+ # Execute the Node.js script
78
+ # Why: Pass all arguments ($@) to support future hook extensions
79
+ exec node "$NODE_HOOK" "$@"