claude-git-hooks 2.3.1 → 2.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +57 -0
- package/README.md +17 -11
- package/bin/claude-hooks +132 -38
- package/lib/config.js +6 -6
- package/lib/hooks/pre-commit.js +34 -0
- package/lib/hooks/prepare-commit-msg.js +5 -0
- package/lib/utils/file-operations.js +38 -4
- package/lib/utils/logger.js +2 -2
- package/package.json +1 -1
- package/templates/CUSTOMIZATION_GUIDE.md +5 -5
- package/templates/config.example.json +39 -39
- package/templates/presets/ai/config.json +10 -10
- package/templates/presets/backend/config.json +10 -10
- package/templates/presets/database/config.json +10 -10
- package/templates/presets/default/config.json +10 -10
- package/templates/presets/frontend/config.json +10 -10
- package/templates/presets/fullstack/config.json +10 -10
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,63 @@ 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.4.0] - 2025-11-17
|
|
9
|
+
|
|
10
|
+
### ⚠️ BREAKING CHANGES
|
|
11
|
+
|
|
12
|
+
- **Debug Mode** - Environment variable `DEBUG` no longer supported
|
|
13
|
+
- **What changed**: Removed `DEBUG` environment variable support from `lib/utils/logger.js`
|
|
14
|
+
- **Why**: Aligns with project philosophy "no environment variables" and provides better user experience
|
|
15
|
+
- **Migration**: Use `claude-hooks --debug true` instead of `DEBUG=1 git commit`
|
|
16
|
+
- **Alternative**: Set `"system": { "debug": true }` in `.claude/config.json`
|
|
17
|
+
|
|
18
|
+
### 🔄 Changed
|
|
19
|
+
|
|
20
|
+
- **File Size Limit Increased** - Default `maxFileSize` increased from 100KB to 1MB
|
|
21
|
+
- **What changed**: `analysis.maxFileSize` now defaults to 1000000 (1MB) instead of 100000 (100KB)
|
|
22
|
+
- **Why**: Previous 100KB limit was too restrictive, rejected many legitimate source files
|
|
23
|
+
- **Files updated**:
|
|
24
|
+
- `lib/config.js:29` - Default configuration
|
|
25
|
+
- All preset configs in `templates/presets/*/config.json`
|
|
26
|
+
- `templates/config.example.json`
|
|
27
|
+
- **Impact**: More files will be analyzed without manual config adjustment
|
|
28
|
+
- **User action**: If you want stricter limits, set `"maxFileSize": 100000` in `.claude/config.json`
|
|
29
|
+
|
|
30
|
+
### ✨ Added
|
|
31
|
+
|
|
32
|
+
- **Debug CLI Command** - New `--debug` flag for managing debug mode
|
|
33
|
+
- **Usage**: `claude-hooks --debug <true|false|status>`
|
|
34
|
+
- **Purpose**: Enables detailed logging for troubleshooting
|
|
35
|
+
- **Implementation**: Generic `updateConfig()` function in `bin/claude-hooks:1262`
|
|
36
|
+
- **Benefits**: Consistent with `--set-preset` pattern, no need to remember env var syntax
|
|
37
|
+
|
|
38
|
+
- **Generic Config Updater** - Reusable `updateConfig()` function
|
|
39
|
+
- **Purpose**: Centralized config update logic for all CLI commands
|
|
40
|
+
- **Location**: `bin/claude-hooks:1262`
|
|
41
|
+
- **Features**: Dot notation path support, custom validators, custom success messages
|
|
42
|
+
- **Extensibility**: Easy to add new config commands (`--set-max-files`, `--set-timeout`, etc.)
|
|
43
|
+
|
|
44
|
+
### 🔄 Changed
|
|
45
|
+
|
|
46
|
+
- **setPreset() Refactored** - Now uses generic `updateConfig()` function
|
|
47
|
+
- **Effect**: Less code duplication, consistent error handling
|
|
48
|
+
- **Location**: `bin/claude-hooks:1349`
|
|
49
|
+
|
|
50
|
+
- **Debug Mode Activation** - Hooks now enable debug from config
|
|
51
|
+
- **Files**: `lib/hooks/pre-commit.js:185`, `lib/hooks/prepare-commit-msg.js:124`
|
|
52
|
+
- **Effect**: Debug logs appear when `config.system.debug` is true
|
|
53
|
+
|
|
54
|
+
### 📚 Documentation
|
|
55
|
+
|
|
56
|
+
- **Help Text Updated** - Added `--debug` command documentation
|
|
57
|
+
- **Location**: `bin/claude-hooks:1170`
|
|
58
|
+
- **Includes**: Command description, examples, debug mode section
|
|
59
|
+
|
|
60
|
+
- **README.md Updated** - Debug command documented in multiple sections
|
|
61
|
+
- **Cheatsheet**: Added debug commands to "Comandos Frecuentes" (line 52-56)
|
|
62
|
+
- **Tips**: Updated tip #4 with CLI command (line 269)
|
|
63
|
+
- **Features**: Updated debug mode description (line 492)
|
|
64
|
+
|
|
8
65
|
## [2.3.1] - 2025-11-13
|
|
9
66
|
|
|
10
67
|
### 🔄 Changed
|
package/README.md
CHANGED
|
@@ -48,6 +48,12 @@ claude-hooks --set-preset <name>
|
|
|
48
48
|
|
|
49
49
|
# Mostrar preset actual
|
|
50
50
|
claude-hooks preset current
|
|
51
|
+
|
|
52
|
+
# Activar modo debug
|
|
53
|
+
claude-hooks --debug true
|
|
54
|
+
|
|
55
|
+
# Ver estado de debug
|
|
56
|
+
claude-hooks --debug status
|
|
51
57
|
```
|
|
52
58
|
|
|
53
59
|
### 📦 Instalación y Gestión
|
|
@@ -127,7 +133,7 @@ cat > .claude/config.json << 'EOF'
|
|
|
127
133
|
{
|
|
128
134
|
"preset": "backend",
|
|
129
135
|
"analysis": {
|
|
130
|
-
"maxFileSize":
|
|
136
|
+
"maxFileSize": 1000000,
|
|
131
137
|
"maxFiles": 12,
|
|
132
138
|
"timeout": 150000
|
|
133
139
|
},
|
|
@@ -260,9 +266,9 @@ git commit -m "fix: resolver issues"
|
|
|
260
266
|
1. **Mensaje automático**: Usa `"auto"` como mensaje para que Claude lo genere
|
|
261
267
|
2. **Skip auth**: Usa `--skip-auth` en CI/CD o desarrollo local
|
|
262
268
|
3. **Force install**: Usa `--force` para reinstalar sin confirmación
|
|
263
|
-
4. **Debug**: Activa en `.claude/config.json`: `{"system": {"debug": true}}`
|
|
264
|
-
5. **Archivos grandes**: Se omiten automáticamente archivos >
|
|
265
|
-
6. **Límite de archivos**: Máximo
|
|
269
|
+
4. **Debug**: Activa con `claude-hooks --debug true` o en `.claude/config.json`: `{"system": {"debug": true}}`
|
|
270
|
+
5. **Archivos grandes**: Se omiten automáticamente archivos > 1MB
|
|
271
|
+
6. **Límite de archivos**: Máximo 30 archivos por commit (configurable)
|
|
266
272
|
|
|
267
273
|
### 🚀 Parallel Analysis (v2.2.0+)
|
|
268
274
|
|
|
@@ -435,8 +441,8 @@ Si no existe un `.gitignore`, se creará uno nuevo. Si ya existe, las entradas s
|
|
|
435
441
|
1. **Intercepta cada intento de commit**
|
|
436
442
|
2. **Extrae y filtra archivos modificados**:
|
|
437
443
|
- Solo analiza: Java, XML, properties, yml, yaml
|
|
438
|
-
- Omite archivos mayores a
|
|
439
|
-
- Límite de
|
|
444
|
+
- Omite archivos mayores a 1MB
|
|
445
|
+
- Límite de 30 archivos por commit
|
|
440
446
|
3. **Construye prompt inteligente**:
|
|
441
447
|
- Usa template de prompt desde `.claude/CLAUDE_ANALYSIS_PROMPT*.md`
|
|
442
448
|
- Lee las pautas desde `.claude/CLAUDE_PRE_COMMIT*.md`
|
|
@@ -462,7 +468,7 @@ Si no existe un `.gitignore`, se creará uno nuevo. Si ya existe, las entradas s
|
|
|
462
468
|
- `"auto"`
|
|
463
469
|
2. **Analiza los cambios del staging area**:
|
|
464
470
|
- Lista archivos modificados con estadísticas
|
|
465
|
-
- Incluye diffs completos para archivos <
|
|
471
|
+
- Incluye diffs completos para archivos < 1MB
|
|
466
472
|
3. **Genera mensaje en formato Conventional Commits**:
|
|
467
473
|
- Determina tipo: feat, fix, docs, style, refactor, test, chore
|
|
468
474
|
- Crea título conciso y descriptivo
|
|
@@ -483,7 +489,7 @@ Si no existe un `.gitignore`, se creará uno nuevo. Si ya existe, las entradas s
|
|
|
483
489
|
- 📝 Archivo `.claude-pr-analysis.json`
|
|
484
490
|
- **Auto-actualización**: Verificación automática de versiones antes de cada commit con prompt interactivo para actualizar
|
|
485
491
|
- **Comando update**: `claude-hooks update` para actualizar manualmente a la última versión
|
|
486
|
-
- **Modo debug**: `
|
|
492
|
+
- **Modo debug**: `claude-hooks --debug true` activa logging detallado para troubleshooting
|
|
487
493
|
- **Análisis de PR**: Nuevo comando `analyze-diff` para generar información de Pull Requests
|
|
488
494
|
- **Validación de dependencias**: Verifica que Claude CLI esté autenticado antes de ejecutar
|
|
489
495
|
- **⚠️ Exclusión de código del análisis (EXPERIMENTAL/BROKEN)**: Los marcadores `// SKIP_ANALYSIS_LINE` y `// SKIP_ANALYSIS_BLOCK` no funcionan correctamente. El hook analiza git diff en lugar del archivo completo, por lo que marcadores agregados en commits anteriores no son detectados en cambios subsecuentes.
|
|
@@ -530,13 +536,13 @@ claude-hooks status
|
|
|
530
536
|
|
|
531
537
|
En `lib/hooks/pre-commit.js`:
|
|
532
538
|
|
|
533
|
-
- **`MAX_FILE_SIZE`**: Tamaño máximo de archivo a analizar (default:
|
|
534
|
-
- **`MAX_FILES`**: Número máximo de archivos por commit (default:
|
|
539
|
+
- **`MAX_FILE_SIZE`**: Tamaño máximo de archivo a analizar (default: 1MB)
|
|
540
|
+
- **`MAX_FILES`**: Número máximo de archivos por commit (default: 30)
|
|
535
541
|
- **`ALLOWED_EXTENSIONS`**: Extensiones permitidas (default: .java, .xml, .properties, .yml, .yaml)
|
|
536
542
|
|
|
537
543
|
En `lib/hooks/prepare-commit-msg.js`:
|
|
538
544
|
|
|
539
|
-
- **`MAX_FILE_SIZE`**: Tamaño máximo para incluir diff completo (default:
|
|
545
|
+
- **`MAX_FILE_SIZE`**: Tamaño máximo para incluir diff completo (default: 1MB)
|
|
540
546
|
|
|
541
547
|
### Pautas de Evaluación
|
|
542
548
|
|
package/bin/claude-hooks
CHANGED
|
@@ -468,6 +468,7 @@ async function install(args) {
|
|
|
468
468
|
console.log(' 📝 Edit .claude/config.json to customize settings');
|
|
469
469
|
console.log(' 🎯 Use presets: backend, frontend, fullstack, database, ai, default');
|
|
470
470
|
console.log(' 🚀 Enable parallel analysis: set subagents.enabled = true');
|
|
471
|
+
console.log(' 🐛 Enable debug mode: claude-hooks --debug true');
|
|
471
472
|
console.log('\n📖 Example config.json:');
|
|
472
473
|
console.log(' {');
|
|
473
474
|
console.log(' "preset": "backend",');
|
|
@@ -1167,6 +1168,7 @@ Commands:
|
|
|
1167
1168
|
presets List all available presets
|
|
1168
1169
|
--set-preset <name> Set the active preset
|
|
1169
1170
|
preset current Show the current active preset
|
|
1171
|
+
--debug <value> Set debug mode (true, false, or status)
|
|
1170
1172
|
--version, -v Show the current version
|
|
1171
1173
|
help Show this help
|
|
1172
1174
|
|
|
@@ -1185,6 +1187,8 @@ Examples:
|
|
|
1185
1187
|
claude-hooks presets # List available presets
|
|
1186
1188
|
claude-hooks --set-preset backend # Set backend preset
|
|
1187
1189
|
claude-hooks preset current # Show current preset
|
|
1190
|
+
claude-hooks --debug true # Enable debug mode
|
|
1191
|
+
claude-hooks --debug status # Check debug status
|
|
1188
1192
|
|
|
1189
1193
|
Commit use cases:
|
|
1190
1194
|
git commit -m "message" # Manual message + blocking analysis
|
|
@@ -1236,6 +1240,12 @@ Parallel Analysis (v2.2.0+):
|
|
|
1236
1240
|
- batchSize: 4+ → Fewer API calls but slower
|
|
1237
1241
|
- Speed improvement: up to 4x faster with batchSize: 1
|
|
1238
1242
|
|
|
1243
|
+
Debug Mode:
|
|
1244
|
+
Enable detailed logging for troubleshooting:
|
|
1245
|
+
- CLI: claude-hooks --debug true
|
|
1246
|
+
- Config: "system": { "debug": true } in .claude/config.json
|
|
1247
|
+
- Check status: claude-hooks --debug status
|
|
1248
|
+
|
|
1239
1249
|
Customization:
|
|
1240
1250
|
Override prompts by copying to .claude/:
|
|
1241
1251
|
cp templates/COMMIT_MESSAGE.md .claude/
|
|
@@ -1247,6 +1257,68 @@ More information: https://github.com/pablorovito/claude-git-hooks
|
|
|
1247
1257
|
`);
|
|
1248
1258
|
}
|
|
1249
1259
|
|
|
1260
|
+
// Configuration Management Functions
|
|
1261
|
+
|
|
1262
|
+
/**
|
|
1263
|
+
* Updates a configuration value in .claude/config.json
|
|
1264
|
+
* Why: Centralized config update logic for all CLI commands
|
|
1265
|
+
*
|
|
1266
|
+
* @param {string} propertyPath - Dot notation path (e.g., 'preset', 'system.debug')
|
|
1267
|
+
* @param {any} value - Value to set
|
|
1268
|
+
* @param {Object} options - Optional settings
|
|
1269
|
+
* @param {Function} options.validator - Custom validation function, receives value, throws on invalid
|
|
1270
|
+
* @param {Function} options.successMessage - Function that receives value and returns success message
|
|
1271
|
+
*/
|
|
1272
|
+
async function updateConfig(propertyPath, value, options = {}) {
|
|
1273
|
+
const { validator, successMessage } = options;
|
|
1274
|
+
|
|
1275
|
+
try {
|
|
1276
|
+
// Validate value if validator provided
|
|
1277
|
+
if (validator) {
|
|
1278
|
+
await validator(value);
|
|
1279
|
+
}
|
|
1280
|
+
|
|
1281
|
+
// Get repo root
|
|
1282
|
+
const repoRoot = execSync('git rev-parse --show-toplevel', { encoding: 'utf8' }).trim();
|
|
1283
|
+
const configDir = path.join(repoRoot, '.claude');
|
|
1284
|
+
const configPath = path.join(configDir, 'config.json');
|
|
1285
|
+
|
|
1286
|
+
// Ensure .claude directory exists
|
|
1287
|
+
if (!fs.existsSync(configDir)) {
|
|
1288
|
+
fs.mkdirSync(configDir, { recursive: true });
|
|
1289
|
+
}
|
|
1290
|
+
|
|
1291
|
+
// Load existing config or create new
|
|
1292
|
+
let config = {};
|
|
1293
|
+
if (fs.existsSync(configPath)) {
|
|
1294
|
+
config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
1295
|
+
}
|
|
1296
|
+
|
|
1297
|
+
// Set value at propertyPath (support dot notation like 'system.debug')
|
|
1298
|
+
const pathParts = propertyPath.split('.');
|
|
1299
|
+
let current = config;
|
|
1300
|
+
for (let i = 0; i < pathParts.length - 1; i++) {
|
|
1301
|
+
const part = pathParts[i];
|
|
1302
|
+
if (!current[part] || typeof current[part] !== 'object') {
|
|
1303
|
+
current[part] = {};
|
|
1304
|
+
}
|
|
1305
|
+
current = current[part];
|
|
1306
|
+
}
|
|
1307
|
+
current[pathParts[pathParts.length - 1]] = value;
|
|
1308
|
+
|
|
1309
|
+
// Save config
|
|
1310
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
1311
|
+
|
|
1312
|
+
// Show success message
|
|
1313
|
+
const message = successMessage ? successMessage(value) : 'Configuration updated';
|
|
1314
|
+
success(message);
|
|
1315
|
+
info(`Configuration saved to ${configPath}`);
|
|
1316
|
+
} catch (err) {
|
|
1317
|
+
error(`Failed to update configuration: ${err.message}`);
|
|
1318
|
+
process.exit(1);
|
|
1319
|
+
}
|
|
1320
|
+
}
|
|
1321
|
+
|
|
1250
1322
|
// Preset Management Functions
|
|
1251
1323
|
|
|
1252
1324
|
/**
|
|
@@ -1282,6 +1354,7 @@ async function showPresets() {
|
|
|
1282
1354
|
|
|
1283
1355
|
/**
|
|
1284
1356
|
* Sets the active preset
|
|
1357
|
+
* Why: Configures tech-stack specific analysis settings
|
|
1285
1358
|
*/
|
|
1286
1359
|
async function setPreset(presetName) {
|
|
1287
1360
|
if (!presetName) {
|
|
@@ -1289,45 +1362,24 @@ async function setPreset(presetName) {
|
|
|
1289
1362
|
return;
|
|
1290
1363
|
}
|
|
1291
1364
|
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
// Ensure .claude directory exists
|
|
1310
|
-
if (!fs.existsSync(configDir)) {
|
|
1311
|
-
fs.mkdirSync(configDir, { recursive: true });
|
|
1312
|
-
}
|
|
1313
|
-
|
|
1314
|
-
// Load existing config or create new
|
|
1315
|
-
let config = {};
|
|
1316
|
-
if (fs.existsSync(configPath)) {
|
|
1317
|
-
config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
1365
|
+
await updateConfig('preset', presetName, {
|
|
1366
|
+
validator: async (name) => {
|
|
1367
|
+
const presets = await listPresets();
|
|
1368
|
+
const preset = presets.find(p => p.name === name);
|
|
1369
|
+
if (!preset) {
|
|
1370
|
+
error(`Preset "${name}" not found`);
|
|
1371
|
+
info('Available presets:');
|
|
1372
|
+
presets.forEach(p => console.log(` - ${p.name}`));
|
|
1373
|
+
throw new Error(`Invalid preset: ${name}`);
|
|
1374
|
+
}
|
|
1375
|
+
return preset;
|
|
1376
|
+
},
|
|
1377
|
+
successMessage: async (name) => {
|
|
1378
|
+
const presets = await listPresets();
|
|
1379
|
+
const preset = presets.find(p => p.name === name);
|
|
1380
|
+
return `Preset '${preset.displayName}' activated`;
|
|
1318
1381
|
}
|
|
1319
|
-
|
|
1320
|
-
// Set preset
|
|
1321
|
-
config.preset = presetName;
|
|
1322
|
-
|
|
1323
|
-
// Save config
|
|
1324
|
-
fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
1325
|
-
|
|
1326
|
-
success(`Preset '${preset.displayName}' activated`);
|
|
1327
|
-
info(`Configuration saved to ${configPath}`);
|
|
1328
|
-
} catch (err) {
|
|
1329
|
-
error(`Failed to set preset: ${err.message}`);
|
|
1330
|
-
}
|
|
1382
|
+
});
|
|
1331
1383
|
}
|
|
1332
1384
|
|
|
1333
1385
|
/**
|
|
@@ -1354,6 +1406,45 @@ async function currentPreset() {
|
|
|
1354
1406
|
}
|
|
1355
1407
|
}
|
|
1356
1408
|
|
|
1409
|
+
/**
|
|
1410
|
+
* Sets debug mode
|
|
1411
|
+
* Why: Enables detailed logging for troubleshooting
|
|
1412
|
+
*/
|
|
1413
|
+
async function setDebug(value) {
|
|
1414
|
+
if (!value) {
|
|
1415
|
+
error('Please specify a value: claude-hooks --debug <true|false|status>');
|
|
1416
|
+
return;
|
|
1417
|
+
}
|
|
1418
|
+
|
|
1419
|
+
const normalizedValue = value.toLowerCase();
|
|
1420
|
+
|
|
1421
|
+
// Handle status check
|
|
1422
|
+
if (normalizedValue === 'status') {
|
|
1423
|
+
try {
|
|
1424
|
+
const config = await getConfig();
|
|
1425
|
+
const isEnabled = config.system.debug || false;
|
|
1426
|
+
console.log('');
|
|
1427
|
+
info(`Debug mode: ${isEnabled ? colors.green + 'enabled' + colors.reset : colors.red + 'disabled' + colors.reset}`);
|
|
1428
|
+
console.log('');
|
|
1429
|
+
} catch (err) {
|
|
1430
|
+
error(`Failed to check debug status: ${err.message}`);
|
|
1431
|
+
}
|
|
1432
|
+
return;
|
|
1433
|
+
}
|
|
1434
|
+
|
|
1435
|
+
// Validate and convert to boolean
|
|
1436
|
+
if (normalizedValue !== 'true' && normalizedValue !== 'false') {
|
|
1437
|
+
error('Invalid value. Use: true, false, or status');
|
|
1438
|
+
return;
|
|
1439
|
+
}
|
|
1440
|
+
|
|
1441
|
+
const debugValue = normalizedValue === 'true';
|
|
1442
|
+
|
|
1443
|
+
await updateConfig('system.debug', debugValue, {
|
|
1444
|
+
successMessage: (val) => `Debug mode ${val ? 'enabled' : 'disabled'}`
|
|
1445
|
+
});
|
|
1446
|
+
}
|
|
1447
|
+
|
|
1357
1448
|
// Main
|
|
1358
1449
|
async function main() {
|
|
1359
1450
|
const args = process.argv.slice(2);
|
|
@@ -1395,6 +1486,9 @@ async function main() {
|
|
|
1395
1486
|
error(`Unknown preset subcommand: ${args[1]}`);
|
|
1396
1487
|
}
|
|
1397
1488
|
break;
|
|
1489
|
+
case '--debug':
|
|
1490
|
+
await setDebug(args[1]);
|
|
1491
|
+
break;
|
|
1398
1492
|
case '--version':
|
|
1399
1493
|
case '-v':
|
|
1400
1494
|
case 'version':
|
package/lib/config.js
CHANGED
|
@@ -26,9 +26,9 @@ const __dirname = path.dirname(__filename);
|
|
|
26
26
|
const defaults = {
|
|
27
27
|
// Analysis configuration
|
|
28
28
|
analysis: {
|
|
29
|
-
maxFileSize:
|
|
30
|
-
maxFiles:
|
|
31
|
-
timeout:
|
|
29
|
+
maxFileSize: 1000000, // 1MB - max file size to analyze
|
|
30
|
+
maxFiles: 20, // Max number of files per commit
|
|
31
|
+
timeout: 300000, // 3 minutes - Claude analysis timeout
|
|
32
32
|
contextLines: 3, // Git diff context lines
|
|
33
33
|
ignoreExtensions: [], // Extensions to exclude (e.g., ['.min.js', '.lock'])
|
|
34
34
|
// NOTE: allowedExtensions comes from preset/template, not here
|
|
@@ -37,14 +37,14 @@ const defaults = {
|
|
|
37
37
|
// Commit message generation
|
|
38
38
|
commitMessage: {
|
|
39
39
|
autoKeyword: 'auto', // Keyword to trigger auto-generation
|
|
40
|
-
timeout:
|
|
40
|
+
timeout: 300000,
|
|
41
41
|
},
|
|
42
42
|
|
|
43
43
|
// Subagent configuration (parallel analysis)
|
|
44
44
|
subagents: {
|
|
45
|
-
enabled:
|
|
45
|
+
enabled: false, // Enable parallel file analysis
|
|
46
46
|
model: 'haiku', // haiku (fast), sonnet (balanced), opus (thorough)
|
|
47
|
-
batchSize:
|
|
47
|
+
batchSize: 1, // Parallel subagents per batch
|
|
48
48
|
},
|
|
49
49
|
|
|
50
50
|
// Template paths
|
package/lib/hooks/pre-commit.js
CHANGED
|
@@ -181,12 +181,38 @@ const main = async () => {
|
|
|
181
181
|
// Load configuration
|
|
182
182
|
const config = await getConfig();
|
|
183
183
|
|
|
184
|
+
// Enable debug mode from config
|
|
185
|
+
if (config.system.debug) {
|
|
186
|
+
logger.setDebugMode(true);
|
|
187
|
+
}
|
|
188
|
+
|
|
184
189
|
// Display configuration info
|
|
185
190
|
const version = await getVersion();
|
|
186
191
|
console.log(`\n🤖 claude-git-hooks v${version}`);
|
|
187
192
|
|
|
188
193
|
logger.info('Starting code quality analysis...');
|
|
189
194
|
|
|
195
|
+
// DEBUG: Log working directories
|
|
196
|
+
const repoRoot = await import('../utils/git-operations.js').then(m => m.getRepoRoot());
|
|
197
|
+
const { getRepoRoot } = await import('../utils/git-operations.js');
|
|
198
|
+
|
|
199
|
+
// Normalize paths for comparison (handle Windows backslash vs forward slash)
|
|
200
|
+
const normalizePath = (p) => p.replace(/\\/g, '/').toLowerCase();
|
|
201
|
+
const cwdNormalized = normalizePath(process.cwd());
|
|
202
|
+
const repoRootNormalized = normalizePath(repoRoot);
|
|
203
|
+
|
|
204
|
+
logger.debug(
|
|
205
|
+
'pre-commit - main',
|
|
206
|
+
'Working directory info',
|
|
207
|
+
{
|
|
208
|
+
'process.cwd()': process.cwd(),
|
|
209
|
+
'repo root': repoRoot,
|
|
210
|
+
'cwd (normalized)': cwdNormalized,
|
|
211
|
+
'repo root (normalized)': repoRootNormalized,
|
|
212
|
+
'match': cwdNormalized === repoRootNormalized
|
|
213
|
+
}
|
|
214
|
+
);
|
|
215
|
+
|
|
190
216
|
logger.debug(
|
|
191
217
|
'pre-commit - main',
|
|
192
218
|
'Configuration',
|
|
@@ -237,6 +263,14 @@ const main = async () => {
|
|
|
237
263
|
});
|
|
238
264
|
|
|
239
265
|
const validFiles = filteredFiles.filter(f => f.valid);
|
|
266
|
+
const invalidFiles = filteredFiles.filter(f => !f.valid);
|
|
267
|
+
|
|
268
|
+
// Show user-facing warnings for rejected files
|
|
269
|
+
if (invalidFiles.length > 0) {
|
|
270
|
+
invalidFiles.forEach(file => {
|
|
271
|
+
logger.warning(`Skipping ${file.path}: ${file.reason}`);
|
|
272
|
+
});
|
|
273
|
+
}
|
|
240
274
|
|
|
241
275
|
if (validFiles.length === 0) {
|
|
242
276
|
logger.warning('No valid files found to review');
|
|
@@ -120,6 +120,11 @@ const main = async () => {
|
|
|
120
120
|
// Load configuration (includes preset + user overrides)
|
|
121
121
|
const config = await getConfig();
|
|
122
122
|
|
|
123
|
+
// Enable debug mode from config
|
|
124
|
+
if (config.system.debug) {
|
|
125
|
+
logger.setDebugMode(true);
|
|
126
|
+
}
|
|
127
|
+
|
|
123
128
|
try {
|
|
124
129
|
// Get hook arguments
|
|
125
130
|
const args = process.argv.slice(2);
|
|
@@ -250,13 +250,24 @@ const filterFiles = async (files, { maxSize = 100000, extensions = [] } = {}) =>
|
|
|
250
250
|
logger.debug(
|
|
251
251
|
'file-operations - filterFiles',
|
|
252
252
|
'Filtering files',
|
|
253
|
-
{
|
|
253
|
+
{
|
|
254
|
+
fileCount: files.length,
|
|
255
|
+
maxSize,
|
|
256
|
+
extensions,
|
|
257
|
+
'process.cwd()': process.cwd(),
|
|
258
|
+
files: files
|
|
259
|
+
}
|
|
254
260
|
);
|
|
255
261
|
|
|
256
262
|
const results = await Promise.allSettled(
|
|
257
263
|
files.map(async (filePath) => {
|
|
258
264
|
// Check extension first (fast)
|
|
259
265
|
if (!hasAllowedExtension(filePath, extensions)) {
|
|
266
|
+
logger.debug(
|
|
267
|
+
'file-operations - filterFiles',
|
|
268
|
+
'Extension rejected',
|
|
269
|
+
{ filePath }
|
|
270
|
+
);
|
|
260
271
|
return {
|
|
261
272
|
path: filePath,
|
|
262
273
|
size: 0,
|
|
@@ -267,6 +278,11 @@ const filterFiles = async (files, { maxSize = 100000, extensions = [] } = {}) =>
|
|
|
267
278
|
|
|
268
279
|
// Check if file exists
|
|
269
280
|
const exists = await fileExists(filePath);
|
|
281
|
+
logger.debug(
|
|
282
|
+
'file-operations - filterFiles',
|
|
283
|
+
'File exists check',
|
|
284
|
+
{ filePath, exists }
|
|
285
|
+
);
|
|
270
286
|
if (!exists) {
|
|
271
287
|
return {
|
|
272
288
|
path: filePath,
|
|
@@ -281,6 +297,11 @@ const filterFiles = async (files, { maxSize = 100000, extensions = [] } = {}) =>
|
|
|
281
297
|
const size = await getFileSize(filePath);
|
|
282
298
|
|
|
283
299
|
if (size > maxSize) {
|
|
300
|
+
logger.debug(
|
|
301
|
+
'file-operations - filterFiles',
|
|
302
|
+
'File too large',
|
|
303
|
+
{ filePath, size, maxSize, 'size (KB)': Math.round(size / 1024), 'maxSize (KB)': Math.round(maxSize / 1024) }
|
|
304
|
+
);
|
|
284
305
|
return {
|
|
285
306
|
path: filePath,
|
|
286
307
|
size,
|
|
@@ -289,6 +310,12 @@ const filterFiles = async (files, { maxSize = 100000, extensions = [] } = {}) =>
|
|
|
289
310
|
};
|
|
290
311
|
}
|
|
291
312
|
|
|
313
|
+
logger.debug(
|
|
314
|
+
'file-operations - filterFiles',
|
|
315
|
+
'File passed size check',
|
|
316
|
+
{ filePath, size, maxSize, 'size (KB)': Math.round(size / 1024) }
|
|
317
|
+
);
|
|
318
|
+
|
|
292
319
|
return {
|
|
293
320
|
path: filePath,
|
|
294
321
|
size,
|
|
@@ -297,6 +324,11 @@ const filterFiles = async (files, { maxSize = 100000, extensions = [] } = {}) =>
|
|
|
297
324
|
};
|
|
298
325
|
|
|
299
326
|
} catch (error) {
|
|
327
|
+
logger.debug(
|
|
328
|
+
'file-operations - filterFiles',
|
|
329
|
+
'Error reading file',
|
|
330
|
+
{ filePath, error: error.message }
|
|
331
|
+
);
|
|
300
332
|
return {
|
|
301
333
|
path: filePath,
|
|
302
334
|
size: 0,
|
|
@@ -312,15 +344,17 @@ const filterFiles = async (files, { maxSize = 100000, extensions = [] } = {}) =>
|
|
|
312
344
|
.filter(r => r.status === 'fulfilled')
|
|
313
345
|
.map(r => r.value);
|
|
314
346
|
|
|
315
|
-
const
|
|
347
|
+
const validFiles = fileMetadata.filter(f => f.valid);
|
|
348
|
+
const invalidFiles = fileMetadata.filter(f => !f.valid);
|
|
316
349
|
|
|
317
350
|
logger.debug(
|
|
318
351
|
'file-operations - filterFiles',
|
|
319
352
|
'Filtering complete',
|
|
320
353
|
{
|
|
321
354
|
totalFiles: files.length,
|
|
322
|
-
validFiles:
|
|
323
|
-
invalidFiles:
|
|
355
|
+
validFiles: validFiles.length,
|
|
356
|
+
invalidFiles: invalidFiles.length,
|
|
357
|
+
rejectedFiles: invalidFiles.map(f => ({ path: f.path, reason: f.reason }))
|
|
324
358
|
}
|
|
325
359
|
);
|
|
326
360
|
|
package/lib/utils/logger.js
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
|
|
17
17
|
class Logger {
|
|
18
18
|
constructor({ debugMode = false } = {}) {
|
|
19
|
-
this.debugMode = debugMode
|
|
19
|
+
this.debugMode = debugMode;
|
|
20
20
|
this.colors = {
|
|
21
21
|
reset: '\x1b[0m',
|
|
22
22
|
red: '\x1b[31m',
|
|
@@ -138,4 +138,4 @@ class Logger {
|
|
|
138
138
|
|
|
139
139
|
// Export singleton instance
|
|
140
140
|
// Why: Single logger instance ensures consistent debug mode across entire application
|
|
141
|
-
export default new Logger(
|
|
141
|
+
export default new Logger();
|
package/package.json
CHANGED
|
@@ -108,7 +108,7 @@ A preset is a **self-contained package** that customizes claude-hooks for a spec
|
|
|
108
108
|
│ 3. CONFIGURATION MERGED │
|
|
109
109
|
│ defaults < user config < preset config │
|
|
110
110
|
│ - File extensions: ['.java', '.xml', '.yml'] │
|
|
111
|
-
│ - Max file size:
|
|
111
|
+
│ - Max file size: 1MB │
|
|
112
112
|
│ - Parallel analysis: enabled │
|
|
113
113
|
└────────────────────────┬────────────────────────────────────┘
|
|
114
114
|
│
|
|
@@ -282,7 +282,7 @@ cd python-django
|
|
|
282
282
|
```json
|
|
283
283
|
{
|
|
284
284
|
"analysis": {
|
|
285
|
-
"maxFileSize":
|
|
285
|
+
"maxFileSize": 1000000,
|
|
286
286
|
"maxFiles": 12,
|
|
287
287
|
"timeout": 180000
|
|
288
288
|
},
|
|
@@ -306,9 +306,9 @@ cd python-django
|
|
|
306
306
|
```javascript
|
|
307
307
|
{
|
|
308
308
|
analysis: {
|
|
309
|
-
maxFileSize:
|
|
310
|
-
maxFiles:
|
|
311
|
-
timeout:
|
|
309
|
+
maxFileSize: 1000000, // 1MB
|
|
310
|
+
maxFiles: 30,
|
|
311
|
+
timeout: 180000 // 3 minutes
|
|
312
312
|
},
|
|
313
313
|
subagents: {
|
|
314
314
|
enabled: true,
|
|
@@ -1,41 +1,41 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
2
|
+
"preset": "ai",
|
|
3
|
+
"analysis": {
|
|
4
|
+
"maxFileSize": 1000000,
|
|
5
|
+
"maxFiles": 30,
|
|
6
|
+
"timeout": 180000,
|
|
7
|
+
"contextLines": 3,
|
|
8
|
+
"ignoreExtensions": []
|
|
9
|
+
},
|
|
10
|
+
"commitMessage": {
|
|
11
|
+
"autoKeyword": "auto",
|
|
12
|
+
"timeout": 180000
|
|
13
|
+
},
|
|
14
|
+
"subagents": {
|
|
15
|
+
"enabled": false,
|
|
16
|
+
"model": "haiku",
|
|
17
|
+
"batchSize": 1
|
|
18
|
+
},
|
|
19
|
+
"templates": {
|
|
20
|
+
"baseDir": ".claude",
|
|
21
|
+
"analysis": "CLAUDE_ANALYSIS_PROMPT_SONAR.md",
|
|
22
|
+
"guidelines": "CLAUDE_PRE_COMMIT_SONAR.md",
|
|
23
|
+
"commitMessage": "COMMIT_MESSAGE.md",
|
|
24
|
+
"analyzeDiff": "ANALYZE_DIFF.md",
|
|
25
|
+
"resolution": "CLAUDE_RESOLUTION_PROMPT.md",
|
|
26
|
+
"subagentInstruction": "SUBAGENT_INSTRUCTION.md"
|
|
27
|
+
},
|
|
28
|
+
"output": {
|
|
29
|
+
"outputDir": ".claude/out",
|
|
30
|
+
"debugFile": ".claude/out/debug-claude-response.json",
|
|
31
|
+
"resolutionFile": ".claude/out/claude_resolution_prompt.md",
|
|
32
|
+
"prAnalysisFile": ".claude/out/pr-analysis.json"
|
|
33
|
+
},
|
|
34
|
+
"system": {
|
|
35
|
+
"debug": false,
|
|
36
|
+
"wslCheckTimeout": 3000
|
|
37
|
+
},
|
|
38
|
+
"git": {
|
|
39
|
+
"diffFilter": "ACM"
|
|
40
|
+
}
|
|
41
41
|
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
2
|
+
"analysis": {
|
|
3
|
+
"maxFileSize": 1000000,
|
|
4
|
+
"maxFiles": 10,
|
|
5
|
+
"timeout": 300000
|
|
6
|
+
},
|
|
7
|
+
"subagents": {
|
|
8
|
+
"enabled": true,
|
|
9
|
+
"model": "haiku",
|
|
10
|
+
"batchSize": 3
|
|
11
|
+
}
|
|
12
12
|
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
2
|
+
"analysis": {
|
|
3
|
+
"maxFileSize": 1000000,
|
|
4
|
+
"maxFiles": 10,
|
|
5
|
+
"timeout": 300000
|
|
6
|
+
},
|
|
7
|
+
"subagents": {
|
|
8
|
+
"enabled": true,
|
|
9
|
+
"model": "haiku",
|
|
10
|
+
"batchSize": 3
|
|
11
|
+
}
|
|
12
12
|
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
2
|
+
"analysis": {
|
|
3
|
+
"maxFileSize": 1000000,
|
|
4
|
+
"maxFiles": 8,
|
|
5
|
+
"timeout": 300000
|
|
6
|
+
},
|
|
7
|
+
"subagents": {
|
|
8
|
+
"enabled": true,
|
|
9
|
+
"model": "haiku",
|
|
10
|
+
"batchSize": 2
|
|
11
|
+
}
|
|
12
12
|
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
2
|
+
"analysis": {
|
|
3
|
+
"maxFileSize": 1000000,
|
|
4
|
+
"maxFiles": 10,
|
|
5
|
+
"timeout": 300000
|
|
6
|
+
},
|
|
7
|
+
"subagents": {
|
|
8
|
+
"enabled": true,
|
|
9
|
+
"model": "haiku",
|
|
10
|
+
"batchSize": 3
|
|
11
|
+
}
|
|
12
12
|
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
2
|
+
"analysis": {
|
|
3
|
+
"maxFileSize": 1000000,
|
|
4
|
+
"maxFiles": 10,
|
|
5
|
+
"timeout": 300000
|
|
6
|
+
},
|
|
7
|
+
"subagents": {
|
|
8
|
+
"enabled": true,
|
|
9
|
+
"model": "haiku",
|
|
10
|
+
"batchSize": 3
|
|
11
|
+
}
|
|
12
12
|
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
2
|
+
"analysis": {
|
|
3
|
+
"maxFileSize": 1000000,
|
|
4
|
+
"maxFiles": 15,
|
|
5
|
+
"timeout": 300000
|
|
6
|
+
},
|
|
7
|
+
"subagents": {
|
|
8
|
+
"enabled": true,
|
|
9
|
+
"model": "haiku",
|
|
10
|
+
"batchSize": 4
|
|
11
|
+
}
|
|
12
12
|
}
|