claude-flow-novice 2.18.26 → 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,59 +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>]
15
16
  *
16
17
  * ============================================================================
17
- * TYPESCRIPT ERROR DETECTION - WHAT'S CAUGHT vs NOT CAUGHT
18
+ * ERROR DETECTION - WHAT'S CAUGHT vs NOT CAUGHT
18
19
  * ============================================================================
19
20
  *
20
- * This pipeline runs: `npx tsc --noEmit --skipLibCheck ${filePath}`
21
- * It checks the EDITED FILE + its IMPORTS (transitively), but NOT files that
22
- * import the edited file.
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).
23
26
  *
24
27
  * ✅ CAUGHT (Blocks Edit):
25
- * ┌────────────────────┬─────────────┬─────────────────────────────────────────┐
26
- * │ Error Type │ Code │ Example
27
- * ├────────────────────┼─────────────┼─────────────────────────────────────────┤
28
- * │ Missing module │ TS2307 │ import { x } from './nonexistent'
29
- * │ Missing export │ TS2305 import { nonExistent } from './exists'
30
- * │ Typo in export │ TS2724import { Uzer } from './user'
31
- * │ Syntax errors TS1005,1128Missing ), unexpected token
32
- * │ Unused imports TS6133,6196import { unused } from...
33
- * │ Type mismatches TS2322,2345Wrong type assignment
34
- * │ Missing properties TS2339,2353obj.nonExistentProp
35
- * └────────────────────┴─────────────┴─────────────────────────────────────────┘
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 errorsTS1005 / 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
+ * └────────────────────┴──────────────────┴─────────────────────────────────────┘
36
40
  *
37
- * ❌ NOT CAUGHT (Single-File Limitation):
41
+ * ❌ NOT CAUGHT (TypeScript Single-File Limitation):
38
42
  * ┌─────────────────────────────────┬────────────────────────────────────────────┐
39
43
  * │ Scenario │ Why │
40
44
  * ├─────────────────────────────────┼────────────────────────────────────────────┤
41
- * │ Cross-file type changes │ Editing types.ts won't catch breaks in │
45
+ * │ Cross-file type changes (TS) │ Editing types.ts won't catch breaks in │
42
46
  * │ │ other.ts that imports it │
43
47
  * │ Deleted exports used elsewhere │ Consumers aren't checked until edited │
44
- * │ Renamed functions/types │ Same - callers won't be validated │
45
48
  * └─────────────────────────────────┴────────────────────────────────────────────┘
46
49
  *
47
- * Example of NOT CAUGHT:
48
- * You edit: src/types.ts (remove UserType export)
49
- * Pipeline: ✅ passes (types.ts is valid)
50
- * Reality: src/user.ts imports UserType → BROKEN but not checked
50
+ * Note: Rust DOES catch cross-file breaks because cargo check compiles the
51
+ * entire crate. TypeScript only checks the single file + its imports.
51
52
  *
52
- * To catch everything: Run `npx tsc --noEmit` project-wide periodically.
53
+ * To catch everything in TS: Run `npx tsc --noEmit` project-wide periodically.
53
54
  * ============================================================================
54
55
  */
55
56
 
56
- import { spawnSync } from 'child_process';
57
+ import { execSync } from 'child_process';
57
58
  import { existsSync, readFileSync, appendFileSync, mkdirSync } from 'fs';
58
59
  import { dirname, extname, resolve } from 'path';
59
60
 
