@muzi3/herdr

Herdr for pi: three independent extensions (agent + blocked state reporting, /pi-hot-restart, and the herdr_layout / herdr_pane / herdr_agent pane control tools forked from @ogulcancelik/pi-herdr) plus the herdr-guide skill.

Packages

Package details

extensionskill

Install @muzi3/herdr from npm and Pi will load the resources declared by the package manifest.

$ pi install npm:@muzi3/herdr
Package
@muzi3/herdr
Version
0.2.2
Published
Sep 16, 2026
Downloads
116/mo · 3/wk
Author
muzi3
License
Apache-2.0 AND MIT
Types
extension, skill
Size
96.1 KB
Dependencies
0 dependencies · 0 peers
Pi manifest JSON
{
  "skills": [
    "./skills"
  ],
  "extensions": [
    "./extensions/herdr-agent-state/index.ts",
    "./extensions/pane-agent-tools/index.ts",
    "./extensions/pi-hot-restart/index.ts"
  ]
}

Security note

Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.

README

@muzi3/herdr

Herdr 相关的官方 guide skill、pane/agent 控制工具与本地补充扩展打包为一个 pi package。

资源 类型 说明
herdr_layout / herdr_pane / herdr_agent 扩展(fork) pane/agent 控制工具:工作区 · tab · pane 拓扑,pane 的命令下发/读取/等输出/收发按键,以及 coding agent 的启动、prompt、等待与读取
herdr-guide user-invoked 向导 引导用户理解、安装、配置与排查 Herdr(herdr.dev/agent-guide.md
herdr-agent-state 扩展 通过 Herdr 的 pane socket 上报 agent 状态(working / blocked / idle)及会话引用,供 Herdr 面板展示;并把 pi 的阻塞式交互弹窗(ui_prompt_start / ui_prompt_end,含 ask_user_question)转发为 herdr 的 herdr:blocked 事件,使 pane 变为 blocked(label 带问题摘要);桌面通知交给 herdr 自己发
pi-hot-restart 扩展 提供 /pi-hot-restart 命令:批量重启当前 herdr 里的所有 pi 窗口(跳过 working),并恢复到原 session

三个扩展互相独立:每个扩展独占 extensions/ 下的一个目录、以 index.ts 为入口,互不 import,package.jsonpi.extensions 逐条列出入口。一个扩展注册失败不会连带拖垮另外两个,日后给某个扩展加内部模块也不会影响另外两个。

扩展结构

extensions/
├── herdr-agent-state/index.ts    agent 状态 + blocked 状态上报
├── pane-agent-tools/index.ts     herdr_layout / herdr_pane / herdr_agent
└── pi-hot-restart/index.ts       /pi-hot-restart
入口 内容 加载门槛
herdr-agent-state/index.ts agent 状态 + blocked 状态上报(herdr 官方集成正文 + 本包并入的 blocked 段落) HERDR_ENV=1HERDR_SOCKET_PATH / HERDR_PANE_ID 非空
pi-hot-restart/index.ts /pi-hot-restart 命令(本包自研补充) 无,走 PATH 调 herdr CLI
pane-agent-tools/index.ts herdr_layout / herdr_pane / herdr_agent(本地 fork) HERDR_ENV=1HERDR_PANE_ID 非空

门槛由各扩展自己判断(没有统一 gate),所以在 herdr 之外 /pi-hot-restart 仍可用(它只控制本机进程,不需要 pane socket),依赖 pane 的两个扩展则静默跳过。

每个扩展目录的入口都叫 index.ts,与 pi 自己的目录式扩展约定一致:扩展目录内可以再放内部模块,pi.extensions 只指向入口,不会误加载旁支文件。

herdr-agent-state/index.ts 的目录名与 herdr 集成注入的文件同名,对齐上游时可以直接对比。内容分两段:官方集成正文(一直到文件末尾之前)与之后的「本包新增」段落。本包对官方正文只有一处改动:官方 export default 开头插入一行 registerBlockedReporting(pi); —— 该 default export 就是本扩展的唯一入口,blocked 上报只能挂在那里。重新同步上游时(HERDR_INTEGRATION_VERSION 从 8 往上升)重取注入文件替换官方正文并补回那一行;diff 应只有三处新增、零删除,漏帖那一行会被 tests/herdr-agent-state.test.ts 拦下。

来源与许可

两者内容均来自 Herdr 官方,上游仓库为 herdrdev/herdr

  • herdr-guide 引导 skill —— 官方 herdr.dev/agent-guide.md,教 agent 引导用户理解/配置/排障 Herdr
  • herdr-agent-state 扩展 —— Herdr 官方 pi 集成的 bundled 扩展,本包保留其正文,只做一处改动(在官方 export default 开头插入一行 blocked 上报),其余新增都在文件末尾的段落里。herdr integration install pi 会把官方版本写入 ~/.pi/agent/extensions/herdr-agent-state.ts(若设置 PI_CODING_AGENT_DIR 则写入 $PI_CODING_AGENT_DIR/extensions/

Herdr 以 Apache-2.0 许可发布,本包保留其许可声明,详见 LICENSE

⚠️ 本包这份与集成写入的那份目录名相同、位置不同(本包 extensions/herdr-agent-state/index.ts,集成写 ~/.pi/agent/extensions/herdr-agent-state.ts)。若已执行过 herdr integration install pi,agent 目录里的副本会被 pi 自动加载,与本包同时上报状态(herdr 的 blockedCount 会短暂变成 2,然后一起归零)。本包已自带该功能,用本包时不必安装集成;已安装的话请删掉 ~/.pi/agent/extensions/herdr-agent-state.ts

无论装不装集成,agent 目录里那份都由集成管理,重装或更新集成会覆盖它;自定义 hooks 请写成旁边的独立文件而非编辑它(本包这份是 vendored 副本,由本包自己维护)。

extensions/pane-agent-tools/index.tsherdr_layout / herdr_pane / herdr_agent)是本地 fork,来源与许可与上面不同:

  • 上游:@ogulcancelik/pi-herdr 0.4.0(仓库,MIT,Copyright (c) 2026 Can Celik),许可全文见本包 LICENSE.MIT
  • 因此本包整体按 Apache-2.0 AND MIT 双许可表述:LICENSE(Apache-2.0)覆盖 Herdr 官方内容与 pi-hot-restart.tsLICENSE.MIT 覆盖 fork 进来的 pane-agent-tools.ts

相对上游的改动

上游 0.4.0 把 pane run / pane send-text / pane send-keys 交给 execHerdrJson,而它额外要求 herdr CLI 输出 JSON envelope。但 herdr 0.9.0 上这三条命令成功时退出码为 0 且 stdout 为空,于是每一次写操作都误报 Expected JSON output from herdr ... —— 即使命令早已成功下发到目标 pane。实测 14 天内 156 个 pi session 日志,误报次数为 send-keys 964、send-text 923、run 438。

这三处改用 execHerdr:它只校验退出码,并把 stderr/stdout 上的结构化 herdr 错误经 parseHerdrError 抛出,因此真实失败照旧报错,只去掉了那个不成立的「stdout 必须非空」约束。pane close / pane wait-output / agent send-keys 确实返回 envelope,保持 execHerdrJsonclose 的边界由回归测试锁定)。

