claude-git-hooks 1.2.1 → 1.3.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.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,65 @@ 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.3.0] - 2025-08-28
9
+
10
+ ### Added
11
+ - 🚀 Nuevo comando `claude-hooks update` para actualizar a la última versión disponible
12
+ - 🔄 Verificación automática de versión antes de cada commit con prompt interactivo
13
+ - 📜 Script compartido `check-version.sh` para evitar duplicación de código
14
+ - ✨ Prompt interactivo compatible con todas las consolas para aceptar actualizaciones
15
+ - 📊 Mensajes informativos detallados durante el proceso de verificación y actualización
16
+
17
+ ### Changed
18
+ - 🎯 El hook `pre-commit` ahora verifica automáticamente si hay actualizaciones disponibles
19
+ - 📦 La instalación ahora incluye el script `check-version.sh` en `.git/hooks/`
20
+ - 🔧 Mejorada la UX con mensajes claros sobre el estado de la versión
21
+
22
+ ### Fixed
23
+ - 🐛 Corregido el parsing de versión desde NPM API (usaba endpoint incorrecto)
24
+ - 🔧 Mejorada la compatibilidad del prompt interactivo para consolas IntelliJ/WSL
25
+ - 📝 Múltiples métodos de fallback para leer input del usuario
26
+
27
+ ### Technical
28
+ - 🏗️ Función `getLatestVersion()` ahora usa `dist-tags.latest` de NPM API
29
+ - 🔄 Función `checkVersionAndPromptUpdate()` reutilizable para verificación
30
+ - 📁 Script compartido para evitar duplicación de lógica de verificación
31
+ - 🎯 Fallback a `npm view` si la API de NPM falla
32
+
33
+ ## [1.2.4] - 2025-08-22
34
+
35
+ ### Fixed
36
+ - 🐛 Corregido el análisis de SonarQube para mostrar correctamente el formato detallado
37
+ - 📊 Arreglado el parsing de JSON para buscar `QUALITY_GATE` en mayúsculas según las pautas
38
+ - 🔧 Actualizado el mapeo de campos JSON para coincidir con la estructura esperada
39
+
40
+ ### Changed
41
+ - 📈 Añadidas métricas de coverage, duplications y complexity al formato SonarQube
42
+ - 📝 Actualizado el archivo de pautas SonarQube para incluir todas las métricas
43
+
44
+ ## [1.2.3] - 2025-08-22
45
+
46
+ ### Added
47
+ - 🚀 Actualización automática de `.gitignore` durante la instalación
48
+ - 📝 Claude Hooks ahora agrega automáticamente las entradas necesarias a `.gitignore`
49
+ - 🔍 El comando `status` ahora muestra el estado de las entradas en `.gitignore`
50
+
51
+ ### Changed
52
+ - 🎯 Mejorado el proceso de instalación para ser más completo y automatizado
53
+ - 📊 El comando `status` ahora proporciona información más detallada sobre la configuración
54
+
55
+ ## [1.2.2] - 2025-08-22
56
+
57
+ ### Fixed
58
+ - 🐛 Corregido problema donde los archivos markdown de pautas no se instalaban correctamente desde npm
59
+ - 📁 Los archivos de pautas ahora se instalan en el directorio `.claude/` en lugar de la raíz del proyecto
60
+ - 🔧 Actualizado el hook pre-commit para buscar los archivos de pautas en la nueva ubicación `.claude/`
61
+
62
+ ### Changed
63
+ - 📂 Los archivos `CLAUDE_PRE_COMMIT.md` y `CLAUDE_PRE_COMMIT_SONAR.md` ahora se almacenan en `.claude/`
64
+ - 🎯 Mejorada la organización del proyecto manteniendo los archivos de configuración separados del código fuente
65
+ - 📝 Actualizada la documentación para reflejar la nueva estructura de directorios
66
+
8
67
  ## [1.2.1] - 2024-07-24
9
68
 
10
69
  ### Fixed
package/LICENSE CHANGED
File without changes
package/README.md CHANGED
@@ -8,8 +8,8 @@ Este directorio contiene un pre-commit hook que utiliza Claude CLI para revisar
8
8
  - **`pre-commit`** - Hook principal para análisis de código (con auto-actualización)
9
9
  - **`prepare-commit-msg`** - Hook para generar mensajes de commit automáticos
10
10
  - **`.gitattributes`** - Configuración para mantener line endings correctos
11
- - **`CLAUDE_PRE_COMMIT.md`** - Pautas de evaluación formato estándar
12
- - **`CLAUDE_PRE_COMMIT_SONAR.md`** - Pautas de evaluación formato SonarQube
11
+ - **`.claude/CLAUDE_PRE_COMMIT.md`** - Pautas de evaluación formato estándar
12
+ - **`.claude/CLAUDE_PRE_COMMIT_SONAR.md`** - Pautas de evaluación formato SonarQube
13
13
 
14
14
  ## 🔧 Configuración Previa Importante
15
15
 
@@ -69,6 +69,7 @@ El comando `claude-hooks install` ahora incluye:
69
69
  - ✅ Configuración de Git (line endings WSL/Windows)
70
70
  - ✅ Verificación de autenticación Claude con entretenimiento
71
71
  - ✅ Instalación de hooks y archivos de pautas
72
+ - ✅ Actualización automática de .gitignore con archivos de Claude
72
73
 
73
74
  #### Añadir como Dependencia de Desarrollo
74
75
 
@@ -92,13 +93,44 @@ Luego añade esto a tu `package.json`:
92
93
 
93
94
  ## 🤖 Características
94
95
 
95
- **✨ Auto-actualización incorporada**: Los hooks se actualizan automáticamente en cada commit.
96
+ **✨ Auto-actualización**: Verifica automáticamente si hay nuevas versiones disponibles antes de cada commit y ofrece actualizar con un simple prompt interactivo.
96
97
 
97
98
  **Hooks disponibles**:
98
99
 
99
- - `pre-commit`: Análisis de código con Claude (solo archivos Java/config)
100
+ - `pre-commit`: Análisis de código con Claude (solo archivos Java/config) con verificación automática de versión
100
101
  - `prepare-commit-msg`: Generación automática de mensajes de commit
101
102
 
