Overview
This example runs Cursor’s headless CLI in a Trigger.dev task. The agent spawns as a child process, and its NDJSON stdout is parsed and piped to the browser in real-time using Realtime Streams. The result is a live terminal UI that renders each Cursor event (system messages, assistant responses, tool calls, results) as it happens. Tech stack:- Next.js for the web app (App Router with server actions)
- Cursor CLI for the headless AI coding agent
- Trigger.dev for task orchestration, real-time streaming, and deployment
Video
Features:- Build extensions: Installs the
cursor-agentbinary into the task container image usingaddLayer, demonstrating how to ship system binaries with your tasks - Realtime Streams v2: NDJSON from a child process stdout is parsed and piped directly to the browser using
streams.define()and.pipe() - Live terminal rendering: Each Cursor event renders as a distinct row with auto-scroll
- Long-running tasks: Cursor agent runs for minutes; Trigger.dev handles lifecycle, timeouts, and retries automatically
- Machine selection: Uses the
medium-2xpreset for resource-intensive CLI tools - LLM model picker: Switch between models from the UI before triggering a run
GitHub repo
View the Cursor background agent repo
Click here to view the full code for this project in our examples repository on GitHub. You can
fork it and use it as a starting point for your own project.
How it works
Task orchestration
The task spawns the Cursor CLI as a child process and streams its output to the frontend:- A Next.js server action triggers the
cursor-agenttask with the user’s prompt and selected model - The task spawns the Cursor CLI binary using a helper that returns a typed NDJSON stream and a
waitUntilExit()promise - Each line of NDJSON stdout is parsed into typed Cursor events and piped to a Realtime Stream
- The frontend subscribes to the stream using
useRealtimeRunWithStreamsand renders each event in a terminal UI - The task waits for the CLI process to exit and returns the result
Build extension for system binaries
The example includes a custom build extension that installs thecursor-agent binary into the container image using addLayer. At runtime, the binary is copied to /tmp and given execute permissions; this is a workaround needed when the container runtime strips execute permissions from added layers.
extensions/cursor-cli.ts
Streaming with Realtime Streams v2
The stream is defined with a typed schema and piped from the child process:trigger/cursor-stream.ts
trigger/cursor-agent.ts
useRealtimeRunWithStreams hook subscribes to these events and renders them as they arrive.
Relevant code
- Build extension + spawn helper: extensions/cursor-cli.ts: installs the binary and provides a typed NDJSON stream with
waitUntilExit() - Task definition: trigger/cursor-agent.ts: spawns the CLI, pipes the stream, waits for exit
- Stream definition: trigger/cursor-stream.ts: Realtime Streams v2 stream with typed schema
- Terminal UI: components/terminal.tsx: renders live events using
useRealtimeRunWithStreams - Event types: lib/cursor-events.ts: TypeScript types and parsers for Cursor NDJSON events
- Trigger config: trigger.config.ts: project config with the cursor CLI build extension
Learn more about Trigger.dev Realtime
To learn more, take a look at the following resources:- Trigger.dev Realtime - learn more about how to subscribe to runs and get real-time updates
- Realtime streaming - learn more about streaming data from your tasks
- Batch Triggering - learn more about how to trigger tasks in batches
- React hooks - learn more about using React hooks to interact with the Trigger.dev API

