shux
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
Chineseshux — terminal multiplexer with a JSON-RPC API + pixel snapshotter
shux — 具备JSON-RPC API与像素快照功能的终端复用器
Install
安装
sh
npx skills add indrasvat/shux --global --yesshux is a Rust terminal multiplexer (sessions / windows / panes, like tmux)
that also exposes a length-prefixed JSON-RPC surface over UDS and TCP,
atomic declarative templates, optimistic concurrency on every entity, a
sealed event bus, and a built-in rasterizer that returns PNG bytes for any
pane — no terminal emulator in the loop.
sh
npx skills add indrasvat/shux --global --yesshux是一款基于Rust开发的终端复用器(支持会话/窗口/面板,类似tmux),它还通过UDS和TCP暴露了带长度前缀的JSON-RPC接口,提供原子化声明式模板、全实体乐观并发机制、封闭事件总线,以及内置光栅化器——无需终端模拟器即可返回任意面板的PNG字节数据。
When to reach for it
适用场景
Pick shux instead of the alternatives when any of these apply:
- You need to drive a TUI from outside (agent, CI, script) without a human at the keyboard.
- You want a PNG of what a terminal looks like right now, headless, no display server.
- You're running scripted CLI/REPL interactions that need typed keystrokes and known wait points.
- You're doing visual regression on a TUI you built (Bubbletea, Charm, ratatui, anything).
- You want declarative workspace templates that apply atomically.
If you're a human at a keyboard and tmux works for you, keep using tmux.
When a human does attach to shux, the normal interactions should still feel
modern: click panes to focus, drag borders to resize, drag visible text to copy
via OSC 52, and right-click a visible selection for the inline Copy / Clear
menu. Reserve prefix copy mode for scrollback, search, and keyboard-only
selection.
当以下任意一种情况成立时,选择shux替代其他工具:
- 你需要从外部(Agent、CI、脚本)驱动TUI,无需人工操作键盘。
- 你需要获取终端当前状态的PNG截图,无需显示服务器的无头环境。
- 你需要运行脚本化CLI/REPL交互,需模拟按键输入并处理等待逻辑。
- 你需要为自己开发的TUI(Bubbletea、Charm、ratatui等)做视觉回归测试。
- 你需要原子化应用的声明式工作区模板。
如果你是人工操作键盘且tmux能满足需求,建议继续使用tmux。当用户连接到shux时,常规交互体验依然现代:点击面板切换焦点、拖动边框调整大小、拖动可见文本通过OSC 52复制、右键选中内容调出内嵌的复制/清除菜单。保留前缀复制模式用于回滚、搜索和纯键盘选择。
Where shux artifacts live: .shux/
.shux/shux文件存储位置:.shux/
.shux/Run once per project. It creates a top-level dir:
shux init.shux/.shux/
├── templates/ # spec.toml files you commit (committed)
├── scripts/ # automation scripts you commit (committed)
├── goldens/ # reference PNGs for visual regression (committed)
├── out/ # snapshots, diffs, logs, anything ephemeral (gitignored)
└── .gitignore # ignores `out/`When you write code that produces shux artifacts:
- Put templates under (apply with
.shux/templates/).shux state apply .shux/templates/<name>.toml - Put driver scripts under .
.shux/scripts/ - Write snapshots, diffs, debug logs into (gitignored by default).
.shux/out/ - Commit golden images to so visual-regression diffs have a ground truth.
.shux/goldens/
Never pollute , , or the project root with shux output.
.claude/~/每个项目只需运行一次,它会在项目根目录创建文件夹:
shux init.shux/.shux/
├── templates/ # 需提交的spec.toml文件 (已提交)
├── scripts/ # 需提交的自动化脚本 (已提交)
├── goldens/ # 视觉回归测试用的参考PNG (已提交)
├── out/ # 快照、差异文件、日志等临时文件 (已加入git忽略)
└── .gitignore # 忽略`out/`目录当你编写生成shux产物的代码时:
- 将模板放在目录下(通过
.shux/templates/应用)。shux state apply .shux/templates/<name>.toml - 将驱动脚本放在目录下。
.shux/scripts/ - 将快照、差异文件、调试日志写入目录(默认已加入git忽略)。
.shux/out/ - 将基准图片提交到目录,为视觉回归测试提供对比基准。
.shux/goldens/
请勿将shux输出文件混入、用户主目录或项目根目录。
.claude/80% quickstart (three RPCs)
80%快速入门(三个RPC调用)
bash
undefinedbash
undefined1. Spawn a session running any command (or shell). Capture the
1. 生成运行任意命令(或Shell)的会话。从响应中获取pane_id,以便后续调用定位目标面板。
pane_id from the response so the next calls can target it. CLI
除非指定--cwd参数,否则CLI会话创建默认使用调用者的当前目录。
session creation starts in the caller's current directory unless
—
you pass --cwd.
—
RESP=$(shux --format json session create demo -d --title demo -- lazygit)
PID=$(echo "$RESP" | jq -r .pane_id)
RESP=$(shux --format json session create demo -d --title demo -- lazygit)
PID=$(echo "$RESP" | jq -r .pane_id)
2. Drive it.
2. 驱动会话。
shux pane set-size -s demo --cols 200 --rows 60
shux pane send-keys -s demo --text 'j' # text input
shux pane send-keys -s demo --data 'Gw==' # base64 control (here: Esc)
shux pane set-size -s demo --cols 200 --rows 60
shux pane send-keys -s demo --text 'j' # 文本输入
shux pane send-keys -s demo --data 'Gw==' # Base64编码的控制字符(此处为Esc)
3. Get a PNG back.
3. 获取PNG截图。
shux --format json pane snapshot -s demo
| jq -r .png_base64 | base64 -d > frame.png
| jq -r .png_base64 | base64 -d > frame.png
shux --format json pane snapshot -s demo
| jq -r .png_base64 | base64 -d > frame.png
| jq -r .png_base64 | base64 -d > frame.png
Tear down when done.
完成后销毁会话。
shux session kill demo
Every CLI verb maps 1:1 to an RPC method (RPC dots become CLI spaces —
`session.create` ↔ `shux session create`). Drop to the raw form with
`shux rpc call <method> --params @file` whenever you'd rather write
the payload in JSON directly. `--params -` reads from stdin.
For declarative multi-pane workspaces, use a template:
```tomlshux session kill demo
每个CLI命令都与RPC方法一一对应(RPC中的点号对应CLI中的空格——`session.create` ↔ `shux session create`)。如果更倾向于直接编写JSON负载,可使用`shux rpc call <method> --params @file`形式调用原生RPC。`--params -`表示从标准输入读取参数。
对于声明式多面板工作区,可使用模板:
```tomlspec.toml
spec.toml
[session]
name = "review"
[[windows]]
title = "vivecaka"
[[windows.panes]]
command = ["vivecaka", "--repo", "cli/cli"]
```bash
shux state apply spec.toml # atomic — all or nothing[session]
name = "review"
[[windows]]
title = "vivecaka"
[[windows.panes]]
command = ["vivecaka", "--repo", "cli/cli"]
```bash
shux state apply spec.toml # 原子化操作——要么全部生效,要么全部不生效Tools shux replaces
shux可替代的工具
| If you'd reach for | For this job | Use this shux primitive |
|---|---|---|
| Multiplex sessions / windows / panes | |
| iTerm2 (Python SDK / AppleScript) | Drive a terminal app from outside | |
| Scripted CLI / REPL interaction | |
iTerm2 | Block until screen contains (or stops containing) a needle | |
| Record a terminal session | |
| Generate TUI demo GIFs / WebPs | |
| Still PNG of a terminal frame | |
| iTerm2 broadcast input | Send keystrokes to many panes at once | |
| Replay a recorded session | Re-feed VT bytes through a fresh pane → |
GNU parallel | Run N tasks in N panes, watch in one place | Template with N panes + RPC orchestrator |
| Custom Bubbletea / ratatui test harness | Visual regression for your TUI | |
| 原本会选用的工具 | 对应工作场景 | 可使用的shux功能 |
|---|---|---|
| 复用会话/窗口/面板 | |
| iTerm2(Python SDK / AppleScript) | 从外部驱动终端应用 | |
| 脚本化CLI/REPL交互 | |
iTerm2 | 阻塞直到屏幕出现(或消失)指定内容 | |
| 录制终端会话 | |
| 生成TUI演示GIF/WebP | |
| 终端界面静态PNG截图 | |
| iTerm2广播输入 | 同时向多个面板发送按键 | |
| 重放录制的会话 | 将VT字节重新输入到新面板 → |
GNU parallel | 在N个面板中运行N个任务,集中监控 | 包含N个面板的模板 + RPC编排器 |
| 自定义Bubbletea/ratatui测试框架 | TUI视觉回归测试 | |
The common RPC surface
通用RPC接口
Every CLI verb maps 1:1 to an RPC method — RPC dots become CLI spaces
( ↔ , ↔
). All RPCs accept JSON in, return JSON out, on
stdin/stdout. lists the full request/response
shape per method.
session.createshux session createpane.send_keysshux pane send-keysreferences/api.md| Category | Methods |
|---|---|
| Session | |
| Window | |
| Pane I/O | |
| Pane mgmt | |
| Window snap | |
| State | |
Every entity carries a field. Pass on
mutating RPCs to get optimistic-concurrency rejection (error code
) on stale writes — useful when multiple agents collaborate.
versionexpected_version-32002每个CLI命令都与RPC方法一一对应——RPC中的点号对应CLI中的空格( ↔ , ↔ )。所有RPC均接收JSON输入并返回JSON输出,通过标准输入/输出交互。列出了每个方法的完整请求/响应格式。
session.createshux session createpane.send_keysshux pane send-keysreferences/api.md| 分类 | 方法 |
|---|---|
| 会话 | |
| 窗口 | |
| 面板I/O | |
| 面板管理 | |
| 窗口快照 | |
| 状态 | |
每个实体都包含字段。在修改型RPC调用中传入参数,可在写入过时数据时触发乐观并发拒绝(错误码)——适用于多Agent协作场景。
versionexpected_version-32002Common control bytes for pane.send_keys --data
pane.send_keys --datapane.send_keys --data
常用控制字节
pane.send_keys --dataThe field sends raw text. For control characters, use (base64).
textdata| Key | Bytes | Base64 |
|---|---|---|
| Enter | | |
| Escape | | |
| Tab | | |
| Backspace | | |
| Ctrl+C | | |
| Ctrl+L | | |
| Up arrow | | |
| Down arrow | | |
textdata| 按键 | 字节 | Base64编码 |
|---|---|---|
| 回车键 | | |
| 退出键 | | |
| 制表键 | | |
| 退格键 | | |
| Ctrl+C | | |
| Ctrl+L | | |
| 上箭头 | | |
| 下箭头 | | |
Decide which method to use
方法选择指南
Need to spawn something? → session.create (shux session create --title <label> -- <cmd>)
Need a multi-pane workspace? → state.apply (shux state apply spec.toml)
Need to type into a TUI? → pane.send_keys (shux pane send-keys --text|--data)
Need pixel feedback of one pane? → pane.snapshot (shux pane snapshot)
Need a snapshot of the whole
window (borders, titles, status)? → window.snapshot (shux window snapshot)
Need a snapshot of the session's
active window? → session.snapshot (shux session snapshot)
Need plain text of the screen? → pane.capture (shux pane capture)
Need to block until text appears? → pane.wait_for (shux pane wait-for --text|--regex)
Need a stream of PTY output? → pane.output.watch (event-bus, sealed)
Want raw RPC for a new method? → shux rpc call <method> --params @file需要生成会话? → session.create (shux session create --title <标签> -- <命令>)
需要多面板工作区? → state.apply (shux state apply spec.toml)
需要向TUI输入内容? → pane.send_keys (shux pane send-keys --text|--data)
需要单个面板的像素级反馈? → pane.snapshot (shux pane snapshot)
需要整个窗口(边框、标题、状态栏)的快照? → window.snapshot(shux window snapshot)
需要会话活动窗口的快照? → session.snapshot(shux session snapshot)
需要屏幕纯文本内容? → pane.capture (shux pane capture)
需要等待指定文本出现? → pane.wait_for (shux pane wait-for --text|--regex)
需要PTY输出流? → pane.output.watch(事件总线,封闭)
需要调用原生RPC方法? → shux rpc call <方法> --params @fileExtend shux with a process plugin
使用进程插件扩展shux
shux has a process-plugin host. A plugin is any executable that speaks
shux's line-delimited JSON-RPC dialect on stdin/stdout — bash, python,
node, anything. It:
- Subscribes to bus events listed in its manifest.
- Calls any registered shux RPC method (,
window.rename,pane.send_keys, …) to react.state.apply - Publishes its own events via . The daemon namespaces them under
event.publishso other plugins (orplugin.<plugin_id>.<type>) can subscribe to them cleanly — see references/plugins.md.shux events watch --filter plugin.<id>. - Persists its own state across hot reload via
. The CLI pins it to the calling project's root at install time (walks up from cwd for
plugin.state.get/set/delete, anchors there) — so a daemon shared across checkouts keeps each project's state isolated. Atomic writes, 256 KiB cap, per-plugin isolation. Path:.shux/.<project-root>/.shux/plugins/<name>/state.json
bash
shux plugin install ./my-plugin.sh # spawn, handshake, register. Hot reload ON
# by default — saves respawn the plugin
# in <500ms. Use `--no-watch` to opt out.
shux plugin list # name · version · pid · subscribes · watching
shux plugin reload <name> # manual hot-reload tick (kill + respawn)
shux plugin kill <name> # graceful shutdown (2s) → SIGKILL
shux plugin grant <name> <method> # opt the plugin in to a sensitive RPC.
# Default-deny model: every plugin RPC
# passes through a permission check
# before reaching the daemon router
# (v0.19+).
shux plugin grant <name> <method> --target <id> # scoped to one entity
shux plugin grant <name> <filter> --subscribe # widen manifest subscribes
shux plugin revoke <name> <method> # mirror of grant
shux plugin grants <name> # show the allow-set
shux plugin audit <name> --tail 50 # tail NDJSON audit logReference plugins:
- — smallest working example (~50 lines): handshake + PTY output + state mutation.
examples/plugins/hello/plugin.sh - — subscribes to
examples/plugins/watcher/plugin.sh, emits a namespacedpane.exitedviaplugin.watcher.command_exitfor downstream plugins.event.publish - — VT-poll watchdog + settle-snapshot archive ⭐ + window-aggregation OS notifications for coding-agent panes (claude / codex / opencode / gemini). Identifies the agent on
examples/plugins/conductor/plugin.sh, pollspane.createdevery 2 s, classifies state, updates the pane border title (pane.capture), auto-dismisses trust prompts viaagent · ○|●|✓|!. On everypane.send_keystransition, callsready→idleand saves the resulting PNG topane.snapshotwith a rolling.shux/conductor/snapshots/— a feature literally impossible in any tool that doesn't own its own rasterizer. Tracks per-window in-flight counts and fires ONEINDEX.tsv/osascriptnotification when a window's last in-flight agent goes idle.notify-send
Full protocol — handshake, event payload shape, RPC-out direction,
, shutdown grace, UUID vs name rule, what's not in v0 — lives in
references/plugins.md.
event.publishshux内置进程插件宿主环境。插件可以是任何能通过标准输入/输出与shux进行行分隔JSON-RPC通信的可执行文件——bash、python、node等均可。插件具备以下能力:
- 订阅其清单中列出的总线事件。
- 调用任何已注册的shux RPC方法(、
window.rename、pane.send_keys等)做出响应。state.apply - 发布自定义事件,通过方法。守护进程会将事件命名为
event.publish,以便其他插件(或plugin.<plugin_id>.<type>)能清晰订阅——详情见references/plugins.md。shux events watch --filter plugin.<id>. - 持久化自身状态,支持热重载,通过方法。安装插件时,CLI会将状态存储路径固定在调用项目的根目录(从当前目录向上查找
plugin.state.get/set/delete目录作为锚点)——因此跨代码共享的守护进程会隔离每个项目的状态。支持原子写入,每个插件状态上限为256 KiB。存储路径:.shux/。<项目根目录>/.shux/plugins/<名称>/state.json
bash
shux plugin install ./my-plugin.sh # 启动插件、握手、注册。默认开启热重载——插件重启耗时<500ms。使用`--no-watch`关闭热重载。
shux plugin list # 显示名称·版本·进程ID·订阅事件·是否监控
shux plugin reload <名称> # 手动触发热重载(杀死并重启插件)
shux plugin kill <名称> # 优雅关闭(等待2秒)→ SIGKILL强制终止
shux plugin grant <名称> <方法> # 授权插件调用敏感RPC方法。
# 默认拒绝模型:每个插件RPC调用都会经过权限检查
# 后才会到达守护进程路由(v0.19+版本)。
shux plugin grant <名称> <方法> --target <ID> # 限制权限至指定实体
shux plugin grant <名称> <过滤器> --subscribe # 扩展插件订阅事件范围
shux plugin revoke <名称> <方法> # 撤销权限,与grant操作对应
shux plugin grants <名称> # 显示当前允许调用的方法集合
shux plugin audit <名称> --tail 50 # 查看最新50条NDJSON审计日志参考插件:
- — 最小可用示例(约50行):握手 + PTY输出 + 状态变更。
examples/plugins/hello/plugin.sh - — 订阅
examples/plugins/watcher/plugin.sh事件,通过pane.exited发送命名空间为event.publish的事件供下游插件使用。plugin.watcher.command_exit - — VT轮询监控 + 就绪-空闲状态快照归档 ⭐ + 窗口聚合系统通知,适用于编码Agent面板(claude / codex / opencode / gemini)。在
examples/plugins/conductor/plugin.sh事件中识别Agent,每2秒轮询pane.created,分类状态,更新面板边框标题(pane.capture),通过agent · ○|●|✓|!自动处理信任提示。每次从就绪切换到空闲状态时,调用pane.send_keys并将生成的PNG保存到pane.snapshot目录,同时维护滚动更新的.shux/conductor/snapshots/——这一功能在不具备自有光栅化器的工具中完全无法实现。 跟踪每个窗口的活跃Agent数量,当窗口最后一个活跃Agent变为空闲时,触发一次INDEX.tsv/osascript通知。notify-send
完整协议——握手流程、事件负载格式、RPC输出方向、方法、优雅关闭、UUID与名称规则、v0版本未包含的功能——详见references/plugins.md。
event.publishDeep dives
深度指南
| Topic | Where |
|---|---|
| Full RPC inventory + JSON request/response shapes | references/api.md |
| Apply-template TOML shape, lowering rules, multi-window workspaces | references/templates.md |
| Scenario-driver patterns (send/wait/snap loops, golden-image diff) | references/scenarios.md |
| Process plugins — protocol, manifest, event/RPC shapes, gotchas | references/plugins.md |
| 主题 | 文档位置 |
|---|---|
| 完整RPC清单 + JSON请求/响应格式 | references/api.md |
| 模板TOML格式、转换规则、多窗口工作区 | references/templates.md |
| 场景驱动模式(发送/等待/快照循环、基准图片对比) | references/scenarios.md |
| 进程插件——协议、清单、事件/RPC格式、注意事项 | references/plugins.md |
Worked examples
实践示例
- examples/headless-tui-test.md — drive a TUI in CI, snapshot at every step, diff against checked-in goldens.
- examples/vision-llm-feedback.md — agent builds a Bubbletea app, snapshots its own UI, feeds PNG to a vision model, self-corrects.
- examples/replace-tmux-workflow.md — common patterns translated to shux.
tmux new-session / send-keys / capture-pane
- examples/headless-tui-test.md — 在CI环境中驱动TUI,每一步都生成快照,与已提交的基准图片对比。
- examples/vision-llm-feedback.md — Agent构建Bubbletea应用,自身UI生成快照,将PNG输入视觉模型,实现自我修正。
- examples/replace-tmux-workflow.md — 常见模式转换为shux操作。
tmux new-session / send-keys / capture-pane
Gotchas
注意事项
- is synchronous (oneshot ack). The next
pane.set_sizesees the new dims. No sleep needed between them.pane.snapshot - caps the output at 16M pixels (~4000×4000). Resize first if you'd exceed.
pane.snapshot - targets the session's active pane (often the last-spawned). In multi-pane templates pass
pane wait-for -s SESSION(from--pane <UUID>orpane list'sstate.apply) — otherwise the wait will silently watch the wrong pane and time out.spawn_results - is JSON-quoted text. For raw control bytes (Esc/Enter/Tab/Ctrl+letter), use
pane.send_keys --textwith base64.--data - atomically commits the graph but PTY spawn outcomes are reported per-pane in
shux state apply foo.toml. A spawn failure does not roll back the graph.spawn_results - The first pane of the first template window is folded into the session's auto-created initial window — there is no phantom default-shell pane.
- The bundled font (JetBrains Mono Regular, OFL-1.1) is monochrome. Color emoji and CJK glyphs render as tofu in the current rasterizer (P2 roadmap).
.notdef
- 是同步操作(一次性确认)。后续的
pane.set_size会使用新的尺寸,无需在两者之间添加等待。pane.snapshot - 输出上限为1600万像素(约4000×4000)。如果超出此范围,请先调整面板尺寸。
pane.snapshot - 会定位到会话的活跃面板(通常是最后生成的面板)。在多面板模板中,需传入
pane wait-for -s SESSION(来自--pane <UUID>或pane list的state.apply)——否则等待操作会静默监控错误的面板并超时。spawn_results - 接收JSON转义后的文本。对于原始控制字节(Esc/Enter/Tab/Ctrl+字母),需使用
pane.send_keys --text参数传入Base64编码内容。--data - 会原子化提交状态图,但PTY生成结果会在
shux state apply foo.toml中按面板返回。生成失败不会回滚状态图。spawn_results - 模板第一个窗口的第一个面板会合并到会话自动创建的初始窗口中——不存在默认Shell的虚拟面板。
- 内置字体(JetBrains Mono Regular,OFL-1.1许可证)为单色字体。当前光栅化器中,彩色表情符号和CJK字符会显示为豆腐块(已列入P2 roadmap)。
.notdef