103
+ ## 📁 Gestión de Archivos
104
+
105
+ ### Archivos creados durante la instalación
106
+
107
+ El comando `claude-hooks install` crea los siguientes archivos y directorios:
108
+
109
+ 1. **`.git/hooks/pre-commit`** - Hook de análisis de código
110
+ 2. **`.git/hooks/prepare-commit-msg`** - Hook de generación de mensajes
111
+ 3. **`.claude/`** - Directorio para archivos de configuración
112
+ - `CLAUDE_PRE_COMMIT.md` - Pautas de evaluación estándar
113
+ - `CLAUDE_PRE_COMMIT_SONAR.md` - Pautas de evaluación SonarQube
114
+ 4. **`.claude-analysis-mode`** - Archivo de preferencia de modo (creado al primer uso)
115
+
116
+ ### Actualización automática de .gitignore
117
+
118
+ Durante la instalación, Claude Hooks actualiza automáticamente tu `.gitignore` para incluir:
119
+
120
+ ```gitignore
121
+ # Claude Git Hooks
122
+ .claude/
123
+ .claude-analysis-mode
124
+ debug-claude-response.json
125
+ ```
126
+
127
+ Esto asegura que:
128
+ - Los archivos de configuración de Claude específicos del proyecto no se suban al repositorio
129
+ - Los archivos de debug temporales se ignoren
130
+ - Cada desarrollador pueda tener sus propias preferencias de análisis
131
+
132
+ Si no existe un `.gitignore`, se creará uno nuevo. Si ya existe, las entradas se agregarán al final solo si no están presentes.
133
+
102
134
  ## 🎯 Funcionamiento
103
135
 
104
136
  ### Hook pre-commit (Análisis de código)
@@ -112,7 +144,7 @@ Luego añade esto a tu `package.json`:
112
144
  - **Estándar**: Formato clásico con score y recomendaciones detalladas
113
145
  - **SonarQube**: Simula salida de SonarQube con Quality Gate, métricas y clasificación de issues
114
146
  4. **Construye prompt inteligente**:
115
- - Lee las pautas desde `CLAUDE_PRE_COMMIT.md` o `CLAUDE_PRE_COMMIT_SONAR.md`
147
+ - Lee las pautas desde `.claude/CLAUDE_PRE_COMMIT.md` o `.claude/CLAUDE_PRE_COMMIT_SONAR.md`
116
148
  - Incluye el diff completo para archivos nuevos
117
149
  - Muestra solo cambios para archivos existentes
118
150
  5. **Envía a Claude CLI para revisión**
@@ -140,7 +172,8 @@ Luego añade esto a tu `package.json`:
140
172
 
141
173
  ### Características adicionales
142
174
 
143
- - **Auto-actualización**: Los hooks se sincronizan automáticamente con las versiones en `git-hooks/`
175
+ - **Auto-actualización**: Verificación automática de versiones antes de cada commit con prompt interactivo para actualizar
176
+ - **Comando update**: `claude-hooks update` para actualizar manualmente a la última versión
144
177
  - **Modo debug**: `DEBUG=1 git commit` guarda respuestas en `debug-claude-response.json`
145
178
  - **Configuración persistente**: Guarda preferencias en `.claude-analysis-mode`
146
179
  - **Validación de dependencias**: Verifica que Claude CLI esté autenticado antes de ejecutar
@@ -241,7 +274,18 @@ Claude responde con un JSON que incluye:
241
274
  - `recommendations`: Recomendaciones de mejora
242
275
  - `blockingIssues`: Problemas que bloquean el commit
243
276
 
244
- ## 🔄 Desactivar/Activar
277
+ ## 🔄 Actualización y Gestión
278
+
279
+ ### Actualizar a la última versión
280
+
281
+ ```bash
282
+ # Actualizar manualmente a la última versión
283
+ claude-hooks update
284
+ ```
285
+
286
+ El comando `update` descarga e instala automáticamente la última versión disponible desde NPM, y luego reinstala los hooks con las nuevas características.
287
+
288
+ ### Desactivar/Activar hooks
245
289
 
246
290
  ```bash
247
291
  # Desactivar todos los hooks
@@ -284,8 +328,8 @@ En el archivo `pre-commit`:
284
328
  - **`MAX_FILE_SIZE`**: Tamaño máximo de archivo a analizar (default: 100KB)
285
329
  - **`MAX_FILES`**: Número máximo de archivos por commit (default: 10)
286
330
  - **`CLAUDE_CLI`**: Comando de Claude CLI (default: "claude")
287
- - **`GUIDELINES_FILE`**: Archivo de pautas para modo estándar (default: "CLAUDE_PRE_COMMIT.md")
288
- - **`GUIDELINES_FILE_SONAR`**: Archivo de pautas para modo SonarQube (default: "CLAUDE_PRE_COMMIT_SONAR.md")
331
+ - **`GUIDELINES_FILE`**: Archivo de pautas para modo estándar (default: ".claude/CLAUDE_PRE_COMMIT.md")
332
+ - **`GUIDELINES_FILE_SONAR`**: Archivo de pautas para modo SonarQube (default: ".claude/CLAUDE_PRE_COMMIT_SONAR.md")
289
333
 
290
334
  En el archivo `prepare-commit-msg`:
291
335
 
@@ -351,7 +395,11 @@ Este problema suele ocurrir por conflictos de configuración de line endings:
351
395
 
352
396
  ## 📝 Personalización
353
397
 
354
- Puedes modificar las pautas de evaluación editando `CLAUDE_PRE_COMMIT.md` para adaptarlas a los estándares de tu equipo.
398
+ Puedes modificar las pautas de evaluación editando los archivos en el directorio `.claude/`:
399
+ - `.claude/CLAUDE_PRE_COMMIT.md` - Para el modo estándar
400
+ - `.claude/CLAUDE_PRE_COMMIT_SONAR.md` - Para el modo SonarQube
401
+
402
+ Estos archivos son específicos de tu proyecto y puedes adaptarlos a los estándares de tu equipo.
355
403
 
356
404
  ## 🔄 Arquitectura del Sistema
357
405
 
@@ -394,6 +442,9 @@ claude-git-hooks/
394
442
  │ ├── prepare-commit-msg # Hook de generación de mensajes
395
443
  │ ├── CLAUDE_PRE_COMMIT.md # Pautas estándar
396
444
  │ └── CLAUDE_PRE_COMMIT_SONAR.md # Pautas SonarQube
445
+ ├── .claude/ # Directorio creado en el repo del usuario
446
+ │ ├── CLAUDE_PRE_COMMIT.md # Pautas copiadas aquí durante la instalación
447
+ │ └── CLAUDE_PRE_COMMIT_SONAR.md
397
448
  ├── package.json # Configuración NPM
398
449
  ├── README.md # Este archivo
399
450
  ├── CHANGELOG.md # Historial de versiones
package/bin/claude-hooks CHANGED
@@ -7,6 +7,86 @@ const os = require('os');
7
7
  const readline = require('readline');
8
8
  const https = require('https');
9
9
 
10
+ // Función para obtener la última versión desde NPM
11
+ function getLatestVersion(packageName) {
12
+ return new Promise((resolve, reject) => {
13
+ // Usar la API principal de NPM, no /latest
14
+ https.get(`https://registry.npmjs.org/${packageName}`, (res) => {
15
+ let data = '';
16
+ res.on('data', chunk => data += chunk);
17
+ res.on('end', () => {
18
+ try {
19
+ const json = JSON.parse(data);
20
+ // Obtener la versión del tag 'latest'
21
+ if (json['dist-tags'] && json['dist-tags'].latest) {
22
+ resolve(json['dist-tags'].latest);
23
+ } else {
24
+ reject(new Error('No se pudo obtener la versión'));
25
+ }
26
+ } catch (e) {
27
+ // Si falla, intentar con npm view
28
+ try {
29
+ const version = execSync(`npm view ${packageName} version`, { encoding: 'utf8' }).trim();
30
+ resolve(version);
31
+ } catch (npmError) {
32
+ reject(e);
33
+ }
34
+ }
35
+ });
36
+ }).on('error', reject);
37
+ });
38
+ }
39
+
40
+ // Función para verificar versión (usada por los hooks)
41
+ async function checkVersionAndPromptUpdate() {
42
+ try {
43
+ const currentVersion = require('../package.json').version;
44
+ const latestVersion = await getLatestVersion('claude-git-hooks');
45
+
46
+ if (currentVersion === latestVersion) {
47
+ return true; // Ya actualizado
48
+ }
49
+
50
+ console.log('');
51
+ warning(`Nueva versión disponible: ${latestVersion} (actual: ${currentVersion})`);
52
+
53
+ // Prompt interactivo compatible con todas las consolas
54
+ const rl = readline.createInterface({
55
+ input: process.stdin,
56
+ output: process.stdout
57
+ });
58
+
59
+ return new Promise((resolve) => {
60
+ rl.question('¿Deseas actualizar ahora? (s/n): ', (answer) => {
61
+ rl.close();
62
+
63
+ if (answer.toLowerCase() === 's' || answer.toLowerCase() === 'si' || answer.toLowerCase() === 'sí' || answer.toLowerCase() === 'yes' || answer.toLowerCase() === 'y') {
64
+ info('Actualizando claude-git-hooks...');
65
+ try {
66
+ execSync('npm install -g claude-git-hooks@latest', { stdio: 'inherit' });
67
+ success('Actualización completada. Por favor, vuelve a ejecutar tu comando.');
68
+ process.exit(0); // Salir para que el usuario reinicie el proceso
69
+ } catch (e) {
70
+ error('Error al actualizar: ' + e.message);
71
+ resolve(false);
72
+ }
73
+ } else {
74
+ info('Actualización pospuesta. Puedes actualizar más tarde con: claude-hooks update');
75
+ resolve(true); // Continuar sin actualizar
76
+ }
77
+ });
78
+ });
79
+ } catch (e) {
80
+ // Si hay error verificando la versión, continuar sin bloquear
81
+ return true;
82
+ }
83
+ }
84
+
85
+ // Exportar para uso en hooks
86
+ if (typeof module !== 'undefined' && module.exports) {
87
+ module.exports = { checkVersionAndPromptUpdate };
88
+ }
89
+
10
90
  // Colores para output
