claude-git-hooks 1.2.4 → 1.4.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,58 @@ 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.4.0] - 2025-08-29
9
+
10
+ ### Added
11
+ - 🎯 Prompts externalizados en archivos `.md` para facilitar personalización
12
+ - 🤖 Generación automática de prompt AI-friendly para resolución de problemas críticos
13
+ - 📝 Nuevos templates de prompts: `CLAUDE_ANALYSIS_PROMPT.md`, `CLAUDE_ANALYSIS_PROMPT_SONAR.md`, `CLAUDE_RESOLUTION_PROMPT.md`
14
+ - 🔍 Localización precisa en blockingIssues (archivo, línea, método, severidad)
15
+ - 📋 Archivo `claude_resolution_prompt.md` generado automáticamente con issues para resolver
16
+ - 🚀 Flag `--skip-auth` para omitir verificación de autenticación en instalación
17
+ - 📖 Ejemplos detallados de respuestas JSON con blocking issues en README
18
+
19
+ ### Changed
20
+ - 🏗️ Estandarización de `blockingIssues` a formato objeto (nunca strings)
21
+ - 📦 Separación de prompts del código para mayor mantenibilidad
22
+ - 🔧 Hook pre-commit simplificado sin fallbacks complejos
23
+ - 📁 Instalador actualizado para copiar todos los templates de prompts
24
+
25
+ ### Fixed
26
+ - 🐛 Consistencia en el formato de respuesta JSON para ambos modos
27
+ - 🔧 Mejor manejo de errores cuando faltan archivos de configuración
28
+
29
+ ### Technical
30
+ - 🏗️ Función `generate_resolution_prompt()` para crear prompts de resolución
31
+ - 📝 Templates de prompts con placeholders reemplazables
32
+ - 🎯 Estructura JSON estricta para blockingIssues con campos obligatorios
33
+ - 🔄 Instalación en modo force actualiza todos los templates
34
+
35
+ ## [1.3.0] - 2025-08-28
36
+
37
+ ### Added
38
+ - 🚀 Nuevo comando `claude-hooks update` para actualizar a la última versión disponible
39
+ - 🔄 Verificación automática de versión antes de cada commit con prompt interactivo
40
+ - 📜 Script compartido `check-version.sh` para evitar duplicación de código
41
+ - ✨ Prompt interactivo compatible con todas las consolas para aceptar actualizaciones
42
+ - 📊 Mensajes informativos detallados durante el proceso de verificación y actualización
43
+
44
+ ### Changed
45
+ - 🎯 El hook `pre-commit` ahora verifica automáticamente si hay actualizaciones disponibles
46
+ - 📦 La instalación ahora incluye el script `check-version.sh` en `.git/hooks/`
47
+ - 🔧 Mejorada la UX con mensajes claros sobre el estado de la versión
48
+
49
+ ### Fixed
50
+ - 🐛 Corregido el parsing de versión desde NPM API (usaba endpoint incorrecto)
51
+ - 🔧 Mejorada la compatibilidad del prompt interactivo para consolas IntelliJ/WSL
52
+ - 📝 Múltiples métodos de fallback para leer input del usuario
53
+
54
+ ### Technical
55
+ - 🏗️ Función `getLatestVersion()` ahora usa `dist-tags.latest` de NPM API
56
+ - 🔄 Función `checkVersionAndPromptUpdate()` reutilizable para verificación
57
+ - 📁 Script compartido para evitar duplicación de lógica de verificación
58
+ - 🎯 Fallback a `npm view` si la API de NPM falla
59
+
8
60
  ## [1.2.4] - 2025-08-22
9
61
 
10
62
  ### Fixed
package/README.md CHANGED
@@ -10,6 +10,9 @@ Este directorio contiene un pre-commit hook que utiliza Claude CLI para revisar
10
10
  - **`.gitattributes`** - Configuración para mantener line endings correctos
11
11
  - **`.claude/CLAUDE_PRE_COMMIT.md`** - Pautas de evaluación formato estándar
12
12
  - **`.claude/CLAUDE_PRE_COMMIT_SONAR.md`** - Pautas de evaluación formato SonarQube
13
+ - **`.claude/CLAUDE_ANALYSIS_PROMPT.md`** - Template de prompt para análisis estándar
14
+ - **`.claude/CLAUDE_ANALYSIS_PROMPT_SONAR.md`** - Template de prompt para análisis SonarQube
15
+ - **`.claude/CLAUDE_RESOLUTION_PROMPT.md`** - Template para generar prompt de resolución AI
13
16
 
14
17
  ## 🔧 Configuración Previa Importante
15
18
 
@@ -64,13 +67,19 @@ claude-hooks install
64
67
  ```
65
68
 
66
69
  El comando `claude-hooks install` ahora incluye:
70
+
67
71
  - ✅ Verificación completa de dependencias del sistema
68
72
  - ✅ Instalación automática de paquetes faltantes (jq, curl)
69
73
  - ✅ Configuración de Git (line endings WSL/Windows)
70
- - ✅ Verificación de autenticación Claude con entretenimiento
74
+ - ✅ Verificación de autenticación Claude con entretenimiento (omitible con `--skip-auth`)
71
75
  - ✅ Instalación de hooks y archivos de pautas
72
76
  - ✅ Actualización automática de .gitignore con archivos de Claude
73
77
 
78
+ **Opciones de instalación:**
79
+
80
+ - `--force`: Reinstala aunque los hooks ya existan
81
+ - `--skip-auth`: Omite la verificación de autenticación de Claude (útil para CI/CD)
82
+
74
83
  #### Añadir como Dependencia de Desarrollo
75
84
 
76
85
  ```bash
@@ -93,11 +102,11 @@ Luego añade esto a tu `package.json`:
93
102
 
94
103
  ## 🤖 Características
95
104
 
96
- **✨ Auto-actualización incorporada**: Los hooks se actualizan automáticamente en cada commit.
105
+ **✨ Auto-actualización**: Verifica automáticamente si hay nuevas versiones disponibles antes de cada commit y ofrece actualizar con un simple prompt interactivo.
97
106
 
98
107
  **Hooks disponibles**:
