Skip to content

Latest commit

 

History

History
111 lines (83 loc) · 5.47 KB

File metadata and controls

111 lines (83 loc) · 5.47 KB
title Background Cursor agent using the Cursor CLI
sidebarTitle Cursor background agent
description Run Cursor's headless CLI agent in a Trigger.dev task and stream the live output to the frontend using Trigger.dev Realtime Streams.

import RealtimeLearnMore from "/snippets/realtime-learn-more.mdx";

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

<video controls className="w-full aspect-video" src="https://github.com/user-attachments/assets/459aa160-6659-478e-868f-32e74f79d21a"

Features:

  • Build extensions: Installs the cursor-agent binary into the task container image using addLayer, 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-2x preset for resource-intensive CLI tools
  • LLM model picker: Switch between models from the UI before triggering a run

GitHub repo

<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.

How it works

Task orchestration

The task spawns the Cursor CLI as a child process and streams its output to the frontend:

  1. A Next.js server action triggers the cursor-agent task with the user's prompt and selected model
  2. The task spawns the Cursor CLI binary using a helper that returns a typed NDJSON stream and a waitUntilExit() promise
  3. Each line of NDJSON stdout is parsed into typed Cursor events and piped to a Realtime Stream
  4. The frontend subscribes to the stream using useRealtimeRunWithStreams and renders each event in a terminal UI
  5. 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 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}`,
        ],
      },
    });
  },
});

Streaming with Realtime Streams v2

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.

Relevant code