@@ -102,11 +103,11 @@ if (!existsSync(filePath)) {
102
103
  // Read file content for analysis
103
104
  const fileContent = readFileSync(filePath, 'utf-8');
104
105
  const ext = extname(filePath);
105
- const baseName = filePath.replace(ext, '').split('/').pop();
106
106
 
107
107
  // Initialize results object
108
108
  const results = {
109
109
  typescript: null,
110
+ rust: null,
110
111
  eslint: null,
111
112
  prettier: null,
112
113
  security: null,
@@ -114,784 +115,1283 @@ const results = {
114
115
  recommendations: []
115
116
  };
116
117
 
117
- // [Remaining TypeScript, ESLint, and Prettier validation code remains the same]
118
-
119
118
  // ============================================================================
120
- // PHASE 2: Security Analysis
119
+ // PHASE 1: TypeScript Validation
121
120
  // ============================================================================
122
121
 
123
- log('VALIDATING', 'Running security analysis');
122
+ if (['.ts', '.tsx'].includes(ext)) {
123
+ try {
124
+ log('VALIDATING', 'Running TypeScript validation');
124
125
 
125
- try {
126
- // Primary scanner method: security scanner script
127
- const securityScanProcess = spawnSync('bash', [
128
- '.claude/skills/hook-pipeline/security-scanner.sh',
129
- filePath
130
- ], {
131
- encoding: 'utf-8',
132
- timeout: 10000
133
- });
126
+ // Use tsc to check only this file
127
+ const cmd = `npx tsc --noEmit --skipLibCheck ${filePath}`;
128
+ execSync(cmd, { stdio: 'pipe', encoding: 'utf-8' });
134
129
 
135
- const securityScanOutput = securityScanProcess.stdout || '{}';
136
- const exitCode = securityScanProcess.status;
130
+ results.typescript = {
131
+ passed: true,
132
+ errors: []
133
+ };
134
+ log('SUCCESS', 'TypeScript validation passed');
137
135
 
138
- log('DEBUG', 'Security scanner output', {
139
- stdout: securityScanOutput,
140
- stderr: securityScanProcess.stderr,
141
- exitCode: exitCode
142
- });
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);
143
157
 
144
- try {
145
- const securityScanResults = JSON.parse(securityScanOutput);
146
-
147
- results.security = {
148
- passed: securityScanResults.passed,
149
- confidence: securityScanResults.confidence || 0,
150
- issues: Array.isArray(securityScanResults.vulnerabilities)
151
- ? securityScanResults.vulnerabilities
152
- : JSON.parse(securityScanResults.vulnerabilities || '[]'),
153
- details: securityScanOutput
154
- };
158
+ const severity = errorTypes.syntaxError > 0 ? 'SYNTAX_ERROR' :
159
+ lines.length > 5 ? 'LINT_ISSUES' : 'TYPE_WARNING';
155
160
 
156
- if (results.security.issues.length > 0) {
157
- log('SECURITY_WARNING', `Security scanner detected ${results.security.issues.length} vulnerabilities`, {
158
- confidence: results.security.confidence,
159
- 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)
160
173
  });
161
174
 
162
- // Transform scanner issues into recommendations
163
- results.security.issues.slice(0, 3).forEach(vuln => {
175
+ // Add recommendations based on error types
176
+ if (errorTypes.syntaxError > 0) {
164
177
  results.recommendations.push({
165
- type: 'security',
178
+ type: 'typescript',
166
179
  priority: 'critical',
167
- message: `Security vulnerability: ${vuln}`,
168
- action: `Review and remediate ${vuln} vulnerability`
180
+ message: 'Fix syntax errors before proceeding',
181
+ action: 'Review and fix TypeScript syntax issues'
169
182
  });
170
- });
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
+ }
171
196
 
172
- // Add general security warning
173
- results.recommendations.push({
174
- type: 'security',
175
- priority: 'critical',
176
- message: 'Security vulnerabilities detected by security scanner',
177
- action: 'Conduct thorough security review and address all vulnerabilities'
178
- });
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' };
179
221
  } else {
180
- 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');
181
231
  }
182
- } catch (parseError) {
183
- log('ERROR', 'Failed to parse security scanner output', {
184
- parseError: parseError.message,
185
- output: securityScanOutput
186
- });
187
232
 
188
- // Fallback vulnerability detection (minimal built-in checks)
189
- const builtinChecks = [
190
- {
191
- pattern: /eval\(/,
192
- vulnerability: 'POTENTIAL_RCE',
193
- severity: 'critical'
194
- },
195
- {
196
- pattern: /innerHTML\s*=/,
197
- vulnerability: 'XSS_POTENTIAL',
198
- severity: 'high'
199
- },
200
- {
201
- pattern: /(password|secret|token|api[-_]?key|anthropic|openai|openrouter|kimi|npm[-_]?token|zai|z[-_]ai).*=.*['"]?[^'"\s]{20,}['"]?/i,
202
- vulnerability: 'HARDCODED_SECRET',
203
- severity: 'critical'
204
- }
205
- ];
206
-
207
- const foundVulnerabilities = builtinChecks
208
- .filter(check => check.pattern.test(fileContent))
209
- .map(check => ({
210
- type: check.vulnerability,
211
- severity: check.severity
212
- }));
213
-
214
- results.security = {
215
- passed: foundVulnerabilities.length === 0,
216
- confidence: 50,
217
- issues: foundVulnerabilities,
218
- details: 'Fallback vulnerability detection'
219
- };
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
+ };
220
267
 
221
- if (foundVulnerabilities.length > 0) {
222
- log('SECURITY_WARNING', 'Vulnerabilities detected by fallback method', {
223
- vulnerabilities: foundVulnerabilities
268
+ log(severity, `Rust errors detected: ${lines.length}`, {
269
+ errorCount: lines.length,
270
+ errorTypes,
271
+ errors: lines.slice(0, 5)
224
272
  });
225
273
 
226
- foundVulnerabilities.forEach(vuln => {
274
+ // Add recommendations based on error types
275
+ if (errorTypes.borrowCheck > 0) {
227
276
  results.recommendations.push({
228
- type: 'security',
229
- priority: vuln.severity === 'critical' ? 'critical' : 'high',
230
- message: `Potential ${vuln.type} vulnerability detected`,
231
- action: `Manually review code for ${vuln.type} vulnerability`
277
+ type: 'rust',
278
+ priority: 'critical',
279
+ message: 'Fix borrow checker errors',
280
+ action: 'Review ownership and borrowing rules'
232
281
  });
233
- });
282
+ } else if (errorTypes.lifetime > 0) {
283
+ results.recommendations.push({
284
+ type: 'rust',
285
+ priority: 'high',
286
+ message: 'Fix lifetime annotations',
287
+ action: 'Add or correct lifetime parameters'
288
+ });
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
+ }
234
297
  }
235
298
  }
236
- } catch (error) {
237
- log('CRITICAL_ERROR', 'Unexpected security scanning failure', {
238
- error: error.message,
239
- stack: error.stack
240
- });
241
-
242
- results.security = {
243
- passed: false,
244
- confidence: 0,
245
- issues: [],
246
- details: 'Complete security scanning failure'
247
- };
248
-
249
- results.recommendations.push({
250
- type: 'security',
251
- priority: 'critical',
252
- message: 'Security scanning infrastructure failure',
253
- action: 'Verify security scanning script and dependencies'
254
- });
299
+ } else {
300
+ log('SKIPPED', 'Rust validation skipped for non-Rust file');
255
301
  }
256
302
 
257
303
  // ============================================================================
258
- // PHASE 2.5: Bash Validator Integration
304
+ // PHASE 1: ESLint Integration
259
305
  // ============================================================================
260
306
 
261
- log('VALIDATING', 'Running bash validators');
262
-
263
- // Validator mapping by file extension
264
- const validatorsByExtension = {
265
- '.sh': [
266
- 'bash-pipe-safety.sh',
267
- 'bash-dependency-checker.sh',
268
- 'enforce-lf.sh'
269
- ],
270
- '.bash': [
271
- 'bash-pipe-safety.sh',
272
- 'bash-dependency-checker.sh',
273
- 'enforce-lf.sh'
274
- ],
275
- '.py': [
276
- 'python-subprocess-safety.py',
277
- 'python-async-safety.py',
278
- 'python-import-checker.py',
279
- 'enforce-lf.sh'
280
- ],
281
- '.js': [
282
- 'js-promise-safety.sh',
283
- 'enforce-lf.sh'
284
- ],
285
- '.ts': [
286
- 'js-promise-safety.sh',
287
- 'enforce-lf.sh'
288
- ],
289
- '.jsx': [
290
- 'js-promise-safety.sh',
291
- 'enforce-lf.sh'
292
- ],
293
- '.tsx': [
294
- 'js-promise-safety.sh',
295
- 'enforce-lf.sh'
296
- ],
297
- '.rs': [
298
- 'rust-command-safety.sh',
299
- 'rust-future-safety.sh',
300
- 'rust-dependency-checker.sh',
301
- 'enforce-lf.sh'
302
- ]
303
- };
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
+ }
318
+
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: [] };
304
330
 
305
- // Helper function to run a single validator
306
- function runValidator(validatorName, targetFile) {
307
- const validatorPath = `.claude/skills/hook-pipeline/${validatorName}`;
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
+ }
308
393
 
309
- log('DEBUG', `Executing validator: ${validatorName}`, { targetFile });
394
+ // ============================================================================
395
+ // PHASE 1: Prettier Integration
396
+ // ============================================================================
310
397
 
398
+ if (['.js', '.jsx', '.ts', '.tsx', '.json', '.css', '.html'].includes(ext)) {
311
399
  try {
312
- // Determine interpreter based on file extension
313
- const isPython = validatorName.endsWith('.py');
314
- const interpreter = isPython ? 'python3' : 'bash';
315
-
316
- const result = spawnSync(interpreter, [validatorPath, targetFile], {
317
- encoding: 'utf-8',
318
- timeout: 5000,
319
- cwd: process.cwd()
320
- });
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
+ }
321
409
 
322
- const exitCode = result.status;
323
- const stdout = (result.stdout || '').trim();
324
- 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
+ });
325
419
 
326
- log('DEBUG', `Validator ${validatorName} completed`, {
327
- exitCode,
328
- stdout: stdout.substring(0, 200), // Truncate for logging
329
- stderr: stderr.substring(0, 200)
330
- });
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');
331
435
 
332
- // Exit code convention:
333
- // 0 = pass (no issues)
334
- // 1 = error (blocking issue)
335
- // 2 = warning (non-blocking issue)
336
- return {
337
- validator: validatorName,
338
- exitCode,
339
- passed: exitCode === 0,
340
- isBlocking: exitCode === 1,
341
- isWarning: exitCode === 2,
342
- message: stderr || stdout || 'Validator passed',
343
- stdout,
344
- stderr
345
- };
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
+ }
346
444
  } catch (error) {
347
- log('ERROR', `Validator ${validatorName} execution failed`, {
348
- error: error.message,
349
- stack: error.stack
350
- });
351
-
352
- return {
353
- validator: validatorName,
354
- exitCode: -1,
355
- passed: false,
356
- isBlocking: false,
357
- isWarning: true,
358
- message: `Validator execution failed: ${error.message}`,
359
- error: error.message
360
- };
445
+ log('ERROR', 'Prettier execution failed', { error: error.message });
446
+ results.prettier = { available: false, error: error.message };
361
447
  }
