ClaudeMap

·技能与命令

2026 年 Claude Code 状态栏实战——statusLine 配置、stdin JSON 全字段协议(model / context_window / cost / rate_limits)、三个可直接复用的 bash 模板、事件驱动刷新机制,以及状态栏空白的五条最快排错路径。

Claude Code 状态栏完全指南:stdin JSON 协议与可复用脚本模板(2026)

Claude Code 的状态栏(statusline)是终端底部一行可定制信息,由一个外部命令脚本渲染——Claude Code 把当前会话状态(模型、目录、上下文用量、成本、限流)以 JSON 从脚本的 stdin 喂进去,脚本打印到 stdout 的内容就是状态栏。本文基于 2026 年 9 月的官方文档(v2.1.251),覆盖 statusLine 配置项、stdin JSON 全字段协议、三个可直接复用的脚本模板,以及状态栏空白的五条常见排错路径。

TL;DR

  • ~/.claude/settings.jsonstatusLine: { type: "command", command: "..." },或直接用 /statusline 命令生成
  • 脚本从 stdin 收到一份 JSON:model / workspace / context_window / cost / rate_limits 全在里面
  • 更新是事件驱动的(新消息、/compact、权限模式切换);refreshInterval 可以追加定时刷新
  • 输出必须走 stdout;大量字段可能缺失或为 null,一律用 // 0 这类兜底
  • 不想自己写脚本:生态里有 ccstatusline(12.7k stars,2026 年 9 月)等现成工具

五分钟配置 statusLine

状态栏配置写在 settings.json(用户级 ~/.claude/settings.json 或项目级):

{
  "statusLine": {
    "type": "command",
    "command": "~/.claude/statusline.sh",
    "padding": 2
  }
}

三个字段的含义:

| 字段 | 必填 | 说明 | |---|---|---| | type | 是 | 目前只有 "command" 一种 | | command | 是 | 脚本路径,或一行内联 shell 命令(如 jq 单行) | | padding | 否 | 状态栏左右留白字符数,默认 0 |

三个可选字段值得知道:refreshInterval(每 N 秒重跑一次脚本,最小 1,适合时钟这类非事件驱动的显示)、hideVimModeIndicator(你的脚本自己渲染 vim 模式时,关掉内建的 -- INSERT --)、以及独立于主状态栏的 subagentStatusLine(定制 agent 面板里的子代理行)。

最快的上手方式是在 Claude Code 里运行 /statusline,用自然语言描述你想要的状态栏,它会生成配置和脚本。删除状态栏用 /statusline delete,或直接删掉 statusLine 字段。

stdin JSON 协议:脚本收到的到底是什么

每次刷新,Claude Code 把一份 JSON 写到你脚本的 stdin。这是整份协议的核心字段(带 * 的字段可能整个缺席):

| 字段 | 类型 | 内容 | |---|---|---| | model.id / model.display_name | string | 当前模型 ID 与显示名 | | workspace.current_dir | string | 当前工作目录(比顶层 cwd 更可靠) | | workspace.project_dir | string | 项目根目录 | | context_window.used_percentage | number | 上下文窗口已用百分比(可能为 null) | | context_window.remaining_percentage | number | 剩余百分比(可能为 null) | | cost.total_cost_usd | number | 本次会话累计美元成本 | | cost.total_duration_ms | number | 会话墙钟时长(毫秒) | | cost.total_lines_added / removed | number | 累计新增/删除行数 | | output_style.name | string | 当前 output style | | version | string | Claude Code 版本号 | | rate_limits * | object | Pro/Max 订阅的 5 小时/7 天窗口用量(used_percentageresets_at) | | prompt_cache * | object | 缓存观测:warmhit_ratiottlexpires_at 等(v2.1.251+) | | vim.mode | string | NORMAL / INSERT / VISUAL | | pr * | object | 当前关联 PR:编号、review 状态 | | effort.level | string | 思考力度档位(low → max) |

两个容易踩的细节:

