ai-devx 1.0.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.
Files changed (33) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +325 -0
  3. package/bin/cli.js +65 -0
  4. package/package.json +63 -0
  5. package/src/commands/init.js +86 -0
  6. package/src/commands/status.js +60 -0
  7. package/src/commands/update.js +77 -0
  8. package/src/config.js +72 -0
  9. package/src/utils/fileSystem.js +64 -0
  10. package/src/utils/logger.js +18 -0
  11. package/templates/.agent/.gitignore +6 -0
  12. package/templates/.agent/agents/backend-specialist.md +147 -0
  13. package/templates/.agent/agents/database-architect.md +164 -0
  14. package/templates/.agent/agents/debugger.md +128 -0
  15. package/templates/.agent/agents/devops-engineer.md +185 -0
  16. package/templates/.agent/agents/frontend-specialist.md +122 -0
  17. package/templates/.agent/agents/orchestrator.md +137 -0
  18. package/templates/.agent/agents/project-planner.md +127 -0
  19. package/templates/.agent/agents/security-auditor.md +122 -0
  20. package/templates/.agent/agents/test-engineer.md +176 -0
  21. package/templates/.agent/scripts/checklist.js +260 -0
  22. package/templates/.agent/scripts/security_scan.js +251 -0
  23. package/templates/.agent/skills/api-patterns/SKILL.md +236 -0
  24. package/templates/.agent/skills/database-design/SKILL.md +303 -0
  25. package/templates/.agent/skills/docker-expert/SKILL.md +286 -0
  26. package/templates/.agent/skills/react-best-practices/SKILL.md +246 -0
  27. package/templates/.agent/skills/testing-patterns/SKILL.md +262 -0
  28. package/templates/.agent/workflows/create.md +131 -0
  29. package/templates/.agent/workflows/debug.md +138 -0
  30. package/templates/.agent/workflows/deploy.md +163 -0
  31. package/templates/.agent/workflows/plan.md +153 -0
  32. package/templates/.agent/workflows/security.md +181 -0
  33. package/templates/.agent/workflows/test.md +165 -0
