Skip to content

Course · Cursor · The CLI, headless & CI

Wiring it into CI

The headless command (the CLI’s --print mode - it answers one prompt and exits) plus a Cursor API key is everything you need to put budgetcli’s agent - the course’s illustrative personal-finance API - on a runner. Create the key in Cursor Dashboard > API Keys (CLI authentication reference, as of September 2026) and save it as the repository secret CURSOR_API_KEY. The job we’ll automate is the transaction check: on every push, inspect whatever landed and flag anything that looks off, so a bad import doesn’t quietly corrupt months of history before you notice.

The workflow is unremarkable on purpose - install the CLI (it provides the agent command), set the key from a secret, run one headless command, gate the step on its result. The flags are the ones the reference documents (as of September 2026 - parameters reference); the install line matches the CLI installation docs as of the same date:

# .github/workflows/budgetcli-check.yml (shape only - confirm flags before use)
name: budgetcli transaction check
on: [push]
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install the CLI
run: curl https://cursor.com/install -fsS | bash
- name: Run the check
env:
CURSOR_API_KEY: ${{ secrets.CURSOR_API_KEY }}
run: |
agent -p "Inspect the transactions added in this push without editing files.
Reply with exactly PASS or FAIL: FAIL if any move sends an account
negative or lands in 'uncategorized', otherwise PASS." \
--output-format json > result.json
test "$(jq -r '.result' result.json)" = "PASS"

The assertion here reads the documented .result field - the headless JSON output carries .result and no .ok boolean (output-format reference), so jq -r '.result' pulls the agent’s answer and test turns it into the step’s exit code. This example deliberately omits --force, which auto-approves command execution (“Force allow commands unless explicitly denied” is the reference’s own definition): the prompt is read-only, so the runner does not need it. If a real CI task must edit files, use --force only inside a disposable, isolated runner and review the prompt and permissions separately.

The one genuinely new thing is the gate: reading .result and comparing it turns the agent’s JSON answer into the step’s exit code, so a failed workflow fails after the push. To prevent merging, configure branch protection or required status checks; a workflow triggered by push cannot retroactively prevent that push.

The gate is also where an unattended run’s honesty comes from - control, in this course’s sense (what enforces a boundary), made mechanical. With no one at the keyboard, PASS is just the agent’s opinion until something the agent can’t sweet-talk - a build, the type checker, the test suite, a full end-to-end run - has to agree. The scene below replays a generic overnight batch (a different repo, nine chores, the same question) and asks what the run has to get past before its morning report means anything. In this page’s spelling, each answer is another step in this same workflow file - a build step, tsc, the unit suite, an end-to-end job - with the .result gate deciding what the whole thing reports:

Nine chores, queued for an agent to run overnight. The terminal replays the night as it happened; the panel is the morning report - the only thing you’ll actually read. Watch what its checkmarks are made of, and when playback pauses, decide what the run has to get past before you believe it.

overnight batch - ledger-app
overnight batch · repo: ledger-app · 9 chores queued · nobody watching
the morning report

    The flaw rate here is theatrical - seven duds in nine - because the point is what each wiring would have caught, not the odds. The checks wear different clothes per tool (a CI step, a test command the loop must pass, a verifier agent), but the question is always the same: what does this run have to get past before “done” reaches you? And the residual is real: no check catches a requirement nobody wrote down.

    A CI job still burns your minutes on your runner, and it only fires on a push. Cloud Agents and Bugbot move the work off your infrastructure entirely and let it start from a comment. Next: Cloud Agents and Bugbot.