claude-git-hooks 2.6.3 → 2.8.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.
@@ -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
  /**
@@ -103,12 +103,15 @@ const getClaudeCommand = () => {
103
103
  if (wslPath) {
104
104
  try {
105
105
  // Verify Claude exists in WSL
106
- execSync(`"${wslPath}" claude --version`, { stdio: 'ignore', timeout: 5000 });
106
+ // Increased timeout from 5s to 15s to handle system load better
107
+ const wslCheckTimeout = config.system.wslCheckTimeout || 15000;
108
+ execSync(`"${wslPath}" claude --version`, { stdio: 'ignore', timeout: wslCheckTimeout });
107
109
  logger.debug('claude-client - getClaudeCommand', 'Using WSL Claude CLI', { wslPath });
108
110
  return { command: wslPath, args: ['claude'] };
109
111
  } catch (wslError) {
110
112
  // Differentiate error types for accurate user feedback
111
113
  const errorMsg = wslError.message || '';
114
+ const wslCheckTimeout = config.system.wslCheckTimeout || 15000;
112
115
 
113
116
  // Timeout: Transient system load issue
114
117
  if (errorMsg.includes('ETIMEDOUT')) {
@@ -117,6 +120,7 @@ const getClaudeCommand = () => {
117
120
  platform: 'Windows',
118
121
  wslPath,
119
122
  error: 'ETIMEDOUT',
123
+ timeoutValue: wslCheckTimeout,
120
124
  suggestion: 'System is busy. Wait a moment and try again, or skip analysis: git commit --no-verify'
121
125
  }
122
126
  });
@@ -241,13 +245,57 @@ const executeClaude = (prompt, { timeout = 120000, allowedTools = [] } = {}) =>
241
245
 
242
246
  // Handle process completion
243
247
  claude.on('close', (code) => {
244
- const duration = Date.now() - startTime;
248
+ const elapsedTime = Date.now() - startTime;
249
+
250
+ // Check for "Execution error" even when exit code is 0
251
+ // Why: Claude CLI sometimes returns "Execution error" with exit code 0
252
+ // This occurs during API rate limiting or temporary backend issues
253
+ // IMPORTANT: Only check for EXACT match to avoid false positives
254
+ if (stdout.trim() === 'Execution error') {
255
+ const errorInfo = detectClaudeError(stdout, stderr, code);
256
+
257
+ logger.error(
258
+ 'claude-client - executeClaude',
259
+ `Claude CLI returned execution error (exit ${code})`,
260
+ new ClaudeClientError(`Claude CLI error: ${errorInfo.type}`, {
261
+ output: { stdout, stderr },
262
+ context: {
263
+ exitCode: code,
264
+ elapsedTime,
265
+ timeoutValue: timeout,
266
+ errorType: errorInfo.type
267
+ }
268
+ })
269
+ );
270
+
271
+ // Merge timing info into errorInfo for formatting
272
+ const errorInfoWithTiming = {
273
+ ...errorInfo,
274
+ elapsedTime,
275
+ timeoutValue: timeout
276
+ };
277
+
278
+ // Show formatted error to user
279
+ const formattedError = formatClaudeError(errorInfoWithTiming);
280
+ console.error(`\n${ formattedError }\n`);
281
+
282
+ reject(new ClaudeClientError(`Claude CLI error: ${errorInfo.type}`, {
283
+ output: { stdout, stderr },
284
+ context: {
285
+ exitCode: code,
286
+ elapsedTime,
287
+ timeoutValue: timeout,
288
+ errorInfo
289
+ }
290
+ }));
291
+ return;
292
+ }
245
293
 
246
294
  if (code === 0) {
247
295
  logger.debug(
248
296
  'claude-client - executeClaude',
249
297
  'Claude CLI execution successful',
250
- { duration, outputLength: stdout.length }
298
+ { elapsedTime, outputLength: stdout.length }
251
299
  );
252
300
  resolve(stdout);
253
301
  } else {
@@ -259,23 +307,42 @@ const executeClaude = (prompt, { timeout = 120000, allowedTools = [] } = {}) =>
259
307
  `Claude CLI failed: ${errorInfo.type}`,
260
308
  new ClaudeClientError(`Claude CLI error: ${errorInfo.type}`, {
261
309
  output: { stdout, stderr },
262
- context: { exitCode: code, duration, errorType: errorInfo.type }
310
+ context: {
311
+ exitCode: code,
312
+ elapsedTime,
313
+ timeoutValue: timeout,
314
+ errorType: errorInfo.type
315
+ }
263
316
  })
264
317
  );
265
318
 
319
+ // Merge timing info into errorInfo for formatting
320
+ const errorInfoWithTiming = {
321
+ ...errorInfo,
322
+ elapsedTime,
323
+ timeoutValue: timeout
324
+ };
325
+
266
326
  // Show formatted error to user
267
- const formattedError = formatClaudeError(errorInfo);
327
+ const formattedError = formatClaudeError(errorInfoWithTiming);
268
328
  console.error(`\n${ formattedError }\n`);
269
329
 
270
330
  reject(new ClaudeClientError(`Claude CLI error: ${errorInfo.type}`, {
271
331
  output: { stdout, stderr },
272
- context: { exitCode: code, duration, errorInfo }
332
+ context: {
333
+ exitCode: code,
334
+ elapsedTime,
335
+ timeoutValue: timeout,
336
+ errorInfo
337
+ }
273
338
  }));
274
339
  }
275
340
  });
276
341
 
277
342
  // Handle errors
278
343
  claude.on('error', (error) => {
344
+ const elapsedTime = Date.now() - startTime;
345
+
279
346
  logger.error(
280
347
  'claude-client - executeClaude',
281
348
  'Failed to spawn Claude CLI process',
@@ -284,23 +351,36 @@ const executeClaude = (prompt, { timeout = 120000, allowedTools = [] } = {}) =>
284
351
 
285
352
  reject(new ClaudeClientError('Failed to spawn Claude CLI', {
286
353
  cause: error,
287
- context: { command, args }
354
+ context: {
355
+ command,
356
+ args,
357
+ elapsedTime,
358
+ timeoutValue: timeout
359
+ }
288
360
  }));
289
361
  });
290
362
 
291
363
  // Set up timeout
292
364
  const timeoutId = setTimeout(() => {
365
+ const elapsedTime = Date.now() - startTime;
293
366
  claude.kill();
367
+
294
368
  logger.error(
295
369
  'claude-client - executeClaude',
296
370
  'Claude CLI execution timed out',
297
371
  new ClaudeClientError('Claude CLI timeout', {
298
- context: { timeout }
372
+ context: {
373
+ elapsedTime,
374
+ timeoutValue: timeout
375
+ }
299
376
  })
300
377
  );
301
378
 
302
379
  reject(new ClaudeClientError('Claude CLI execution timed out', {
303
- context: { timeout }
380
+ context: {
381
+ elapsedTime,
382
+ timeoutValue: timeout
383
+ }
304
384
  }));
305
385
  }, timeout);
306
386
 
@@ -580,6 +660,90 @@ const saveDebugResponse = async (prompt, response, filename = config.output.debu
580
660
  }
581
661
  };
582
662
 
663
+ /**
664
+ * Wraps an async function with retry logic for recoverable errors
665
+ * Why: Reusable retry logic for Claude CLI operations
666
+ *
667
+ * @param {Function} fn - Async function to execute with retry
668
+ * @param {Object} options - Retry options
669
+ * @param {number} options.maxRetries - Maximum retry attempts (default: 3)
670
+ * @param {number} options.baseRetryDelay - Base delay in ms (default: 2000)
671
+ * @param {number} options.retryCount - Current retry attempt (internal, default: 0)
672
+ * @param {string} options.operationName - Name for logging (default: 'operation')
673
+ * @returns {Promise<any>} Result from fn
674
+ * @throws {Error} If fn fails after all retries
675
+ */
676
+ const withRetry = async (fn, { maxRetries = 3, baseRetryDelay = 2000, retryCount = 0, operationName = 'operation' } = {}) => {
677
+ const retryDelay = baseRetryDelay * Math.pow(2, retryCount);
678
+
679
+ try {
680
+ return await fn();
681
+ } catch (error) {
682
+ // Check if error is recoverable and we haven't exceeded retry limit
683
+ const hasContext = !!error.context;
684
+ const hasErrorInfo = !!error.context?.errorInfo;
685
+ const isRecoverable = hasErrorInfo ? isRecoverableError(error.context.errorInfo) : false;
686
+ const canRetry = retryCount < maxRetries;
687
+
688
+ logger.debug(
689
+ 'claude-client - withRetry',
690
+ `Retry check for ${operationName}`,
691
+ {
692
+ retryCount,
693
+ maxRetries,
694
+ hasContext,
695
+ hasErrorInfo,
696
+ isRecoverable,
697
+ canRetry,
698
+ errorType: error.context?.errorInfo?.type,
699
+ errorName: error.name
700
+ }
701
+ );
702
+
703
+ const shouldRetry = canRetry && hasContext && hasErrorInfo && isRecoverable;
704
+
705
+ if (shouldRetry) {
706
+ logger.debug(
707
+ 'claude-client - withRetry',
708
+ `Recoverable error detected, retrying ${operationName} in ${retryDelay}ms (attempt ${retryCount + 1}/${maxRetries})`,
709
+ { errorType: error.context.errorInfo.type }
710
+ );
711
+
712
+ console.log(`\n⏳ Retrying in ${retryDelay / 1000} seconds due to ${error.context.errorInfo.type}...\n`);
713
+
714
+ // Wait before retry
715
+ await new Promise(resolve => setTimeout(resolve, retryDelay));
716
+
717
+ // Retry with incremented count
718
+ return withRetry(fn, { maxRetries, baseRetryDelay, retryCount: retryCount + 1, operationName });
719
+ }
720
+
721
+ // Add retry attempt to error context if not already present
722
+ if (error.context && !error.context.retryAttempt) {
723
+ error.context.retryAttempt = retryCount;
724
+ error.context.maxRetries = maxRetries;
725
+ }
726
+
727
+ logger.error('claude-client - withRetry', `${operationName} failed after retries`, error);
728
+ throw error;
729
+ }
730
+ };
731
+
732
+ /**
733
+ * Executes Claude CLI with retry logic
734
+ * Why: Provides retry capability for executeClaude calls
735
+ *
736
+ * @param {string} prompt - Prompt text
737
+ * @param {Object} options - Execution options (timeout, allowedTools)
738
+ * @returns {Promise<string>} Claude's response
739
+ */
740
+ const executeClaudeWithRetry = async (prompt, options = {}) => {
741
+ return withRetry(
742
+ () => executeClaude(prompt, options),
743
+ { operationName: 'executeClaude' }
744
+ );
745
+ };
746
+
583
747
  /**
584
748
  * Analyzes code using Claude CLI
585
749
  * Why: High-level interface that handles execution, parsing, and debug
@@ -598,34 +762,34 @@ const analyzeCode = async (prompt, { timeout = 120000, saveDebug = config.system
598
762
  { promptLength: prompt.length, timeout, saveDebug }
599
763
  );
600
764
 
601
- try {
602
- // Execute Claude CLI
603
- const response = await executeClaude(prompt, { timeout });
604
-
605
- // Save debug if requested
606
- if (saveDebug) {
607
- await saveDebugResponse(prompt, response);
608
- }
765
+ // Use withRetry to wrap the entire analysis flow
766
+ return withRetry(
767
+ async () => {
768
+ // Execute Claude CLI
769
+ const response = await executeClaude(prompt, { timeout });
609
770
 
610
- // Extract and parse JSON
611
- const result = extractJSON(response);
612
-
613
- logger.debug(
614
- 'claude-client - analyzeCode',
615
- 'Analysis complete',
616
- {
617
- hasApproved: 'approved' in result,
618
- hasQualityGate: 'QUALITY_GATE' in result,
619
- blockingIssuesCount: result.blockingIssues?.length ?? 0
771
+ // Save debug if requested
772
+ if (saveDebug) {
773
+ await saveDebugResponse(prompt, response);
620
774
  }
621
- );
622
775
 
623
- return result;
776
+ // Extract and parse JSON
777
+ const result = extractJSON(response);
624
778
 
625
- } catch (error) {
626
- logger.error('claude-client - analyzeCode', 'Code analysis failed', error);
627
- throw error;
628
- }
779
+ logger.debug(
780
+ 'claude-client - analyzeCode',
781
+ 'Analysis complete',
782
+ {
783
+ hasApproved: 'approved' in result,
784
+ hasQualityGate: 'QUALITY_GATE' in result,
785
+ blockingIssuesCount: result.blockingIssues?.length ?? 0
786
+ }
787
+ );
788
+
789
+ return result;
790
+ },
791
+ { operationName: 'analyzeCode' }
792
+ );
629
793
  };
630
794
 
631
795
  /**
@@ -679,6 +843,7 @@ const analyzeCodeParallel = async (prompts, options = {}) => {
679
843
  export {
680
844
  ClaudeClientError,
681
845
  executeClaude,
846
+ executeClaudeWithRetry,
682
847
  executeClaudeInteractive,
683
848
  extractJSON,
684
849
  saveDebugResponse,
@@ -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. Rate limit detection
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
- // 2. Authentication failure detection
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
- // 3. Network errors
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
- // 4. Invalid response (JSON parsing errors)
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
- // 5. Generic error
104
+ // 6. Generic error
91
105
  return {
92
106
  type: ClaudeErrorType.GENERIC,
93
107
  exitCode,
@@ -96,6 +110,38 @@ export const detectClaudeError = (stdout = '', stderr = '', exitCode = 1) => {
96
110
  };
97
111
  };
98
112
 
113
+ /**
114
+ * Formats timing information for error messages
115
+ * @param {Object} errorInfo - Error information with optional timing data
116
+ * @returns {Array<string>} Array of formatted timing lines
117
+ */
118
+ const formatTimingInfo = (errorInfo) => {
119
+ const lines = [];
120
+ const { elapsedTime, timeoutValue, retryAttempt, maxRetries } = errorInfo;
121
+
122
+ if (elapsedTime !== undefined || timeoutValue !== undefined || retryAttempt !== undefined) {
123
+ lines.push('');
124
+ lines.push('Timing information:');
125
+
126
+ if (elapsedTime !== undefined) {
127
+ const seconds = (elapsedTime / 1000).toFixed(2);
128
+ lines.push(` Elapsed time: ${seconds}s`);
129
+ }
130
+
131
+ if (timeoutValue !== undefined) {
132
+ const timeoutSeconds = (timeoutValue / 1000).toFixed(0);
133
+ lines.push(` Timeout limit: ${timeoutSeconds}s`);
134
+ }
135
+
136
+ if (retryAttempt !== undefined) {
137
+ const maxRetriesText = maxRetries !== undefined ? `/${maxRetries}` : '';
138
+ lines.push(` Retry attempt: ${retryAttempt}${maxRetriesText}`);
139
+ }
140
+ }
141
+
142
+ return lines;
143
+ };
144
+
99
145
  /**
100
146
  * Formats Claude error message with diagnostics and remediation steps
101
147
  * Why: Provides consistent, actionable error messages
@@ -107,6 +153,9 @@ export const formatClaudeError = (errorInfo) => {
107
153
  const lines = [];
108
154
 
109
155
  switch (errorInfo.type) {
156
+ case ClaudeErrorType.EXECUTION_ERROR:
157
+ return formatExecutionError(errorInfo);
158
+
110
159
  case ClaudeErrorType.RATE_LIMIT:
111
160
  return formatRateLimitError(errorInfo);
112
161
 
@@ -125,10 +174,42 @@ export const formatClaudeError = (errorInfo) => {
125
174
  }
126
175
  };
127
176
 
177
+ /**
178
+ * Format execution error
179
+ */
180
+ const formatExecutionError = (errorInfo) => {
181
+ const { exitCode, stdout } = errorInfo;
182
+ const lines = [];
183
+
184
+ lines.push('❌ Claude CLI execution error');
185
+ lines.push('');
186
+ lines.push('Claude returned "Execution error" instead of valid response.');
187
+ lines.push(...formatTimingInfo(errorInfo));
188
+ lines.push('');
189
+ lines.push('Common causes:');
190
+ lines.push(' 1. API rate limiting (burst limits)');
191
+ lines.push(' 2. Temporary Claude API backend issues');
192
+ lines.push(' 3. Request/response size limits exceeded');
193
+ lines.push('');
194
+ lines.push('Solutions:');
195
+ lines.push(' 1. Wait 10-30 seconds and try again');
196
+ lines.push(' 2. Reduce prompt size by committing fewer files at once');
197
+ lines.push(' 3. Switch to haiku model (faster, higher rate limits):');
198
+ lines.push(' Edit .claude/config.json:');
199
+ lines.push(' { "subagents": { "model": "haiku" } }');
200
+ lines.push(' 4. Skip analysis for now:');
201
+ lines.push(' git commit --no-verify -m "your message"');
202
+ lines.push('');
203
+ lines.push('The hook will automatically retry once after 2 seconds.');
204
+
205
+ return lines.join('\n');
206
+ };
207
+
128
208
  /**
129
209
  * Format rate limit error
130
210
  */
