claude-flow-novice 2.18.25 → 2.18.27

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.
@@ -1,20 +1,60 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
3
  * Enhanced Post-Edit Pipeline - Comprehensive Validation Hook
4
- * Validates edited files with TypeScript, ESLint, Prettier, Security Analysis, and Code Metrics
4
+ * Validates edited files with TypeScript, Rust, ESLint, Prettier, Security Analysis, and Code Metrics
5
5
  *
6
6
  * Features:
7
7
  * - TypeScript validation with error categorization
8
+ * - Rust validation via cargo check
8
9
  * - ESLint integration for code quality
9
10
  * - Prettier formatting checks
10
- * - Security analysis (integrated security scanner)
11
+ * - Security analysis (eval, password logging, XSS detection)
11
12
  * - Code metrics (lines, functions, classes, complexity)
12
13
  * - Actionable recommendations engine
13
14
  *
14
15
  * Usage: node config/hooks/post-edit-pipeline.js <file_path> [--memory-key <key>] [--agent-id <id>]
16
+ *
17
+ * ============================================================================
18
+ * ERROR DETECTION - WHAT'S CAUGHT vs NOT CAUGHT
19
+ * ============================================================================
20
+ *
21
+ * TYPESCRIPT: Runs `npx tsc --noEmit --skipLibCheck ${filePath}`
22
+ * RUST: Runs `cargo check` in the crate directory
23
+ *
24
+ * Both check the EDITED FILE + its IMPORTS (transitively).
25
+ * Rust's cargo check also catches cross-file breaks (better than TS).
26
+ *
27
+ * ✅ CAUGHT (Blocks Edit):
28
+ * ┌────────────────────┬──────────────────┬─────────────────────────────────────┐
29
+ * │ Error Type │ TS / Rust Code │ Example │
30
+ * ├────────────────────┼──────────────────┼─────────────────────────────────────┤
31
+ * │ Missing module │ TS2307 / E0432 │ import from './nonexistent' │
32
+ * │ Missing export │ TS2305 / E0433 │ use crate::nonexistent │
33
+ * │ Syntax errors │ TS1005 / E0xxx │ Missing ), unexpected token │
34
+ * │ Unused imports │ TS6133 / warn │ unused import │
35
+ * │ Type mismatches │ TS2322 / E0308 │ Wrong type assignment │
36
+ * │ Missing properties │ TS2339 / E0609 │ field does not exist │
37
+ * │ Borrow errors │ N/A / E0502 │ cannot borrow as mutable │
38
+ * │ Lifetime errors │ N/A / E0106 │ missing lifetime specifier │
39
+ * └────────────────────┴──────────────────┴─────────────────────────────────────┘
40
+ *
41
+ * ❌ NOT CAUGHT (TypeScript Single-File Limitation):
42
+ * ┌─────────────────────────────────┬────────────────────────────────────────────┐
43
+ * │ Scenario │ Why │
44
+ * ├─────────────────────────────────┼────────────────────────────────────────────┤
45
+ * │ Cross-file type changes (TS) │ Editing types.ts won't catch breaks in │
46
+ * │ │ other.ts that imports it │
47
+ * │ Deleted exports used elsewhere │ Consumers aren't checked until edited │
48
+ * └─────────────────────────────────┴────────────────────────────────────────────┘
49
+ *
50
+ * Note: Rust DOES catch cross-file breaks because cargo check compiles the
51
+ * entire crate. TypeScript only checks the single file + its imports.
52
+ *
53
+ * To catch everything in TS: Run `npx tsc --noEmit` project-wide periodically.
54
+ * ============================================================================
15
55
  */
16
56
 
17
- import { spawnSync } from 'child_process';
57
+ import { execSync } from 'child_process';
18
58
  import { existsSync, readFileSync, appendFileSync, mkdirSync } from 'fs';
19
59
  import { dirname, extname, resolve } from 'path';
20
60
 
