How to Set Up Automated Linting and Hooks for AI-Generated Code
Problem
My AI-generated code kept breaking. Every time I used Claude to write code, I’d find issues later: console.log statements left in production, placeholder API keys that should have been replaced, empty catch blocks where error handling should have been.
At first I tried to catch these manually during code review. But that got exhausting fast. I was spending more time reviewing AI output than writing code myself.
I’d tell Claude “make sure there are no errors” but it would still leave debugging artifacts. I’d say “use proper error handling” but still find empty catch blocks. The AI meant well, but it kept making the same mistakes.
Then I found a Reddit comment that changed my approach:
“You also should have claude implement linting and especially custom linting that runs on agent on save hooks and at sessions stop for the whole repo. If you do this then you don’t need to hold its hand, it will act more like a software engineer. The more rigid you enforce coding rules the higher the performance.”
The key insight: stop asking AI to be careful, and start enforcing rules automatically.
Environment
Here’s what I’m working with:
Tool: Claude Code CLILanguages: TypeScript, PythonLinter: ESLint, RuffHook System: Claude Code hooks, pre-commitWhat happened?
I decided to set up automated quality gates instead of relying on manual review or trusting the AI. The goal was to catch AI mistakes automatically, the same way CI/CD catches human mistakes.
My first attempt was basic: I added ESLint with default settings and hoped for the best.
npm install --save-dev eslint @typescript-eslint/parser @typescript-eslint/eslint-pluginnpx eslint --initThis helped catch some issues, but AI-specific problems still slipped through. Claude would add console.log for debugging, leave TODO comments without issue references, and sometimes generate placeholder values like YOUR_API_KEY_HERE.
The Reddit insight made me realize I needed more than generic linting. I needed rules specifically designed to catch AI patterns.
How to solve it?
I set up a layered validation system with four components:
Layer 1: Enhanced Linter Configuration
I started with ESLint but added rules for common AI mistakes:
module.exports = { extends: [ 'eslint:recommended', '@typescript-eslint/recommended', 'plugin:security/recommended' ], rules: { // Catch common AI mistakes 'no-unused-vars': 'error', 'no-console': 'warn', // AI often leaves console.log 'prefer-const': 'error', 'no-var': 'error',
// Security rules (AI sometimes skips these) 'security/detect-eval-with-expression': 'error', 'security/detect-non-literal-fs-filename': 'warn',
// Code quality limits 'complexity': ['warn', 10], 'max-depth': ['warn', 4], 'max-lines-per-function': ['warn', 50] }}The no-console rule catches debugging artifacts. The security plugin catches common vulnerabilities that AI sometimes overlooks. The complexity rules prevent AI from generating deeply nested code.
Layer 2: Pre-commit Hooks
Next, I added pre-commit hooks to catch issues before they enter the codebase:
repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.5.0 hooks: - id: trailing-whitespace - id: end-of-file-fixer - id: check-yaml - id: check-added-large-files - id: check-merge-conflict
- repo: local hooks: - id: ai-code-check name: AI Code Quality Check entry: ./scripts/ai-code-check.sh language: script files: \.(py|js|ts|tsx)$The key is the custom ai-code-check script that catches AI-specific patterns:
#!/bin/bash# Custom checks for AI-generated code
echo "Running AI code quality checks..."
# Check for common AI mistakes# 1. Console.log statementsif grep -r "console\.log" --include="*.ts" --include="*.tsx" src/; then echo "ERROR: Found console.log statements (AI debugging artifact)" exit 1fi
# 2. Empty catch blocksif grep -rz "catch.*{[[:space:]]*}" --include="*.ts" src/; then echo "ERROR: Found empty catch blocks (AI often skips error handling)" exit 1fi
# 3. Hardcoded placeholder valuesif grep -rE "(YOUR_API_KEY|API_KEY_HERE|TODO_API)" --include="*.ts" src/; then echo "ERROR: Found placeholder API keys" exit 1fi
echo "AI code quality checks passed!"Layer 3: Session Stop Hooks
The Reddit comment specifically mentioned “session stop for the whole repo.” This is critical because AI might introduce issues in files it didn’t directly modify (imports, type definitions, etc.).
I configured Claude Code to run comprehensive validation when coding sessions end:
{ "hooks": { "PostToolUse": [ { "matcher": { "toolName": "Write|Edit" }, "hooks": [ { "type": "command", "command": "npm run lint -- --fix" } ] } ], "Stop": [ { "type": "command", "command": "./scripts/session-stop-check.sh" } ] }}The session stop script runs a full validation suite:
#!/bin/bash# Run comprehensive checks when AI coding session ends
echo "=== Session Stop Validation ==="echo "Running comprehensive codebase validation..."
# 1. Lint all filesecho "1. Linting..."npm run lintLINT_RESULT=$?
# 2. Type checkecho "2. Type checking..."npm run type-checkTYPE_RESULT=$?
# 3. Run testsecho "3. Running tests..."npm test -- --passWithNoTestsTEST_RESULT=$?
# 4. Check for console.log (AI debugging artifact)echo "4. Checking for debugging artifacts..."if grep -r "console\.log" --include="*.ts" --include="*.tsx" src/; then echo "WARNING: Found console.log statements" CONSOLE_RESULT=1else CONSOLE_RESULT=0fi
# Summaryecho ""echo "=== Validation Summary ==="echo "Lint: $([ $LINT_RESULT -eq 0 ] && echo 'PASS' || echo 'FAIL')"echo "Types: $([ $TYPE_RESULT -eq 0 ] && echo 'PASS' || echo 'FAIL')"echo "Tests: $([ $TEST_RESULT -eq 0 ] && echo 'PASS' || echo 'FAIL')"echo "Artifacts: $([ $CONSOLE_RESULT -eq 0 ] && echo 'CLEAN' || echo 'WARNINGS')"
# Exit with error if any check failedif [ $LINT_RESULT -ne 0 ] || [ $TYPE_RESULT -ne 0 ] || [ $TEST_RESULT -ne 0 ]; then echo "" echo "ERROR: Session validation failed. Please fix issues before ending session." exit 1fi
echo ""echo "SUCCESS: All validation checks passed!"Layer 4: Custom Linting Rules
Finally, I created a custom ESLint rule to catch AI placeholder patterns:
module.exports = { meta: { type: 'problem', docs: { description: 'Disallow AI placeholder patterns', category: 'Possible Errors', recommended: true } }, create(context) { const placeholderPatterns = [ /YOUR_.*_HERE/i, /TODO_IMPLEMENT/i, /FIXME_AI/i, /placeholder/i, /example\.com/i ]
return { Literal(node) { if (typeof node.value === 'string') { for (const pattern of placeholderPatterns) { if (pattern.test(node.value)) { context.report({ node, message: 'Found AI placeholder pattern: {{ value }}', data: { value: node.value } }) } } } } } }}The reason
Why does rigid enforcement improve AI performance?
Feedback loop: When the AI sees lint errors after generating code, it learns what patterns to avoid. The immediate feedback creates a training signal within the session.
Reduced ambiguity: Instead of interpreting vague instructions like “be careful,” the AI sees specific errors with clear fix suggestions. This removes interpretation overhead.
Consistent standards: Automated enforcement ensures every piece of AI-generated code meets the same bar. No exceptions, no “I’ll fix it later.”
Whole-repo validation: The session stop hook catches integration issues that file-level checks miss. When AI adds an import, the whole repo validation ensures that import actually exists.
I also found a community comment that reinforced this approach:
“Insufficiently supervised AI tends to build on unstable foundations. At some point, the house of cards just collapses.”
Automated supervision prevents the accumulation of small errors that eventually break the system.
Common mistakes
I made several mistakes while setting this up:
Mistake 1: Relying on AI to catch its own errors
# WRONG:"Write this code and make sure there are no errors"
# CORRECT:Set up automated linting that catches errors regardless of what AI claimsMistake 2: Only checking modified files
# WRONG:Run linter only on changed files
# CORRECT:Validate entire codebase at session end (catches integration issues)Mistake 3: Generic linting rules
# WRONG:Use default ESLint configuration
# CORRECT:Add custom rules for common AI mistakes specific to your projectMistake 4: Ignoring warnings
# WRONG:Treat warnings as acceptable
# CORRECT:Treat warnings as errors for AI-generated code (enforces discipline)Summary
In this post, I showed how to set up automated linting and hooks for AI-assisted development. The key point is that rigid enforcement of coding rules improves AI performance by creating immediate, specific feedback loops.
The four-layer approach works:
- Enhanced linter catches AI-specific patterns (console.log, placeholders)
- Pre-commit hooks catch issues before they enter the codebase
- Session stop hooks validate the entire repo after AI coding sessions
- Custom rules catch patterns specific to AI-generated code
The result: I stopped asking “Did the AI make mistakes?” and started trusting that mistakes will be caught automatically. My AI assistant became a true software engineering partner, not an error-prone code generator.
Final Words + More Resources
My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me
Here are also the most important links from this article along with some further resources that will help you in this scope:
Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!
Comments