Přejít na hlavní obsah
Sleva 50% všechny plány, omezený čas. Od $2.48/mo
14 min left
Vývojářské nástroje a DevOps

Jak nastavit linter pro kód generovaný AI

S By Sherwin 14 min read
ESLint output showing AI-specific lint violations in a TypeScript file

AI agenti dokážou generovat kód, který se zkompiluje.

To pořád není laťka, jakou chcete. Laťka je kód, který neimportuje věci, které nikdy nepoužije, nepřetypuje se přes any z typového systému, neignoruje návratové hodnoty chyb a nezadrátuje natvrdo přihlašovací údaje, které by gosec měl zachytit ještě před revizí.

AI modely se učí na starých odpovědích ze Stack Overflow a dodávají vzory, které se tam naučily, včetně zastaralých API, chybějících typových anotací a funkcí, které jsou technicky správné, ale příliš velké na to, aby šly bezpečně zrevidovat. Potřebujete linter ve smyčce. Ne jako návrh, ale jako bránu.

Tento návod pokrývá konfiguraci pro tři ekosystémy (Python s Ruff, TypeScript/JavaScript s ESLint v10 flat config a Go s golangci-lint) s pravidly vyladěnými konkrétně na chybové vzory, které AI zavádí. Poté popisuje, jak bránu výrazně ztížit k obejití, aby agent nemohl jednoduše přeskočit lokální hooky pomocí --no-verify, aniž by ho zachytila další vrstva.

Nastavení je vrstvené: linting na úrovni IDE zachytává problémy přímo během psaní agentem, pre-commit hooky zachytávají cokoli, co se dostane k pokusu o commit, a CI zachytává cokoli, co projde lokálně. Každá vrstva je nezávislá a můžete si vybrat jazykové sekce, které se týkají vašeho stacku. Vynucovací vrstva funguje stejně bez ohledu na to, jaký jazyk lintujete.

Stručně řečeno

  • Ruff (Python), ESLint v10 (TS/JS) a golangci-lint (Go) mají každý konkrétní pravidla, která zachytávají nejčastější chyby AI
  • Konfigurace níže jsou okomentované; každé pravidlo tam má svůj důvod
  • Lefthook se stará o pre-commit bránu; Cursor hook afterFileEdit spouští lint přímo na místě
  • Čtyři vynucovací vrstvy výrazně ztěžují AI agentům přeskočit bránu pomocí --no-verify
  • CI je poslední pojistka: agenti nemohou předat --no-verify do GitHub Actions

Jak nakonfigurovat Ruff pro AI kód v Pythonu

Terminal output from Ruff flagging unused imports and a missing return type annotation in AI-generated Python code

Ruff je ten správný Python linter pro kódové báze s asistencí AI. Je dost rychlý na to, aby běžel při každém uložení souboru, aniž by cokoli blokoval, pokrývá jak styl, tak skutečné logické chyby a v jednom binárním souboru dodává i chování formátovače (nahrazuje Black). Pravidla níže cílí na konkrétní chybové vzory, které AI modely v Pythonu zavádějí nejčastěji.

Instalace Ruff

pip install ruff

# or via uv (faster for new projects):
uv add --dev ruff

A to je vše. Žádný ekosystém pluginů, žádné dohadování o peer závislostech.

Konfigurace pyproject.toml

[tool.ruff]
line-length = 88
target-version = "py311"

[tool.ruff.lint]
select = [
  "E",   # pycodestyle — style consistency
  "F",   # pyflakes — catches unused imports (the most common AI artifact)
  "I",   # isort — import ordering (AI frequently reorders imports incorrectly)
  "N",   # pep8-naming — naming conventions
  "UP",  # pyupgrade — flags deprecated APIs (AI trains on old Stack Overflow answers)
  "S",   # flake8-bandit — security rules: subprocess.shell=True, eval(), hardcoded creds
  "ANN", # type annotation enforcement (AI frequently omits return type annotations)
]
ignore = []  # intentional: do not add sweeping ignores in AI-assisted codebases

[tool.ruff.lint.per-file-ignores]
"tests/**" = ["S101"]  # allow assert in tests

