All jobs

Upgrade npm Dependencies in a Next.js App

Yes. Each week an agent upgrades a Next.js app's npm dependencies in groups with pnpm, keeps what builds and type-checks, and opens one pull request with each bump.

Run this on RushIn a Project that has your repository.

What the agent does

The agent reads the manifest and the lockfile to see what moves together. In nextjs/saas-starter package.json pins next to 15.6.0-canary.59, react and react-dom to 19.1.0, @types/react and @types/react-dom to 19.1.4 and 19.1.5, and tailwindcss and @tailwindcss/postcss to 4.1.7, exact; the rest use carets: drizzle-orm 0.43 with drizzle-kit 0.31, postgres 3.4, stripe 18, jose 6, bcryptjs 3, zod 3.24, swr 2.3, radix-ui 1.4, lucide-react 0.511, class-variance-authority, clsx and tailwind-merge. pnpm-lock.yaml is lockfile version 9, so the sandbox uses pnpm. There is no test or lint script and no test file, so the checks are pnpm build and npx tsc --noEmit, plus a browser pass over sign-up, sign-in and the dashboard against a local Postgres.

The canary pin is the first decision. next.config.ts turns on experimental.ppr and clientSegmentCache, which Next.js only allows on the canary channel; moving next to a stable release makes next build refuse to start until those flags are removed. The agent therefore upgrades within the canary line by default and lists the stable move as a choice, or, if the prompt says to go stable, removes the two flags in the same commit and says so. react, react-dom and their types move together with next, since the canary pins a React version.

Then the groups. Patch and minor bumps run in one pnpm up with the build and type check after; majors go one at a time. zod 4 changes the error object that lib/auth/middleware.ts reads as result.error.errors[0].message in both validatedAction and validatedActionWithUser, so that major is taken only if the type check passes or with a one-line change called out in the description. stripe majors move the SDK's pinned apiVersion (2025-04-30.basil in lib/payments/stripe.ts) and can change the shape of prices.list and products.list, so the agent reads the SDK changelog for the methods used in lib/payments/stripe.ts and app/api/stripe/webhook/route.ts. drizzle-orm and drizzle-kit move as a pair and the agent runs pnpm db:generate afterwards to confirm the schema produces no unexpected migration. lucide-react bumps are checked by the type check, which fails on a renamed icon. Tailwind 4 minors are taken together with @tailwindcss/postcss.

Every group that passes is a commit; a group that fails is reverted with the error kept. The agent starts Postgres, writes .env from .env.example with placeholders (a Stripe key only if a Project secret provides one), runs pnpm db:migrate, then pnpm build, npx tsc --noEmit, and opens the app in its browser to sign up, sign in and load /dashboard, since the middleware and the session cookie are what a jose or next bump would break. The pull request lists each bump with old and new versions, the skips with their reason, and the three checks' output.

Start it with one API call

POST https://api.prix.dev/o/{org}/sessions with this body:

{
  "agent": "claude",
  "input": "Upgrade this app's npm dependencies with pnpm in groups: patch and minor releases together, then each major on its own, keeping next, react, react-dom and their type packages in step and staying on the canary channel unless told otherwise, because next.config.ts enables experimental.ppr. After each group run pnpm build and npx tsc --noEmit, and after the whole set start a local Postgres, run pnpm db:migrate and check sign-up, sign-in and /dashboard in the browser. Keep a group only if everything passes; otherwise revert it and record the error. Open one pull request with one commit per group, one line per bump with old and new versions, and every upgrade left out with the reason.",
  "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

What comes back is a pull request on a new branch that changes package.json and pnpm-lock.yaml, in one commit per group that passed, with at most a one-line source change where a major required it (for example the zod error field in lib/auth/middleware.ts), named in the description. next.config.ts is unchanged unless the prompt asked for the move to stable.

The description is one line per bump with old and new versions, a list of upgrades left out with the build or type error that excluded them, and the result lines of pnpm build, tsc --noEmit and the browser pass. The trajectory keeps the full output for every group, including the reverted ones, and the screenshots from the dashboard check.

Before merging, a reviewer checks that next, react, react-dom and their types moved together, that drizzle-kit generate produced no migration, that the Stripe webhook route still constructs events with the SDK version in the lockfile, and that a left-out major is not a security fix that should be taken with a code change instead.

Run it on every Monday morning

Create an automation in the Project with this prompt and a schedule trigger (cron 0 9 * * 1 in your timezone). Each week the run opens one pull request with that week's bumps, or none when nothing moved. Because each group is its own commit, dropping a bump before merge is a revert of one commit.

Give the API key a webhook URL to receive run.completed with the session_url; a handler on your side can post the pull request link to the frontend channel. A run that ends with no safe group opens no pull request and says why in its result.

The use case this job follows, with its automation and trigger: Upgrade dependencies weekly.

Why run it on Rush?

Bot upgrade pull requests on a Next.js app are red more often than green, and the red ones sit until someone has an afternoon: a canary pin, a React version lock-step, a zod major that changes an error field. On Rush the agent runs the build and the type check after each group in a sandbox, reverts what fails, and keeps the output, so the pull request that reaches you is already green and the failures are written down rather than left for you to rediscover.

The browser pass over sign-up, sign-in and the dashboard is the kind of check a bot cannot do and a person forgets. The record shows it, along with the commands, and the same request runs on every Next.js app you maintain in parallel.

Frequently asked questions

Why stay on the Next.js canary channel?

Because next.config.ts enables experimental.ppr and clientSegmentCache, which a stable release refuses at build time. The agent upgrades within canary and lists the stable move as a decision; tell it in the prompt to go stable and it removes the two flags in the same commit.

There are no tests. What stops a bad bump?

pnpm build, tsc --noEmit and the browser pass over sign-up, sign-in and the dashboard with a local Postgres. A renamed lucide-react icon or a changed zod error field fails the type check; a jose or next change that breaks the session cookie fails the browser pass. A test suite added later runs in the same place.

Does it need a Stripe key?

Only to render the pricing page. The build and the dashboard pass work with a placeholder key, and the webhook route is checked by the type check. A Stripe test key stored as a Project secret adds the pricing page to the browser pass.

Will it regenerate migrations?

It runs pnpm db:generate after a Drizzle bump to confirm the schema still produces nothing new. A generated migration from an upgrade is a signal something changed in Drizzle's column mapping, and the agent reverts the bump and reports it rather than committing the migration.

Can I exclude a package?

Yes. Name it in the prompt and the agent leaves it at its current version and says so in the pull request. A pnpm.overrides entry in package.json works too and is respected.