给 Claude Code 装个仪表盘:钱花多少、上下文剩多少、Git 状态一眼看清

0 阅读10分钟

使用 Claude Code 时,你有没有过这样的体验:一口气让它改了几十个文件,月底看账单才发现花了多少钱;长对话聊到一半被"自动压缩",之前敲定的 SDK 配置、接口约定、踩坑结论被丢得七七八八;想看一眼当前在哪个分支、有没有未提交的改动,还得手动敲 git branch / git status

这三个痛点,靠 Claude Code 的自定义状态栏(Status Line)就能一次性解决。本文从零开始,带你搭一个"专属仪表盘",把每次对话的成本、剩余上下文、Git 分支与改动状态,实时钉在终端底部,再也不用手动查。

目录

一、先说说这三个痛点

痛点 1:成本黑洞,月底才见分晓

Claude Code 是按 token 计费的,会话过程中每多一次大改、每多一轮自检,成本都在悄悄累积。但默认界面上看不到任何费用信息,你只能凭感觉估——"应该没花多少吧"。等账单出来才发现,一个下午的重构已经烧掉了好几美元。

如果能把"本次会话累计花费"实时钉在眼前,那种"再让它多改一轮"的手会自然收住。

痛点 2:上下文压缩的"信息蒸发"

长对话是所有 AI 编程助手的宿命。一旦上下文逼近窗口上限,Claude Code 会触发自动压缩(auto-compact):把历史对话摘要化,腾出空间继续干活。

问题是——摘要永远会丢细节。前两轮你确认过的 SDK 版本、某个接口的参数约定、某个踩坑后得出的结论,压缩之后可能只剩下"用户提到过 xxx"。等模型真用到的细节发现没了,要么靠记忆硬编,要么回滚重来,非常痛苦。

如果能在上下文即将逼近红线之前就看到剩余余量,你就能主动决定:是及时收尾、手动换会话,还是先让它把关键信息写进项目文档再继续。

痛点 3:Git 状态全靠手动

在多个分支之间横跳、改了文件忘记 commit 是常态。每次想确认当前分支、有没有未暂存/未提交的改动,都得手动敲 git branchgit statusgit diff --stat,既打断思路又容易漏。

如果这些信息常驻终端底部,随会话实时刷新,你就能一直知道自己"站在哪、手里有什么牌"。

二、状态栏是什么,怎么工作

Claude Code 的 状态栏(Status Line) 是终端底部一条可完全自定义的栏。它的工作方式非常简单直观:

  1. Claude Code 把当前会话的 JSON 数据通过 stdin 管道传给一个脚本;
  2. 你的脚本解析 JSON、按需加工(算进度条、拼字符串、读 git 状态);
  3. 脚本打印到 stdout 的文本,就是状态栏最终显示的内容。
flowchart LR
    A[Claude Code 会话事件<br/>新消息/权限变化/Vim 切换] -->|触发更新| B[运行 statusLine 脚本]
    B -->|stdin 传入 JSON 会话数据| C[你的脚本<br/>解析/加工/调 git]
    C -->|stdout 输出文本| D[终端底部状态栏<br/>常驻显示]

几个值得记住的特性:

  • 纯本地运行,不消耗 API token:脚本跑在你自己机器上,白嫖的实时信息。
  • 更新时机:每次新的助手消息之后、权限模式变化、Vim 模式切换时触发;更新有 300ms 的防抖合并。如果脚本还没跑完又触发新更新,正在执行的脚本会被取消。
  • 空闲会"静默":当主会话空闲(比如在等后台 subagent 干活)时,事件驱动会停更。这时需要设置 refreshInterval 定时刷新,保证 Git 状态这类外部信息依然实时。

三、3 分钟快速上手

方式一:用 /statusline 命令(最省事)

在 Claude Code 里直接发命令,用自然语言描述你想要的效果,它会自动生成脚本并帮你写好配置:

/statusline 显示模型名、上下文使用百分比和一个进度条

它会在 ~/.claude/ 下生成脚本并自动更新 settings.json。想删掉也一样简单:

/statusline delete

方式二:手动配置(可控性最强)

在用户设置文件 ~/.claude/settings.json(或项目级 settings)里加一个 statusLine 字段:

{
  "statusLine": {
    "type": "command",
    "command": "~/.claude/statusline.sh",
    "padding": 2
  }
}
  • type 固定为 "command",表示"运行一条 shell 命令";
  • command 指向脚本路径,也可以直接写内联命令(比如 jq 一行流);
  • padding 可选,控制状态栏内容的额外水平缩进,默认 0
  • refreshInterval 可选,单位秒(最小 1),让脚本在事件驱动之外再按固定间隔重跑。

以 jq 一行流为例,不用建脚本文件也能显示模型名和上下文百分比:

{
  "statusLine": {
    "type": "command",
    "command": "jq -r '\"[\\(.model.display_name)] \\(.context_window.used_percentage // 0)% context\"'"
  }
}

脚本的三个硬性要求

  1. 可执行chmod +x ~/.claude/statusline.sh
  2. 输出到 stdout,不是 stderr;
  3. 足够快:慢脚本会阻塞状态栏刷新,直到它跑完。

四、状态栏能拿到哪些数据

Claude Code 通过 stdin 传给脚本的完整 JSON 结构长这样:

{
  "cwd": "/current/working/directory",
  "session_id": "abc123...",
  "session_name": "my-session",
  "transcript_path": "/path/to/transcript.jsonl",
  "model": {
    "id": "claude-opus-4-6",
    "display_name": "Opus"
  },
  "workspace": {
    "current_dir": "/current/working/directory",
    "project_dir": "/original/project/directory",
    "added_dirs": [],
    "git_worktree": "feature-xyz"
  },
  "version": "2.1.90",
  "output_style": {
    "name": "default"
  },
  "cost": {
    "total_cost_usd": 0.01234,
    "total_duration_ms": 45000,
    "total_api_duration_ms": 2300,
    "total_lines_added": 156,
    "total_lines_removed": 23
  },
  "context_window": {
    "total_input_tokens": 15234,
    "total_output_tokens": 4521,
    "context_window_size": 200000,
    "used_percentage": 8,
    "remaining_percentage": 92,
    "current_usage": {
      "input_tokens": 8500,
      "output_tokens": 1200,
      "cache_creation_input_tokens": 5000,
      "cache_read_input_tokens": 2000
    }
  },
  "exceeds_200k_tokens": false,
  "rate_limits": {
    "five_hour": { "used_percentage": 23.5, "resets_at": 1738425600 },
    "seven_day": { "used_percentage": 41.2, "resets_at": 1738857600 }
  },
  "vim": { "mode": "NORMAL" },
  "agent": { "name": "security-reviewer" },
  "worktree": {
    "name": "my-feature",
    "path": "/path/to/.claude/worktrees/my-feature",
    "branch": "worktree-my-feature",
    "original_cwd": "/path/to/project",
    "original_branch": "main"
  }
}

常用字段速查表:

字段说明
model.id / model.display_name当前模型标识 / 展示名
workspace.current_dir当前工作目录(与 cwd 同值,推荐用这个)
workspace.project_dir启动 Claude Code 时的项目目录
workspace.git_worktree位于链接 worktree 时的 worktree 名
cost.total_cost_usd本次会话累计花费(美元)
cost.total_duration_ms会话开始以来的墙钟总时长(毫秒)
cost.total_api_duration_ms等待 API 响应的时间(毫秒)
cost.total_lines_added / total_lines_removed新增 / 删除的代码行数
context_window.context_window_size上下文窗口上限(默认 200000)
context_window.used_percentage已用上下文百分比(预计算)
context_window.remaining_percentage剩余上下文百分比(预计算)
context_window.total_input_tokens / total_output_tokens会话累计 token 数
context_window.current_usage最近一次 API 调用的 token 明细
exceeds_200k_tokens最近一次响应总 token 是否超 20 万
rate_limits.five_hour / seven_day5 小时 / 7 天速率限制用量与重置时间
session_id会话唯一标识
session_name自定义会话名(--name / /rename 设置后才有)
transcript_path对话记录文件路径
versionClaude Code 版本
vim.modeVim 模式下的当前模式
agent.name--agent 模式下的 agent 名
worktree.*--worktree 会话的 worktree 信息

