如何在 VPS 上配置 Claude Code 状态栏
statusLine 会运行脚本,并将 stdout 显示在提示符下方。本文展示如何显示主机名、目录、Git 分支和模型,避免在错误服务器上操作。
Claude Code 状态栏显示的内容
Claude Code 状态栏位于提示符下方,用于显示您编写的脚本输出。您需要将一个 statusLine 块添加到 settings.json,并为其指定一个命令。Claude Code 运行该命令,通过标准输入以 JSON 形式向其传递会话状态,然后将该命令写入标准输出的内容显示出来。
这就是全部约定。您的脚本从标准输入读取 JSON,并向标准输出写入文本。脚本在您的计算机上运行,其输出不会发送给模型,因此不会消耗 token。
在只有一个项目的笔记本电脑上,状态栏只是装饰。在 3 台服务器上,它则是一道安全防线。每个 Claude Code 会话在每个终端中的显示都相同,因此打开 4 个没有标签的 SSH 窗口,很容易把迁移操作执行到错误的服务器上。以主机名开头的状态栏可以避免这类错误。
settings.json 中 statusLine 设置的位置
将其写入用户设置 ~/.claude/settings.json。该设置适用于这台计算机上的所有项目。仓库中的项目设置 .claude/settings.json 也可使用,并且对该目录具有更高优先级。
{
"statusLine": {
"type": "command",
"command": "~/.claude/statusline.sh"
}
}type 始终为 "command"。command 值会通过 shell 执行,因此可以是脚本路径,也可以是普通命令。在编写脚本前,先验证配置链路是否正常:
{
"statusLine": {
"type": "command",
"command": "hostname -s"
}
}启动 Claude Code 并发送一条消息。提示符下方的状态栏现在应显示服务器的短主机名。如果状态栏仍为空,问题出在设置或信任对话框,而不是脚本。请阅读下方的“statusline 为什么保持空白”。
截至 August 2026,共有 3 个可选键。padding 用于添加以字符计的水平间距,默认值为 0。refreshInterval 会在正常触发条件之外每 N 秒重新运行一次命令,最小值为 1。只有当状态栏显示时钟,或显示会在会话空闲期间变化的内容时,才需要使用该设置。hideVimModeIndicator 会隐藏内置的 -- INSERT -- 文本,适用于您自己的脚本已经渲染 vim 模式的情况。
statusline 脚本接收哪些数据?
不要相信你在任何地方看到的字段列表,包括本页内容。请捕获你的版本实际发送的对象。编写一个临时脚本,将标准输入保存到文件:
cat > ~/.claude/statusline-capture.sh <<'EOF'
#!/bin/bash
cat > /tmp/statusline-input.json
echo "captured"
EOF
chmod +x ~/.claude/statusline-capture.sh将 statusLine.command 指向该文件,启动会话并发送一条消息。状态栏会读取 captured。现在查看接收到的内容:
jq . /tmp/statusline-input.json这样你就能得到当前构建的准确数据结构。更新改变数据结构后,也可以随时重复此操作。
截至 2026 年 8 月,文档中记录的稳定部分是嵌套对象,而不是扁平键。model 包含 id 和 display_name。workspace 包含 current_dir 和 project_dir:current_dir 表示当前会话所在的位置,project_dir 表示会话启动时所在的位置;工作目录在会话期间发生变化后,两者会不同。顶层的 cwd 携带与 workspace.current_dir 相同的值。context_window 包含 token 计数以及预先计算的 used_percentage。cost 包含 total_cost_usd 和时长计数器。session_id 在整个会话期间保持不变,并且在不同会话之间具有唯一性,这对后续缓存很重要。
以下 3 条规则可以让脚本在数据结构变化后继续运行。
某些键不存在,而不是值为 null。 vim、agent、pr、worktree 和 effort 仅在相应功能启用时出现。vim 模式关闭时,使用 jq -r 读取 .vim.mode 会输出字面字符串 null,状态栏会向用户显示 null。请将 // empty 附加到每个选择器,这样缺失的键就完全不会输出任何内容。
某些值在早期为 null。 首次 API 响应前,context_window.used_percentage 和 context_window.current_usage 为 null;/compact 之后,current_usage 会恢复为 null,直到下一次调用重新填充该值。因此,状态栏中的上下文百分比需要使用 // 0,否则每次会话开始后的最初几秒都会读取 null。在将这个数字放入状态栏前,建议先了解上下文窗口实际如何填充。
git 分支不在 JSON 中。 JSON 没有字段报告 git 分支。状态栏中的任何分支信息,都是脚本自行运行 git 获取的。
降级而不是中断的状态栏脚本
这是可直接复制粘贴的版本。它会输出主机名、工作目录、git 分支和模型名称。每个字段都有回退值,因此即使 JSON 对象为空,也能生成可用的状态行。
#!/bin/bash
# ~/.claude/statusline.sh
input=$(cat)
# Read one field. Prints nothing when the key is missing or null.
field() { printf '%s' "$input" | jq -r "$1 // empty" 2>/dev/null; }
HOST=$(hostname -s 2>/dev/null)
[ -z "$HOST" ] && HOST="host"
DIR=$(field '.workspace.current_dir')
[ -z "$DIR" ] && DIR=$(field '.cwd')
[ -z "$DIR" ] && DIR="$PWD"
MODEL=$(field '.model.display_name')
[ -z "$MODEL" ] && MODEL="claude"
SHORT="$DIR"
if [ -n "$HOME" ]; then
case "$DIR" in
"$HOME") SHORT="~" ;;
"$HOME"/*) SHORT="~/${DIR#"$HOME"/}" ;;
esac
fi
BRANCH=""
if git -C "$DIR" rev-parse --git-dir >/dev/null 2>&1; then
BRANCH=$(git -C "$DIR" branch --show-current 2>/dev/null)
[ -z "$BRANCH" ] && BRANCH="detached"
fi
CYAN=$'\033[36m'
YELLOW=$'\033[33m'
DIM=$'\033[2m'
RESET=$'\033[0m'
LINE="${CYAN}${HOST}${RESET} ${SHORT}"
[ -n "$BRANCH" ] && LINE="${LINE} ${YELLOW}${BRANCH}${RESET}"
LINE="${LINE} ${DIM}${MODEL}${RESET}"
printf '%s\n' "$LINE"每次读取都通过 field 完成,并追加 // empty。因此,重命名或删除的键会生成空字符串,下一行会提供默认值。目录会依次从 workspace.current_dir 回退到 cwd,再回退到 $PWD。分支来自 git -C "$DIR",而不是直接使用 git,因此分支始终与状态栏显示的目录匹配。
保存脚本,然后为其添加可执行权限:
chmod +x ~/.claude/statusline.sh执行权限不是可选项。Claude Code 通过 shell 运行该命令,因此缺少 +x 的脚本会以 Permission denied 失败,不产生 stdout,并且状态行会保持空白且不会显示错误。
jq 用于在命令行解析 JSON,但全新安装的 Ubuntu 服务器通常未安装该工具:
sudo apt update && sudo apt install -y jq然后将设置指向该脚本,并使用上方第一个 settings.json 代码块。
在信任脚本前先测试
手动运行两次。第一次使用普通的会话对象:
echo '{"model":{"display_name":"Opus"},"workspace":{"current_dir":"/srv/api"},"session_id":"t1"}' | ~/.claude/statusline.sh您会先得到主机名,然后是 /srv/api,最后是 Opus。不会出现分支信息,因为您机器上的 /srv/api 很可能不是 Git 仓库。
第二次进行降级测试。这一步最容易被跳过:
echo '{}' | ~/.claude/statusline.sh对于模式变更可能传入的对象来说,空对象是最坏的情况。该行仍会输出主机名、由 $PWD 获取的当前目录,以及模型名称位置上的 claude。不会发生崩溃,也不会输出 null。通过此测试的脚本可以应对字段重命名,因为对脚本而言,字段重命名和字段缺失是同一种情况。
应显示的内容
状态行会单独显示在内置页脚标记上方,不会替换这些标记。配置正常时,它占用一行,依次显示青色的短主机名、工作目录(主目录会折叠为 ~)、黄色的分支名称(当前目录是 git 仓库时显示),以及使用暗色显示的模型名称。整体效果应接近 web-01 ~/api main Opus,并为这四部分使用对应颜色。
会话启动时(包括恢复会话)、收到新的助手消息时、/compact 完成后、权限模式更改时、切换 vim 模式时,以及按 refreshInterval 设置的周期触发时,该行都会重新运行脚本。更新采用 300 ms 防抖,因此一连串变更只会运行脚本一次。自动补全、帮助菜单和权限提示显示期间,该状态栏会隐藏,之后恢复显示。
主机名必须放在最前面
当您在多台服务器上运行代理时,终端是唯一能告诉您当前位置的东西,而终端并不可靠。在 tmux 窗格中打开第二个 ssh 连接时,窗口标题通常仍保留旧名称,因为设置标题的 shell 根本不知道连接已经切换。让 Claude Code 在 VPS 的分离 tmux 会话中运行,一天后重新连接,屏幕上没有任何信息能区分构建服务器和生产服务器。
状态栏则不同,因为它由 Claude Code 针对每个会话自行渲染,使用的是该会话持有的数据。它不会从错误的窗格继承内容,也不会因为 shell 提示符未刷新而保留过时信息。它显示的就是代理正在写入文件的服务器。
为每台服务器设置独立颜色,这样您无需阅读内容就能先认出它。在 LINE= 赋值语句上方添加以下两行:
CODE=$(printf '%s' "$HOST" | cksum | cut -d' ' -f1)
HOST_COLOR=$(printf '\033[%dm' "$((31 + CODE % 6))")然后用 ${HOST_COLOR} 替代 ${CYAN}。cksum 会输出主机名的校验和,因此同一个名称始终映射到 31 到 36 范围内的同一种颜色,即从红色到青色。将同一脚本复制到每台服务器后,每台服务器都会自行显示标签。
目录也出于同样的原因需要显示。/srv/api 和 /srv/api-staging 在 ssh 命令中只相差一个按键,但实际影响可能相差整个事件。模型和分支是另外两个值得占用状态栏宽度的信息:模型告诉您恢复的是哪个会话,分支告诉您代理是否即将提交到 main。
小屏幕会让这些信息更加重要,因为没有窗口标题可供参考。如果您的使用场景属于这种情况,请参阅通过手机驱动 Claude Code。
保持脚本运行迅速
脚本会在每条 assistant 消息上运行,而 Claude Code 会在收到新更新时取消正在运行的任务。因此,运行缓慢的脚本可能显示过时的文本,或者不显示任何文本。
每次调用 jq 只需几毫秒。git 才是导致速度变慢的部分:在大型仓库中,缓存未命中时,git status 需要数百毫秒。上面的脚本特意避免使用 git status,而是调用 git branch --show-current;它读取 .git/HEAD,可以立即返回。
如果要添加开销更大的操作,请将结果缓存到文件中,并每隔几秒刷新一次。使用 session 作为缓存键:
CACHE="/tmp/statusline-$(field '.session_id')"使用 session_id,不要使用 $$。$$ 是脚本的进程 ID,每次执行都不同,因此使用它作为缓存键永远无法命中缓存,每次都会承担完整的执行开销。session_id 在整个 session 期间保持稳定,并且在不同 session 之间不同,因此两个位于不同仓库中的 Claude Code session 无法读取彼此缓存的分支名称。
还有一个需要注意的限制:tput cols 在 statusline 脚本中不起作用。Claude Code 会捕获输出,而不是将脚本附加到终端,因此无法检测终端宽度。Claude Code 会在运行命令前设置 COLUMNS 和 LINES 环境变量;v2.1.153 及更高版本支持此行为。需要决定输出多少内容时,请读取 $COLUMNS。
状态行为什么一直为空
完全没有任何输出。 使用 ls -l ~/.claude/statusline.sh 检查执行权限,然后使用上面的模拟输入手动运行脚本。如果脚本在 shell 中能输出一行,但在 Claude Code 中没有输出,请先查看 claude --debug。它会记录本次会话首次运行状态行时的退出码和 stderr。
调试日志显示 Status line command skipped: workspace trust not accepted。 状态行会执行 shell 命令,因此受与 hooks 相同的工作区信任机制限制。在接受该目录的信任对话框之前,命令不会运行。这在 VPS 上很常见,因为每个新克隆的目录都是 Claude Code 尚未识别的目录。在该目录中重新启动 Claude Code,并接受对话框。
所有内容都为空,并且已设置 disableAllHooks。 settings.json 中的 "disableAllHooks": true 也会禁用状态行,因为它使用相同的 shell 执行控制。删除它,或将其设置为 false。
状态行输出 null。 jq 选择器访问了缺失或值为 null 的键,而 jq -r 会将 null 输出为 4 个字符的 null。为文本添加 // empty,为数字添加 // 0。
编辑脚本后,状态行立即变为空。 退出码非零或没有输出的命令会使状态行为空。常见原因是末尾有类似 [ -n "$BRANCH" ] && LINE="..." 的行:当分支为空时,它会以 1 退出,并使整个脚本使用该退出码。将 printf 放在最后,或添加 exit 0。
转义序列显示为字面文本,例如状态栏中的 \e]8;;。使用 printf '%b',不要使用 echo -e。可点击的 OSC 8 链接还需要终端支持,并且 tmux 或 SSH 可能会移除这些序列,因此在远程主机上使用普通颜色更可靠。
状态行右侧被截断。 系统通知和 verbose-mode token 计数器会从右侧共用这一行,终端较窄时会发生重叠。请缩短输出。如需准确统计用量,而不是查看状态栏中的数字,请参阅 Claude Code 如何计算 token。
FAQ
Claude Code 状态栏设置存放在哪里?
设置位于 settings.json 中,使用 statusLine 块,其中 type 设置为 "command",command 设置为脚本路径或 shell 命令。用户设置位于 ~/.claude/settings.json,适用于该计算机上的所有项目。项目设置位于仓库内的 .claude/settings.json,并对该目录优先生效。设置会自动重新加载,但更改要到下一次更新触发时才会显示,例如发送下一条消息时。
为什么我的 Claude Code 状态栏为空?
几乎所有情况都可归结为4个原因。脚本没有执行权限,因此 shell 返回 Permission denied,没有内容写入 stdout。工作区信任对话框从未被接受,claude --debug 记录了 Status line command skipped: workspace trust not accepted。disableAllHooks 为 true,因此在同一限制条件下禁用状态栏。或者脚本以非零状态退出,导致该行为空。请先手动测试:echo '{}' | ~/.claude/statusline.sh 必须输出内容。
状态栏 JSON 是否包含 git 分支?
不包含。JSON 携带模型、工作区目录、上下文窗口编号和费用等会话状态。其中没有任何字段报告 git 信息。状态栏中的分支来自您自己的脚本调用 git branch --show-current。使用 git -C "$DIR" 从 JSON 传递目录,这样分支始终与状态栏显示的目录匹配。
状态栏是否消耗 token 或拖慢会话?
不消耗 token,因为脚本在本地运行,其输出不会发送给模型。运行速度由您负责。该命令会在每条 assistant 消息后运行,并采用 300 ms 防抖。新更新到达时,Claude Code 会取消正在运行的任务,因此如果脚本耗时整整1秒,状态栏会显示过时的文本。避免在大型仓库中使用 git status,并将耗时操作的结果缓存到以 session_id 为键的文件中。
如何在每台服务器上显示不同的状态栏?
保留一个脚本,并让它读取计算机信息。上面的脚本会输出 $HOSTNAME,并以 hostname -s 作为回退值。因此,将同一个文件复制到每台服务器后,每台服务器都能正确显示自身标识;校验和颜色机制还会为每个主机名分配独立颜色。如果某台服务器需要不同的布局,请在该服务器上所使用仓库的项目设置中添加 statusLine 块,因为项目设置对该目录的优先级高于用户设置。