11
91
  const colors = {
12
92
  reset: '\x1b[0m',
@@ -258,7 +338,13 @@ async function install(args) {
258
338
  error('No estás en un repositorio Git. Por favor, ejecuta este comando desde la raíz de un repositorio.');
259
339
  }
260
340
 
261
- info('Instalando Claude Git Hooks...');
341
+ const isForce = args.includes('--force');
342
+
343
+ if (isForce) {
344
+ info('Instalando Claude Git Hooks (modo force)...');
345
+ } else {
346
+ info('Instalando Claude Git Hooks...');
347
+ }
262
348
 
263
349
  // Solicitar contraseña sudo al inicio si es necesario
264
350
  let sudoPassword = null;
@@ -308,14 +394,32 @@ async function install(args) {
308
394
  success(`${hook} instalado`);
309
395
  });
310
396
 
311
- // Copiar archivos de pautas si no existen
397
+ // Copiar script de verificación de versión
398
+ const checkVersionSource = path.join(templatesPath, 'check-version.sh');
399
+ const checkVersionDest = path.join(hooksPath, 'check-version.sh');
400
+
401
+ if (fs.existsSync(checkVersionSource)) {
402
+ fs.copyFileSync(checkVersionSource, checkVersionDest);
403
+ fs.chmodSync(checkVersionDest, '755');
404
+ success('Script de verificación de versión instalado');
405
+ }
406
+
407
+ // Crear directorio .claude si no existe
408
+ const claudeDir = '.claude';
409
+ if (!fs.existsSync(claudeDir)) {
410
+ fs.mkdirSync(claudeDir, { recursive: true });
411
+ success('.claude directory created');
412
+ }
413
+
414
+ // Copiar archivos de pautas a .claude si no existen
312
415
  const guidelines = ['CLAUDE_PRE_COMMIT.md', 'CLAUDE_PRE_COMMIT_SONAR.md'];
