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.
Not showing after you paste? Fix it

Preview

Clean repo
Opus · F:15% ██▍ ▏ A:7% █▏ ▏ ⟳ 3d
New session
Opus · F:15% ██▍ ▏ A:9% █▌ ▏ ⟳ 3d
Dirty branch
Sonnet · F:15% ██▍ ▏ A:18% ██▉ ▏ ⟳ 3d
Near-full
Opus · F:15% ██▍ ▏ A:61% █████████▊ ▏ ⟳ 20h
1M context
Fable · F:80% █████🮕🮕🮕🮕🮕🮕🮕🮕 A:80% █████🮕🮕🮕🮕🮕🮕🮕🮕 ⟳ 5d
Post-compact
Haiku · F:15% ██▍ ▏ A:9% █▌ ▏ ⟳ 3d
Worktree
Opus · F:15% ██▍ ▏ A:20% ███▎ ▏ ⟳ 2d
Non-git
Opus · F:15% ██▍ ▏ A:9% █▌ ▏ ⟳ 3d

In Claude Desktop

Fable / All Models usage Relative to Weekly Reset "Pace". in Claude Desktop

Recorded with statusline-anywhere.

What 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

  1. Takes the model family from stdin and an OAuth token from Keychain or .credentials.json.
  2. Fetches api.anthropic.com/api/oauth/usage and reuses the reply for 60 seconds.
  3. Works out pace as the share of the 7-day window already gone before resets_at.
  4. 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

  • 52 installs
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
app │ Opus 4.8 │ context: ██░░░░░░░░ 22% │ Fixing the null check in the parser
app ⎇ 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
  • 20 installs
Opus 4.8 │ ✍️ 22% │ app (main) │ ● high current ●●○○○○○○○○ 26% ⟳ 6:59pm weekly ○○○○○○○○○○ 7% ⟳ aug 1, 5:52pm
  • 20 installs
Opus 4.8 | app@main | 44k/200k (22%) | effort: high | 5h 26% @18:59 | 7d 7% @Sat Aug 1, 17:52

Related mods

Deck in Claude Desktop

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.

Token Weather Usage in Claude 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 7m

Your 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 in Claude Desktop

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