# Turn GitHub Issues into Pull Requests for a Vue App

Yes. Label a GitHub issue on a Vue 3 app and an agent reproduces it with a Vitest or Cypress case, fixes the page or store and opens a pull request that closes it.

[Run this on Rush](https://cloud.getrush.ai/run/github-issue-to-pr/vue/), in a Project that has your repository.

## What the agent does

The agent reads the issue, then finds the file the behaviour lives in, which in a file-based Vue app is a matter of convention. In an app like antfu-collective/vitesse, `src/pages/` is the router: `index.vue` is `/`, `hi/[name].vue` is `/hi/:name`, `about.md` is a Markdown page at `/about`, and `[...all].vue` is the 404. Layouts are chosen per page with a `<route lang="yaml">` block, as `index.vue` picks `home`. Components in `src/components/` and composables in `src/composables/` are auto-imported, so there is no import line to follow; `vite.config.ts` registers `unplugin-auto-import` with `src/composables` and `src/stores` as directories and `unplugin-vue-components` for `.vue` and `.md`. State is a Pinia store in `src/stores/user.ts`, whose `setNewName` moves the previous name into `otherNames`. Text comes from `locales/*.yml` through `vue-i18n`, loaded lazily by `loadLanguageAsync` in `src/modules/i18n.ts`.

That structure decides where the fix goes. An issue about the greeting goes to `src/pages/hi/[name].vue`, which sets the name from `route.params.name` in a `watchEffect` and lists `user.otherNames` as links. An issue about the name box is `src/components/TheInput.vue` (the `id="input"` element) and the `go()` function in `index.vue`, which `encodeURIComponent`s the name before `router.push`. An issue about a missing translation is a key in `locales/en.yml` and the other 18 locale files, and an issue about the language switch is `toggleLocales` in `src/components/TheFooter.vue`, which steps through `availableLocales` in order. A dark mode issue is `src/composables/dark.ts`, three lines over `useDark` and `useToggle` from VueUse.

The reproduction uses the suite the app has. `vite.config.ts` points Vitest at `test/**/*.test.ts` in a `jsdom` environment, and `test/component.test.ts` shows the pattern: `mount` from `@vue/test-utils`, props passed in, `wrapper.text()` and `wrapper.get('.inc').trigger('click')`, plus a snapshot in `test/__snapshots__/`. A store bug gets a test that creates a fresh Pinia with `setActivePinia(createPinia())` and calls `setNewName` twice. A page bug gets a mount with a router, or a case in `cypress/e2e/basic.spec.ts`, which visits `http://localhost:3333`, types into `#input`, expects the `/hi/Vitesse` URL and clicks `[data-test-id="about"]`. The new case fails first on the issue's input, and the pull request quotes that.

Then the agent runs the four jobs from `.github/workflows/ci.yml` with the pinned toolchain: `pnpm@10.30.2` from `packageManager`, `pnpm install --frozen-lockfile`, `pnpm run lint` under `@antfu/eslint-config`, `pnpm run typecheck` with `vue-tsc --noEmit`, `pnpm run test:unit --run` and `pnpm run build` through `vite-ssg build`, and for a page change `npx vite preview --port 3333` with `cypress run`. A new component or page regenerates `src/components.d.ts` or `src/route-map.d.ts`, and the agent commits the regenerated file so `typecheck` passes on a clean checkout. The pull request body says `Closes #<issue>` and explains the cause in the words of the file.

## Start it with one API call

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

```json
{
  "agent": "claude",
  "input": "Fix the GitHub issue that follows this prompt in this Vue 3 app built with Vite, file-based routing in src/pages, auto-imported components and composables, a Pinia store in src/stores and vue-i18n locales in locales/*.yml. Find the page, component, store or locale file the behavior lives in. Reproduce the reported behavior first: a Vitest case in test/ using mount from @vue/test-utils and a fresh Pinia where the store is involved, or a case in cypress/e2e/basic.spec.ts for a routing or page behavior. Confirm it fails. Fix the cause, keep the diff small, and commit any regenerated src/components.d.ts or src/route-map.d.ts. Use pnpm 10.30.2 and pnpm install --frozen-lockfile, then run pnpm run lint, pnpm run typecheck, pnpm run test:unit --run and pnpm run build; for a page change also serve npx vite preview --port 3333 and run cypress run. Open a pull request whose description says Closes #<issue>, explains the cause and quotes the failing then passing test.\n\nIssue #31: Visiting /hi/Ana%20Lu shows the raw percent-encoded name in the greeting and in the Also known as list.",
  "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 with `Closes #<issue>` in its body. The diff holds the new test in `test/` or `cypress/e2e/`, the change in the page, component, store or locale file, and any regenerated declaration file. The description names the cause in the file's own terms, such as the `watchEffect` in `hi/[name].vue` or the `encodeURIComponent` in `index.vue`, and quotes the test failing before the fix and the `vitest` summary after.

The reviewer reads the test first and confirms it fails on `main`. They check the fix stayed where the issue was: a locale change touches `locales/en.yml` and the matching key in the other files rather than hard-coding a string in a template, a store change keeps `savedName` and `otherNames` consistent, and a regenerated `.d.ts` adds only the new name. The push runs `ci.yml` on GitHub, so `lint`, `typecheck`, both `test` rows and `test-e2e` confirm the sandbox's result.

The session holds the whole record: the failing `vitest` run, the edits, each CI command and the Cypress output if a page changed. `GET /o/{org}/sessions/{id}` returns the pull request once the turn has finished, and a follow-up can ask the same sandbox to add a case for another locale.

## Run it on every GitHub issue labelled agent

In the console, add an automation to the Project with this prompt and a `github` trigger on `issues.labeled`, filtered to the `agent` label in `config.labels`. The trigger appends the issue after the prompt, so leave the example issue out of the automation's prompt. Only labels added by an owner, member or collaborator fire it, by GitHub's `author_association`, so a stranger's issue cannot start a run. Each labelled issue gets its own sandbox, branch and pull request.

The same request can come from your own triage tool through `POST /o/{org}/sessions`, with the issue's text after the prompt and the delivery id as the `Idempotency-Key`. Set a `webhook_url` on the key and Rush POSTs `run.completed` when the turn finishes, with the `session_url` that reads the pull request.

## Why run it on Rush?

A Vue issue in a file-routed app is quick to fix and slow to prove: the right pnpm, a frozen install, `vue-tsc`, Vitest, an SSG build, and a browser running Cypress against `vite preview`, with generated declaration files that must be committed or `typecheck` fails only on CI. A Rush run does all of that in its own sandbox with a browser, from a clean clone, and keeps every command, so the pull request shows the failing test rather than describing it.

The fix is small because the agent reads the page, store and locale conventions before editing, and lands where the issue lives. Several labelled issues run at once, each on its own branch, and each ends in a pull request whose own four CI jobs confirm it, or in a note saying why the issue could not be reproduced.

## Frequently asked questions

### Where does a new test go?

In `test/`, matched by `test/**/*.test.ts` in `vite.config.ts`, next to `basic.test.ts` and `component.test.ts`, with `mount` from `@vue/test-utils` and a `jsdom` environment. A routing or whole-page behaviour goes into `cypress/e2e/basic.spec.ts` or a new spec matching `cypress/e2e/**/*.spec.*`.

### Does it handle all 19 locales?

It adds a key to `locales/en.yml` and, when the issue is about a translation, to the file for that language; other locale files fall back as `vue-i18n` is configured. It does not machine-translate the key into every file, and the pull request lists the files it changed.

### Why does the pull request include a .d.ts file?

Because `src/components.d.ts`, `src/auto-imports.d.ts` and `src/route-map.d.ts` are generated by the plugins in `vite.config.ts` and committed. A new component or page changes them, and `pnpm run typecheck` on CI fails on a clean checkout without the regenerated file.

### Will it run Cypress for every issue?

Only when the fix touches a page, layout or route, since `cypress/e2e/basic.spec.ts` is the only spec and it covers navigation, the name form and the Markdown page. A store or composable fix is proven by Vitest, and the pull request says which it ran.

### What if the issue is about the Netlify deploy?

`netlify.toml` builds with `pnpm run build` on Node 22 and redirects every path to `index.html`. The agent reproduces with the same `vite-ssg build` and `vite preview`; a redirect or header issue is a change to `netlify.toml` or `public/_headers`, which it makes and explains, but it cannot run Netlify itself.