313
416
  guidelines.forEach(guideline => {
314
- if (!fs.existsSync(guideline)) {
417
+ const destPath = path.join(claudeDir, guideline);
418
+ if (!fs.existsSync(destPath)) {
315
419
  const sourcePath = path.join(templatesPath, guideline);
316
420
  if (fs.existsSync(sourcePath)) {
317
- fs.copyFileSync(sourcePath, guideline);
318
- success(`${guideline} creado`);
421
+ fs.copyFileSync(sourcePath, destPath);
422
+ success(`${guideline} creado en .claude/`);
319
423
  }
320
424
  }
321
425
  });
@@ -323,6 +427,9 @@ async function install(args) {
323
427
  // Configurar Git
324
428
  configureGit();
325
429
 
430
+ // Actualizar .gitignore
431
+ updateGitignore();
432
+
326
433
  success('¡Claude Git Hooks instalado exitosamente! 🎉');
327
434
  console.log('\nUso:');
328
435
  console.log(' git commit -m "auto" # Genera mensaje automáticamente');
@@ -409,7 +516,7 @@ async function checkAndInstallDependencies(sudoPassword = null) {
409
516
  if (missingTools.length === 0) {
410
517
  success('Herramientas Unix estándar verificadas');
411
518
  } else {
412
- error(`Faltan herramientas Unix estándar: ${missingTools.join(', ')}`);
519
+ error(`Faltan herramientas Unix estándar: ${missingTools.join(', ')}. Reintenta instalación en una consola Ubuntu`);
413
520
  }
414
521
 
415
522
  // Verificar e instalar Claude CLI
@@ -503,6 +610,79 @@ async function checkClaudeAuth() {
503
610
  }
504
611
  }
505
612
 
613
+ // Actualizar .gitignore con las entradas de Claude
614
+ function updateGitignore() {
615
+ info('Actualizando .gitignore...');
616
+
617
+ const gitignorePath = '.gitignore';
618
+ const claudeEntries = [
619
+ '# Claude Git Hooks',
620
+ '.claude/',
621
+ '.claude-analysis-mode',
622
+ 'debug-claude-response.json'
623
+ ];
624
+
625
+ let gitignoreContent = '';
626
+ let fileExists = false;
627
+
628
+ // Leer .gitignore existente si existe
629
+ if (fs.existsSync(gitignorePath)) {
630
+ gitignoreContent = fs.readFileSync(gitignorePath, 'utf8');
631
+ fileExists = true;
632
+ }
633
+
634
+ // Verificar qué entradas faltan
635
+ const missingEntries = [];
636
+ claudeEntries.forEach(entry => {
637
+ if (entry.startsWith('#')) {
638
+ // Para comentarios, verificar si ya existe algún comentario de Claude
639
+ if (!gitignoreContent.includes('# Claude')) {
640
+ missingEntries.push(entry);
641
+ }
642
+ } else {
643
+ // Para entradas normales, verificar si ya están presentes
644
+ const regex = new RegExp(`^${entry.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}$`, 'm');
645
+ if (!regex.test(gitignoreContent)) {
646
+ missingEntries.push(entry);
647
+ }
648
+ }
649
+ });
650
+
651
+ // Si hay entradas faltantes, agregarlas
652
+ if (missingEntries.length > 0) {
653
+ // Asegurar que hay una nueva línea al final si el archivo existe y no está vacío
654
+ if (fileExists && gitignoreContent.length > 0 && !gitignoreContent.endsWith('\n')) {
655
+ gitignoreContent += '\n';
656
+ }
657
+
658
+ // Si el archivo no está vacío, agregar una línea en blanco antes
659
+ if (gitignoreContent.length > 0) {
660
+ gitignoreContent += '\n';
661
+ }
662
+
663
+ // Agregar las entradas faltantes
664
+ gitignoreContent += missingEntries.join('\n') + '\n';
665
+
666
+ // Escribir el archivo actualizado
667
+ fs.writeFileSync(gitignorePath, gitignoreContent);
668
+
669
+ if (fileExists) {
670
+ success('.gitignore actualizado con entradas de Claude');
671
+ } else {
672
+ success('.gitignore creado con entradas de Claude');
673
+ }
674
+
675
+ // Mostrar qué se agregó
676
+ missingEntries.forEach(entry => {
677
+ if (!entry.startsWith('#')) {
678
+ info(` + ${entry}`);
679
+ }
680
+ });
681
+ } else {
682
+ info('.gitignore ya contiene todas las entradas necesarias');
683
+ }
684
+ }
685
+
506
686
  // Configurar Git (line endings, etc.)
507
687
  function configureGit() {
508
688
  info('Configurando Git...');
@@ -631,12 +811,40 @@ function status() {
631
811
  console.log('\nArchivos de pautas:');
632
812
  const guidelines = ['CLAUDE_PRE_COMMIT.md', 'CLAUDE_PRE_COMMIT_SONAR.md'];
633
813
  guidelines.forEach(guideline => {
634
- if (fs.existsSync(guideline)) {
635
- success(`${guideline}: presente`);
814
+ const claudePath = path.join('.claude', guideline);
815
+ if (fs.existsSync(claudePath)) {
816
+ success(`${guideline}: presente en .claude/`);
817
+ } else if (fs.existsSync(guideline)) {
818
+ warning(`${guideline}: presente en raíz (debería estar en .claude/)`);
636
819
  } else {
637
820
  warning(`${guideline}: faltante`);
638
821
  }
639
822
  });
823
+
824
+ // Verificar entradas en .gitignore
825
+ console.log('\n.gitignore:');
826
+ const gitignorePath = '.gitignore';
827
+ if (fs.existsSync(gitignorePath)) {
828
+ const gitignoreContent = fs.readFileSync(gitignorePath, 'utf8');
829
+ const claudeIgnores = ['.claude/', '.claude-analysis-mode', 'debug-claude-response.json'];
830
+ let allPresent = true;
831
+
832
+ claudeIgnores.forEach(entry => {
833
+ const regex = new RegExp(`^${entry.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}$`, 'm');
834
+ if (regex.test(gitignoreContent)) {
835
+ success(`${entry}: incluido`);
836
+ } else {
837
+ warning(`${entry}: faltante`);
838
+ allPresent = false;
839
+ }
840
+ });
841
+
842
+ if (!allPresent) {
843
+ info('\nEjecuta "claude-hooks install" para actualizar .gitignore');
844
+ }
845
+ } else {
846
+ warning('.gitignore no existe');
847
+ }
640
848
  }
641
849
 
642
850
  // Comando set-mode
@@ -673,6 +881,87 @@ function setMode(mode) {
673
881
  }
674
882
  }
675
883
 
884
+ // Función para comparar versiones usando el script compartido
885
+ function compareVersions(v1, v2) {
886
+ try {
887
+ // Usar el script compartido para mantener consistencia
888
+ const result = execSync(`bash -c 'source "${getTemplatesPath()}/check-version.sh" && compare_versions "${v1}" "${v2}"; echo $?'`, { encoding: 'utf8' }).trim();
889
+ const exitCode = parseInt(result);
890
+
891
+ // Convertir los códigos de retorno del script bash a valores JS
892
+ if (exitCode === 0) return 0; // iguales
893
+ if (exitCode === 1) return 1; // v1 > v2
894
+ if (exitCode === 2) return -1; // v1 < v2
895
+
896
+ // Fallback: comparación simple si el script falla
897
+ if (v1 === v2) return 0;
898
+ const sorted = [v1, v2].sort((a, b) => {
899
+ const aParts = a.split('.').map(Number);
900
+ const bParts = b.split('.').map(Number);
901
+ for (let i = 0; i < Math.max(aParts.length, bParts.length); i++) {
902
+ const aPart = aParts[i] || 0;
903
+ const bPart = bParts[i] || 0;
904
+ if (aPart !== bPart) return aPart - bPart;
905
+ }
906
+ return 0;
907
+ });
908
+ return v1 === sorted[1] ? 1 : -1;
909
+ } catch (e) {
910
+ // Si falla todo, usar comparación simple
911
+ if (v1 === v2) return 0;
912
+ return v1 > v2 ? 1 : -1;
913
+ }
914
+ }
915
+
916
+ // Comando update - actualizar a la última versión
917
+ async function update() {
918
+ info('Verificando última versión disponible...');
919
+
920
+ try {
921
+ const currentVersion = require('../package.json').version;
922
+ const latestVersion = await getLatestVersion('claude-git-hooks');
923
+
924
+ const comparison = compareVersions(currentVersion, latestVersion);
925
+
926
+ if (comparison === 0) {
927
+ success(`Ya tienes la última versión instalada (${currentVersion})`);
928
+ return;
929
+ } else if (comparison > 0) {
930
+ info(`Estás usando una versión de desarrollo (${currentVersion})`);
931
+ info(`Última versión publicada: ${latestVersion}`);
932
+ success(`Ya tienes la última versión instalada (${currentVersion})`);
933
+ return;
934
+ }
935
+
936
+ info(`Versión actual: ${currentVersion}`);
937
+ info(`Versión disponible: ${latestVersion}`);
938
+
939
+ // Actualizar el paquete
940
+ info('Actualizando claude-git-hooks...');
941
+ try {
942
+ execSync('npm install -g claude-git-hooks@latest', { stdio: 'inherit' });
943
+ success(`Actualizado exitosamente a la versión ${latestVersion}`);
944
+
945
+ // Reinstalar hooks con la nueva versión
946
+ info('Reinstalando hooks con la nueva versión...');
947
+ await install(['--force']);
948
+
949
+ } catch (updateError) {
950
+ error('Error al actualizar. Intenta ejecutar: npm install -g claude-git-hooks@latest');
951
+ }
952
+ } catch (e) {
953
+ warning('No se pudo verificar la última versión disponible');
954
+ warning('Intentando actualizar de todas formas...');
955
+ try {
956
+ execSync('npm install -g claude-git-hooks@latest', { stdio: 'inherit' });
957
+ success('Actualización completada');
958
+ await install(['--force']);
959
+ } catch (updateError) {
960
+ error('Error al actualizar: ' + updateError.message);
961
+ }
962
+ }
963
+ }
964
+
676
965
  // Comando help
677
966
  function showHelp() {
678
967
  console.log(`
@@ -682,6 +971,7 @@ Uso: claude-hooks <comando> [opciones]
682
971
 
683
972
  Comandos:
684
973
  install Instala los hooks en el repositorio actual
974
+ update Actualiza a la última versión disponible
685
975
  uninstall Desinstala los hooks del repositorio
686
976
  enable [hook] Habilita hooks (todos o uno específico)
687
977
  disable [hook] Deshabilita hooks (todos o uno específico)
@@ -695,6 +985,7 @@ Hooks disponibles:
695
985
 
696
986
  Ejemplos:
697
987
  claude-hooks install # Instala todos los hooks
988
+ claude-hooks update # Actualiza a la última versión
698
989
  claude-hooks set-mode sonar # Cambiar a modo SonarQube
699
990
  claude-hooks set-mode standard # Cambiar a modo estándar
700
991
  claude-hooks disable pre-commit # Deshabilita solo pre-commit
@@ -714,6 +1005,9 @@ async function main() {
714
1005
  case 'install':
715
1006
  await install(args.slice(1));
716
1007
  break;
1008
+ case 'update':
1009
+ await update();
1010
+ break;
717
1011
  case 'uninstall':
718
1012
  uninstall();
719
1013
  break;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-git-hooks",
3
- "version": "1.2.1",
3
+ "version": "1.3.0",
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": {
@@ -44,7 +44,8 @@ Responde ÚNICAMENTE con un JSON válido siguiendo esta estructura:
44
44
  ],
45
45
  "blockingIssues": [
46
46
  "Problema crítico 1 (si existe)"
47
- ]
47
+ ],
48
+ "details": "Información adicional detallada (opcional)"
48
49
  }
49
50
  ```
50
51
 
@@ -23,6 +23,11 @@ Evaluar el código modificado siguiendo métricas similares a SonarQube para ase
23
23
  - Duplicación de código
24
24
  - Deuda técnica
25
25
 
26
+ ### Métricas Adicionales
27
+ - **Coverage**: Porcentaje estimado de cobertura de pruebas (0-100)
28
+ - **Duplications**: Porcentaje estimado de código duplicado (0-100)
29
+ - **Complexity**: Complejidad ciclomática promedio del código
30
+
26
31
  ## Clasificación de Issues
27
32
 
28
33
  ### Severidad
@@ -42,7 +47,10 @@ Responde ÚNICAMENTE con un JSON válido siguiendo esta estructura:
42
47
  "metrics": {
43
48
  "reliability": "A/B/C/D/E",
44
49
  "security": "A/B/C/D/E",
45
- "maintainability": "A/B/C/D/E"
50
+ "maintainability": "A/B/C/D/E",
51
+ "coverage": 75,
52
+ "duplications": 5,
53
+ "complexity": 15
46
54
  },
47
55
  "issues": {
48
56
  "blocker": 0,
@@ -60,7 +68,9 @@ Responde ÚNICAMENTE con un JSON válido siguiendo esta estructura:
60
68
  "line": 42
61
69
  }
62
70
  ],
63
- "securityHotspots": 0
71
+ "securityHotspots": 0,
72
+ "approved": true/false,
73
+ "blockingIssues": ["Lista de problemas bloqueantes si existen"]
64
74
  }
65
75
  ```
66
76
 
@@ -0,0 +1,266 @@
1
+ #!/bin/bash
2
+
3
+ # Script compartido para verificar versión de claude-git-hooks
4
+ # Este script puede ser invocado desde cualquier hook
5
+
6
+ # Colores para output
7
+ RED='\033[0;31m'
8
+ GREEN='\033[0;32m'
9
+ YELLOW='\033[1;33m'
10
+ BLUE='\033[0;34m'
11
+ NC='\033[0m' # No Color
12
+
13
+ # Función para logging
14
+ log() {
15
+ echo -e "${GREEN}[VERSION-CHECK]${NC} $1"
16
+ }
17
+
18
+ error() {
19
+ echo -e "${RED}[ERROR]${NC} $1"
20
+ }
21
+
22
+ warning() {
23
+ echo -e "${YELLOW}[WARNING]${NC} $1"
24
+ }
25
+
26
+ info() {
27
+ echo -e "${BLUE}[INFO]${NC} $1"
28
+ }
29
+
30
+ # Función compartida para comparar versiones semánticas
31
+ # Retorna: 1 si v1 > v2, 2 si v1 < v2, 0 si son iguales
32
+ compare_versions() {
33
+ local v1="$1"
34
+ local v2="$2"
35
+
36
+ if [ -n "$DEBUG_VERSION" ]; then
37
+ echo "[DEBUG] compare_versions: comparando '$v1' con '$v2'"
38
+ fi
39
+
40
+ # Si son exactamente iguales
41
+ if [ "$v1" = "$v2" ]; then
42
+ if [ -n "$DEBUG_VERSION" ]; then
43
+ echo "[DEBUG] compare_versions: versiones iguales, retornando 0"
44
+ fi
45
+ return 0
46
+ fi
47
+
48
+ # Usar sort -V que entiende versiones semánticas correctamente
49
+ local sorted=$(printf "%s\n%s" "$v1" "$v2" | sort -V)
50
+ local smaller=$(echo "$sorted" | head -1)
51
+ local larger=$(echo "$sorted" | tail -1)
52
+
53
+ if [ -n "$DEBUG_VERSION" ]; then
54
+ echo "[DEBUG] compare_versions: sorted='$sorted', smaller='$smaller', larger='$larger'"
55
+ fi
56
+
57
+ if [ "$v1" = "$larger" ]; then
58
+ if [ -n "$DEBUG_VERSION" ]; then
59
+ echo "[DEBUG] compare_versions: v1 es mayor, retornando 1"
60
+ fi
61
+ return 1 # v1 es mayor que v2
62
+ else
63
+ if [ -n "$DEBUG_VERSION" ]; then
64
+ echo "[DEBUG] compare_versions: v1 es menor, retornando 2"
65
+ fi
66
+ return 2 # v1 es menor que v2 (cambié de 255 a 2 para evitar problemas con set -e)
67
+ fi
68
+ }
69
+
70
+ # Función principal de verificación
71
+ check_version() {
72
+ # Debug mode si está activado
73
+ if [ -n "$DEBUG_VERSION" ]; then
74
+ echo "[DEBUG] Iniciando verificación de versión..."
75
+ fi
76
+
77
+ info "Verificando versión instalada..."
78
+
79
+ # Intentar obtener versión actual desde package.json global
80
+ CURRENT_VERSION=""
81
+ NPM_PREFIX=$(npm prefix -g 2>/dev/null || echo "/usr/local")
82
+ PACKAGE_JSON_PATHS=(
83
+ "$NPM_PREFIX/lib/node_modules/claude-git-hooks/package.json"
84
+ "/usr/lib/node_modules/claude-git-hooks/package.json"
85
+ "/usr/local/lib/node_modules/claude-git-hooks/package.json"
86
+ "$HOME/.npm-global/lib/node_modules/claude-git-hooks/package.json"
87
+ )
88
+
89
+ for path in "${PACKAGE_JSON_PATHS[@]}"; do
90
+ if [ -f "$path" ]; then
91
+ CURRENT_VERSION=$(grep '"version"' "$path" 2>/dev/null | sed 's/.*"version".*"\(.*\)".*/\1/')
92
+ break
93
+ fi
94
+ done
95
+
96
+ if [ -z "$CURRENT_VERSION" ]; then
97
+ warning "No se pudo determinar la versión actual"
98
+ return 0 # Continuar sin bloquear
99
+ fi
100
+
101
+ if [ -n "$DEBUG_VERSION" ]; then
102
+ echo "[DEBUG] Versión actual encontrada: $CURRENT_VERSION"
103
+ fi
104
+
105
+ # Obtener última versión desde NPM (usando npm view que es más confiable)
106
+ info "Verificando última versión disponible..."
107
+
108
+ # Método principal: usar npm view que es más confiable
109
+ if [ -n "$DEBUG_VERSION" ]; then
110
+ echo "[DEBUG] Ejecutando: npm view claude-git-hooks version"
111
+ fi
112
+
113
+ LATEST_VERSION=$(npm view claude-git-hooks version 2>/dev/null || echo "")
114
+
115
+ if [ -n "$DEBUG_VERSION" ]; then
116
+ echo "[DEBUG] npm view retornó: '$LATEST_VERSION'"
117
+ echo "[DEBUG] Longitud de LATEST_VERSION: ${#LATEST_VERSION}"
118
+ fi
119
+
120
+ # Si npm view falla, intentar con la API de NPM
121
+ if [ -z "$LATEST_VERSION" ]; then
122
+ # Obtener el JSON completo y extraer dist-tags.latest con jq si está disponible
123
+ if command -v jq &> /dev/null; then
124
+ LATEST_VERSION=$(curl -s https://registry.npmjs.org/claude-git-hooks 2>/dev/null | jq -r '."dist-tags".latest' 2>/dev/null || echo "")
125
+ if [ -n "$DEBUG_VERSION" ]; then
126
+ echo "[DEBUG] jq parsing retornó: '$LATEST_VERSION'"
127
+ fi
128
+ else
129
+ # Sin jq, usar método más robusto con sed
130
+ LATEST_VERSION=$(curl -s https://registry.npmjs.org/claude-git-hooks 2>/dev/null | sed -n 's/.*"dist-tags":{[^}]*"latest":"\([^"]*\)".*/\1/p' | head -1)
131
+ if [ -n "$DEBUG_VERSION" ]; then
132
+ echo "[DEBUG] sed parsing retornó: '$LATEST_VERSION'"
133
+ fi
134
+ fi
135
+ fi
136
+
137
+ # Validar que lo obtenido parece una versión (formato X.Y.Z)
138
+ if [ -n "$DEBUG_VERSION" ]; then
139
+ echo "[DEBUG] Validando versión: '$LATEST_VERSION'"
140
+ fi
141
+
142
+ if ! echo "$LATEST_VERSION" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+' ; then
143
+ if [ -n "$DEBUG_VERSION" ]; then
144
+ echo "[DEBUG] Versión obtenida no es válida: '$LATEST_VERSION'"
145
+ fi
146
+ warning "No se pudo obtener una versión válida del registro NPM"
147
+ return 0
148
+ fi
149
+
150
+ if [ -n "$DEBUG_VERSION" ]; then
151
+ echo "[DEBUG] Versión válida confirmada: '$LATEST_VERSION'"
152
+ fi
153
+
154
+ if [ -z "$LATEST_VERSION" ]; then
155
+ # Si no se puede obtener la versión, continuar sin bloquear
156
+ return 0
157
+ fi
158
+
159
+ if [ "$CURRENT_VERSION" = "$LATEST_VERSION" ]; then
160
+ log "Versión actual ($CURRENT_VERSION) está actualizada ✓"
161
+ return 0
162
+ fi
163
+
164
+ # Comparar versiones usando la función compartida
165
+ if [ -n "$DEBUG_VERSION" ]; then
166
+ echo "[DEBUG] Comparando versiones: '$CURRENT_VERSION' vs '$LATEST_VERSION'"
167
+ fi
168
+
169
+ # Deshabilitar set -e temporalmente para capturar el código de retorno
170
+ set +e
171
+ compare_versions "$CURRENT_VERSION" "$LATEST_VERSION"
172
+ comparison_result=$?
173
+ set -e
174
+
175
+ if [ -n "$DEBUG_VERSION" ]; then
176
+ echo "[DEBUG] Resultado de comparación: $comparison_result"
177
+ fi
178
+
179
+ if [ $comparison_result -eq 0 ]; then
180
+ # Las versiones son iguales
181
+ if [ -n "$DEBUG_VERSION" ]; then
182
+ echo "[DEBUG] Las versiones son iguales"
183
+ fi
184
+ log "Versión actual ($CURRENT_VERSION) está actualizada ✓"
185
+ return 0
186
+ elif [ $comparison_result -eq 1 ]; then
187
+ # La versión actual es mayor que la publicada (desarrollo local)
188
+ if [ -n "$DEBUG_VERSION" ]; then
189
+ echo "[DEBUG] Versión local ($CURRENT_VERSION) es más nueva que la publicada ($LATEST_VERSION)"
190
+ fi
191
+ log "Versión de desarrollo ($CURRENT_VERSION) > versión publicada ($LATEST_VERSION) ✓"
192
+ return 0
193
+ elif [ $comparison_result -eq 2 ]; then
194
+ # La versión publicada es mayor, continuar con prompt de actualización
195
+ if [ -n "$DEBUG_VERSION" ]; then
196
+ echo "[DEBUG] La versión publicada ($LATEST_VERSION) es más nueva que la local ($CURRENT_VERSION)"
197
+ fi
198
+ # Continuar con el prompt de actualización (no return aquí)
199
+ fi
200
+
201
+ if [ -n "$DEBUG_VERSION" ]; then
202
+ echo "[DEBUG] La versión publicada es más nueva, procediendo con prompt de actualización"
203
+ fi
204
+
205
+ # Solo mostrar actualización si la versión publicada es realmente más nueva
206
+ echo
207
+ warning "Nueva versión disponible: $LATEST_VERSION (actual: $CURRENT_VERSION)"
208
+ echo -e "${YELLOW}╭─────────────────────────────────────────────╮${NC}"
209
+ echo -e "${YELLOW}│${NC} Una nueva versión de claude-git-hooks está ${YELLOW}│${NC}"
210
+ echo -e "${YELLOW}│${NC} disponible con mejoras y correcciones ${YELLOW}│${NC}"
211
+ echo -e "${YELLOW}╰─────────────────────────────────────────────╯${NC}"
212
+ echo
213
+
214
+ # Leer respuesta del usuario de forma compatible con todas las consolas
215
+ echo -n "¿Deseas actualizar ahora? (s/n): "
216
+
217
+ # Intentar varios métodos de lectura para máxima compatibilidad
218
+ REPLY=""
219
+
220
+ # Método 1: Leer desde /dev/tty si existe
221
+ if [ -c /dev/tty ]; then
222
+ read -r REPLY </dev/tty 2>/dev/null || REPLY=""
223
+ fi
224
+
225
+ # Método 2: Si falla, intentar leer desde stdin estándar
226
+ if [ -z "$REPLY" ]; then
227
+ read -r REPLY 2>/dev/null || REPLY=""
228
+ fi
229
+
230
+ # Método 3: Si aún no tenemos respuesta, usar bash -c
231
+ if [ -z "$REPLY" ]; then
232
+ REPLY=$(bash -c 'read -r reply && echo "$reply"' 2>/dev/null || echo "")
233
+ fi
234
+
235
+ # Si ningún método funcionó, informar y continuar
236
+ if [ -z "$REPLY" ]; then
237
+ echo
238
+ info "No se pudo leer la respuesta. Continuando sin actualizar."
239
+ info "Para actualizar manualmente, ejecuta: claude-hooks update"
240
+ return 0
241
+ fi
242
+
243
+ if [ "$REPLY" = "s" ] || [ "$REPLY" = "S" ] || [ "$REPLY" = "si" ] || [ "$REPLY" = "SI" ] || [ "$REPLY" = "sí" ] || [ "$REPLY" = "SÍ" ] || [ "$REPLY" = "y" ] || [ "$REPLY" = "Y" ] || [ "$REPLY" = "yes" ] || [ "$REPLY" = "YES" ]; then
244
+ echo
245
+ info "Actualizando claude-git-hooks..."
246
+
247
+ if npm install -g claude-git-hooks@latest; then
248
+ echo
249
+ log "✅ Actualización completada exitosamente"
250
+ info "Por favor, vuelve a ejecutar tu comando git commit"
251
+ exit 0 # Salir para que el usuario reinicie con la nueva versión
252
+ else
253
+ error "Error al actualizar. Intenta manualmente con: npm install -g claude-git-hooks@latest"
254
+ return 1
255
+ fi
256
+ else
257
+ info "Actualización pospuesta. Puedes actualizar más tarde con: claude-hooks update"
258
+ echo
259
+ return 0
260
+ fi
261
+ }
262
+
263
+ # Si se ejecuta directamente (no como source), ejecutar la verificación
264
+ if [ "${BASH_SOURCE[0]}" = "${0}" ]; then
265
+ check_version
266
+ fi
@@ -14,6 +14,7 @@ MAX_FILE_SIZE=100000 # 100KB máximo por archivo
14
14
  RED='\033[0;31m'
15
15
  GREEN='\033[0;32m'
16
16
  YELLOW='\033[1;33m'
17
+ BLUE='\033[0;34m'
17
18
  NC='\033[0m' # No Color
18
19
 
19
20
  # Función para logging
@@ -29,6 +30,32 @@ warning() {
29
30
  echo -e "${YELLOW}[WARNING]${NC} $1"
30
31
  }
31
32
 
33
+ # Verificar versión al inicio (antes de cualquier análisis)
34
+ # Intentar encontrar el script check-version.sh
35
+ CHECK_VERSION_SCRIPT=""
36
+ # Buscar en múltiples ubicaciones posibles
37
+ SCRIPT_PATHS=(
38
+ "$(dirname "$0")/check-version.sh"
39
+ "/usr/local/lib/node_modules/claude-git-hooks/templates/check-version.sh"
40
+ "/usr/lib/node_modules/claude-git-hooks/templates/check-version.sh"
41
+ "$HOME/.npm-global/lib/node_modules/claude-git-hooks/templates/check-version.sh"
42
+ "$(npm prefix -g 2>/dev/null)/lib/node_modules/claude-git-hooks/templates/check-version.sh"
43
+ )
44
+
45
+ for path in "${SCRIPT_PATHS[@]}"; do
46
+ if [ -f "$path" ]; then
47
+ CHECK_VERSION_SCRIPT="$path"
48
+ break
49
+ fi
50
+ done
51
+
52
+ # Si encontramos el script, ejecutarlo
53
+ if [ -n "$CHECK_VERSION_SCRIPT" ]; then
54
+ # Source el script para tener acceso a la función
55
+ source "$CHECK_VERSION_SCRIPT"
56
+ check_version
57
+ fi
58
+
32
59
  # Detectar qué archivo de pautas usar
33
60
  # Prioridad: variable de entorno > archivo > pregunta interactiva
34
61
  if [ -n "$CLAUDE_ANALYSIS_MODE" ]; then
@@ -64,10 +91,10 @@ if [ "$ANALYSIS_MODE" != "standard" ] && [ "$ANALYSIS_MODE" != "sonar" ]; then
64
91
 
65
92
  if [ "$REPLY" = "2" ]; then
66
93
  ANALYSIS_MODE="sonar"
67
- GUIDELINES_FILE="CLAUDE_PRE_COMMIT_SONAR.md"
94
+ GUIDELINES_FILE=".claude/CLAUDE_PRE_COMMIT_SONAR.md"
68
95
  else
69
96
  ANALYSIS_MODE="standard"
70
- GUIDELINES_FILE="CLAUDE_PRE_COMMIT.md"
97
+ GUIDELINES_FILE=".claude/CLAUDE_PRE_COMMIT.md"
71
98
  fi
72
99
 
73
100
  # Guardar preferencia para futuros commits
@@ -78,10 +105,10 @@ if [ "$ANALYSIS_MODE" != "standard" ] && [ "$ANALYSIS_MODE" != "sonar" ]; then
78
105
  else
79
106
  # Usar modo guardado
80
107
  if [ "$ANALYSIS_MODE" = "sonar" ]; then
81
- GUIDELINES_FILE="CLAUDE_PRE_COMMIT_SONAR.md"
108
+ GUIDELINES_FILE=".claude/CLAUDE_PRE_COMMIT_SONAR.md"
82
109
  log "Usando modo de análisis: SonarQube"
83
110
  else
84
- GUIDELINES_FILE="CLAUDE_PRE_COMMIT.md"
111
+ GUIDELINES_FILE=".claude/CLAUDE_PRE_COMMIT.md"
85
112
  log "Usando modo de análisis: Estándar"
86
113
  fi
87
114
  fi
@@ -233,7 +260,7 @@ if $CLAUDE_CLI < "$PROMPT_FILE" > "$RESPONSE_FILE" 2>&1; then
233
260
  BLOCKING_ISSUES=$(echo "$JSON_RESPONSE" | jq -r '.blockingIssues[]?' 2>/dev/null | sed '/^$/d')
234
261
 
235
262
  # Verificar si estamos en modo SonarQube
236
- QUALITY_GATE=$(echo "$JSON_RESPONSE" | jq -r '.qualityGate // ""' 2>/dev/null)
263
+ QUALITY_GATE=$(echo "$JSON_RESPONSE" | jq -r '.QUALITY_GATE // ""' 2>/dev/null)
237
264
 
238
265
  if [ "$ANALYSIS_MODE" = "sonar" ] && [ -n "$QUALITY_GATE" ] && [ "$QUALITY_GATE" != "null" ]; then
239
266
  # Mostrar resultados estilo SonarQube
@@ -265,15 +292,15 @@ if $CLAUDE_CLI < "$PROMPT_FILE" > "$RESPONSE_FILE" 2>&1; then
265
292
  fi
266
293
 
267
294
  # Issues Summary
268
- SUMMARY=$(echo "$JSON_RESPONSE" | jq -r '.summary // {}' 2>/dev/null)
269
- if [ "$SUMMARY" != "{}" ] && [ "$SUMMARY" != "null" ]; then
295
+ ISSUES=$(echo "$JSON_RESPONSE" | jq -r '.issues // {}' 2>/dev/null)
296
+ if [ "$ISSUES" != "{}" ] && [ "$ISSUES" != "null" ]; then
270
297
  echo "📋 ISSUES SUMMARY"
271
- TOTAL=$(echo "$SUMMARY" | jq -r '.total // 0' 2>/dev/null)
272
- BLOCKER=$(echo "$SUMMARY" | jq -r '.blocker // 0' 2>/dev/null)
273
- CRITICAL=$(echo "$SUMMARY" | jq -r '.critical // 0' 2>/dev/null)
274
- MAJOR=$(echo "$SUMMARY" | jq -r '.major // 0' 2>/dev/null)
275
- MINOR=$(echo "$SUMMARY" | jq -r '.minor // 0' 2>/dev/null)
276
- INFO=$(echo "$SUMMARY" | jq -r '.info // 0' 2>/dev/null)
298
+ BLOCKER=$(echo "$ISSUES" | jq -r '.blocker // 0' 2>/dev/null)
299
+ CRITICAL=$(echo "$ISSUES" | jq -r '.critical // 0' 2>/dev/null)
300
+ MAJOR=$(echo "$ISSUES" | jq -r '.major // 0' 2>/dev/null)
301
+ MINOR=$(echo "$ISSUES" | jq -r '.minor // 0' 2>/dev/null)
302
+ INFO=$(echo "$ISSUES" | jq -r '.info // 0' 2>/dev/null)
303
+ TOTAL=$((BLOCKER + CRITICAL + MAJOR + MINOR + INFO))
277
304
 
278
305
  echo "Total: $TOTAL issues found"
279
306
  [ "$BLOCKER" -gt 0 ] && echo -e " ${RED}🔴 Blocker: $BLOCKER${NC}"
@@ -285,18 +312,19 @@ if $CLAUDE_CLI < "$PROMPT_FILE" > "$RESPONSE_FILE" 2>&1; then
285
312
  fi
286
313
 
287
314
  # Detailed Issues
288
- ISSUES_COUNT=$(echo "$JSON_RESPONSE" | jq -r '.issues | length' 2>/dev/null)
289
- if [ "$ISSUES_COUNT" -gt 0 ] 2>/dev/null; then
315
+ DETAILS_COUNT=$(echo "$JSON_RESPONSE" | jq -r '.details | length' 2>/dev/null)
316
+ if [ "$DETAILS_COUNT" -gt 0 ] 2>/dev/null; then
290
317
  echo "🔍 DETAILED ISSUES"
291
- echo "$JSON_RESPONSE" | jq -r '.issues[]? |
292
- "[\(.severity)] \(.type) in \(.file):\(.line // "?")\n \(.message)\n Rule: \(.rule // "N/A") | Effort: \(.effort // "?")\n"' 2>/dev/null
318
+ echo "$JSON_RESPONSE" | jq -r '.details[]? |
319
+ "[\(.severity)] \(.type) in \(.file):\(.line // "?")\n \(.message)\n"' 2>/dev/null
293
320
  fi
294
321
 
295
322
  # Security Hotspots
296
- HOTSPOTS_COUNT=$(echo "$JSON_RESPONSE" | jq -r '.hotspots | length' 2>/dev/null)
297
- if [ "$HOTSPOTS_COUNT" -gt 0 ] 2>/dev/null; then
298
- echo "🔥 SECURITY HOTSPOTS"
299
- echo "$JSON_RESPONSE" | jq -r '.hotspots[]? | " • \(.file): \(.message)"' 2>/dev/null
323
+ HOTSPOTS=$(echo "$JSON_RESPONSE" | jq -r '.securityHotspots // 0' 2>/dev/null)
324
+ if [ "$HOTSPOTS" -gt 0 ] 2>/dev/null; then
325
+ echo "🔥 SECURITY HOTSPOTS: $HOTSPOTS found"
326
+ echo " Review security-sensitive code carefully"
327
+
300
328
  echo
301
329
  fi
302
330
 
@@ -332,11 +360,18 @@ if $CLAUDE_CLI < "$PROMPT_FILE" > "$RESPONSE_FILE" 2>&1; then
332
360
  exit 1
333
361
  fi
334
362
 
335
- DETAILS=$(echo "$JSON_RESPONSE" | jq -r '.details // {}')
336
- if [ -n "$DETAILS" ] && [ "$DETAILS" != "{}" ] && [ "$DETAILS" != "null" ]; then
363
+ # Mostrar detalles adicionales si existen
364
+ DETAILS=$(echo "$JSON_RESPONSE" | jq -r '.details // null')
365
+ if [ -n "$DETAILS" ] && [ "$DETAILS" != "null" ]; then
337
366
  echo
338
- echo "=== DETALLES POR CATEGORÍA ==="
339
- echo "$DETAILS" | jq
367
+ echo "=== DETALLES ADICIONALES ==="
368
+ # Si details es un string, imprimirlo directamente
369
+ if echo "$DETAILS" | jq -e 'type == "string"' >/dev/null 2>&1; then
370
+ echo "$DETAILS" | jq -r '.'
371
+ # Si es un objeto o array, formatearlo
372
+ else
373
+ echo "$DETAILS" | jq '.'
374
+ fi
340
375
  fi
341
376
 
342
377
  log "✅ Revisión completada. Commit aprobado (Score: $SCORE/10)"
File without changes