@@ -63,11 +103,11 @@ if (!existsSync(filePath)) {
63
103
  // Read file content for analysis
64
104
  const fileContent = readFileSync(filePath, 'utf-8');
65
105
  const ext = extname(filePath);
66
- const baseName = filePath.replace(ext, '').split('/').pop();
67
106
 
68
107
  // Initialize results object
69
108
  const results = {
70
109
  typescript: null,
110
+ rust: null,
71
111
  eslint: null,
72
112
  prettier: null,
73
113
  security: null,
@@ -75,784 +115,1283 @@ const results = {
75
115
  recommendations: []
76
116
  };
77
117
 
78
- // [Remaining TypeScript, ESLint, and Prettier validation code remains the same]
79
-
80
118
  // ============================================================================
81
- // PHASE 2: Security Analysis
119
+ // PHASE 1: TypeScript Validation
82
120
  // ============================================================================
83
121
 
84
- log('VALIDATING', 'Running security analysis');
122
+ if (['.ts', '.tsx'].includes(ext)) {
123
+ try {
124
+ log('VALIDATING', 'Running TypeScript validation');
85
125
 
86
- try {
87
- // Primary scanner method: security scanner script
88
- const securityScanProcess = spawnSync('bash', [
89
- '.claude/skills/hook-pipeline/security-scanner.sh',
90
- filePath
91
- ], {
92
- encoding: 'utf-8',
93
- timeout: 10000
94
- });
126
+ // Use tsc to check only this file
127
+ const cmd = `npx tsc --noEmit --skipLibCheck ${filePath}`;
128
+ execSync(cmd, { stdio: 'pipe', encoding: 'utf-8' });
95
129
 
96
- const securityScanOutput = securityScanProcess.stdout || '{}';
97
- const exitCode = securityScanProcess.status;
130
+ results.typescript = {
131
+ passed: true,
132
+ errors: []
133
+ };
134
+ log('SUCCESS', 'TypeScript validation passed');
98
135
 
99
- log('DEBUG', 'Security scanner output', {
100
- stdout: securityScanOutput,
101
- stderr: securityScanProcess.stderr,
102
- exitCode: exitCode
103
- });
136
+ } catch (error) {
137
+ // Parse TypeScript errors
138
+ const output = error.stdout || error.stderr || '';
139
+ const lines = output.split('\n').filter(line => line.includes('error TS'));
140
+
141
+ if (lines.length === 0) {
142
+ results.typescript = {
143
+ passed: true,
144
+ errors: []
145
+ };
146
+ log('SUCCESS', 'No TypeScript errors detected');
147
+ } else {
148
+ // Categorize errors
149
+ const errorTypes = {
150
+ implicitAny: lines.filter(l => l.includes('TS7006') || l.includes('TS7031')).length,
151
+ propertyMissing: lines.filter(l => l.includes('TS2339')).length,
152
+ typeMismatch: lines.filter(l => l.includes('TS2322') || l.includes('TS2345')).length,
153
+ syntaxError: lines.filter(l => l.includes('TS1005') || l.includes('TS1128')).length,
154
+ other: 0
155
+ };
156
+ errorTypes.other = lines.length - Object.values(errorTypes).reduce((a, b) => a + b, 0);
104
157
 
105
- try {
106
- const securityScanResults = JSON.parse(securityScanOutput);
107
-
108
- results.security = {
109
- passed: securityScanResults.passed,
110
- confidence: securityScanResults.confidence || 0,
111
- issues: Array.isArray(securityScanResults.vulnerabilities)
112
- ? securityScanResults.vulnerabilities
113
- : JSON.parse(securityScanResults.vulnerabilities || '[]'),
114
- details: securityScanOutput
115
- };
158
+ const severity = errorTypes.syntaxError > 0 ? 'SYNTAX_ERROR' :
159
+ lines.length > 5 ? 'LINT_ISSUES' : 'TYPE_WARNING';
116
160
 
117
- if (results.security.issues.length > 0) {
118
- log('SECURITY_WARNING', `Security scanner detected ${results.security.issues.length} vulnerabilities`, {
119
- confidence: results.security.confidence,
120
- issueTypes: results.security.issues
161
+ results.typescript = {
162
+ passed: false,
163
+ errorCount: lines.length,
164
+ errorTypes,
165
+ errors: lines.slice(0, 5),
166
+ severity
167
+ };
168
+
169
+ log(severity, `TypeScript errors detected: ${lines.length}`, {
170
+ errorCount: lines.length,
171
+ errorTypes,
172
+ errors: lines.slice(0, 5)
121
173
  });
122
174
 
123
- // Transform scanner issues into recommendations
124
- results.security.issues.slice(0, 3).forEach(vuln => {
175
+ // Add recommendations based on error types
176
+ if (errorTypes.syntaxError > 0) {
125
177
  results.recommendations.push({
126
- type: 'security',
178
+ type: 'typescript',
127
179
  priority: 'critical',
128
- message: `Security vulnerability: ${vuln}`,
129
- action: `Review and remediate ${vuln} vulnerability`
180
+ message: 'Fix syntax errors before proceeding',
181
+ action: 'Review and fix TypeScript syntax issues'
130
182
  });
131
- });
183
+ } else if (errorTypes.implicitAny > 0) {
184
+ results.recommendations.push({
185
+ type: 'typescript',
186
+ priority: 'high',
187
+ message: 'Add explicit type annotations',
188
+ action: 'Add type annotations for parameters and return values'
189
+ });
190
+ }
191
+ }
192
+ }
193
+ } else {
194
+ log('SKIPPED', 'TypeScript validation skipped for non-TypeScript file');
195
+ }
132
196
 
133
- // Add general security warning
134
- results.recommendations.push({
135
- type: 'security',
136
- priority: 'critical',
137
- message: 'Security vulnerabilities detected by security scanner',
138
- action: 'Conduct thorough security review and address all vulnerabilities'
139
- });
197
+ // ============================================================================
198
+ // PHASE 1.5: Rust Validation (via cargo check)
199
+ // ============================================================================
200
+
201
+ if (ext === '.rs') {
202
+ try {
203
+ log('VALIDATING', 'Running Rust validation via cargo check');
204
+
205
+ // Find the Cargo.toml directory (walk up from file)
206
+ let cargoDir = dirname(resolve(filePath));
207
+ let foundCargo = false;
208
+ for (let i = 0; i < 10; i++) {
209
+ if (existsSync(`${cargoDir}/Cargo.toml`)) {
210
+ foundCargo = true;
211
+ break;
212
+ }
213
+ const parent = dirname(cargoDir);
214
+ if (parent === cargoDir) break;
215
+ cargoDir = parent;
216
+ }
217
+
218
+ if (!foundCargo) {
219
+ log('SKIPPED', 'No Cargo.toml found, skipping Rust validation');
220
+ results.rust = { passed: true, skipped: true, reason: 'No Cargo.toml found' };
140
221
  } else {
141
- log('SUCCESS', 'No security vulnerabilities detected');
222
+ // Run cargo check in the crate directory
223
+ const cmd = `cd "${cargoDir}" && cargo check --message-format=short 2>&1`;
224
+ execSync(cmd, { stdio: 'pipe', encoding: 'utf-8', timeout: 60000 });
225
+
226
+ results.rust = {
227
+ passed: true,
228
+ errors: []
229
+ };
230
+ log('SUCCESS', 'Rust validation passed');
142
231
  }
143
- } catch (parseError) {
144
- log('ERROR', 'Failed to parse security scanner output', {
145
- parseError: parseError.message,
146
- output: securityScanOutput
147
- });
148
232
 
149
- // Fallback vulnerability detection (minimal built-in checks)
150
- const builtinChecks = [
151
- {
152
- pattern: /eval\(/,
153
- vulnerability: 'POTENTIAL_RCE',
154
- severity: 'critical'
155
- },
156
- {
157
- pattern: /innerHTML\s*=/,
158
- vulnerability: 'XSS_POTENTIAL',
159
- severity: 'high'
160
- },
161
- {
162
- pattern: /(password|secret|token|api[-_]?key|anthropic|openai|openrouter|kimi|npm[-_]?token|zai|z[-_]ai).*=.*['"]?[^'"\s]{20,}['"]?/i,
163
- vulnerability: 'HARDCODED_SECRET',
164
- severity: 'critical'
165
- }
166
- ];
167
-
168
- const foundVulnerabilities = builtinChecks
169
- .filter(check => check.pattern.test(fileContent))
170
- .map(check => ({
171
- type: check.vulnerability,
172
- severity: check.severity
173
- }));
174
-
175
- results.security = {
176
- passed: foundVulnerabilities.length === 0,
177
- confidence: 50,
178
- issues: foundVulnerabilities,
179
- details: 'Fallback vulnerability detection'
180
- };
233
+ } catch (error) {
234
+ // Parse Rust errors
235
+ const output = error.stdout || error.stderr || '';
236
+ const lines = output.split('\n').filter(line => line.includes('error[E') || line.includes('error:'));
237
+
238
+ if (lines.length === 0) {
239
+ results.rust = {
240
+ passed: true,
241
+ errors: []
242
+ };
243
+ log('SUCCESS', 'No Rust errors detected');
244
+ } else {
245
+ // Categorize Rust errors
246
+ const errorTypes = {
247
+ borrowCheck: lines.filter(l => l.includes('E0502') || l.includes('E0499') || l.includes('E0503')).length,
248
+ typeError: lines.filter(l => l.includes('E0308') || l.includes('E0277')).length,
249
+ missingImport: lines.filter(l => l.includes('E0432') || l.includes('E0433')).length,
250
+ lifetime: lines.filter(l => l.includes('E0106') || l.includes('E0495')).length,
251
+ syntaxError: lines.filter(l => l.includes('error:') && !l.includes('error[E')).length,
252
+ other: 0
253
+ };
254
+ errorTypes.other = lines.length - Object.values(errorTypes).reduce((a, b) => a + b, 0);
255
+
256
+ const severity = errorTypes.syntaxError > 0 ? 'SYNTAX_ERROR' :
257
+ errorTypes.borrowCheck > 0 ? 'BORROW_ERROR' :
258
+ lines.length > 5 ? 'RUST_ERRORS' : 'RUST_WARNING';
259
+
260
+ results.rust = {
261
+ passed: false,
262
+ errorCount: lines.length,
263
+ errorTypes,
264
+ errors: lines.slice(0, 5),
265
+ severity
266
+ };
181
267
 
182
- if (foundVulnerabilities.length > 0) {
183
- log('SECURITY_WARNING', 'Vulnerabilities detected by fallback method', {
184
- vulnerabilities: foundVulnerabilities
268
+ log(severity, `Rust errors detected: ${lines.length}`, {
269
+ errorCount: lines.length,
270
+ errorTypes,
271
+ errors: lines.slice(0, 5)
185
272
  });
186
273
 
187
- foundVulnerabilities.forEach(vuln => {
274
+ // Add recommendations based on error types
275
+ if (errorTypes.borrowCheck > 0) {
276
+ results.recommendations.push({
277
+ type: 'rust',
278
+ priority: 'critical',
279
+ message: 'Fix borrow checker errors',
280
+ action: 'Review ownership and borrowing rules'
281
+ });
282
+ } else if (errorTypes.lifetime > 0) {
188
283
  results.recommendations.push({
189
- type: 'security',
190
- priority: vuln.severity === 'critical' ? 'critical' : 'high',
191
- message: `Potential ${vuln.type} vulnerability detected`,
192
- action: `Manually review code for ${vuln.type} vulnerability`
284
+ type: 'rust',
285
+ priority: 'high',
286
+ message: 'Fix lifetime annotations',
287
+ action: 'Add or correct lifetime parameters'
193
288
  });
194
- });
289
+ } else if (errorTypes.typeError > 0) {
290
+ results.recommendations.push({
291
+ type: 'rust',
292
+ priority: 'high',
293
+ message: 'Fix type mismatches',
294
+ action: 'Check expected vs actual types'
295
+ });
296
+ }
195
297
  }
196
298
  }
197
- } catch (error) {
198
- log('CRITICAL_ERROR', 'Unexpected security scanning failure', {
199
- error: error.message,
200
- stack: error.stack
201
- });
202
-
203
- results.security = {
204
- passed: false,
205
- confidence: 0,
206
- issues: [],
207
- details: 'Complete security scanning failure'
208
- };
209
-
210
- results.recommendations.push({
211
- type: 'security',
212
- priority: 'critical',
213
- message: 'Security scanning infrastructure failure',
214
- action: 'Verify security scanning script and dependencies'
215
- });
299
+ } else {
300
+ log('SKIPPED', 'Rust validation skipped for non-Rust file');
216
301
  }
217
302
 
218
303
  // ============================================================================
219
- // PHASE 2.5: Bash Validator Integration
304
+ // PHASE 1: ESLint Integration
220
305
  // ============================================================================
221
306
 
222
- log('VALIDATING', 'Running bash validators');
223
-
224
- // Validator mapping by file extension
225
- const validatorsByExtension = {
226
- '.sh': [
227
- 'bash-pipe-safety.sh',
228
- 'bash-dependency-checker.sh',
229
- 'enforce-lf.sh'
230
- ],
231
- '.bash': [
232
- 'bash-pipe-safety.sh',
233
- 'bash-dependency-checker.sh',
234
- 'enforce-lf.sh'
235
- ],
236
- '.py': [
237
- 'python-subprocess-safety.py',
238
- 'python-async-safety.py',
239
- 'python-import-checker.py',
240
- 'enforce-lf.sh'
241
- ],
242
- '.js': [
243
- 'js-promise-safety.sh',
244
- 'enforce-lf.sh'
245
- ],
246
- '.ts': [
247
- 'js-promise-safety.sh',
248
- 'enforce-lf.sh'
249
- ],
250
- '.jsx': [
251
- 'js-promise-safety.sh',
252
- 'enforce-lf.sh'
253
- ],
254
- '.tsx': [
255
- 'js-promise-safety.sh',
256
- 'enforce-lf.sh'
257
- ],
258
- '.rs': [
259
- 'rust-command-safety.sh',
260
- 'rust-future-safety.sh',
261
- 'rust-dependency-checker.sh',
262
- 'enforce-lf.sh'
263
- ]
264
- };
307
+ if (['.js', '.jsx', '.ts', '.tsx'].includes(ext)) {
308
+ try {
309
+ log('VALIDATING', 'Running ESLint validation');
310
+
311
+ // Check if ESLint is available
312
+ try {
313
+ execSync('npx eslint --version', { stdio: 'ignore', timeout: 5000 });
314
+ } catch {
315
+ log('SKIPPED', 'ESLint not available');
316
+ results.eslint = { available: false };
317
+ }
265
318
 
266
- // Helper function to run a single validator
267
- function runValidator(validatorName, targetFile) {
268
- const validatorPath = `.claude/skills/hook-pipeline/${validatorName}`;
319
+ if (results.eslint === null) {
320
+ // Run ESLint
321
+ const eslintCmd = `npx eslint "${filePath}" --format json`;
322
+ const eslintOutput = execSync(eslintCmd, {
323
+ stdio: 'pipe',
324
+ encoding: 'utf-8',
325
+ timeout: 10000
326
+ });
327
+
328
+ const eslintResults = JSON.parse(eslintOutput);
329
+ const fileResults = eslintResults[0] || { messages: [] };
330
+
331
+ results.eslint = {
332
+ available: true,
333
+ passed: fileResults.errorCount === 0,
334
+ errorCount: fileResults.errorCount || 0,
335
+ warningCount: fileResults.warningCount || 0,
336
+ messages: fileResults.messages.slice(0, 5)
337
+ };
338
+
339
+ if (fileResults.errorCount > 0) {
340
+ log('LINT_ISSUES', `ESLint found ${fileResults.errorCount} errors`, {
341
+ errorCount: fileResults.errorCount,
342
+ warningCount: fileResults.warningCount
343
+ });
344
+
345
+ results.recommendations.push({
346
+ type: 'eslint',
347
+ priority: 'high',
348
+ message: `Fix ${fileResults.errorCount} ESLint errors`,
349
+ action: `Run: npx eslint "${filePath}" --fix`
350
+ });
351
+ } else {
352
+ log('SUCCESS', 'ESLint validation passed');
353
+ }
354
+ }
355
+ } catch (error) {
356
+ // ESLint errors are expected, parse them
357
+ if (error.stdout) {
358
+ try {
359
+ const eslintResults = JSON.parse(error.stdout);
360
+ const fileResults = eslintResults[0] || { messages: [] };
361
+
362
+ results.eslint = {
363
+ available: true,
364
+ passed: fileResults.errorCount === 0,
365
+ errorCount: fileResults.errorCount || 0,
366
+ warningCount: fileResults.warningCount || 0,
367
+ messages: fileResults.messages.slice(0, 5)
368
+ };
369
+
370
+ if (fileResults.errorCount > 0) {
371
+ log('LINT_ISSUES', `ESLint found ${fileResults.errorCount} errors`, {
372
+ errorCount: fileResults.errorCount,
373
+ warningCount: fileResults.warningCount
374
+ });
375
+
376
+ results.recommendations.push({
377
+ type: 'eslint',
378
+ priority: 'high',
379
+ message: `Fix ${fileResults.errorCount} ESLint errors`,
380
+ action: `Run: npx eslint "${filePath}" --fix`
381
+ });
382
+ }
383
+ } catch {
384
+ log('ERROR', 'ESLint execution failed', { error: error.message });
385
+ results.eslint = { available: false, error: error.message };
386
+ }
387
+ } else {
388
+ log('ERROR', 'ESLint execution failed', { error: error.message });
389
+ results.eslint = { available: false, error: error.message };
390
+ }
391
+ }
392
+ }
269
393
 
270
- log('DEBUG', `Executing validator: ${validatorName}`, { targetFile });
394
+ // ============================================================================
395
+ // PHASE 1: Prettier Integration
396
+ // ============================================================================
271
397
 
398
+ if (['.js', '.jsx', '.ts', '.tsx', '.json', '.css', '.html'].includes(ext)) {
272
399
  try {
273
- // Determine interpreter based on file extension
274
- const isPython = validatorName.endsWith('.py');
275
- const interpreter = isPython ? 'python3' : 'bash';
276
-
277
- const result = spawnSync(interpreter, [validatorPath, targetFile], {
278
- encoding: 'utf-8',
279
- timeout: 5000,
280
- cwd: process.cwd()
281
- });
400
+ log('VALIDATING', 'Running Prettier formatting check');
401
+
402
+ // Check if Prettier is available
403
+ try {
404
+ execSync('npx prettier --version', { stdio: 'ignore', timeout: 5000 });
405
+ } catch {
406
+ log('SKIPPED', 'Prettier not available');
407
+ results.prettier = { available: false };
408
+ }
282
409
 
283
- const exitCode = result.status;
284
- const stdout = (result.stdout || '').trim();
285
- const stderr = (result.stderr || '').trim();
410
+ if (results.prettier === null) {
411
+ // Run Prettier check
412
+ try {
413
+ const prettierCmd = `npx prettier --check "${filePath}"`;
414
+ execSync(prettierCmd, {
415
+ stdio: 'pipe',
416
+ encoding: 'utf-8',
417
+ timeout: 10000
418
+ });
286
419
 
287
- log('DEBUG', `Validator ${validatorName} completed`, {
288
- exitCode,
289
- stdout: stdout.substring(0, 200), // Truncate for logging
290
- stderr: stderr.substring(0, 200)
291
- });
420
+ results.prettier = {
421
+ available: true,
422
+ passed: true,
423
+ formatted: true
424
+ };
425
+ log('SUCCESS', 'Prettier formatting check passed');
426
+
427
+ } catch (error) {
428
+ results.prettier = {
429
+ available: true,
430
+ passed: false,
431
+ formatted: false,
432
+ needsFormatting: true
433
+ };
434
+ log('LINT_ISSUES', 'File needs Prettier formatting');
292
435
 
293
- // Exit code convention:
294
- // 0 = pass (no issues)
295
- // 1 = error (blocking issue)
296
- // 2 = warning (non-blocking issue)
297
- return {
298
- validator: validatorName,
299
- exitCode,
300
- passed: exitCode === 0,
301
- isBlocking: exitCode === 1,
302
- isWarning: exitCode === 2,
303
- message: stderr || stdout || 'Validator passed',
304
- stdout,
305
- stderr
306
- };
436
+ results.recommendations.push({
437
+ type: 'prettier',
438
+ priority: 'medium',
439
+ message: 'File needs formatting',
440
+ action: `Run: npx prettier --write "${filePath}"`
441
+ });
442
+ }
443
+ }
307
444
  } catch (error) {
308
- log('ERROR', `Validator ${validatorName} execution failed`, {
309
- error: error.message,
310
- stack: error.stack
311
- });
312
-
313
- return {
314
- validator: validatorName,
315
- exitCode: -1,
316
- passed: false,
317
- isBlocking: false,
318
- isWarning: true,
319
- message: `Validator execution failed: ${error.message}`,
320
- error: error.message
321
- };
445
+ log('ERROR', 'Prettier execution failed', { error: error.message });
446
+ results.prettier = { available: false, error: error.message };
322
447
  }
323
448
  }
324
449
 
325
- // Run validators for applicable file types
326
- const applicableValidators = validatorsByExtension[ext] || [];
450
+ // ============================================================================
451
+ // PHASE 2: Security Analysis
452
+ // ============================================================================
327
453
 
328
- if (applicableValidators.length > 0) {
329
- log('INFO', `Running ${applicableValidators.length} bash validators for ${ext} file`);
454
+ log('VALIDATING', 'Running security analysis');
330
455
 
331
- // Sequential execution of validators
332
- const validatorResults = applicableValidators.map(validator =>
333
- runValidator(validator, filePath)
334
- );
456
+ const securityIssues = [];
335
457
 
336
- // Process validator results
337
- validatorResults.forEach(result => {
338
- if (result.isBlocking) {
339
- // Blocking error (exit code 1)
340
- log('VALIDATOR_ERROR', `Blocking issue detected by ${result.validator}`, {
341
- message: result.message
342
- });
458
+ // Check for eval() usage
459
+ if (fileContent.includes('eval(')) {
460
+ securityIssues.push({
461
+ type: 'security',
462
+ severity: 'critical',
463
+ message: 'Use of eval() function detected - security risk',
464
+ suggestion: 'Replace eval() with safer alternatives like JSON.parse() or Function constructor with proper validation'
465
+ });
466
+ }
343
467
 
344
- results.recommendations.push({
345
- type: 'bash-validator',
346
- priority: 'critical',
347
- message: `${result.validator}: ${result.message}`,
348
- action: 'Fix blocking issue before proceeding'
349
- });
350
- } else if (result.isWarning) {
351
- // Warning (exit code 2)
352
- log('VALIDATOR_WARNING', `Warning from ${result.validator}`, {
353
- message: result.message
354
- });
468
+ // Check for new Function() usage
469
+ if (fileContent.includes('new Function(')) {
470
+ securityIssues.push({
471
+ type: 'security',
472
+ severity: 'critical',
473
+ message: 'Use of new Function() detected - security risk',
474
+ suggestion: 'Avoid dynamic code execution; use safer alternatives'
475
+ });
476
+ }
355
477
 
356
- results.recommendations.push({
357
- type: 'bash-safety',
358
- priority: 'medium',
359
- message: `${result.validator}: ${result.message}`,
360
- action: 'Review recommendations and consider fixing'
361
- });
362
- } else if (result.passed) {
363
- // Pass (exit code 0)
364
- log('SUCCESS', `Validator ${result.validator} passed`);
365
- }
478
+ // Check for password logging
479
+ if (fileContent.includes('password') && (fileContent.includes('console.log') || fileContent.includes('console.debug'))) {
480
+ securityIssues.push({
481
+ type: 'security',
482
+ severity: 'critical',
483
+ message: 'Potential password logging detected',
484
+ suggestion: 'Remove password logging from code immediately'
485
+ });
486
+ }
487
+
488
+ // Check for XSS vulnerabilities (innerHTML with concatenation)
489
+ if (fileContent.includes('innerHTML') && fileContent.match(/innerHTML\s*[+]=|innerHTML\s*=\s*.*\+/)) {
490
+ securityIssues.push({
491
+ type: 'security',
492
+ severity: 'high',
493
+ message: 'Potential XSS vulnerability with innerHTML concatenation',
494
+ suggestion: 'Use textContent, createElement, or proper sanitization libraries'
366
495
  });
496
+ }
367
497
 
368
- // Store validator results for exit code determination
369
- results.bashValidators = {
370
- executed: validatorResults.length,
371
- passed: validatorResults.filter(r => r.passed).length,
372
- warnings: validatorResults.filter(r => r.isWarning).length,
373
- errors: validatorResults.filter(r => r.isBlocking).length,
374
- results: validatorResults
375
- };
376
-
377
- log('SUCCESS', `Bash validators completed`, {
378
- executed: results.bashValidators.executed,
379
- passed: results.bashValidators.passed,
380
- warnings: results.bashValidators.warnings,
381
- errors: results.bashValidators.errors
498
+ // Check for hardcoded secrets (basic patterns)
499
+ const secretPatterns = [
500
+ /api[_-]?key\s*=\s*['"][^'"]{20,}['"]/i,
501
+ /secret\s*=\s*['"][^'"]{20,}['"]/i,
502
+ /token\s*=\s*['"][^'"]{20,}['"]/i,
503
+ /password\s*=\s*['"][^'"]+['"]/i
504
+ ];
505
+
506
+ for (const pattern of secretPatterns) {
507
+ if (pattern.test(fileContent)) {
508
+ securityIssues.push({
509
+ type: 'security',
510
+ severity: 'critical',
511
+ message: 'Potential hardcoded secret detected',
512
+ suggestion: 'Move secrets to environment variables or secure configuration'
513
+ });
514
+ break; // Only report once
515
+ }
516
+ }
517
+
518
+ results.security = {
519
+ passed: securityIssues.length === 0,
520
+ issueCount: securityIssues.length,
521
+ issues: securityIssues
522
+ };
523
+
524
+ if (securityIssues.length > 0) {
525
+ log('SECURITY_ISSUES', `Security analysis found ${securityIssues.length} issues`, {
526
+ issueCount: securityIssues.length,
527
+ issues: securityIssues
528
+ });
529
+
530
+ results.recommendations.push({
531
+ type: 'security',
532
+ priority: 'critical',
533
+ message: `Address ${securityIssues.length} security ${securityIssues.length === 1 ? 'issue' : 'issues'} immediately`,
534
+ action: 'Review security recommendations and apply fixes'
382
535
  });
383
536
  } else {
384
- log('DEBUG', `No bash validators configured for ${ext} files`);
537
+ log('SUCCESS', 'No security issues detected');
385
538
  }
386
539
 
387
540
  // ============================================================================
388
- // PHASE 2.6: SQL Injection Detection
541
+ // PHASE 3: Code Metrics
389
542
  // ============================================================================
390
543
 
391
- if (ext.match(/\.(sql|ts|js|py)$/)) {
392
- log('VALIDATING', 'Running SQL injection detection');
544
+ log('VALIDATING', 'Calculating code metrics');
393
545
 
394
- const sqlInjectionPatterns = [
395
- // String concatenation in queries
396
- { pattern: /['"`]\s*\+\s*\w+\s*\+\s*['"`].*(?:SELECT|INSERT|UPDATE|DELETE|WHERE)/i, risk: 'STRING_CONCATENATION', severity: 'critical' },
397
- { pattern: /(?:SELECT|INSERT|UPDATE|DELETE|WHERE).*['"`]\s*\+\s*\w+/i, risk: 'STRING_CONCATENATION', severity: 'critical' },
546
+ const lines = fileContent.split('\n');
547
+ const lineCount = lines.length;
548
+ const functionCount = (fileContent.match(/function\s+\w+|const\s+\w+\s*=\s*\([^)]*\)\s*=>/g) || []).length;
549
+ const classCount = (fileContent.match(/class\s+\w+/g) || []).length;
550
+ const todoCount = (fileContent.match(/\/\/\s*TODO|\/\*\s*TODO/gi) || []).length;
551
+ const fixmeCount = (fileContent.match(/\/\/\s*FIXME|\/\*\s*FIXME/gi) || []).length;
552
+
553
+ // Calculate cyclomatic complexity (simplified)
554
+ let complexity = 'low';
555
+ if (lineCount > 300 || functionCount > 10) {
556
+ complexity = 'high';
557
+ } else if (lineCount > 150 || functionCount > 5) {
558
+ complexity = 'medium';
559
+ }
398
560
 
399
- // Template literals with variables in SQL
400
- { pattern: /\$\{[^}]+\}.*(?:SELECT|INSERT|UPDATE|DELETE|WHERE)/i, risk: 'TEMPLATE_INJECTION', severity: 'high' },
561
+ results.metrics = {
562
+ lines: lineCount,
563
+ functions: functionCount,
564
+ classes: classCount,
565
+ todos: todoCount,
566
+ fixmes: fixmeCount,
567
+ complexity
568
+ };
401
569
 
402
- // f-strings in Python SQL
403
- { pattern: /f['"].*(?:SELECT|INSERT|UPDATE|DELETE|WHERE).*\{/i, risk: 'FSTRING_SQL_INJECTION', severity: 'critical' },
570
+ log('SUCCESS', 'Code metrics calculated', {
571
+ lines: lineCount,
572
+ functions: functionCount,
573
+ classes: classCount,
574
+ complexity
575
+ });
404
576
 
405
- // .format() in Python SQL
406
- { pattern: /['"].*(?:SELECT|INSERT|UPDATE|DELETE).*['"]\.format\(/i, risk: 'FORMAT_SQL_INJECTION', severity: 'critical' },
577
+ // ============================================================================
578
+ // PHASE 3: Recommendations Engine
579
+ // ============================================================================
407
580
 
408
- // Raw user input in query
409
- { pattern: /(?:req\.body|req\.query|req\.params|request\.form|request\.args)\.[^\s]+.*(?:SELECT|INSERT|UPDATE|DELETE)/i, risk: 'UNSANITIZED_INPUT', severity: 'critical' },
581
+ log('VALIDATING', 'Generating recommendations');
410
582
 
411
- // execute() with string concatenation
412
- { pattern: /\.execute\s*\(\s*['"`].*\+/i, risk: 'EXECUTE_CONCATENATION', severity: 'critical' },
413
- { pattern: /\.execute\s*\(\s*f['"`]/i, risk: 'EXECUTE_FSTRING', severity: 'critical' },
414
- ];
583
+ // Maintainability recommendations
584
+ if (lineCount > 200) {
585
+ results.recommendations.push({
586
+ type: 'maintainability',
587
+ priority: 'medium',
588
+ message: `File has ${lineCount} lines - consider breaking it down`,
589
+ action: 'Split into smaller, focused modules (150-200 lines per file)'
590
+ });
591
+ }
415
592
 
416
- const sqlInjectionIssues = sqlInjectionPatterns
417
- .filter(check => check.pattern.test(fileContent))
418
- .map(check => ({ type: check.risk, severity: check.severity }));
593
+ // Code quality recommendations
594
+ if (fileContent.includes('var ')) {
595
+ results.recommendations.push({
596
+ type: 'code-quality',
597
+ priority: 'low',
598
+ message: 'Use const or let instead of var',
599
+ action: 'Replace var declarations with const or let for better scoping'
600
+ });
601
+ }
419
602
 
420
- if (sqlInjectionIssues.length > 0) {
421
- log('SQL_INJECTION_WARNING', `Found ${sqlInjectionIssues.length} potential SQL injection risks`, {
422
- issues: sqlInjectionIssues
423
- });
603
+ if (fileContent.includes('==') && !fileContent.includes('===') && fileContent.includes('==') > fileContent.includes('===')) {
604
+ results.recommendations.push({
605
+ type: 'code-quality',
606
+ priority: 'medium',
607
+ message: 'Prefer strict equality (===) over loose equality (==)',
608
+ action: 'Replace == with === for type-safe comparisons'
609
+ });
610
+ }
424
611
 
425
- results.sqlInjection = {
426
- passed: false,
427
- issues: sqlInjectionIssues
428
- };
612
+ // TypeScript-specific recommendations
613
+ if (['.ts', '.tsx'].includes(ext)) {
614
+ if (fileContent.includes(': any')) {
615
+ results.recommendations.push({
616
+ type: 'typescript',
617
+ priority: 'medium',
618
+ message: 'Avoid using "any" type when possible',
619
+ action: 'Use specific types or unknown for better type safety'
620
+ });
621
+ }
429
622
 
430
- sqlInjectionIssues.forEach(issue => {
431
- results.recommendations.push({
432
- type: 'sql-injection',
433
- priority: issue.severity,
434
- message: `Potential SQL injection: ${issue.type}`,
435
- action: 'Use parameterized queries ($1, $2) or prepared statements instead of string concatenation'
436
- });
623
+ if (!fileContent.includes('interface') && !fileContent.includes('type ') && lineCount > 100) {
624
+ results.recommendations.push({
625
+ type: 'typescript',
626
+ priority: 'low',
627
+ message: 'Consider defining interfaces or types',
628
+ action: 'Add type definitions for better code structure and maintainability'
437
629
  });
438
- } else {
439
- results.sqlInjection = { passed: true, issues: [] };
440
- log('SUCCESS', 'No SQL injection risks detected');
441
630
  }
442
631
  }
443
632
 
444
- // ============================================================================
445
- // PHASE 3: Root Directory Detection
446
- // ============================================================================
633
+ // Documentation recommendations
634
+ if (fileContent.includes('export ') && !fileContent.includes('/**') && functionCount > 0) {
635
+ results.recommendations.push({
636
+ type: 'documentation',
637
+ priority: 'low',
638
+ message: 'Public exports could benefit from JSDoc comments',
639
+ action: 'Add JSDoc documentation for exported functions/classes'
640
+ });
641
+ }
447
642
 
448
- log('VALIDATING', 'Checking file location (root directory warning)');
643
+ // Testing recommendations
644
+ if (!filePath.includes('test') && !filePath.includes('spec') && (functionCount > 0 || classCount > 0)) {
645
+ results.recommendations.push({
646
+ type: 'testing',
647
+ priority: 'medium',
648
+ message: 'Consider writing tests for this module',
649
+ action: 'Create corresponding test file to ensure code reliability'
650
+ });
651
+ }
449
652
 
450
- const isRootFile = dirname(resolve(filePath)) === resolve('.');
451
- if (isRootFile && !filePath.match(/^(package\.json|tsconfig\.json|\.gitignore|\.env.*|README\.md|LICENSE|CLAUDE\.md)$/)) {
452
- // Suggest appropriate location based on file type
453
- const suggestions = [];
454
- if (ext.match(/\.(js|ts|jsx|tsx)$/)) {
455
- suggestions.push({ location: `src/${filePath}`, reason: 'Source files belong in src/' });
456
- }
457
- if (ext.match(/\.(test|spec)\.(js|ts|jsx|tsx)$/)) {
458
- suggestions.push({ location: `tests/${filePath}`, reason: 'Test files belong in tests/' });
459
- }
460
- if (ext === '.md' && !filePath.match(/^(README|CLAUDE)\.md$/)) {
461
- suggestions.push({ location: `docs/${filePath}`, reason: 'Documentation belongs in docs/' });
462
- }
463
- if (ext === '.json' && !filePath.match(/^package\.json$/)) {
464
- suggestions.push({ location: `config/${filePath}`, reason: 'Config files belong in config/' });
465
- }
466
- if (ext === '.sh') {
467
- suggestions.push({ location: `scripts/${filePath}`, reason: 'Scripts belong in scripts/' });
468
- }
653
+ // TODO/FIXME recommendations
654
+ if (todoCount > 0 || fixmeCount > 0) {
655
+ results.recommendations.push({
656
+ type: 'maintenance',
657
+ priority: 'low',
658
+ message: `Found ${todoCount} TODOs and ${fixmeCount} FIXMEs`,
659
+ action: 'Address pending tasks and technical debt'
660
+ });
661
+ }
469
662
 
470
- if (suggestions.length > 0) {
471
- log('ROOT_WARNING', 'File in root directory - should be organized', {
472
- file: filePath,
473
- suggestions
474
- });
663
+ // Sort recommendations by priority
664
+ const priorityOrder = { critical: 0, high: 1, medium: 2, low: 3 };
665
+ results.recommendations.sort((a, b) => priorityOrder[a.priority] - priorityOrder[b.priority]);
475
666
 
476
- results.recommendations.push({
477
- type: 'organization',
478
- priority: 'high',
479
- message: `File "${filePath}" should not be in root directory`,
480
- action: `Move to: ${suggestions[0].location}`,
481
- suggestions
482
- });
667
+ // Limit to top 10 recommendations
668
+ results.recommendations = results.recommendations.slice(0, 10);
483
669
 
484
- // Store for handler processing
485
- results.rootWarning = { suggestions };
486
- }
487
- }
670
+ log('SUCCESS', `Generated ${results.recommendations.length} recommendations`);
488
671
 
489
672
  // ============================================================================
490
- // PHASE 4: TDD Violation Detection
673
+ // MULTI-LANGUAGE VALIDATION
491
674
  // ============================================================================
492
675
 
493
- if (ext.match(/\.(js|ts|jsx|tsx|py|go|rs)$/) && !filePath.match(/\.(test|spec)\./)) {
494
- log('VALIDATING', 'Checking TDD compliance');
676
+ // JavaScript Type Checking (JSDoc via TypeScript)
677
+ if (['.js', '.jsx', '.mjs', '.cjs'].includes(ext)) {
678
+ try {
679
+ log('VALIDATING', 'Running JavaScript type checking (JSDoc via TypeScript)');
680
+
681
+ // Check if TypeScript is available
682
+ try {
683
+ execSync('npx tsc --version', { stdio: 'ignore', timeout: 5000 });
684
+ } catch {
685
+ log('WARNING', 'TypeScript not available for JavaScript type checking');
686
+ results.javascript = { available: false };
687
+ results.recommendations.push({
688
+ type: 'dependency',
689
+ priority: 'medium',
690
+ message: 'TypeScript not installed - JavaScript type checking unavailable',
691
+ action: 'Install TypeScript: npm install -g typescript',
692
+ line: null
693
+ });
694
+ throw new Error('TypeScript not available');
695
+ }
495
696
 
496
- const testPatterns = {
497
- js: [`${dirname(filePath)}/${baseName}.test.js`, `tests/${baseName}.test.js`],
498
- ts: [`${dirname(filePath)}/${baseName}.test.ts`, `tests/${baseName}.test.ts`],
499
- py: [`${dirname(filePath)}/test_${baseName}.py`, `tests/test_${baseName}.py`],
500
- go: [`${dirname(filePath)}/${baseName}_test.go`],
501
- rs: null // Rust uses inline tests
502
- };
697
+ // Use TypeScript with allowJs and checkJs to validate JSDoc
698
+ const tempConfig = JSON.stringify({
699
+ compilerOptions: {
700
+ allowJs: true,
701
+ checkJs: true,
702
+ noEmit: true,
703
+ skipLibCheck: true,
704
+ strict: false,
705
+ target: 'ES2020',
706
+ module: 'commonjs'
707
+ }
708
+ });
503
709
 
504
- const langKey = ext.replace('.', '');
505
- const patterns = testPatterns[langKey];
710
+ const configPath = '/tmp/tsconfig.jsdoc.json';
711
+ require('fs').writeFileSync(configPath, tempConfig);
506
712
 
507
- if (patterns) {
508
- const hasTest = patterns.some(p => existsSync(p));
713
+ const cmd = `npx tsc --noEmit --project ${configPath} ${filePath}`;
714
+ execSync(cmd, { stdio: 'pipe', encoding: 'utf-8', timeout: 10000 });
509
715
 
510
- if (!hasTest) {
511
- log('TDD_VIOLATION', 'No test file found', {
512
- file: filePath,
513
- expectedLocations: patterns
514
- });
716
+ results.javascript = {
717
+ available: true,
718
+ passed: true,
719
+ errors: [],
720
+ hasJSDoc: fileContent.includes('/**') || fileContent.includes('@param') || fileContent.includes('@returns')
721
+ };
722
+
723
+ log('SUCCESS', 'JavaScript type checking passed');
515
724
 
725
+ if (!results.javascript.hasJSDoc) {
516
726
  results.recommendations.push({
517
- type: 'testing',
518
- priority: 'high',
519
- message: 'No test file found for this module',
520
- action: 'Create test file or run feedback-resolver.sh --type TDD_VIOLATION'
727
+ type: 'javascript',
728
+ priority: 'low',
729
+ message: 'No JSDoc comments found in JavaScript file',
730
+ action: 'Add JSDoc type annotations for better type safety',
731
+ line: null
521
732
  });
733
+ }
522
734
 
523
- results.tddViolation = {
524
- hasTests: false,
525
- testFile: patterns[0],
526
- recommendations: [`Create ${patterns[0]}`]
735
+ } catch (error) {
736
+ if (!results.javascript || results.javascript.available !== false) {
737
+ // Parse TypeScript/JSDoc errors
738
+ const output = error.stdout || error.stderr || '';
739
+ const lines = output.split('\n').filter(line => line.includes('error TS'));
740
+
741
+ const errors = lines.map(line => {
742
+ const match = line.match(/\((\d+),(\d+)\): error TS(\d+): (.+)/);
743
+ if (match) {
744
+ return {
745
+ line: parseInt(match[1]),
746
+ column: parseInt(match[2]),
747
+ code: match[3],
748
+ message: match[4]
749
+ };
750
+ }
751
+ return null;
752
+ }).filter(Boolean);
753
+
754
+ results.javascript = {
755
+ available: true,
756
+ passed: false,
757
+ errorCount: errors.length,
758
+ errors
527
759
  };
760
+
761
+ log('FAILED', `JavaScript type checking failed (${errors.length} errors)`, { errors });
762
+
763
+ // Add recommendations
764
+ errors.slice(0, 5).forEach(err => {
765
+ results.recommendations.push({
766
+ type: 'javascript',
767
+ priority: 'medium',
768
+ message: `JSDoc type error: ${err.message}`,
769
+ action: `Fix type annotation at line ${err.line}`,
770
+ line: err.line
771
+ });
772
+ });
528
773
  }
529
774
  }
530
775
  }
531
776
 
532
- // ============================================================================
533
- // PHASE 5: Code Metrics and Complexity Analysis
534
- // ============================================================================
777
+ // Python Validation (pylint + black)
778
+ if (ext === '.py') {
779
+ try {
780
+ log('VALIDATING', 'Running Python validation (pylint + black)');
781
+
782
+ // Check for pylint
783
+ let pylintAvailable = false;
784
+ try {
785
+ execSync('pylint --version', { stdio: 'ignore', timeout: 5000 });
786
+ pylintAvailable = true;
787
+ } catch {
788
+ log('WARNING', 'pylint not available for Python validation');
789
+ }
535
790
 
536
- log('VALIDATING', 'Calculating code metrics');
791
+ // Check for black
792
+ let blackAvailable = false;
793
+ try {
794
+ execSync('black --version', { stdio: 'ignore', timeout: 5000 });
795
+ blackAvailable = true;
796
+ } catch {
797
+ log('WARNING', 'black not available for Python formatting check');
798
+ }
537
799
 
538
- const lines = fileContent.split('\n').length;
539
- const functions = (fileContent.match(/function\s+\w+|const\s+\w+\s*=\s*\(/g) || []).length;
540
- const classes = (fileContent.match(/class\s+\w+/g) || []).length;
541
- const todos = (fileContent.match(/\/\/\s*TODO/gi) || []).length;
542
- const fixmes = (fileContent.match(/\/\/\s*FIXME/gi) || []).length;
800
+ results.python = {
801
+ available: pylintAvailable || blackAvailable,
802
+ pylint: null,
803
+ black: null
804
+ };
543
805
 
544
- results.metrics = {
545
- lines,
546
- functions,
547
- classes,
548
- todos,
549
- fixmes,
550
- complexity: lines > 300 ? 'high' : lines > 100 ? 'medium' : 'low'
551
- };
806
+ // Add warning if no Python tools available
807
+ if (!pylintAvailable && !blackAvailable) {
808
+ results.recommendations.push({
809
+ type: 'dependency',
810
+ priority: 'medium',
811
+ message: 'Python validation tools not installed (pylint, black)',
812
+ action: 'Install Python tools: pip install pylint black',
813
+ line: null
814
+ });
815
+ }
552
816
 
553
- log('SUCCESS', 'Code metrics calculated', results.metrics);
817
+ // Run pylint if available
818
+ if (pylintAvailable) {
819
+ try {
820
+ const pylintCmd = `pylint "${filePath}" --output-format=json`;
821
+ const pylintOutput = execSync(pylintCmd, { encoding: 'utf-8', timeout: 10000 });
822
+ const pylintResults = JSON.parse(pylintOutput || '[]');
823
+
824
+ const errors = pylintResults.filter(r => r.type === 'error');
825
+ const warnings = pylintResults.filter(r => r.type === 'warning');
826
+
827
+ results.python.pylint = {
828
+ passed: errors.length === 0,
829
+ errorCount: errors.length,
830
+ warningCount: warnings.length,
831
+ issues: pylintResults
832
+ };
833
+
834
+ log('SUCCESS', `pylint validation complete (${errors.length} errors, ${warnings.length} warnings)`);
835
+
836
+ // Add critical error recommendations
837
+ errors.slice(0, 3).forEach(err => {
838
+ results.recommendations.push({
839
+ type: 'python',
840
+ priority: 'high',
841
+ message: `pylint error: ${err.message}`,
842
+ action: `Fix ${err.symbol} at line ${err.line}`,
843
+ line: err.line
844
+ });
845
+ });
554
846
 
555
- // ============================================================================
556
- // PHASE 5.1: Cyclomatic Complexity Analysis
557
- // ============================================================================
847
+ } catch (error) {
848
+ // pylint may exit with non-zero for warnings
849
+ try {
850
+ const output = error.stdout || '[]';
851
+ const pylintResults = JSON.parse(output);
852
+ const errors = pylintResults.filter(r => r.type === 'error');
853
+ const warnings = pylintResults.filter(r => r.type === 'warning');
854
+
855
+ results.python.pylint = {
856
+ passed: errors.length === 0,
857
+ errorCount: errors.length,
858
+ warningCount: warnings.length,
859
+ issues: pylintResults
860
+ };
861
+ } catch {
862
+ results.python.pylint = { passed: false, error: 'Failed to parse pylint output' };
863
+ }
864
+ }
865
+ }
866
+
867
+ // Run black if available
868
+ if (blackAvailable) {
869
+ try {
870
+ const blackCmd = `black --check "${filePath}"`;
871
+ execSync(blackCmd, { encoding: 'utf-8', timeout: 10000 });
872
+
873
+ results.python.black = {
874
+ passed: true,
875
+ formatted: true
876
+ };
558
877
 
559
- log('VALIDATING', 'Analyzing cyclomatic complexity');
878
+ log('SUCCESS', 'black formatting check passed');
560
879
 
561
- // Only analyze files >200 lines to reduce overhead
562
- if (lines > 200 && ext.match(/\.(sh|js|ts|jsx|tsx|py)$/)) {
880
+ } catch (error) {
881
+ results.python.black = {
882
+ passed: false,
883
+ formatted: false
884
+ };
885
+
886
+ log('FAILED', 'File needs black formatting');
887
+
888
+ results.recommendations.push({
889
+ type: 'python',
890
+ priority: 'medium',
891
+ message: 'Python file needs formatting',
892
+ action: `Run: black "${filePath}"`,
893
+ line: null
894
+ });
895
+ }
896
+ }
897
+
898
+ } catch (error) {
899
+ log('ERROR', 'Python validation error', { error: error.message });
900
+ results.python = { available: false, error: error.message };
901
+ }
902
+ }
903
+
904
+ // Rust Validation (cargo clippy + rustfmt)
905
+ if (ext === '.rs') {
563
906
  try {
564
- // Use simple-complexity.sh for bash scripts
565
- if (ext === '.sh') {
566
- const complexityResult = spawnSync('bash', [
567
- 'scripts/simple-complexity.sh',
568
- filePath
569
- ], {
570
- encoding: 'utf-8',
571
- timeout: 5000
907
+ log('VALIDATING', 'Running Rust validation (cargo clippy + rustfmt)');
908
+
909
+ // Check for cargo
910
+ let cargoAvailable = false;
911
+ try {
912
+ execSync('cargo --version', { stdio: 'ignore', timeout: 5000 });
913
+ cargoAvailable = true;
914
+ } catch {
915
+ log('WARNING', 'cargo not available for Rust validation');
916
+ results.recommendations.push({
917
+ type: 'dependency',
918
+ priority: 'medium',
919
+ message: 'Rust toolchain not installed - cargo validation unavailable',
920
+ action: 'Install Rust: curl --proto \'=https\' --tlsv1.2 -sSf https://sh.rustup.rs | sh',
921
+ line: null
572
922
  });
923
+ }
573
924
 
574
- if (complexityResult.status === 0) {
575
- const output = complexityResult.stdout;
576
- const complexityMatch = output.match(/Total Complexity:\s*(\d+)/);
925
+ results.rust = {
926
+ available: cargoAvailable,
927
+ clippy: null,
928
+ rustfmt: null
929
+ };
577
930
 
578
- if (complexityMatch) {
579
- const complexity = parseInt(complexityMatch[1], 10);
580
- results.metrics.cyclomaticComplexity = complexity;
931
+ if (cargoAvailable) {
932
+ // Check for clippy
933
+ let clippyAvailable = false;
934
+ try {
935
+ execSync('cargo clippy --version', { stdio: 'ignore', timeout: 5000 });
936
+ clippyAvailable = true;
937
+ } catch {
938
+ log('WARNING', 'cargo clippy not available');
939
+ }
581
940
 
582
- log('SUCCESS', `Cyclomatic complexity: ${complexity}`, { complexity });
941
+ // Check for rustfmt
942
+ let rustfmtAvailable = false;
943
+ try {
944
+ execSync('rustfmt --version', { stdio: 'ignore', timeout: 5000 });
945
+ rustfmtAvailable = true;
946
+ } catch {
947
+ log('WARNING', 'rustfmt not available');
948
+ }
583
949
 
584
- // Warning threshold: 30
585
- if (complexity >= 30 && complexity < 40) {
586
- log('COMPLEXITY_WARNING', `Moderate complexity detected: ${complexity}`, {
587
- threshold: 30,
588
- complexity
589
- });
950
+ // Warn if Rust components missing
951
+ if (!clippyAvailable || !rustfmtAvailable) {
952
+ const missing = [];
953
+ if (!clippyAvailable) missing.push('clippy');
954
+ if (!rustfmtAvailable) missing.push('rustfmt');
955
+ results.recommendations.push({
956
+ type: 'dependency',
957
+ priority: 'low',
958
+ message: `Rust components missing: ${missing.join(', ')}`,
959
+ action: `Install: rustup component add ${missing.join(' ')}`,
960
+ line: null
961
+ });
962
+ }
963
+
964
+ // Run clippy if available (requires being in a Cargo project)
965
+ if (clippyAvailable) {
966
+ try {
967
+ const projectDir = dirname(filePath);
968
+ const clippyCmd = `cd "${projectDir}" && cargo clippy --message-format=json -- -W clippy::all 2>&1`;
969
+ const clippyOutput = execSync(clippyCmd, { encoding: 'utf-8', timeout: 30000 });
970
+
971
+ // Parse JSON messages
972
+ const messages = clippyOutput.split('\n')
973
+ .filter(line => line.trim().startsWith('{'))
974
+ .map(line => {
975
+ try {
976
+ return JSON.parse(line);
977
+ } catch {
978
+ return null;
979
+ }
980
+ })
981
+ .filter(msg => msg && msg.message && msg.message.level === 'warning');
590
982
 
983
+ results.rust.clippy = {
984
+ passed: messages.length === 0,
985
+ warningCount: messages.length,
986
+ warnings: messages
987
+ };
988
+
989
+ log('SUCCESS', `cargo clippy complete (${messages.length} warnings)`);
990
+
991
+ // Add recommendations for clippy warnings
992
+ messages.slice(0, 3).forEach(msg => {
591
993
  results.recommendations.push({
592
- type: 'complexity',
994
+ type: 'rust',
593
995
  priority: 'medium',
594
- message: `Cyclomatic complexity is ${complexity} (threshold: 30)`,
595
- action: 'Consider refactoring to reduce complexity'
596
- });
597
- }
598
-
599
- // Critical threshold: 40 - invoke lizard for detailed analysis
600
- if (complexity >= 40) {
601
- log('COMPLEXITY_CRITICAL', `High complexity detected: ${complexity}, invoking lizard`, {
602
- threshold: 40,
603
- complexity
996
+ message: `clippy: ${msg.message.message}`,
997
+ action: msg.message.rendered || 'Review clippy suggestion',
998
+ line: msg.message.spans?.[0]?.line_start || null
604
999
  });
1000
+ });
605
1001
 
606
- // Check if lizard is available
607
- const lizardCheck = spawnSync('which', ['lizard'], { encoding: 'utf-8' });
608
-
609
- if (lizardCheck.status === 0) {
610
- // Run lizard for detailed analysis
611
- const lizardResult = spawnSync('lizard', [
612
- filePath,
613
- '-C', '15' // Show functions with complexity >15
614
- ], {
615
- encoding: 'utf-8',
616
- timeout: 10000
617
- });
618
-
619
- if (lizardResult.status === 0) {
620
- const lizardOutput = lizardResult.stdout;
621
-
622
- log('LIZARD_ANALYSIS', 'Detailed complexity analysis', {
623
- output: lizardOutput
624
- });
625
-
626
- results.complexityAnalysis = {
627
- tool: 'lizard',
628
- complexity,
629
- detailedReport: lizardOutput
630
- };
631
-
632
- results.recommendations.push({
633
- type: 'complexity',
634
- priority: 'critical',
635
- message: `Critical complexity level: ${complexity} (threshold: 40)`,
636
- action: 'Refactor immediately. Run cyclomatic-complexity-reducer agent',
637
- details: lizardOutput
638
- });
639
- } else {
640
- log('WARN', 'Lizard analysis failed', {
641
- stderr: lizardResult.stderr
642
- });
643
- }
644
- } else {
645
- log('WARN', 'Lizard not installed, skipping detailed analysis');
646
-
647
- results.recommendations.push({
648
- type: 'complexity',
649
- priority: 'critical',
650
- message: `Critical complexity level: ${complexity} (threshold: 40)`,
651
- action: 'Refactor immediately. Install lizard: ./scripts/install-lizard.sh'
652
- });
653
- }
654
- }
1002
+ } catch (error) {
1003
+ log('SKIP', 'cargo clippy requires Cargo project context');
1004
+ results.rust.clippy = { passed: true, note: 'Requires Cargo project' };
655
1005
  }
656
- } else {
657
- log('WARN', 'Complexity analysis failed', {
658
- stderr: complexityResult.stderr
659
- });
660
1006
  }
661
- }
662
- // For TypeScript/JavaScript, use lizard directly if available
663
- else if (ext.match(/\.(js|ts|jsx|tsx)$/)) {
664
- const lizardCheck = spawnSync('which', ['lizard'], { encoding: 'utf-8' });
665
-
666
- if (lizardCheck.status === 0) {
667
- const lizardResult = spawnSync('lizard', [
668
- filePath,
669
- '--json'
670
- ], {
671
- encoding: 'utf-8',
672
- timeout: 10000
673
- });
674
1007
 
675
- if (lizardResult.status === 0) {
676
- try {
677
- const lizardData = JSON.parse(lizardResult.stdout);
678
-
679
- // Calculate average complexity
680
- let totalComplexity = 0;
681
- let functionCount = 0;
682
-
683
- if (lizardData.function_list) {
684
- lizardData.function_list.forEach(func => {
685
- totalComplexity += func.cyclomatic_complexity || 0;
686
- functionCount++;
687
- });
688
- }
689
-
690
- const avgComplexity = functionCount > 0 ? Math.round(totalComplexity / functionCount) : 0;
691
- results.metrics.cyclomaticComplexity = avgComplexity;
692
-
693
- if (avgComplexity >= 30) {
694
- log('COMPLEXITY_WARNING', `Average complexity: ${avgComplexity}`, {
695
- avgComplexity,
696
- functionCount
697
- });
698
-
699
- results.recommendations.push({
700
- type: 'complexity',
701
- priority: avgComplexity >= 40 ? 'critical' : 'medium',
702
- message: `Average cyclomatic complexity: ${avgComplexity}`,
703
- action: avgComplexity >= 40
704
- ? 'Critical: Refactor high-complexity functions immediately'
705
- : 'Consider refactoring complex functions'
706
- });
707
- }
708
- } catch (parseError) {
709
- log('WARN', 'Failed to parse lizard JSON output', {
710
- error: parseError.message
711
- });
712
- }
1008
+ // Run rustfmt if available
1009
+ if (rustfmtAvailable) {
1010
+ try {
1011
+ const rustfmtCmd = `rustfmt --check "${filePath}"`;
1012
+ execSync(rustfmtCmd, { encoding: 'utf-8', timeout: 10000 });
1013
+
1014
+ results.rust.rustfmt = {
1015
+ passed: true,
1016
+ formatted: true
1017
+ };
1018
+
1019
+ log('SUCCESS', 'rustfmt check passed');
1020
+
1021
+ } catch (error) {
1022
+ results.rust.rustfmt = {
1023
+ passed: false,
1024
+ formatted: false
1025
+ };
1026
+
1027
+ log('FAILED', 'Rust file needs formatting');
1028
+
1029
+ results.recommendations.push({
1030
+ type: 'rust',
1031
+ priority: 'low',
1032
+ message: 'Rust file needs formatting',
1033
+ action: `Run: rustfmt "${filePath}"`,
1034
+ line: null
1035
+ });
713
1036
  }
714
1037
  }
715
1038
  }
1039
+
716
1040
  } catch (error) {
717
- log('WARN', 'Complexity analysis error', {
718
- error: error.message
719
- });
1041
+ log('ERROR', 'Rust validation error', { error: error.message });
1042
+ results.rust = { available: false, error: error.message };
720
1043
  }
721
1044
  }
722
1045
 
723
- // Check for Rust-specific quality issues
724
- if (ext === '.rs') {
725
- log('VALIDATING', 'Running Rust quality checks');
726
-
727
- const rustIssues = [];
728
- if (fileContent.match(/println!\(/)) rustIssues.push('debug_println');
729
- if (fileContent.match(/unwrap\(\)/)) rustIssues.push('unwrap_usage');
730
- if (fileContent.match(/panic!\(/)) rustIssues.push('panic_usage');
731
-
732
- if (rustIssues.length > 0) {
733
- log('RUST_QUALITY', 'Rust quality issues detected', { issues: rustIssues });
1046
+ // Go Validation (gofmt + go vet)
1047
+ if (ext === '.go') {
1048
+ try {
1049
+ log('VALIDATING', 'Running Go validation (gofmt + go vet)');
1050
+
1051
+ // Check for go
1052
+ let goAvailable = false;
1053
+ try {
1054
+ execSync('go version', { stdio: 'ignore', timeout: 5000 });
1055
+ goAvailable = true;
1056
+ } catch {
1057
+ log('WARNING', 'go not available for Go validation');
1058
+ results.recommendations.push({
1059
+ type: 'dependency',
1060
+ priority: 'medium',
1061
+ message: 'Go toolchain not installed - Go validation unavailable',
1062
+ action: 'Install Go: https://golang.org/doc/install or brew install go',
1063
+ line: null
1064
+ });
1065
+ }
734
1066
 
735
- results.recommendations.push({
736
- type: 'rust',
737
- priority: 'medium',
738
- message: 'Rust quality issues detected',
739
- action: 'Run: cargo fmt && cargo clippy --fix --allow-dirty'
740
- });
1067
+ results.go = {
1068
+ available: goAvailable,
1069
+ gofmt: null,
1070
+ govet: null
1071
+ };
741
1072
 
742
- results.rustQuality = { issues: rustIssues };
743
- }
744
- }
1073
+ if (goAvailable) {
1074
+ // Run gofmt
1075
+ try {
1076
+ const gofmtCmd = `gofmt -l "${filePath}"`;
1077
+ const gofmtOutput = execSync(gofmtCmd, { encoding: 'utf-8', timeout: 10000 });
1078
+
1079
+ const needsFormatting = gofmtOutput.trim().length > 0;
1080
+
1081
+ results.go.gofmt = {
1082
+ passed: !needsFormatting,
1083
+ formatted: !needsFormatting
1084
+ };
1085
+
1086
+ if (needsFormatting) {
1087
+ log('FAILED', 'Go file needs formatting');
1088
+ results.recommendations.push({
1089
+ type: 'go',
1090
+ priority: 'medium',
1091
+ message: 'Go file needs formatting',
1092
+ action: `Run: gofmt -w "${filePath}"`,
1093
+ line: null
1094
+ });
1095
+ } else {
1096
+ log('SUCCESS', 'gofmt check passed');
1097
+ }
745
1098
 
746
- // ============================================================================
747
- // PHASE 6: Final Recommendations
748
- // ============================================================================
1099
+ } catch (error) {
1100
+ results.go.gofmt = { passed: false, error: error.message };
1101
+ }
749
1102
 
750
- log('VALIDATING', 'Generating recommendations');
1103
+ // Run go vet
1104
+ try {
1105
+ const govetCmd = `go vet "${filePath}"`;
1106
+ execSync(govetCmd, { encoding: 'utf-8', timeout: 10000 });
1107
+
1108
+ results.go.govet = {
1109
+ passed: true,
1110
+ issues: []
1111
+ };
1112
+
1113
+ log('SUCCESS', 'go vet passed');
1114
+
1115
+ } catch (error) {
1116
+ const output = error.stderr || error.stdout || '';
1117
+ const issues = output.split('\n').filter(line => line.trim().length > 0);
1118
+
1119
+ results.go.govet = {
1120
+ passed: false,
1121
+ issueCount: issues.length,
1122
+ issues
1123
+ };
1124
+
1125
+ log('FAILED', `go vet found ${issues.length} issues`);
1126
+
1127
+ // Add recommendations
1128
+ issues.slice(0, 3).forEach(issue => {
1129
+ results.recommendations.push({
1130
+ type: 'go',
1131
+ priority: 'high',
1132
+ message: `go vet: ${issue}`,
1133
+ action: 'Fix static analysis issue',
1134
+ line: null
1135
+ });
1136
+ });
1137
+ }
1138
+ }
751
1139
 
752
- // Type safety recommendations
753
- if (ext.match(/\.(ts|tsx)$/) && fileContent.match(/:\s*any\b/)) {
754
- results.recommendations.push({
755
- type: 'typescript',
756
- priority: 'medium',
757
- message: 'Avoid using "any" type when possible',
758
- action: 'Use specific types or unknown for better type safety'
759
- });
1140
+ } catch (error) {
1141
+ log('ERROR', 'Go validation error', { error: error.message });
1142
+ results.go = { available: false, error: error.message };
1143
+ }
760
1144
  }
761
1145
 
762
- // Testing recommendations
763
- if (!filePath.match(/\.(test|spec)\./)) {
764
- results.recommendations.push({
765
- type: 'testing',
766
- priority: 'medium',
767
- message: 'Consider writing tests for this module',
768
- action: 'Create corresponding test file to ensure code reliability'
769
- });
1146
+ // Java Validation (google-java-format)
1147
+ if (ext === '.java') {
1148
+ try {
1149
+ log('VALIDATING', 'Running Java validation (google-java-format)');
1150
+ let javaFormatterAvailable = false;
1151
+ try {
1152
+ execSync('google-java-format --version', { stdio: 'ignore', timeout: 5000 });
1153
+ javaFormatterAvailable = true;
1154
+ } catch {
1155
+ log('WARNING', 'google-java-format not available for Java validation');
1156
+ results.recommendations.push({
1157
+ type: 'dependency',
1158
+ priority: 'medium',
1159
+ message: 'Java formatter not installed - google-java-format unavailable',
1160
+ action: 'Install: brew install google-java-format or download from https://github.com/google/google-java-format',
1161
+ line: null
1162
+ });
1163
+ }
1164
+ results.java = {
1165
+ available: javaFormatterAvailable,
1166
+ format: null
1167
+ };
1168
+ if (javaFormatterAvailable) {
1169
+ try {
1170
+ const javaCmd = `google-java-format --dry-run --set-exit-if-changed "${filePath}"`;
1171
+ execSync(javaCmd, { encoding: 'utf-8', timeout: 10000 });
1172
+ results.java.format = { passed: true, formatted: true };
1173
+ log('SUCCESS', 'google-java-format check passed');
1174
+ } catch (error) {
1175
+ results.java.format = { passed: false, formatted: false };
1176
+ log('FAILED', 'Java file needs formatting');
1177
+ results.recommendations.push({
1178
+ type: 'java',
1179
+ priority: 'medium',
1180
+ message: 'Java file needs formatting',
1181
+ action: `Run: google-java-format -i "${filePath}"`,
1182
+ line: null
1183
+ });
1184
+ }
1185
+ }
1186
+ } catch (error) {
1187
+ log('ERROR', 'Java validation error', { error: error.message });
1188
+ results.java = { available: false, error: error.message };
1189
+ }
770
1190
  }
771
1191
 
772
- log('SUCCESS', `Generated ${results.recommendations.length} recommendations`);
1192
+ // C/C++ Validation (clang-format + cppcheck)
1193
+ if (['.c', '.cpp', '.cc', '.cxx', '.h', '.hpp', '.hxx'].includes(ext)) {
1194
+ try {
1195
+ log('VALIDATING', 'Running C/C++ validation (clang-format + cppcheck)');
1196
+ let clangFormatAvailable = false;
1197
+ try {
1198
+ execSync('clang-format --version', { stdio: 'ignore', timeout: 5000 });
1199
+ clangFormatAvailable = true;
1200
+ } catch {
1201
+ log('WARNING', 'clang-format not available');
1202
+ }
1203
+ let cppcheckAvailable = false;
1204
+ try {
1205
+ execSync('cppcheck --version', { stdio: 'ignore', timeout: 5000 });
1206
+ cppcheckAvailable = true;
1207
+ } catch {
1208
+ log('WARNING', 'cppcheck not available');
1209
+ }
1210
+ results.cpp = {
1211
+ available: clangFormatAvailable || cppcheckAvailable,
1212
+ clangFormat: null,
1213
+ cppcheck: null
1214
+ };
773
1215
 
774
- // ============================================================================
775
- // PHASE 7: Exit Code Determination
1216
+ // Add warning if no C/C++ tools available
1217
+ if (!clangFormatAvailable && !cppcheckAvailable) {
1218
+ results.recommendations.push({
1219
+ type: 'dependency',
1220
+ priority: 'medium',
1221
+ message: 'C/C++ validation tools not installed (clang-format, cppcheck)',
1222
+ action: 'Install: brew install clang-format cppcheck or apt-get install clang-format cppcheck',
1223
+ line: null
1224
+ });
1225
+ }
1226
+ if (clangFormatAvailable) {
1227
+ try {
1228
+ const clangCmd = `clang-format --dry-run -Werror "${filePath}"`;
1229
+ execSync(clangCmd, { encoding: 'utf-8', timeout: 10000 });
1230
+ results.cpp.clangFormat = { passed: true, formatted: true };
1231
+ log('SUCCESS', 'clang-format check passed');
1232
+ } catch (error) {
1233
+ results.cpp.clangFormat = { passed: false, formatted: false };
1234
+ log('FAILED', 'C/C++ file needs formatting');
1235
+ results.recommendations.push({
1236
+ type: 'cpp',
1237
+ priority: 'low',
1238
+ message: 'C/C++ file needs formatting',
1239
+ action: `Run: clang-format -i "${filePath}"`,
1240
+ line: null
1241
+ });
1242
+ }
1243
+ }
1244
+ if (cppcheckAvailable) {
1245
+ try {
1246
+ const cppcheckCmd = `cppcheck --enable=all --suppress=missingIncludeSystem "${filePath}" 2>&1`;
1247
+ const cppcheckOutput = execSync(cppcheckCmd, { encoding: 'utf-8', timeout: 10000 });
1248
+ const lines = cppcheckOutput.split('\n').filter(line => line.includes(':'));
1249
+ const errors = lines.filter(l => l.includes(':error:'));
1250
+ const warnings = lines.filter(l => l.includes(':warning:') || l.includes(':style:'));
1251
+ results.cpp.cppcheck = {
1252
+ passed: errors.length === 0,
1253
+ errorCount: errors.length,
1254
+ warningCount: warnings.length,
1255
+ issues: lines
1256
+ };
1257
+ log('SUCCESS', `cppcheck complete (${errors.length} errors, ${warnings.length} warnings)`);
1258
+ errors.slice(0, 3).forEach(err => {
1259
+ results.recommendations.push({
1260
+ type: 'cpp',
1261
+ priority: 'critical',
1262
+ message: `cppcheck error: ${err}`,
1263
+ action: 'Fix static analysis error',
1264
+ line: null
1265
+ });
1266
+ });
1267
+ } catch (error) {
1268
+ const output = error.stdout || error.stderr || '';
1269
+ const lines = output.split('\n').filter(line => line.includes(':'));
1270
+ const errors = lines.filter(l => l.includes(':error:'));
1271
+ results.cpp.cppcheck = {
1272
+ passed: errors.length === 0,
1273
+ errorCount: errors.length,
1274
+ issues: lines
1275
+ };
1276
+ if (errors.length > 0) {
1277
+ log('FAILED', `cppcheck found ${errors.length} errors`);
1278
+ }
1279
+ }
1280
+ }
1281
+ } catch (error) {
1282
+ log('ERROR', 'C/C++ validation error', { error: error.message });
1283
+ results.cpp = { available: false, error: error.message };
1284
+ }
1285
+ }
1286
+ // Summary and Exit
776
1287
  // ============================================================================
777
1288
 
1289
+ // Calculate overall status
1290
+ const hasCriticalIssues = results.recommendations.some(r => r.priority === 'critical');
1291
+ const hasHighIssues = results.recommendations.some(r => r.priority === 'high');
1292
+ const hasTypeScriptErrors = results.typescript && !results.typescript.passed;
1293
+ const hasRustErrors = results.rust && !results.rust.passed && !results.rust.skipped;
1294
+ const hasESLintErrors = results.eslint && results.eslint.errorCount > 0;
1295
+
1296
+ let overallStatus = 'SUCCESS';
778
1297
  let exitCode = 0;
779
- let finalStatus = 'SUCCESS';
780
-
781
- // Check for critical complexity issues
782
- const hasComplexityIssue = results.recommendations.find(r => r.type === 'complexity');
783
-
784
- // Check for bash validator issues
785
- const hasBashValidatorError = results.bashValidators && results.bashValidators.errors > 0;
786
- const hasBashValidatorWarning = results.bashValidators && results.bashValidators.warnings > 0;
787
-
788
- // Check for SQL injection issues
789
- const hasSqlInjectionCritical = results.sqlInjection && !results.sqlInjection.passed &&
790
- results.sqlInjection.issues.some(issue => issue.severity === 'critical');
791
-
792
- if (hasSqlInjectionCritical) {
793
- exitCode = 12;
794
- finalStatus = 'SQL_INJECTION_CRITICAL';
795
- } else if (hasBashValidatorError) {
796
- exitCode = 9;
797
- finalStatus = 'BASH_VALIDATOR_ERROR';
798
- } else if (results.rootWarning) {
1298
+
1299
+ if (hasCriticalIssues) {
1300
+ overallStatus = 'CRITICAL_ISSUES';
1301
+ exitCode = 2;
1302
+ } else if (hasTypeScriptErrors && results.typescript.severity === 'SYNTAX_ERROR') {
1303
+ overallStatus = 'SYNTAX_ERROR';
799
1304
  exitCode = 2;
800
- finalStatus = 'ROOT_WARNING';
801
- } else if (results.tddViolation) {
802
- exitCode = 3;
803
- finalStatus = 'TDD_VIOLATION';
804
- } else if (hasBashValidatorWarning) {
805
- exitCode = 10;
806
- finalStatus = 'BASH_VALIDATOR_WARNING';
807
- } else if (hasComplexityIssue && hasComplexityIssue.priority === 'critical') {
808
- exitCode = 7;
809
- finalStatus = 'COMPLEXITY_CRITICAL';
810
- } else if (hasComplexityIssue && hasComplexityIssue.priority === 'medium') {
811
- exitCode = 8;
812
- finalStatus = 'COMPLEXITY_WARNING';
813
- } else if (results.rustQuality) {
814
- exitCode = 5;
815
- finalStatus = 'RUST_QUALITY';
816
- } else if (results.prettier && !results.prettier.passed) {
817
- exitCode = 6;
818
- finalStatus = 'LINT_ISSUES';
819
- } else if (results.typescript && !results.typescript.passed) {
820
- exitCode = 1;
821
- finalStatus = 'TYPE_WARNING';
1305
+ } else if (hasTypeScriptErrors) {
1306
+ overallStatus = 'TYPE_ERRORS';
1307
+ exitCode = 2; // Now blocking for all TS errors
1308
+ } else if (hasRustErrors && results.rust.severity === 'SYNTAX_ERROR') {
1309
+ overallStatus = 'RUST_SYNTAX_ERROR';
1310
+ exitCode = 2;
1311
+ } else if (hasRustErrors && results.rust.severity === 'BORROW_ERROR') {
1312
+ overallStatus = 'RUST_BORROW_ERROR';
1313
+ exitCode = 2; // Borrow checker errors are critical
1314
+ } else if (hasRustErrors) {
1315
+ overallStatus = 'RUST_ERRORS';
1316
+ exitCode = 2; // Now blocking for all Rust errors
1317
+ } else if (hasHighIssues || hasESLintErrors) {
1318
+ overallStatus = 'LINT_ISSUES';
1319
+ exitCode = 0; // Non-blocking
822
1320
  } else if (results.recommendations.length > 0) {
823
- finalStatus = 'IMPROVEMENTS_SUGGESTED';
1321
+ overallStatus = 'IMPROVEMENTS_SUGGESTED';
1322
+ exitCode = 0;
824
1323
  }
825
1324
 
826
- const finalResult = {
1325
+ // Log final summary
1326
+ log(overallStatus, 'Pipeline validation complete', {
827
1327
  typescript: results.typescript,
1328
+ rust: results.rust,
828
1329
  eslint: results.eslint,
829
1330
  prettier: results.prettier,
830
1331
  security: results.security,
831
1332
  metrics: results.metrics,
832
1333
  recommendationCount: results.recommendations.length,
833
1334
  topRecommendations: results.recommendations.slice(0, 3)
834
- };
1335
+ });
1336
+
1337
+ // Print user-friendly summary
1338
+ console.error('\n' + '='.repeat(80));
1339
+ console.error('Enhanced Post-Edit Pipeline Summary');
1340
+ console.error('='.repeat(80));
835
1341
 
836
- // Include structured data for feedback handlers
837
- if (results.sqlInjection) {
838
- finalResult.sqlInjection = results.sqlInjection;
1342
+ if (results.typescript) {
1343
+ console.error(`\nTypeScript: ${results.typescript.passed ? '✅ PASSED' : '❌ FAILED'}`);
1344
+ if (!results.typescript.passed) {
1345
+ console.error(` Errors: ${results.typescript.errorCount}`);
1346
+ }
839
1347
  }
840
- if (results.rootWarning) {
841
- finalResult.rootWarning = results.rootWarning;
1348
+
1349
+ if (results.rust) {
1350
+ if (results.rust.skipped) {
1351
+ console.error(`\nRust: ⏭️ SKIPPED (${results.rust.reason})`);
1352
+ } else {
1353
+ console.error(`\nRust: ${results.rust.passed ? '✅ PASSED' : '❌ FAILED'}`);
1354
+ if (!results.rust.passed) {
1355
+ console.error(` Errors: ${results.rust.errorCount}`);
1356
+ }
1357
+ }
842
1358
  }
843
- if (results.tddViolation) {
844
- finalResult.tddViolation = results.tddViolation;
1359
+
1360
+ if (results.eslint && results.eslint.available) {
1361
+ console.error(`ESLint: ${results.eslint.passed ? '✅ PASSED' : '❌ FAILED'}`);
1362
+ if (!results.eslint.passed) {
1363
+ console.error(` Errors: ${results.eslint.errorCount}, Warnings: ${results.eslint.warningCount}`);
1364
+ }
845
1365
  }
846
- if (results.bashValidators) {
847
- finalResult.bashValidators = results.bashValidators;
1366
+
1367
+ if (results.prettier && results.prettier.available) {
1368
+ console.error(`Prettier: ${results.prettier.passed ? '✅ PASSED' : '⚠️ NEEDS FORMATTING'}`);
848
1369
  }
849
- if (results.rustQuality) {
850
- finalResult.rustQuality = results.rustQuality;
1370
+
1371
+ console.error(`\nSecurity: ${results.security.passed ? '✅ NO ISSUES' : '❌ ISSUES FOUND'}`);
1372
+ if (!results.security.passed) {
1373
+ console.error(` Issues: ${results.security.issueCount}`);
851
1374
  }
852
- if (results.complexityAnalysis) {
853
- finalResult.complexityAnalysis = results.complexityAnalysis;
1375
+
1376
+ console.error(`\nCode Metrics:`);
1377
+ console.error(` Lines: ${results.metrics.lines}`);
1378
+ console.error(` Functions: ${results.metrics.functions}`);
1379
+ console.error(` Classes: ${results.metrics.classes}`);
1380
+ console.error(` Complexity: ${results.metrics.complexity.toUpperCase()}`);
1381
+
1382
+ if (results.recommendations.length > 0) {
1383
+ console.error(`\nTop Recommendations:`);
1384
+ results.recommendations.slice(0, 3).forEach((rec, i) => {
1385
+ const icon = rec.priority === 'critical' ? '🔴' : rec.priority === 'high' ? '🟠' : rec.priority === 'medium' ? '🟡' : '🔵';
1386
+ console.error(` ${icon} [${rec.type.toUpperCase()}] ${rec.message}`);
1387
+ console.error(` Action: ${rec.action}`);
1388
+ });
1389
+
1390
+ if (results.recommendations.length > 3) {
1391
+ console.error(` ... and ${results.recommendations.length - 3} more recommendations`);
1392
+ }
854
1393
  }
855
1394
 
856
- log(finalStatus, 'Pipeline validation complete', finalResult);
1395
+ console.error('='.repeat(80) + '\n');
857
1396
 
858
- process.exit(exitCode);
1397
+ process.exit(exitCode);