几个容易踩的坑

  • session_nameworkspace.git_worktreevimagentworktreerate_limits 这些字段可能压根不在 JSON 里,脚本要优雅处理缺失;
  • context_window.current_usage 在会话首次 API 调用前是 nullused_percentage / remaining_percentage 在会话早期也可能是 null
  • 处理缺失/null 的标准姿势是 jq 的 fallback:.context_window.used_percentage // 0

理解 used_percentage 的计算口径:它只统计"输入类" token,即 input_tokens + cache_creation_input_tokens + cache_read_input_tokens不包含 output tokens。如果你要自己手算百分比,务必用同一个公式,否则数值会对不上。

五、实战:解决三大痛点的完整状态栏

字段到痛点的映射

痛点用到的字段/命令
每次对话成本cost.total_cost_usd(累计花费)+ cost.total_duration_ms(耗时)
上下文余量防压缩context_window.used_percentage + remaining_percentage(配进度条和颜色阈值)
Git 分支与状态脚本内跑 git branch --show-current / git diff --cached --numstat / git diff --numstat

完整脚本

保存为 ~/.claude/statusline.sh

#!/bin/bash
# Claude Code 状态栏:成本 + 上下文余量 + Git 状态
# 输入:Claude Code 通过 stdin 传入的 JSON 会话数据

input=$(cat)

MODEL=$(echo "$input" | jq -r '.model.display_name')
DIR=$(echo "$input" | jq -r '.workspace.current_dir')
COST=$(echo "$input" | jq -r '.cost.total_cost_usd // 0')
PCT=$(echo "$input" | jq -r '.context_window.used_percentage // 0' | cut -d. -f1)
REMAIN=$(echo "$input" | jq -r '.context_window.remaining_percentage // 0' | cut -d. -f1)
DURATION_MS=$(echo "$input" | jq -r '.cost.total_duration_ms // 0')

# ANSI 颜色(终端需支持)
CYAN='\033[36m'; GREEN='\033[32m'; YELLOW='\033[33m'; RED='\033[31m'; RESET='\033[0m'

# 按上下文占用切换进度条颜色:<70 绿,70-89 黄,>=90 红
if [ "$PCT" -ge 90 ]; then BAR_COLOR="$RED"
elif [ "$PCT" -ge 70 ]; then BAR_COLOR="$YELLOW"
else BAR_COLOR="$GREEN"; fi

# 生成 10 格进度条:█ 已用,░ 剩余
BAR_WIDTH=10
FILLED=$((PCT * BAR_WIDTH / 100))
EMPTY=$((BAR_WIDTH - FILLED))
BAR=""
[ "$FILLED" -gt 0 ] && printf -v F "%${FILLED}s" && BAR="${F// /█}"
[ "$EMPTY"  -gt 0 ] && printf -v E "%${EMPTY}s"  && BAR="${BAR}${E// /░}"

# 时长:毫秒 -> m s
DURATION_SEC=$((DURATION_MS / 1000))
MINS=$((DURATION_SEC / 60))
SECS=$((DURATION_SEC % 60))

# 成本:保留两位小数
COST_FMT=$(printf '$%.2f' "$COST")

# ---- Git 状态(非 git 仓库时静默跳过)----
BRANCH=""
GIT_STATUS=""
if git rev-parse --git-dir > /dev/null 2>&1; then
  BRANCH=$(git branch --show-current 2>/dev/null)
  STAGED=$(git diff --cached --numstat 2>/dev/null | wc -l | tr -d ' ')
  MODIFIED=$(git diff --numstat 2>/dev/null | wc -l | tr -d ' ')
  [ -n "$STAGED" ]   && [ "$STAGED"   -gt 0 ] && GIT_STATUS="${GREEN}+${STAGED}${RESET}"
  [ -n "$MODIFIED" ] && [ "$MODIFIED" -gt 0 ] && GIT_STATUS="${GIT_STATUS}${YELLOW}~${MODIFIED}${RESET}"
fi

