---
title: "命令行"
description: "thinkterm 可执行文件、它的子命令，以及与运行中的多路复用服务端通信的 cli 子命令。"
url: "https://docs.closex.org/zh/thinkterm/cli.html"
markdown_url: "https://docs.closex.org/zh/thinkterm/cli.md"
site: "CloseX Docs"
product: "ThinkTerm"
language: "zh-Hans"
last_updated: "2026-11-08T12:04:16.000Z"
alternates:
  en: "https://docs.closex.org/thinkterm/cli.md"
  zh-Hans: "https://docs.closex.org/zh/thinkterm/cli.md"
  ja: "https://docs.closex.org/ja/thinkterm/cli.md"
---

# 命令行

> **文档编写中**
> 部分内容可能尚不完整。

`thinkterm` 是你在 shell 里日常使用的那个命令。

```
thinkterm --help
```

它不是唯一装上的可执行文件。桌面端和服务器安装都会带上 `thinkterm-mux-server` —— 真正为远程客户端和浏览器提供服务的是它，见 [远程](https://docs.closex.org/zh/thinkterm/remote.md)、[网页访问](https://docs.closex.org/zh/thinkterm/web.md) 和下文的 [mux 服务端](#the-mux-server)。同时还会装一个兼容用的 `wezterm` 名字，补全脚本两个名字都覆盖。桌面端和服务器安装还都带有 `thinkterm-plugin-server`，插件都在它之下运行，内置的「片段」插件也在其中；它在第一次有需要时被启动，从不需要手动运行 —— 见 [插件](https://docs.closex.org/zh/thinkterm/plugins.md)。

## 子命令

| 命令 | 作用 |
|---|---|
| `start` | 启动 GUI，可选择运行另一个程序。别名 `-e` |
| `tui [DOMAIN…]` | 在当前终端里打开 ThinkTerm 界面 |
| `connect <name>` | 连接到一个 ThinkTerm 多路复用器 |
| `ssh` | 建立 SSH 会话 |
| `serial` | 打开串口 |
| `cli <subcommand>` | 通过运行中的多路复用服务端查看和控制 pane、标签与窗口，并查看编码 agent 的情况 |
| `update` | 检查新版本并安装。见 [更新](https://docs.closex.org/zh/thinkterm/updates.md) |
| `plugin` | 列出插件、开关插件、重新加载插件，以及向某个插件发送调用。见 [插件](https://docs.closex.org/zh/thinkterm/plugins.md) |
| `skill setup\|status\|remove\|show` | 把 ThinkTerm 的 skill 和会话启动 hook 装进编码 agent。见 [让编码 agent 使用 ThinkTerm](#skill) |
| `config check [FILE]` | 像应用那样加载一份配置文件，报告其中的错误和警告，但不应用它；运行中的应用不受影响。不给文件时检查应用会加载的那一份。文件加载失败时以状态 1 退出 |
| `imgcat <file>` | 把图片输出到终端 |
| `ls-fonts` | 显示当前使用的字体信息 |
| `show-keys` | 显示当前的按键绑定 |
| `set-working-directory` | 告知终端当前工作目录，用于 shell 集成 |
| `record` | 把终端会话录制为 asciicast |
| `replay` | 回放 asciicast |
| `shell-completion` | 输出 bash、zsh、fish、elvish、PowerShell 或 Fig 的补全脚本 |

还有一个子命令不出现在 `--help` 里。`list-files` 输出一个 Project 的文件列表，桌面端在远程主机上搜索文件时运行的就是它 —— 见 [文件与笔记](https://docs.closex.org/zh/thinkterm/files-and-notes.md)。它不是给人手动运行的，但必须存在：ThinkTerm 版本早于它的主机无法被搜索。

服务器版安装里没有 GUI 可启动。`thinkterm start`、不带参数的 `thinkterm` 以及其他要打开窗口的命令，会回答 `this ThinkTerm has no GUI to start`，并指向 `thinkterm tui`；桌面版安装才带 GUI。

## 与运行中的多路复用器通信

`thinkterm cli` 面向的是一个已经在运行的服务端 —— 你 GUI 窗口背后的那个，或者一个后台 mux 服务端。由于多路复用器能寻址任意窗口、Thread 和已连接客户端中的任意 pane，这些命令可以够到你当前并没有在看的会话。

到底由哪个服务端应答是隐含的，而这通常就是「命令看上去什么也没做」的原因：默认优先找运行中的 GUI，找不到就自己起一个。`--prefer-mux` 改为发给后台 mux 服务端，`--no-auto-start` 则让命令直接失败，而不是为了应答它而拉起一个服务端。

`--class <NAME>` 用来找以自己的窗口类启动的 GUI，也就是 `thinkterm start` 带了 `--class` 的那种。不加它时，命令找的是你正常启动的那个 GUI；只有你用自定义窗口类另外启动过 GUI，才需要用 `--class` 去找它。`thinkterm tui` 也接受同样的参数，用法一样。

作用于 pane 的命令，用 `--pane-id` 接收 pane 的编号，不给时就作用于命令所在的那个 pane，也就是 `WEZTERM_PANE` 指的那个。`--pane` 则按你给它起的 label 来指定 pane —— 见 [给 pane 起名](#naming-panes)。

**查询**

| 命令 | 作用 |
|---|---|
| `list` | 列出窗口、标签和 pane，有 label 的一并显示 |
| `list-clients` | 列出已连接的客户端 |
| `get-pane-direction` | 报告某个方向上的 pane |

**创建与销毁**

| 命令 | 作用 |
|---|---|
| `spawn` | 新建标签或窗口。打印新 pane 的 id。`--label` 给这个 pane 起名；`--wait` 等它的程序结束，并以其状态退出 |
| `split-pane` | 分割已有的 pane。打印新 pane 的 id，和 `spawn` 一样接受 `--label` 与 `--wait` |
| `move-pane-to-new-tab` | 把一个 pane 移出为独立标签 |
| `kill-pane` | 关闭一个 pane |

**移动与调整大小**

| 命令 | 作用 |
|---|---|
| `activate-pane` | 聚焦某个 pane |
| `activate-pane-direction` | 聚焦某个方向上的 pane |
| `activate-tab` | 聚焦某个标签 |
| `adjust-pane-size` | 调整 pane 大小 |
| `zoom-pane` | 把 pane 放大到填满其标签 |

**文本进出**

| 命令 | 作用 |
|---|---|
| `send-text` | 以粘贴的方式向某个 pane 写入文本；如果其中的程序要求，就用括号粘贴。不会补 Enter：`--enter` 会在文本之后稍等片刻按一下 Enter，`--no-paste` 则把文本作为普通输入发送 |
| `send-key` | 按名字依次按下按键，按 pane 里的程序要求的方式编码，就像真的敲下去一样。`--wait` 会接着等它们启动的命令结束。见 [按键](#send-key) |
| `get-text` | 读取某个 pane 屏幕上的文本。`--pane-id` 或 `--pane` 指定 pane，默认是命令所在的那个 pane。`--start-line` 和 `--end-line` 指定行范围：0 是屏幕顶端，负数向回延伸到回滚区。`--format` 可以是 `text`（默认）、像 `--escapes` 那样保留颜色和样式序列的 `vt`，或带颜色的 `html`。`--trim` 去掉行尾空格和末尾的空行，`--unwrap` 把终端折行的内容接回原来的一行 |

**等待与观察**

| 命令 | 作用 |
|---|---|
| `wait` | 等某个 pane 里的程序结束，加 `--command` 则等它 shell 里正在运行的命令结束，并以其状态退出。见 [等待程序或命令](#wait) |
| `events` | 事情一发生就打印出来，直到被中断。见 [观察发生了什么](#events) |

**命名与基础设施**

| 命令 | 作用 |
|---|---|
| `set-tab-title` | 重命名标签 |
| `set-pane-label` | 给 pane 起个 label，之后 `--pane` 就能找到它。空 label 表示去掉 |
| `set-window-title` | 重命名窗口 |
| `rename-workspace` | 重命名工作区 |
| `agent list` | 列出检测到正在运行编码 agent 的 pane，以及各自的状态。只读 |
| `agent context` | 为在其中启动的编码 agent，打印它所在的 pane 在哪里、同一标签里还有什么。见 [让编码 agent 使用 ThinkTerm](#skill) |
| `tlscreds` | 获取 TLS 凭据 |
| `proxy` | 启动 RPC 代理管道 |
| `color-schemes` | 列出内置配色方案。由可执行文件直接应答，不需要服务端；`--json` 会连同颜色一起输出 |
| `stop-server` | 停止后台会话服务器。里面的每个终端都会结束，因此在终端里运行时会先询问；`--force` 不询问 |
| `restore-output status\|on\|off` | 这台机器在终端停止时，是否保存 pane 里显示的内容，并在它们的布局恢复时放回去。由这台机器上的一个开关回答，不涉及任何服务端。见[保存 pane 输出](#restore-output) |

**网页访问**

| 命令 | 作用 |
|---|---|
| `web-server status\|on\|off` | 是否在接受浏览器连接、在哪里接受。`on` 可带 `--bind-address` |
| `web-token mint\|list\|revoke` | 发放、列出和吊销浏览器用来进入的链接 |

这两个命令面向的都是 mux 服务端，而不是 GUI 自带的多路复用器；而发放出去的链接等同于在那台机器上以你的身份登录。如果开启了[连接其他机器](https://docs.closex.org/zh/thinkterm/web.md#reaching-other-machines)，它还是通往那个账户用 SSH 进得去的每一台机器的入口。[网页访问](https://docs.closex.org/zh/thinkterm/web.md)说明了这意味着什么。

**共享**

| 命令 | 作用 |
|---|---|
| `share list\|stop\|kick\|revoke\|deny` | 查看并收回你与这台机器上其他账户共享的 pane。见[共享的 pane](#shared-panes) |
| `share incoming\|watch` | 列出这台机器上其他账户与你共享的 pane，并在这个终端里观看其中一个 |

`list`、`list-clients`、`agent list` 和 `web-token list` 默认输出表格，加 `--format json` 则输出 JSON。前三个和 `events` 还可以简写为 `--json`。

会打印列表或文本的命令 —— 上面这四个，以及 `get-text`、`color-schemes` 等 —— 在管道另一端不再读取时，会悄悄结束并返回成功状态，比如 `thinkterm cli list | head`。

## 停止会话服务器

`thinkterm cli stop-server` 停止默认 socket 上的后台会话服务器，里面的每个终端都随之结束。它从不启动服务器：没有服务器在运行时，它会说明这一点，什么也不改。

在终端里运行时，它会报出服务器的进程和 socket 并先询问，回答 `y` 或 `yes` 以外的任何内容都会让服务器继续运行。只有标准输入是终端时它才询问，所以脚本或管道会不经询问直接停掉服务器；`--force` 在终端里也跳过询问。随后它最多等十秒让服务器退出，过后若服务器仍在关闭中，会如实说明。

下一次需要服务器的启动、`connect` 或 `cli` 命令会起一个全新的。其他停止它的方式见 [远程](https://docs.closex.org/zh/thinkterm/remote.md)。

## 给 pane 起名 {#naming-panes}

pane 的编号每次运行都会变，label 不会。`thinkterm cli set-pane-label --pane-id 12 build` 给 12 号 pane 起名；`spawn` 和 `split-pane` 用 `--label build` 给它们创建的 pane 起名；此后 `--pane build` 就能找到它。

```
thinkterm cli set-pane-label build
thinkterm cli spawn --label tests -- npm test
thinkterm cli get-text --pane tests --trim
```

label 就是窗口里 pane 标签上的那个名字，所以在那里重命名标签，设置的是同一个 label。它由服务端保存，因此每个窗口、CLI、浏览器和恢复出来的 Thread 都看得到。label 里不能有控制字符，最多 128 个字符，两端的空白会被去掉。只由数字构成的 label 会被拒绝，因为 `--pane` 把数字当作 pane 编号。空 label 表示去掉 label。`list` 和 `agent list` 把 label 显示在 `LABEL` 列和 `label` 字段里，`events` 会报告 pane 被起了名。

`--pane` 接受：

- **pane 编号。** 只有数字。如果另一个 pane 的 label 恰好是这串数字，命令会停下并把两个都列出来，而不是去猜。
- **label**，不区分大小写。
- **Thread 的名字**，单独写，或者像侧边栏那样接在 Project 名后面（`my-project · fix login`）；仅当这个 Thread 只有一个 pane 时。
- **`thread/label`**，用于多个 Thread 都在用的 label。
- **`MACHINE/ID`** 或 **`MACHINE/LABEL`**，用于你的窗口所连接的 server 上的 pane，前提是[相应的设置](#servers-terminals)允许这里的程序访问它。
- **`desktop-0123abcd/21`**，在 server 上，指你连接过来的那台电脑上的终端，前提是你在那边批准了；见[让已连接的服务器访问你的本地终端](https://docs.closex.org/zh/thinkterm/remote.md#connect-reverse-access)。

当一个 label 被好几个 pane 用着，而其中只有一个在运行命令的那个 pane 所在的 Thread 里，指的就是它。否则命令会停下，说明这个名字可能指几个 pane，并列出每个的编号、label、标题和 Thread；请按编号指定。`--pane` 不能和 `--pane-id` 一起用。每个接收 pane 的命令都接受 `--pane`，`spawn`、`split-pane` 和 `activate-tab` 也不例外。

## 按键 {#send-key}

`send-key` 接受一个或多个按键，每个写成修饰键和按键用 `+` 连起来，不区分大小写：

```
thinkterm cli send-key --pane build ctrl+c
thinkterm cli send-key escape up up enter
```

修饰键有 `ctrl`、`alt`（或 `opt`）、`shift` 和 `cmd`（或 `super`）。按键可以是单个字符，或 `enter`、`tab`、`escape`、`backspace`、`delete`、`space`、`up`、`down`、`left`、`right`、`home`、`end`、`pageup`、`pagedown`、`insert`，以及 `f1` 到 `f24`。每个键都按 pane 里的程序要求的方式编码，就像真的敲下去一样。遇到不认识的键，命令会在发送任何东西之前停下。

## 等待程序或命令 {#wait}

`wait` 一直等到 pane 里的某件事结束，并以它的结束方式作为退出状态：

```
thinkterm cli spawn --label tests --wait -- npm test
thinkterm cli wait --pane tests --for 10m
thinkterm cli send-key --pane build --wait enter
```

- **`wait`** 等 pane 启动的那个程序结束，并以它的状态退出。
- **`wait --command`** 等 pane 的 shell 里正在运行的命令结束，并以该命令的状态退出。刚敲下去的命令有一秒钟时间开始；没有开始的话，就以最近一条结束的命令的状态退出，一条也没结束过则为 0。
- **`send-key --wait KEY…`** 按下按键，然后等它们启动的命令结束。它会先数一下 shell 已结束的命令数，所以一下就结束的命令不会漏掉。要敲入一条命令并等它，先 `send-text`，再 `send-key --wait enter`，比事后再 `wait --command` 更稳妥。
- **`spawn --wait`** 和 **`split-pane --wait`** 先打印新 pane 的编号，再等它的程序结束。

`--timeout`（简写 `--for`）在若干秒、或数字加 `s`、`m`、`h` 之后放弃：`90`、`30s`、`10m`、`1.5h`。放弃的等待会打印 `timed out`，以状态 124 退出。程序或命令结束了但没有已知状态时，会如实说明并以 125 退出：shell 结束一条命令却没给退出状态，或者 `--takeover` 之后被新服务端接管的 pane，它里面的程序已经说不出自己是怎么结束的。两者都不会被当作成功。pane 关闭时程序的状态还没送到，则以 1 退出；被信号结束的程序，会在标准错误里写出信号的名字。

`wait --command` 和 `send-key --wait` 需要一个会报告命令从哪里开始、到哪里结束的 shell，也就是 ThinkTerm 的 shell 集成，见 [安装](https://docs.closex.org/zh/thinkterm/install.md)。没有它们会报错停下并说明这一点；`spawn --wait` 不需要。

## 观察发生了什么 {#events}

`thinkterm cli events` 为服务端上发生的每件事打印一行，直到被中断：

```
14:02:11  command_finished    pane 12  status 0
14:02:11  agent_status        pane 12  claude idle
```

`--pane` 或 `--pane-id` 只保留发生在某一个 pane 上的事；标签、窗口和客户端的事件会被略去。`--format json`，或简写 `--json`，改为每行打印一个对象，带 `type` 和该事件自己的字段。这些会保持稳定：事件和字段可能增加，但不会被删除或改变类型。如果有几千条事件来不及打印而堆积，多出的会被丢弃。服务端连接被替换时，命令会自行重连；连接丢失时报错停下。

| `type` | 何时 | `type` 之外的字段 |
|---|---|---|
| `pane_added`, `pane_removed` | pane 打开或关闭 | `pane_id` |
| `program_exited` | pane 启动的程序结束了 | `pane_id`、`exit_code`（未知时为 `null`）、`signal` |
| `command_finished` | pane 的 shell 报告一条命令结束了 | `pane_id`、`exit_code`（没给时为 `null`） |
| `bell` | pane 响了铃 | `pane_id` |
| `notification` | 程序发出了桌面通知 | `pane_id`、`title`、`body` |
| `title_changed` | pane 的标题变了 | `pane_id`、`title` |
| `cwd_changed` | pane 的工作目录变了 | `pane_id` |
| `progress` | 一条进度报告 | `pane_id`、`state`（`none`、`percentage`、`error` 或 `indeterminate`）、`percent` |
| `user_var` | 程序设置了用户变量 | `pane_id`、`name`、`value` |
| `clipboard_written` | 程序写了剪贴板。写的内容不会重复 | `pane_id` |
| `agent_status` | pane 里发现了编码 agent、状态变了，或它走了 | `pane_id`、`agent`、`state` |
| `foreground_program` | pane 的前台程序变了 | `pane_id`、`executable`、`runs` |
| `pane_focused` | 某个 pane 获得焦点 | `pane_id` |
| `pane_label_changed` | pane 被起了 label，或 label 被去掉 | `pane_id`、`label`（被去掉时为 `null`） |
| `tab_added` | 窗口里新增了标签 | `tab_id`、`window_id` |
| `tab_title_changed` | 标签被重命名 | `tab_id`、`title` |
| `window_title_changed` | 窗口被重命名 | `window_id`、`title` |
| `workspace_changed` | 窗口换到了另一个工作区 | `window_id`、`workspace` |
| `workspace_renamed` | 工作区被重命名 | `old_workspace`、`new_workspace` |
| `client_joined`, `client_left` | 一个窗口、浏览器或手机开始、或不再显示 pane | `hostname`、`username`、`pid`、`epoch`、`id` |

## 已连接 server 上的 pane {#servers-terminals}

连着某个 server 的窗口持有那个 server 的终端，这台电脑上的程序可以用 `thinkterm cli` 访问它们 —— 但只限于 `设置 › Agent › 服务器上的终端 › 本机程序` 允许的范围。它默认是 `关`。`只能看` 让它们列出并读取这些终端，`能看也能打字` 则让它们也能打字。有这个设置，是因为这台电脑的终端里的程序，比如编码 agent 或插件，并不是坐在窗口前的那个人，而它们够到的是另一台机器上的 shell，登录的是你：走的是窗口已经登录的那条连接。

开着这个设置、并且有 ThinkTerm 窗口开着且已连接时，`list` 会在这台机器的 pane 之后加上 server 的 pane。每个都叫 `MACHINE/ID`：窗口对那台机器的称呼、一个斜杠，以及这个 pane 在那个 server 上的编号。在 JSON 里它是字符串，不是数字。`--pane` 接受同样的名字，pane 在那边有 label 时也可以用 `MACHINE/LABEL`；不区分大小写，机器名本身也可以含有斜杠。`get-text`、`send-text`、`send-key`、`wait` 和 `events` 接受这样的名字；`--pane-id` 和其他命令够不到这些 pane。在 `events` 里，关于它们的事件会在 `pane` 字段里写出这个名字，表格里也用它代替 pane 编号。那边没有匹配的 pane 时，命令以状态 3 停下。设置是关的、或者没有窗口开着时，错误信息会说：server 上的 pane 叫 `MACHINE/NAME`，如 `list` 所示，要求 ThinkTerm 开着，并且设置允许这里的程序访问它。

反方向也有。server 上的程序在你于这边批准之后，可以访问你连接过来的那台电脑的终端；那里的 `list` 把它们显示为 `desktop-0123abcd/21`，同样的命令用 `--pane` 接受这个名字。见[让已连接的服务器访问你的本地终端](https://docs.closex.org/zh/thinkterm/remote.md#connect-reverse-access)。

## 共享的 pane {#shared-panes}

在 `设置 › 共享与同步 › 分享终端` 打开之后，可以在 ThinkTerm 里通过终端的分享按钮，把它分享给这台机器上的其他账户；按钮和面板见[分享终端](https://docs.closex.org/zh/thinkterm/window.md#sharing-a-terminal)。它默认关闭，并标明是实验功能。分享是由人来决定的事，所以命令行不能发起分享：它能做的是查看分享、收回分享，以及观看别人与你共享的。被分享的人能实时看到这个 pane，但不能在里面打字；要打字就要提出请求，由所有者决定。一次分享最长持续 30 分钟，或者在发起它的桌面端离开时结束。打字权限一次授予 30 分钟，所有者随时可以收回。

| 命令 | 作用 |
|---|---|
| `share list` | 列出你的分享：pane、分享给谁、谁在观看、谁可以打字、谁请求了打字，以及何时结束。`--format json` 输出 JSON |
| `share stop SHARE` | 结束一次分享；所有正在观看的人都会被断开 |
| `share kick SHARE VIEWER` | 把一位观看者从分享里踢出去。观看者可以用 `list` 显示的 id，或账户名 |
| `share revoke SHARE` | 收回打字权限 |
| `share deny SHARE VIEWER` | 拒绝一位观看者的打字请求；他仍然可以继续观看 |
| `share incoming` | 列出这台机器上其他账户与你共享的 pane，以 `OWNER/SHARE` 命名，附带标题、开始时间和谁在打字。`--format json` 输出 JSON |
| `share watch [OWNER/SHARE]` | 在这个终端里观看其中一个 |

前五个命令找的是后台 mux 服务端，和 `web-token` 一样，不是窗口。`incoming` 和 `watch` 则去问其他账户的服务端，每个都在它用来共享的那个 socket 上，不需要你自己的服务端。

`watch` 用 `incoming` 显示的 `OWNER/SHARE` 来指定分享；那个账户只与你共享一个 pane 时可以只写 `OWNER`；只有一个 pane 与你共享时可以什么都不写。符合的不止一个时，它以状态 2 停下并列出它们；一个也没有时，以状态 3 停下。运行另一个版本 ThinkTerm 的账户所共享的东西，要等双方版本一致才会出现，命令会说明这一点。pane 按所有者的尺寸绘制：终端比它小，就显示光标周围的部分；比它大，其余部分留空。在所有者允许你打字之前，你的按键只用来浏览：方向键或 `j`、`k`，Page Up 和 Page Down 在历史里滚动，`g` 或 Home 到顶部，`G` 或 End 回到实时画面，`h`、`l` 或左右方向键平移。`Ctrl-]` 开始一条属于观看器自己的命令：`q` 退出，`r` 请求所有者让你打字，已经拿到打字权限时则交还。能打字时，你的按键进入 pane，连按两次 `Ctrl-]` 才会发出一个真正的 `Ctrl-]`。`--once` 把共享的屏幕当作文本打印一次然后退出。

## 保存 pane 输出 {#restore-output}

`thinkterm cli restore-output` 是 `设置 › 通用 › 重新打开时恢复窗格内容` 在命令行上的对应物，作用于你运行它的那台机器。`status` 说明这台机器在终端停止时是否保存 pane 里显示的内容，`on` 打开它，`off` 关闭它并删除已保存的内容。它读写的是这台机器上的一个开关，所以不涉及也不会启动任何服务端。桌面端运行的地方，这个开关除非被关掉，否则是开着的；在任何其他机器上，比如远程主机，要在那里运行 `restore-output on` 才会开。保存什么（写入之前会把密码和令牌遮掉）以及怎样恢复，见[恢复窗格内容](https://docs.closex.org/zh/thinkterm/configuration.md#恢复窗格内容)。

## 退出状态

失败的 `cli` 命令以 1 退出，下面这些除外：

| 状态 | 含义 |
|---|---|
| 2 | 命令行要求的事不可能成立：参数解析不了、不认识的键、只由数字构成的 label 等 |
| 3 | 没有这个 pane，或者 `--pane` 的名字一个都对不上、或对上了好几个 |
| 4 | 连不上服务端，或者服务端不肯和这个版本的程序通信 |

会等待的命令，则像[上面](#wait)那样，以所等待之物的结果退出。

## 在脚本里读 agent 状态

`thinkterm cli agent list` 为 ThinkTerm 发现了编码 agent 的每个 pane 打印一行：

| 列 | 内容 |
|---|---|
| `PANEID` | 这个 pane，用的是其他 `cli` 命令接受的那个 id |
| `AGENT` | agent 的 id，比如 `claude` |
| `STATE` | `working`、`blocked`、`idle`、`error` 或 `unknown` |
| `EVIDENCE` | 状态由什么决定：`report` 表示程序发送了自己的状态上报，`contract` 表示程序通过 `THINKTERM_AGENT` 自己报告，`screen` 表示某条屏幕规则匹配，`fallback` 表示什么都没匹配上，于是把已知的 agent 视为空闲 |
| `AGE` | 状态上次变化到现在多久 |
| `WORKSPACE` | 这个 pane 所在的多路复用器工作区。Thread 里的 pane 这里是一个内部名字，不是 Thread 的名字 |
| `TITLE` | pane 的标题 |
| `LABEL` | pane 的 [label](#naming-panes)，没有时为空 |

`--format json` 改为输出一个对象数组，字段为 `pane_id`、`label`（没有时为 `null`）、`agent`、`state`、`evidence`、`session_id`（没有时为 `null`）、`since_unix`（状态上次变化的时间，Unix 秒，按检测它的那台机器的时钟）、`workspace` 和 `title`。这些字段是给脚本用的，会保持稳定。状态从哪里来，见 [Agent 状态](https://docs.closex.org/zh/thinkterm/agents.md)。

## 让编码 agent 使用 ThinkTerm {#skill}

在 ThinkTerm 的 pane 里运行的编码 agent，可以用 `thinkterm cli` 查看其他 pane、读取它们、向它们输入，以及新开 pane。`thinkterm skill setup` 会教 Claude Code 和 Codex 这么做，并在它们启动时告诉它们自己在哪里。它和[识别 agent](https://docs.closex.org/zh/thinkterm/agents.md)是两回事，后者什么也不安装；而它在你运行之前什么也不会改。

```
thinkterm skill setup --dry-run
thinkterm skill setup
```

| 命令 | 作用 |
|---|---|
| `skill setup` | 在这台机器上找到的 agent 里安装或更新 skill 和 hook。可以放心重复运行。`--dry-run` 只说明会改什么，不真的改 |
| `skill status` | 对每个 agent，显示 skill 和 hook 是 `current`、`outdated`、`missing`、`someone else's` 还是 `unreadable` |
| `skill remove` | 把 skill 和 hook 再取出来 |
| `skill show` | 打印 skill，给 `setup` 不认识的 agent 用。`npx skills add RoversX/ThinkTerm --skill thinkterm -g` 会把同一个 skill 从仓库装给其他 agent，比如 Cursor |

`setup`、`status` 和 `remove` 都接受 `--agent claude` 或 `--agent codex`，指定一个 agent，不管有没有找到它。不加时，它们处理自己的文件夹在这里存在的那些 agent；一个也没有就如实说明。

**`setup` 写入的内容**

| Agent | skill | hook |
|---|---|---|
| Claude Code | `~/.claude/skills/thinkterm/SKILL.md` | `~/.claude/settings.json` 里的一条 `SessionStart` |
| Codex | `~/.agents/skills/thinkterm/SKILL.md` | `~/.codex/hooks.json` 里的一条 `SessionStart` |

`CLAUDE_CONFIG_DIR` 和 `CODEX_HOME` 会挪动 agent 自己的文件夹，Claude Code 的 skill 和两个设置文件跟着挪。skill 是一份简短的说明：agent 身在 ThinkTerm 里，`thinkterm cli --help` 列出了所有命令，`thinkterm` 不在 `PATH` 里时它在哪，以及向不是自己开的 pane 输入、关闭 pane、停掉服务端之前，也许该先问问你。pane 里设有 `THINKTERM=1`，skill 的描述就以它为触发条件。

hook 在 agent 启动时运行。在 ThinkTerm 的 pane 里，它会打印这个 pane 的编号、label 和标题，以及所在标签的标题；然后是同一标签里的其他 pane（最多十二个，其余只给个数）、其他标签里有多少个 pane，以及怎么读一个 pane。这就是 `thinkterm cli agent context`。在 ThinkTerm 的 pane 之外，或者 ThinkTerm 不应答时，它什么也不打印并返回成功，也从不启动服务端，所以 agent 的启动和没有它时一样。等待它最多十秒。hook 暂时还不会装到 Windows 上；skill 会。

`setup` 只编辑设置文件里的 `hooks` 一项，文件其余部分的文本保持原样。不是 JSON 对象的设置文件，以及不是 ThinkTerm 的、也叫 `thinkterm` 的 skill 文件，它都不碰，并会说明。Claude Code 只在你信任的文件夹里运行 hook，Codex 则在你用 `/hooks` 批准新 hook 之后才运行。更新之后再运行一次 `setup`，就能把 `outdated` 的 skill 或 hook 更新到最新。

**撤销。** `thinkterm skill remove` 删掉 skill 文件（里面没有别的东西时连文件夹一起删），并把 hook 从设置文件里取出，同时清掉因此变空的 `SessionStart` 列表或 `hooks` 对象。别的 hook 和 skill 原样保留。手动的话，删掉文件，再删掉那一项。

## 作为数据的配色方案

`thinkterm cli color-schemes` 逐行打印内置配色方案的名字。`--json` 改为输出一个 JSON 数组，每个方案一个对象：`name`、`foreground`、`background`、`cursor_bg`、`cursor_fg`、`cursor_border`、`selection_bg` 和 `selection_fg` 是 `#rrggbb` 字符串，`ansi` 和 `brights` 各是八个颜色的列表。没有给出光标或选区颜色的方案，会用默认值补上。

这份列表是编译进可执行文件的那一份，所以不需要服务端，也不读配置。你在配置里定义的、或放在配色目录里的方案都不在其中。

## mux 服务端 {#the-mux-server}

`thinkterm-mux-server` 是单独的多路复用器，没有窗口。桌面端把终端放在会话服务器里时，那个服务器就是它，由桌面端替你启动；在没有桌面的主机上，你自己启动它，通常带上 `--daemonize`。

| 参数 | 作用 |
|---|---|
| `--daemonize` | 脱离终端，在后台运行 |
| `--takeover` | 接管同一 socket 上已经在运行的 mux 服务端：它的 pane、窗口和客户端在新的可执行文件下继续，旧服务端退出。正在运行的那个必须支持接管，并且它会拒绝比自己更旧的构建 |
| `--no-initial-pane` | 启动时不带任何 pane，而不是带一个运行默认程序的 pane。桌面端的会话服务器就是这样启动的，因为其中每个 pane 都是桌面端要求开的 |
| `--cwd <DIR>` | 第一个 pane 里程序的工作目录 |
| `-- <PROG>…` | 在第一个 pane 里运行这个程序，而不是你的 shell |
| `-n`、`--config-file`、`--config name=value` | 跳过配置、指定配置文件，或覆盖单个选项 —— 与 `thinkterm` 的全局参数相同 |

## 两个 pane 里的程序可以互相通信

`send-text` 和 `get-text` 就足以让一个程序驱动另一个，两边都不需要任何改动：

```
程序 A ──send-text──►  程序 B 的终端输入
                       B 完成工作并打印回应
程序 A ◄──get-text───  读回结果
```

任何交互式程序都能参与 —— 编码助手、shell 脚本、REPL。接口就是 pane 本身，所以不需要为此专门实现什么。

目前这个交换是靠注入终端输入完成的，这意味着没有消息身份、没有结构化回应。输入由 `send-text --enter` 和 `send-key` 完成，屏幕用 `get-text --trim` 读回。完成与否不必轮询：B 是一条 shell 命令时，`send-key --wait enter` 会在它结束时带着它的状态返回，程序则由 `wait` 做同样的事；B 是编码 agent 时，`events --pane` 会报告它的 `agent_status` 回到了空闲。更结构化的协议正在设计中。

## 相关

- [Agent 状态](https://docs.closex.org/zh/thinkterm/agents.md) —— `cli agent` 和 `cli events` 报告的内容
- [网页访问](https://docs.closex.org/zh/thinkterm/web.md) —— `web-server` 和 `web-token` 打开的是什么
- [TUI](https://docs.closex.org/zh/thinkterm/tui.md) —— 纯终端里的工作区
- [插件](https://docs.closex.org/zh/thinkterm/plugins.md) —— `thinkterm plugin` 管理的对象
- [快捷键与命令](https://docs.closex.org/zh/thinkterm/keyboard.md) —— `show-keys` 打印的键位表
