@muzi3/herdr

Herdr for pi: three independent extensions (agent + blocked state reporting, /pi-hot-restart, and the herdr_overview / herdr_read / herdr_wait / herdr_prompt / herdr_run / herdr_create / herdr_agent_start / herdr_close manager tools aligned with dsh's her

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.3.0
Published
Nov 4, 2026
Downloads
310/mo · 202/wk
Author
muzi3
License
Apache-2.0 AND MIT
Types
extension, skill
Size
106.8 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_overview / herdr_read / herdr_wait / herdr_prompt / herdr_run / herdr_create / herdr_agent_start / herdr_close 扩展 管理工具面(8 个动词):一次盘点全机 agent、读屏、等落定、下发 prompt、跑命令、开新位置、启动 agent、关 pane。与 dsh 的 @muzi3/dsh-plugin-herdr-tools manager 面逐个对齐
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.json 的 pi.extensions 逐条列出入口。一个扩展注册失败不会连带拖垮另外两个,日后给某个扩展加内部模块也不会影响另外两个。

扩展结构

extensions/
├── herdr-agent-state/index.ts    agent 状态 + blocked 状态上报
├── pane-agent-tools/index.ts     herdr_overview · read · wait · prompt · run · create · agent_start · close
└── pi-hot-restart/index.ts       /pi-hot-restart
入口 内容 加载门槛
herdr-agent-state/index.ts agent 状态 + blocked 状态上报(herdr 官方集成正文 + 本包并入的 blocked 段落) HERDR_ENV=1 且 HERDR_SOCKET_PATH / HERDR_PANE_ID 非空
pi-hot-restart/index.ts /pi-hot-restart 命令(本包自研补充) 无,走 PATH 调 herdr CLI
pane-agent-tools/index.ts manager 8 动词面(herdr_overview / herdr_read / herdr_wait / herdr_prompt / herdr_run / herdr_create / herdr_agent_start / herdr_close) HERDR_ENV=1 且 HERDR_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.ts(manager 8 动词面)来源与许可是这样分的:

  • 面结构与判据语义移植自 dsh 侧同源实现的 manager 面:@muzi3/dsh-plugin-herdr-tools(MIT,Copyright (c) 2026 muzi)的 src/manager.js。pi 早期版本的 herdr_layout / herdr_pane / herdr_agent action 面已整体移除,两套不共存。
  • 两者共用的 herdr 调用与摘要 helper(execHerdr / summarize* / chooseSplitDirection 等)血缘上来自 @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.ts,LICENSE.MIT 覆盖 pane-agent-tools.ts 的 helper 血缘。

相对 dsh manager 面的差异

工具名、参数、信封与判据逐条对齐,差异只有三处,都是 pi 侧刻意保留的加固:

  • agent_blocked 与 agent_prompt_stalled 反向处理:前者是 herdr 在发送任何输入之前就拒绝,确定没下发,返回 exitCode 2 并指向先看清阻塞界面;后者是 submission accepted 之后没观测到 working/blocked,prompt 已下发,返回 ok: true 并劝阻重发。dsh manager 面把两者都当普通 herdr 错误。
  • agent start --kind 用枚举约束,等于 herdr agent start --help 的 possible values(0.9.5 共 24 个,含 qwen / letta / muse)。
  • herdr_agent_start 保留 agentArgs(放在 -- 之后的原生参数):dsh manager 面漏了这个能力。

环境变量用 PI_ 前缀(PI_HERDR_PROMPT_GUARD / PI_HERDR_READ_MODE / PI_HERDR_READ_TAIL_LINES),语义与 dsh 的 DSH_HERDR_* 一致。

与 herdr 版本的对账(当前基线 0.9.5)

工具面是照着 herdr CLI 写死的,herdr 升级后会有静默缺口。已固化的两处:

  • agent start --kind 的枚举必须等于 herdr agent start --help 的 possible values(0.9.5 共 24 个)。0.2.2 漏了 qwen / letta / muse,这三个 kind 当时根本起不来;tests/ 里有对账用例,加新 kind 时同步改。
  • agent_blocked 必须与 agent_prompt_stalled 反向处理:目标卡在审批/提问弹窗时,herdr 在发送任何输入之前就拒绝 prompt,所以这不是「已下发」,重试也不会好 —— herdr_prompt 返回 exitCode 2 并指向 herdr_read 先看清阻塞界面。

pane run / agent prompt 成功时退出码为 0 且 stdout 为空(0.9.5 实测),所以 herdr_run / herdr_prompt 只校验退出码,不要求 JSON envelope;真实失败仍经 parseHerdrError 读出结构化错误。

8 个动词没有覆盖、需要时直连 CLI 的命令族:herdr worktree、api、notification、session、machine、agent attach,以及 pane wait-output/send-text/send-keys/list/layout/focus/swap/move/zoom/resize/process-info/neighbors/edges/rename、tab list/focus、workspace focus、agent list/get/focus/rename/send-keys。其中 agent list 由 herdr_overview 覆盖,agent read / pane read 由 herdr_read 覆盖。

本包不依赖 @ogulcancelik/pi-herdr:同时安装两者会重复注册同名工具,需先卸载上游包。

安装

pi install npm:@muzi3/herdr

用法

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

管理工具面(8 个动词)

八个工具直接驱动 herdr CLI,需在 herdr 集成环境(HERDR_ENV=1 + HERDR_PANE_ID)中加载。每个工具返回统一信封:{ ok, exitCode, timedOut?, output, note?, via? } —— 正文先给 note(判据提示)、再给 output,details 里是同名字段。

工具 参数 做什么
herdr_overview limit / workspace 一次拿到自己的位置 + workspace 拓扑 + 全机 agent 列表(只读,管别人的第一步)
herdr_read target / scope / source / lines / mode / tail 读屏;默认 digest(前段信号行 + 尾部 30 行),mode="raw" 看全量;agent 视图不可用时回退 pane
herdr_wait target / until / timeoutMs 等某个 agent 落定;超时只表示没观测到
herdr_prompt target / prompt / wait / force 给别的 agent 下发 prompt;agent_not_ready 时回退 pane run 直接键入,via 说明实际路径
herdr_run pane / command 在别的 pane 里原子下发一条命令(文本 + Enter)
herdr_create kind / workspace / label / cwd / direction / focus 建 workspace / tab / 兄弟 pane,默认不动焦点
herdr_agent_start name / kind / pane / timeoutMs / agentArgs 在停着提示符的 shell pane 里启动 agent
herdr_close pane / dryRun 关 pane;默认 dryRun=true 只预览

exitCode 语义:0 成功 / 1 herdr 错误 / 2 参数或前置校验失败(没下发)/ -1 本地超时(此时 ok 仍为 true:本地截止时间只说明没观测到)。本地截止时间默认 120s,herdr_wait 是它请求的等待 + 30s。

环境变量(pi 侧用 PI_ 前缀,语义同 dsh 的 DSH_HERDR_*):

变量 默认 说明
PI_HERDR_PROMPT_GUARD on 置 off 关闭「目标 working 时拒绝下发」的插队保护
PI_HERDR_READ_MODE digest herdr_read 的默认渲染模式,可设 raw
PI_HERDR_READ_TAIL_LINES 30 digest 原样保留的尾部行数

相对旧 action 面的能力差异

0.2.x 的 herdr_layout / herdr_pane / herdr_agent 三个 action 工具已整体移除:两套面共存会让模型选哪套全凭运气。随之丢掉的操作按需直连 CLI,主要是 pane wait-output(含 echoedInput 判定)、pane send-text / send-keys、pane list / layout、tab list / focus、workspace focus、agent focus / rename / send-keys。herdr_overview 覆盖 agent list,herdr_read 覆盖 agent read / pane read(不含 --format / --raw)。

批量重启 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 识别规则(0.9.5 的 herdr agent explain 依旧显示 rule: none、screen_detection_skip_reason: full_lifecycle_hook_authority,内置规则只有一条 working_literal,匹配 Working...),所以 ask_user_question / confirm 弹窗期间 pane 会一直停在 working。本扩展的 blocked 上报段落订阅 pi core 的 ui_prompt_start / ui_prompt_end(select/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-state 与 pane-agent-tools 两个扩展静默跳过(门槛见「扩展结构」),/pi-hot-restart 仍可用:它通过 PATH 调用 herdr CLI(agent list / pane process-info / pane run),不依赖上述变量。

测试

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

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

tests/pane-agent-tools.test.ts —— 33 个用例,按工具分组,全部走假 pi.exec:锁的是「herdr 的某种结局被归成哪个信封」以及这层自己加的守卫,而不是 herdr 服务器怎么答。

  • 八个工具的名字与加载门槛(不在 herdr 里一个都不注册;旧 action 面不再存在)
  • herdr_overview:三段拼装、workspace 收窄、limit 截断写明隐藏条数、单段失败仍给其余段
  • herdr_read:digest 压缩与 raw、agent→pane 回退(via 说明原因)、失败不压缩
  • herdr_wait:落定、herdr timeout 的「没观测到」文案、其他错误按普通失败
  • herdr_prompt:语义路径、--wait、插队保护(含 force)、自己 pane、agent_not_ready 回退、pane id 解析失败、agent_blocked / agent_prompt_stalled 的反向处理
  • herdr_run / herdr_create / herdr_agent_start / herdr_close:参数拼装与守卫(默认 dry-run、名字与 timeout 边界、agentArgs 透传)

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.extensions 与 extensions/ 下的扩展目录一一对应(不漏声明、也不留死路径)、每个扩展目录都有 index.ts 入口且顶层没有散落的扩展文件、扩展之间没有跨目录 import,并且单独加载每个扩展时只会注册自己的资源(herdr-agent-state 只挂状态监听,pi-hot-restart 只有 /pi-hot-restart,pane-agent-tools 只有八个 manager 工具)。

参考