Minecraft Bars

Three Minecraft-style lines: the model tinted by material (netherite, diamond, gold or iron), folder, git branch, cost, session time and cache tokens; hearts for five-hour usage and drumsticks for seven-day usage, emptying as they climb, with reset countdowns; then an XP bar for the context window. Node built-ins only, no network calls.

In Claude Desktop? This status line shows there once you also install statusline-anywhere.

Before you install

  • This version was reviewed before it was listed on Nov 2, 2026
  • Doesn't ask to connect to the internet
Not showing after you paste? Fix it

Preview

Clean repo
[Opus 5.5] 📁 app | 🌿 main | $0.41 | ⏱️ 10m 12s ❤️❤️❤️❤️❤️❤️❤️🖤🖤🖤 26% 2h6m 🍗🍗🍗🍗🍗🍗🍗🍗🍗🦴 7% 2d0h 🟩🟩🟩🟩🟩⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛ 22%
New session
[Opus 5.5] 📁 app | 🌿 main | $0.00 | ⏱️ 10m 12s ⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛ 0%
Dirty branch
[Sonnet 5.5] 📁 app | 🌿 feat/auth! | $0.41 | ⏱️ 10m 12s ❤️❤️❤️❤️❤️❤️🖤🖤🖤🖤 40% 1h11m 🍗🍗🍗🍗🍗🍗🍗🍗🦴🦴 18% 2d21h 🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛ 48%
Near-full
[Opus 5.5] 📁 app | 🌿 main | $4.12 | ⏱️ 10m 12s ❤️🖤🖤🖤🖤🖤🖤🖤🖤🖤 88% 17m 🍗🍗🍗🍗🦴🦴🦴🦴🦴🦴 61% 19h59m 🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩⬛⬛⬛ 91%
1M context
[Fable 5.1] 📁 app | 🌿 main | $0.41 | ⏱️ 10m 12s ❤️❤️❤️❤️❤️❤️❤️🖤🖤🖤 33% 3h29m 🍗🍗🦴🦴🦴🦴🦴🦴🦴🦴 80% 4d23h 🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛ 64%
Post-compact
[Haiku 4.5] 📁 app | 🌿 main | $0.41 | ⏱️ 10m 12s ❤️❤️❤️❤️❤️🖤🖤🖤🖤🖤 52% 4h1m ⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛ 0%
Worktree
[Opus 5.5] 📁 feature | 🌿 worktree-feature | $0.41 | ⏱️ 10m 12s 🟩🟩🟩🟩🟩🟩🟩🟩🟩⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛ 37%
Non-git
[Opus 5.5] 📁 scratch | $0.41 | ⏱️ 10m 12s 🟩🟩🟩🟩🟩⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛ 22%

What it does

Written in
Node.js
Network access
None requested
Works in
Terminal

Source

#!/usr/bin/env node
// minecraft-statusline — a Minecraft-themed statusline for Claude Code
// https://github.com/jonahmabry/minecraft-statusline
//
// Line 1: model (tinted by "material" — netherite/diamond/gold/iron), dir, git branch, cost, elapsed time, cache tokens
// Line 2: hearts = 5-hour rate limit (depletes as usage climbs), food = 7-day rate limit
// Line 3: XP bar = context window fill percentage
//
// Pure Node — no external dependencies. git is optional (used only for the branch segment).

const { execSync } = require("child_process");

const NETHERITE = "\x1b[0;90m";
const DIAMOND = "\x1b[96m";
const GOLD = "\x1b[1;33m";
const IRON = "\x1b[38;5;251m";
const CYAN = "\x1b[36m";
const MAGENTA = "\x1b[35m";
const RESET = "\x1b[0m";

// ---------- helpers ----------
// Integer part of a possibly-decimal value ("42.5" -> 42), mirroring bash ${x%.*}.
function intPart(v) {
  const n = parseInt(String(v), 10);
  return Number.isNaN(n) ? 0 : n;
}

