---
title: 插件
description: 编写带有清单动作、事件钩子和窗格的本地 Herdr 插件。
---

Herdr 插件是可分享、可执行的工作流包。插件可以是 Bash 脚本、JavaScript
应用、Lua 脚本、Rust 二进制,或你机器上能运行的任何 argv 命令。Herdr
负责宿主侧: 安装、清单校验、按键绑定、终端窗格、事件、调用上下文和
socket 访问。插件负责自己的实现语言、依赖、文件和持久状态。

插件的存在是为了让 Herdr 保持精简。核心继续专注于终端工作区、窗格、
智能体和稳定的 CLI/socket API。插件把这套已有的扩展面变成可复用的
工作流,让大家可以构建、安装和分享,而不必把每种工作流都塞进 Herdr 本体。

插件不是 SDK 集成。它是一个带 `herdr-plugin.toml` 清单和 Herdr 可启动
命令的目录。Herdr 校验清单、注入运行时上下文、启动声明的命令并记录日志。
命令在需要做更多工作时,通过 CLI 或 socket 回调 Herdr。

没有单独的插件 SDK,也没有受限的命令集。整个 Herdr CLI 就是插件 API:
[CLI 参考](/zh-cn/docs/cli-reference/)中的每条命令插件都能用,你自己能以
`herdr ...` 运行的任何东西,插件也能运行。大多数插件应通过指向运行中
Herdr 二进制的 `HERDR_BIN_PATH` 调用 Herdr,这样插件在 Unix socket 和
Windows 命名管道之间保持可移植。想自己发送原始 JSON 请求时,使用
[socket API](/zh-cn/docs/socket-api/)。

运行时动作注册和非终端的原生插件 UI 不在插件 v1 范围内。动作、事件钩子、
窗格和链接处理器都在清单中声明。

## 信任与安全

插件是运行在你机器上的普通代码。安装或链接一个插件时,它的构建和运行时
命令以你的用户身份、在你的环境中执行,并且可以调用完整的 Herdr CLI —
和你给编辑器、shell 或编程智能体添加的任何扩展一样。这种开放性正是设计
初衷,加上一点判断力就能保持安全。

从你信任的作者和仓库安装插件,并先大致看看新插件做什么: `herdr-plugin.toml`
清单,以及它运行的脚本或二进制。`herdr plugin install` 在交互式终端中会
展示来源和将要运行的命令的预览,你可以在确认前审查。对已经信任的来源用
`--yes`,想固定某个特定版本时用 `--ref`。

Herdr 校验清单,并把每个插件的配置和状态放在各自的目录中,但它不会审查
或沙箱化插件的行为。第三方插件来自它们的作者,而不是 Herdr,审查与运行
由你自行决定。

## 清单

清单是 Herdr 和插件之间的契约。它声明包元数据、支持的平台、可选的构建
命令,以及 Herdr 可以运行的入口点。

```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"]

[[startup]]
command = ["node", "dist/restore.js"]

[[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"
```

顶层的 `id`、`name`、`version` 和 `min_herdr_version` 是必填项。
把 `min_herdr_version` 设为支持你插件所用的插件 API、事件名和清单字段的
最老 Herdr 版本。当插件的最低版本比当前二进制更新时,Herdr 会拒绝链接
或安装。`description` 可选。插件 id 可以使用 ASCII 字母、数字、点、
冒号、下划线和连字符。

动作 id、窗格 id 和链接处理器 id 是插件内部的本地 id。它们可以使用
ASCII 字母、数字、冒号、下划线和连字符,但不能用点。每种 id 在插件内
必须唯一。当需要全局唯一名称时,Herdr 会把动作 id 限定为
`plugin.id.action` 的形式。

用 `platforms = ["linux", "macos", "windows"]` 声明插件可以运行的平台。
构建命令、启动钩子、动作、事件钩子、窗格和链接处理器也可以声明自己的
`platforms`;条目级的 platforms 覆盖顶层列表。没有顶层 `platforms` 的本地
插件在链接时会给出警告。

`command` 的值是 argv 数组。Herdr 不会通过 shell 运行它们,所以除非你的
命令自己启动 shell,否则没有 shell 展开。语言相关的行为放到你的脚本或
二进制里。

## 第一个插件

从一个包含 `herdr-plugin.toml` 和一个可执行脚本或程序的目录开始:

```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"]
```

在命令内部通过 `HERDR_BIN_PATH` 回调 Herdr:

```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);
```

这个示例用了 Node,但插件本身完全不要求 Node。清单可以启动 Bash、
PowerShell、Python、Rust、Go、Lua、Bun 或用户机器上可用的任何其他命令。

## 安装与链接

安装一个示例插件:

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

在本地编写插件时,改为链接工作目录:

