Skip to content

Plugins

Preview docs describe unreleased preview builds. Stable docs remain at /docs/.

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 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 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.

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.

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.

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.

Start with a directory that contains herdr-plugin.toml and one executable script or program:

my-plugin/
herdr-plugin.toml
index.js
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:

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 an example plugin:

Terminal window
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:

Terminal window
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 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.

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 for available commands and socket API for raw request shapes.

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.

Bind a key to an installed plugin action:

[[keys.command]]
key = "prefix+l"
type = "plugin_action"
command = "example.layout.apply"
description = "apply layout"

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.

There is no Herdr-managed plugin storage API in v1. Plugins that need durable state should own their files or database.

A plugin marketplace is coming. Plugins are already shareable today: publish a GitHub repository with herdr-plugin.toml, then share herdr plugin install owner/repo[/subdir].

If you want the plugin to appear in the website marketplace when discovery launches, add the GitHub topic herdr-plugin to the repository now. See Marketplace for how publishing and discovery will work.