Zum Hauptinhalt springen
tsecurity.de LIVE
Echtzeit-Radar & Feeds
Alle RSS Feeds
👥 Community & Social
Sichere ProgrammierungKI half beim Finden: iOS 27 schließt mehr als 100 Sicherheitslücken(21.09.2026 um 06:00 Uhr)
Sichere ProgrammierungWhat Is Rowhammer? How Can Repeated Memory Access Flip Bits in RAM?(21.09.2026 um 07:12 Uhr)
Sichere Programmierungnpm publish Ignores .gitignore: The .npmignore Override Rule(21.09.2026 um 07:15 Uhr)
Sichere ProgrammierungAphelion Editor - A free node-based video / VFX editor(21.09.2026 um 07:21 Uhr)
Sichere ProgrammierungGovernance Attack Surface Review: OKX(21.09.2026 um 07:31 Uhr)
Sichere ProgrammierungJSM Portal Request Create Property Panel Submit(21.09.2026 um 07:34 Uhr)
Reverse Engineeringsearch instructions assembly easy (X86,RISCV,AARCH64,etc)(20.09.2026 um 15:44 Uhr)
Sichere ProgrammierungKI half beim Finden: iOS 27 schließt mehr als 100 Sicherheitslücken(21.09.2026 um 06:00 Uhr)
Sichere ProgrammierungWhat Is Rowhammer? How Can Repeated Memory Access Flip Bits in RAM?(21.09.2026 um 07:12 Uhr)
Sichere Programmierungnpm publish Ignores .gitignore: The .npmignore Override Rule(21.09.2026 um 07:15 Uhr)
Sichere ProgrammierungAphelion Editor - A free node-based video / VFX editor(21.09.2026 um 07:21 Uhr)
Sichere ProgrammierungGovernance Attack Surface Review: OKX(21.09.2026 um 07:31 Uhr)
Sichere ProgrammierungJSM Portal Request Create Property Panel Submit(21.09.2026 um 07:34 Uhr)
Reverse Engineeringsearch instructions assembly easy (X86,RISCV,AARCH64,etc)(20.09.2026 um 15:44 Uhr)
Intelligence View
⚡ tsecurity.de Intelligence

Fully Custom Claude Code Status Line: A JSON-Driven 3-Line Readout of Quota, Cost, and Health

Reagiere als Erste:r — dein Feedback zählt!

This is part of my "Claude Code environment" series. Last time I wrote about the Codex worktree swarm, a setup that gets Codex and Claude Code collaborating. This time the topic is something far less flashy but that you look at every single turn: customizing the status line.

Claude Code can print a single status line at the bottom of the window. By default it shows little more than the model name. Using the feature that lets you point statusLine.command in settings.json at an arbitrary shell script, I built a version that formats your 5h/7d plan quota, context usage, session cost, and automation health into three lines — and I'll walk through the whole implementation. The nice part is that the quota numbers flow in directly from stdin, so no API calls and no auth are required.

The problem: an unattended job goes haywire and you never notice in the terminal

Once I started running autopilot.sh overnight, I kept hitting the same accident: "the 5h block quota is at zero by morning, so every job after that got SKIPPED." The issue was that checking cost or quota meant running ccusage every time. Because checking was a hassle, I stopped checking, and I noticed problems too late.

The ideal state is "one glance at the screen tells me my current plan consumption." If I can just put that on Claude Code's status line, I don't even need to open a terminal.

The settings.json config: how JSON drives it

Lines 269–274 of ~/.claude/settings.json are just this:

"statusLine": {
  "type": "command",
  "command": "~/.claude/scripts/statusline.sh",
  "padding": 0
}

With type: "command", every turn Claude Code launches that command and pipes JSON into stdin. Standard output is displayed verbatim as the status line.

So what's in that stdin? The actual fields are:

Path Contents
.rate_limits.five_hour.used_percentage 5h block usage rate (0–100)
.rate_limits.five_hour.resets_at Reset time (UNIX epoch)
.rate_limits.seven_day.used_percentage 7-day block usage rate
.rate_limits.seven_day.resets_at 7-day reset time (UNIX epoch)
.context_window.used_percentage Context usage rate (pre-computed)
.cost.total_cost_usd Current session cost (USD)
.model.display_name Model name
.effort.level Effort level

