@muzi3/hc
v0.2.0
Published
Pi extensions for herdr-core (hc): /hc-reload reloads skills, extensions and keybindings in every pi window in place, and herdr-agent-state reports working/blocked/idle to the herdr pane so waiting-for-input panes show as blocked.
Maintainers
Readme
@muzi3/hc
给 Pi 用的 hc(herdr-core)扩展包,两个独立扩展:
hc-reload:/hc-reload一次让 hc 里所有 pi 窗口重新加载 skills / extensions / keybindings / prompts / themes / context files。herdr-agent-state:把 pi 的 agent 状态(working / blocked / idle)上报给 herdr,让 pane 在等你回答时显示成 blocked。自动生效,无命令。
/hc-reload它做什么
等价于在每个 pi 窗口敲一次 pi 自带的 /reload,但不杀进程、不丢 session:
- 目标取自
hc ls --json:agent为pi、state为live、本机(地址不带machine/)的终端。 - 用
hc keys真敲按键:/reload逐字符敲进去再敲回车,先读屏确认输入框为空。 - 跳过
working(敲进去等于插话)与blocked(敲等于回答弹窗)。 - 跳过当前窗口自己,循环结束后调
ctx.reload()——reload 会拆掉本扩展,所以放最后。 - 发送前弹确认框,列出目标与每个跳过原因。
herdr-agent-state:等回答时让 pane 变红
herdr 的危险标记只认 pane 自己上报的 blocked:herdr-core 既不抓屏,也没有上报 socket,状态来自程序输出里的 OSC 7501(hc --skill:"Status comes from the program's OSC 7501 reports")。所以以普通 pi 窗口在 Herdr APP 里打开的会话,弹 ask_user_question / 选择框时 hc 一直显示 working,切走再回来根本看不出它在等你。
装上本包后在 pane 里生效,符合条件才上报:
- 上报方式是往 stdout 写
ESC ] 7501 ; state=<state>[:kind=question] BEL。herdr 把这条记录从输出里摘掉,不渲染,所以不会脏了画面;也只写state和kind——msg/id/title这类字段 herdr 会直接丢掉整条记录。 - 只在 hc 面板里报(
HC_TERMINAL_ID存在)。其他终端不认识]7501,写进去就是脏输出。注意 hc 面板里没有HERDR_ENV/HERDR_PANE_ID,所以官方的门槛条件在这里不成立。 - 只报 TUI 会话。herdr-chat 打开的 pi 走 RPC,那边 herdr-chat host 自己按 dialog 上报 blocked,重复上报只会打架;
print模式也没有 pane 可标。 - blocked 只在回合内、且真的挂起一个需要回答的提示时上报:回合外的装饰性 overlay(如 prompt-nav)不报,否则 pane 一开机就永久红点。
- 新会话、回合结束、会话关闭都会清掉遗留的 blocked,不会卡在红点上。
- 同一状态不重复写;
state:idle与state=idle语义相同,herdr 侧按最后一条生效。
为什么 blocked 不是只靠 ui_prompt_start
pi 的 ui_prompt_start 把嵌套提示合并成一个外层等待区间(dist/core/extensions/runner.js 的 withUIPrompt:const outerPrompt = this.uiPromptDepth++ === 0)。只要有常驻 overlay 在会话开始时就挂上并永不 resolve——prompt-nav 就是这样(extensions/index.ts:460 的 ctx.ui.custom<void>)——深度就卡在 1,之后所有提示都不再发 ui_prompt_start。实测:对话框明明在屏幕上,事件一条都没有。
所以 blocked 有两个信号源:
rpiv:ask-user:blocked(@juicesharp/rpiv-ask-user-question的ASK_USER_BLOCKED_EVENT,payload{ active, toolCallId },回答 / 取消 / 出错时在finally里发active: false)——按toolCallId收进一个集合,同时开多个问卷时关掉一个仍保持 blocked。这条不依赖 pi 的等待区间,是主力。ui_prompt_start/ui_prompt_end——覆盖 select / confirm / input 这类普通提示,只在收到过 start 的回合内才配对关闭。
已知缺口:装常驻 overlay(prompt-nav)时,pi 自己的权限 / 确认对话框同样会被合并掉,不会上报 blocked;此时只有 ask_user_question 能标红。
为什么不用 hc send(0.1.0 的 bug)
hc send 的说明是「paste text and press enter」,但那只对普通程序成立。目标是 agent 终端时,hc 走的是 note 通道,文本会变成目标对话里的一条用户消息:
❯ <hc from="t3g">/reload</hc>斜杠命令就这样被当成了聊天内容,永远不会执行——跟目标是不是 herdr-chat 会话无关,所有 pi 窗口都中招(实测:向空闲 TUI 窗口发 /reload,屏幕上出现上述消息,配置没重载)。
hc keys 才是真正的 TUI 输入:/ 会打开斜杠补全菜单,逐字符敲到 reload,回车即执行。代价是按键没有 hc send 的草稿保护,所以敲之前用 hc read --json 看一眼输入框:非空(有草稿)或看不出输入框(不是 pi TUI)就跳过并报原因,不覆盖别人的输入。
为什么不做成工具集
hc 和 herdr-app 都自带 --skill 手册,随 CLI 升级。把命令面复制进工具定义,版本一升就变成过期但看起来可用的说明——比没有更糟。所以这个包只补 CLI 没有的东西:一个批量动作。
怎么用 hc 本身,读 hc --skill;窗口与布局读 herdr-app --skill;参数不确定就 hc <cmd> --help。
装
pi install npm:@muzi3/hc
# 或从源码
pi install /path/to/pi-packages/packages/hc需要什么
- 已装 herdr-core(提供
hc),PATH 上有hc。 /hc-reload只在 hc 终端里注册(HC_TERMINAL_ID有值),否则没有批量重载的对象。herdr-agent-state只在 hc 面板里生效(HC_TERMINAL_ID),且只对 TUI 会话上报。
与 @muzi3/herdr 的关系
@muzi3/herdr 已废弃:面向 herdr 0.9.5,带 8 个 herdr_* 工具、状态上报扩展和 /pi-hot-restart。其中值得留的两样都搬到了本包——/pi-hot-restart 被 ctx.reload() + /hc-reload 取代,状态上报(含 blocked)就是上面的 herdr-agent-state。herdr_* 工具不再提供:用 hc / herdr-app 自带的 --skill 手册。
测试
node --test tests/tests/hc-reload.test.ts:纯函数(目标筛选与文案)。真正发命令的路径要连真实 hc 与 pi 窗口,属手工冒烟。tests/herdr-agent-state.test.ts:把process.stdout.write换成记录器,解析ESC ] 7501 ; ... BEL后断言上报(working / idle、ask_user 的 blocked + kind=question、常驻 overlay 占住等待区间时仍能报、多问卷并发、回合外 overlay 不报、非 TUI 不报、没有HC_TERMINAL_ID不注册)。
扩展加载冒烟(报 "No API key" 属正常,说明扩展已加载):
pi -ne -e extensions/herdr-agent-state/index.ts -p --no-session test许可
MIT。见 LICENSE。
