Skip to content

Instantly share code, notes, and snippets.

@mmatczuk
Last active March 18, 2026 21:49
Show Gist options
  • Select an option

  • Save mmatczuk/c1a8551376fff5c937396d2e5aafdb45 to your computer and use it in GitHub Desktop.

Select an option

Save mmatczuk/c1a8551376fff5c937396d2e5aafdb45 to your computer and use it in GitHub Desktop.
Claude Code status line: Repo(branch) | session/today cost | duration | model
#!/usr/bin/env bun
import { readFileSync, writeFileSync, existsSync } from "fs";
import { execSync } from "child_process";
import { homedir } from "os";
import { join } from "path";
const input = JSON.parse(readFileSync("/dev/stdin", "utf8"));
const modelRaw: string = input.model?.display_name ?? input.model ?? "";
const sessionId: string = input.session_id ?? "";
const sessionCost: number = input.cost?.total_cost_usd ?? 0;
const totalDurationMs: number = input.cost?.total_duration_ms ?? 0;
const ctxPct: number | null = input.context_window?.used_percentage ?? null;
const cwd: string = input.cwd ?? process.cwd();
// --- Model short name: "Claude Sonnet 4.6" → "Sonnet 4.6" ---
function shortModel(name: string): string {
const m = name.match(/\b(Opus|Sonnet|Haiku)\b.*?(\d+\.\d+)/i);
if (!m) return name;
return `${m[1]} ${m[2]}`;
}
// --- Repo label: just Repo(branch), no user prefix ---
function repoLabel(dir: string): string {
try {
const remote = execSync("git remote get-url origin", { cwd: dir, stdio: ["pipe","pipe","pipe"] })
.toString().trim();
const match = remote.match(/[:/]([^/:]+\/([^/]+?))(?:\.git)?$/);
const repo = match ? match[2] : dir;
const branch = execSync("git rev-parse --abbrev-ref HEAD", { cwd: dir, stdio: ["pipe","pipe","pipe"] })
.toString().trim();
return `${repo}(${branch})`;
} catch { /* no remote */ }
try {
execSync("git rev-parse --git-dir", { cwd: dir, stdio: ["pipe","pipe","pipe"] });
const branch = execSync("git rev-parse --abbrev-ref HEAD", { cwd: dir, stdio: ["pipe","pipe","pipe"] })
.toString().trim();
return `${dir}(${branch})`;
} catch {
return dir;
}
}
// --- Session duration ---
function fmtDuration(ms: number): string {
const totalMin = Math.floor(ms / 60_000);
const h = Math.floor(totalMin / 60);
const m = totalMin % 60;
return h > 0 ? `${h}h ${m}m` : `${m}m`;
}
// --- Ledger: cost per session (keyed, one entry per session_id) ---
interface LedgerEntry { ts: number; session_id: string; cost: number; }
const LEDGER = join(homedir(), ".claude", "cost-ledger.jsonl");
function readLines<T>(path: string): T[] {
if (!existsSync(path)) return [];
return readFileSync(path, "utf8").split("\n")
.filter(Boolean).flatMap(line => { try { return [JSON.parse(line) as T]; } catch { return []; } });
}
const now = Date.now() / 1000;
let entries = readLines<LedgerEntry>(LEDGER);
if (sessionId) {
entries = entries.filter(e => e.session_id !== sessionId);
entries.push({ ts: now, session_id: sessionId, cost: sessionCost });
writeFileSync(LEDGER, entries.map(e => JSON.stringify(e)).join("\n") + "\n", "utf8");
}
const midnight = (() => { const d = new Date(); d.setHours(0,0,0,0); return d.getTime()/1000; })();
const todayCost = entries.filter(e => e.ts >= midnight).reduce((s, e) => s + e.cost, 0);
// --- Output ---
const fmt = (n: number) => `$${n.toFixed(2)}`;
const repo = repoLabel(cwd);
const duration = fmtDuration(totalDurationMs);
const model = shortModel(modelRaw);
const ctx = ctxPct !== null ? ` (${Math.round(ctxPct)}%)` : "";
process.stdout.write(`${repo} | 💰 ${fmt(sessionCost)} (${duration}) session / ${fmt(todayCost)} today | 🤖 ${model}${ctx}\n`);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment