Compare commits

..
Author SHA1 Message Date
Jonny Barnes
417542807b
Measure line one's branch budget instead of assuming 56 columns
In wrap mode the branch is truncated to whatever the rest of line one leaves,
and "the rest" was a flat 56 columns. It is not flat: the token counts and the
cost figure vary with the session, and with a four-digit cost the real width is
57, so line one came out exactly one column over and the renderer clipped it.
Reachable at COLUMNS 73-81 with padding 2 — a narrow split pane.

Add up the segments that follow the branch instead, from the same values that
render them, and count the ahead/behind markers too, which the constant also
ignored. That needs the cost formatted and the token bar width chosen before
the git segment is built, so both move up; neither depends on anything in
between.

The budget is now exact rather than approximate, so the branch also gets the
columns the old constant was over-reserving when the cost was short.

Tests: the large-cost sweep only used the short branch 'main', where the
overflow cannot show. Repeat it with the long branch, which fails on the old
budget at usable 68, 69, 70 and 75 by exactly one column.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-31 19:01:24 +01:00
Jonny Barnes
c88a291047
Wrap the status line rather than drop the per-model limit
The measured ladder tried every single-line variant before considering a
second line, so whenever line one plus the full group did not fit — from
around 92 usable columns upwards, depending on how long the branch, cwd and
model names are — it emitted rl_bare (5h and 7d only) and silently dropped
the per-model (Fable) bar that is the main reason the group is worth
rendering. On a 110-column laptop the limit was invisible.

Reorder so a second line beats losing that bar: one line rich/mid/lean, then
wrap, and rl_bare only when WRAP_NARROW is false. Reset times and
extra-usage credits are still given up rather than wrapped for, which makes
the rendered content non-monotone in width; the comment on the ladder spells
that out.

Also stop the tests writing fixtures to the caches the live status line
reads. ~/.claude/statusline.sh is a symlink to the script, so a test run was
visibly rendering fabricated usage — a Fable bar at 10%, extra-usage credits
of $1234.56/$2000.00 — in whatever session happened to be open, and a run
killed before its trap fired would have left that in place for an hour. The
cache directory is now $STATUSLINE_CACHE_DIR (default /tmp/claude) and each
suite points it at a temporary directory, which also removes the
backup/restore dance and the deletion of the live git-status cache.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-31 18:54:02 +01:00
5 changed files with 85 additions and 55 deletions

View file

