Setting up herdr for agent jobs

Install herdr, organise workspaces, tabs and panes for agents, and run headless Claude jobs at low priority with logs you can read later.

Updated 2026-11-11Raw markdown

Strategy sections
On this page

Use this page to set up a machine where agents run long or recurring SRE jobs (triage sweeps, drift plans, report drafts) in the background, where a human can watch them and step in. herdr is a terminal workspace manager for AI coding agents: like tmux, a background server owns real terminals that survive detach and SSH drops, and it also detects agents in panes and shows whether each one is working, blocked, done or idle.

Run it on a machine your organization has approved for AI tooling and for the data the jobs will touch.

Install

curl -fsSL https://herdr.dev/install.sh | sh
herdr --version

Other install methods (Homebrew, mise, Nix, Windows) are on the install docs. Update a direct install with herdr update.

Install the Claude integration so sessions restore natively:

herdr integration install claude
herdr integration status

Learn the model in two minutes

Concept What it is Use it for
Session A persistent background server Usually just the default one
Workspace Project-level container One per agent role or job family, for example jobs: triage
Tab A layout inside a workspace One per job run
Pane A real terminal The job's process
Agent A recognised coding agent in a pane Lifecycle state in the sidebar

Run herdr to attach. Detach with ctrl+b then q; everything keeps running. herdr server stop stops everything. Config lives in ~/.config/herdr/config.toml; print defaults with herdr --default-config.

The CLI talks to the server over a local socket and returns JSON, so scripts read IDs from responses instead of guessing them. Discover commands with herdr pane, herdr agent, herdr workspace and herdr tab (each prints its subcommands). The full reference is the CLI docs.

Give every job its own tab and log

The pattern: one workspace per job family, one tab per run, the job runs through a small wrapper that lowers priority and tees output to a log file.

#!/usr/bin/env bash
# ~/bin/agent-job: run one low-priority job in a new herdr tab, logged to ~/agent-jobs/<id>/
set -euo pipefail
title="$1"; shift            # usage: agent-job "<title>" <command...>
id="$(date +%Y%m%d-%H%M%S)-$RANDOM"
dir="$HOME/agent-jobs/$id"; mkdir -p "$dir"
printf '%q ' "$@" > "$dir/cmd"

cat > "$dir/run.sh" <<RUN
#!/usr/bin/env bash
cd "$PWD"
echo "=== $title  \$(date -u +%FT%TZ) ==="
nice -n 19 ionice -c3 bash -c "\$(cat '$dir/cmd')" </dev/null 2>&1 | tee -a "$dir/output.log"
echo "\${PIPESTATUS[0]}" > "$dir/exit_code"
echo "=== finished exit=\$(cat "$dir/exit_code") ==="
RUN
chmod +x "$dir/run.sh"

ws="$(herdr workspace list | jq -r '.result.workspaces[] | select(.label=="agent jobs") | .workspace_id' | head -n1)"
if [ -z "$ws" ]; then
  ws="$(herdr workspace create --cwd "$HOME" --label "agent jobs" --no-focus | jq -r '.result.workspace.workspace_id')"
fi
pane="$(herdr tab create --workspace "$ws" --cwd "$PWD" --label "${title:0:60}" | jq -r '.result.root_pane.pane_id')"
herdr pane run "$pane" "bash $dir/run.sh"
echo "{\"id\":\"$id\",\"pane\":\"$pane\",\"dir\":\"$dir\"}"
  • nice -n 19 and ionice -c3 keep jobs from slowing the human's work.
  • </dev/null means a job can never sit waiting for keyboard input.
  • Output goes to a file, so results survive the pane being closed.
  • Cap concurrency (for example three running jobs) and add quiet hours if the machine is shared.

Run a headless Claude job

agent-job "nightly drift summary" \
  claude -p "$(cat ~/prompts/drift-summary.md)" \
    --output-format text \
    --permission-mode plan \
    --max-turns 5 \
    --disallowedTools "Bash(git push:*)" "Bash(terraform apply:*)" WebFetch
  • Prefer --permission-mode plan (read and propose only) or an explicit --allowedTools list. Never use the skip-permissions flag on a machine with production credentials.
  • Keep prompts in files under version control so you can see exactly what each job was told.
  • Secrets never go in the prompt. Redact inputs first; see Claude Code for SRE.

Watch and read jobs

herdr agent list                                    # agents and their states
herdr pane read <pane-id> --source recent --lines 50
herdr pane wait-output <pane-id> --match "=== finished" --timeout 600000
tail -n 50 ~/agent-jobs/<id>/output.log
cat ~/agent-jobs/<id>/exit_code

For an interactive agent instead of a one-shot job, start it in an existing shell pane and prompt it:

pane="$(herdr pane split --current --direction right --cwd "$PWD" --no-focus | jq -r '.result.pane.pane_id')"
herdr agent start reviewer --kind claude --pane "$pane"
herdr agent prompt reviewer "Review the diff on this branch and list only actionable findings." --wait --timeout 300000
herdr agent read reviewer

A blocked state means the agent is waiting on an approval prompt. A human answers it; scripts should not.

Schedule recurring jobs

# m h dom mon dow  command   (crontab -e)
30 6 * * 1-5  $HOME/bin/agent-job "daily triage" $HOME/sre-automation/bin/daily.sh >/dev/null 2>&1
0 15 * * 5    $HOME/bin/agent-job "friday status" $HOME/sre-automation/bin/friday-status.sh >/dev/null 2>&1

Clean up

herdr tab list --workspace <workspace-id>
herdr tab close <tab-id>
find ~/agent-jobs -maxdepth 1 -mtime +30 -type d -exec rm -rf {} +   # keep 30 days of logs

Pitfalls

  • Running jobs at normal priority on the machine you work on. Use nice.
  • Jobs that need a credential prompt. Load short-lived credentials before the job, or the pane hangs.
  • Agents inside herdr controlling other panes without being asked. Check HERDR_ENV=1 and only control herdr when the task says so.
  • Logging secrets. Anything a job prints lands in output.log.