Inspiration

Most of our coding agent sessions used to die in the gap between "the agent asked a question" and "someone got back to the laptop". A plan would sit waiting for a yes, or a task would be done but nobody had looked at the diff. We wanted to answer the agent from wherever we were, and merge the result without opening a laptop.

We also didn't want to build yet another coding agent. Every developer we talked to already paid for one, whether a Cursor plan, a Claude subscription or ChatGPT for Codex, and asking them to buy another just to use our tool felt like friction for no reason. The thing that made it click was a talk by Ben Brandt from Zed, together with JetBrains, about the Agent Client Protocol (ACP). They were solving the M×N problem of every editor needing a custom integration for every agent. If the agents speak one protocol, we thought, a phone can just be one more client.

What it does

GetPullRequest (GPR) lets you hand a task to a coding agent running on your own machine, then follow it, answer its questions and merge its pull request from your phone. Tasks come from the board in the app, from a GitHub issue with the gpr:agent label, from a Jira project, or from an @mention in Slack.

A small daemon on your machine picks the task up, makes a git worktree so your checked-out branch is never touched, and runs the agent you chose for that repo. It currently recognizes Cursor, Claude Code, Codex, Kiro, OpenCode, Qwen, Cline and Copilot. The agent plans, implements, verifies and opens a PR, writing a checkpoint at each step so an interrupted run resumes instead of starting over. If it needs you, the task moves to "Needs your input" and you get a notification. On the phone you see the plan, the tool calls, the diff and a merge check, and you can merge from there.

Your code, your agent's login and your API keys stay on your machine. Our backend keeps the board and the queue and relays the session to your phone, and it never has a copy of your working tree.

How we built it

The backend is FastAPI, the app is Flutter, and the daemon is Go, all in one Nx monorepo. The daemon talks ACP to the agent over stdio, and our relay carries those same structured messages to the phone. That is why one mobile UI works for every agent: we get plans, diffs and slash commands as data rather than scraping a terminal.

The backend is hexagonal (ports and adapters). The app depends on small interfaces like a GitHost and an IssueProvider, and GitHub, GitLab, Jira and Slack each live in their own folder and register themselves. On top of that we follow what we call the enrichment model: every core feature, including coding, review, merge checks, conflict resolution and changing a base branch, has to work with nothing external connected, using the daemon and local git. Integrations only add to it, by syncing work in or opening the hosted PR. This came out of planning the GitHub integration, when we realised the agent's real work lives in a worktree with changes GitHub has never seen, so local git has to be the source of truth.

We built GPR with GPR, using Cursor, Claude Code, Kiro and OpenCode, and nobody had to agree on one. Our history has plenty of merges from branches named gpr/task-tsk_…, 37 commits carry a Co-authored-by: Cursor trailer, and the conversation minimap in the task screen was a PR that GPR opened against its own repo. When Jira retired its old search endpoint and started returning 410 Gone, the fix came in as a Jira ticket that GPR turned into a pull request.

Challenges we ran into

Our first version ran agents in hosted sandboxes on Daytona, and we dropped it. Getting our whole stack running inside a sandbox took more effort than the feature itself, the cloud bill was hard to justify for a small startup when our own laptops sat idle most of the day, and checking an agent's work was slow because every sandbox had to rebuild the project from scratch. Running the agent on our own machines solved all three. The trade-off is that a machine has to be switched on.

Real machines brought their own surprises. Laptops sleep, networks drop, and Windows behaves differently from macOS and Linux in more ways than we expected. Every agent also has its own habits, and some of them left processes running long after a task was over. We spent a good part of the summer making the connection between the phone, our backend and the machine hold up when things go wrong. We also found that agents sometimes say they're done without having changed anything, so GPR now treats a task with no commits as failed instead of finished.

Accomplishments that we're proud of

GPR builds GPR. A lot of our own features and fixes now start as a task on the board and come back as a PR that a human reviews and merges. Some of them land on the first try and some take four or five rounds of feedback, which is a fair picture of where agents are today.

We're also happy with the enrichment model. You can use GPR on day one without connecting anything, and adding GitLab after GitHub didn't touch the task pipeline at all. And the same daemon runs on macOS, Linux and Windows, which cost us more Windows-specific bugs than we'd like to admit (a self-update that silently did nothing, a pairing QR code that printed as an empty box).

What we learned

Keep agent tasks small. A phone is good for quick decisions like answering a plan question, approving a command, reading a short diff or merging. It is bad at reviewing a large architectural change, and when a PR is that big we open a laptop. Small tasks are also the ones agents tend to get right, so we stopped fighting it.

We also learned that each agent has its own habits around startup, auth and process handling, and the daemon has to absorb them. Supporting several isn't about loyalty to any one; it means you can switch when a better one comes out, from a dropdown, without changing anything else.

What's next for GetPullRequest

The iOS app isn't in the App Store yet, and getting it there is first. We're working on live preview of your running app on the phone, which will start out working only when the phone and the machine share a Wi-Fi network. We want to finish making the backend adapters properly independent of the app's database layer, and make expired agent logins something you can recover from without walking over to the machine.

The bigger piece we're building is a command center for teams running several agents at once. It will give agents a shared memory, so what one agent learns about your codebase carries over to the next one, even if it's a different agent. It will add governance, so a team can decide what agents are allowed to do and see what they did. And it will come with a web UI for the times we rather work from a browser than a phone.

Built With

Share this project:

Updates

Submission history