# Review Pull Requests on a Hono API

Yes. An agent checks out the pull request, runs the touched example's Vitest suite and tsc in its sandbox, and posts one review on middleware and validation.

[Run this on Rush](https://cloud.getrush.ai/run/review-pull-requests/hono/), in a Project that has your repository.

## What the agent does

The agent first works out which example the diff touches, because honojs/examples is one npm workspace with many small apps that run on different runtimes. The root `package.json` lists `basic`, `blog`, `durable-objects`, `jsx-ssr`, `pages-stack`, `nextjs-stack`, `hono-vite-jsx`, `env-vars` and `stytch-auth` as workspaces and pins `packageManager` to `npm@10.0.0`; the `deno`, `bun` and `serve-static` directories sit outside the workspace and are run with `deno run` or `bun run` as the README describes. There is no GitHub Actions workflow, only `.github/dependabot.yml` checking npm daily, so whatever the agent runs is the only check the pull request gets.

Only two examples have tests. `basic/src/index.test.ts` calls `app.request('http://localhost/')` and expects a 200 with `x-powered-by: Hono`, under a `vitest.config.ts` that sets `globals: true`; `basic/package.json` runs it with `vitest --run`. `blog/src/api.test.ts` drives the Worker through `worker.request(path, init, env)` with `env` from `cloudflare:test`, creating, reading, updating and deleting a post, and `blog/vitest.config.ts` uses `defineWorkersConfig` with `isolatedStorage: false` and `wrangler: { configPath: './wrangler.toml' }`. That file is not committed: `wrangler.example.toml` declares the `BLOG_EXAMPLE` KV namespace with placeholder ids, so the agent copies it to `wrangler.toml` before `npm test -w blog` can start. For every workspace it runs `npx tsc --noEmit -p <example>` against the example's `tsconfig.json`, which in `blog` types `vitest/globals`, `@cloudflare/workers-types/experimental` and `@cloudflare/vitest-pool-workers`.

Then it reads the diff with the three questions the prompt names. Middleware order: in `basic/src/index.ts` the `X-Response-Time` middleware is registered on `*` after `basicAuth` on `/auth/*` and `etag` on `/etag/*`, `notFound` and `onError` are set once for the whole app, and `/api/*` ends in a catch-all 404 that any new `/api` route must be registered before. In `blog/src/index.ts` the auth middleware on `/posts/*` only wraps non-GET methods with `basicAuth` built from `c.env.USERNAME` and `c.env.PASSWORD`, and the `middleware` and `api` sub-apps are both mounted at `/api`, so a route added to one but not matched by the other's path pattern silently skips auth. Validation: `pages-stack/functions/api/[[route]].ts` is the only example that validates input, with `zValidator('query', z.object({ name: z.string() }))` and `c.req.valid('query')`, and it exports `AppType` for the `hc` client; `blog/src/api.ts` calls `await c.req.json()` with no guard before `model.createPost`. Runtime-specific APIs: `durable-objects/src/counter.ts` extends `DurableObject` from `cloudflare:workers` and `wrangler.toml` carries a `migrations` entry with `tag = "v1"` and `new_classes = ["Counter"]`; `env-vars/src/index.ts` builds a Stripe client per request from `LIVEMODE`, `STRIPE_RK` and `STRIPE_TK` bindings; `stytch-auth/api/TodoAPI.ts` uses `Consumer.authenticateSessionLocal()` for reads and `authenticateSessionRemote()` for writes.

For each candidate finding the agent checks whether an existing test pins the behaviour and, where it can, writes a short Vitest case in the sandbox to confirm it before posting, without adding that case to the branch. One GitHub review is posted per run: the summary quotes the `vitest --run` and `tsc --noEmit` results for each touched example, separates blocking issues from suggestions, and every finding cites file and line. A middleware registered after the route it was meant to protect is blocking; a `// @ts-ignore` like the one on `/type-error` in the basic example is a suggestion unless the pull request added it. The agent pushes nothing.

## Start it with one API call

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

```json
{
  "agent": "claude",
  "input": "Review this pull request on a Hono API. Check it out, run npm install at the root, and for every workspace the diff touches run npx tsc --noEmit -p <example> and npm test -w <example> where a test script exists; for blog, copy wrangler.example.toml to wrangler.toml first so the Workers test pool can start. Read the diff with the surrounding src/index.ts, the sub-apps it mounts and the wrangler config. Look for middleware registered after the routes it should wrap or on a path pattern that misses a new route, routes under /api added after the catch-all 404, request bodies read with c.req.json() and no validation where the example already uses zValidator, Durable Object classes changed without a migrations entry, and bindings read from globals instead of c.env. Confirm a finding with a short Vitest case in the sandbox where you can, but do not add it to the branch. Post one GitHub review that quotes the test and type-check results, separates blocking issues from suggestions, and cites file and line for every finding. Do not push commits.",
  "resources": [
    {
      "url": "https://github.com/your-org/your-app/pull/42"
    }
  ]
}
```

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 review on the pull request. Its summary quotes `vitest --run` for `basic` or `blog` when the diff touches them, including the Workers pool's own output for `blog`, and `tsc --noEmit` for every touched workspace. Findings follow with file and line in the example they belong to, marked blocking or suggestion, and a finding about auth names the path pattern in `index.ts` that the new route falls outside.

The transcript keeps the `npm install`, the copied `wrangler.toml`, every command with its output, and the short test cases the agent wrote to confirm a finding, so when the review says a POST to a new route skips `basicAuth`, the request that proved it is there to read.

Before merging, a reviewer runs the touched example's test script on the branch, opens each cited line, and decides which suggestions to take. A route that bypasses auth or a Durable Object change without a migration is blocking by default; a request to add validation to an example that never had it is a suggestion.

## Run it on every pull request opened

Create an automation in the Project with a `github` trigger on `pull_request.opened` and `pull_request.synchronize`, `branch_patterns` set to `main`, and this prompt. The trigger adds the pull request after the prompt, so each pull request and each push to it gets its own sandbox, its own `npm install` and its own review. Only pull requests by an owner, member or collaborator fire it; one from a fork never does.

Set `read_only` on the automation so its runs can comment but not push. The review is the result and shows on the pull request; the run is listed in the Project. The automation's `max_runs_per_hour`, 1 to 30, bounds a Dependabot morning, since this repository takes daily npm updates; a fire past the cap is skipped as `rate_limited` and that pull request waits for its next push.

## Why run it on Rush?

This repository has no CI, so a pull request is checked only if someone installs the workspace, creates the missing `wrangler.toml`, starts the Workers test pool and runs `tsc` against each example's own `tsconfig.json`. That is the setup a reviewer skips when the diff is twenty lines. On Rush the agent does it in its own sandbox and keeps every command and its output, so the review rests on an executed test rather than a reading, and the author sees what a type error in `durable-objects` looks like before a Cloudflare deploy would.

One sandbox per pull request means a change to `blog` and a change to `stytch-auth` are reviewed at the same time without one install clobbering the other, and nothing is pushed to the branch. The reviewer gets findings with the test output attached and the author keeps their checkout as it was.

## Frequently asked questions

### Which examples get a test run?

Only `basic` and `blog` have test scripts, so those run under Vitest when the diff touches them. Every other touched workspace gets `tsc --noEmit` against its own `tsconfig.json`, and the review says which checks ran for which example rather than implying a suite that does not exist.

### Why does the blog test need a wrangler.toml?

`blog/vitest.config.ts` points the Workers pool at `./wrangler.toml` for the `BLOG_EXAMPLE` KV binding, but only `wrangler.example.toml` is in the repository. The agent copies it, which is enough for the pool's local KV, and leaves the copy out of the branch.

### Does it review the Deno and Bun examples?

It reads them, since they are plain Hono apps, and the sandbox can run `deno run --allow-net hello.ts` and `bun run hello.ts` as the README shows to confirm they start. They are outside the npm workspace, so there is no `tsc` project or test script for them, and the review labels those findings as read rather than executed.

### What does it do with the hard-coded basic-auth password in the basic example?

`basic/src/index.ts` registers `basicAuth({ username: 'hono', password: 'acoolproject' })` on `/auth/*`, which is fine for an example. The review raises it only if the pull request copies that pattern into `blog` or `stytch-auth`, where credentials already come from bindings.