362
448
  }
363
449
 
364
- // Run validators for applicable file types
365
- const applicableValidators = validatorsByExtension[ext] || [];
450
+ // ============================================================================
451
+ // PHASE 2: Security Analysis
452
+ // ============================================================================
366
453
 
367
- if (applicableValidators.length > 0) {
368
- log('INFO', `Running ${applicableValidators.length} bash validators for ${ext} file`);
454
+ log('VALIDATING', 'Running security analysis');
369
455
 
370
- // Sequential execution of validators
371
- const validatorResults = applicableValidators.map(validator =>
372
- runValidator(validator, filePath)
373
- );
456
+ const securityIssues = [];
374
457
 
375
- // Process validator results
376
- validatorResults.forEach(result => {
377
- if (result.isBlocking) {
378
- // Blocking error (exit code 1)
379
- log('VALIDATOR_ERROR', `Blocking issue detected by ${result.validator}`, {
380
- message: result.message
381
- });
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
+ }
382
467
 
383
- results.recommendations.push({
384
- type: 'bash-validator',
385
- priority: 'critical',
386
- message: `${result.validator}: ${result.message}`,
387
- action: 'Fix blocking issue before proceeding'
388
- });
389
- } else if (result.isWarning) {
390
- // Warning (exit code 2)
391
- log('VALIDATOR_WARNING', `Warning from ${result.validator}`, {
392
- message: result.message
393
- });
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
+ }
394
477
 
395
- results.recommendations.push({
396
- type: 'bash-safety',
397
- priority: 'medium',
398
- message: `${result.validator}: ${result.message}`,
399
- action: 'Review recommendations and consider fixing'
400
- });
401
- } else if (result.passed) {
402
- // Pass (exit code 0)
403
- log('SUCCESS', `Validator ${result.validator} passed`);
404
- }
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'
405
495
  });
