為了做一個線上的 statusLine 設定頁面 —— 勾欄位、調樣式、即時預覽、匯出一份能交給 coding agent 的規格 —— 有幾件事得先查清楚。
Claude Code 的 statusLine 是一條命令:把 session 狀態序列化成 JSON 從 stdin 餵進來,腳本印什麼就顯示什麼。官方文件列了 34 個欄位組,但接下來的問題沒有答案:哪些欄位真的會動、哪些值得佔用終端那一行、進度條字元在別人的終端顯示得出來嗎、腳本跑一次要多久才不至於拖慢每一則回應。
以下是做到那個頁面能用為止,累積出來的答案。
1. 組裝器
先看成品。statusLine 組裝器 把 45 個欄位、13 種進度條字元、12 套終端配色攤成選項,預覽即時更新 —— 不必改腳本、重開 Claude Code 才看得到結果。本文後面的欄位清單與取捨判準,都可以在上面直接試。四列預覽分別餵不同的用量情境(低用量、常態、偏高、接近上限),用來檢視顏色門檻在實際數值下的表現。本文第 4 節那組進度條對比就是從這裡得到的。
下面是它產生的一種配置,11 個欄位:
context 的進度條是五格逐格漸層,5h 與 7d 只有數字加門檻換色,其餘用 dim 灰。為什麼這樣分配,是第 4 節的主題。
調完之後按「複製給 Claude」,會得到一份機器可讀的設定規格:欄位順序、每個欄位在 payload 裡的確切路徑、進度條參數、配色的 hex 值,以及四種情境下應該長的樣子。把那段貼回 Claude Code 對話,它就能據此產生對應平台的腳本並寫進設定檔。規格裡逐欄位標明 payload 路徑不是多餘的 —— 第 3 節會說明為什麼消費端不能照單次取樣的結果寫死。
頁面底下另外收錄了 115 個現成的 statusLine 實作,附最後更新時間;不想自己組的話可以直接挑一個,選擇的依據見第 7 節。
2. 更新模型
statusLine 何時重跑,決定了哪些欄位有意義。文件列出的觸發條件:
| 類別 | 觸發 |
|---|---|
| session 生命週期 | 開始、resume、/compact 結束 |
| 狀態變更 | 新的 assistant 訊息、權限模式改變、vim 模式切換、command 設定被改 |
| payload 內的時間點 | rate-limit 視窗走到 resets_at、熱 cache 走到 expires_at |
| 計時器 | refreshInterval 到點(選用,單位秒,最小 1) |
前兩類是事件驅動,第三類是資料驅動,只有第四類是時間驅動而且預設關閉。更新有 300ms debounce,連續變化會合併成一次;腳本還在跑時有新的觸發進來,in-flight 的那次會被取消。
這個模型有個直接後果:腳本自己算出來的時間值不會自己更新。
| 時間類欄位 | 沒設 refreshInterval 的行為 |
|---|---|
rate_limits.*.resets_at | 視窗真的重置時跳一次 |
prompt_cache.expires_at | cache 過期時跳一次 |
| 由上述兩者換算的「還剩多久」 | 不動,停在最後一次事件當下的值 |
倒數會凍結在最後一則訊息抵達的時刻。文件另外指出主 session 閒置時事件會整個停止,例如等待背景 subagent 期間,這時只有計時器能維持新鮮度。
代價是腳本按固定頻率重跑。設 refreshInterval: 60 而腳本要 600ms,等於每分鐘燒掉 1% 的一個核心 —— 這個數字在第 5 節會有意義。
3. payload 結構與可用性
欄位表看得到有什麼,看不到哪些會出現在你的機器上。這兩件事要分開處理。
取得實際內容
在 command 前面串一個 tee,payload 會在送進腳本的同時落地:
"statusLine": {
"type": "command",
"command": "tee /tmp/statusline-last.json | node ~/.claude/statusline.js"
}
下一次觸發後 jq . /tmp/statusline-last.json 就是完整內容。這個做法不必改腳本,取樣完把 tee 段拿掉即可。
也可以寫進腳本內部:
input=$(cat) # 必須在腳本開頭
printf '%s' "$input" > /tmp/statusline-last.json
注意這兩行只在腳本裡有意義。直接貼進互動 shell 的話,cat 會停在那裡等你打字,按 Ctrl+D 結束只會得到一個空檔案。
條件性欄位
文件列 34 個欄位組,但單次 dump 拿不到那麼多。在 Claude Code 2.1.268、WSL2、1M context 模型下的一次取樣只有 17 個 top-level 鍵,缺席的包括 vim.mode(未啟用 vim mode)、agent.name(未帶 --agent)、pr.*(分支無開啟中 PR)、workspace.git_worktree(不在 linked worktree)、rate_limits.spend_limit(未走 gateway)。
欄位可用性分三級:
| 級別 | 欄位 | 消費端處理 |
|---|---|---|
| 恆存 | model、cwd、workspace.current_dir、version、session_id、context_window.*、cost.* | 可直接取用 |
| 帳號/方案相關 | rate_limits.five_hour、seven_day、prompt_cache.* | 判空後省略整格 |
| 情境相關 | vim.mode、agent.name、pr.*、git_worktree、spend_limit、session_name | 判空,且不可影響其他欄位排版 |
取樣結果不能當成規格。任何欄位都要當成可能不存在,包括第一級 —— 那只是這次取樣沒缺而已。
另外,dump 裡有一個 scratchpad_dir,文件的欄位表沒有列。
4. 顯示取捨
單看欄位表看不出取捨。終端寬度是固定的,一行大約 80 到 140 字元,扣掉分隔符後每個欄位的預算是個位數到十幾個字元。決定放什麼,實際上是在四個維度上排序:
| 維度 | 問題 | 為什麼重要 |
|---|---|---|
| 變動頻率 | 這個值多久變一次 | 不變的欄位是靜態裝飾,佔寬度而不提供資訊 |
| 決策價值 | 看到它會不會改變行為 | statusLine 的功能是讓人在不下指令的情況下做決定 |
| 字元成本 | 渲染後佔幾個字元 | 寬度是零和的,長欄位排擠其他欄位 |
| 可用性 | 是否條件性存在 | 條件性欄位需要額外的判空邏輯 |
下面逐類列出欄位與渲染結果,長度以等寬字型計。判斷依上述四個維度,但不逐欄位打分 —— 同一個欄位在不同工作型態下的決策價值差很多,列成固定評分反而誤導。
位置類
| 欄位 | 渲染 | 長度 | 備註 |
|---|---|---|---|
workspace.current_dir | ~/work/StoreOps/server 或 server | 6–40 | 多視窗時的辨識依據 |
workspace.project_dir | StoreOps | 8 | 多數時候等於 current_dir |
workspace.git_worktree | feat-ledger | 11 | worktree 之間唯一的區分 |
workspace.repo.owner/.name | Austinsuyoyo/StoreOps | 21 | 21 字元換一個幾乎不變的值 |
workspace.added_dirs | +16 dirs | 8 | 陣列,需自行取長度 |
cwd 與 workspace.current_dir 值相同,文件建議用後者以配合 project_dir。全路徑在深層目錄下會吃掉半行,取 basename 是常見處理。
模型與 session
| 欄位 | 渲染 | 長度 | 備註 |
|---|---|---|---|
model.display_name | Opus 5 | 6 | 切換模型後容易忘記當前是哪個 |
session_name | 重構 ledger | 不定 | --name 或 /rename 設定,否則自動生成 |
effort.level | xhigh | 3–6 | low / medium / high / xhigh / max |
thinking.enabled | thinking | 8 | 布林,false 時顯示什麼要自行決定 |
fast_mode | fast | 4 | 布林 |
vim.mode | NORMAL | 6–11 | 唯一高頻變動的狀態欄位;VISUAL LINE 最長 |
agent.name | executor | 不定 | --agent 或 agent 設定才存在 |
version | 2.1.268 | 7 | 幾乎不變 |
output_style.name | default | 7 | 九成時間是 default |
session_id | UUID | 36 | 須截斷才能用 |
prompt_id | UUID | 36 | 每則訊息都變,但變化不可行動 |
transcript_path | 絕對路徑 | 80+ | 長度不適合狀態列 |
vim.mode 是少數幾個高頻變動又有決策價值的欄位,而且它本身就是 statusLine 的觸發條件之一。prompt_id 相反:每則訊息都變,但變化不帶任何可行動的資訊,是四個維度裡最差的組合。
context window
| 欄位 | 渲染 | 備註 |
|---|---|---|
context_window.used_percentage | 42% | 已算好,直接用;對應 /compact 這個明確行動 |
context_window.remaining_percentage | 58% | 與上互補,兩者擇一 |
context_window.context_window_size | 1M | 200000 或 1000000 |
context_window.total_input_tokens | 253.4k | 含 cache 讀取,與百分比資訊重複 |
context_window.total_output_tokens | 1.3k | |
context_window.current_usage.cache_read_input_tokens | 252.4k | 最後一次 API 呼叫的量,屬除錯資料 |
exceeds_200k_tokens | 200k+ | 見下 |
exceeds_200k_tokens 的語意是「最近一次回應的 token 總數超過 200k」。在 200k 模型上這是有效的警示;在 1M context 模型上,工作一陣子後它會永遠是 true —— 變動頻率歸零,決策價值歸零。
額度與 cache
| 欄位 | 渲染 | 備註 |
|---|---|---|
rate_limits.five_hour.used_percentage | 7% | 0–100 |
rate_limits.seven_day.used_percentage | 67% | 0–100 |
rate_limits.*.resets_at | 2h14m | epoch 秒;換算成倒數需搭配 refreshInterval,見第 2 節 |
rate_limits.spend_limit.* | 21% | 僅走 gateway 時存在 |
prompt_cache.hit_ratio | 97% | 0–1 浮點數,需乘 100;沒有對應的行動 |
prompt_cache.warm | warm | 布林 |
prompt_cache.ttl | 1h | 字串,5m 或 1h |
prompt_cache.expires_at | 42m | epoch 秒,同樣需 refreshInterval |
prompt_cache.requests/.misses | 51/0 | 除錯資料 |
hit_ratio 是 0 到 1 的浮點數,要自行乘 100。prompt_cache 底下另有 cache_write_tokens、recache_tokens_if_cold 等,屬於除錯資料。
成本與版本控制
| 欄位 | 渲染 | 備註 |
|---|---|---|
cost.total_cost_usd | $9.70 | client 端牌價估算,非帳單金額 |
cost.total_duration_ms | 73m | session 總時長 |
cost.total_api_duration_ms | 26m | 等待 API 的時間;與上者相減是自己花的時間 |
cost.total_lines_added/_removed | +1529 -8 | |
pr.number | #42 | 當前分支的開啟中 PR |
pr.review_state | approved | approved / pending / changes_requested / draft |
pr.kind | mr | GitLab 為 mr,GitHub 缺席 |
git 的 branch 與工作區狀態不在 payload 裡,要自己呼叫 git。這是唯一會讓腳本 fork 子行程的欄位群,成本見下節。
收斂
把上面的判斷收斂,會落在三組:
高決策價值 + 高變動:used_percentage、five_hour、seven_day、vim.mode。這四個是 statusLine 的核心 —— 它們會讓人在不下指令的情況下改變下一步。
高決策價值 + 低變動:model、session_name、git_worktree、agent.name、pr.review_state。它們幾乎不動,但在多視窗、多 worktree 的情境下是唯一的辨識依據。值得放,但不需要進度條這類強調。
低決策價值:其餘。prompt_id、transcript_path、session_id 連截斷後都不值得;output_style.name、version、context_window_size 是變動頻率近零的靜態值,適合放在別處而不是狀態列。
一個推論:進度條只值得畫在決策門檻明確的欄位上。context 有 /compact 這個明確的行動門檻,額度有「把重活往後排」的門檻。hit_ratio 沒有對應的行動,畫條只是把注意力導向一個看了也不會做什麼的數字。
把 context、5h、7d 都畫成進度條:
只留 context 一條,5h 與 7d 用數字加門檻換色:
兩者資料完全相同。三條並排時視覺重量一致,但 ctx 72% 逼近 /compact 門檻、7d 41% 還很寬鬆 —— 眼睛要讀完數字才分得出哪個要緊。只留一條時,進度條本身就成為「這個要注意」的標記,5h 與 7d 退回背景,超過門檻才靠顏色跳出來。
這不是說進度條不好,而是它是強調機制,強調三個等於沒有強調。
5. 渲染成本
statusLine 每次觸發都重跑整支腳本,所以啟動成本是主要開銷,不是腳本邏輯本身。
跨平台的限制先於效能:
- Claude Code 不提供 node。 安裝文件明載
The installed claude binary does not itself invoke Node,npm 安裝版裝的同樣是原生 binary。「Claude Code 用 Node 寫所以機器上有 node」不成立,node 必須偵測。 - Git for Windows 不含
jq.exe。 shell + jq 的組合在原生 Windows 無法直接運作。 - PowerShell 5.1 為 Windows 內建,是唯一無條件存在的 host。
四種 host 的啟動成本,在 WSL2 透過 interop 呼叫 Windows 執行檔測得,含約 20–40ms 的 interop 開銷,各執行 5 次取中位數:
| Host | 啟動 | 完整渲染 | JSON 解析 |
|---|---|---|---|
| PowerShell 5.1(內建) | 676ms | — | ConvertFrom-Json |
| PowerShell 7 | 328ms | — | ConvertFrom-Json |
| node | 84ms | 110ms | 內建 |
| Git Bash (MSYS) | 67ms | — | 無 jq,須自行抓欄位 |
這組數字不適合當成原生 Windows 的絕對值,但相對順序清楚:PowerShell 5.1 光啟動就是 node 的八倍。同一份 node 腳本在 WSL 上是 28ms,對照 sh + jq 的 26ms,差異在量測誤差內。
另外兩項與成本相關的機制:
- MSYS 的 fork 約 25ms。 Git Bash 路線下,渲染路徑每個
$(...)命令替換都是一次 fork。實測一份到處使用命令替換、且分四次呼叫 git 的腳本,在 Linux 上正常而在 Windows/MSYS 上是 1.24 秒;移除渲染路徑的 subshell、git 併成單一git status --porcelain --branch後降到 0.22 秒。 - 路徑展開。 Claude Code 2.1.47 起,
command字串中的~在 Windows 會被展開,%USERPROFILE%則不再可靠。因此"command": "node ~/.claude/statusline.js"可三平台共用。
終端寬度不能用 tput cols 或語言層的偵測取得 —— Claude Code 捕捉腳本輸出而非連接終端。寬度由環境變數 COLUMNS 和 LINES 傳入。
6. 字元覆蓋率
進度條與分隔符的字元選擇受限於終端字型。直接讀取 Windows 內建字型的 cmap 表:
| 字型 | 缺少 |
|---|---|
| Consolas | ▰ ▱ ⣿ ⣀ ▕ ▶ ▷ ✦ ✧ ❯ ⟩ ⟨ ⎇ ⇅ ◐ ◆ ▦ ⟲ ✓ |
| Lucida Console | 上列再加 ● ━ ╌ ╭ ╮ ╰ ╯ ▸ |
兩者都沒有全形寬的問題。█ ░ ▓ ■ □ │ ─ 在兩者皆存在,是跨平台最安全的集合。Powerline 的尖角與 capsule 的圓帽屬於 Nerd Font 私有區,與此表無關。
瀏覽器無法用來代測這件事。 想在網頁上預覽終端呈現時,直覺做法是用 canvas measureText 比對字元寬度與豆腐字寬度,但瀏覽器遇到缺字會靜默 fallback 到系統其他字型,canvas 量不到豆腐。document.fonts.check() 同樣不可用 —— 它對未安裝的字型回傳 true,在一台只有 DejaVu Sans Mono 的 Linux 機器上會回報「已安裝 SF Mono、Menlo、Consolas」。字型覆蓋率只能從 cmap 表取得。
7. 生態現況
2026-09-11 查了 115 個 statusLine 專案最後一次更新程式碼的時間:
| 最後 push | 專案數 |
|---|---|
| 30 天內 | 35 |
| 30 天至半年 | 62 |
| 半年至一年 | 14 |
| 超過一年 | 4 |
star 數與維護狀態沒有相關性。星數第二高的 CCometixLine(3,458★,Rust)已經六個月沒有更新,而 ccstatusline(12,836★,TypeScript)在查詢前四天還有更新。
這對選型有直接影響:本文列出的欄位裡,rate_limits、prompt_cache、pr.* 都是後期加入 payload 的,停更超過半年的專案多半讀不到。挑現成專案時,「最後一次更新是多久以前」比 star 數更有參考價值。
編譯型(Rust、Go)的優勢是零 runtime 依賴,在沒有 node 的 Windows 上是唯一不需額外安裝的選項;代價是設定必須透過 config schema,欄位組合被鎖定在作者預設的範圍內。腳本型(Node、Python、Shell)反過來,可任意客製但依賴 runtime。前面提到的 CCometixLine 支援 7 個 segment,對照本文的 34 個欄位組,差距就在這裡。
參考資料
- Customize your status line —— payload 欄位表、更新觸發條件、
refreshInterval與COLUMNS/LINES的說明都出自這頁 - Advanced setup —— 安裝方式,以及「claude binary 不 invoke Node」的原文
- Settings reference ——
statusLine在設定檔中的完整結構 - ccstatusline —— 目前最活躍的實作,60 幾個 widget 加 TUI 設定器
- CCometixLine —— Rust 單一執行檔,零 runtime 依賴的代表
- claude-powerline —— 段落設計最完整的 powerline 實作
- awesome-claude-statusline —— 本文專案清單的來源之一