99
108
 
100
- - `pre-commit`: Análisis de código con Claude (solo archivos Java/config)
109
+ - `pre-commit`: Análisis de código con Claude (solo archivos Java/config) con verificación automática de versión
101
110
  - `prepare-commit-msg`: Generación automática de mensajes de commit
102
111
 
103
112
  ## 📁 Gestión de Archivos
@@ -108,10 +117,14 @@ El comando `claude-hooks install` crea los siguientes archivos y directorios:
108
117
 
109
118
  1. **`.git/hooks/pre-commit`** - Hook de análisis de código
110
119
  2. **`.git/hooks/prepare-commit-msg`** - Hook de generación de mensajes
111
- 3. **`.claude/`** - Directorio para archivos de configuración
120
+ 3. **`.git/hooks/check-version.sh`** - Script de verificación de versión
121
+ 4. **`.claude/`** - Directorio para archivos de configuración
112
122
  - `CLAUDE_PRE_COMMIT.md` - Pautas de evaluación estándar
113
123
  - `CLAUDE_PRE_COMMIT_SONAR.md` - Pautas de evaluación SonarQube
114
- 4. **`.claude-analysis-mode`** - Archivo de preferencia de modo (creado al primer uso)
124
+ - `CLAUDE_ANALYSIS_PROMPT.md` - Template de prompt para análisis estándar
125
+ - `CLAUDE_ANALYSIS_PROMPT_SONAR.md` - Template de prompt para análisis SonarQube
126
+ - `CLAUDE_RESOLUTION_PROMPT.md` - Template para prompt de resolución AI
127
+ 5. **`.claude-analysis-mode`** - Archivo de preferencia de modo (creado al primer uso)
115
128
 
116
129
  ### Actualización automática de .gitignore
117
130
 
@@ -122,9 +135,11 @@ Durante la instalación, Claude Hooks actualiza automáticamente tu `.gitignore`
122
135
  .claude/
123
136
  .claude-analysis-mode
124
137
  debug-claude-response.json
