長時間のコーディングセッションをこなしていると、「いま自分はどのモデルで動かしているのか」「コンテキストはあとどれくらい残っているのか」「いまどのブランチにいるのか」を、画面のどこかでさっと確認したくなる場面があります。Claude Code には、こうした情報をプロンプト下部に固定表示できる ステータスライン という仕組みがあるのですが、デフォルトの表示はかなり控えめで、はじめて触る方は「これは何を見せるための機能なのだろう?」と感じるかもしれません。
私は普段、4 つのドメインを横断して Claude Code を使っているのですが、モデルを Sonnet と Opus で切り替えながら作業することが多く、いまどのモデルが選ばれているかをひと目で確認できないと、料金感覚が狂いやすいと感じています。ここではそうした実用視点で ステータスラインに何を出すと役に立つのか、そして どう書けば壊れにくいのか を、シェルスクリプトと Node.js の 2 通りで具体的にまとめておきます。
ステータスラインとは何か — まず仕組みを 1 分で押さえる
Claude Code のステータスラインは、ユーザーが指定した 任意の実行可能ファイル を、Claude Code が会話の節目ごとに呼び出して、その標準出力をそのまま表示する仕組みです。コマンドはセッション情報を JSON として標準入力から受け取り、表示したい 1 行(または ANSI エスケープ付きの装飾文字列)を標準出力に書き出すだけ、という非常にシンプルな設計になっています。
設定方法は ~/.claude/settings.json(または各プロジェクトの .claude/settings.json)に、次のように statusLine フィールドを追加するだけです。
{
"statusLine": {
"type": "command",
"command": "/Users/you/.claude/statusline.sh",
"padding": 0
}
}type は現状 command のみ、command は実行ファイルへの絶対パス、padding は出力前後の空白制御です。padding: 0 にしておくと、後述するスクリプト側で自由にレイアウトを組めるようになります。
なぜカスタムステータスラインを作る価値があるのか
「ターミナルのプロンプトを派手にする」のと似た話に聞こえるかもしれませんが、私が実際に使ってみて、3 つの場面で明確にメリットを感じました。
ひとつ目は モデルの取り違え防止 です。Opus と Sonnet を切り替える運用をしていると、課金額の桁が一段違ってくるため、「いつの間にか Opus のままで雑談していた」事故を避けたくなります。ふたつ目は コンテキスト残量の早期警告 です。残量が少なくなると、ある時点を境に応答が急にぎこちなくなるのですが、ステータスラインに残量バーを出しておくと、/compact の発動タイミングを逃しにくくなります。みっつ目は 作業ブランチの可視化 で、複数の worktree を行き来する人ほど効きます。
逆に、これらを必要としていない場面では、無理にカスタムステータスラインを作る必要はありません。情報量が多すぎるステータスラインは、視線がそちらに引っ張られて、肝心の会話への集中を逆に削いでしまうこともあります。
シェルスクリプトで作る最小構成のステータスライン
まずは、外部依存がない Bash 1 ファイルで、モデル・コンテキスト残量・Git ブランチを表示する例から見ていきます。これは「動くものを 5 分で用意したい」場合の最短ルートです。
#!/usr/bin/env bash
# ~/.claude/statusline.sh
# Claude Code から呼ばれ、JSON を stdin で受け取り、1行を stdout に出す。
set -eu
# 1) Claude Code から渡されるセッション情報を読み取る
INPUT="$(cat)"
# 2) jq で必要なフィールドだけ抜き出す(jq が無い場合は次節の Node.js 版を使う)
MODEL_ID="$(echo "$INPUT" | jq -r '.model.id // "unknown"')"
MODEL_NAME="$(echo "$INPUT" | jq -r '.model.display_name // .model.id // "unknown"')"
CTX_USED="$(echo "$INPUT" | jq -r '.context.input_tokens // 0')"
CTX_LIMIT="$(echo "$INPUT" | jq -r '.context.limit // 200000')"
CWD="$(echo "$INPUT" | jq -r '.workspace.current_dir // "."')"
# 3) コンテキスト残量を%で算出
if [ "$CTX_LIMIT" -gt 0 ]; then
USED_PCT=$(( (CTX_USED * 100) / CTX_LIMIT ))
else
USED_PCT=0
fi
REMAIN_PCT=$(( 100 - USED_PCT ))
# 4) Git ブランチ(取得できない場合は省略)
BRANCH=""
if command -v git >/dev/null 2>&1; then
BRANCH="$(git -C "$CWD" symbolic-ref --short -q HEAD 2>/dev/null || true)"
fi
# 5) ANSI 装飾を付けて 1 行で出力
RESET=$'\033[0m'
DIM=$'\033[2m'
CYAN=$'\033[36m'
GREEN=$'\033[32m'
YELLOW=$'\033[33m'
RED=$'\033[31m'
# 残量で色を変える(30% 未満は赤、60% 未満は黄色、それ以上は緑)
if [ "$REMAIN_PCT" -lt 30 ]; then COLOR="$RED";
elif [ "$REMAIN_PCT" -lt 60 ]; then COLOR="$YELLOW";
else COLOR="$GREEN"; fi
LINE="${CYAN}${MODEL_NAME}${RESET}"
LINE="${LINE} ${DIM}|${RESET} ctx ${COLOR}${REMAIN_PCT}%${RESET}"
[ -n "$BRANCH" ] && LINE="${LINE} ${DIM}|${RESET} ${BRANCH}"
printf '%s' "$LINE"これを chmod +x ~/.claude/statusline.sh で実行可能にして、settings.json に登録すれば動きます。期待される出力は、たとえば以下のような 1 行です。
Sonnet 4.5 | ctx 73% | feature/auth-refactor
私の経験では、残量を%で見せる のがいちばん運用に乗りやすかったです。トークン数の生値(例: 47,231)はピンとこない一方、%は「半分は使ったな」「あと少しで /compact だな」という判断と直結します。
Node.js で作るバージョン — jq が無い環境向け
会社の貸与マシンや CI 環境など、jq が入っていない PC で動かしたい場合は、Node.js だけで完結させるのが楽です。Node 18 以降なら、外部パッケージなしで書けます。
#!/usr/bin/env node
// ~/.claude/statusline.mjs
// stdin で受け取った JSON を整形して 1 行で stdout に出す。
import { execSync } from "node:child_process";
async function main() {
// stdin を全部読む
const chunks = [];
for await (const chunk of process.stdin) chunks.push(chunk);
let input = {};
try {
input = JSON.parse(Buffer.concat(chunks).toString("utf8") || "{}");
} catch {
// パース失敗時は控えめに何も出さない(セッション開始時など空入力のケース)
process.stdout.write("");
return;
}
const modelName = input?.model?.display_name || input?.model?.id || "unknown";
const used = Number(input?.context?.input_tokens || 0);
const limit = Number(input?.context?.limit || 200000);
const cwd = input?.workspace?.current_dir || process.cwd();
const usedPct = limit > 0 ? Math.floor((used * 100) / limit) : 0;
const remainPct = 100 - usedPct;
// Git ブランチ取得(失敗しても落とさない)
let branch = "";
try {
branch = execSync("git symbolic-ref --short -q HEAD", {
cwd,
stdio: ["ignore", "pipe", "ignore"],
})
.toString()
.trim();
} catch {
branch = "";
}
const c = (code, s) => `\x1b[${code}m${s}\x1b[0m`;
const cyan = (s) => c(36, s);
const dim = (s) => c(2, s);
const color =
remainPct < 30 ? 31 : remainPct < 60 ? 33 : 32; // red / yellow / green
let line = `${cyan(modelName)} ${dim("|")} ctx ${c(color, `${remainPct}%`)}`;
if (branch) line += ` ${dim("|")} ${branch}`;
process.stdout.write(line);
}
main().catch(() => {
// 最終的なエラーも握りつぶし、ステータスラインを壊さない
process.stdout.write("");
});settings.json の command には node /Users/you/.claude/statusline.mjs のように Node 経由で起動するパスを書きます。シバン (#!/usr/bin/env node) を活かすなら、chmod +x してから絶対パスを直接書いても構いません。
失敗しても会話を壊さない — エラーの握りつぶしが必須
実装で見落としがちなのが、ステータスラインのスクリプトが失敗したとき、Claude Code 本体の会話に悪影響を与えないようにする という観点です。Claude Code はステータスラインスクリプトの非ゼロ終了や stderr を、デフォルトでは表示エラーとしてレンダリングしません。ですが、長時間タイムアウトしたりプロセスがハングすると、ステータスライン更新がブロックされて違和感のある体験になります。
私が必ず入れているのは次の 4 つです。第一に、外部コマンド呼び出しはすべて try で包んで失敗を握りつぶす。git が無い環境でも 1 行は出るようにしておきます。第二に、入力 JSON のパースエラーでも空文字を返す。セッション初期化直後にスキーマが揺れた場合でも、ステータスラインが「壊れた」と認識されないようにします。第三に、外部 API を叩かない。たとえば「現在の Anthropic 残クレジットを取得」みたいな処理は、ネットワーク遅延がそのまま体感を悪化させるので、別のスクリプトに切り出して定期実行で更新したファイルから読む形が無難です。第四に、処理時間の予算を 50ms 程度に収める。これを超え始めると、明らかに「描画がカクつく」感覚が出てきます。
残コンテキストバーをアスキーで描いて視認性を上げる
%表示で慣れてくると、もう一段「視覚的に」残量を把握したくなる時があります。私は次のような単純な 10 文字バーを足して、ターミナル上で視線がそちらに向くようにしました。
# 残量%を 10 段階のバーに変換する関数
make_bar() {
local pct="$1"
local filled=$(( pct / 10 ))
local empty=$(( 10 - filled ))
local bar=""
for ((i=0; i<filled; i++)); do bar="${bar}█"; done
for ((i=0; i<empty; i++)); do bar="${bar}░"; done
printf '%s' "$bar"
}
BAR="$(make_bar "$REMAIN_PCT")"
LINE="${LINE} ${DIM}[${RESET}${COLOR}${BAR}${RESET}${DIM}]${RESET}"たとえば残量 73% なら ███████░░░ のように見えます。/compact を打つかどうかの判断が、数字を読まなくても瞬時にできるようになるので、長時間セッションでは特に効きます。
プロジェクトごとに表示を変える — 設定の優先順位を理解する
Claude Code の settings.json は、ユーザー全体(~/.claude/settings.json)と、プロジェクトごと(<repo>/.claude/settings.json)の 2 階層で管理できます。プロジェクト側の設定が優先される ため、特定のリポジトリだけ別のステータスラインを使いたい、というカスタマイズが可能です。
私が実際にやっているのは、本番運用しているプロダクト用のリポジトリにだけ「環境名(dev/staging/prod)」を表示するステータスラインを差し込む、という運用です。プロジェクトの環境変数(CLAUDE_ENV など)を読んで色を変えるだけで、「いまうっかり prod を触っていないか?」というヒヤリを減らせます。
# ~/projects/your-app/.claude/settings.json (プロジェクト固有)
{
"statusLine": {
"type": "command",
"command": "/Users/you/.claude/statusline-prod-aware.sh",
"padding": 0
}
}このように、全体は控えめにしておき、特に注意したいプロジェクトだけ強めの表示にする のがバランスが良いと感じています。すべてのプロジェクトで派手な表示にすると、強調表示が「強調」として機能しなくなります。
押さえておきたい落とし穴
最後に、設定時にハマりやすいポイントを 3 つだけまとめておきます。
ひとつ目は 絶対パス指定 です。command に ~/.claude/statusline.sh のようにチルダ展開を期待した書き方をすると、環境によっては展開されず実行ファイルが見つからない、というケースがあります。必ず /Users/you/... のような絶対パスで書くのが安全です。
ふたつ目は 改行を出力しない こと。echo をそのまま使うと末尾に改行が付き、ステータスラインの行が崩れて見えることがあります。シェルなら printf '%s'、Node なら process.stdout.write を使い、明示的に改行を入れない実装にしてください。
みっつ目は 長過ぎる出力 です。ターミナル幅によっては折り返されて 2 行になり、レイアウトが崩れます。50 〜 70 文字以内に収める、長くなりそうな項目は省略表示(...)にする、といった配慮を入れておくと安心です。
ステータスラインは、はじめは「あれば便利」程度のものに見えますが、設定を 30 分かけて整えるだけで、長時間セッション中の 小さな迷い が大幅に減るのを実感できると思います。まずは本記事のシェルスクリプトをそのまま ~/.claude/statusline.sh として置いて、settings.json から呼び出してみてください。動かしながら自分の運用に合う情報を 1 つずつ足していくのが、いちばん早く納得のいく設定にたどり着く道筋だと感じています。
このあたりの設定をさらに突き詰めたい方は、Claude Code の挙動全体をフックで包む話とも相性が良いので、Claude Code Hooks 自動化マスターガイド や Claude Code 環境変数と起動フラグの完全リファレンス も合わせて目を通しておくと、ステータスラインに「何を出すか」の引き出しが一気に増えます。