JavaScript / TypeScript
- ESLint z twoją wspólną konfiguracją
- Prettier do formatowania
- typescript-eslint dla reguł lintingu świadomych TypeScriptu
tsc --noEmitdo sprawdzania typów
Pewien null prześlizgnął się przez przegląd, trafił na produkcję w piątek i wybudził cię o drugiej w nocy. Diff wyglądał w porządku — zatwierdziło go trzech recenzentów — ale nikt nie zauważył niezabezpieczonego response.data.user.id na ścieżce, która uruchamia się wyłącznie przy logowaniu przez SSO. Pytanie z retrospektywy jest bezlitosne, ale słuszne: „Jak czworo ludzi mogło to przeoczyć?”.
Przeoczyli to, bo przegląd przez człowieka to niewłaściwe narzędzie do wychwytywania mechanicznych defektów. Styl, dziury w typach, zapytania N+1, brakująca walidacja danych wejściowych i nieobsłużone odrzucenia — to dokładnie to, co agent AI wpięty w bramę jakości wyłapuje za każdym razem, zanim jakikolwiek człowiek otworzy PR. Ten artykuł pokazuje, jak zbudować taką bramę w Cursorze, Claude Code i Codeksie, aby recenzenci poświęcali uwagę architekturze i intencji zamiast udawać linter.
.cursor/rules, CLAUDE.md lub AGENTS.md), który każdy agent egzekwuje spójnietsc --noEmit na każdym pliku edytowanym przez agenta — z poprawnym schematem kluczowanym zdarzeniem i ścieżką pliku ze stdinany na typowany interfejs oraz test obciążeniowy k6 z prawdziwymi progamiChcesz wyłapywać defekty tak wcześnie i tak tanio, jak to możliwe. To oznacza trzy warstwy, z których każda wyłapuje to, co przepuściła poprzednia:
Reszta tego artykułu buduje każdą z warstw. Warstwy 1 i 2 są tam, gdzie te trzy narzędzia się różnią, więc korzystają z <Tabs>.
Wszystkie trzy narzędzia czytają plik reguł na poziomie projektu i stosują go do wszystkiego, co generują. Format i lokalizacja pliku się różnią; treść jest niemal identyczna. Trzymaj go w kontroli wersji, aby cały zespół — i każdy agent — pracowali według tego samego standardu.
---description: Enterprise Code Quality StandardsalwaysApply: true---## Style- 2-space indentation, max line length 100- Every exported function has a JSDoc block- No `any` without a `// eslint-disable-next-line` and a reason
## Architecture- Data access goes through the repository layer, never inline SQL in handlers- Services receive dependencies via constructor injection- All outbound HTTP calls go through the shared `httpClient` wrapper
## Performance- Paginate any endpoint that returns a list- No queries inside loops — batch with `IN (...)` or a join- Memoize pure functions that run on every render
## Security- Parameterized queries only- Validate request bodies with the Zod schema in `schemas/`- Never log tokens, passwords, or full request bodies## Coding Standards
### Style- ESLint config: `.eslintrc.json`; Prettier: `.prettierrc`- TypeScript strict mode; no `any` without an inline justification comment- No `console.log` in committed code — use the `logger` module
### Quality gates- Coverage floor: 80% on changed lines- Cyclomatic complexity limit: 10 (enforced by `eslint-plugin-complexity`)- Every TODO references a ticket: `// TODO(PROJ-1234): ...`
### Before you finish a task- Run `npm run lint && npm run typecheck && npm test`- Add or update tests for new behavior- Update the relevant doc in `docs/` if you changed a public API## Project standards
Codex reads AGENTS.md from the repo root (and merges nested ones insubdirectories). Same rules as the other tools — keep them in sync.
### Style- 2-space indentation, max line length 100, Prettier-formatted- TypeScript strict; no `any` without a justification comment- Use `typescript-eslint` rules, not legacy formatting lint
### Quality gates- 80% coverage on changed lines; complexity limit 10- Parameterized queries only; validate inputs with Zod- Run `npm run lint && npm run typecheck && npm test` before declaring doneNajczęstszym błędem jest tutaj konfiguracja hooka, która po cichu nigdy się nie uruchamia. Claude Code zagnieżdża tablice hooków pod nazwą zdarzenia (PostToolUse, PreToolUse) wewnątrz obiektu hooks najwyższego poziomu — goła tablica hooks na najwyższym poziomie się nie załaduje. Hooki nie otrzymują też edytowanej ścieżki w zmiennej środowiskowej; czytają JSON ze stdin i wyciągają .tool_input.file_path.
Umieść logikę w skrypcie, aby konfiguracja pozostała czytelna:
{ "hooks": { "PostToolUse": [ { "matcher": "Edit|Write", "hooks": [ { "type": "command", "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/format-and-lint.sh" } ] } ] }}#!/usr/bin/env bashset -euo pipefail
# The edited path arrives as JSON on stdin, not as an env var.FILE_PATH=$(jq -r '.tool_input.file_path // empty')[ -z "$FILE_PATH" ] && exit 0
npx prettier --write "$FILE_PATH"npx eslint --fix "$FILE_PATH"
# Type-check only TS files; tsc does the type checking, not a linter.case "$FILE_PATH" in *.ts|*.tsx) npx tsc --noEmit ;;esac$CLAUDE_PROJECT_DIR to jedna z nielicznych prawdziwych zmiennych hooków (obok $CLAUDE_ENV_FILE dla SessionStart i $CLAUDE_CODE_REMOTE). Otocz ją cudzysłowami, aby ścieżki ze spacjami przetrwały.
W Cursorze odpowiednikiem jest pętla auto-naprawy: gdy błędy ESLint trafiają do panelu Problems, agent je naprawia i uruchamia ponownie, aż będzie czysto. Codex stosuje ten sam krok eslint --fix w swoim sandboksie, gdy poprosisz go o „make lint pass” w ramach zadania.
JavaScript / TypeScript
tsc --noEmit do sprawdzania typówPython
Java
Go
To tutaj brama zarabia na swoje utrzymanie. Konfiguracja jest naprawdę trójnarzędziowa: każde narzędzie uruchamia bezgłowego agenta na diffie PR i publikuje uwagi.
Wbudowanym przeglądem PR w Cursorze jest BugBot. Włącz go z integracji GitHub w panelu, a następnie wrzuć .cursor/BUGBOT.md do katalogu głównego repo, aby sterować tym, co oznacza (zobacz wytyczne przeglądu poniżej). Po połączeniu BugBot automatycznie komentuje inline na PR.
Dodaj serwer MCP GitHuba (zdalny HTTP — nie ma wbudowanego skrótu github, a transport oraz URL są wymagane):
claude mcp add --transport http github https://api.githubcopilot.com/mcp/# Auth via OAuth on first use, or pass a token:# --header "Authorization: Bearer $GITHUB_PAT"Konkretnie do automatyzacji PR zainstaluj aplikację GitHub App, aby można było wzmiankować Claude’a na PR-ach:
/install-github-appUżyj Codex Cloud code review: połącz repo w panelu Codex Cloud i włącz automatyczny przegląd dla pull requestów. Codex czyta AGENTS.md w poszukiwaniu twoich standardów i publikuje komentarze przeglądu. Do doraźnego przeglądu lokalnego uruchom bezgłowy krok codex exec pokazany w następnej sekcji.
Zarówno .cursor/BUGBOT.md w Cursorze, jak i prompt podany Claude Code lub Codeksowi zyskują na jawnej liście kontrolnej. Skup ją na tym, co ludzie regularnie przeoczają:
# .cursor/BUGBOT.md (or paste into the review prompt)
## Security (block on any of these)- Hardcoded credentials, tokens, or API keys- Unparameterized SQL or string-concatenated queries- Unvalidated request bodies reaching the database- Missing auth check on a protected route- User input rendered without escaping (XSS)
## Correctness- Unhandled promise rejections / missing `await`- Null/undefined dereferences on optional fields- N+1 query patterns (a query inside a `.map`/loop)
## Quality- New code without tests- Functions over 50 lines or complexity over 10- Logging that includes sensitive dataWrzuć to do dowolnego repo. Uruchamia się na pull requestach i publikuje uwagi agenta. Zwróć uwagę na actions/checkout@v6 — @v3 jest wycofany i wymusza uruchamianie akcji JavaScript na nieobsługiwanym środowisku Node.
BugBot działa jako hostowana integracja GitHub, więc nie ma żadnego YAML-a CI do utrzymywania — po włączeniu przegląda PR-y automatycznie. Skorzystaj z zakładki Claude Code lub Codex, jeśli zamiast tego chcesz, aby krok przeglądu mieszkał w twoim własnym pliku workflow.
name: AI Code Reviewon: [pull_request]
jobs: review: runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 with: fetch-depth: 0 - name: AI review env: ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} run: | git diff origin/${{ github.base_ref }}...HEAD > diff.patch claude -p "Review the diff in diff.patch against .cursor/BUGBOT.md. \ Report only real defects as 'file:line — issue — fix', \ grouped by Security / Correctness / Quality. \ If nothing is wrong, say 'No blocking issues.'" \ --output-format json > review.jsonname: AI Code Reviewon: [pull_request]
jobs: review: runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 with: fetch-depth: 0 - name: AI review env: OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} run: | git diff origin/${{ github.base_ref }}...HEAD > diff.patch codex exec --full-auto \ "Review diff.patch against AGENTS.md. Report only real \ defects as 'file:line — issue — fix', grouped by \ Security / Correctness / Quality."Najużyteczniejszym nawykiem w edytorze jest zabijanie any w momencie, gdy się pojawi. Pętla auto-naprawy Cursora robi to przy włączeniu, ale prompt działa we wszystkich trzech narzędziach.
Czysty wynik wygląda tak — rzutowanie staje się nazwanym, sprawdzalnym kontraktem:
interface UserResponse { id: string; status: 'active' | 'inactive'; metadata: Record<string, unknown>;}
const data = response.data as UserResponse;Prawdziwe monitorowanie oznacza prawdziwe narzędzia, a nie funkcję zwracającą zaszyte na sztywno liczby. SonarQube (lub SonarCloud) to standard: liczy pokrycie, złożoność cyklomatyczną i duplikację przy każdym buildzie oraz śledzi trend. Wepnij go w ten sam workflow:
# add to .github/workflows/ai-review.yml - name: SonarQube scan uses: SonarSource/sonarqube-scan-action@v6 env: SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} with: args: > -Dsonar.qualitygate.wait=trueFlaga qualitygate.wait=true blokuje PR, jeśli brama jakości Sonara dla projektu nie przejdzie (na przykład pokrycie nowego kodu poniżej 80% albo nowy problem o krytyczności blocker). To twój punkt egzekwowania — konkretny, zmierzony i nie do podrobienia przez agenta.
Aby uzyskać odpowiedź na pytanie „co AI o tym sądzi?”, podaj agentowi wyniki Sonara, zamiast prosić go o wymyślanie metryk:
Częstą regresją na produkcji jest zapytanie lub endpoint, które działają świetnie podczas przeglądu i padają pod obciążeniem. Wpisz testy obciążeniowe w bramę za pomocą k6 — progi są prawdziwe i sprawiają, że test sam przechodzi lub nie przechodzi.
Wygenerowany test koduje progi jako warunki bramy, więc regresja zmienia krok CI na czerwony:
import http from 'k6/http';import { check } from 'k6';
export const options = { stages: [ { duration: '2m', target: 200 }, { duration: '5m', target: 200 }, { duration: '2m', target: 0 }, ], thresholds: { http_req_duration: ['p(95)<500'], http_req_failed: ['rate<0.01'], },};
export default function () { const res = http.post( `${__ENV.BASE_URL}/api/checkout`, JSON.stringify({ cartId: 'c_1', paymentMethodId: 'pm_1', idempotencyKey: `${__VU}-${__ITER}` }), { headers: { 'Content-Type': 'application/json' }, tags: { name: 'checkout' } }, ); check(res, { 'status 200': (r) => r.status === 200 });}Gdy powyższe prompty okażą się przydatne, zapisz je jako wielokrotnego użytku polecenia slash. Plik .claude/commands/security-audit.md staje się poleceniem /security-audit w interaktywnej sesji Claude Code (podkatalogi dodają przestrzenie nazw — .claude/commands/review/pr.md to /review:pr). Wywołaj je w REPL:
> /security-auditprzy czym plik polecenia zawiera twój prompt skupiony na OWASP. Cursor udostępnia tę samą ideę przez zapisane prompty; Codex przez workflow w AGENTS.md i niestandardowe prompty.