字段会缺席、也会为 null。 rate_limits 只有订阅用户且收到过第一次 API 响应后才有;context_window.current_usage 在首次 API 调用前、以及每次 /compact 之后都是 null。所以 jq 里一律 // 0 兜底,Python 里 or 0,Node 里 ?.

used_percentage 只算输入侧。 它的公式是 input_tokens + cache_creation_input_tokens + cache_read_input_tokenscontext_window_size(默认 200k,扩展后 1M)的比例——输出 token 不计入。自己估算余量时要对齐这个口径,否则你的数字会和官方进度条对不上。

三个可直接复用的脚本模板

模板一:jq 单行——模型名 + 上下文进度条

不用写文件,command 直接内联也能用:

jq -r '"[\(.model.display_name)] \(.context_window.used_percentage // 0)%"'

真正常用的是官方文档同款进度条,存成 ~/.claude/statusline.sh

#!/bin/bash
input=$(cat)
MODEL=$(echo "$input" | jq -r '.model.display_name')
PCT=$(echo "$input" | jq -r '.context_window.used_percentage // 0' | cut -d. -f1)
BAR_WIDTH=10
FILLED=$((PCT * BAR_WIDTH / 100))
EMPTY=$((BAR_WIDTH - FILLED))
BAR=""
[ "$FILLED" -gt 0 ] && printf -v FILL "%${FILLED}s" && BAR="${FILL// /▓}"
[ "$EMPTY" -gt 0 ] && printf -v PAD "%${EMPTY}s" && BAR="${BAR}${PAD// /░}"
echo "[$MODEL] $BAR $PCT%"

渲染效果:[Sonnet 4.5] ▓▓▓░░░░░░░ 32%used_percentage // 0 兜底了会话开头的 null;cut -d. -f1 把小数截成整数供算术使用。

模板二:成本与时长

#!/bin/bash
input=$(cat)
COST=$(echo "$input" | jq -r '.cost.total_cost_usd // 0')
DUR=$(echo "$input" | jq -r '.cost.total_duration_ms // 0')
MIN=$((DUR / 60000)); SEC=$(((DUR % 60000) / 1000))
printf '💰 $%.2f | ⏱️ %dm %ds\n' "$COST" "$MIN" "$SEC"

total_cost_usd 是 API 实际计费口径,订阅用户显示的是等值参考。想再显示本次会话改了多少行,加 .cost.total_lines_added.cost.total_lines_removed 即可。

模板三:多行状态栏(git 分支 + 上下文 + 成本)

多行输出是官方支持的:每个 echo 一行。Git 信息不要每次现查——把 git 状态缓存到 /tmp(按 session_id 做 key、约 5 秒 TTL)是官方文档推荐的做法:

#!/bin/bash
input=$(cat)
SESSION=$(echo "$input" | jq -r '.session_id')
DIR=$(echo "$input" | jq -r '.workspace.current_dir')
CACHE="/tmp/cc-git-${SESSION}"
MTIME=$(stat -f %m "$CACHE" 2>/dev/null || stat -c %Y "$CACHE" 2>/dev/null || echo 0)

if [ ! -f "$CACHE" ] || [ $(( $(date +%s) - MTIME )) -gt 5 ]; then
  (cd "$DIR" && git branch --show-current 2>/dev/null; git status --porcelain 2>/dev/null | wc -l) > "$CACHE"
fi
BRANCH=$(sed -n 1p "$CACHE"); DIRTY=$(sed -n 2p "$CACHE")

PCT=$(echo "$input" | jq -r '.context_window.used_percentage // 0' | cut -d. -f1)
COST=$(echo "$input" | jq -r '.cost.total_cost_usd // 0')

echo "🌿 ${BRANCH:-no-git} · ${DIRTY} changed"
echo "[$(echo "$input" | jq -r '.model.display_name')] ctx ${PCT}% · \$$(printf '%.2f' $COST)"

两行状态栏:第一行 git 分支与脏文件数,第二行模型、上下文余量与累计成本。ANSI 颜色码和 OSC 8 可点击链接也都被支持,可以在此基础上加色。

刷新时机与性能:你的脚本什么时候会跑

