claude-git-hooks 2.6.3 → 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 +60 -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 +74 -3
- 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,66 @@ 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
|
+
|
|
8
68
|
## [2.6.3]
|
|
9
69
|
|
|
10
70
|
### 🐛 Fixed
|
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
|
/**
|
|
@@ -243,6 +243,33 @@ const executeClaude = (prompt, { timeout = 120000, allowedTools = [] } = {}) =>
|
|
|
243
243
|
claude.on('close', (code) => {
|
|
244
244
|
const duration = Date.now() - startTime;
|
|
245
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
|
+
|
|
246
273
|
if (code === 0) {
|
|
247
274
|
logger.debug(
|
|
248
275
|
'claude-client - executeClaude',
|
|
@@ -591,11 +618,16 @@ const saveDebugResponse = async (prompt, response, filename = config.output.debu
|
|
|
591
618
|
* @returns {Promise<Object>} Parsed analysis result
|
|
592
619
|
* @throws {ClaudeClientError} If analysis fails
|
|
593
620
|
*/
|
|
594
|
-
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
|
+
|
|
595
627
|
logger.debug(
|
|
596
628
|
'claude-client - analyzeCode',
|
|
597
629
|
'Starting code analysis',
|
|
598
|
-
{ promptLength: prompt.length, timeout, saveDebug }
|
|
630
|
+
{ promptLength: prompt.length, timeout, saveDebug, retryCount }
|
|
599
631
|
);
|
|
600
632
|
|
|
601
633
|
try {
|
|
@@ -623,6 +655,45 @@ const analyzeCode = async (prompt, { timeout = 120000, saveDebug = config.system
|
|
|
623
655
|
return result;
|
|
624
656
|
|
|
625
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
|
+
|
|
626
697
|
logger.error('claude-client - analyzeCode', 'Code analysis failed', error);
|
|
627
698
|
throw error;
|
|
628
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",
|