What the agent does
The agent clones the app into its own cloud sandbox and reads what there is to run before it writes a workflow. In a React Router 7 app made from remix-run/react-router-templates, package.json has four scripts and no test: dev (react-router dev), build (react-router build), start (react-router-serve ./build/server/index.js) and typecheck, which is react-router typegen && tsc. The order in that last script matters: tsconfig.json includes .react-router/types/**/* and sets rootDirs to . and ./.react-router/types, so tsc on its own fails on a clean checkout until react-router typegen has written the route types. The workflow calls the script, not tsc directly.
Then it settles the Node version and the package manager from files rather than defaults. The template's Dockerfile builds every stage from node:24-alpine and runs npm ci against package-lock.json; in the templates repository itself .nvmrc says 24 while the root workflow pins Node 22 with pnpm 10 for its own Playwright suite under .tests. For a single app the agent uses the lockfile it finds: npm ci with package-lock.json, pnpm install --frozen-lockfile with pnpm-lock.yaml, and actions/setup-node with the version from .nvmrc or, failing that, the Dockerfile's base image, with the matching cache setting. An app made with create-react-router pins react-router and @react-router/*; the templates repository leaves them as * for its workspace, and the agent notes which it is looking at.
The workflow it writes to .github/workflows/ci.yml runs on push to the default branch and on pull_request, with permissions: contents: read and a concurrency group so a second push cancels the first. Its job installs, runs typecheck, then build, and uploads nothing: build/client and build/server are reproducible from the commit. Because the template ships a multi-stage Dockerfile, the agent adds a second job that runs docker build with push: false, which catches the case where .dockerignore or the production-dependencies-env stage drifts from package.json. With no test script in the app, it adds no test step and says so in the pull request rather than inventing a Vitest setup the repository does not have.
Before opening the pull request it runs the same steps in the sandbox on a clean clone: the install, npm run typecheck and npm run build, and docker build when Docker is available, so the first CI run on GitHub is not the first time the commands are tried. It also checks vite.config.ts, which loads @tailwindcss/vite and reactRouter(), and react-router.config.ts, where ssr: true, since an app switched to SPA mode builds different output and the workflow's comments should say what to expect.
Start it with one API call
POST https://api.prix.dev/o/{org}/sessions with this body:
{
"agent": "claude",
"input": "Set up GitHub Actions CI for this React Router 7 app. Read package.json for the scripts (typecheck is react-router typegen && tsc, so call the script, not tsc), the lockfile to pick npm, pnpm or yarn, .nvmrc and the Dockerfile for the Node version, react-router.config.ts for ssr, and vite.config.ts. Write .github/workflows/ci.yml that runs on push to the default branch and on pull_request, with contents: read permissions and a concurrency group, installs from the lockfile with the matching setup-node cache, runs the typecheck script and then the build script. If a Dockerfile exists add a job that builds the image without pushing. Add no test step unless package.json has a test script. Run every step in the sandbox on a clean clone first, then open a pull request that explains each choice and says what the app has no tests for.",
"resources": [
{
"url": "https://github.com/your-org/your-app"
}
]
}- 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.
- 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.
- 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 with one new file, .github/workflows/ci.yml, and a description that names the Node version it chose and where it read it, the package manager and lockfile, the two scripts the job runs and in what order, and whether a Docker job was added. The push opens the workflow's first run on GitHub, so the pull request shows its own checks before anyone reviews it.
A reviewer reads the workflow against package.json once: the install command matches the lockfile, typecheck comes before build, and setup-node names the same major as the Dockerfile. If the app has since gained a test script, the reviewer asks for the step in a follow-up on the same session rather than editing the file by hand.
The session keeps the sandbox run of every step with its output, so a typecheck failure on the first CI run can be compared with what passed in the sandbox, which usually points at a difference in Node version or a file ignored by .dockerignore.
Run it on every new React Router repository
When your platform team creates a repository from the template, the script that creates it also POSTs this body to https://api.prix.dev/o/{org}/sessions with resources set to the new repository and a request_id made from the repository name, so a retried script starts no second run. The Rush GitHub App has to be installed on the new repository first, or the resource is refused as RESOURCE_NOT_ACCESSIBLE.
Give the key a webhook_url and Rush POSTs run.completed when the turn finishes, with data.session_url from which a handler reads the pull request and posts its link in the team's channel. Every run the key starts reports to that URL; a key may have at most max_concurrent runs in progress, so a batch of new repositories is started a few at a time.
The use case this job follows, with its automation and trigger: Hand off from your terminal.
Why run it on Rush?
The usual way to add CI is to push a workflow and read the failure log four times. Here the agent runs the install, the typecheck and the build on a clean clone in its own sandbox first, so the pull request's first run on GitHub is a confirmation rather than an experiment, and the transcript shows each command's output beside the file that was written.
The choices that go wrong most often, the Node major, the lockfile command and the typegen before tsc, are read from the repository rather than copied from a template, and the pull request says where each came from. Several repositories can be set up at once, each in its own sandbox, with a result that is a reviewable file and a green check rather than a promise.
Frequently asked questions
The app has no tests. Will the agent add a test framework?
No. The prompt says to add a test step only when package.json has a test script, and the templates ship none. The pull request says so plainly. Adding Vitest or Playwright is a separate job with its own page, and a follow-up on the same session can start it.
Does it work with pnpm or yarn?
Yes. The agent picks the install command from the lockfile it finds, pnpm-lock.yaml, yarn.lock or package-lock.json, sets the matching cache on setup-node, and adds pnpm/action-setup when pnpm is in use, as the templates repository's own workflow does. A repository with two lockfiles is reported rather than guessed.
Why does typecheck run before build?
Because react-router typegen writes the route types that tsconfig.json includes, and a tsc failure is faster and clearer than a build failure. The build script also runs react-router build through Vite, which does not typecheck, so without the first step a type error would merge.
Will it change the Dockerfile or add deployment?
No. The Docker job only proves the image builds from the commit, with push: false. Deployment targets, registry logins and secrets are not in the prompt, and the agent writes nothing that needs a secret the repository does not have.