Skip to content

Commit 49b4115

Browse files
committed
Bump version to 1.0.20 and rebuild
1 parent 0f30df1 commit 49b4115

2 files changed

Lines changed: 199 additions & 15 deletions

File tree

karpeslop-bin.js

Lines changed: 198 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ class AISlopDetector {
6161
description: 'Placeholder comments where AI failed to implement'
6262
}, {
6363
id: 'assumption_comment',
64-
pattern: /\b(assuming|assumes?|presumably|apparently|it seems|seems like|probably|hopefully)\b.{0,50}\b(that|this|the|it)\b/gi,
64+
pattern: /\b(assuming|assumes?|presumably|apparently|it seems|seems like)\b.{0,50}\b(that|this|the|it)\b/gi,
6565
message: "AI making unverified assumptions — dangerous in production",
6666
severity: 'high',
6767
description: 'Comments indicating unverified assumptions'
@@ -131,8 +131,8 @@ class AISlopDetector {
131131
description: 'Detects unsafe as any assertions'
132132
}, {
133133
id: 'unsafe_double_type_assertion',
134-
pattern: /as\s+unknown\s+as\s+\w+/g,
135-
message: "Found unsafe 'as unknown as Type' type assertion. Use proper type guards or validation.",
134+
pattern: /as\s+\w+\s+as\s+\w+/g,
135+
message: "Found unsafe double type assertion. Consider using 'as unknown as Type' for safe conversions.",
136136
severity: 'high',
137137
description: 'Detects unsafe double type assertions'
138138
}, {
@@ -162,13 +162,9 @@ class AISlopDetector {
162162
message: "Found TODO/FIXME/HACK comment indicating incomplete implementation.",
163163
severity: 'medium',
164164
description: 'Detects incomplete implementation markers'
165-
}, {
166-
id: 'complex_nested_conditionals',
167-
pattern: /(?:if\s*\(|for\s*\(|while\s*\()/g,
168-
message: "Found potentially complex nested control structures. Consider refactoring for readability.",
169-
severity: 'medium',
170-
description: 'Detects potential complex conditionals'
171-
}, {
165+
},
166+
// Note: complex_nested_conditionals is handled separately below with improved logic
167+
{
172168
id: 'unsafe_member_access',
173169
pattern: /\.\s*any\s*\[/g,
174170
message: "Found potentially unsafe member access on 'any' type.",
@@ -235,7 +231,9 @@ class AISlopDetector {
235231
// Generated library files
236232
'scripts/ai-slop-detector.ts',
237233
// Exclude the detector script itself to avoid false positives
238-
'ai-slop-detector.ts' // Also exclude when in root directory
234+
'ai-slop-detector.ts',
235+
// Also exclude when in root directory
236+
'improved-ai-slop-detector.ts' // Exclude the improved detector script to avoid false positives
239237
]
240238
});
241239

@@ -364,6 +362,11 @@ class AISlopDetector {
364362
continue;
365363
}
366364

365+
// Skip the complex_nested_conditionals pattern since we handle it separately
366+
if (pattern.id === 'complex_nested_conditionals') {
367+
continue;
368+
}
369+
367370
// Create a new RegExp object for each check to reset lastIndex
368371
const regex = new RegExp(pattern.pattern.source, pattern.pattern.flags);
369372
let match;
@@ -378,6 +381,37 @@ class AISlopDetector {
378381
continue;
379382
}
380383

384+
// Skip legitimate JSON parsing patterns
385+
if (pattern.id === 'any_type_usage' && (line.includes('JSON.parse(') || line.includes('.json') || line.includes('response.json'))) {
386+
continue;
387+
}
388+
389+
// Skip legitimate API response handling where 'any' is often unavoidable
390+
if (pattern.id === 'any_type_usage' && (line.includes('ApiResponse') || line.includes('apiResponse') || line.includes('res.json') || line.includes('fetch') || line.includes('axios'))) {
391+
continue;
392+
}
393+
394+
// Skip legitimate uses of 'any' for dynamic data processing
395+
if (pattern.id === 'any_type_usage' && (line.includes('data: any') || line.includes('(data: any)') || line.includes('result: any') || line.includes('response: any'))) {
396+
// Check if it's in a function that processes dynamic data
397+
if (line.includes('parse') || line.includes('process') || line.includes('transform')) {
398+
continue;
399+
}
400+
}
401+
402+
// Special handling for function_param_any_type pattern
403+
if (pattern.id === 'function_param_any_type') {
404+
// Skip legitimate uses in data processing functions
405+
if (line.includes('(data: any)') && (line.includes('parse') || line.includes('process') || line.includes('transform'))) {
406+
continue;
407+
}
408+
409+
// Skip legitimate uses in generic functions dealing with external data
410+
if (line.includes('ApiResponse') || line.includes('apiResponse') || line.includes('JSON.parse') || line.includes('response: any')) {
411+
continue;
412+
}
413+
}
414+
381415
// Special handling for missing error handling - look for properly handled fetch calls
382416
if (pattern.id === 'missing_error_handling') {
383417
// Check if this fetch call is part of a properly handled async function
@@ -389,10 +423,55 @@ class AISlopDetector {
389423

390424
// Special handling for unsafe_double_type_assertion - skip legitimate UI library patterns
391425
if (pattern.id === 'unsafe_double_type_assertion') {
392-
// Check the full line context to identify legitimate UI library patterns
426+
// Check the full line context to identify potentially legitimate patterns
427+
const fullLine = line.trim();
428+
// Skip patterns that are actually safe (as unknown as Type) since we changed the regex
429+
// but double-check to be extra sure
430+
if (fullLine.includes('as unknown as')) {
431+
continue; // This is actually safe - skip it
432+
}
433+
}
434+
435+
// Special handling for production_console_log - skip legitimate error handling and debugging patterns
436+
if (pattern.id === 'production_console_log') {
393437
const fullLine = line.trim();
394-
if (fullLine.includes('as unknown as React.ElementType') || fullLine.includes('as unknown as TooltipFormatter') || fullLine.includes('as unknown as import(') || fullLine.includes('as unknown as import ')) {
395-
continue; // Skip legitimate UI library patterns
438+
439+
// Skip console.error logs inside catch blocks (legitimate error handling)
440+
if (fullLine.includes('console.error(') && this.isInTryCatchBlock(lines, i)) {
441+
continue;
442+
}
443+
444+
// Skip general debugging logs that might be intentional in development
445+
if (fullLine.includes('console.log(') && (fullLine.includes('Debug') || fullLine.includes('debug') || fullLine.includes('debug:'))) {
446+
continue;
447+
}
448+
449+
// Skip console logs that contain the word 'error' in a non-error context (like error handling)
450+
if ((fullLine.includes('console.log(') || fullLine.includes('console.info(')) && (fullLine.includes('error') || fullLine.includes('Error'))) {
451+
continue;
452+
}
453+
}
454+
455+
// Special handling for hedging_uncertainty_comment - skip legitimate test patterns
456+
if (pattern.id === 'hedging_uncertainty_comment' || pattern.id === 'assumption_comment') {
457+
// Skip these patterns in test files where they might be legitimate test descriptions
458+
if (filePath.includes('test') || filePath.includes('spec') || filePath.includes('__tests__')) {
459+
continue;
460+
}
461+
462+
// Skip common English phrases that are not code-related
463+
const fullLine = line.trim().toLowerCase();
464+
if (fullLine.includes('should work') && (fullLine.includes('//') || fullLine.includes('/*') || fullLine.includes('*/'))) {
465+
// This is likely a comment in a test file
466+
continue;
467+
}
468+
}
469+
470+
// Special handling for unsafe_type_assertion - skip legitimate test patterns
471+
if (pattern.id === 'unsafe_type_assertion') {
472+
// Skip these in test files where they might be legitimate for testing
473+
if (filePath.includes('test') || filePath.includes('spec') || filePath.includes('__tests__')) {
474+
continue;
396475
}
397476
}
398477

@@ -414,9 +493,114 @@ class AISlopDetector {
414493
});
415494
}
416495
}
496+
497+
// Now handle complex nested conditionals separately with improved logic
498+
this.analyzeComplexNestedConditionals(filePath, lines, i, lineNumber, line);
417499
}
418500
}
419501

502+
/**
503+
* Analyze complex nested conditionals using a more sophisticated approach
504+
* This tracks nesting depth rather than just finding control structure keywords
505+
*/
506+
analyzeComplexNestedConditionals(filePath, lines, lineIndex, lineNumber, line) {
507+
// Count opening braces in this line to determine if we're entering nested blocks
508+
const ifMatches = line.match(/\bif\s*\(/g);
509+
const forMatches = line.match(/\bfor\s*\(/g);
510+
const whileMatches = line.match(/\bwhile\s*\(/g);
511+
512+
// Only flag if there are potentially nested control structures in a single line
513+
// or if the line has multiple indicators of complexity
514+
if (ifMatches && ifMatches.length > 1 || forMatches && forMatches.length > 1 || whileMatches && whileMatches.length > 1 || ifMatches && (forMatches || whileMatches) || forMatches && whileMatches) {
515+
this.issues.push({
516+
type: 'complex_nested_conditionals',
517+
file: filePath,
518+
line: lineNumber,
519+
column: 1,
520+
code: line.trim(),
521+
message: "Found potentially complex nested control structures in a single line. Consider refactoring for readability.",
522+
severity: 'medium'
523+
});
524+
}
525+
526+
// Also look for deeply nested if statements across multiple lines
527+
// Count indentation to detect nesting
528+
const indentation = line.search(/\S/); // Get leading whitespace length
529+
if (indentation >= 16 && (line.includes('if (') || line.includes('for (') || line.includes('while ('))) {
530+
// This might indicate a highly nested structure
531+
// But first, verify it's not a simple case like formatting
532+
const trimmedLine = line.trim();
533+
if (!trimmedLine.startsWith('//') && !trimmedLine.includes('=>')) {
534+
// Skip comments and arrow functions
535+
this.issues.push({
536+
type: 'complex_nested_conditionals',
537+
file: filePath,
538+
line: lineNumber,
539+
column: indentation + 1,
540+
code: line.trim(),
541+
message: "Highly indented control structure suggests deep nesting. Consider refactoring for readability.",
542+
severity: 'medium'
543+
});
544+
}
545+
}
546+
}
547+
548+
/**
549+
* Check if a particular line is within a try-catch block
550+
* Used to determine if console.error is legitimate error handling
551+
*/
552+
isInTryCatchBlock(lines, lineIndex) {
553+
// Look backwards from the given line to find try/catch blocks
554+
let tryBlockDepth = 0;
555+
let catchBlockStartLine = -1;
556+
557+
// Track opening and closing braces to understand block scope
558+
for (let i = lineIndex; i >= 0; i--) {
559+
const line = lines[i];
560+
561+
// Check for catch blocks (which are often where error logging happens)
562+
if (line.includes('catch (')) {
563+
catchBlockStartLine = i;
564+
// Find the opening brace of the catch block
565+
if (line.includes('{')) {
566+
return true;
567+
} else {
568+
// If the catch is on its own line, the next line with { is the start
569+
for (let j = i + 1; j < Math.min(i + 5, lines.length); j++) {
570+
if (lines[j].includes('{')) {
571+
return true;
572+
}
573+
}
574+
}
575+
}
576+
577+
// Check for try blocks
578+
if (line.includes('try {') || line.includes('try') && line.includes('{')) {
579+
if (catchBlockStartLine > i) {
580+
return true; // We found a try block that encompasses the current line
581+
}
582+
}
583+
584+
// More sophisticated brace tracking to identify block depth
585+
const openBraces = (line.match(/{/g) || []).length;
586+
const closeBraces = (line.match(/}/g) || []).length;
587+
if (openBraces > closeBraces) {
588+
tryBlockDepth++;
589+
} else if (closeBraces > openBraces) {
590+
tryBlockDepth = Math.max(0, tryBlockDepth - closeBraces + openBraces);
591+
}
592+
593+
// If we're at top level (depth 0) and haven't found a try/catch, we're outside
594+
if (tryBlockDepth === 0) {
595+
// Check if there was a catch block before we exited
596+
if (catchBlockStartLine > i) {
597+
return true;
598+
}
599+
}
600+
}
601+
return false;
602+
}
603+
420604
/**
421605
* Generate a detailed report of findings
422606
*/

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "karpeslop",
3-
"version": "1.0.19",
3+
"version": "1.0.20",
44
"description": "The linter Andrej Karpathy wishes existed. Detects the three axes of AI slop with extreme prejudice.",
55
"type": "module",
66
"bin": {

0 commit comments

Comments
 (0)