Skip to content

Instantly share code, notes, and snippets.

@gapurov
Forked from steipete/statusline-worktree.js
Created August 15, 2025 22:11
Show Gist options
  • Save gapurov/75eb3bfe6393caab090a71eb9c4601c5 to your computer and use it in GitHub Desktop.
Save gapurov/75eb3bfe6393caab090a71eb9c4601c5 to your computer and use it in GitHub Desktop.
Ny Claude Code Status Bar - see https://x.com/steipete/status/1956465968835915897
{
"$schema": "https://json.schemastore.org/claude-code-settings.json",
"includeCoAuthoredBy": false,
"permissions": {
"defaultMode": "bypassPermissions"
},
"model": "opusplan",
"statusLine": {
"type": "command",
"command": "/Users/steipete/.claude/statusline-worktree.js"
},
"feedbackSurveyState": {
"lastShownTime": 1754086675675
}
}
#!/usr/bin/env bun
"use strict";
const fs = require("fs");
const { execSync } = require("child_process");
const path = require("path");
// ANSI color constants
const c = {
cy: '\033[36m', // cyan
g: '\033[32m', // green
m: '\033[35m', // magenta
gr: '\033[90m', // gray
r: '\033[31m', // red
o: '\033[38;5;208m', // orange
y: '\033[33m', // yellow
x: '\033[0m' // reset
};
// Unified execution function with error handling
const exec = (cmd, cwd = null) => {
try {
const options = { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] };
if (cwd) options.cwd = cwd;
return execSync(cmd, options).trim();
} catch {
return '';
}
};
// Fast context percentage calculation
function getContextPct(transcriptPath) {
if (!transcriptPath) return "0";
try {
const data = fs.readFileSync(transcriptPath, "utf8");
const lines = data.split('\n');
// Scan last 50 lines only for performance
let latestUsage = null;
let latestTs = -Infinity;
for (let i = Math.max(0, lines.length - 50); i < lines.length; i++) {
const line = lines[i].trim();
if (!line) continue;
try {
const j = JSON.parse(line);
const ts = typeof j.timestamp === "string" ? new Date(j.timestamp).getTime() : j.timestamp;
const usage = j.message?.usage;
if (ts > latestTs && usage && j.message?.role === "assistant") {
latestTs = ts;
latestUsage = usage;
}
} catch {}
}
if (latestUsage) {
const used = (latestUsage.input_tokens || 0) + (latestUsage.output_tokens || 0) +
(latestUsage.cache_read_input_tokens || 0) + (latestUsage.cache_creation_input_tokens || 0);
const pct = Math.min(100, (used * 100) / 160000);
return pct >= 90 ? pct.toFixed(1) : Math.round(pct).toString();
}
} catch {}
return "0";
}
// Cached PR lookup with optimized file operations
function getPR(branch, workingDir) {
const gitDir = exec('git rev-parse --git-common-dir', workingDir);
if (!gitDir) return '';
const cacheFile = `${gitDir}/statusbar/pr-${branch}`;
const tsFile = `${cacheFile}.timestamp`;
// Check cache freshness (60s TTL)
try {
const age = Math.floor(Date.now() / 1000) - parseInt(fs.readFileSync(tsFile, 'utf8'));
if (age < 60) return fs.readFileSync(cacheFile, 'utf8').trim();
} catch {}
// Fetch and cache new PR data
const url = exec(`gh pr list --head "${branch}" --json url --jq '.[0].url // ""'`, workingDir);
try {
fs.mkdirSync(path.dirname(cacheFile), { recursive: true });
fs.writeFileSync(cacheFile, url);
fs.writeFileSync(tsFile, Math.floor(Date.now() / 1000).toString());
} catch {}
return url;
}
// Main statusline function
function statusline() {
let input;
try {
input = JSON.parse(fs.readFileSync(0, "utf8"));
} catch {
input = {};
}
const currentDir = input.workspace?.current_dir;
const model = input.model?.display_name;
// Build model display with context
let modelDisplay = '';
if (model) {
const abbrev = model.includes('Opus') ? 'O' : model.includes('Sonnet') ? 'S' : model.includes('Haiku') ? 'H' : '?';
const pct = getContextPct(input.transcript_path);
const pctNum = parseFloat(pct);
const pctColor = pctNum >= 90 ? c.r : pctNum >= 70 ? c.o : pctNum >= 50 ? c.y : c.gr;
modelDisplay = ` ${c.gr}(${pctColor}${pct}% ${c.gr}${abbrev})${c.x}`;
}
// Handle non-directory cases
if (!currentDir) return `${c.cy}~${c.x}${modelDisplay}`;
// Don't chdir - work with the provided directory directly
const workingDir = currentDir;
// Check git repo status
if (exec('git rev-parse --is-inside-work-tree', workingDir) !== 'true') {
return `${c.cy}${workingDir.replace(process.env.HOME, '~')}${c.x}${modelDisplay}`;
}
// Get git info in one batch
const branch = exec('git branch --show-current', workingDir);
const gitDir = exec('git rev-parse --git-dir', workingDir);
const repoUrl = exec('git remote get-url origin', workingDir);
const repoName = repoUrl ? path.basename(repoUrl, '.git') : '';
// Smart path display logic
const prUrl = getPR(branch, workingDir);
const homeProjects = `${process.env.HOME}/Projects/${repoName}`;
let displayDir = '';
if (workingDir === homeProjects) {
displayDir = prUrl ? '' : `${workingDir.replace(process.env.HOME, '~')} `;
} else if (workingDir.startsWith(homeProjects + '/')) {
displayDir = `${workingDir.slice(homeProjects.length + 1)} `;
} else {
displayDir = `${workingDir.replace(process.env.HOME, '~')} `;
}
// Git status processing (optimized)
const statusOutput = exec('git status --porcelain', workingDir);
let gitStatus = '';
if (statusOutput) {
const lines = statusOutput.split('\n');
let added = 0, modified = 0, deleted = 0, untracked = 0;
for (const line of lines) {
if (!line) continue;
const s = line.slice(0, 2);
if (s[0] === 'A' || s === 'M ') added++;
else if (s[1] === 'M' || s === ' M') modified++;
else if (s[0] === 'D' || s === ' D') deleted++;
else if (s === '??') untracked++;
}
if (added) gitStatus += ` +${added}`;
if (modified) gitStatus += ` ~${modified}`;
if (deleted) gitStatus += ` -${deleted}`;
if (untracked) gitStatus += ` ?${untracked}`;
}
// Line changes calculation
const diffOutput = exec('git diff --numstat', workingDir);
if (diffOutput) {
let totalAdd = 0, totalDel = 0;
for (const line of diffOutput.split('\n')) {
if (!line) continue;
const [add, del] = line.split('\t');
totalAdd += parseInt(add) || 0;
totalDel += parseInt(del) || 0;
}
const delta = totalAdd - totalDel;
if (delta) gitStatus += delta > 0 ? ` Δ+${delta}` : ` Δ${delta}`;
}
// Format final output
const prDisplay = prUrl ? ` ${c.gr}${prUrl}${c.x}` : '';
const isWorktree = gitDir.includes('/.git/worktrees/');
if (isWorktree) {
const worktreeName = path.basename(displayDir.replace(/ $/, ''));
const branchDisplay = branch === worktreeName ? '↟' : `${branch}↟`;
return `${c.cy}${displayDir}${c.x}${c.m}[${branchDisplay}${gitStatus}]${c.x}${prDisplay}${modelDisplay}`;
} else {
if (!displayDir) {
return `${c.g}[${branch}${gitStatus}]${c.x}${prDisplay}${modelDisplay}`;
} else {
return `${c.cy}${displayDir}${c.x}${c.g}[${branch}${gitStatus}]${c.x}${prDisplay}${modelDisplay}`;
}
}
}
// Output result
process.stdout.write(statusline());
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment