goose-herdr
by aquaherd
README.md
# goose-herdr
A **goose** integration for **[Herdr](https://herdr.dev)** — the terminal
workspace manager for AI coding agents.
The repo ships two complementary pieces:
| Piece | Direction | What it does |
|---|---|---|
| `goose_herdr` MCP extension | goose → Herdr | Exposes the `herdr` CLI as first-class goose tools: inspect workspaces/tabs/panes/agents, create layout, run commands, read output, wait for state. |
| `agent-state/` hook plugin | goose → Herdr | Reports goose's lifecycle state (`working` / `idle`) to the Herdr pane hosting the goose session, so Herdr's sidebar shows goose correctly. |
> Both directions are "goose → Herdr". Herdr itself needs no code change: it
> detects agents by foreground process and exposes a local socket + CLI.
---
## Prerequisites
- **[goose](https://goose-docs.ai/)** installed.
- **[Herdr](https://herdr.dev/)** installed and on your `PATH` (or
`HERDR_BIN_PATH` set), with a Herdr server running.
The MCP extension works from any shell that can reach the Herdr server. The
state-reporting hook only activates when goose itself is running inside a
Herdr-managed pane (`HERDR_ENV=1`).
---
## Install the MCP extension
goose extensions are MCP servers. The package exposes a `herdr-mcp` console
script.
### Option A — config file (persistent)
Add an entry under `extensions` in `~/.config/goose/config.yaml`:
```yaml
extensions:
herdr:
name: Herdr
cmd: uvx
args: ["--from", "git+https://github.com/aquaherd/goose-herdr", "herdr-mcp"]
enabled: true
envs: {}
type: stdio
timeout: 300
```
Once the package is published to PyPI, you can use the shorter form:
```yaml
args: ["--from", "goose-herdr", "herdr-mcp"]
```
### Option B — interactive CLI
```sh
goose configure
```
Choose **Add Extension → Command-line Extension**, then enter:
```text
uvx --from git+https://github.com/aquaherd/goose-herdr herdr-mcp
```
### Option C — one-off session (not installed)
```sh
goose session --with-extension "uvx --from git+https://github.com/aquaherd/goose-herdr herdr-mcp"
```
### Option D — local development install
```sh
git clone https://github.com/aquaherd/goose-herdr
cd goose-herdr
uv pip install -e . # or: pip install -e .
```
Then register the extension with `cmd: herdr-mcp` (and empty `args`):
```yaml
extensions:
herdr:
name: Herdr
cmd: herdr-mcp
args: []
enabled: true
envs: {}
type: stdio
timeout: 300
```
---
## Install the state-reporting hook
Copy the `agent-state/` directory into a goose plugin location, for example:
```sh
mkdir -p ~/.agents/plugins/goose-herdr-agent-state
cp -r agent-state/plugin.json agent-state/hooks agent-state/scripts \
~/.agents/plugins/goose-herdr-agent-state/
chmod +x ~/.agents/plugins/goose-herdr-agent-state/scripts/report-state.sh
```
This reports `idle` on `SessionStart`/`Stop`, `working` on
`UserPromptSubmit`/`PreToolUse`, and releases the agent on `SessionEnd`. It is
a no-op unless `HERDR_ENV=1` and `HERDR_PANE_ID` are set (i.e. goose is running
in a Herdr pane).
On `SessionStart` it also forwards the session identity (`session_id` from the
hook payload) via `herdr pane report-agent-session`, so Herdr persists an
`agent_session` reference for the pane.
> `SessionStart` reports `idle` (not `working`) so a freshly started or
> restarted goose session is not shown as active in Herdr's sidebar until it
> actually begins working.
>
> **Resume caveat:** this hook reports the session *id* only (goose stores
> sessions in `sessions.db` and resumes by id via
> `goose session --resume --session-id <id>`). Herdr persists the id but will
> not relaunch the pane on a Herdr restore by itself — that would require a
> Herdr-native `goose` integration. See
> [Restore goose after a Herdr restart](#restore-goose-after-a-herdr-restart)
> for the `goose-herdr-restore` command that works around this.
---
## Restore goose after a Herdr restart
Herdr's `resume_agents_on_restore` only relaunches agents it has an official
integration for, and goose is not one of them. The hook records each goose
session's identity (pane id + session id) on `SessionStart`, and the
`goose-herdr-restore` command relaunches goose in those panes after a Herdr
restart.
```sh
goose-herdr-restore # relaunch any goose sessions that are not running
goose-herdr-restore --dry-run # preview without sending commands
```
Records live under `${XDG_STATE_HOME:-~/.local/state}/goose-herdr/restore/`.
The command skips panes where goose is already running, so it is safe to run
repeatedly. Relaunch uses `goose session --resume --session-id <id>`, which
restores the conversation; extensions come from your normal goose config.
For fully automatic restore, run it from a user timer (the `herdr` and `goose`
binaries must be on `PATH`):
```ini
# ~/.config/systemd/user/goose-herdr-restore.service
[Unit]
Description=Restore goose sessions into Herdr panes
[Service]
Type=oneshot
ExecStart=%h/.local/bin/goose-herdr-restore
```
```ini
# ~/.config/systemd/user/goose-herdr-restore.timer
[Unit]
Description=Restore goose sessions into Herdr panes periodically
[Timer]
OnBootSec=10s
OnUnitActiveSec=30s
[Install]
WantedBy=timers.target
```
```sh
systemctl --user daemon-reload
systemctl --user enable --now goose-herdr-restore.timer
```
---
## Tools
The extension registers `herdr_*` tools. The read-only ones are safe to call
anytime; the rest perform one Herdr action each.
| Group | Tools |
|---|---|
| Status / sessions | `herdr_status`, `herdr_session_list`, `herdr_help` |
| Workspaces | `herdr_workspace_list`, `_get`, `_create`, `_focus`, `_rename`, `_close` |
| Tabs | `herdr_tab_list`, `_get`, `_create`, `_focus`, `_rename`, `_close` |
| Panes | `herdr_pane_list`, `_current`, `_get`, `_split`, `_read`, `_run`, `_send_text`, `_send_keys`, `_wait_output`, `_rename`, `_focus`, `_close` |
| Agents | `herdr_agent_list`, `_get`, `_read`, `_prompt`, `_send_keys`, `_rename`, `_focus`, `_wait`, `_start`, `_goose_restore` |
Agent states used by Herdr: `idle`, `working`, `blocked`, `done`, `unknown`.
---
## Development
```sh
uv venv .venv
uv pip install --python .venv/bin/python -e .
.venv/bin/herdr-mcp # starts the MCP server over stdio
.venv/bin/goose-herdr-restore # restores goose sessions after a Herdr restart
```
The package uses the official `mcp` Python SDK (>= 2.0) and wraps the `herdr`
CLI rather than speaking the raw socket protocol.
This server cannot be deployed
Maintenance
ActivitySlowing
ResponsivenessNo issues