Pravidla F se vyplatí okamžitě. AI kód generuje příkazy import pro balíčky, které nakonec nepoužije, a F401 (nepoužitý import) zachytí každý z nich. Pravidla UP zachytávají volání zastaralých API vzorů, které se AI naučila z odpovědí pro Python před verzí 3.10; samotná UP006 a UP007 označí desítky zbytečných vzorů typové kontroly. Pravidla S (Bandit) zachytávají bezpečnostní selhání: natvrdo zadrátované řetězce, které vypadají jako přihlašovací údaje (S105/S106), shell injection přes subprocess(shell=True) (S603/S607), slabé volby kryptografie (S324).

Hodnota ignore = [] je záměrná. Každá výjimka, kterou do tohoto seznamu přidáte, je třída AI selhání, kterou jste se rozhodli povolit.

Spuštění Ruff

ruff check .           # lint only — see what's wrong
ruff check --fix .     # auto-fix safe violations (imports, formatting)
ruff format .          # format the codebase (replaces Black)

--fix ve výchozím nastavení aplikuje bezpečné opravy Ruff, jako je odstranění nepoužitých importů nebo aplikace přímočarého formátování a oprav lintu. Ruff má také nebezpečné opravy, ale ty vyžadují explicitní přihlášení a měly by se kontrolovat pečlivěji. Cokoli, co Ruff nedokáže bezpečně opravit, zkontrolujte ručně.

Jak nakonfigurovat ESLint v10 pro AI kód v TypeScriptu a JavaScriptu

ESLint v10 flat config reporting no-explicit-any and max-lines-per-function violations in an AI-generated TypeScript file

ESLint v10 zrušil starší konfigurační formát .eslintrc.*. Vše je nyní flat config v eslint.config.mjs. Pokud najdete tutoriál používající .eslintrc.json nebo .eslintrc.js, cílí na ESLint v8 nebo v9, kde je syntaxe odlišná. Použijte to, co je níže.

Pravidla @typescript-eslint v této konfiguraci cílí na konkrétní chybové režimy, které se opakovaně objevují v TypeScriptu generovaném AI: únikový východ any, monolitické funkce, které se těžko revidují, a natvrdo zadrátované hodnoty, které by měly být konstantami.

Instalace ESLint v10 s podporou TypeScriptu

npm install --save-dev eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin

# or with pnpm:
pnpm add -D eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin

ESLint v10.5.0 je aktuální k červnu 2026. Balíčky @typescript-eslint by měly odpovídat vaší verzi TypeScriptu; matici kompatibility najdete v jejich README.

Konfigurace eslint.config.mjs

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

export default [
  {
    files: ['**/*.ts', '**/*.tsx'],
    languageOptions: {
      parser: tsParser,
      parserOptions: { project: './tsconfig.json' },
    },
    plugins: { '@typescript-eslint': tseslint },
    rules: {
      // AI defaults to `any` to bypass the type system — this blocks it
      '@typescript-eslint/no-explicit-any': 'error',

      // AI generates variables it declares but never uses
      '@typescript-eslint/no-unused-vars': 'error',

      // AI writes functions that work but are too long to review safely
      'max-lines-per-function': ['error', { max: 50 }],

      // AI overloads function signatures; this forces decomposition
      'max-params': ['error', 2],

      // AI hardcodes values that should be named constants
      'no-magic-numbers': ['error', { ignore: [0, 1, -1] }],

      // AI writes files that are too large to reason about in a single review
      'max-lines': ['error', { max: 250 }],

      // AI leaves console.log debugging in production code
      'no-console': 'warn',
    },
  },
];

Pravidlo max-lines-per-function: 50 je nejagresivnější věc v této konfiguraci. Při prvním spuštění v kódové bázi s asistencí AI na něj budete narážet neustále. To je ten záměr. Měli byste na něj narážet. Funkce, které přesahují 50 řádků, jsou první věcí, kterou je nemožné udržet v hlavě, když revidujete výstup AI ve velkém.

Pravidlo max-params: 2 vynucuje rozklad. AI modely se učí z kódových bází, kde jsou funkce s pěti argumenty normální; pravidlo se tomu vzpírá tím, že vyžaduje, aby agent použil objekt s možnostmi, což je lepší návrh a snáze se čte.

Spuštění ESLint

npx eslint .                       # lint
npx eslint . --fix                 # auto-fix safe issues
npx eslint . --max-warnings 0      # CI mode — treats warnings as errors

Ve svém kroku CI použijte --max-warnings 0. Povýší to varování no-console z "technicky zaznamenáno" na "skutečně blokující".

Volitelné: přísnější pravidla pro soubory generované AI

