Claude Code のコンテキスト残量を常に表示する
Claude Code の v1.0.71 で、TUI の最下部に表示する status line をカスタマイズできるようになりました。
コンテキストサイズや auto-compact までの残量を常に可視化しておくために便利です。
モチベーション
Claude のコンテキスト幅 (モデルに一度に入力できる上限トークン数) は 200K トークンです。
Claude Code はこのコンテキスト幅に到達する前に、大体 80% ぐらいのタイミングで自動的に /compact
を実行します。
/compact
はコンテキストを圧縮するために、過去の会話履歴の情報を大幅に落とした要約を作ります。
その結果、必要なコンテキストが足りずに Claude Code が迷走することが非常によくあります。
そのため、auto-compact まで後どれくらいトークンを使えるかを常に把握しておきたいわけですが、 Claude Code は auto-compact の直前まで情報を表示しません。
この不便さを解消するためにちょうど良い機能がリリースされたので、早速利用しましょう。
設定方法
まず、下記の設定を .claude/settings.json
に追加します。
{
"statusLine": {
"type": "command",
"command": "~/.claude/statusline.js"
}
}
statusline.js
の中身は以下の通りです。
#!/usr/bin/env node
const fs = require('fs');
const path = require('path');
const readline = require('readline');
// Constants
const COMPACTION_THRESHOLD = 200000 * 0.8
// Read JSON from stdin
let input = '';
process.stdin.on('data', chunk => input += chunk);
process.stdin.on('end', async () => {
try {
const data = JSON.parse(input);
// Extract values
const model = data.model?.display_name || 'Unknown';
const currentDir = path.basename(data.workspace?.current_dir || data.cwd || '.');
const sessionId = data.session_id;
// Calculate token usage for current session
let totalTokens = 0;
if (sessionId) {
// Find all transcript files
const projectsDir = path.join(process.env.HOME, '.claude', 'projects');
if (fs.existsSync(projectsDir)) {
// Get all project directories
const projectDirs = fs.readdirSync(projectsDir)
.map(dir => path.join(projectsDir, dir))
.filter(dir => fs.statSync(dir).isDirectory());
// Search for the current session's transcript file
for (const projectDir of projectDirs) {
const transcriptFile = path.join(projectDir, `${sessionId}.jsonl`);
if (fs.existsSync(transcriptFile)) {
totalTokens = await calculateTokensFromTranscript(transcriptFile);
break;
}
}
}
}
// Calculate percentage
const percentage = Math.min(100, Math.round((totalTokens / COMPACTION_THRESHOLD) * 100));
// Format token display
const tokenDisplay = formatTokenCount(totalTokens);
// Color coding for percentage
let percentageColor = '\x1b[32m'; // Green
if (percentage >= 70) percentageColor = '\x1b[33m'; // Yellow
if (percentage >= 90) percentageColor = '\x1b[31m'; // Red
// Build status line
const statusLine = `[${model}] 📁 ${currentDir} | 🪙 ${tokenDisplay} | ${percentageColor}${percentage}%\x1b[0m`;
console.log(statusLine);
} catch (error) {
// Fallback status line on error
console.log('[Error] 📁 . | 🪙 0 | 0%');
}
});
async function calculateTokensFromTranscript(filePath) {
return new Promise((resolve, reject) => {
let lastUsage = null;
const fileStream = fs.createReadStream(filePath);
const rl = readline.createInterface({
input: fileStream,
crlfDelay: Infinity
});
rl.on('line', (line) => {
try {
const entry = JSON.parse(line);
// Check if this is an assistant message with usage data
if (entry.type === 'assistant' && entry.message?.usage) {
lastUsage = entry.message.usage;
}
} catch (e) {
// Skip invalid JSON lines
}
});
rl.on('close', () => {
if (lastUsage) {
// The last usage entry contains cumulative tokens
const totalTokens = (lastUsage.input_tokens || 0) +
(lastUsage.output_tokens || 0) +
(lastUsage.cache_creation_input_tokens || 0) +
(lastUsage.cache_read_input_tokens || 0);
resolve(totalTokens);
} else {
resolve(0);
}
});
rl.on('error', (err) => {
reject(err);
});
});
}
function formatTokenCount(tokens) {
if (tokens >= 1000000) {
return `${(tokens / 1000000).toFixed(1)}M`;
} else if (tokens >= 1000) {
return `${(tokens / 1000).toFixed(1)}K`;
}
return tokens.toString();
}
仕組み
上の statusline.js
はトークン数を分析するために transcript と呼ばれるファイルをパースしています。transcript はセッションごとに自動生成されるファイルで、完全な履歴が保存されています。
現在のセッションに対応する transcript のパスは、標準入力経由で status line のコマンドに渡されます。
transcript の各行は下記のような形式になっており、今回利用するのは message.usage
だけです。
{
"parentUuid": "b48011c8-b40c-4fb7-afa1-7b179409613b",
"isSidechain": false,
"userType": "external",
"cwd": "/redacted/.local/share/chezmoi",
"sessionId": "0aa7a121-20aa-4e58-8c46-9709131bdd7f",
"version": "1.0.60",
"gitBranch": "main",
"message": {
"id": "msg_01Kt2aXgS4g2xwkRjY6bsjaB",
"type": "message",
"role": "assistant",
"model": "claude-opus-4-20250514",
"content": [
{
"type": "text",
"text": "You're absolutely right - I should have modified the chezmoi source file instead of the deployed file directly. Let me find and update the correct file in the chezmoi repository:"
}
],
"stop_reason": null,
"stop_sequence": null,
"usage": {
"input_tokens": 10,
"cache_creation_input_tokens": 28596,
"cache_read_input_tokens": 0,
"output_tokens": 5,
"service_tier": "standard"
}
},
"requestId": "req_011CRTQ2kR2epr87wVM5VHzC",
"type": "assistant",
"uuid": "85289783-055a-4fbc-9a32-97c222085ca3",
"timestamp": "2025-07-25T08:10:33.582Z"
}
Discussion
settings.jsonのところ、
のように
node
コマンドの指定は不要ですか?(自分の環境では、これ指定しないと動きませんでした)こんにちは。 statusline.js で shebang が設定されているので node コマンドなしでも動作すると思いますが、
chmod +x statusline.js
のようなコマンドでスクリプト自体に実行権限をつける必要がありそうです。あ、なるほど。ありがとうございます!
ありがたく使わせていただいております。
ご参考までに、settings.jsonを以下のように変更することで、WindowsネイティブのPowerShellとGit Bashでも無事動作しました。