pi-bg-shell
v0.3.0
Published
Background shell tasks for the Pi coding agent: start non-blocking commands with bash_bg and get woken with the output when they exit, Claude Code style.
Maintainers
Readme
pi-bg-shell
English | 简体中文
Pi 编码 agent 的后台 shell 任务扩展——Claude Code 式体验:启动命令、继续干活、命令退出时输出自动送回。
你: 跑一下测试套件,趁这个时间顺便起草 changelog
Agent: bash_bg → Started background task #1 (pid 4242): npm test
Agent: (继续写 changelog、回你消息、读文件……)
通知: Background task #1 completed (exit 0, 42.1s): npm test
--- stdout tail (1234/5678 bytes) ---
……为什么需要它
Pi 内置的 bash 工具会阻塞整个 agent 轮次直到命令结束——两分钟的测试套件就是两分钟的会话冻结。pi-bg-shell 给 agent 三个工具,把长命令(构建、测试套件、dev server、文件监听)丢到后台,命令一退出就自动唤醒 agent 并送上输出——不轮询、不蹲守。
工具
| 工具 | 作用 |
|------|------|
| bash_bg | 用 bash -c 后台执行命令,立即返回任务 id。每流 1 MiB 尾部缓冲捕获输出;超出即把全量历史溢写到文件。 |
| bg_status | 列出全部任务的单行摘要,或按 id 取单个任务的完整状态与输出尾部。列表末尾附全局池分区:其他会话的脱离任务,可见但未认领。 |
| bg_kill | 按 id 终止任务(默认 SIGTERM)。池任务的杀权归创建者或任一订阅者——即能在自己注册表里看到它的任何人。 |
| bg_adopt | 按清单路径(来自 bg_status 全局池分区)把一个外来池任务订阅到本会话:完成通知、模式唤醒、杀权随之而来(ADR-0007)。 |
完成通知以 bg-shell-notify 消息经 pi.sendMessage({ triggerTurn: true }) 送进对话——与官方 file-trigger 示例、pi-subagents 的完成通知同一条唤醒原语。100 ms 窗口内的并发完成合并为一条消息,fan-out 不会冲垮会话。默认墙钟上限每任务 10 分钟(timeout_sec 可覆盖;0 关闭)。
任务还在跑时的唤醒
退出不再是唯一的交付时机(ADR-0005):
on_pattern——字面子串、逐行匹配(grep 语义)。首次命中即唤醒 agent,附命中行与输出尾部;任务继续运行。on_pattern_all改为每次命中都唤醒(限频),on_pattern_stop在投递命中后立即停掉任务。运行中bg_status显示running·matched×N子状态。对会反复发生的告警(守望),优先常驻任务(timeout_sec: 0)+on_pattern_all,而非每次命中后重新拉起;标记只在状态变化时输出,持续满足的条件会按 10s 下限反复唤醒。report_every_sec——任务运行期间每 N 秒(钳到 ≥5)投递一份进度报告(已运行时长 + 输出尾部),不中断任务。
Agent: bash_bg {command: "./day_runner.sh", timeout_sec: 0,
on_pattern: "ROOTED", report_every_sec: 600}
Notify: Background task #4 still running (600.2s elapsed, report every 600s): ./day_runner.sh …
Notify: Background task #4 pattern match (on_pattern "ROOTED", match #1, running 1412.8s): ./day_runner.sh
--- matched line (stdout) ---
ROOTED device 3 ready
…两者都只控制「何时交付信息」,不是编排:被唤醒后的下一步仍由 agent 在对话里决定。
脱离任务(活过 pi 退出)
bash_bg { detach: true } 让命令在自己的 session(setsid)里跑,输出直写文件——退出 pi 不会杀它(重启机器仍会)。真实退出码由包装层落盘;tmpdir()/pi-bg-shell/ 下的清单把根目录变成机器级任务池(ADR-0007):同 session id 复活的会话(pi -c / --resume)自动重新订阅自己的幸存任务;其他会话在 bg_status 的全局池分区里看得见,用 bg_adopt 显式认领。无人见证的死亡由下一个扫描的会话补发一次(合并成一条、标注 unattended — finished while pi was away)——「跑完汇报」的承诺跨任何重启成立。脱离任务默认不限时——需要限时就显式传 timeout_sec。
You: 把 24h 设备烤机跑起来,脱离,ROOTED 了叫我
Agent: bash_bg {command: "./soak.sh", detach: true, on_pattern: "ROOTED"}
… pi 退出,次日重开 …
Notify: Background task #1 pattern match (on_pattern "ROOTED", match #1, running 9h12m): ./soak.shbg_kill 对脱离任务发整进程组信号。
Windows 上 bash 通常是 WSL 中继,继承的 Windows 文件句柄跨不了这个边界——脱离任务在该平台改走 shell 自重定向(路径译为 /mnt/<drive>/…,( cmd ) >> OUT 2>> ERR),输出文件与退出码仍落在 bg_status 和下会话领养预期的位置。包装层本身以宿主侧脚本文件传递(bash <file>,不经 -c argv),退出码不会被 WSL 中继的参数重引号展开成 0。Windows 上 detached 中继改经 GUI 子系统的 wscript 启动器拉起——仅靠 detached+windowsHide 仍会弹控制台——因此启动零弹窗,且任务不再被「关闭终端窗口」连带杀死;bg_kill 在 Windows 上用 taskkill /t /f 整树终止(跨 WSL 边界没有 POSIX 信号语义),并写入杀归因标记,让其他订阅者的通知注明凶手。自有脱离任务的 on_pattern 从第 0 字节起监视——启动早期的 milestone 同样会唤醒会话(认领任务另有全量回放)。任务池是公告板不是私有领地(ADR-0007):每个会话都能看到每份清单;订阅是每 pid 一个独占创建的标记文件(并发会话无竞态);工件只在任务终态、已汇报、无活订阅、创建进程消失后才被回收。
用户界面
任务运行期间,输入框上方常驻卡片式状态窗,每 500ms 重绘——计时逐秒在走、spinner 轮换,每张卡片带原始命令与当前输出及体量:
bg · background
⠸ #1 chatty-ticker · running · 1m 12s / 10m
cmd: for i in $(seq 150); do echo …
⎿ ↓ 18.0k · [16:52:31] chatty heartbeat #93 …
⠋ #2 quiet-soak · running · 4m 2s / 10m
cmd: sleep 300
2 running · /bg panel表头与任务名保持白色;装饰(· running ·、cmd:、⎿)dim,统计 muted,命令与实时输出行 toolOutput 色;每行按终端宽度截断。运行中任务超过六个折叠为 … +N more;最后一个任务结束后 widget 连同 ticker 一起消失。运行行在装了超时时于已跑时长后追加超时预算(1m 12s / 10m)——timeout_sec: 0 与 detached 任务(无默认超时)不显示。
所有查看类指令一律拉起弹窗面板,不往正文窗口灌内容:
| 指令 | 行为 |
|------|------|
| /bg | 弹窗面板:任务列表,↑↓ 选择、Enter 详情、K 终止、r 刷新、q/esc 关闭 |
| /bg <id> | 直接打开该任务详情(状态、退出码、命令、运行中超时预算与剩余倒计时、输出尾部、溢写路径) |
| /bg tail <id> [bytes] | 同详情视图,可指定更大的尾部字节数(默认 4096) |
| /bg log <id> | 同详情视图;被截断的任务会展示全量输出的溢写文件路径 |
| /bg kill <id> [signal] | 终止任务(默认 SIGTERM),toast 确认 |
Tab 补全提供子命令与任务 id。无界面模式(RPC/JSON/print)退化为 notify 摘要。
安装
pi install git:github.com/No-World/pi-bg-shell本地开发:
git clone https://github.com/No-World/pi-bg-shell
pi --extension /path/to/pi-bg-shell/extensions/bg-shell/index.ts/reload 与会话切换保留运行中的任务;退出 pi 时 SIGTERM+SIGKILL 全部子进程并清理溢写文件。
配置
| 环境变量 | 作用 |
|---------|------|
| PI_BG_SHELL_TIMEOUT_SEC | 未传 timeout_sec 时的默认限时(秒)。默认 600;设为 0 关闭默认限时。在 pi 启动时读取一次,修改变量后需重启 pi 生效。 |
bash_bg 显式传入的 timeout_sec 永远优先于环境变量默认值。
唤醒机制
bash_bg拉起子进程;工具结果瞬间返回。- 退出时注册表经
onExit钩子发出最终快照。 - debounce 窗口(100 ms)把并发完成合并成一条消息。
- notifier 调
pi.sendMessage(..., { triggerTurn: true })——空闲会话直接开新一轮 agent 运行;busy 会话由 pi 排队。 - agent 读到输出尾部(及全量输出的溢写文件路径),继续干活。
任务注册表挂在 globalThis(Symbol.for 键)上,跨扩展 reload 存活;入口工厂每次加载重绑 exit 钩子,reload 后的完成照样到达 agent。设计权衡记录在 docs/adrs/0003-background-shell-wake-mechanism.md。
开发
npm install
npm test # node:test 套件 + 机械文档门禁
npm run typecheck # 严格 TypeScript 检查
npm run check:docs # 单独跑文档门禁
pi --extension . # 交互式体验仓库带完整工程 harness:带机械索引门禁的 ADR(docs/adrs/README.md)、坑位手册(docs/PITFALLS.md)、postmortem 闭环模板(docs/postmortems/README.md)、术语表(CONTEXT.md),以及与本地门禁同清单的 CI lane(.github/workflows/pr-checks.yml)。贡献规则见 AGENTS.md。
