Read thinking state from the payload and show the effort level
The indicator read alwaysThinkingEnabled from settings.json, which Option+T
never writes - it toggles thinking for the session only. The key was also
absent here, so `// false` pinned the segment to the hollow "off" diamond
while thinking was actually on: an inverted indicator, not just a stale one.
Claude Code pipes the live state in as .thinking.enabled, alongside
.effort.level. Read both, in one jq pass since the line re-renders on every
redraw. Absent thinking means enabled, matching the renderer's own
`thinking:{enabled: lt !== false}`.
The effort level now replaces the static "thinking" label, so the segment
says something that changes: "◆ high" rather than a word that was there
either way. Unrecognised levels fall back to the old label, since the wrap
branch sizes line one from this string.
Test widths are usable widths, not raw terminal widths: the tiers are
computed after padding is subtracted, so a raw width would change tier
whenever statusLine.padding did. The usage cache is seeded for the same
reason width_test.sh seeds it - an unseeded cache makes the first render
fetch live usage over the network.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-02 19:20:56 +01:00
|
|
|
#!/bin/bash
|
|
|
|
|
# Tests for the thinking/effort segment of claude/statusline.isaacaudet.sh.
|
|
|
|
|
#
|
|
|
|
|
# The state comes from the payload Claude Code pipes in (.thinking.enabled and
|
|
|
|
|
# .effort.level), not from alwaysThinkingEnabled in settings.json: the Option+T
|
|
|
|
|
# toggle is session-only, so a settings read cannot track it. Claude Code's own
|
|
|
|
|
# payload builder is `thinking:{enabled: lt !== false}`, so an absent field
|
|
|
|
|
# means enabled -- which is why the missing-field cases below expect the filled
|
|
|
|
|
# diamond rather than the hollow one.
|
|
|
|
|
SCRIPT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)/statusline.isaacaudet.sh"
|
|
|
|
|
# Widths below are USABLE widths, converted to a TERM_WIDTH here. The script
|
|
|
|
|
# tiers on USABLE_WIDTH (columns minus padding both sides minus a margin), so a
|
|
|
|
|
# raw TERM_WIDTH would land in a different tier the moment statusLine.padding
|
|
|
|
|
# changed -- same convention as width_test.sh.
|
|
|
|
|
PAD=$(jq -r '.statusLine.padding // 0' "$HOME/.claude/settings.json" 2>/dev/null || echo 0)
|
|
|
|
|
OVERHEAD=$(( 2 * PAD + 1 ))
|
|
|
|
|
export STATUSLINE_CACHE_DIR="$(mktemp -d)"
|
|
|
|
|
CACHE="$STATUSLINE_CACHE_DIR/statusline-usage-cache.json"
|
|
|
|
|
REPO="$(mktemp -d)"
|
|
|
|
|
|
|
|
|
|
cleanup() { rm -rf "$STATUSLINE_CACHE_DIR" "$REPO"; }
|
|
|
|
|
trap cleanup EXIT
|
|
|
|
|
|
|
|
|
|
# Seed the usage cache. Without a fixture the first render treats the rate
|
|
|
|
|
# limits as live and curls /api/oauth/usage with the real OAuth token, which
|
|
|
|
|
# makes the test non-hermetic and sensitive to account state.
|
|
|
|
|
cat > "$CACHE" <<'JSON'
|
|
|
|
|
{"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"},
|
|
|
|
|
"extra_usage":{"is_enabled":false},
|
|
|
|
|
"limits":[]}
|
|
|
|
|
JSON
|
|
|
|
|
|
|
|
|
|
git -C "$REPO" init -q 2>/dev/null
|
|
|
|
|
git -C "$REPO" -c user.email=t@t -c user.name=t commit -q --allow-empty -m init 2>/dev/null
|
|
|
|
|
|
|
|
|
|
pass=0; fail=0
|
|
|
|
|
ok() { echo " PASS $1"; pass=$((pass+1)); }
|
|
|
|
|
bad() { echo " FAIL $1"; [ -n "$2" ] && printf '%s\n' "$2" | sed 's/^/ /'; fail=$((fail+1)); }
|
|
|
|
|
|
|
|
|
|
# Build the payload in python so a thinking/effort block can be omitted
|
|
|
|
|
# entirely -- absent and false are different states here.
|
|
|
|
|
stdin_json() {
|
|
|
|
|
CWD="$REPO" THINKING="$1" EFFORT="$2" python3 -c '
|
|
|
|
|
import json, os
|
|
|
|
|
p = {"model": {"display_name": "Opus 5"},
|
|
|
|
|
"cwd": os.environ["CWD"],
|
|
|
|
|
"cost": {"total_cost_usd": 0.5},
|
|
|
|
|
"context_window": {"context_window_size": 200000,
|
|
|
|
|
"current_usage": {"input_tokens": 1000}}}
|
|
|
|
|
t = os.environ["THINKING"]
|
|
|
|
|
if t: p["thinking"] = {"enabled": t == "true"}
|
|
|
|
|
e = os.environ["EFFORT"]
|
|
|
|
|
if e: p["effort"] = {"level": e}
|
|
|
|
|
print(json.dumps(p))'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# $1 = thinking ("true"/"false"/"" for absent), $2 = effort level or "",
|
|
|
|
|
# $3 = USABLE width. ANSI stripped.
|
|
|
|
|
render() {
|
|
|
|
|
stdin_json "$1" "$2" \
|
|
|
|
|
| TERM_WIDTH="$(( ${3:-200} + OVERHEAD ))" bash "$SCRIPT" 2>&1 | sed $'s/\033\[[0-9;]*m//g'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
has() { case "$(render "$1" "$2" "$4")" in *"$3"*) ok "$5";; *) bad "$5" "want '$3' in: $(render "$1" "$2" "$4")";; esac; }
|
|
|
|
|
lacks() { case "$(render "$1" "$2" "$4")" in *"$3"*) bad "$5" "unwanted '$3' in: $(render "$1" "$2" "$4")";; *) ok "$5";; esac; }
|
|
|
|
|
|
|
|
|
|
echo "Thinking state from the payload:"
|
|
|
|
|
has true high "◆ high" 200 "enabled + high effort renders a filled diamond and the level"
|
|
|
|
|
has false low "◇ low" 200 "disabled renders a hollow diamond"
|
|
|
|
|
has "" high "◆ high" 200 "absent thinking field means enabled, matching Claude Code's default"
|
|
|
|
|
|
|
|
|
|
echo
|
|
|
|
|
echo "Effort level as the label:"
|
|
|
|
|
has true medium "◆ medium" 200 "medium is spelled out"
|
|
|
|
|
has true xhigh "◆ xhigh" 200 "xhigh is spelled out"
|
|
|
|
|
has true max "◆ max" 200 "max is spelled out"
|
|
|
|
|
lacks true high "thinking" 200 "the static word 'thinking' is gone when an effort level is known"
|
|
|
|
|
|
|
|
|
|
echo
|
|
|
|
|
echo "Fallbacks and tiers:"
|
|
|
|
|
has true "" "◆ thinking" 200 "absent effort falls back to the old label"
|
|
|
|
|
has true garbage "◆ thinking" 200 "an unrecognised level falls back rather than widening the line"
|
|
|
|
|
has true high "◆ high" 120 "the label survives the wide tier"
|
Give the usage bars a line of their own, always
Squeezing the 5h/7d group onto line one was what cost the reset times and
the pace figure at laptop width, and it made the layout jump between one
and two lines as the branch name or the cost changed width. Line two is
now the group's own at every terminal size, so both timestamps and the
burn rate fit; the ladder only starts giving things up below ~80 columns.
The bars also survive down to 35 columns now rather than being dropped
below 68, since a line of their own is all they need. Every rung of the
ladder is fit-checked, the last one included: with the floor that low, a
percentage the API reports in more digits than anyone expects would
otherwise have overflowed the line rather than dropped the group.
The two timestamps still cost ~10 subprocesses to format on a line that
redraws per keystroke, so that is skipped when line two provably cannot
show them -- an under-estimate, so the fit check stays the decider.
With nothing left to squeeze, WRAP_NARROW, the wrap band and the split
tier go: line one is the full tier down to 150, wide to 68, then narrow.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-03 16:51:05 +01:00
|
|
|
# Below 68 columns the narrow tier drops the segment entirely; between there
|
|
|
|
|
# and 100 the compact tier keeps the label, since the usage group has moved off
|
|
|
|
|
# line one and left room for it.
|
|
|
|
|
has true high "◆ high" 85 "the label survives the compact tier"
|
Read thinking state from the payload and show the effort level
The indicator read alwaysThinkingEnabled from settings.json, which Option+T
never writes - it toggles thinking for the session only. The key was also
absent here, so `// false` pinned the segment to the hollow "off" diamond
while thinking was actually on: an inverted indicator, not just a stale one.
Claude Code pipes the live state in as .thinking.enabled, alongside
.effort.level. Read both, in one jq pass since the line re-renders on every
redraw. Absent thinking means enabled, matching the renderer's own
`thinking:{enabled: lt !== false}`.
The effort level now replaces the static "thinking" label, so the segment
says something that changes: "◆ high" rather than a word that was there
either way. Unrecognised levels fall back to the old label, since the wrap
branch sizes line one from this string.
Test widths are usable widths, not raw terminal widths: the tiers are
computed after padding is subtracted, so a raw width would change tier
whenever statusLine.padding did. The usage cache is seeded for the same
reason width_test.sh seeds it - an unseeded cache makes the first render
fetch live usage over the network.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-02 19:20:56 +01:00
|
|
|
lacks true high "◆" 60 "narrow tier hides the segment"
|
|
|
|
|
|
|
|
|
|
echo
|
|
|
|
|
echo " $pass passed, $fail failed"
|
|
|
|
|
[ "$fail" -eq 0 ]
|