Set Up a Self-Running Loop That Prompts the AI for You
This page installs a loop in your own Claude Code that lets the AI decide what to do next and keeps running on its own. Paste the one-shot prompt below into Claude Code once, and Claude creates the four files it needs (memory, roles, loop) and runs the first round with your confirmation. On 2026-10-23 we ran each part on Claude Code 2.1.223 and fixed the errors we found. The results are in "What we checked by actually running it". For the background and the idea behind it, see What is a self-running loop.
Updated 2026-10-23: fixed loop.sh skipping every task after the first, replaced a hook example that blocked nothing, changed how the plan JSON is read, and added when to use /goal plus our test results.
loop.sh or /goal: which to use
If you only need one session to keep working until one completion condition holds, Claude Code's built-in /goal is enough.
Use the loop.sh on this page when you want to separate the role that decides from the role that builds, cap the cost of each call, or keep every plan as JSON.
- Who decides the next step
- /goal: The working Claude
- loop.sh on this page: planner (a read-only role)
- How it decides it is done
- /goal: After every turn, a small fast model reads what surfaced in the conversation. The evaluator does not run commands
- loop.sh on this page: planner returns done, or MAX_ITERS is reached
- Permission split
- /goal: The session's permission mode
- loop.sh on this page: planner only reads; only worker can edit
- How to stop
- /goal: Write "stop after 20 turns" into the condition,
/goal clear, Ctrl+C - loop.sh on this page: MAX_ITERS,
--max-budget-usdper call, Ctrl+C
- /goal: Write "stop after 20 turns" into the condition,
- Setup
- /goal: None.
claude -p "/goal <condition>"starts it - loop.sh on this page: Create four files with the one-shot prompt below
- /goal: None.
- Our test (2026-10-23)
- /goal: Met the condition in 5 turns, 26 s, $0.081
- loop.sh on this page: One planner call $0.055, one worker call $0.093
How /goal works is from the official page "Keep Claude working toward a goal" (checked 2026-10-23).
Because the evaluator only reads the conversation, both /goal and loop.sh need a task whose completion shows up as a test or build result; otherwise there is nothing to stop on.
First, be clear about where this applies
This works for tasks whose results a machine can verify, such as coding. Three conditions must hold.
- Verifiable: tests, builds, or running the app can judge the result.
- Safe to start: the first action is a draft, a suggestion, or a small fix, not something destructive.
- Bounded: caps on iteration count and cost so you can stop a runaway.
Tasks whose right answer is a matter of taste, such as judging prose quality, fail the first condition, so do not put them on this loop.
Just paste this into Claude (one-shot setup)
Copy the whole prompt below and paste it once into Claude Code opened in your target project. Claude creates the memory file, the role agents, and the loop itself, confirms the goal and verification command with one question, then runs the first round. Press "Copy" at the top right to grab the full text.
You are the setup agent that installs a "self-running loop" into this repository.
Create the following four files, then explain how to use them. Each file body is separated by ━━━.
First, ask me exactly one question (if unanswered, proceed with the example values):
- The goal of this loop, in one sentence
- The verification command that checks completion mechanically (tests / build / smoke run)
━━━ File 1: CLAUDE.md (project root) ━━━
# Project assumptions
## What we are building
(1-2 lines. e.g. an internal FastAPI server)
## Rules
- Match the existing code style. No unrequested refactoring
- Minimal changes. Don't touch unrelated files
- No destructive ops (delete, DROP, force push)
## Verification commands (used as the loop's pass/fail check)
- Tests: e.g. pytest -q
- Smoke: e.g. start uvicorn app.main:app --port 8999 & then curl -sf localhost:8999/health
## Off-limits
- app/secrets/ , .env , production config
━━━ File 2: .claude/agents/planner.md ━━━
---
name: planner
description: Decides the next small, independent task toward the goal
tools: ["Read", "Grep", "Glob", "Bash(git log:*)", "Bash(git diff:*)", "Bash(ls:*)"]
model: opus
---
You are the planner. You do not write code.
Inspect the current state of the repo and pick the next independent, small tasks toward the goal (at most 3).
Each task must include a verification command that checks completion mechanically.
If the goal is already met, set done to true.
━━━ File 3: .claude/agents/worker.md ━━━
---
name: worker
description: Implements one task and verifies it before finishing
tools: ["Read", "Edit", "Write", "Bash"]
model: sonnet
---
You are the implementer. You implement only the single task you are given.
After implementing, you must run the given verification command and fix any failures before finishing.
Do not touch files outside scope. Do not claim completion while verification fails.
━━━ File 4: loop.sh (repository root) ━━━
#!/usr/bin/env bash
set -euo pipefail
GOAL="${1:?usage: loop.sh \"<goal in one sentence>\"}"
MAX_ITERS="${MAX_ITERS:-8}"
BUDGET="${BUDGET:-5}"
SCHEMA='{"type":"object","properties":{"done":{"type":"boolean"},"tasks":{"type":"array","items":{"type":"object","properties":{"title":{"type":"string"},"prompt":{"type":"string"},"verify":{"type":"string"}},"required":["title","prompt","verify"]}}},"required":["done","tasks"]}'
for ((i=1; i<=MAX_ITERS; i++)); do
echo "---- iteration $i / $MAX_ITERS ----"
envelope=$(claude -p "Goal: $GOAL. Inspect the current state and emit the next independent tasks (at most 3) as JSON. If everything is done, set done:true." --agent planner --output-format json --json-schema "$SCHEMA" --max-budget-usd "$BUDGET") || true
plan=$(echo "$envelope" | jq -c '.structured_output // empty')
[ -z "$plan" ] && { echo "No planner output: $(echo "$envelope" | jq -r '.subtype // "unknown"')"; exit 1; }
[ "$(echo "$plan" | jq -r '.done')" = "true" ] && { echo "Done"; break; }
echo "$plan" | jq -c '.tasks[]' | while read -r t; do
prompt=$(echo "$t" | jq -r '.prompt'); verify=$(echo "$t" | jq -r '.verify')
echo "= $(echo "$t" | jq -r '.title')"
claude -p "Implement this task: $prompt . When done, you must run this to confirm and fix it if it fails: $verify" --agent worker --permission-mode acceptEdits --max-budget-usd "$BUDGET" < /dev/null
done
done
━━━ Finally ━━━
List the four files you created and run chmod +x loop.sh.
Then run only the first iteration after confirming with me, and show the result.
Do not run fully autonomously from the start. Keep the cost cap (--max-budget-usd) and iteration cap (MAX_ITERS) in place.
That single paste is all you need.
What this one-shot prompt does
When you paste it, Claude reads the text as "instructions plus embedded file bodies" and creates three things in your project.
- Memory:
CLAUDE.md. Pins what you are building, the rules, the verification commands, and the off-limits areas, so you never re-explain them. - Roles:
.claude/agents/planner.md(decides the next job) andworker.md(implements and self-verifies). Thetoolsfield splits permissions: the planner only reads, only the worker can edit. - Loop:
loop.sh. Has the planner emit the next tasks as JSON, has the worker implement and verify them, and repeats until done or a cap is hit.
After creating them, Claude asks you one question about the goal and verification command, then runs only the first round, with your confirmation.
It does not run fully autonomously from the start.
The ━━━ markers are file separators and part of the instruction — leave them in when you paste.
What you need to run it
- Claude Code 2.x (check with
claude --version; we tested 2.1.223 on 2026-10-23) jq(loop.shuses it for JSON)- A project under git
- The tools your verification command needs (such as
pytest), installed beforehand. The worker is not allowed to install dependencies (pip installand the like); in our test the attempt was denied
Keeping it running after the first round
Once setup is done, you drive it.
Start with ./loop.sh "your goal in one sentence".
Cap it with the iteration limit MAX_ITERS and the cost limit BUDGET (USD per call), e.g. MAX_ITERS=3 BUDGET=2 ./loop.sh "...".
Press Ctrl-C to stop. A call that hits the cost cap stops slightly past the cap.
What we checked by actually running it (2026-10-23)
Conditions: macOS, Claude Code 2.1.223, jq 1.7.1, an empty git repository.
To keep costs down we ran the tests with --model haiku; six calls cost $0.464 in total.
This machine's ~/.claude/settings.json already allows the python3 and grep commands, and the results below are under that condition.
- Do the flags exist
- How:
claude --help - Result:
--agent,--json-schema,--max-budget-usd,--output-format, and--permission-modeare all there - What we changed on this page: Nothing
- How:
- Does the worker call swallow the remaining tasks
- How: Called
claude -pinsidewhile readand checked whether the second input line reached the loop - Result: The first call read the second line (the second task), and the loop ended after one pass
- What we changed on this page: Added
< /dev/nullto the worker call. The old version ran only the first of up to three tasks every iteration
- How: Called
- Which field holds the planner's JSON
- How: Called the planner role once with
--json-schema - Result: It was in
.structured_output, and.resultheld the same JSON as a string - What we changed on this page: Switched to
.structured_output, which the official docs guarantee
- How: Called the planner role once with
- What comes back when the cost cap stops a call
- How: Same call with
--max-budget-usd 0.10 - Result: Stopped at turn 2 with
error_max_budget_usd; it actually cost $0.147, and neither JSON field existed - What we changed on this page: The loop now prints the reason and stops when there is no output
- How: Same call with
- Can the worker run the verification command
- How: Asked for
grepandpython3 -m pytestchecks under--permission-mode acceptEdits - Result: Both ran. pytest was not installed so it failed, and the worker's
pip3 install pytestwas denied - What we changed on this page: Added "install the verification tools first" to the requirements
- How: Asked for
- Does the hook example block dangerous commands
- How: Fed inputs such as
rm -rf buildto the old and new command - Result: The old one exited 0 and blocked nothing. The new one exited 2 for
rm -rf,git push --force, andDROP TABLE, and letls -lathrough - What we changed on this page: Replaced the example in "Step 4" below
- How: Fed inputs such as
- Can /goal do the same
- How:
claude -p "/goal line 2 of README.md is hello … stop after 5 turns" - Result: Met the condition in 5 turns, 26 s, $0.081
- What we changed on this page: Added the comparison at the top
- How:
The cost cap stops a call after it is exceeded, so a call can spend more than the cap.
On this machine the first call wrote about 34,000 tokens to the cache (loading the global settings and CLAUDE.md), which alone went past $0.10.
Set BUDGET too low and the planner stops before it answers at all.
For commands your settings.json does not allow, the official page "Run Claude Code programmatically" (checked 2026-10-23) says shell commands still need --allowedTools under acceptEdits.
In such an environment, add your verification command to the worker call, for example --allowedTools "Bash(pytest *)".
How it works, piece by piece (for hand-building or customizing)
The sections below explain what the one-shot prompt creates, file by file. The full file bodies are in the one-shot prompt, so here we cover only the decisions that matter.
Step 1: Fix the memory (CLAUDE.md)
What you write in CLAUDE.md at the project root is loaded automatically every session, so every agent in the loop shares the same assumptions.
The key is to write the "verification commands" as commands you can actually type.
If they are vague, the AI moves on believing it is done.
If your project has type checks, add a line such as mypy app/.
Step 2: Split the roles (subagents)
Put one file per role under .claude/agents/ and scope each role's permissions with the tools field in the frontmatter.
The planner gets only Read, Grep, Glob, and read-only commands such as git log, so no file changes during planning.
The worker gets Edit and Bash and runs the verification command itself.
model is opus for the planner and sonnet for the worker.
You can drop the worker to haiku to save money; whether that increases the number of fix-and-retry rounds is not something we measured.
Step 3: Run it (the loop runner)
This script has the planner emit the next tasks as JSON, then runs each one through the worker with verification.
It stops when done:true comes back or the iteration cap is reached.
#!/usr/bin/env bash
set -euo pipefail
GOAL="${1:?usage: loop.sh \"<goal in one sentence>\"}"
MAX_ITERS="${MAX_ITERS:-8}" # iteration cap (runaway brake)
BUDGET="${BUDGET:-5}" # cost cap per call (USD); a call stops after it passes the cap, so it can overshoot a little
# JSON schema that constrains the planner's output
SCHEMA='{"type":"object","properties":{
"done":{"type":"boolean"},
"tasks":{"type":"array","items":{"type":"object","properties":{
"title":{"type":"string"},
"prompt":{"type":"string"},
"verify":{"type":"string"}
},"required":["title","prompt","verify"]}}
},"required":["done","tasks"]}'
for ((i=1; i<=MAX_ITERS; i++)); do
echo "──── iteration $i / $MAX_ITERS ────"
# 1) Let the AI decide what to do next (read-only, structured output)
envelope=$(claude -p "Goal: $GOAL
Inspect the current state and emit the next independent tasks (at most 3) as JSON. If everything is done, set done:true." \
--agent planner \
--output-format json \
--json-schema "$SCHEMA" \
--max-budget-usd "$BUDGET") || true
# Schema-conforming output lands in .structured_output; the field is missing when a cap stopped the call
plan=$(echo "$envelope" | jq -c '.structured_output // empty')
if [ -z "$plan" ]; then
echo "No planner output: $(echo "$envelope" | jq -r '.subtype // "unknown"')"; exit 1
fi
if [ "$(echo "$plan" | jq -r '.done')" = "true" ]; then
echo "Goal reached. Exiting."; break
fi
# 2) Implement and verify each task, one at a time
echo "$plan" | jq -c '.tasks[]' | while read -r t; do
title=$(echo "$t" | jq -r '.title')
prompt=$(echo "$t" | jq -r '.prompt')
verify=$(echo "$t" | jq -r '.verify')
echo "▶ $title"
# Without < /dev/null, claude -p reads the remaining task lines and every task after the first is skipped
claude -p "Implement this task:
$prompt
When done, you must run this to confirm the result and fix it if it fails:
$verify" \
--agent worker \
--permission-mode acceptEdits \
--max-budget-usd "$BUDGET" < /dev/null
done
done
Usage is one line.
chmod +x loop.sh
./loop.sh "Raise auth test coverage to 80%"
Deciding "what to do" is the planner's job, not yours. What you write shrinks to one sentence of goal, plus the loop itself.
Step 4: Brakes (stop runaways and cost)
Left alone, a loop keeps running and the bill keeps climbing. Stop it three ways.
- Iteration cap: the runner's
MAX_ITERS. - Cost cap:
--max-budget-usdon each call (only works with-p). A call stops after it passes the cap, so it can spend a little more than you set (in our test, $0.147 against a $0.10 cap). - Permission wall: reject dangerous operations with a
PreToolUsehook in~/.claude/settings.json. The hook receives the tool input as JSON on stdin, and exit code 2 blocks that tool call.
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{ "type": "command",
"command": "jq -r '.tool_input.command // empty' | grep -qiE 'rm -rf|git push --force|drop table' && { echo 'Blocked a dangerous command' >&2; exit 2; } || exit 0" }
]
}
]
}
}
The old version of this page read the input from an environment variable, $CLAUDE_TOOL_INPUT.
According to the official "Hooks reference" (checked 2026-10-23), the input arrives on stdin and no variable by that name exists.
With the old example the variable stays empty, so even rm -rf goes through.
Start the worker's permissions at --permission-mode acceptEdits (edits auto, everything else confirmed) and widen to dontAsk or bypassPermissions only once you trust it.
Do not reach for --dangerously-skip-permissions.
Copy-paste: instruction prompts
For trying the same flow inside an interactive session, without the script.
Keep going until a condition holds (/goal)
/goal pytest -q passes and git status shows no unintended changes. Stop after 20 turns
Make the planner decide what's next
You are the planner. Do not write code.
Goal: "__your goal__"
Inspect the current state of the repo and list the next independent, small tasks (at most 3),
each with a verification command.
If the goal is already met, just answer "done".
Make the worker implement and self-verify
Implement only the following task (do not touch anything out of scope):
__task__
When finished, run this, paste the result, and fix it if it failed before saying done:
__verification command__
Self-run one round (mini loop)
Run one cycle of "inspect state → decide the next single task → implement → verify".
If verification passes, ask me in one line whether to proceed to the next cycle.
For cost and safety, keep each cycle to a single concern.