Pokud váš tým používá konvenci pojmenování souborů k označení kódu generovaného AI (*.ai.ts, *-generated.ts nebo podobně), můžete na tyto soubory aplikovat přísnější pravidla konkrétně:

// Add to eslint.config.mjs after the main config object
{
  files: ['**/*.ai.ts', '**/*.ai.tsx', '**/*-generated.ts'],
  rules: {
    'max-lines': ['error', { max: 100 }],  // tighter file ceiling
    'complexity': ['error', 5],             // McCabe complexity limit
  },
}

Jak nakonfigurovat golangci-lint pro AI kód v Go

golangci-lint je standardní spouštěč více linterů pro Go. Dodává se s gosec, errcheck, staticcheck a více než 40 dalšími lintery konfigurovatelnými z jediného YAML souboru. Pro Go kód generovaný AI jsou kritická pravidla kontrola návratových hodnot chyb a detekce bezpečnostních vzorů: dvě kategorie selhání, které AI modely v Go vynechávají nejdůsledněji.

Instalace golangci-lint

# Official binary installer:
curl -sSfL https://golangci-lint.run/install.sh | sh -s -- -b "$(go env GOPATH)/bin" v2.12.2

# or via Homebrew:
brew install golangci-lint

Konfigurace .golangci.yml

version: "2"

linters:
  enable:
    - gosec        # security: flags hardcoded creds (G101), file path injection (G304), weak crypto (G401)
    - unused       # flags unused vars and functions — a common AI artifact in Go
    - errcheck     # AI frequently ignores error returns — this blocks it
    - govet        # catches subtle correctness bugs AI introduces
    - staticcheck  # comprehensive static analysis
    - revive       # style: catches non-idiomatic Go patterns AI writes
    - misspell     # AI occasionally misspells in comments and string literals

  settings:
    gosec:
      severity: medium
      confidence: medium
    errcheck:
      check-type-assertions: true  # check `val, ok := x.(Type)` patterns
      check-blank: true            # catch `_ = someErr` error suppression

run:
  timeout: 3m
  issues-exit-code: 1

Vzor zpracování chyb v Go je z principu explicitní: každá funkce, která může selhat, vrací hodnotu chyby. AI modely tomu rozumějí, ale podceňují to; vynechávají kontroly chyb v nekritických cestách kódu. errcheck z tohoto vynechání udělá selhání lintu.

gosec je bezpečnostní linter. U AI kódu zachytává vzory, které AI pochytila z Go tutoriálů z doby před rokem 2020: nezabezpečené generování náhodných čísel (G404), nezabezpečené hashovací funkce (G401), problémy s oprávněními souborů (G306). To jsou chyby, které při revizi kódu nezachytíte, protože vypadají syntakticky normálně.

Spuštění golangci-lint

golangci-lint run ./...            # lint all packages
golangci-lint run --fix ./...      # auto-fix where possible

Jak zapojit linter do pre-commit hooků

Pre-commit hook spustí lint před každým git commit a zablokuje commit, pokud lint selže. To znamená, že AI agent nemůže commitnout kód, který neprojde vašimi nakonfigurovanými pravidly. Musí nejprve porušení opravit.

Lefthook je doporučená volba. Je multiplatformní, rychlý a má konfigurační vzor, který funguje konkrétně s vynucováním u AI agentů (popsáno v další sekci).

Lefthook

npm install --save-dev lefthook
npx lefthook install

lefthook.yml:

pre-commit:
  parallel: true
  commands:
    lint-python:
      glob: "*.py"
      run: ruff check {staged_files} --fix
    lint-js-ts:
      glob: "*.{js,ts,tsx}"
      run: npx eslint {staged_files} --fix
    lint-go:
      glob: "*.go"
      run: golangci-lint run {staged_files}
  fail_text: |
    Lint failed. For AI Agents: fix all lint violations before committing.
    Do not use --no-verify to bypass this gate.

Zprávu fail_text čtou AI agenti, když commit selže. Tento vzor je zdokumentován v článku Liama Bigelowa o vynucování lintu přes Lefthook pro Claude Code. Samo o sobě to odhodlaného agenta nezastaví, ale dá mu to správnou další instrukci ("oprav porušení lintu") namísto toho, aby si musel obejití domyslet sám.

Alternativa: pre-commit (nastavení pouze pro Python)

