There are two supported ways to drive work off the queue: the built-in
afk loop worker-driver, and an external loop you write around afk take /
afk set. Both are valid. afk run is no longer part of the public binary.
afk loop claims ready tasks and runs an agent command per task, with no
external supervisor. Each iteration:
- Claims the first ready task (taking an exclusive lease).
- Renders the configured
prompt_templateagainst the task. - Runs the configured
commandwith that prompt, extending the lease on theheartbeatinterval while it runs. - Re-reads lifecycle state after the child exits, preserving an agent-issued
doneorsuperseded; otherwise finalizes from the command exit status. - Pauses for
cooldownwhen no task is found, then repeats.
The agent command and prompt template come from ~/.config/afk/loop.yaml
(written with defaults on first run — see configuration.md). command is empty
by default, so the loop is fail-closed:
afk loop
# Error: no agent command configured (set 'command' in
# ~/.config/afk/loop.yaml or pass --command)Configure command in loop.yaml, or pass it for a single run. Use
--max-tasks N for a bounded run that exits cleanly after N tasks:
afk loop --command 'claude -p {{.Prompt}}' --max-tasks 5Loop results are emitted as JSONL on stdout; agent output goes to stderr. Any
flag overrides the matching loop.yaml value for that run
(--worker, --lease, --timeout, --cooldown, --heartbeat,
--max-failures). The loop halts after --max-failures consecutive task
failures.
The generated task prompt requires the child to re-ground against live project
instructions, Git state/history, relevant files, original evidence, success
conditions, verification, and Reject-if before editing. A worker-owned active
task may be refreshed, revised, completed as a verified no-op, or superseded
from inside the child without the loop overwriting that terminal result.
Write your own loop when you want process supervision, custom retry policy, or logging the built-in driver does not provide.
Preview readiness without claiming:
afk take --dry-run --limit 5 --json --fullA minimal external loop body:
task_json=$(afk take --lease 30m --worker "$USER:$$" --summary)
test -n "$task_json" || exit 0
id=$(printf '%s\n' "$task_json" | jq -r .task.id)
body=$(printf '%s\n' "$task_json" | jq -r .task.body)
if agent-command "$body"; then
afk set "$id" done --note "agent-command completed" --worker "$USER:$$" --summary
else
afk set "$id" failed --note "agent-command failed" --worker "$USER:$$" --summary
fiThis keeps AFK focused on durable task state, readiness, claims, prompts, and visibility. The caller owns execution policy, retries, logs, and process lifetime.