496
+ }
497
+
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
+ }
406
517
 
407
- // Store validator results for exit code determination
408
- results.bashValidators = {
409
- executed: validatorResults.length,
410
- passed: validatorResults.filter(r => r.passed).length,
411
- warnings: validatorResults.filter(r => r.isWarning).length,
412
- errors: validatorResults.filter(r => r.isBlocking).length,
413
- results: validatorResults
414
- };
415
-
416
- log('SUCCESS', `Bash validators completed`, {
417
- executed: results.bashValidators.executed,
418
- passed: results.bashValidators.passed,
419
- warnings: results.bashValidators.warnings,
420
- errors: results.bashValidators.errors
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'
421
535
  });
422
536
  } else {
423
- log('DEBUG', `No bash validators configured for ${ext} files`);
537
+ log('SUCCESS', 'No security issues detected');
424
538
  }
425
539
 
426
540
  // ============================================================================
427
- // PHASE 2.6: SQL Injection Detection
541
+ // PHASE 3: Code Metrics
428
542
  // ============================================================================
429
543
 
430
- if (ext.match(/\.(sql|ts|js|py)$/)) {
431
- log('VALIDATING', 'Running SQL injection detection');
544
+ log('VALIDATING', 'Calculating code metrics');
432
545
 
433
- const sqlInjectionPatterns = [
434
- // String concatenation in queries
435
- { pattern: /['"`]\s*\+\s*\w+\s*\+\s*['"`].*(?:SELECT|INSERT|UPDATE|DELETE|WHERE)/i, risk: 'STRING_CONCATENATION', severity: 'critical' },
436
- { 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
+ }
437
560
 
438
- // Template literals with variables in SQL
439
- { 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
+ };
440
569
 
441
- // f-strings in Python SQL
442
- { 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
+ });
443
576
 
444
- // .format() in Python SQL
445
- { pattern: /['"].*(?:SELECT|INSERT|UPDATE|DELETE).*['"]\.format\(/i, risk: 'FORMAT_SQL_INJECTION', severity: 'critical' },
577
+ // ============================================================================
578
+ // PHASE 3: Recommendations Engine
579
+ // ============================================================================
446
580
 
447
- // Raw user input in query
448
- { 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');
449
582
 
450
- // execute() with string concatenation
451
- { pattern: /\.execute\s*\(\s*['"`].*\+/i, risk: 'EXECUTE_CONCATENATION', severity: 'critical' },
452
- { pattern: /\.execute\s*\(\s*f['"`]/i, risk: 'EXECUTE_FSTRING', severity: 'critical' },
453
- ];
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
+ }
454
592
 
455
- const sqlInjectionIssues = sqlInjectionPatterns
456
- .filter(check => check.pattern.test(fileContent))
457
- .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
+ }
458
602
 
459
- if (sqlInjectionIssues.length > 0) {
460
- log('SQL_INJECTION_WARNING', `Found ${sqlInjectionIssues.length} potential SQL injection risks`, {
461
- issues: sqlInjectionIssues
462
- });
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
+ }
463
611
 
464
- results.sqlInjection = {
465
- passed: false,
466
- issues: sqlInjectionIssues
467
- };
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
+ }
468
622
 
469
- sqlInjectionIssues.forEach(issue => {
470
- results.recommendations.push({
471
- type: 'sql-injection',
472
- priority: issue.severity,
473
- message: `Potential SQL injection: ${issue.type}`,
474
- action: 'Use parameterized queries ($1, $2) or prepared statements instead of string concatenation'
475
- });
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'
476
629
  });
477
- } else {
478
- results.sqlInjection = { passed: true, issues: [] };
479
- log('SUCCESS', 'No SQL injection risks detected');
480
630
  }
481
631
  }
482
632
 
483
- // ============================================================================
484
- // PHASE 3: Root Directory Detection
485
- // ============================================================================
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
+ }
486
642
 
487
- 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
+ }
488
652
 
489
- const isRootFile = dirname(resolve(filePath)) === resolve('.');
490
- if (isRootFile && !filePath.match(/^(package\.json|tsconfig\.json|\.gitignore|\.env.*|README\.md|LICENSE|CLAUDE\.md)$/)) {
491
- // Suggest appropriate location based on file type
492
- const suggestions = [];
493
- if (ext.match(/\.(js|ts|jsx|tsx)$/)) {
494
- suggestions.push({ location: `src/${filePath}`, reason: 'Source files belong in src/' });
495
- }
496
- if (ext.match(/\.(test|spec)\.(js|ts|jsx|tsx)$/)) {
497
- suggestions.push({ location: `tests/${filePath}`, reason: 'Test files belong in tests/' });
498
- }
499
- if (ext === '.md' && !filePath.match(/^(README|CLAUDE)\.md$/)) {
500
- suggestions.push({ location: `docs/${filePath}`, reason: 'Documentation belongs in docs/' });
501
- }
502
- if (ext === '.json' && !filePath.match(/^package\.json$/)) {
503
- suggestions.push({ location: `config/${filePath}`, reason: 'Config files belong in config/' });
504
- }
505
- if (ext === '.sh') {
506
- suggestions.push({ location: `scripts/${filePath}`, reason: 'Scripts belong in scripts/' });
507
- }
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
+ }
508
662
 
509
- if (suggestions.length > 0) {
510
- log('ROOT_WARNING', 'File in root directory - should be organized', {
511
- file: filePath,
512
- suggestions
513
- });
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]);
514
666
 
515
- results.recommendations.push({
516
- type: 'organization',
517
- priority: 'high',
518
- message: `File "${filePath}" should not be in root directory`,
519
- action: `Move to: ${suggestions[0].location}`,
520
- suggestions
521
- });
667
+ // Limit to top 10 recommendations
668
+ results.recommendations = results.recommendations.slice(0, 10);
522
669
 
523
- // Store for handler processing
524
- results.rootWarning = { suggestions };
525
- }
526
- }
670
+ log('SUCCESS', `Generated ${results.recommendations.length} recommendations`);
527
671
 
528
672
  // ============================================================================
529
- // PHASE 4: TDD Violation Detection
673
+ // MULTI-LANGUAGE VALIDATION
530
674
  // ============================================================================
531
675
 
532
- if (ext.match(/\.(js|ts|jsx|tsx|py|go|rs)$/) && !filePath.match(/\.(test|spec)\./)) {
533
- 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
+ }
534
696
 
535
- const testPatterns = {
536
- js: [`${dirname(filePath)}/${baseName}.test.js`, `tests/${baseName}.test.js`],
537
- ts: [`${dirname(filePath)}/${baseName}.test.ts`, `tests/${baseName}.test.ts`],
538
- py: [`${dirname(filePath)}/test_${baseName}.py`, `tests/test_${baseName}.py`],
539
- go: [`${dirname(filePath)}/${baseName}_test.go`],
540
- rs: null // Rust uses inline tests
541
- };
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
+ });
542
709
 
