aboutsummaryrefslogtreecommitdiff
path: root/docs
diff options
context:
space:
mode:
authorOgulcan Celik <ogulcancelik@gmail.com>2026-05-16 23:43:10 +0300
committerOgulcan Celik <ogulcancelik@gmail.com>2026-05-16 23:43:10 +0300
commit8ee3a7bdc03f435175d9c5abecfa4e2b5e295264 (patch)
tree82baa4deeafb8152989dfc735511b05841c1d6da /docs
parent79be292911cea4f617c1994fc4b8fd022f85cab5 (diff)
docs: move next-release docs out of .pi
Diffstat (limited to 'docs')
-rw-r--r--docs/next/CHANGELOG.md438
-rw-r--r--docs/next/CONFIGURATION.md485
-rw-r--r--docs/next/INTEGRATIONS.md231
-rw-r--r--docs/next/README.md301
-rw-r--r--docs/next/SOCKET_API.md1048
5 files changed, 2503 insertions, 0 deletions
diff --git a/docs/next/CHANGELOG.md b/docs/next/CHANGELOG.md
new file mode 100644
index 00000000..72209d15
--- /dev/null
+++ b/docs/next/CHANGELOG.md
@@ -0,0 +1,438 @@
+# Changelog
+
+## Unreleased
+
+### Added
+- Added `ui.prompt_new_tab_name = false` for creating new tabs immediately with generated names instead of opening the rename dialog.
+- Added optional `keys.edit_scrollback` to open the focused pane's retained scrollback in `$EDITOR` inside a temporary zoomed pane.
+
+### Fixed
+- GitHub Copilot is now correctly detected when its process name is `copilot`.
+- Integration installs now respect `PI_CODING_AGENT_DIR`, `CLAUDE_CONFIG_DIR`, and `CODEX_HOME` when choosing Pi, Claude Code, and Codex config paths.
+- Split pane resize hit areas no longer overlap the first content column or row, making text selection work from the start of right and bottom panes.
+
+## [0.5.9] - 2026-05-15
+
+### Added
+- Added experimental Kitty graphics rendering for local panes and attached clients behind `experimental.kitty_graphics`, including support for larger graphics frames.
+- Added `ui.toast.delivery = "system"` for OS-level background notifications, using `notify-send` on Linux and `terminal-notifier` or `osascript` on macOS.
+- Added light variants for Catppuccin, Tokyo Night, Gruvbox, One, Solarized, Kanagawa, and Rosé Pine themes.
+- Added `ui.mouse_capture = false` for tmux-style mouse behavior, letting the terminal handle normal clicks while still forwarding mouse input to pane apps that request it.
+
+### Changed
+- Moved experimental settings into `[experimental]`.
+
+### Fixed
+- PageUp and PageDown now scroll Herdr pane scrollback for normal panes while still forwarding keys to full-screen or mouse-reporting apps.
+- Enhanced tilde key sequences now parse correctly, improving compatibility with terminals that emit them.
+- `herdr integration install codex` now enables the current Codex `[features] hooks = true` flag and migrates the deprecated top-level `codex_hooks` flag.
+
+### Breaking Changes
+- `advanced.allow_nested` has moved to `experimental.allow_nested`; update configs that allow nested Herdr launches.
+- The client/server protocol is now version 5. Stop and restart any running v0.5.8 server before attaching with this release.
+
+## [0.5.8] - 2026-05-12
+
+### Added
+- Added manual pane labels through `herdr pane rename`, the `pane.rename` socket API, an optional `keys.rename_pane` binding, and the right-click pane menu.
+- Added `ui.show_agent_labels_on_pane_borders`, which can show detected or reported agent names in split pane borders when no manual pane label is set.
+- Added `herdr integration status [--outdated-only]` so installed agent integrations can be checked for legacy or outdated versions.
+- Added an optional `keys.open_notification_target` binding for jumping to the pane behind the current notification.
+- Added optional `keys.previous_agent` and `keys.next_agent` bindings for cycling through sidebar agent entries.
+
+### Changed
+- Scrolling over the tab bar now switches tabs directly, including overflowing tab bars.
+
+### Fixed
+- Indexed terminal palette colors now render correctly for 256-color terminal apps.
+- Hook-based agent integrations now reject stale out-of-order reports and base notifications on effective agent state, reducing duplicate or stuck state changes.
+- Background tabs now resize when the outer terminal size changes, preventing stale pane dimensions when switching back to them.
+- Client shutdown now drains queued control messages more reliably.
+- Pane cursors are now hidden while scrolled back, and omitted while the mobile switcher is open.
+- Mobile agent switcher entries now include tab context, making agents easier to identify on narrow terminals.
+- macOS foreground job detection now uses process groups, improving agent state tracking for foreground commands.
+- Remote SSH no longer fails before connecting when macOS temporary bridge socket paths exceed Unix socket length limits. (#103, thanks @moonsphere)
+- Nix-wrapped agent commands are now detected by their underlying agent entrypoint.
+- Pane renames made through the socket API now rerender immediately.
+
+## [0.5.7] - 2026-05-10
+
+### Added
+- Added ANSI-formatted pane reads to the CLI and socket API with `herdr pane read --format ansi` / `--ansi`, preserving colors and styles for visible and recent pane output.
+
+### Changed
+- The agents panel now highlights the currently focused agent entry, matching the active workspace styling. (#84, thanks @soomtong)
+
+### Fixed
+- Git branch and ahead/behind refreshes now run off the main loop, preventing slow Git status checks from freezing the UI.
+- Update and startup flows now detect incompatible running servers earlier and give clear stop/restart guidance instead of trying to attach with a mismatched client/server protocol.
+- `herdr update` now downloads and prepares the new binary before stopping a running server, reducing the chance of interrupting an active session when download or install preparation fails.
+
+## [0.5.6] - 2026-05-09
+
+### Added
+- Added the `vesper` built-in theme. (#71, thanks @nexxeln)
+- Added `herdr --remote <ssh-target>`, so you can use Herdr as a thin client for remote servers without SSHing in first. Herdr connects over SSH, bootstraps a matching remote `herdr` binary when needed, starts the remote server automatically, and streams an efficient terminal view back to your local terminal.
+
+### Changed
+- Updated the bundled `libghostty-vt` engine and removed the custom Linux C++ runtime link workaround from static builds.
+- CLI workspace, tab, and pane creation now preserve the current focus by default; pass `--focus` to switch to the newly created item.
+
+### Fixed
+- OSC 8 hyperlinks emitted inside panes now remain clickable after Herdr renders them, including titled markdown-style links.
+- Agent panel scope now defaults to `all` and is saved to config when changed, so choosing `current` or `all` survives session resets and upgrades.
+- Native agent hook state now clears when the detected native agent exits, preventing stale hook-reported status from sticking to a pane.
+- Clicking an in-app agent toast now jumps to the relevant pane and clears the toast after focus.
+
+## [0.5.5] - 2026-05-06
+
+### Added
+- Added a mobile layout for narrow terminals, making it practical to SSH into your machine and run herdr from your phone.
+
+### Fixed
+- Non-ASCII terminal input is no longer dropped when UTF-8 characters arrive split across multiple reads.
+- Native agent detection now clears agents after their foreground process exits and control returns to the shell, preventing stale agent status in the sidebar.
+- Pane contents no longer shift horizontally when scrollback appears, keeping the scrollbar gutter stable.
+
+## [0.5.4] - 2026-05-03
+
+### Fixed
+- Visible active-tab panes that finish while the outer terminal is unfocused are now marked as seen when you return to herdr, preventing stale done/attention indicators.
+- IME candidate windows and mobile SSH cursor tracking now stay anchored to the focused pane during client redraws, including apps that hide the cursor, instead of drifting to sidebar or repaint positions.
+
+## [0.5.3] - 2026-04-30
+
+### Added
+- Added named persistent sessions, so you can keep separate herdr environments for different projects or contexts while sharing the same global config. See the docs for the full session CLI. (#57, thanks @fbettag)
+- Added `herdr status`, `herdr status server`, and `herdr status client` to inspect the local client, running server, protocol compatibility, socket path, and whether a restart is needed.
+
+### Changed
+- Focused panes can now still alert you through terminal notifications when the herdr terminal window is unfocused, so active work does not go quiet just because you switched to another app.
+
+### Fixed
+- Dragging pane split borders now works when the app inside the pane has mouse reporting enabled, including Claude Code no-flicker mode. (#61, thanks @EYH0602)
+- Pressing the prefix key twice now forwards a literal prefix key into the focused pane in client mode again.
+- `herdr integration install` and `herdr integration uninstall` now work without requiring a running herdr server.
+- Pane PTYs now keep their last attached size while detached, preventing detached output from being resized or rewrapped to fallback dimensions.
+
+## [0.5.2] - 2026-04-27
+
+### Added
+- Config can now be reloaded in the running app/server from the global menu or with `herdr server reload-config`, applying safe live settings without restarting the persistent server.
+
+### Fixed
+- Persistent server startup now surfaces config diagnostics in attached clients instead of silently hiding parse or validation errors.
+- Pane backgrounds now stay transparent when the host terminal background color is unknown, while explicit terminal cell backgrounds still render correctly.
+- Persistent-session toast and sound notifications now target the foreground attached client instead of firing across every connected client.
+- Claude Code subagent hook events no longer make the parent Claude pane look idle or released when a subagent finishes, and permissioned tool-call completion keeps the pane in the correct working state.
+
+## [0.5.1] - 2026-04-25
+
+### Added
+- Toast notifications can now be delivered through the outer terminal as desktop notifications. Configure this with `ui.toast.delivery = "terminal"`; see `CONFIGURATION.md` for details.
+- Herdr now writes separate capped support logs for app, client, and server modes, making persistent-session issue reports easier to diagnose without unbounded log growth.
+- The bundled opencode plugin now reports question prompts as blocked while waiting for user input, then returns to working or idle when answered or dismissed. Question prompts are also detected by the default terminal-screen heuristics. (#51, thanks @mspiegel31)
+
+### Changed
+- Routine API request traces now log at debug level by default, making normal support logs smaller and easier to read while preserving detailed traces when debug logging is enabled.
+
+### Fixed
+- Pasted text and other reverse-video terminal content now stays readable when pane backgrounds are transparent. (#45, thanks @EYH0602)
+- Panes now advertise a stable `TERM=xterm-256color` and `COLORTERM=truecolor` by default, improving redraw and cursor behavior in shells and remote sessions.
+- Pane scrollbars once again reserve their own rightmost column instead of overlaying terminal content in persistent session mode.
+- Terminal-delivered toast notifications now use the server-approved delivery decision in persistent session mode, so attaching clients do not incorrectly suppress them.
+- In-app toast delivery now stays inside herdr instead of also forwarding a terminal/desktop notification.
+
+## [0.5.0] - 2026-04-21
+
+### Breaking Changes Please Read
+- herdr now defaults to a persistent server/client session model. running `herdr` starts or reattaches to a background session server instead of launching the old single-process UI.
+- quitting the UI in default mode now detaches the current client and leaves the shared session running. use `herdr server stop` to stop the background server explicitly.
+- the old monolithic behavior is still available as an escape hatch with `herdr --no-session`.
+
+### Added
+- Persistent sessions are now the default product behavior. You can detach and reattach without stopping pane processes.
+- Added the thin client and headless server as first-class product components, including auto-detect launch, explicit `herdr client`, and `herdr server stop`.
+- Sessions now restore cleanly after full restart, preserving workspaces, tabs, panes, and running process state.
+- Multi-client attach is now supported. Multiple clients can connect to the same shared session.
+
+### Changed
+- In persistence mode, in-app quit actions now detach the current client by default instead of shutting down the whole background server.
+- The current persistence model is a shared session view across attached clients. It is not yet full tmux-style per-client independent navigation.
+- Restored sessions now land in terminal mode, while fresh sessions still start in navigate mode.
+
+## [0.4.11] - 2026-04-16
+
+### Breaking Changes Please Read
+- The update flow changes in `0.4.11`. Herdr no longer installs updates silently in the background. Starting with this release, herdr only checks for updates and shows them in the UI. To install a new release, quit herdr and then run `herdr update` manually in your shell.
+- This prepares the upcoming `0.5.0` persistence release. Herdr is moving from the old single-binary update model toward a persistent server/client session model, so your workspace can keep running while clients attach, detach, and reconnect.
+- The reason for this change is upgrade safety. Herdr needs to stop the old running process cleanly before the new client/server model takes over, so manual update avoids mixed-version states during the transition.
+
+### Added
+- Hook-reported agent state can now use custom agent labels, so integrations are no longer limited to herdr’s built-in agent names. Custom labels now flow through pane/workspace UI and the socket API anywhere agent names are shown.
+
+## [0.4.10] - 2026-04-14
+
+### Added
+- Prefix mode now supports custom command keybindings via `[[keys.command]]`, so you can launch detached shell helpers or open temporary overlay panes from inside herdr using the active workspace, tab, pane, and cwd context.
+- Pressing the prefix key twice now forwards a literal prefix keystroke into the focused pane, which makes nested tools and terminal apps that use the same prefix easier to control.
+
+### Fixed
+- App-level key handling now normalizes enhanced keyboard reporting consistently, so shifted bindings and text like `?` and uppercase characters work correctly in navigate mode and text-entry UI.
+- Ctrl+letter input is now encoded correctly when pane apps enable kitty keyboard mode, improving compatibility with terminal programs that expect CSI-u style key reporting.
+- The collapsed sidebar now keeps the active workspace visibly highlighted even while you stay in terminal mode.
+- Droid Mission Control screens are now treated as idle instead of active work, reducing false busy-state detection.
+
+## [0.4.9] - 2026-04-13
+
+### Fixed
+- Droid's primary-screen redraws no longer erase pane scrollback inside herdr, while normal scrollback-clear behavior is preserved elsewhere.
+- `q` is now dedicated to quitting in navigate mode instead of also acting as a generic cancel key in modals and overlays, reducing accidental quits.
+- Tab bar scrolling is tighter: the scroll-right button and new-tab button now sit directly adjacent to the last visible tab without a gap, and manual scroll no longer overscrolls past the last tab.
+
+## [0.4.8] - 2026-04-12
+
+### Added
+- Themes can now set `panel_bg = "reset"` to let herdr’s panel chrome inherit the host terminal background instead of painting an opaque panel fill. This also accepts the aliases `default`, `none`, and `transparent`.
+- Ghostty-backed panes now preserve the host terminal’s default background when it matches the outer terminal theme, so terminal window transparency can show through pane content instead of being repainted as an opaque color.
+
+### Fixed
+- Clipboard writes now prefer native platform clipboard tools (`pbcopy`, `wl-copy`, `xclip`, or `xsel`) before falling back to OSC 52, which makes copy operations from panes more reliable across terminal setups.
+
+## [0.4.7] - 2026-04-10
+
+### Added
+- The tab bar now handles large tab sets better: you can scroll overflowing tabs with the mouse controls or wheel, and reorder tabs by dragging them.
+- `workspace create` and `tab create` now return the created root pane in their JSON response, so automation can act on the new pane immediately without an extra lookup.
+
+### Fixed
+- Background panes that start idle no longer show up as `done` or trigger finished-state attention until they have actually transitioned from working or blocked to idle.
+- Left-click now focuses panes and right-click now opens the pane context menu even when the inner TUI has mouse reporting enabled, fixing apps like Claude Code. (#25, thanks @othavioquiliao)
+- OSC 52 clipboard writes from apps running inside panes now reach the host clipboard correctly, including copy requests emitted by child processes inside the pane.
+- `pane close` now removes only the targeted tab when other tabs still exist in the workspace, instead of closing the whole workspace.
+- Amp approval prompts are now detected more reliably as blocked, including tool-call, command, and file edit/create approval screens.
+
+### Breaking Changes
+- Socket API clients that match `result.type` exactly need to handle `workspace_created` and `tab_created` for `workspace.create` and `tab.create`; these calls no longer return `workspace_info` and `tab_info`.
+
+## [0.4.6] - 2026-04-09
+
+### Fixed
+- Agent state detection is now more reliable when panes are scrolled back, when Codex is running in narrow panes, and when Claude opens slash-command or settings menus, reducing false blocked or idle states.
+- Mouse-driven terminal text selection now autoscrolls into pane scrollback and clears cleanly after copy, so selecting beyond the visible viewport works as expected.
+- Pane terminal colors now return to the outer terminal theme after fullscreen TUIs exit, fixing cases like Droid leaving stale background colors behind. This restore path now also works correctly on macOS.
+
+## [0.4.5] - 2026-04-09
+
+### Added
+- `herdr workspace create` and `herdr tab create` now support `--label`, so scripts and agents can name new workspaces and tabs immediately instead of creating them first and renaming them afterward.
+- The global menu now includes a manual **reload keybinds** action, so you can apply `config.toml` keybinding changes without restarting herdr.
+- The socket API and CLI now expose a `done` agent status, including `herdr wait agent-status --status done`, so automation can distinguish finished agent runs from panes that are merely idle.
+
+### Changed
+- Session state is now saved automatically with a debounce while you work, so recent workspace, tab, pane, and sidebar changes are preserved more reliably even if herdr exits unexpectedly.
+
+### Fixed
+- Only the focused pane now owns the terminal cursor, which removes stray cursor blocks from unfocused panes.
+- In-app **What's New** / release notes now render inline code spans and fenced code blocks correctly.
+- Default numbered tabs now stay auto-named when you keep or rename them back to their numeric label, so generated tab numbering stays compact and predictable.
+
+## [0.4.4] - 2026-04-08
+
+### Changed
+- The expanded sidebar can now be split into resizable workspace and agent sections with a draggable divider, and that section sizing is preserved across restarts.
+
+### Fixed
+- IME input now works properly for Chinese and other UTF-8 input methods in pane terminals, so candidate selection no longer falls back to typing raw digit keys. (#9, thanks @Edmund-a7)
+- `herdr pane run ...` now uses the bracketed-paste-aware input path, improving compatibility with shells and terminal apps that expect pasted command text to arrive atomically.
+- The local socket API is more robust and secure: its Unix socket is now restricted to the current user, and long-running output waits and subscriptions stop cleanly on disconnect or shutdown instead of hanging indefinitely.
+
+## [0.4.3] - 2026-04-07
+
+### Fixed
+- Update checks and in-app **What's New** release notes no longer depend on GitHub’s release API, which avoids the transient 403 failures from the previous update path.
+- `herdr pane run ...` now submits the full command atomically in one request, fixing cases where scripted commands did not reliably execute because the final Enter was sent separately.
+- Bare line-feed input is now preserved in raw terminal input instead of being normalized to Enter, fixing Linux terminal cases where inputs like Shift+Enter or Ctrl+J could be interpreted incorrectly.
+
+## [0.4.2] - 2026-04-07
+
+### Added
+- The expanded sidebar agent panel can now switch between the current workspace and all workspaces, so you can scan and jump to agents across the whole session.
+- The collapsed sidebar now shows compact per-pane agent indicators, so you can keep an eye on agent activity without reopening the full sidebar.
+
+### Changed
+- The sidebar now handles larger workspace sets more cleanly: the workspace section has headers, its own scrolling, better-aligned drag/drop slots, and manual width changes persist across restarts. Double-clicking the divider resets it to the configured default width.
+- Pane scrollback is now configured with `advanced.scrollback_limit_bytes`, matching Ghostty's byte-based scrollback limit. Set it to `0` to disable pane scrollback entirely. The old `advanced.scrollback_lines` key is still accepted as an alias, but it now uses the same byte-based value.
+- Linux release binaries now ship with libghostty SIMD enabled again without reintroducing the musl startup issue, restoring the optimized Linux build path.
+
+### Fixed
+- Typing in pane terminals on macOS is responsive again after the Ghostty migration, by keeping a persistent per-pane Ghostty key encoder instead of rebuilding it on every keypress.
+- The collapsed sidebar expand toggle works again.
+- Creating a new tab now waits until you confirm the dialog, so cancelling the new-tab flow no longer leaves behind an unwanted tab.
+- Copying selected pane text now uses Ghostty's native selection extraction, which preserves wrapped text and wide characters more accurately.
+- Session restore is more tolerant of older and current snapshot formats, including pre-tab session files.
+
+## [0.4.1] - 2026-04-06
+
+### Fixed
+- Fixed Linux release binaries crashing on startup.
+
+## [0.4.0] - 2026-04-05
+
+### Major Changes
+- Herdr now uses a Ghostty-backed terminal engine as its pane runtime.
+- The legacy vt100 pane backend has been removed, making Ghostty the single terminal backend going forward.
+
+### UX and Interaction
+- Workspaces can now be reordered by dragging them in the sidebar.
+- Notification sounds now support custom mp3 file overrides, with either one shared file or separate files for finished vs needs-attention alerts.
+
+### API and Integration
+- Workspace API ids are now stable, making socket and CLI automation more predictable across workspace changes and restores.
+
+### Packaging and Runtime
+- macOS builds now statically link the vendored `libghostty-vt`, preserving the single-binary install and update flow.
+
+## [0.3.2] - 2026-04-03
+
+### Changed
+- The global launcher now surfaces update-related actions more clearly: when release notes are available you can open **What's New**, and when an update has been downloaded you can **quit to apply update** directly from the menu.
+- Release notes are now retained as the latest available notes after you dismiss the startup modal, so you can reopen them later from the UI instead of only seeing them once.
+
+### Fixed
+- Fixed held-key repeat in terminal panes on macOS terminals that send explicit repeat events through the enhanced keyboard protocol, restoring continuous backspace, character, and arrow-key repeat without letting modal close/confirm key repeats leak into the shell.
+
+## [0.3.1] - 2026-04-03
+
+### Added
+- New tabs now open directly into the rename flow, with the default tab name prefilled and replaced on first type so you can name tabs as you create them.
+
+### Changed
+- Polished modal layout and spacing across onboarding, settings, keybind help, and release notes so overlays feel more consistent and their content/actions line up more cleanly.
+- Debug builds now use separate runtime/config paths from normal releases, which avoids local development sessions colliding with your main herdr install.
+
+### Fixed
+- Starting a second herdr instance against an active socket now fails fast with a clear error instead of clobbering the running session.
+- Fixed pane and agent state updates being dropped under internal event queue pressure, which could leave a pane showing stale status after work finished.
+- Fixed onboarding modal sizing and click targets, and corrected release-notes scroll calculations when a scrollbar is present.
+
+## [0.3.0] - 2026-04-03
+
+### Major Changes
+- Added tabs within workspaces, so a single workspace can now hold multiple terminal tab contexts with their own pane layouts.
+- Added first-class tab support to the local socket API and CLI wrappers, including `herdr tab ...` commands and tab ids like `1:2` alongside workspace-scoped pane ids.
+- Added built-in direct integrations for pi, claude code, codex, and opencode, plus authoritative hook-driven state reporting so supported agents can report semantic state directly instead of relying only on screen heuristics.
+- Added a post-update release-notes screen so herdr can explain what changed after an update is installed.
+
+### UX and Controls
+- Added optional direct pane-focus keybindings for terminal mode, so you can switch panes with modifier shortcuts like `alt+h` or `alt+right` without entering navigate mode first.
+- Reworked keybind discoverability so the in-app keybind help now shows all supported actions, including optional bindings that are currently unset.
+- Keybind help now uses a centered scrollable modal with mouse and keyboard scrolling, matching the release-notes interaction model more closely.
+- Popups and action-button interactions now use more consistent modal geometry and button semantics across the UI.
+- Polished the sidebar agent section so it focuses on detected agents only and uses clearer two-line agent cards with more breathing room.
+
+### Behavior Fixes
+- Hook-driven agent state updates now stay correct in tabbed workspaces.
+- Modifier-only keypresses no longer leak into panes as stray input.
+- Multi-tab agent labels now include tab names when that extra context matters.
+- Workspace identity now follows the first tab's root pane again instead of stale creation-time cwd.
+- Background notification suppression is now tab-aware rather than workspace-wide, so background tabs in the current workspace can still alert correctly.
+
+### Documentation
+- Updated the README, configuration guide, integrations guide, skill, and socket API docs to reflect tabs, direct integrations, unset optional keybindings, direct terminal-mode navigation examples, workspace-scoped pane ids, and the current workspace identity/sidebar model.
+
+## [0.2.4] - 2026-04-01
+
+### Fixed
+- Fixed a macOS-only startup misdetection where pi could briefly appear as codex in the sidebar because process environment entries were being parsed as command-line arguments.
+
+## [0.2.3] - 2026-03-31
+
+### Changed
+- Mouse wheel handling now follows the tmux/Ghostty model more closely: fullscreen apps receive wheel input when they own scrolling, while herdr keeps host scrollback for panes that are behaving like a normal terminal transcript.
+- Pane scrollbars now only appear when herdr has real host scrollback for that pane, instead of implying a host-managed scroll position for app-owned scrolling.
+
+### Fixed
+- Fixed Codex and pi panes becoming unscrollable in herdr by preserving recoverable host history for top-anchored normal-screen output, without relying on alternate-screen scrollback retention.
+- Fixed pane wheel routing so apps using mouse reporting or alternate-scroll behavior can receive scroll input directly instead of having herdr always intercept it.
+
+## [0.2.2] - 2026-03-31
+
+### Fixed
+- Fixed pane scrollbars so they reserve their own lane instead of drawing over terminal content, which makes scrolling and scrollbar dragging behave more cleanly in narrow panes.
+- Fixed alternate-screen scrollback handling so full-screen terminal apps can preserve recoverable history inside herdr panes instead of losing rows that scroll off.
+- Fixed Codex in herdr panes losing transcript/history while running in alternate screen, so past output remains scrollable instead of disappearing as the session grows.
+- Hid the rendered terminal cursor while a pane is scrolled back, avoiding stray cursor blocks appearing in the wrong place during history navigation.
+
+## [0.2.1] - 2026-03-31
+
+### Added
+- Herdr now checks for updates at startup and periodically while it stays open, so long-running sessions can still discover new releases without a restart cycle.
+- Added a lightweight bottom-right toast when an update has been downloaded and is ready, with a simple restart-to-use-it flow.
+
+### Changed
+- Rendering is now driven more directly by app events instead of relying as much on polling, which makes the UI feel snappier and cuts unnecessary redraw work.
+
+### Fixed
+- Restored smooth fast spinner animation for working agents.
+- Closing a pane or workspace now reliably terminates the processes running inside that pane session instead of leaving shells or child processes behind.
+- Fixed bracketed paste handling so incomplete paste sequences are preserved across read timeouts instead of being dropped or misread.
+
+## [0.2.0] - 2026-03-30
+
+### Added
+- Added a local Unix socket API for controlling running herdr sessions, including workspace and pane management, pane reads, text/key input, pane splitting, and output waits.
+- Added event subscriptions over the socket API for workspace and pane lifecycle events, pane output matches, and agent state changes.
+- Added CLI wrappers on top of the socket API with `herdr workspace ...`, `herdr pane ...`, and `herdr wait ...`, using compact public ids like `1` and `1-2` for scripting and agent orchestration.
+- Added a settings popup with mouse support for changing themes, sound alerts, and toast notifications from inside herdr.
+- Added 9 built-in themes: catppuccin, tokyo night, dracula, nord, gruvbox, one dark, solarized, kanagawa, and rosé pine.
+- Added interactive pane scrollbars, manual sidebar resizing, and upstream git ahead/behind indicators in the workspace sidebar.
+
+### Changed
+- Redesigned the sidebar into a two-section layout that separates workspace-level triage from per-agent detail, making it easier to supervise multiple agents in parallel.
+- Agent state names exposed in the UI and integration surfaces now use `working` and `blocked`.
+- Herdr now blocks nested launches by default when started inside a herdr-managed pane; set `advanced.allow_nested = true` to opt back in.
+
+### Fixed
+- Improved terminal keyboard protocol parsing and input forwarding across terminal variants, including better handling for shifted printable keys.
+- Fixed Ghostty on macOS misparsing some arrow-key and modifier/enhanced key sequences.
+- Refined sidebar rollups and pane ordering so workspace status and agent lists stay more stable and predictable.
+
+### Documentation
+- Refreshed the README, socket API reference, and reusable agent skill docs to better explain herdr's agent multiplexer model and integration surface.
+
+## [0.1.2] - 2026-03-28
+
+### Added
+- Added first-run onboarding flow that lets you choose notification preferences (sound and toast) on startup.
+- Added optional visual toast notifications in the top-right corner for background workspace events (completion and attention-needed alerts).
+- Added configurable keybindings for all navigate mode actions: new workspace, rename workspace, close workspace, resize mode, and toggle sidebar. See `CONFIGURATION.md` for the full key reference.
+- Added configuration validation with startup diagnostics. Invalid key combinations or duplicate bindings now fall back to safe defaults with a visible warning.
+
+### Changed
+- **Breaking:** Default prefix key changed from `ctrl+s` to `ctrl+b` to avoid common terminal flow control conflicts.
+- Workspaces now derive their identity from the repository or folder of their root pane, updating automatically as you navigate. Custom names act as overrides rather than static labels.
+- Sidebar now shows workspace numbers again in expanded view.
+- Refined sidebar presentation with consistent marker/name/state ordering and comma-separated agent summaries.
+- Keybinding parser now accepts special keys (`enter`, `esc`, `tab`, `backspace`, `space`) and function keys (`f1`–`f12`).
+
+### Documentation
+- Split configuration reference into a dedicated `CONFIGURATION.md` file with full keybinding documentation and config diagnostics explanation.
+
+## [0.1.1] - 2026-03-28
+
+### Added
+- Added optional sound notifications for agent state changes, including a completion chime when background work finishes and an alert when an agent needs input.
+- Added per-agent sound overrides under `[ui.sound.agents]`, so you can mute or enable notifications by agent instead of using one global setting. Droid notifications are muted by default.
+
+### Changed
+- Request alerts now play even when the agent is in the active workspace, while completion sounds remain limited to background workspaces.
+
+### Fixed
+- Improved foreground job detection on Linux and macOS so herdr can recognize agents that run through wrapper processes or generic runtimes, including cases like Codex running under `node`.
+- Made Claude Code state detection more stable by handling more spinner variants and smoothing short busy/idle flicker during screen updates.
+
+## [0.1.0] - 2026-03-27
+
+### Added
+- Initial release.
diff --git a/docs/next/CONFIGURATION.md b/docs/next/CONFIGURATION.md
new file mode 100644
index 00000000..ee524942
--- /dev/null
+++ b/docs/next/CONFIGURATION.md
@@ -0,0 +1,485 @@
+# configuration
+
+herdr reads config from:
+
+```text
+~/.config/herdr/config.toml
+```
+
+Named sessions share this config file. Sessions are runtime/socket namespaces, not workspace replacements; per-session sockets and persistent runtime state are separate:
+
+```text
+~/.config/herdr/session.json
+~/.config/herdr/sessions/<name>/session.json
+```
+
+Use `herdr session list`, `herdr session attach <name>`, `herdr session stop <name>`, and `herdr session delete <name>` to inspect and manage named session namespaces. Add `--json` to session commands when scripts need machine-readable output.
+
+In default persistence mode, quitting the UI detaches the current client. Use `herdr server stop` to stop the shared background server.
+
+print the full default config with:
+
+```bash
+herdr --default-config
+```
+
+if a config value is invalid, or two navigate actions use the same keybinding, herdr falls back to a safe default and shows a startup warning in the UI.
+
+## live reload
+
+After editing `config.toml`, reload the running app without restarting the persistent server:
+
+```bash
+herdr server reload-config
+```
+
+You can also use the global menu inside herdr and choose `reload config`.
+
+Reload is server-owned. In persistent mode the CLI sends a request to the running server, and the server reads, parses, validates, and applies `config.toml`.
+
+Reloadable now:
+- keybindings and prefix
+- theme, custom theme colors, and legacy `ui.accent`
+- `ui.confirm_close`
+- `ui.agent_panel_scope`
+- `ui.toast.delivery`
+- server-side `ui.sound` policy; attached thin clients refresh local sound config after a successful sound-policy change
+- `experimental.kitty_graphics`
+- `advanced.scrollback_limit_bytes` for panes created after reload
+- `ui.sidebar_width` as the default width; current width updates only while it is still config-owned
+
+Startup-only or special-case:
+- `onboarding` does not reopen onboarding during reload
+- `experimental.allow_nested` is checked before launch and needs a restart
+- existing pane scrollback buffers are not resized during reload
+- terminal notifications and sounds are client-local side effects and are sent to the foreground attached client
+
+If the TOML cannot be read or parsed, reload applies nothing and keeps the current running state. If keybindings are invalid, herdr keeps the current keybindings while applying other valid reloadable settings where possible.
+
+## onboarding
+
+```toml
+onboarding = true
+```
+
+| option | default | description |
+|--------|---------|-------------|
+| `onboarding` | unset | show first-run notification setup; set `false` after choosing |
+
+notes:
+- missing `onboarding` currently behaves like `true`
+- set `onboarding = true` to force the setup screen again for testing
+- continuing from onboarding writes `onboarding = false` and opens the normal settings UI
+
+## keybindings
+
+keybindings live under `[keys]`.
+
+supported syntax:
+- plain keys: `n`, `x`, `-`, `` ` ``
+- modifiers: `ctrl+b`, `shift+n`, `alt+x`, `cmd+x`, `super+x`
+- special keys: `enter`, `esc`, `tab`, `backspace`, `left`, `right`, `up`, `down`
+- function keys: `f1`, `f12`
+- uppercase letters also imply shift: `D` works like `shift+d`
+
+notes:
+- most reliable bindings are plain keys, `ctrl+letter`, `esc`/`tab`/`enter`, and function keys
+- `alt+...`, `cmd`/`super`, and punctuation-with-modifiers may vary depending on terminal/tmux setup
+- bindings marked `unset` in the key reference are supported actions with no default key assigned
+- for navigate-mode actions, duplicate keybindings are treated as config errors; later conflicting bindings fall back to defaults
+
+example:
+
+```toml
+[keys]
+prefix = "ctrl+b"
+new_workspace = "n"
+rename_workspace = "shift+n"
+close_workspace = "X"
+reload_config = "" # optional, unset by default
+open_notification_target = "" # optional, unset by default
+new_tab = "c"
+split_vertical = "d"
+split_horizontal = "D"
+close_pane = "x"
+rename_pane = "" # optional, unset by default
+edit_scrollback = "" # optional, opens focused pane scrollback in $EDITOR
+fullscreen = "f"
+resize_mode = "r"
+toggle_sidebar = "b"
+previous_workspace = "ctrl+alt+["
+next_workspace = "ctrl+alt+]"
+previous_agent = "ctrl+["
+next_agent = "ctrl+]"
+previous_tab = "alt+["
+next_tab = "alt+]"
+focus_pane_left = "alt+h"
+focus_pane_down = "alt+j"
+focus_pane_up = "alt+k"
+focus_pane_right = "alt+l"
+
+[keys.indexed]
+tabs = "" # optional; e.g. "ctrl" makes ctrl+1..9 switch tabs
+workspaces = "" # optional; e.g. "ctrl+shift" makes ctrl+shift+1..9 switch workspaces
+agents = "" # optional; follows visible agent panel order
+```
+
+### key reference
+
+| key | default | action |
+|-----|---------|--------|
+| `prefix` | `ctrl+b` | enter or leave navigate mode |
+| `new_workspace` | `n` | create a new workspace |
+| `rename_workspace` | `shift+n` | rename selected workspace |
+| `close_workspace` | `shift+d` | close selected workspace |
+| `detach` | unset | optional explicit detach shortcut in the persistent session |
+| `reload_config` | unset | reload `config.toml` in the running app/server |
+| `open_notification_target` | unset | jump to the currently visible notification target |
+| `previous_workspace` | unset | switch to the previous workspace directly from terminal mode |
+| `next_workspace` | unset | switch to the next workspace directly from terminal mode |
+| `previous_agent` | unset | focus the previous agent shown in the sidebar agent list |
+| `next_agent` | unset | focus the next agent shown in the sidebar agent list |
+| `new_tab` | `c` | create a new tab |
+| `rename_tab` | unset | rename the active tab |
+| `previous_tab` | unset | switch to the previous tab directly from terminal mode |
+| `next_tab` | unset | switch to the next tab directly from terminal mode |
+| `close_tab` | unset | close the active tab |
+| `focus_pane_left` | unset | focus the pane to the left directly from terminal mode |
+| `focus_pane_down` | unset | focus the pane below directly from terminal mode |
+| `focus_pane_up` | unset | focus the pane above directly from terminal mode |
+| `focus_pane_right` | unset | focus the pane to the right directly from terminal mode |
+| `split_vertical` | `v` | split pane vertically (side by side) |
+| `split_horizontal` | `-` | split pane horizontally (stacked) |
+| `close_pane` | `x` | close focused pane |
+| `rename_pane` | unset | rename the focused pane |
+| `edit_scrollback` | unset | open the focused pane's retained scrollback in `$EDITOR` inside a temporary zoomed pane |
+| `fullscreen` | `f` | toggle focused pane fullscreen |
+| `resize_mode` | `r` | enter or leave resize mode |
+| `toggle_sidebar` | `b` | collapse or expand the sidebar |
+
+`edit_scrollback` writes the focused pane's retained plain-text scrollback to a temporary file, opens `${EDITOR:-vi}` on that file in a temporary zoomed pane, then removes the file when the editor exits.
+
+### indexed keybindings
+
+Use `[keys.indexed]` to bind number keys `1` through `9` as positional shortcuts. Each value is a modifier combo only. Empty values disable that shortcut family.
+
+```toml
+[keys.indexed]
+tabs = ""
+workspaces = ""
+agents = ""
+```
+
+| key | default | action |
+|-----|---------|--------|
+| `tabs` | unset | switch to tab 1-9 in the active workspace, left to right |
+| `workspaces` | unset | switch to workspace 1-9 in sidebar order, top to bottom |
+| `agents` | unset | focus agent row 1-9 in the visible agent panel order |
+
+### custom command keybindings
+
+Use `[[keys.command]]` to bind a prefix-mode key to a command. Press the prefix key, then the configured key.
+
+```toml
+[[keys.command]]
+key = "g"
+type = "pane"
+command = "lazygit"
+```
+
+`type` is optional and defaults to `shell`.
+
+| type | behavior |
+|------|----------|
+| `shell` | run the command detached in the background |
+| `pane` | open a temporary zoomed pane, run the command there, then close the pane when the command exits |
+
+Commands run through `/bin/sh -lc`. Herdr sets the command working directory to the active pane cwd when available and provides context through environment variables:
+
+| variable | value |
+|----------|-------|
+| `HERDR_SOCKET_PATH` | active herdr socket path |
+| `HERDR_BIN_PATH` | current herdr binary path |
+| `HERDR_ACTIVE_WORKSPACE_ID` | active workspace id |
+| `HERDR_ACTIVE_TAB_ID` | active tab id |
+| `HERDR_ACTIVE_PANE_ID` | focused pane id |
+| `HERDR_ACTIVE_PANE_CWD` | focused pane cwd |
+
+Example detached helper:
+
+```toml
+[[keys.command]]
+key = "shift+g"
+type = "shell"
+command = "notify-send herdr 'custom command ran'"
+```
+
+## theme
+
+herdr ships with 17 built-in color themes. set one in config:
+
+```toml
+[theme]
+name = "tokyo-night"
+```
+
+### built-in themes
+
+| name | description |
+|------|-------------|
+| `catppuccin` | soft pastel mocha palette (default) |
+| `catppuccin-latte` | light catppuccin palette |
+| `tokyo-night` | blue-purple aesthetic |
+| `tokyo-night-day` | light tokyo night palette |
+| `dracula` | purple/pink/green classic |
+| `nord` | frosty scandinavian blues |
+| `gruvbox` | warm retro browns/oranges |
+| `gruvbox-light` | light gruvbox palette |
+| `one-dark` | atom's beloved dark palette |
+| `one-light` | atom's light palette |
+| `solarized` | ethan schoonover's classic dark palette |
+| `solarized-light` | ethan schoonover's classic light palette |
+| `kanagawa` | hokusai-inspired |
+| `kanagawa-lotus` | light kanagawa palette |
+| `rose-pine` | muted, elegant |
+| `rose-pine-dawn` | light rosé pine palette |
+| `vesper` | high-contrast monochrome with peach and mint accents |
+
+theme names are flexible: `tokyo-night`, `tokyonight`, and `tokyo_night` all work.
+
+### custom overrides
+
+override individual color tokens on top of any base theme:
+
+```toml
+[theme]
+name = "dracula"
+
+[theme.custom]
+panel_bg = "reset"
+accent = "#f5c2e7"
+red = "rgb(255, 85, 85)"
+green = "#a6e3a1"
+```
+
+all tokens are optional — only set what you want to change.
+
+### available tokens
+
+| token | used for |
+|-------|----------|
+| `accent` | highlights, active borders, navigation UI |
+| `panel_bg` | floating panel, tab bar, and overlay background |
+| `surface0` | selected item background |
+| `surface1` | hover/active backgrounds |
+| `surface_dim` | active workspace background, separators |
+| `overlay0` | muted text, secondary info |
+| `overlay1` | slightly brighter secondary text |
+| `text` | primary text |
+| `subtext0` | workspace names, dimmed labels |
+| `mauve` | git branch names, special labels |
+| `green` | idle/done states |
+| `yellow` | busy/running states |
+| `red` | waiting/needs attention states |
+| `blue` | unseen notifications |
+| `teal` | done notification accents |
+| `peach` | interrupted/warning states |
+
+tokens accept the same color formats as `accent`: hex (`#rrggbb`), named colors, or `rgb(r,g,b)`.
+
+for `panel_bg`, you can also use `reset`, `default`, `none`, or `transparent` to stop herdr from painting an opaque panel background and instead use the host terminal's default background.
+
+## ui
+
+```toml
+[ui]
+sidebar_width = 26
+mouse_capture = true
+confirm_close = true
+prompt_new_tab_name = true
+show_agent_labels_on_pane_borders = false
+agent_panel_scope = "all"
+accent = "cyan"
+```
+
+### options
+
+| option | default | description |
+|--------|---------|-------------|
+| `sidebar_width` | `26` | base sidebar width before auto-scaling |
+| `mouse_capture` | `true` | capture mouse input for Herdr's mouse UI; set false to let the terminal handle normal clicks while still forwarding mouse to pane apps that request it |
+| `confirm_close` | `true` | ask before closing a workspace |
+| `prompt_new_tab_name` | `true` | ask for a tab name before creating a new tab; set false to create tabs immediately with generated names |
+| `show_agent_labels_on_pane_borders` | `false` | show detected/reported agent labels in split pane borders when no manual pane name is set |
+| `agent_panel_scope` | `all` | sidebar agent list scope: `current` or `all` |
+| `accent` | `cyan` | highlight and border color |
+
+Changing the agent panel scope from the sidebar writes `agent_panel_scope` to config so it survives session resets and upgrades.
+
+`accent` accepts:
+- named colors like `cyan`, `blue`, `magenta`
+- hex like `#89b4fa`
+- rgb like `rgb(137,180,250)`
+
+## toast notifications
+
+```toml
+[ui.toast]
+delivery = "off"
+```
+
+### options
+
+| option | default | description |
+|--------|---------|-------------|
+| `ui.toast.delivery` | `off` | where background popup notifications should appear |
+
+available values:
+- `off` — disable popup notifications
+- `herdr` — show top-right in-app toasts
+- `terminal` — ask the outer terminal to show a desktop notification. some terminals suppress foreground notifications, including ghostty on macos.
+- `system` — ask the os notification service directly. on macos, herdr uses `terminal-notifier` when available and falls back to built-in `osascript`. on linux, `system` requires `notify-send`.
+
+### macos system notifications
+
+for best macos support, install `terminal-notifier`:
+
+```sh
+brew install terminal-notifier
+```
+
+when `terminal-notifier` is installed, herdr tries to focus the hosting terminal when a notification is clicked. click-to-return is supported for detected ghostty, iterm2, wezterm, kitty, alacritty, and terminal.app sessions.
+
+without `terminal-notifier`, herdr falls back to built-in `osascript`. this still shows a macos notification, but clicking the notification may focus the apple script runner instead of returning to your terminal.
+
+compatibility note:
+- older configs may still use `ui.toast.enabled = true|false`
+- herdr still reads that legacy key for compatibility
+- if you save toast settings from inside herdr, it rewrites the setting to `ui.toast.delivery`
+
+current behavior:
+- informational only
+- one notification event at a time
+- shown for background agent events like `needs attention` and `finished`
+- suppression is tab-aware: the active tab stays quiet, but background tabs in the same workspace can still notify
+- `terminal` delivery is best-effort and depends on terminal support
+- `system` delivery is best-effort and depends on the os helper being available
+- macos `system` delivery prefers `terminal-notifier` when present and falls back to `osascript`
+- currently targets terminals such as ghostty, kitty, iterm2, and wezterm
+- inside tmux, herdr wraps notification escapes with tmux passthrough
+
+## sound
+
+```toml
+[ui.sound]
+enabled = true
+
+[ui.sound.agents]
+claude = "default"
+droid = "off"
+```
+
+### options
+
+| option | default | description |
+|--------|---------|-------------|
+| `ui.sound.enabled` | `true` | enable background agent sounds |
+
+per-agent values:
+- `default`
+- `on`
+- `off`
+
+available agent keys:
+- `pi`
+- `claude`
+- `codex`
+- `gemini`
+- `cursor`
+- `cline`
+- `open_code`
+- `github_copilot`
+- `kimi`
+- `droid`
+- `amp`
+
+## experimental
+
+```toml
+[experimental]
+allow_nested = false
+kitty_graphics = false
+```
+
+### options
+
+| option | default | description |
+|--------|---------|-------------|
+| `experimental.allow_nested` | `false` | allow launching herdr from inside a herdr-managed pane |
+| `experimental.kitty_graphics` | `false` | enable experimental local Kitty graphics rendering for attached clients |
+
+### nested launches
+
+By default, herdr blocks nested launches when `HERDR_ENV=1` is already present.
+
+Set `allow_nested = true` only for debugging or intentionally nested setups.
+
+### Kitty graphics
+
+`kitty_graphics` enables experimental local Kitty graphics rendering for attached clients.
+
+It requires a Kitty graphics-compatible outer terminal.
+
+Known limitation: resizing the terminal window or changing the terminal font while images are visible can leave existing images misplaced or stale. Restart the pane app or clear and redraw the image output after changing size or font. Please report any findings so this experimental path can improve.
+
+## advanced
+
+```toml
+[advanced]
+scrollback_limit_bytes = 10000000
+```
+
+### options
+
+| option | default | description |
+|--------|---------|-------------|
+| `advanced.scrollback_limit_bytes` | `10000000` | maximum scrollback buffer size in bytes retained per pane terminal |
+
+### scrollback
+
+`scrollback_limit_bytes` limits retained terminal scrollback per pane.
+
+The default matches Ghostty's `scrollback-limit` value.
+
+Set `scrollback_limit_bytes = 0` to disable pane scrollback entirely.
+
+The legacy `scrollback_lines` key is still accepted inside `[advanced]`, but it uses the same byte-based value.
+
+## environment variables
+
+| variable | description |
+|----------|-------------|
+| `HERDR_LOG` | log level filter (default: `herdr=info`) |
+
+## logs
+
+herdr writes local file logs under:
+
+```text
+~/.config/herdr/
+```
+
+common files:
+
+```text
+~/.config/herdr/herdr.log
+~/.config/herdr/herdr-client.log
+~/.config/herdr/herdr-server.log
+```
+
+notes:
+- `herdr.log` is used by monolithic `--no-session` mode and some top-level startup paths
+- persistent session mode mainly uses `herdr-client.log` and `herdr-server.log`
+- logs rotate automatically by size and keep a few older files as `.1`, `.2`, and so on
+- default logs are metadata-focused and are intended to be shareable for issue diagnosis
+- `HERDR_LOG` can increase verbosity when you need a local repro or deeper debugging
diff --git a/docs/next/INTEGRATIONS.md b/docs/next/INTEGRATIONS.md
new file mode 100644
index 00000000..ff46d0c5
--- /dev/null
+++ b/docs/next/INTEGRATIONS.md
@@ -0,0 +1,231 @@
+# integrations
+
+herdr works without any hook or plugin setup.
+
+out of the box, it detects supported agents automatically by combining foreground process detection with screen heuristics. that is enough to give you workspace awareness with zero configuration.
+
+when an agent exposes hooks or plugins, the more robust path is to forward semantic state to herdr over the local socket api. the built-in integrations in this document do exactly that.
+
+if you want to inspect the exact files herdr installs, they are versioned in this repo:
+
+- [pi extension](./src/integration/assets/pi/herdr-agent-state.ts)
+- [claude code hook](./src/integration/assets/claude/herdr-agent-state.sh)
+- [codex hook](./src/integration/assets/codex/herdr-agent-state.sh)
+- [opencode plugin](./src/integration/assets/opencode/herdr-agent-state.js)
+
+## how herdr uses integrations
+
+herdr uses a hybrid model:
+
+- **process detection** owns pane identity, liveness, and "the process is gone"
+- **agent integrations** report semantic state like `working`, `blocked`, and `idle` over the local socket api when the tool exposes those events
+- **screen heuristics** remain the fallback for gaps, unsupported tools, or incomplete hook surfaces
+
+that means hooks/plugins do **not** become the source of truth for pane ownership. they enrich state reporting; they do not replace process detection.
+
+## install commands
+
+```bash
+herdr integration install pi
+herdr integration install claude
+herdr integration install codex
+herdr integration install opencode
+```
+
+## uninstall commands
+
+```bash
+herdr integration uninstall pi
+herdr integration uninstall claude
+herdr integration uninstall codex
+herdr integration uninstall opencode
+```
+
+## pi
+
+install:
+
+```bash
+herdr integration install pi
+```
+
+this writes the bundled pi extension to:
+
+```text
+~/.pi/agent/extensions/herdr-agent-state.ts
+```
+
+if `PI_CODING_AGENT_DIR` is set, herdr uses that agent directory instead and writes to:
+
+```text
+$PI_CODING_AGENT_DIR/extensions/herdr-agent-state.ts
+```
+
+`~` is expanded in `PI_CODING_AGENT_DIR`.
+
+pi is the cleanest integration. it already has an authoritative hook model, so herdr can get direct state reports over the socket api without guessing as much from the terminal.
+
+bundled source: [`src/integration/assets/pi/herdr-agent-state.ts`](./src/integration/assets/pi/herdr-agent-state.ts)
+
+uninstall:
+
+```bash
+herdr integration uninstall pi
+```
+
+this removes the same extension path herdr would install, using `PI_CODING_AGENT_DIR` when it is set.
+
+## claude code
+
+install:
+
+```bash
+herdr integration install claude
+```
+
+this:
+
+- writes the hook script to `~/.claude/hooks/herdr-agent-state.sh`
+- updates `~/.claude/settings.json`
+
+if `CLAUDE_CONFIG_DIR` is set, herdr uses that directory instead, for example `$CLAUDE_CONFIG_DIR/hooks/herdr-agent-state.sh` and `$CLAUDE_CONFIG_DIR/settings.json`. `~` is expanded in `CLAUDE_CONFIG_DIR`.
+
+bundled source: [`src/integration/assets/claude/herdr-agent-state.sh`](./src/integration/assets/claude/herdr-agent-state.sh)
+
+current hook mapping:
+
+- `UserPromptSubmit` → `working`
+- `PreToolUse` → `working`
+- `PermissionRequest` → `blocked`
+- `PostToolUse` → `working`
+- `PostToolUseFailure` → `working`
+- `SubagentStop` → `working`
+- `Stop` → `idle`
+- `SessionEnd` → `release`
+
+notes:
+
+- claude code hooks also run inside subagents. herdr treats subagent `working` and `blocked` reports as real pane state.
+- subagent stop/release events are converted to `working` by the bundled hook script so a completed subagent does not make the parent claude pane look idle.
+- `PostToolUse` and `PostToolUseFailure` move the pane back to `working` after a permissioned tool call resolves.
+
+uninstall:
+
+```bash
+herdr integration uninstall claude
+```
+
+this removes the same hook path and settings entries herdr would install, using `CLAUDE_CONFIG_DIR` when it is set.
+
+## codex
+
+install:
+
+```bash
+herdr integration install codex
+```
+
+this:
+
+- writes the hook script to `~/.codex/herdr-agent-state.sh`
+- updates `~/.codex/hooks.json`
+- ensures `hooks = true` under `[features]` in `~/.codex/config.toml`
+- migrates the deprecated top-level `[features] codex_hooks = true` setting to `hooks = true`
+
+if `CODEX_HOME` is set, herdr uses that directory instead, for example `$CODEX_HOME/herdr-agent-state.sh`, `$CODEX_HOME/hooks.json`, and `$CODEX_HOME/config.toml`. `~` is expanded in `CODEX_HOME`.
+
+bundled source: [`src/integration/assets/codex/herdr-agent-state.sh`](./src/integration/assets/codex/herdr-agent-state.sh)
+
+current hook mapping:
+
+- `SessionStart` → `idle`
+- `UserPromptSubmit` → `working`
+- `PreToolUse` → `working`
+- `Stop` → `idle`
+
+notes:
+
+- codex does **not** currently expose a permission-specific hook like claude or opencode, so `blocked` still depends on herdr's normal heuristics.
+- codex currently renders hook lifecycle messages in its own tui, for example `Running SessionStart hook` and `SessionStart hook (completed)`.
+- that noise is an upstream codex limitation, not a herdr-specific issue.
+- codex has a `suppressOutput` field in its hook output schema, but it is currently not effective for suppressing those tui lifecycle lines.
+
+uninstall:
+
+```bash
+herdr integration uninstall codex
+```
+
+this:
+
+- removes the same hook path herdr would install, using `CODEX_HOME` when it is set
+- removes herdr-owned hook entries from the matching `hooks.json`
+- intentionally leaves the matching `config.toml` alone
+
+that last point is deliberate: herdr does **not** try to guess whether `hooks = true` is still needed for some other codex hook setup.
+
+## opencode
+
+install:
+
+```bash
+herdr integration install opencode
+```
+
+this writes the bundled plugin to:
+
+```text
+~/.config/opencode/plugins/herdr-agent-state.js
+```
+
+bundled source: [`src/integration/assets/opencode/herdr-agent-state.js`](./src/integration/assets/opencode/herdr-agent-state.js)
+
+current plugin mapping:
+
+- `permission.asked` → `blocked`
+- `permission.replied: once|always` → `working`
+- `permission.replied: reject` → `idle`
+- `question.asked` → `blocked`
+- `question.replied` → `working`
+- `question.rejected` → `idle`
+- `session.status: busy|retry` → `working`
+- `session.status: idle` → `idle`
+- `session.idle` → `idle`
+
+notes:
+
+- opencode has the richest event surface of the currently supported integrations.
+- herdr intentionally does **not** guess that `session.deleted` means process exit. process detection still owns liveness and pane identity.
+
+uninstall:
+
+```bash
+herdr integration uninstall opencode
+```
+
+this removes:
+
+```text
+~/.config/opencode/plugins/herdr-agent-state.js
+```
+
+## amp
+
+herdr does not currently install an Amp plugin.
+
+Amp's public plugin API exposes lifecycle and tool-call hooks, but not passive permission/request-blocked events. A Herdr Amp plugin that only reports `idle` and `working` would take hook authority for the pane and mask Herdr's existing screen heuristics for Amp `blocked` states. Until Amp exposes permission state as an observable plugin event, Amp remains heuristic-only in Herdr.
+
+## known limitations
+
+- these integrations only activate inside herdr-managed panes.
+- if an agent has an incomplete hook surface, herdr falls back to process detection and screen heuristics rather than inventing lease or ttl behavior.
+- codex currently shows hook lifecycle chatter in its own tui until upstream adds a real silent mode.
+
+## troubleshooting
+
+if an install command succeeds but you do not see improved state reporting:
+
+1. make sure you launched the agent inside a herdr pane
+2. restart the agent session so it picks up the new hook/plugin config
+3. verify the expected config file was written to the path above
+4. remember that unsupported transitions still fall back to herdr's built-in heuristics
diff --git a/docs/next/README.md b/docs/next/README.md
new file mode 100644
index 00000000..1b5ed7fa
--- /dev/null
+++ b/docs/next/README.md
@@ -0,0 +1,301 @@
+# herdr
+
+
+<p align="center">
+ <img src="assets/logo.png" alt="herdr" width="100" />
+</p>
+
+<p align="center">
+ <a href="https://herdr.dev">herdr.dev</a> · <a href="#install">install</a> · <a href="#quick-start">quick start</a> · <a href="#supported-agents">supported agents</a> · <a href="./INTEGRATIONS.md">integrations</a> · <a href="./CONFIGURATION.md">configuration</a> · <a href="./SOCKET_API.md">socket api</a>
+</p>
+
+---
+
+https://github.com/user-attachments/assets/043ec09f-4bdd-41d5-aee0-8fda6b83e267
+
+**agent multiplexer that lives in your terminal.**
+
+workspaces, tabs, panes. mouse-native: click, drag, split. every agent at a glance: blocked, working, done. detach and reattach, agents keep running. no gui app, no electron, no mac-only native wrapper. you see the agent's own terminal, not someone's interpretation of it.
+
+---
+
+## install
+
+```bash
+curl -fsSL https://herdr.dev/install.sh | sh
+```
+
+or download the binary from [releases](https://github.com/ogulcancelik/herdr/releases). requires linux or macos.
+
+### update
+
+herdr notifies you when a new version is available. run manually to update:
+
+```bash
+herdr update
+```
+
+## quick start
+
+```bash
+herdr
+```
+
+by default herdr launches or attaches to one background session server. `ctrl+b q` detaches the client. agents keep running. use `herdr server stop` to stop the default server. use `--no-session` for the old single-process mode.
+
+named sessions are runtime/socket namespaces for separate persistent herdr servers. they do not replace workspaces; each named session has its own panes, tabs, workspaces, sockets, and session state while sharing the same global config file.
+
+```bash
+herdr session list
+herdr session attach work
+herdr session attach side-project
+herdr session stop work
+herdr session delete side-project
+```
+
+1. press `n` to create a workspace
+2. run an agent in the root pane
+3. press `ctrl+b` to enter navigate mode
+4. use `v` or `-` to split panes, or `c` to create a new tab
+5. watch the sidebar for blocked, working, and done states
+
+on first run herdr opens a short onboarding flow. after that, restored sessions land in terminal mode; fresh sessions start in **navigate mode**.
+
+## how it compares
+
+| | tmux | gui managers | herdr |
+|--------------------------|------|--------------|-------|
+| persistent sessions | ✓ | — | ✓ |
+| detach / reattach | ✓ | — | ✓ |
+| panes, tabs, workspaces | ✓ | ✓ | ✓ |
+| agent awareness | — | ✓ | ✓ |
+| lives in your terminal | ✓ | — | ✓ |
+| real terminal views | ✓ | — | ✓ |
+| mouse-native | — | ✓ | ✓ |
+| lightweight binary | ✓ | — | ✓ |
+| agents can orchestrate | ? | ? | ✓ |
+
+tmux gives you persistence and panes, but it was built before agents existed. gui managers show agent state, but they make you leave your terminal and use their wrapped view. herdr is persistence and awareness in one tool that stays out of your way.
+
+## persistence
+
+start herdr on your desktop or server. run your agents, split panes, do your work. press `ctrl+b q` to detach. close your terminal, close your laptop; your agents keep running. open a new terminal, run `herdr`, you're back. same session, same panes, same agents.
+
+### from anywhere
+
+need to check on your agents from your phone? just ssh in and run herdr. any ssh client works. no app to download, no account to create.
+
+```
+ssh you@yourserver
+herdr
+```
+
+or attach from your local terminal through ssh:
+
+```bash
+herdr --remote workbox
+herdr --remote ssh://you@yourserver:2222
+```
+
+for repeat targets, use your ssh config:
+
+```sshconfig
+Host workbox
+ HostName yourserver
+ User you
+ Port 2222
+```
+
+same session, same agents, same state.
+
+## agent awareness
+
+the sidebar shows which agents are blocked, working, or done. workspaces roll up to their most urgent state so you can scan the full list at a glance.
+
+states:
+
+- 🔴 **blocked** — agent needs input or approval
+- 🟡 **working** — agent is actively running
+- 🔵 **done** — work finished, you have not looked at it yet
+- 🟢 **idle** — done and seen
+
+detection works by reading foreground process and terminal output. zero config, no hooks required. for agents that expose hooks, the socket api integration gives more robust state reporting.
+
+## lives in your terminal
+
+not a gui window, not a web dashboard, not electron. herdr runs inside whatever terminal you already use. single rust binary, no dependencies. works inside tmux.
+
+## what you get
+
+- **workspaces** — organized around git repos or folder names, each with its own tabs and panes
+- **tabs** — first-class in the socket api and cli
+- **mouse-native** — click panes/tabs/workspaces/agents, drag borders, select text to copy, right-click menus; not keyboard-only
+- **notifications** — sounds and toasts for background events; tab-aware suppression
+- **17 built-in themes** — catppuccin, tokyo night, gruvbox, one, solarized, kanagawa, rosé pine, vesper, and light variants for the main palettes
+- **session persistence** — pane processes survive client detach; sessions restore after full restart
+
+## agents can use herdr too
+
+the local unix socket lets agents create workspaces, split panes, spawn helpers, read output, and wait for state changes.
+
+```bash
+# create a workspace and tab
+herdr workspace create --cwd ~/project --label "api"
+herdr tab create --label "logs"
+
+# split a pane and run
+herdr pane split 1-1 --direction right
+herdr pane run 1-2 "npm test"
+
+# wait for an agent
+herdr wait agent-status 1-1 --status done
+
+# read output
+herdr pane read 1-2 --source recent --lines 50
+
+# read a rendered ANSI snapshot for TUI feedback loops
+herdr pane read 1-2 --source visible --ansi
+```
+
+full reference: [`SOCKET_API.md`](./SOCKET_API.md) and [`SKILL.md`](./SKILL.md).
+
+## supported agents
+
+automatic detection works out of the box. process name matching plus terminal output heuristics.
+
+| 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) | ✓ | ✓ | ✓ |
+| [droid](https://factory.ai) | ✓ | ✓ | ✓ |
+| [amp](https://ampcode.com) | ✓ | ✓ | ✓ |
+| [opencode](https://github.com/anomalyco/opencode) | ✓ | ✓ | ✓ |
+
+detected but not fully tested: gemini cli, cursor agent, cline, kimi, github copilot cli.
+
+for agents outside the built-in list, herdr still works as a terminal multiplexer with workspaces, panes, and tiling. custom integrations can report agent labels over the socket api. see [`SOCKET_API.md`](./SOCKET_API.md).
+
+### direct integrations
+
+the built-in pi, claude code, codex, and opencode integrations forward semantic state to herdr over the socket api. install with:
+
+```bash
+herdr integration install pi
+herdr integration install claude
+herdr integration install codex
+herdr integration install opencode
+```
+
+see [`INTEGRATIONS.md`](./INTEGRATIONS.md) for setup details.
+
+## keybindings
+
+press `ctrl+b` to enter navigate mode.
+
+| key | action |
+|-----|--------|
+| `n` | new workspace |
+| `shift+n` | rename workspace |
+| `shift+d` | close workspace |
+| `c` | new tab |
+| `v` / `-` | split pane |
+| `x` | close pane |
+| `b` | toggle sidebar |
+| `f` | fullscreen pane |
+| `r` | resize mode |
+| `q` | detach (quit client) |
+
+resize mode: `h`/`l` resize width, `j`/`k` resize height, `esc` exit.
+
+custom command keybindings can launch detached shell helpers or temporary panes from prefix mode:
+
+```toml
+[[keys.command]]
+key = "g"
+type = "pane" # "shell" or "pane"
+command = "lazygit"
+```
+
+mouse is supported throughout. full reference: [`CONFIGURATION.md`](./CONFIGURATION.md).
+
+## configuration
+
+config file: `~/.config/herdr/config.toml`
+
+```bash
+herdr --default-config # print full default config
+```
+
+in-app settings screen for theme, sound, and toast preferences. full reference: [`CONFIGURATION.md`](./CONFIGURATION.md).
+
+## logs
+
+herdr writes logs under `~/.config/herdr/`.
+
+common files:
+
+```text
+~/.config/herdr/herdr.log
+~/.config/herdr/herdr-client.log
+~/.config/herdr/herdr-server.log
+```
+
+in persistent session mode, the client and server logs are usually the useful ones. logs rotate automatically and keep a few older files like `.1` and `.2`.
+
+for issue reports, include the relevant current log plus rotated siblings if they exist. default logs are metadata-focused and avoid pane contents by default.
+
+use a higher log level only when needed:
+
+```bash
+HERDR_LOG=herdr=debug herdr
+```
+
+full logging and environment variable details: [`CONFIGURATION.md`](./CONFIGURATION.md).
+
+## docs
+
+- [`CONFIGURATION.md`](./CONFIGURATION.md) — keybindings, themes, notifications, environment variables
+- [`INTEGRATIONS.md`](./INTEGRATIONS.md) — pi, claude code, codex, opencode integrations
+- [`SKILL.md`](./SKILL.md) — reusable agent skill
+- [`SOCKET_API.md`](./SOCKET_API.md) — socket protocol and cli reference
+
+## building from source
+
+```bash
+git clone https://github.com/ogulcancelik/herdr
+cd herdr
+cargo build --release
+./target/release/herdr
+```
+
+## testing
+
+```bash
+just test # unit tests
+just test-all # full local test suite
+```
+
+## license
+
+AGPL-3.0: free to use, modify, and distribute. modified versions must be open-sourced under the same license.
+
+## pi, ghostty, and shift+enter
+
+herdr does not require or install terminal keybinds for pi.
+
+ghostty does not ship a default `shift+enter=text:\n` or `shift+enter=text:\x1b\r` keybind. if those lines exist in your ghostty config, they were added by user config or another tool, commonly claude code. they collapse shift+enter into legacy bytes, so downstream programs cannot reliably distinguish shift+enter from ctrl+j or alt+enter.
+
+if shift+enter behaves differently in pi inside herdr, first remove those custom terminal keybinds and retest. do not file this as a herdr keyboard encoding bug unless it reproduces with a clean terminal config.
+
+related context: #78, #81, #106, and earendil-works/pi#1872.
+
+## mandatory star history
+
+<a href="https://www.star-history.com/?repos=ogulcancelik%2Fherdr&type=date&legend=top-left">
+ <picture>
+ <source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/chart?repos=ogulcancelik/herdr&type=date&theme=dark&legend=top-left" />
+ <source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/chart?repos=ogulcancelik/herdr&type=date&legend=top-left" />
+ <img alt="Star History Chart" src="https://api.star-history.com/chart?repos=ogulcancelik/herdr&type=date&legend=top-left" />
+ </picture>
+</a>
diff --git a/docs/next/SOCKET_API.md b/docs/next/SOCKET_API.md
new file mode 100644
index 00000000..783d6e97
--- /dev/null
+++ b/docs/next/SOCKET_API.md
@@ -0,0 +1,1048 @@
+# 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 events.
+
+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 server stop`, `herdr workspace ...`, `herdr tab ...`, `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-status` are **cli conveniences**, not raw socket methods.
+
+## transport
+
+- transport: unix domain socket
+- 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
+
+named sessions are runtime/socket namespaces, not replacements for herdr workspaces. each named session has its own server sockets and persistent runtime state while config remains global.
+
+socket path resolution order:
+
+1. explicit `herdr --session <name>`:
+ `$XDG_CONFIG_HOME/herdr/sessions/<name>/herdr.sock` or `$HOME/.config/herdr/sessions/<name>/herdr.sock`
+2. `HERDR_SOCKET_PATH`
+3. `HERDR_SESSION=<name>`:
+ `$XDG_CONFIG_HOME/herdr/sessions/<name>/herdr.sock` or `$HOME/.config/herdr/sessions/<name>/herdr.sock`
+4. default session path:
+ `$XDG_CONFIG_HOME/herdr/herdr.sock` or `$HOME/.config/herdr/herdr.sock`
+
+this means `HERDR_SOCKET_PATH` remains an exact low-level socket override, but an explicit cli `--session <name>` still wins when a command runs inside a pane that inherited `HERDR_SOCKET_PATH`.
+
+session names may contain ASCII letters, numbers, `.`, `_`, and `-`. `default` is reserved for the default session. use `herdr session list`, `herdr session attach <name>`, `herdr session stop <name>`, and `herdr session delete <name>` to inspect and manage session namespaces. session commands print human-readable output by default; pass `--json` for machine-readable output. `session delete` refuses running sessions and does not delete the default session.
+
+## request and response envelopes
+
+all socket requests use this envelope:
+
+```json
+{
+ "id": "req_1",
+ "method": "ping",
+ "params": {}
+}
+```
+
+successful responses look like:
+
+```json
+{
+ "id": "req_1",
+ "result": {
+ "type": "pong",
+ "version": "0.1.2",
+ "protocol": 2
+ }
+}
+```
+
+errors look like:
+
+```json
+{
+ "id": "req_1",
+ "error": {
+ "code": "pane_not_found",
+ "message": "pane 1-99 not found"
+ }
+}
+```
+
+## ids and numbering
+
+workspace ids are opaque, stable ids like:
+
+- `w64e95948145ed1`
+- `w64e95948146a82`
+
+pane ids are workspace-scoped and stable across workspace reorder:
+
+- `w64e95948145ed1-1`
+- `w64e95948145ed1-2`
+- `w64e95948146a82-1`
+
+that means:
+
+- workspace id = stable workspace identity
+- pane number = compact pane number within that workspace
+
+workspace ids are durable for the life of the workspace and survive display reordering. pane numbers are still compact public numbers, so if a pane closes, higher pane numbers in that same workspace compact down.
+
+tabs are first-class socket api objects now.
+
+- tab ids look like `w64e95948145ed1:1`, `w64e95948145ed1:2`
+- workspace id = stable workspace identity
+- tab number = tab number within that workspace
+- pane ids still stay workspace-scoped like `w64e95948145ed1-2` rather than becoming `workspace-tab-pane` triples
+
+for backward compatibility, requests also accept the older positional forms like `1`, `1:2`, and `1-2` as shorthand for the current session order. responses use the stable ids.
+
+## core objects
+
+`workspace_info` responses contain objects like:
+
+```json
+{
+ "workspace_id": "w64e95948145ed1",
+ "number": 1,
+ "label": "herdr",
+ "focused": true,
+ "pane_count": 1,
+ "tab_count": 1,
+ "active_tab_id": "w64e95948145ed1:1",
+ "agent_status": "unknown"
+}
+```
+
+`tab_info` responses contain objects like:
+
+```json
+{
+ "tab_id": "w64e95948145ed1:1",
+ "workspace_id": "w64e95948145ed1",
+ "number": 1,
+ "label": "1",
+ "focused": true,
+ "pane_count": 1,
+ "agent_status": "unknown"
+}
+```
+
+`pane_info` responses contain objects like:
+
+```json
+{
+ "pane_id": "w64e95948145ed1-1",
+ "workspace_id": "w64e95948145ed1",
+ "tab_id": "w64e95948145ed1:1",
+ "focused": true,
+ "cwd": "/home/can/Projects/herdr",
+ "label": "reviewer",
+ "agent": "pi",
+ "agent_status": "working",
+ "custom_status": "indexing",
+ "revision": 0
+}
+```
+
+`label` is an optional manual pane name set through `pane.rename`.
+
+`agent` is an optional display label string.
+
+- when herdr detects a built-in agent, this is that built-in name like `pi` or `claude`
+- when a hook or plugin reports a custom agent through `pane.report_agent`, this can be any non-empty label like `hermes`
+- when no agent identity is known, it is omitted
+
+`custom_status` is an optional hook-owned display label. It is present only while hook authority is active. It does not change `agent_status`, `done` semantics, waits, notifications, or workspace/tab rollup priority.
+
+`pane_read` responses contain objects like:
+
+```json
+{
+ "pane_id": "w64e95948145ed1-1",
+ "workspace_id": "w64e95948145ed1",
+ "tab_id": "w64e95948145ed1:1",
+ "source": "recent",
+ "format": "text",
+ "text": "...",
+ "revision": 0,
+ "truncated": false
+}
+```
+
+`agent_status` is the public agent field:
+
+- `idle`
+- `working`
+- `blocked`
+- `done`
+- `unknown`
+
+`done` means the agent has finished, but you have not looked at that finished pane yet.
+
+## methods at a glance
+
+| method | purpose | success result type |
+|---|---|---|
+| `ping` | health check / version | `pong` |
+| `server.stop` | gracefully stop the running background server | `ok` |
+| `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` |
+| `tab.list` | list tabs, optionally filtered by workspace | `tab_list` |
+| `tab.get` | inspect one tab | `tab_info` |
+| `tab.create` | create a tab in a workspace | `tab_info` |
+| `tab.focus` | focus a tab | `tab_info` |
+| `tab.rename` | rename a tab | `tab_info` |
+| `tab.close` | close a tab | `ok` |
+| `pane.list` | list panes, optionally filtered by workspace | `pane_list` |
+| `pane.get` | inspect one pane | `pane_info` |
+| `pane.rename` | set or clear a manual pane label | `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.send_input` | send literal text plus keypresses in order | `ok` |
+| `pane.report_agent` | report hook-authoritative agent label and state for a pane | `ok` |
+| `pane.clear_agent_authority` | clear hook-authoritative agent state for a pane | `ok` |
+| `pane.release_agent` | release a pane from the reported agent back to shell state | `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 |
+
+### `server.stop`
+
+request:
+
+```json
+{
+ "id": "req_stop",
+ "method": "server.stop",
+ "params": {}
+}
+```
+
+returns `ok` and asks the running background server to shut down cleanly.
+
+this is the explicit server-level shutdown path for persistence mode. normal in-app quit actions detach the current client instead of sending this request.
+
+## workspace methods
+
+### `workspace.list`
+
+request:
+
+```json
+{
+ "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 also defaults to no focus; pass `--focus` to switch to the new workspace
+
+example response:
+
+```json
+{
+ "id": "req_create",
+ "result": {
+ "type": "workspace_info",
+ "workspace": {
+ "workspace_id": "1",
+ "number": 1,
+ "label": "herdr",
+ "focused": true,
+ "pane_count": 1,
+ "tab_count": 1,
+ "active_tab_id": "1:1",
+ "agent_status": "unknown"
+ }
+ }
+}
+```
+
+### `workspace.focus`
+
+params:
+
+```json
+{
+ "workspace_id": "1"
+}
+```
+
+returns the focused workspace as `workspace_info`.
+
+### `workspace.rename`
+
+params:
+
+```json
+{
+ "workspace_id": "1",
+ "label": "api"
+}
+```
+
+returns updated `workspace_info`.
+
+### `workspace.close`
+
+params:
+
+```json
+{
+ "workspace_id": "1"
+}
+```
+
+returns:
+
+```json
+{
+ "id": "req_close",
+ "result": {
+ "type": "ok"
+ }
+}
+```
+
+## tab methods
+
+### `tab.list`
+
+request with no filter:
+
+```json
+{
+ "id": "req_tabs",
+ "method": "tab.list",
+ "params": {}
+}
+```
+
+request filtered to one workspace:
+
+```json
+{
+ "id": "req_tabs_ws",
+ "method": "tab.list",
+ "params": {
+ "workspace_id": "1"
+ }
+}
+```
+
+returns `tab_list`.
+
+### `tab.get`
+
+params:
+
+```json
+{
+ "tab_id": "1:2"
+}
+```
+
+returns `tab_info`.
+
+### `tab.create`
+
+params:
+
+```json
+{
+ "workspace_id": "1",
+ "cwd": "/home/can/Projects/herdr",
+ "focus": true
+}
+```
+
+notes:
+
+- `workspace_id` is optional and defaults to the active workspace
+- `cwd` is optional; if omitted, herdr uses the focused pane cwd in that workspace when available
+- `focus` is optional in raw socket requests and defaults to `false`
+- the cli wrapper also defaults to no focus; pass `--focus` to switch to the new tab
+
+returns `tab_info` for the new tab.
+
+### `tab.focus`
+
+params:
+
+```json
+{
+ "tab_id": "1:2"
+}
+```
+
+returns focused `tab_info`.
+
+### `tab.rename`
+
+params:
+
+```json
+{
+ "tab_id": "1:2",
+ "label": "logs"
+}
+```
+
+returns updated `tab_info`.
+
+### `tab.close`
+
+params:
+
+```json
+{
+ "tab_id": "1:2"
+}
+```
+
+returns `ok`. the last tab in a workspace cannot be closed.
+
+## pane methods
+
+### `pane.list`
+
+request with no filter:
+
+```json
+{
+ "id": "req_panes",
+ "method": "pane.list",
+ "params": {}
+}
+```
+
+request filtered to one workspace:
+
+```json
+{
+ "id": "req_panes_ws",
+ "method": "pane.list",
+ "params": {
+ "workspace_id": "1"
+ }
+}
+```
+
+returns `pane_list`.
+
+### `pane.get`
+
+params:
+
+```json
+{
+ "pane_id": "1-1"
+}
+```
+
+returns `pane_info`.
+
+### `pane.rename`
+
+params:
+
+```json
+{
+ "pane_id": "1-1",
+ "label": "reviewer"
+}
+```
+
+send `label: null` or omit `label` to clear the manual pane label.
+
+returns `pane_info`.
+
+### `pane.read`
+
+params:
+
+```json
+{
+ "pane_id": "1-1",
+ "source": "recent",
+ "lines": 80,
+ "format": "text",
+ "strip_ansi": true
+}
+```
+
+notes:
+
+- `source` is required and must be `visible`, `recent`, or `recent_unwrapped`
+- `lines` is optional
+- current implementation defaults to `80` lines when `lines` is omitted and caps reads at `1000`
+- `format` defaults to `text`; use `ansi` for a rendered VT/ANSI snapshot with styles preserved
+- `strip_ansi` defaults to `true` and is kept for compatibility
+
+`source` meanings:
+
+- `visible` — current viewport
+- `recent` — recent scrollback text
+- `recent_unwrapped` — recent scrollback text with soft wraps joined
+
+example response:
+
+```json
+{
+ "id": "req_read",
+ "result": {
+ "type": "pane_read",
+ "read": {
+ "pane_id": "1-1",
+ "workspace_id": "1",
+ "tab_id": "1:1",
+ "source": "recent",
+ "format": "text",
+ "text": "...",
+ "revision": 0,
+ "truncated": false
+ }
+ }
+}
+```
+
+### `pane.split`
+
+params:
+
+```json
+{
+ "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 also defaults to no focus; pass `--focus` to switch to the new pane
+
+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.send_input`
+
+params:
+
+```json
+{
+ "pane_id": "1-1",
+ "text": "bun run dev",
+ "keys": ["Enter"]
+}
+```
+
+this sends text plus encoded keypresses in order within one request. when bracketed paste is enabled in the pane, the text portion is sent as a paste payload before the keys. use this when you need `text + Enter` to behave more like a real keypress sequence than `pane.send_text` with a literal trailing `\r`.
+
+`text` and `keys` are both optional, but at least one should usually be present.
+
+### `pane.report_agent`
+
+use this when an agent hook or plugin wants to report a semantic state directly over the socket api.
+
+params:
+
+```json
+{
+ "pane_id": "1-1",
+ "source": "custom:hermes",
+ "agent": "hermes",
+ "state": "working",
+ "message": "running tools",
+ "custom_status": "indexing"
+}
+```
+
+notes:
+
+- `source` is required and identifies the reporting integration instance
+- `agent` is required and may be any non-empty label string
+- built-in names like `pi` are normalized to their public label form
+- custom labels like `hermes` are accepted as-is
+- while this authority is active, the reported `agent` and `state` override heuristic display for that pane
+- process detection still owns pane liveness and fallback when hook authority is cleared or released
+- `message` is optional metadata for the reporting integration
+- `custom_status` is an optional short display label such as `scheduled`, `indexing`, or `stuck`
+- `custom_status` is visual-only; use `state` for semantic behavior like working or blocked
+- omitting `custom_status` clears any previous custom status from this reporting source
+
+returns `ok`.
+
+### `pane.clear_agent_authority`
+
+params:
+
+```json
+{
+ "pane_id": "1-1",
+ "source": "custom:hermes"
+}
+```
+
+notes:
+
+- `source` is optional
+- when `source` is omitted, any hook authority for that pane is cleared
+- when `source` is present, only that reporting source is cleared
+
+returns `ok`.
+
+### `pane.release_agent`
+
+use this when the reported agent is exiting cleanly and wants herdr to drop agent identity immediately instead of waiting for fallback detection.
+
+params:
+
+```json
+{
+ "pane_id": "1-1",
+ "source": "custom:hermes",
+ "agent": "hermes"
+}
+```
+
+notes:
+
+- `agent` uses the same non-empty label rules as `pane.report_agent`
+- this clears the pane's effective agent identity immediately when the source and label match the active authority
+- for built-in detected agents, herdr also applies its normal short reacquire suppression during graceful release
+
+returns `ok`.
+
+### `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`, `recent`, or `recent_unwrapped`
+- `lines` is optional
+- `timeout_ms` is optional
+- `strip_ansi` defaults to `true`
+- for `source = "recent"`, output matching uses unwrapped recent terminal text so soft wraps do not break matches
+- `source = "recent_unwrapped"` is also available on `pane.read` when you want to inspect the same unwrapped transcript directly
+- 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",
+ "tab_id": "1:1",
+ "source": "recent_unwrapped",
+ "text": "...server ready...",
+ "revision": 0,
+ "truncated": false
+ }
+ }
+}
+```
+
+## subscriptions
+
+`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.
+
+### subscription ack
+
+```json
+{
+ "id": "sub_1",
+ "result": {
+ "type": "subscription_started"
+ }
+}
+```
+
+### supported subscriptions
+
+base lifecycle subscriptions:
+
+- `workspace.created`
+- `workspace.closed`
+- `workspace.focused`
+- `tab.created`
+- `tab.closed`
+- `tab.focused`
+- `tab.renamed`
+- `pane.created`
+- `pane.closed`
+- `pane.focused`
+- `pane.exited`
+- `pane.agent_detected`
+
+parameterized subscriptions:
+
+- `pane.output_matched`
+- `pane.agent_status_changed`
+
+### 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
+{
+ "id": "sub_life",
+ "method": "events.subscribe",
+ "params": {
+ "subscriptions": [
+ { "type": "workspace.created" },
+ { "type": "workspace.focused" },
+ { "type": "tab.created" },
+ { "type": "tab.focused" },
+ { "type": "tab.renamed" },
+ { "type": "tab.closed" },
+ { "type": "pane.created" },
+ { "type": "pane.focused" },
+ { "type": "pane.agent_detected" },
+ { "type": "pane.closed" },
+ { "type": "workspace.closed" }
+ ]
+ }
+}
+```
+
+example pushed event:
+
+```json
+{
+ "event": "workspace_created",
+ "data": {
+ "workspace": {
+ "workspace_id": "1",
+ "number": 1,
+ "label": "herdr",
+ "focused": true,
+ "pane_count": 1,
+ "tab_count": 1,
+ "active_tab_id": "1:1",
+ "agent_status": "unknown"
+ }
+ }
+}
+```
+
+### example: subscribe to output matches and agent status changes
+
+request:
+
+```json
+{
+ "id": "sub_1",
+ "method": "events.subscribe",
+ "params": {
+ "subscriptions": [
+ {
+ "type": "pane.output_matched",
+ "pane_id": "1-1",
+ "source": "recent",
+ "lines": 200,
+ "match": { "type": "substring", "value": "ready" }
+ },
+ {
+ "type": "pane.agent_status_changed",
+ "pane_id": "1-1",
+ "agent_status": "done"
+ }
+ ]
+ }
+}
+```
+
+notes:
+
+- `pane.output_matched` supports `source`, optional `lines`, matcher config, and optional `strip_ansi`
+- `pane.agent_status_changed` accepts an optional `agent_status` filter; if omitted, any status transition for that pane can match
+
+example pushed `pane.output_matched` event:
+
+```json
+{
+ "event": "pane.output_matched",
+ "data": {
+ "pane_id": "1-1",
+ "matched_line": "server ready",
+ "read": {
+ "pane_id": "1-1",
+ "workspace_id": "1",
+ "tab_id": "1:1",
+ "source": "recent_unwrapped",
+ "text": "...server ready...",
+ "revision": 0,
+ "truncated": false
+ }
+ }
+}
+```
+
+example pushed `pane.agent_status_changed` event:
+
+```json
+{
+ "event": "pane.agent_status_changed",
+ "data": {
+ "pane_id": "1-1",
+ "workspace_id": "1",
+ "agent_status": "done",
+ "agent": "pi",
+ "custom_status": "scheduled"
+ }
+}
+```
+
+`agent` in pushed events follows the same rules as `pane_info.agent`: it may be a built-in detected name, a custom hook-reported label, or omitted. `custom_status` may be included when the pane has hook-owned custom status metadata at the time of the semantic status transition.
+## cli wrappers
+
+these commands provide the shell-facing control surface. most command groups talk to the local socket; `status client` only inspects the local executable.
+
+### command groups
+
+status commands:
+
+```text
+herdr status
+herdr status server
+herdr status client
+```
+
+`herdr -V` and `herdr --version` print the local executable version without contacting the server. `herdr status` compares that local executable with the running server when one is reachable.
+
+workspace commands:
+
+```text
+herdr workspace list
+herdr workspace create [--cwd PATH] [--label TEXT] [--focus] [--no-focus]
+herdr workspace get <workspace_id>
+herdr workspace focus <workspace_id>
+herdr workspace rename <workspace_id> <label>
+herdr workspace close <workspace_id>
+```
+
+tab commands:
+
+```text
+herdr tab list [--workspace <workspace_id>]
+herdr tab create [--workspace <workspace_id>] [--cwd PATH] [--label TEXT] [--focus] [--no-focus]
+herdr tab get <tab_id>
+herdr tab focus <tab_id>
+herdr tab rename <tab_id> <label>
+herdr tab close <tab_id>
+```
+
+pane commands:
+
+```text
+herdr pane list [--workspace <workspace_id>]
+herdr pane get <pane_id>
+herdr pane rename <pane_id> <label>|--clear
+herdr pane read <pane_id> [--source visible|recent|recent-unwrapped] [--lines N] [--format text|ansi] [--ansi]
+herdr pane split <pane_id> --direction right|down [--cwd PATH] [--focus] [--no-focus]
+herdr pane close <pane_id>
+herdr pane send-text <pane_id> <text>
+herdr pane send-keys <pane_id> <key> [key ...]
+herdr pane report-agent <pane_id> --source ID --agent LABEL --state idle|working|blocked|unknown [--message TEXT] [--custom-status TEXT] [--seq N]
+herdr pane run <pane_id> <command>
+```
+
+wait commands:
+
+```text
+herdr wait output <pane_id> --match <text> [--source visible|recent|recent-unwrapped] [--lines N] [--timeout MS] [--regex] [--raw]
+herdr wait agent-status <pane_id> --status <idle|working|blocked|done|unknown> [--timeout MS]
+```
+
+### cli behavior notes
+
+- `status` prints local client version/protocol, running server version/protocol when reachable, socket path, compatibility, and whether a restart is needed
+- `status server` prints only the running server side; if no server is reachable it exits successfully and prints `status: not running`
+- `status client` prints only the local executable version/protocol and binary path without contacting the server
+- `workspace create` keeps focus where it is by default; pass `--focus` to switch to the new workspace
+- `workspace create` without `--label` keeps the default cwd-based workspace naming
+- `workspace create --label` applies the custom workspace name immediately
+- `workspace create` returns `result.workspace`, `result.tab`, and `result.root_pane`
+- `tab create` keeps focus where it is by default; pass `--focus` to switch to the new tab
+- `tab create` without `--label` keeps the default numbered tab naming
+- `tab create --label` applies the custom tab name immediately
+- `tab create` returns `result.tab` and `result.root_pane`
+- `pane split` keeps focus where it is by default; pass `--focus` to switch to the new pane
+- `pane read` prints **text**, not json
+- `pane read --format ansi` and `pane read --ansi` print a rendered ANSI snapshot with colors/styles preserved
+- `pane read --source recent-unwrapped` returns recent terminal text with soft wraps joined back together
+- `pane send-text`, `pane send-keys`, `pane report-agent`, 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_input` with the command text followed by a real `Enter` keypress
+- `wait agent-status` is a cli convenience built on top of event subscriptions
+- use it when you want the same `done` / `idle` distinction the UI shows
+- `--raw` is a legacy alias for ANSI formatted `pane read` output and still disables ansi stripping for `wait output`
+- `wait output --source recent` matches against unwrapped recent terminal text by default, so pane width and soft wrapping do not break matches
+
+### cli examples
+
+create a workspace, split a pane, run a server, and wait for readiness:
+
+```bash
+herdr workspace create --cwd /path/to/project --label "api server"
+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 in the same user-facing sense the UI shows:
+
+```bash
+herdr wait agent-status 1-1 --status done --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`, use `pane.send_input` for ordered `text + keypress` input, or use cli `pane run`, which sends the text and then a real Enter key in one request.
+- `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 event transport.
+- the same herdr process can serve regular request/response calls and long-lived subscription connections at the same time.