Claude Code 的 statusline 与主题配置,版本化管理。
./install.sh # 接入(幂等,可重复执行)
./install.sh --check # 只检查是否已正确接入,不写入install.sh 只改 ~/.claude/settings.json 的三处路径引用(statusLine.command + 两个 sync hook),其它设置一律不动,写入前自动备份且校验 JSON 合法性。
claude-code-config/
├── install.sh 幂等接入脚本
├── statusline/
│ └── statusline-command.sh statusline 主体
└── theme/
├── dopamine-dark.json 深色变体
├── dopamine-light.json 浅色变体
└── sync.sh 按系统外观选变体,写入 ~/.claude/themes/
| 路径 | 原因 |
|---|---|
~/.claude/settings.json |
CC 只读这个位置;由 install.sh 改写其中三处路径 |
~/.claude/themes/dopamine.json |
生成产物,由 theme/sync.sh 写入。CC 只监听 ~/.claude/themes/ 并热重载,所以输出目标不可改。不要手改它,改 theme/dopamine-*.json |
[you] 最近一条我发的消息(常驻,滚动不消失)
[dir] Documents/Builder [wt] 名称 [branch] 分支 [状态徽章]
[模型] ▰▰▱▱▱ 上下文% | 5h 用量% (重置倒计时) 7d 用量%
| 徽章 | 含义 |
|---|---|
conflict:N |
冲突未解决的文件数 |
stash:N |
stash 条目数 |
deleted:N |
删除的文件数 |
renamed:N |
重命名的文件数 |
modified:N |
工作区已改但未 git add |
staged:N |
已 git add 进暂存区 |
untracked:N |
未跟踪的新文件 |
ahead:N |
比上游多 N 个 commit |
behind:N |
比上游少 N 个 commit |
ahead:N behind:M |
分叉(紫色) |
用量颜色:<60% 绿 / 60–85% 黄 / ≥85% 红。
statusline 的 stdin JSON 里有 transcript_path。transcript 是 JSONL,真实用户输入的特征是 .message.content 为 string(tool_result 一律是数组)。此外排除:
- 以 XML 标签开头的系统注入伪消息(
<task-notification>、<command-name>、<local-command-caveat>、<task-id>等) isMeta/isCompactSummary/[Request interrupted- 内嵌的
<system-reminder>块(剥离)
性能上没有全量解析 transcript:tail -r 反向读 + grep -F -m 60 提前关闭管道触发 SIGPIPE,只解析最近 60 条 user 行。
改 theme/dopamine-{light,dark}.json 的 overrides,然后 sh theme/sync.sh(或发一条消息触发 hook)。CC 会热重载。
statusline 只读 themes/dopamine.json 的 base 字段来决定自己用 light 还是 dark 配色,两边色值保持一致:
| 用途 | theme key | light | dark |
|---|---|---|---|
| 消息块背景 | userMessageBackground |
#DCD2F5 |
#3A2A5E |
| 消息文字 | text |
#2A2440 |
#F4F0FF |
> 前缀 |
subtle |
#857BB0 |
#8B7FC4 |
[you] 标签 |
briefLabelYou |
#0096C7 |
#00E5FF |
CC 渲染用户消息的逻辑(从二进制中确认):
case "user": {
let N = to("subtle", s)("> "), // 前缀 > 符号
P = to("text", s), // 消息文字
U = to("userMessageBackground", s, "background"); // 整块背景
}注意 userMessageBackground 若不 override,会落回 CC 内置默认值(light rgb(240,240,240) / dark rgb(55,55,55)),与终端背景对比度仅约 1.14:1,几乎看不见——这是当初消息块"看不清"的根因。
改这些脚本时务必注意,两个都实际踩过:
1. IFS 非默认时数组切片会退化
IFS='/'
parts=("${parts[@]: -5}") # ✘ 被当成 ${parts[*]},合并成单个空格分隔字符串必须让 IFS 只作用于单条命令:IFS='/' read -r -a parts <<< "$disp"。否则深路径截断后 [dir] 会显示错乱。bash 4+ 无此问题。
2. 变量名紧跟全角字符会被吞字节
echo "已写入 $SETTINGS(备份)" # ✘ set -u 下报 unbound variable
echo "已写入 ${SETTINGS}(备份)" # ✔ 用 ${} 显式界定statusline 每次刷新都会重跑整个脚本,所以进程数是主要成本。当前典型路径(普通 repo、非 worktree、非 detached)约 8 个外部进程:jq×2、git×2、date、tput、tail、grep。
| 版本 | 耗时 |
|---|---|
| 每字段一个 jq(10 次)+ git 6 次 | ~113 ms/次 |
| jq 合并为 1 次 | ~100 ms/次 |
| git 也合并 | ~42 ms/次 |
三处关键合并,改这个脚本时不要退回去:
- 一次 jq 取全部字段,用
@sh生成安全引用的赋值再eval(@sh会正确转义含空格/引号/;的路径,不构成注入面) rev-parse一次问多个:--show-toplevel --git-dir --git-common-dir按参数顺序逐行输出- 分支名直接取自
status --porcelain=v2 --branch的表头(# branch.head/# branch.oid),不需要额外的symbolic-ref;只有 detached 才回退一次rev-parse --short
另外 stash 数不启动 git——直接数 $(git-common-dir)/logs/refs/stash 行数(linked worktree 共用 common dir,计数依然正确);dirname/basename 用参数展开替代。
