教程Claude Code系列第 2 / 4 篇

Claude Code 状态栏怎么配:把模型、上下文用量、花费和 git 分支放在输入框下面

状态栏是输入框下面的一行,内容由你写的脚本决定。本站实测了从零配一个:先看 Claude Code 实际发来哪些数据,再显示模型、分支、上下文进度条和花费,加一行提示缓存的倒计时。附它什么时候刷新、脚本出错时为什么整行消失、怎么从调试日志里找原因,以及通过中转站用时少了哪些数据。

发布 · 约 10 分钟读完 · 经人工审核()· 本站的内容怎么来的

本站已实测,测试日期:2026-10-12。

本页目录(12 节)
  1. 它是怎么工作的
  2. 第一步:先看它发来什么
  3. 第二步:写一个真正用得上的
  4. 第三步:接上
  5. 它什么时候刷新
  6. 加第二行:缓存还能热多久
  7. 状态栏不显示了,怎么查
  8. 能用的数据
  9. 通过中转站用,有两点不一样
  10. 几条提醒
  11. 这一趟花了多少
  12. 这次实测没有覆盖到的

这篇教程解决一个问题:干活的时候想随时看到几样东西:上下文用了多少、这个会话花了多少钱、现在在哪个分支。不想每次敲命令去查。

Claude Code 的状态栏就是干这个的。它是输入框下面的一行,显示什么由你写的一个脚本决定。本站从一个空目录开始配了一遍。

实测环境
日期 2026-10-12
版本 Claude Code 2.1.296
系统 Ubuntu 24.04,tmux 里 110 列宽的终端,装了 jq
模型 Opus 5.5,effort 是 medium
连接方式 通过酷站AI 中转站,不是直连官方

它是怎么工作的

三句话:

  1. 你在设置里指定一条命令(通常是一个脚本)。
  2. Claude Code 每次要刷新状态栏时运行它,把当前会话的数据用 JSON 从标准输入传进去。
  3. 脚本往标准输出打印什么,状态栏就显示什么。

脚本在你本机运行,不发请求,不花 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

退出码和脚本的报错都在里面。

排查的顺序:

  1. 这个目录信任了吗?没信任的话脚本根本不跑。
  2. 脚本有执行权限吗?
  3. 用假数据在命令行里跑一遍(第二步里那条命令),看有没有输出、退出码是不是 0。
  4. 还不行,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 设置,用来定制代理面板里每一行的显示。
  • 每种情况只试了一次。

来源

通过 API 中转站使用这些模型?到 Veridrop 免费检测中转站给你的是不是它声称的模型,或者看看中转站排行榜前 100 名。

相关文章