543
- const langKey = ext.replace('.', '');
544
- const patterns = testPatterns[langKey];
710
+ const configPath = '/tmp/tsconfig.jsdoc.json';
711
+ require('fs').writeFileSync(configPath, tempConfig);
545
712
 
546
- if (patterns) {
547
- 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 });
548
715
 
549
- if (!hasTest) {
550
- log('TDD_VIOLATION', 'No test file found', {
551
- file: filePath,
552
- expectedLocations: patterns
553
- });
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');
554
724
 
725
+ if (!results.javascript.hasJSDoc) {
555
726
  results.recommendations.push({
556
- type: 'testing',
557
- priority: 'high',
558
- message: 'No test file found for this module',
559
- 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
560
732
  });
733
+ }
561
734
 
562
- results.tddViolation = {
563
- hasTests: false,
564
- testFile: patterns[0],
565
- 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
566
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
+ });
567
773
  }
568
774
  }
569
775
  }
570
776
 
571
- // ============================================================================
572
- // PHASE 5: Code Metrics and Complexity Analysis
573
- // ============================================================================
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
+ }
574
790
 
575
- 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
+ }
576
799
 
577
- const lines = fileContent.split('\n').length;
578
- const functions = (fileContent.match(/function\s+\w+|const\s+\w+\s*=\s*\(/g) || []).length;
579
- const classes = (fileContent.match(/class\s+\w+/g) || []).length;
580
- const todos = (fileContent.match(/\/\/\s*TODO/gi) || []).length;
581
- const fixmes = (fileContent.match(/\/\/\s*FIXME/gi) || []).length;
800
+ results.python = {
801
+ available: pylintAvailable || blackAvailable,
802
+ pylint: null,
803
+ black: null
804
+ };
582
805
 
583
- results.metrics = {
584
- lines,
585
- functions,
586
- classes,
587
- todos,
588
- fixmes,
589
- complexity: lines > 300 ? 'high' : lines > 100 ? 'medium' : 'low'
590
- };
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
+ }
816
+
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
+ });
591
846
 
