Claude Code 状态栏怎么配:把模型、上下文用量、花费和 git 分支放在输入框下面
状态栏是输入框下面的一行,内容由你写的脚本决定。本站实测了从零配一个:先看 Claude Code 实际发来哪些数据,再显示模型、分支、上下文进度条和花费,加一行提示缓存的倒计时。附它什么时候刷新、脚本出错时为什么整行消失、怎么从调试日志里找原因,以及通过中转站用时少了哪些数据。
本站已实测,测试日期:2026-10-12。
本页目录(12 节)
这篇教程解决一个问题:干活的时候想随时看到几样东西:上下文用了多少、这个会话花了多少钱、现在在哪个分支。不想每次敲命令去查。
Claude Code 的状态栏就是干这个的。它是输入框下面的一行,显示什么由你写的一个脚本决定。本站从一个空目录开始配了一遍。
| 实测环境 | |
|---|---|
| 日期 | 2026-10-12 |
| 版本 | Claude Code 2.1.296 |
| 系统 | Ubuntu 24.04,tmux 里 110 列宽的终端,装了 jq |
| 模型 | Opus 5.5,effort 是 medium |
| 连接方式 | 通过酷站AI 中转站,不是直连官方 |
它是怎么工作的
三句话:
- 你在设置里指定一条命令(通常是一个脚本)。
- Claude Code 每次要刷新状态栏时运行它,把当前会话的数据用 JSON 从标准输入传进去。
- 脚本往标准输出打印什么,状态栏就显示什么。
脚本在你本机运行,不发请求,不花 token。
有两种配法。一种是在会话里输入 /statusline 加一句描述,比如 /statusline show model name and context percentage with a progress bar,让 Claude Code 替你生成脚本并改好设置。它会把脚本放进 ~/.claude/,并修改用户级的设置文件。另一种是自己写。本站走的是第二种,下面是全过程;/statusline 命令没有实测。
1先看它发来什么
文档列了几十个字段,但哪些字段在你的环境里真的有值,要自己看一眼。先写一个只做两件事的脚本:把收到的 JSON 存下来,再打印一行最简单的内容。
#!/bin/bash
input=$(cat)
echo "$input" > "$HOME/demo/h1-statusline/.claude/last-input.json"
echo "[$(echo "$input" | jq -r '.model.display_name')] $(echo "$input" | jq -r '.context_window.used_percentage // 0')% context"
存成 .claude/statusline-dump.sh,加上执行权限(chmod +x),然后在项目的 .claude/settings.json 里指向它:
{
"statusLine": {
"type": "command",
"command": "/home/ccdemo/demo/h1-statusline/.claude/statusline-dump.sh"
}
}
写在项目设置里只对这个项目生效。想让所有项目都用,写到 ~/.claude/settings.json。
启动 Claude Code。第一次进这个目录会问是否信任这个文件夹,选信任之后,输入框下面出现了这一行:
❯
──────────────────────────────────────────────────────────────
[Opus 5.5] 0% context ◐ medium · /effort
⏵⏵ auto mode on (shift+tab to cycle) · ← for agents
信任之前脚本不会运行。 本站在信任对话框还开着的时候检查过,脚本一次都没被调用。原因文档写了:状态栏执行的是一条 shell 命令,和设置文件里的 hooks 受同一条规则管,没信任的目录里不跑。
发一句话(“只说一个字:好”),再看存下来的 JSON。下面是实际内容,去掉了会话 ID 和记录文件的路径:
{
"cwd": "/home/ccdemo/demo/h1-statusline",
"effort": { "level": "medium" },
"model": { "id": "claude-opus-5-5", "display_name": "Opus 5.5" },
"workspace": {
"current_dir": "/home/ccdemo/demo/h1-statusline",
"project_dir": "/home/ccdemo/demo/h1-statusline",
"added_dirs": []
},
"version": "2.1.296",
"output_style": { "name": "default" },
"cost": {
"total_cost_usd": 0.153833,
"total_duration_ms": 33484,
"total_api_duration_ms": 4128,
"total_lines_added": 0,
"total_lines_removed": 0
},
"context_window": {
"total_input_tokens": 30755,
"total_output_tokens": 3,
"context_window_size": 1000000,
"current_usage": {
"input_tokens": 2,
"output_tokens": 3,
"cache_creation_input_tokens": 30753,
"cache_read_input_tokens": 0
},
"used_percentage": 3,
"remaining_percentage": 97
},
"exceeds_200k_tokens": false,
"prompt_cache": {
"warm": true,
"ttl": "5m",
"expires_at": 1791812520,
"requests": 1,
"misses": 0,
"hit_ratio": 0,
"cache_write_tokens": 30753,
"recache_tokens_if_cold": 30755
},
"fast_mode": false,
"thinking": { "enabled": true }
}
(prompt_cache 里省略了几个值为空的字段。)
这一眼看出四件事:
- 第一次回复之前,很多字段是空的。 刚启动时
used_percentage和current_usage都是null。所以脚本里要写// 0这样的兜底,否则状态栏会显示null。 - 回一个字,花了 0.15 美元。 输出只有 3 个 token,但有 30753 个 token 被写进了缓存。那是 Claude Code 自己的系统提示和工具定义,每个新会话的第一句都要付这一笔。
- 没有
rate_limits。 文档说明这个字段只在 claude.ai 的 Pro 和 Max 订阅下才有(里面是 5 小时和 7 天两个额度窗口的用量)。按量计费、走中转站都没有。 - 没有
workspace.repo、session_name、worktree、pr。 示例仓库没有配 origin,会话没起名字,也不在 worktree 里。这些字段是“有才出现”的。
顺带看到上下文窗口是 100 万(context_window_size: 1000000)。
2写一个真正用得上的
本站用的脚本,显示五样:模型和 effort、目录名、git 分支和没提交的改动数、上下文用量的进度条、花费。
#!/bin/bash
# Claude Code 状态栏:模型和 effort、目录、git 分支和改动数、上下文用量、花费。
input=$(cat)
MODEL=$(jq -r '.model.display_name' <<<"$input")
EFFORT=$(jq -r '.effort.level // "-"' <<<"$input")
DIR=$(jq -r '.workspace.current_dir' <<<"$input")
PCT=$(jq -r '.context_window.used_percentage // 0' <<<"$input" | cut -d. -f1)
COST=$(jq -r '.cost.total_cost_usd // 0' <<<"$input")
BRANCH=$(git -C "$DIR" branch --show-current 2>/dev/null)
DIRTY=$(git -C "$DIR" status --porcelain 2>/dev/null | wc -l | tr -d ' ')
GIT=""
[ -n "$BRANCH" ] && GIT=" ⎇ $BRANCH"
[ "${DIRTY:-0}" -gt 0 ] && GIT="$GIT ±$DIRTY"
# 上下文用量:一条 10 格的进度条,过半变黄,过八成变红
if [ "$PCT" -ge 80 ]; then COLOR='\033[31m'; elif [ "$PCT" -ge 50 ]; then COLOR='\033[33m'; else COLOR='\033[32m'; fi
FILLED=$((PCT / 10))
BAR=$(printf '%*s' "$FILLED" '' | tr ' ' '#')$(printf '%*s' $((10 - FILLED)) '' | tr ' ' '-')
printf '%b\n' "[$MODEL·$EFFORT] ${DIR##*/}$GIT | ${COLOR}${BAR}\033[0m ${PCT}% | \$$(printf '%.2f' "$COST")"
接上之前,先在命令行里用假数据试。 脚本就是一个读标准输入的程序,不需要开着 Claude Code 才能调试:
echo '{"model":{"display_name":"Opus 5.5"},"effort":{"level":"high"},
"workspace":{"current_dir":"'$PWD'"},
"context_window":{"used_percentage":62.4},"cost":{"total_cost_usd":1.2345}}' | .claude/statusline.sh
[Opus 5.5·high] h1-statusline ⎇ feature/statusline ±1 | ######---- 62% | $1.23
再喂一份字段不全的,确认兜底管用:
[Opus 5.5·-] tmp | ---------- 0% | $0.00
3接上
把设置里的 command 改成新脚本的路径,保存。不用重启会话,几秒后状态栏就换了:
[Opus 5.5·medium] h1-statusline ⎇ feature/statusline ±1 | ---------- 3% | $0.15
⏵⏵ auto mode on (shift+tab to cycle) · ← for agents
±1 是因为仓库里有一个没提交的东西:.claude/ 这个目录本身。
它什么时候刷新
这一点决定了什么内容适合放在状态栏上。本站在脚本里加了一行记录运行时间,试了几种情况:
| 操作 | 状态栏刷新了吗 |
|---|---|
| Claude 回复了一条新消息 | 刷新 |
按 Shift+Tab 切换权限模式 |
刷新,每按一次运行一次 |
改了设置里的 command |
刷新,不用重启 |
| 在仓库里新建了一个文件 | 不刷新,等到下一条回复才从 ±1 变成 ±2 |
| 什么都不做,空等 8 秒 | 不刷新 |
文档列的触发条件还有:/compact 完成、切换 vim 模式、订阅额度窗口或提示缓存到了过期时间。300 毫秒内的多次变化会合并成一次。
所以状态栏是“事件来了才更新”的。git 的状态、时钟这类自己会变的东西,要么接受它慢半拍,要么加一个定时刷新:
{
"statusLine": {
"type": "command",
"command": "~/.claude/statusline.sh",
"refreshInterval": 5
}
}
refreshInterval 的单位是秒,最小是 1。除了事件触发,它还让脚本每隔这么久再跑一次。
加第二行:缓存还能热多久
脚本打印几行,状态栏就显示几行。第二行放什么?本站选了提示缓存的倒计时,因为它直接和钱有关。
前面看到,缓存的有效期(ttl)是 5 分钟。过了这个时间再发消息,整段上下文要重新写一遍缓存,按写入的价格收费。会话越长,这一笔越大。停下来想事情、去倒杯水,回来时缓存可能已经凉了。
#!/bin/bash
# 第二行:提示缓存还能热多久。第一行交给上面那个脚本。
input=$(cat)
"$(dirname "$0")/statusline.sh" <<<"$input"
WARM=$(jq -r '.prompt_cache.warm // empty' <<<"$input")
[ -z "$WARM" ] && exit 0 # 第一次回复之前没有缓存数据,只显示一行
LEFT=$(( $(jq -r '.prompt_cache.expires_at // 0' <<<"$input") - $(date +%s) ))
COLD=$(jq -r '.prompt_cache.recache_tokens_if_cold // 0' <<<"$input")
if [ "$WARM" = "true" ] && [ "$LEFT" -gt 0 ]; then
printf '缓存还热 %d:%02d · 过期后下一句要重写约 %s token\n' $((LEFT / 60)) $((LEFT % 60)) "$COLD"
else
printf '\033[33m缓存已过期\033[0m · 下一句要重写约 %s token\n' "$COLD"
fi
配上 "refreshInterval": 5,实测的效果:
[Opus 5.5·medium] h1-statusline ⎇ feature/statusline ±2 | ---------- 3% | $0.18
缓存还热 4:04 · 过期后下一句要重写约 30791 token
没有任何操作,11 秒后再看:
缓存还热 3:54 · 过期后下一句要重写约 30791 token
倒计时在走。这一行的用处:看到快到 0 了,要么趁热把下一句发出去,要么干脆在这里 /compact 或者收尾。
缓存和花费的关系,提示词缓存为什么这么重要和怎么省用量两篇讲得更细。
状态栏不显示了,怎么查
状态栏出问题时不报错,整行直接消失。本站故意做了四个坏脚本:
| 脚本的问题 | 状态栏的表现 |
|---|---|
| 打印了内容,但退出码是 1 | 整行消失 |
| 只往标准错误写,标准输出没有内容 | 整行消失 |
| 没有执行权限 | 整行消失 |
| 要跑 6 秒才出结果 | 这 6 秒里一直显示旧内容,跑完才换成新的 |
前三种在界面上看起来一模一样。要分清是哪一种,用 claude --debug 启动,然后看调试日志。日志在 ~/.claude/debug/ 下面,每个会话一个文件,latest 指向最新的那个:
grep StatusLine ~/.claude/debug/latest
三个坏脚本对应的日志:
[WARN] StatusLine [/…/bad-exit.sh] completed with status 1
[DEBUG] StatusLine [/…/bad-stderr.sh] stderr: 只写到标准错误
[DEBUG] StatusLine [/…/bad-noexec.sh] stderr: /bin/sh: 1: /…/bad-noexec.sh: Permission denied
[WARN] StatusLine [/…/bad-noexec.sh] completed with status 126
退出码和脚本的报错都在里面。
排查的顺序:
- 这个目录信任了吗?没信任的话脚本根本不跑。
- 脚本有执行权限吗?
- 用假数据在命令行里跑一遍(第二步里那条命令),看有没有输出、退出码是不是 0。
- 还不行,
claude --debug看日志。
文档另外提了两种和组织设置有关的情况:disableAllHooks 为 true,或者管理员设了 allowManagedHooksOnly,自定义的状态栏都会被关掉。
能用的数据
按用途挑着列。完整的清单见官方文档。
| 想显示 | 字段 | 本站实测里有没有 |
|---|---|---|
| 模型 | model.display_name、model.id |
有 |
| effort 档位 | effort.level |
有 |
| 当前目录、启动目录 | workspace.current_dir、workspace.project_dir |
有 |
| 上下文用了百分之几 | context_window.used_percentage |
有,第一次回复前是空的 |
| 上下文窗口多大 | context_window.context_window_size |
有,1000000 |
| 这个会话的花费估算 | cost.total_cost_usd |
有 |
| 会话时长、等接口的时长 | cost.total_duration_ms、cost.total_api_duration_ms |
有 |
| 改了多少行代码 | cost.total_lines_added、cost.total_lines_removed |
有 |
| 缓存状态 | prompt_cache.warm、expires_at、hit_ratio 等 |
有,第一次回复后才出现 |
| 输出风格、fast mode、思考开没开 | output_style.name、fast_mode、thinking.enabled |
有 |
| Claude Code 版本 | version |
有 |
| 订阅额度用了多少 | rate_limits.five_hour、rate_limits.seven_day |
没有,只有 Pro 和 Max 订阅才有 |
| 仓库的远端地址 | workspace.repo.host、owner、name |
没有,示例仓库没配 origin |
| 当前分支的 PR | pr.number、pr.url、pr.review_state |
没有 |
| worktree | worktree.name、worktree.branch |
没有,不在 worktree 会话里 |
| 会话名、vim 模式、代理名 | session_name、vim.mode、agent.name |
没有 |
git 分支不在数据里,要脚本自己用 git 命令取,上面的脚本就是这么做的。
通过中转站用,有两点不一样
- 看不到额度。
rate_limits是订阅才有的。中转站的余额要去它的后台看,状态栏拿不到。 - 花费是估算。
cost.total_cost_usd是 Claude Code 在本地按官方价格算的,文档原话是可能和实际账单不一样。中转站按自己的倍率扣费,这个数字只能用来比较“这个会话比那个贵多少”。
其他字段和接入方式无关。缓存的数据也有:这家中转站把缓存相关的用量字段传了回来,所以 prompt_cache 是有值的。文档说明,如果中转站或网关不回传这些字段,caching_observed 会是 false。
几条提醒
- 脚本要快。 它跑得很频繁,慢脚本会让状态栏显示旧内容。大仓库里
git status可能要几百毫秒,文档的做法是把 git 的结果缓存到临时文件里,几秒才重新取一次,缓存文件名用输入里的session_id,免得几个会话互相读错。 - 别用
tput cols取宽度。 脚本的输出是被 Claude Code 接走的,不直接连着终端。要知道宽度,读环境变量COLUMNS和LINES。本站在 110 列的终端里读到的COLUMNS是 110。 - 内容别太长。 状态栏右边还要显示通知(比如 effort 的提示、MCP 报错),窄终端里会把你的内容截断。
- 颜色用 ANSI 转义码。 上面的进度条在 tmux 里显示正常。打印时用
printf '%b',比echo -e稳。
这一趟花了多少
两句各一个字的对话,状态栏上显示 0.18 美元。几乎全是第一句写缓存的那一笔。脚本本身不花钱。这是 Claude Code 按官方价格估算的数字,不是中转站实际扣的金额。
这次实测没有覆盖到的
/statusline命令没有跑。 它会改用户级的设置文件,测试账号的那份文件我们不动。- 只在 Linux 的 tmux 里测过。 Windows 的写法(PowerShell、Git Bash)文档里有,没有试。
- 可点击的链接没有试。 文档说要 iTerm2、Kitty、WezTerm 这类支持 OSC 8 的终端,经过 SSH 和 tmux 还可能被吃掉。
- 订阅才有的额度字段、PR、worktree、vim 模式这些字段没有见到实际的值。
- 子代理的状态栏没有试。 文档里有一个单独的
subagentStatusLine设置,用来定制代理面板里每一行的显示。 - 每种情况只试了一次。