A practical guide to Herdr plugins
By Flavio Copes
Install, inspect, configure, run, and build Herdr plugins, with practical commands and the security checks to make before trusting one.
Herdr plugins are executable workflow packages.
They can create a project layout, add an action, react to an event, open a terminal pane, or connect Herdr to another local tool.
A plugin can be written in Bash, JavaScript, PowerShell, Lua, Rust, or any other language your machine can run.
There is no separate plugin SDK.
Herdr provides the host: installation, the manifest, keybindings, events, panes, logs, and runtime context. The plugin calls the normal Herdr CLI or socket API when it needs to control the workspace.
In this guide we’ll install a plugin, inspect it, run an action, and build a small local plugin.
For the broader terminal and agent model, read my Herdr deep dive first.
Find a plugin
Community plugins are listed at herdr.dev/plugins.
The marketplace is an automatic index of public GitHub repositories tagged with herdr-plugin. A listing is not a security review.
Before installing anything:
- Open the repository.
- Read
herdr-plugin.toml. - Check the build and runtime commands.
- Inspect the scripts or source code those commands run.
- Decide whether you trust the author.
Herdr plugins run as your user. They inherit your environment and can call the full Herdr CLI. They are not sandboxed.
Treat one like a shell script or editor extension you are about to run locally.
Install a plugin from GitHub
The install command accepts GitHub shorthand:
herdr plugin install owner/repository
If the repository contains several plugins, add the subdirectory:
herdr plugin install owner/repository/path/to/plugin
For example, the official documentation uses this community example:
herdr plugin install \
ogulcancelik/herdr-plugin-examples/agent-telegram-notify
In an interactive terminal, Herdr shows the source and the commands it plans to run before asking for confirmation.
Do not add --yes the first time you install a plugin. That flag skips the interactive confirmation and is better reserved for a source and revision you have already reviewed.
You can pin a Git ref:
herdr plugin install \
owner/repository/path/to/plugin \
--ref v1.2.0
Herdr uses Git, runs the build commands declared in the manifest, stores the checkout in a managed directory, and registers the plugin.
If a build command fails, the plugin is not registered.
List and inspect installed plugins
List everything:
herdr plugin list
Ask for structured output when you are scripting:
herdr plugin list --json
Filter by plugin ID:
herdr plugin list --plugin examples.agent-telegram-notify
The plugin ID comes from its manifest. It does not have to match the GitHub repository name.
Plugin installation and enabled state are global for the current user. Install a plugin in one named Herdr session and it is available in the others too.
Configure a plugin
Each plugin gets a stable configuration directory.
Print it with:
herdr plugin config-dir examples.agent-telegram-notify
Put user-editable settings such as an .env file there when the plugin documentation asks for them.
Do not put credentials inside the managed plugin checkout. Reinstalling a GitHub plugin replaces that checkout.
Herdr also gives a plugin a separate state directory at runtime. The plugin owns the files and data format inside it.
Run plugin actions
List the actions a plugin exposes:
herdr plugin action list \
--plugin examples.agent-telegram-notify
Invoke one:
herdr plugin action invoke \
examples.agent-telegram-notify.send-test
The exact action IDs depend on the manifest.
If action names collide across plugins, use the fully qualified form:
plugin.id.action
You can bind an action in ~/.config/herdr/config.toml:
[[keys.command]]
key = "prefix+l"
type = "plugin_action"
command = "example.layout.apply"
description = "apply layout"
Reload the configuration:
herdr server reload-config
Open a plugin pane
A plugin can declare terminal pane entrypoints.
List or read the plugin documentation to find the entrypoint ID, then open it:
herdr plugin pane open \
--plugin example.layout \
--entrypoint board
Herdr supports several placements:
overlayfor a temporary zoomed panepopupfor a modal terminal that does not change the layoutsplitbeside an existing panetabin a new tabzoomedas a normal zoomed Herdr pane
You can override the manifest placement:
herdr plugin pane open \
--plugin example.layout \
--entrypoint board \
--placement popup \
--width 80% \
--height 20
A popup is intentionally temporary. Normal split, tab, zoomed, and overlay panes participate in the usual Herdr pane APIs.
Read plugin logs
If an action fails, inspect its command log:
herdr plugin log list \
--plugin example.layout \
--limit 20
The log tells you which declared command ran and how it exited.
It does not interpret the tool’s output for you. A failing Node, Bash, or Rust command is still debugged as that kind of program.
Disable, enable, and remove plugins
Temporarily disable a plugin:
herdr plugin disable example.layout
Enable it again:
herdr plugin enable example.layout
Uninstall a GitHub-managed plugin:
herdr plugin uninstall example.layout
You can also use the original source shorthand:
herdr plugin uninstall owner/repository/path/to/plugin
There is no separate plugin update command in the current plugin system. Reinstall the GitHub source to refresh its managed checkout.
Build a small local plugin
Let’s create an action that lists the current Herdr workspaces.
The directory has two files:
workspace-tools/
herdr-plugin.toml
index.js
The manifest is:
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"]
Herdr runs commands as argv arrays. It does not send them through a shell, so shell expansion does not happen unless the declared command starts a shell itself.
The JavaScript file can call the installed Herdr binary:
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)
HERDR_BIN_PATH is better than hardcoding herdr. It points to the binary that owns the running session and works across Unix sockets and Windows named pipes.
Link the working directory
During development, link the local directory:
herdr plugin link /absolute/path/to/workspace-tools
List and invoke the action:
herdr plugin action list \
--plugin example.workspace-tools
herdr plugin action invoke \
example.workspace-tools.list-workspaces
Read the log if it fails:
herdr plugin log list \
--plugin example.workspace-tools
When you are done testing, unlink it:
herdr plugin unlink example.workspace-tools
Unlinking removes the registration and leaves your local files alone.
Notice that plugin link does not run manifest build commands. You are working from a live checkout, so you build it yourself.
What a manifest can declare
A plugin manifest can currently define:
| Entry | Use |
|---|---|
[[build]] | Prepare a GitHub-installed plugin |
[[startup]] | Run one initialization command after server restore |
[[actions]] | Expose commands that people, keys, or tools can invoke |
[[events]] | React to known Herdr events |
[[panes]] | Open a terminal interface managed by the plugin |
[[link_handlers]] | Route matching terminal links to a plugin action |
Startup hooks are one-shot initialization commands, not supervised background services.
Actions and panes are declared in the manifest. Runtime action registration and native non-terminal plugin interfaces are not part of the current v1 surface.
Runtime context
Herdr starts plugin commands inside the plugin directory and injects useful variables, including:
HERDR_BIN_PATH
HERDR_ENV
HERDR_PLUGIN_ID
HERDR_PLUGIN_ROOT
HERDR_PLUGIN_CONFIG_DIR
HERDR_PLUGIN_STATE_DIR
HERDR_PLUGIN_CONTEXT_JSON
HERDR_WORKSPACE_ID
HERDR_TAB_ID
HERDR_PANE_ID
Not every invocation has every workspace, tab, or pane value.
HERDR_PLUGIN_CONTEXT_JSON can include the focused pane, worktree, agent, selected text, or clicked URL. Use it when the action needs more than the common IDs.
My recommendation
Start by installing one small plugin from a repository you have read.
Learn these four commands first:
herdr plugin list
herdr plugin action list --plugin ID
herdr plugin action invoke ID.ACTION
herdr plugin log list --plugin ID
Then build one local action and link it.
Herdr plugins are deliberately simple. The manifest describes how a workflow connects to Herdr, while the actual program remains a normal command you can run, inspect, and debug.
The complete manifest reference is in the official Herdr plugin guide. If you want an agent to operate the workspace for you, read how to use the Herdr skill.
Related posts about ai: