# Migrate an Express Project to ESLint Flat Config

Send Rush your Express repository to migrate ESLint config, preserve rule coverage, and return a pull request with lint evidence.

[Run this on Rush](https://cloud.getrush.ai/run/migrate-eslint-flat-config/express/), in a Project that has your repository.

## What the agent does

In an Express app like gothinkster/node-express-realworld-example-app, the migration starts with the lint runner. package.json declares ESLint ~8.48.0, @nx/eslint and @nx/eslint-plugin 17.2.6, and TypeScript ESLint packages ^6.9.1. It has start, build and test scripts, but no lint script. The agent reads project.json, where the api lint target uses @nx/eslint:lint over ./src, and e2e/project.json, which declares a separate lint target. It installs from package-lock.json with npm ci and records the baseline from npx nx run-many --target=lint --all --skip-nx-cache, including errors that prevent a baseline from completing.

The configuration inventory includes .eslintrc.json, e2e/.eslintrc.json and .eslintignore. The root config selects Nx TypeScript and JavaScript presets and gives Jest globals to *.spec files. The e2e config repeats the language presets. Both contain the negated ignore pattern !**/*, while .eslintignore names node_modules. The agent compares resolved settings for application code, a service test and an e2e spec before changing anything. Empty rules objects do not mean there are no rules: the inherited presets are part of the behavior to preserve.

The agent checks the current ESLint release and the compatible Nx and TypeScript ESLint versions before editing package.json and package-lock.json. It writes eslint.config.js using the module format the project supports, maps file selection and globals explicitly, and accounts for ignores in the new configuration. The official ESLint migration guide at https://eslint.org/docs/latest/use/configure/migration-guide explains these format changes. Any rule without an equivalent is listed for review. The task does not authorize silencing warnings by dropping a preset, excluding source directories or adding blanket disable comments.

Finally, the agent reruns both Nx lint targets without cached results and compares the same file set against the baseline. nx.json already lists eslint.config.js among lint inputs alongside the legacy files, so those cache inputs need review as the old configs are retired. A direct whole-repository lint invocation checks whether configuration files or tests fall outside the Nx targets; added coverage is reported separately from changed diagnostics. npm test and npm run build check that dependency changes still support the existing test and build paths. The report distinguishes an executable lint configuration from a migration that actually preserves coverage.

## Start it with one API call

`POST https://api.prix.dev/o/{org}/sessions`

```json
{
  "agent": "codex",
  "input": "Migrate this Express repository from ESLint 8 and .eslintrc to the current ESLint with eslint.config.js. Inspect root and nested configurations, Nx lint targets, ignores and resolved rules first. Record uncached lint errors and warning counts before editing. Preserve every rule and plugin with a flat-config equivalent; list anything without one. Update compatible lint dependencies and the npm lockfile, retire superseded configs and update Nx cache inputs. Run all lint targets and whole-repository lint, compare the same files before and after, then run tests and build. Open a pull request with commands, diagnostic counts, coverage differences and unresolved failures. Do not hide failures by disabling rules.",
  "resources": [
    {
      "url": "https://github.com/your-org/your-app"
    }
  ]
}
```

1. **Get an API key.** In the console, open Settings, then API keys, and create a key for your org. Send it in the Authorization header as a Bearer token.
2. **Send the request.** POST the body in request.json to https://api.prix.dev/o/{org}/sessions, with your repository as the resources URL. The run starts in its own cloud sandbox.
3. **Read what came back.** Follow the run on its live stream or wait for the run.completed webhook, then read the result and its trajectory.

## What comes back

The requested pull request contains eslint.config.js, the dependency and lockfile changes, and any necessary Nx target adjustments. Its description maps the old presets, file patterns, globals and ignores to their replacements. It includes before-and-after lint commands, their exit status and warning counts, with pre-existing errors identified. If incompatible tooling prevents completion, the result names the package and failure instead of presenting the migration as verified.

A reviewer checks the effective rules on representative files and confirms that e2e code remains included. Fewer warnings are meaningful only when the same files and rules are still checked. Review dependency upgrades separately from source fixes, and look for accidental changes to module loading or formatting policy. The session read returns the latest finished answer and repository or PR information; its transcript lets you inspect the commands and their output behind the summary.

## Run it on every lint migration request

Your issue automation or terminal script sends POST /o/{org}/sessions with the repository URL and this migration prompt when a project is ready to upgrade. Install the Rush GitHub App on that repository first, then authenticate the request with an API key. Use an Idempotency-Key header when retrying the same submission so a network retry does not start another migration.

Keep the returned execution_id to revisit the job. Your server can read the session or receive the key’s completion webhook. Once the turn finishes, send a follow-up asking the agent to explain a changed rule or restore a missing file pattern; the messages endpoint wakes the parked sandbox. A follow-up while the turn is active is refused, so check its status first.

## Why run it on Rush?

Lint migrations involve repeated installations, configuration experiments and comparisons. Rush gives the agent its own cloud sandbox, so this work can continue without tying the migration to the terminal where you submitted it. The transcript links tool calls with their results, making a claimed warning reduction something you can examine rather than accept from a summary alone.

For several Express services, submit a separate repository per run within your key’s concurrency allowance. Each migration can inspect its own Nx version and inherited presets. You review the resulting changes independently, and use follow-ups to resolve differences before merging. The useful proof is a preserved lint policy with recorded commands, including any failures that remain.

## Frequently asked questions

### Will this preserve my existing rules?

That is the acceptance criterion in the prompt. The agent compares effective configuration as well as the visible rules objects, because Nx presets supply inherited rules. Any missing equivalent or intentional coverage change belongs in the review notes.

### Does this example use npm or pnpm?

The fixture has package-lock.json and its README uses npm. The migration should keep that package manager and regenerate its lockfile when dependencies change. A different repository needs its own lockfile and scripts inspected first.

### Can I leave my terminal and pick this up later?

Yes. Save the execution_id returned by the API and read the session when you return. After the turn finishes, a follow-up wakes its parked sandbox; the quickstart specifies a retention period of 7 idle days.

### Does a passing lint command prove the migration is correct?

It proves only what that invocation checked. Compare file coverage, inherited rules and warning counts before accepting the pull request. An ignored directory can make the command pass while removing checks you intended to keep.