function formatCountdown(resetsAt) {
  const now = Math.floor(Date.now() / 1000);
  const diff = Number(resetsAt) - now;
  if (diff <= 0) return "now";
  if (diff < 3600) return `${Math.floor(diff / 60)}m`;
  if (diff < 86400) return `${Math.floor(diff / 3600)}h${Math.floor((diff % 3600) / 60)}m`;
  return `${Math.floor(diff / 86400)}d${Math.floor((diff % 86400) / 3600)}h`;
}

// 10-icon bar that DEPLETES as usage climbs.
function renderMcBar(used, full, empty) {
  let remaining = 100 - intPart(used);
  if (remaining < 0) remaining = 0;
  if (remaining > 100) remaining = 100;
  let filled = Math.floor((remaining + 5) / 10);
  if (filled > 10) filled = 10;
  if (filled === 0 && remaining > 0) filled = 1;
  const empties = 10 - filled;
  return full.repeat(filled) + empty.repeat(empties);
}

// N-segment bar that FILLS as usage climbs.
function renderFillBar(pct, full, empty, segs) {
  let filled = Math.floor((pct * segs) / 100);
  if (filled > segs) filled = segs;
  const empties = segs - filled;
  return full.repeat(filled) + empty.repeat(empties);
}

function fmtTokens(n) {
  n = Number(n) || 0;
  return n >= 1000 ? `${Math.floor(n / 1000)}k` : `${n}`;
}

// Present = value provided and non-empty, mirroring bash `// empty` + [ -n ] (0 counts as present).
function present(v) {
  return v !== undefined && v !== null && v !== "";
}

function gitBranch(dir) {
  if (!dir) return "";
  const run = (cmd) => execSync(cmd, { cwd: dir, stdio: ["ignore", "pipe", "ignore"] }).toString();
  try {
    run("git rev-parse --git-dir");
  } catch {
    return "";
  }
  let branchName = "";
  try {
    branchName = run("git branch --show-current").trim();
  } catch {
    /* detached HEAD or no commits — leave blank */
  }
  let dirty = "";
  try {
    const lines = run("git status --porcelain").split("\n").filter((l) => l.length > 0);
    if (lines.length > 0) {
      // A staged/changed index char (not space, not ?) means dirty "*"; otherwise "!".
      dirty = lines.some((l) => l[0] !== " " && l[0] !== "?") ? "*" : "!";
    }
  } catch {
    /* ignore */
  }
  return ` | 🌿 ${branchName}${dirty}`;
}