上游尚未修复,相关讨论:issue #22issue #40PR #27(仅覆盖 run,未合并)。本包已不依赖 @ogulcancelik/pi-herdr:同时安装两者会重复注册同名工具,需先卸载上游包。

安装

pi install npm:@muzi3/herdr

用法

  • 用户询问 Herdr 是什么、怎么安装/配置/使用,或排查 agent 未识别、快捷键失效等问题时,触发 herdr-guide 引导(/herdr-guide
  • 触发词:herdr / herdr 安装 / herdr 配置 / 终端工作区 / agent 状态检测

pane / agent 控制工具

三个工具直接驱动 herdr CLI,需在 herdr 集成环境(HERDR_ENV=1 + HERDR_PANE_ID)中加载:

  • herdr_layout —— 工作区 / tab / pane 的创建、列举与拓扑查询(current / workspace_list / pane_split / pane_layout 等)
  • herdr_pane —— 普通进程的 pane 控制:run 下发命令、read 读回输出、wait_output 等匹配输出、send_text / send_keys 收发按键、close 关闭(拒绝关闭 pi 自身所在 pane)
  • herdr_agent —— 已识别 coding agent 的 pane:start / prompt / wait / read / send_keys / rename

批量重启 pi 窗口

在任意 pi 窗口执行 /pi-hot-restart,会:

  1. 列出当前 herdr 里所有 pi 窗口;
  2. 跳过 agent_status === "working" 的窗口(含当前发起命令的窗口本身);
  3. 弹出确认框,展示将重启的窗口清单与数量;
  4. 对每个目标窗口:向 pi 前台进程组发 SIGTERM 优雅退出(session 落盘),等待退出后用 pi --session <path|id> 在原 pane 恢复。

等用户输入时上报 blocked

herdr 的 pi 集成只认 herdr:blocked 事件,且 herdr 0.9.0 对 pi 仍没有终端 blocked 识别规则(内置规则只有一条 working_literal,匹配 Working...),所以 ask_user_question / confirm 弹窗期间 pane 会一直停在 working。本扩展的 blocked 上报段落订阅 pi core 的 ui_prompt_start / ui_prompt_endselect/confirm/input/editor/custom 统一包装,只在外层触发):

  • 等待期发 herdr:blocked { active: true, label },结束后发 { active: false };label 形如 等待回答:<问题 header>custom 无 title,取 rpiv:ask-user:prompt 的问题内容)、等待确认:<title>
  • 契约同 pi-subagents 的 herdr-status bridge:herdr 的 blockedCount 是计数式的,切 label 前先发 active:false 再发 active:true;本扩展严格配对,只占一个计数。

桌面通知交给 herdr 自己发(~/.config/herdr/config.toml[ui.toast] delivery = "terminal"):通知经终端转交给 Ghostty 显示,点击横幅会把 Ghostty 窗口调到前面。注意它只能到窗口粒度 —— herdr 的 pane 是 herdr TUI 内部的概念,点通知不会切到对应 pane;正文也固定为 <工作区名> · <工作区编号> · <tab 名>,不含 pane id 与问题原文。早期版本试过自建横幅(osascript / terminal-notifier),内容能带 pane 与问题原文,但要么不支持点击动作、要么在本机通知权限不稳;点击跳转要能捕获回调的通知器(如 alerter)才可能做到,为避免与 herdr 自己的通知重复弹两份,自建横幅已整体移除。

扩展环境变量

扩展需在 Herdr 集成环境中运行,依赖以下环境变量:

变量 说明
HERDR_ENV 1 时启用扩展
HERDR_SOCKET_PATH Herdr pane socket 路径
HERDR_PANE_ID 当前 pane id(同时作为扩展的运行门槛)

未设置时 herdr-agent-statepane-agent-tools 两个扩展静默跳过(门槛见「扩展结构」),/pi-hot-restart 仍可用:它通过 PATH 调用 herdr CLI(agent list / pane process-info / pane run),不依赖上述变量。

测试

node --test tests/    # 等价于 npm test

tests/ 下三个文件、共 28 个用例,node:test + node:assert/strict,无需引入 bun(Node 原生剥离 TS 类型):

tests/pane-agent-tools.test.ts —— 由上游 index.test.tsbun:test)改写。除移植的 9 个用例外,额外覆盖本 fork 的 3 行改动:

  • pane run / send_text / send_keys 在 stdout 为空时成功,且恰好调用一次 herdr
  • stdout 为 banner/ANSI 等非 JSON 内容时不当作协议数据解析
  • 结构化 herdr 错误(stderr envelope)与裸非零退出仍然报错,不会被「只校验退出码」吞掉
  • pane close 仍走 JSON envelope 路径

这组用例对上游写法运行会有 4 个失败、对当前实现 16 个全通过,可用于确认回归网没有失效。

tests/herdr-agent-state.test.ts —— 起一个真实 unix socket 充当 pane socket,验证 agent 状态(working / idle)与 blocked 上报(含 label 取值、弹窗结束后的回落)真的落到连接上。用真 socket 而非桩:上报的成败判据本身就在连接上(sendRequestAttempt 只有连上并收到对端数据才算送达),桩掉 net 等于把被测逻辑一起桩掉。这一组只调 default export(扩展唯一入口),所以官方正文里那行 registerBlockedReporting(pi); 被漏帖时会直接失败。

tests/extensions.test.ts —— 把「三个扩展各自独立」锁成断言:pi.extensionsextensions/ 下的扩展目录一一对应(不漏声明、也不留死路径)、每个扩展目录都有 index.ts 入口且顶层没有散落的扩展文件、扩展之间没有跨目录 import,并且单独加载每个扩展时只会注册自己的资源(herdr-agent-state 只挂状态监听,pi-hot-restart 只有 /pi-hot-restartpane-agent-tools 只有三个工具)。

参考