claude-git-hooks 2.3.1 → 2.4.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.
@@ -67,22 +67,49 @@ const isWSLAvailable = () => {
67
67
 
68
68
  /**
69
69
  * Get Claude command configuration for current platform
70
- * Why: On Windows, Claude CLI runs in WSL, so we need 'wsl' as command and 'claude' as arg
70
+ * Why: On Windows, try native Claude first, then WSL as fallback
71
71
  *
72
72
  * @returns {Object} { command, args } - Command and base arguments
73
- * @throws {ClaudeClientError} If Windows without WSL
73
+ * @throws {ClaudeClientError} If Claude not available on any method
74
74
  */
75
75
  const getClaudeCommand = () => {
76
76
  if (isWindows()) {
77
- if (!isWSLAvailable()) {
78
- throw new ClaudeClientError('WSL is required on Windows but not found', {
79
- context: {
80
- platform: 'Windows',
81
- suggestion: 'Install WSL: https://docs.microsoft.com/en-us/windows/wsl/install'
82
- }
83
- });
77
+ // Try native Windows Claude first (e.g., installed via npm/scoop/choco)
78
+ try {
79
+ execSync('claude --version', { stdio: 'ignore', timeout: 3000 });
80
+ logger.debug('claude-client - getClaudeCommand', 'Using native Windows Claude CLI');
81
+ return { command: 'claude', args: [] };
82
+ } catch (nativeError) {
83
+ logger.debug('claude-client - getClaudeCommand', 'Native Claude not found, trying WSL');
84
+
85
+ // Fallback to WSL
86
+ if (!isWSLAvailable()) {
87
+ throw new ClaudeClientError('Claude CLI not found. Install Claude CLI natively on Windows or via WSL', {
88
+ context: {
89
+ platform: 'Windows',
90
+ suggestions: [
91
+ 'Native Windows: npm install -g @anthropic-ai/claude-cli',
92
+ 'WSL: wsl --install, then install Claude in WSL'
93
+ ]
94
+ }
95
+ });
96
+ }
97
+
98
+ // Check if Claude is available in WSL
99
+ try {
100
+ execSync('wsl claude --version', { stdio: 'ignore', timeout: 5000 });
101
+ logger.debug('claude-client - getClaudeCommand', 'Using WSL Claude CLI');
102
+ return { command: 'wsl', args: ['claude'] };
103
+ } catch (wslError) {
104
+ throw new ClaudeClientError('Claude CLI not found in Windows or WSL', {
105
+ context: {
106
+ platform: 'Windows',
107
+ nativeError: nativeError.message,
108
+ wslError: wslError.message
109
+ }
110
+ });
111
+ }
84
112
  }
85
- return { command: 'wsl', args: ['claude'] };
86
113
  }
87
114
  return { command: 'claude', args: [] };
88
115
  };
@@ -114,8 +141,10 @@ const executeClaude = (prompt, { timeout = 120000 } = {}) => {
114
141
 
115
142
  // Why: Use spawn instead of exec to handle large prompts and responses
116
143
  // spawn streams data, exec buffers everything in memory
144
+ // shell: true needed on Windows to resolve .cmd/.bat executables
117
145
  const claude = spawn(command, args, {
118
- stdio: ['pipe', 'pipe', 'pipe'] // stdin, stdout, stderr
146
+ stdio: ['pipe', 'pipe', 'pipe'], // stdin, stdout, stderr
147
+ shell: true // Required for Windows to find .cmd/.bat files
119
148
  });
120
149
 
121
150
  let stdout = '';
@@ -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
- { fileCount: files.length, maxSize, extensions }
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 validCount = fileMetadata.filter(f => f.valid).length;
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: validCount,
323
- invalidFiles: fileMetadata.length - validCount
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
 
@@ -16,7 +16,7 @@
16
16
 
17
17
  class Logger {
18
18
  constructor({ debugMode = false } = {}) {
19
- this.debugMode = debugMode || process.env.DEBUG === 'true';
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({ debugMode: process.env.DEBUG === 'true' });
141
+ export default new Logger();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-git-hooks",
3
- "version": "2.3.1",
3
+ "version": "2.4.1",
4
4
  "description": "Git hooks with Claude CLI for code analysis and automatic commit messages",
5
5
  "type": "module",
6
6
  "bin": {
@@ -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: 100KB
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": 150000,
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: 100000, // 100KB
310
- maxFiles: 10,
311
- timeout: 120000 // 2 minutes
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
- {
2
- "preset": null,
3
- "analysis": {
4
- "maxFileSize": 100000,
5
- "maxFiles": 10,
6
- "timeout": 120000,
7
- "contextLines": 3,
8
- "ignoreExtensions": []
9
- },
10
- "commitMessage": {
11
- "autoKeyword": "auto",
12
- "timeout": 180000
13
- },
14
- "subagents": {
15
- "enabled": true,
16
- "model": "haiku",
17
- "batchSize": 3
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
- }
1
+ {
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
+ }
@@ -1,12 +1,12 @@
1
- {
2
- "analysis": {
3
- "maxFileSize": 100000,
4
- "maxFiles": 10,
5
- "timeout": 120000
6
- },
7
- "subagents": {
8
- "enabled": false,
9
- "model": "sonnet",
10
- "batchSize": 3
11
- }
12
- }
1
+ {
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
+ }
@@ -1,42 +1,37 @@
1
1
  {
2
- "name": "ai",
3
- "displayName": "AI/CLI (Node.js + Claude)",
4
- "description": "Node.js CLI tools with Claude API integration",
5
- "version": "1.0.0",
2
+ "name": "ai",
3
+ "displayName": "AI/CLI (Node.js + Claude)",
4
+ "description": "Node.js CLI tools with Claude API integration",
5
+ "version": "1.0.0",
6
6
 
7
- "techStack": [
8
- "Node.js",
9
- "ES Modules",
10
- "Claude API",
11
- "CLI tools",
12
- "Git hooks",
13
- "Bash scripting",
14
- "Markdown templates"
15
- ],
7
+ "techStack": [
8
+ "Node.js",
9
+ "ES Modules",
10
+ "Claude API",
11
+ "CLI tools",
12
+ "Git hooks",
13
+ "Bash scripting",
14
+ "Markdown templates"
15
+ ],
16
16
 
17
- "fileExtensions": [
18
- ".js",
19
- ".json",
20
- ".md",
21
- ".sh"
22
- ],
17
+ "fileExtensions": [".js", ".json", ".md", ".sh"],
23
18
 
24
- "focusAreas": [
25
- "Claude API usage and best practices",
26
- "Prompt engineering quality",
27
- "CLI user experience",
28
- "Error handling and logging",
29
- "Git operations safety",
30
- "Cross-platform compatibility",
31
- "Token usage optimization",
32
- "Security (API keys, secrets)"
33
- ],
19
+ "focusAreas": [
20
+ "Claude API usage and best practices",
21
+ "Prompt engineering quality",
22
+ "CLI user experience",
23
+ "Error handling and logging",
24
+ "Git operations safety",
25
+ "Cross-platform compatibility",
26
+ "Token usage optimization",
27
+ "Security (API keys, secrets)"
28
+ ],
34
29
 
35
- "templates": {
36
- "analysis": "ANALYSIS_PROMPT.md",
37
- "guidelines": "PRE_COMMIT_GUIDELINES.md",
38
- "commitMessage": "../shared/COMMIT_MESSAGE.md",
39
- "analyzeDiff": "../shared/ANALYZE_DIFF.md",
40
- "resolution": "../shared/RESOLUTION_PROMPT.md"
41
- }
30
+ "templates": {
31
+ "analysis": "ANALYSIS_PROMPT.md",
32
+ "guidelines": "PRE_COMMIT_GUIDELINES.md",
33
+ "commitMessage": "../shared/COMMIT_MESSAGE.md",
34
+ "analyzeDiff": "../shared/ANALYZE_DIFF.md",
35
+ "resolution": "../shared/RESOLUTION_PROMPT.md"
36
+ }
42
37
  }
@@ -13,32 +13,35 @@ Perform a comprehensive code quality analysis focusing on these areas:
13
13
  ## Analysis Guidelines
14
14
 
15
15
  1. **Security First**: Check for OWASP Top 10 vulnerabilities, especially:
16
- - SQL injection risks
17
- - Authentication/authorization flaws
18
- - Sensitive data exposure
19
- - XML external entities (XXE)
20
- - Insecure deserialization
16
+ - SQL injection risks
17
+ - Authentication/authorization flaws
18
+ - Sensitive data exposure
19
+ - XML external entities (XXE)
20
+ - Insecure deserialization
21
21
 
22
22
  2. **Spring Boot Best Practices**:
23
- - Proper use of `@Transactional`
24
- - Correct exception handling
25
- - Appropriate use of DTOs vs Entities
26
- - Proper dependency injection
27
- - Configuration management
23
+ - Proper use of `@Transactional`
24
+ - Correct exception handling
25
+ - Appropriate use of DTOs vs Entities
26
+ - Proper dependency injection // Si intelligence -> recomendar @RequiredArgsConstructor || Si Automation -> recomendar @Autowired
27
+ - Configuration management
28
28
 
29
29
  3. **JPA/Hibernate**:
30
- - N+1 query problems
31
- - Lazy loading issues
32
- - Proper use of relationships
33
- - Query optimization
34
- - Transaction boundaries
30
+ - N+1 query problems
31
+ - Lazy loading issues
32
+ - Proper use of relationships
33
+ - Query optimization
34
+ - Transaction boundaries
35
35
 
36
36
  4. **Code Quality**:
37
- - SOLID principles
38
- - DRY violations
39
- - Proper error handling
40
- - Logging best practices
41
- - Test coverage
37
+ - SOLID principles
38
+ - DRY violations
39
+ - Proper error handling
40
+ - Logging should be like so
41
+ - debug: regular flow, operations details
42
+ - info: important business events
43
+ - warn: anomalies and manageable errors
44
+ - error: exceptions and errors
42
45
 
43
46
  ## Output Format
44
47
 
@@ -48,14 +51,6 @@ Respond with a valid JSON following the SonarQube format:
48
51
  {
49
52
  "QUALITY_GATE": "PASSED|FAILED",
50
53
  "approved": true|false,
51
- "metrics": {
52
- "reliability": "A|B|C|D|E",
53
- "security": "A|B|C|D|E",
54
- "maintainability": "A|B|C|D|E",
55
- "coverage": 0-100,
56
- "duplications": 0-100,
57
- "complexity": "number"
58
- },
59
54
  "issues": {
60
55
  "blocker": 0,
61
56
  "critical": 0,
@@ -3,45 +3,63 @@
3
3
  ## Spring Boot Standards
4
4
 
5
5
  ### Controllers
6
- - Use proper HTTP methods and status codes
6
+
7
+ - Use proper HTTP methods
8
+ - Endpoints should handle entities as substantives
9
+ - Make sure Response Codes include: [200, 201, 204, 400, 401, 403, 404, 409, 422, 500, 503] (if non-compliant classify as BLOCKER)
7
10
  - Validate input with `@Valid`
8
11
  - Handle exceptions with `@ExceptionHandler`
9
12
  - Keep controllers thin - business logic in services
10
13
  - Use DTOs for API contracts
11
14
 
12
15
  ### Services
16
+
13
17
  - Use `@Transactional` appropriately
14
18
  - Handle exceptions properly
15
19
  - Keep methods focused and small
16
20
  - Avoid business logic in controllers or repositories
17
21
 
18
22
  ### Repositories
23
+
19
24
  - Extend appropriate Spring Data interfaces
20
25
  - Use method naming conventions for queries
21
26
  - Optimize queries with `@Query` when needed
22
27
  - Avoid N+1 problems with `@EntityGraph`
23
28
 
24
29
  ### Entities
30
+
25
31
  - Use Lombok annotations appropriately
26
32
  - Define proper relationships (`@OneToMany`, `@ManyToOne`, etc.)
27
33
  - Use `@Version` for optimistic locking
28
34
  - Never expose entities in API - use DTOs
29
35
 
36
+ ### Mappers
37
+
38
+ - Use Mapstruct for all mapping (If non-compliant, classify as MINOR)
39
+ - Mappers should not have logic (If non-compliant, classify as MAJOR)
40
+
41
+ ### Logging
42
+
43
+ - Recommend @Slf4j annotation in Lombok (If non-compliant, classify as MINOR)
44
+
30
45
  ## Security Requirements
31
46
 
32
47
  ### Authentication & Authorization
48
+
33
49
  - Never hardcode credentials
34
50
  - Use Spring Security properly
35
51
  - Validate JWT tokens correctly
36
52
  - Check permissions before operations
37
53
 
38
54
  ### Data Validation
55
+
39
56
  - Validate all user input
40
57
  - Use parameterized queries (JPA does this by default)
41
58
  - Sanitize data before logging
42
59
  - Never trust client-side validation alone
43
60
 
44
61
  ### SQL Injection Prevention
62
+
45
63
  - Always use JPA/JPQL or prepared statements
46
64
  - Never concatenate SQL strings
47
65
  - Be careful with native queries
@@ -50,35 +68,55 @@
50
68
  ## Performance
51
69
 
52
70
  ### Database
71
+
53
72
  - Use pagination for large result sets
54
73
  - Optimize queries with proper indexes
55
74
  - Avoid loading unnecessary data
56
75
  - Use projections when you don't need full entities
57
76
 
58
77
  ### Threading
78
+
59
79
  - Be careful with `@Async` methods
60
80
  - Use proper thread pool configuration
61
81
  - Avoid blocking operations in async methods
62
82
  - Handle exceptions in async methods
63
83
 
64
84
  ### Caching
85
+
65
86
  - Use `@Cacheable` appropriately
66
87
  - Clear caches when data changes
67
88
  - Don't cache sensitive data without encryption
68
89
 
69
90
  ## Testing
70
91
 
71
- - Write unit tests for business logic
92
+ ### Unit Tests
93
+
94
+ - Unit tests should use exclusively JUnit 5, and specific annotations @SpringBootTest, @MockBean, @Test
72
95
  - Use `@DataJpaTest` for repository tests
73
96
  - Use `@WebMvcTest` for controller tests
74
97
  - Mock external dependencies
75
98
  - Aim for 80%+ coverage on new code
76
99
 
100
+ ### Integration Tests
101
+
102
+ - Use `@SpringBootTest` with real application context
103
+ - Test complete request-response flows
104
+ - Verify database transactions and rollbacks
105
+ - Test API endpoint integration with all layers
106
+ - Use `@Transactional` with `@Rollback` for test data cleanup
107
+
108
+ ### Security Tests
109
+
110
+ - Test authentication and authorization scenarios
111
+ - Verify access control for protected endpoints
112
+ - Test with invalid/expired tokens
113
+ - Validate input sanitization and XSS prevention
114
+ - Test SQL injection prevention with malicious input
115
+
77
116
  ## Common Issues to Avoid
78
117
 
79
118
  ❌ Returning entities from controllers
80
119
  ❌ Missing `@Transactional` on write operations
81
- ❌ N+1 query problems
82
120
  ❌ Hardcoded secrets or credentials
83
121
  ❌ Catching and ignoring exceptions
84
122
  ❌ Missing input validation
@@ -1,12 +1,12 @@
1
- {
2
- "analysis": {
3
- "maxFileSize": 100000,
4
- "maxFiles": 10,
5
- "timeout": 120000
6
- },
7
- "subagents": {
8
- "enabled": true,
9
- "model": "sonnet",
10
- "batchSize": 3
11
- }
12
- }
1
+ {
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
+ }
@@ -1,12 +1,12 @@
1
- {
2
- "analysis": {
3
- "maxFileSize": 150000,
4
- "maxFiles": 8,
5
- "timeout": 120000
6
- },
7
- "subagents": {
8
- "enabled": false,
9
- "model": "sonnet",
10
- "batchSize": 2
11
- }
12
- }
1
+ {
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
+ }