Herdr
Herdr organizes terminals into workspaces, tabs, and panes, recognizes
coding agents running inside panes, and exposes the current session
through the herdr CLI.
Before issuing any control command, verify that this agent is running inside a Herdr-managed pane:
test "${HERDR_ENV:-}" = 1
If the check fails, say that you are not running inside Herdr and stop. Do not inspect or control the focused Herdr session from outside Herdr.
When the check passes, the herdr binary in PATH talks to this
pane's server. On the Mini that is Local. It is not Omarchy.
Learn the current CLI
The installed binary is the authority for command syntax. Start with:
herdr --help
Then print the relevant command group by running the group without a subcommand:
herdr agent
herdr pane
herdr workspace
herdr tab
herdr worktree
herdr terminal
herdr notification
herdr integration
herdr session
herdr machine
Do not run bare herdr for discovery; it launches or attaches the TUI.
Do not probe a mutating nested command by omitting arguments. Commands
such as herdr workspace create are valid with defaults and will
execute.
Most control commands return JSON. Read identifiers and state from those responses instead of predicting them.
Understand layout, panes, and agents
Choose the primitive that matches the job:
- Workspace, tab, and pane topology organize terminal locations.
- Pane commands control raw terminals, shells, tests, servers, input, and output.
- Agent commands control the recognized coding agent currently occupying a pane.
A pane exists whether or not it contains an agent. agent start
requires an existing available shell pane and never creates, splits, or
moves layout. Use pane commands for ordinary processes. Use agent
commands when Herdr must validate agent identity or interpret idle,
working, blocked, done, and unknown lifecycle states.
Agent commands accept either a unique live agent name or the pane ID
currently hosting that agent. They do not accept terminal IDs or bare
agent-kind labels. Names must match [a-z][a-z0-9_-]{0,31} and be
unique among live agents. A name follows the current pane occupant and
is cleared when that agent exits, is released, or is replaced.
idle and done both mean the agent is ready for input. The CLI/API
uses the server's seen state to distinguish them; explicit focus
commands mark the target seen, while reads do not. Each TUI client
tracks viewed completions independently, so its Done badge can differ
from the CLI or another client's badge. blocked means Herdr recognized
an approval or question UI. unknown means an agent is present but
Herdr cannot classify it confidently; it does not prove completion.
Use IDs and caller context
Public IDs are opaque stable handles:
- workspace:
w1 - tab:
w1:t1 - pane:
w1:p1
Closed tab and pane IDs are not reused. A pane moved into another
workspace receives a new workspace-qualified pane ID. After pane move,
continue with .result.move_result.pane.pane_id or the live agent name.
The old value is reported as .result.move_result.previous_pane_id;
only the moved process's inherited caller context keeps resolving that
old ID, so do not use it as a general agent target.
Herdr injects the caller's context into each managed pane:
printf '%s\n' "$HERDR_WORKSPACE_ID" "$HERDR_TAB_ID" "$HERDR_PANE_ID"
Prefer --current when a pane command should target the calling pane.
Omitting a target may use the UI-focused pane, which can belong to the
user or another client.
Discover live state with:
herdr workspace list
herdr tab list --workspace "$HERDR_WORKSPACE_ID"
herdr pane current --current
herdr pane list --workspace "$HERDR_WORKSPACE_ID"
herdr agent list
Creation responses expose the IDs to use next. workspace create
returns .result.workspace, .result.tab, and .result.root_pane.
tab create returns .result.tab and .result.root_pane. pane split
returns the new pane as .result.pane.
Saved machines
0.9.0 keeps Local and saved SSH machines in one window. Each machine has its own Herdr server, sessions, and IDs.
herdr machine list --json
That lists connection profiles, not a cross-machine pane inventory.
This setup's laptop profile is label Omarchy, target omarchy.ts.
Selecting a machine in the TUI does not retarget CLI commands in this
pane. Local herdr workspace create, herdr pane list, and
herdr agent prompt still hit the Mini server. Two machines may both
have w1:p1.
To create or inspect work on a remote machine, run herdr on that
host against its session and rediscover IDs there. Pin
~/.local/bin/herdr (0.9.0). Bare herdr over SSH hits packaged
/usr/bin/herdr 0.8.2, which cannot talk to the 0.9.0 server.
ssh -o BatchMode=yes omarchy.ts \
'"$HOME/.local/bin/herdr" workspace list'
ssh -o BatchMode=yes omarchy.ts \
'"$HOME/.local/bin/herdr" pane read w1:p1 --source recent --lines 40'
Saved-machine display needs a 0.9.0+ remote server (surface_interest
health_check). An older server shows Attention even if standaloneherdr --remoteattaches.
Only add, remove, enable, or disable profiles when the user asks.
machine add is interactive and may replace a remote server; default
answer is No. Do not approve replacement without consent. Removing a
profile disconnects the client and leaves remote sessions running.
NEVER fake a remote machine by creating a Local workspace whose pane
runs ssh -t omarchy.ts. That is a Mini pane displaying SSH, not an
Omarchy workspace. Offload coding work with delegate-to-omarchy.
The user views Omarchy by selecting it in the sidebar. You stay on Local unless they asked you to operate there.
Start and coordinate an agent
Default to a sibling pane in the current tab and the current working directory. Do not create a workspace, tab, worktree, or different cwd unless the user explicitly requests that topology or location.
Honor a direction requested by the user. Otherwise inspect the caller pane:
herdr pane layout --pane "$HERDR_PANE_ID"
Split a wide pane to the right and a narrow or tall pane down. Avoid repeated same-direction splits that create unusably narrow columns or short rows. Keep the user's focus in the calling pane and explicitly preserve the caller's working directory:
herdr pane split --current --direction right --cwd "$PWD" --no-focus
Replace right with down when appropriate. Read the new pane ID from
.result.pane.pane_id.
An available shell pane must be at its interactive prompt, with the shell itself in the foreground and no foreground command, editor, or agent running. Start a supported agent in that pane with a useful unique name:
herdr agent start reviewer --kind codex --pane <returned-pane-id>
Use the kind requested by the user. Run herdr agent to inspect the
installed kind list and options. Pass native agent arguments only after
--.
A successful agent start returns only after Herdr detects the expected
agent in the same pane and considers it ready for interactive input. If
the agent is blocked during startup, the command returns
agent_not_ready immediately but keeps the name available for
agent read and agent send-keys. Wait until the agent becomes idle
before prompting it. Startup defaults to a 30-second timeout.
Submit work through the agent surface:
herdr agent prompt reviewer "Review the current diff and report only actionable findings." --wait --timeout 120000
agent prompt honors the pane's live bracketed-paste mode and sends
text followed by encoded Enter as one ordered submission. It reports
successful submission only after both have been written; that alone
does not prove the agent started a turn. It rejects an agent already
waiting at an approval or question dialog with agent_blocked before
sending any input. Inspect the blocked UI and ask the user before
answering it. For normal agent work, --wait is enough.
With --wait, a prompt sent from a non-working state must produce
observed working or blocked activity. After submission, Herdr waits
up to five seconds for that activity; unrelated idle, done, or
session changes do not satisfy this gate. It returns
agent_prompt_stalled if no activity is observed, or timeout if the
caller's timeout expires first.
Use --until only for a state-specific workflow:
herdr agent wait reviewer --until blocked --timeout 120000
Use logical keys for interactive agent UI controls:
herdr agent send-keys reviewer esc
herdr agent send-keys reviewer ctrl+c
Read the result through the resolved agent:
herdr agent get reviewer
herdr agent read reviewer --source recent-unwrapped --lines 120
If a wait fails or returns blocked, inspect agent get and
agent read before deciding what input to send. A timeout or stalled
response does not prove the prompt was never delivered; do not blindly
submit it again.
Run an ordinary command in another pane
Create a sibling pane with the same geometry rule, preserve the caller's working directory, and keep user focus unchanged:
herdr pane split --current --direction right --cwd "$PWD" --no-focus
Read the new pane ID from .result.pane.pane_id, then run and inspect
the command:
herdr pane run <returned-pane-id> "just test"
herdr pane wait-output <returned-pane-id> --match "test result" --timeout 120000
herdr pane read <returned-pane-id> --source recent-unwrapped --lines 120
pane run atomically sends command text and Enter. pane wait-output
searches the selected snapshot immediately, so output that already
exists can match. Use --match <text> for a literal substring or
--regex <pattern> for a Rust regular expression.
Use the read source that matches the task:
visible: the currently rendered viewport.recent: recent rendered output, including soft wraps.recent-unwrapped: recent output with soft wraps joined; prefer it for logs and transcripts.detection: the plain-text bottom-buffer snapshot used for agent detection.
Use --format ansi when colors and terminal styling are evidence.
Otherwise use text.
If increasing --lines does not reveal more of a completed response,
the pane is probably on the terminal's alternate screen. Ask the agent
to write its complete response as Markdown in a temporary directory and
reply only with the file path, then read the file. Use this only as a
fallback.
Safety and coordination rules
- Use
--no-focusfor background work unless the user asked to switch context. - Use
--current, an explicit pane ID, or a unique agent name. Do not rely on another client's focused pane. - Parse IDs from JSON responses. Do not derive them from sidebar order or examples.
- Do not close workspaces, tabs, panes, or sessions you did not create
unless the user explicitly asked.
workspace close --groupcloses the primary workspace and its linked worktree workspaces; never add it merely to bypassworkspace_group_close_required. - Use
--trust-repositoryonly after the user has verified the repository. - Client and server versions can differ after an update. Check
herdr statusbefore relying on new server features. A missing method is not permission to stop or upgrade a server. - Never run
herdr server stopfrom an active session unless the user explicitly intends to stop the server and its pane processes. - Never kill the main Herdr process. Use named test sessions for experiments that need an isolated server.
- CLI server errors are JSON on stderr with exit status 1. CLI syntax errors exit with status 2.