# ---- 输出(两行)----
# 第一行:模型 + 目录 + Git 分支/改动
echo -e "${CYAN}[$MODEL]${RESET} 📁 ${DIR##*/} | 🌿 $BRANCH $GIT_STATUS"
# 第二行:上下文进度条 + 已用/剩余 + 成本 + 耗时
echo -e "${BAR_COLOR}${BAR}${RESET} ${PCT}% used / ${REMAIN}% left | ${YELLOW}💰 $COST_FMT${RESET} | ⏱️ ${MINS}m ${SECS}s"

然后:

chmod +x ~/.claude/statusline.sh

配置文件

~/.claude/settings.json

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

refreshInterval: 5 是关键:它让脚本每 5 秒强制刷新一次,这样后台 subagent 改代码、你切了分支但会话空闲时,Git 状态也能跟上,不会停留在旧画面。

最终效果

终端底部会出现类似这样的两行常驻信息:

[Opus] 📁 my-app | 🌿 feature/auth +2 ~5
████████░░ 42% used / 58% left | 💰 $0.08 | ⏱️ 7m 3s
  • 第一行:当前模型、项目文件夹、Git 分支;+2 表示 2 个文件已暂存(绿色),~5 表示 5 个文件已修改(黄色);
  • 第二行:上下文进度条(红色阈值自动告警)、已用/剩余百分比、本次会话累计花费、会话已耗时。

一眼扫过去,三个痛点全部覆盖:钱花了多少、上下文还剩多少、代码处于什么状态,全都不用再手动查。

六、进阶技巧

1. 缓存 Git 命令,避免大仓库卡顿

状态栏脚本在活跃会话里跑得很频繁,大仓库里 git status / git diff 可能很慢。方案是把 Git 信息写进缓存文件,5 秒内不重复执行。

缓存文件命名有个讲究:必须用 session_id(会话期间稳定、跨会话唯一),而不是 $$pid——进程号每次调用都会变,缓存就失效了。

SESSION_ID=$(echo "$input" | jq -r '.session_id')
CACHE_FILE="/tmp/statusline-git-cache-$SESSION_ID"
CACHE_MAX_AGE=5

cache_is_stale() {
  [ ! -f "$CACHE_FILE" ] || \
  [ $(($(date +%s) - $(stat -f %m "$CACHE_FILE" 2>/dev/null || stat -c %Y "$CACHE_FILE" 2>/dev/null || echo 0))) -gt $CACHE_MAX_AGE ]
}

if cache_is_stale; then
  if git rev-parse --git-dir > /dev/null 2>&1; then
    BRANCH=$(git branch --show-current 2>/dev/null)
    STAGED=$(git diff --cached --numstat 2>/dev/null | wc -l | tr -d ' ')
    MODIFIED=$(git diff --numstat 2>/dev/null | wc -l | tr -d ' ')
    echo "$BRANCH|$STAGED|$MODIFIED" > "$CACHE_FILE"
  else
    echo "||" > "$CACHE_FILE"
  fi
fi
IFS='|' read -r BRANCH STAGED MODIFIED < "$CACHE_FILE"

stat -f %m 是 macOS 写法,Linux 用 stat -c %Y,上面的 || 已做了兼容。)

2. 多行状态栏

脚本里每个 echo / print 就是一行,想要多少行都行。上面的实战脚本就是两行示例。注意:带转义码的多行输出比纯文本更容易出现渲染毛刺,出问题就简化成纯文本。

3. 颜色阈值告警

用 ANSI 转义码给数值上色,让"健康度"一目了然:

GREEN='\033[32m'; YELLOW='\033[33m'; RED='\033[31m'; RESET='\033[0m'

if [ "$PCT" -ge 90 ]; then BAR_COLOR="$RED"
elif [ "$PCT" -ge 70 ]; then BAR_COLOR="$YELLOW"
else BAR_COLOR="$GREEN"; fi

4. 显示速率限制(Claude.ai Pro/Max 订阅者)

rate_limits 只对 Claude.ai 订阅用户出现,且要在会话首次 API 响应后才有。用 // empty 优雅处理缺失:

