Skip to content

Instantly share code, notes, and snippets.

@hankbao
Last active September 2, 2026 22:05
Show Gist options
  • Select an option

  • Save hankbao/85f33e2782f0451590f7afcd97cd79fb to your computer and use it in GitHub Desktop.

Select an option

Save hankbao/85f33e2782f0451590f7afcd97cd79fb to your computer and use it in GitHub Desktop.
Claude Code Statusline
@if (@CodeSection == @Batch) @then
@echo off
rem Claude Code status line (bullet-train inspired, single-line layout).
rem CMD/JScript hybrid port of the macOS bash version:
rem https://gist.github.com/hankbao/85f33e2782f0451590f7afcd97cd79fb
rem The batch header re-runs this same file through cscript's JScript
rem engine (built into Windows), which parses the statusLine JSON payload
rem from stdin and renders:
rem time | dir | session | cost | rate limits | context remaining | model | effort | thinking
rem The rate-limit group covers the 5h and 7d windows plus the per-model Fable
rem weekly window, which is read from disk rather than stdin (see section 5b).
rem
rem Managed by the "statusline-setup" agent. Ask Claude to make further
rem changes to this file rather than editing it by hand.
setlocal
rem Switch the console to UTF-8 so non-ASCII directory/session names in the
rem JSON payload decode correctly, then restore the original codepage.
set "_cp="
for /f "tokens=2 delims=:" %%a in ('chcp 2^>nul') do set "_cp=%%a"
chcp 65001 >nul 2>nul
cscript //nologo //e:jscript "%~f0"
if defined _cp chcp %_cp% >nul 2>nul
endlocal
exit /b 0
@end
// ---- JScript section (executed by cscript, skipped by cmd) ----
// ---- Helpers ----
// WSH JScript has no JSON.parse, so a guarded eval stands in for it. Both
// inputs are local files written by Claude Code itself; well-formed JSON is
// always a safe JS expression, and anything else throws into the catch.
function parseJson(text) {
if (!text) return null;
if (text.charAt(0) === String.fromCharCode(65279)) text = text.substring(1); // UTF-8 BOM
if (text.replace(/^\s+/, '').charAt(0) !== '{') return null;
try { return eval('(' + text + ')'); } catch (e) { return null; }
}
// Null-safe nested lookup, so every segment below can degrade gracefully.
function getFrom(obj, path) {
var cur = obj;
var parts = path.split('.');
for (var i = 0; i < parts.length; i++) {
if (cur === null || cur === undefined || typeof cur !== 'object') return null;
cur = cur[parts[i]];
}
return (cur === undefined) ? null : cur;
}
// Read a UTF-8 file. ADODB.Stream is the only WSH reader that understands
// UTF-8; FileSystemObject would decode via the ANSI codepage, where a CJK
// byte pair can swallow a following backslash and corrupt the JSON.
function readUtf8File(path) {
try {
var st = new ActiveXObject('ADODB.Stream');
st.Type = 2; // adTypeText
st.Charset = 'utf-8';
st.Open();
st.LoadFromFile(path);
var txt = st.ReadText();
st.Close();
return txt;
} catch (e) {
try {
var fso = new ActiveXObject('Scripting.FileSystemObject');
var f = fso.OpenTextFile(path, 1);
var t = f.ReadAll();
f.Close();
return t;
} catch (e2) { return null; }
}
}
// JScript's Date.parse predates ISO 8601, so parse resets_at by hand.
// Returns whole epoch seconds, or null when the timestamp is unrecognisable.
function parseIso8601Sec(s) {
if (!s) return null;
var m = /^(\d{4})-(\d{2})-(\d{2})[T ](\d{2}):(\d{2}):(\d{2})(?:\.\d+)?(Z|[+-]\d{2}:?\d{2})?$/
.exec(String(s));
if (!m) return null;
var ms = Date.UTC(+m[1], +m[2] - 1, +m[3], +m[4], +m[5], +m[6], 0);
var tz = m[7];
if (tz && tz !== 'Z') {
// Unlike jq's fromdateiso8601 this reads the offset, so a non-UTC offset
// yields a correct countdown instead of none.
var sign = (tz.charAt(0) === '-') ? -1 : 1;
var body = tz.substring(1).replace(':', '');
var mins = parseInt(body.substring(0, 2), 10) * 60 + parseInt(body.substring(2, 4), 10);
ms -= sign * mins * 60000;
}
return Math.floor(ms / 1000);
}
// Coarse single-unit countdown: days, else hours, else minutes.
function countdown(s) {
if (s >= 86400) return Math.floor(s / 86400) + 'd';
if (s >= 3600) return Math.floor(s / 3600) + 'h';
return Math.floor(s / 60) + 'm';
}
function isNum(v) { return v !== null && v !== '' && !isNaN(v); }
function pad2(n) { return (n < 10 ? '0' : '') + n; }
// ---- Payload from stdin ----
var raw = '';
try { raw = WScript.StdIn.ReadAll(); } catch (e) {}
var data = parseJson(raw);
function get(path) { return getFrom(data, path); }
// ---- Dim ANSI palette (the status line is rendered dimmed by the terminal) ----
var RESET = '\x1b[0m';
var DIM = '\x1b[2m';
var C_TIME = '\x1b[2;37m'; // dim white
var C_DIR = '\x1b[2;34m'; // dim blue
var C_SESSION = '\x1b[2;35m'; // dim magenta
var C_MODEL = '\x1b[2;32m'; // dim green
var C_COST = '\x1b[2;33m'; // dim yellow
var C_LIMITS = '\x1b[2;31m'; // dim red
var C_EFFORT = '\x1b[2;36m'; // dim cyan
var C_THINK = '\x1b[2;36m'; // dim cyan
var C_CTX = '\x1b[2;32m'; // dim green
var SEP = DIM + ' | ' + RESET;
var segments = [];
// ---- 1. Time (wall clock, not from the JSON payload) ----
var now = new Date();
segments.push(C_TIME + pad2(now.getHours()) + ':' + pad2(now.getMinutes()) + RESET);
// ---- 2. Directory (basename of the current working directory) ----
var dirPath = get('workspace.current_dir');
if (!dirPath) dirPath = get('cwd');
if (dirPath) {
var parts = String(dirPath).replace(/[\\\/]+$/, '').split(/[\\\/]/);
var dirName = parts[parts.length - 1];
if (!dirName) dirName = String(dirPath);
segments.push(C_DIR + dirName + RESET);
}
// ---- 3. Session (session_name, falling back to a short session_id prefix) ----
var sessionLabel = get('session_name');
if (!sessionLabel) {
var sid = get('session_id');
if (sid) sessionLabel = String(sid).substring(0, 8);
}
if (sessionLabel) segments.push(C_SESSION + sessionLabel + RESET);
// ---- 4. Session cost (USD) ----
var cost = get('cost.total_cost_usd');
if (isNum(cost)) segments.push(C_COST + '$' + Number(cost).toFixed(2) + RESET);
// ---- 5. Rate limits (Claude.ai subscription usage, 5h / 7d windows) ----
var limitsParts = [];
var fiveHour = get('rate_limits.five_hour.used_percentage');
if (isNum(fiveHour)) limitsParts.push('5h ' + Math.round(fiveHour) + '%');
var sevenDay = get('rate_limits.seven_day.used_percentage');
if (isNum(sevenDay)) limitsParts.push('7d ' + Math.round(sevenDay) + '%');
// ---- 5b. Fable weekly window (absent from the status line payload) ----
// Claude Code only ever emits five_hour / seven_day in .rate_limits, so the
// per-model weekly bucket -- internally "weekly_scoped", labelled "Fable limit"
// -- cannot be reached from stdin at all. It is however persisted verbatim in
// ~/.claude.json under .cachedUsageUtilization, which is the cached
// /api/oauth/usage response that opening /usage refreshes (at most every 5min).
// Strictly read-only: Claude Code rewrites that file constantly.
//
// A "~" prefix marks a reading older than Claude Code's own 1h cache TTL, so a
// stale percentage is never passed off as current -- the countdown stays exact.
var cached = null;
try {
var homeDir = new ActiveXObject('WScript.Shell').ExpandEnvironmentStrings('%USERPROFILE%');
if (homeDir && homeDir.charAt(0) !== '%') {
cached = getFrom(parseJson(readUtf8File(homeDir + '\\.claude.json')),
'cachedUsageUtilization');
}
} catch (e) { cached = null; }
if (cached) {
var limits = getFrom(cached, 'utilization.limits');
var fable = null;
if (limits && typeof limits.length === 'number') {
for (var i = 0; i < limits.length; i++) {
var dn = getFrom(limits[i], 'scope.model.display_name');
if (limits[i] && limits[i].kind === 'weekly_scoped' &&
String(dn === null ? '' : dn).toLowerCase() === 'fable') {
fable = limits[i];
break;
}
}
}
if (fable && typeof fable.percent === 'number') {
var nowMs = now.getTime();
var stale = (typeof cached.fetchedAtMs === 'number' &&
(nowMs - cached.fetchedAtMs) > 3600000) ? '~' : '';
var cdown = '';
var resetSec = parseIso8601Sec(fable.resets_at);
if (resetSec !== null) {
var d = Math.floor(resetSec - nowMs / 1000);
if (d > 0) cdown = ' ' + countdown(d);
}
limitsParts.push('fable ' + stale + Math.floor(fable.percent) + '%' + cdown);
}
}
if (limitsParts.length) segments.push(C_LIMITS + limitsParts.join(' | ') + RESET);
// ---- 6. Context window remaining ----
var ctxRemaining = get('context_window.remaining_percentage');
if (isNum(ctxRemaining)) segments.push(C_CTX + 'ctx ' + Math.round(ctxRemaining) + '%' + RESET);
// ---- 7. Model display name ----
var modelName = get('model.display_name');
if (modelName) segments.push(C_MODEL + modelName + RESET);
// ---- 8. Effort level ----
var effort = get('effort.level');
if (effort) segments.push(C_EFFORT + 'eff:' + effort + RESET);
// ---- 9. Thinking on/off ----
var thinking = get('thinking.enabled');
if (thinking === true) segments.push(C_THINK + 'think:on' + RESET);
else if (thinking === false) segments.push(C_THINK + 'think:off' + RESET);
var line = segments.join(SEP);
if (line) WScript.StdOut.Write(line + '\n');
# Claude Code status line (bullet-train inspired, single-line layout).
# Windows/PowerShell port of the macOS bash version:
# https://gist.github.com/hankbao/85f33e2782f0451590f7afcd97cd79fb
# Reads the statusLine JSON payload from stdin and renders:
# time | dir | session | cost | rate limits | context remaining | model | effort | thinking
# The rate-limit group covers the 5h and 7d windows plus the per-model Fable
# weekly window, which is read from disk rather than stdin (see section 5b).
#
# Managed by the "statusline-setup" agent. Ask Claude to make further
# changes to this file rather than editing it by hand.
$ErrorActionPreference = 'SilentlyContinue'
# Read stdin as UTF-8 regardless of the console codepage, so non-ASCII
# directory/session names survive the round trip.
$reader = New-Object IO.StreamReader([Console]::OpenStandardInput(), [Text.Encoding]::UTF8)
$raw = $reader.ReadToEnd()
$json = $null
try { $json = $raw | ConvertFrom-Json } catch {}
# ---- Dim ANSI palette (the status line is rendered dimmed by the terminal) ----
$E = [char]27
$RESET = "$E[0m"
$DIM = "$E[2m"
$C_TIME = "$E[2;37m" # dim white
$C_DIR = "$E[2;34m" # dim blue
$C_SESSION = "$E[2;35m" # dim magenta
$C_MODEL = "$E[2;32m" # dim green
$C_COST = "$E[2;33m" # dim yellow
$C_LIMITS = "$E[2;31m" # dim red
$C_EFFORT = "$E[2;36m" # dim cyan
$C_THINK = "$E[2;36m" # dim cyan
$C_CTX = "$E[2;32m" # dim green
$SEP = "${DIM} | ${RESET}"
$inv = [Globalization.CultureInfo]::InvariantCulture
# ConvertFrom-Json hands back Int32/Int64/Double/Decimal for JSON numbers;
# this stands in for jq's `type == "number"` guard.
function Test-JsonNumber($v) {
return ($v -is [int] -or $v -is [long] -or $v -is [double] -or
$v -is [decimal] -or $v -is [single] -or $v -is [short] -or $v -is [byte])
}
# Coarse single-unit countdown: days, else hours, else minutes.
function Format-Countdown($seconds) {
if ($seconds -ge 86400) { return "$([Math]::Floor($seconds / 86400))d" }
if ($seconds -ge 3600) { return "$([Math]::Floor($seconds / 3600))h" }
return "$([Math]::Floor($seconds / 60))m"
}
$segments = @()
# ---- 1. Time (wall clock, not from the JSON payload) ----
$segments += "${C_TIME}$(Get-Date -Format HH:mm)${RESET}"
# ---- 2. Directory (basename of the current working directory) ----
$dirPath = $json.workspace.current_dir
if (-not $dirPath) { $dirPath = $json.cwd }
if ($dirPath) {
$dirName = ("$dirPath" -replace '[\\/]+$', '' -split '[\\/]')[-1]
if (-not $dirName) { $dirName = "$dirPath" }
$segments += "${C_DIR}${dirName}${RESET}"
}
# ---- 3. Session (session_name, falling back to a short session_id prefix) ----
$sessionLabel = $json.session_name
if (-not $sessionLabel) {
$sid = $json.session_id
if ($sid) { $sessionLabel = "$sid".Substring(0, [Math]::Min(8, "$sid".Length)) }
}
if ($sessionLabel) { $segments += "${C_SESSION}${sessionLabel}${RESET}" }
# ---- 4. Session cost (USD) ----
$cost = $json.cost.total_cost_usd
if ($null -ne $cost) {
$segments += "${C_COST}`$$(([double]$cost).ToString('0.00', $inv))${RESET}"
}
# ---- 5. Rate limits (Claude.ai subscription usage, 5h / 7d windows) ----
$limitsParts = @()
$fiveHour = $json.rate_limits.five_hour.used_percentage
if ($null -ne $fiveHour) { $limitsParts += "5h $([Math]::Round([double]$fiveHour))%" }
$sevenDay = $json.rate_limits.seven_day.used_percentage
if ($null -ne $sevenDay) { $limitsParts += "7d $([Math]::Round([double]$sevenDay))%" }
# ---- 5b. Fable weekly window (absent from the status line payload) ----
# Claude Code only ever emits five_hour / seven_day in .rate_limits, so the
# per-model weekly bucket -- internally "weekly_scoped", labelled "Fable limit"
# -- cannot be reached from stdin at all. It is however persisted verbatim in
# ~/.claude.json under .cachedUsageUtilization, which is the cached
# /api/oauth/usage response that opening /usage refreshes (at most every 5min).
# Strictly read-only: Claude Code rewrites that file constantly, hence the
# shared-write handle below (an exclusive read would fail intermittently).
#
# A "~" prefix marks a reading older than Claude Code's own 1h cache TTL, so a
# stale percentage is never passed off as current -- the countdown stays exact.
$cached = $null
$homeDir = $HOME
if (-not $homeDir) { $homeDir = $env:USERPROFILE }
$claudeJson = Join-Path $homeDir '.claude.json'
if ($homeDir -and (Test-Path -LiteralPath $claudeJson)) {
try {
$fs = [IO.File]::Open($claudeJson, [IO.FileMode]::Open, [IO.FileAccess]::Read,
[IO.FileShare]::ReadWrite)
try {
$cachedRaw = (New-Object IO.StreamReader($fs, [Text.Encoding]::UTF8)).ReadToEnd()
} finally { $fs.Dispose() }
$cached = ($cachedRaw | ConvertFrom-Json).cachedUsageUtilization
} catch { $cached = $null }
}
if ($cached) {
$fable = $null
foreach ($lim in @($cached.utilization.limits)) {
if ($lim.kind -eq 'weekly_scoped' -and
"$($lim.scope.model.display_name)".ToLowerInvariant() -eq 'fable') {
$fable = $lim
break
}
}
if ($fable -and (Test-JsonNumber $fable.percent)) {
$nowMs = [DateTimeOffset]::UtcNow.ToUnixTimeMilliseconds()
$stale = ''
if ((Test-JsonNumber $cached.fetchedAtMs) -and
($nowMs - [double]$cached.fetchedAtMs) -gt 3600000) { $stale = '~' }
# resets_at looks like 2026-09-06T21:00:00.512+00:00. Unlike jq's
# fromdateiso8601 the .NET parser reads the offset directly, so a non-UTC
# offset yields a correct countdown instead of none.
# NB: not $reset -- PowerShell variable names are case-insensitive, so that
# would silently clobber the $RESET ANSI escape used by every segment.
$cdown = ''
$resetAt = [DateTimeOffset]::MinValue
if ($fable.resets_at -and [DateTimeOffset]::TryParse(
"$($fable.resets_at)", $inv,
[Globalization.DateTimeStyles]::AssumeUniversal -bor
[Globalization.DateTimeStyles]::AdjustToUniversal,
[ref]$resetAt)) {
$d = [Math]::Floor($resetAt.ToUnixTimeSeconds() - ($nowMs / 1000))
if ($d -gt 0) { $cdown = ' ' + (Format-Countdown $d) }
}
$limitsParts += "fable ${stale}$([Math]::Floor([double]$fable.percent))%${cdown}"
}
}
if ($limitsParts.Count -gt 0) {
$segments += "${C_LIMITS}$($limitsParts -join ' | ')${RESET}"
}
# ---- 6. Context window remaining ----
$ctxRemaining = $json.context_window.remaining_percentage
if ($null -ne $ctxRemaining) {
$segments += "${C_CTX}ctx $([Math]::Round([double]$ctxRemaining))%${RESET}"
}
# ---- 7. Model display name ----
$modelName = $json.model.display_name
if ($modelName) { $segments += "${C_MODEL}${modelName}${RESET}" }
# ---- 8. Effort level ----
$effort = $json.effort.level
if ($effort) { $segments += "${C_EFFORT}eff:${effort}${RESET}" }
# ---- 9. Thinking on/off ----
$thinking = $json.thinking.enabled
if ($thinking -eq $true) { $segments += "${C_THINK}think:on${RESET}" }
elseif ($thinking -eq $false) { $segments += "${C_THINK}think:off${RESET}" }
$line = $segments -join $SEP
if ($line) {
# Write raw UTF-8 bytes so the output encoding never mangles the line.
$bytes = [Text.Encoding]::UTF8.GetBytes($line + "`n")
$stdout = [Console]::OpenStandardOutput()
$stdout.Write($bytes, 0, $bytes.Length)
$stdout.Flush()
}
#!/bin/bash
#
# Claude Code status line (bullet-train inspired, single-line layout).
# Reads the statusLine JSON payload from stdin and renders:
# time | dir | session | cost | rate limits | context remaining | model | effort | thinking
# The rate-limit group covers the 5h and 7d windows plus the per-model Fable
# weekly window, which is read from disk rather than stdin (see section 6b).
#
# Managed by the "statusline-setup" agent. Ask Claude to make further
# changes to this file rather than editing it by hand.
input=$(cat)
# jq helper: returns an empty string (never the literal "null") when a
# field is missing or null, so every segment below can degrade gracefully.
jget() {
printf '%s' "$input" | jq -r "$1 // empty" 2>/dev/null
}
# ---- Dim ANSI palette (the status line is rendered dimmed by the terminal) ----
RESET=$'\033[0m'
DIM=$'\033[2m'
C_TIME=$'\033[2;37m' # dim white
C_DIR=$'\033[2;34m' # dim blue
C_SESSION=$'\033[2;35m' # dim magenta
C_MODEL=$'\033[2;32m' # dim green
C_COST=$'\033[2;33m' # dim yellow
C_LIMITS=$'\033[2;31m' # dim red
C_EFFORT=$'\033[2;36m' # dim cyan
C_THINK=$'\033[2;36m' # dim cyan
C_CTX=$'\033[2;32m' # dim green
SEP="${DIM} | ${RESET}"
# Join all non-empty segments with a dim separator.
join() {
local out=""
for seg in "$@"; do
[ -z "$seg" ] && continue
if [ -z "$out" ]; then
out="$seg"
else
out="${out}${SEP}${seg}"
fi
done
printf '%s' "$out"
}
# ---- 1. Time (wall clock, not from the JSON payload) ----
seg_time="${C_TIME}$(date +%H:%M)${RESET}"
# ---- 2. Directory (basename of the current working directory) ----
dir_path=$(jget '.workspace.current_dir')
[ -z "$dir_path" ] && dir_path=$(jget '.cwd')
seg_dir=""
[ -n "$dir_path" ] && seg_dir="${C_DIR}$(basename "$dir_path")${RESET}"
# ---- 3. Session (session_name, falling back to a short session_id prefix) ----
session_label=$(jget '.session_name')
if [ -z "$session_label" ]; then
sid=$(jget '.session_id')
[ -n "$sid" ] && session_label="${sid:0:8}"
fi
seg_session=""
[ -n "$session_label" ] && seg_session="${C_SESSION}${session_label}${RESET}"
# ---- 4. Model display name ----
model_name=$(jget '.model.display_name')
seg_model=""
[ -n "$model_name" ] && seg_model="${C_MODEL}${model_name}${RESET}"
# ---- 5. Session cost (USD) ----
cost=$(jget '.cost.total_cost_usd')
seg_cost=""
if [ -n "$cost" ]; then
cost_fmt=$(printf '$%.2f' "$cost" 2>/dev/null)
[ -n "$cost_fmt" ] && seg_cost="${C_COST}${cost_fmt}${RESET}"
fi
# ---- 6. Rate limits (Claude.ai subscription usage, 5h / 7d windows) ----
five_hour=$(jget '.rate_limits.five_hour.used_percentage')
seven_day=$(jget '.rate_limits.seven_day.used_percentage')
limits_str=""
if [ -n "$five_hour" ]; then
five_fmt=$(printf '%.0f' "$five_hour" 2>/dev/null)
[ -n "$five_fmt" ] && limits_str="5h ${five_fmt}%"
fi
if [ -n "$seven_day" ]; then
seven_fmt=$(printf '%.0f' "$seven_day" 2>/dev/null)
if [ -n "$seven_fmt" ]; then
if [ -n "$limits_str" ]; then
limits_str="${limits_str} | 7d ${seven_fmt}%"
else
limits_str="7d ${seven_fmt}%"
fi
fi
fi
# ---- 6b. Fable weekly window (absent from the status line payload) ----
# Claude Code only ever emits five_hour / seven_day in .rate_limits, so the
# per-model weekly bucket -- internally "seven_day_overage_included", labelled
# "Fable limit" -- cannot be reached from stdin at all. It is however persisted
# verbatim in ~/.claude.json under .cachedUsageUtilization, which is the cached
# /api/oauth/usage response that opening /usage refreshes (at most every 5min).
# Strictly read-only: Claude Code rewrites that file constantly.
#
# percent is already 0-100. resets_at looks like 2026-09-06T21:00:00.512+00:00,
# which fromdateiso8601 rejects, hence the two subs; a non-UTC offset would fall
# through to "no countdown" rather than render a wrong one. A "~" prefix marks a
# reading older than Claude Code's own 1h cache TTL, so a stale percentage is
# never passed off as current -- the countdown stays exact either way.
fable_str=""
if [ -r "$HOME/.claude.json" ]; then
fable_str=$(jq -r '
def cd($s):
if $s >= 86400 then "\($s/86400|floor)d"
elif $s >= 3600 then "\($s/3600|floor)h"
else "\($s/60|floor)m" end;
(.cachedUsageUtilization // {}) as $c
| ($c.utilization.limits // []
| map(select(.kind == "weekly_scoped"
and ((.scope.model.display_name // "") | ascii_downcase) == "fable"))
| first) as $f
| if $f == null or ($f.percent | type) != "number" then empty
else
(if ($c.fetchedAtMs | type) == "number" and (now * 1000 - $c.fetchedAtMs) > 3600000
then "~" else "" end) as $stale
| (try ($f.resets_at | sub("\\.[0-9]+"; "") | sub("\\+00:00$"; "Z") | fromdateiso8601)
catch null) as $reset
| (if $reset == null then ""
else (($reset - now) | floor) as $d
| if $d > 0 then " " + cd($d) else "" end
end) as $cdown
| "fable \($stale)\($f.percent | floor)%\($cdown)"
end' "$HOME/.claude.json" 2>/dev/null)
fi
if [ -n "$fable_str" ]; then
if [ -n "$limits_str" ]; then
limits_str="${limits_str} | ${fable_str}"
else
limits_str="$fable_str"
fi
fi
seg_limits=""
[ -n "$limits_str" ] && seg_limits="${C_LIMITS}${limits_str}${RESET}"
# ---- 7. Effort level ----
effort=$(jget '.effort.level')
seg_effort=""
[ -n "$effort" ] && seg_effort="${C_EFFORT}eff:${effort}${RESET}"
# ---- 8. Thinking on/off ----
thinking=$(jget '.thinking.enabled')
seg_think=""
if [ "$thinking" = "true" ]; then
seg_think="${C_THINK}think:on${RESET}"
elif [ "$thinking" = "false" ]; then
seg_think="${C_THINK}think:off${RESET}"
fi
# ---- 9. Context window remaining ----
ctx_remaining=$(jget '.context_window.remaining_percentage')
seg_ctx=""
if [ -n "$ctx_remaining" ]; then
ctx_fmt=$(printf '%.0f' "$ctx_remaining" 2>/dev/null)
[ -n "$ctx_fmt" ] && seg_ctx="${C_CTX}ctx ${ctx_fmt}%${RESET}"
fi
line=$(join "$seg_time" "$seg_dir" "$seg_session" "$seg_cost" "$seg_limits" "$seg_ctx" "$seg_model" "$seg_effort" "$seg_think")
[ -n "$line" ] && printf '%s\n' "$line"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment