0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Claude Code のステータスライン(statusLine)を自作してコンテキスト使用率とセッションコストを常時表示する実装手順 — used_percentage が null・tput cols が効かない・git status で重くなる、3つのハマりどころ【2026】

0
Posted at

はじめに / 対象と前提

Claude Code で長いセッションを回していると、「今コンテキストをどれだけ食っているか」「このセッションいくら使ったか」が気になる。/context や /cost を毎回叩くのは面倒なので、画面下部の ステータスライン(statusLine) に常時出すようにした。

この記事は、Claude Code を日常的に使っていて、シェルスクリプトが書ける人向け。設定の書き方、stdin で渡ってくる JSON の中身、実際にハマった 3 点をまとめる。

検証環境:

  • Claude Code v2.1.274(macOS 15 / zsh)
  • jq 1.7.1
  • Node.js 23.x(Node 版スクリプトの動作確認用)

TL;DR

  • ~/.claude/settings.json に statusLine.command を書くと、Claude Code が 任意のシェルコマンドを実行し、stdout をそのまま画面下に表示する
  • 入力は stdin の JSON 1 発。model.display_name / context_window.used_percentage / cost.total_cost_usd あたりを拾えば実用十分
  • ハマりどころは null 対策・端末幅の取り方・毎回走ることを忘れた重い処理 の 3 つ

手順 / 動かし方

1. スクリプトを置く

~/.claude/statusline.sh を作る。モデル名・作業ディレクトリ・コンテキスト使用率のバー・セッションコストを 1 行に出す最小構成。

#!/bin/bash
# Claude Code が stdin に流してくる JSON を丸ごと受ける
input=$(cat)

MODEL=$(echo "$input" | jq -r '.model.display_name')
DIR=$(echo "$input"   | jq -r '.workspace.current_dir')
# null のことがあるので // 0 でフォールバック(後述)
PCT=$(echo "$input"   | jq -r '.context_window.used_percentage // 0' | cut -d. -f1)
COST=$(echo "$input"  | jq -r '.cost.total_cost_usd // 0')

# 10 マスのバーを組む
FILLED=$((PCT / 10)); EMPTY=$((10 - FILLED))
printf -v FILL "%${FILLED}s"; printf -v PAD "%${EMPTY}s"
BAR="${FILL// /▓}${PAD// /░}"

# 70% 超えたら黄色、90% 超えたら赤
COLOR="\033[32m"
[ "$PCT" -ge 70 ] && COLOR="\033[33m"
[ "$PCT" -ge 90 ] && COLOR="\033[31m"

printf "[%s] %s ${COLOR}%s %s%%\033[0m \$%.2f\n" \
  "$MODEL" "${DIR##*/}" "$BAR" "$PCT" "$COST"

実行権限を忘れずに。

chmod +x ~/.claude/statusline.sh

2. settings.json に登録する

{
  "statusLine": {
    "type": "command",
    "command": "~/.claude/statusline.sh",
    "padding": 0
  }
}

保存すると 再起動なしで即反映される。padding は左右の余白(文字数)で省略可。

3. 手元で単体テストする

Claude Code を起動しなくても、JSON を流し込めば動作確認できる。

echo '{"model":{"display_name":"Opus"},"workspace":{"current_dir":"/tmp/demo"},
"context_window":{"used_percentage":42},"cost":{"total_cost_usd":0.1234}}' \
  | ~/.claude/statusline.sh

出力:

[Opus] demo ▓▓▓▓░░░░░░ 42% $0.12

ここまでで実際の Claude Code 画面下にも同じ行が出た。

4. 渡ってくる JSON の主なフィールド

自分が使ったものだけ抜粋。全量は公式ドキュメントの Available data を参照。

フィールド 中身
model.id / model.display_name モデル ID と表示名
workspace.current_dir / workspace.project_dir 現在 cwd と起動時ディレクトリ(cd で乖離しうる)
context_window.used_percentage コンテキスト使用率(入力トークンのみで計算)
context_window.current_usage 直近 API 呼び出しの input_tokens / cache_read_input_tokens などの内訳
cost.total_cost_usd セッション累計コスト(定価ベースの推定値)
cost.total_lines_added / total_lines_removed 変更行数
rate_limits.five_hour.used_percentage 5 時間枠の消費率(Pro/Max のみ、初回応答後に出現)
vim.mode vim モード有効時のみ

いつ再実行されるかも把握しておくと設計しやすい。セッション開始時に 1 回、その後は アシスタントのメッセージ到着・/compact 完了・permission モード変更 などのイベントごとに走る。連打は 300ms でデバウンスされる。

ハマりどころ

1. used_percentage が null で算術展開が落ちる

最初は // 0 を付けずに書いていて、セッション開始直後にステータスラインが空になった。原因は 最初の API 呼び出しが終わるまで context_window.used_percentage と current_usage が null なこと。jq -r は null を文字列 null で吐くので、$((PCT / 10)) が null: syntax error になって bash が非ゼロ終了し、何も表示されない。

さらに /compact 直後も current_usage は一度 null に戻る。「起動時だけガードすればいい」ではなく、常に // 0 を噛ませる。

PCT=$(echo "$input" | jq -r '.context_window.used_percentage // 0' | cut -d. -f1)

rate_limits や vim のように キー自体が存在しないフィールドは // empty にして、無いときは表示しないのが素直。

2. tput cols が端末幅を返さない

長い行を端末幅で切り詰めようとして tput cols を使ったら、常に 80 が返ってきた。Claude Code は スクリプトの stdout を捕捉して自前描画しているので、スクリプトから見ると tty に繋がっていない。Python の shutil.get_terminal_size() も同じ理由でデフォルト値を返す。

Claude Code 側が環境変数 COLUMNS と LINES を渡してくれるので、そちらを読む。

WIDTH=${COLUMNS:-80}
LINE="[$MODEL] ${DIR##*/} $BAR $PCT% \$$COST"
printf "%.${WIDTH}s\n" "$LINE"

3. git status を毎回叩いたら入力がもたつく

ブランチ名とダーティ状態を出したくて git status --porcelain を無邪気に入れたら、大きめのモノレポで メッセージが来るたびに数百 ms 固まるようになった。ステータスラインは「たまに動くもの」ではなく イベントのたびに走るので、重い処理はキャッシュ前提で書く。

自分は結果を /tmp に落として 5 秒だけ使い回す形にした。

CACHE="/tmp/claude-sl-git-$(echo "$DIR" | md5 -q)"
if [ ! -f "$CACHE" ] || [ $(( $(date +%s) - $(stat -f %m "$CACHE") )) -gt 5 ]; then
  (cd "$DIR" && {
    BR=$(git symbolic-ref --short HEAD 2>/dev/null || echo "-")
    [ -n "$(git status --porcelain 2>/dev/null | head -1)" ] && BR="$BR*"
    echo "$BR"
  }) > "$CACHE" 2>/dev/null
fi
GIT=$(cat "$CACHE")

Linux なら md5 -q を md5sum | cut -d' ' -f1、stat -f %m を stat -c %Y に読み替える。

背景・補足

  • used_percentage は入力トークンのみで計算される(input_tokens + cache_creation_input_tokens + cache_read_input_tokens)。自分で current_usage から計算するなら output_tokens を足さないこと。足すと公式値と食い違う
  • 複数行出力もできる。echo を 2 回すれば 2 行になる。1 行目に git 情報、2 行目にバーという構成が見やすかった
  • ANSI カラーはそのまま通る。ただし permission プロンプトやヘルプ表示中は一時的に隠れる仕様
  • /statusline コマンドに自然言語で頼めば、上記相当のスクリプトと設定を Claude Code が自動生成する。まずそれで叩き台を作って手で直すのが速い
  • スクリプトはローカル実行なので API トークンは消費しない
  • prompt_cache(キャッシュヒット率など)は v2.1.251 以降でしか流れてこない。古いバージョンで null 参照して落とさないよう注意

まとめ

  • statusLine.command に任意スクリプトを登録するだけで、Claude Code の画面下に好きな情報を常時表示できる
  • stdin の JSON は セッション初期と /compact 直後に null が混ざる。// 0 / // empty を常に付ける
  • 端末幅は tput cols ではなく COLUMNS 環境変数で取る
  • スクリプトは イベントごとに毎回走る。git status などの重い処理はキャッシュする
  • 迷ったらまず /statusline で生成してから手で育てるのが楽
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?