状态栏不是每秒轮询,而是事件驱动:会话启动(含 resume)跑一次,之后在新 assistant 消息、/compact 完成、权限模式切换、vim 模式切换、command 配置变更、refreshInterval 到期、限流窗口 resets_at 到点、缓存 expires_at 到点这些事件上触发。更新之间有 300ms 去抖;如果上一轮脚本还没跑完又来了新事件,在跑的脚本会被直接取消

这决定了两条性能纪律:

  1. 慢脚本会掉帧。 在大仓库里裸跑 git status 就是典型的慢脚本——按模板三缓存到 /tmp,key 用 session_id(不要用 PID,脚本每次调用的 PID 都不同)。
  2. 想要时钟就得 refreshInterval 没有新消息时状态栏不会自己动;显示时钟、或想在后台 subagent 干活时看到状态变化,都靠这个字段。

常见坑:状态栏为什么是空的

坑 1:输出写到了 stderr。 状态栏只读 stdout——调试信息打到 stderr 或退出码非零,状态栏直接空白。修法:脚本末尾确认最终 echo 走 stdout,测试时直接命令行跑一遍看输出。

坑 2:没做 null 兜底。 会话开头大量字段为 null,jq -r '.context_window.used_percentage' 返回字符串 null 拼进算术直接报错、脚本非零退出、状态栏空白。修法:所有数值字段 // 0 兜底。

坑 3:tput cols 拿不到宽度。 Claude Code 捕获脚本输出,tutl 检测不到真实终端。修法:读 COLUMNS / LINES 环境变量。

坑 4:workspace 信任未确认。 新项目里状态栏空白且无报错——信任确认前脚本会被跳过(claude --debug 里能看到 Status line command skipped: workspace trust not accepted)。修法:接受 workspace 信任提示。

坑 5:Windows 路径被 Git Bash 吃掉反斜杠。 command 里的 C:\Users\... 未加引号时反斜杠被剥离。修法:用正斜杠 C:/Users/...~

改了脚本不生效是正常的——脚本修改在下一次触发事件时生效,不会即时重跑。

相关指南

常见问题

statusLine 配置写在哪里?type 必须是什么?

写在 ~/.claude/settings.json(用户级)或项目级 settings.json 里,type 目前只有 "command" 一种,command 可以是脚本路径或一行内联 shell 命令。最快的生成方式是在 Claude Code 里运行 /statusline 用自然语言描述需求;删除用 /statusline delete

状态栏多久刷新一次?

事件驱动:会话启动跑一次,之后在新 assistant 消息、/compact 完成、权限或 vim 模式切换等事件上触发,更新间隔有 300ms 去抖。没有新消息时不会自动刷新——需要时钟类显示就配 refreshInterval(每 N 秒重跑,最小 1)。

为什么我的状态栏一片空白?

按顺序查五件事:脚本输出是否走了 stdout(stderr 和非零退出码都会导致空白)、数值字段是否做了 // 0 的 null 兜底、workspace 信任是否已确认(claude --debug 可见 skip 信息)、disableAllHooks 类策略是否禁用了它、以及脚本是否有语法错误——命令行直接跑一遍最快定位。

context_window.used_percentage 是怎么算的?

输入侧三个 token 之和(input_tokens + cache_creation_input_tokens + cache_read_input_tokens)除以 context_window_size(默认 200k、扩展后 1M)。输出 token 不计入。自算余量时务必对齐这个口径,否则和官方进度条对不上。

脚本里怎么拿到终端宽度?

COLUMNS / LINES 环境变量。tput cols 在状态栏脚本里拿不到真实值——Claude Code 会捕获脚本输出,不接终端。

有现成的状态栏工具吗,不想自己写脚本?

有。生态里最主流的是 ccstatusline(12.7k stars,2026 年 9 月),配置式、可装成 TUI 定制器;此外还有 claude-hud、Rust 写的 CCometixLine 等选择。想理解自己状态栏的每个字节,还是本文的三个模板最直接。

官方参考资料

本文基于截至 2026 年 9 月 1 日的公开文档(v2.1.251),statusline 字段仍在快速演进,以官方文档为准。