592
- log('SUCCESS', 'Code metrics calculated', results.metrics);
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
+ }
593
866
 
594
- // ============================================================================
595
- // PHASE 5.1: Cyclomatic Complexity Analysis
596
- // ============================================================================
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
+ };
877
+
878
+ log('SUCCESS', 'black formatting check passed');
879
+
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
+ }
597
897
 
598
- log('VALIDATING', 'Analyzing cyclomatic complexity');
898
+ } catch (error) {
899
+ log('ERROR', 'Python validation error', { error: error.message });
900
+ results.python = { available: false, error: error.message };
901
+ }
902
+ }
599
903
 
600
- // Only analyze files >200 lines to reduce overhead
601
- if (lines > 200 && ext.match(/\.(sh|js|ts|jsx|tsx|py)$/)) {
904
+ // Rust Validation (cargo clippy + rustfmt)
905
+ if (ext === '.rs') {
602
906
  try {
603
- // Use simple-complexity.sh for bash scripts
604
- if (ext === '.sh') {
605
- const complexityResult = spawnSync('bash', [
606
- 'scripts/simple-complexity.sh',
607
- filePath
608
- ], {
609
- encoding: 'utf-8',
610
- 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
611
922
  });
923
+ }
612
924
 
613
- if (complexityResult.status === 0) {
614
- const output = complexityResult.stdout;
615
- const complexityMatch = output.match(/Total Complexity:\s*(\d+)/);
925
+ results.rust = {
926
+ available: cargoAvailable,
927
+ clippy: null,
928
+ rustfmt: null
929
+ };
616
930
 
617
- if (complexityMatch) {
618
- const complexity = parseInt(complexityMatch[1], 10);
619
- 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
+ }
620
940
 
621
- 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
+ }
622
949
 
623
- // Warning threshold: 30
624
- if (complexity >= 30 && complexity < 40) {
625
- log('COMPLEXITY_WARNING', `Moderate complexity detected: ${complexity}`, {
626
- threshold: 30,
627
- complexity
628
- });
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');
982
+
983
+ results.rust.clippy = {
984
+ passed: messages.length === 0,
985
+ warningCount: messages.length,
986
+ warnings: messages
987
+ };
629
988
 
989
+ log('SUCCESS', `cargo clippy complete (${messages.length} warnings)`);
990
+
991
+ // Add recommendations for clippy warnings
992
+ messages.slice(0, 3).forEach(msg => {
630
993
  results.recommendations.push({
631
- type: 'complexity',
994
+ type: 'rust',
632
995
  priority: 'medium',
633
- message: `Cyclomatic complexity is ${complexity} (threshold: 30)`,
634
- action: 'Consider refactoring to reduce complexity'
635
- });
636
- }
637
-
638
- // Critical threshold: 40 - invoke lizard for detailed analysis
639
- if (complexity >= 40) {
640
- log('COMPLEXITY_CRITICAL', `High complexity detected: ${complexity}, invoking lizard`, {
641
- threshold: 40,
642
- complexity
996
+ message: `clippy: ${msg.message.message}`,
997
+ action: msg.message.rendered || 'Review clippy suggestion',
998
+ line: msg.message.spans?.[0]?.line_start || null
643
999
  });
1000
+ });
644
1001
 
645
- // Check if lizard is available
646
- const lizardCheck = spawnSync('which', ['lizard'], { encoding: 'utf-8' });
647
-
648
- if (lizardCheck.status === 0) {
649
- // Run lizard for detailed analysis
650
- const lizardResult = spawnSync('lizard', [
651
- filePath,
652
- '-C', '15' // Show functions with complexity >15
653
- ], {
654
- encoding: 'utf-8',
655
- timeout: 10000
656
- });
657
-
658
- if (lizardResult.status === 0) {
659
- const lizardOutput = lizardResult.stdout;
660
-
661
- log('LIZARD_ANALYSIS', 'Detailed complexity analysis', {
662
- output: lizardOutput
663
- });
664
-
665
- results.complexityAnalysis = {
666
- tool: 'lizard',
667
- complexity,
668
- detailedReport: lizardOutput
669
- };
670
-
671
- results.recommendations.push({
672
- type: 'complexity',
673
- priority: 'critical',
674
- message: `Critical complexity level: ${complexity} (threshold: 40)`,
675
- action: 'Refactor immediately. Run cyclomatic-complexity-reducer agent',
676
- details: lizardOutput
677
- });
678
- } else {
679
- log('WARN', 'Lizard analysis failed', {
680
- stderr: lizardResult.stderr
681
- });
682
- }
683
- } else {
684
- log('WARN', 'Lizard not installed, skipping detailed analysis');
685
-
686
- results.recommendations.push({
687
- type: 'complexity',
688
- priority: 'critical',
689
- message: `Critical complexity level: ${complexity} (threshold: 40)`,
690
- action: 'Refactor immediately. Install lizard: ./scripts/install-lizard.sh'
691
- });
692
- }
693
- }
1002
+ } catch (error) {
1003
+ log('SKIP', 'cargo clippy requires Cargo project context');
1004
+ results.rust.clippy = { passed: true, note: 'Requires Cargo project' };
694
1005
  }
695
- } else {
696
- log('WARN', 'Complexity analysis failed', {
697
- stderr: complexityResult.stderr
698
- });
699
1006
  }
700
- }
701
- // For TypeScript/JavaScript, use lizard directly if available
702
- else if (ext.match(/\.(js|ts|jsx|tsx)$/)) {
703
- const lizardCheck = spawnSync('which', ['lizard'], { encoding: 'utf-8' });
704
-
705
- if (lizardCheck.status === 0) {
706
- const lizardResult = spawnSync('lizard', [
707
- filePath,
708
- '--json'
709
- ], {
710
- encoding: 'utf-8',
711
- timeout: 10000
712
- });
713
1007
 
714
- if (lizardResult.status === 0) {
715
- try {
716
- const lizardData = JSON.parse(lizardResult.stdout);
717
-
718
- // Calculate average complexity
719
- let totalComplexity = 0;
720
- let functionCount = 0;
721
-
722
- if (lizardData.function_list) {
723
- lizardData.function_list.forEach(func => {
724
- totalComplexity += func.cyclomatic_complexity || 0;
725
- functionCount++;
726
- });
727
- }
728
-
729
- const avgComplexity = functionCount > 0 ? Math.round(totalComplexity / functionCount) : 0;
730
- results.metrics.cyclomaticComplexity = avgComplexity;
731
-
732
- if (avgComplexity >= 30) {
733
- log('COMPLEXITY_WARNING', `Average complexity: ${avgComplexity}`, {
734
- avgComplexity,
735
- functionCount
736
- });
737
-
738
- results.recommendations.push({
739
- type: 'complexity',
740
- priority: avgComplexity >= 40 ? 'critical' : 'medium',
741
- message: `Average cyclomatic complexity: ${avgComplexity}`,
742
- action: avgComplexity >= 40
743
- ? 'Critical: Refactor high-complexity functions immediately'
744
- : 'Consider refactoring complex functions'
745
- });
746
- }
747
- } catch (parseError) {
748
- log('WARN', 'Failed to parse lizard JSON output', {
749
- error: parseError.message
750
- });
751
- }
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
+ });
752
1036
  }
