diff options
| author | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-03-30 15:33:12 +0300 |
|---|---|---|
| committer | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-03-30 15:33:12 +0300 |
| commit | fff21d64905cc50424aec698c88965aefd2df724 (patch) | |
| tree | fa72dfdc937a522e94aad9de61dc34a1b25eb5e3 | |
| parent | 55684493890b4f67c73ea07b5af5911ec1b4d80e (diff) | |
docs: refresh readme and socket api positioning
| -rw-r--r-- | README.md | 166 | ||||
| -rw-r--r-- | SKILL.md | 96 | ||||
| -rw-r--r-- | SOCKET_API.md | 564 |
3 files changed, 584 insertions, 242 deletions
@@ -7,52 +7,95 @@ <p align="center">herd your agents.</p> <p align="center"> - <a href="https://herdr.dev">herdr.dev</a> · <a href="#install">install</a> · <a href="#usage">usage</a> · <a href="./CONFIGURATION.md">configuration</a> · <a href="./SOCKET_API.md">socket api</a> + <a href="https://herdr.dev">herdr.dev</a> · <a href="#install">install</a> · <a href="#usage">usage</a> · <a href="./CONFIGURATION.md">configuration</a> · <a href="./SKILL.md">agent skill</a> · <a href="./SOCKET_API.md">socket api</a> </p> --- -herdr is a terminal workspace manager for AI coding agents. it runs inside your existing terminal: ghostty, alacritty, kitty, wezterm, even inside tmux. a single rust binary that gives you workspaces, tiled panes, intelligent agent state detection, and optional notification alerts. +herdr is a terminal-native agent multiplexer for coding agents. -it stays terminal-native without forcing a keyboard-only workflow: use prefix keys when you want, click and drag when you don't. +it runs inside your existing terminal: ghostty, alacritty, kitty, wezterm, even inside tmux. a single rust binary that gives you workspaces, tiled panes, automatic agent detection, and notification alerts without asking you to leave the terminal for a separate gui window, electron wrapper, or web dashboard. -you keep your terminal. herdr keeps track of your agents. +it is also becoming a shared control surface. you manage agents in herdr, and increasingly those agents can interact with herdr too through its local socket api, cli commands, and the example agent skill in this repo. <p align="center"> <img src="assets/screenshot.png" alt="herdr screenshot" width="900" /> </p> -## philosophy +## what herdr is -every tool in this space is building more. desktop apps, electron wrappers, web dashboards, all trying to replace your terminal with their own environment. +most tools in this space try to replace your environment. -herdr takes the opposite approach. it's a tool that lives where you already work. it sits alongside tmux, inside your terminal emulator, part of your existing workflow. it does one thing well: lets you run multiple coding agents in parallel and know when they need you. +herdr takes the opposite approach. it lives where cli agents already live, keeps mouse and keyboard both first-class, and adds the missing layer: awareness and coordination for running multiple agents in parallel. -the terminal is already a great environment for coding agents. what's been missing is awareness: seeing at a glance which agent is idle, which is working, and which needs your input. herdr adds that layer. +that means two things: -herdr also keeps workspace creation lightweight. a workspace is a terminal context, not a preconfigured project artifact. create one and it opens immediately; herdr labels it from your current repo or folder. you can rename it later, but that's an override, not a prerequisite. +- **for you:** one terminal-native place to supervise multiple agents, jump between contexts, and notice when something needs attention. +- **for your agents:** an automation surface they can increasingly use themselves to create panes, spawn other agents, run helpers, read output, and wait on state. -it also treats mouse support as a first-class interaction model, not an afterthought. click the sidebar to switch workspaces, drag pane borders to resize, scroll panes, and use the keyboard whenever it's faster. +## workspace model -## how it works +herdr is opinionated about workspaces. -herdr embeds real terminal emulators using PTY and vt100 parsing. each pane is a full terminal: your shell, your agent, your tools, exactly as they'd run anywhere else. +it does **not** start by asking you to create an empty named project or pick a folder in a setup flow. you create a workspace, and it opens immediately as a new terminal context. -the sidebar shows your workspaces. each workspace can have multiple tiled panes. the agent detection system reads terminal output in real time and determines what state each agent is in: +from there, the workspace identity comes from its **root pane**: -- **●** red: agent is waiting for your input -- **●** yellow: agent is working -- **●** blue: agent finished (you haven't looked yet) -- **○** green: agent is idle, you've seen it +- the first pane in a workspace is the root pane +- it starts as the top-left anchor and owns the workspace identity +- the workspace label defaults to the root pane's git repo name +- if there is no git repo, it falls back to the root pane's current folder name +- manual rename is supported, but it is an override, not the primary model -when an agent finishes work in a background workspace, its dot turns blue. you see it at a glance and switch over. if you want more interruption, herdr can also play sounds or show top-right toast alerts for background events. +this keeps workspaces lightweight. they are project contexts first, labels second. + +## awareness and notifications + +the sidebar is split into two layers: + +- **top:** workspaces, each with one aggregate state dot +- **bottom:** agents inside the selected or active workspace + +herdr automatically detects running agents by looking at the foreground process and reading terminal output. the top section compresses each workspace into one prioritized signal so you can scan the whole workspace list quickly; the bottom section shows which specific agent is causing it. + +workspace and agent states map to: + +- **● red** — blocked: agent needs input or approval +- **● blue** — done: work finished and you have not looked at it yet +- **● yellow** — working +- **○ green** — idle: done, seen, and calm +- **· dim** — unknown / plain shell + +workspace rollups prefer the most urgent thing happening in that workspace: blocked first, then unseen finished work, then working, then idle. + +if you want more interruption than ambient sidebar awareness, herdr can also play sounds or show top-right toast notifications for background events. + +## agents can use herdr too + +herdr is not just a passive manager for humans watching agents. it gives agents two clean integration paths: + +- [`SKILL.md`](./SKILL.md) — the reusable agent skill. use this if you want an agent inside herdr to learn the workflow quickly through the existing cli surface. +- [`SOCKET_API.md`](./SOCKET_API.md) — the direct integration doc. use this if you want the low-level socket protocol, event subscriptions, or the cli wrapper reference that sits on top of it. + +those two paths meet at the same control surface. the built-in `herdr workspace ...`, `herdr pane ...`, and `herdr wait ...` commands all talk to the same local socket api. + +that means agents running inside herdr can do useful orchestration work themselves: + +- create new workspaces for parallel tasks +- split panes for servers, logs, tests, or scratch work +- spawn other agents in sibling panes +- read pane output or wait for output matches +- send text and keys into other panes +- wait for another agent to finish before continuing + +in that sense, herdr is for you **and** for your agents. ## supported agents herdr detects agent state by identifying the foreground process and reading terminal output patterns. the following agents have been tested: -| agent | idle | busy | needs attention | -|-------|------|------|-----------------| +| agent | idle / done | working | blocked | +|-------|-------------|---------|---------| | [pi](https://pi.dev) | ✓ | ✓ | partial | | [claude code](https://docs.anthropic.com/en/docs/claude-code) | ✓ | ✓ | ✓ | | [codex](https://github.com/openai/codex) | ✓ | ✓ | ✓ | @@ -60,7 +103,7 @@ herdr detects agent state by identifying the foreground process and reading term | [amp](https://ampcode.com) | ✓ | ✓ | partial | | [opencode](https://github.com/anomalyco/opencode) | ✓ | ✓ | ✓ | -detection heuristics also exist for these agents but haven't been fully tested yet. if you use them and run into issues, please [open an issue](https://github.com/ogulcancelik/herdr/issues): +detection heuristics also exist for these agents but have not been fully tested yet. if you use them and run into issues, please [open an issue](https://github.com/ogulcancelik/herdr/issues): - [gemini cli](https://github.com/google-gemini/gemini-cli) - [cursor agent](https://cursor.com/cli) @@ -68,7 +111,7 @@ detection heuristics also exist for these agents but haven't been fully tested y - [kimi](https://kimi.ai) - [github copilot cli](https://cli.github.com) -for any other CLI agent, herdr still works as a workspace manager. you still get workspaces, panes, and tiling. a hook system for custom agent state reporting is coming soon. +for any other cli agent, herdr still works as a terminal-native multiplexer. you still get workspaces, panes, tiling, and notifications. richer direct agent-side reporting on top of the socket/event layer is still evolving. ## install @@ -82,7 +125,7 @@ requirements: linux or macos. ### update -herdr checks for updates automatically in the background. when a new version is ready, you'll see a notification in the UI. just restart to apply. you can also update manually: +herdr checks for updates automatically in the background. when a new version is ready, you'll see a notification in the ui. just restart to apply. you can also update manually: ```bash herdr update @@ -98,7 +141,7 @@ herdr on first run, herdr opens a short onboarding flow so you can choose your notification style. after that, if a session is restored you'll land in terminal mode; otherwise you'll start in **navigate mode**. -press `n` to create your first workspace. it opens immediately as a new terminal context, using an automatic label based on your current repo or folder. +press `n` to create your first workspace. it opens immediately as a new terminal context in your current project context, and herdr labels it automatically from the root pane's repo or folder. press `ctrl+b` (the prefix key) to switch back to navigate mode. from there you can manage workspaces and panes. @@ -118,6 +161,15 @@ common defaults: full keybinding and config reference: [`CONFIGURATION.md`](./CONFIGURATION.md) +### sidebar + +the sidebar is your triage surface. + +- the top section tells you which workspace needs attention +- the bottom section tells you which agent inside that workspace is causing it + +this is the core loop of herdr: scan the workspace list, drop into the right context, then act. + ### resize mode | key | action | @@ -165,7 +217,7 @@ name = "tokyo-night" you can also override individual color tokens on top of any base theme. see [`CONFIGURATION.md`](./CONFIGURATION.md) for the full token reference. -for all keybindings, onboarding, notification, sound, UI options, and environment variables, see [`CONFIGURATION.md`](./CONFIGURATION.md). +for all keybindings, onboarding, notification, sound, ui options, and environment variables, see [`CONFIGURATION.md`](./CONFIGURATION.md). ## session persistence @@ -175,81 +227,49 @@ use `--no-session` to start fresh. ## how agent detection works -herdr doesn't require hooks or agent-side configuration. detection works by: +herdr does not require hooks or agent-side configuration for its built-in detection. it works by: -1. identifying the foreground process of each pane's PTY (via `/proc` on linux, `proc_pidinfo` on macos) +1. identifying the foreground process of each pane's pty (via `/proc` on linux, `proc_pidinfo` on macos) 2. matching the process name against known agents 3. reading terminal screen content and applying per-agent heuristics to determine state this means detection works with any supported agent, installed any way, with zero setup. if it runs in a terminal, herdr can see it. -the heuristics are pattern-matched against each agent's actual terminal output: prompt boxes, spinners, "waiting for input" messages, tool execution indicators. detection runs on a separate async task per pane, polled every 300-500ms, decoupled from terminal rendering. +the heuristics are pattern-matched against each agent's actual terminal output: prompt boxes, spinners, waiting-for-input messages, tool execution indicators. detection runs on a separate async task per pane, polled every 300-500ms, decoupled from terminal rendering. -## socket api +## api and automation -herdr now has a local unix socket API for scripts, tools, and coding agents. +for direct integration details, use the docs instead of reverse-engineering the README: -you can: -- create, focus, rename, and close workspaces -- list, inspect, read, split, and close panes -- send text / keys into panes -- wait for output matches -- subscribe to lifecycle, agent, and output-match events over a single long-lived connection +- [`SKILL.md`](./SKILL.md) — reusable agent skill for agents already running inside herdr +- [`SOCKET_API.md`](./SOCKET_API.md) — canonical socket protocol + cli wrapper reference -see [`SOCKET_API.md`](./SOCKET_API.md) for request shapes, examples, and subscription behavior. +`SOCKET_API.md` now covers transport, request/response envelopes, workspace and pane methods, subscription behavior, and the `herdr workspace`, `herdr pane`, and `herdr wait` commands that wrap the same socket surface. ## what's coming -- **notification hooks**: richer agent/script-side state reporting on top of the socket foundation, so unsupported tools can report status directly to herdr. +- **richer agent-side hooks**: better ways for unsupported tools and custom workflows to report state directly into herdr. +- **deeper orchestration helpers**: more event-driven and shell-friendly wrappers on top of the socket foundation. - **in-app preferences**: rerun onboarding and adjust things like sound and toast notifications without editing config by hand. -- **native notifications**: OS-level notifications when an agent needs attention and herdr isn't in focus. -- **expanded cli wrapper**: more shell-friendly commands on top of the socket API, especially broader workspace/pane management and streaming event wrappers. +- **native notifications**: os-level notifications when an agent needs attention and herdr is not in focus. ## built with agents -i had never written rust before starting this project. herdr was built almost entirely through AI coding agents, the same ones it's designed to manage. i supervised the architecture and specs, agents wrote the code. +i had never written rust before starting this project. herdr was built almost entirely through ai coding agents, the same ones it is designed to multiplex. i supervised the architecture and specs; agents wrote the code. -this is a proof of concept in more ways than one. it's a functional tool, but it's also a statement about what's possible right now. if you can build a terminal multiplexer in a language you don't know, by directing the same agents the tool is built for, that says something about where we are. +this is a proof of concept in more ways than one. it is a functional tool, but it is also a statement about what is possible right now. if you can build a terminal-native agent multiplexer in a language you do not know, by directing the same agents the tool is built for, that says something about where we are. there will be rough edges. if you hit one, [open an issue](https://github.com/ogulcancelik/herdr/issues). that's why it's open source. -## cli - -built-in commands: - -```text -herdr launch herdr -herdr update download and install the latest version -herdr workspace list list workspaces -herdr workspace create ... create a workspace -herdr workspace get <workspace> inspect one workspace -herdr workspace focus <workspace> focus a workspace -herdr workspace rename <workspace> ... rename a workspace -herdr workspace close <workspace> close a workspace -herdr pane list ... list panes -herdr pane get <pane> inspect one pane -herdr pane read <pane> ... read pane output -herdr pane split <pane> ... split a pane -herdr pane close <pane> close a pane -herdr pane send-text <pane> <text> send text without submitting -herdr pane send-keys <pane> <keys...> send keypresses like Enter -herdr pane run <pane> <command> send text and press Enter -herdr wait output <pane> ... block until output matches -herdr wait agent-state <pane> ... block until pane reaches a state -herdr --version print version -herdr --default-config print default configuration -herdr --no-session start without restoring or saving sessions -herdr --help show help -``` +## cli wrappers + +herdr's workspace, pane, and wait commands are documented in [`SOCKET_API.md`](./SOCKET_API.md) together with the socket methods they wrap. workspace ids are compact public ids like `1`, `2`, `3`. pane ids are compact public ids like `1-1`, `1-2`, `2-1`. they are positional within the current live session, so numbering compacts when workspaces or panes are closed. -these commands are thin wrappers over the socket API: -- [`SOCKET_API.md`](./SOCKET_API.md) - ## building from source ```bash @@ -1,84 +1,78 @@ # herdr — agent skill -you are running inside herdr, a terminal workspace manager. herdr gives you workspaces (tabs) and panes (splits) — each pane is a full terminal with its own shell or agent. you can control all of it from the CLI. +you are running inside herdr, a terminal-native agent multiplexer. herdr gives you workspaces and panes — each pane is a real terminal with its own shell, agent, server, or log stream — and you can control all of it from the cli. this means you can: -- see what other agents/panes are doing + +- see what other panes and agents are doing - split panes and run commands in them -- start servers, watch logs, run tests — in separate panes +- start servers, watch logs, and run tests in sibling panes - wait for specific output before continuing - wait for another agent to finish -- spawn new agent instances +- spawn more agent instances + +the `herdr` binary is available in your PATH. its workspace, pane, and wait commands talk to the running herdr instance over a local unix socket. -the `herdr` binary is available in your PATH. all commands talk to the running herdr instance over a local unix socket. +if you need the raw protocol or full api reference, read [`SOCKET_API.md`](./SOCKET_API.md). ## concepts -**workspaces** are like tabs. each workspace has one or more panes. you switch between workspaces in the sidebar. +**workspaces** are project contexts. each workspace has one or more panes. **panes** are terminal splits inside a workspace. each pane runs its own process — a shell, an agent, a server, anything. **agent state** is detected automatically by herdr. each pane can be: -- `idle` — agent finished, user has seen it -- `busy` — agent is working -- `waiting` — agent needs user input + +- `idle` — agent finished and the state is calm +- `working` — agent is actively working +- `blocked` — agent needs input or approval - `unknown` — no recognized agent, or just a shell -**ids** — workspace ids look like `1`, `2`. pane ids look like `1-1`, `1-2`, `2-1`. these are the public ids you should use. +**ids** — workspace ids look like `1`, `2`. pane ids look like `1-1`, `1-2`, `2-1`. these are compact public ids for the current live session. + +important: ids can compact when panes or workspaces are closed. do not treat them as durable ids. ## discover yourself -find out which pane you're in and what else is running: +see what panes exist and which one is focused: ```bash herdr pane list ``` -```json -{"id":"cli:pane:list","result":{"panes":[ - {"agent":"claude","agent_state":"waiting","cwd":"/home/user/project","focused":true,"pane_id":"1-2","workspace_id":"1"}, - {"agent":"pi","agent_state":"idle","cwd":"/home/user/project","focused":false,"pane_id":"1-1","workspace_id":"1"} -],"type":"pane_list"}} -``` - the focused pane is yours. other panes are your neighbors. +list workspaces: + ```bash herdr workspace list ``` -```json -{"id":"cli:workspace:list","result":{"type":"workspace_list","workspaces":[ - {"workspace_id":"1","number":1,"label":"project","focused":true,"pane_count":2,"agent_state":"waiting"} -]}} -``` - ## read another pane -see what's on another pane's screen: +see what is on another pane's screen: ```bash herdr pane read 1-1 --source recent --lines 50 ``` -`--source visible` shows the current viewport. `--source recent` shows recent scrollback (default). +- `--source visible` = current viewport +- `--source recent` = recent scrollback ## split a pane and run a command -split your pane to the right and run a dev server: +split your pane to the right and keep focus on your current pane: ```bash herdr pane split 1-2 --direction right --no-focus ``` -this returns the new pane's id. then run a command in it: +that prints json with the new pane id. read the returned `pane_id`, then run a command in that pane: ```bash herdr pane run 1-3 "npm run dev" ``` -`pane run` sends the text and presses Enter. use `--no-focus` on split to keep focus on your pane. - split downward instead: ```bash @@ -87,7 +81,7 @@ herdr pane split 1-2 --direction down --no-focus ## wait for output -block until specific text appears in a pane. useful for waiting on servers, builds, tests: +block until specific text appears in a pane. useful for waiting on servers, builds, and tests: ```bash herdr wait output 1-3 --match "ready on port 3000" --timeout 30000 @@ -99,9 +93,9 @@ with regex: herdr wait output 1-3 --match "server.*ready" --regex --timeout 30000 ``` -timeout is in milliseconds. if it times out, exit code is 1. +if it times out, exit code is `1`. -## wait for an agent to finish +## wait for an agent state block until another agent reaches a specific state: @@ -119,13 +113,13 @@ send text without pressing Enter: herdr pane send-text 1-1 "hello from claude" ``` -press Enter (or other keys): +press Enter or other keys: ```bash herdr pane send-keys 1-1 Enter ``` -`pane run` combines both — sends text then Enter: +`pane run` combines both — sends text, then presses Enter: ```bash herdr pane run 1-1 "echo hello" @@ -139,6 +133,8 @@ create a new workspace: herdr workspace create --cwd /path/to/project ``` +create one without focusing it: + ```bash herdr workspace create --no-focus ``` @@ -169,30 +165,30 @@ herdr pane close 1-3 ## recipes -### run a server and watch for ready +### run a server and wait until it is ready ```bash # split a pane for the server herdr pane split 1-2 --direction right --no-focus -# assume new pane is 1-3 from the response +# read the returned JSON to get the new pane_id, for example 1-3 # start the server herdr pane run 1-3 "npm run dev" -# wait until it's ready +# wait until it is ready herdr wait output 1-3 --match "ready" --timeout 30000 -# now read the output to confirm -herdr pane read p_1_7 --source recent --lines 20 +# read the output to confirm +herdr pane read 1-3 --source recent --lines 20 ``` -### run tests in a separate pane and check results +### run tests in a separate pane and inspect the result ```bash herdr pane split 1-2 --direction down --no-focus herdr pane run 1-3 "cargo test" herdr wait output 1-3 --match "test result" --timeout 60000 -herdr pane read p_1_7 --source recent --lines 30 +herdr pane read 1-3 --source recent --lines 30 ``` ### check what another agent is working on @@ -207,26 +203,22 @@ herdr pane read 1-1 --source recent --lines 80 ```bash herdr pane split 1-2 --direction right --no-focus herdr pane run 1-3 "claude" -# wait for it to start herdr wait output 1-3 --match ">" --timeout 15000 -# send it a task herdr pane run 1-3 "review the test coverage in src/api/" ``` ### coordinate with another agent ```bash -# wait for the other agent to finish its current work herdr wait agent-state 1-1 --state idle --timeout 120000 - -# read what it produced herdr pane read 1-1 --source recent --lines 100 ``` ## notes -- all commands return JSON. parse the response to get pane ids from splits, workspace ids from creates, etc. -- `pane run` = `pane send-text` + `pane send-keys Enter`. use the separate commands when you need finer control. -- pane ids from split responses are the source of truth — don't guess pane ids, read them from the response. -- `--no-focus` on split keeps your terminal focused. without it, herdr focuses the new pane. -- if you're running inside herdr, the `HERDR_ENV` environment variable is set to `1`. ids are compact and can change when panes/workspaces are closed. +- `workspace list`, `pane list`, `workspace create`, `pane split`, `wait output`, and `wait agent-state` print json on success. +- `pane read` prints text, not json. +- `pane send-text`, `pane send-keys`, and `pane run` print nothing on success. +- parse ids from `workspace create` and `pane split` responses when you need the new ids. do not guess. +- `--no-focus` on split and workspace create keeps your current terminal context focused. +- if you are running inside herdr, the `HERDR_ENV` environment variable is set to `1`. diff --git a/SOCKET_API.md b/SOCKET_API.md index bccfd3c6..333da511 100644 --- a/SOCKET_API.md +++ b/SOCKET_API.md @@ -1,18 +1,29 @@ # herdr socket api -herdr exposes a local unix socket API for scripts, tools, and coding agents that want to control a running herdr instance or subscribe to pane/workspace events. +herdr exposes a local unix socket api for scripts, tools, and coding agents that want to control a running herdr instance or subscribe to events. -this is the low-level integration surface. -a CLI wrapper on top of it is planned, but the socket API is the foundation. +if you are teaching an agent that is already running inside herdr, start with [`SKILL.md`](./SKILL.md). use this document when you want the direct protocol, or when you want the cli wrapper reference for the commands that sit on top of it. + +## choose your integration layer + +there are three practical ways to integrate with herdr: + +- **agent skill** — [`SKILL.md`](./SKILL.md). best when an agent inside herdr just needs to learn the workflow quickly. +- **cli wrappers** — `herdr workspace ...`, `herdr pane ...`, `herdr wait ...`. best for shell scripts and simple orchestration. +- **raw socket api** — best when you want direct request/response control or long-lived event subscriptions. + +these layers are intentionally stacked on top of the same control surface. + +important difference: `pane.run` and `wait agent-state` are **cli conveniences**, not raw socket methods. ## transport - transport: unix domain socket -- encoding: newline-delimited JSON -- request/response: one JSON request per line, one JSON response per line -- subscriptions: send `events.subscribe`, receive an ack, then keep the same connection open for pushed events +- encoding: newline-delimited json +- request/response: send one json request per line, read one json response per line +- subscriptions: send `events.subscribe`, receive an ack, then keep the same connection open and continue reading pushed events -socket path resolution: +socket path resolution order: 1. `HERDR_SOCKET_PATH` 2. `$XDG_RUNTIME_DIR/herdr.sock` @@ -20,9 +31,9 @@ socket path resolution: 4. `$HOME/.config/herdr/herdr.sock` 5. `/tmp/herdr.sock` -## request shape +## request and response envelopes -all requests use this envelope: +all socket requests use this envelope: ```json { @@ -32,7 +43,7 @@ all requests use this envelope: } ``` -success responses: +successful responses look like: ```json { @@ -44,7 +55,7 @@ success responses: } ``` -error responses: +errors look like: ```json { @@ -56,7 +67,7 @@ error responses: } ``` -## ids +## ids and numbering workspace ids look like: @@ -70,52 +81,128 @@ pane ids look like: - `2-1` that means: + - first number = current workspace number - second number = current pane number within that workspace -these are compact public ids for the current live session. if a workspace or pane is closed, numbering compacts. +these are compact public ids for the current live session. they are **not durable database ids**. if a workspace or pane closes, higher numbers compact down. -## core request methods +## core objects -currently useful methods include: +`workspace_info` responses contain objects like: -### basic -- `ping` +```json +{ + "workspace_id": "1", + "number": 1, + "label": "herdr", + "focused": true, + "pane_count": 1, + "agent_state": "unknown" +} +``` -### workspace -- `workspace.list` -- `workspace.get` -- `workspace.create` -- `workspace.focus` -- `workspace.rename` -- `workspace.close` +`pane_info` responses contain objects like: -### pane -- `pane.list` -- `pane.get` -- `pane.read` -- `pane.send_text` -- `pane.send_keys` -- `pane.split` -- `pane.close` +```json +{ + "pane_id": "1-1", + "workspace_id": "1", + "focused": true, + "cwd": "/home/can/Projects/herdr", + "agent": "pi", + "agent_state": "working", + "revision": 0 +} +``` -### waits / events -- `pane.wait_for_output` -- `events.subscribe` +`pane_read` responses contain objects like: -## example: create a workspace +```json +{ + "pane_id": "1-1", + "workspace_id": "1", + "source": "recent", + "text": "...", + "revision": 0, + "truncated": false +} +``` + +agent states are: + +- `idle` +- `working` +- `blocked` +- `unknown` + +## methods at a glance + +| method | purpose | success result type | +|---|---|---| +| `ping` | health check / version | `pong` | +| `workspace.list` | list workspaces | `workspace_list` | +| `workspace.get` | inspect one workspace | `workspace_info` | +| `workspace.create` | create a workspace | `workspace_info` | +| `workspace.focus` | focus a workspace | `workspace_info` | +| `workspace.rename` | rename a workspace | `workspace_info` | +| `workspace.close` | close a workspace | `ok` | +| `pane.list` | list panes, optionally filtered by workspace | `pane_list` | +| `pane.get` | inspect one pane | `pane_info` | +| `pane.read` | read pane output | `pane_read` | +| `pane.split` | split a pane and create a sibling pane | `pane_info` | +| `pane.send_text` | send literal text without Enter | `ok` | +| `pane.send_keys` | send keypresses like `Enter` | `ok` | +| `pane.close` | close a pane | `ok` | +| `pane.wait_for_output` | one-shot blocking wait for text | `output_matched` | +| `events.subscribe` | start a long-lived subscription stream | `subscription_started` ack | + +## workspace methods + +### `workspace.list` + +request: ```json { - "id": "req_create", - "method": "workspace.create", - "params": { - "cwd": "/home/can/Projects/herdr", - "focus": true - } + "id": "req_list", + "method": "workspace.list", + "params": {} +} +``` + +returns `workspace_list` with zero or more workspace objects. + +### `workspace.get` + +params: + +```json +{ + "workspace_id": "1" +} +``` + +returns `workspace_info` for one workspace. + +### `workspace.create` + +params: + +```json +{ + "cwd": "/home/can/Projects/herdr", + "focus": true } ``` +notes: + +- `cwd` is optional +- if `cwd` is omitted, herdr uses its current working directory and falls back to `/` if needed +- `focus` is optional in raw socket requests and defaults to `false` +- the cli wrapper is more ergonomic here: `herdr workspace create` focuses by default unless you pass `--no-focus` + example response: ```json @@ -135,78 +222,252 @@ example response: } ``` -## example: read pane output +### `workspace.focus` + +params: ```json { - "id": "req_read", - "method": "pane.read", - "params": { - "pane_id": "1-1", - "source": "recent", - "lines": 80 - } + "workspace_id": "1" } ``` -`source` can be: -- `visible` -- `recent` +returns the focused workspace as `workspace_info`. -## example: send text and press enter +### `workspace.rename` -low-level input is intentionally explicit: +params: ```json { - "id": "req_send_text", - "method": "pane.send_text", - "params": { - "pane_id": "1-1", - "text": "bun run dev" + "workspace_id": "1", + "label": "api" +} +``` + +returns updated `workspace_info`. + +### `workspace.close` + +params: + +```json +{ + "workspace_id": "1" +} +``` + +returns: + +```json +{ + "id": "req_close", + "result": { + "type": "ok" } } ``` -then: +## pane methods + +### `pane.list` + +request with no filter: ```json { - "id": "req_send_keys", - "method": "pane.send_keys", + "id": "req_panes", + "method": "pane.list", + "params": {} +} +``` + +request filtered to one workspace: + +```json +{ + "id": "req_panes_ws", + "method": "pane.list", "params": { - "pane_id": "1-1", - "keys": ["Enter"] + "workspace_id": "1" } } ``` -this is kept separate on purpose. sending text is not always the same thing as submitting it. +returns `pane_list`. -a future CLI wrapper will likely offer a more ergonomic `pane run` style command on top of this. +### `pane.get` -## example: one-shot wait for output +params: ```json { - "id": "req_wait", - "method": "pane.wait_for_output", - "params": { - "pane_id": "1-1", - "source": "recent", - "lines": 200, - "match": { "type": "substring", "value": "ready" }, - "timeout_ms": 30000 + "pane_id": "1-1" +} +``` + +returns `pane_info`. + +### `pane.read` + +params: + +```json +{ + "pane_id": "1-1", + "source": "recent", + "lines": 80, + "strip_ansi": true +} +``` + +notes: + +- `source` is required and must be `visible` or `recent` +- `lines` is optional +- current implementation defaults to `80` lines when `lines` is omitted and caps reads at `1000` +- `strip_ansi` defaults to `true` + +`source` meanings: + +- `visible` — current viewport +- `recent` — recent scrollback text + +example response: + +```json +{ + "id": "req_read", + "result": { + "type": "pane_read", + "read": { + "pane_id": "1-1", + "workspace_id": "1", + "source": "recent", + "text": "...", + "revision": 0, + "truncated": false + } } } ``` -regex matching is also supported: +### `pane.split` + +params: ```json { - "type": "regex", - "value": "server.*ready" + "target_pane_id": "1-1", + "direction": "right", + "focus": true +} +``` + +notes: + +- `direction` must be `right` or `down` +- `cwd` is optional +- `focus` is optional in raw socket requests and defaults to `false` +- the cli wrapper is more ergonomic here too: `herdr pane split ...` focuses by default unless you pass `--no-focus` + +returns `pane_info` for the new pane. + +### `pane.send_text` + +params: + +```json +{ + "pane_id": "1-1", + "text": "bun run dev" +} +``` + +this sends literal text only. it does **not** press Enter. + +### `pane.send_keys` + +params: + +```json +{ + "pane_id": "1-1", + "keys": ["Enter"] +} +``` + +use this after `pane.send_text` when you want to submit a command. + +### `pane.close` + +params: + +```json +{ + "pane_id": "1-2" +} +``` + +returns `ok`. + +## waits + +### `pane.wait_for_output` + +this is the direct socket-side one-shot blocking wait. + +params: + +```json +{ + "pane_id": "1-1", + "source": "recent", + "lines": 200, + "match": { "type": "substring", "value": "ready" }, + "timeout_ms": 30000, + "strip_ansi": true +} +``` + +matcher forms: + +```json +{ "type": "substring", "value": "ready" } +``` + +```json +{ "type": "regex", "value": "server.*ready" } +``` + +notes: + +- `source` must be `visible` or `recent` +- `lines` is optional +- `timeout_ms` is optional +- `strip_ansi` defaults to `true` +- on success you get `output_matched` +- on timeout you get an error response with code `timeout` + +example success response: + +```json +{ + "id": "req_wait", + "result": { + "type": "output_matched", + "pane_id": "1-1", + "revision": 0, + "matched_line": "server ready", + "read": { + "pane_id": "1-1", + "workspace_id": "1", + "source": "recent", + "text": "...server ready...", + "revision": 0, + "truncated": false + } + } } ``` @@ -214,7 +475,7 @@ regex matching is also supported: `events.subscribe` is the long-lived pubsub entrypoint. -you send a subscribe request once, get an ack on the same connection, and then keep reading newline-delimited JSON events from that same socket. +you send a subscribe request once, get an ack on the same connection, and then keep reading newline-delimited json events from that same socket. ### subscription ack @@ -227,9 +488,10 @@ you send a subscribe request once, get an ack on the same connection, and then k } ``` -## supported subscriptions +### supported subscriptions + +base lifecycle subscriptions: -### lifecycle / base events - `workspace.created` - `workspace.closed` - `workspace.focused` @@ -238,12 +500,28 @@ you send a subscribe request once, get an ack on the same connection, and then k - `pane.focused` - `pane.exited` - `pane.agent_detected` -- `pane.agent_state_changed` -### parameterized event +parameterized subscriptions: + - `pane.output_matched` +- `pane.agent_state_changed` -## example: subscribe to lifecycle events +### event naming rule + +this part matters because the pushed event names are **not all shaped the same**. + +- when you subscribe to a **base lifecycle event**, the pushed `event` value uses snake_case with underscores: + - subscribe with `workspace.created` + - receive `workspace_created` +- when you subscribe to a **parameterized subscription**, the pushed `event` value keeps the dotted name: + - subscribe with `pane.output_matched` + - receive `pane.output_matched` + +examples below show both forms. + +### example: subscribe to lifecycle events + +request: ```json { @@ -281,7 +559,9 @@ example pushed event: } ``` -## example: subscribe to output matches and agent state changes +### example: subscribe to output matches and agent state changes + +request: ```json { @@ -306,6 +586,11 @@ example pushed event: } ``` +notes: + +- `pane.output_matched` supports `source`, optional `lines`, matcher config, and optional `strip_ansi` +- `pane.agent_state_changed` accepts an optional `state` filter; if omitted, any state transition for that pane can match + example pushed `pane.output_matched` event: ```json @@ -340,37 +625,82 @@ example pushed `pane.agent_state_changed` event: } ``` -## behavior notes +## cli wrappers + +these commands talk to the same local socket surface and are usually the easiest starting point for shell scripts and coding agents. + +### command groups + +workspace commands: + +```text +herdr workspace list +herdr workspace create [--cwd PATH] [--no-focus] +herdr workspace get <workspace_id> +herdr workspace focus <workspace_id> +herdr workspace rename <workspace_id> <label> +herdr workspace close <workspace_id> +``` + +pane commands: + +```text +herdr pane list [--workspace <workspace_id>] +herdr pane get <pane_id> +herdr pane read <pane_id> [--source visible|recent] [--lines N] [--raw] +herdr pane split <pane_id> --direction right|down [--cwd PATH] [--no-focus] +herdr pane close <pane_id> +herdr pane send-text <pane_id> <text> +herdr pane send-keys <pane_id> <key> [key ...] +herdr pane run <pane_id> <command> +``` + +wait commands: + +```text +herdr wait output <pane_id> --match <text> [--source visible|recent] [--lines N] [--timeout MS] [--regex] [--raw] +herdr wait agent-state <pane_id> --state <idle|working|blocked|unknown> [--timeout MS] +``` + +### cli behavior notes -- `pane.output_matched` emits when a subscription transitions into a matching state. it does not repeatedly spam the same visible match on every poll. +- `workspace create` focuses by default; pass `--no-focus` to keep focus where it is +- `pane split` focuses the new pane by default; pass `--no-focus` to keep focus on the original pane +- `pane read` prints **text**, not json +- `pane send-text`, `pane send-keys`, and `pane run` print nothing on success +- list/get/create/split/wait commands print json on success +- `pane run` is a convenience wrapper for `pane send-text` + `pane send-keys Enter` +- `wait agent-state` is a cli convenience built on top of event subscriptions +- `--raw` disables ansi stripping for `pane read` and `wait output` + +### cli examples + +create a workspace, split a pane, run a server, and wait for readiness: + +```bash +herdr workspace create --cwd /path/to/project +herdr pane split 1-1 --direction right --no-focus +herdr pane run 1-2 "npm run dev" +herdr wait output 1-2 --match "ready" --timeout 30000 +``` + +wait for another agent to finish: + +```bash +herdr wait agent-state 1-1 --state idle --timeout 60000 +``` + +inspect another pane's output: + +```bash +herdr pane read 1-1 --source recent --lines 80 +``` + +## behavior notes and gotchas + +- `pane.send_text` sends literal text only. if you want to execute a command, follow it with `pane.send_keys` and `Enter`, or use cli `pane run`. +- `pane.read` and `pane.wait_for_output` strip ansi by default. +- `pane.output_matched` subscriptions fire on transitions into a matching state; they do not repeatedly spam the same still-visible match on every poll. - closing the socket connection ends the subscription. -- there is no separate transport for events. +- there is no separate event transport. - the same herdr process can serve regular request/response calls and long-lived subscription connections at the same time. - -## intended layering - -recommended architecture: -- socket api = foundational integration protocol -- `herdr ...` commands = ergonomic wrapper for humans and coding agents - -current wrapper commands include: -- `herdr workspace list` -- `herdr workspace create ...` -- `herdr workspace get ...` -- `herdr workspace focus ...` -- `herdr workspace rename ...` -- `herdr workspace close ...` -- `herdr pane list ...` -- `herdr pane get ...` -- `herdr pane read ...` -- `herdr pane split ...` -- `herdr pane close ...` -- `herdr pane send-text ...` -- `herdr pane send-keys ...` -- `herdr pane run ...` -- `herdr wait output ...` -- `herdr wait agent-state ...` - -those commands sit on top of this socket surface rather than replacing it. - -for convenience, the CLI accepts pane ids in either raw socket form (`1-1`) or short human form (`1-1`). |