Pokud jste na stacku pouze pro Python a preferujete framework pre-commit:

# .pre-commit-config.yaml
repos:
  - repo: https://github.com/astral-sh/ruff-pre-commit
    rev: v0.15.17
    hooks:
      - id: ruff-check
        args: [--fix]
      - id: ruff-format

Cursor: inline linting přes hook afterFileEdit

Pokud používáte Cursor, můžete spustit lint okamžitě, jakmile AI upraví soubor, ještě než se vůbec pokusí o commit. Vytvořte .cursor/hooks.json v kořeni projektu:

{
  "hooks": {
    "afterFileEdit": [
      {
        "match": "*.py",
        "run": "ruff check {file} --fix"
      },
      {
        "match": "*.{ts,tsx,js}",
        "run": "npx eslint {file} --fix"
      },
      {
        "match": "*.go",
        "run": "golangci-lint run {file}"
      }
    ]
  }
}

Spustí se pokaždé, když AI v Cursoru upraví soubor. Agent dostane zpětnou vazbu lintu přímo na místě, než úkol považuje za hotový, takže většina porušení se opraví dříve, než se dostane k pre-commit bráně.

Jak ztížit obejití brány AI agenty

Diagram of four enforcement layers (CLAUDE.md policy, Claude Code deny rule, PreToolUse hook, and CI backstop) blocking an AI agent from skipping the lint gate with --no-verify

Čtyři vynucovací vrstvy výrazně ztěžují AI agentovi přeskočit pre-commit bránu pomocí --no-verify. Každá cílí na jiný útočný povrch: politika v CLAUDE.md, deny pravidlo v Claude Code, PreToolUse hook a CI pojistka. Berte je jako vrstvené nastavení, ne jako čtyři nezávislé záruky.

AI agenti někdy předají --no-verify do git commit, aby přeskočili pre-commit hooky, když lint selhává a agent rozhodl, že selhání "nesouvisí s jeho změnami". To rozhodnutí nemusí být vždy špatné, ale neměli byste nechat agenta, aby ho dělal jednostranně. Celý smysl lint brány je v tom, že politiku nastavil člověk; úkolem agenta je ji splnit, ne ji obejít.

Zde je každá vrstva a útočný povrch, který pokrývá.

Vrstva 1: zdokumentujte politiku v CLAUDE.md

## Linting Policy

NEVER use `git commit --no-verify`. All commits must pass pre-commit hooks.
Pre-commit hooks run lint. Fix lint violations before committing. Do not treat
lint failures as unrelated to your changes — they may not be, and you don't get
to decide that.

Claude Code čte CLAUDE.md na začátku sezení. To není vynucování; agent se o obejití stále může pokusit. Ale odstraňuje to cestu "nevěděl jsem" a stanovuje jasnou politiku, kterou musí agent aktivně zvolit porušit, což je méně pravděpodobné, než aby si obejití domyslel z mlčení.

Vrstva 2: deny pravidlo v Claude Code

Přidejte do .claude/settings.json:

{
  "permissions": {
    "deny": [
      "Bash(git commit --no-verify*)"
    ]
  }
}

Toto zablokuje explicitní vyvolání. Jedno omezení: deny pravidlo používá shodu podle prefixu, takže zachytí --no-verify pouze bezprostředně po commit. Dostatečně kreativní agent by mohl volání strukturovat jinak. Nespoléhejte se na to samotné.

Vrstva 3: PreToolUse hook

Nainstalujte balíček block-no-verify:

npm install --save-dev block-no-verify

Poté ho nakonfigurujte jako PreToolUse hook v nastavení Claude Code. Spustí se před každým voláním nástroje a prozkoumá argumenty na --no-verify napříč šesti git podpříkazy, nejen commit. Skončí s nenulovým kódem, aby volání zablokoval ještě před jeho provedením.

Příručka na adrese pydevtools.com to říká bez obalu: "vrstva hooku je jediná, která pravidlo spolehlivě vynucuje." Používejte ji s ostatními vrstvami; nepovažujte žádný lokální hook za celou záruku.

Vrstva 4: CI pojistka

CI běží na serveru, kde agent nemá žádný shell, do kterého by mohl předat flagy:

# .github/workflows/lint.yml
name: Lint
on: [push, pull_request]

jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Lint Python
        run: |
          pip install ruff
          ruff check . --output-format github

      - name: Lint JS/TS
        run: npx eslint . --max-warnings 0

      - name: Lint Go
        uses: golangci/golangci-lint-action@v9
        with:
          version: v2.12.2