753
1037
  }
754
1038
  }
1039
+
755
1040
  } catch (error) {
756
- log('WARN', 'Complexity analysis error', {
757
- error: error.message
758
- });
1041
+ log('ERROR', 'Rust validation error', { error: error.message });
1042
+ results.rust = { available: false, error: error.message };
759
1043
  }
760
1044
  }
761
1045
 
762
- // Check for Rust-specific quality issues
763
- if (ext === '.rs') {
764
- log('VALIDATING', 'Running Rust quality checks');
765
-
766
- const rustIssues = [];
767
- if (fileContent.match(/println!\(/)) rustIssues.push('debug_println');
768
- if (fileContent.match(/unwrap\(\)/)) rustIssues.push('unwrap_usage');
769
- if (fileContent.match(/panic!\(/)) rustIssues.push('panic_usage');
770
-
771
- if (rustIssues.length > 0) {
772
- 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
+ }
773
1066
 
774
- results.recommendations.push({
775
- type: 'rust',
776
- priority: 'medium',
777
- message: 'Rust quality issues detected',
778
- action: 'Run: cargo fmt && cargo clippy --fix --allow-dirty'
779
- });
1067
+ results.go = {
1068
+ available: goAvailable,
1069
+ gofmt: null,
1070
+ govet: null
1071
+ };
780
1072
 
781
- results.rustQuality = { issues: rustIssues };
782
- }
783
- }
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
+ }
784
1098
 
785
- // ============================================================================
786
- // PHASE 6: Final Recommendations
787
- // ============================================================================
1099
+ } catch (error) {
1100
+ results.go.gofmt = { passed: false, error: error.message };
1101
+ }
788
1102
 
789
- 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
+ }
790
1139
 
