Skip to content
# AI-Agent Prompt — Configure Linters for FastAPI (Python) + Next.js (TypeScript)

## Non-negotiables
- Only add **tooling** (linters/formatters/type checks). Do **not** change runtime behavior unless a linter autofix requires a safe mechanical edit.
- Changes must be **reproducible** via pinned versions and committed config files.
- After implementation, run the verification commands in the “Verification” section and ensure they pass.

## Why we’re doing this (brutal truth)
Linters/formatters catch a lot of AI-generated garbage (syntax mistakes, unused imports, unsafe patterns, inconsistent style). They **will not** catch most business-logic bugs. They reduce noise and prevent obvious failures from shipping — that’s it.

---

## Pin the “latest known” tool versions (as of 2025-12-18)
### Frontend (Node)
- Next.js: **16.0.10** :contentReference[oaicite:0]{index=0}
- ESLint: **9.39.2** :contentReference[oaicite:1]{index=1}
- Prettier: **3.7.4** :contentReference[oaicite:2]{index=2}
- eslint-config-next: **16.0.10** :contentReference[oaicite:3]{index=3}
- lint-staged (optional): **16.2.7** :contentReference[oaicite:4]{index=4}

### Backend (Python)
- Ruff: **0.14.9** :contentReference[oaicite:5]{index=5}
- pre-commit: **4.5.1** (optional, but recommended) :contentReference[oaicite:6]{index=6}
- Pyright: **1.1.407** (optional, but recommended) :contentReference[oaicite:7]{index=7}

---

## Step 0 — Detect project roots (zero guessing)
1. Locate the **Python backend root** = the directory that contains `pyproject.toml` (or the backend’s `pyproject.toml`).
2. Locate the **Next.js frontend root** = the directory that contains `package.json` + `next.config.*` (or `app/` / `pages/`).

Do all edits in the correct root(s).

---

# A) Python (FastAPI backend) — Ruff (+ optional pre-commit, optional Pyright)

## A1) Install Ruff (pinned)
In the backend root, add Ruff **0.14.9** to your dev dependencies using whichever dependency manager is already in the repo.
- Ruff is distributed on PyPI and installed via pip tooling. :contentReference[oaicite:8]{index=8}

**If the repo uses requirements files**, add:
- `requirements-dev.txt` (or equivalent):
  - `ruff==0.14.9` :contentReference[oaicite:9]{index=9}

## A2) Configure Ruff in `pyproject.toml`
Ruff supports configuration in `pyproject.toml`. :contentReference[oaicite:10]{index=10}

Add (or merge) this in the backend `pyproject.toml`:

