Skip to content
Deployments

Run a Cursor SDK agent in CI on a self-hosted runner

Deployment

Run a Cursor SDK agent in CI on a self-hosted runner

Stand up a CI job that invokes a Cursor SDK agent on a self-hosted runner on Quake AI. The job runs on pull requests, reviews the checked-out diff, and posts the result as a pull request comment. You supply the Cursor API key as a CI secret and the runner from Deploy a self-hosted CI runner.

Monthly cost estimate

Pricing calculator ↗

Sized as a custom package on shared vCPU.

Starting template$11.50/mo

Monthly total for the required template above. Use the configurator below to add optional pieces and see the total update.

What each resource is for

s1a.small

s1a.small · 2 shared vCPU, 2 GiB RAM, 0.5 Gbps

$16.50/mo

Compute shown per role at custom-package rates ($29/dedicated vCPU, $7.25/shared vCPU, $1/GiB RAM). The headline above is the billed total: the cheaper of a named plan and the custom package, plus add-ons.

Included in baseline

s1a.small

2 shared vCPU, 2 GiB RAM, 0.5 Gbps

$16.50

Compute + RAM rate basis

2 vCPU + 2 GiB RAM at $29/dedicated vCPU, $7.25/shared vCPU, $1/GiB RAM (regular). Totals apply the flat −$5/mo package promotion.

—

Package promotional discount

Flat −$5.00/mo on the custom package (same promotion as named plans).

$-5.00

Included at no charge

These line items are zero on Quake AI. Many other providers meter them separately.

Data transfer (inbound and outbound)

Unlimited data transfer on every plan; Quake AI does not meter per-GB egress.

AWS, GCP, and Azure meter outbound transfer per GB. DigitalOcean and Hetzner include an allowance on compute plans, then charge overage.

Learn more
$0.00

Private networking

Private networks, subnets, Neutron routers, and security groups are included with the plan.

VPC objects are usually free to create elsewhere, but NAT gateways bill hourly plus per-GB processed. Quake AI uses router SNAT with no separate NAT line item.

$0.00

Control-plane API requests

OpenStack API calls for provisioning and management are included.

Some managed services on other clouds meter API calls or charge for premium control-plane features.

$0.00

Pricing data last validated: . For current rates, check quake.ai/pricing.

GitHub or GitLabrepositoryQuake AI VMCursor APISelf-hosted runner(from deploy-ci-runner)CI job:agent review step pull request eventopens or updates PRAgent.prompt(diff review)review textPR comment
Click to zoom
Pull-request workflow on a self-hosted runner: a one-shot Cursor SDK agent reviews the diff and posts a PR comment

Prerequisites#

You need:

  • A self-hosted runner already registered against your repository, see Deploy a self-hosted CI runner. The examples below assume a GitHub Actions runner labeled quake-ai; the GitLab Runner equivalent is the same shape with tags instead of runs-on.
  • A Cursor API key. User keys live at Cursor Dashboard → Integrations; team service-account keys live in Team Settings → Service accounts.
  • Node.js available in the workflow, either preinstalled on the runner or added with a setup step.
  • Admin access to the repository to add a CI secret.

Step 1: Store the Cursor API key as a CI secret#

Add the key to your repository's secret store. Never commit it to a workflow file or to the runner's filesystem outside the CI secret mechanism.

For GitHub Actions, go to your repository's Settings > Secrets and variables > Actions > New repository secret, name it CURSOR_API_KEY, and paste the key.

For GitLab CI/CD, go to Settings > CI/CD > Variables > Add variable, name it CURSOR_API_KEY, and mark it Masked and Protected.

Step 2: Add the agent review script#

Add scripts/agent-pr-review.mjs to the repository:

JavaScript
import { Agent, CursorAgentError } from "@cursor/sdk";

const baseRef = process.env.PR_BASE_REF ?? "main";

const prompt = [
  `Run \`git diff origin/${baseRef}...HEAD\` in this repository.`,
  "Write a concise code-review comment: summarize the change, flag",
  "correctness or security risks you see, and note missing tests.",
  "Output only the comment text, no preamble.",
].join(" ");

