claude-git-hooks 2.6.2 → 2.7.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 +86 -0
- package/README.md +17 -12
- package/bin/claude-hooks +16 -2
- package/lib/config.js +2 -2
- package/lib/hooks/pre-commit.js +11 -14
- package/lib/utils/claude-client.js +105 -5
- package/lib/utils/claude-diagnostics.js +53 -6
- package/lib/utils/prompt-builder.js +2 -2
- package/package.json +1 -1
- package/templates/{CLAUDE_ANALYSIS_PROMPT_SONAR.md → CLAUDE_ANALYSIS_PROMPT.md} +2 -1
- package/templates/{CLAUDE_PRE_COMMIT_SONAR.md → CLAUDE_PRE_COMMIT.md} +10 -1
- package/templates/CUSTOMIZATION_GUIDE.md +3 -3
- package/templates/config.example.json +2 -2
- package/templates/presets/ai/ANALYSIS_PROMPT.md +1 -1
- package/templates/presets/backend/ANALYSIS_PROMPT.md +1 -1
- package/templates/presets/database/ANALYSIS_PROMPT.md +1 -1
- package/templates/presets/frontend/ANALYSIS_PROMPT.md +1 -1
- package/templates/presets/fullstack/ANALYSIS_PROMPT.md +1 -1
- package/templates/shared/ANALYSIS_PROMPT.md +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,88 @@ 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
|
+
## [2.7.1] - 2025-12-22
|
|
9
|
+
|
|
10
|
+
### 🐛 Fixed
|
|
11
|
+
|
|
12
|
+
- **Improved reliability with automatic retry logic** - Fixed intermittent "Execution error" failures from Claude API
|
|
13
|
+
- **What was broken**: Claude CLI occasionally returned "Execution error" (15 chars) with exit code 0, causing commits to fail ~50% of the time
|
|
14
|
+
- **Root cause**: Claude API rate limiting or temporary backend issues returned error text instead of valid JSON, but was treated as success due to exit code 0
|
|
15
|
+
- **Fix**: Added detection for "Execution error" responses and automatic retry logic with 2-second delay
|
|
16
|
+
- **Files changed**:
|
|
17
|
+
- `lib/utils/claude-diagnostics.js:22-29,47-57,122-170,307-310` - Added EXECUTION_ERROR type detection and formatting
|
|
18
|
+
- `lib/utils/claude-client.js:24,246-270,620-679` - Check for error even with exit code 0, added retry logic
|
|
19
|
+
- **Impact**: Significantly improved reliability - transient API errors now automatically retry once, reducing failure rate
|
|
20
|
+
- **User experience**: Clear error messages explain the issue, automatic retry happens transparently with "⏳ Retrying..." message
|
|
21
|
+
|
|
22
|
+
### 🎯 User Experience
|
|
23
|
+
|
|
24
|
+
- **Before**: Commits failed ~50% of the time with cryptic "No valid JSON found" error, requiring manual retries
|
|
25
|
+
- **After**: System automatically detects and retries recoverable errors, with helpful messages explaining what's happening
|
|
26
|
+
- **Error messages**: Now include specific guidance for "Execution error" (likely rate limiting, try haiku model, commit fewer files)
|
|
27
|
+
|
|
28
|
+
## [2.7.0] - 2025-12-19
|
|
29
|
+
|
|
30
|
+
### 🎨 Changed
|
|
31
|
+
|
|
32
|
+
- **Simplified code analysis output format** - Replaced fake SonarQube metrics with issue count summary (#47)
|
|
33
|
+
- **What changed**: Removed BLOCKER/CRITICAL/MAJOR/MINOR severity labels and A-E reliability/security/maintainability ratings
|
|
34
|
+
- **Why**: Metrics were Claude's opinion presented as facts, causing confusion and false sense of precision
|
|
35
|
+
- **New format**: Simple summary "X issue(s) found across Y file(s)" or "✅ No issues found!"
|
|
36
|
+
- **Files changed**:
|
|
37
|
+
- `lib/hooks/pre-commit.js:67-100` - Replaced metrics display with issue count summary
|
|
38
|
+
- `templates/CLAUDE_PRE_COMMIT_SONAR.md` → `templates/CLAUDE_PRE_COMMIT.md` (renamed)
|
|
39
|
+
- `templates/CLAUDE_ANALYSIS_PROMPT_SONAR.md` → `templates/CLAUDE_ANALYSIS_PROMPT.md` (renamed)
|
|
40
|
+
- `lib/config.js:58-59` - Updated template paths to remove SONAR suffix
|
|
41
|
+
- `bin/claude-hooks:387-407` - Installer now removes old SONAR template files
|
|
42
|
+
- **Impact**: Cleaner output focused on actual issues, not fake quality scores
|
|
43
|
+
|
|
44
|
+
- **Added false-positive prevention guidance** - Pre-commit hook now avoids reporting improvements as issues (#47)
|
|
45
|
+
- **What was broken**: Hook reported routine improvements (better formatting, added docs) as CODE_SMELL issues
|
|
46
|
+
- **Root cause**: Prompts lacked explicit guidance to distinguish problems from improvements
|
|
47
|
+
- **Fix**: Added clear instructions to only report actual bugs, security issues, or quality degradations
|
|
48
|
+
- **New guidance in templates**:
|
|
49
|
+
- "Only report actual problems, bugs, security issues, or code quality concerns"
|
|
50
|
+
- "Do NOT report improvements, positive changes, or routine maintenance as issues"
|
|
51
|
+
- "It is perfectly acceptable to find ZERO issues - this is a positive outcome"
|
|
52
|
+
- "When in doubt, err on the side of NOT reporting it"
|
|
53
|
+
- **Files changed**:
|
|
54
|
+
- `templates/CLAUDE_PRE_COMMIT.md` - Added false-positive prevention section at top
|
|
55
|
+
- `templates/CLAUDE_ANALYSIS_PROMPT.md` - Updated instructions before JSON schema
|
|
56
|
+
- **Compatibility**: Existing configurations continue to work, but users get fewer false positives
|
|
57
|
+
|
|
58
|
+
### 🔧 Fixed
|
|
59
|
+
|
|
60
|
+
- **Pre-commit hook no longer reports improvements as issues** - Documentation additions and formatting improvements now correctly pass analysis (#47)
|
|
61
|
+
|
|
62
|
+
### ⚠️ Breaking Changes (Minor)
|
|
63
|
+
|
|
64
|
+
- **Template file renames**: `CLAUDE_PRE_COMMIT_SONAR.md` → `CLAUDE_PRE_COMMIT.md`, `CLAUDE_ANALYSIS_PROMPT_SONAR.md` → `CLAUDE_ANALYSIS_PROMPT.md`
|
|
65
|
+
- **Migration**: Installer automatically removes old files and installs new ones on `claude-hooks install --force`
|
|
66
|
+
- **Impact**: Low - most users don't customize these files. Custom templates need manual rename.
|
|
67
|
+
|
|
68
|
+
## [2.6.3]
|
|
69
|
+
|
|
70
|
+
### 🐛 Fixed
|
|
71
|
+
|
|
72
|
+
- **WSL timeout error misreporting** - Fixed misleading error message when WSL times out under system load (#49)
|
|
73
|
+
- **What was broken**: ETIMEDOUT errors during WSL Claude verification were misreported as "Claude CLI not found in WSL"
|
|
74
|
+
- **Root cause**: Catch block in WSL verification treated all errors identically, including transient ETIMEDOUT timeouts from system load
|
|
75
|
+
- **Fix**: Added error type differentiation to distinguish between ETIMEDOUT (transient timeout), ENOENT (missing CLI), and generic WSL errors
|
|
76
|
+
- **Files changed**:
|
|
77
|
+
- `lib/utils/claude-client.js:109-146` - Enhanced error detection with three distinct error paths
|
|
78
|
+
- **Impact**:
|
|
79
|
+
- ETIMEDOUT: Clear message "Timeout connecting to WSL - system under heavy load" with suggestion to retry or skip
|
|
80
|
+
- ENOENT: Preserved original "Claude CLI not found in WSL" with installation instructions
|
|
81
|
+
- Generic: New fallback "Failed to verify Claude CLI in WSL" with WSL diagnostic suggestions
|
|
82
|
+
- **Compatibility**: No breaking changes, improves error accuracy for Windows WSL users
|
|
83
|
+
|
|
84
|
+
### 🎯 User Experience
|
|
85
|
+
|
|
86
|
+
- **Before**: Users experiencing system load timeouts saw misleading "Claude CLI not found" error and tried reinstalling unnecessarily
|
|
87
|
+
- **After**: Clear distinction between transient timeouts (retry/skip) and missing CLI (install) with actionable suggestions for each
|
|
88
|
+
- **Debug**: All error cases now include context with platform, wslPath, error type, and specific remediation steps
|
|
89
|
+
|
|
8
90
|
## [2.6.2] - 2025-12-12
|
|
9
91
|
|
|
10
92
|
### 🐛 Fixed
|
|
@@ -118,11 +200,13 @@ y este proyecto adhiere a [Semantic Versioning](https://semver.org/spec/v2.0.0.h
|
|
|
118
200
|
### 🔧 Technical Details
|
|
119
201
|
|
|
120
202
|
**DEP0190 Explanation**:
|
|
203
|
+
|
|
121
204
|
- **What**: Node.js 24 deprecates `spawn(cmd, args, { shell: true })`
|
|
122
205
|
- **Why**: Args are not escaped, just concatenated → shell injection risk
|
|
123
206
|
- **Fix**: Use absolute executable paths, remove `shell: true`
|
|
124
207
|
|
|
125
208
|
**Files Changed**:
|
|
209
|
+
|
|
126
210
|
- `lib/utils/claude-client.js` - Command resolution and spawn call
|
|
127
211
|
- `lib/utils/which-command.js` - NEW utility for path resolution
|
|
128
212
|
- `package.json` - Platform documentation
|
|
@@ -131,6 +215,7 @@ y este proyecto adhiere a [Semantic Versioning](https://semver.org/spec/v2.0.0.h
|
|
|
131
215
|
- `MIGRATION_NODE24.md` - NEW migration guide
|
|
132
216
|
|
|
133
217
|
**Backward Compatibility**:
|
|
218
|
+
|
|
134
219
|
- ✅ Works on Node 16.9.0+ (unchanged)
|
|
135
220
|
- ✅ Works on Node 18 (unchanged)
|
|
136
221
|
- ✅ Works on Node 20 (unchanged)
|
|
@@ -149,6 +234,7 @@ y este proyecto adhiere a [Semantic Versioning](https://semver.org/spec/v2.0.0.h
|
|
|
149
234
|
### 📋 Migration Checklist
|
|
150
235
|
|
|
151
236
|
For users on Node 24:
|
|
237
|
+
|
|
152
238
|
- [x] Update to claude-git-hooks 2.6.0
|
|
153
239
|
- [x] Verify no DEP0190 warnings: `claude-hooks install`
|
|
154
240
|
- [x] Test pre-commit hook: `git commit`
|
package/README.md
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
# Pre-commit Hook con Claude CLI
|
|
2
2
|
|
|
3
|
-
🚀 **Transforma tu flujo de desarrollo con IA**: análisis de código instantáneo, mensajes de commit automáticos y generación de PRs perfectas. Claude revisa tu código antes de cada commit, detecta issues críticos
|
|
3
|
+
🚀 **Transforma tu flujo de desarrollo con IA**: análisis de código instantáneo, mensajes de commit automáticos y generación de PRs perfectas. Claude revisa tu código antes de cada commit, detecta issues críticos con análisis estructurado, y genera toda la documentación que necesitas. ¿Lo mejor? Se ejecuta localmente sin contaminar tu CI/CD.
|
|
4
|
+
|
|
5
|
+
## ✨ Novedad v2.7.1 - Improved Reliability
|
|
6
|
+
|
|
7
|
+
**Nuevo v2.7.1**: Sistema de reintentos automáticos para errores transitorios de Claude API. Cuando Claude devuelve "Execution error" (típicamente por rate limiting), el sistema ahora reintenta automáticamente después de 2 segundos.
|
|
4
8
|
|
|
5
9
|
## ✨ Novedad v2.0.0 - Ahora Cross-Platform
|
|
6
10
|
|
|
@@ -22,6 +26,7 @@
|
|
|
22
26
|
- 🚀 **Parallel Analysis**: Multiple Claude CLI processes analyzing file batches simultaneously (v2.2.0+)
|
|
23
27
|
- 🔄 **Auto-actualización**: Se mantiene actualizado automáticamente
|
|
24
28
|
- 🌍 **Cross-platform**: Windows, WSL, macOS, Linux sin configuración especial
|
|
29
|
+
- ♻️ **Reintentos automáticos**: Recuperación automática de errores transitorios de API (v2.7.1+)
|
|
25
30
|
|
|
26
31
|
## 📋 CHEATSHEET
|
|
27
32
|
|
|
@@ -207,8 +212,8 @@ Toda idea es bienvenida, aunque parezca espantosa. Si hay bugs, incluirlos tambi
|
|
|
207
212
|
# Configuración vía .claude/config.json
|
|
208
213
|
.claude/
|
|
209
214
|
├── config.json # Configuración principal (v2.2.0+)
|
|
210
|
-
├──
|
|
211
|
-
└──
|
|
215
|
+
├── CLAUDE_PRE_COMMIT.md # Personalizar criterios (override)
|
|
216
|
+
└── CLAUDE_ANALYSIS_PROMPT.md # Modificar prompt (override)
|
|
212
217
|
|
|
213
218
|
# Ejemplo de config.json (todas las opciones son opcionales)
|
|
214
219
|
cat > .claude/config.json << 'EOF'
|
|
@@ -418,8 +423,8 @@ git commit -m "fix: resolver issues"
|
|
|
418
423
|
**Archivos clave para modificar:**
|
|
419
424
|
|
|
420
425
|
- `.claude/config.json` - Configuración principal (v2.2.0+)
|
|
421
|
-
- `.claude/
|
|
422
|
-
- `.claude/
|
|
426
|
+
- `.claude/CLAUDE_PRE_COMMIT.md` - Criterios (backend/frontend/data/db)
|
|
427
|
+
- `.claude/CLAUDE_ANALYSIS_PROMPT.md` - Template del prompt
|
|
423
428
|
|
|
424
429
|
**Crear o modificar presets personalizados:**
|
|
425
430
|
|
|
@@ -434,7 +439,7 @@ cat templates/CUSTOMIZATION_GUIDE.md
|
|
|
434
439
|
**Ejemplo:**
|
|
435
440
|
|
|
436
441
|
```bash
|
|
437
|
-
vim .claude/
|
|
442
|
+
vim .claude/CLAUDE_PRE_COMMIT.md # Agregar reglas API REST/Spring Boot
|
|
438
443
|
```
|
|
439
444
|
|
|
440
445
|
## 🔧 Configuración Previa Importante
|
|
@@ -501,8 +506,8 @@ El comando `claude-hooks install` crea los siguientes archivos y directorios:
|
|
|
501
506
|
2. **`.git/hooks/prepare-commit-msg`** - Hook de generación de mensajes
|
|
502
507
|
3. **`.git/hooks/check-version.sh`** - Script de verificación de versión
|
|
503
508
|
4. **`.claude/`** - Directorio para archivos de configuración
|
|
504
|
-
- `
|
|
505
|
-
- `
|
|
509
|
+
- `CLAUDE_PRE_COMMIT.md` - Criterios de evaluación de calidad
|
|
510
|
+
- `CLAUDE_ANALYSIS_PROMPT.md` - Template de prompt para análisis
|
|
506
511
|
- `CLAUDE_RESOLUTION_PROMPT.md` - Template para prompt de resolución AI
|
|
507
512
|
|
|
508
513
|
### Actualización automática de .gitignore
|
|
@@ -621,11 +626,11 @@ En `lib/hooks/prepare-commit-msg.js`:
|
|
|
621
626
|
|
|
622
627
|
### Pautas de Evaluación
|
|
623
628
|
|
|
624
|
-
- **`
|
|
629
|
+
- **`CLAUDE_PRE_COMMIT.md`** - Criterios de evaluación de calidad
|
|
625
630
|
|
|
626
631
|
### Templates de Prompts
|
|
627
632
|
|
|
628
|
-
- **`
|
|
633
|
+
- **`CLAUDE_ANALYSIS_PROMPT.md`** - Estructura del prompt de análisis
|
|
629
634
|
- **`CLAUDE_RESOLUTION_PROMPT.md`** - Template para generar prompts de resolución
|
|
630
635
|
|
|
631
636
|
Todos estos archivos son personalizables y específicos de tu proyecto. Puedes:
|
|
@@ -672,8 +677,8 @@ claude-git-hooks/
|
|
|
672
677
|
│ ├── pre-commit # Bash wrapper (llama a lib/hooks/pre-commit.js)
|
|
673
678
|
│ ├── prepare-commit-msg # Bash wrapper (llama a lib/hooks/prepare-commit-msg.js)
|
|
674
679
|
│ ├── check-version.sh # Script de verificación de versión
|
|
675
|
-
│ ├──
|
|
676
|
-
│ ├──
|
|
680
|
+
│ ├── CLAUDE_PRE_COMMIT.md # Criterios de evaluación
|
|
681
|
+
│ ├── CLAUDE_ANALYSIS_PROMPT.md # Template de prompt para análisis
|
|
677
682
|
│ ├── CLAUDE_RESOLUTION_PROMPT.md # Template para resolución de issues
|
|
678
683
|
│ ├── ANALYZE_DIFF.md # Template para análisis de diff (PR metadata)
|
|
679
684
|
│ ├── CREATE_GITHUB_PR.md # Template para creación de PR
|
package/bin/claude-hooks
CHANGED
|
@@ -384,6 +384,20 @@ async function install(args) {
|
|
|
384
384
|
success('.claude directory created');
|
|
385
385
|
}
|
|
386
386
|
|
|
387
|
+
// Remove old SONAR template files if they exist (migration from v2.6.x to v2.7.0+)
|
|
388
|
+
const oldSonarFiles = [
|
|
389
|
+
'CLAUDE_PRE_COMMIT_SONAR.md',
|
|
390
|
+
'CLAUDE_ANALYSIS_PROMPT_SONAR.md'
|
|
391
|
+
];
|
|
392
|
+
|
|
393
|
+
oldSonarFiles.forEach(oldFile => {
|
|
394
|
+
const oldPath = path.join(claudeDir, oldFile);
|
|
395
|
+
if (fs.existsSync(oldPath)) {
|
|
396
|
+
fs.unlinkSync(oldPath);
|
|
397
|
+
info(`Removed old template: ${oldFile}`);
|
|
398
|
+
}
|
|
399
|
+
});
|
|
400
|
+
|
|
387
401
|
// Copy ALL template files (.md and .json) to .claude directory
|
|
388
402
|
const templateFiles = fs.readdirSync(templatesPath)
|
|
389
403
|
.filter(file => {
|
|
@@ -1455,7 +1469,7 @@ function status() {
|
|
|
1455
1469
|
|
|
1456
1470
|
// Check guidelines files
|
|
1457
1471
|
console.log('\nGuidelines files:');
|
|
1458
|
-
const guidelines = ['
|
|
1472
|
+
const guidelines = ['CLAUDE_PRE_COMMIT.md'];
|
|
1459
1473
|
guidelines.forEach(guideline => {
|
|
1460
1474
|
const claudePath = path.join('.claude', guideline);
|
|
1461
1475
|
if (fs.existsSync(claudePath)) {
|
|
@@ -1683,7 +1697,7 @@ Customization:
|
|
|
1683
1697
|
Override prompts by copying to .claude/:
|
|
1684
1698
|
cp templates/COMMIT_MESSAGE.md .claude/
|
|
1685
1699
|
cp templates/ANALYZE_DIFF.md .claude/
|
|
1686
|
-
cp templates/
|
|
1700
|
+
cp templates/CLAUDE_PRE_COMMIT.md .claude/
|
|
1687
1701
|
# Edit as needed - system uses .claude/ version if exists
|
|
1688
1702
|
|
|
1689
1703
|
More information: https://github.com/pablorovito/claude-git-hooks
|
package/lib/config.js
CHANGED
|
@@ -55,8 +55,8 @@ const defaults = {
|
|
|
55
55
|
// Template paths
|
|
56
56
|
templates: {
|
|
57
57
|
baseDir: '.claude', // Base directory for all templates
|
|
58
|
-
analysis: '
|
|
59
|
-
guidelines: '
|
|
58
|
+
analysis: 'CLAUDE_ANALYSIS_PROMPT.md', // Pre-commit analysis prompt
|
|
59
|
+
guidelines: 'CLAUDE_PRE_COMMIT.md', // Analysis guidelines
|
|
60
60
|
commitMessage: 'COMMIT_MESSAGE.md', // Commit message prompt
|
|
61
61
|
analyzeDiff: 'ANALYZE_DIFF.md', // PR analysis prompt
|
|
62
62
|
resolution: 'CLAUDE_RESOLUTION_PROMPT.md', // Issue resolution prompt
|
package/lib/hooks/pre-commit.js
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* 2. Build analysis prompt with file diffs
|
|
10
10
|
* 3. Send to Claude CLI for analysis
|
|
11
11
|
* 4. Parse JSON response
|
|
12
|
-
* 5. Display
|
|
12
|
+
* 5. Display structured analysis results
|
|
13
13
|
* 6. Generate resolution prompt if issues found
|
|
14
14
|
* 7. Block commit if quality gate fails
|
|
15
15
|
*
|
|
@@ -52,8 +52,8 @@ import { getConfig } from '../config.js';
|
|
|
52
52
|
*/
|
|
53
53
|
|
|
54
54
|
/**
|
|
55
|
-
* Displays
|
|
56
|
-
* Why: Provides
|
|
55
|
+
* Displays structured analysis results
|
|
56
|
+
* Why: Provides clear, visual feedback about code quality
|
|
57
57
|
*
|
|
58
58
|
* @param {Object} result - Analysis result from Claude
|
|
59
59
|
*/
|
|
@@ -73,19 +73,16 @@ const displayResults = (result) => {
|
|
|
73
73
|
}
|
|
74
74
|
console.log();
|
|
75
75
|
|
|
76
|
-
//
|
|
77
|
-
if (result.
|
|
78
|
-
|
|
79
|
-
console.log(
|
|
80
|
-
|
|
81
|
-
console.log(
|
|
82
|
-
console.log(`├─ Coverage: ${result.metrics.coverage || '?'}%`);
|
|
83
|
-
console.log(`├─ Duplications: ${result.metrics.duplications || '?'}%`);
|
|
84
|
-
console.log(`└─ Complexity: ${result.metrics.complexity || '?'}`);
|
|
85
|
-
console.log();
|
|
76
|
+
// Issues Summary - Simple count
|
|
77
|
+
if (Array.isArray(result.details) && result.details.length > 0) {
|
|
78
|
+
const fileCount = new Set(result.details.map(i => i.file)).size;
|
|
79
|
+
console.log(`📊 ${result.details.length} issue(s) found across ${fileCount} file(s)`);
|
|
80
|
+
} else {
|
|
81
|
+
console.log('✅ No issues found!');
|
|
86
82
|
}
|
|
83
|
+
console.log();
|
|
87
84
|
|
|
88
|
-
// Issues
|
|
85
|
+
// Issues Breakdown (severity counts)
|
|
89
86
|
if (result.issues && typeof result.issues === 'object') {
|
|
90
87
|
console.log('📋 ISSUES SUMMARY');
|
|
91
88
|
|
|
@@ -21,7 +21,7 @@ import path from 'path';
|
|
|
21
21
|
import os from 'os';
|
|
22
22
|
import logger from './logger.js';
|
|
23
23
|
import config from '../config.js';
|
|
24
|
-
import { detectClaudeError, formatClaudeError, ClaudeErrorType } from './claude-diagnostics.js';
|
|
24
|
+
import { detectClaudeError, formatClaudeError, ClaudeErrorType, isRecoverableError } from './claude-diagnostics.js';
|
|
25
25
|
import { which } from './which-command.js';
|
|
26
26
|
|
|
27
27
|
/**
|
|
@@ -107,11 +107,40 @@ const getClaudeCommand = () => {
|
|
|
107
107
|
logger.debug('claude-client - getClaudeCommand', 'Using WSL Claude CLI', { wslPath });
|
|
108
108
|
return { command: wslPath, args: ['claude'] };
|
|
109
109
|
} catch (wslError) {
|
|
110
|
-
|
|
110
|
+
// Differentiate error types for accurate user feedback
|
|
111
|
+
const errorMsg = wslError.message || '';
|
|
112
|
+
|
|
113
|
+
// Timeout: Transient system load issue
|
|
114
|
+
if (errorMsg.includes('ETIMEDOUT')) {
|
|
115
|
+
throw new ClaudeClientError('Timeout connecting to WSL - system under heavy load', {
|
|
116
|
+
context: {
|
|
117
|
+
platform: 'Windows',
|
|
118
|
+
wslPath,
|
|
119
|
+
error: 'ETIMEDOUT',
|
|
120
|
+
suggestion: 'System is busy. Wait a moment and try again, or skip analysis: git commit --no-verify'
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Not found: Claude CLI missing in WSL
|
|
126
|
+
if (errorMsg.includes('ENOENT') || errorMsg.includes('command not found')) {
|
|
127
|
+
throw new ClaudeClientError('Claude CLI not found in WSL', {
|
|
128
|
+
context: {
|
|
129
|
+
platform: 'Windows',
|
|
130
|
+
wslPath,
|
|
131
|
+
error: 'ENOENT',
|
|
132
|
+
suggestion: 'Install Claude in WSL: wsl -e bash -c "npm install -g @anthropic-ai/claude-cli"'
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Generic error: Other WSL issues
|
|
138
|
+
throw new ClaudeClientError('Failed to verify Claude CLI in WSL', {
|
|
111
139
|
context: {
|
|
112
140
|
platform: 'Windows',
|
|
113
141
|
wslPath,
|
|
114
|
-
|
|
142
|
+
error: errorMsg,
|
|
143
|
+
suggestion: 'Check WSL is functioning: wsl --version, or skip analysis: git commit --no-verify'
|
|
115
144
|
}
|
|
116
145
|
});
|
|
117
146
|
}
|
|
@@ -214,6 +243,33 @@ const executeClaude = (prompt, { timeout = 120000, allowedTools = [] } = {}) =>
|
|
|
214
243
|
claude.on('close', (code) => {
|
|
215
244
|
const duration = Date.now() - startTime;
|
|
216
245
|
|
|
246
|
+
// Check for "Execution error" even when exit code is 0
|
|
247
|
+
// Why: Claude CLI sometimes returns "Execution error" with exit code 0
|
|
248
|
+
// This occurs during API rate limiting or temporary backend issues
|
|
249
|
+
// IMPORTANT: Only check for EXACT match to avoid false positives
|
|
250
|
+
if (stdout.trim() === 'Execution error') {
|
|
251
|
+
const errorInfo = detectClaudeError(stdout, stderr, code);
|
|
252
|
+
|
|
253
|
+
logger.error(
|
|
254
|
+
'claude-client - executeClaude',
|
|
255
|
+
`Claude CLI returned execution error (exit ${code})`,
|
|
256
|
+
new ClaudeClientError(`Claude CLI error: ${errorInfo.type}`, {
|
|
257
|
+
output: { stdout, stderr },
|
|
258
|
+
context: { exitCode: code, duration, errorType: errorInfo.type }
|
|
259
|
+
})
|
|
260
|
+
);
|
|
261
|
+
|
|
262
|
+
// Show formatted error to user
|
|
263
|
+
const formattedError = formatClaudeError(errorInfo);
|
|
264
|
+
console.error(`\n${ formattedError }\n`);
|
|
265
|
+
|
|
266
|
+
reject(new ClaudeClientError(`Claude CLI error: ${errorInfo.type}`, {
|
|
267
|
+
output: { stdout, stderr },
|
|
268
|
+
context: { exitCode: code, duration, errorInfo }
|
|
269
|
+
}));
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
|
|
217
273
|
if (code === 0) {
|
|
218
274
|
logger.debug(
|
|
219
275
|
'claude-client - executeClaude',
|
|
@@ -562,11 +618,16 @@ const saveDebugResponse = async (prompt, response, filename = config.output.debu
|
|
|
562
618
|
* @returns {Promise<Object>} Parsed analysis result
|
|
563
619
|
* @throws {ClaudeClientError} If analysis fails
|
|
564
620
|
*/
|
|
565
|
-
const analyzeCode = async (prompt, { timeout = 120000, saveDebug = config.system.debug } = {}) => {
|
|
621
|
+
const analyzeCode = async (prompt, { timeout = 120000, saveDebug = config.system.debug, retryCount = 0 } = {}) => {
|
|
622
|
+
const maxRetries = 3; // Retry up to 3 times for recoverable errors
|
|
623
|
+
const baseRetryDelay = 2000; // Base delay: 2 seconds
|
|
624
|
+
// Exponential backoff: 2s, 4s, 8s
|
|
625
|
+
const retryDelay = baseRetryDelay * Math.pow(2, retryCount);
|
|
626
|
+
|
|
566
627
|
logger.debug(
|
|
567
628
|
'claude-client - analyzeCode',
|
|
568
629
|
'Starting code analysis',
|
|
569
|
-
{ promptLength: prompt.length, timeout, saveDebug }
|
|
630
|
+
{ promptLength: prompt.length, timeout, saveDebug, retryCount }
|
|
570
631
|
);
|
|
571
632
|
|
|
572
633
|
try {
|
|
@@ -594,6 +655,45 @@ const analyzeCode = async (prompt, { timeout = 120000, saveDebug = config.system
|
|
|
594
655
|
return result;
|
|
595
656
|
|
|
596
657
|
} catch (error) {
|
|
658
|
+
// Check if error is recoverable and we haven't exceeded retry limit
|
|
659
|
+
const hasContext = !!error.context;
|
|
660
|
+
const hasErrorInfo = !!error.context?.errorInfo;
|
|
661
|
+
const isRecoverable = hasErrorInfo ? isRecoverableError(error.context.errorInfo) : false;
|
|
662
|
+
const canRetry = retryCount < maxRetries;
|
|
663
|
+
|
|
664
|
+
logger.debug(
|
|
665
|
+
'claude-client - analyzeCode',
|
|
666
|
+
'Retry check',
|
|
667
|
+
{
|
|
668
|
+
retryCount,
|
|
669
|
+
maxRetries,
|
|
670
|
+
hasContext,
|
|
671
|
+
hasErrorInfo,
|
|
672
|
+
isRecoverable,
|
|
673
|
+
canRetry,
|
|
674
|
+
errorType: error.context?.errorInfo?.type,
|
|
675
|
+
errorName: error.name
|
|
676
|
+
}
|
|
677
|
+
);
|
|
678
|
+
|
|
679
|
+
const shouldRetry = canRetry && hasContext && hasErrorInfo && isRecoverable;
|
|
680
|
+
|
|
681
|
+
if (shouldRetry) {
|
|
682
|
+
logger.debug(
|
|
683
|
+
'claude-client - analyzeCode',
|
|
684
|
+
`Recoverable error detected, retrying in ${retryDelay}ms (attempt ${retryCount + 1}/${maxRetries})`,
|
|
685
|
+
{ errorType: error.context.errorInfo.type }
|
|
686
|
+
);
|
|
687
|
+
|
|
688
|
+
console.log(`\n⏳ Retrying in ${retryDelay / 1000} seconds due to ${error.context.errorInfo.type}...\n`);
|
|
689
|
+
|
|
690
|
+
// Wait before retry
|
|
691
|
+
await new Promise(resolve => setTimeout(resolve, retryDelay));
|
|
692
|
+
|
|
693
|
+
// Retry with incremented count
|
|
694
|
+
return analyzeCode(prompt, { timeout, saveDebug, retryCount: retryCount + 1 });
|
|
695
|
+
}
|
|
696
|
+
|
|
597
697
|
logger.error('claude-client - analyzeCode', 'Code analysis failed', error);
|
|
598
698
|
throw error;
|
|
599
699
|
}
|
|
@@ -25,6 +25,7 @@ export const ClaudeErrorType = {
|
|
|
25
25
|
TIMEOUT: 'TIMEOUT',
|
|
26
26
|
NETWORK: 'NETWORK',
|
|
27
27
|
INVALID_RESPONSE: 'INVALID_RESPONSE',
|
|
28
|
+
EXECUTION_ERROR: 'EXECUTION_ERROR',
|
|
28
29
|
GENERIC: 'GENERIC'
|
|
29
30
|
};
|
|
30
31
|
|
|
@@ -44,7 +45,20 @@ export const ClaudeErrorType = {
|
|
|
44
45
|
* @returns {Object|null} Error information or null if no specific error detected
|
|
45
46
|
*/
|
|
46
47
|
export const detectClaudeError = (stdout = '', stderr = '', exitCode = 1) => {
|
|
47
|
-
// 1.
|
|
48
|
+
// 1. Execution error detection (Claude CLI internal error)
|
|
49
|
+
// This occurs when Claude API returns an error instead of valid response
|
|
50
|
+
// Often caused by rate limiting, API errors, or temporary backend issues
|
|
51
|
+
// IMPORTANT: Only match EXACT "Execution error" response, not partial matches
|
|
52
|
+
const trimmedStdout = stdout.trim();
|
|
53
|
+
if (trimmedStdout === 'Execution error') {
|
|
54
|
+
return {
|
|
55
|
+
type: ClaudeErrorType.EXECUTION_ERROR,
|
|
56
|
+
exitCode,
|
|
57
|
+
stdout: stdout.substring(0, 100)
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// 2. Rate limit detection
|
|
48
62
|
const rateLimitMatch = stdout.match(/Claude AI usage limit reached\|(\d+)/);
|
|
49
63
|
if (rateLimitMatch) {
|
|
50
64
|
const resetTimestamp = parseInt(rateLimitMatch[1], 10);
|
|
@@ -61,7 +75,7 @@ export const detectClaudeError = (stdout = '', stderr = '', exitCode = 1) => {
|
|
|
61
75
|
};
|
|
62
76
|
}
|
|
63
77
|
|
|
64
|
-
//
|
|
78
|
+
// 3. Authentication failure detection
|
|
65
79
|
if (stdout.includes('not authenticated') || stderr.includes('not authenticated') ||
|
|
66
80
|
stdout.includes('authentication failed') || stderr.includes('authentication failed')) {
|
|
67
81
|
return {
|
|
@@ -70,7 +84,7 @@ export const detectClaudeError = (stdout = '', stderr = '', exitCode = 1) => {
|
|
|
70
84
|
};
|
|
71
85
|
}
|
|
72
86
|
|
|
73
|
-
//
|
|
87
|
+
// 4. Network errors
|
|
74
88
|
if (stderr.includes('ENOTFOUND') || stderr.includes('ECONNREFUSED') ||
|
|
75
89
|
stderr.includes('network error') || stderr.includes('connection refused')) {
|
|
76
90
|
return {
|
|
@@ -79,7 +93,7 @@ export const detectClaudeError = (stdout = '', stderr = '', exitCode = 1) => {
|
|
|
79
93
|
};
|
|
80
94
|
}
|
|
81
95
|
|
|
82
|
-
//
|
|
96
|
+
// 5. Invalid response (JSON parsing errors)
|
|
83
97
|
if (stdout.includes('SyntaxError') || stdout.includes('Unexpected token')) {
|
|
84
98
|
return {
|
|
85
99
|
type: ClaudeErrorType.INVALID_RESPONSE,
|
|
@@ -87,7 +101,7 @@ export const detectClaudeError = (stdout = '', stderr = '', exitCode = 1) => {
|
|
|
87
101
|
};
|
|
88
102
|
}
|
|
89
103
|
|
|
90
|
-
//
|
|
104
|
+
// 6. Generic error
|
|
91
105
|
return {
|
|
92
106
|
type: ClaudeErrorType.GENERIC,
|
|
93
107
|
exitCode,
|
|
@@ -107,6 +121,9 @@ export const formatClaudeError = (errorInfo) => {
|
|
|
107
121
|
const lines = [];
|
|
108
122
|
|
|
109
123
|
switch (errorInfo.type) {
|
|
124
|
+
case ClaudeErrorType.EXECUTION_ERROR:
|
|
125
|
+
return formatExecutionError(errorInfo);
|
|
126
|
+
|
|
110
127
|
case ClaudeErrorType.RATE_LIMIT:
|
|
111
128
|
return formatRateLimitError(errorInfo);
|
|
112
129
|
|
|
@@ -125,6 +142,35 @@ export const formatClaudeError = (errorInfo) => {
|
|
|
125
142
|
}
|
|
126
143
|
};
|
|
127
144
|
|
|
145
|
+
/**
|
|
146
|
+
* Format execution error
|
|
147
|
+
*/
|
|
148
|
+
const formatExecutionError = ({ exitCode, stdout }) => {
|
|
149
|
+
const lines = [];
|
|
150
|
+
|
|
151
|
+
lines.push('❌ Claude CLI execution error');
|
|
152
|
+
lines.push('');
|
|
153
|
+
lines.push('Claude returned "Execution error" instead of valid response.');
|
|
154
|
+
lines.push('');
|
|
155
|
+
lines.push('Common causes:');
|
|
156
|
+
lines.push(' 1. API rate limiting (burst limits)');
|
|
157
|
+
lines.push(' 2. Temporary Claude API backend issues');
|
|
158
|
+
lines.push(' 3. Request/response size limits exceeded');
|
|
159
|
+
lines.push('');
|
|
160
|
+
lines.push('Solutions:');
|
|
161
|
+
lines.push(' 1. Wait 10-30 seconds and try again');
|
|
162
|
+
lines.push(' 2. Reduce prompt size by committing fewer files at once');
|
|
163
|
+
lines.push(' 3. Switch to haiku model (faster, higher rate limits):');
|
|
164
|
+
lines.push(' Edit .claude/config.json:');
|
|
165
|
+
lines.push(' { "subagents": { "model": "haiku" } }');
|
|
166
|
+
lines.push(' 4. Skip analysis for now:');
|
|
167
|
+
lines.push(' git commit --no-verify -m "your message"');
|
|
168
|
+
lines.push('');
|
|
169
|
+
lines.push('The hook will automatically retry once after 2 seconds.');
|
|
170
|
+
|
|
171
|
+
return lines.join('\n');
|
|
172
|
+
};
|
|
173
|
+
|
|
128
174
|
/**
|
|
129
175
|
* Format rate limit error
|
|
130
176
|
*/
|
|
@@ -261,6 +307,7 @@ const formatGenericError = ({ exitCode, stdout, stderr }) => {
|
|
|
261
307
|
* @returns {boolean} True if error might resolve with retry
|
|
262
308
|
*/
|
|
263
309
|
export const isRecoverableError = (errorInfo) => {
|
|
264
|
-
return errorInfo.type === ClaudeErrorType.
|
|
310
|
+
return errorInfo.type === ClaudeErrorType.EXECUTION_ERROR ||
|
|
311
|
+
errorInfo.type === ClaudeErrorType.RATE_LIMIT ||
|
|
265
312
|
errorInfo.type === ClaudeErrorType.NETWORK;
|
|
266
313
|
};
|
|
@@ -215,8 +215,8 @@ const formatFileSection = ({ path, diff, content, isNew }) => {
|
|
|
215
215
|
* }
|
|
216
216
|
*/
|
|
217
217
|
const buildAnalysisPrompt = async ({
|
|
218
|
-
templateName = '
|
|
219
|
-
guidelinesName = '
|
|
218
|
+
templateName = 'CLAUDE_ANALYSIS_PROMPT.md',
|
|
219
|
+
guidelinesName = 'CLAUDE_PRE_COMMIT.md',
|
|
220
220
|
files = [],
|
|
221
221
|
metadata = {},
|
|
222
222
|
subagentConfig = null
|
package/package.json
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
ROLE:SeniorCodeReviewer,
|
|
1
|
+
ROLE:SeniorCodeReviewer,CodeQualityExpert
|
|
2
2
|
TASK:Analyze code->JSON only
|
|
3
3
|
CRITICAL:NoTextOutsideJSON,ValidJSON
|
|
4
4
|
|
|
@@ -44,6 +44,7 @@ OUTPUT_SCHEMA:
|
|
|
44
44
|
```
|
|
45
45
|
|
|
46
46
|
RULES:
|
|
47
|
+
- **FALSE_POSITIVE_PREVENTION**: Only report actual problems. Do not report improvements, positive changes, or routine maintenance as issues. Zero issues is a valid and positive outcome.
|
|
47
48
|
- blockingIssues=ALWAYS_ARRAY_OF_OBJECTS(never_strings)
|
|
48
49
|
- blockingIssues=ALL(details.severity∈{BLOCKER,CRITICAL})
|
|
49
50
|
- Each_blockingIssue_MUST_have:description,file,line,method,severity
|
|
@@ -1,4 +1,13 @@
|
|
|
1
|
-
#
|
|
1
|
+
# CODE_QUALITY_EVALUATION_CRITERIA
|
|
2
|
+
|
|
3
|
+
## FALSE_POSITIVE_PREVENTION
|
|
4
|
+
|
|
5
|
+
**IMPORTANT**: Only report actual problems, bugs, security issues, or code quality concerns.
|
|
6
|
+
|
|
7
|
+
- Do NOT report improvements, positive changes, or routine maintenance as issues
|
|
8
|
+
- It is perfectly acceptable to find ZERO issues - this is a positive outcome
|
|
9
|
+
- When in doubt about whether something is an issue, err on the side of NOT reporting it
|
|
10
|
+
- Focus on what could break, harm security, or degrade quality - not what could be "better"
|
|
2
11
|
|
|
3
12
|
## EXCEPTIONS_AND_ALLOWED_PATTERNS
|
|
4
13
|
### Spring Framework Patterns (NOT blocking issues)
|
|
@@ -149,7 +149,7 @@ A preset is a **self-contained package** that customizes claude-hooks for a spec
|
|
|
149
149
|
│
|
|
150
150
|
┌────────────────────────▼────────────────────────────────────┐
|
|
151
151
|
│ 8. RESPONSE PROCESSED │
|
|
152
|
-
│ - JSON format
|
|
152
|
+
│ - JSON format with structured results │
|
|
153
153
|
│ - Quality gate: PASSED/FAILED │
|
|
154
154
|
│ - Issues by severity: blocker, critical, major, minor │
|
|
155
155
|
│ - Commit proceeds or blocked │
|
|
@@ -382,7 +382,7 @@ Perform a comprehensive code quality analysis focusing on these areas:
|
|
|
382
382
|
**Template**:
|
|
383
383
|
|
|
384
384
|
````markdown
|
|
385
|
-
#
|
|
385
|
+
# Code Quality Evaluation Criteria
|
|
386
386
|
|
|
387
387
|
## Severity Levels
|
|
388
388
|
|
|
@@ -599,7 +599,7 @@ Please generate the following files:
|
|
|
599
599
|
- This preset will be installed at: `templates/presets/{preset-name}/`
|
|
600
600
|
- It should follow the same structure as existing presets in `templates/presets/backend/`
|
|
601
601
|
- Users will select it with: `claude-hooks --set-preset {preset-name}`
|
|
602
|
-
- The preset must output JSON
|
|
602
|
+
- The preset must output JSON with structured analysis results
|
|
603
603
|
|
|
604
604
|
**Format**: Return each file's content as a separate code block with clear file headers.
|
|
605
605
|
```
|
|
@@ -18,8 +18,8 @@
|
|
|
18
18
|
},
|
|
19
19
|
"templates": {
|
|
20
20
|
"baseDir": ".claude",
|
|
21
|
-
"analysis": "
|
|
22
|
-
"guidelines": "
|
|
21
|
+
"analysis": "CLAUDE_ANALYSIS_PROMPT.md",
|
|
22
|
+
"guidelines": "CLAUDE_PRE_COMMIT.md",
|
|
23
23
|
"commitMessage": "COMMIT_MESSAGE.md",
|
|
24
24
|
"analyzeDiff": "ANALYZE_DIFF.md",
|
|
25
25
|
"resolution": "CLAUDE_RESOLUTION_PROMPT.md",
|