AnleitungKI

Die Statuszeile in Claude Code ist ein Skript. Schreib sie nicht fest.

Statuszeile in Claude Code einrichten: JSON auf stdin, settings.json und die Felder, die zählen. Ein Baukasten kopiert einen Prompt, kein festgeschriebenes Skript.

Manuel Hedinger
8 Min. Lesezeit

Ich kompaktiere zu spät. Das Fünf-Stunden-Limit merke ich, wenn die nächste Antwort eine Bezahlschranke ist. Welches Modell ich vor drei Messages umgestellt habe, habe ich schon wieder vergessen. All das steckt bereits in der Session. Claude Code legt es nur nicht dorthin, wo ich hinschaue.

Genau dafür ist die Statuszeile da.

Die kurze Antwort

Die Statuszeile ist eine Zeile über den eingebauten Fusszeilen-Badges. Claude Code führt einen Befehl aus, schickt ihm die aktuelle Session als JSON auf stdin und zeichnet, was der Befehl auf stdout schreibt. Sie verbraucht keine API-Tokens. Sie ersetzt die Badges nicht. Sie blendet aber die meisten Tastaturhinweise aus — esc to interrupt, ? for shortcuts, den Hold-Space-to-Speak-Hinweis. Wenn du davon lebst, ist das der Preis.

Drei Wege führen hin. Beschreib die Zeile mit /statusline und lass Claude das Skript nach ~/.claude/ schreiben. Setz selbst ein statusLine-Objekt in ~/.claude/settings.json. Oder stell das Layout im Statusleisten-Baukasten zusammen und füg den Prompt ein. Der Baukasten gibt kein Skript aus. Ein Skript in einem Blogpost ist veraltet, sobald Anthropic ein Feld dazunimmt. Einen Prompt liest Claude Code gegen die Docs, die es schon hat.

Die offizielle Anleitung ist Customize your status line. Was folgt, ist die Fassung, die ich selbst gern gehabt hätte: was sich zu zeigen lohnt, was man weglässt — und die Stellen, die lautlos scheitern.

Drei Wege zu einer Statuszeile: beschreiben, settings.json schreiben oder im Baukasten einen Prompt zusammensetzen.

/statusline fragen

Zum Beispiel:

/statusline show model name and context percentage with a progress bar

Claude legt ein Skript unter ~/.claude/ an und schreibt die Settings. Die Datei-Edit-Prompts musst du bestätigen, wenn es danach fragt. Das ist der richtige erste Schritt, wenn du noch nie eine Statuszeile hattest und dir egal ist, wie sie innen aussieht.

Zum Entfernen reicht /statusline delete (oder clear, oder remove it). Oder du löschst das Feld statusLine selbst aus der settings.json.

Die Settings schreiben

User-Settings liegen in ~/.claude/settings.json. Projekt-Settings gehen auch. Die Form ist klein:

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

type ist "command". command ist ein Skriptpfad oder ein Shell-Schnipsel. Settings laden beim Speichern nach; Claude Code musst du nicht neu starten.

padding ist extra horizontaler Abstand in Zeichen, zusätzlich zur eingebauten Rinne. Standard 0.

refreshInterval führt den Befehl alle N Sekunden zusätzlich zu den ereignisgetriebenen Updates aus. Minimum 1. Setz es für eine Uhr, oder wenn Hintergrund-Subagents den Git-Stand ändern, während die Hauptsession idle ist. Lass es weg, wenn dich nur Antworten interessieren.

hideVimModeIndicator blendet das eingebaute -- INSERT -- unter dem Prompt aus. Setz es, wenn dein Skript vim.mode schon selbst druckt, sonst siehst du den Modus doppelt.

Der Befehl läuft in einer Shell, deshalb reicht für den ersten Check eine Zeile:

{
  "statusLine": {
    "type": "command",
    "command": "jq -r '\"[\\(.model.display_name)] \\(.context_window.used_percentage // 0)% context\"'"
  }
}

Du brauchst jq. Unter Windows den Pfad mit Schrägstrichen schreiben (C:/Users/you/.claude/statusline.sh). Git Bash frisst Backslashes als Escape-Zeichen — und der Befehl scheitert ohne eine Zeile auf dem Schirm.

Zusammensetzen, dann Claude schreiben lassen