@@ -0,0 +1,176 @@
1
+ ---
2
+ name: test-engineer
3
+ description: Testing and QA expert for writing comprehensive tests and ensuring code quality
4
+ skills:
5
+ - testing-patterns
6
+ - webapp-testing
7
+ - tdd-workflow
8
+ - e2e-testing
9
+ mode: thorough
10
+ expertise:
11
+ - Unit Testing
12
+ - Integration Testing
13
+ - E2E Testing
14
+ - Test-Driven Development
15
+ - Test Automation
16
+ - Code Coverage
17
+ - Mocking & Stubbing
18
+ ---
19
+
20
+ # Test Engineer Agent
21
+
22
+ ## Role
23
+ You are a testing expert responsible for ensuring code quality through comprehensive testing strategies and automated test suites.
24
+
25
+ ## Testing Pyramid
26
+
27
+ ```
28
+ /\
29
+ / \ E2E Tests (Few)
30
+ /____\
31
+ / \ Integration Tests (Some)
32
+ /________\
33
+ Unit Tests (Many)
34
+ ```
35
+
36
+ ### Unit Tests (70%)
37
+ - Test individual functions/components
38
+ - Fast execution (< 100ms)
39
+ - No external dependencies (mocked)
40
+ - High code coverage target (80%+)
41
+
42
+ ### Integration Tests (20%)
43
+ - Test component interactions
44
+ - Test API endpoints
45
+ - Database interactions
46
+ - External service integrations
47
+
48
+ ### E2E Tests (10%)
49
+ - Test complete user flows
50
+ - Browser automation
51
+ - Critical paths only
52
+ - Slow but comprehensive
53
+
54
+ ## Testing Frameworks
55
+
56
+ ### JavaScript/TypeScript
57
+ - **Jest**: Unit and integration tests
58
+ - **Vitest**: Fast unit tests (Vite projects)
59
+ - **Playwright**: E2E testing
60
+ - **Cypress**: Alternative E2E
61
+ - **Testing Library**: Component testing
62
+
63
+ ### Python
64
+ - **pytest**: All test levels
65
+ - **unittest**: Built-in option
66
+
67
+ ### Go
68
+ - Built-in testing package
69
+ - Testify for assertions
70
+
71
+ ## Test Structure
72
+
73
+ ### AAA Pattern
74
+ ```typescript
75
+ // Arrange - Setup
76
+ const user = { name: 'John', age: 30 };
77
+
78
+ // Act - Execute
79
+ const result = calculateAgeGroup(user);
80
+
81
+ // Assert - Verify
82
+ expect(result).toBe('adult');
83
+ ```
84
+
85
+ ### Naming Conventions
86
+ ```typescript
87
+ // File: ComponentName.test.tsx
88
+ describe('ComponentName', () => {
89
+ describe('when user is logged in', () => {
90
+ it('should display user name', () => {
91
+ // test
92
+ });
93
+
94
+ it('should show logout button', () => {
95
+ // test
96
+ });
97
+ });
98
+ });
99
+ ```
100
+
101
+ ## Best Practices
102
+
103
+ ### Test Independence
104
+ - Each test should be isolated
105
+ - Clean up after tests
106
+ - Don't share state between tests
107
+ - Use `beforeEach` for setup
108
+
109
+ ### Assertions
110
+ - One assertion per test (ideally)
111
+ - Use descriptive messages
112
+ - Test behavior, not implementation
113
+ - Test edge cases
114
+
115
+ ### Mocking
116
+ ```typescript
117
+ // Mock external dependencies
118
+ jest.mock('./api', () => ({
119
+ fetchUser: jest.fn()
120
+ }));
121
+
122
+ // Mock implementation
123
+ (fetchUser as jest.Mock).mockResolvedValue({ id: 1, name: 'John' });
124
+ ```
125
+
126
+ ### Code Coverage
127
+ ```json
128
+ {
129
+ "coverageThreshold": {
130
+ "global": {
131
+ "branches": 80,
132
+ "functions": 80,
133
+ "lines": 80,
134
+ "statements": 80
135
+ }
136
+ }
137
+ }
138
+ ```
139
+
140
+ ## E2E Testing Best Practices
141
+
142
+ ### Critical Paths
143
+ - User registration/login
144
+ - Core business flows
145
+ - Payment processing
146
+ - Data persistence
147
+
148
+ ### Page Object Model
149
+ ```typescript
150
+ class LoginPage {
151
+ async login(email: string, password: string) {
152
+ await this.page.fill('[name="email"]', email);
153
+ await this.page.fill('[name="password"]', password);
154
+ await this.page.click('button[type="submit"]');
155
+ }
156
+ }
157
+ ```
158
+
159
+ ## TDD Workflow
160
+
161
+ 1. **Red**: Write failing test
162
+ 2. **Green**: Write minimal code to pass
163
+ 3. **Refactor**: Improve code quality
164
+
165
+ ## Response Format
166
+
167
+ When assisting with testing:
168
+
169
+ 1. **Identify testing needs** based on context
170
+ 2. **Choose appropriate framework**
171
+ 3. **Write tests following AAA pattern**
172
+ 4. **Suggest test coverage improvements**
173
+ 5. **Recommend testing strategies**
174
+ 6. **Help with mocking/stubbing**
175
+
176
+ Always announce: `šŸ¤– Applying @test-engineer...`
@@ -0,0 +1,260 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * AI-DEVX Quick Check Script
5
+ * Performs fast validation checks on the codebase
6
+ * Usage: node .agent/scripts/checklist.js [path]
7
+ */
8
+
9
+ const fs = require('fs');
10
+ const path = require('path');
11
+ const { execSync } = require('child_process');
12
+
13
+ const CHECKS = {
14
+ security: {
15
+ name: 'Security Scan',
16
+ run: checkSecurity
17
+ },
18
+ lint: {
19
+ name: 'Code Quality (Lint)',
20
+ run: checkLint
21
+ },
22
+ types: {
23
+ name: 'Type Checking',
24
+ run: checkTypes
25
+ },
26
+ tests: {
27
+ name: 'Test Suite',
28
+ run: checkTests
29
+ },
30
+ format: {
31
+ name: 'Format Check',
32
+ run: checkFormat
33
+ }
34
+ };
35
+
36
+ const GREEN = '\x1b[32māœ“\x1b[0m';
37
+ const RED = '\x1b[31māœ—\x1b[0m';
38
+ const YELLOW = '\x1b[33m⚠\x1b[0m';
39
+
40
+ function log(message) {
41
+ console.log(message);
42
+ }
43
+
44
+ // Security Checks
45
+ function checkSecurity(projectPath) {
46
+ const issues = [];
47
+
48
+ // Check for common secrets patterns
49
+ const secretPatterns = [
50
+ /['"]?(password|passwd|pwd)['"]?\s*[:=]\s*['"][^'"]+['"]/i,
51
+ /['"]?(api[_-]?key|apikey)['"]?\s*[:=]\s*['"][a-zA-Z0-9]{16,}['"]/i,
52
+ /['"]?(secret|token)['"]?\s*[:=]\s*['"][a-zA-Z0-9]{16,}['"]/i,
53
+ /['"]?aws[_-]?(access[_-]?key|secret)['"]?
54
+ ?\s*[:=]\s*['"][A-Za-z0-9/+=]{20,}['"]/i
55
+ ];
56
+
57
+ const files = getAllFiles(projectPath, ['.js', '.ts', '.jsx', '.tsx', '.json', '.env']);
58
+
59
+ for (const file of files) {
60
+ if (file.includes('node_modules') || file.includes('.git')) continue;
61
+
62
+ try {
63
+ const content = fs.readFileSync(file, 'utf-8');
64
+ for (const pattern of secretPatterns) {
65
+ if (pattern.test(content) && !content.includes('process.env')) {
66
+ issues.push(`Potential secret in ${path.relative(projectPath, file)}`);
67
+ break;
68
+ }
69
+ }
70
+ } catch (e) {
71
+ // Skip unreadable files
72
+ }
73
+ }
74
+
75
+ return {
76
+ ok: issues.length === 0,
77
+ message: issues.length === 0 ? 'No secrets detected' : `${issues.length} potential secrets found`,
78
+ details: issues.slice(0, 5)
79
+ };
80
+ }
81
+
82
+ // Lint Check
83
+ function checkLint(projectPath) {
84
+ try {
85
+ // Check if eslint config exists
86
+ const hasEslint = [
87
+ '.eslintrc.js',
88
+ '.eslintrc.json',
89
+ '.eslintrc',
90
+ 'eslint.config.js'
91
+ ].some(f => fs.existsSync(path.join(projectPath, f)));
92
+
93
+ if (!hasEslint) {
94
+ return { ok: true, message: 'No ESLint config found (skipped)' };
95
+ }
96
+
97
+ try {
98
+ execSync('npx eslint . --max-warnings=0', {
99
+ cwd: projectPath,
100
+ stdio: 'pipe'
101
+ });
102
+ return { ok: true, message: 'ESLint passed' };
103
+ } catch (e) {
104
+ return { ok: false, message: 'ESLint found issues' };
105
+ }
106
+ } catch (e) {
107
+ return { ok: true, message: 'ESLint not available' };
108
+ }
109
+ }
110
+
111
+ // Type Check
112
+ function checkTypes(projectPath) {
113
+ try {
114
+ const hasTsconfig = fs.existsSync(path.join(projectPath, 'tsconfig.json'));
115
+ if (!hasTsconfig) {
116
+ return { ok: true, message: 'No TypeScript config (skipped)' };
117
+ }
118
+
119
+ try {
120
+ execSync('npx tsc --noEmit', {
121
+ cwd: projectPath,
122
+ stdio: 'pipe'
123
+ });
124
+ return { ok: true, message: 'TypeScript check passed' };
125
+ } catch (e) {
126
+ return { ok: false, message: 'TypeScript errors found' };
127
+ }
128
+ } catch (e) {
129
+ return { ok: true, message: 'TypeScript not available' };
130
+ }
131
+ }
132
+
133
+ // Test Check
134
+ function checkTests(projectPath) {
135
+ try {
136
+ const hasTests = [
137
+ 'jest.config.js',
138
+ 'jest.config.ts',
139
+ 'vitest.config.js',
140
+ 'vitest.config.ts'
141
+ ].some(f => fs.existsSync(path.join(projectPath, f)));
142
+
143
+ if (!hasTests) {
144
+ return { ok: true, message: 'No test config found (skipped)' };
145
+ }
146
+
147
+ try {
148
+ execSync('npm test -- --passWithNoTests', {
149
+ cwd: projectPath,
150
+ stdio: 'pipe'
151
+ });
152
+ return { ok: true, message: 'Tests passing' };
153
+ } catch (e) {
154
+ return { ok: false, message: 'Tests failing' };
155
+ }
156
+ } catch (e) {
157
+ return { ok: true, message: 'Tests not available' };
158
+ }
159
+ }
160
+
161
+ // Format Check
162
+ function checkFormat(projectPath) {
163
+ try {
164
+ const hasPrettier = fs.existsSync(path.join(projectPath, '.prettierrc')) ||
165
+ fs.existsSync(path.join(projectPath, '.prettierrc.json')) ||
166
+ fs.existsSync(path.join(projectPath, 'prettier.config.js'));
167
+
168
+ if (!hasPrettier) {
169
+ return { ok: true, message: 'No Prettier config (skipped)' };
170
+ }
171
+
172
+ try {
173
+ execSync('npx prettier --check .', {
174
+ cwd: projectPath,
175
+ stdio: 'pipe'
176
+ });
177
+ return { ok: true, message: 'Code formatted correctly' };
178
+ } catch (e) {
179
+ return { ok: false, message: 'Code formatting issues' };
180
+ }
181
+ } catch (e) {
182
+ return { ok: true, message: 'Prettier not available' };
183
+ }
184
+ }
185
+
186
+ // Utility: Get all files recursively
187
+ function getAllFiles(dir, extensions) {
188
+ const files = [];
189
+
190
+ function traverse(currentDir) {
191
+ try {
192
+ const items = fs.readdirSync(currentDir);
193
+ for (const item of items) {
194
+ const fullPath = path.join(currentDir, item);
195
+ const stat = fs.statSync(fullPath);
196
+
197
+ if (stat.isDirectory()) {
198
+ if (!['node_modules', '.git', 'dist', 'build'].includes(item)) {
199
+ traverse(fullPath);
200
+ }
201
+ } else if (extensions.some(ext => item.endsWith(ext))) {
202
+ files.push(fullPath);
203
+ }
204
+ }
205
+ } catch (e) {
206
+ // Skip inaccessible directories
207
+ }
208
+ }
209
+
210
+ traverse(dir);
211
+ return files;
212
+ }
213
+
214
+ // Main execution
215
+ async function main() {
216
+ const projectPath = process.argv[2] || process.cwd();
217
+
218
+ log('\nšŸ” AI-DEVX Quick Check\n');
219
+ log('=' .repeat(50));
220
+
221
+ let passed = 0;
222
+ let failed = 0;
223
+
224
+ for (const [key, check] of Object.entries(CHECKS)) {
225
+ process.stdout.write(`${check.name}... `);
226
+
227
+ try {
228
+ const result = check.run(projectPath);
229
+
230
+ if (result.ok) {
231
+ log(`${GREEN} ${result.message}`);
232
+ passed++;
233
+ } else {
234
+ log(`${RED} ${result.message}`);
235
+ failed++;
236
+ if (result.details) {
237
+ result.details.forEach(d => log(` ${YELLOW} ${d}`));
238
+ }
239
+ }
240
+ } catch (e) {
241
+ log(`${YELLOW} Check failed to run`);
242
+ }
243
+ }
244
+
245
+ log('=' .repeat(50));
246
+
247
+ if (failed === 0) {
248
+ log(`\n${GREEN} All checks passed!`);
249
+ } else {
250
+ log(`\n${GREEN} ${passed} passed, ${RED} ${failed} failed`);
251
+ process.exit(1);
252
+ }
253
+
254
+ log('\nā±ļø Quick check complete (~30 seconds)\n');
255
+ }
256
+
257
+ main().catch(e => {
258
+ console.error('Error running checks:', e.message);
259
+ process.exit(1);
260
+ });
@@ -0,0 +1,251 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * AI-DEVX Security Scanner
5
+ * Scans codebase for security vulnerabilities
6
+ * Usage: node .agent/scripts/security_scan.js [path]
7
+ */
8
+
9
+ const fs = require('fs');
10
+ const path = require('path');
11
+
12
+ const SEVERITY = {
13
+ CRITICAL: { label: 'šŸ”“ CRITICAL', score: 4 },
14
+ HIGH: { label: '🟠 HIGH', score: 3 },
15
+ MEDIUM: { label: '🟔 MEDIUM', score: 2 },
16
+ LOW: { label: '🟢 LOW', score: 1 },
17
+ INFO: { label: 'ℹ INFO', score: 0 }
18
+ };
19
+
20
+ const SECURITY_PATTERNS = [
21
+ {
22
+ name: 'Hardcoded Secret/Password',
23
+ pattern: /['"]?(password|passwd|pwd|secret)['"]?\s*[:=]\s*['"][^'"]{8,}['"]/i,
24
+ severity: 'CRITICAL',
25
+ check: (content) => !content.includes('process.env') && !content.includes('import.meta.env')
26
+ },
27
+ {
28
+ name: 'Hardcoded API Key',
29
+ pattern: /['"]?(api[_-]?key|apikey)['"]?\s*[:=]\s*['"][a-zA-Z0-9]{16,}['"]/i,
30
+ severity: 'CRITICAL',
31
+ check: (content) => !content.includes('process.env')
32
+ },
33
+ {
34
+ name: 'Hardcoded Token',
35
+ pattern: /['"]?(token|access[_-]?token|auth[_-]?token)['"]?\s*[:=]\s*['"][a-zA-Z0-9-_]{20,}['"]/i,
36
+ severity: 'CRITICAL',
37
+ check: (content) => !content.includes('process.env')
38
+ },
39
+ {
40
+ name: 'AWS Access Key ID',
41
+ pattern: /['"]?AKIA[0-9A-Z]{16}['"]?/,
42
+ severity: 'CRITICAL',
43
+ check: () => true
44
+ },
45
+ {
46
+ name: 'Private Key',
47
+ pattern: /-----BEGIN (RSA |DSA |EC |OPENSSH )?PRIVATE KEY-----/,
48
+ severity: 'CRITICAL',
49
+ check: () => true
50
+ },
51
+ {
52
+ name: 'SQL Injection Risk',
53
+ pattern: /(SELECT|INSERT|UPDATE|DELETE|DROP).*\$\{/,
54
+ severity: 'HIGH',
55
+ check: () => true
56
+ },
57
+ {
58
+ name: 'eval() Usage',
59
+ pattern: /\beval\s*\(/,
60
+ severity: 'HIGH',
61
+ check: () => true
62
+ },
63
+ {
64
+ name: 'innerHTML Assignment',
65
+ pattern: /\.innerHTML\s*=/,
66
+ severity: 'MEDIUM',
67
+ check: (content, match) => !content.includes('DOMPurify') && !content.includes('sanitize')
68
+ },
69
+ {
70
+ name: 'Debug Mode Enabled',
71
+ pattern: /DEBUG\s*[:=]\s*true/i,
72
+ severity: 'MEDIUM',
73
+ check: (content) => content.includes('.env') || content.includes('production')
74
+ },
75
+ {
76
+ name: 'Insecure HTTP',
77
+ pattern: /http:\/\/(?!localhost|127\.0\.0\.1)/,
78
+ severity: 'MEDIUM',
79
+ check: () => true
80
+ }
81
+ ];
82
+
83
+ const VULNERABLE_DEPENDENCIES = [
84
+ { name: 'lodash', vulnerable: '<4.17.21', severity: 'HIGH' },
85
+ { name: 'express', vulnerable: '<4.18.2', severity: 'MEDIUM' },
86
+ { name: 'axios', vulnerable: '<0.28.0', severity: 'MEDIUM' },
87
+ { name: 'minimist', vulnerable: '<1.2.6', severity: 'HIGH' }
88
+ ];
89
+
90
+ function log(message) {
91
+ console.log(message);
92
+ }
93
+
94
+ function getAllFiles(dir, extensions) {
95
+ const files = [];
96
+
97
+ function traverse(currentDir) {
98
+ try {
99
+ const items = fs.readdirSync(currentDir);
100
+ for (const item of items) {
101
+ const fullPath = path.join(currentDir, item);
102
+ const stat = fs.statSync(fullPath);
103
+
104
+ if (stat.isDirectory()) {
105
+ if (!['node_modules', '.git', 'dist', 'build', '.agent'].includes(item)) {
106
+ traverse(fullPath);
107
+ }
108
+ } else {
109
+ files.push(fullPath);
110
+ }
111
+ }
112
+ } catch (e) {
113
+ // Skip inaccessible directories
114
+ }
115
+ }
116
+
117
+ traverse(dir);
118
+ return files;
119
+ }
120
+
121
+ function scanFile(filePath, content) {
122
+ const issues = [];
123
+
124
+ for (const pattern of SECURITY_PATTERNS) {
125
+ const matches = content.match(pattern.pattern);
126
+ if (matches && pattern.check(content, matches)) {
127
+ issues.push({
128
+ file: filePath,
129
+ pattern: pattern.name,
130
+ severity: pattern.severity,
131
+ line: content.substring(0, matches.index).split('\n').length
132
+ });
133
+ }
134
+ }
135
+
136
+ return issues;
137
+ }
138
+
139
+ function checkDependencies(projectPath) {
140
+ const issues = [];
141
+ const packageJsonPath = path.join(projectPath, 'package.json');
142
+
143
+ if (!fs.existsSync(packageJsonPath)) {
144
+ return issues;
145
+ }
146
+
147
+ try {
148
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
149
+ const deps = { ...packageJson.dependencies, ...packageJson.devDependencies };
150
+
151
+ for (const [dep, version] of Object.entries(deps)) {
152
+ const vulnerable = VULNERABLE_DEPENDENCIES.find(v =>
153
+ v.name === dep && version.match(/\d+\.\d+\.\d+/)?.[0] < v.vulnerable.replace('<', '')
154
+ );
155
+
156
+ if (vulnerable) {
157
+ issues.push({
158
+ file: 'package.json',
159
+ pattern: `Vulnerable dependency: ${dep}@${version}`,
160
+ severity: vulnerable.severity,
161
+ line: 0
162
+ });
163
+ }
164
+ }
165
+ } catch (e) {
166
+ // Skip if package.json can't be parsed
167
+ }
168
+
169
+ return issues;
170
+ }
171
+
172
+ function main() {
173
+ const projectPath = process.argv[2] || process.cwd();
174
+
175
+ log('\nšŸ” AI-DEVX Security Scanner\n');
176
+ log('=' .repeat(60));
177
+
178
+ const allIssues = [];
179
+
180
+ // Scan source files
181
+ log('\nšŸ“ Scanning source files...\n');
182
+ const files = getAllFiles(projectPath, []);
183
+
184
+ for (const file of files) {
185
+ if (file.includes('node_modules') || file.includes('.git')) continue;
186
+
187
+ try {
188
+ const content = fs.readFileSync(file, 'utf-8');
189
+ const issues = scanFile(path.relative(projectPath, file), content);
190
+ allIssues.push(...issues);
191
+ } catch (e) {
192
+ // Skip unreadable files
193
+ }
194
+ }
195
+
196
+ // Check dependencies
197
+ log('šŸ“¦ Checking dependencies...\n');
198
+ const depIssues = checkDependencies(projectPath);
199
+ allIssues.push(...depIssues);
200
+
201
+ // Sort by severity
202
+ const severityOrder = { CRITICAL: 4, HIGH: 3, MEDIUM: 2, LOW: 1, INFO: 0 };
203
+ allIssues.sort((a, b) => severityOrder[b.severity] - severityOrder[a.severity]);
204
+
205
+ // Group by severity
206
+ const grouped = allIssues.reduce((acc, issue) => {
207
+ acc[issue.severity] = acc[issue.severity] || [];
208
+ acc[issue.severity].push(issue);
209
+ return acc;
210
+ }, {});
211
+
212
+ // Display results
213
+ if (allIssues.length === 0) {
214
+ log('\nāœ… No security issues found!\n');
215
+ } else {
216
+ log(`\nāš ļø Found ${allIssues.length} security issue(s):\n`);
217
+
218
+ for (const severity of ['CRITICAL', 'HIGH', 'MEDIUM', 'LOW', 'INFO']) {
219
+ const issues = grouped[severity];
220
+ if (issues) {
221
+ log(`\n${SEVERITY[severity].label} (${issues.length}):\n`);
222
+ issues.forEach(issue => {
223
+ log(` šŸ“„ ${issue.file}:${issue.line}`);
224
+ log(` ${issue.pattern}\n`);
225
+ });
226
+ }
227
+ }
228
+ }
229
+
230
+ // Summary
231
+ log('=' .repeat(60));
232
+ const critical = grouped.CRITICAL?.length || 0;
233
+ const high = grouped.HIGH?.length || 0;
234
+ const medium = grouped.MEDIUM?.length || 0;
235
+ const low = grouped.LOW?.length || 0;
236
+
237
+ log(`\nSummary: šŸ”“ ${critical} Critical | 🟠 ${high} High | 🟔 ${medium} Medium | 🟢 ${low} Low`);
238
+
239
+ if (critical > 0 || high > 0) {
240
+ log('\nāš ļø Critical or High severity issues found! Fix immediately.');
241
+ process.exit(1);
242
+ } else if (medium > 0) {
243
+ log('\nāš ļø Medium severity issues found. Fix recommended.');
244
+ process.exit(0);
245
+ } else {
246
+ log('\nāœ… Security scan complete.');
247
+ process.exit(0);
248
+ }
249
+ }
250
+
251
+ main();