```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` 只接受 GitHub 简写,比如 `owner/repo/subdir`。它用 `git`
克隆,在交互式终端展示预览,运行受支持的构建命令,然后把检出保存到
Herdr 管理的插件数据下并注册。非交互式安装用 `--yes`。重新安装 GitHub
管理的插件会替换该托管检出。已安装和已链接的插件及其启用状态对当前用户
全局生效,可在所有 Herdr 会话中使用。即使 Herdr 服务器没有运行,也可以通过
`plugin install` 和 `plugin link` 注册插件。仅安装在 Herdr 0.7.3 命名会话中的
插件必须重新 install 或 link;现有的插件配置和状态会保留。
不允许覆盖安装到本地链接的插件之上;请先 unlink 或 uninstall 本地插件。
`plugin install` 和 `plugin link` 会创建
插件的配置和状态目录,`plugin config-dir <id>` 打印配置目录,方便安装
文档和 shell 脚本使用。

`plugin uninstall <id-or-source>` 注销插件。对 GitHub 管理的安装,它还会
删除托管检出,并且既接受插件 id,也接受与 install 相同的
`owner/repo[/subdir...]` 简写。`plugin unlink <id>` 只注销插件、不动文件,
对本地开发很有用。v1 没有单独的 `plugin update`;要刷新托管插件,请从
GitHub 重新安装。

示例菜谱仓库是 `ogulcancelik/herdr-plugin-examples`。它在子目录中包含
多个独立示例插件,包括 `agent-telegram-notify`、`github-link-preview` 和
`dev-layout-bootstrap`。这些是供复制的示例,不是持续维护的官方插件。

## 构建命令

构建命令在 GitHub `plugin install` 过程中运行,时机在确认之后、Herdr
注册插件之前。如果构建命令失败,安装中止,插件不会被注册。`plugin link`
不运行构建命令;本地作者自己构建工作树。构建命令可以生成文件,但在
安装预览之后修改 `herdr-plugin.toml` 会导致安装中止。构建失败时会显示
插件 id、构建序号、工作目录、命令、退出状态或 spawn 错误,以及截断后的
stdout/stderr,不会解读工具输出。

构建命令同样是普通的 argv 命令,但它们不会收到运行时插件上下文或
Herdr socket 环境变量。插件作者应在文档中说明所需的系统工具,比如
`cargo`、`npm`、`bun` 或 `lua`;Herdr 报告构建失败,但不会安装缺失的
工具链。

## 启动钩子

Herdr 恢复会话且 API socket 就绪后,会为每个已启用插件运行一次
`[[startup]]` 命令。实时交接由新服务器接管时会再次运行,但客户端连接、
配置重载以及插件 link 或 enable 时不会运行。Herdr 异步启动这些命令,并在
普通插件命令日志中记录完成情况。启动钩子失败不会停止服务器。

启动钩子是一次性初始化命令,不是受监督的守护进程。钩子应恢复插件自己的
状态、调用所需的 Herdr API,然后退出。例如,插件可以把声明式 Agent 视图保存到
`HERDR_PLUGIN_STATE_DIR`,再由启动钩子读取并重新应用。

启动钩子会收到普通运行时插件环境和 `HERDR_PLUGIN_EVENT=startup`。安装预览会
列出所有启动命令,让用户检查将自动运行的代码。

## 命令与环境

运行时命令以插件目录为工作目录执行。Herdr 注入 `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`,以及可用时的 `HERDR_WORKSPACE_ID`、
`HERDR_TAB_ID` 和 `HERDR_PANE_ID`。动作命令还会收到
`HERDR_PLUGIN_ACTION_ID`;启动钩子和事件钩子收到 `HERDR_PLUGIN_EVENT`
（启动钩子中的值为 `startup`）,事件钩子还会收到
`HERDR_PLUGIN_EVENT_JSON`;窗格命令收到 `HERDR_PLUGIN_ENTRYPOINT_ID`。

`HERDR_PLUGIN_ROOT` 是已安装或已链接的插件目录。不要把用户凭据或持久
状态放在那里,因为 GitHub 安装的插件根目录是托管的源码检出。把 `.env`
这类用户可编辑的配置放在 `HERDR_PLUGIN_CONFIG_DIR` 下,把本地运行时
状态放在 `HERDR_PLUGIN_STATE_DIR` 下。Herdr 会创建这些目录,并在旧版
插件配置位置存在时把内容初始化到 `HERDR_PLUGIN_CONFIG_DIR`,但不会校验、
同步或删除其中的内容。文件格式和生命周期归插件所有。