Note: rate_limits only appears after the first API response for subscription members. Before the first turn it's empty, so the script needs to fall back to --.

statusline.sh: reading stdin and falling back

At the top of the script it reads stdin and, if it's valid JSON, caches it to /tmp/cc-statusline-last.json.

RAW_INPUT="$(cat 2>/dev/null || true)"
if printf '%s' "$RAW_INPUT" | jq -e 'type == "object" and length > 0' >/dev/null 2>&1; then
  INPUT="$RAW_INPUT"
  printf '%s' "$INPUT" > /tmp/cc-statusline-last.json 2>/dev/null || true
elif [ -s /tmp/cc-statusline-last.json ]; then
  INPUT="$(cat /tmp/cc-statusline-last.json 2>/dev/null || echo '{}')"
else
  INPUT='{}'
fi

Even when stdin is empty (e.g. when another tool references it), it can read back from the cache. claude-limits-segment.sh is set up to receive this cache path via the CLAUDE_STATUSLINE_JSON environment variable:

STATUS_JSON="${CLAUDE_STATUSLINE_JSON:-/tmp/cc-statusline-last.json}"

Hide the jq call behind a helper function:

j() { printf '%s' "$INPUT" | jq -r "$1" 2>/dev/null; }

Fetching plan quota and context is just this:

CTX=$(j '.context_window.used_percentage // empty')
H5=$(j '.rate_limits.five_hour.used_percentage // empty')
H5R=$(j '.rate_limits.five_hour.resets_at // empty')
D7=$(j '.rate_limits.seven_day.used_percentage // empty')
D7R=$(j '.rate_limits.seven_day.resets_at // empty')
SESSION_USD=$(j '.cost.total_cost_usd // 0')

Color coding: two thresholds at 50% and 80%

pcolor() converts a number to an ANSI color:

pcolor() { local n="${1%%.*}"; [ -z "$n" ] && { printf '%s' "$DIM"; return; }
  if [ "$n" -ge 80 ] 2>/dev/null; then printf '%s' "$RED"
  elif [ "$n" -ge 50 ] 2>/dev/null; then printf '%s' "$YEL"
  else printf '%s' "$GRN"; fi; }
  • Under 50% → green (comfortable)
  • 50–79% → yellow (caution)
  • 80%+ → red (danger zone)

Apply this to all three: context, 5h, and 7d:

CTXC=$(pcolor "$CTX")
C5=$(pcolor "$H5")
C7=$(pcolor "$D7")

Assembling the three lines

The spec in the script's comments maps directly to the final output:

line1: 📁 dir   ⌥ branch
line2: 🤖 model·effort   🧠 ctx%   🕐 5h N% ⏪reset   📅 7d N% ⏪reset
line3: 💴 session ≈¥x   今日 ≈¥y   <health>

In code:

L1=$(printf "${DIM}📁${R} ${CYAN}%s${R}%s%s" "$DIRNAME" "$BR" "$WT")
L2=$(printf "${MAG}🤖 %s%s${R}  ${DIM}·${R}  ${CTXC}🧠 ctx %s${R}  ${DIM}·${R}  ${C5}🕐 5h %s${R}%b  ${DIM}·${R}  ${C7}📅 7d %s${R}%b" \
  "$MODEL" "$EFF" "$(pct "$CTX")" "$(pct "$H5")" "$R5" "$(pct "$D7")" "$R7")
L3=$(printf "${DIM}💴 session${R} %s   ${DIM}今日${R} %s  %s%b" \
  "$(yen "$SESSION_USD")" "$(yen "$DAY_USD")" "$HEALTH" "$PROJ_HEALTH_SEG")

printf "%b\n%b\n%b" "$L1" "$L2" "$L3"

Reset times are converted from epoch with date -r. The 5h uses HH:MM, and the 7d uses M/D HH:MM (since it can cross day boundaries):

