Opus 4.8 high ✦ │ ctx 22% │ app (main) │ PR #1287 ● │ $0.41 · 10m +128 -34
Session ● ● ● ● ● ● ● ● ○ ○ ○ 74% left Resets in 2h 6m
Weekly ● ● ● ● ● ● ● ● ● ● ○ 93% left Resets in 2d 0h
Fable ● ● ● ● ● ● ● ● ● ○ ○ 85% left Resets in 2d 0h
◐ Read:index.ts ✓ Read×1 ✓ Bash×1 ✓ Edit×1 ✓ Grep×1
◐ code-reviewer Review the change for regressions
Fable / All Models usage Relative to Weekly Reset "Pace".
A custom status line for Claude that shows usage relative to "pace" based on your quota reset timeline. Useful for knowing if your burn rate is too high or low relative to your next reset.
Before you install
- This version was reviewed before it was listed on Jul 30, 2026
- Reads your Claude login
- Connects to api.anthropic.com
- Needs Node 18+ for fetch, and a font with Symbols for Legacy Computing to draw 🮕.
- Only weekly windows appear; the 5-hour session limit is never drawn.
- Each run saves the full stdin JSON to ~/.claude/.statusline-last-input.json.
Preview
Opus · F:15% ██▍ ▏ A:7% █▏ ▏ ⟳ 3dOpus · F:15% ██▍ ▏ A:9% █▌ ▏ ⟳ 3dSonnet · F:15% ██▍ ▏ A:18% ██▉ ▏ ⟳ 3dOpus · F:15% ██▍ ▏ A:61% █████████▊ ▏ ⟳ 20hFable · F:80% █████🮕🮕🮕🮕🮕🮕🮕🮕 A:80% █████🮕🮕🮕🮕🮕🮕🮕🮕 ⟳ 5dHaiku · F:15% ██▍ ▏ A:9% █▌ ▏ ⟳ 3dOpus · F:15% ██▍ ▏ A:20% ███▎ ▏ ⟳ 2dOpus · F:15% ██▍ ▏ A:9% █▌ ▏ ⟳ 3dWhat it does
Weekly quota bars for each per-model limit and for all models, each marked against an even pace to the reset, plus a countdown to the weekly reset.
- Written in
- Node.js
- Network access
- api.anthropic.com
- Reads your Claude login
- To fetch your weekly per-model and all-model usage limits
- Works in
- Terminal
How it works
- Takes the model family from stdin and an OAuth token from Keychain or .credentials.json.
- Fetches api.anthropic.com/api/oauth/usage and reuses the reply for 60 seconds.
- Works out pace as the share of the 7-day window already gone before resets_at.
- Behind pace, ▏ marks the pace point; ahead (the Fable run), cells past it turn 🮕.
Source
#!/usr/bin/env node
// Claude Code statusline: weekly quota meters (per-model + all models) with
// even-pacing indicators. Solid fill = usage, 🮕 = spent ahead of pace,
// ▏ = pace point when behind it, ⟳ Nd = weekly reset countdown.
//
// Install: save to ~/.claude/statusline-quota.js, then in ~/.claude/settings.json:
// "statusLine": { "type": "command", "command": "node ~/.claude/statusline-quota.js" }
//
// Reads your Claude Code OAuth token locally (macOS Keychain or
// ~/.claude/.credentials.json) and calls only api.anthropic.com/api/oauth/usage
// (the endpoint /usage uses; undocumented, may change). Needs Node 18+ and a
// font with Symbols for Legacy Computing (else swap 🮕 for ▒).
const fs = require("fs");
const os = require("os");
const path = require("path");
const { execFileSync } = require("child_process");
const CACHE_FILE = path.join(os.homedir(), ".claude", ".statusline-usage-cache.json");
const CACHE_TTL_MS = 60_000;
const WEEK_MS = 7 * 24 * 60 * 60 * 1000;
const BAR_WIDTH = 16;
const ESC = "\x1b[";
const RESET = ESC + "0m";
const DIM = ESC + "2m";
const BOLD = ESC + "1m";
// Solid fill in the bar's identity color on a dark track
const FG_PURPLE = "38;5;141m"; // Fable
const FG_BLUE = "38;5;75m"; // All models
const BG_TRACK = ESC + "48;5;236m";
const FG_MUTED = ESC + "38;5;245m";
function readStdin() {
try {
return JSON.parse(fs.readFileSync(0, "utf8"));
} catch {
return {};
}
}
function getToken() {
try {
const raw = execFileSync(
"security",
["find-generic-password", "-s", "Claude Code-credentials", "-w"],
{ encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }
);
const tok = JSON.parse(raw).claudeAiOauth?.accessToken;
if (tok) return tok;
} catch {}
try {
const raw = fs.readFileSync(path.join(os.homedir(), ".claude", ".credentials.json"), "utf8");
return JSON.parse(raw).claudeAiOauth?.accessToken || null;
} catch {}
return null;
}
function readCache() {
try {
return JSON.parse(fs.readFileSync(CACHE_FILE, "utf8"));
} catch {
return null;
}
}
async function getUsage() {
const cache = readCache();
if (cache && Date.now() - cache.fetchedAt < CACHE_TTL_MS) return cache.data;
const token = getToken();
if (!token) return cache?.data ?? null;
try {
const ctl = new AbortController();
const timer = setTimeout(() => ctl.abort(), 3000);
const res = await fetch("https://api.anthropic.com/api/oauth/usage", {
headers: {
Authorization: `Bearer ${token}`,
"anthropic-beta": "oauth-2025-04-20",
"Content-Type": "application/json",
},
signal: ctl.signal,
});
clearTimeout(timer);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const data = await res.json();
fs.writeFileSync(CACHE_FILE, JSON.stringify({ fetchedAt: Date.now(), data }));
return data;
} catch {
return cache?.data ?? null; // stale is better than nothing
}
}
function pacePercent(resetsAt) {
const reset = Date.parse(resetsAt);
if (Number.isNaN(reset)) return null;
const start = reset - WEEK_MS;
return Math.min(100, Math.max(0, ((Date.now() - start) / WEEK_MS) * 100));
}
// Bar with the percent embedded: fill = actual usage (background color),
// white '│' = where even weekly pacing would put you, drawn over fill or track.
function segment(label, used, resetsAt, identityFg) {
const pace = pacePercent(resetsAt);
const fillFg = ESC + identityFg;
// Pace is shown one of two ways, so any two-tone fill is intentional:
// - behind pace: normal '▒' fill, thin '▏' hairline out in the track at
// the pace position
// - ahead of pace: no hairline; fill cells past the pace point render as
// denser '▓' to mark the overage
const EIGHTHS = ["", "▏", "▎", "▍", "▌", "▋", "▊", "▉"];
const used8 = Math.round((used / 100) * BAR_WIDTH * 8);
const ahead = pace !== null && used > pace;
let paceCell =
pace === null ? -1 : Math.min(BAR_WIDTH - 1, Math.round((pace / 100) * BAR_WIDTH));
// Behind pace: eighth-precision edge glyph. Ahead of pace: whole cells only,
// with at least the final fill cell textured '▚' so overage is always visible.
let fullCells, rem, edgeCell;
if (ahead) {
fullCells = Math.max(1, Math.round(used8 / 8));
rem = 0;
edgeCell = -1;
if (paceCell >= fullCells) paceCell = fullCells - 1;
} else {
fullCells = Math.floor(used8 / 8);
rem = used8 % 8;
edgeCell = rem > 0 ? fullCells : -1;
}
let markerCell = -1;
if (pace !== null && !ahead) {
// '▏' marks the LEFT boundary of its cell; keep it at/past the fill edge
markerCell = paceCell;
while (markerCell < BAR_WIDTH - 1 && markerCell * 8 < used8) markerCell++;
}
let out = "";
for (let i = 0; i < BAR_WIDTH; i++) {
if (i === markerCell && i !== edgeCell) {
out += BG_TRACK + ESC + "38;5;240m" + "▏" + RESET;
} else if (i < fullCells) {
// Overage cells use '🮕' (U+1FB95 CHECKER BOARD FILL) — a fine
// full-bleed checker texture from Symbols for Legacy Computing
if (ahead && i >= paceCell) out += BG_TRACK + fillFg + "🮕" + RESET;
else out += BG_TRACK + fillFg + "█" + RESET;
} else if (i === edgeCell) {
out += BG_TRACK + fillFg + EIGHTHS[rem] + RESET;
} else {
out += BG_TRACK + " " + RESET;
}
}
return `${DIM}${label}:${RESET}${FG_MUTED}${Math.round(used)}%${RESET} ${out}`;
}
async function main() {
const input = readStdin();
try {
fs.writeFileSync(
path.join(os.homedir(), ".claude", ".statusline-last-input.json"),
JSON.stringify(input, null, 2)
);
} catch {}
const model = input.model?.display_name || "";
const usage = await getUsage();
const parts = [];
// Family name only ("Fable 5" -> "Fable") to save characters
const family = model.split(/[\s\d]/)[0];
if (family) parts.push(`${DIM}${family} ·${RESET}`);
if (!usage?.limits) {
parts.push(`${DIM}quota unavailable${RESET}`);
} else {
const weeklyAll = usage.limits.find((l) => l.kind === "weekly_all");
const scoped = usage.limits.filter((l) => l.kind === "weekly_scoped");
for (const lim of scoped) {
const label = (lim.scope?.model?.display_name || "S")[0].toUpperCase();
parts.push(segment(label, lim.percent, lim.resets_at, FG_PURPLE));
}
if (weeklyAll)
parts.push(segment("A", weeklyAll.percent, weeklyAll.resets_at, FG_BLUE));
if (!scoped.length && !weeklyAll) parts.push(`${DIM}no weekly limits reported${RESET}`);
const resetsAt = weeklyAll?.resets_at || scoped[0]?.resets_at;
const resetMs = resetsAt ? Date.parse(resetsAt) - Date.now() : NaN;
if (!Number.isNaN(resetMs) && resetMs > 0) {
let left;
if (resetMs >= 24 * 60 * 60 * 1000) left = `${Math.ceil(resetMs / 86_400_000)}d`;
else if (resetMs >= 60 * 60 * 1000) left = `${Math.ceil(resetMs / 3_600_000)}h`;
else left = `${Math.ceil(resetMs / 60_000)}m`;
parts.push(`${DIM}⟳ ${left}${RESET}`);
}
}
process.stdout.write(parts.join(" "));
}
main();More status lines
app │ Opus 4.8 │ context: ██░░░░░░░░ 22% │ Fixing the null check in the parserapp ⎇ main
⠀
★ Opus 4.8 · high │ Ctx ██▏ 22% 45k │ 5h ▀▀▀▀▀▀▀▀▀▀ 26% ↻2h6m │ 7d ▀▀▀▀▀▀▀▀▀▀ 7% ↻2d0h
Model: Opus 4.8 | Thinking: high | Context: [▰▰▱▱▱▱▱▱▱▱] 45k/200k (22%) | cwd: ~/app | ⎇ main | (+0,-0)
Cost: $0.41 | Block: 2hr 6m | Weekly: 7.0% | Session: 10m | In: 916.7 t/s | Out: 29.2 t/s | Cached: 2k | Session: [▰▱▱▱▱] 26.0%
Mem: 0.8G/1.0G | (*゚∀゚*) | Session ID: b8e1c0d2-4a6f-4e2a-9c1b-3f5d7a9e2c10
Opus 4.8 │ ✍️ 22% │ app (main) │ ● high
current ●●○○○○○○○○ 26% ⟳ 6:59pm
weekly ○○○○○○○○○○ 7% ⟳ aug 1, 5:52pmOpus 4.8 | app@main | 44k/200k (22%) | effort: high | 5h 26% @18:59 | 7d 7% @Sat Aug 1, 17:52Related mods

A cockpit for Claude Code: plan bars and subagent strips, context, usage limits with reset countdowns and burn rates, spend, the model route and a pet. Six bar styles, three collapsed looks. CLI and Desktop.

One line above the prompt: context tokens with their weather, a bar per recent prompt, your 5-hour and 7-day limits against the time elapsed, the prompt cache with its time left, the session cost with the last prompt's share in dollars and in points of the 5-hour limit, and the subagents running. Labels in English, French or Russian. After Token Weather (Anthropic, Apache-2.0) and the ideas of usage-meter (HolyGrail) and prompt-cache-control (Daniel San).
⏵⏵ auto mode on · ← for agents 5h ━━━─────── 26% ↻ 2h 7mYour subscription usage at the bottom right of Claude Code: the 5-hour and weekly windows, taking turns, side by side or fixed on one of them.

Flightdeck: a live agent dashboard for Claude Code. Main model vitals, an on-call architect, every permission check, subagent cards and swimlanes, a turn receipt and a session log, all from real session events