Ich hefte kein Gist von statusline.sh ab. Das stdin-JSON wächst. Das Skript von letztem Jahr kennt rate_limits nicht, auch nicht pr.kind für GitLab, auch nicht COLUMNS. Der Follow-up, den ich wirklich will, heisst «now make the bar wider» oder «add the open PR».

Deshalb kopiert der Statusleisten-Baukasten einen Prompt, keine Datei. Du ziehst die Segmente an ihren Platz — Verzeichnis, Branch, Modell, Kontext, das 5-Stunden- und das 7-Tage-Limit, Kosten, eine Uhr, geänderte Zeilen, die PR, der Worktree — wählst eine Palette und fügst den englischen Prompt in eine Session ein. Claude schreibt das Skript, zeigt in der settings.json darauf und bleibt für die nächste Änderung da.

Es läuft im Browser. Was du anordnest, wird nicht hochgeladen.

Wie die Daten laufen

Claude Code serialisiert die laufende Session und piped sie rein. Dein Befehl druckt eine Zeile. Diese Zeile ist die Row.

Claude Code schreibt die Session als JSON, dein Befehl druckt eine Zeile, die Zeile erscheint über der Fusszeile. Keine Tokens.

Der Befehl läuft einmal, wenn eine Session startet, auch beim Fortsetzen. Danach wieder, wenn:

  • eine neue Assistant-Message ankommt
  • /compact fertig ist
  • sich der Permission-Modus ändert
  • der Vim-Modus umschaltet
  • du command in den Settings änderst
  • ein refreshInterval-Timer zündet, falls du einen gesetzt hast

Updates werden mit 300ms entprellt. Eine Änderung an command selbst überspringt das und läuft sofort. Kommt ein neues Update, während das Skript noch läuft, wird der laufende Lauf abgebrochen. Editierst du das Skript auf der Platte, greift der nächste Trigger; extra nachladen musst du nicht.

Die ereignisgetriebenen Updates werden still, wenn die Hauptsession idle ist, zum Beispiel während ein Coordinator auf Hintergrund-Subagents wartet. Dafür ist refreshInterval da.

Du kannst mehrere Zeilen drucken. Du kannst ANSI-Farben nutzen. Du kannst Text in OSC-8-Sequenzen packen, damit er klickbar wird (Cmd-Klick auf macOS, Ctrl-Klick sonst) in iTerm2, Kitty oder WezTerm. Terminal.app kann das nicht. Stehen die Sequenzen als literales \e]8;; da, nimm printf '%b', nicht echo -e.

Ruf innerhalb des Skripts nicht tput cols auf. Claude Code fängt stdout ab, statt dich an das Terminal zu hängen — die Breitenerkennung von innen ist blind. Lies COLUMNS und LINES. Die setzt Claude Code vor dem Lauf, ab Version 2.1.153.

Die Zeile verschwindet während Autocomplete, Hilfemenü und Permission-Prompts. Ausserhalb von Fullscreen teilen sich Notifications dieselbe Zeile und schneiden dich auf einem schmalen Terminal ab.

Was sich zu zeigen lohnt

Das JSON ist gross. Auf einer 80-Zeichen-Zeile ist das meiste davon Rauschen.

Kontext. context_window.used_percentage ist das Feld. Es wird nur aus Input-Tokens gerechnet (input_tokens + cache_creation_input_tokens + cache_read_input_tokens). Output-Tokens stecken nicht drin. Rechnest du den Prozentsatz selbst aus current_usage, nimm dieselbe Formel, sonst widersprichst du /context. current_usage ist null vor dem ersten API-Call und nach /compact wieder, bis die nächste Antwort es füllt. Dasselbe gilt früh in der Session für die Prozentfelder. Fallback in jq: // 0.

Das Standardfenster ist 200k Tokens, oder 1M bei Modellen mit extended context. exceeds_200k_tokens ist eine feste Schwelle, nicht «das Fenster ist voll».

Kosten. cost.total_cost_usd ist eine Schätzung auf dem Client. Das ist nicht deine Rechnung. Bei /clear fällt sie auf 0 $ (vor v2.1.211 hat sie sich mitgeschleppt, das war verwirrend). total_duration_ms ist Wanduhr seit Sessionstart; total_api_duration_ms ist die Wartezeit auf die API.

Rate Limits. rate_limits.five_hour und rate_limits.seven_day gibt es für Claude.ai Pro/Max nach der ersten API-Antwort. Jedes Fenster kann für sich fehlen. Fehlend heisst «Segment nicht zeichnen», nicht 0 %.