791
- // Type safety recommendations
792
- if (ext.match(/\.(ts|tsx)$/) && fileContent.match(/:\s*any\b/)) {
793
- results.recommendations.push({
794
- type: 'typescript',
795
- priority: 'medium',
796
- message: 'Avoid using "any" type when possible',
797
- action: 'Use specific types or unknown for better type safety'
798
- });
1140
+ } catch (error) {
1141
+ log('ERROR', 'Go validation error', { error: error.message });
1142
+ results.go = { available: false, error: error.message };
1143
+ }
799
1144
  }
800
1145
 
801
- // Testing recommendations
802
- if (!filePath.match(/\.(test|spec)\./)) {
803
- results.recommendations.push({
804
- type: 'testing',
805
- priority: 'medium',
806
- message: 'Consider writing tests for this module',
807
- action: 'Create corresponding test file to ensure code reliability'
808
- });
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
+ }
809
1190
  }
810
1191
 
811
- 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
+ };
812
1215
 
813
- // ============================================================================
814
- // 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
815
1287
  // ============================================================================
816
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';
817
1297
  let exitCode = 0;
818
- let finalStatus = 'SUCCESS';
819
-
820
- // Check for critical complexity issues
821
- const hasComplexityIssue = results.recommendations.find(r => r.type === 'complexity');
822
-
823
- // Check for bash validator issues
824
- const hasBashValidatorError = results.bashValidators && results.bashValidators.errors > 0;
825
- const hasBashValidatorWarning = results.bashValidators && results.bashValidators.warnings > 0;
826
-
827
- // Check for SQL injection issues
828
- const hasSqlInjectionCritical = results.sqlInjection && !results.sqlInjection.passed &&
829
- results.sqlInjection.issues.some(issue => issue.severity === 'critical');
830
-
831
- if (hasSqlInjectionCritical) {
832
- exitCode = 12;
833
- finalStatus = 'SQL_INJECTION_CRITICAL';
834
- } else if (hasBashValidatorError) {
835
- exitCode = 9;
836
- finalStatus = 'BASH_VALIDATOR_ERROR';
837
- } 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';
838
1304
  exitCode = 2;
839
- finalStatus = 'ROOT_WARNING';
840
- } else if (results.tddViolation) {
841
- exitCode = 3;
842
- finalStatus = 'TDD_VIOLATION';
843
- } else if (hasBashValidatorWarning) {
844
- exitCode = 10;
845
- finalStatus = 'BASH_VALIDATOR_WARNING';
846
- } else if (hasComplexityIssue && hasComplexityIssue.priority === 'critical') {
847
- exitCode = 7;
848
- finalStatus = 'COMPLEXITY_CRITICAL';
849
- } else if (hasComplexityIssue && hasComplexityIssue.priority === 'medium') {
850
- exitCode = 8;
851
- finalStatus = 'COMPLEXITY_WARNING';
852
- } else if (results.rustQuality) {
853
- exitCode = 5;
854
- finalStatus = 'RUST_QUALITY';
855
- } else if (results.prettier && !results.prettier.passed) {
856
- exitCode = 6;
857
- finalStatus = 'LINT_ISSUES';
858
- } else if (results.typescript && !results.typescript.passed) {
859
- exitCode = 1;
860
- 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
861
1320
  } else if (results.recommendations.length > 0) {
862
- finalStatus = 'IMPROVEMENTS_SUGGESTED';
1321
+ overallStatus = 'IMPROVEMENTS_SUGGESTED';
1322
+ exitCode = 0;
863
1323
  }
864
1324
 
865
- const finalResult = {
1325
+ // Log final summary
1326
+ log(overallStatus, 'Pipeline validation complete', {
866
1327
  typescript: results.typescript,
1328
+ rust: results.rust,
867
1329
  eslint: results.eslint,
868
1330
  prettier: results.prettier,
869
1331
  security: results.security,
870
1332
  metrics: results.metrics,
871
1333
  recommendationCount: results.recommendations.length,
872
1334
  topRecommendations: results.recommendations.slice(0, 3)
873
- };
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));
874
1341
 
875
- // Include structured data for feedback handlers
876
- if (results.sqlInjection) {
877
- 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
+ }
878
1347
  }
879
- if (results.rootWarning) {
880
- 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
+ }
881
1358
  }
882
- if (results.tddViolation) {
883
- 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
+ }
884
1365
  }
885
- if (results.bashValidators) {
886
- finalResult.bashValidators = results.bashValidators;
1366
+
1367
+ if (results.prettier && results.prettier.available) {
1368
+ console.error(`Prettier: ${results.prettier.passed ? '✅ PASSED' : '⚠️ NEEDS FORMATTING'}`);
887
1369
  }
888
- if (results.rustQuality) {
889
- 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}`);
890
1374
  }
891
- if (results.complexityAnalysis) {
892
- 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
+ }
893
1393
  }
894
1394
 
895
- log(finalStatus, 'Pipeline validation complete', finalResult);
1395
+ console.error('='.repeat(80) + '\n');
896
1396
 
897
- process.exit(exitCode);
1397
+ process.exit(exitCode);