How to Run a Ralph Loop With the Cursor CLI
To run a Ralph loop with the Cursor CLI, install Ralph into your project, create a task list, log in to Cursor once inside its sandbox, then run ./ralph.sh --agent cursor. Ralph wraps the headless cursor-agent in print mode, gives it a fresh context window every iteration, and keeps re-running it against your task list until the work is done or you hit the iteration cap.
This is the full setup walkthrough, from an empty repo to a finished diff. If you are new to the loop itself, start with what the Ralph technique is. If you want the flag-level mechanics of the Cursor backend, the companion piece is running the Cursor CLI agent in a loop. This post is the end-to-end recipe.
Why pair Ralph with the Cursor CLI?
Section titled “Why pair Ralph with the Cursor CLI?”Cursor ships a headless agent, cursor-agent, that runs from the terminal with no editor attached. In print mode (-p) it reads a prompt, edits files, runs shell commands, prints a final message, and exits. That exit is the hook Ralph needs. Each iteration is a discrete unit: spawn the agent, let it do one task, read what it emitted, repeat.
Ralph supplies the three things a single cursor-agent call cannot:
- A loop, so the agent gets another pass after every commit instead of stopping.
- Fresh context per iteration, so a long run never rots into a confused, drifting session.
- A Docker sandbox boundary, so the agent can edit files and run commands in YOLO mode without that blast radius reaching your real machine.
The state lives on disk (the task list, the logs, the git history), not in a chat transcript. That is what lets a stateless cursor-agent reorient on every pass and still make forward progress across dozens of iterations.
Step 1: Install Ralph
Section titled “Step 1: Install Ralph”Run the installer in your project root. It drops the ralph.sh script and the .agent/ directory into the repo.
npx @pageai/ralph-loopThe installer asks for your dev server URL and port and your default agent, then writes a default implementation-mode setup. Pick cursor when prompted, or set it per run with --agent cursor later. After install, make the script executable if it is not already:
chmod +x ralph.shRalph runs each agent inside a Docker Sandbox, so install and sign in to Docker Sandbox first. See the Docker Sandboxes docs for the official setup flow. The loop drives the standalone sbx CLI under the hood.
Step 2: Create a PRD and task list
Section titled “Step 2: Create a PRD and task list”The loop executes a task list. It does not invent one. Before you run anything, turn your requirements into a PRD and a set of task specs with the prd-creator skill.
Open Cursor (or any agent) in plan mode and prompt it with your requirements:
Use the prd-creator skill to help me create a PRD and task list for the below requirements.
An app is already set up with React, Tailwind CSS and TypeScript.
Requirements:- A SaaS product that helps users manage their finances.- Track income and expenses, create invoices, generate reports.- Use shadcn/ui for components and Supabase for the database.// etc.The skill writes .agent/prd/PRD.md, a short .agent/prd/SUMMARY.md, and per-task specs under .agent/tasks/, indexed by .agent/tasks.json. Review each task individually before you start. A clear task list with real acceptance criteria is the single biggest factor in whether the loop succeeds. The deeper version of that workflow is in spec-driven development with AI.
Step 3: Log in to Cursor inside the sandbox
Section titled “Step 3: Log in to Cursor inside the sandbox”Cursor runs inside an isolated sandbox, not on your host, so it needs credentials in that environment. Authenticate once with the login action:
./ralph.sh --login --agent cursorRalph prints the login command for every supported agent, highlights the Cursor one, and drops you into the sandbox shell. The command it runs to create the box looks like this:
sbx run --name ralph-cursor-<project>-<hash8> cursor .Inside the sandbox, authenticate Cursor once. Run cursor-agent login and follow the prompt, or set a CURSOR_API_KEY environment variable. Confirm the session with cursor-agent status. The credential persists in that named sandbox, so later runs attach to the same box already logged in.
Each agent gets its own deterministic sandbox name, built from the agent slug, the project directory, and an 8-character hash of the absolute path:
ralph-cursor-<project>-<hash8>Print the exact name for your project without starting a run:
./ralph.sh --print-name --agent cursorIf your tasks run a dev server you want to reach from the host, publish the configured port to the Cursor sandbox. The port comes from the dev-server URL you set during install:
./ralph.sh --ports --agent cursorThat expands to an sbx ports call against the Cursor sandbox, for example sbx ports ralph-cursor-<project>-<hash8> --publish 3000:3000. Per-agent names keep state separate: your Cursor sandbox and your Claude sandbox never share credentials or installed tools, so you can run both against the same repo without one clobbering the other.
Step 4: Run the loop with Cursor
Section titled “Step 4: Run the loop with Cursor”With a task list in place and Cursor authenticated, start the loop. The default agent is Claude, so name Cursor explicitly:
# 10 iterations (the default)./ralph.sh --agent cursor
# 50 iterations for a longer unattended run./ralph.sh --agent cursor -n 50
# exactly one iteration, good for a smoke test./ralph.sh --agent cursor --onceThe short form -a works too: ./ralph.sh -a cursor -n 5. The iteration count is a safety budget, not a target. The loop stops the moment the work is done.
Under the hood, Ralph builds a cursor-agent command and runs it inside the sandbox. For ./ralph.sh --agent cursor, the expansion is:
sbx run --name ralph-cursor-<project>-<hash8> cursor . -- -p "$PROMPT_CONTENT"The -p flag is Cursor’s print (headless) mode, which still has full tool access including file writes and shell. That is exactly what a loop needs: an agent that can do real work without a person approving each step.
Pass a model and flags after the separator
Section titled “Pass a model and flags after the separator”Everything to the right of Ralph’s own -- separator is forwarded straight to cursor-agent, inserted right after -p. So this:
./ralph.sh --agent cursor -- --model autoexpands to:
sbx run --name ralph-cursor-<project>-<hash8> cursor . -- -p --model auto "$PROMPT_CONTENT"Do not guess at model names. Run cursor-agent models inside the sandbox to print the exact identifiers Cursor accepts, then pass one. The rule to remember: everything left of -- configures Ralph (agent, iteration count, login), and everything right of -- configures Cursor. A practical full command for an overnight run:
./ralph.sh --agent cursor -n 50 -- --model auto --forceThe --force flag (alias --yolo) lets Cursor run commands without pausing for approval, which is what keeps an unattended run from stalling on a prompt nobody is there to answer. It is safe here only because the sandbox is the boundary.
What happens each iteration
Section titled “What happens each iteration”Every pass is mechanical and identical. The agent reorients from disk, does one task, proves it works, and commits.
- Find the highest-priority incomplete task in
.agent/tasks.json. - Work the steps in
.agent/tasks/TASK-{ID}.json. - Run tests, linting, and type checking.
- Complete the task, take a screenshot, update the task status, and commit.
- Repeat until all tasks pass or the iteration cap is reached.
The key detail is that each iteration spawns a fresh cursor-agent -p with a clean context window. The agent does not carry an hours-long transcript from one task to the next. It reads the current state from disk, does one task, and exits. This is why the loop deliberately does not use Cursor’s own session resume flags: continuity is a liability here, not a feature.
flowchart TD
Start(["./ralph.sh --agent cursor -n 50"]) --> Pick["Pick top task from .agent/tasks.json"]
Pick --> Spawn["sbx run cursor . -- -p (fresh context)"]
Spawn --> Work["cursor-agent reads state, edits files, runs commands"]
Work --> Verify["Run tests, lint, type check, screenshot"]
Verify --> Commit["Commit and update task status"]
Commit --> Check{"Promise tag emitted?"}
Check -->|"none"| Pick
Check -->|"COMPLETE"| Done(["exit 0, all tasks done"])
Check -->|"BLOCKED or DECIDE"| Stop(["exit 2 or 3, wants a human"])
The filesystem and git history are the memory layer. Progress lives in .agent/tasks.json, .agent/logs/LOG.md, the per-task specs, and the git log, not in a chat transcript. The prompt that drives every pass lives in .agent/PROMPT.md, and writing it well is its own skill, covered in how to write the PROMPT.md file.
One rule keeps the whole thing reliable: one task per invocation. Cursor completes exactly one task, commits, and stops. It never batches several tasks into a single iteration, which is what keeps each commit small and each diff reviewable.
How the loop knows when to stop
Section titled “How the loop knows when to stop”A loop that never ends is a money fire, so Ralph stops on an explicit signal rather than a guess. Cursor emits a semantic promise tag in its final message and Ralph reads it:
<promise>COMPLETE</promise>means every task is finished.<promise>BLOCKED:reason</promise>means the agent needs human help.<promise>DECIDE:question</promise>means it needs a decision you have to make.
Those map to exit codes, which is what makes the loop scriptable:
./ralph.sh --agent cursor -n 50echo $? # 0 COMPLETE, 1 MAX_ITERATIONS, 2 BLOCKED, 3 DECIDEExit code 1 is not a failure. It means the loop spent its iteration budget with work still pending, which is exactly what a safety cap is for. The full design lives in completion promises and exit codes. If Cursor is not authenticated when the loop starts, Ralph detects the auth failure, stops, and tells you to run ./ralph.sh --login --agent cursor, so it never thrashes on a box that can never make progress.
Steer, debug, and review
Section titled “Steer, debug, and review”Three things keep a long Cursor run under control.
Steer without stopping
Section titled “Steer without stopping”If you notice the agent heading the wrong way, you do not have to kill the loop. Edit .agent/STEERING.md while it runs. The agent checks that file on each pass and handles the critical work there before resuming the normal task order. That is steering, not stopping, and it keeps momentum while you correct course.
Get inside the sandbox
Section titled “Get inside the sandbox”The sandbox is a normal container you can poke at. List what exists, then open a shell in the Cursor box:
sbx lssbx exec -it ralph-cursor-<project>-<hash8> bashFrom there you can re-run a failing test by hand, confirm auth with cursor-agent status, or read .agent/logs/LOG.md and the per-iteration logs in .agent/history/. When an install fails, the deny-by-default network is usually the cause. Allow the domain it needs:
sbx policy allow network ralph-cursor-<project>-<hash8> registry.npmjs.orgThat is a feature, not a bug. The agent installs what the task needs without a path to exfiltrate your source. The full argument is in running AI coding agents in Docker sandboxes safely.
Review the diff in the morning
Section titled “Review the diff in the morning”Because every task is committed separately, the morning review is a git review, not an archaeology dig:
git log --onelinegit diff main...HEADYou read the commits in order, eyeball the screenshots the agent captured, and accept or revert. A single bad task is one revert, not a tangled mess. This is the whole premise of running an AI coding agent overnight.
Should you use Cursor or another agent?
Section titled “Should you use Cursor or another agent?”Ralph is agent agnostic. The supported agents are claude (default), codex, copilot, cursor, gemini, and opencode, and switching is a single flag. Cursor is a strong default when you already pay for it and want its model routing through --model auto. The loop machinery is identical across agents, so the choice comes down to which CLI holds up over a multi-hour run. That comparison is in the best agentic CLI for long-running tasks, and the broader field guide is agentic coding CLIs.
A real Cursor loop, start to finish, is four commands:
# 1. install Ralph and scaffoldingnpx @pageai/ralph-loop
# 2. authenticate once (creates the sandbox, you log in inside it)./ralph.sh --login --agent cursor
# 3. smoke test a single pass./ralph.sh --agent cursor --once
# 4. run the loop for real with a model and force mode./ralph.sh --agent cursor -n 50 -- --model auto --forceDefine your tasks in .agent/tasks.json, write a clear .agent/PROMPT.md, start the loop, and read the commits in the morning.
Frequently asked questions
How do I run a Ralph loop with the Cursor CLI?
Install Ralph with npx @pageai/ralph-loop, create a task list, log in with ./ralph.sh --login --agent cursor, then run ./ralph.sh --agent cursor. Ralph wraps the headless cursor-agent in print mode inside a Docker sandbox, starts a fresh context each iteration, and repeats until every task in .agent/tasks.json is done or the iteration cap is reached.
Does ralph.sh support the Cursor CLI?
Yes. Cursor is one of the supported agents, alongside claude, codex, copilot, gemini, and opencode. Select it with --agent cursor or the short form -a cursor. Ralph builds a cursor-agent command and runs it inside a deterministic sandbox named ralph-cursor-<project>-<hash8>.
How do I pass a Cursor model through Ralph?
Put it after the -- separator. Anything to the right of -- is forwarded to cursor-agent, so ./ralph.sh --agent cursor -- --model auto runs cursor-agent in print mode with that model and the Ralph prompt. Run cursor-agent models inside the sandbox to list the exact identifiers Cursor accepts.
How do I log in to Cursor for the loop?
Run ./ralph.sh --login --agent cursor. It drops you into the sandbox shell where you run cursor-agent login, or you can set CURSOR_API_KEY. The credential persists in that named sandbox, so future runs attach to the same box already authenticated.
Is it safe to run cursor-agent in force or YOLO mode?
It is unsafe on your laptop and reasonable inside a sandbox. Ralph runs cursor-agent in an isolated Docker Sandbox microVM with network denied by default, so --force lets the agent run commands without approval prompts while staying unable to touch your real files. The sandbox is the boundary, not the agent.