clk()  { [ -n "$1" ] && date -r "$1" "+%H:%M" 2>/dev/null; }
mdhm() { [ -n "$1" ] && date -r "$1" "+%-m/%-d %H:%M" 2>/dev/null; }

Today's cost: 2-minute cache plus background refresh

The 5h block quota comes straight from stdin, but today's total cost requires an external command, ccusage daily. Calling it every turn feels slow, so I use a 2-minute cache plus background refresh:

NEED=true
if [ -f "$DAY_CACHE" ]; then
  AGE=$(($(date +%s) - $(stat -f %m "$DAY_CACHE" 2>/dev/null || echo 0)))
  [ "$AGE" -lt 120 ] && NEED=false
fi
[ "$NEED" = true ] && ( "$CCUSAGE" daily --json 2>/dev/null > "${DAY_CACHE}.tmp" && mv "${DAY_CACHE}.tmp" "$DAY_CACHE" ) &

The & launches it in the background, while the foreground reads immediately from the cache. The display can lag by up to 2 minutes, but the status line never freezes. The atomic tmpmv swap also prevents corruption from reading a half-written file.

Health icon: reading with zero subprocesses

automation-health.sh checks the success/failure of every launchd job, so it's expensive to start. Calling it every turn produces noticeable lag. The solution is the same pattern — 5-minute cache plus background refresh:

HEALTH_CACHE="/tmp/cc-health-cache"; HEALTH=""
if [ -f "$HEALTH_CACHE" ]; then
  HAGE=$(($(date +%s) - $(stat -f %m "$HEALTH_CACHE" 2>/dev/null || echo 0)))
  [ "$HAGE" -lt 300 ] && HEALTH=$(cat "$HEALTH_CACHE" 2>/dev/null)
fi
if [ -z "$HEALTH" ]; then
  ( h=$(~/.claude/scripts/automation-health.sh 2>&1 | grep -oE "ALL GREEN|WARN|FAIL" | head -1)
    case "$h" in
      "ALL GREEN") echo "🟢" > "$HEALTH_CACHE" ;;
      "WARN")      echo "🟡" > "$HEALTH_CACHE" ;;
      "FAIL")      echo "🔴" > "$HEALTH_CACHE" ;;
      *)           echo "⚫" > "$HEALTH_CACHE" ;;
    esac ) &
  HEALTH="·"   # show a middle dot while updating
fi

While the cache is valid, it just cats the file. Zero subprocesses. If it's expired, it fires the refresh into the background with & and shows · (a middle dot) in the meantime. By the next turn the cache is updated and it switches to an icon.

project-health-latest.md (the project health report) follows the same philosophy — resolve everything by just reading a file:

PH_FILE="$HOME/.claude/logs/project-health-latest.md"
PH_WARN=$(grep -oE '^## Warnings \([0-9]+\)' "$PH_FILE" 2>/dev/null | grep -oE '[0-9]+' | head -1)

It picks up the warning count with grep, showing 🟢 if 0 and 🟡/🔴 if there are any. No external process is started.

Note: "The status line script is called every turn." Startup cost adds up. The rule is: the only things done synchronously are jq and ANSI formatting; everything else is offloaded to a cache or the background.

claude-limits-segment.sh: carving out a component for other tools

Separately from statusline.sh, I also keep a small script that returns just the plan quota. It's used by things like autopilot.sh to check "does the plan have room right now?":

STATUS_JSON="${CLAUDE_STATUSLINE_JSON:-/tmp/cc-statusline-last.json}"

h5=$(jq_field '.rate_limits.five_hour.used_percentage')
h5r=$(jq_field '.rate_limits.five_hour.resets_at')
d7=$(jq_field '.rate_limits.seven_day.used_percentage')
d7r=$(jq_field '.rate_limits.seven_day.resets_at')

stale=''
[ "$age" -gt 600 ] && stale='*'   # mark caches older than 10 minutes

printf '🕐 5h %s' "$(pct "$h5")"
[ -n "$r5" ] && printf ' ⏪%s' "$r5"
printf ' · 📅 7d %s' "$(pct "$d7")"
[ -n "$r7" ] && printf ' ⏪%s' "$r7"
printf '%s' "$stale"