138
+ claude_resolution_prompt.md
125
139
  ```
126
140
 
127
141
  Esto asegura que:
142
+
128
143
  - Los archivos de configuración de Claude específicos del proyecto no se suban al repositorio
129
144
  - Los archivos de debug temporales se ignoren
130
145
  - Cada desarrollador pueda tener sus propias preferencias de análisis
@@ -144,17 +159,28 @@ Si no existe un `.gitignore`, se creará uno nuevo. Si ya existe, las entradas s
144
159
  - **Estándar**: Formato clásico con score y recomendaciones detalladas
145
160
  - **SonarQube**: Simula salida de SonarQube con Quality Gate, métricas y clasificación de issues
146
161
  4. **Construye prompt inteligente**:
147
- - Lee las pautas desde `.claude/CLAUDE_PRE_COMMIT.md` o `.claude/CLAUDE_PRE_COMMIT_SONAR.md`
162
+ - Usa template de prompt desde `.claude/CLAUDE_ANALYSIS_PROMPT*.md`
163
+ - Lee las pautas desde `.claude/CLAUDE_PRE_COMMIT*.md`
148
164
  - Incluye el diff completo para archivos nuevos
149
165
  - Muestra solo cambios para archivos existentes
150
166
  5. **Envía a Claude CLI para revisión**
151
- 6. **Procesa respuesta JSON**:
167
+ 6. **Procesa respuesta JSON estructurada**:
168
+ - blockingIssues siempre como objetos con localización precisa
152
169
  - En modo estándar: evalúa `approved`, `score`, `recommendations`
153
170
  - En modo SonarQube: verifica `QUALITY_GATE`, muestra métricas y issues por severidad
154
171
  7. **Decisión final**:
155
- - Si hay problemas críticos o quality gate falla commit bloqueado
172
+ - Si hay problemas críticos genera prompt AI de resolución y bloquea commit
156
173
  - Si todo está bien → commit procede
157
174
 
175
+ #### 🤖 Generación de Prompt de Resolución AI
176
+
177
+ Cuando se detectan problemas críticos:
178
+
179
+ - Se genera automáticamente un archivo `claude_resolution_prompt.md`
180
+ - Contiene información estructurada y AI-friendly de todos los issues
181
+ - Incluye localización precisa (archivo, línea, método)
182
+ - Puede copiarse a otra instancia de Claude para resolución automática
183
+
158
184
  ### Hook prepare-commit-msg (Generación automática de mensajes)
159
185
 
160
186
  1. **Se activa cuando el mensaje es**:
@@ -172,7 +198,8 @@ Si no existe un `.gitignore`, se creará uno nuevo. Si ya existe, las entradas s
172
198
 
173
199
  ### Características adicionales
174
200
 
175
- - **Auto-actualización**: Los hooks se sincronizan automáticamente con las versiones en `git-hooks/`
201
+ - **Auto-actualización**: Verificación automática de versiones antes de cada commit con prompt interactivo para actualizar
202
+ - **Comando update**: `claude-hooks update` para actualizar manualmente a la última versión
176
203
  - **Modo debug**: `DEBUG=1 git commit` guarda respuestas en `debug-claude-response.json`
177
204
  - **Configuración persistente**: Guarda preferencias en `.claude-analysis-mode`
178
205
  - **Validación de dependencias**: Verifica que Claude CLI esté autenticado antes de ejecutar
@@ -232,11 +259,13 @@ unset CLAUDE_ANALYSIS_MODE
232
259
  #### 📋 Diferencias entre Modos
233
260
 
234
261
  **Modo Estándar**:
262
+
235
263
  - Análisis con puntuación del 1-10
236
264
  - Recomendaciones detalladas por categoría
237
265
  - Formato tradicional fácil de leer
238
266
 
239
267
  **Modo SonarQube**:
268
+
240
269
  - Quality Gate (PASSED/FAILED)
241
270
  - Métricas: Reliability, Security, Maintainability
242
271
  - Issues clasificados por severidad (Blocker, Critical, Major, Minor, Info)
@@ -265,15 +294,111 @@ DEBUG=1 git commit -m "mensaje"
265
294
 
266
295
  ## 📊 Formato de Respuesta
267
296
 
268
- Claude responde con un JSON que incluye:
297
+ Claude responde con un JSON estructurado que incluye:
298
+
299
+ - `approved`: Si el commit es aprobado (boolean)
300
+ - `score`: Puntuación del 1-10 (integer)
301
+ - `blockingIssues`: Array de objetos con problemas críticos:
302
+ - `description`: Descripción del problema
303
+ - `file`: Archivo afectado
304
+ - `line`: Línea del problema
305
+ - `method`: Método o clase afectada
306
+ - `severity`: Severidad (critical, high, etc.)
307
+ - `recommendations`: Array de strings con sugerencias no bloqueantes
308
+ - `details`: Objeto con comentarios por categoría (security, performance, etc.)
309
+
310
+ ### Ejemplos de Respuestas con Problemas Críticos
311
+
312
+ #### Modo Estándar - Commit Bloqueado
313
+
314
+ ```json
315
+ {
316
+ "approved": false,
317
+ "score": 3,
318
+ "blockingIssues": [
319
+ {
320
+ "description": "SQL Injection vulnerability: concatenación directa de strings en query",
321
+ "file": "src/main/java/UserRepository.java",
322
+ "line": 45,
323
+ "method": "findUserByName",
324
+ "severity": "critical"
325
+ },
326
+ {
327
+ "description": "Posible NullPointerException: variable 'user' no validada",
328
+ "file": "src/main/java/UserService.java",
329
+ "line": 78,
330
+ "method": "processUser",
331
+ "severity": "high"
332
+ }
333
+ ],
334
+ "recommendations": [
335
+ "Usar PreparedStatement en lugar de concatenación de strings",
336
+ "Agregar validación de null antes de acceder a propiedades"
337
+ ]
338
+ }
339
+ ```
340
+
341
+ #### Modo SonarQube - Quality Gate Failed
342
+
343
+ ```json
344
+ {
345
+ "QUALITY_GATE": "FAILED",
346
+ "approved": false,
347
+ "score": 4,
348
+ "metrics": {
349
+ "reliability": "D",
350
+ "security": "E",
351
+ "maintainability": "C"
352
+ },
353
+ "issues": {
354
+ "blocker": 1,
355
+ "critical": 2,
356
+ "major": 3,
357
+ "minor": 1
358
+ },
359
+ "blockingIssues": [
360
+ {
361
+ "description": "Security Hotspot: Hardcoded credentials detected",
362
+ "file": "src/main/resources/application.yml",
363
+ "line": 23,
364
+ "method": "datasource.password",
365
+ "severity": "blocker"
366
+ },
367
+ {
368
+ "description": "Resource leak: Connection not closed in finally block",
369
+ "file": "src/main/java/DatabaseUtil.java",
370
+ "line": 112,
371
+ "method": "executeQuery",
372
+ "severity": "critical"
373
+ }
374
+ ],
375
+ "details": [
376
+ {
377
+ "severity": "BLOCKER",
378
+ "type": "VULNERABILITY",
379
+ "file": "src/main/resources/application.yml",
380
+ "line": 23,
381
+ "message": "Never store passwords in plain text",
382
+ "rule": "java:S2068"
383
+ }
384
+ ]
385
+ }
386
+ ```
387
+
388
+ Cuando se detectan estos problemas críticos, se genera automáticamente un archivo `claude_resolution_prompt.md` con toda la información necesaria para que otra instancia de Claude pueda resolver los issues de forma eficiente.
269
389
 
270
- - `approved`: Si el commit es aprobado
271
- - `score`: Puntuación del 1-10
272
- - `commitMessage`: Mensaje de commit generado (type, title, body)
273
- - `recommendations`: Recomendaciones de mejora
274
- - `blockingIssues`: Problemas que bloquean el commit
390
+ ## 🔄 Actualización y Gestión
275
391
 
276
- ## 🔄 Desactivar/Activar
392
+ ### Actualizar a la última versión
393
+
394
+ ```bash
395
+ # Actualizar manualmente a la última versión
396
+ claude-hooks update
397
+ ```
398
+
399
+ 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.
400
+
401
+ ### Desactivar/Activar hooks
277
402
 
278
403
  ```bash
279
404
  # Desactivar todos los hooks
@@ -383,11 +508,24 @@ Este problema suele ocurrir por conflictos de configuración de line endings:
383
508
 
384
509
  ## 📝 Personalización
385
510
 
386
- Puedes modificar las pautas de evaluación editando los archivos en el directorio `.claude/`:
387
- - `.claude/CLAUDE_PRE_COMMIT.md` - Para el modo estándar
388
- - `.claude/CLAUDE_PRE_COMMIT_SONAR.md` - Para el modo SonarQube
511
+ ### Archivos de Configuración en `.claude/`
512
+
513
+ #### Pautas de Evaluación
514
+
515
+ - **`CLAUDE_PRE_COMMIT.md`** - Criterios de evaluación para modo estándar
516
+ - **`CLAUDE_PRE_COMMIT_SONAR.md`** - Criterios para modo SonarQube
517
+
518
+ #### Templates de Prompts
389
519
 
390
- Estos archivos son específicos de tu proyecto y puedes adaptarlos a los estándares de tu equipo.
520
+ - **`CLAUDE_ANALYSIS_PROMPT.md`** - Estructura del prompt de análisis estándar
521
+ - **`CLAUDE_ANALYSIS_PROMPT_SONAR.md`** - Estructura del prompt SonarQube
522
+ - **`CLAUDE_RESOLUTION_PROMPT.md`** - Template para generar prompts de resolución
523
+
524
+ Todos estos archivos son personalizables y específicos de tu proyecto. Puedes:
525
+
526
+ - Modificar los criterios de evaluación según estándares del equipo
527
+ - Ajustar la estructura de los prompts para obtener respuestas más precisas
528
+ - Personalizar el formato de salida del prompt de resolución
391
529
 
392
530
  ## 🔄 Arquitectura del Sistema
393
531
 
@@ -562,11 +700,3 @@ git commit -m "auto"
562
700
  3. **Commit** tus cambios: `git commit -m "feat: nueva funcionalidad"`