function render(input) {
  let data;
  try {
    data = JSON.parse(input);
  } catch {
    return; // no/!invalid input — print nothing rather than a broken line
  }

  const model = data?.model?.display_name ?? "";
  const dir = data?.workspace?.current_dir ?? "";
  const cost = Number(data?.cost?.total_cost_usd ?? 0);
  const pct = intPart(data?.context_window?.used_percentage ?? 0);
  const durationMs = Number(data?.cost?.total_duration_ms ?? 0);
  const cacheRead = Number(data?.cost?.cache_read_input_tokens ?? 0);
  const cacheWrite = Number(data?.cost?.cache_creation_input_tokens ?? 0);

  // ---------- model color (tints the model name in the header) ----------
  const modelLc = model.toLowerCase();
  let modelColor = IRON;
  if (modelLc.includes("fable")) modelColor = NETHERITE;
  else if (modelLc.includes("opus")) modelColor = DIAMOND;
  else if (modelLc.includes("sonnet")) modelColor = GOLD;
  else if (modelLc.includes("haiku")) modelColor = IRON;

  // ---------- XP bar = context window (fills up as the window fills) ----------
  const xpBar = renderFillBar(pct, "🟩", "⬛", 26);

  // ---------- elapsed time ----------
  const mins = Math.floor(durationMs / 60000);
  const secs = Math.floor((durationMs % 60000) / 1000);
  const timeFmt = `⏱️ ${mins}m ${secs}s`;

  // ---------- git branch ----------
  const branch = gitBranch(dir);

  // ---------- rate limits: hearts = 5h, food = 7d ----------
  const fivePct = data?.rate_limits?.five_hour?.used_percentage;
  const fiveResets = data?.rate_limits?.five_hour?.resets_at;
  const weekPct = data?.rate_limits?.seven_day?.used_percentage;
  const weekResets = data?.rate_limits?.seven_day?.resets_at;

  let healthSeg = "";
  if (present(fivePct)) {
    healthSeg = `${renderMcBar(fivePct, "❤️", "🖤")} ${intPart(fivePct)}%`;
    if (present(fiveResets)) healthSeg += ` ${MAGENTA}${formatCountdown(fiveResets)}${RESET}`;
  }
  let foodSeg = "";
  if (present(weekPct)) {
    foodSeg = `${renderMcBar(weekPct, "🍗", "🦴")} ${intPart(weekPct)}%`;
    if (present(weekResets)) foodSeg += ` ${MAGENTA}${formatCountdown(weekResets)}${RESET}`;
  }

  // ---------- cost + cache ----------
  const costFmt = `$${cost.toFixed(2)}`;
  let cacheSegment = "";
  if (cacheRead > 0 || cacheWrite > 0) {
    cacheSegment = ` | ${CYAN}↩${fmtTokens(cacheRead)} ↪${fmtTokens(cacheWrite)}${RESET}`;
  }

  // ---------- render the HUD ----------
  const dirBase = String(dir).split(/[/\\]/).pop();
  const line1 = `${modelColor}[${model}]${RESET} 📁 ${dirBase}${branch} | ${GOLD}${costFmt}${RESET} | ${timeFmt}${cacheSegment}`;
  const line2 = `${healthSeg}   ${foodSeg}`;
  const line3 = `${xpBar} ${pct}%`;
  process.stdout.write(`${line1}\n${line2}\n${line3}\n`);
}

let input = "";
process.stdin.setEncoding("utf8");
process.stdin.on("data", (chunk) => (input += chunk));
process.stdin.on("end", () => render(input));

Badge for your README

Own this listing? Add it to your README. The badge shows the install count as it grows.

Reviewed on statuslin.es
[![Reviewed on statuslin.es](https://staging.statuslin.es/badge/c/minecraft-bars-a3364fe0.svg)](https://staging.statuslin.es/c/minecraft-bars-a3364fe0?utm_source=readme-badge)

Or show the preview card:

Preview on statuslin.es
[![Preview on statuslin.es](https://staging.statuslin.es/og/c/minecraft-bars-a3364fe0.png)](https://staging.statuslin.es/c/minecraft-bars-a3364fe0?utm_source=readme-badge)

More status lines

┌─────────────────────────────────────────────────────────────────────────────────┬────────────────────────────────┐ │ session summary session b8e1c0d2 │ │ ├──────────────────────────┬──────────────────────────────────────────────────────┤ │ │ 📁 user/app +0 -0 lines │ Opus 5.5 effort high · 0min │ │ │ app main │ cost $0.00 (all time) · $0.00 (this session) │ │ ├──────────────────────────┴──────────────────────────────────────────────────────┤ │ │ tokens 0 (all time) · 0 (this session) context ██░░░░░░░░ 22% compact 0 times │ │ ├─────────────────────────────────────────────────────────────────────────────────┤ │ │ 5h-quota ███░░░░░░░ 26% resets 2h6m 7d-quota █░░░░░░░░░ 7% resets 2d0h │ │ └─────────────────────────────────────────────────────────────────────────────────┴────────────────────────────────┘
🤖 Opus 4.8 [200K] │ 🎯 high │ 📁 app │ 🌿 main 📅 07/07/26 Tue │ ⏰ 22:59:15 │ ₿$63k │ ☀️ +90°F Ctx: ● ▓▓░░░░ 33% [66K/200K] │ ⏱ 10m │ 🔧 2.1.155
◆ Opus 4.8 │ ▪ app │ ⎇ main │ ◷ 13:32 │ ██▄░░░░░░░ 26% (44K/167K) │ 26% ↻2h6m │ 7% ⟳2d0h
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