import type { GrepPatternRuleDefinition, GrepPatternRuleDescriptions } from "./shared"; export const AI_SLOP_GREP_PATTERN_RULES: GrepPatternRuleDefinition[] = [ { id: "BANNER_COMMENT", pattern: /^[\s]*(\/\/|#)\s*[─═━─\-=]{10,}/, category: "ai-slop", severity: "LOW", tier: 1, message: "ASCII banner separator — visual noise from LLM generation", fix: "Remove the banner line", }, { id: "NARRATION_COMMENT", pattern: /\/\/\s*(First,?\s+(we|let's)|Now\s+(we|let's)|Next,?\s+(we|let's)|Step\s+\d+)/i, category: "ai-slop", severity: "LOW", tier: 1, message: "Process narration comment — code isn't a story", fix: "Remove the narration comment", }, { id: "APOLOGETIC_COMMENT", pattern: /\/\/\s*(Note:\s*you\s+may|Feel free to|You might want to|TODO:\s*consider)/i, category: "ai-slop", severity: "LOW", tier: 1, message: "Apologetic/suggestive comment — decide and act, don't hedge", fix: "Remove the comment or implement the suggestion", }, { id: "OBVIOUS_JSX_LABEL", pattern: /\{\/\*\s*(Header|Footer|Sidebar|Main content|Navigation|Content|Body|Wrapper)\s*\*\/\}/i, category: "ai-slop", severity: "LOW", tier: 1, message: "JSX comment labels the obvious — the component tag is the label", fix: "Remove the comment", }, { id: "DEMO_PLACEHOLDER", pattern: /^\s*\/\/\s*(for demo|placeholder|mock data|sample data|dummy data|fake data|wire.*real.*api|todo.*wire|todo.*replace.*real)/i, category: "ai-slop", severity: "MEDIUM", tier: 1, message: "Demo/placeholder code flagged by comment", fix: "Replace with real implementation or remove", }, { id: "HEDGING_COMMENT", pattern: /(\/\/|#)\s*(this\s+)?(should work|should be fine|hopefully|might need|in theory|this may|ideally|this seems to|probably)/i, category: "ai-slop", severity: "LOW", tier: 1, message: "Hedging comment — LLM uncertainty leaked into code", fix: "Remove the hedging comment or fix the uncertainty", }, { id: "SECTION_LABEL_COMMENT", pattern: /^\s*(\/\/|#)\s*(Setup|Initialization|Cleanup|Teardown|Main logic|Business logic|Helper functions|Utility functions|Constants|Imports)\s*$/i, category: "ai-slop", severity: "LOW", tier: 1, message: "Section label comment — fake structure in a flat file", fix: "Remove or extract into a real module", }, { id: "INSTRUCTIONAL_COMMENT", pattern: /(\/\/|#)\s*(Make sure to|Don't forget|Remember to|Be sure to|Be careful|Important:?\s+always)/i, category: "ai-slop", severity: "LOW", tier: 1, message: "Tutorial voice in production code", fix: "Remove the instructional comment", }, { id: "STATED_RETURN_COMMENT", pattern: /\/\/\s*[Rr]eturn(s|ing)?\s+(the|a|an)\s+\w+/, category: "ai-slop", severity: "LOW", tier: 1, message: "Comment narrates the return value — the code already says this", fix: "Remove the comment", }, { id: "TRIPLE_NULL_GUARD", pattern: /!==?\s*(null|undefined)\s*&&\s*\w+\s*!==?\s*(null|undefined)\s*&&\s*\w+\s*!==?\s*['"]/, category: "ai-slop", severity: "MEDIUM", tier: 2, message: "Triple null/undefined/empty guard — use ?? or != null", fix: "Simplify to a single nullish check", }, { id: "EXPLICIT_TRUE_COMPARE", pattern: /\w\s*===?\s*(true|false)\s*[;),\]}&|?:]/, category: "ai-slop", severity: "LOW", tier: 1, message: "Redundant boolean comparison — just use the value directly", fix: "Remove === true or negate for === false", }, { id: "RETURN_UNDEFINED", pattern: /^\s*return\s+undefined\s*;/, category: "ai-slop", severity: "LOW", tier: 1, message: "Explicit return undefined — just return;", fix: "Replace with bare return;", }, { id: "PLACEHOLDER_VAR_NAME", pattern: /\b(data[2-9]|result[2-9]|temp\d+|foo\d*|bar\d*|baz\d*|myVar|someVar|testData)\b\s*[=:]/, category: "ai-slop", severity: "MEDIUM", tier: 0, message: "Placeholder variable name — rename to something meaningful", }, { id: "LINT_ESCAPE", pattern: /^\s*(\/\/\s*eslint-disable|\/\*\s*eslint-disable|\/\/\s*@ts-ignore|\/\/\s*@ts-nocheck|#\s*noqa|#\s*type:\s*ignore)/, category: "ai-slop", severity: "MEDIUM", tier: 0, message: "Lint suppression — fix the underlying issue instead", }, { id: "ENTRY_EXIT_LOG", pattern: /console\.(log|debug)\s*\(\s*['"`](Entering|Exiting|Called|Starting|Ending|Leaving)\b/i, category: "ai-slop", severity: "MEDIUM", tier: 1, message: "Function lifecycle logging — debugging residue", fix: "Remove the entry/exit log statement", }, { id: "COMMENTED_CODE_BLOCK", pattern: /^\s*(\/\/|#)\s*(?:(?:const|let|var|function|class|import|export|if|for|while|return)\b|async(?:\s+function\b|\s*\())/, category: "ai-slop", severity: "LOW", tier: 1, message: "Commented-out code — delete it, git remembers", fix: "Remove the commented-out code block", }, { id: "PLACEHOLDER_VALUE", pattern: /["'](your-api-key-here|CHANGE_ME|TODO:\s*replace|example\.com|xxx|changeme|replace-me)["']/i, category: "ai-slop", severity: "HIGH", tier: 0, message: "Placeholder value shipped in code — replace with real value", }, { id: "HARDCODED_FAKE_DATA", pattern: /^\s*(const|let|var)\s+\w+(Data|Items|Stats|Metrics)\s*=\s*\[$/, category: "ai-slop", severity: "HIGH", tier: 0, message: "Hardcoded data array — should come from API or props", }, { id: "UNNECESSARY_INTERMEDIATE", pattern: /^\s*(const|let)\s+(\w+)\s*=\s*.+;\s*$/, category: "ai-slop", severity: "LOW", tier: 0, message: "Unnecessary intermediate variable — return the expression directly", }, { id: "DEBUG_BREAKPOINT", pattern: /\bdebugger\b|\bbreakpoint\b|\bpdb\.set_trace\b|dbg!/, category: "ai-slop", severity: "HIGH", tier: 1, message: "Debug breakpoint left in code — remove before shipping", fix: "Remove the breakpoint/debug statement", skipTest: true, }, { id: "USEMEMO_EMPTY_DEPS", pattern: /useMemo\(\s*\(\)\s*=>\s*(?!\s*\{).*?,\s*\[\s*\]\s*\)/, category: "ai-slop", severity: "LOW", tier: 1, message: "useMemo with empty deps on a constant — just use the value directly", fix: "Remove the useMemo wrapper", }, { id: "FAKE_LOADING_DELAY", pattern: /await\s+new\s+Promise\s*\(.*setTimeout\s*\(/, category: "ai-slop", severity: "MEDIUM", tier: 1, message: "Fake loading delay in production code — simulated latency should not ship", fix: "Remove the artificial delay or gate it to demos/tests", skipTest: true, }, { id: "UNNECESSARY_USECALLBACK", pattern: /useCallback\(\s*\(\)\s*=>.*\[\s*\]\s*\)/, category: "ai-slop", severity: "LOW", tier: 1, message: "useCallback with empty deps and no captured values — use the function directly", fix: "Remove the useCallback wrapper", }, { id: "REDUNDANT_BOOLEAN_RETURN", pattern: /^\s*if\s*\(.+\)/, category: "ai-slop", severity: "LOW", tier: 1, message: "Redundant boolean-return scaffolding — return the condition directly", fix: "Replace the if/else boolean return with a direct return", }, { id: "UNDERSCORE_STATE", pattern: /\[\s*_\w+\s*,\s*set\w+\s*\]\s*=\s*useState/, category: "ai-slop", severity: "MEDIUM", tier: 0, message: "Underscore-prefixed state variable — likely unused dead state", }, { id: "REDUNDANT_CAST", pattern: /\b(String|Number|Boolean)\(\s*\w+\s*\)\s*$/, category: "ai-slop", severity: "LOW", tier: 0, message: "Possibly redundant type cast — check if value is already the right type", }, { id: "KEY_INDEX", pattern: /^\s*<\w[\w.]*\s+.*key=\{(index|i|idx)\}/, category: "ai-slop", severity: "LOW", tier: 0, message: "Using array index as React key — use a stable ID if available", }, { id: "OR_CASCADE", pattern: /\.get\(\s*["']\w+["']\s*\)\s*(or|\|\|)\s*\S+.*\.get\(\s*["']\w+["']\s*\)\s*(or|\|\|)/, category: "ai-slop", severity: "LOW", tier: 0, message: "Chained defensive .get() or fallbacks — simplify the data model", }, ]; export const AI_SLOP_GREP_PATTERN_DESCRIPTIONS: GrepPatternRuleDescriptions = { BANNER_COMMENT: "ASCII banner separator", NARRATION_COMMENT: "Process narration comment", APOLOGETIC_COMMENT: "Apologetic/suggestive comment", OBVIOUS_JSX_LABEL: "JSX comment restating the tag name", DEMO_PLACEHOLDER: "Demo/placeholder/mock data marker", HEDGING_COMMENT: "Uncertainty hedging — 'should work', 'hopefully'", SECTION_LABEL_COMMENT: "Section label in flat file — // Setup, // Cleanup", INSTRUCTIONAL_COMMENT: "Tutorial voice — 'Make sure to', 'Don't forget'", STATED_RETURN_COMMENT: "Comment narrates the return value", TRIPLE_NULL_GUARD: "Triple null/undefined/empty guard", EXPLICIT_TRUE_COMPARE: "Redundant === true/false comparison", RETURN_UNDEFINED: "Explicit return undefined — just return;", PLACEHOLDER_VAR_NAME: "Meaningless variable name — data2, temp1, foo", LINT_ESCAPE: "Lint suppression instead of fixing the issue", ENTRY_EXIT_LOG: "Function entry/exit debugging log", COMMENTED_CODE_BLOCK: "Commented-out code line", PLACEHOLDER_VALUE: "Placeholder value shipped in code", HARDCODED_FAKE_DATA: "Hardcoded data array — should come from API", UNNECESSARY_INTERMEDIATE: "Unnecessary intermediate variable before return", DEBUG_BREAKPOINT: "debugger/breakpoint/pdb.set_trace/dbg! left in code", USEMEMO_EMPTY_DEPS: "useMemo with empty deps on constant", FAKE_LOADING_DELAY: "Artificial loading delay shipped in production code", UNNECESSARY_USECALLBACK: "useCallback with empty deps and no captures", REDUNDANT_BOOLEAN_RETURN: "if/else returns boolean literals — return condition directly", UNDERSCORE_STATE: "Underscore-prefixed useState — dead state", REDUNDANT_CAST: "Possibly redundant String/Number/Boolean cast", KEY_INDEX: "Array index as React key — use stable ID", OR_CASCADE: "Chained defensive .get() or fallbacks", };