Skip to content

Wiring it into CI

The headless command plus CURSOR_API_KEY is everything you need to put budgetcli’s agent on a runner. The job we’ll automate is the transaction check: on every push, recategorise 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, set the key from a secret, run one headless command, gate the step on its result:

# .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 cursor-agent
run: curl https://cursor.com/install -fsS | bash
- name: Run the check
env:
CURSOR_API_KEY: ${{ secrets.CURSOR_API_KEY }}
run: |
cursor-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 'uncategorised', 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, so jq -r '.result' pulls the agent’s answer and test turns it into the step’s exit code. An earlier draft of this example asserted a .ok boolean; there is no documented .ok field, so don’t reach for one. This example deliberately omits --force: the prompt is read-only, so the runner does not need unattended write permission. 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. With no one at the keyboard, PASS is just the agent’s opinion until something the agent can’t sweet-talk - a build, the suite, a full-path run - has to agree. Which checks you wire into the job decide what its green actually means:

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. The last two surfaces move the work off your infrastructure entirely and let it start from a comment. Next: Cloud Agents and Bugbot.