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 +52 -0
- package/README.md +158 -28
- package/bin/claude-hooks +217 -15
- package/package.json +1 -1
- package/templates/CLAUDE_ANALYSIS_PROMPT.md +44 -0
- package/templates/CLAUDE_ANALYSIS_PROMPT_SONAR.md +61 -0
- package/templates/CLAUDE_RESOLUTION_PROMPT.md +46 -0
- package/templates/check-version.sh +266 -0
- package/templates/pre-commit +128 -40
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
|
|
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. **`.
|
|
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
|
-
|
|
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
|
-
-
|
|
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
|
|
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**:
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
387
|
-
|
|
388
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
319
|
-
const
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
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(`${
|
|
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
|
-
|
|
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
|
|
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
|
@@ -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
|
package/templates/pre-commit
CHANGED
|
@@ -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
|
-
|
|
117
|
-
|
|
118
|
-
|
|
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
|
-
|
|
127
|
-
|
|
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
|
-
|
|
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
|