563
701
  4. **Push** al branch: `git push origin feature/nueva-funcionalidad`
564
702
  5. **Abrir Pull Request**
565
-
566
- ### Roadmap
567
-
568
- - [ ] Soporte para más lenguajes (Python, JavaScript, etc.)
569
- - [ ] Configuración más granular por proyecto
570
- - [ ] Integration con IDEs populares
571
- - [ ] Métricas de uso y performance
572
- - [ ] Tests automatizados
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,14 @@ 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
+ const skipAuth = args.includes('--skip-auth');
343
+
344
+ if (isForce) {
345
+ info('Instalando Claude Git Hooks (modo force)...');
346
+ } else {
347
+ info('Instalando Claude Git Hooks...');
348
+ }
262
349
 
263
350
  // Solicitar contraseña sudo al inicio si es necesario
264
351
  let sudoPassword = null;
@@ -278,7 +365,7 @@ async function install(args) {
278
365
  }
279
366
 
280
367
  // Verificar dependencias con instalación automática
281
- await checkAndInstallDependencies(sudoPassword);
368
+ await checkAndInstallDependencies(sudoPassword, skipAuth);
282
369
 
283
370
  const templatesPath = getTemplatesPath();
284
371
  const hooksPath = '.git/hooks';
@@ -308,6 +395,16 @@ async function install(args) {
308
395
  success(`${hook} instalado`);
309
396
  });
310
397
 
398
+ // Copiar script de verificación de versión
399
+ const checkVersionSource = path.join(templatesPath, 'check-version.sh');
400
+ const checkVersionDest = path.join(hooksPath, 'check-version.sh');
401
+
402
+ if (fs.existsSync(checkVersionSource)) {
403
+ fs.copyFileSync(checkVersionSource, checkVersionDest);
404
+ fs.chmodSync(checkVersionDest, '755');
405
+ success('Script de verificación de versión instalado');
406
+ }
407
+
311
408
  // Crear directorio .claude si no existe
312
409
  const claudeDir = '.claude';