It references the very same /tmp/cc-statusline-last.json that statusline.sh wrote. If the cache is older than 10 minutes, it appends a * to make staleness visible.

Pitfalls I hit

  • rate_limits always looks empty → before the first turn it genuinely doesn't exist. Without // empty to fall back on null, jq returns an empty string and pcolor dies.
  • Reset times can't be converted to local time → macOS's date -r is the BSD version, with different syntax from Linux's date -d @epoch. stat -f %m is the same story. Using this on Linux requires rewriting.
  • Calling ccusage every turn freezes the terminal → solved with a 2-minute cache plus background refresh. The atomic swap via a tmp file is also essential.
  • automation-health.sh is heavy to start and causes lag → offload to a 5-minute cache plus background. Show · while updating.
  • Some terminals let ANSI color codes interfere with output → using printf "%b" for the status line output expands the escapes correctly. echo -e is not portable.
  • Can't get the git branch inside a worktree → without making the working directory explicit with git -C "$REAL_CWD", it references the main repo instead.

Summary

  • Just specifying statusLine.command in settings.json delivers JSON stdin every turn.
  • rate_limits, context_window, and cost.total_cost_usd can be read straight from stdin with no API call.
  • Color coding is handled by pcolor() with two thresholds at 50% and 80%.
  • Offload heavy work (ccusage, health check) to a cache plus background. Keep the synchronous path to jq and string formatting only.
  • The atomic tmpmv swap prevents cache reads from being corrupted.
  • Carving out a component script (claude-limits-segment.sh) makes it easy to reuse from other automation.

Next time I plan to write about the internals of the automation health check (automation-health.sh) shown on this status line — a setup that lists the liveness of launchd jobs, script exit codes, and the last successful run time.

Written by **Lily* — I ship iOS apps and automate my content stack with Claude Code.
Follow along: Portfolio · X · GitHub*

Ähnliche Beiträge
🔍 Verwandte News

Auch interessante Nachrichten Fully Custom Claude Code Status Line: A JSON-Driven 3-Line Readout of Quota, Cost, and Health

Thematisch verwandte Begriffe: Fully, Custom, Claude, Code · 6 Treffer

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Zum Aktualisieren ziehen
ZERO-DAY CVE-2026-94111 | Tencent BrowserSkill through 0.3.0 contains an authentication bypass vul…
Advisory →
TTS Reader • tsecurity.de Voice
tsecurity.de Icon
tsecurity.de App
Offline-Lesen, Eilmeldungen & 0ms Ladezeit

Installiere tsecurity.de direkt auf deinen Home-Bildschirm für das ultimative Vollbild-Magazinerlebnis ohne Browser-Leisten.

Nächster Beitrag
Themen-Radar & Intelligence Matrix
Echtzeit-Taxonomie nach Angriffsvektoren & Plattformen

tsecurity.de Live Threat Radar

🔴 LIVE RADAR
MONITORING
AKTIV
CVE-DATENBANK
LIVE
🔍
Community Radar & Live Chat
Sentinel Bot online • Live-Stream
Dein Cluster: Security Explorer
Match:
lädt…
Verbindung zum Community-Stream wird aufgebaut...
Bearbeitungsmodus — Senden überschreibt deine Nachricht
Community-Puls — was gerade passiert
lädt…
Aktivitäten deiner Analysten
lädt…
Neues Thema oder Eilmeldung einreichen

Reiche interessante Links, Zero-Days oder Debatten ein. Die Community entscheidet per Upvote über die Veröffentlichung.

Heiß diskutierte Einreichungen
🔖 Gespeicherte Artikel
📂 Keine gespeicherten Artikel vorhanden.
Zurück Ziehen Vor
Links: vorheriger Artikel Rechts: nächster Artikel unten: schließen
News NIS-2 Frühwarnung Tier-1 Intel ⏱️ 3 Min vor 10 Min
Artikeldaten werden geladen...

Zurück: vorheriger Vor: nächster
↗ Original-Quelle
Social Reaktionen Deine Reaktion zählt
Einstufung & Relevanz-Poll 0 Stimmen
In sozialen Netzwerken teilen 1-Klick