use std::io::{self, Read, Write};
use std::process::Command;
const YELLOW: &str = "\x1b[33m";
const MAGENTA: &str = "\x1b[35m";
const BLUE: &str = "\x1b[34m";
const GREEN: &str = "\x1b[32m";
const WHITE: &str = "\x1b[97m";
const RESET: &str = "\x1b[0m";
fn get_git_branch(cwd: &str) -> String {
let mut cmd = Command::new("git");
cmd.args(["branch", "--show-current"]);
if !cwd.is_empty() {
cmd.current_dir(cwd);
}
match cmd.output() {
Ok(o) if o.status.success() => String::from_utf8_lossy(&o.stdout).trim().to_string(),
_ => "none".to_string(),
}
}
fn extract_string_field(json: &str, field: &str) -> Option<String> {
let search = format!("\"{}\":", field);
if let Some(idx) = json.find(&search) {
let remainder = json[idx + search.len()..].trim_start();
if let Some(stripped) = remainder.strip_prefix('"') {
if let Some(end) = stripped.find('"') {
return Some(stripped[..end].to_string());
}
}
}
None
}
fn extract_f64_field(json: &str, field: &str) -> Option<f64> {
let search = format!("\"{}\":", field);
if let Some(idx) = json.find(&search) {
let remainder = &json[idx + search.len()..];
let remainder = remainder.trim_start();
let end = remainder
.find(|c: char| !c.is_ascii_digit() && c != '.' && c != '-')
.unwrap_or(remainder.len());
if let Ok(val) = remainder[..end].parse::<f64>() {
return Some(val);
}
}
None
}
fn build_progress_bar(pct: u32, total_blocks: usize) -> String {
let safe_pct = if pct > 100 { 100 } else { pct };
let filled = ((safe_pct as f32 / 100.0) * (total_blocks as f32)).round() as usize;
let empty = total_blocks.saturating_sub(filled);
format!("{}{}", "█".repeat(filled), "░".repeat(empty))
}
fn extract_quota_frac(json: &str, key: &str) -> Option<f64> {
if let Some(quota_idx) = json.find("\"quota\":") {
let q_block = &json[quota_idx..];
let search = format!("\"{}\":", key);
if let Some(k_idx) = q_block.find(&search) {
if let Some(frac_idx) = q_block[k_idx..].find("\"remaining_fraction\":") {
let frac_str = q_block[k_idx + frac_idx + 21..].trim_start();
let end_frac = frac_str
.find(|c: char| !c.is_ascii_digit() && c != '.' && c != '-')
.unwrap_or(frac_str.len());
if let Ok(val) = frac_str[..end_frac].parse::<f64>() {
return Some(val);
}
}
}
}
None
}
fn extract_quota_reset_secs(json: &str, key: &str) -> Option<u32> {
if let Some(quota_idx) = json.find("\"quota\":") {
let q_block = &json[quota_idx..];
let search = format!("\"{}\":", key);
if let Some(k_idx) = q_block.find(&search) {
if let Some(reset_idx) = q_block[k_idx..].find("\"reset_in_seconds\":") {
let reset_str = q_block[k_idx + reset_idx + 19..].trim_start();
let end = reset_str
.find(|c: char| !c.is_ascii_digit())
.unwrap_or(reset_str.len());
if let Ok(val) = reset_str[..end].parse::<u32>() {
return Some(val);
}
}
}
}
None
}
fn format_time_left(secs: u32) -> String {
let hours = secs / 3600;
let mins = (secs % 3600) / 60;
if hours > 48 {
let days = hours / 24;
format!("{}d", days)
} else if hours > 24 {
let days = hours / 24;
let rem_hours = hours % 24;
format!("{}d{}h", days, rem_hours)
} else if hours > 0 {
format!("{}h{}m", hours, mins)
} else {
format!("{}m", mins)
}
}
fn visible_len(s: &str) -> usize {
let mut len = 0;
let mut in_ansi = false;
for c in s.chars() {
if c == '\x1b' {
in_ansi = true;
} else if in_ansi {
if c.is_ascii_alphabetic() {
in_ansi = false;
}
} else {
len += 1;
}
}
len
}
fn main() {
let mut input = String::new();
let _ = io::stdin().read_to_string(&mut input);
let term_width = extract_f64_field(&input, "terminal_width").unwrap_or(0.0) as usize;
let cwd = extract_string_field(&input, "cwd").unwrap_or_default();
let cwd_basename = cwd.rsplit('/').next().unwrap_or(&cwd);
let home = std::env::var("HOME").unwrap_or_else(|_| "/Users/ash".to_string());
let cwd_display_text = if term_width > 0 && term_width < 140 {
cwd_basename.to_string()
} else if cwd.starts_with(&home) {
cwd.replacen(&home, "~", 1)
} else {
cwd.clone()
};
let branch_json = extract_string_field(&input, "branch").unwrap_or_default();
let mut branch_raw = if branch_json.is_empty() || branch_json == "none" {
get_git_branch(&cwd)
} else {
branch_json
};
if branch_raw.len() > 15 {
let parts: Vec<&str> = branch_raw.split('-').collect();
if parts.len() >= 2 {
branch_raw = format!("{}-{}", parts[0], parts[1]);
}
}
let mut location = String::new();
if !cwd_display_text.is_empty() {
location.push_str(&format!(" {YELLOW}{}", cwd_display_text));
}
if branch_raw != "none" && !branch_raw.is_empty() {
if !location.is_empty() {
location.push(' ');
}
location.push_str(&format!("{WHITE}({}{}{WHITE})", MAGENTA, branch_raw));
}
let location_display = if location.is_empty() {
"".to_string()
} else {
format!("{} {RESET}│", location)
};
let mut model = extract_string_field(&input, "display_name")
.or_else(|| extract_string_field(&input, "id"))
.unwrap_or_else(|| "Unknown Model".to_string());
if term_width > 0 && term_width < 140 {
if let Some(idx) = model.rfind(" (") {
model.truncate(idx);
}
}
let context_frac = extract_f64_field(&input, "used_percentage").unwrap_or(0.0);
let context_pct = context_frac.round() as u32;
let active_agents =
extract_string_field(&input, "agent_state").unwrap_or_else(|| "working".to_string());
let is_3p = model.to_lowercase().contains("claude")
|| model.to_lowercase().contains("gpt")
|| model.to_lowercase().contains("oss");
let prefix = if is_3p { "3p" } else { "gemini" };
let key_5h = format!("{}-5h", prefix);
let key_w = format!("{}-weekly", prefix);
let frac_5h = extract_quota_frac(&input, &key_5h).unwrap_or(1.0);
let frac_w = extract_quota_frac(&input, &key_w).unwrap_or(1.0);
let reset_5h = extract_quota_reset_secs(&input, &key_5h).unwrap_or(0);
let reset_w = extract_quota_reset_secs(&input, &key_w).unwrap_or(0);
let pct_5h = (frac_5h * 100.0).round() as u32;
let pct_w = (frac_w * 100.0).round() as u32;
let context_bar = build_progress_bar(context_pct, 5);
let context_bar_colored = format!("{}{}{}", YELLOW, context_bar, WHITE);
let bar_5h = build_progress_bar(pct_5h, 5);
let bar_w = build_progress_bar(pct_w, 5);
let r_5h_str = if reset_5h > 0 {
format!(" (↻ {})", format_time_left(reset_5h))
} else {
"".to_string()
};
let r_w_str = if reset_w > 0 {
format!(" (↻ {})", format_time_left(reset_w))
} else {
"".to_string()
};
let quotas = format!(
"5h {GREEN}{}{WHITE} {}%{} - W {GREEN}{}{WHITE} {}%{}",
bar_5h, pct_5h, r_5h_str, bar_w, pct_w, r_w_str
);
let left_side = format!(
"{}{WHITE} Context {} {}% {RESET}│ {WHITE} Quotas {} {RESET}",
location_display, context_bar_colored, context_pct, quotas
);
let right_side = format!("{BLUE}{} - {} {RESET}", active_agents, model);
let left_len = visible_len(&left_side);
let right_len = visible_len(&right_side);
let padding = if term_width > left_len + right_len {
" ".repeat(term_width - left_len - right_len)
} else {
" ".to_string()
};
let status_line = format!("{}{}{}", left_side, padding, right_side);
print!("{}", status_line);
let _ = io::stdout().flush();
}