Driving herdr
herdr is a terminal workspace manager for AI coding agents. It runs a background
server and exposes everything over a CLI that talks to that server's socket. This skill
covers reading and changing the layout, and driving other agents.
The object model (read this first)
session (a named background server, own socket, own workspace tree)
└── workspace ← the per-project / per-worktree unit. HAS THE LABEL in the sidebar.
└── tab ← a tab within a workspace
└── pane ← a terminal within a tab; where a shell or agent runs
└── agent ← an AI agent (e.g. claude) attached to a pane
The trap: "rename this session", "what session am I in", "close that session" usually mean the workspace - the labeled sidebar item. Map "session" → workspace by default.
But sessions are real and plural. A session is a separate server with its own socket and
its own workspaces; users routinely run one per project, and default is often
stopped while the work lives in named sessions. Every command here talks to one
session - the one your pane is in. Workspace w1 in neochain and w1 in personal
are different workspaces. Run herdr session list before assuming.
| Object | id example | rename command |
|---|---|---|
| workspace | w1 |
herdr workspace rename <id> <label> |
| tab | w1:t1X |
herdr tab rename <id> <label> |
| pane | w1:p2M |
herdr pane rename <id> <label> |
| agent | pane id / name | herdr agent rename <target> <name> |
Tabs are <ws>:t<suffix>, panes are <ws>:p<suffix> - same colon, different letter. Ids
are opaque and version-dependent: read them from output, never construct or pattern-match
them.
All herdr <noun> ... subcommands print a JSON envelope ({"id":..,"result":..}) to
stdout with or without --json. Parse result.
Resolving "current" / "the X one"
Native, and enough for most cases - --current anchors on $HERDR_PANE_ID, exact even
from a subdirectory:
herdr pane current --current # current pane + tab + workspace ids, cwd, agent, status
The bundled script adds label→id lookup and a rename that defaults to the current object:
scripts/herdr_here.py whoami # same, human-readable
scripts/herdr_here.py list # all workspaces, current marked with *
scripts/herdr_here.py resolve [<label>|<id>] # → workspace id
scripts/herdr_here.py rename <new-name> [--target <label|id>] [--what tab|pane|agent]
So "rename this session to backups" is scripts/herdr_here.py rename backups.
Pick a short, kebab-ish label matching the user's existing naming (run list to see it),
not a verbose invented one.
Running a subagent
scripts/herdr-agent.ts (bun) does the whole exchange in one command. Use it instead of
hand-rolling agent start + wait + send + send-keys Enter + read: the manual
sequence has two traps that silently return the previous answer, and this handles both
(see references/agents.md for what they are).
scripts/herdr-agent.ts presets # which subagents are configured + installed
scripts/herdr-agent.ts running # what is alive now, and its status
scripts/herdr-agent.ts start codex --prompt "..." # spawn, wait for boot, ask once, print the reply
scripts/herdr-agent.ts ask <pane|name> "..." # next turn in the same conversation
scripts/herdr-agent.ts read <pane|name> [--raw] # what is on its screen
scripts/herdr-agent.ts stop <pane|name> --force # close the pane (kills the agent)
Add --json to presets, running, start and ask when you need to consume the
result rather than show it. start takes --name, --cwd, --workspace, --tab,
--split right|down, --focus, --env K=V, --session, --timeout.
Which agent runs is a config question, not a command line. Presets live under
agents in agent config (scripts/herdr-agent.ts config path global, ACS v1 - see
standards/agent-config.md), keyed by name, holding argv plus cwd, env, session, split,
timeouts and the TUI markers used to scrape replies. Eight agents are built in, so the
skill works with no config file; config.example.json shows how to override one or add a
role of your own (reviewer, scratch). When the user names an agent you do not have a
preset for, run presets before guessing a binary.
Reference - load what the task needs
Each file is the verb list plus the gotchas for that area. Read one when the task touches it; do not read all four.
| File | Covers |
|---|---|
references/workspaces.md |
workspace + worktree verbs; creating worktree-backed workspaces |
references/tabs-and-panes.md |
tab verbs; pane verbs incl. split, move, zoom, resize, read, input |
references/agents.md |
subagent presets and config; the one-shot runner; raw agent verbs; status semantics |
references/sessions.md |
the literal session/server: list, attach, stop, --session, --remote |
Anything not listed there: herdr <noun> --help prints the authoritative subcommand list.
Gotchas
- Use
scripts/herdr-agent.tsfor anything conversational. Rawherdr agent senddoes not press Enter, an Enter sent in the same breath as the text is swallowed by the TUI redraw, and a status wait straight after submitting returns instantly - each of which reads back the previous answer. Seereferences/agents.md. - "session" is usually the workspace - but check
herdr session listrather than assuming there is onlydefault. - Destructive verbs (
workspace close,worktree remove,tab close,pane close,session delete) remove things - confirm with the user first, and noteworktree removedeletes the git worktree on disk. Renames and focus are safe and reversible. - Don't invent ids. Resolve from
herdr pane current,herdr_here.py, or aherdr ... list. Labels can collide; the resolver errors and asks you to disambiguate. - Naming. New worktree-backed workspaces get auto-codenames like
worktree-brave-river-06c0; users rename them to a short task/repo label. Match style.