313
410
  if (!fs.existsSync(claudeDir)) {
@@ -315,15 +412,26 @@ async function install(args) {
315
412
  success('.claude directory created');
316
413
  }
317
414
 
318
- // Copiar archivos de pautas a .claude si no existen
319
- const guidelines = ['CLAUDE_PRE_COMMIT.md', 'CLAUDE_PRE_COMMIT_SONAR.md'];
320
- guidelines.forEach(guideline => {
321
- const destPath = path.join(claudeDir, guideline);
322
- if (!fs.existsSync(destPath)) {
323
- const sourcePath = path.join(templatesPath, guideline);
415
+ // Copiar archivos de pautas y prompts a .claude
416
+ const claudeFiles = [
417
+ 'CLAUDE_PRE_COMMIT.md',
418
+ 'CLAUDE_PRE_COMMIT_SONAR.md',
419
+ 'CLAUDE_ANALYSIS_PROMPT.md',
420
+ 'CLAUDE_ANALYSIS_PROMPT_SONAR.md',
421
+ 'CLAUDE_RESOLUTION_PROMPT.md'
422
+ ];
423
+
424
+ claudeFiles.forEach(file => {
425
+ const destPath = path.join(claudeDir, file);
426
+ const sourcePath = path.join(templatesPath, file);
427
+
428
+ // En modo force o si no existe, copiar el archivo
429
+ if (isForce || !fs.existsSync(destPath)) {
324
430
  if (fs.existsSync(sourcePath)) {
325
431
  fs.copyFileSync(sourcePath, destPath);
326
- success(`${guideline} creado en .claude/`);
432
+ success(`${file} instalado en .claude/`);
433
+ } else {
434
+ warning(`Archivo de template no encontrado: ${file}`);
327
435
  }
328
436
  }
329
437
  });
@@ -342,7 +450,7 @@ async function install(args) {
342
450
  }
343
451
 
344
452
  // Verificar dependencias completas (como setup-wsl.sh)
345
- async function checkAndInstallDependencies(sudoPassword = null) {
453
+ async function checkAndInstallDependencies(sudoPassword = null, skipAuth = false) {
346
454
  info('Verificando dependencias del sistema...');
347
455
 
348
456
  // Verificar Node.js
@@ -420,14 +528,18 @@ async function checkAndInstallDependencies(sudoPassword = null) {
420
528
  if (missingTools.length === 0) {
421
529
  success('Herramientas Unix estándar verificadas');
422
530
  } else {
423
- error(`Faltan herramientas Unix estándar: ${missingTools.join(', ')}`);
531
+ error(`Faltan herramientas Unix estándar: ${missingTools.join(', ')}. Reintenta instalación en una consola Ubuntu`);
424
532
  }
425
533
 
426
534
  // Verificar e instalar Claude CLI
427
535
  await checkAndInstallClaude();
428
536
 
429
- // Verificar autenticación de Claude
430
- await checkClaudeAuth();
537
+ // Verificar autenticación de Claude (si no se salta)
538
+ if (!skipAuth) {
539
+ await checkClaudeAuth();
540
+ } else {
541
+ warning('Saltando verificación de autenticación de Claude (--skip-auth)');
542
+ }
431
543
 
432
544
  // Limpiar contraseña de memoria
433
545
  sudoPassword = null;
@@ -523,7 +635,8 @@ function updateGitignore() {
523
635
  '# Claude Git Hooks',
524
636
  '.claude/',
525
637
  '.claude-analysis-mode',
526
- 'debug-claude-response.json'
638
+ 'debug-claude-response.json',
639
+ 'claude_resolution_prompt.md'
527
640
  ];
528
641
 
529
642
  let gitignoreContent = '';
@@ -785,6 +898,87 @@ function setMode(mode) {
785
898
  }
786
899
  }
787
900
 
901
+ // Función para comparar versiones usando el script compartido
902
+ function compareVersions(v1, v2) {
903
+ try {
904
+ // Usar el script compartido para mantener consistencia
905
+ const result = execSync(`bash -c 'source "${getTemplatesPath()}/check-version.sh" && compare_versions "${v1}" "${v2}"; echo $?'`, { encoding: 'utf8' }).trim();
906
+ const exitCode = parseInt(result);
907
+
908
+ // Convertir los códigos de retorno del script bash a valores JS
909
+ if (exitCode === 0) return 0; // iguales
910
+ if (exitCode === 1) return 1; // v1 > v2
911
+ if (exitCode === 2) return -1; // v1 < v2
912
+
913
+ // Fallback: comparación simple si el script falla
914
+ if (v1 === v2) return 0;
915
+ const sorted = [v1, v2].sort((a, b) => {
916
+ const aParts = a.split('.').map(Number);
917
+ const bParts = b.split('.').map(Number);
918
+ for (let i = 0; i < Math.max(aParts.length, bParts.length); i++) {
919
+ const aPart = aParts[i] || 0;
920
+ const bPart = bParts[i] || 0;
921
+ if (aPart !== bPart) return aPart - bPart;
922
+ }
923
+ return 0;
924
+ });
925
+ return v1 === sorted[1] ? 1 : -1;
926
+ } catch (e) {
927
+ // Si falla todo, usar comparación simple
928
+ if (v1 === v2) return 0;
929
+ return v1 > v2 ? 1 : -1;
930
+ }
931
+ }
932
+
933
+ // Comando update - actualizar a la última versión
934
+ async function update() {
935
+ info('Verificando última versión disponible...');
936
+
937
+ try {
938
+ const currentVersion = require('../package.json').version;
939
+ const latestVersion = await getLatestVersion('claude-git-hooks');
940
+
941
+ const comparison = compareVersions(currentVersion, latestVersion);
942
+
943
+ if (comparison === 0) {
944
+ success(`Ya tienes la última versión instalada (${currentVersion})`);
945
+ return;
946
+ } else if (comparison > 0) {
947
+ info(`Estás usando una versión de desarrollo (${currentVersion})`);
948
+ info(`Última versión publicada: ${latestVersion}`);
949
+ success(`Ya tienes la última versión instalada (${currentVersion})`);
950
+ return;
951
+ }
952
+
953
+ info(`Versión actual: ${currentVersion}`);
954
+ info(`Versión disponible: ${latestVersion}`);
955
+
956
+ // Actualizar el paquete
957
+ info('Actualizando claude-git-hooks...');
958
+ try {
959
+ execSync('npm install -g claude-git-hooks@latest', { stdio: 'inherit' });
960
+ success(`Actualizado exitosamente a la versión ${latestVersion}`);
961
+
962
+ // Reinstalar hooks con la nueva versión
963
+ info('Reinstalando hooks con la nueva versión...');
964
+ await install(['--force']);
965
+
966
+ } catch (updateError) {
967
+ error('Error al actualizar. Intenta ejecutar: npm install -g claude-git-hooks@latest');
968
+ }
969
+ } catch (e) {
970
+ warning('No se pudo verificar la última versión disponible');
971
+ warning('Intentando actualizar de todas formas...');
972
+ try {
973
+ execSync('npm install -g claude-git-hooks@latest', { stdio: 'inherit' });
974
+ success('Actualización completada');
975
+ await install(['--force']);
976
+ } catch (updateError) {
977
+ error('Error al actualizar: ' + updateError.message);
978
+ }
979
+ }
980
+ }
981
+
788
982
  // Comando help
789
983
  function showHelp() {
790
984
  console.log(`
@@ -793,7 +987,10 @@ Claude Git Hooks - Análisis de código y mensajes automáticos con Claude CLI
793
987
  Uso: claude-hooks <comando> [opciones]
794
988
 
795
989
  Comandos:
796
- install Instala los hooks en el repositorio actual
990
+ install [opciones] Instala los hooks en el repositorio actual
991
+ --force Reinstala aunque ya existan
992
+ --skip-auth Salta la verificación de autenticación de Claude
993
+ update Actualiza a la última versión disponible
797
994
  uninstall Desinstala los hooks del repositorio
798
995
  enable [hook] Habilita hooks (todos o uno específico)
799
996
  disable [hook] Deshabilita hooks (todos o uno específico)
@@ -807,6 +1004,8 @@ Hooks disponibles:
807
1004
 
808
1005
  Ejemplos:
809
1006
  claude-hooks install # Instala todos los hooks
1007
+ claude-hooks install --skip-auth # Instala sin verificar autenticación
1008
+ claude-hooks update # Actualiza a la última versión
810
1009
  claude-hooks set-mode sonar # Cambiar a modo SonarQube
811
1010
  claude-hooks set-mode standard # Cambiar a modo estándar
812
1011
  claude-hooks disable pre-commit # Deshabilita solo pre-commit
@@ -826,6 +1025,9 @@ async function main() {
826
1025
  case 'install':
827
1026
  await install(args.slice(1));
828
1027
  break;
1028
+ case 'update':
1029
+ await update();
1030
+ break;
829
1031
  case 'uninstall':
830
1032
  uninstall();
831
1033
  break;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-git-hooks",
3
- "version": "1.2.4",
3
+ "version": "1.4.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": {
@@ -0,0 +1,44 @@
1
+ Eres un revisor de código senior con experiencia en proyectos Spring Boot empresariales.
2
+
3
+ Tu tarea es analizar los siguientes cambios de código y responder exclusivamente con un JSON válido, **sin ningún texto fuera del JSON**, sin introducciones ni explicaciones adicionales.
4
+
5
+ ⚠️ La respuesta debe ser un objeto JSON con esta estructura exacta:
6
+
7
+ ```json
8
+ {
9
+ "approved": true, // true si el commit puede aceptarse, false si debe bloquearse
10
+ "score": 1-10, // calificación de calidad (entero)
11
+ "blockingIssues": [ // SIEMPRE array de objetos, nunca strings
12
+ {
13
+ "description": "texto descriptivo del problema",
14
+ "file": "path/to/file.java",
15
+ "line": 123, // línea donde se detectó el problema
16
+ "method": "methodName", // método o clase afectada
17
+ "severity": "critical" // critical, high, medium, low
18
+ }
19
+ ],
20
+ "recommendations": [ // sugerencias no bloqueantes (array de strings)
21
+ "mejora 1",
22
+ "mejora 2"
23
+ ],
24
+ "details": {
25
+ "security": [ // comentarios específicos por área
26
+ "observación sobre seguridad"
27
+ ],
28
+ "architecture": [ "..." ],
29
+ "performance": [ "..." ],
30
+ "maintainability": [ "..." ]
31
+ }
32
+ }
33
+ ```
34
+
35
+ ✅ Importante:
36
+ - blockingIssues SIEMPRE debe ser un array de objetos, NUNCA un array de strings
37
+ - Cada blockingIssue DEBE incluir: description, file, line, method, severity
38
+ - Usa listas vacías si no hay issues (ejemplo: `"blockingIssues": []`)
39
+ - No uses texto introductorio o explicaciones fuera del JSON
40
+ - No omitas claves aunque estén vacías
41
+ - Sé directo, claro y profesional en tus observaciones
42
+ - Los problemas críticos deben incluir contexto suficiente para que otra IA pueda resolverlos
43
+
44
+ A continuación se detallan las pautas y los cambios:
@@ -0,0 +1,61 @@
1
+ Eres un revisor de código senior con experiencia en proyectos Spring Boot empresariales y métricas SonarQube.
2
+
3
+ Tu tarea es analizar los siguientes cambios de código desde una perspectiva de calidad similar a SonarQube y responder exclusivamente con un JSON válido, **sin ningún texto fuera del JSON**, sin introducciones ni explicaciones adicionales.
4
+
5
+ ⚠️ La respuesta debe ser un objeto JSON con esta estructura exacta de SonarQube:
6
+
7
+ ```json
8
+ {
9
+ "QUALITY_GATE": "PASSED", // PASSED o FAILED
10
+ "approved": true, // compatibilidad con modo estándar
11
+ "score": 1-10, // calificación general
12
+ "metrics": {
13
+ "reliability": "A", // A, B, C, D, E
14
+ "security": "A", // A, B, C, D, E
15
+ "maintainability": "A", // A, B, C, D, E
16
+ "coverage": 85, // porcentaje de cobertura estimado
17
+ "duplications": 2, // porcentaje de duplicación estimado
18
+ "complexity": 15 // complejidad ciclomática estimada
19
+ },
20
+ "issues": {
21
+ "blocker": 0, // cantidad de issues bloqueantes
22
+ "critical": 0, // cantidad de issues críticos
23
+ "major": 0, // cantidad de issues mayores
24
+ "minor": 0, // cantidad de issues menores
25
+ "info": 0 // cantidad de issues informativos
26
+ },
27
+ "details": [ // detalle de cada issue encontrado con localización
28
+ {
29
+ "severity": "CRITICAL", // BLOCKER, CRITICAL, MAJOR, MINOR, INFO
30
+ "type": "BUG", // BUG, VULNERABILITY, CODE_SMELL
31
+ "file": "src/main/java/Example.java",
32
+ "line": 42,
33
+ "method": "processData", // método o clase afectada
34
+ "message": "Null pointer dereference possible",
35
+ "rule": "java:S2259" // regla de SonarQube (opcional)
36
+ }
37
+ ],
38
+ "blockingIssues": [ // SIEMPRE array de objetos con issues BLOCKER y CRITICAL
39
+ {
40
+ "description": "texto descriptivo del problema",
41
+ "file": "path/to/file.java",
42
+ "line": 123,
43
+ "method": "methodName",
44
+ "severity": "critical" // solo "blocker" o "critical" aquí
45
+ }
46
+ ],
47
+ "securityHotspots": 0 // cantidad de security hotspots
48
+ }
49
+ ```
50
+
51
+ ✅ Importante:
52
+ - blockingIssues SIEMPRE debe ser un array de objetos, NUNCA un array de strings
53
+ - blockingIssues debe contener TODOS los issues BLOCKER y CRITICAL del array details
54
+ - Cada blockingIssue DEBE incluir: description, file, line, method, severity
55
+ - El QUALITY_GATE debe ser FAILED si hay issues BLOCKER o CRITICAL
56
+ - Los ratings (A-E) deben reflejar la calidad real del código
57
+ - Para cada issue en details, SIEMPRE incluye file, line y method para localización precisa
58
+ - No uses texto fuera del JSON
59
+ - Sé preciso con las localizaciones para facilitar la resolución automatizada
60
+
61
+ A continuación se detallan las pautas y los cambios:
@@ -0,0 +1,46 @@
1
+ # AI Resolution Assistant Prompt
2
+
3
+ You are an expert code reviewer tasked with resolving critical issues identified in a code review. Your goal is to provide precise, minimal, and efficient fixes.
4
+
5
+ ## Context
6
+ Repository: {{REPO_NAME}}
7
+ Branch: {{BRANCH_NAME}}
8
+ Commit: {{COMMIT_SHA}}
9
+ Files analyzed: {{FILE_COUNT}}
10
+ Analysis mode: {{ANALYSIS_MODE}}
11
+
12
+ ## Critical Issues to Resolve
13
+
14
+ {{BLOCKING_ISSUES}}
15
+
16
+ ## Affected Files Content
17
+
18
+ {{FILE_CONTENTS}}
19
+
20
+ ## Instructions
21
+
22
+ 1. For each critical issue:
23
+ - Navigate to the exact file and line number specified
24
+ - Understand the context and the problem
25
+ - Apply the minimal fix required
26
+ - Ensure the fix doesn't introduce new issues
27
+
28
+ 2. Fix requirements:
29
+ - Make the smallest possible change to resolve the issue
30
+ - Maintain existing code style and conventions
31
+ - Don't refactor unrelated code
32
+ - Preserve all existing functionality
33
+
34
+ 3. Output format:
35
+ - Provide exact code changes using diff format
36
+ - Include file path, line numbers, and context
37
+ - Explain each fix briefly (one line max)
38
+
39
+ ## Fix Priority
40
+ 1. Security vulnerabilities (highest)
41
+ 2. Null pointer / runtime errors
42
+ 3. Logic errors
43
+ 4. Performance issues
44
+ 5. Code quality issues (lowest)
45
+
46
+ Begin fixing the issues in order of severity.
@@ -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,103 @@ 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
+
59
+ # Función para generar prompt de resolución AI-friendly
60
+ generate_resolution_prompt() {
61
+ local RESOLUTION_FILE="./claude_resolution_prompt.md"
62
+ local RESOLUTION_TEMPLATE=".claude/CLAUDE_RESOLUTION_PROMPT.md"
63
+
64
+ if [ ! -f "$RESOLUTION_TEMPLATE" ]; then
65
+ warning "No se encontró template de resolución: $RESOLUTION_TEMPLATE"
66
+ return
67
+ fi
68
+
69
+ # Obtener información del contexto
70
+ local REPO_NAME=$(basename $(git rev-parse --show-toplevel))
71
+ local BRANCH_NAME=$(git rev-parse --abbrev-ref HEAD)
72
+ local COMMIT_SHA="pending"
73
+ local FILE_COUNT=$(echo "$JAVA_FILES" | wc -l)
74
+
75
+ # Formatear los blocking issues para el prompt de resolución
76
+ local ISSUES_FORMATTED=""
77
+ local issue_num=1
78
+
79
+ # Parsear cada blocking issue como objeto JSON
80
+ echo "$JSON_RESPONSE" | jq -c '.blockingIssues[]?' 2>/dev/null | while IFS= read -r issue; do
81
+ if [ -n "$issue" ]; then
82
+ local desc=$(echo "$issue" | jq -r '.description')
83
+ local file=$(echo "$issue" | jq -r '.file')
84
+ local line=$(echo "$issue" | jq -r '.line')
85
+ local method=$(echo "$issue" | jq -r '.method')
86
+ local severity=$(echo "$issue" | jq -r '.severity')
87
+
88
+ ISSUES_FORMATTED="${ISSUES_FORMATTED}### Issue #${issue_num} [${severity^^}]\n"
89
+ ISSUES_FORMATTED="${ISSUES_FORMATTED}**Description:** ${desc}\n"
90
+ ISSUES_FORMATTED="${ISSUES_FORMATTED}**Location:** ${file}:${line}\n"
91
+ ISSUES_FORMATTED="${ISSUES_FORMATTED}**Method/Class:** ${method}\n\n"
92
+ issue_num=$((issue_num + 1))
93
+ fi
94
+ done
95
+
96
+ # Generar el prompt desde el template
97
+ cp "$RESOLUTION_TEMPLATE" "$RESOLUTION_FILE"
98
+
99
+ # Reemplazar placeholders
100
+ sed -i "s|{{REPO_NAME}}|$REPO_NAME|g" "$RESOLUTION_FILE"
101
+ sed -i "s|{{BRANCH_NAME}}|$BRANCH_NAME|g" "$RESOLUTION_FILE"
102
+ sed -i "s|{{COMMIT_SHA}}|$COMMIT_SHA|g" "$RESOLUTION_FILE"
103
+ sed -i "s|{{FILE_COUNT}}|$FILE_COUNT|g" "$RESOLUTION_FILE"
104
+ sed -i "s|{{ANALYSIS_MODE}}|$ANALYSIS_MODE|g" "$RESOLUTION_FILE"
105
+
106
+ # Agregar los issues formateados
107
+ sed -i "/{{BLOCKING_ISSUES}}/r /dev/stdin" "$RESOLUTION_FILE" <<< "$ISSUES_FORMATTED"
108
+ sed -i "s|{{BLOCKING_ISSUES}}||g" "$RESOLUTION_FILE"
109
+
110
+ # Agregar contenido de archivos afectados
111
+ local FILES_CONTENT=""
112
+ echo "$JSON_RESPONSE" | jq -r '.blockingIssues[].file' 2>/dev/null | sort -u | while IFS= read -r file; do
113
+ if [ -f "$file" ]; then
114
+ FILES_CONTENT="${FILES_CONTENT}### File: $file\n\n\`\`\`\n"
115
+ FILES_CONTENT="${FILES_CONTENT}$(cat "$file")\n"
116
+ FILES_CONTENT="${FILES_CONTENT}\`\`\`\n\n"
117
+ fi
118
+ done
119
+
120
+ sed -i "/{{FILE_CONTENTS}}/r /dev/stdin" "$RESOLUTION_FILE" <<< "$FILES_CONTENT"
121
+ sed -i "s|{{FILE_CONTENTS}}||g" "$RESOLUTION_FILE"
122
+
123
+ echo
124
+ echo -e "${YELLOW}=== PROMPT DE RESOLUCIÓN AI GENERADO ===${NC}"
125
+ echo -e "${GREEN}Se ha generado un prompt AI-friendly en: ${BLUE}$RESOLUTION_FILE${NC}"
126
+ echo -e "${YELLOW}Copia este archivo a una nueva instancia de Claude para resolver los problemas automáticamente.${NC}"
127
+ echo
128
+ }
129
+
32
130
  # Detectar qué archivo de pautas usar
33
131
  # Prioridad: variable de entorno > archivo > pregunta interactiva
34
132
  if [ -n "$CLAUDE_ANALYSIS_MODE" ]; then
@@ -65,9 +163,11 @@ if [ "$ANALYSIS_MODE" != "standard" ] && [ "$ANALYSIS_MODE" != "sonar" ]; then
65
163
  if [ "$REPLY" = "2" ]; then
66
164
  ANALYSIS_MODE="sonar"
67
165
  GUIDELINES_FILE=".claude/CLAUDE_PRE_COMMIT_SONAR.md"
166
+ PROMPT_TEMPLATE=".claude/CLAUDE_ANALYSIS_PROMPT_SONAR.md"
68
167
  else
69
168
  ANALYSIS_MODE="standard"
70
169
  GUIDELINES_FILE=".claude/CLAUDE_PRE_COMMIT.md"
170
+ PROMPT_TEMPLATE=".claude/CLAUDE_ANALYSIS_PROMPT.md"
71
171
  fi
72
172
 
73
173
  # Guardar preferencia para futuros commits
@@ -79,12 +179,23 @@ else
79
179
  # Usar modo guardado
80
180
  if [ "$ANALYSIS_MODE" = "sonar" ]; then
81
181
  GUIDELINES_FILE=".claude/CLAUDE_PRE_COMMIT_SONAR.md"
182
+ PROMPT_TEMPLATE=".claude/CLAUDE_ANALYSIS_PROMPT_SONAR.md"
82
183
  log "Usando modo de análisis: SonarQube"
83
184
  else
84
185
  GUIDELINES_FILE=".claude/CLAUDE_PRE_COMMIT.md"
186
+ PROMPT_TEMPLATE=".claude/CLAUDE_ANALYSIS_PROMPT.md"
85
187
  log "Usando modo de análisis: Estándar"
86
188
  fi
87
189
  fi
190
+
191
+ # Verificar que el template de prompt existe
192
+ if [ ! -f "$PROMPT_TEMPLATE" ]; then
193
+ error "No se encontró el template de prompt: $PROMPT_TEMPLATE"
194
+ error "Los archivos de configuración de Claude parecen estar incompletos."
195
+ error "Por favor, reinstala claude-git-hooks ejecutando:"
196
+ error " claude-hooks install --force"
197
+ exit 1
198
+ fi
88
199
  # Función para limpiar archivos temporales
89
200
  cleanup() {
90
201
  rm -rf "$TEMP_DIR"
@@ -113,9 +224,10 @@ fi
113
224
 
114
225
  # Verificar si existe el archivo de pautas
115
226
  if [ ! -f "$GUIDELINES_FILE" ]; then
116
- warning "No se encontró el archivo de pautas: $GUIDELINES_FILE"
117
- warning "Saltando análisis de código"
118
- exit 0
227
+ error "No se encontró el archivo de pautas: $GUIDELINES_FILE"
228
+ error "Por favor, reinstala claude-git-hooks ejecutando:"
229
+ error " claude-hooks install --force"
230
+ exit 1
119
231
  fi
120
232
 
121
233
  log "Archivos Java/config a revisar: $(echo "$JAVA_FILES" | wc -l)"
@@ -123,42 +235,8 @@ log "Archivos Java/config a revisar: $(echo "$JAVA_FILES" | wc -l)"
123
235
  # Construir el prompt para análisis de código
124
236
  PROMPT_FILE="$TEMP_DIR/code_review_prompt.txt"
125
237
 
126
- cat > "$PROMPT_FILE" << 'EOF'
127
- Eres un revisor de código senior con experiencia en proyectos Spring Boot empresariales.
128
-
129
- Tu tarea es analizar los siguientes cambios de código y responder exclusivamente con un JSON válido, **sin ningún texto fuera del JSON**, sin introducciones ni explicaciones adicionales.
130
-
131
- ⚠️ La respuesta debe ser un objeto JSON con esta estructura exacta:
132
-
133
- {
134
- "approved": true, // true si el commit puede aceptarse, false si debe bloquearse
135
- "score": 1-10, // calificación de calidad (entero)
136
- "blockingIssues": [ // lista de errores críticos (vacía si no hay)
137
- "texto descriptivo 1",
138
- "texto descriptivo 2"
139
- ],
140
- "recommendations": [ // sugerencias no bloqueantes
141
- "mejora 1",
142
- "mejora 2"
143
- ],
144
- "details": {
145
- "security": [ // comentarios específicos por área
146
- "observación sobre seguridad"
147
- ],
148
- "architecture": [ "..." ],
149
- "performance": [ "..." ],
150
- "maintainability": [ "..." ]
151
- }
152
- }
153
-
154
- ✅ Importante:
155
- - Usa listas aunque estén vacías (por ejemplo `"blockingIssues": []`)
156
- - No uses texto introductorio o explicaciones fuera del JSON
157
- - No omitas claves aunque estén vacías
158
- - Sé directo, claro y profesional en tus observaciones
159
-
160
- A continuación se detallan las pautas y los cambios:
161
- EOF
238
+ # Copiar el template de prompt
239
+ cat "$PROMPT_TEMPLATE" > "$PROMPT_FILE"
162
240
 
163
241
  # Agregar las pautas
164
242
  echo "=== PAUTAS DE EVALUACIÓN ===" >> "$PROMPT_FILE"
@@ -230,7 +308,14 @@ if $CLAUDE_CLI < "$PROMPT_FILE" > "$RESPONSE_FILE" 2>&1; then
230
308
  APPROVED=$(echo "$JSON_RESPONSE" | jq -r '.approved // false')
231
309
  SCORE=$(echo "$JSON_RESPONSE" | jq -r '.score // 0')
232
310
  RECOMMENDATIONS=$(echo "$JSON_RESPONSE" | jq -r '.recommendations[]?' 2>/dev/null | sed '/^$/d')
233
- BLOCKING_ISSUES=$(echo "$JSON_RESPONSE" | jq -r '.blockingIssues[]?' 2>/dev/null | sed '/^$/d')
311
+
312
+ # Parsear blockingIssues como objetos y extraer las descripciones
313
+ BLOCKING_ISSUES=""
314
+ BLOCKING_COUNT=$(echo "$JSON_RESPONSE" | jq '.blockingIssues | length' 2>/dev/null || echo "0")
315
+
316
+ if [ "$BLOCKING_COUNT" -gt 0 ]; then
317
+ BLOCKING_ISSUES=$(echo "$JSON_RESPONSE" | jq -r '.blockingIssues[].description' 2>/dev/null | sed '/^$/d')
318
+ fi
234
319
 
235
320
  # Verificar si estamos en modo SonarQube
236
321
  QUALITY_GATE=$(echo "$JSON_RESPONSE" | jq -r '.QUALITY_GATE // ""' 2>/dev/null)
@@ -329,6 +414,9 @@ if $CLAUDE_CLI < "$PROMPT_FILE" > "$RESPONSE_FILE" 2>&1; then
329
414
  echo
330
415
  echo "=== PROBLEMAS CRÍTICOS ==="
331
416
  echo "$BLOCKING_ISSUES" | sed 's/^/- /'
417
+
418
+ # Generar prompt de resolución AI-friendly
419
+ generate_resolution_prompt
332
420
  fi
333
421
  exit 1
334
422
  fi