ステータスラインのカスタマイズ
- 契約を理解する: Claude Code はセッション全体を JSON としてスクリプトにパイプし、スクリプトが出力したものをそのまま表示する
- 両方の方法で設定する — /statusline に自然言語で頼むか、statusLine ブロックを自分で書く
- 60 ほどあるフィールドの中から、本当に載せる価値のある少数を選ぶ
- スクリプトを再実行する 8 つのイベント、300 ms のデバウンス、そして refreshInterval が必要になる場面を知る
- 3 つのレシピを作る: コンテキストバー、PR とレート制限の行、プロンプトキャッシュの健全性インジケーター
- 4 つの落とし穴を避ける: tput cols が動かない、最初の API 呼び出し前の null フィールド、実行中のスクリプトのキャンセル、キーボードヒントの消失
ステータスラインは、Claude Code の下部にある帯で、設定した任意のシェルスクリプトを実行します。Claude Code はセッション全体の状態を stdin に JSON としてパイプし、スクリプトが何かを出力すると、Claude Code がそれを表示します。ローカルで動作し、API トークンは消費しません。
この契約が機能のすべてです。以下はすべて、JSON に何が入っているかと、スクリプトがいつ実行されるかについてです。
ステータスラインは、組み込みのフッターバッジを置き換えるのではなく、その上の専用の行に表示されます。ただし設定すると、Claude Code はフッターのキーボードヒントのほとんどを表示しなくなります。esc to interrupt、フォールバックの ? for shortcuts、hold space to speak の音声入力ヒントなどです。これらに頼っているチームメイトにこの設定を渡す前に、知っておく価値があります。
セットアップ
方法は 2 つあります。手早いのは、欲しいものを説明することです。
Let Claude Code write the script for you
/statusline show model name and context percentage with a progress bar
~/.claude/ にスクリプトを生成し、設定を更新して、ファイル編集の許可を求めます。/statusline delete(または clear、あるいは「削除して」)を実行すると、元に戻せます。
手動の方法は、~/.claude/settings.json またはプロジェクト設定の statusLine ブロックです。
{
"statusLine": {
"type": "command",
"command": "~/.claude/statusline.sh",
"padding": 2,
"refreshInterval": 10
}
}
フィールドは 4 つで、そのうち 3 つは任意です。
| フィールド | 意味 |
|---|---|
type | "command" |
command | スクリプトのパスまたはインラインのシェルコマンド — シェルで実行されるので、ワンライナーも使える |
padding | 文字数で表す追加の水平方向の余白。デフォルトは 0。ターミナルの端からの絶対距離ではなく、相対的なインデント |
refreshInterval | イベントのトリガーに加えて、N 秒ごとに再実行する。最小値は 1。未設定ならイベントのみで実行 |
hideVimModeIndicator | スクリプトが vim.mode を自分で描画する場合に true にして、モードが二重に表示されないようにする |
command はシェルで実行されるので、実用的な最小のステータスラインにはスクリプトファイルすら要りません。jq だけで足ります。
{
"statusLine": {
"type": "command",
"command": "jq -r '\"[\\(.model.display_name)] \\(.context_window.used_percentage // 0)% context\"'"
}
}
JSON に入っているもの
入力は大きく、モデル、ワークスペース、コスト、コンテキストウィンドウ、レート制限、PR の状態、ワークツリー、vim モード、プロンプトキャッシュが含まれます。すべて表示したくなりますが、こらえてください。読まなくなったステータスラインは、ないほうがましです。
次のフィールドは多くの人にとって載せる価値があり、防げる「想定外」ごとにまとめています。
| フィールド | 防げるもの |
|---|---|
model.display_name | 些細なタスクに Opus を使い果たすこと |
context_window.used_percentage | 作業の途中でコンテキストが尽きること — /compact のきっかけ |
workspace.current_dir、workspace.git_worktree | 複数のワークツリーを開いているときに、違うチェックアウトを編集すること |
rate_limits.five_hour.used_percentage | 警告なしに壁にぶつかること |
cost.total_cost_usd | セッションが知らぬ間に高額になること(定価に基づくクライアント側の見積もりで、請求額ではない) |
effort.level、fast_mode | effort を max のままにしたのを忘れること |
pr.number、pr.review_state | レビューが済んだか確認するためにブラウザへ切り替えること |
prompt_cache.hit_ratio、prompt_cache.warm | 最も目立たないコストの漏れ — 3 つ目のレシピを参照 |
スキーマについての構造上の注意が 3 つあり、実際のデバッグ時間を節約できます。
cwdとworkspace.current_dirは同じ値です。workspace.project_dirとの一貫性のため、workspace.current_dirを使うことを推奨します。workspace.project_dirは Claude Code が起動された場所で、セッション中に作業ディレクトリが変わると異なることがあります。workspace.git_worktreeとworktree.*は別物です。workspace.git_worktreeはgit worktree addで作られたあらゆるリンクされたワークツリーで設定され、メインのツリーでは存在しません。worktree.*オブジェクトは、セッションが Claude Code のワークツリーセッションにあるあいだだけ存在します。used_percentageは入力のみです。input_tokens + cache_creation_input_tokens + cache_read_input_tokensから計算され、出力トークンは含まれません。current_usageから自分でパーセンテージを計算する場合は、同じ入力のみの式を使わないと、Claude Code の数値と一致しません。
スクリプトが実行されるタイミング
セッション開始時に一度(再開時を含む)。その後は、次のそれぞれで実行されます。
- 最も一般的なケースで、タイマーなしでもコンテキストのパーセンテージがほぼ最新に保たれる理由です。
- /compact の完了、権限モードの変更、vim モードの切り替え、または settings の statusLine のコマンドの編集。
- レート制限のウィンドウが resets_at に達する、または温まったプロンプトキャッシュが expires_at に達するとき。Claude Code は、すでに渡した JSON からそれらのタイムスタンプを読み取り、再実行をスケジュールします。
- 何も起きていないあいだにも発火する唯一のトリガーで、まさにそれが必要な場面のためのものです。
この一覧から導かれる動作が 3 つあります。
- 更新は 300 ms でデバウンスされるので、素早い変更は 1 回の実行にまとめられます。
command自体を編集した場合はデバウンスを飛ばして即座に実行されます。 - 新しいトリガーは実行中のスクリプトをキャンセルします。 速く保ってください。低速なネットワーク呼び出しを行うステータスラインは、出力する前に日常的に強制終了されます。
- メインセッションがアイドルのとき、イベントトリガーは静かになります。 典型的なのは、背後で git の状態を変更しているバックグラウンドのサブエージェントを待つコーディネーターです。それに加えて、時計や外部から取得するセグメントのために使うのが
refreshIntervalです。
スクリプトは単なる 1 行以外も出力できます。echo ごとに 1 行になり、ANSI エスケープコードで色が付けられ(\033[32m が緑、\033[0m がリセット)、OSC 8 シーケンスで iTerm2、Kitty、WezTerm ではテキストをクリック可能にできます。
3 つのレシピ
レシピ 1: コンテキストバー
最も価値の高い行で、ほかに何も追加しなくても持っておく価値があります。// 0 のフォールバックに注意してください。current_usage は最初の API 呼び出し前と /compact の直後に null になるので、ガードのない jq の式は、まさに画面を最も見ている瞬間に null を出力します。
#!/usr/bin/env bash
input=$(cat)
model=$(echo "$input" | jq -r '.model.display_name // "claude"')
dir=$(echo "$input" | jq -r '.workspace.current_dir // "."')
pct=$(echo "$input" | jq -r '.context_window.used_percentage // 0' | cut -d. -f1)
# 20-cell bar: fill proportional to pct
filled=$(( pct / 5 ))
bar=""
for ((i = 0; i < 20; i++)); do
if (( i < filled )); then bar+="▓"; else bar+="░"; fi
done
# green under 60%, yellow under 85%, red above
if (( pct < 60 )); then color="\033[32m"
elif (( pct < 85 )); then color="\033[33m"
else color="\033[31m"
fi
printf "%s · %s · ${color}%s %s%%\033[0m" "$model" "$(basename "$dir")" "$bar" "$pct"
レシピ 2: ブランチ、PR の状態、レート制限の余裕
pr.* はフッターの PR バッジを反映します。GitLab のリポジトリでは、代わりにブランチのオープンなマージリクエストから値が入り、pr.kind が mr に設定され、pr.number は MR の番号になります。レート制限のフィールドは、後になって欲しかったと思う人の多いものです。
#!/usr/bin/env bash
input=$(cat)
branch=$(git branch --show-current 2>/dev/null)
wt=$(echo "$input" | jq -r '.workspace.git_worktree // empty')
pr=$(echo "$input" | jq -r '.pr.number // empty')
state=$(echo "$input" | jq -r '.pr.review_state // empty')
limit=$(echo "$input" | jq -r '.rate_limits.five_hour.used_percentage // empty')
line="${branch:-no-git}"
[ -n "$wt" ] && line+=" ⎇ $wt"
[ -n "$pr" ] && line+=" · #$pr${state:+ ($state)}"
[ -n "$limit" ] && line+=" · 5h ${limit%.*}%"
printf "%s" "$line"
レシピ 3: キャッシュの健全性、見えない漏れ
誰も書かないレシピですが、長いエージェント的セッションで最も見返りの大きいものです。プロンプトキャッシュがあるから 100 ターンのセッションが現実的な費用に収まります。そしてプロンプトの上部近くで 1 バイトでも変わると、それが静かにキャッシュを壊し、毎ターン、プレフィックス全体を満額で再処理することになります。デフォルトの UI には、これが起きていることを示すものがありません。
prompt_cache がそれを教えてくれます。信号の大半は 2 つのフィールドが担い、warm(プレフィックスがまだ TTL 内か)と hit_ratio(全入力トークンに対するキャッシュ読み取りの比率、0〜1)です。そして last_miss_cause.causes が原因を名指しします。tools_changed、system_prompt_changed、ttl_expired_5m、likely_server_side のいずれかです。
#!/usr/bin/env bash
input=$(cat)
warm=$(echo "$input" | jq -r '.prompt_cache.warm // empty')
ratio=$(echo "$input" | jq -r '.prompt_cache.hit_ratio // empty')
cause=$(echo "$input" | jq -r '.prompt_cache.last_miss_cause.causes[0] // empty')
misses=$(echo "$input" | jq -r '.prompt_cache.misses // 0')
# prompt_cache is absent until the main conversation's first API response
[ -z "$warm" ] && { printf "cache: —"; exit 0; }
pct=$(awk "BEGIN{printf \"%d\", ${ratio:-0} * 100}")
if [ "$warm" = "true" ]; then icon="\033[32m●\033[0m"; else icon="\033[31m○\033[0m"; fi
printf "cache: ${icon} %s%% hit" "$pct"
(( misses > 0 )) && printf " · %s miss%s%s" "$misses" "$([ "$misses" -eq 1 ] || echo es)" "${cause:+ ($cause)}"
system_prompt_changed が毎ターン現れるなら、プロンプトのプレフィックス内の何かが非決定的だということです。CLAUDE.md 内のタイムスタンプや、変化する出力を注入するフックなどです。tools_changed が毎ターン出るなら、たいていは MCP サーバーがツールを一貫せずに登録しています。どちらも、見えるようになれば修正できます。必要なときに確認したい場合、Claude Code は /usage の Prompt cache (main) の行に同じ統計を表示します。
これらの統計はメインの会話のみを対象とし、サブエージェントのリクエストは数えられないことに注意してください。
4 つの落とし穴
tput colsは動きません。 Claude Code はスクリプトの出力をターミナルに接続せずキャプチャするため、tput colsや言語レベルの幅検出ではターミナルのサイズが分かりません。代わりに環境変数COLUMNSとLINESを読んでください。Claude Code は実行のたびに、その両方を現在の寸法に設定します。- すべてのフィールドをガードしてください。
current_usageは最初の API 呼び出し前と/compactの直後にnullです。prompt_cacheとrate_limits.spend_limitは最初の API レスポンスまで存在しません。pr.review_stateはprがあっても存在しないことがあります。effort.levelはそのパラメーターのないモデルでは存在しません。すべてに// emptyか// 0を使ってください。 - 遅いスクリプトはキューに入らずキャンセルされます。 新しいトリガーが実行中のものを強制終了します。外部データが欲しい場合は、別のプロセスでファイルにキャッシュし、ステータスラインにそのファイルを読ませてください。
- 利用上限のドル金額は遅れます。 Claude アプリのゲートウェイの背後では、
used_percentageはレスポンスごとに届きますが、used_usd、limit_usd、periodは約 5 分ごとに別途取得されるため、パーセンテージと一時的に食い違うことがあります。spend_limitがあっても、これらは任意として扱ってください。
スクリプトを書きたくない場合は?
ccstatusline のようなコミュニティのツールには、そのまま使える設定可能なステータスラインがあります。出発点としては妥当ですが、アシスタントのメッセージごとにあなたの権限で動作するサードパーティのスクリプトと同様、信頼する前に中身を読んでください。サードパーティコードのレビューを参照してください。
理解度チェック
0/5- ステータスラインは単なるスクリプトです。Claude Code はセッション全体を stdin に JSON としてパイプし、スクリプトが stdout に出力したものをそのまま描画します。ローカルで動作し、トークンは消費しません。
- /statusline に自然言語で説明すると、スクリプトを書いて設定も配線してくれます。手動の形は、type、command、任意の padding、refreshInterval、hideVimModeIndicator を持つ statusLine ブロックです。
- 60 ほどのフィールドのうち、載せる価値があるのは少数です: model.display_name、context_window.used_percentage、ディレクトリまたはワークツリー、rate_limits.five_hour.used_percentage、そして長いセッションでは prompt_cache。
- イベント駆動で、300 ms のデバウンスがあります。アイドルのメインセッションはイベントを発火しないため、時間ベースや外部から取得するセグメントには refreshInterval が必要です。新しいトリガーは実行中のスクリプトをキャンセルするので、速く保ってください。
- すべてのフィールドを // empty か // 0 でガードしてください。current_usage は最初の API 呼び出し前と /compact の直後に null で、prompt_cache と spend_limit は最初の API レスポンスまで存在しません。
- used_percentage は入力のみ(新規入力 + キャッシュ書き込み + キャッシュ読み取り)です。自分で計算する場合は、その式に合わせてください。
- tput cols はスクリプトの内部からターミナルを検出できません。Claude Code が実行前に設定する COLUMNS と LINES を読んでください。
- prompt_cache のセグメントは、最も効果の大きい少し変わった追加です。warm と hit_ratio でプレフィックスがキャッシュされているかが分かり、last_miss_cause.causes が壊した原因を名指しします。
次へ
- コンテキスト管理 — 画面に出したパーセンテージが実際に何を意味し、どう対処するか
- プロンプトキャッシュ —
hit_ratioを見る価値がある理由と、キャッシュされたプレフィックスを壊すもの - settings.json —
statusLineブロックの置き場所と、どの層が優先されるか - Git ワークツリー —
workspace.git_worktreeとworktree.*が表す機能