关于 tty7
tty7 是用于通过命令行操作 tty7 服务的非交互式客户端,适合需要管理终端窗格和委派编码任务的 Agent。它支持启动命令、读写窗格、等待命令或 Agent 状态,并可输出机器可读结果。
主要功能
- 01运行阻塞式或后台命令
- 02向窗格发送文本和按键
- 03捕获并读取窗格内容
- 04等待命令或 Agent 状态变化
- 05检查进程、端口和服务状态
适用场景
- 01在可见终端窗格运行开发服务器
- 02连接并操作 REPL、SSH 或调试器
- 03查看其他窗格中的进程与输出
- 04将编码任务委派给其他 Agent
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ l0ng-ai/ tty7 --skill tty7安装后在 Claude Code 中输入/tty7确认已加载。
附带文件references/commands.mdreferences/delegation.md
详细介绍
从命令行驱动 tty7
tty7 是 tty7 服务端的轻量级非交互客户端。每个动词都会返回并退出;--json 会让输出变为机器可读。GUI 不必运行——负责管理窗格的是服务端。
首先:你身处何处?
tty7 doctor
一张表就能回答你开始操作前需要了解的所有信息:服务端是否可访问、协议是否匹配、每个 Agent 的状态钩子是否已安装,以及是否设置了 TTY7_CONFIG_DIR / TTY7_WS / TTY7_PANE——也就是说,你是否正在 tty7 窗格内运行。
身处窗格内有两个重要原因:需要地址的动词(split、send、exec、capture、procs、wait、pane close)默认使用 $TTY7_PANE,而 run --keep 会将其窗格归入 $TTY7_WS。在 tty7 shell 外,你必须明确指定目标;否则会直接提示错误,而不是擅自猜测。
如果你打算委派任务给另一个 Agent,钩子这一行就很重要:没有这些钩子,Agent 不会报告状态,因此对其运行 tty7 wait 只会超时。
如果 tty7 doctor 显示服务端无法访问,请停止操作并告知用户——不要自行运行 tty7 server start。启动用户未要求启动的服务端,会改变其 GUI 所连接的对象。
你要做什么?
四种任务,四种方式:
- 运行不应阻塞你的任务,或需要真实 TTY 的任务——开发服务、长时间测试运行、TUI。运行命令。
- 与需要持续交互的对象对话——REPL、
ssh、调试器。使用相同的基本操作:发送、读取、重复。 - 查看这台机器正在做什么——其他窗格、其他 Agent、端口。查看周边情况,严格只读。
- 将工作交给另一个编码 Agent——一个工作者,或同时分派给多个工作者。请先阅读
references/delegation.md;简要说明见下文。
对于任何启动后自行完成任务并退出,且不需要终端或旁观者的操作,Bash 工具仍然是合适的选择。当进程的生命周期超出你的当前操作、需要真实 PTY,或应在用户的 tty7 窗口中可见时,窗格才真正派得上用场——最后一种情况往往正是使用窗格的全部目的。
地址
| 形式 | 含义 | 稳定? |
|---|---|---|
%42 | 一个窗格 | 是 — 窗格在其整个生命周期内都保留同一 ID |
@7 | 一个标签页,按整个机器的树形顺序编号 | 否 — 工作区或标签页出现或消失时,编号都会变化 |
@<full tab UUID> | 通过 ID 指定同一个标签页 | 是 |
api / 76698a44 / 一个完整的 UUID | 一个工作区,可按名称、唯一 ID 前缀或 ID 指定 | 是 |
使用前应重新解析 @N;创建或删除标签页的步骤前后,切勿缓存其编号。窗格 ID、标签页 ID 和工作区 ID 都可以安全记住——因此,创建标签页后若要再次指定它,请保留 tty7 tab new --json 返回的 ID,而不要再次按顺序数出 @N。
在任何需要地址的位置,标记符都可以省略:%42 和 42 表示同一个窗格,@7 和 7 表示同一个标签页。从 --json 中复制的 ID 可以直接粘贴回来。
在 tty7 shell 中省略地址,表示“此窗格”/“此工作区”。显式指定的地址始终优先于环境变量。
运行命令:三种方式
阻塞运行,并返回真实退出码
tty7 run -- cargo test # streams to your stdout, exits with cargo's code
tty7 run --cwd /path -- make
tty7 run --keep -- cargo build # leaves the pane as a new tab afterwards
命令输出会实时流式传输到你的 stdout,tty7 会以命令自身的退出码退出。这是最接近 Bash 调用的方式——不同之处在于它使用 PTY,而且用户可以看到输出。
有三点需要注意。--keep 需要工作区,因此只能在 tty7 shell 中使用,或配合 --ws <workspace> 使用。使用 --json 时,流式输出会先出现,JSON 对象最后出现——合并后的流不能作为 JSON 解析,因此请读取最后一行。窗格宽度为 120 列,无法更改,因此假定终端更宽的输出会发生换行。
阻塞式:在已有窗格中执行
tty7 exec "$PANE" --timeout 900 -- cargo test # prints its output, exits with its code
exec 会在窗格的 shell 提示符处输入这一行——使用该窗格的当前工作目录、环境和历史记录——等待提示符重新出现,输出命令打印的内容(以文本形式,与 capture --plain 一样;--raw 会保留转义序列),然后以命令自身的退出码退出。这样,你就能在之前已设置好的窗格中运行下一条命令(例如 cd、已激活虚拟环境,或远端已启用 tty7 shell 集成的 ssh 会话),不用再经历发送、等待、捕获、猜测这一套操作。
它所需的条件,以及不满足时的提示:窗格的 shell 必须启用 tty7 shell 集成(从未发送过提示符标记的窗格会被拒绝),而且窗格必须停留在提示符处(忙碌的窗格会被拒绝——请先执行 wait --until free)。-- 后面的内容会成为一整行 shell 命令,因此只要加上引号,&&、管道和重定向都可以正常使用:tty7 exec %3 -- 'make && ./run'。无人监看时,务必指定 --timeout:如果 shell 始终没有运行这一行——例如未闭合的引号一直等待 dquote>——就会一直等不到结束。超时后会以 124 退出,并让命令继续运行。
非阻塞式:随时可以交互的窗格
这正是 tty7 值得使用的地方。获取一个窗格,给它发送任务,之后再回来查看。
PANE=$(tty7 split --v) # or --h; splits $TTY7_PANE, prints "%83"
tty7 send "$PANE" 'npm run dev' --enter
split 会将新窗格的地址打印到 stdout,你可以将其捕获到变量中。不指定轴向会导致用法错误——--v 会将新窗格放在下方,--h 会将其放在右侧。
拆分 $TTY7_PANE 会改变用户可见的布局,这通常正是目的所在:用户可以查看你启动的开发服务器。请告知用户你进行了拆分,并在用完后关闭窗格。
如果你不在 tty7 窗格中,就没有可供拆分的窗格,因此请先创建自己的工作位置。tty7 new --json /path/to/repo 会一次性返回两个 ID——不用从 ws tree 中翻找窗格:
read -r WS PANE < <(tty7 new --json /path/to/repo \
| python3 -c 'import json,sys; d=json.load(sys.stdin); print(d["id"], "%%%d" % d["pane"])')
send 会像键盘输入一样,将文本输入窗格;--enter 会在末尾附加回车,或者在未提供文本时单独按下 Enter(tty7 send "$PANE" --enter 会运行窗格中已输入的内容)。它不会等待,也不会告诉你发生了什么——读取是单独的步骤,等待则使用 tty7 wait。
若要发送按键而非字符——Ctrl-C、Escape、方向键——请使用 --key:它接受 enter escape tab backtab space backspace delete up down right left home end pageup pagedown,以及表示 Ctrl 的 C-<char> 和表示 Alt 的 M-<char>。按顺序发送多个按键时,请重复使用该选项;文本和按键可以组合,文本先发送。将 ^C 作为文本输入没有任何作用——它会作为两个字符送达;--key C-c 才是真正的中断。
命令行文本有两种做不到的事。机密信息:tty7 send %3 "$TOKEN" 会让本机所有用户都能在 ps 中看到令牌,也会将其写入 shell 历史记录——请改用管道:printf %s "$TOKEN" | tty7 send %3 --stdin --enter
(或使用 --from-file PATH);字节会按读取到的内容原样发送到窗格,且绝不会回显到 --json 中。远端的回显由程序自行处理:docker login --password-stdin、ssh-add 和 gpg 都会自行关闭回显。
多行文本:在 pty 中,换行符就是 Enter,因此每一行到达时都会运行。--paste 会像粘贴到窗口中那样发送内容——如果窗格已启用括号粘贴模式,就会使用该模式发送,因此 shell 或 Agent 的输入框会将整块内容作为文本接收;--json 会报告 "bracketed",让你知道采用了哪种方式。
新建窗格可能会吞掉 Enter。 shell 仍在执行启动文件时——例如提示符框架、fastfetch,或任何会在登录时绘制内容的程序——会接收你发送的文本,却丢失其后跟随的回车,导致命令停留在提示符行上而未执行。不会有任何提示:send 已成功,窗格看起来却像是尚未启动的工作进程。因此,向刚创建的窗格发送第一条命令后,请读回屏幕并确认命令确实已离开提示符行:
tty7 capture "$PANE" --plain | tail -3 # command still sitting on the prompt?
tty7 send "$PANE" --enter # then give it the Enter it lost
这比之后排查问题省事,而且只有向新窗格发送第一条命令时才需要检查。
阅读窗格
如果你想看屏幕,请使用 --plain
tty7 capture %83 --plain
capture 会返回守护进程存储的内容——窗格的字节、转义序列,一应俱全;而 --plain 会将其重新通过终端网格渲染,并打印生成的文本。它不只是去除转义序列:颜色和光标转义序列会消失,但被 Shell 按窗格宽度换行的内容也会还原为一行,使用 \r 重绘自身的进度条会显示最终值,TUI 的屏幕内容则会呈现为绘制时的样子。只要人需要阅读输出,就使用它。
无论哪种方式,返回结果都有两点需要注意:capture 返回的是快照,不是数据流——要获取更新的内容,请再次调用。默认情况下,它会打印最新的回滚片段(环形缓冲区会在调整大小时分段);--scrollback 会打印整个缓冲区。对于从未调整过大小的窗格,两者内容相同。
如果你想要结果,请将输出重定向到文件
--plain 返回的是屏幕,而屏幕是一个矩形:长篇构建日志中滚出顶部的内容已经消失,退出代码也从未显示在屏幕上。当窗格中的 Shell 正停在提示符处时,tty7 exec(见上文)会同时返回这两者。否则——窗格正忙,或其中的 Shell 没有集成 tty7——让 Shell 将答案写入一个干净的位置:
tty7 send "$PANE" 'cargo test > /tmp/t.log 2>&1; echo $? > /tmp/t.rc' --enter
# ...wait for it to finish (below), then:
cat /tmp/t.rc /tmp/t.log
完整输出、真实退出代码,终端不会介入。
判断命令何时完成
不要轮询屏幕,也不要自己编写循环——直接阻塞等待:
tty7 wait "$PANE" --until free --changed --timeout 900
free 表示前台命令已退出,窗格已恢复到空闲 Shell 状态。--changed 还会额外要求“等待期间确实有命令运行”,这正是你在 send 后紧接着执行时所需要的:如果没有它,尚未启动的命令会让窗格看起来像是已完成。
完整流程如下:
tty7 send "$PANE" 'cargo test > /tmp/t.log 2>&1; echo $? > /tmp/t.rc' --enter
tty7 wait "$PANE" --until free --changed --timeout 900
cat /tmp/t.rc /tmp/t.log
tty7 pane close "$PANE"
退出代码就是为此设计的:0 表示已达到你要求的状态,124 表示超时(遵循 timeout(1) 的约定,因此可以区分“尚未完成”和“出错”),1 表示窗格先行终止。
使用 --changed 时有个陷阱:如果命令在一次轮询内(默认 500ms)就完成,它就不会被观察到处于运行状态,因此等待会一直持续,直到超时。对于运行如此之快的命令,可以使用 --interval 100,或者去掉 --changed 并读取 .rc 文件。发生这种情况时,超时消息会说明原因。
如果你想查看进程树本身——“里面正在运行什么”“这个窗格在提供哪个端口的服务”——可以使用 tty7 procs %83:输出按深度缩进,在前台进程旁标上 *,然后列出这些进程正在监听的端口。不过,这并不是检查编码 Agent 的方法:对于其中运行着繁忙 Agent 的窗格,procs 会报告 nothing running in this pane,因此将其理解为“工作进程已退出”是错误的。要查询这些 Agent,请使用 tty7 agents。
将工作交给另一个 Agent
一个窗格可以容纳另一个编码 Agent,上面提到的每个基本功能都适用,
此外还有一项只有 Agent 才有的功能:状态钩子会报告 working / waiting /
done,因此 tty7 wait 可以等待 Agent,而不是等待它的进程树。
委派工作有一套操作指南——references/delegation.md。启动工作 Agent 前请先阅读。 其中涵盖了整个流程:为工作 Agent 提供独立的 git
工作树和工作区,在提示词中通过交付约定移交任务,确认命令确实已启动,跟进各个状态,从 git 中收集结果,同时启动多个工作 Agent,以及清理工作环境。
即使不看其他内容,也请牢记其中的四条规则:
- 使用交互模式,绝不使用
-p。claude -p不会绘制 TUI:窗格会保持 空白,capture --plain不会返回任何内容,用户看到的是一个空矩形,而且会话在一轮后结束,无法继续交互。请将任务作为参数传给交互式命令。 - 会写入文件的工作 Agent 必须使用独立的 git 工作树。 两个 Agent 共用一个检出目录会互相覆盖,并影响用户的工作树。
- 从 git 中收集结果,而不是从屏幕上收集。 告诉工作 Agent 提交更改; 然后查看差异。屏幕有边界,顶部内容会消失。
- 向新窗格发送内容后,确认命令已离开提示符——见上文的回车键被吞检查。
查看状态
tty7 ls # every workspace: tabs, panes, who's attached
tty7 ws tree api # one workspace as a tree — tabs, splits, panes, cwds
tty7 pane ls # panes with their workspace, tab, cwd, live flag
tty7 pane ls --all # + orphans: panes the server runs that no workspace holds
tty7 agents # every coding agent on the machine and its status
tty7 status # server pid, uptime, pane count, build, socket
tty7 machine ls # this machine plus any linked remotes
tty7 events # stream server events, one per line, until interrupted
值得了解一下 tty7 agents:它会报告每个运行已识别编码 Agent 的窗格状态:idle / working / waiting / done,并显示 Agent 自己的消息。如果你也是其中之一,你也会出现在该列表中。如果它发现某个 Agent 正在运行,但缺少状态钩子或状态钩子已过时,也会输出诊断信息——即 JSON 中的 diagnostics 字段,这可以解释为何某个 Agent 看起来像是卡住了。
为这些命令中的任意一个添加 --json,即可解析输出,而不是靠目视查看。成功时,-q 会抑制输出,但不会抑制错误。
不要破坏用户的会话
此机器上的窗格属于用户的真实工作,其中有些窗格里还有正在执行任务的其他编码 Agent。凡是你没有创建的内容,都应视为只读:
- 绝不要对你未打开的窗格执行
send或exec。 向另一个 Agent 的窗格发送按键,或向用户正在输入的 shell 发送按键,都会打断当时正在进行的操作。操作窗格前先检查tty7 agents。使用--key时尤其要小心:一个误发的C-c就会终止别人的工作。 - 绝不要对你未创建的内容执行
pane close/tab close/ws rm。 - 绝不要执行
pane close --orphans。 它会关闭机器上所有被遗弃的窗格,而被遗弃的窗格仍可能在运行真实命令。清理工具是给用户用的;告诉用户如何使用,不要擅自操作。 - 绝不要执行
server stop或server restart。 服务器停止后,机器上的所有窗格都会关闭,包括你自己的。如果服务器确实似乎卡住了,请告知用户并让用户决定。 - 清理你创建的内容。 临时窗格用完后执行
tty7 pane close %83;它可以一次接收多个 ID。ws rm会断开工作区中所有窗格,因此只需移除临时工作区即可。真正可能遗留的是被中断的tty7 run——对应窗格仍在运行,却没有任何引用它,并会显示在tty7 pane ls --all的结果中。
远程机器
-m <machine> 会通过本地服务器已有的链路路由任意命令:
tty7 -m devbox ls
tty7 -m devbox run -- cargo test
名称可以是完整的链路键(me@devbox:22),也可以只是主机名。CLI
不会建立新连接——如果链路已断开,或它属于跳板/代理链,CLI 会说明情况;此时应将问题交还用户,由用户从 GUI 连接。
尚未接入的功能
ws stop、machine connect 和 machine disconnect 会退出并显示尚未实现的消息。不要围绕它们制定计划。
参考文档
references/delegation.md— 委派操作指南:工作树、交接、看护、收集、扇出和清理。每当有另一个 Agent 即将开始工作时,都要阅读此文档。references/commands.md— 一张表列出所有动词、子命令和标志,以及每项命令输出的 JSON 格式。需要使用上文未列出的动词,或准备解析--json输出并想了解字段名称时,请阅读此文档。