FIVE_H=$(echo "$input" | jq -r '.rate_limits.five_hour.used_percentage // empty')
WEEK=$(echo "$input" | jq -r '.rate_limits.seven_day.used_percentage // empty')

LIMITS=""
[ -n "$FIVE_H" ] && LIMITS="5h: $(printf '%.0f' "$FIVE_H")%"
[ -n "$WEEK" ] && LIMITS="${LIMITS:+$LIMITS }7d: $(printf '%.0f' "$WEEK")%"
[ -n "$LIMITS" ] && echo "[$MODEL] | $LIMITS"

5. 可点击链接(OSC 8)

用 OSC 8 转义序列可以把文本变成可点击链接(macOS 用 Cmd+点击,Windows/Linux 用 Ctrl+点击),比如直接链到 GitHub 仓库。注意 printf '%b' 处理转义比 echo -e 更可靠:

REMOTE=$(git remote get-url origin 2>/dev/null | sed 's/git@github.com:/https:\/\/github.com\//' | sed 's/\.git$//')
if [ -n "$REMOTE" ]; then
  REPO_NAME=$(basename "$REMOTE")
  printf '%b' "[$MODEL] 🔗 \e]8;;${REMOTE}\a${REPO_NAME}\e]8;;\a\n"
fi

需要 iTerm2 / Kitty / WezTerm 这类支持超链接的终端;macOS 自带 Terminal.app 不支持。如果链接文字出现了但点不了,试试启动前强制开启:

FORCE_HYPERLINK=1 claude

6. Windows 配置

Windows 上状态栏命令通过 Git Bash 运行,可以在里面再调 PowerShell,也可以直接跑 Bash 脚本:

{
  "statusLine": {
    "type": "command",
    "command": "powershell -NoProfile -File C:/Users/username/.claude/statusline.ps1"
  }
}

7. Subagent 状态栏

subagentStatusLine 可以自定义 agent 面板里每个 subagent 的展示行,输入是一个 JSON 对象,包含 tasks 数组(每个任务有 idnamestatustokenCountcwd 等字段)。按 {"id": "<task id>", "content": "<行内容>"} 逐行输出即可覆盖。

七、常见坑与排错

症状原因与解法
状态栏不出现① 脚本没 chmod +x;② 输出到了 stderr 而非 stdout;③ disableAllHooks 被设为 true;④ 当前目录未接受工作区信任(会提示 statusline skipped · restart to fix,重启并接受信任即可);⑤ 用 claude --debug 查看首次调用的退出码和 stderr
显示 -- 或空白字段在会话早期是 null。用 jq fallback:.xxx // 0;多条消息后仍为空就重启 Claude Code
上下文百分比不对用预计算的 used_percentage,别用 total_input_tokens(那是会话累计值,会超过窗口);注意百分比仅按输入类 token 计算;它可能与 /context 命令的数值有细微差异(计算时机不同)
状态栏卡住/不刷新脚本太慢会阻塞更新直到跑完。优化:缓存 git 命令、精简逻辑;设置 refreshInterval 让空闲期也刷新
OSC 8 链接点了没反应终端不支持(Terminal.app);或没被自动识别,用 FORCE_HYPERLINK=1 强制;SSH/tmux 可能剥掉转义序列
出现 \e]8;; 之类的乱码printf '%b' 替代 echo -e
复杂转义导致花屏简化成纯文本或多行普通输出

八、总结

Claude Code 的状态栏是一个被低估的"白嫖"功能:本地运行、不耗 token、可无限定制。通过一个不到 40 行的脚本,就能把三个高频痛点全部消灭:

  1. 成本透明cost.total_cost_usd 实时显示,花钱有数;
  2. 上下文可控:进度条 + 颜色阈值预警,在自动压缩之前主动收手或换会话,保住关键细节;
  3. Git 常驻:分支 + 暂存/修改状态一眼可见,配合 refreshInterval 保证实时。

动手改脚本时记住三条黄金法则:输出到 stdout、处理 null(// 0)、保持脚本足够快。剩下的,交给你的想象力——时钟、天气、速率限制、可点击的仓库链接……状态栏什么都能放。


参考