Files
custom-statusline/custom_statusline.js
T
mikkeli 95af855cf5 Add 5h rate limit bar and session cost to statusline
Dumps harness JSON to os.tmpdir() on each render to inspect available
fields. Adds a combined usage segment showing the Pro 5-hour rate limit
as a colour-coded bar and session cost in USD; each part is omitted
gracefully when its data is absent, so the script works for API instances too.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-11 09:21:59 +09:00

125 lines
3.7 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env node
// Claude Code Statusline
// Shows: <directory> | <git branch> | <model> | [===== ] 60%
// Git branch segment is omitted entirely when not in a git repository.
'use strict';
const { execSync } = require('child_process');
const fs = require('fs');
const path = require('path');
const os = require('os');
const R = '\x1b[0m';
const DIM = '\x1b[90m'; // separators
const CYAN = '\x1b[36m'; // directory
const GREEN = '\x1b[32m'; // git branch
const MAGENTA = '\x1b[35m'; // model
const BAR_OK = '\x1b[32m'; // usage < 70%
const BAR_WARN = '\x1b[33m'; // usage 7089%
const BAR_CRIT = '\x1b[31m'; // usage >= 90%
const SEP = `${DIM} | ${R}`;
let input = '';
process.stdin.on('data', chunk => input += chunk);
process.stdin.on('end', () => {
let data = {};
try { if (input.trim()) data = JSON.parse(input); } catch (_) {}
try {
fs.writeFileSync(path.join(os.tmpdir(), 'statusline_data.json'), JSON.stringify(data, null, 2));
} catch (_) {}
// --- Directory ---
const cwd =
(data.workspace && data.workspace.current_dir) ||
data.cwd ||
process.env.PWD ||
process.cwd();
const home = os.homedir().replace(/\\/g, '/');
const cwdNorm = cwd.replace(/\\/g, '/');
const displayDir = cwdNorm.startsWith(home)
? '~' + cwdNorm.slice(home.length)
: cwdNorm;
// --- Git branch ---
let gitBranch = null;
try {
const branch = execSync('git rev-parse --abbrev-ref HEAD', {
cwd,
stdio: ['ignore', 'pipe', 'ignore'],
timeout: 2000,
env: { ...process.env, GIT_OPTIONAL_LOCKS: '0' },
}).toString().trim();
if (branch && branch !== 'HEAD') {
gitBranch = branch;
} else {
try {
gitBranch = execSync('git rev-parse --short HEAD', {
cwd,
stdio: ['ignore', 'pipe', 'ignore'],
timeout: 2000,
env: { ...process.env, GIT_OPTIONAL_LOCKS: '0' },
}).toString().trim() || 'HEAD';
} catch (_) { gitBranch = 'HEAD'; }
}
} catch (_) { gitBranch = null; }
// --- Model ---
const model =
(data.model && data.model.display_name) ||
(data.model && data.model.id) ||
'Claude';
// --- Context window usage ---
const pct = Math.round(
(data.context_window && data.context_window.used_percentage) || 0
);
const barColor = pct >= 90 ? BAR_CRIT : pct >= 70 ? BAR_WARN : BAR_OK;
const BAR_WIDTH = 10;
const filled = Math.round((pct / 100) * BAR_WIDTH);
const bar =
`${barColor}[` +
'='.repeat(filled) +
`${DIM}` + ' '.repeat(BAR_WIDTH - filled) +
`${barColor}] ${pct}%${R}`;
// --- 5h rate limit + cost ---
const fiveHour = data.rate_limits && data.rate_limits.five_hour;
const totalCost = data.cost && data.cost.total_cost_usd;
let usageSegment = null;
if (fiveHour || totalCost) {
const parts = [];
if (fiveHour) {
const fivePct = Math.round(fiveHour.used_percentage);
const fiveColor = fivePct >= 90 ? BAR_CRIT : fivePct >= 70 ? BAR_WARN : BAR_OK;
const fiveFilled = Math.round((fivePct / 100) * BAR_WIDTH);
const fiveBar =
`${fiveColor}[` +
'='.repeat(fiveFilled) +
`${DIM}` + ' '.repeat(BAR_WIDTH - fiveFilled) +
`${fiveColor}] ${fivePct}%${R}`;
parts.push(`5h ${fiveBar}`);
}
if (totalCost) {
parts.push(`${DIM}$${totalCost.toFixed(2)}${R}`);
}
usageSegment = parts.join(' ');
}
// --- Assemble ---
const segments = [`${CYAN}${displayDir}${R}`];
if (gitBranch !== null) segments.push(`${GREEN}${gitBranch}${R}`);
segments.push(`${MAGENTA}${model}${R}`);
segments.push(bar);
if (usageSegment !== null) segments.push(usageSegment);
process.stdout.write(segments.join(SEP) + '\n');
});