Convergence-loop tool for Claude Code. Automates "do, check, repeat" cycles using a Stop hook and state file.
/until "Fix all lint warnings"
The same Claude session does the action and the check. A Stop hook reads a state file between turns and either intercepts the stop with a new prompt or lets the agent finish. The loop ends when the agent reports approve or hits an iteration cap.
Requires Claude Code (the Stop hook only fires inside Claude Code sessions), Node.js 20+, macOS or Linux.
npm install -g do-until
do-until setupdo-until setup installs three things: the /until slash command at ~/.claude/commands/until.md, the Stop hook wrapper at ~/.until/bin/do-until-hook-stop, and a Stop hook entry in ~/.claude/settings.json. Run do-until setup --remove to uninstall all three.
In Claude Code, type /until followed by your task:
/until "Make all tests pass"
/until --max 3 "Fix all TypeScript errors"
/until --check "npm run lint exits 0" "Fix lint warnings"
Type do-until in bash for CLI commands:
do-until status # show active sessions
do-until cancel --session <id>The slash command is /until (no shell involved). The CLI binary is do-until (avoids bash keyword conflict with until).
/untilwrites action + check to temp files, callsdo-until start- Agent works on the task, then naturally ends its turn
- Stop hook fires, transitions state to CHECK, and re-prompts the agent with the check instructions
- Agent verifies its work, calls
do-until report --session <id> --verdict approve|request_changes - If
request_changes: loop back to step 2 with the findings included in the next prompt - If
approve: the agent stops, done
The loop also ends without approve in three ways:
- max iterations reached: the agent receives a closure prompt to summarize what was done and what remains
- agent fails to report a verdict after 3 reminders: same closure prompt
- manual cancel:
do-until cancel --session <id>clears state and ends the loop silently
The same loop works whether a human types /until or an agent calls do-until start directly via Bash. Agents tend to declare work done before it's verified; wrapping ambiguous-success tasks in a do-until loop forces the agent to grade against a falsifiable check before exiting. Useful for multi-file refactors, migrations, and "fix all X" tasks where iteration 1 usually leaves residue.
mkdir -p "$HOME/.until/tmp"
STAGE=$(mktemp -d "$HOME/.until/tmp/XXXXXX")
echo "Refactor X to use Y" > "$STAGE/action.txt"
echo "tests pass and no occurrences of X remain" > "$STAGE/check.txt"
do-until start --action-file "$STAGE/action.txt" --check-file "$STAGE/check.txt" --max 5From there the Stop hook takes over: agent works, hook injects the check prompt, agent verifies, reports a verdict. Same mechanics as the slash command.
To make this the default discipline for an agent, add a line to CLAUDE.md: For multi-file refactors, migrations, and similar "fix all X" tasks, wrap the work in do-until start so verification is enforced, not optional.
do-until works best with falsifiable checks: lint exits 0, tests pass, typecheck clean, specific string absent from output. The same model writes the code and grades it, so its blind spots are not caught independently. Subjective checks ("is this well-designed?") expose that weakness; anchor on mechanical evidence instead.
do-until statuslists active sessions with state and iteration countdo-until cancel --session <id>clears a stuck session- Session state lives in
~/.until/sessions/<id>/state.json; the per-session event log is at~/.until/sessions/<id>/log.jsonl - Hook errors are appended to
~/.until/last-error.log - If a loop misbehaves, check
~/.claude/settings.jsonfor other synchronous Stop hooks that may conflict
All data stays local in ~/.until/. No telemetry, no cloud sync. do-until cancel --session <id> clears state and logs.