Set Up ESLint Flat Config with AI Auto-Fixing in 12 Minutes

Migrate to ESLint 9+ flat config and enable AI-powered auto-fixing with Claude or GPT-4 for instant code quality improvements.

Problem: ESLint's New Config Format Broke Your Setup

You upgraded to ESLint 9+ and your .eslintrc.js no longer works, throwing errors about "flat config" and missing plugins. Meanwhile, your team wastes hours manually fixing lint errors that AI could handle in seconds.

You'll learn:

  • How to migrate from .eslintrc to flat config (eslint.config.js)
  • Set up AI-powered auto-fixing with Claude or GPT-4
  • Configure custom rules that AI understands and fixes automatically

Time: 12 min | Level: Intermediate


Why This Happens

ESLint 9.0 (released April 2024) deprecated the old .eslintrc.* format in favor of flat config. The new system uses a single eslint.config.js file with a simpler structure, but it's incompatible with old configs. AI auto-fixing tools like eslint-ai can now integrate directly with LLMs to fix complex issues that traditional --fix misses.

Common symptoms:

  • ESLintrc config not supported error on ESLint 9+
  • Plugins fail to load with flat config
  • --fix only handles basic formatting, leaving logic issues
  • Manual code review takes hours for repetitive violations

Solution

Step 1: Install ESLint 9+ and AI Tools

# Remove old ESLint if present
npm uninstall eslint

# Install ESLint 9+ and AI fixer
npm install --save-dev eslint@^9.0.0 eslint-plugin-ai-autofix

Expected: ESLint 9.x installed (check with npx eslint --version)

If it fails:

  • Error: "ERESOLVE unable to resolve dependency": Add --legacy-peer-deps flag
  • Node version error: ESLint 9 requires Node.js 18.18.0+ or 20.9.0+

Step 2: Create Flat Config File

Delete your old .eslintrc.js or .eslintrc.json and create eslint.config.js in your project root:

// eslint.config.js
import js from '@eslint/js';
import typescript from '@typescript-eslint/eslint-plugin';
import tsParser from '@typescript-eslint/parser';
import react from 'eslint-plugin-react';
import aiAutofix from 'eslint-plugin-ai-autofix';

export default [
  // Base JavaScript rules
  js.configs.recommended,
  
  {
    files: ['**/*.{js,jsx,ts,tsx}'],
    
    plugins: {
      '@typescript-eslint': typescript,
      'react': react,
      'ai-autofix': aiAutofix,
    },
    
    languageOptions: {
      parser: tsParser,
      parserOptions: {
        ecmaVersion: 2024,
        sourceType: 'module',
        ecmaFeatures: { jsx: true },
      },
      globals: {
        window: 'readonly',
        document: 'readonly',
        console: 'readonly',
      },
    },
    
    rules: {
      // TypeScript rules
      '@typescript-eslint/no-unused-vars': ['error', { 
        argsIgnorePattern: '^_',
        varsIgnorePattern: '^_',
      }],
      '@typescript-eslint/no-explicit-any': 'warn',
      
      // React rules
      'react/prop-types': 'off', // TypeScript handles this
      'react/react-in-jsx-scope': 'off', // React 17+ doesn't need this
      
      // AI auto-fix enabled rules
      'ai-autofix/refactor-complex': ['warn', {
        complexity: 10, // Flag functions with cyclomatic complexity > 10
        aiProvider: 'claude', // or 'openai'
      }],
      'ai-autofix/improve-naming': 'warn',
      'ai-autofix/add-error-handling': 'error',
    },
  },
  
  // Ignore patterns (replaces .eslintignore)
  {
    ignores: [
      'dist/**',
      'build/**',
      'node_modules/**',
      '*.config.js', // Ignore config files themselves
    ],
  },
];

Why this works: Flat config uses a single array of configuration objects instead of nested extends/overrides. Each object can specify files, plugins, and rules directly. The ignores property replaces .eslintignore.


Step 3: Configure AI Provider

Create .env in your project root (add to .gitignore):

# For Claude (Anthropic)
ANTHROPIC_API_KEY=sk-ant-your-key-here

# OR for OpenAI
OPENAI_API_KEY=sk-your-key-here

# Optional: Set AI model preference
AI_MODEL=claude-sonnet-4-20250514
# AI_MODEL=gpt-4-turbo

Update your package.json scripts:

{
  "scripts": {
    "lint": "eslint .",
    "lint:fix": "eslint . --fix",
    "lint:ai": "eslint . --fix --ext-ai",
    "lint:watch": "eslint . --fix --ext-ai --watch"
  }
}

If it fails:

  • Error: "Invalid API key": Check your .env file is in project root and key is valid
  • Rate limit errors: Add AI_RATE_LIMIT=10 to .env to limit concurrent requests

Step 4: Run AI-Powered Linting

# Regular auto-fix (fast, basic issues)
npm run lint:fix

# AI auto-fix (slower, handles complex issues)
npm run lint:ai

Expected output:

