はじめに
Claude Codeを使っていると、気がついたらコンテキストが枯渇していた、という経験はありませんか。
長い作業セッションの途中で突然「コンテキストウィンドウがいっぱいです」と言われると、かなりテンポが崩れますよね。
レート制限も同じで、「あと何時間使えるんだっけ?」と都度確認しないといけないのは地味にストレスです。
そこで今回は、Claude Codeのステータスラインをカスタマイズして、コンテキスト使用率とレート制限の残量を常に画面下部で確認できるようにする方法を紹介します。
完成するとこんな表示になります。
1行目にモデル名・ディレクトリ・Gitブランチ、2行目にコンテキスト使用率・5時間セッション制限・7日間制限がバーグラフ付きで表示されます。
statusLine設定の仕組み
Claude Codeの ~/.claude/settings.json には statusLine という設定項目があります。
type: "command" にすると、指定したコマンドを定期的に実行し、その出力をステータスラインとして表示してくれます。
{
"statusLine": {
"type": "command",
"command": "bash ~/.claude/statusline-command.sh"
}
}
コマンドはstdinでJSON形式のデータを受け取ります。
調べたところ、以下のようなフィールドが渡されます。
{
"model": { "display_name": "Sonnet 4.6" },
"workspace": { "current_dir": "/home/user/myproject" },
"context_window": { "used_percentage": 31.2 },
"rate_limits": {
"five_hour": { "used_percentage": 7.3, "resets_at": 1774443600 },
"seven_day": { "used_percentage": 30.8, "resets_at": 1774494000 }
}
}
model はオブジェクト型で display_name フィールドにモデル名が入ります。
rate_limits のリセット時刻は resets_at というキー名でUnixタイムスタンプが渡されます。
起動直後は rate_limits が空になることがあります。
後述のキャッシュ機能でこの問題を回避します。
1行目: モデル名・ディレクトリ・Gitブランチの表示
1行目は現在の作業コンテキストをひと目で把握するための情報です。
MODEL=$(echo "$input" | jq -r '.model.display_name')
DIR=$(echo "$input" | jq -r '.workspace.current_dir')
ディレクトリはパス全体だと長くなるので、ベースネームだけを表示します。
${DIR##*/} というbashのパラメータ展開を使うと、パスの最後の部分だけを取り出せます。
Gitブランチの取得では、git -c gc.auto=0 というオプションを使っています。
これはコマンドレベルでGCの自動起動を抑制するための設定で、ステータスライン更新のたびにGCが走らないようにするためです。
BRANCH=""
git -c gc.auto=0 rev-parse --git-dir > /dev/null 2>&1 && BRANCH=" | git:$(git -c gc.auto=0 branch --show-current 2>/dev/null)"
Gitリポジトリ外では rev-parse が失敗するので、&& でつなぐことでブランチ表示をスキップできます。
2行目: コンテキスト使用率とレート制限の表示
2行目は数値をバーグラフで可視化します。
バーの生成と色の判定は make_bar / bar_color 関数として共通化し、コンテキストとレート制限の両方で流用します。
make_bar() {
local pct=$1
local filled=$((pct / 10)); local empty=$((10 - filled))
# filled/empty が 0 のとき seq が空を返すため、ガードが必要
[ $filled -gt 0 ] && printf '%.0s█' $(seq 1 $filled)
[ $empty -gt 0 ] && printf '%.0s░' $(seq 1 $empty)
}
bar_color() {
local pct=$1
if [ "$pct" -ge 90 ]; then echo "$RED"
elif [ "$pct" -ge 70 ]; then echo "$YELLOW"
else echo "$GREEN"; fi
}
ブロック文字 █(U+2588)と ░(U+2591)を組み合わせて10段階のバーを描画します。
色はANSIエスケープシーケンスで付けており、0〜69%が緑、70〜89%が黄、90%以上が赤になります。
jqのフォールバック値の使い分けにも注意が必要です。
context_window.used_percentage は0%が意味のある値なので // 0 でデフォルト値を0にします。
一方、rate_limits のフィールドは値が存在しないこと自体に意味があるので // "NONE" をプレースホルダーにして、後続で空文字に変換して条件分岐に活かします。
# 完成スクリプトでは mapfile + 配列で取得(_F[2] がPCT、_F[3] がSESSION_PCTに対応)
PCT="${_F[2]}"
SESSION_PCT="${_F[3]}"; [ "$SESSION_PCT" = "NONE" ] && SESSION_PCT=""
残り時間の計算は remaining_time 関数で行います。
resets_at はUnixタイムスタンプなので、現在時刻との差分を秒で出し、時・分に変換します。
remaining_time() {
local reset_at=$1
[ -z "$reset_at" ] && return
local now diff_s
now=$(date +%s)
diff_s=$((reset_at - now))
[ "$diff_s" -le 0 ] && echo "0m" && return
local h=$((diff_s / 3600)); local m=$(((diff_s % 3600) / 60))
if [ "$h" -gt 0 ]; then echo "${h}h${m}m"; else echo "${m}m"; fi
}
表示では S: を5時間セッション制限、W: を7日間週次制限のプレフィックスとして使い、どちらの数字かを識別しやすくしています。
起動直後の問題とキャッシュによる解決
Claude Codeを起動した直後は rate_limits フィールドが空で渡されることがあります。
これだとバーが表示できないので、前回の値をキャッシュファイルに保存して補完します。
if [ -n "$SESSION_PCT" ] && [ -n "$WEEK_PCT" ]; then
# データがあればキャッシュを更新
echo "$input" | jq '{rate_limits: .rate_limits}' > "$CACHE_FILE" 2>/dev/null
elif [ -f "$CACHE_FILE" ]; then
# データがなければキャッシュから読み込む(1回のjq呼び出しで取得)
mapfile -t _C < <(
jq -r '
(.rate_limits.five_hour.used_percentage // "NONE"),
(.rate_limits.five_hour.resets_at // "NONE"),
(.rate_limits.seven_day.used_percentage // "NONE"),
(.rate_limits.seven_day.resets_at // "NONE")
' "$CACHE_FILE"
)
[ -z "$SESSION_PCT" ] && [ "${_C[0]}" != "NONE" ] && SESSION_PCT="${_C[0]}"
[ -z "$SESSION_RESET" ] && [ "${_C[1]}" != "NONE" ] && SESSION_RESET="${_C[1]}"
[ -z "$WEEK_PCT" ] && [ "${_C[2]}" != "NONE" ] && WEEK_PCT="${_C[2]}"
[ -z "$WEEK_RESET" ] && [ "${_C[3]}" != "NONE" ] && WEEK_RESET="${_C[3]}"
fi
データが取得できた場合はキャッシュを更新し、取得できなかった場合はキャッシュから読み込む、というシンプルな構成です。
キャッシュファイルが存在しない初回起動時は ░░░░ S:--% のようなフォールバック表示になります。
完成スクリプト全文と設定方法
#!/bin/bash
input=$(cat)
CACHE_FILE="${HOME}/.claude/statusline-rate-cache.json"
# 全変数を取得
mapfile -t _F < <(
echo "$input" | jq -r '
.model.display_name,
.workspace.current_dir,
(.context_window.used_percentage // 0 | floor | tostring),
(.rate_limits.five_hour.used_percentage // "NONE"),
(.rate_limits.five_hour.resets_at // "NONE"),
(.rate_limits.seven_day.used_percentage // "NONE"),
(.rate_limits.seven_day.resets_at // "NONE")
'
)
MODEL="${_F[0]}"
DIR="${_F[1]}"
PCT="${_F[2]}"
SESSION_PCT="${_F[3]}"; [ "$SESSION_PCT" = "NONE" ] && SESSION_PCT=""
SESSION_RESET="${_F[4]}"; [ "$SESSION_RESET" = "NONE" ] && SESSION_RESET=""
WEEK_PCT="${_F[5]}"; [ "$WEEK_PCT" = "NONE" ] && WEEK_PCT=""
WEEK_RESET="${_F[6]}"; [ "$WEEK_RESET" = "NONE" ] && WEEK_RESET=""
if [ -n "$SESSION_PCT" ] && [ -n "$WEEK_PCT" ]; then
echo "$input" | jq '{rate_limits: .rate_limits}' > "$CACHE_FILE" 2>/dev/null
elif [ -f "$CACHE_FILE" ]; then
mapfile -t _C < <(
jq -r '
(.rate_limits.five_hour.used_percentage // "NONE"),
(.rate_limits.five_hour.resets_at // "NONE"),
(.rate_limits.seven_day.used_percentage // "NONE"),
(.rate_limits.seven_day.resets_at // "NONE")
' "$CACHE_FILE"
)
[ -z "$SESSION_PCT" ] && [ "${_C[0]}" != "NONE" ] && SESSION_PCT="${_C[0]}"
[ -z "$SESSION_RESET" ] && [ "${_C[1]}" != "NONE" ] && SESSION_RESET="${_C[1]}"
[ -z "$WEEK_PCT" ] && [ "${_C[2]}" != "NONE" ] && WEEK_PCT="${_C[2]}"
[ -z "$WEEK_RESET" ] && [ "${_C[3]}" != "NONE" ] && WEEK_RESET="${_C[3]}"
fi
CYAN='\033[36m'; GREEN='\033[32m'; YELLOW='\033[33m'; RED='\033[31m'; RESET='\033[0m'
remaining_time() {
local reset_at=$1
[ -z "$reset_at" ] && return
local now diff_s
now=$(date +%s)
diff_s=$((reset_at - now))
[ "$diff_s" -le 0 ] && echo "0m" && return
local h=$((diff_s / 3600)); local m=$(((diff_s % 3600) / 60))
if [ "$h" -gt 0 ]; then echo "${h}h${m}m"; else echo "${m}m"; fi
}
make_bar() {
local pct=$1
local filled=$((pct / 10)); local empty=$((10 - filled))
[ $filled -gt 0 ] && printf '%.0s█' $(seq 1 $filled)
[ $empty -gt 0 ] && printf '%.0s░' $(seq 1 $empty)
}
bar_color() {
local pct=$1
if [ "$pct" -ge 90 ]; then echo "$RED"
elif [ "$pct" -ge 70 ]; then echo "$YELLOW"
else echo "$GREEN"; fi
}
BAR_COLOR=$(bar_color "$PCT")
BAR=$(make_bar "$PCT")
BRANCH=""
git -c gc.auto=0 rev-parse --git-dir > /dev/null 2>&1 && BRANCH=" | git:$(git -c gc.auto=0 branch --show-current 2>/dev/null)"
if [ -n "$SESSION_PCT" ]; then
SESSION_INT=$(printf '%.0f' "$SESSION_PCT")
S_COLOR=$(bar_color "$SESSION_INT")
S_BAR=$(make_bar "$SESSION_INT")
S_TIME=$(remaining_time "$SESSION_RESET")
S_TIME_STR=${S_TIME:+" $S_TIME"}
S_PART="${S_COLOR}${S_BAR}${RESET} S:${SESSION_INT}%${S_TIME_STR}"
else
S_PART="░░░░░░░░░░ S:--%"
fi
if [ -n "$WEEK_PCT" ]; then
WEEK_INT=$(printf '%.0f' "$WEEK_PCT")
W_COLOR=$(bar_color "$WEEK_INT")
W_BAR=$(make_bar "$WEEK_INT")
W_TIME=$(remaining_time "$WEEK_RESET")
W_TIME_STR=${W_TIME:+" $W_TIME"}
W_PART="${W_COLOR}${W_BAR}${RESET} W:${WEEK_INT}%${W_TIME_STR}"
else
W_PART="░░░░░░░░░░ W:--%"
fi
echo -e "${CYAN}[$MODEL]${RESET} ${DIR##*/}$BRANCH"
echo -e "${BAR_COLOR}${BAR}${RESET} ${PCT}% | ${S_PART} | ${W_PART}"
設定手順は以下の通りです。
- 上記スクリプトを
~/.claude/statusline-command.shとして保存します - 実行権限を付与します:
chmod +x ~/.claude/statusline-command.sh -
~/.claude/settings.jsonにstatusLineの設定を追加します - jqが未インストールの場合はインストールします:
apt install jqまたはbrew install jq
settings.json に他の設定がすでにある場合は、既存のオブジェクトに "statusLine": { ... } を追記するだけで大丈夫です。
カスタマイズのヒント
基本の動作を理解したら、さらに手を加えることができます。
バーの細かさを上げる
make_bar の中を pct / 5 と 20 - filled に変えると、20段階のより細かいバーになります。
数字の精度が上がるので、70〜80%あたりの微妙な変化も視覚的に追いやすくなります。
コンテキスト80%超えのアラート
[ "$PCT" -ge 80 ] && echo -e "${YELLOW}[!] /compact でコンテキストを圧縮できます${RESET}"
/compact はClaude Codeのコマンドで、現在の会話を要約してコンテキストを圧縮します。
80%を超えたあたりで促すと、枯渇前に対処できます。
Nerd Fontsアイコンの活用
Nerd Fontsが導入済みであれば、ブランチ記号(U+E0A0)やモデルアイコンを添えるとさらに見やすくなります。
ターミナルとフォントがNerd Fontsに対応していることを事前に確認してから使うとよいです。
おわりに
このカスタマイズを入れてから、コンテキストの残量をちゃんと意識しながら作業できるようになりました。
レート制限の残時間も常に見えているので、「今日はあとこれくらいしか使えない」という判断も瞬時にできます。
無理に使い切ろうとせず、作業の優先順位をつけやすくなった感覚があります。
statusLine の type: "command" は何でも出力できるので、今回紹介した以外にも通知・外部ツール連携・コスト計算など、さまざまな発展が考えられます。以上。