`HERDR_PLUGIN_CONTEXT_JSON` 在本次调用可用时,可以包含工作区、标签页、
聚焦窗格、worktree、智能体、选中文本、点击的 URL 和链接处理器字段。
shell 插件可以从各个环境变量读取常用 id,或解析上下文 JSON 获取完整结构。

当插件需要从 Node、PowerShell、Bash 或其他运行时可移植地调用 Herdr 时,
使用 `HERDR_BIN_PATH`。`HERDR_SOCKET_PATH` 背后的原始 socket 传输是
操作系统相关的: Unix 客户端连接 Unix socket 路径,Windows 客户端连接
命名管道。通过 `HERDR_BIN_PATH` 的 CLI 调用可以避开这种传输差异。可用
命令见 [CLI 参考](/zh-cn/docs/cli-reference/),原始请求结构见
[socket API](/zh-cn/docs/socket-api/)。

## 窗格

清单中窗格的 `placement` 默认为 `overlay`,它在活动窗格上方打开一个
临时的缩放覆盖层,关闭时恢复之前的焦点和缩放。`plugin.pane.open` 请求
可以用 `overlay`、`popup`、`split`、`tab` 或 `zoomed` 覆盖清单的 placement。

`placement = "popup"` 会打开一个会话级模态终端弹窗,而不改变平铺布局。
可以在清单或 open 请求中指定可选的 `width` 和 `height`;省略时默认为终端大小的一半,数字表示外层终端单元格数,`"80%"` 这样的字符串表示终端区域的百分比。
弹窗会接收包括 Escape 在内的所有终端输入,并在命令退出或发送 `popup.close` 请求时关闭。
小于弹窗最小尺寸的值会限制为最小值。

如果某个插件窗格应始终是临时的,可直接在入口点上声明 placement:

```toml
[[panes]]
id = "picker"
title = "Picker"
platforms = ["linux", "macos"]
placement = "popup"
width = "80%"
height = 20
command = ["sh", "picker.sh"]
```

split、tab、zoomed 和 overlay 插件窗格打开后就是普通的 Herdr 窗格。插件可以通过 socket 或 CLI 调用
`pane.move`、`pane.swap`、`pane.resize`、`pane.zoom` 等标准窗格 API;
窗格跨标签页或工作区移动时,Herdr 会让插件窗格的所有权跟随底层窗格。
弹窗不是 Herdr 窗格,而是会话级单例资源:它没有窗格 id,不会改变插件焦点上下文,不会发出窗格生命周期事件,也不参与 pane、layout、持久化或智能体 API。
其进程不会收到 `HERDR_PANE_ID`;底层平铺窗格仍可通过 `HERDR_PLUGIN_CONTEXT_JSON` 获取。
在 Settings、复制模式或其他 Herdr 模态界面打开时尝试打开弹窗会返回 `ui_busy`;启动成功后,`plugin.pane.open` 返回 `ok`。

在 Windows 上,构建命令、动作命令和事件命令会在裸命令位于 `PATH` 上时
解析常见的 `PATHEXT` shim,比如 `npm.cmd`、`bun.cmd` 和 `pnpm.cmd`。
窗格命令使用 Herdr 常规的 Windows 窗格启动器,仍然必须是有效的 Windows
argv 命令。

## 按键绑定

把某个键绑定到已安装的插件动作:

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

## 链接处理器

用 `[[link_handlers]]` 把对匹配终端 URL 的修饰键点击路由到插件动作,
而不是在浏览器中打开 URL。修饰键点击的修饰键在所有平台上都是 Control,
包括 macOS,因为被捕获的终端鼠标上报无法把 Command/Super 和普通点击
区分开。`pattern` 是对被点击 URL 匹配的 Rust 正则表达式,`action` 必须
指向同一插件声明的动作。链接处理器动作在 `HERDR_PLUGIN_CONTEXT_JSON`
中收到 `invocation_source = "link_click"`、`clicked_url` 和
`link_handler_id`;shell 插件也可以读取 `HERDR_PLUGIN_CLICKED_URL` 和
`HERDR_PLUGIN_LINK_HANDLER_ID`。每个插件内的处理器按清单顺序检查。

## 存储

v1 没有 Herdr 管理的插件存储 API。需要持久状态的插件应自己管理文件或
数据库。

## 插件市场

社区插件可以在[插件市场](/plugins/)中发现,它是打了 `herdr-plugin` 主题
标签的公开 GitHub 仓库的自动索引。插件仍然是普通的 GitHub 仓库: 发布一个
带 `herdr-plugin.toml` 的仓库,然后分享
`herdr plugin install owner/repo[/subdir]`。

要让插件被收录,给它的公开仓库添加 GitHub 主题标签 `herdr-plugin`。索引
每 30 分钟刷新一次。发现机制的工作方式见[插件市场](/zh-cn/docs/marketplace/)。