131
- const formatRateLimitError = ({ resetDate, minutesUntilReset }) => {
211
+ const formatRateLimitError = (errorInfo) => {
212
+ const { resetDate, minutesUntilReset } = errorInfo;
132
213
  const lines = [];
133
214
 
134
215
  lines.push('❌ Claude API usage limit reached');
@@ -145,6 +226,7 @@ const formatRateLimitError = ({ resetDate, minutesUntilReset }) => {
145
226
  lines.push(' Limit should be reset now');
146
227
  }
147
228
 
229
+ lines.push(...formatTimingInfo(errorInfo));
148
230
  lines.push('');
149
231
  lines.push('Options:');
150
232
  lines.push(' 1. Wait for rate limit to reset');
@@ -160,10 +242,12 @@ const formatRateLimitError = ({ resetDate, minutesUntilReset }) => {
160
242
  /**
161
243
  * Format authentication error
162
244
  */
163
- const formatAuthError = ({ exitCode }) => {
245
+ const formatAuthError = (errorInfo) => {
246
+ const { exitCode } = errorInfo;
164
247
  const lines = [];
165
248
 
166
249
  lines.push('❌ Claude CLI authentication failed');
250
+ lines.push(...formatTimingInfo(errorInfo));
167
251
  lines.push('');
168
252
  lines.push('Possible causes:');
169
253
  lines.push(' 1. Not logged in to Claude CLI');
@@ -181,10 +265,12 @@ const formatAuthError = ({ exitCode }) => {
181
265
  /**
182
266
  * Format network error
183
267
  */
184
- const formatNetworkError = ({ exitCode }) => {
268
+ const formatNetworkError = (errorInfo) => {
269
+ const { exitCode } = errorInfo;
185
270
  const lines = [];
186
271
 
187
272
  lines.push('❌ Network error connecting to Claude API');
273
+ lines.push(...formatTimingInfo(errorInfo));
188
274
  lines.push('');
189
275
  lines.push('Possible causes:');
190
276
  lines.push(' 1. No internet connection');
@@ -203,10 +289,12 @@ const formatNetworkError = ({ exitCode }) => {
203
289
  /**
204
290
  * Format invalid response error
205
291
  */
206
- const formatInvalidResponseError = ({ exitCode }) => {
292
+ const formatInvalidResponseError = (errorInfo) => {
293
+ const { exitCode } = errorInfo;
207
294
  const lines = [];
208
295
 
209
296
  lines.push('❌ Claude returned invalid response');
297
+ lines.push(...formatTimingInfo(errorInfo));
210
298
  lines.push('');
211
299
  lines.push('This usually means:');
212
300
  lines.push(' - Claude did not return valid JSON');
@@ -223,12 +311,14 @@ const formatInvalidResponseError = ({ exitCode }) => {
223
311
  /**
224
312
  * Format generic error
225
313
  */
226
- const formatGenericError = ({ exitCode, stdout, stderr }) => {
314
+ const formatGenericError = (errorInfo) => {
315
+ const { exitCode, stdout, stderr } = errorInfo;
227
316
  const lines = [];
228
317
 
229
318
  lines.push('❌ Claude CLI execution failed');
230
319
  lines.push('');
231
320
  lines.push(`Exit code: ${exitCode}`);
321
+ lines.push(...formatTimingInfo(errorInfo));
232
322
 
233
323
  if (stdout && stdout.trim()) {
234
324
  lines.push('');
@@ -261,6 +351,7 @@ const formatGenericError = ({ exitCode, stdout, stderr }) => {
261
351
  * @returns {boolean} True if error might resolve with retry
262
352
  */
263
353
  export const isRecoverableError = (errorInfo) => {
264
- return errorInfo.type === ClaudeErrorType.RATE_LIMIT ||
354
+ return errorInfo.type === ClaudeErrorType.EXECUTION_ERROR ||
355
+ errorInfo.type === ClaudeErrorType.RATE_LIMIT ||
265
356
  errorInfo.type === ClaudeErrorType.NETWORK;
266
357
  };
@@ -215,8 +215,8 @@ const formatFileSection = ({ path, diff, content, isNew }) => {
215
215
  * }
216
216
  */
217
217
  const buildAnalysisPrompt = async ({
218
- templateName = 'CLAUDE_ANALYSIS_PROMPT_SONAR.md',
219
- guidelinesName = 'CLAUDE_PRE_COMMIT_SONAR.md',
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,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-git-hooks",
3
- "version": "2.6.3",
3
+ "version": "2.8.0",
4
4
  "description": "Git hooks with Claude CLI for code analysis and automatic commit messages",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,4 +1,4 @@
1
- ROLE:SeniorCodeReviewer,SonarQube-experienced
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
- # SONARQUBE_EVAL_CRITERIA
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 (SonarQube-style)
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
- # SonarQube-Style Evaluation Criteria
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 in SonarQube format
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
  ```