#!/usr/bin/env node /** * Claude Code Status Line (Standalone) * * Designed for headless Linux servers — no browser, no tray app needed. * * Context window: Always shown, read from stdin (JSON piped by Claude Code). * Token usage: Optional. Reads from a cache file that can be populated by * the included fetcher (cron) or any external source. * * Output: Context ▓▓▓▓░░░░░░ 40% | Token ░░░░░░░░░░ 10% 267M */ const fs = require('fs'); const path = require('path'); // --- Config --- const CACHE_FILE = process.env.CLAUDE_USAGE_CACHE || path.join(process.env.TMPDIR || process.env.TEMP || '/tmp', 'claude_usage.json'); const CACHE_MAX_AGE_S = parseInt(process.env.CLAUDE_USAGE_MAX_AGE || '900', 10); // 15 min default // --- Bar renderer --- function bar(pct, width) { width = width || 10; const filled = Math.floor(Math.min(100, Math.max(0, pct)) / (100 / width)); return '\u2593'.repeat(filled) + '\u2591'.repeat(width - filled); } // --- Context Window --- function getContextPart(data) { try { const pct = Math.round(parseFloat(data?.context_window?.used_percentage) || 0); return 'Context ' + bar(pct) + ' ' + pct + '%'; } catch { return 'Context ' + '\u2591'.repeat(10); } } // --- Usage Cache --- function readCache() { try { if (!fs.existsSync(CACHE_FILE)) return null; const stat = fs.statSync(CACHE_FILE); const ageS = (Date.now() - stat.mtimeMs) / 1000; if (ageS > CACHE_MAX_AGE_S) return null; return JSON.parse(fs.readFileSync(CACHE_FILE, 'utf8')); } catch { return null; } } function formatMinutes(isoStr) { if (!isoStr) return ''; try { const remaining = Math.max(0, Math.round((new Date(isoStr).getTime() - Date.now()) / 60000)); return remaining + 'M'; } catch { return ''; } } function getUsagePart(data) { if (!data) return ''; const parts = []; if (data.five_hour && data.five_hour.utilization > 0) { const pct = Math.round(data.five_hour.utilization); const remaining = formatMinutes(data.five_hour.resets_at); parts.push('Token ' + bar(pct) + ' ' + pct + '%' + (remaining ? ' ' + remaining : '')); } if (data.seven_day && data.seven_day.utilization > 20) { const pct = Math.round(data.seven_day.utilization); parts.push('7d ' + pct + '%'); } return parts.join(' | '); } // --- Main --- function main() { let stdinData = {}; try { stdinData = JSON.parse(fs.readFileSync(0, 'utf8')); } catch { /* no stdin or invalid JSON */ } const ctx = getContextPart(stdinData); const usage = getUsagePart(readCache()); console.log(usage ? ctx + ' | ' + usage : ctx); } main();