mirror of
https://github.com/herdrdev/herdr.git
synced 2026-09-24 00:01:10 +00:00
315 lines
13 KiB
Plaintext
315 lines
13 KiB
Plaintext
---
|
|
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.
|
|
|
|
Plugins exist so Herdr can stay lean. The core stays focused on terminal
|
|
workspaces, panes, agents, and a stable CLI/socket API. Plugins turn that
|
|
existing extension surface into reusable workflows that people can build,
|
|
install, and share without adding every workflow to Herdr itself.
|
|
|
|
A plugin is not an SDK integration. It is a directory with a
|
|
`herdr-plugin.toml` manifest and commands Herdr can launch. Herdr validates the
|
|
manifest, injects runtime context, starts the declared commands, and records
|
|
logs. The commands call back into Herdr through the CLI or socket when they need
|
|
to do more work.
|
|
|
|
There is no separate plugin SDK or restricted command set. The entire Herdr CLI
|
|
is the plugin API: every command in the [CLI reference](/docs/cli-reference/) is
|
|
available to a plugin, and anything you can run as `herdr ...` yourself a plugin
|
|
can run too. Most plugins should call Herdr through `HERDR_BIN_PATH`, which
|
|
points at the running Herdr binary. That keeps plugins portable across Unix
|
|
sockets and Windows named pipes. Use the [socket API](/docs/socket-api/) when
|
|
you want to send raw JSON requests yourself.
|
|
|
|
Runtime action registration and native non-terminal plugin UI are not part of
|
|
plugin v1. Actions, event hooks, panes, and link handlers are all declared in
|
|
the manifest.
|
|
|
|
## Trust and security
|
|
|
|
A plugin is ordinary code that runs on your machine. When you install or link
|
|
one, its build and runtime commands run as your user, with your environment, and
|
|
can call the full Herdr CLI — the same as any extension you add to an editor,
|
|
shell, or coding agent. That openness is the point, and a little judgment keeps
|
|
it safe.
|
|
|
|
Install plugins from authors and repositories you trust, and skim what a new one
|
|
does first: the `herdr-plugin.toml` manifest and the scripts or binaries it runs.
|
|
`herdr plugin install` shows a preview of the source and the commands it will run
|
|
in interactive terminals, so you can review before confirming. Use `--yes` for
|
|
sources you already trust, and pin `--ref` when you want a specific revision.
|
|
|
|
Herdr validates the manifest and keeps each plugin's config and state in its own
|
|
directory, but it does not review or sandbox what a plugin does. Third-party
|
|
plugins come from their authors, not from Herdr, so they are yours to vet and run
|
|
at your own discretion.
|
|
|
|
## Manifest
|
|
|
|
The manifest is the contract between Herdr and the plugin. It declares package
|
|
metadata, supported platforms, optional build commands, and the entrypoints
|
|
Herdr can run.
|
|
|
|
```toml
|
|
id = "example.layout"
|
|
name = "Layout"
|
|
version = "0.1.0"
|
|
min_herdr_version = "0.7.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"
|
|
```
|
|
|
|
Top-level `id`, `name`, `version`, and `min_herdr_version` are required.
|
|
Set `min_herdr_version` to the oldest Herdr version that supports the plugin
|
|
APIs, event names, and manifest fields your plugin uses. Herdr refuses to link
|
|
or install a plugin when its minimum version is newer than the current binary.
|
|
`description` is optional. Plugin ids may use ASCII letters, digits, dot,
|
|
colon, underscore, and hyphen.
|
|
|
|
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. Herdr qualifies action ids
|
|
as `plugin.id.action` when it needs a globally unique name.
|
|
|
|
Use `platforms = ["linux", "macos", "windows"]` to declare where the plugin
|
|
can run. Build commands, actions, event hooks, panes, and link handlers can
|
|
also declare their own `platforms`; item-level platforms override the top-level
|
|
list. Local plugins without top-level `platforms` link with a warning.
|
|
|
|
`command` values are argv arrays. Herdr does not run them through a shell, so
|
|
there is no shell expansion unless your command starts a shell itself. Put
|
|
language-specific behavior in your script or binary.
|
|
|
|
## First plugin
|
|
|
|
Start with a directory that contains `herdr-plugin.toml` and one executable
|
|
script or program:
|
|
|
|
```text
|
|
my-plugin/
|
|
herdr-plugin.toml
|
|
index.js
|
|
```
|
|
|
|
```toml
|
|
id = "example.workspace-tools"
|
|
name = "Workspace Tools"
|
|
version = "0.1.0"
|
|
min_herdr_version = "0.7.0"
|
|
description = "Small workspace helpers"
|
|
platforms = ["linux", "macos", "windows"]
|
|
|
|
[[actions]]
|
|
id = "list-workspaces"
|
|
title = "List workspaces"
|
|
contexts = ["workspace"]
|
|
command = ["node", "index.js"]
|
|
```
|
|
|
|
Inside the command, call back into Herdr with `HERDR_BIN_PATH`:
|
|
|
|
```js
|
|
const { spawnSync } = require("node:child_process");
|
|
|
|
const herdr = process.env.HERDR_BIN_PATH ?? "herdr";
|
|
const result = spawnSync(herdr, ["workspace", "list"], {
|
|
encoding: "utf8",
|
|
stdio: ["ignore", "pipe", "pipe"],
|
|
});
|
|
|
|
process.stdout.write(result.stdout);
|
|
process.stderr.write(result.stderr);
|
|
process.exit(result.status ?? 1);
|
|
```
|
|
|
|
This example uses Node, but nothing about plugins requires Node. The manifest
|
|
could launch Bash, PowerShell, Python, Rust, Go, Lua, Bun, or any other command
|
|
available on the user's machine.
|
|
|
|
## Install and link
|
|
|
|
Install an example plugin:
|
|
|
|
```bash
|
|
herdr plugin install ogulcancelik/herdr-plugin-examples/agent-telegram-notify
|
|
herdr plugin config-dir 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 config-dir example.layout
|
|
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 install` and `plugin link` create the plugin's
|
|
config and state directories, and `plugin config-dir <id>` prints the config
|
|
directory for setup docs and shell scripts.
|
|
|
|
`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 creates those directories and seeds
|
|
`HERDR_PLUGIN_CONFIG_DIR` from the legacy plugin config locations when present,
|
|
but it does not 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.
|
|
|
|
Use `HERDR_BIN_PATH` when a plugin needs to call Herdr portably from Node,
|
|
PowerShell, Bash, or another runtime. The raw socket transport behind
|
|
`HERDR_SOCKET_PATH` is OS-specific: Unix clients connect to a Unix socket path,
|
|
while Windows clients connect to a named pipe. CLI calls through
|
|
`HERDR_BIN_PATH` avoid that transport difference. See the
|
|
[CLI reference](/docs/cli-reference/) for available commands and
|
|
[socket API](/docs/socket-api/) for raw request shapes.
|
|
|
|
## 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.
|
|
|
|
On Windows, build commands, action commands, and event commands resolve common
|
|
`PATHEXT` shims such as `npm.cmd`, `bun.cmd`, and `pnpm.cmd` when the bare
|
|
command is on `PATH`. Pane commands use Herdr's normal Windows pane launcher and
|
|
must still be valid Windows argv commands.
|
|
|
|
## 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 every platform, including macOS, because captured
|
|
terminal mouse reports do not expose Command/Super separately from a plain
|
|
click. `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
|
|
|
|
Community plugins are discoverable in the [marketplace](/plugins/), an automatic
|
|
index of public GitHub repositories tagged with the topic `herdr-plugin`.
|
|
Plugins stay ordinary GitHub repositories: publish one with `herdr-plugin.toml`,
|
|
then share `herdr plugin install owner/repo[/subdir]`.
|
|
|
|
To get a plugin listed, add the GitHub topic `herdr-plugin` to its public
|
|
repository. The index refreshes every 30 minutes. See
|
|
[Marketplace](/docs/marketplace/) for how discovery works.
|