---
title: Setting up herdr for agent jobs
slug: herdr-setup-for-agents
summary: Install herdr, organise workspaces, tabs and panes for agents, and run headless Claude jobs at low priority with logs you can read later.
order: 26
updated: 2026-11-11
status: ready
---

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](https://herdr.dev) 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

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

Other install methods (Homebrew, mise, Nix, Windows) are on the [install docs](https://herdr.dev/tui/docs/install/). Update a direct install with `herdr update`.

Install the Claude integration so sessions restore natively:

```bash
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](https://herdr.dev/tui/docs/cli-reference/).

## 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.

```bash
#!/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

```bash
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](./claude-code-for-sre.md).

## Watch and read jobs

```bash
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:

```bash
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

```cron
# 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

```bash
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`.