```toml
[tool.ruff]
line-length = 88
extend-exclude = [".venv", "venv", "dist", "build", ".mypy_cache", ".pytest_cache"]

[tool.ruff.lint]
# Start simple: keep Ruff defaults, only make it enforce import sorting.
# (Ruff can act as linter/formatter; keep config minimal to avoid breaking existing code.) :contentReference[oaicite:11]{index=11}
select = ["I"]

Then run:

ruff check . --fix
ruff format .

Ruff’s PyPI docs show ruff check and ruff format usage patterns. (PyPI)

Install pre-commit (4.5.1). (PyPI)

Create .pre-commit-config.yaml in the backend root (or repo root if you run hooks repo-wide):

repos:
  - repo: https://github.com/astral-sh/ruff-pre-commit
    rev: v0.14.9
    hooks:
      - id: ruff
        args: [--fix]
      - id: ruff-format

This exact Ruff pre-commit configuration format is documented on Ruff’s PyPI page. (PyPI)

Then run:

pre-commit install
pre-commit run --all-files

pre-commit autoupdate is the documented way to update hooks later. (Pre-Commit)

Pyright supports config via pyrightconfig.json or [tool.pyright] in pyproject.toml, and pyrightconfig.json takes precedence if both exist. (GitHub)

To keep everything in one file, add to backend pyproject.toml:

[tool.pyright]
include = ["src"]
exclude = ["**/__pycache__", ".venv", "venv", "dist", "build"]
strict = ["src"]

Pyright’s official config docs describe include, exclude, and strict (strict enables most type-checking rules for those paths). (GitHub)

Run:

pyright

B) Next.js frontend — ESLint (Next.js 16) + Prettier (+ optional lint-staged)

B1) IMPORTANT: Next.js 16 removed next lint

Starting with Next.js 16, next lint is removed, and the eslint option in next.config.* is no longer needed and can be removed. (Next.js)

So: do not add next lint scripts. Use ESLint CLI.

B2) Install frontend dev dependencies (pinned)

In the frontend root:

  • eslint@9.39.2 (npm)
  • eslint-config-next@16.0.10 (npm)
  • prettier@3.7.4 (npm)
  • eslint-config-prettier@10.1.8 (npm)

Next.js recommends eslint-config-prettier in flat config to avoid ESLint formatting conflicts with Prettier. (Next.js)

Security note (don’t ignore): multiple compromised versions of eslint-config-prettier have been reported (incl. 10.1.6 and 10.1.7). Pin to a safe version (current latest: 10.1.8). (npm)

B3) Create eslint.config.mjs (Next.js 16 flat config)

Create (or replace) eslint.config.mjs in the frontend root:

import { defineConfig, globalIgnores } from "eslint/config";
import nextVitals from "eslint-config-next/core-web-vitals";
import nextTs from "eslint-config-next/typescript";
import prettier from "eslint-config-prettier/flat";

export default defineConfig([
  ...nextVitals,
  ...nextTs,
  prettier,
  // Override default ignores of eslint-config-next:
  globalIgnores([
    ".next/**",
    "out/**",
    "build/**",
    "next-env.d.ts",
  ]),
]);

This structure (core-web-vitals, typescript config, prettier flat config, and globalIgnores list) is shown in the official Next.js ESLint configuration docs. (Next.js)

B4) Add scripts to package.json

In frontend package.json, add/ensure:

{
  "scripts": {
    "lint": "eslint . --max-warnings 0",
    "lint:fix": "eslint . --fix",
    "format": "prettier . --write",
    "format:check": "prettier . --check"
  }
}

ESLint CLI setup is documented by ESLint. (ESLint)

In tsconfig.json (frontend root), set:

{
  "compilerOptions": {
    "strict": true
  }
}

The TypeScript docs state that strict enables a wide range of strict type-checking behavior for stronger correctness guarantees. (typescriptlang.org)

Install lint-staged@16.2.7. (npm)

Create .lintstagedrc.js in the frontend root (start from Next.js docs example; extend it if needed):

const path = require("path");

const buildEslintCommand = (filenames) =>
  `eslint --fix ${filenames
    .map((f) => `"${path.relative(process.cwd(), f)}"`)
    .join(" ")}`;

module.exports = {
  "*.{js,jsx,ts,tsx}": [buildEslintCommand],
};

This exact pattern is provided in the Next.js ESLint docs under “Running lint on staged files”. (Next.js)


Verification (must pass before you stop)

Backend (run in backend root)

ruff check .
ruff format . --check

Ruff supports check and format as shown in its docs. (PyPI)

If you enabled Pyright:

pyright

Pyright configuration and usage are documented. (GitHub)

Frontend (run in frontend root)

npm run lint
npm run format:check

If you enabled TypeScript strict checks, also run your usual typecheck script (or add one).


Deliverables you must commit

Backend:

  • pyproject.toml updates (Ruff, optional Pyright config)
  • (optional) .pre-commit-config.yaml

Frontend:

  • eslint.config.mjs (Next.js 16 flat config) (Next.js)
  • package.json script updates
  • (optional) .lintstagedrc.js (Next.js)
  • tsconfig.json strict=true (optional) (typescriptlang.org)