|
1 | | -# Cursor Agent Runner |
| 1 | +# Cursor Agent using the Cursor CLI and Trigger.dev |
2 | 2 |
|
3 | | -Run Cursor's CLI agent on [Trigger.dev](https://trigger.dev), streamed live to a terminal UI. |
| 3 | +Run Cursor's headless CLI agent inside a Trigger.dev task, parsing NDJSON stdout into a Realtime Stream that renders live in a browser terminal. Built with Next.js and Trigger.dev. |
4 | 4 |
|
5 | | -Type a prompt, pick a model, and watch the agent create files and write code from scratch in real time. |
| 5 | +## Tech stack |
6 | 6 |
|
7 | | -## How it works |
| 7 | +- **[Next.js](https://nextjs.org)** – App Router frontend with server actions to trigger runs |
| 8 | +- **[Cursor CLI](https://cursor.com)** – Headless AI coding agent spawned as a child process |
| 9 | +- **[Trigger.dev](https://trigger.dev)** – Background task orchestration with real-time streaming to the frontend, observability, and deployment |
| 10 | +- **[Tailwind CSS](https://tailwindcss.com)** – Styling with Geist Mono for the terminal UI |
8 | 11 |
|
9 | | -1. User types a prompt in the browser (e.g. "Create a TypeScript CLI that converts CSV to JSON") |
10 | | -2. Next.js API route triggers a Trigger.dev task |
11 | | -3. The task spawns `cursor-agent` in an empty workspace |
12 | | -4. NDJSON output from stdout is parsed and piped to a Realtime Stream |
13 | | -5. `useRealtimeRunWithStreams` renders each event live in a terminal panel |
| 12 | +## Running the project locally |
14 | 13 |
|
15 | | -```text |
16 | | -[Browser] <-- Realtime Streams v2 --> [Trigger.dev Cloud] |
17 | | - | | |
18 | | - | POST /api/trigger | task.trigger() |
19 | | - | | |
20 | | - +--- Next.js API route ------------------+ |
21 | | - | |
22 | | - cursor-agent child process |
23 | | - stdout -> NDJSON -> stream |
24 | | -``` |
| 14 | +1. **Install dependencies** |
25 | 15 |
|
26 | | -## Key concepts demonstrated |
| 16 | + ```bash |
| 17 | + pnpm install |
| 18 | + ``` |
27 | 19 |
|
28 | | -- **Build extensions** — install any system binary (cursor-agent) into the task container via `addLayer` |
29 | | -- **Realtime Streams v2** — pipe NDJSON from a child process directly to the browser |
30 | | -- **Long-running tasks** — cursor-agent runs for minutes; Trigger.dev handles lifecycle, timeouts, retries |
31 | | -- **Machine selection** — `medium-2x` for resource-intensive CLI tools |
| 20 | +2. **Configure environment variables** |
32 | 21 |
|
33 | | -## Setup |
| 22 | + ```bash |
| 23 | + cp env.local.example .env.local |
| 24 | + ``` |
34 | 25 |
|
35 | | -```bash |
36 | | -# Install dependencies |
37 | | -pnpm install |
| 26 | + - `TRIGGER_SECRET_KEY` – From [Trigger.dev dashboard](https://cloud.trigger.dev/) (starts with `tr_dev_` or `tr_`) |
| 27 | + - `TRIGGER_PROJECT_REF` – Your project ref (starts with `proj_`) |
| 28 | + - `CURSOR_API_KEY` – Your Cursor API key for headless CLI access |
38 | 29 |
|
39 | | -# Copy env template and fill in values |
40 | | -cp env.local.example .env.local |
41 | | -``` |
| 30 | +3. **Start development servers** |
42 | 31 |
|
43 | | -Required environment variables: |
| 32 | + ```bash |
| 33 | + # Terminal 1: Next.js |
| 34 | + pnpm dev |
44 | 35 |
|
45 | | -| Variable | Description | |
46 | | -|----------|-------------| |
47 | | -| `TRIGGER_SECRET_KEY` | Your Trigger.dev secret key (starts with `tr_dev_` or `tr_`) | |
48 | | -| `TRIGGER_PROJECT_REF` | Your Trigger.dev project ref (starts with `proj_`) | |
49 | | -| `CURSOR_API_KEY` | Your Cursor API key for headless CLI access | |
| 36 | + # Terminal 2: Trigger.dev |
| 37 | + npx trigger.dev@latest dev |
| 38 | + ``` |
50 | 39 |
|
51 | | -## Run locally |
| 40 | +4. Open [http://localhost:3000](http://localhost:3000) in your browser to see the demo |
52 | 41 |
|
53 | | -```bash |
54 | | -# Start Next.js dev server |
55 | | -pnpm dev |
| 42 | +## Features |
56 | 43 |
|
57 | | -# In another terminal, start Trigger.dev dev |
58 | | -npx trigger.dev dev |
59 | | -``` |
| 44 | +- **Build extensions** – Installs `cursor-agent` into the task container image via `addLayer`, so any system binary can ship with your task |
| 45 | +- **Realtime Streams v2** – NDJSON from a child process stdout is parsed and piped directly to the browser using `streams.define()` and `.pipe()` |
| 46 | +- **Live terminal rendering** – Each cursor event (system, assistant, tool_call, result) renders as a distinct row with auto-scroll |
| 47 | +- **Long-running tasks** – cursor-agent runs for minutes; Trigger.dev handles lifecycle, timeouts, and retries |
| 48 | +- **Machine selection** – `medium-2x` preset for resource-intensive CLI tools |
| 49 | +- **Model picker** – Switch between Claude models from the UI before triggering a run |
| 50 | +- **Container binary workaround** – Demonstrates the `/tmp` copy + `chmod` pattern needed when the runtime strips execute permissions |
60 | 51 |
|
61 | | -Open [http://localhost:3000](http://localhost:3000). |
| 52 | +## Relevant files |
62 | 53 |
|
63 | | -## Deploy to Trigger.dev Cloud |
64 | | - |
65 | | -```bash |
66 | | -npx trigger.dev deploy |
67 | | -``` |
68 | | - |
69 | | -The build extension in `trigger.config.ts` installs `cursor-agent` into the container image automatically. |
70 | | - |
71 | | -## Project structure |
72 | | - |
73 | | -```text |
74 | | -├── app/ |
75 | | -│ ├── layout.tsx # Root layout with Geist fonts |
76 | | -│ ├── page.tsx # Main UI: control bar + terminal |
77 | | -│ └── api/trigger/route.ts # Trigger task, return run ID |
78 | | -├── components/ |
79 | | -│ ├── terminal.tsx # Realtime terminal with auto-scroll |
80 | | -│ ├── cursor-event.tsx # Render each NDJSON event type |
81 | | -│ └── control-bar.tsx # Prompt input, model select, run button |
82 | | -├── trigger/ |
83 | | -│ └── cursor-agent.ts # The task: spawn CLI, stream NDJSON |
84 | | -├── lib/ |
85 | | -│ └── cursor-events.ts # TypeScript types for cursor events |
86 | | -├── trigger.config.ts # Build extension for cursor CLI binary |
87 | | -└── env.local.example # Env var template |
88 | | -``` |
89 | | - |
90 | | -## Links |
91 | | - |
92 | | -- [Trigger.dev Build Extensions docs](https://trigger.dev/docs/config/extensions/custom) |
93 | | -- [Trigger.dev Realtime Streams docs](https://trigger.dev/docs/realtime) |
94 | | -- [Cursor CLI Output Format](https://cursor.com/docs/cli/reference/output-format) |
95 | | - |
96 | | -## Stack |
97 | | - |
98 | | -Next.js 16 · Trigger.dev v4 · Tailwind CSS · Geist Mono |
| 54 | +- [extensions/cursor-cli.ts](extensions/cursor-cli.ts) – Build extension + spawn helper that returns a typed NDJSON stream and `waitUntilExit()` |
| 55 | +- [trigger/cursor-agent.ts](trigger/cursor-agent.ts) – The task: spawns the CLI, pipes the stream, waits for exit |
| 56 | +- [trigger/cursor-stream.ts](trigger/cursor-stream.ts) – Realtime Streams v2 stream definition |
| 57 | +- [components/terminal.tsx](components/terminal.tsx) – Realtime terminal UI with `useRealtimeRunWithStreams` |
| 58 | +- [lib/cursor-events.ts](lib/cursor-events.ts) – TypeScript types and parsers for cursor NDJSON events |
| 59 | +- [trigger.config.ts](trigger.config.ts) – Trigger.dev config with the cursor CLI build extension |
0 commit comments