try {
  const result = await Agent.prompt(prompt, {
    apiKey: process.env.CURSOR_API_KEY,
    model: { id: "composer-2.5" },
    local: { cwd: process.cwd() },
  });
  if (result.status === "error") {
    console.error(`run failed: ${result.id}`);
    process.exit(2);
  }
  process.stdout.write(result.result ?? "");
} catch (err) {
  if (err instanceof CursorAgentError) {
    console.error(`startup failed: ${err.message}, retryable=${err.isRetryable}`);
    process.exit(1);
  }
  throw err;
}

The script uses the one-shot Agent.prompt(...) pattern: no streaming, no follow-up turns, the process exits once the agent finishes. The agent runs locally against the runner's checked-out repository (local: { cwd: process.cwd() }), so it reads the diff itself with its own tool calls rather than the script shelling out to git diff. A thrown CursorAgentError means the run never started (exit code 1); result.status === "error" means the run started and failed midway (exit code 2). See the Cursor SDK skill for the full pattern reference.

Step 3: Add the CI job#

For a GitHub Actions workflow at .github/workflows/agent-review.yml:

YAML
name: Agent PR review

on:
  pull_request:
    types: [opened, synchronize]

jobs:
  review:
    runs-on: [self-hosted, quake-ai]
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - uses: actions/setup-node@v4
        with:
          node-version: "22"

      - run: npm install @cursor/sdk

      - name: Run agent review
        id: agent
        env:
          CURSOR_API_KEY: ${{ secrets.CURSOR_API_KEY }}
          PR_BASE_REF: ${{ github.base_ref }}
        run: node scripts/agent-pr-review.mjs > agent-review.md

      - uses: peter-evans/create-or-update-comment@v4
        with:
          issue-number: ${{ github.event.pull_request.number }}
          body-path: agent-review.md

fetch-depth: 0 is required: the agent's git diff call needs the base branch's history, which a shallow checkout does not include. The create-or-update-comment step posts the captured output as a PR comment using the workflow's built-in GITHUB_TOKEN permissions; it needs no extra secret.

For GitLab CI/CD, the equivalent job uses tags instead of runs-on and the GitLab API to post a merge request note:

YAML
agent-review:
  stage: review
  tags:
    - quake-ai
  rules:
    - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
  script:
    - npm install @cursor/sdk
    - node scripts/agent-pr-review.mjs > agent-review.md
    - |
      curl --request POST \
        --header "PRIVATE-TOKEN: $GITLAB_API_TOKEN" \
        --form "body=<agent-review.md" \
        "$CI_API_V4_URL/projects/$CI_PROJECT_ID/merge_requests/$CI_MERGE_REQUEST_IID/notes"
  variables:
    PR_BASE_REF: $CI_MERGE_REQUEST_TARGET_BRANCH_NAME

GITLAB_API_TOKEN is a separate masked, protected CI/CD variable holding a project or personal access token with API scope; GitLab does not expose an equivalent of GitHub's automatic GITHUB_TOKEN for posting notes from a job.

Step 4: Verify#

Open a test pull request against the repository. Confirm the workflow runs on the self-hosted runner and check the job logs from your CI provider's pipeline UI.

A successful run ends with the comment posted on the pull request. If the job exits with code 1, the run never started; check that CURSOR_API_KEY is set and the runner has outbound network access to reach the Cursor API. If it exits with code 2, the run started and the agent's transcript failed midway; rerun with the run ID logged by the script for further investigation.

Scope#

This is a CI job pattern on infrastructure you operate, not a hosted review product. You are responsible for the runner's uptime, the CI secret's rotation, and any cost the Cursor API key's plan accrues per run. Keep each job's prompt narrow and reproducible so reviewers can predict what the agent does on every run.

Next steps#

Clean up#

Remove the workflow file (.github/workflows/agent-review.yml or the GitLab agent-review job) and the scripts/agent-pr-review.mjs script from the repository. Delete the CURSOR_API_KEY secret from your CI provider's secret store, and revoke the key from Cursor Dashboard → Integrations if you created it solely for this deployment. The self-hosted runner itself is unaffected; follow Deploy a self-hosted CI runner's clean-up section if you also want to deregister it.

Before this
Was this page helpful?