@ -18,6 +18,12 @@ CWD_MAX_LEN=20 # truncate the cwd basename longer than this
GIT_CACHE_SECS=10 # seconds to cache git status (git diff is slow on large repos) GIT_CACHE_SECS=10 # seconds to cache git status (git diff is slow on large repos)
TOKEN_BAR_WIDTH=8 # width of token progress bar TOKEN_BAR_WIDTH=8 # width of token progress bar
# Where the git and usage-API caches live. Overridable via the environment so a
# test run can render into its own directory: ~/.claude/statusline.sh is a
# symlink to this script, so a fixture written to the live cache is picked up by
# the next redraw and shown as real usage for up to an hour.
CACHE_DIR="${STATUSLINE_CACHE_DIR:-/tmp/claude}"
# Terminal width detection. # Terminal width detection.
# Claude Code exports COLUMNS for this subprocess. There is no controlling # Claude Code exports COLUMNS for this subprocess. There is no controlling
# terminal, so the stty fallback fails; when both fail we default to 80 # terminal, so the stty fallback fails; when both fail we default to 80
@ -77,7 +83,7 @@ esac
input=$(cat) input=$(cat)
[ -z "$input" ] && printf "Claude" && exit 0 [ -z "$input" ] && printf "Claude" && exit 0
mkdir -p /tmp/claude mkdir -p "$CACHE_DIR"
# ===== Colors ===== # ===== Colors =====
# Palette indices rather than RGB, so these resolve through the terminal's own # Palette indices rather than RGB, so these resolve through the terminal's own
@ -173,7 +179,7 @@ get_git_info() {
# Stable cache key per directory path # Stable cache key per directory path
local dir_hash local dir_hash
dir_hash=$(printf '%s' "$dir" | cksum | awk '{print $1}') dir_hash=$(printf '%s' "$dir" | cksum | awk '{print $1}')
local cache_file="/tmp/claude/git-${dir_hash}" local cache_file="$CACHE_DIR/git-${dir_hash}"
local needs_refresh=true local needs_refresh=true
if [ -f "$cache_file" ]; then if [ -f "$cache_file" ]; then
@ -296,6 +302,10 @@ format_reset_time() {
model_name=$(echo "$input" | jq -r '.model.display_name // "Claude"') model_name=$(echo "$input" | jq -r '.model.display_name // "Claude"')
cwd=$(echo "$input" | jq -r '.cwd // empty') cwd=$(echo "$input" | jq -r '.cwd // empty')
cost_usd=$(echo "$input" | jq -r '.cost.total_cost_usd // empty') cost_usd=$(echo "$input" | jq -r '.cost.total_cost_usd // empty')
# Formatted here rather than where it is rendered: the wrap-mode branch budget
# needs its width, and it is not fixed ("$4.61" vs "$1234.56").
cost_fmt=""
[ -n "$cost_usd" ] && cost_fmt=$(printf '%.2f' "$cost_usd" 2>/dev/null)
size=$(echo "$input" | jq -r '.context_window.context_window_size // 200000') size=$(echo "$input" | jq -r '.context_window.context_window_size // 200000')
[ "$size" -eq 0 ] 2>/dev/null && size=200000 [ "$size" -eq 0 ] 2>/dev/null && size=200000
@ -389,6 +399,11 @@ if [ "$width_tier" = "full" ] && [ -n "$cwd" ]; then
out+="${sep}${dim}$(esc_data "$display_dir")${reset}" out+="${sep}${dim}$(esc_data "$display_dir")${reset}"
fi fi
bar_w="$TOKEN_BAR_WIDTH"
[ "$width_tier" = "wide" ] && bar_w=6
[ "$width_tier" = "split" ] && bar_w=5
[ "$width_tier" = "narrow" ] && bar_w=4
# Git branch + dirty + ahead/behind # Git branch + dirty + ahead/behind
if $SHOW_GIT && [ -n "$cwd" ]; then if $SHOW_GIT && [ -n "$cwd" ]; then
git_info=$(get_git_info "$cwd") git_info=$(get_git_info "$cwd")
@ -400,11 +415,21 @@ if $SHOW_GIT && [ -n "$cwd" ]; then
[ "$width_tier" = "wide" ] && local_max=24 [ "$width_tier" = "wide" ] && local_max=24
[ "$width_tier" = "split" ] && local_max=18 [ "$width_tier" = "split" ] && local_max=18
[ "$width_tier" = "narrow" ] && local_max=12 [ "$width_tier" = "narrow" ] && local_max=12
# In wrap mode line one is model+branch+tokens+thinking+cost; with the # In wrap mode line one is
# shortened model name everything but the branch is ~56 cols, so give # model │ ⎇ branch ✔ ↑1 │ <bar> used/total pct% │ ◇ thinking │ $cost
# the branch whatever is left. # and the branch gets whatever the rest of it leaves. Everything after
# the branch is appended below, so its width is added up here rather
# than assumed: a flat 56 columns was three short of a four-digit cost,
# which pushed line one past the edge to be clipped by the renderer.
if $wrap_mode; then if $wrap_mode; then
local_max=$(( USABLE_WIDTH - 56 )) tail_len=$(( 5 + 2 )) # "│ ⎇ " and the dirty mark
[ "${g_ahead:-0}" -gt 0 ] && tail_len=$(( tail_len + 2 + ${#g_ahead} ))
[ "${g_behind:-0}" -gt 0 ] && tail_len=$(( tail_len + 2 + ${#g_behind} ))
$SHOW_TOKENS && tail_len=$(( tail_len + 3 + bar_w + 1 \
+ ${#used_tokens} + 1 + ${#total_tokens} + 1 + ${#pct_used} + 1 ))
$SHOW_THINKING && tail_len=$(( tail_len + 3 + 10 ))
[ -n "$cost_fmt" ] && tail_len=$(( tail_len + 3 + 1 + ${#cost_fmt} ))
local_max=$(( USABLE_WIDTH - $(vis_len "$out") - tail_len ))
[ "$local_max" -lt 8 ] && local_max=8 [ "$local_max" -lt 8 ] && local_max=8
[ "$local_max" -gt 24 ] && local_max=24 [ "$local_max" -gt 24 ] && local_max=24
fi fi
@ -428,10 +453,6 @@ fi
# Token bar # Token bar
if $SHOW_TOKENS; then if $SHOW_TOKENS; then
bar_w="$TOKEN_BAR_WIDTH"
[ "$width_tier" = "wide" ] && bar_w=6
[ "$width_tier" = "split" ] && bar_w=5
[ "$width_tier" = "narrow" ] && bar_w=4
token_bar=$(build_bar "$pct_used" "$bar_w") token_bar=$(build_bar "$pct_used" "$bar_w")
out+="${sep}${token_bar} ${orange}${used_tokens}${dim}/${reset}${white}${total_tokens}${reset} ${dim}${pct_used}%${reset}" out+="${sep}${token_bar} ${orange}${used_tokens}${dim}/${reset}${white}${total_tokens}${reset} ${dim}${pct_used}%${reset}"
fi fi
@ -452,16 +473,15 @@ if $SHOW_THINKING && [ "$width_tier" != "narrow" ]; then
fi fi
# Session cost — wide/full only # Session cost — wide/full only
if [ -n "$cost_usd" ] && [ "$width_tier" = "wide" -o "$width_tier" = "full" ]; then if [ -n "$cost_fmt" ] && [ "$width_tier" = "wide" -o "$width_tier" = "full" ]; then
cost_fmt=$(printf '%.2f' "$cost_usd" 2>/dev/null) out+="${sep}${dim}\$${cost_fmt}${reset}"
[ -n "$cost_fmt" ] && out+="${sep}${dim}\$${cost_fmt}${reset}"
fi fi
# ===== Rate limits (API, cached 60s) ===== # ===== Rate limits (API, cached 60s) =====
# Built at every tier except narrow. Which variant is actually emitted, # Built at every tier except narrow. Which variant is actually emitted,
# and on how many lines, is decided by the measured ladder at the end. # and on how many lines, is decided by the measured ladder at the end.
if $SHOW_RATE_LIMITS && [ "$width_tier" != "narrow" ]; then if $SHOW_RATE_LIMITS && [ "$width_tier" != "narrow" ]; then
api_cache="/tmp/claude/statusline-usage-cache.json" api_cache="$CACHE_DIR/statusline-usage-cache.json"
api_cache_max=3600 # 1 hour — rate limit data changes slowly api_cache_max=3600 # 1 hour — rate limit data changes slowly
needs_refresh=true needs_refresh=true
usage_data="" usage_data=""
@ -602,19 +622,26 @@ EOF
fi fi
fi fi
# Attach the rate-limit group, emitting the richest layout that fits: # Attach the rate-limit group, emitting the richest layout that fits.
# 1-3. one line: with resets / with extra usage / bars only #
# A second line is worth it for the per-model bar and nothing else: rl_bare is
# the only variant that drops that bar, and it is the whole reason the group is
# worth showing on an account that has one. Reset times and extra-usage credits
# are given up instead of wrapped for, so content is NOT monotone in width -- at
# one column narrower, rung 3 stops fitting and the wrap that follows has room
# for the timestamps as well. Only wrapping being switched off makes rl_bare the
# answer.
# 1-3. one line: with resets / with extra usage / bars + per-model
# 4-6. two lines: same order, line two having room the single line lacked # 4-6. two lines: same order, line two having room the single line lacked
# 7. one line, bars only -- WRAP_NARROW=false, the least-bad single line
# Every branch is fit-checked, so no layout is chosen that would be clipped by # Every branch is fit-checked, so no layout is chosen that would be clipped by
# the renderer, whatever the branch name, cwd, model name or extra-usage width. # the renderer, whatever the branch name, cwd, model name or extra-usage width.
if [ -n "$rl_bare" ]; then if [ -n "$rl_bare" ]; then
rich_len=$(vis_len "$rl_rich") rich_len=$(vis_len "$rl_rich")
lean_len=$(vis_len "$rl_lean") lean_len=$(vis_len "$rl_lean")
bare_len=$(vis_len "$rl_bare")
if [ $(( line1_len + 3 + rich_len )) -le "$USABLE_WIDTH" ]; then out+="${sep}${rl_rich}" if [ $(( line1_len + 3 + rich_len )) -le "$USABLE_WIDTH" ]; then out+="${sep}${rl_rich}"
elif [ $(( line1_len + 3 + mid_len )) -le "$USABLE_WIDTH" ]; then out+="${sep}${rl_mid}" elif [ $(( line1_len + 3 + mid_len )) -le "$USABLE_WIDTH" ]; then out+="${sep}${rl_mid}"
elif [ $(( line1_len + 3 + lean_len )) -le "$USABLE_WIDTH" ]; then out+="${sep}${rl_lean}" elif [ $(( line1_len + 3 + lean_len )) -le "$USABLE_WIDTH" ]; then out+="${sep}${rl_lean}"
elif [ $(( line1_len + 3 + bare_len )) -le "$USABLE_WIDTH" ]; then out+="${sep}${rl_bare}"
elif $WRAP_NARROW; then elif $WRAP_NARROW; then
if [ "$rich_len" -le "$USABLE_WIDTH" ]; then out+=$'\n'"$rl_rich" if [ "$rich_len" -le "$USABLE_WIDTH" ]; then out+=$'\n'"$rl_rich"
elif [ "$mid_len" -le "$USABLE_WIDTH" ]; then out+=$'\n'"$rl_mid" elif [ "$mid_len" -le "$USABLE_WIDTH" ]; then out+=$'\n'"$rl_mid"
@ -622,7 +649,6 @@ if [ -n "$rl_bare" ]; then
else out+=$'\n'"$rl_bare" else out+=$'\n'"$rl_bare"
fi fi
else else
# Wrapping disabled: the barest group is the least-bad single line.
out+="${sep}${rl_bare}" out+="${sep}${rl_bare}"
fi fi
fi fi

View file

@ -4,8 +4,9 @@
# Usage: claude/tests/run.sh # Usage: claude/tests/run.sh
# #
# The tests render the status line directly, so they need the same tools it # The tests render the status line directly, so they need the same tools it
# does (bash, jq, git, python3). They briefly replace the cached usage-API # does (bash, jq, git, python3). They point STATUSLINE_CACHE_DIR at a temporary
# response in /tmp/claude with fixtures and restore it on exit. # directory and write their fixtures there, so the caches the live status line
# reads are never touched.
cd "$(dirname "${BASH_SOURCE[0]}")" || exit 1 cd "$(dirname "${BASH_SOURCE[0]}")" || exit 1
status=0 status=0

View file

@ -31,7 +31,13 @@ EXTRA = {
"on": '"extra_usage":{"is_enabled":true,"monthly_limit":200000,' "on": '"extra_usage":{"is_enabled":true,"monthly_limit":200000,'
'"used_credits":123456,"utilization":62.0}', '"used_credits":123456,"utilization":62.0}',
} }
CACHE = "/tmp/claude/statusline-usage-cache.json" # Throwaway cache directory, inherited by every rendered subprocess: the live
# cache belongs to the running status line (~/.claude/statusline.sh symlinks to
# the script under test) and a fixture written there would be shown as real
# usage until it expired.
CACHE_DIR = tempfile.mkdtemp()
os.environ["STATUSLINE_CACHE_DIR"] = CACHE_DIR
CACHE = os.path.join(CACHE_DIR, "statusline-usage-cache.json")
FIXTURE = ('{"five_hour":{"utilization":5.0,"resets_at":"2026-08-27T15:40:00+00:00"},' FIXTURE = ('{"five_hour":{"utilization":5.0,"resets_at":"2026-08-27T15:40:00+00:00"},'
'"seven_day":{"utilization":7.0,"resets_at":"2026-08-28T02:00:00+00:00"},%s,' '"seven_day":{"utilization":7.0,"resets_at":"2026-08-28T02:00:00+00:00"},%s,'
'"limits":[{"kind":"weekly_scoped","percent":10,' '"limits":[{"kind":"weekly_scoped","percent":10,'
@ -46,12 +52,6 @@ def main():
["jq", "-r", ".statusLine.padding // 0", os.path.expanduser("~/.claude/settings.json")], ["jq", "-r", ".statusLine.padding // 0", os.path.expanduser("~/.claude/settings.json")],
capture_output=True, text=True).stdout.strip() or 0) capture_output=True, text=True).stdout.strip() or 0)
root = tempfile.mkdtemp() root = tempfile.mkdtemp()
# Back up the live usage cache. If there wasn't one, remove the fixture at
# the end rather than leaving it: the status line would treat it as a fresh
# response for up to an hour and report fabricated usage.
had_cache = os.path.exists(CACHE)
saved = open(CACHE, "rb").read() if had_cache else None
fails, checked = [], 0 fails, checked = [], 0
try: try:
for cwd_name, extra_name in itertools.product(CWDS, EXTRA): for cwd_name, extra_name in itertools.product(CWDS, EXTRA):
@ -64,9 +64,9 @@ def main():
"commit", "-q", "--allow-empty", "-m", "i"], check=True) "commit", "-q", "--allow-empty", "-m", "i"], check=True)
for br in BRANCHES: for br in BRANCHES:
subprocess.run(["git", "-C", repo, "checkout", "-q", "-B", br], capture_output=True) subprocess.run(["git", "-C", repo, "checkout", "-q", "-B", br], capture_output=True)
for f in os.listdir("/tmp/claude"): for f in os.listdir(CACHE_DIR):
if f.startswith("git-"): if f.startswith("git-"):
os.remove("/tmp/claude/" + f) os.remove(os.path.join(CACHE_DIR, f))
for model, cols in itertools.product(MODELS, WIDTHS): for model, cols in itertools.product(MODELS, WIDTHS):
stdin = ('{"model":{"display_name":"%s"},"cwd":"%s",' stdin = ('{"model":{"display_name":"%s"},"cwd":"%s",'
'"cost":{"total_cost_usd":8.31},"context_window":' '"cost":{"total_cost_usd":8.31},"context_window":'
@ -89,11 +89,7 @@ def main():
fails.append(tag + (f"max {mx} > usable {usable}",)) fails.append(tag + (f"max {mx} > usable {usable}",))
finally: finally:
shutil.rmtree(root, ignore_errors=True) shutil.rmtree(root, ignore_errors=True)
if saved is not None: shutil.rmtree(CACHE_DIR, ignore_errors=True)
with open(CACHE, "wb") as fh:
fh.write(saved)
elif os.path.exists(CACHE):
os.remove(CACHE)
print(f"checked {checked} combinations " print(f"checked {checked} combinations "
f"(widths {WIDTHS.start}-{WIDTHS.stop - 1}, {len(BRANCHES)} branches, " f"(widths {WIDTHS.start}-{WIDTHS.stop - 1}, {len(BRANCHES)} branches, "

View file

@ -9,18 +9,14 @@
# particular the per-model ("weekly_scoped") limit such as Fable, and how the # particular the per-model ("weekly_scoped") limit such as Fable, and how the
# renderer degrades when the group will not fit. # renderer degrades when the group will not fit.
SCRIPT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)/statusline.isaacaudet.sh" SCRIPT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)/statusline.isaacaudet.sh"
CACHE="/tmp/claude/statusline-usage-cache.json" # Render into a throwaway cache directory. The live cache must not be touched:
BACKUP="$(mktemp)" # ~/.claude/statusline.sh is a symlink to the script under test, so a fixture
# written there is shown in the running TUI as real usage until it expires.
export STATUSLINE_CACHE_DIR="$(mktemp -d)"
CACHE="$STATUSLINE_CACHE_DIR/statusline-usage-cache.json"
REPO="$(mktemp -d)" REPO="$(mktemp -d)"
[ -f "$CACHE" ] && cp "$CACHE" "$BACKUP"
# Restore the real cache. If there was no cache to begin with, REMOVE the cleanup() { rm -rf "$STATUSLINE_CACHE_DIR" "$REPO"; }
# fixture rather than leaving it: the status line treats a fixture as a fresh
# response for api_cache_max (1h) and would report fabricated usage.
cleanup() {
if [ -s "$BACKUP" ]; then cp "$BACKUP" "$CACHE"; else rm -f "$CACHE"; fi
rm -f "$BACKUP"; rm -rf "$REPO"
}
trap cleanup EXIT trap cleanup EXIT
git -C "$REPO" init -q 2>/dev/null git -C "$REPO" init -q 2>/dev/null
@ -80,6 +76,16 @@ case "$(nth "$out" 2)" in *"Fable"*"10%"*) ok "narrow: Fable is on line two" ;;
case "$(nth "$out" 1)" in *"Fable"*) bad "narrow: Fable not duplicated on line one" "$out" ;; case "$(nth "$out" 1)" in *"Fable"*) bad "narrow: Fable not duplicated on line one" "$out" ;;
*) ok "narrow: Fable not duplicated on line one" ;; esac *) ok "narrow: Fable not duplicated on line one" ;; esac
# The per-model bar is the reason the group exists on a Fable-capable account,
# so it must survive by wrapping and never be dropped to squeeze the group onto
# one line. Regression: on a ~110-column laptop it vanished silently.
echo "Mid widths keep the per-model bar (wrapping if need be):"
for w in 105 110 116 120; do
out=$(render "$w" "$REPO" "Opus 5 (1M context)")
case "$out" in *"Fable"*"10%"*) ok "width $w: Fable still shown" ;;
*) bad "width $w: Fable still shown" "$out" ;; esac
done
# The label must come from the payload, not be hardcoded. # The label must come from the payload, not be hardcoded.
fixture <<JSON fixture <<JSON
{$BASE,"extra_usage":{"is_enabled":false}, {$BASE,"extra_usage":{"is_enabled":false},

View file

@ -12,17 +12,14 @@
# Asserting invariants rather than fixed line counts keeps these from rotting # Asserting invariants rather than fixed line counts keeps these from rotting
# every time a segment's content changes. # every time a segment's content changes.
SCRIPT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)/statusline.isaacaudet.sh" SCRIPT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)/statusline.isaacaudet.sh"
CACHE="/tmp/claude/statusline-usage-cache.json"
PAD=$(jq -r '.statusLine.padding // 0' "$HOME/.claude/settings.json" 2>/dev/null || echo 0) PAD=$(jq -r '.statusLine.padding // 0' "$HOME/.claude/settings.json" 2>/dev/null || echo 0)
OVERHEAD=$(( 2 * PAD + 1 )) OVERHEAD=$(( 2 * PAD + 1 ))
BACKUP=$(mktemp); [ -f "$CACHE" ] && cp "$CACHE" "$BACKUP" # Throwaway cache directory, so fixtures never reach the live status line's
# cache (~/.claude/statusline.sh is a symlink to the script under test).
export STATUSLINE_CACHE_DIR=$(mktemp -d)
CACHE="$STATUSLINE_CACHE_DIR/statusline-usage-cache.json"
REPO=$(mktemp -d) REPO=$(mktemp -d)
# If there was no cache to begin with, REMOVE the fixture rather than leaving cleanup() { rm -rf "$STATUSLINE_CACHE_DIR" "$REPO"; }
# it: the status line would treat it as a fresh response for up to an hour.
cleanup() {
if [ -s "$BACKUP" ]; then cp "$BACKUP" "$CACHE"; else rm -f "$CACHE"; fi
rm -f "$BACKUP"; rm -rf "$REPO"
}
trap cleanup EXIT trap cleanup EXIT
cat > "$CACHE" <<'JSON' cat > "$CACHE" <<'JSON'
@ -44,7 +41,7 @@ pass=0; fail=0
check() { # usable, branch, model, cost, [xfail-reason] check() { # usable, branch, model, cost, [xfail-reason]
local u="$1" branch="$2" model="$3" cost="$4" xfail="${5:-}" local u="$1" branch="$2" model="$3" cost="$4" xfail="${5:-}"
local w=$(( u + OVERHEAD )) local w=$(( u + OVERHEAD ))
git -C "$REPO" checkout -q -B "$branch" 2>/dev/null; rm -f /tmp/claude/git-* git -C "$REPO" checkout -q -B "$branch" 2>/dev/null; rm -f "$STATUSLINE_CACHE_DIR"/git-*
local stdin="{\"model\":{\"display_name\":\"$model\"},\"cwd\":\"$REPO\",\"cost\":{\"total_cost_usd\":$cost},\"context_window\":{\"context_window_size\":200000,\"current_usage\":{\"input_tokens\":45000,\"cache_read_input_tokens\":30000}}}" local stdin="{\"model\":{\"display_name\":\"$model\"},\"cwd\":\"$REPO\",\"cost\":{\"total_cost_usd\":$cost},\"context_window\":{\"context_window_size\":200000,\"current_usage\":{\"input_tokens\":45000,\"cache_read_input_tokens\":30000}}}"
local out ws n mx=0 over=0 local out ws n mx=0 over=0
out=$(TERM_WIDTH=$w bash "$SCRIPT" <<<"$stdin") out=$(TERM_WIDTH=$w bash "$SCRIPT" <<<"$stdin")
@ -89,5 +86,9 @@ for u in 200 150 116 100 85 68; do check "$u" "$LONG" "$M2" 4.61; done
check 40 "$LONG" "$M2" 4.61 "narrow tier floor ~43 cols with a long branch" check 40 "$LONG" "$M2" 4.61 "narrow tier floor ~43 cols with a long branch"
echo "Large cost figure:" echo "Large cost figure:"
for u in 116 85 68; do check "$u" "$SHORT" "$M1" 1234.56; done for u in 116 85 68; do check "$u" "$SHORT" "$M1" 1234.56; done
# ... and with a long branch, which is what makes the wrap-mode branch budget
# bite: a four-digit cost is three columns wider than the budget assumed.
echo "Large cost figure with a long branch:"
for u in 68 69 70 75 85; do check "$u" "$LONG" "$M1" 1234.56; done
echo; echo "pass=$pass fail=$fail"; [ "$fail" -eq 0 ] echo; echo "pass=$pass fail=$fail"; [ "$fail" -eq 0 ]