claude-git-hooks 1.4.1 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,65 @@ Todos los cambios notables en este proyecto se documentarán en este archivo.
5
5
  El formato está basado en [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  y este proyecto adhiere a [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [1.5.0] - 2025-09-10
9
+
10
+ ### Added
11
+ - 🔍 Nuevo comando `claude-hooks analyze-diff [base]` para análisis de diferencias entre ramas
12
+ - Genera título de PR conciso (máx. 72 caracteres)
13
+ - Crea descripción detallada del PR con markdown
14
+ - Sugiere nombre de rama siguiendo formato tipo/descripcion
15
+ - Identifica tipo de cambio (feature/fix/refactor/docs/test/chore)
16
+ - Detecta breaking changes
17
+ - Proporciona notas de testing recomendado
18
+ - Guarda resultados en `.claude-pr-analysis.json`
19
+
20
+ ### Changed
21
+ - 🎯 Modo SonarQube ahora es el predeterminado (no hay selección interactiva)
22
+ - ⚡ Eliminada lógica de selección de modo del pre-commit hook
23
+ - 🗑️ Removidas referencias a guardado de preferencias `.claude-analysis-mode`
24
+
25
+ ### Fixed
26
+ - 🐛 Eliminado prompt interactivo incompatible con algunas consolas
27
+ - 🔧 Pre-commit ahora va directo al análisis sin solicitar input del usuario
28
+
29
+ ### Removed
30
+ - 🗑️ Eliminada selección interactiva de modo de análisis
31
+ - 🗑️ Removido comando `set-mode` (ya no es necesario)
32
+ - 🗑️ Eliminada variable de entorno `CLAUDE_ANALYSIS_MODE`
33
+
34
+ ## [1.4.2] - 2025-09-08
35
+
36
+ ### Added
37
+ - 🚫 Comentario `// SKIP-ANALYSIS` para excluir código del análisis
38
+ - Una línea: excluye la siguiente línea
39
+ - Bloque: código entre dos comentarios `// SKIP-ANALYSIS` es excluido
40
+ - 📝 Excepciones para Spring Framework en criterios de evaluación
41
+ - `@Autowired` no es considerado issue bloqueante (máximo severidad MAJOR)
42
+ - Inyección de dependencias con field injection es aceptable
43
+
44
+ ### Changed
45
+ - 📋 Actualizada documentación con casos de uso de commits clarificados
46
+ - 🎯 Templates de prompts actualizados con reglas de Spring Framework
47
+
48
+ ### Fixed
49
+ - 🔧 Clarificado que `git commit --no-verify -m "auto"` genera mensaje automático sin análisis
50
+
51
+ ## [1.4.1] - 2025-09-04
52
+
53
+ ### Changed
54
+ - 🚀 **BREAKING**: Eliminado modo estándar, ahora solo existe modo SonarQube
55
+ - ⚡ Prompts optimizados para Claude: reducción de ~80% en tokens manteniendo eficacia
56
+ - 🎯 Nuevo formato ultra-compacto de prompts usando notación concisa y directivas claras
57
+ - 📝 Templates simplificados: CLAUDE_ANALYSIS_PROMPT_SONAR.md, CLAUDE_PRE_COMMIT_SONAR.md, CLAUDE_RESOLUTION_PROMPT.md
58
+
59
+ ### Fixed
60
+ - 🐛 Mejorada la lógica de escritura para mejor formación del prompt de resolución de problemas críticos
61
+ - 🔧 Corregido el formato del prompt AI-friendly para resolución de issues
62
+
63
+ ### Removed
64
+ - 🗑️ Eliminados archivos del modo estándar: CLAUDE_PRE_COMMIT.md, CLAUDE_ANALYSIS_PROMPT.md
65
+ - 🗑️ Eliminada lógica de selección de modo (ahora siempre usa SonarQube)
66
+
8
67
  ## [1.4.0] - 2025-08-29
9
68
 
10
69
  ### Added
package/LICENSE CHANGED
File without changes
package/README.md CHANGED
@@ -201,89 +201,78 @@ Cuando se detectan problemas críticos:
201
201
  - **Auto-actualización**: Verificación automática de versiones antes de cada commit con prompt interactivo para actualizar
202
202
  - **Comando update**: `claude-hooks update` para actualizar manualmente a la última versión
203
203
  - **Modo debug**: `DEBUG=1 git commit` guarda respuestas en `debug-claude-response.json`
204
- - **Configuración persistente**: Guarda preferencias en `.claude-analysis-mode`
204
+ - **Análisis de PR**: Nuevo comando `analyze-diff` para generar información de Pull Requests
205
205
  - **Validación de dependencias**: Verifica que Claude CLI esté autenticado antes de ejecutar
206
206
 
207
207
  ## 🛠️ Uso
208
208
 
209
209
  **IMPORTANTE**: Todos los comandos git deben ejecutarse desde WSL.
210
210
 
211
- ### Commit normal
211
+ ### Casos de uso de commits
212
212
 
213
213
  ```bash
214
214
  # Desde WSL
215
215
  git add .
216
- git commit -m "feat: nueva funcionalidad"
217
- ```
218
216
 
219
- ### Commit con mensaje automático 🤖
217
+ # 1. Mensaje manual + análisis bloqueante
218
+ git commit -m "feat: nueva funcionalidad"
220
219
 
221
- ```bash
222
- # Desde WSL
223
- git add .
220
+ # 2. Mensaje automático + análisis bloqueante
224
221
  git commit -m "auto" # Claude generará el mensaje automáticamente
222
+
223
+ # 3. Mensaje manual sin análisis
224
+ git commit --no-verify -m "fix: corrección urgente"
225
+
226
+ # 4. Mensaje automático sin análisis
227
+ git commit --no-verify -m "auto"
225
228
  ```
226
229
 
227
230
  Claude analizará los cambios y generará un mensaje de commit siguiendo las convenciones (feat, fix, docs, etc.).
228
231
 
229
232
  **⚠️ Si la generación automática falla**: El commit se cancelará completamente. Simplemente ejecuta `git commit -m "tu mensaje"` manualmente.
230
233
 
231
- ### Modos de análisis 📊
232
-
233
- Puedes elegir entre dos formatos de análisis:
234
+ ### Excluir código del análisis con SKIP-ANALYSIS
234
235
 
235
- #### 🎯 Configurar Modo (Recomendado)
236
-
237
- ```bash
238
- # Cambiar a modo SonarQube (métricas y quality gate)
239
- claude-hooks set-mode sonar
236
+ Puedes excluir código específico del análisis usando comentarios `// SKIP-ANALYSIS`:
240
237
 
241
- # Cambiar a modo estándar (score y recomendaciones)
242
- claude-hooks set-mode standard
243
-
244
- # Ver modo actual
245
- claude-hooks status
246
- ```
238
+ ```java
239
+ // Excluir una sola línea
240
+ // SKIP-ANALYSIS
241
+ @Autowired private LegacyService legacyService; // Esta línea no será analizada
247
242
 
248
- #### 🔧 Variable de Entorno (Temporal)
249
-
250
- ```bash
251
- # Sobrescribir temporalmente el modo configurado
252
- export CLAUDE_ANALYSIS_MODE=sonar
253
- git commit -m "mensaje"
254
-
255
- # Volver al modo configurado
256
- unset CLAUDE_ANALYSIS_MODE
243
+ // Excluir un bloque de código
244
+ // SKIP-ANALYSIS
245
+ @Deprecated
246
+ public void methodWithKnownIssues() {
247
+ // Código legacy que no queremos que sea analizado
248
+ System.out.println("Debug temporal");
249
+ }
250
+ // SKIP-ANALYSIS
257
251
  ```
258
252
 
259
- #### 📋 Diferencias entre Modos
260
-
261
- **Modo Estándar**:
262
-
263
- - Análisis con puntuación del 1-10
264
- - Recomendaciones detalladas por categoría
265
- - Formato tradicional fácil de leer
266
-
267
- **Modo SonarQube**:
253
+ **Nota**: El código entre comentarios `SKIP-ANALYSIS` no será enviado a Claude para su análisis.
268
254
 
269
- - Quality Gate (PASSED/FAILED)
270
- - Métricas: Reliability, Security, Maintainability
271
- - Issues clasificados por severidad (Blocker, Critical, Major, Minor, Info)
272
- - Security hotspots
255
+ ### Análisis de diferencias entre ramas 🔍
273
256
 
274
- #### 🔄 Cambio de Modo Interactivo
257
+ Nueva funcionalidad para analizar cambios y generar información para Pull Requests:
275
258
 
276
259
  ```bash
277
- # Sin parámetros muestra ayuda interactiva
278
- claude-hooks set-mode
260
+ # Analizar diferencias con la rama main
261
+ claude-hooks analyze-diff
262
+
263
+ # Analizar diferencias con otra rama base
264
+ claude-hooks analyze-diff develop
279
265
  ```
280
266
 
281
- ### Saltar la revisión (usar con precaución)
267
+ Este comando genera automáticamente:
268
+ - 📝 Título de PR conciso (máx. 72 caracteres)
269
+ - 📄 Descripción detallada con markdown
270
+ - 🌿 Nombre de rama sugerido (formato: tipo/descripcion)
271
+ - 📋 Tipo de cambio (feature/fix/refactor/docs/test/chore)
272
+ - ⚠️ Indicador de breaking changes
273
+ - 🧪 Notas de testing recomendado
282
274
 
283
- ```bash
284
- # Desde WSL
285
- git commit --no-verify -m "fix: corrección urgente"
286
- ```
275
+ Los resultados se guardan en `.claude-pr-analysis.json` para fácil acceso.
287
276
 
288
277
  ### Modo debug
289
278
 
@@ -423,12 +412,6 @@ claude-hooks status
423
412
 
424
413
  ### Variables de entorno disponibles
425
414
 
426
- - **`CLAUDE_ANALYSIS_MODE`**: Selecciona el modo de análisis
427
-
428
- - Valores: `"standard"` o `"sonar"`
429
- - Ejemplo: `CLAUDE_ANALYSIS_MODE=sonar git commit -m "mensaje"`
430
- - Sobrescribe la preferencia guardada en `.claude-analysis-mode`
431
-
432
415
  - **`DEBUG`**: Activa el modo debug
433
416
  - Valores: `1` para activar
434
417
  - Ejemplo: `DEBUG=1 git commit -m "mensaje"`
package/bin/claude-hooks CHANGED
@@ -414,9 +414,7 @@ async function install(args) {
414
414
 
415
415
  // Copiar archivos de pautas y prompts a .claude
416
416
  const claudeFiles = [
417
- 'CLAUDE_PRE_COMMIT.md',
418
417
  'CLAUDE_PRE_COMMIT_SONAR.md',
419
- 'CLAUDE_ANALYSIS_PROMPT.md',
420
418
  'CLAUDE_ANALYSIS_PROMPT_SONAR.md',
421
419
  'CLAUDE_RESOLUTION_PROMPT.md'
422
420
  ];
@@ -446,6 +444,12 @@ async function install(args) {
446
444
  console.log('\nUso:');
447
445
  console.log(' git commit -m "auto" # Genera mensaje automáticamente');
448
446
  console.log(' git commit -m "mensaje" # Analiza código antes del commit');
447
+ console.log(' git commit --no-verify # Omite el análisis completamente');
448
+ console.log('\nExcluir código del análisis:');
449
+ console.log(' // SKIP-ANALYSIS # Excluye la siguiente línea');
450
+ console.log(' // SKIP-ANALYSIS # Entre dos comentarios excluye el bloque');
451
+ console.log(' ...código excluido...');
452
+ console.log(' // SKIP-ANALYSIS');
449
453
  console.log('\nPara más opciones: claude-hooks --help');
450
454
  }
451
455
 
@@ -636,7 +640,8 @@ function updateGitignore() {
636
640
  '.claude/',
637
641
  '.claude-analysis-mode',
638
642
  'debug-claude-response.json',
639
- 'claude_resolution_prompt.md'
643
+ 'claude_resolution_prompt.md',
644
+ '.claude-pr-analysis.json',
640
645
  ];
641
646
 
642
647
  let gitignoreContent = '';
@@ -790,6 +795,258 @@ function disable(hookName) {
790
795
  });
791
796
  }
792
797
 
798
+ // Comando analyze-diff
799
+ function analyzeDiff(args) {
800
+ if (!checkGitRepo()) {
801
+ error('No estás en un repositorio Git.');
802
+ return;
803
+ }
804
+
805
+ const currentBranch = execSync('git branch --show-current', { encoding: 'utf8' }).trim();
806
+
807
+ if (!currentBranch) {
808
+ error('No estás en una rama válida.');
809
+ return;
810
+ }
811
+
812
+ let baseBranch, compareWith, contextDescription;
813
+
814
+ if (args[0]) {
815
+ // Caso con argumento: comparar rama actual vs rama especificada
816
+ baseBranch = args[0];
817
+ compareWith = `${baseBranch}...HEAD`;
818
+ contextDescription = `${currentBranch} vs ${baseBranch}`;
819
+
820
+ // Verificar que la rama base existe
821
+ try {
822
+ execSync(`git rev-parse --verify ${baseBranch}`, { stdio: 'ignore' });
823
+ } catch (e) {
824
+ error(`La rama ${baseBranch} no existe.`);
825
+ return;
826
+ }
827
+ } else {
828
+ // Caso sin argumento: comparar local vs remoto
829
+ try {
830
+ // Obtener la rama remota tracked
831
+ const remoteBranch = execSync(`git rev-parse --abbrev-ref ${currentBranch}@{upstream}`, { encoding: 'utf8' }).trim();
832
+
833
+ // Actualizar referencias remotas
834
+ execSync('git fetch', { stdio: 'ignore' });
835
+
836
+ baseBranch = remoteBranch;
837
+ compareWith = `HEAD..${remoteBranch}`;
838
+ contextDescription = `cambios locales sin pushear en ${currentBranch}`;
839
+
840
+ info(`Comparando cambios locales vs remoto: ${remoteBranch}`);
841
+ } catch (e) {
842
+ // Si no hay rama remota, usar develop como fallback
843
+ baseBranch = 'develop';
844
+ compareWith = `${baseBranch}...HEAD`;
845
+ contextDescription = `${currentBranch} vs ${baseBranch} (sin rama remota)`;
846
+
847
+ try {
848
+ execSync(`git rev-parse --verify ${baseBranch}`, { stdio: 'ignore' });
849
+ warning(`No hay rama remota configurada. Comparando con ${baseBranch}.`);
850
+ } catch (e2) {
851
+ baseBranch = 'main';
852
+ compareWith = `${baseBranch}...HEAD`;
853
+ contextDescription = `${currentBranch} vs ${baseBranch} (fallback)`;
854
+
855
+ try {
856
+ execSync(`git rev-parse --verify ${baseBranch}`, { stdio: 'ignore' });
857
+ warning(`No hay develop ni rama remota. Comparando con main.`);
858
+ } catch (e3) {
859
+ error('No se pudo encontrar una rama de comparación válida.');
860
+ return;
861
+ }
862
+ }
863
+ }
864
+ }
865
+
866
+ info(`Analizando: ${contextDescription}...`);
867
+
868
+ // Obtener los archivos modificados
869
+ let diffFiles;
870
+ try {
871
+ // Para cambios locales sin pushear, invertir la comparación
872
+ if (!args[0] && compareWith.includes('HEAD..')) {
873
+ diffFiles = execSync(`git diff ${compareWith} --name-only`, { encoding: 'utf8' }).trim();
874
+ } else {
875
+ diffFiles = execSync(`git diff ${compareWith} --name-only`, { encoding: 'utf8' }).trim();
876
+ }
877
+
878
+ if (!diffFiles) {
879
+ // Verificar si hay cambios staged o unstaged
880
+ const stagedFiles = execSync('git diff --cached --name-only', { encoding: 'utf8' }).trim();
881
+ const unstagedFiles = execSync('git diff --name-only', { encoding: 'utf8' }).trim();
882
+
883
+ if (stagedFiles || unstagedFiles) {
884
+ warning('No hay diferencias con el remoto, pero tienes cambios locales sin commitear.');
885
+ console.log('Cambios staged:', stagedFiles || 'ninguno');
886
+ console.log('Cambios unstaged:', unstagedFiles || 'ninguno');
887
+ } else {
888
+ success('✅ No hay diferencias. Tu rama está sincronizada.');
889
+ }
890
+ return;
891
+ }
892
+ } catch (e) {
893
+ error('Error al obtener las diferencias: ' + e.message);
894
+ return;
895
+ }
896
+
897
+ // Obtener el diff completo
898
+ let fullDiff, commits;
899
+ try {
900
+ if (!args[0] && compareWith.includes('HEAD..')) {
901
+ fullDiff = execSync(`git diff ${compareWith}`, { encoding: 'utf8' });
902
+ commits = execSync(`git log ${baseBranch}..HEAD --oneline`, { encoding: 'utf8' }).trim();
903
+ } else {
904
+ fullDiff = execSync(`git diff ${compareWith}`, { encoding: 'utf8' });
905
+ commits = execSync(`git log ${baseBranch}..HEAD --oneline`, { encoding: 'utf8' }).trim();
906
+ }
907
+ } catch (e) {
908
+ error('Error al obtener diff o commits: ' + e.message);
909
+ return;
910
+ }
911
+
912
+ // Crear el prompt para Claude
913
+ const tempDir = `/tmp/claude-analyze-${Date.now()}`;
914
+ fs.mkdirSync(tempDir, { recursive: true });
915
+
916
+ const promptFile = path.join(tempDir, 'prompt.txt');
917
+ const prompt = `Analiza los siguientes cambios. CONTEXTO: ${contextDescription}
918
+
919
+ Por favor genera:
920
+ 1. Un título de PR conciso y descriptivo (máximo 72 caracteres)
921
+ 2. Una descripción detallada del PR que incluya:
922
+ - Resumen de los cambios
923
+ - Motivación/contexto
924
+ - Tipo de cambio (feature/fix/refactor/docs/etc)
925
+ - Testing recomendado
926
+ 3. Un nombre de rama sugerido siguiendo el formato: tipo/descripcion-corta (ejemplo: feature/add-user-auth, fix/memory-leak)
927
+
928
+ IMPORTANTE: Si estos son cambios locales sin pushear, el nombre de rama sugerido debe ser para crear una nueva rama desde la actual.
929
+
930
+ Responde EXCLUSIVAMENTE con un JSON válido con esta estructura:
931
+ {
932
+ "prTitle": "título del PR",
933
+ "prDescription": "descripción detallada del PR con markdown",
934
+ "suggestedBranchName": "tipo/nombre-rama-sugerido",
935
+ "changeType": "feature|fix|refactor|docs|test|chore",
936
+ "breakingChanges": false,
937
+ "testingNotes": "notas sobre testing necesario"
938
+ }
939
+
940
+ === COMMITS ===
941
+ ${commits}
942
+
943
+ === ARCHIVOS MODIFICADOS ===
944
+ ${diffFiles}
945
+
946
+ === DIFF COMPLETO ===
947
+ ${fullDiff.substring(0, 50000)} ${fullDiff.length > 50000 ? '\n... (diff truncado)' : ''}`;
948
+
949
+ fs.writeFileSync(promptFile, prompt);
950
+
951
+ info('Enviando a Claude para análisis...');
952
+
953
+ try {
954
+ const response = execSync(`claude < "${promptFile}"`, { encoding: 'utf8', maxBuffer: 1024 * 1024 * 10 });
955
+
956
+ // Extraer el JSON de la respuesta
957
+ const jsonMatch = response.match(/\{[\s\S]*\}/);
958
+ if (!jsonMatch) {
959
+ error('No se recibió una respuesta JSON válida de Claude.');
960
+ console.log('Respuesta completa:', response);
961
+ return;
962
+ }
963
+
964
+ let result;
965
+ try {
966
+ result = JSON.parse(jsonMatch[0]);
967
+ } catch (e) {
968
+ error('Error al parsear la respuesta JSON: ' + e.message);
969
+ console.log('JSON recibido:', jsonMatch[0]);
970
+ return;
971
+ }
972
+
973
+ // Mostrar los resultados
974
+ console.log('');
975
+ console.log('═══════════════════════════════════════════════════════════════');
976
+ console.log(' ANÁLISIS DE DIFERENCIAS ');
977
+ console.log('═══════════════════════════════════════════════════════════════');
978
+ console.log('');
979
+
980
+ console.log(`🔍 ${colors.blue}Contexto:${colors.reset} ${contextDescription}`);
981
+ console.log(`📊 ${colors.blue}Archivos modificados:${colors.reset} ${diffFiles.split('\n').length}`);
982
+ console.log('');
983
+
984
+ console.log(`📝 ${colors.green}Título del PR:${colors.reset}`);
985
+ console.log(` ${result.prTitle}`);
986
+ console.log('');
987
+
988
+ console.log(`🌿 ${colors.green}Nombre de rama sugerido:${colors.reset}`);
989
+ console.log(` ${result.suggestedBranchName}`);
990
+ console.log('');
991
+
992
+ console.log(`📋 ${colors.green}Tipo de cambio:${colors.reset} ${result.changeType}`);
993
+
994
+ if (result.breakingChanges) {
995
+ console.log(`⚠️ ${colors.yellow}Breaking Changes: SÍ${colors.reset}`);
996
+ }
997
+
998
+ console.log('');
999
+ console.log(`📄 ${colors.green}Descripción del PR:${colors.reset}`);
1000
+ console.log('───────────────────────────────────────────────────────────────');
1001
+ console.log(result.prDescription);
1002
+ console.log('───────────────────────────────────────────────────────────────');
1003
+
1004
+ if (result.testingNotes) {
1005
+ console.log('');
1006
+ console.log(`🧪 ${colors.green}Notas de Testing:${colors.reset}`);
1007
+ console.log(result.testingNotes);
1008
+ }
1009
+
1010
+ // Guardar los resultados en un archivo con contexto
1011
+ const outputData = {
1012
+ ...result,
1013
+ context: {
1014
+ currentBranch,
1015
+ baseBranch,
1016
+ contextDescription,
1017
+ filesChanged: diffFiles.split('\n').length,
1018
+ timestamp: new Date().toISOString()
1019
+ }
1020
+ };
1021
+
1022
+ const outputFile = '.claude-pr-analysis.json';
1023
+ fs.writeFileSync(outputFile, JSON.stringify(outputData, null, 2));
1024
+ console.log('');
1025
+ info(`Resultados guardados en ${outputFile}`);
1026
+
1027
+ // Sugerencias contextuales
1028
+ console.log('');
1029
+ if (!args[0] && contextDescription.includes('cambios locales sin pushear')) {
1030
+ // Caso de cambios locales sin pushear
1031
+ console.log(`💡 ${colors.yellow}Para crear nueva rama con estos cambios:${colors.reset}`);
1032
+ console.log(` git checkout -b ${result.suggestedBranchName}`);
1033
+ console.log(` git push -u origin ${result.suggestedBranchName}`);
1034
+ } else if (currentBranch !== result.suggestedBranchName) {
1035
+ // Caso normal de comparación entre ramas
1036
+ console.log(`💡 ${colors.yellow}Para renombrar tu rama actual:${colors.reset}`);
1037
+ console.log(` git branch -m ${result.suggestedBranchName}`);
1038
+ }
1039
+
1040
+ console.log(`💡 ${colors.yellow}Tip:${colors.reset} Usa esta información para crear tu PR en GitHub.`);
1041
+
1042
+ } catch (e) {
1043
+ error('Error al ejecutar Claude: ' + e.message);
1044
+ } finally {
1045
+ // Limpiar archivos temporales
1046
+ fs.rmSync(tempDir, { recursive: true, force: true });
1047
+ }
1048
+ }
1049
+
793
1050
  // Comando status
794
1051
  function status() {
795
1052
  if (!checkGitRepo()) {
@@ -870,31 +1127,26 @@ function setMode(mode) {
870
1127
  error('No estás en un repositorio Git.');
871
1128
  }
872
1129
 
873
- const validModes = ['standard', 'sonar'];
1130
+ const validModes = ['sonar'];
874
1131
 
875
1132
  if (!mode) {
876
1133
  // Modo interactivo
877
- console.log('\nSelecciona el modo de análisis:');
878
- console.log('1) Standard - Formato clásico con score y recomendaciones');
879
- console.log('2) SonarQube - Formato similar a SonarQube con métricas');
880
- console.log('\nEjemplo de uso: claude-hooks set-mode standard');
881
- console.log(' claude-hooks set-mode sonar');
1134
+ console.log('\nModo de análisis configurado:');
1135
+ console.log('SonarQube - Formato con métricas y quality gate');
1136
+ console.log('\nEjemplo de uso: claude-hooks set-mode sonar');
882
1137
  return;
883
1138
  }
884
1139
 
885
1140
  if (!validModes.includes(mode)) {
886
- error(`Modo inválido: ${mode}. Usa 'standard' o 'sonar'`);
1141
+ error(`Modo inválido: ${mode}. Usa 'sonar'`);
887
1142
  }
888
1143
 
889
1144
  // Guardar el modo
890
1145
  fs.writeFileSync('.claude-analysis-mode', mode);
891
1146
 
892
1147
  if (mode === 'sonar') {
893
- success('Modo cambiado a: SonarQube');
1148
+ success('Modo configurado: SonarQube');
894
1149
  info('Los commits usarán formato SonarQube con métricas y quality gate');
895
- } else {
896
- success('Modo cambiado a: Estándar');
897
- info('Los commits usarán formato clásico con score y recomendaciones');
898
1150
  }
899
1151
  }
900
1152
 
@@ -994,8 +1246,8 @@ Comandos:
994
1246
  uninstall Desinstala los hooks del repositorio
995
1247
  enable [hook] Habilita hooks (todos o uno específico)
996
1248
  disable [hook] Deshabilita hooks (todos o uno específico)
997
- set-mode [standard|sonar] Cambia el modo de análisis
998
1249
  status Muestra el estado de los hooks
1250
+ analyze-diff [base] Analiza diferencias entre ramas y genera PR info
999
1251
  help Muestra esta ayuda
1000
1252
 
1001
1253
  Hooks disponibles:
@@ -1006,11 +1258,28 @@ Ejemplos:
1006
1258
  claude-hooks install # Instala todos los hooks
1007
1259
  claude-hooks install --skip-auth # Instala sin verificar autenticación
1008
1260
  claude-hooks update # Actualiza a la última versión
1009
- claude-hooks set-mode sonar # Cambiar a modo SonarQube
1010
- claude-hooks set-mode standard # Cambiar a modo estándar
1011
1261
  claude-hooks disable pre-commit # Deshabilita solo pre-commit
1012
1262
  claude-hooks enable # Habilita todos los hooks
1013
1263
  claude-hooks status # Ver estado actual
1264
+ claude-hooks analyze-diff main # Analiza diferencias con main
1265
+
1266
+ Casos de uso de commits:
1267
+ git commit -m "mensaje" # Mensaje manual + análisis bloqueante
1268
+ git commit -m "auto" # Mensaje automático + análisis bloqueante
1269
+ git commit --no-verify -m "auto" # Mensaje automático sin análisis
1270
+ git commit --no-verify -m "msg" # Mensaje manual sin análisis
1271
+
1272
+ Caso de uso analyze-diff:
1273
+ claude-hooks analyze-diff main # Analiza cambios vs main y genera:
1274
+ → Título PR: "feat: add user authentication module"
1275
+ → Descripción PR: "## Summary\n- Added JWT authentication..."
1276
+ → Rama sugerida: "feature/user-authentication"
1277
+
1278
+ Excluir código del análisis:
1279
+ // SKIP-ANALYSIS # Excluye la siguiente línea del análisis
1280
+ // SKIP-ANALYSIS # Entre dos comentarios excluye el bloque
1281
+ ...código excluido...
1282
+ // SKIP-ANALYSIS
1014
1283
 
1015
1284
  Más información: https://github.com/pablorovito/claude-git-hooks
1016
1285
  `);
@@ -1037,12 +1306,12 @@ async function main() {
1037
1306
  case 'disable':
1038
1307
  disable(args[1]);
1039
1308
  break;
1040
- case 'set-mode':
1041
- setMode(args[1]);
1042
- break;
1043
1309
  case 'status':
1044
1310
  status();
1045
1311
  break;
1312
+ case 'analyze-diff':
1313
+ analyzeDiff(args.slice(1));
1314
+ break;
1046
1315
  case 'help':
1047
1316
  case '--help':
1048
1317
  case '-h':
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-git-hooks",
3
- "version": "1.4.1",
3
+ "version": "1.5.0",
4
4
  "description": "Git hooks con Claude CLI para análisis de código y generación automática de mensajes de commit",
5
5
  "main": "lib/index.js",
6
6
  "bin": {
@@ -1,61 +1,56 @@
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:
1
+ ROLE:SeniorCodeReviewer,SonarQube-experienced
2
+ TASK:Analyze code->JSON only
3
+ CRITICAL:NoTextOutsideJSON,ValidJSON
6
4
 
5
+ OUTPUT_SCHEMA:
7
6
  ```json
8
7
  {
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
8
+ "QUALITY_GATE":"PASSED|FAILED",
9
+ "approved":bool,
10
+ "score":1-10,
11
+ "metrics":{
12
+ "reliability":"A-E",
13
+ "security":"A-E",
14
+ "maintainability":"A-E",
15
+ "coverage":0-100,
16
+ "duplications":0-100,
17
+ "complexity":int
19
18
  },
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
19
+ "issues":{
20
+ "blocker":int,
21
+ "critical":int,
22
+ "major":int,
23
+ "minor":int,
24
+ "info":int
26
25
  },
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
26
+ "details":[{
27
+ "severity":"BLOCKER|CRITICAL|MAJOR|MINOR|INFO",
28
+ "type":"BUG|VULNERABILITY|CODE_SMELL",
29
+ "file":"path",
30
+ "line":int,
31
+ "method":"name",
32
+ "message":"desc",
33
+ "rule":"optional"
34
+ }],
35
+ "blockingIssues":[{
36
+ "description":"text",
37
+ "file":"path",
38
+ "line":int,
39
+ "method":"name",
40
+ "severity":"blocker|critical"
41
+ }],
42
+ "securityHotspots":int
48
43
  }
49
44
  ```
50
45
 
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
- - preciso con las localizaciones para facilitar la resolución automatizada
46
+ RULES:
47
+ - blockingIssues=ALWAYS_ARRAY_OF_OBJECTS(never_strings)
48
+ - blockingIssues=ALL(details.severity∈{BLOCKER,CRITICAL})
49
+ - Each_blockingIssue_MUST_have:description,file,line,method,severity
50
+ - QUALITY_GATE=FAILED if(blocker>0||critical>0)
51
+ - details:ALWAYS_include:file,line,method
52
+ - ratings:A(0issues),B(1-2minor),C(1major|3-5minor),D(2+major|1critical),E(1+blocker|2+critical)
53
+ - IMPORTANT: @Autowired usage in Spring is NOT a BLOCKER/CRITICAL issue (max severity: MAJOR)
54
+ - Spring dependency injection patterns (@Autowired) should NOT block commits
60
55
 
61
- A continuación se detallan las pautas y los cambios:
56
+ ANALYZE_BELOW:
@@ -1,96 +1,41 @@
1
- # Pautas de Evaluación de Código - Formato SonarQube
2
-
3
- ## Objetivo
4
- Evaluar el código modificado siguiendo métricas similares a SonarQube para asegurar calidad, confiabilidad y mantenibilidad.
5
-
6
- ## Métricas de Evaluación
7
-
8
- ### Reliability (Confiabilidad)
9
- - Bugs potenciales
10
- - Código que podría fallar en runtime
11
- - Manejo inadecuado de excepciones
12
- - Condiciones de carrera
13
-
14
- ### Security (Seguridad)
15
- - Vulnerabilidades de seguridad
16
- - Credenciales expuestas
17
- - Validación de inputs
18
- - Inyección de código
19
-
20
- ### Maintainability (Mantenibilidad)
21
- - Code smells
22
- - Complejidad ciclomática
23
- - Duplicación de código
24
- - Deuda técnica
25
-
26
- ### Métricas Adicionales
27
- - **Coverage**: Porcentaje estimado de cobertura de pruebas (0-100)
28
- - **Duplications**: Porcentaje estimado de código duplicado (0-100)
29
- - **Complexity**: Complejidad ciclomática promedio del código
30
-
31
- ## Clasificación de Issues
32
-
33
- ### Severidad
34
- - **BLOCKER**: Debe corregirse inmediatamente
35
- - **CRITICAL**: Debe corregirse antes del release
36
- - **MAJOR**: Debe corregirse pronto
37
- - **MINOR**: Debería corregirse eventualmente
38
- - **INFO**: Sugerencia o mejora opcional
39
-
40
- ## Formato de Respuesta Esperado
41
-
42
- Responde ÚNICAMENTE con un JSON válido siguiendo esta estructura:
43
-
44
- ```json
45
- {
46
- "QUALITY_GATE": "PASSED/FAILED",
47
- "metrics": {
48
- "reliability": "A/B/C/D/E",
49
- "security": "A/B/C/D/E",
50
- "maintainability": "A/B/C/D/E",
51
- "coverage": 75,
52
- "duplications": 5,
53
- "complexity": 15
54
- },
55
- "issues": {
56
- "blocker": 0,
57
- "critical": 0,
58
- "major": 0,
59
- "minor": 0,
60
- "info": 0
61
- },
62
- "details": [
63
- {
64
- "severity": "CRITICAL",
65
- "type": "BUG/VULNERABILITY/CODE_SMELL",
66
- "message": "Descripción del problema",
67
- "file": "archivo.java",
68
- "line": 42
69
- }
70
- ],
71
- "securityHotspots": 0,
72
- "approved": true/false,
73
- "blockingIssues": ["Lista de problemas bloqueantes si existen"]
74
- }
75
- ```
76
-
77
- ## Reglas para Quality Gate
78
-
79
- - **PASSED** si:
80
- - No hay issues BLOCKER
81
- - Issues CRITICAL <= 1
82
- - Todas las métricas son A o B
83
-
84
- - **FAILED** si:
85
- - Hay al menos 1 issue BLOCKER
86
- - Issues CRITICAL > 1
87
- - Alguna métrica es D o E
88
- - Security hotspots > 0
89
-
90
- ## Rating de Métricas
91
-
92
- - **A**: Excelente (0 issues)
93
- - **B**: Bueno (1-2 issues minor)
94
- - **C**: Aceptable (1 issue major o 3-5 minor)
95
- - **D**: Pobre (2+ major o 1 critical)
96
- - **E**: Muy pobre (1+ blocker o 2+ critical)
1
+ # SONARQUBE_EVAL_CRITERIA
2
+
3
+ ## EXCEPTIONS_AND_ALLOWED_PATTERNS
4
+ ### Spring Framework Patterns (NOT blocking issues)
5
+ - @Autowired for dependency injection is ALLOWED and NOT a blocking issue
6
+ - Field injection with @Autowired is acceptable in Spring Boot projects
7
+ - Constructor injection is preferred but @Autowired is NOT a blocker
8
+
9
+ ## METRICS
10
+ ### Reliability
11
+ - bugs,runtime_failures,exception_handling,race_conditions
12
+
13
+ ### Security
14
+ - vulns,exposed_creds,input_validation,injection
15
+
16
+ ### Maintainability
17
+ - code_smells,cyclomatic_complexity,duplication,tech_debt
18
+ - NOTE: @Autowired usage is NOT considered a code smell for blocking purposes
19
+
20
+ ### Additional
21
+ - coverage:estimated_test_coverage(0-100)
22
+ - duplications:estimated_duplication(0-100)
23
+ - complexity:avg_cyclomatic_complexity
24
+
25
+ ## SEVERITIES
26
+ - BLOCKER:fix_immediately
27
+ - CRITICAL:fix_before_release
28
+ - MAJOR:fix_soon
29
+ - MINOR:fix_eventually
30
+ - INFO:optional_improvement
31
+
32
+ ## QUALITY_GATE
33
+ PASSED:no_blockers AND critical<=1 AND all_metrics∈{A,B}
34
+ FAILED:blocker>=1 OR critical>1 OR any_metric∈{D,E} OR security_hotspots>0
35
+
36
+ ## RATINGS
37
+ A:0_issues
38
+ B:1-2_minor
39
+ C:1_major|3-5_minor
40
+ D:2+_major|1_critical
41
+ E:1+_blocker|2+_critical
@@ -1,46 +1,33 @@
1
- # AI Resolution Assistant Prompt
1
+ # FIX_CRITICAL_ISSUES
2
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
3
+ CONTEXT:
4
+ repo:{{REPO_NAME}}
5
+ branch:{{BRANCH_NAME}}
6
+ commit:{{COMMIT_SHA}}
7
+ files:{{FILE_COUNT}}
8
+ mode:{{ANALYSIS_MODE}}
13
9
 
10
+ CRITICAL_ISSUES:
14
11
  {{BLOCKING_ISSUES}}
15
12
 
16
- ## Affected Files Content
17
-
13
+ AFFECTED_FILES:
18
14
  {{FILE_CONTENTS}}
19
15
 
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
16
+ INSTRUCTIONS:
17
+ 1.Navigate→exact_file:line
18
+ 2.Apply_minimal_fix
19
+ 3.Maintain_style/conventions
20
+ 4.No_unrelated_refactoring
33
21
 
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)
22
+ OUTPUT:
23
+ - diff_format
24
+ - one_line_explanation_max
38
25
 
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)
26
+ PRIORITY:
27
+ 1.security_vulns
28
+ 2.null_ptr/runtime
29
+ 3.logic_errors
30
+ 4.performance
31
+ 5.code_quality
45
32
 
46
- Begin fixing the issues in order of severity.
33
+ START_FIXING:
File without changes
@@ -109,7 +109,7 @@ generate_resolution_prompt() {
109
109
  sed -i "s|{{BRANCH_NAME}}|${BRANCH_NAME}|g" "$RESOLUTION_FILE"
110
110
  sed -i "s|{{COMMIT_SHA}}|${COMMIT_SHA}|g" "$RESOLUTION_FILE"
111
111
  sed -i "s|{{FILE_COUNT}}|${FILE_COUNT}|g" "$RESOLUTION_FILE"
112
- sed -i "s|{{ANALYSIS_MODE}}|${ANALYSIS_MODE:-standard}|g" "$RESOLUTION_FILE"
112
+ sed -i "s|{{ANALYSIS_MODE}}|sonar|g" "$RESOLUTION_FILE"
113
113
 
114
114
  # Crear archivo temporal para issues formateados
115
115
  local TEMP_ISSUES_FILE=$(mktemp)
@@ -149,65 +149,17 @@ generate_resolution_prompt() {
149
149
  echo
150
150
  }
151
151
 
152
- # Detectar qué archivo de pautas usar
153
- # Prioridad: variable de entorno > archivo > pregunta interactiva
154
- if [ -n "$CLAUDE_ANALYSIS_MODE" ]; then
155
- ANALYSIS_MODE="$CLAUDE_ANALYSIS_MODE"
156
- elif [ -f ".claude-analysis-mode" ]; then
157
- ANALYSIS_MODE=$(cat .claude-analysis-mode)
158
- else
159
- ANALYSIS_MODE=""
160
- fi
152
+ # Usar siempre modo SonarQube
153
+ ANALYSIS_MODE="sonar"
161
154
 
162
- # Verificar si el usuario quiere cambiar el modo (con --change-mode)
163
- if [ "$1" = "--change-mode" ]; then
164
- ANALYSIS_MODE=""
165
- fi
155
+ # Configurar archivos para modo SonarQube
156
+ GUIDELINES_FILE=".claude/CLAUDE_PRE_COMMIT_SONAR.md"
157
+ PROMPT_TEMPLATE=".claude/CLAUDE_ANALYSIS_PROMPT_SONAR.md"
158
+ log "Usando modo de análisis: SonarQube"
166
159
 
167
- # Si no hay modo guardado o es inválido, preguntar
168
- if [ "$ANALYSIS_MODE" != "standard" ] && [ "$ANALYSIS_MODE" != "sonar" ]; then
169
- echo
170
- echo "🔍 Selecciona el formato de análisis de código:"
171
- echo "1) Estándar - Formato clásico con recomendaciones"
172
- echo "2) SonarQube - Formato similar a SonarQube con métricas"
173
- echo
174
-
175
- # Intentar leer desde terminal
176
- if [ -t 0 ] && [ -c /dev/tty ]; then
177
- read -p "Opción (1 o 2): " -n 1 -r </dev/tty
178
- echo
179
- else
180
- # Si no hay terminal interactiva, usar modo estándar por defecto
181
- echo "No se puede leer input interactivo, usando modo estándar por defecto"
182
- REPLY="1"
183
- fi
184
-
185
- if [ "$REPLY" = "2" ]; then
186
- ANALYSIS_MODE="sonar"
187
- GUIDELINES_FILE=".claude/CLAUDE_PRE_COMMIT_SONAR.md"
188
- PROMPT_TEMPLATE=".claude/CLAUDE_ANALYSIS_PROMPT_SONAR.md"
189
- else
190
- ANALYSIS_MODE="standard"
191
- GUIDELINES_FILE=".claude/CLAUDE_PRE_COMMIT.md"
192
- PROMPT_TEMPLATE=".claude/CLAUDE_ANALYSIS_PROMPT.md"
193
- fi
194
-
195
- # Guardar preferencia para futuros commits
196
- echo "$ANALYSIS_MODE" > .claude-analysis-mode
197
- echo -e "${GREEN}✓ Modo $ANALYSIS_MODE guardado para futuros commits${NC}"
198
- echo -e "${YELLOW}Tip: Para cambiar el modo, ejecuta: .git/hooks/pre-commit --change-mode${NC}"
199
- echo
200
- else
201
- # Usar modo guardado
202
- if [ "$ANALYSIS_MODE" = "sonar" ]; then
203
- GUIDELINES_FILE=".claude/CLAUDE_PRE_COMMIT_SONAR.md"
204
- PROMPT_TEMPLATE=".claude/CLAUDE_ANALYSIS_PROMPT_SONAR.md"
205
- log "Usando modo de análisis: SonarQube"
206
- else
207
- GUIDELINES_FILE=".claude/CLAUDE_PRE_COMMIT.md"
208
- PROMPT_TEMPLATE=".claude/CLAUDE_ANALYSIS_PROMPT.md"
209
- log "Usando modo de análisis: Estándar"
210
- fi
160
+ # Guardar preferencia si no existe
161
+ if [ ! -f ".claude-analysis-mode" ]; then
162
+ echo "sonar" > .claude-analysis-mode
211
163
  fi
212
164
 
213
165
  # Verificar que el template de prompt existe
@@ -254,6 +206,46 @@ fi
254
206
 
255
207
  log "Archivos Java/config a revisar: $(echo "$JAVA_FILES" | wc -l)"
256
208
 
209
+ # Función para filtrar contenido con SKIP-ANALYSIS
210
+ filter_skip_analysis() {
211
+ local file_content="$1"
212
+ local filtered_content=""
213
+ local skip_next_line=false
214
+ local inside_skip_block=false
215
+
216
+ while IFS= read -r line; do
217
+ # Detectar inicio de bloque SKIP-ANALYSIS
218
+ if echo "$line" | grep -q "// SKIP-ANALYSIS"; then
219
+ if [ "$inside_skip_block" = true ]; then
220
+ # Fin del bloque
221
+ inside_skip_block=false
222
+ else
223
+ # Inicio del bloque o línea única
224
+ inside_skip_block=true
225
+ skip_next_line=true
226
+ fi
227
+ continue
228
+ fi
229
+
230
+ # Si estamos dentro de un bloque, saltar la línea
231
+ if [ "$inside_skip_block" = true ]; then
232
+ continue
233
+ fi
234
+
235
+ # Si debemos saltar la siguiente línea (comentario único)
236
+ if [ "$skip_next_line" = true ]; then
237
+ skip_next_line=false
238
+ inside_skip_block=false
239
+ continue
240
+ fi
241
+
242
+ # Agregar línea al contenido filtrado
243
+ filtered_content="${filtered_content}${line}"$'\n'
244
+ done <<< "$file_content"
245
+
246
+ echo "$filtered_content"
247
+ }
248
+
257
249
  # Construir el prompt para análisis de código
258
250
  PROMPT_FILE="$TEMP_DIR/code_review_prompt.txt"
259
251
 
@@ -278,14 +270,20 @@ for FILE in $JAVA_FILES; do
278
270
 
279
271
  echo -e "\n--- Archivo: $FILE ---" >> "$PROMPT_FILE"
280
272
 
281
- # Mostrar el diff del archivo
273
+ # Obtener el diff y filtrarlo
274
+ DIFF_CONTENT=$(git diff --cached "$FILE" 2>/dev/null || echo "No se pudo obtener diff")
275
+ FILTERED_DIFF=$(filter_skip_analysis "$DIFF_CONTENT")
276
+
277
+ # Mostrar el diff filtrado del archivo
282
278
  echo -e "\nDiff:" >> "$PROMPT_FILE"
283
- git diff --cached "$FILE" >> "$PROMPT_FILE" 2>/dev/null || echo "No se pudo obtener diff"
279
+ echo "$FILTERED_DIFF" >> "$PROMPT_FILE"
284
280
 
285
- # Si es un archivo nuevo, mostrar contenido completo
281
+ # Si es un archivo nuevo, mostrar contenido completo filtrado
286
282
  if git diff --cached --name-status | grep "^A.*$FILE" > /dev/null; then
287
283
  echo -e "\nContenido completo (archivo nuevo):" >> "$PROMPT_FILE"
288
- git show ":$FILE" >> "$PROMPT_FILE" 2>/dev/null || cat "$FILE" >> "$PROMPT_FILE"
284
+ FILE_CONTENT=$(git show ":$FILE" 2>/dev/null || cat "$FILE")
285
+ FILTERED_CONTENT=$(filter_skip_analysis "$FILE_CONTENT")
286
+ echo "$FILTERED_CONTENT" >> "$PROMPT_FILE"
289
287
  fi
290
288
 
291
289
  FILE_COUNT=$((FILE_COUNT + 1))
File without changes
@@ -1,44 +0,0 @@
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:
@@ -1,63 +0,0 @@
1
- # Pautas de Evaluación de Código - Claude Pre-commit Hook
2
-
3
- ## Objetivo
4
- Evaluar el código modificado antes del commit para asegurar calidad, mantenibilidad y adherencia a buenas prácticas.
5
-
6
- ## Criterios de Evaluación
7
-
8
- ### 1. Calidad del Código (40%)
9
- - **Claridad y legibilidad**: El código es fácil de entender
10
- - **Nombres descriptivos**: Variables, métodos y clases tienen nombres significativos
11
- - **Estructura lógica**: El código está bien organizado
12
- - **Principio DRY**: No hay duplicación innecesaria
13
- - **Complejidad**: Funciones y métodos no son excesivamente complejos
14
-
15
- ### 2. Buenas Prácticas (30%)
16
- - **SOLID principles**: Adherencia cuando aplique
17
- - **Patrones de diseño**: Uso apropiado cuando sea necesario
18
- - **Manejo de errores**: Excepciones manejadas correctamente
19
- - **Validación de inputs**: Datos de entrada validados
20
- - **Separación de concerns**: Responsabilidades bien definidas
21
-
22
- ### 3. Seguridad (20%)
23
- - **No hay credenciales hardcodeadas**
24
- - **Validación de datos de usuario**
25
- - **No hay vulnerabilidades obvias** (SQL injection, XSS, etc.)
26
- - **Manejo seguro de datos sensibles**
27
-
28
- ### 4. Performance (10%)
29
- - **No hay operaciones obviamente ineficientes**
30
- - **Uso apropiado de estructuras de datos**
31
- - **Queries optimizadas** (cuando aplique)
32
-
33
- ## Formato de Respuesta Esperado
34
-
35
- Responde ÚNICAMENTE con un JSON válido siguiendo esta estructura:
36
-
37
- ```json
38
- {
39
- "approved": true/false,
40
- "score": 8,
41
- "recommendations": [
42
- "Sugerencia 1",
43
- "Sugerencia 2"
44
- ],
45
- "blockingIssues": [
46
- "Problema crítico 1 (si existe)"
47
- ],
48
- "details": "Información adicional detallada (opcional)"
49
- }
50
- ```
51
-
52
- ## Reglas de Aprobación
53
-
54
- - **approved = true** si score >= 7 Y no hay blockingIssues
55
- - **approved = false** si score < 7 O hay blockingIssues
56
-
57
- ## Problemas que SIEMPRE son Blocking Issues
58
-
59
- 1. Credenciales o tokens hardcodeados
60
- 2. Vulnerabilidades de seguridad evidentes
61
- 3. Código que podría causar pérdida de datos
62
- 4. Errores de sintaxis o código que no compilaría
63
- 5. Eliminación de tests sin justificación