AI agenti nemohou předat --no-verify do CI. GitHub Actions běží nezávisle na tom, co agent udělal lokálně. Pokud nějakým způsobem prošel commit s neúspěšným lintem, CI ho zachytí dříve, než se sloučí.

Toto je poslední pojistka.

Bonus: ESLint MCP Server

Pokud používáte Claude Code, existuje proaktivní vrstva, která snižuje, jak často na bránu vůbec narazíte.

Server ESLint MCP (@eslint/mcp) integruje ESLint přímo do nástrojové smyčky agenta. Agent může dotazovat ESLint během úkolu, ještě než se pokusí o commit. Nainstalujte ho globálně:

npm install -g @eslint/mcp@latest

Přidejte do .claude/settings.json:

{
  "mcpServers": {
    "eslint": {
      "command": "npx",
      "args": ["@eslint/mcp@latest"]
    }
  }
}

S tímto nastavením může agent dotazovat ESLint během úkolu. Agent dostává zpětnou vazbu lintu přímo na místě a některá porušení lze opravit dříve, než se dostanou k hooku. Toto bránu nenahrazuje, snižuje na ní šum.

Časté dotazy

Musím nakonfigurovat všechny tři lintery, když pracuji jen v jednom jazyce?

Ne. Nastavte linter pro svůj hlavní jazyk a vynucovací vrstvu. Pokud je váš stack pouze TypeScript, nakonfigurujte ESLint a přeskočte Ruff a golangci-lint. Sekce o prevenci obcházení agentem platí bez ohledu na to, jaký jazyk lintujete.

Rozbijí mi tyto konfigurace moji stávající kódovou bázi při prvním spuštění?

Téměř jistě ano, a to záměrně. Spusťte ruff check --fix . nebo npx eslint . --fix, abyste nejprve automaticky opravili bezpečná porušení. Co zbude po automatické opravě, je seznam k ruční revizi: přetypování no-explicit-any, funkce přesahující 50 řádků, chybějící zpracování chyb. Procházejte je postupně. Nepřidávejte ignore pravidla, abyste se jim vyhnuli.

Je Biome v tuto chvíli náhradou za ESLint?

Biome v2.5.0 (vydáno v červnu 2026) je konkurenceschopný ve formátování a základních pravidlech lintu. Je rychlejší než ESLint a má nulovou konfigurační režii, pokud ho nainstalujete přes bun x ultracite@latest init. Pro týmy, které chtějí jediný nástroj a nepotřebují plnou hloubku pravidel @typescript-eslint, je Biome rozumná volba. Pro pravidla specifická pro AI v tomto návodu (max-params, no-magic-numbers, max-lines-per-function s prahy cílenými na AI) má ESLint s @typescript-eslint stále větší pokrytí. Můžete spustit oba: Biome pro formátování a základní lint, ESLint pro pravidla specifická pro AI.

Co když můj AI agent stále znovu generuje stejná porušení lintu?

Agent obchází pravidlo, místo aby opravil základní problém. U no-explicit-any to znamená přidání typového přetypování namísto definování skutečného typu. U max-lines-per-function to znamená vytažení pomocné funkce, která nedělá nic užitečného, ale dostane počet řádků pod práh. Ani jedno řešení neprojde revizí kódu. Pravidlo lintu zachytilo symptom; kořenovou příčinou je prompt agenta. Upřesněte prompt tak, aby specifikoval typové omezení nebo očekávaný rozklad; agent bude spolehlivěji následovat explicitní pokyny ke struktuře než implicitní pravidla. Pokud provozujete složitější nastavení agentů, vymezení práce dedikovanému subagentovi s omezeními zabudovanými do jeho instrukcí drží obvykle lépe než jediný široký prompt.

Nahrazuje server ESLint MCP pre-commit bránu?

Ne. Snižuje, jak často agent generuje kód, který bránou neprojde. Brána stále běží při každém commitu. Inline kontrola serverem MCP a vynucování pre-commit hookem se doplňují, takže neodstraňujte ani jedno.

Share

Další z blogu

Pokračuj ve čtení.

Hotov k nasazení? Od 2,48 $/měs.

Nezávislý cloud od roku 2008. AMD EPYC, NVMe, 40 Gbps. Vrácení peněz do 14 dnů.