claude-git-hooks 1.0.0 → 1.2.1
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 +97 -0
- package/README.md +226 -48
- package/bin/claude-hooks +501 -51
- package/package.json +2 -1
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
Todos los cambios notables en este proyecto se documentarán en este archivo.
|
|
4
|
+
|
|
5
|
+
El formato está basado en [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
y este proyecto adhiere a [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [1.2.1] - 2024-07-24
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
- 🐛 Corregido problema de bloqueo del spinner durante verificación de autenticación Claude
|
|
12
|
+
- 🎨 Arreglado sistema de entretenimiento para mostrar correctamente spinner, chistes y countdown
|
|
13
|
+
- 🔧 Solucionado problema de líneas duplicadas en consola después del segundo chiste
|
|
14
|
+
- 🧹 Mejorada limpieza de pantalla para evitar superposición de texto al finalizar spinner
|
|
15
|
+
- ⏱️ Aumentado timeout de verificación de autenticación a 2 minutos
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
- 🔄 Reemplazado `execSync` por `spawn` en verificación de autenticación para evitar bloqueos
|
|
19
|
+
- 📍 Mejorado sistema de posicionamiento del cursor para renderizado consistente
|
|
20
|
+
- 🎯 Optimizada lógica de actualización del spinner para evitar race conditions
|
|
21
|
+
|
|
22
|
+
### Technical
|
|
23
|
+
- 🏗️ Uso de `spawn` con `stdio: 'ignore'` para mantener compatibilidad con comportamiento original
|
|
24
|
+
- 🎮 Sistema de reserva de espacio (3 líneas) para evitar problemas de renderizado
|
|
25
|
+
- 🧮 Contador de chistes cada 10 segundos real (sin drift de tiempo)
|
|
26
|
+
- 🔍 Limpieza precisa línea por línea al finalizar para evitar artefactos visuales
|
|
27
|
+
|
|
28
|
+
## [1.2.0] - 2024-07-24
|
|
29
|
+
|
|
30
|
+
### Added
|
|
31
|
+
- ✨ Sistema de entretenimiento con spinner animado y chistes durante operaciones largas
|
|
32
|
+
- 🔐 Lectura segura y no-persistente de contraseña sudo para instalación automática
|
|
33
|
+
- 🛠️ Verificación completa de dependencias del sistema (jq, curl, herramientas Unix)
|
|
34
|
+
- 🚀 Instalación automática de dependencias faltantes con contraseña sudo
|
|
35
|
+
- ⚙️ Configuración automática de Git (line endings para WSL/Windows)
|
|
36
|
+
- 🎭 Chistes rotativos cada 10 segundos durante verificación de autenticación Claude
|
|
37
|
+
|
|
38
|
+
### Enhanced
|
|
39
|
+
- 📦 Función `install` ahora incluye verificación e instalación completa de dependencias
|
|
40
|
+
- 🔍 Verificación de autenticación Claude con entretenimiento visual
|
|
41
|
+
- 💻 Detección inteligente de plataforma (Linux/macOS) para instalación de paquetes
|
|
42
|
+
|
|
43
|
+
### Removed
|
|
44
|
+
- 🗑️ Eliminados archivos `setup-wsl.sh` y `setup-wsl.js` (redundantes)
|
|
45
|
+
- 🧹 Simplificación de la arquitectura eliminando duplicación de código
|
|
46
|
+
|
|
47
|
+
### Technical
|
|
48
|
+
- 🔧 Añadido módulo `https` para obtener chistes de API externa
|
|
49
|
+
- 🎨 Clase `Entertainment` para manejo de spinner y chistes
|
|
50
|
+
- 🔐 Funciones `readPassword()` y `testSudoPassword()` para manejo seguro de credenciales
|
|
51
|
+
- ⚡ Conversión a funciones async/await para mejor manejo de procesos asíncronos
|
|
52
|
+
|
|
53
|
+
## [1.1.0] - 2024-07-24
|
|
54
|
+
|
|
55
|
+
### Added
|
|
56
|
+
- 🎯 Comando `set-mode` para cambiar entre análisis estándar y SonarQube
|
|
57
|
+
- 📊 Modo SonarQube con métricas detalladas y quality gate
|
|
58
|
+
- 📈 Visualización del modo de análisis actual en comando `status`
|
|
59
|
+
- 🔧 Configuración persistente del modo de análisis en archivo `.claude-analysis-mode`
|
|
60
|
+
|
|
61
|
+
### Enhanced
|
|
62
|
+
- 📋 Comando `status` ahora muestra modo de análisis y archivos de pautas
|
|
63
|
+
- 📚 Documentación mejorada con ejemplos de ambos modos
|
|
64
|
+
- 🎨 Mejor formateo de salida en comandos informativos
|
|
65
|
+
|
|
66
|
+
### Technical
|
|
67
|
+
- 📁 Detección automática de archivos de pautas (CLAUDE_PRE_COMMIT.md, CLAUDE_PRE_COMMIT_SONAR.md)
|
|
68
|
+
- 🏗️ Estructura preparada para múltiples modos de análisis
|
|
69
|
+
|
|
70
|
+
## [1.0.0] - 2024-07-24
|
|
71
|
+
|
|
72
|
+
### Added
|
|
73
|
+
- 🎉 Primera versión estable del paquete npm `claude-git-hooks`
|
|
74
|
+
- 📦 CLI global `claude-hooks` para gestión de hooks de Git
|
|
75
|
+
- 🪝 Instalación automática de hooks `pre-commit` y `prepare-commit-msg`
|
|
76
|
+
- 🔍 Verificación de dependencias (Claude CLI, jq)
|
|
77
|
+
- 📋 Comandos principales: `install`, `uninstall`, `enable`, `disable`, `status`
|
|
78
|
+
- 📖 Sistema de ayuda completo con ejemplos
|
|
79
|
+
- 🎛️ Gestión individual de hooks (habilitar/deshabilitar por separado)
|
|
80
|
+
- 📂 Creación automática de archivos de pautas si no existen
|
|
81
|
+
- 🔄 Sistema de backup automático de hooks existentes
|
|
82
|
+
|
|
83
|
+
### Technical
|
|
84
|
+
- 🏗️ Arquitectura modular con funciones especializadas
|
|
85
|
+
- 🎨 Sistema de colores para output legible
|
|
86
|
+
- 🛡️ Validaciones de entorno (repositorio Git, dependencias)
|
|
87
|
+
- 📝 Documentación completa en README
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## Tipos de cambios
|
|
92
|
+
- `Added` - para nuevas funcionalidades
|
|
93
|
+
- `Changed` - para cambios en funcionalidades existentes
|
|
94
|
+
- `Deprecated` - para funcionalidades que serán removidas
|
|
95
|
+
- `Removed` - para funcionalidades removidas
|
|
96
|
+
- `Fixed` - para corrección de bugs
|
|
97
|
+
- `Security` - para vulnerabilidades corregidas
|
package/README.md
CHANGED
|
@@ -7,7 +7,6 @@ Este directorio contiene un pre-commit hook que utiliza Claude CLI para revisar
|
|
|
7
7
|
- **`README.md`** - Esta documentación
|
|
8
8
|
- **`pre-commit`** - Hook principal para análisis de código (con auto-actualización)
|
|
9
9
|
- **`prepare-commit-msg`** - Hook para generar mensajes de commit automáticos
|
|
10
|
-
- **`setup-wsl.sh`** - Script de instalación inicial para WSL
|
|
11
10
|
- **`.gitattributes`** - Configuración para mantener line endings correctos
|
|
12
11
|
- **`CLAUDE_PRE_COMMIT.md`** - Pautas de evaluación formato estándar
|
|
13
12
|
- **`CLAUDE_PRE_COMMIT_SONAR.md`** - Pautas de evaluación formato SonarQube
|
|
@@ -49,35 +48,57 @@ git config --global credential.helper store
|
|
|
49
48
|
# O configura tu credential helper específico
|
|
50
49
|
```
|
|
51
50
|
|
|
52
|
-
## 🚀 Instalación
|
|
51
|
+
## 🚀 Instalación
|
|
52
|
+
|
|
53
|
+
### Paquete NPM Global (Recomendado)
|
|
53
54
|
|
|
54
55
|
**IMPORTANTE**: Claude CLI corre en WSL, por lo que toda la instalación y uso de git debe hacerse desde la terminal WSL.
|
|
55
56
|
|
|
56
57
|
```bash
|
|
57
|
-
#
|
|
58
|
-
|
|
58
|
+
# Instalar el paquete globalmente
|
|
59
|
+
npm install -g claude-git-hooks
|
|
60
|
+
|
|
61
|
+
# En cualquier repositorio, instalar los hooks
|
|
62
|
+
cd tu-proyecto
|
|
63
|
+
claude-hooks install
|
|
64
|
+
```
|
|
59
65
|
|
|
60
|
-
|
|
61
|
-
|
|
66
|
+
El comando `claude-hooks install` ahora incluye:
|
|
67
|
+
- ✅ Verificación completa de dependencias del sistema
|
|
68
|
+
- ✅ Instalación automática de paquetes faltantes (jq, curl)
|
|
69
|
+
- ✅ Configuración de Git (line endings WSL/Windows)
|
|
70
|
+
- ✅ Verificación de autenticación Claude con entretenimiento
|
|
71
|
+
- ✅ Instalación de hooks y archivos de pautas
|
|
62
72
|
|
|
63
|
-
|
|
64
|
-
|
|
73
|
+
#### Añadir como Dependencia de Desarrollo
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
# Instalar como devDependency
|
|
77
|
+
npm install --save-dev claude-git-hooks
|
|
78
|
+
```
|
|
65
79
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
80
|
+
Luego añade esto a tu `package.json`:
|
|
81
|
+
|
|
82
|
+
```json
|
|
83
|
+
{
|
|
84
|
+
"scripts": {
|
|
85
|
+
"postinstall": "claude-hooks install"
|
|
86
|
+
},
|
|
87
|
+
"devDependencies": {
|
|
88
|
+
"claude-git-hooks": "^1.2.0"
|
|
89
|
+
}
|
|
90
|
+
}
|
|
71
91
|
```
|
|
72
92
|
|
|
93
|
+
## 🤖 Características
|
|
94
|
+
|
|
73
95
|
**✨ Auto-actualización incorporada**: Los hooks se actualizan automáticamente en cada commit.
|
|
74
96
|
|
|
75
|
-
|
|
97
|
+
**Hooks disponibles**:
|
|
76
98
|
|
|
77
99
|
- `pre-commit`: Análisis de código con Claude (solo archivos Java/config)
|
|
78
100
|
- `prepare-commit-msg`: Generación automática de mensajes de commit
|
|
79
101
|
|
|
80
|
-
|
|
81
102
|
## 🎯 Funcionamiento
|
|
82
103
|
|
|
83
104
|
### Hook pre-commit (Análisis de código)
|
|
@@ -105,10 +126,7 @@ git config --global credential.helper store
|
|
|
105
126
|
### Hook prepare-commit-msg (Generación automática de mensajes)
|
|
106
127
|
|
|
107
128
|
1. **Se activa cuando el mensaje es**:
|
|
108
|
-
- Vacío (`""`)
|
|
109
129
|
- `"auto"`
|
|
110
|
-
- `"--"`
|
|
111
|
-
- `"tmp"`
|
|
112
130
|
2. **Analiza los cambios del staging area**:
|
|
113
131
|
- Lista archivos modificados con estadísticas
|
|
114
132
|
- Incluye diffs completos para archivos < 100KB
|
|
@@ -151,48 +169,54 @@ Claude analizará los cambios y generará un mensaje de commit siguiendo las con
|
|
|
151
169
|
|
|
152
170
|
**⚠️ Si la generación automática falla**: El commit se cancelará completamente. Simplemente ejecuta `git commit -m "tu mensaje"` manualmente.
|
|
153
171
|
|
|
154
|
-
###
|
|
172
|
+
### Modos de análisis 📊
|
|
155
173
|
|
|
156
|
-
|
|
174
|
+
Puedes elegir entre dos formatos de análisis:
|
|
175
|
+
|
|
176
|
+
#### 🎯 Configurar Modo (Recomendado)
|
|
157
177
|
|
|
158
178
|
```bash
|
|
159
|
-
#
|
|
160
|
-
|
|
161
|
-
git commit -m "mensaje"
|
|
179
|
+
# Cambiar a modo SonarQube (métricas y quality gate)
|
|
180
|
+
claude-hooks set-mode sonar
|
|
162
181
|
|
|
163
|
-
#
|
|
164
|
-
|
|
165
|
-
|
|
182
|
+
# Cambiar a modo estándar (score y recomendaciones)
|
|
183
|
+
claude-hooks set-mode standard
|
|
184
|
+
|
|
185
|
+
# Ver modo actual
|
|
186
|
+
claude-hooks status
|
|
166
187
|
```
|
|
167
188
|
|
|
168
|
-
|
|
189
|
+
#### 🔧 Variable de Entorno (Temporal)
|
|
169
190
|
|
|
170
191
|
```bash
|
|
192
|
+
# Sobrescribir temporalmente el modo configurado
|
|
193
|
+
export CLAUDE_ANALYSIS_MODE=sonar
|
|
171
194
|
git commit -m "mensaje"
|
|
172
|
-
|
|
173
|
-
#
|
|
174
|
-
|
|
195
|
+
|
|
196
|
+
# Volver al modo configurado
|
|
197
|
+
unset CLAUDE_ANALYSIS_MODE
|
|
175
198
|
```
|
|
176
199
|
|
|
177
|
-
|
|
200
|
+
#### 📋 Diferencias entre Modos
|
|
178
201
|
|
|
179
|
-
|
|
180
|
-
-
|
|
181
|
-
-
|
|
182
|
-
-
|
|
202
|
+
**Modo Estándar**:
|
|
203
|
+
- Análisis con puntuación del 1-10
|
|
204
|
+
- Recomendaciones detalladas por categoría
|
|
205
|
+
- Formato tradicional fácil de leer
|
|
183
206
|
|
|
184
|
-
|
|
207
|
+
**Modo SonarQube**:
|
|
208
|
+
- Quality Gate (PASSED/FAILED)
|
|
209
|
+
- Métricas: Reliability, Security, Maintainability
|
|
210
|
+
- Issues clasificados por severidad (Blocker, Critical, Major, Minor, Info)
|
|
211
|
+
- Security hotspots
|
|
185
212
|
|
|
186
|
-
|
|
213
|
+
#### 🔄 Cambio de Modo Interactivo
|
|
187
214
|
|
|
188
215
|
```bash
|
|
189
|
-
|
|
216
|
+
# Sin parámetros muestra ayuda interactiva
|
|
217
|
+
claude-hooks set-mode
|
|
190
218
|
```
|
|
191
219
|
|
|
192
|
-
**Formato Estándar**: Análisis tradicional con score, recomendaciones y detalles por categoría.
|
|
193
|
-
|
|
194
|
-
**Formato SonarQube**: Análisis con Quality Gate, métricas (Reliability, Security, Maintainability), clasificación de issues por severidad (Blocker, Critical, Major, Minor, Info), y security hotspots.
|
|
195
|
-
|
|
196
220
|
### Saltar la revisión (usar con precaución)
|
|
197
221
|
|
|
198
222
|
```bash
|
|
@@ -221,18 +245,21 @@ Claude responde con un JSON que incluye:
|
|
|
221
245
|
|
|
222
246
|
```bash
|
|
223
247
|
# Desactivar todos los hooks
|
|
224
|
-
|
|
248
|
+
claude-hooks disable
|
|
225
249
|
|
|
226
250
|
# Desactivar un hook específico
|
|
227
|
-
|
|
228
|
-
|
|
251
|
+
claude-hooks disable pre-commit
|
|
252
|
+
claude-hooks disable prepare-commit-msg
|
|
229
253
|
|
|
230
254
|
# Habilitar todos los hooks
|
|
231
|
-
|
|
255
|
+
claude-hooks enable
|
|
232
256
|
|
|
233
257
|
# Habilitar un hook específico
|
|
234
|
-
|
|
235
|
-
|
|
258
|
+
claude-hooks enable pre-commit
|
|
259
|
+
claude-hooks enable prepare-commit-msg
|
|
260
|
+
|
|
261
|
+
# Ver estado actual
|
|
262
|
+
claude-hooks status
|
|
236
263
|
```
|
|
237
264
|
|
|
238
265
|
## ⚙️ Configuración
|
|
@@ -353,3 +380,154 @@ WSL Terminal
|
|
|
353
380
|
2. **Line Endings**: La configuración de `core.autocrlf` es crítica para evitar problemas entre Windows y Linux
|
|
354
381
|
3. **Credenciales**: Necesitarás reconfigurar tus credenciales git en WSL
|
|
355
382
|
4. **Performance**: La revisión puede tardar unos segundos dependiendo del tamaño de los cambios
|
|
383
|
+
|
|
384
|
+
## 🔧 Desarrollo y Contribución
|
|
385
|
+
|
|
386
|
+
### Estructura del Proyecto
|
|
387
|
+
|
|
388
|
+
```
|
|
389
|
+
claude-git-hooks/
|
|
390
|
+
├── bin/
|
|
391
|
+
│ └── claude-hooks # CLI principal con verificación completa
|
|
392
|
+
├── templates/
|
|
393
|
+
│ ├── pre-commit # Hook de análisis de código
|
|
394
|
+
│ ├── prepare-commit-msg # Hook de generación de mensajes
|
|
395
|
+
│ ├── CLAUDE_PRE_COMMIT.md # Pautas estándar
|
|
396
|
+
│ └── CLAUDE_PRE_COMMIT_SONAR.md # Pautas SonarQube
|
|
397
|
+
├── package.json # Configuración NPM
|
|
398
|
+
├── README.md # Este archivo
|
|
399
|
+
├── CHANGELOG.md # Historial de versiones
|
|
400
|
+
└── PUBLISH.md # Guía de publicación
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
### Configuración del Entorno de Desarrollo
|
|
404
|
+
|
|
405
|
+
```bash
|
|
406
|
+
# 1. Clonar el repositorio
|
|
407
|
+
git clone https://github.com/pablorovito/claude-git-hooks.git
|
|
408
|
+
cd claude-git-hooks
|
|
409
|
+
|
|
410
|
+
# 2. Instalar dependencias (si las hubiera)
|
|
411
|
+
npm install
|
|
412
|
+
|
|
413
|
+
# 3. Enlazar para desarrollo local
|
|
414
|
+
npm link
|
|
415
|
+
|
|
416
|
+
# 4. Probar en un repositorio de prueba
|
|
417
|
+
cd /path/to/test-repo
|
|
418
|
+
claude-hooks install
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
### Workflow de Desarrollo
|
|
422
|
+
|
|
423
|
+
#### 1. Modificar Hooks
|
|
424
|
+
|
|
425
|
+
Los hooks están en `templates/`:
|
|
426
|
+
|
|
427
|
+
- `templates/pre-commit` - Lógica de análisis de código
|
|
428
|
+
- `templates/prepare-commit-msg` - Lógica de generación de mensajes
|
|
429
|
+
|
|
430
|
+
#### 2. Probar Localmente
|
|
431
|
+
|
|
432
|
+
```bash
|
|
433
|
+
# Después de modificar archivos
|
|
434
|
+
npm link
|
|
435
|
+
|
|
436
|
+
# En un repo de prueba
|
|
437
|
+
claude-hooks install --force # Fuerza reinstalación
|
|
438
|
+
|
|
439
|
+
# Probar funcionalidad
|
|
440
|
+
git add .
|
|
441
|
+
git commit -m "auto" # Probar generación automática
|
|
442
|
+
git commit -m "test" # Probar análisis
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
#### 3. Actualizar Documentación
|
|
446
|
+
|
|
447
|
+
- `README.md` - Documentación principal
|
|
448
|
+
- `README-NPM.md` - Para usuarios de NPM
|
|
449
|
+
- `templates/CLAUDE_PRE_COMMIT*.md` - Pautas de evaluación
|
|
450
|
+
|
|
451
|
+
#### 4. Versioning y Publicación
|
|
452
|
+
|
|
453
|
+
```bash
|
|
454
|
+
# Actualizar versión
|
|
455
|
+
npm version patch # 1.0.0 -> 1.0.1
|
|
456
|
+
npm version minor # 1.0.0 -> 1.1.0
|
|
457
|
+
npm version major # 1.0.0 -> 2.0.0
|
|
458
|
+
|
|
459
|
+
# Publicar nueva versión
|
|
460
|
+
npm publish
|
|
461
|
+
|
|
462
|
+
# Tag en git
|
|
463
|
+
git push --tags
|
|
464
|
+
```
|
|
465
|
+
|
|
466
|
+
### Debugging
|
|
467
|
+
|
|
468
|
+
#### Modo Debug
|
|
469
|
+
|
|
470
|
+
```bash
|
|
471
|
+
# Activar debug en hooks
|
|
472
|
+
DEBUG=1 git commit -m "test"
|
|
473
|
+
|
|
474
|
+
# Ver logs del CLI
|
|
475
|
+
claude-hooks install --debug
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
#### Logs Útiles
|
|
479
|
+
|
|
480
|
+
- `debug-claude-response.json` - Respuestas de Claude en modo debug
|
|
481
|
+
- `~/.npm/_logs/` - Logs de npm install
|
|
482
|
+
- `.git/hooks/` - Verificar que hooks están instalados
|
|
483
|
+
|
|
484
|
+
### Testing
|
|
485
|
+
|
|
486
|
+
#### Test Manual
|
|
487
|
+
|
|
488
|
+
```bash
|
|
489
|
+
# 1. Crear repo de prueba
|
|
490
|
+
mkdir test-repo && cd test-repo
|
|
491
|
+
git init
|
|
492
|
+
|
|
493
|
+
# 2. Instalar hooks
|
|
494
|
+
claude-hooks install
|
|
495
|
+
|
|
496
|
+
# 3. Crear archivos de prueba
|
|
497
|
+
echo 'public class Test {}' > Test.java
|
|
498
|
+
git add .
|
|
499
|
+
|
|
500
|
+
# 4. Probar análisis
|
|
501
|
+
git commit -m "test: nueva clase"
|
|
502
|
+
|
|
503
|
+
# 5. Probar generación automática
|
|
504
|
+
echo 'public void newMethod() {}' >> Test.java
|
|
505
|
+
git add .
|
|
506
|
+
git commit -m "auto"
|
|
507
|
+
```
|
|
508
|
+
|
|
509
|
+
#### Casos de Prueba
|
|
510
|
+
|
|
511
|
+
- ✅ Análisis de código Java
|
|
512
|
+
- ✅ Generación automática de mensajes
|
|
513
|
+
- ✅ Modo SonarQube
|
|
514
|
+
- ✅ Archivos grandes (>100KB)
|
|
515
|
+
- ✅ Commits sin archivos Java
|
|
516
|
+
- ✅ Enable/disable hooks
|
|
517
|
+
- ✅ Modo debug
|
|
518
|
+
|
|
519
|
+
### Contribuir
|
|
520
|
+
|
|
521
|
+
1. **Fork** el repositorio
|
|
522
|
+
2. **Crear branch** para tu feature: `git checkout -b feature/nueva-funcionalidad`
|
|
523
|
+
3. **Commit** tus cambios: `git commit -m "feat: nueva funcionalidad"`
|
|
524
|
+
4. **Push** al branch: `git push origin feature/nueva-funcionalidad`
|
|
525
|
+
5. **Abrir Pull Request**
|
|
526
|
+
|
|
527
|
+
### Roadmap
|
|
528
|
+
|
|
529
|
+
- [ ] Soporte para más lenguajes (Python, JavaScript, etc.)
|
|
530
|
+
- [ ] Configuración más granular por proyecto
|
|
531
|
+
- [ ] Integration con IDEs populares
|
|
532
|
+
- [ ] Métricas de uso y performance
|
|
533
|
+
- [ ] Tests automatizados
|
package/bin/claude-hooks
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
const { execSync } = require('child_process');
|
|
3
|
+
const { execSync, spawn } = require('child_process');
|
|
4
4
|
const fs = require('fs');
|
|
5
5
|
const path = require('path');
|
|
6
6
|
const os = require('os');
|
|
7
|
+
const readline = require('readline');
|
|
8
|
+
const https = require('https');
|
|
7
9
|
|
|
8
10
|
// Colores para output
|
|
9
11
|
const colors = {
|
|
@@ -35,6 +37,206 @@ function warning(message) {
|
|
|
35
37
|
log(`⚠️ ${message}`, 'yellow');
|
|
36
38
|
}
|
|
37
39
|
|
|
40
|
+
// Función para leer contraseña de forma segura
|
|
41
|
+
function readPassword(prompt) {
|
|
42
|
+
return new Promise((resolve) => {
|
|
43
|
+
const rl = readline.createInterface({
|
|
44
|
+
input: process.stdin,
|
|
45
|
+
output: process.stdout
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
// Deshabilitar echo
|
|
49
|
+
rl.stdoutMuted = true;
|
|
50
|
+
rl._writeToOutput = function _writeToOutput(stringToWrite) {
|
|
51
|
+
if (rl.stdoutMuted)
|
|
52
|
+
rl.output.write("*");
|
|
53
|
+
else
|
|
54
|
+
rl.output.write(stringToWrite);
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
rl.question(prompt, (password) => {
|
|
58
|
+
rl.close();
|
|
59
|
+
console.log(); // Nueva línea
|
|
60
|
+
resolve(password);
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Verificar si la contraseña sudo es correcta
|
|
66
|
+
function testSudoPassword(password) {
|
|
67
|
+
try {
|
|
68
|
+
execSync('echo "' + password + '" | sudo -S true', {
|
|
69
|
+
stdio: 'ignore',
|
|
70
|
+
timeout: 5000
|
|
71
|
+
});
|
|
72
|
+
return true;
|
|
73
|
+
} catch (e) {
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Instalar paquete con sudo automático
|
|
79
|
+
function installPackage(packageName, sudoPassword = null) {
|
|
80
|
+
try {
|
|
81
|
+
if (sudoPassword) {
|
|
82
|
+
if (os.platform() === 'linux') {
|
|
83
|
+
execSync(`echo "${sudoPassword}" | sudo -S apt-get update && echo "${sudoPassword}" | sudo -S apt-get install -y ${packageName}`, {
|
|
84
|
+
stdio: 'inherit'
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
} else {
|
|
88
|
+
if (os.platform() === 'linux') {
|
|
89
|
+
execSync(`sudo apt-get update && sudo apt-get install -y ${packageName}`, {
|
|
90
|
+
stdio: 'inherit'
|
|
91
|
+
});
|
|
92
|
+
} else if (os.platform() === 'darwin') {
|
|
93
|
+
execSync(`brew install ${packageName}`, {
|
|
94
|
+
stdio: 'inherit'
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return true;
|
|
99
|
+
} catch (e) {
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Sistema de entretenimiento
|
|
105
|
+
class Entertainment {
|
|
106
|
+
static jokes = [
|
|
107
|
+
"¿Por qué los programadores prefieren el modo oscuro? Porque la luz atrae bugs!",
|
|
108
|
+
"Un QA entra a un bar. Pide 1 cerveza. Pide 0 cervezas. Pide -1 cervezas.",
|
|
109
|
+
"¿Cuál es el lenguaje favorito de los piratas? R!",
|
|
110
|
+
"Hay 10 tipos de personas: las que entienden binario y las que no.",
|
|
111
|
+
"¿Por qué los programadores confunden Halloween con Navidad? Porque Oct 31 = Dec 25",
|
|
112
|
+
"¿Qué le dice un bit al otro? Nos vemos en el bus!",
|
|
113
|
+
"¿Por qué Java y C++ no se llevan bien? Porque tienen diferentes puntos de vista sobre los punteros.",
|
|
114
|
+
"Mi código no tiene bugs, solo features no documentadas."
|
|
115
|
+
];
|
|
116
|
+
|
|
117
|
+
static async getJoke() {
|
|
118
|
+
return new Promise((resolve) => {
|
|
119
|
+
// Intentar obtener chiste de API
|
|
120
|
+
const req = https.get('https://icanhazdadjoke.com/', {
|
|
121
|
+
headers: { 'Accept': 'text/plain' },
|
|
122
|
+
timeout: 3000
|
|
123
|
+
}, (res) => {
|
|
124
|
+
let data = '';
|
|
125
|
+
res.on('data', chunk => data += chunk);
|
|
126
|
+
res.on('end', () => resolve(data.trim()));
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
req.on('error', () => {
|
|
130
|
+
// Si falla, usar chiste local
|
|
131
|
+
const randomJoke = this.jokes[Math.floor(Math.random() * this.jokes.length)];
|
|
132
|
+
resolve(randomJoke);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
req.on('timeout', () => {
|
|
136
|
+
req.abort();
|
|
137
|
+
const randomJoke = this.jokes[Math.floor(Math.random() * this.jokes.length)];
|
|
138
|
+
resolve(randomJoke);
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
static async showSpinner(promise, message = 'Procesando') {
|
|
144
|
+
const spinners = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
145
|
+
let spinnerIndex = 0;
|
|
146
|
+
let jokeCountdown = 10;
|
|
147
|
+
let currentJoke = this.jokes[Math.floor(Math.random() * this.jokes.length)];
|
|
148
|
+
let isFinished = false;
|
|
149
|
+
let isFirstRender = true;
|
|
150
|
+
|
|
151
|
+
// Obtener primer chiste de la API sin bloquear
|
|
152
|
+
this.getJoke().then(joke => {
|
|
153
|
+
if (!isFinished) currentJoke = joke;
|
|
154
|
+
}).catch(() => {}); // Si falla, mantener el local
|
|
155
|
+
|
|
156
|
+
// Ocultar cursor
|
|
157
|
+
process.stdout.write('\x1B[?25l');
|
|
158
|
+
|
|
159
|
+
// Reservar espacio para las 3 líneas
|
|
160
|
+
process.stdout.write('\n\n\n');
|
|
161
|
+
|
|
162
|
+
const interval = setInterval(() => {
|
|
163
|
+
if (isFinished) {
|
|
164
|
+
clearInterval(interval);
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
spinnerIndex++;
|
|
169
|
+
|
|
170
|
+
// Actualizar countdown cada segundo (10 iteraciones de 100ms)
|
|
171
|
+
if (spinnerIndex % 10 === 0) {
|
|
172
|
+
jokeCountdown--;
|
|
173
|
+
|
|
174
|
+
// Renovar chiste cada 10 segundos
|
|
175
|
+
if (jokeCountdown <= 0) {
|
|
176
|
+
this.getJoke().then(joke => {
|
|
177
|
+
if (!isFinished) currentJoke = joke;
|
|
178
|
+
}).catch(() => {
|
|
179
|
+
if (!isFinished) {
|
|
180
|
+
currentJoke = this.jokes[Math.floor(Math.random() * this.jokes.length)];
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
jokeCountdown = 10;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Siempre volver exactamente 3 líneas arriba
|
|
188
|
+
process.stdout.write('\x1B[3A');
|
|
189
|
+
|
|
190
|
+
// Renderizar las 3 líneas desde el inicio
|
|
191
|
+
const spinner = spinners[spinnerIndex % spinners.length];
|
|
192
|
+
|
|
193
|
+
// Línea 1: Spinner
|
|
194
|
+
process.stdout.write('\r\x1B[2K' + `${colors.yellow}${spinner} ${message}${colors.reset}\n`);
|
|
195
|
+
|
|
196
|
+
// Línea 2: Chiste
|
|
197
|
+
process.stdout.write('\r\x1B[2K' + `${colors.green}🎭 ${currentJoke}${colors.reset}\n`);
|
|
198
|
+
|
|
199
|
+
// Línea 3: Countdown
|
|
200
|
+
process.stdout.write('\r\x1B[2K' + `${colors.yellow}⏱️ Próximo chiste en: ${jokeCountdown}s${colors.reset}\n`);
|
|
201
|
+
}, 100);
|
|
202
|
+
|
|
203
|
+
try {
|
|
204
|
+
const result = await promise;
|
|
205
|
+
isFinished = true;
|
|
206
|
+
clearInterval(interval);
|
|
207
|
+
|
|
208
|
+
// Limpiar exactamente 3 líneas completamente
|
|
209
|
+
process.stdout.write('\x1B[3A'); // Subir 3 líneas
|
|
210
|
+
process.stdout.write('\r\x1B[2K'); // Limpiar línea 1
|
|
211
|
+
process.stdout.write('\n\r\x1B[2K'); // Bajar y limpiar línea 2
|
|
212
|
+
process.stdout.write('\n\r\x1B[2K'); // Bajar y limpiar línea 3
|
|
213
|
+
process.stdout.write('\x1B[2A'); // Subir 2 líneas para quedar en la primera
|
|
214
|
+
process.stdout.write('\r'); // Ir al inicio de línea
|
|
215
|
+
|
|
216
|
+
// Mostrar cursor
|
|
217
|
+
process.stdout.write('\x1B[?25h');
|
|
218
|
+
|
|
219
|
+
return result;
|
|
220
|
+
} catch (error) {
|
|
221
|
+
isFinished = true;
|
|
222
|
+
clearInterval(interval);
|
|
223
|
+
|
|
224
|
+
// Limpiar exactamente 3 líneas completamente
|
|
225
|
+
process.stdout.write('\x1B[3A'); // Subir 3 líneas
|
|
226
|
+
process.stdout.write('\r\x1B[2K'); // Limpiar línea 1
|
|
227
|
+
process.stdout.write('\n\r\x1B[2K'); // Bajar y limpiar línea 2
|
|
228
|
+
process.stdout.write('\n\r\x1B[2K'); // Bajar y limpiar línea 3
|
|
229
|
+
process.stdout.write('\x1B[2A'); // Subir 2 líneas para quedar en la primera
|
|
230
|
+
process.stdout.write('\r'); // Ir al inicio de línea
|
|
231
|
+
|
|
232
|
+
// Mostrar cursor
|
|
233
|
+
process.stdout.write('\x1B[?25h');
|
|
234
|
+
|
|
235
|
+
throw error;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
38
240
|
// Verificar si estamos en un repositorio git
|
|
39
241
|
function checkGitRepo() {
|
|
40
242
|
try {
|
|
@@ -51,13 +253,33 @@ function getTemplatesPath() {
|
|
|
51
253
|
}
|
|
52
254
|
|
|
53
255
|
// Comando install
|
|
54
|
-
function install(args) {
|
|
256
|
+
async function install(args) {
|
|
55
257
|
if (!checkGitRepo()) {
|
|
56
258
|
error('No estás en un repositorio Git. Por favor, ejecuta este comando desde la raíz de un repositorio.');
|
|
57
259
|
}
|
|
58
260
|
|
|
59
261
|
info('Instalando Claude Git Hooks...');
|
|
60
262
|
|
|
263
|
+
// Solicitar contraseña sudo al inicio si es necesario
|
|
264
|
+
let sudoPassword = null;
|
|
265
|
+
if (os.platform() === 'linux') {
|
|
266
|
+
const needsInstall = await checkIfInstallationNeeded();
|
|
267
|
+
if (needsInstall) {
|
|
268
|
+
info('Para la instalación automática de dependencias se necesita acceso sudo, por favor ingrese contraseña');
|
|
269
|
+
sudoPassword = await readPassword('Introduce tu contraseña de Ubuntu para sudo: ');
|
|
270
|
+
|
|
271
|
+
if (sudoPassword && !testSudoPassword(sudoPassword)) {
|
|
272
|
+
warning('Contraseña incorrecta. Continuando sin instalación automática.');
|
|
273
|
+
sudoPassword = null;
|
|
274
|
+
} else if (sudoPassword) {
|
|
275
|
+
success('Contraseña verificada. Procediendo con instalación automática.');
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// Verificar dependencias con instalación automática
|
|
281
|
+
await checkAndInstallDependencies(sudoPassword);
|
|
282
|
+
|
|
61
283
|
const templatesPath = getTemplatesPath();
|
|
62
284
|
const hooksPath = '.git/hooks';
|
|
63
285
|
|
|
@@ -91,13 +313,15 @@ function install(args) {
|
|
|
91
313
|
guidelines.forEach(guideline => {
|
|
92
314
|
if (!fs.existsSync(guideline)) {
|
|
93
315
|
const sourcePath = path.join(templatesPath, guideline);
|
|
94
|
-
fs.
|
|
95
|
-
|
|
316
|
+
if (fs.existsSync(sourcePath)) {
|
|
317
|
+
fs.copyFileSync(sourcePath, guideline);
|
|
318
|
+
success(`${guideline} creado`);
|
|
319
|
+
}
|
|
96
320
|
}
|
|
97
321
|
});
|
|
98
322
|
|
|
99
|
-
//
|
|
100
|
-
|
|
323
|
+
// Configurar Git
|
|
324
|
+
configureGit();
|
|
101
325
|
|
|
102
326
|
success('¡Claude Git Hooks instalado exitosamente! 🎉');
|
|
103
327
|
console.log('\nUso:');
|
|
@@ -106,31 +330,198 @@ function install(args) {
|
|
|
106
330
|
console.log('\nPara más opciones: claude-hooks --help');
|
|
107
331
|
}
|
|
108
332
|
|
|
109
|
-
// Verificar dependencias
|
|
110
|
-
function
|
|
111
|
-
info('
|
|
333
|
+
// Verificar dependencias completas (como setup-wsl.sh)
|
|
334
|
+
async function checkAndInstallDependencies(sudoPassword = null) {
|
|
335
|
+
info('Verificando dependencias del sistema...');
|
|
336
|
+
|
|
337
|
+
// Verificar Node.js
|
|
338
|
+
try {
|
|
339
|
+
const nodeVersion = execSync('node --version', { encoding: 'utf8' }).trim();
|
|
340
|
+
success(`Node.js ${nodeVersion}`);
|
|
341
|
+
} catch (e) {
|
|
342
|
+
error('Node.js no está instalado. Instala Node.js e intenta de nuevo.');
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
// Verificar npm
|
|
346
|
+
try {
|
|
347
|
+
const npmVersion = execSync('npm --version', { encoding: 'utf8' }).trim();
|
|
348
|
+
success(`npm ${npmVersion}`);
|
|
349
|
+
} catch (e) {
|
|
350
|
+
error('npm no está instalado.');
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// Verificar e instalar jq
|
|
354
|
+
try {
|
|
355
|
+
const jqVersion = execSync('jq --version', { encoding: 'utf8' }).trim();
|
|
356
|
+
success(`jq ${jqVersion}`);
|
|
357
|
+
} catch (e) {
|
|
358
|
+
warning('jq no está instalado. Instalando...');
|
|
359
|
+
if (installPackage('jq', sudoPassword)) {
|
|
360
|
+
success('jq instalado correctamente');
|
|
361
|
+
} else {
|
|
362
|
+
warning('No se pudo instalar jq automáticamente');
|
|
363
|
+
if (os.platform() === 'linux') {
|
|
364
|
+
console.log('Instálalo manualmente con: sudo apt install jq');
|
|
365
|
+
} else if (os.platform() === 'darwin') {
|
|
366
|
+
console.log('Instálalo manualmente con: brew install jq');
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// Verificar e instalar curl
|
|
372
|
+
try {
|
|
373
|
+
const curlVersion = execSync('curl --version', { encoding: 'utf8' }).split('\n')[0];
|
|
374
|
+
success(`curl ${curlVersion.split(' ')[1]}`);
|
|
375
|
+
} catch (e) {
|
|
376
|
+
warning('curl no está instalado. Instalando...');
|
|
377
|
+
if (installPackage('curl', sudoPassword)) {
|
|
378
|
+
success('curl instalado correctamente');
|
|
379
|
+
} else {
|
|
380
|
+
warning('No se pudo instalar curl automáticamente');
|
|
381
|
+
if (os.platform() === 'linux') {
|
|
382
|
+
console.log('Instálalo manualmente con: sudo apt install curl');
|
|
383
|
+
} else if (os.platform() === 'darwin') {
|
|
384
|
+
console.log('Instálalo manualmente con: brew install curl');
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
// Verificar Git
|
|
390
|
+
try {
|
|
391
|
+
const gitVersion = execSync('git --version', { encoding: 'utf8' }).trim();
|
|
392
|
+
success(`${gitVersion}`);
|
|
393
|
+
} catch (e) {
|
|
394
|
+
error('Git no está instalado. Instala Git e intenta de nuevo.');
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
// Verificar herramientas Unix estándar
|
|
398
|
+
const unixTools = ['sed', 'awk', 'grep', 'head', 'tail', 'stat', 'tput'];
|
|
399
|
+
const missingTools = [];
|
|
400
|
+
|
|
401
|
+
unixTools.forEach(tool => {
|
|
402
|
+
try {
|
|
403
|
+
execSync(`which ${tool}`, { stdio: 'ignore' });
|
|
404
|
+
} catch (e) {
|
|
405
|
+
missingTools.push(tool);
|
|
406
|
+
}
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
if (missingTools.length === 0) {
|
|
410
|
+
success('Herramientas Unix estándar verificadas');
|
|
411
|
+
} else {
|
|
412
|
+
error(`Faltan herramientas Unix estándar: ${missingTools.join(', ')}`);
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
// Verificar e instalar Claude CLI
|
|
416
|
+
await checkAndInstallClaude();
|
|
417
|
+
|
|
418
|
+
// Verificar autenticación de Claude
|
|
419
|
+
await checkClaudeAuth();
|
|
420
|
+
|
|
421
|
+
// Limpiar contraseña de memoria
|
|
422
|
+
sudoPassword = null;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
// Verificar si necesitamos instalar dependencias
|
|
426
|
+
async function checkIfInstallationNeeded() {
|
|
427
|
+
const dependencies = ['jq', 'curl'];
|
|
428
|
+
|
|
429
|
+
for (const dep of dependencies) {
|
|
430
|
+
try {
|
|
431
|
+
execSync(`which ${dep}`, { stdio: 'ignore' });
|
|
432
|
+
} catch (e) {
|
|
433
|
+
return true; // Necesita instalación
|
|
434
|
+
}
|
|
435
|
+
}
|
|
112
436
|
|
|
113
437
|
// Verificar Claude CLI
|
|
114
438
|
try {
|
|
115
439
|
execSync('claude --version', { stdio: 'ignore' });
|
|
116
|
-
success('Claude CLI encontrado');
|
|
117
440
|
} catch (e) {
|
|
118
|
-
|
|
119
|
-
console.log('Instálalo con: npm install -g @anthropic-ai/claude-cli');
|
|
120
|
-
console.log('Luego ejecuta: claude auth login');
|
|
441
|
+
return true; // Necesita instalación de Claude
|
|
121
442
|
}
|
|
122
443
|
|
|
123
|
-
|
|
444
|
+
return false;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
// Verificar e instalar Claude CLI
|
|
448
|
+
async function checkAndInstallClaude() {
|
|
449
|
+
try {
|
|
450
|
+
execSync('claude --version', { stdio: 'ignore' });
|
|
451
|
+
success('Claude CLI detectado');
|
|
452
|
+
} catch (e) {
|
|
453
|
+
info('Claude CLI no detectado. Instalando...');
|
|
454
|
+
try {
|
|
455
|
+
execSync('npm install -g @anthropic-ai/claude-cli', { stdio: 'inherit' });
|
|
456
|
+
success('Claude CLI instalado correctamente');
|
|
457
|
+
} catch (installError) {
|
|
458
|
+
error('Error al instalar Claude CLI. Instálalo manualmente: npm install -g @anthropic-ai/claude-cli');
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
// Verificar autenticación de Claude con entretenimiento
|
|
464
|
+
async function checkClaudeAuth() {
|
|
465
|
+
info('Verificando autenticación de Claude...');
|
|
466
|
+
|
|
467
|
+
// Usar spawn para no bloquear, pero con stdio: 'ignore' como el original
|
|
468
|
+
const authPromise = new Promise((resolve, reject) => {
|
|
469
|
+
const child = spawn('claude', ['auth', 'status'], {
|
|
470
|
+
stdio: 'ignore', // Igual que el original
|
|
471
|
+
detached: false,
|
|
472
|
+
windowsHide: true
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
// Timeout manual ya que spawn no tiene timeout nativo
|
|
476
|
+
const timeout = setTimeout(() => {
|
|
477
|
+
child.kill();
|
|
478
|
+
reject(new Error('timeout'));
|
|
479
|
+
}, 120000); // 2 minutos
|
|
480
|
+
|
|
481
|
+
child.on('exit', (code) => {
|
|
482
|
+
clearTimeout(timeout);
|
|
483
|
+
if (code === 0) {
|
|
484
|
+
resolve('success');
|
|
485
|
+
} else {
|
|
486
|
+
reject(new Error('not_authenticated'));
|
|
487
|
+
}
|
|
488
|
+
});
|
|
489
|
+
|
|
490
|
+
child.on('error', (err) => {
|
|
491
|
+
clearTimeout(timeout);
|
|
492
|
+
reject(err);
|
|
493
|
+
});
|
|
494
|
+
});
|
|
495
|
+
|
|
124
496
|
try {
|
|
125
|
-
|
|
126
|
-
success('
|
|
497
|
+
await Entertainment.showSpinner(authPromise, 'Verificando autenticación de Claude');
|
|
498
|
+
success('Autenticado en Claude');
|
|
127
499
|
} catch (e) {
|
|
128
|
-
warning('
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
500
|
+
warning('No estás autenticado en Claude');
|
|
501
|
+
console.log('Ejecuta: claude auth login');
|
|
502
|
+
console.log('Después vuelve a ejecutar este comando');
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
// Configurar Git (line endings, etc.)
|
|
507
|
+
function configureGit() {
|
|
508
|
+
info('Configurando Git...');
|
|
509
|
+
|
|
510
|
+
try {
|
|
511
|
+
// Configurar line endings para WSL
|
|
512
|
+
execSync('git config core.autocrlf input', { stdio: 'ignore' });
|
|
513
|
+
success('Line endings configurados para WSL (core.autocrlf = input)');
|
|
514
|
+
|
|
515
|
+
// Intentar configurar en Windows a través de PowerShell
|
|
516
|
+
try {
|
|
517
|
+
execSync('powershell.exe -Command "git config core.autocrlf true"', { stdio: 'ignore' });
|
|
518
|
+
success('Line endings configurados para Windows (core.autocrlf = true)');
|
|
519
|
+
} catch (psError) {
|
|
520
|
+
info('No se pudo configurar automáticamente en Windows');
|
|
133
521
|
}
|
|
522
|
+
|
|
523
|
+
} catch (e) {
|
|
524
|
+
warning('Error al configurar Git');
|
|
134
525
|
}
|
|
135
526
|
}
|
|
136
527
|
|
|
@@ -224,6 +615,18 @@ function status() {
|
|
|
224
615
|
}
|
|
225
616
|
});
|
|
226
617
|
|
|
618
|
+
// Verificar modo de análisis actual
|
|
619
|
+
console.log('\nModo de análisis:');
|
|
620
|
+
const analysisMode = fs.existsSync('.claude-analysis-mode')
|
|
621
|
+
? fs.readFileSync('.claude-analysis-mode', 'utf8').trim()
|
|
622
|
+
: 'standard (por defecto)';
|
|
623
|
+
|
|
624
|
+
if (analysisMode === 'sonar') {
|
|
625
|
+
info(`Modo actual: SonarQube`);
|
|
626
|
+
} else {
|
|
627
|
+
info(`Modo actual: Estándar`);
|
|
628
|
+
}
|
|
629
|
+
|
|
227
630
|
// Verificar archivos de pautas
|
|
228
631
|
console.log('\nArchivos de pautas:');
|
|
229
632
|
const guidelines = ['CLAUDE_PRE_COMMIT.md', 'CLAUDE_PRE_COMMIT_SONAR.md'];
|
|
@@ -236,6 +639,40 @@ function status() {
|
|
|
236
639
|
});
|
|
237
640
|
}
|
|
238
641
|
|
|
642
|
+
// Comando set-mode
|
|
643
|
+
function setMode(mode) {
|
|
644
|
+
if (!checkGitRepo()) {
|
|
645
|
+
error('No estás en un repositorio Git.');
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
const validModes = ['standard', 'sonar'];
|
|
649
|
+
|
|
650
|
+
if (!mode) {
|
|
651
|
+
// Modo interactivo
|
|
652
|
+
console.log('\nSelecciona el modo de análisis:');
|
|
653
|
+
console.log('1) Standard - Formato clásico con score y recomendaciones');
|
|
654
|
+
console.log('2) SonarQube - Formato similar a SonarQube con métricas');
|
|
655
|
+
console.log('\nEjemplo de uso: claude-hooks set-mode standard');
|
|
656
|
+
console.log(' claude-hooks set-mode sonar');
|
|
657
|
+
return;
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
if (!validModes.includes(mode)) {
|
|
661
|
+
error(`Modo inválido: ${mode}. Usa 'standard' o 'sonar'`);
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
// Guardar el modo
|
|
665
|
+
fs.writeFileSync('.claude-analysis-mode', mode);
|
|
666
|
+
|
|
667
|
+
if (mode === 'sonar') {
|
|
668
|
+
success('Modo cambiado a: SonarQube');
|
|
669
|
+
info('Los commits usarán formato SonarQube con métricas y quality gate');
|
|
670
|
+
} else {
|
|
671
|
+
success('Modo cambiado a: Estándar');
|
|
672
|
+
info('Los commits usarán formato clásico con score y recomendaciones');
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
|
|
239
676
|
// Comando help
|
|
240
677
|
function showHelp() {
|
|
241
678
|
console.log(`
|
|
@@ -248,6 +685,7 @@ Comandos:
|
|
|
248
685
|
uninstall Desinstala los hooks del repositorio
|
|
249
686
|
enable [hook] Habilita hooks (todos o uno específico)
|
|
250
687
|
disable [hook] Deshabilita hooks (todos o uno específico)
|
|
688
|
+
set-mode [standard|sonar] Cambia el modo de análisis
|
|
251
689
|
status Muestra el estado de los hooks
|
|
252
690
|
help Muestra esta ayuda
|
|
253
691
|
|
|
@@ -257,41 +695,53 @@ Hooks disponibles:
|
|
|
257
695
|
|
|
258
696
|
Ejemplos:
|
|
259
697
|
claude-hooks install # Instala todos los hooks
|
|
698
|
+
claude-hooks set-mode sonar # Cambiar a modo SonarQube
|
|
699
|
+
claude-hooks set-mode standard # Cambiar a modo estándar
|
|
260
700
|
claude-hooks disable pre-commit # Deshabilita solo pre-commit
|
|
261
701
|
claude-hooks enable # Habilita todos los hooks
|
|
262
702
|
claude-hooks status # Ver estado actual
|
|
263
703
|
|
|
264
|
-
Más información: https://github.com/
|
|
704
|
+
Más información: https://github.com/pablorovito/claude-git-hooks
|
|
265
705
|
`);
|
|
266
706
|
}
|
|
267
707
|
|
|
268
708
|
// Main
|
|
269
|
-
|
|
270
|
-
const
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
install
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
uninstall
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
enable
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
disable
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
709
|
+
async function main() {
|
|
710
|
+
const args = process.argv.slice(2);
|
|
711
|
+
const command = args[0];
|
|
712
|
+
|
|
713
|
+
switch (command) {
|
|
714
|
+
case 'install':
|
|
715
|
+
await install(args.slice(1));
|
|
716
|
+
break;
|
|
717
|
+
case 'uninstall':
|
|
718
|
+
uninstall();
|
|
719
|
+
break;
|
|
720
|
+
case 'enable':
|
|
721
|
+
enable(args[1]);
|
|
722
|
+
break;
|
|
723
|
+
case 'disable':
|
|
724
|
+
disable(args[1]);
|
|
725
|
+
break;
|
|
726
|
+
case 'set-mode':
|
|
727
|
+
setMode(args[1]);
|
|
728
|
+
break;
|
|
729
|
+
case 'status':
|
|
730
|
+
status();
|
|
731
|
+
break;
|
|
732
|
+
case 'help':
|
|
733
|
+
case '--help':
|
|
734
|
+
case '-h':
|
|
735
|
+
case undefined:
|
|
736
|
+
showHelp();
|
|
737
|
+
break;
|
|
738
|
+
default:
|
|
739
|
+
error(`Comando desconocido: ${command}`);
|
|
740
|
+
showHelp();
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
// Ejecutar main
|
|
745
|
+
main().catch(err => {
|
|
746
|
+
error(`Error inesperado: ${err.message}`);
|
|
747
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-git-hooks",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.1",
|
|
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": {
|
|
@@ -35,6 +35,7 @@
|
|
|
35
35
|
"lib/",
|
|
36
36
|
"templates/",
|
|
37
37
|
"README.md",
|
|
38
|
+
"CHANGELOG.md",
|
|
38
39
|
"LICENSE"
|
|
39
40
|
]
|
|
40
41
|
}
|