claude-git-hooks 1.5.2 → 1.5.4

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 CHANGED
@@ -5,6 +5,40 @@ Todos los cambios notables en este proyecto se documentarán en este archivo.
5
5
  El formato está basado en [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  y este proyecto adhiere a [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [1.5.4] - 2024-12-22
9
+
10
+ ### Fixed
11
+
12
+ - 🔧 **Generación de prompt de resolución en modo SonarQube**
13
+ - Ahora se genera correctamente `claude_resolution_prompt.md` cuando el Quality Gate falla
14
+ - El archivo se genera cuando hay `blockingIssues`, independiente del tipo de fallo
15
+ - Corregido el flujo para que siempre muestre los issues críticos antes de generar el prompt
16
+
17
+ - 🎯 **Unificación de lógica de análisis**
18
+ - Eliminada lógica duplicada entre modo "clásico" y SonarQube
19
+ - Ahora siempre se usa modo SonarQube (como se especificó en v1.4.1)
20
+ - Simplificación del flujo de decisión en el pre-commit hook
21
+
22
+ ### Changed
23
+
24
+ - 📝 Refactorización del hook pre-commit para mayor claridad y mantenibilidad
25
+ - 🔄 La función `generate_resolution_prompt` ahora se llama consistentemente cuando hay issues bloqueantes
26
+
27
+ ## [1.5.3] - 2025-09-12
28
+
29
+ ### Fixed
30
+
31
+ - 🔧 Comando `analyze-diff` ahora siempre compara con ramas origin
32
+ - Si se especifica rama: compara con `origin/rama-especificada`
33
+ - Si no se especifica: compara con `origin/rama-actual`
34
+ - Fallback automático a `origin/develop` y luego `origin/main` si no existe la rama origin
35
+
36
+ ### Changed
37
+
38
+ - 📝 Documentación mejorada con intro atractiva y características principales
39
+ - ⚠️ Aclaración importante sobre uso exclusivo en WSL/Ubuntu (no PowerShell/CMD)
40
+ - 🔗 Enlaces directos a configuración previa en sección de instalación
41
+
8
42
  ## [1.5.2] - 2025-09-11
9
43
 
10
44
  ### Changed
package/LICENSE CHANGED
File without changes
package/README.md CHANGED
@@ -1,10 +1,15 @@
1
1
  # Pre-commit Hook con Claude CLI
2
2
 
3
- Este directorio contiene un pre-commit hook que utiliza Claude CLI para revisar automáticamente el código antes de cada commit.
4
- Otras funciones de interés:
3
+ 🚀 **Transforma tu flujo de desarrollo con IA**: análisis de código instantáneo, mensajes de commit automáticos y generación de PRs perfectas. Claude revisa tu código antes de cada commit, detecta issues críticos al estilo SonarQube, y genera toda la documentación que necesitas. ¿Lo mejor? Se ejecuta localmente sin contaminar tu CI/CD.
5
4
 
6
- - Generación de mensaje de commit.
7
- - Generación de información de PR y tests de validación de PR - Utiliza análisis de diferencias entre contenido local y origin.
5
+ ## 🎯 Características principales
6
+
7
+ - 🔍 **Análisis de código pre-commit**: Detecta issues críticos antes de que lleguen al repo
8
+ - 💬 **Mensajes de commit automáticos**: Escribe "auto" y Claude genera el mensaje perfecto
9
+ - 📋 **Generación de PRs**: Título, descripción y tests sugeridos con un solo comando
10
+ - 🎨 **Modo SonarQube**: Métricas de calidad y Quality Gate integrados
11
+ - 🚫 **Skip inteligente**: Excluye código legacy con comentarios SKIP-ANALYSIS
12
+ - 🔄 **Auto-actualización**: Se mantiene actualizado automáticamente
8
13
 
9
14
  ## 📋 CHEATSHEET
10
15
 
@@ -14,10 +19,10 @@ Otras funciones de interés:
14
19
  # Commit rápido sin análisis + mensaje automático
15
20
  git commit --no-verify -m "auto"
16
21
 
17
- # Analizar diferencias con rama origin actual
22
+ # Analizar diferencias con origin de la rama actual
18
23
  claude-hooks analyze-diff
19
24
 
20
- # Analizar diferencias para PR (comparar con develop LOCAL)
25
+ # Analizar diferencias para PR (comparar con origin/develop)
21
26
  claude-hooks analyze-diff develop
22
27
 
23
28
  # Reinstalar durante desarrollo (después de npm link)
@@ -26,8 +31,16 @@ claude-hooks install --force --skip-auth
26
31
 
27
32
  ### 📦 Instalación y Gestión
28
33
 
34
+ ⚠️ **IMPORTANTE**: Todo debe ejecutarse desde consola WSL/Ubuntu (no PowerShell ni CMD). Ver [Configuración Previa](#-configuración-previa-importante) antes de comenzar.
35
+
36
+ Se debe instalar el paquete globalmente, para luego gestionarlo localmente en cada repositorio.
37
+
29
38
  ```bash
30
- # Instalación inicial
39
+ # En consola WSL/Ubuntu - Instalar globalmente
40
+ npm install -g claude-git-hooks
41
+
42
+ # Luego en cada proyecto (también desde WSL/Ubuntu)
43
+ cd /tu/proyecto
31
44
  claude-hooks install
32
45
 
33
46
  # Actualizar a última versión
@@ -123,28 +136,7 @@ git commit -m "fix: resolver issues"
123
136
 
124
137
  ## 🔧 Configuración Previa Importante
125
138
 
126
- ### 1. Armonización de Line Endings (EOL) -- Automatizada
127
-
128
- Debido a que Claude CLI corre en WSL y el desarrollo puede hacerse en Windows, es crucial configurar correctamente los line endings para evitar que los archivos de hooks se corrompan:
129
-
130
- ```bash
131
- # IMPORTANTE: Usar la misma configuración en WSL y Windows
132
- # Recomendación: usar 'input' en ambos entornos
133
-
134
- # En WSL
135
- git config core.autocrlf input
136
-
137
- # En PowerShell/Windows
138
- git config core.autocrlf input
139
-
140
- # Verificar configuración actual
141
- git config --local core.autocrlf
142
- git config --global core.autocrlf
143
- ```
144
-
145
- **⚠️ ADVERTENCIA**: Si tienes `core.autocrlf = true` en local e `input` en global, esto puede causar que los archivos de los hooks se vacíen. Asegúrate de que ambas configuraciones sean consistentes.
146
-
147
- ### 2. Credenciales Git en WSL
139
+ ### Credenciales Git en WSL
148
140
 
149
141
  Debes configurar tus credenciales de git nuevamente en WSL:
150
142
 
@@ -264,7 +256,7 @@ Si no existe un `.gitignore`, se creará uno nuevo. Si ya existe, las entradas s
264
256
 
265
257
  ### Características adicionales
266
258
 
267
- - **Generación de información para Pull Requests**: `claude-hooks analyze-diffs {branch}` para comparar rama local con otra rama, propone nombre para rama actual, título y detalles para pull request, da tips para verificar trabajo. Este comando genera automáticamente:
259
+ - **Generación de información para Pull Requests**: `claude-hooks analyze-diff [branch]` para comparar rama local con rama origin, propone nombre para rama actual, título y detalles para pull request, da tips para verificar trabajo. Si se especifica branch, compara con origin/branch. Si no, compara con origin de la rama actual. Este comando genera automáticamente:
268
260
  - 📝 Título de PR conciso (máx. 72 caracteres)
269
261
  - 📄 Descripción detallada con markdown
270
262
  - 🌿 Nombre de rama sugerido (formato: tipo/descripcion)
package/bin/claude-hooks CHANGED
@@ -808,15 +808,19 @@ function analyzeDiff(args) {
808
808
  return;
809
809
  }
810
810
 
811
+ // Update remote references
812
+ execSync('git fetch', { stdio: 'ignore' });
813
+
811
814
  let baseBranch, compareWith, contextDescription;
812
815
 
813
816
  if (args[0]) {
814
- // Case with argument: compare current branch vs specified branch
815
- baseBranch = args[0];
817
+ // Case with argument: compare current branch vs origin/specified-branch
818
+ const targetBranch = args[0];
819
+ baseBranch = `origin/${targetBranch}`;
816
820
  compareWith = `${baseBranch}...HEAD`;
817
821
  contextDescription = `${currentBranch} vs ${baseBranch}`;
818
822
 
819
- // Check that the base branch exists
823
+ // Check that the origin branch exists
820
824
  try {
821
825
  execSync(`git rev-parse --verify ${baseBranch}`, { stdio: 'ignore' });
822
826
  } catch (e) {
@@ -824,38 +828,34 @@ function analyzeDiff(args) {
824
828
  return;
825
829
  }
826
830
  } else {
827
- // Case without argument: compare local vs remote
831
+ // Case without argument: compare current branch vs origin/current-branch
832
+ baseBranch = `origin/${currentBranch}`;
833
+ compareWith = `${baseBranch}...HEAD`;
834
+ contextDescription = `${currentBranch} vs ${baseBranch}`;
835
+
836
+ // Check that the origin branch exists
828
837
  try {
829
- // Get the tracked remote branch
830
- const remoteBranch = execSync(`git rev-parse --abbrev-ref ${currentBranch}@{upstream}`, { encoding: 'utf8' }).trim();
831
-
832
- // Update remote references
833
- execSync('git fetch', { stdio: 'ignore' });
834
-
835
- baseBranch = remoteBranch;
836
- compareWith = `HEAD..${remoteBranch}`;
837
- contextDescription = `local changes without push in ${currentBranch}`;
838
-
839
- info(`Comparing local changes vs remote: ${remoteBranch}`);
838
+ execSync(`git rev-parse --verify ${baseBranch}`, { stdio: 'ignore' });
840
839
  } catch (e) {
841
- // If there's no remote branch, use develop as fallback
842
- baseBranch = 'develop';
840
+ // Try fallback to origin/develop
841
+ baseBranch = 'origin/develop';
843
842
  compareWith = `${baseBranch}...HEAD`;
844
- contextDescription = `${currentBranch} vs ${baseBranch} (sin rama remota)`;
843
+ contextDescription = `${currentBranch} vs ${baseBranch} (fallback)`;
845
844
 
846
845
  try {
847
846
  execSync(`git rev-parse --verify ${baseBranch}`, { stdio: 'ignore' });
848
- warning(`No remote branch configured. Comparing with ${baseBranch}.`);
847
+ warning(`Branch origin/${currentBranch} does not exist. Using ${baseBranch} as fallback.`);
849
848
  } catch (e2) {
850
- baseBranch = 'main';
849
+ // Try fallback to origin/main
850
+ baseBranch = 'origin/main';
851
851
  compareWith = `${baseBranch}...HEAD`;
852
852
  contextDescription = `${currentBranch} vs ${baseBranch} (fallback)`;
853
853
 
854
854
  try {
855
855
  execSync(`git rev-parse --verify ${baseBranch}`, { stdio: 'ignore' });
856
- warning(`No develop or remote branch. Comparing with main.`);
856
+ warning(`No origin/develop branch. Using ${baseBranch} as fallback.`);
857
857
  } catch (e3) {
858
- error('Could not find a valid comparison branch.');
858
+ error('Could not find a valid comparison branch (tried origin/current, origin/develop, origin/main).');
859
859
  return;
860
860
  }
861
861
  }
@@ -867,12 +867,7 @@ function analyzeDiff(args) {
867
867
  // Get modified files
868
868
  let diffFiles;
869
869
  try {
870
- // For local changes without push, invert the comparison
871
- if (!args[0] && compareWith.includes('HEAD..')) {
872
- diffFiles = execSync(`git diff ${compareWith} --name-only`, { encoding: 'utf8' }).trim();
873
- } else {
874
- diffFiles = execSync(`git diff ${compareWith} --name-only`, { encoding: 'utf8' }).trim();
875
- }
870
+ diffFiles = execSync(`git diff ${compareWith} --name-only`, { encoding: 'utf8' }).trim();
876
871
 
877
872
  if (!diffFiles) {
878
873
  // Check if there are staged or unstaged changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-git-hooks",
3
- "version": "1.5.2",
3
+ "version": "1.5.4",
4
4
  "description": "Git hooks con Claude CLI para análisis de código y generación automática de mensajes de commit",
5
5
  "main": "lib/index.js",
6
6
  "bin": {
@@ -28,7 +28,6 @@
28
28
  "engines": {
29
29
  "node": ">=14.0.0"
30
30
  },
31
- "dependencies": {},
32
31
  "preferGlobal": true,
33
32
  "files": [
34
33
  "bin/",
File without changes
File without changes
@@ -5,7 +5,6 @@ repo:{{REPO_NAME}}
5
5
  branch:{{BRANCH_NAME}}
6
6
  commit:{{COMMIT_SHA}}
7
7
  files:{{FILE_COUNT}}
8
- mode:{{ANALYSIS_MODE}}
9
8
 
10
9
  CRITICAL_ISSUES:
11
10
  {{BLOCKING_ISSUES}}
File without changes
@@ -332,125 +332,95 @@ if $CLAUDE_CLI < "$PROMPT_FILE" > "$RESPONSE_FILE" 2>&1; then
332
332
  BLOCKING_ISSUES=$(echo "$JSON_RESPONSE" | jq -r '.blockingIssues[].description' 2>/dev/null | sed '/^$/d')
333
333
  fi
334
334
 
335
+ # Always use SonarQube mode (as per v1.4.1)
335
336
  QUALITY_GATE=$(echo "$JSON_RESPONSE" | jq -r '.QUALITY_GATE // ""' 2>/dev/null)
336
-
337
- if [ -n "$QUALITY_GATE" ] && [ "$QUALITY_GATE" != "null" ]; then
338
- # Show SonarQube style results
339
- echo
340
- echo "╔════════════════════════════════════════════════════════════════════╗"
341
- echo "║ CODE QUALITY ANALYSIS ║"
342
- echo "╚════════════════════════════════════════════════════════════════════╝"
337
+
338
+ # Show SonarQube style results
339
+ echo
340
+ echo "╔════════════════════════════════════════════════════════════════════╗"
341
+ echo "║ CODE QUALITY ANALYSIS ║"
342
+ echo "╚════════════════════════════════════════════════════════════════════╝"
343
+ echo
344
+
345
+ # Quality Gate Status
346
+ if [ "$QUALITY_GATE" = "PASSED" ]; then
347
+ echo -e "${GREEN}✓ Quality Gate: PASSED${NC}"
348
+ else
349
+ echo -e "${RED}✗ Quality Gate: FAILED${NC}"
350
+ fi
351
+ echo
352
+
353
+ # Metrics
354
+ METRICS=$(echo "$JSON_RESPONSE" | jq -r '.metrics // {}' 2>/dev/null)
355
+ if [ "$METRICS" != "{}" ] && [ "$METRICS" != "null" ]; then
356
+ echo "📊 METRICS"
357
+ echo "├─ Reliability: $(echo "$METRICS" | jq -r '.reliability // "?"' 2>/dev/null)"
358
+ echo "├─ Security: $(echo "$METRICS" | jq -r '.security // "?"' 2>/dev/null)"
359
+ echo "├─ Maintainability: $(echo "$METRICS" | jq -r '.maintainability // "?"' 2>/dev/null)"
360
+ echo "├─ Coverage: $(echo "$METRICS" | jq -r '.coverage // "?"' 2>/dev/null)%"
361
+ echo "├─ Duplications: $(echo "$METRICS" | jq -r '.duplications // "?"' 2>/dev/null)%"
362
+ echo "└─ Complexity: $(echo "$METRICS" | jq -r '.complexity // "?"' 2>/dev/null)"
343
363
  echo
344
-
345
- # Quality Gate Status
346
- if [ "$QUALITY_GATE" = "PASSED" ]; then
347
- echo -e "${GREEN}✓ Quality Gate: PASSED${NC}"
348
- else
349
- echo -e "${RED}✗ Quality Gate: FAILED${NC}"
350
- fi
364
+ fi
365
+
366
+ # Issues Summary
367
+ ISSUES=$(echo "$JSON_RESPONSE" | jq -r '.issues // {}' 2>/dev/null)
368
+ if [ "$ISSUES" != "{}" ] && [ "$ISSUES" != "null" ]; then
369
+ echo "📋 ISSUES SUMMARY"
370
+ BLOCKER=$(echo "$ISSUES" | jq -r '.blocker // 0' 2>/dev/null)
371
+ CRITICAL=$(echo "$ISSUES" | jq -r '.critical // 0' 2>/dev/null)
372
+ MAJOR=$(echo "$ISSUES" | jq -r '.major // 0' 2>/dev/null)
373
+ MINOR=$(echo "$ISSUES" | jq -r '.minor // 0' 2>/dev/null)
374
+ INFO=$(echo "$ISSUES" | jq -r '.info // 0' 2>/dev/null)
375
+ TOTAL=$((BLOCKER + CRITICAL + MAJOR + MINOR + INFO))
376
+
377
+ echo "Total: $TOTAL issues found"
378
+ [ "$BLOCKER" -gt 0 ] && echo -e " ${RED}🔴 Blocker: $BLOCKER${NC}"
379
+ [ "$CRITICAL" -gt 0 ] && echo -e " ${RED}🟠 Critical: $CRITICAL${NC}"
380
+ [ "$MAJOR" -gt 0 ] && echo -e " ${YELLOW}🟡 Major: $MAJOR${NC}"
381
+ [ "$MINOR" -gt 0 ] && echo " 🔵 Minor: $MINOR"
382
+ [ "$INFO" -gt 0 ] && echo " ⚪ Info: $INFO"
351
383
  echo
352
-
353
- # Metrics
354
- METRICS=$(echo "$JSON_RESPONSE" | jq -r '.metrics // {}' 2>/dev/null)
355
- if [ "$METRICS" != "{}" ] && [ "$METRICS" != "null" ]; then
356
- echo "📊 METRICS"
357
- echo "├─ Reliability: $(echo "$METRICS" | jq -r '.reliability // "?"' 2>/dev/null)"
358
- echo "├─ Security: $(echo "$METRICS" | jq -r '.security // "?"' 2>/dev/null)"
359
- echo "├─ Maintainability: $(echo "$METRICS" | jq -r '.maintainability // "?"' 2>/dev/null)"
360
- echo "├─ Coverage: $(echo "$METRICS" | jq -r '.coverage // "?"' 2>/dev/null)%"
361
- echo "├─ Duplications: $(echo "$METRICS" | jq -r '.duplications // "?"' 2>/dev/null)%"
362
- echo "└─ Complexity: $(echo "$METRICS" | jq -r '.complexity // "?"' 2>/dev/null)"
363
- echo
364
- fi
365
-
366
- # Issues Summary
367
- ISSUES=$(echo "$JSON_RESPONSE" | jq -r '.issues // {}' 2>/dev/null)
368
- if [ "$ISSUES" != "{}" ] && [ "$ISSUES" != "null" ]; then
369
- echo "📋 ISSUES SUMMARY"
370
- BLOCKER=$(echo "$ISSUES" | jq -r '.blocker // 0' 2>/dev/null)
371
- CRITICAL=$(echo "$ISSUES" | jq -r '.critical // 0' 2>/dev/null)
372
- MAJOR=$(echo "$ISSUES" | jq -r '.major // 0' 2>/dev/null)
373
- MINOR=$(echo "$ISSUES" | jq -r '.minor // 0' 2>/dev/null)
374
- INFO=$(echo "$ISSUES" | jq -r '.info // 0' 2>/dev/null)
375
- TOTAL=$((BLOCKER + CRITICAL + MAJOR + MINOR + INFO))
376
-
377
- echo "Total: $TOTAL issues found"
378
- [ "$BLOCKER" -gt 0 ] && echo -e " ${RED}🔴 Blocker: $BLOCKER${NC}"
379
- [ "$CRITICAL" -gt 0 ] && echo -e " ${RED}🟠 Critical: $CRITICAL${NC}"
380
- [ "$MAJOR" -gt 0 ] && echo -e " ${YELLOW}🟡 Major: $MAJOR${NC}"
381
- [ "$MINOR" -gt 0 ] && echo " 🔵 Minor: $MINOR"
382
- [ "$INFO" -gt 0 ] && echo " ⚪ Info: $INFO"
383
- echo
384
- fi
385
-
386
- # Detailed Issues
387
- DETAILS_COUNT=$(echo "$JSON_RESPONSE" | jq -r '.details | length' 2>/dev/null)
388
- if [ "$DETAILS_COUNT" -gt 0 ] 2>/dev/null; then
389
- echo "🔍 DETAILED ISSUES"
390
- echo "$JSON_RESPONSE" | jq -r '.details[]? |
391
- "[\(.severity)] \(.type) in \(.file):\(.line // "?")\n \(.message)\n"' 2>/dev/null
392
- fi
393
-
394
- # Security Hotspots
395
- HOTSPOTS=$(echo "$JSON_RESPONSE" | jq -r '.securityHotspots // 0' 2>/dev/null)
396
- if [ "$HOTSPOTS" -gt 0 ] 2>/dev/null; then
397
- echo "🔥 SECURITY HOTSPOTS: $HOTSPOTS found"
398
- echo " Review security-sensitive code carefully"
384
+ fi
399
385
 
400
- echo
401
- fi
402
-
403
- # Check if commit should be blocked
404
- if [ "$QUALITY_GATE" = "FAILED" ] || [ "$APPROVED" = "false" ]; then
405
- echo
406
- error "❌ Commit blocked due to quality gate failure"
407
- exit 1
408
- fi
409
-
386
+ # Detailed Issues
387
+ DETAILS_COUNT=$(echo "$JSON_RESPONSE" | jq -r '.details | length' 2>/dev/null)
388
+ if [ "$DETAILS_COUNT" -gt 0 ] 2>/dev/null; then
389
+ echo "🔍 DETAILED ISSUES"
390
+ echo "$JSON_RESPONSE" | jq -r '.details[]? |
391
+ "[\(.severity)] \(.type) in \(.file):\(.line // "?")\n \(.message)\n"' 2>/dev/null
392
+ fi
393
+
394
+ # Security Hotspots
395
+ HOTSPOTS=$(echo "$JSON_RESPONSE" | jq -r '.securityHotspots // 0' 2>/dev/null)
396
+ if [ "$HOTSPOTS" -gt 0 ] 2>/dev/null; then
397
+ echo "🔥 SECURITY HOTSPOTS: $HOTSPOTS found"
398
+ echo " Review security-sensitive code carefully"
410
399
  echo
411
- log "✅ Code analysis completed. Quality gate passed."
412
- else
413
- # Show results in classic format
400
+ fi
401
+
402
+ # Check if commit should be blocked
403
+ if [ "$QUALITY_GATE" = "FAILED" ] || [ "$APPROVED" = "false" ]; then
414
404
  echo
415
- echo "=== REVIEW RESULTS ==="
416
- echo "Score: $SCORE/10"
417
-
418
- if [ -n "$RECOMMENDATIONS" ] && [ "$RECOMMENDATIONS" != "null" ]; then
405
+ error " Commit blocked due to quality gate failure"
406
+
407
+ # Show blocking issues if they exist
408
+ if [ -n "$BLOCKING_ISSUES" ] && [ "$BLOCKING_ISSUES" != "null" ]; then
419
409
  echo
420
- echo "=== RECOMMENDATIONS ==="
421
- echo "$RECOMMENDATIONS" | sed 's/^/- /'
422
- fi
423
-
424
- # Check if the commit should be blocked
425
- if [ "$APPROVED" = "false" ]; then
426
- error "❌ Commit rejected due to critical issues"
427
- if [ -n "$BLOCKING_ISSUES" ] && [ "$BLOCKING_ISSUES" != "null" ]; then
428
- echo
429
- echo "=== CRITICAL ISSUES ==="
430
- echo "$BLOCKING_ISSUES" | sed 's/^/- /'
431
-
432
- # Generate AI-friendly resolution prompt
433
- generate_resolution_prompt
434
- fi
435
- exit 1
410
+ echo "=== CRITICAL ISSUES ==="
411
+ echo "$BLOCKING_ISSUES" | sed 's/^/- /'
436
412
  fi
437
413
 
438
- # Show additional details if they exist
439
- DETAILS=$(echo "$JSON_RESPONSE" | jq -r '.details // null')
440
- if [ -n "$DETAILS" ] && [ "$DETAILS" != "null" ]; then
441
- echo
442
- echo "=== ADDITIONAL DETAILS ==="
443
- # If details is a string, print it directly
444
- if echo "$DETAILS" | jq -e 'type == "string"' >/dev/null 2>&1; then
445
- echo "$DETAILS" | jq -r '.'
446
- # If it's an object or array, format it
447
- else
448
- echo "$DETAILS" | jq '.'
449
- fi
414
+ # Generate AI-friendly resolution prompt if there are blocking issues
415
+ if [ "$BLOCKING_COUNT" -gt 0 ]; then
416
+ generate_resolution_prompt
450
417
  fi
451
-
452
- log "✅ Review completed. Commit approved (Score: $SCORE/10)"
418
+
419
+ exit 1
453
420
  fi
421
+
422
+ echo
423
+ log "✅ Code analysis completed. Quality gate passed."
454
424
 
455
425
  else
456
426
  error "Error executing Claude CLI"
File without changes