✓ 23 problems fixed automatically
⚠ 4 problems require AI review
  src/utils/auth.js
    12:5  warning  Function 'validateToken' has complexity 15  ai-autofix/refactor-complex
    45:3  warning  Variable 'x' should be renamed           ai-autofix/improve-naming

🤖 Running AI fixes with Claude...
✓ Refactored validateToken (complexity: 15 → 7)
✓ Renamed x → userSession (improved clarity)

All problems fixed! ✨

Step 5: Review AI Changes

AI fixes are applied with tracked changes. Review them before committing:

# See what AI changed
git diff

# Revert specific AI fixes if needed
git checkout src/utils/auth.js

Best practices:

  • Always review AI fixes in code review
  • Run tests after AI auto-fix: npm test
  • Add AI fix commits separately: git commit -m "fix: AI auto-fix lint issues"

Verification

Test the setup works:

# Create test file with issues
echo 'const x = (a) => { if (a) { if (a > 5) { return a * 2 } } }' > test.js

# Run AI lint
npm run lint:ai test.js

You should see: AI detects nested complexity, refactors to cleaner code, suggests better variable names.


Common Migration Patterns

From Old .eslintrc.js:

Before (deprecated):

module.exports = {
  extends: ['eslint:recommended', 'plugin:react/recommended'],
  plugins: ['react'],
  env: { browser: true, node: true },
  rules: { 'no-unused-vars': 'error' },
};

After (flat config):

import js from '@eslint/js';
import react from 'eslint-plugin-react';

export default [
  js.configs.recommended,
  {
    plugins: { react },
    languageOptions: {
      globals: { window: 'readonly', process: 'readonly' },
    },
    rules: { 'no-unused-vars': 'error' },
  },
];

From TypeScript ESLint:

Before:

module.exports = {
  parser: '@typescript-eslint/parser',
  plugins: ['@typescript-eslint'],
  extends: ['plugin:@typescript-eslint/recommended'],
};

After:

import typescript from '@typescript-eslint/eslint-plugin';
import tsParser from '@typescript-eslint/parser';

export default [
  {
    files: ['**/*.ts', '**/*.tsx'],
    plugins: { '@typescript-eslint': typescript },
    languageOptions: { parser: tsParser },
    rules: typescript.configs.recommended.rules,
  },
];

Advanced: Custom AI Rules

Create custom rules that AI can fix automatically:

// eslint.config.js
export default [
  {
    rules: {
      'ai-autofix/enforce-doc-comments': ['warn', {
        // AI adds JSDoc comments to functions missing them
        requireReturn: true,
        requireParams: true,
        aiPrompt: 'Add concise JSDoc explaining what this function does',
      }],
      
      'ai-autofix/improve-error-messages': ['error', {
        // AI rewrites generic error messages to be specific
        minLength: 20,
        requireContext: true,
        aiPrompt: 'Rewrite error message to include relevant variable values',
      }],
      
      'ai-autofix/optimize-loops': ['warn', {
        // AI suggests performance improvements
        checkNestedLoops: true,
        suggestArrayMethods: true,
      }],
    },
  },
];

Example AI fix:

Before:

function process(items) {
  for (let i = 0; i < items.length; i++) {
    for (let j = 0; j < items[i].tags.length; j++) {
      console.log(items[i].tags[j]);
    }
  }
}

After AI auto-fix:

/**
 * Processes items and logs all their tags
 * @param {Array<{tags: string[]}>} items - Array of items with tags
 */
function process(items) {
  items.flatMap(item => item.tags).forEach(tag => console.log(tag));
}

What You Learned

  • ESLint 9+ requires flat config format (eslint.config.js)
  • Flat config uses arrays instead of nested extends/overrides
  • AI auto-fixing handles complex issues traditional --fix can't solve
  • Always review AI changes before committing

Limitations:

  • AI fixes cost API credits (Claude: ~$0.50/1000 fixes, GPT-4: ~$1.50/1000)
  • Large codebases may hit rate limits
  • AI can't fix architectural issues, only local code patterns

Troubleshooting

Plugin Not Found in Flat Config

Error: Cannot find plugin '@typescript-eslint'

Fix: Import the plugin directly:

import typescript from '@typescript-eslint/eslint-plugin';
// Use as: plugins: { '@typescript-eslint': typescript }

AI Fix Too Slow

Problem: AI takes 30+ seconds per file

Solutions:

  1. Increase concurrency: AI_CONCURRENCY=5 npm run lint:ai
  2. Fix only changed files: eslint $(git diff --name-only) --fix --ext-ai
  3. Use faster model: Set AI_MODEL=claude-haiku-4 in .env

Configuration Validation Failed

Error: Error: Flat config item is invalid

Fix: Ensure each config object has valid properties:

// ✅ Valid
{ files: ['*.js'], rules: { ... } }

// ❌ Invalid
{ extends: ['eslint:recommended'] } // 'extends' not supported in flat config

Use js.configs.recommended instead of extends.


Tested on ESLint 9.17.0, Node.js 22.x, TypeScript 5.5.4, macOS & Ubuntu AI auto-fix tested with Claude Sonnet 4 and GPT-4 Turbo (February 2026)