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.
- package/CHANGELOG.md +84 -0
- package/README.md +51 -40
- package/bin/claude-hooks +147 -45
- 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/claude-client.js +40 -11
- 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 +41 -41
- package/templates/presets/ai/config.json +12 -12
- package/templates/presets/ai/preset.json +31 -36
- package/templates/presets/backend/ANALYSIS_PROMPT.md +23 -28
- package/templates/presets/backend/PRE_COMMIT_GUIDELINES.md +41 -3
- package/templates/presets/backend/config.json +12 -12
- package/templates/presets/database/config.json +12 -12
- package/templates/presets/default/config.json +12 -12
- package/templates/presets/frontend/config.json +12 -12
- package/templates/presets/fullstack/config.json +12 -12
|
@@ -67,22 +67,49 @@ const isWSLAvailable = () => {
|
|
|
67
67
|
|
|
68
68
|
/**
|
|
69
69
|
* Get Claude command configuration for current platform
|
|
70
|
-
* Why: On Windows,
|
|
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
|
|
73
|
+
* @throws {ClaudeClientError} If Claude not available on any method
|
|
74
74
|
*/
|
|
75
75
|
const getClaudeCommand = () => {
|
|
76
76
|
if (isWindows()) {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
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
|
-
{
|
|
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
|
-
{
|
|
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
|
-
|
|
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
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
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
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
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
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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
|
-
|
|
18
|
-
".js",
|
|
19
|
-
".json",
|
|
20
|
-
".md",
|
|
21
|
-
".sh"
|
|
22
|
-
],
|
|
17
|
+
"fileExtensions": [".js", ".json", ".md", ".sh"],
|
|
23
18
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
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
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
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
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
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
|
+
}
|