aboutsummaryrefslogtreecommitdiff
path: root/docs/next
diff options
context:
space:
mode:
Diffstat (limited to 'docs/next')
-rw-r--r--docs/next/CHANGELOG.md10
-rw-r--r--docs/next/website/src/content/docs/cli-reference.mdx81
-rw-r--r--docs/next/website/src/content/docs/configuration.mdx11
-rw-r--r--docs/next/website/src/content/docs/index.mdx10
-rw-r--r--docs/next/website/src/content/docs/marketplace.mdx36
-rw-r--r--docs/next/website/src/content/docs/plugins.mdx189
-rw-r--r--docs/next/website/src/content/docs/socket-api.mdx247
7 files changed, 574 insertions, 10 deletions
diff --git a/docs/next/CHANGELOG.md b/docs/next/CHANGELOG.md
index c4271cf6..13e84347 100644
--- a/docs/next/CHANGELOG.md
+++ b/docs/next/CHANGELOG.md
@@ -3,6 +3,16 @@
## Unreleased
### Added
+- Added local plugin v1 support with `plugin.link/list/unlink/enable/disable`,
+ manifest-declared actions, event hooks, managed plugin panes, link handlers,
+ command logs, keybinding integration, and authoring docs under Preview docs.
+- Added `herdr plugin install <owner>/<repo>[/subdir...]`, `plugin uninstall`,
+ source metadata in `plugin.list`, offline registry fallback, and a
+ human-readable default `plugin list` with `--json` for scripts.
+- Added supporting plugin host APIs for `pane.current`, `pane.process_info`,
+ `client.window_title.set/clear`, `layout.export/apply`, plugin pane placement,
+ plugin invocation context/env injection, and plugin pane ownership across
+ `pane.move`.
- Added `pane.move` and `herdr pane move` to relocate a running pane into another tab, a new tab, or a new workspace without restarting its terminal process. (#299)
### Changed
diff --git a/docs/next/website/src/content/docs/cli-reference.mdx b/docs/next/website/src/content/docs/cli-reference.mdx
index fc6be710..106823b3 100644
--- a/docs/next/website/src/content/docs/cli-reference.mdx
+++ b/docs/next/website/src/content/docs/cli-reference.mdx
@@ -69,7 +69,7 @@ Use `default` as the session name when you need to stop the default session expl
```bash
herdr workspace list
-herdr workspace create [--cwd PATH] [--label TEXT] [--focus] [--no-focus]
+herdr workspace create [--cwd PATH] [--label TEXT] [--env KEY=VALUE] [--focus] [--no-focus]
herdr workspace get <workspace_id>
herdr workspace focus <workspace_id>
herdr workspace rename <workspace_id> <label>
@@ -99,7 +99,7 @@ Worktrees are normal Herdr workspaces with Git checkout provenance. `worktree cr
```bash
herdr tab list [--workspace <workspace_id>]
-herdr tab create [--workspace <workspace_id>] [--cwd PATH] [--label TEXT] [--focus] [--no-focus]
+herdr tab create [--workspace <workspace_id>] [--cwd PATH] [--label TEXT] [--env KEY=VALUE] [--focus] [--no-focus]
herdr tab get <tab_id>
herdr tab focus <tab_id>
herdr tab rename <tab_id> <label>
@@ -110,15 +110,17 @@ herdr tab close <tab_id>
```bash
herdr pane list [--workspace <workspace_id>]
+herdr pane current [--pane ID|--current]
herdr pane get <pane_id>
herdr pane layout [--pane ID|--current]
+herdr pane process-info [--pane ID|--current]
herdr pane neighbor --direction left|right|up|down [--pane ID|--current]
herdr pane edges [--pane ID|--current]
herdr pane focus --direction left|right|up|down [--pane ID|--current]
herdr pane resize --direction left|right|up|down [--amount FLOAT] [--pane ID|--current]
herdr pane zoom [<pane_id>|--pane ID|--current] [--toggle|--on|--off]
herdr pane rename <pane_id> <label>|--clear
-herdr pane split [<pane_id>|--pane ID|--current] --direction right|down [--ratio FLOAT] [--cwd PATH] [--focus] [--no-focus]
+herdr pane split [<pane_id>|--pane ID|--current] --direction right|down [--ratio FLOAT] [--cwd PATH] [--env KEY=VALUE] [--focus] [--no-focus]
herdr pane swap --direction left|right|up|down [--pane ID|--current]
herdr pane swap --source-pane ID --target-pane ID
herdr pane move <pane_id> --tab <tab_id> --split right|down [--target-pane ID] [--ratio FLOAT] [--focus|--no-focus]
@@ -181,6 +183,10 @@ herdr pane report-metadata <pane_id> \
`STATUS` is one of `idle`, `working`, `blocked`, `done`, or `unknown`. `--agent` is a guard for the authoritative agent label. `--applies-to-source` is a guard for the active lifecycle authority source. Use `--display-agent` to change the visible name.
+Metadata text is normalized before storage. Herdr trims surrounding whitespace, removes control characters, caps `--custom-status` at 32 characters, and caps `--title`, `--display-agent`, and each `--state-label` value at 80 characters. Empty normalized values are ignored.
+
+`--source` and `--applies-to-source` must be 80 characters or fewer and may contain only ASCII letters, digits, colon, dot, underscore, and hyphen. `--ttl-ms` makes metadata expire automatically and must be between `1` and `86400000` milliseconds. Omit it for metadata that should stay until replaced, cleared, or the pane closes. `--seq` lets Herdr ignore stale reports from the same `--source`; stale reports are accepted by the API but ignored by pane state.
+
## Agents
```bash
@@ -192,7 +198,7 @@ herdr agent rename <target> <name>|--clear
herdr agent focus <target>
herdr agent wait <target> --status <idle|working|blocked|unknown> [--timeout MS]
herdr agent attach <target> [--takeover]
-herdr agent start <name> [--cwd PATH] [--workspace ID] [--tab ID] [--split right|down] [--focus|--no-focus] -- <argv...>
+herdr agent start <name> [--cwd PATH] [--workspace ID] [--tab ID] [--split right|down] [--env KEY=VALUE] [--focus|--no-focus] -- <argv...>
herdr agent explain <target> [--json|--verbose]
herdr agent explain --file PATH --agent LABEL [--json|--verbose]
```
@@ -209,9 +215,12 @@ Use `pane send-text`, `pane send-keys`, `pane run`, and `terminal attach` for or
```bash
herdr terminal attach <terminal_id> [--takeover]
+herdr terminal title set <title>
+herdr terminal title clear
```
Detach from direct attach with `ctrl+b q`. Send literal `ctrl+b` with `ctrl+b ctrl+b`.
+`terminal title clear` restores Herdr's default outer terminal window title.
## Waits
@@ -259,6 +268,66 @@ herdr integration uninstall cursor
herdr integration status [--outdated-only]
```
+## Plugins
+
+Plugin commands install and run local executable workflow plugins. A plugin is a manifest plus out-of-process commands; Herdr owns the host surface and plugins own their implementation language.
+
+Install, list, and remove plugins:
+
+```bash
+herdr plugin install <owner>/<repo>[/subdir...] [--ref REF] [--yes]
+herdr plugin list [--plugin ID] [--json]
+herdr plugin uninstall <plugin_id|owner/repo[/subdir...]>
+herdr plugin enable <plugin_id>
+herdr plugin disable <plugin_id>
+```
+
+`plugin install` accepts GitHub shorthand only, such as `ogulcancelik/herdr-plugin-examples/worktree-bootstrap`. It uses `git`, shows a trust preview in interactive terminals, runs supported manifest build commands, and stores GitHub installs in a Herdr-managed directory. Use `--yes` for noninteractive installs. Reinstalling a GitHub-managed plugin replaces that managed checkout. Installing over a locally linked plugin is refused. `plugin list` is human-readable by default; pass `--json` for the raw API response.
+
+Local development:
+
+```bash
+herdr plugin link <path> [--disabled]
+herdr plugin unlink <plugin_id>
+```
+
+`plugin link` accepts a plugin directory containing `herdr-plugin.toml` or a direct manifest path. It is still the right command while authoring or testing a plugin from a local checkout. `plugin unlink` unregisters the plugin and leaves files alone. `plugin uninstall` unregisters a plugin and also removes Herdr-managed GitHub checkout files. For GitHub installs, uninstall accepts either the plugin id or the same `owner/repo[/subdir...]` shorthand used by install. Actions, event hooks, panes, and link handlers are declared in the manifest; runtime action registration is not part of v1.
+
+Actions:
+
+```bash
+herdr plugin action list [--plugin ID]
+herdr plugin action invoke <action_id> [--plugin ID]
+```
+
+`plugin action invoke` starts the manifest command for an installed, enabled,
+platform-compatible plugin action and prints the started command log record in
+the JSON response. Use the qualified action id (`plugin.id.action`) when more
+than one plugin uses the same action id. Local action ids cannot contain dots,
+so qualified ids remain unambiguous even when plugin ids contain dots.
+
+Logs:
+
+```bash
+herdr plugin log list [--plugin ID] [--limit N]
+```
+
+Managed terminal panes:
+
+```bash
+herdr plugin pane open --plugin ID --entrypoint ID [--placement overlay|split|tab|zoomed] [--workspace ID] [--target-pane PANE] [--direction right|down] [--cwd PATH] [--env KEY=VALUE] [--focus|--no-focus]
+herdr plugin pane focus <pane_id>
+herdr plugin pane close <pane_id>
+```
+
+`plugin pane open` requires the plugin to be linked, enabled, and compatible
+with the current platform. It starts a manifest-declared `[[panes]]` command as
+a Herdr-managed terminal pane. The manifest default is `overlay`, which opens a
+temporary zoomed overlay over the active pane. It can also open as a split, a
+new tab, or a zoomed pane. Native non-terminal plugin panes are a later surface.
+
+`--env KEY=VALUE` can be repeated on process-launching commands. It applies to the newly launched process only. Herdr-managed variables such as `HERDR_SOCKET_PATH`, `HERDR_BIN_PATH`, `HERDR_ENV`, `HERDR_WORKSPACE_ID`, `HERDR_TAB_ID`, `HERDR_PANE_ID`, `HERDR_PLUGIN_ID`, `HERDR_PLUGIN_ROOT`, `HERDR_PLUGIN_CONFIG_DIR`, `HERDR_PLUGIN_STATE_DIR`, `HERDR_PLUGIN_ENTRYPOINT_ID`, and `HERDR_PLUGIN_CONTEXT_JSON` stay authoritative when they conflict with caller-provided env.
+
## Read sources
| Source | Meaning |
@@ -275,5 +344,9 @@ herdr integration status [--outdated-only]
| `HERDR_CONFIG_PATH` | Override the config file path. |
| `HERDR_SESSION` | Select a named session for CLI commands. |
| `HERDR_SOCKET_PATH` | Low-level socket path override. |
+| `HERDR_ENV` | Set to `1` inside Herdr-managed pane processes. |
+| `HERDR_PANE_ID` | Public pane id for the running pane process. |
+| `HERDR_TAB_ID` | Public tab id for the running pane process. |
+| `HERDR_WORKSPACE_ID` | Public workspace id for the running pane process. |
| `HERDR_LOG` | Set log filter, for example `HERDR_LOG=herdr=debug`. |
| `HERDR_DISABLE_SOUND` | Disable sound playback even when sound notifications are enabled. |
diff --git a/docs/next/website/src/content/docs/configuration.mdx b/docs/next/website/src/content/docs/configuration.mdx
index c22e0107..8fc35afb 100644
--- a/docs/next/website/src/content/docs/configuration.mdx
+++ b/docs/next/website/src/content/docs/configuration.mdx
@@ -224,6 +224,17 @@ description = "run lazygit"
`type = "shell"` runs detached in the background.
+`type = "plugin_action"` invokes an installed plugin action id. Use the
+qualified id when action ids are not globally unique:
+
+```toml
+[[keys.command]]
+key = "prefix+l"
+type = "plugin_action"
+command = "example.layout.apply"
+description = "apply layout"
+```
+
An optional `description` can be provided. When specified, this description is displayed in the keybind help panel (opened with `prefix+?`) in place of the default `'custom command'` label.
Custom commands receive `HERDR_SOCKET_PATH`, `HERDR_BIN_PATH`, `HERDR_ACTIVE_WORKSPACE_ID`, `HERDR_ACTIVE_TAB_ID`, `HERDR_ACTIVE_PANE_ID`, and `HERDR_ACTIVE_PANE_CWD` when those values are available. Shell commands run from the focused pane's working directory when Herdr can detect it.
diff --git a/docs/next/website/src/content/docs/index.mdx b/docs/next/website/src/content/docs/index.mdx
index 68e1d869..1502462d 100644
--- a/docs/next/website/src/content/docs/index.mdx
+++ b/docs/next/website/src/content/docs/index.mdx
@@ -64,4 +64,14 @@ The guide teaches your agent Herdr's concepts, setup, configuration, and common
[Read the API guide →](/docs/socket-api/)
</Card>
+ <Card title="Plugins">
+ Author local executable workflow plugins with manifest actions and event hooks.
+
+ [Write a plugin →](/docs/plugins/)
+ </Card>
+ <Card title="Marketplace">
+ Share plugins from GitHub today and tag your repo to be listed when the marketplace launches.
+
+ [Publish a plugin →](/docs/marketplace/)
+ </Card>
</CardGrid>
diff --git a/docs/next/website/src/content/docs/marketplace.mdx b/docs/next/website/src/content/docs/marketplace.mdx
new file mode 100644
index 00000000..e06a1f79
--- /dev/null
+++ b/docs/next/website/src/content/docs/marketplace.mdx
@@ -0,0 +1,36 @@
+---
+title: Marketplace
+description: Publish and share Herdr plugins, and get listed when the marketplace launches.
+---
+
+The Herdr plugin marketplace is a discoverable index of installable plugins. It
+is not live yet. This page is how to be ready for it.
+
+## Share a plugin today
+
+The marketplace adds discovery on top of installation; it does not replace it.
+Anyone can already install your plugin straight from GitHub:
+
+```bash
+herdr plugin install owner/repo[/subdir...]
+```
+
+Publish a normal public GitHub repository with a `herdr-plugin.toml` manifest at
+its root, or in a subdirectory, and that command works now. See
+[Plugins](/docs/plugins/) for the manifest and authoring reference.
+
+## Get listed when it launches
+
+Add the GitHub topic `herdr-plugin` to your repository. That topic is the exact
+signal the future index will use for discovery, so tagging now means your plugin
+is indexed on day one, with no migration and no resubmission.
+
+## What a listing will show
+
+A marketplace card will surface your plugin's `id`, `name`, `description`,
+declared `platforms`, and a link back to the source repository. Getting that
+manifest metadata right today is what makes your listing accurate later. Declare
+`platforms` honestly, because the index will use it to show where a plugin runs.
+
+Plugins stay ordinary GitHub repositories. The marketplace is a discovery and
+trust layer on top of the same `herdr plugin install` path you can use now.
diff --git a/docs/next/website/src/content/docs/plugins.mdx b/docs/next/website/src/content/docs/plugins.mdx
new file mode 100644
index 00000000..652b44a0
--- /dev/null
+++ b/docs/next/website/src/content/docs/plugins.mdx
@@ -0,0 +1,189 @@
+---
+title: Plugins
+description: Author local Herdr plugins with manifest actions, event hooks, and panes.
+---
+
+Herdr plugins are shareable, executable workflow packages. A plugin can be a
+Bash script, JavaScript app, Lua script, Rust binary, or any other argv command
+your machine can run. Herdr owns the host surface: installation, manifest
+validation, keybindings, terminal panes, events, invocation context, and socket
+access. The plugin owns its implementation language, dependencies, files, and
+durable state.
+
+A plugin directory contains `herdr-plugin.toml`. Herdr installs or links that
+manifest, then runs declared action, event, and pane commands out of process.
+The manifest is the contract. Runtime action registration and native
+non-terminal plugin UI are not part of plugin v1.
+
+## Manifest
+
+```toml
+id = "example.layout"
+name = "Layout"
+version = "0.1.0"
+description = "Apply project layouts"
+platforms = ["linux", "macos", "windows"]
+
+[[build]]
+command = ["npm", "ci"]
+
+[[build]]
+command = ["npm", "run", "build"]
+platforms = ["linux", "macos"]
+
+[[actions]]
+id = "apply"
+title = "Apply layout"
+contexts = ["workspace"]
+command = ["node", "dist/apply.js"]
+
+[[events]]
+on = "worktree.created"
+command = ["herdr", "workspace", "list"]
+
+[[panes]]
+id = "board"
+title = "Project board"
+placement = "overlay"
+command = ["herdr-board"]
+
+[[link_handlers]]
+id = "github-issue"
+title = "Open GitHub issue"
+pattern = "^https://github\\.com/[^/]+/[^/]+/(issues|pull)/[0-9]+$"
+action = "apply"
+```
+
+Actions are manifest-only. Pane commands are manifest-only too;
+`plugin.pane.open` selects a declared `[[panes]]` entrypoint and does not
+accept runtime argv. Action ids, pane ids, and link handler ids are local ids
+inside the plugin; they may use ASCII letters, digits, colon, underscore, and
+hyphen, but not dots. Each id type must be unique inside a plugin.
+
+Use `platforms` to declare where the plugin can run. Local plugins without
+`platforms` link with a warning.
+
+## Install and link
+
+Install an example plugin:
+
+```bash
+herdr plugin install ogulcancelik/herdr-plugin-examples/agent-telegram-notify
+herdr plugin list
+herdr plugin action list --plugin examples.agent-telegram-notify
+```
+
+When you are authoring a local plugin, link the working directory instead:
+
+```bash
+herdr plugin link /path/to/plugin
+herdr plugin action list --plugin example.layout
+herdr plugin action invoke example.layout.apply
+herdr plugin pane open --plugin example.layout --entrypoint board
+herdr plugin log list --plugin example.layout
+```
+
+`plugin install` accepts GitHub shorthand only, such as
+`owner/repo/subdir`. It clones with `git`, shows a preview in interactive
+terminals, runs supported build commands, then stores the checkout under
+Herdr-managed plugin data and registers it. Use `--yes` for noninteractive
+installs. Reinstalling a GitHub-managed plugin replaces that managed checkout.
+Installing over a locally linked plugin is refused; unlink or uninstall the
+local plugin first.
+
+`plugin uninstall <id-or-source>` unregisters the plugin. For GitHub-managed
+installs it also removes the managed checkout, and it accepts either the plugin
+id or the same `owner/repo[/subdir...]` shorthand used by install.
+`plugin unlink <id>` only unregisters a plugin and leaves files alone, which is
+useful for local development. There is no separate `plugin update` in v1;
+reinstall from GitHub to refresh a managed plugin.
+
+The example cookbook repo is `ogulcancelik/herdr-plugin-examples`. It contains
+separate example plugins in subdirectories, including `agent-telegram-notify`,
+`github-link-preview`, and `dev-layout-bootstrap`. These are examples to copy,
+not maintained official plugins.
+
+## Build commands
+
+Build commands run during GitHub `plugin install` after confirmation and before
+Herdr registers the plugin. If a build command fails, install aborts and the
+plugin is not registered. `plugin link` does not run build commands; local
+authors build their working tree themselves. Build commands may generate files,
+but changing `herdr-plugin.toml` after the install preview aborts install. Build
+failures show the plugin id, build index, working directory, command, exit
+status or spawn error, and capped stdout/stderr without interpreting tool output.
+
+Build commands are plain argv commands too, but they do not receive runtime
+plugin context or Herdr socket env. Plugin authors should document required
+system tools such as `cargo`, `npm`, `bun`, or `lua`; Herdr reports build
+failures but does not install missing toolchains.
+
+## Commands and environment
+
+Runtime commands run with the plugin directory as their working directory. Herdr
+injects `HERDR_SOCKET_PATH`, `HERDR_BIN_PATH`, `HERDR_ENV=1`,
+`HERDR_PLUGIN_ID`, `HERDR_PLUGIN_ROOT`, `HERDR_PLUGIN_CONFIG_DIR`,
+`HERDR_PLUGIN_STATE_DIR`, `HERDR_PLUGIN_CONTEXT_JSON`, and any available
+`HERDR_WORKSPACE_ID`, `HERDR_TAB_ID`, and `HERDR_PANE_ID`. Action commands also
+receive `HERDR_PLUGIN_ACTION_ID`; event hooks receive `HERDR_PLUGIN_EVENT` and
+`HERDR_PLUGIN_EVENT_JSON`; pane commands receive `HERDR_PLUGIN_ENTRYPOINT_ID`.
+
+`HERDR_PLUGIN_ROOT` is the installed or linked plugin directory. Do not store
+user credentials or durable state there, because GitHub-installed plugin roots
+are managed source checkouts. Put user-editable config such as `.env` files
+under `HERDR_PLUGIN_CONFIG_DIR`, and put local runtime state under
+`HERDR_PLUGIN_STATE_DIR`. Herdr does not read, create, migrate, validate, sync,
+or delete their contents. The plugin owns the file format and lifecycle.
+
+`HERDR_PLUGIN_CONTEXT_JSON` can include workspace, tab, focused pane, worktree,
+agent, selected text, clicked URL, and link handler fields when they are
+available for that invocation. Shell plugins can read the individual env vars
+for common ids, or parse the context JSON for the full shape.
+
+## Panes
+
+Manifest pane `placement` defaults to `overlay`, which opens a temporary zoomed
+overlay over the active pane and restores the previous focus and zoom when it
+closes. A `plugin.pane.open` request can override the manifest placement with
+`overlay`, `split`, `tab`, or `zoomed`.
+
+Plugin panes are normal Herdr panes after they open. Plugins can call standard
+pane APIs such as `pane.move`, `pane.swap`, `pane.resize`, and `pane.zoom`
+through the socket or CLI; Herdr keeps plugin pane ownership attached to the
+underlying pane when it moves across tabs or workspaces.
+
+## Keybindings
+
+Bind a key to an installed plugin action:
+
+```toml
+[[keys.command]]
+key = "prefix+l"
+type = "plugin_action"
+command = "example.layout.apply"
+description = "apply layout"
+```
+
+## Link handlers
+
+Use `[[link_handlers]]` to route modified clicks on matching terminal URLs to a
+plugin action instead of opening the URL in the browser. The modified-click
+modifier is Control on Linux and Windows, and Command/Super on macOS. `pattern`
+is a Rust regular expression matched against the clicked URL, and `action` must
+name an action declared by the same plugin. Link handler actions receive
+`invocation_source = "link_click"`, `clicked_url`, and `link_handler_id` in
+`HERDR_PLUGIN_CONTEXT_JSON`; shell plugins can also read
+`HERDR_PLUGIN_CLICKED_URL` and `HERDR_PLUGIN_LINK_HANDLER_ID`. Handlers are
+checked in manifest order inside each plugin.
+
+## Storage
+
+There is no Herdr-managed plugin storage API in v1. Plugins that need durable
+state should own their files or database.
+
+## Marketplace
+
+A plugin marketplace is coming. You can already publish a GitHub repository and
+share it with `herdr plugin install owner/repo[/subdir]`. Tag your repo with the
+GitHub topic `herdr-plugin` now to be listed when the marketplace launches. See
+[Marketplace](/docs/marketplace/) for how publishing and discovery will work.
diff --git a/docs/next/website/src/content/docs/socket-api.mdx b/docs/next/website/src/content/docs/socket-api.mdx
index fb124ac9..4f8071ad 100644
--- a/docs/next/website/src/content/docs/socket-api.mdx
+++ b/docs/next/website/src/content/docs/socket-api.mdx
@@ -82,13 +82,16 @@ Raw socket method names use dot notation:
| --- | --- |
| Server | `ping`, `server.stop`, `server.reload_config`, `server.agent_manifests`, `server.reload_agent_manifests` |
| Notification | `notification.show` |
+| Client | `client.window_title.set`, `client.window_title.clear` |
| Workspace | `workspace.create`, `workspace.list`, `workspace.get`, `workspace.focus`, `workspace.rename`, `workspace.close` |
| Worktree | `worktree.list`, `worktree.create`, `worktree.open`, `worktree.remove` |
| Tab | `tab.create`, `tab.list`, `tab.get`, `tab.focus`, `tab.rename`, `tab.close` |
-| Pane | `pane.split`, `pane.swap`, `pane.move`, `pane.zoom`, `pane.layout`, `pane.neighbor`, `pane.edges`, `pane.focus_direction`, `pane.resize`, `pane.list`, `pane.get`, `pane.rename`, `pane.send_text`, `pane.send_keys`, `pane.send_input`, `pane.read`, `pane.report_agent`, `pane.report_agent_session`, `pane.report_metadata`, `pane.clear_agent_authority`, `pane.release_agent`, `pane.close`, `pane.wait_for_output` |
+| Pane | `pane.split`, `pane.swap`, `pane.move`, `pane.zoom`, `pane.layout`, `pane.process_info`, `pane.neighbor`, `pane.edges`, `pane.focus_direction`, `pane.resize`, `pane.list`, `pane.current`, `pane.get`, `pane.rename`, `pane.send_text`, `pane.send_keys`, `pane.send_input`, `pane.read`, `pane.report_agent`, `pane.report_agent_session`, `pane.report_metadata`, `pane.clear_agent_authority`, `pane.release_agent`, `pane.close`, `pane.wait_for_output` |
+| Layout | `layout.export`, `layout.apply` |
| Agent | `agent.list`, `agent.get`, `agent.read`, `agent.explain`, `agent.send`, `agent.rename`, `agent.focus`, `agent.start` |
| Events | `events.subscribe`, `events.wait` |
| Integrations | `integration.install`, `integration.uninstall` |
+| Plugins | `plugin.link`, `plugin.list`, `plugin.unlink`, `plugin.enable`, `plugin.disable`, `plugin.action.list`, `plugin.action.invoke`, `plugin.log.list`, `plugin.pane.open`, `plugin.pane.focus`, `plugin.pane.close` |
Some CLI commands are conveniences around these methods. For example, `herdr agent wait` resolves an agent target and then subscribes to pane agent state events.
@@ -97,20 +100,83 @@ schema makes `pane_id` optional use the server's active focused pane when it is
omitted. `pane.move` always requires the source `pane_id`.
```json
+{"id":"req_current","method":"pane.current","params":{"caller_pane_id":"w1:p1"}}
{"id":"req_layout","method":"pane.layout","params":{"pane_id":"w1:p1"}}
{"id":"req_neighbor","method":"pane.neighbor","params":{"pane_id":"w1:p1","direction":"right"}}
{"id":"req_edges","method":"pane.edges","params":{"pane_id":"w1:p1"}}
{"id":"req_focus","method":"pane.focus_direction","params":{"direction":"right"}}
{"id":"req_resize","method":"pane.resize","params":{"pane_id":"w1:p1","direction":"right","amount":0.1}}
{"id":"req_zoom","method":"pane.zoom","params":{"pane_id":"w1:p1","mode":"toggle"}}
-{"id":"req_split","method":"pane.split","params":{"direction":"right","ratio":0.333}}
+{"id":"req_split","method":"pane.split","params":{"direction":"right","ratio":0.333,"env":{"HERDR_ROLE":"tests"}}}
+{"id":"req_process","method":"pane.process_info","params":{"pane_id":"w1:p1"}}
```
+`pane.current` returns a single `PaneInfo`. When `caller_pane_id` is present,
+Herdr returns that pane. When it is omitted, Herdr returns the active focused
+pane.
+
`pane.layout` returns the tab layout snapshot with `workspace_id`, `tab_id`,
`zoomed`, outer `area`, `focused_pane_id`, pane rects, and split rects/ratios.
`pane.neighbor` and `pane.edges` include that same layout snapshot so clients
can make the next decision without private layout state.
+`pane.process_info` returns the pane's shell pid, foreground process group id
+when available, and foreground processes with pid, name, argv/cmdline, and cwd
+when the platform exposes them.
+
+`layout.export` returns a portable tab layout tree. Omit `tab_id` and `pane_id`
+to export the active tab, pass `tab_id` to export that tab, or pass `pane_id` to
+export the tab containing that pane.
+
+```json
+{"id":"req_export","method":"layout.export","params":{"tab_id":"w1:t1"}}
+```
+
+The response includes `workspace_id`, `tab_id`, `zoomed`, `focused_pane_id`, and
+`root`. `root` is a BSP tree of `pane` and `split` nodes. Pane nodes can include
+`pane_id`, `label`, `cwd`, and argv `command`. Split nodes use
+`direction` (`right` or `down`), `ratio`, `first`, and `second`.
+
+`layout.apply` creates a fresh tab from a declarative tree. If `tab_id` is
+provided, Herdr creates the replacement tab first and then closes the old tab.
+This restores structure, labels, cwd, env, and optional argv commands; it does
+not preserve live PTYs, scrollback, or running processes.
+
+```json
+{
+ "id": "req_apply",
+ "method": "layout.apply",
+ "params": {
+ "workspace_id": "wabc",
+ "tab_label": "dev",
+ "focus": true,
+ "root": {
+ "type": "split",
+ "direction": "right",
+ "ratio": 0.65,
+ "first": {
+ "type": "pane",
+ "label": "editor",
+ "cwd": "/repo"
+ },
+ "second": {
+ "type": "pane",
+ "label": "tests",
+ "cwd": "/repo",
+ "command": ["sh", "-c", "just test"],
+ "env": { "HERDR_ROLE": "tests" }
+ }
+ }
+ }
+}
+```
+
+Process-launching methods accept an `env` object. Herdr applies those key/value
+pairs to the newly launched process only. Herdr also injects `HERDR_SOCKET_PATH`,
+`HERDR_ENV=1`, `HERDR_WORKSPACE_ID`, `HERDR_TAB_ID`, and `HERDR_PANE_ID` into
+managed pane processes. Herdr-managed variables are authoritative when they
+conflict with caller-provided env.
+
`pane.swap` supports directional and explicit forms:
```json
@@ -185,6 +251,17 @@ The response reports whether anything was shown:
Possible reasons are `shown`, `disabled`, `rate_limited`, `no_foreground_client`, and `busy`. `disabled` means `ui.toast.delivery = "off"`. `busy` means an existing in-app toast was not replaced. Terminal and system delivery are best-effort through the current foreground attached Herdr client.
+Set or clear the foreground client's outer terminal window title:
+
+```json
+{"id":"req_title","method":"client.window_title.set","params":{"title":"herdr api"}}
+{"id":"req_title_clear","method":"client.window_title.clear","params":{}}
+```
+
+`client.window_title.clear` restores Herdr's default title. The response is
+`type: "client_window_title"` with `changed` and reason `set`, `cleared`, or
+`no_foreground_client`.
+
Worktree methods manage Git checkouts as Herdr workspaces. `worktree.create` creates a checkout and returns the new `workspace`, `tab`, `root_pane`, and `worktree` records. `worktree.open` opens an existing checkout or returns the already-open workspace. `worktree.remove` runs `git worktree remove` against a linked child workspace and never deletes the branch.
Create a worktree from a source workspace:
@@ -207,6 +284,154 @@ Remove a linked checkout:
Use at most one of `workspace_id` or `cwd` for `worktree.list`, `worktree.create`, and `worktree.open`; omit both to use the active workspace. Use exactly one of `path` or `branch` for `worktree.open`. Raw socket `cwd` and `path` values must be absolute; the CLI expands relative `--cwd` and `--path` values before sending requests. Workspace responses include optional `worktree` provenance when a workspace belongs to a Herdr worktree group. Worktree commands can emit `workspace.updated` when an existing workspace gains or changes worktree provenance.
+Worktree commands also emit lifecycle events. `worktree.create` emits `workspace.created`, `tab.created`, `pane.created`, and `worktree.created`. `worktree.open` emits `worktree.opened`, and it also emits workspace/tab/pane creation events when it opens a new Herdr workspace. `worktree.remove` emits `worktree.removed`; if the linked workspace is still open, it also emits `workspace.closed`.
+
+## Plugin APIs
+
+The plugin API is an early host surface for executable workflow tools. A plugin
+is a package with a `herdr-plugin.toml` manifest. The manifest declares
+shareable actions, event hooks, terminal pane entrypoints, and link handlers.
+Actions and panes are manifest-only; runtime action registration and runtime
+argv pane creation are not part of v1.
+
+Installed and linked plugins persist across restarts. Herdr writes a
+`plugins.json` registry file alongside `session.json` on `plugin.link`,
+`plugin.unlink`, `plugin.enable`, and `plugin.disable`. The
+`herdr plugin install` CLI also writes the same registry when Herdr is not
+running, then startup loads it automatically. On startup, Herdr re-reads each
+manifest from its original path; if the file is missing or unparseable the entry
+is kept with a `warnings` field so `plugin.list` surfaces it.
+
+Event hook `on` values are validated against the known Herdr event names at link time. An unrecognised name is not an error — the link still succeeds — but the returned plugin info includes a warning (e.g. `"unknown event 'worktree.craeted'"`). Check the `warnings` field in the `plugin.link` and `plugin.list` responses.
+
+Link a local plugin manifest:
+
+```json
+{"id":"req_plugin_link","method":"plugin.link","params":{"path":"/path/to/plugin","enabled":true}}
+```
+
+`plugin.link` also accepts optional `source` metadata. The CLI uses this when it installs from GitHub so `plugin.list` can show origin, requested ref, resolved commit, and managed checkout path:
+
+```json
+{"id":"req_plugin_link","method":"plugin.link","params":{"path":"/managed/plugin/herdr-plugin.toml","enabled":true,"source":{"kind":"github","owner":"ogulcancelik","repo":"herdr-plugin-examples","subdir":"worktree-bootstrap","requested_ref":"main","resolved_commit":"abc123","managed_path":"/data/plugins/github/example.worktree-bootstrap","installed_unix_ms":1780000000000}}}
+```
+
+The path can be a plugin directory containing `herdr-plugin.toml` or a direct
+manifest path. The manifest shape is:
+
+```toml
+id = "example.worktree-bootstrap"
+name = "Worktree Bootstrap"
+version = "0.1.0"
+description = "Prepare new worktrees"
+platforms = ["linux", "macos"]
+
+[[build]]
+command = ["bun", "install"]
+
+[[actions]]
+id = "bootstrap"
+title = "Bootstrap worktree"
+contexts = ["workspace"]
+command = ["bun", "run", "bootstrap.ts"]
+
+[[actions]]
+id = "bootstrap-win"
+title = "Bootstrap worktree (Windows)"
+contexts = ["workspace"]
+platforms = ["windows"]
+command = ["powershell.exe", "-ExecutionPolicy", "Bypass", "-File", "bootstrap.ps1"]
+
+[[events]]
+on = "worktree.created"
+command = ["bun", "run", "bootstrap.ts"]
+
+[[panes]]
+id = "board"
+title = "Worktree board"
+placement = "overlay"
+command = ["bun", "run", "board.ts"]
+
+[[link_handlers]]
+id = "github-issue"
+title = "Open GitHub issue"
+pattern = "^https://github\\.com/[^/]+/[^/]+/(issues|pull)/[0-9]+$"
+action = "bootstrap"
+```
+
+Declare `platforms` at the top level with the OS identifiers (`linux`, `macos`,
+`windows`) your plugin supports. Omitting `platforms` is allowed for local
+development — `plugin.link` succeeds but the response includes a warning.
+Individual build commands, actions, event hooks, panes, and link handlers can
+declare their own `platforms` to override the plugin-level list; if omitted they
+inherit from the plugin. Invoking an action or opening a pane whose effective
+platforms do not include the current OS returns a `platform_unsupported` error.
+
+List, enable, disable, or unlink linked plugins:
+
+```json
+{"id":"req_plugin_list","method":"plugin.list","params":{}}
+{"id":"req_plugin_disable","method":"plugin.disable","params":{"plugin_id":"example.worktree-bootstrap"}}
+{"id":"req_plugin_enable","method":"plugin.enable","params":{"plugin_id":"example.worktree-bootstrap"}}
+{"id":"req_plugin_unlink","method":"plugin.unlink","params":{"plugin_id":"example.worktree-bootstrap"}}
+```
+
+Actions are resolved from the linked manifest. `plugin.action.list` returns all actions across installed plugins; pass `plugin_id` to filter.
+
+```json
+{"id":"req_plugin_actions","method":"plugin.action.list","params":{}}
+{"id":"req_plugin_actions_filtered","method":"plugin.action.list","params":{"plugin_id":"example.worktree-bootstrap"}}
+```
+
+Invoke an action by its qualified id or bare action id:
+
+```json
+{"id":"req_plugin_invoke","method":"plugin.action.invoke","params":{"action_id":"example.worktree-bootstrap.bootstrap","context":{"invocation_source":"keybinding"}}}
+```
+
+`plugin.action.invoke` resolves the manifest action, starts the manifest
+command, and returns the Herdr-built invocation context plus the started command
+log record. Missing context fields are filled from the active workspace, tab,
+focused pane, worktree provenance, and request id. Invoking an action from a
+disabled plugin returns a `plugin_disabled` error.
+
+Herdr injects `HERDR_SOCKET_PATH`, `HERDR_BIN_PATH`, `HERDR_ENV=1`,
+`HERDR_PLUGIN_ID`, `HERDR_PLUGIN_ROOT`, `HERDR_PLUGIN_CONFIG_DIR`,
+`HERDR_PLUGIN_STATE_DIR`, `HERDR_PLUGIN_CONTEXT_JSON`, and available
+`HERDR_WORKSPACE_ID`, `HERDR_TAB_ID`, and `HERDR_PANE_ID` values. Action
+commands also receive `HERDR_PLUGIN_ACTION_ID`; event hooks receive
+`HERDR_PLUGIN_EVENT` and `HERDR_PLUGIN_EVENT_JSON`; pane commands receive
+`HERDR_PLUGIN_ENTRYPOINT_ID`.
+
+List recent action and event command logs:
+
+```json
+{"id":"req_plugin_logs","method":"plugin.log.list","params":{"plugin_id":"example.worktree-bootstrap","limit":20}}
+```
+
+Event hooks run for enabled installed plugins when Herdr emits a matching event
+name such as `worktree.created`.
+
+There is no Herdr-managed plugin storage API in v1. `HERDR_PLUGIN_CONFIG_DIR`
+and `HERDR_PLUGIN_STATE_DIR` are path discovery only; plugins own their files,
+schemas, migrations, and cleanup.
+
+Open a managed terminal UI:
+
+```json
+{"id":"req_plugin_pane","method":"plugin.pane.open","params":{"plugin_id":"example.board","entrypoint":"board","placement":"zoomed","target_pane_id":"w1:p1","env":{"HERDR_ROLE":"board"},"focus":true}}
+```
+
+`plugin.pane.open` requires an installed, enabled, platform-compatible plugin,
+then launches the requested manifest `[[panes]]` entrypoint as an argv-backed
+terminal pane. Manifest pane `placement` defaults to `overlay`; request
+`placement` overrides the manifest with `overlay`, `split`, `tab`, or `zoomed`.
+Overlay panes target the active pane. Split and zoomed panes target an existing
+pane; tab panes can target a workspace. The pane
+behaves like a normal Herdr pane, but `plugin.pane.focus` and
+`plugin.pane.close` only operate on panes opened through the plugin API. Focus
+returns `plugin_pane_focused`; close returns `plugin_pane_closed`.
+
## Socket transport
Herdr uses newline-delimited JSON over a Unix domain socket.
@@ -266,7 +491,7 @@ Integrations report agent state with `pane.report_agent`.
`state` is semantic. It affects waits, notifications, and rollups.
-`custom_status` is visual. It can show a short label like `indexing` without changing semantic behavior.
+`custom_status` is visual. It can show a short activity label like `indexing` without changing semantic behavior.
Session-only official integrations report native session references with `pane.report_agent_session`. State-reporting integrations can still include native session references in `pane.report_agent`. State-independent session reports do not affect waits, notifications, or rollups.
@@ -323,7 +548,15 @@ Use `pane.report_metadata` when a user hook wants to customize presentation with
}
```
-Metadata reports are display-only. Valid metadata can override the pane title, displayed agent name, compact custom status, and visible state labels. `working`, `blocked`, `idle`, waits, notifications, and rollups still come from semantic state. Native session restore comes from stored official session references. `agent` is an optional guard for the authoritative agent label; `applies_to_source` is an optional guard for the active lifecycle authority source. Use `display_agent` to change the visible name. `state_labels` keys must be `idle`, `working`, `blocked`, `done`, or `unknown`. Use clear fields such as `clear_custom_status: true` with the same `source` to remove one presentation override.
+Metadata reports are display-only. Valid metadata can override the pane title, displayed agent name, compact activity label, and visible state labels. `working`, `blocked`, `idle`, waits, notifications, and rollups still come from semantic state. Native session restore comes from stored official session references. `agent` is an optional guard for the authoritative agent label; `applies_to_source` is an optional guard for the active lifecycle authority source. Use `display_agent` to change the visible name. `state_labels` keys must be `idle`, `working`, `blocked`, `done`, or `unknown`. Use clear fields such as `clear_custom_status: true` with the same `source` to remove one presentation override.
+
+Presentation text is normalized before storage. Herdr trims surrounding whitespace, removes control characters, caps `custom_status` at 32 characters, and caps `title`, `display_agent`, and each state label at 80 characters. Empty normalized values are ignored.
+
+`source` and `applies_to_source` are source identifiers. They must be 80 characters or fewer and may contain only ASCII letters, digits, colon, dot, underscore, and hyphen.
+
+Use `ttl_ms` for short-lived metadata. It must be between `1` and `86400000` milliseconds. Omit `ttl_ms` for metadata that should stay until replaced, cleared, or the pane closes. When the TTL expires, Herdr removes that source's metadata and emits a presentation change if the visible pane presentation changed.
+
+Use `seq` when a hook may send updates out of order. For the same `source`, reports with a sequence number less than or equal to the last accepted sequence are accepted by the API but ignored by the pane state.
## Event subscriptions
@@ -343,12 +576,14 @@ Subscribe to events when you need a long-lived stream:
The first response acknowledges the subscription. Later lines are pushed events.
-Workspace event subscriptions include `workspace.created`, `workspace.updated`, `workspace.renamed`, `workspace.closed`, and `workspace.focused`.
+Workspace event subscriptions include `workspace.created`, `workspace.updated`, `workspace.renamed`, `workspace.closed`, and `workspace.focused`. Workspace events describe Herdr UI/runtime lifecycle. `workspace.created` includes optional `workspace.worktree` provenance when the workspace belongs to a worktree group. `workspace.closed` includes a final `workspace` snapshot when Herdr can still identify it before removal.
Pane event subscriptions include `pane.created`, `pane.closed`, `pane.focused`,
`pane.moved`, `pane.exited`, `pane.agent_detected`,
`pane.output_matched`, and `pane.agent_status_changed`.
-Use `events.wait` when you want one matching event and then a response.
+Worktree event subscriptions include `worktree.created`, `worktree.opened`, and `worktree.removed`. Worktree events describe Git checkout lifecycle. `worktree.created` includes the opened `workspace` and created `worktree`. `worktree.opened` includes the target `workspace`, opened `worktree`, and `already_open`. `worktree.removed` includes the `workspace_id`, removed `worktree`, and `forced`.
+
+Use `events.subscribe` for lifecycle events. Dedicated wait helpers are documented separately when a one-shot wait is supported.
## Reading panes