docs/guides/example-projects/cursor-background-agent.mdx
import RealtimeLearnMore from "/snippets/realtime-learn-more.mdx";
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:
<video controls className="w-full aspect-video" src="https://github.com/user-attachments/assets/459aa160-6659-478e-868f-32e74f79d21a"
</video>
Features:
cursor-agent binary into the task container image using addLayer, demonstrating how to ship system binaries with your tasksstreams.define() and .pipe()medium-2x preset for resource-intensive CLI tools<Card title="View the Cursor background agent repo" icon="GitHub" href="https://github.com/triggerdotdev/examples/tree/main/cursor-cli-demo"
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. </Card>
The task spawns the Cursor CLI as a child process and streams its output to the frontend:
cursor-agent task with the user's prompt and selected modelwaitUntilExit() promiseuseRealtimeRunWithStreams and renders each event in a terminal UIThe example includes a custom build extension that installs cursor-agent into the container image using addLayer. The official install script is run at build time, then the resolved entry point and its dependencies are copied to a fixed path so the task can invoke them at runtime with the bundled Node binary.
const CURSOR_AGENT_DIR = "/usr/local/lib/cursor-agent";
export const cursorCli = (): BuildExtension => ({
name: "cursor-cli",
onBuildComplete(context) {
if (context.target === "dev") return;
context.addLayer({
id: "cursor-cli",
image: {
instructions: [
"RUN apt-get update && apt-get install -y curl ca-certificates && rm -rf /var/lib/apt/lists/*",
'ENV PATH="/root/.local/bin:$PATH"',
"RUN curl -fsSL https://cursor.com/install | bash",
`RUN cp -r $(dirname $(readlink -f /root/.local/bin/cursor-agent)) ${CURSOR_AGENT_DIR}`,
],
},
});
},
});
The stream is defined with a typed schema and piped from the child process:
export const cursorStream = streams.define("cursor", cursorEventSchema);
const { stream, waitUntilExit } = spawnCursorAgent({ prompt, model });
cursorStream.pipe(stream);
await waitUntilExit();
On the frontend, the useRealtimeRunWithStreams hook subscribes to these events and renders them as they arrive.
waitUntilExit()useRealtimeRunWithStreams