Git. Im JSON steht kein git.branch. git rufst du selbst auf. Das ist in einem grossen Repo langsam — und das Skript läuft oft, also cachen. Nimm session_id in den Cache-Dateinamen, nicht $$ oder os.getpid(): die ändern sich bei jedem Aufruf, der Cache trifft nie. Gleichzeitige Sessions in verschiedenen Repos dürfen sich keine Datei teilen.

workspace.git_worktree ist gesetzt, wenn du in einem linked Worktree bist. worktree.* ist ein anderes Objekt und nur während einer Claude-Code-Worktree-Session da.

Was ich sonst noch nutze. model.display_name plus effort.level. workspace.current_dir (gleicher Wert wie cwd; nimm das verschachtelte Feld). pr.number und pr.review_state, wenn auf dem Branch eine PR oder eine GitLab-Merge-Request offen ist. session_name, wenn du die Session benannt hast; der Default im Stil my-app-3f füllt das Feld nicht. vim.mode, wenn du den Vim-Modus nutzt.

Ein fehlendes Feld ist nicht dasselbe wie ein Feld mit null. Beides abfangen.

Ein erstes Skript

Bash, auf macOS und Linux. Ausführbar machen (chmod +x ~/.claude/statusline.sh) und command darauf zeigen.

#!/bin/bash
input=$(cat)
 
MODEL=$(echo "$input" | jq -r '.model.display_name')
DIR=$(echo "$input" | jq -r '.workspace.current_dir')
PCT=$(echo "$input" | jq -r '.context_window.used_percentage // 0' | cut -d. -f1)
 
echo "[$MODEL] ${DIR##*/} | ${PCT}% context"

Testen, ohne Claude Code zu öffnen:

echo '{"model":{"display_name":"Opus"},"workspace":{"current_dir":"/home/user/project"},"context_window":{"used_percentage":25},"session_id":"test"}' | ./statusline.sh

Die gedruckte Zeile kurz halten. Die Bar ist nicht breit — und auf einem schmalen Terminal fressen die Notifications rechts sie auf.

Wenn sie leer bleibt

Die übliche Liste, in der Reihenfolge, in der ich prüfe:

  1. Ist das Skript ausführbar und schreibt es auf stdout statt stderr?
  2. Hast du den Workspace-Trust-Dialog bestätigt? Bis dahin bleibt die Zeile leer und claude --debug loggt, dass der Befehl übersprungen wurde.
  3. Unter Windows: hat Git Bash einen Backslash im Pfad gefressen?
  4. Schaust du auf Nulls vor der ersten Antwort? Fallbacks, dann eine Runde warten.
  5. Hat jemand disableAllHooks gesetzt? Ausserhalb von Managed Settings schaltet das eine User-Statuszeile ab. allowManagedHooksOnly in organisationsverwalteten Settings ignoriert deine lautlos.

claude --debug loggt Exit-Code und stderr des ersten Aufrufs in einer Session. Claude den statusLine-Befehl gegen deine Settings-Datei ausführen zu lassen, bringt den Fehler auch schneller als eine leere Zeile anzustarren.

Wenn nach mehreren Messages -- oder leere Werte stehen, einmal neu starten. Wenn OSC-8-Links als Text gerendert werden, Terminal prüfen, dann FORCE_HYPERLINK=1 claude versuchen.

Was ich wirklich laufen lasse

Drei Zeilen. Verzeichnis und Branch links, eine Uhr rechts. Modell links in Zeile zwei, Kontext rechts. Das 5-Stunden-Limit in Zeile drei, das 7-Tage-Limit gegenüber. Das ist das Preset «Mine» im Baukasten. Mir geht es darum, vor dem vollen Fenster zu kompaktieren — und um die Wochenkappe vor Freitagmittag.

Das Skript checke ich trotzdem nicht ein. Will ich ein anderes Segment, füge ich einen neuen Prompt ein. Im Baukasten entscheide ich das Layout. Claude Code schreibt die Datei.

Wenn die Fusszeile klickbare Badges wachsen soll, sobald in der Konversation eine ID auftaucht, ist das eine andere Einstellung: footerLinksRegexes. Kein Skript.

Wenn eine Statuszeile sich mit dir anlegt und du den Abend nicht mit ANSI-Codes verbringen willst: schreib mir. Manchmal reicht eine 40-Zeichen-jq-Zeile.