Claude Code: konfiguracja paska stanu na VPS
Konfiguracja opcji statusLine w pliku settings.json pozwala na wyświetlanie nazwy hosta, katalogu i brancha git. Uniknij błędów podczas pracy na wielu serwerach SSH jednocześnie.
Co oznacza pasek stanu Claude Code
Pasek stanu Claude Code to wiersz pod znakiem zachęty, który wyświetla wynik działania napisanego skryptu. Należy dodać blok statusLine do settings.json i wskazać w nim polecenie. Claude Code uruchamia to polecenie, przesyła do niego stan sesji w formacie JSON na standardowe wejście i drukuje wszystko, co polecenie wypisuje na standardowe wyjście.
To cała umowa. Skrypt odczytuje JSON ze standardowego wejścia i wypisuje tekst na standardowe wyjście. Działa on na lokalnej maszynie, a jego dane wyjściowe nie są przesyłane do modelu, więc nie zużywa tokenów.
Na laptopie z jednym projektem jest to element dekoracyjny. Na trzech serwerach stanowi zabezpieczenie. Każda sesja Claude Code wygląda tak samo w każdym terminalu, więc cztery okna SSH bez etykiet to sposób na wykonanie migracji na niewłaściwej maszynie. Pasek stanu zaczynający się od nazwy hosta eliminuje tego typu błędy.
Lokalizacja ustawienia statusLine w pliku settings.json
Należy umieścić je w ustawieniach użytkownika w ~/.claude/settings.json, co spowoduje zastosowanie konfiguracji do każdego projektu na danej maszynie. Ustawienia projektu w .claude/settings.json wewnątrz repozytorium również działają i mają pierwszeństwo dla danego katalogu.
{
"statusLine": {
"type": "command",
"command": "~/.claude/statusline.sh"
}
}type to zawsze "command". Wartość command jest wykonywana przez powłokę, więc może być ścieżką do skryptu lub zwykłym poleceniem. Przed napisaniem jakiegokolwiek skryptu należy sprawdzić poprawność połączenia:
{
"statusLine": {
"type": "command",
"command": "hostname -s"
}
}Uruchom Claude Code i wyślij jedną wiadomość. Pasek pod wierszem poleceń powinien teraz wyświetlać krótką nazwę hosta serwera. Jeśli pozostaje pusty, problemem jest ustawienie lub okno dialogowe zaufania, a nie sam skrypt. Przeczytaj sekcję "Dlaczego pasek statusu pozostaje pusty" poniżej.
Na sierpień 2026 dostępne są trzy opcjonalne klucze. padding dodaje odstępy poziome w znakach, a jego domyślna wartość to 0. refreshInterval uruchamia polecenie ponownie co N sekund niezależnie od standardowych wyzwalaczy, z minimum wynoszącym 1; opcja ta jest przydatna tylko wtedy, gdy wiersz wyświetla zegar lub inny element zmieniający się podczas bezczynności sesji. hideVimModeIndicator wyłącza wbudowany tekst -- INSERT --, gdy własny skrypt renderuje już tryb vim.
Jakie dane otrzymuje skrypt paska stanu?
Nie należy polegać na listach pól z jakiejkolwiek dokumentacji, w tym z tej strony. Należy przechwycić rzeczywisty obiekt wysyłany przez posiadaną wersję. W tym celu należy utworzyć tymczasowy skrypt zapisujący stdin do pliku:
cat > ~/.claude/statusline-capture.sh <<'EOF'
#!/bin/bash
cat > /tmp/statusline-input.json
echo "captured"
EOF
chmod +x ~/.claude/statusline-capture.shNależy wskazać statusLine.command na ten plik, uruchomić sesję i wysłać jedną wiadomość. Pasek odczytuje captured. Następnie należy sprawdzić, co zostało odebrane:
jq . /tmp/statusline-input.jsonW ten sposób uzyskuje się dokładną strukturę dla danej kompilacji, którą można zweryfikować ponownie po każdej aktualizacji zmieniającej format danych.
Stabilne elementy, zgodnie z dokumentacją z sierpnia 2026, to obiekty zagnieżdżone, a nie płaskie klucze. model zawiera id oraz display_name. workspace zawiera current_dir oraz project_dir: current_dir wskazuje bieżącą lokalizację sesji, project_dir wskazuje lokalizację początkową, a wartości te różnią się po zmianie katalogu roboczego w trakcie sesji. Klucz najwyższego poziomu cwd przechowuje tę samą wartość co workspace.current_dir. context_window zawiera liczby tokenów oraz wstępnie obliczone used_percentage. cost zawiera liczniki total_cost_usd oraz czasu trwania. session_id jest stabilne przez cały czas trwania sesji i unikalne dla każdej z nich, co ma znaczenie dla późniejszego buforowania.
Trzy zasady pozwalają utrzymać skrypt w działaniu mimo zmian schematu.
Niektóre klucze są nieobecne, a nie puste (null). vim, agent, pr, worktree oraz effort pojawiają się tylko wtedy, gdy odpowiadająca im funkcja jest aktywna. Odczytanie .vim.mode za pomocą jq -r przy wyłączonym trybie vim powoduje wypisanie dosłownego ciągu null, co skutkuje wyświetleniem null na pasku. Należy dodać // empty do każdego selektora, aby brakujący klucz nie powodował wyświetlania żadnej wartości.
Niektóre wartości są początkowo puste (null). context_window.used_percentage oraz context_window.current_usage przyjmują wartość null przed pierwszą odpowiedzią API, a current_usage wraca do stanu null po /compact do momentu ponownego wypełnienia przez kolejne wywołanie. Dlatego procentowy wskaźnik kontekstu na pasku wymaga użycia // 0, w przeciwnym razie przez pierwsze sekundy sesji będzie wyświetlane null. Przed umieszczeniem tej liczby na pasku warto zapoznać się z informacją jak faktycznie wypełnia się okno kontekstowe.
Gałąź git nie znajduje się w JSON. Żadne pole jej nie raportuje. Każda informacja o gałęzi na pasku pochodzi ze skryptu, który samodzielnie wykonuje git.
Skrypt paska stanu z mechanizmem degradacji zamiast awarii
Poniżej znajduje się wersja gotowa do skopiowania. Skrypt wyświetla nazwę hosta, katalog roboczy, gałąź git oraz nazwę modelu. Każde pole posiada mechanizm awaryjny, dzięki czemu nawet pusty obiekt JSON pozwala na wygenerowanie poprawnej linii.
#!/bin/bash
# ~/.claude/statusline.sh
input=$(cat)
# Read one field. Prints nothing when the key is missing or null.
field() { printf '%s' "$input" | jq -r "$1 // empty" 2>/dev/null; }
HOST=$(hostname -s 2>/dev/null)
[ -z "$HOST" ] && HOST="host"
DIR=$(field '.workspace.current_dir')
[ -z "$DIR" ] && DIR=$(field '.cwd')
[ -z "$DIR" ] && DIR="$PWD"
MODEL=$(field '.model.display_name')
[ -z "$MODEL" ] && MODEL="claude"
SHORT="$DIR"
if [ -n "$HOME" ]; then
case "$DIR" in
"$HOME") SHORT="~" ;;
"$HOME"/*) SHORT="~/${DIR#"$HOME"/}" ;;
esac
fi
BRANCH=""
if git -C "$DIR" rev-parse --git-dir >/dev/null 2>&1; then
BRANCH=$(git -C "$DIR" branch --show-current 2>/dev/null)
[ -z "$BRANCH" ] && BRANCH="detached"
fi
CYAN=$'\033[36m'
YELLOW=$'\033[33m'
DIM=$'\033[2m'
RESET=$'\033[0m'
LINE="${CYAN}${HOST}${RESET} ${SHORT}"
[ -n "$BRANCH" ] && LINE="${LINE} ${YELLOW}${BRANCH}${RESET}"
LINE="${LINE} ${DIM}${MODEL}${RESET}"
printf '%s\n' "$LINE"Każdy odczyt przechodzi przez field, który dodaje // empty, więc zmiana nazwy lub usunięcie klucza skutkuje pustym ciągiem znaków, a kolejna linia dostarcza wartość domyślną. Katalog jest pobierany z workspace.current_dir, a w razie braku z cwd lub $PWD. Gałąź pochodzi z git -C "$DIR", a nie z surowego git, dzięki czemu zawsze odpowiada katalogowi wyświetlanemu na pasku.
Zapisz plik, a następnie nadaj mu uprawnienia do wykonywania:
chmod +x ~/.claude/statusline.shBit wykonywalności jest wymagany. Claude Code uruchamia polecenie przez powłokę, więc skrypt bez +x kończy się błędem Permission denied, nie generuje wyjścia na standardowe wyjście (stdout), a wiersz pozostaje pusty bez widocznego komunikatu o błędzie.
jq służy do parsowania JSON w wierszu poleceń i nie jest zainstalowane na czystym serwerze Ubuntu:
sudo apt update && sudo apt install -y jqNastępnie wskaż ustawienie na ten skrypt, używając pierwszego bloku settings.json powyżej.
Przetestuj skrypt przed wdrożeniem
Uruchom go dwukrotnie ręcznie. Najpierw przy użyciu standardowego obiektu sesji:
echo '{"model":{"display_name":"Opus"},"workspace":{"current_dir":"/srv/api"},"session_id":"t1"}' | ~/.claude/statusline.shOtrzymasz nazwę hosta, a następnie /srv/api oraz Opus. Żadna gałąź nie zostanie wyświetlona, ponieważ /srv/api na Twojej maszynie prawdopodobnie nie jest repozytorium git.
Następnie wykonaj test degradacji, który jest często pomijany:
echo '{}' | ~/.claude/statusline.shPusty obiekt to najgorszy przypadek, jaki może wystąpić przy zmianie schematu. Wiersz nadal wyświetla: nazwę hosta, bieżący katalog z $PWD oraz słowo claude w miejscu nazwy modelu. Nic nie ulega awarii i nie jest wyświetlane null. Skrypt, który przechodzi ten test, przetrwa zmianę nazwy pola, ponieważ dla skryptu zmiana nazwy pola oraz jego brak są traktowane jako to samo zdarzenie.
Co powinno być widoczne
Linia statusu jest renderowana w osobnym wierszu nad wbudowanymi odznakami stopki i nie zastępuje ich. W poprawnie działającej konfiguracji jest to jeden wiersz: krótka nazwa hosta w kolorze cyjan, następnie katalog roboczy ze skróconą ścieżką do katalogu domowego jako ~, potem nazwa gałęzi w kolorze żółtym, gdy katalog jest repozytorium git, a na końcu nazwa modelu w przyciemnionym kolorze. Powinno to wyglądać podobnie do web-01 ~/api main Opus, z tymi czterema kolorowymi elementami.
Wiersz uruchamia skrypt ponownie przy rozpoczęciu sesji, w tym przy wznawianiu, po nadejściu nowej wiadomości od asystenta, po zakończeniu /compact, przy zmianie trybu uprawnień, przy przełączeniu trybu vim oraz przy tyknięciu refreshInterval, jeśli zostało skonfigurowane. Aktualizacje są opóźniane o 300 ms (debouncing), więc seria zmian powoduje jednokrotne uruchomienie skryptu. Pasek ukrywa się podczas autouzupełniania, wyświetlania menu pomocy oraz monitów o uprawnienia, a następnie powraca.
Dlaczego nazwa hosta powinna znajdować się na początku
Gdy agenci działają na więcej niż jednym serwerze, terminal jest jedynym źródłem informacji o lokalizacji, a terminale bywają mylące. Otwarcie drugiego połączenia ssh wewnątrz panelu tmux często skutkuje zachowaniem starej nazwy w tytule okna, ponieważ tytuł jest ustawiany przez powłokę, która nie otrzymała informacji o zmianie kontekstu. Pozostawienie Claude Code działającego w odłączonej sesji tmux na VPS i powrót do niej po dniu pracy sprawia, że na ekranie nic nie odróżnia serwera budowania od maszyny produkcyjnej.
Linia statusu różni się tym, że jest renderowana bezpośrednio przez Claude Code, dla każdej sesji z osobna, na podstawie danych przechowywanych przez tę sesję. Nie może zostać odziedziczona z niewłaściwego panelu ani pozostać nieaktualna z powodu nieodświeżonego znaku zachęty powłoki. Informuje ona dokładnie o tym, na której maszynie agent zapisuje pliki.
Należy przypisać każdemu serwerowi własny kolor, aby rozpoznawać go przed przeczytaniem nazwy. Wystarczy dodać dwie linie powyżej przypisania LINE=:
CODE=$(printf '%s' "$HOST" | cksum | cut -d' ' -f1)
HOST_COLOR=$(printf '\033[%dm' "$((31 + CODE % 6))")Następnie należy użyć ${HOST_COLOR} zamiast ${CYAN}. cksum wypisuje sumę kontrolną nazwy hosta, dzięki czemu dana nazwa zawsze mapuje się na ten sam kolor z zakresu 31 do 36, czyli od czerwonego do cyjanu. Skopiowanie tego samego skryptu na każdą maszynę sprawia, że każda z nich automatycznie nadaje sobie etykietę.
Katalog zajmuje swoje miejsce z tego samego powodu. /srv/api i /srv/api-staging dzieli jedno naciśnięcie klawisza w poleceniu ssh, a w skutkach dzieli je cały incydent. Model i gałąź to dwa kolejne elementy warte zajmowanego miejsca: model informuje, którą sesję wznowiono, a gałąź wskazuje, czy agent zamierza dokonać commita do main.
Mały ekran sprawia, że wszystkie te informacje stają się bardziej istotne, ponieważ nie ma tytułu okna, na którym można by polegać. Jeśli taka jest Twoja konfiguracja, sprawdź obsługę Claude Code z poziomu telefonu.
Optymalizacja szybkości skryptu
Skrypt jest uruchamiany przy każdej wiadomości asystenta, a Claude Code przerywa trwające wykonanie w momencie nadejścia nowej aktualizacji. Wolny skrypt powoduje wyświetlanie nieaktualnych danych lub brak tekstu.
Każde wywołanie jq kosztuje kilka milisekund. Część git jest najbardziej czasochłonna: git status w dużym repozytorium z zimną pamięcią podręczną zajmuje setki milisekund. Powyższy skrypt celowo unika git status i wywołuje git branch --show-current, który odczytuje .git/HEAD i kończy działanie natychmiast.
W przypadku dodawania bardziej złożonych operacji, należy buforować wyniki w pliku i odświeżać je co kilka sekund. Klucz pliku powinien być powiązany z sesją:
CACHE="/tmp/statusline-$(field '.session_id')"Należy używać session_id, a nie $$. $$ to identyfikator procesu skryptu, który jest inny przy każdym wywołaniu, więc pamięć podręczna oparta na tym kluczu nigdy nie przyniesie trafień, co wymusza pełny koszt operacji za każdym razem. session_id jest stałe dla całej sesji i unikalne dla różnych sesji, dzięki czemu dwie sesje Claude Code w dwóch różnych repozytoriach nie odczytają wzajemnie swoich buforowanych nazw gałęzi.
Warto znać jeszcze jedno ograniczenie: tput cols nie działa wewnątrz skryptu paska stanu. Claude Code przechwytuje wyjście zamiast podłączać skrypt do terminala, więc mechanizm wykrywania szerokości nie ma danych do pomiaru. W wersji v2.1.153 i nowszych, Claude Code ustawia zmienne środowiskowe COLUMNS oraz LINES przed uruchomieniem polecenia, dlatego należy odczytywać $COLUMNS w celu określenia ilości danych do wyświetlenia.
Dlaczego pasek stanu pozostaje pusty
Nic się nie wyświetla. Sprawdź bit wykonywalności za pomocą ls -l ~/.claude/statusline.sh, a następnie uruchom skrypt ręcznie, używając powyższych przykładowych danych wejściowych. Jeśli skrypt wypisuje linię w powłoce, ale nie w Claude Code, zacznij od claude --debug, co zarejestruje kod wyjścia oraz stderr pierwszego uruchomienia paska stanu w sesji.
Dziennik debugowania wskazuje Status line command skipped: workspace trust not accepted. Pasek stanu wykonuje polecenie powłoki, więc podlega tym samym ograniczeniom zaufania obszaru roboczego co hooki. Dopóki nie zaakceptujesz okna dialogowego zaufania dla danego katalogu, polecenie nie zostanie uruchomione. Jest to częste zjawisko na serwerach VPS, gdzie każdy nowy klon jest katalogiem, którego Claude Code jeszcze nie rozpoznał. Uruchom ponownie Claude Code w tym katalogu i zaakceptuj okno dialogowe.
Wszystko jest puste, a disableAllHooks jest ustawione. Opcja "disableAllHooks": true w pliku settings.json również wyłącza pasek stanu, ponieważ korzysta z tej samej bramki wykonywania powłoki. Usuń ją lub ustaw na false.
Wiersz wypisuje null. Selektor jq dotarł do klucza, który nie istnieje lub ma wartość null, a jq -r wypisuje null jako cztery znaki null. Dodaj // empty dla tekstu oraz // 0 dla liczb.
Wiersz staje się pusty zaraz po edycji skryptu. Polecenie, które kończy się niezerowym kodem wyjścia lub nic nie wypisuje, czyści wiersz. Częstą przyczyną jest ostatnia linia typu [ -n "$BRANCH" ] && LINE="...", która zwraca kod 1, gdy gałąź jest pusta, co powoduje zakończenie całego skryptu z tym kodem. Umieść printf na końcu lub dodaj exit 0.
Kody sterujące wyświetlają się jako tekst dosłowny, taki jak \e]8;; na pasku. Użyj printf '%b' zamiast echo -e. Klikalne linki OSC 8 wymagają terminala, który je obsługuje; tmux lub SSH mogą usuwać te sekwencje, dlatego zwykły kolor jest bezpieczniejszym wyborem w przypadku pracy zdalnej.
Prawa strona wiersza jest ucięta. Powiadomienia systemowe oraz licznik tokenów w trybie szczegółowym współdzielą ten wiersz od prawej strony, a zbyt wąski terminal powoduje nakładanie się elementów. Utrzymuj dane wyjściowe w krótkiej formie. Aby uzyskać dokładne rozliczenie użycia zamiast liczby na pasku, zobacz jak Claude Code zlicza tokeny.
FAQ
Gdzie znajduje się ustawienie paska stanu Claude Code?
W settings.json, jako blok statusLine z type ustawionym na "command" oraz command ustawionym na ścieżkę do skryptu lub polecenie powłoki. Ustawienia użytkownika znajdują się w ~/.claude/settings.json i dotyczą każdego projektu na danej maszynie. Ustawienia projektu znajdują się w .claude/settings.json wewnątrz repozytorium i mają pierwszeństwo dla danego katalogu. Ustawienia przeładowują się automatycznie, ale zmiana staje się widoczna dopiero przy kolejnym wyzwalaczu aktualizacji, na przykład przy następnej wiadomości.
Dlaczego pasek stanu Claude Code jest pusty?
Prawie wszystkie przypadki wynikają z czterech przyczyn. Skrypt nie posiada bitu wykonywalności, więc powłoka zwraca Permission denied i nic nie trafia do stdout. Okno dialogowe zaufania do obszaru roboczego nie zostało zaakceptowane, a claude --debug rejestruje Status line command skipped: workspace trust not accepted. disableAllHooks ma wartość true, co wyłącza pasek stanu w ramach tego samego mechanizmu. Ewentualnie skrypt kończy działanie z kodem innym niż zero, co powoduje wyczyszczenie wiersza. Należy przetestować to ręcznie: echo '{}' | ~/.claude/statusline.sh musi wyprowadzić jakikolwiek tekst.
Czy JSON paska stanu zawiera nazwę gałęzi git?
Nie. JSON zawiera stan sesji, taki jak model, katalogi obszaru roboczego, liczby okna kontekstowego oraz koszt. Żadne z tych pól nie raportuje danych git. Gałąź na pasku pochodzi z własnego skryptu wywołującego git branch --show-current. Należy przekazać katalog z JSON za pomocą git -C "$DIR", aby gałąź zawsze odpowiadała katalogowi wyświetlanemu na pasku.
Czy pasek stanu zużywa tokeny lub spowalnia sesję?
Nie zużywa tokenów, ponieważ skrypt działa lokalnie, a jego wynik nigdy nie jest wysyłany do modelu. Za szybkość odpowiada użytkownik. Polecenie uruchamia się przy każdej wiadomości asystenta z opóźnieniem 300 ms (debounce), a Claude Code anuluje trwające uruchomienie, gdy pojawi się nowa aktualizacja, więc skrypt trwający pełną sekundę wyświetli nieaktualny tekst. Należy unikać git status w dużych repozytoriach i buforować wszelkie wolne operacje w pliku kluczowanym przez session_id.
Jak wyświetlić inny pasek stanu na każdym serwerze?
Należy użyć jednego skryptu i pozwolić mu odczytywać nazwę maszyny. Powyższy skrypt wypisuje $HOSTNAME z hostname -s jako wartością domyślną, więc ten sam plik skopiowany na każdą maszynę poprawnie etykietuje każdą z nich, a sztuczka z kolorem sumy kontrolnej nadaje każdej nazwie hosta własny kolor. Jeśli jeden serwer wymaga innego układu, należy umieścić blok statusLine w ustawieniach projektu repozytorium, w którym pracujesz na danej maszynie, ponieważ ustawienia projektu nadpisują ustawienia użytkownika dla tego katalogu.