Run it unattended
Run a flow from one shell line, with nobody at the prompt: from a script, a cron entry or a CI job. By the end of this page you can write that line, check it before you schedule it, read what it prints, and act on how it ended. Reach for it whenever a run should start without you, or when another program wants to read the run as it happens.
Try it
hmz exec -f ralph_loop \
-a agent=claude/claude-opus-5:high \
-p budget.duration=2h,budget.cost=10 \
"$(cat TASK.md)"The run is drawn in your terminal as it happens, and the command returns when the flow does: here, when two hours or ten dollars are spent, whichever comes first.
Nobody approves anything
A flow's agents run with approvals bypassed: nothing asks before an agent edits a file or runs a command. What each agent may touch is set by the flow. See Permissions.
Before you start
- Humanize installed, so
hmzis on yourPATH. See Installation. - A coding agent CLI, installed and signed in on this machine.
hmz execruns it as it is signed in, unless you name an account. - The flow within reach.
ralph_loopships with Humanize. A flow you install from a flowverse is there once it is installed; on a machine where it is not, name the flow by its repository instead, as in CI. - A task, as text. Keeping it in a file such as
TASK.mdlets the line stay short and the task be reviewed like any other file.
How a line is read
hmz exec asks the same questions the /flow sheet asks at the prompt, and the line is every answer at once:
| Part | Answers | Needed |
|---|---|---|
-f | which flow: the name /flow offers it under, a path, or a git+…#flow ref | always |
-a | which CLI, account, model and effort fills each agent role | one per role |
-p budget.… | what the run may spend | every flow but chat |
-p | the flow's own params, where you want other than the defaults | when you want them |
-e | where an environment role is, for a flow that works on another machine | when the flow has one |
--profile-flow | sample the flow's own Python, so the run's trace shows where the flow spent its time | no |
--profile-environment | sample the programs the agents start, so the run's trace shows what each turn spent its time on | no |
--resume | carry on the last run of this flow here, rather than start over | no |
--json | write the run to stdout as one JSON object per line | no |
| the task | what the flow is to do | always |
hmz exec checks all of it before the first turn. Anything it cannot use is refused in a second, with nothing started. hmz exec does not read what the interface was set up with in this directory: the line is the whole setup.
Name an agent for each role
<role>=<cli>[@<account>]/<model>[:<effort>]- role is the name the flow gives the agent:
agentfor ralph_loop,actorandreviewerfor rlar. Each flow's page lists its roles. - cli is one of
agy,claude,codex,cursor-agent,dsh,grok,kimi,mcode,mimo,omp,opencode,pi,qwen, or a CLI you added on the Accounts page of/settings. - @account runs the turns as an account you made. Leave it off to run the CLI as this machine is signed in.
- model goes to the CLI as written. Humanize does not check it against a list.
- effort must be on that CLI's ladder; a CLI you added takes any word. Leave it off, or write
auto, for none. A:in the model stays the model's unless a word follows it, somcode/custom_provider:gateway/mneeds no effort; a model whose name ends in:<word>, asqwen3:latest, is written with its effort after it:qwen3:latest:auto.autoasks for none.
The CLI is read up to the first / and the effort after the last :, so a model with slashes in it needs no quoting. Several agents go in one -a, comma-separated, or in one -a each. Type one below, or pick an example:
-a is reada simulation, in your browser · built-in CLIs only- role
agent— must be a role the flow declares- CLI
- Claude Code
- account
- none — the CLI as this machine is signed in
- model
claude-opus-5— passed to the CLI as written- effort
high— on claude's ladder: ultracode, max, xhigh, high, medium, low
You never name the person or the working directory. Humanize fills those roles itself.
Set the budget
A loop runs until something stops it, so every flow but chat needs a budget, given as -p budget.<key>=:
| Key | Written as |
|---|---|
budget.duration | wall clock: 90s, 1h30m, 2d, or ISO PT6H |
budget.cost | US dollars: 50 or $50 |
budget.output_tokens | tokens the agents may write: 200k, 10m |
budget.graceful | false cuts off the turn under way when a limit is reached. By default it finishes. |
budget.graceful.duration, .cost, .output_tokens | the same, for that limit alone: budget.graceful.output_tokens=false cuts a turn off at the token cap and still lets one that reaches the cost finish. |
Give at least one limit. When one is reached, the run stops, hmz exec: stopped -- … names the limit on stderr, and the exit status is still 0: for a loop, that is the ordinary way to end. A flow that calls another shares the budget with it. See Every run has a budget.
A cost limit needs a priced model
cost can only stop a model Humanize has a price for. For any other, hmz exec says so before the first turn, and runs anyway:
hmz exec: nobody lists a price for claude-haiku-4-5-20251001, so cost=0.05 cannot stop what it spendsA run with a cost limit fetches the price list before its first turn when the copy kept is missing or more than a day old, so a machine that has never opened the interface prices a run too. The fetch waits at most 20 s; offline, or with HUMANIZE_PRICES=off, the run goes on with whatever list is kept. Add a duration or output_tokens limit whenever a run must stop.
Example: fix a failing test, stopped by its budget
A project with one bug: add() in calc.py subtracts, and test_calc.py says so. TASK.md holds Fix add() in calc.py so test_calc.py passes. Keep it minimal. Give a Ralph loop three cents of a small model and let it go:
hmz exec -f ralph_loop \
-a agent=claude/claude-haiku-4-5-20251001:low \
-p budget.cost=0.03 \
"$(cat TASK.md)"Points 1 to 4 below are the four parts of that line. What it drew is next, shortened (…), with the model's thinking aloud left out, and ⑤ to ⑧ marked on it:
● agent is working
● I'll read the test file and the calc.py file to see what needs fixing.
…
● Read(/home/me/calc/test_calc.py) ⑤
● Read(/home/me/calc/calc.py)
● The issue is simple: `add()` is using subtraction instead of addition. I'll fix it.
● Edit(/home/me/calc/calc.py)
● Bash(cd /home/me/calc && python -m pytest test_calc.py -v)
● Done. Changed line 2 of calc.py from `return a - b` to `return a + b`. Test passes.
✻ input 50 · output 762 · cache_read 120.6k · cache_write 8.5k · $0.03 · claude-haiku-4-5-20251001 · agent ⑥
✻ Worked for 11s · agent
● agent is working
…
● The test already passes! The `add()` function in calc.py is working correctly.
✻ input 26 · output 643 · cache_read 62.7k · cache_write 848 · $0.01 · claude-haiku-4-5-20251001 · agent
✻ Worked for 9s · agent
hmz exec: stopped -- ralph_loop:ralph_loop: its budget's cost is spent ⑦$ echo $?
0 ⑧What each part means
-f ralph_loopis the flow. A Ralph loop gives the same task to a fresh session every round, so each round reads the project as the last one left it.-a agent=claude/claude-haiku-4-5-20251001:lowfills the loop's one role,agent, with Claude Code on a small model at a low effort. A small model and a low effort are the cheap way to try a line; raise both for real work.-p budget.cost=0.03is the budget: three US cents. Use adurationtoo for anything you leave running, as the tip above says."$(cat TASK.md)"is the task, read from the file by your shell. The quotes keep it one argument, however many lines it has.● Read(…),● Edit(…),● Bash(…): each tool the agent reached for, as it reached for it. Lines in dim italic between them, cut here, are the model thinking aloud.✻ input 50 · output 762 · … · $0.03closes each turn: the tokens it spent by kind, what they come to at list price, the model and the role. See Cost and rate.hmz exec: stopped -- … its budget's cost is spentis the budget ending the run. The turn under way when it filled was let finish;graceful=falsewould have cut it off. The loop prints nothing of its own, so each round shows only as its turn: a new● agent is working.- Exit status
0. A budget reached is how a loop ends, not a failure. The second round found nothing left to do, which a Ralph loop does not notice: that is what the budget is for.
Check that it worked
- The change is in your files, where the run left it:
git diff, or herepython -m pytest test_calc.py. - The run is kept. Open
hmzin the same directory and type/epics: the run is listed as stopped, and can be traced or exported. - The exit status says how it ended: see Script on the exit status.
Example: keep the answer apart from the run
The run goes to stderr. What each turn answered, and whatever the flow prints, goes to stdout, so a script can take one without the other:
hmz exec -f chat -a assistant=claude/claude-haiku-4-5-20251001:low \
"Summarise in one line: Humanize runs coding agents in loops." \
> summary.txt 2> run.log$ cat summary.txt
**Humanize is a system for orchestrating coding agents to iteratively solve tasks in continuous loops.**
$ tail -2 run.log
✻ input 26 · output 596 · cache_read 59.7k · cache_write 4.8k · $0.01 · claude-haiku-4-5-20251001 · assistant
✻ Worked for 9s · assistant> summary.txttakes the answer alone.chatneeds no budget, and with nobody at a prompt it answers once and returns.2> run.logkeeps the run: every tool call, every turn's cost, and anyhmz exec: …line. Redirected, the lines come out without colour or the ticking clock.
At a terminal the same lines are drawn in colour, with a clock at the foot while a turn thinks: ⠹ agent is working · 12s. NO_COLOR turns colour off. FORCE_COLOR=1 turns it on for a log viewer that renders it.
Check the line before you schedule it
hmz exec checks everything it can before the first turn: a line it can't read, a missing role or budget, an effort off the CLI's ladder, a flow that isn't there. Each is refused in a second, with exit status 2, before any agent starts. So try the line by hand once:
$ hmz exec -f ralph_loop -a agent=claude/claude-haiku-4-5-20251001:low "x"
hmz exec: error: ralph_loop requires a budget: specify with -p budget.cost=...,budget.duration=...,budget.output_tokens=...
$ hmz exec -f chat -a assistant=claude/claude-haiku-4-5-20251001:ultra "x"
hmz exec: error: assistant=claude/claude-haiku-4-5-20251001:ultra: claude cannot be asked to think at 'ultra'; expected one of ultracode, max, xhigh, high, medium, lowWhat is only known once a machine is reached, such as an environment that cannot be, is refused the same way, still before the flow runs. Every message is in Troubleshooting.
Script on the exit status
| Status | Means |
|---|---|
0 | The flow returned, or its budget stopped it. |
1 | The run failed: the flow raised an error it did not handle. The traceback on stderr says which. |
2 | Refused before any agent started. The line after hmz exec: error: says why. |
130 | Interrupted with ctrl+c, once the run has let go of everything it started. A second ctrl+c ends it at once, without waiting. |
143 | Stopped by a terminate signal, such as kill or a cancelled CI job, once the run has let go of everything it started. |
if ! hmz exec -f goal -a worker=claude/claude-opus-5:max -p budget.duration=4h "$(cat TASK.md)"; then
echo "the loop did not finish" >&2
exit 1
fiRead it with a program
--json writes the run to stdout as NDJSON: one object per thing an agent says, flushed as it is said. Nothing else reaches stdout: the flow's own lines, such as the review rlar ends on, move to stderr with the rest, so the stream always parses.
hmz exec -f chat -a assistant=claude/claude-haiku-4-5-20251001:low --json "say hello" \
| jq -c 'select(.kind == "result")'That object, spread out here:
{
"at": 1790747303.0952988,
"agent": "assistant",
"cli": "claude",
"model": "claude-haiku-4-5-20251001",
"session": "0a7264b6-8180-406e-bcc0-c1feb14a1964",
"kind": "result",
"text": "Hello! 👋 How can I help you today?",
"whose": "",
"tokens": {"claude-haiku-4-5-20251001": 20829},
"spent": {"input": 10.0, "cache_write": 6885.0, "cache_read": 13874.0, "output": 60.0}
}Every object carries every key. kind is one of:
kind | The agent… |
|---|---|
begins, ends | starts and finishes a turn |
text, reasoning | says something, or thinks aloud |
tool | uses a tool; text names it and what it was given, as Read /home/me/calc/calc.py |
subagent, subagent-ends | starts an agent of its own, which later finishes |
asks | stops to ask a question |
took | has a line steered into the turn in front of it, and says which |
notice | is waiting out a rate limit, moved to another account, or cut off (Humanize says this, not the agent) |
failed | failed the turn |
result | gives the answer the turn ends on. Only this one carries tokens (by model) and spent (by kind of token). |
The full list of keys is in the CLI reference.
Variations
Pass params and environments
A flow with params of its own takes each one you want to change with -p. The flow checks the whole set before the first turn:
hmz exec -f humanize1:rlcr -p max=9,plan_file=docs/plan.md \
-a builder=claude/claude-opus-5:max \
-a reviewer=codex/gpt-5.6-sol:xhigh \
-p budget.duration=12h,budget.cost=100 "add undo"A flow that works on another machine takes it with -e, as role=local/abs/path, role=ssh@<host>/path for a host saved under that name, role=ssh@[user@host:port]/path for one nobody saved, or role=docker@<daemon>/path; where each agent's CLI runs while it works there is the harness setting of the host or daemon saved under that name (env, on that machine, for one nobody saved). Most flows have no such role. See Remote execution.
Stop it, and pick it up again
ctrl+c stops the run: the turn under way ends and its work stays where it got to. /epics lists the run as stopped. A flow that can be picked up, such as stateful_ralph, carries on from there when you run the same line with --resume: its next round carries on the conversation it kept after the last round that finished.
hmz exec -f stateful_ralph -a agent=claude/claude-opus-5:high -p budget.duration=2h \
--resume "$(cat TASK.md)"A Ralph loop keeps nothing but your files, so ralph_loop cannot be picked up: running the same line again, without --resume, carries it on.
When nobody answers
With nobody at a prompt, the run behaves as if you were away the whole time:
- A flow that talks to you is answered with nothing, so
chatdoes the one thing it was given and returns. - An agent that stops to ask a question is told nobody answered, and carries on. The question is still printed, and with
--jsonit is an object of kindasks. - A flow that needs an answer it has no default for fails, and says so.
A task that starts with a dash
Put -- before it, so it is read as the task rather than a flag:
hmz exec -f ralph_loop -a agent=claude/claude-opus-5:high -p budget.cost=5 \
-- "--force is not a flag here"Pitfalls
not installed -- install it from /flow, orthe official flowverse has not been fetched yet. The flow is one a flowverse lists, and this machine has not installed it. Install it from/flow, or name the release by its repository, as in CI.- A
costlimit that never stops the run. The model has no price, and the line said so before the first turn. Adddurationoroutput_tokens. - The CLI is not signed in. Nothing catches it before the run: the first turn fails, and the run ends with what the CLI said, exit status
1. Run the line by hand once and read what it prints. - A script that waits for ever on
chat. It will not: with nobody at a prompt,chatanswers once and returns. - A long job that dies when the round ends. A server or a training run an agent starts goes with its session, unless the agent detached it: started with
setsid nohup … &from a shell command that then returns, it keeps running into the next round. Have the agent write down where it logs, so the next session can find it.
More in Troubleshooting.
Next steps
- Humanize in CI: the same line in a scheduled job that opens a pull request
- Picking a run up:
--resume, and what carries over - Tracing a run: what the run did, as a timeline
- Remote execution:
-e, and where the agent runs - CLI reference: every flag of
hmz exec, and every refusal