Como configurar uma statusline do Claude Code em VPS
Configure statusLine para exibir hostname, diretório, branch Git e modelo sob o prompt. O script recebe JSON na entrada e imprime a saída na sessão correta.
O que uma statusline do Claude Code mostra
Uma statusline do Claude Code é uma linha apresentada por baixo do prompt. Ela mostra a saída de um script que você escreve. Adicione um bloco statusLine a settings.json e indique um comando nesse bloco. O Claude Code executa esse comando, envia o estado da sessão para ele como JSON na entrada padrão e apresenta na saída padrão o que o comando escrever.
Esse é todo o contrato. O seu script lê JSON da entrada padrão e escreve texto na saída padrão. Ele é executado na sua máquina. Nada do que escreve é enviado ao modelo, por isso não consome tokens.
Num laptop com um único projeto, isto é apenas decoração. Em três servidores, funciona como uma proteção contra erros. Todas as sessões do Claude Code têm o mesmo aspeto em todos os terminais. Por isso, quatro janelas SSH sem identificação podem fazer uma migração ser aplicada no servidor errado. Uma statusline que começa pelo hostname elimina esse tipo de erro.
Onde fica a definição statusLine em settings.json
Coloque-a nas definições do utilizador em ~/.claude/settings.json. Esta localização aplica-se a todos os projetos nessa máquina. As definições do projeto em .claude/settings.json, dentro de um repositório, também funcionam e têm precedência nesse diretório.
{
"statusLine": {
"type": "command",
"command": "~/.claude/statusline.sh"
}
}type é sempre "command". O valor command é executado através de uma shell, pelo que pode ser um caminho para um script ou um comando simples. Confirme o funcionamento da ligação antes de escrever qualquer script:
{
"statusLine": {
"type": "command",
"command": "hostname -s"
}
}Inicie o Claude Code e envie uma mensagem. A barra por baixo do prompt passa a mostrar o hostname curto do servidor. Se continuar vazia, o problema está na definição ou na caixa de diálogo de confiança, não no seu script. Consulte abaixo "Porque é que a statusline continua vazia".
Existem três chaves opcionais desde August 2026. padding adiciona espaçamento horizontal em caracteres e, por predefinição, é 0. refreshInterval volta a executar o comando a cada N segundos, além dos acionadores normais, com um mínimo de 1. Utilize-a apenas quando a linha mostrar um relógio ou outro valor que se altere enquanto a sessão está inativa. hideVimModeIndicator suprime o texto incorporado -- INSERT -- quando o seu próprio script já apresenta o modo vim.
Que dados o script da statusline recebe?
Não confie numa lista de campos que leia em qualquer lugar, incluindo esta página. Capture o objeto real que a sua versão envia. Escreva um script temporário que guarde o stdin num ficheiro:
cat > ~/.claude/statusline-capture.sh <<'EOF'
#!/bin/bash
cat > /tmp/statusline-input.json
echo "captured"
EOF
chmod +x ~/.claude/statusline-capture.shAponte statusLine.command para esse ficheiro, inicie uma sessão e envie uma mensagem. A barra lê captured. Agora veja o que chegou:
jq . /tmp/statusline-input.jsonAssim obtém a estrutura exata da sua compilação e pode repetir o processo sempre que uma atualização alterar alguma coisa.
As partes estáveis, conforme documentado em agosto de 2026, são objetos aninhados, e não chaves planas. model contém id e display_name. workspace contém current_dir e project_dir: current_dir indica onde a sessão está agora, project_dir indica onde foi iniciada, e os dois valores diferem quando o diretório de trabalho muda durante a sessão. O cwd de nível superior contém o mesmo valor que workspace.current_dir. context_window contém as contagens de tokens e um used_percentage pré-calculado. cost contém total_cost_usd e contadores de duração. session_id permanece estável durante toda a sessão e é exclusivo entre sessões, o que é importante para o armazenamento em cache mais tarde.
Três regras mantêm um script funcional durante alterações do esquema.
Algumas chaves estão ausentes, e não têm valor null. vim, agent, pr, worktree e effort aparecem apenas quando a funcionalidade correspondente está ativa. Ler .vim.mode com jq -r quando o modo vim está desativado imprime a cadeia literal null, e a barra mostra null ao utilizador. Acrescente // empty a cada seletor, para que uma chave ausente não imprima nada.
Alguns valores são null no início. context_window.used_percentage e context_window.current_usage têm o valor null antes da primeira resposta da API, e current_usage volta a ter o valor null depois de /compact, até que a chamada seguinte o preencha novamente. Por isso, uma percentagem do contexto na barra precisa de // 0; caso contrário, lê null durante os primeiros segundos de cada sessão. Antes de colocar esse número numa barra, é útil saber como a janela de contexto é realmente preenchida.
O branch do git não está no JSON. Nenhum campo o informa. Qualquer branch apresentado na barra vem do próprio script executar git.
Um script de statusline que degrada em vez de falhar
Esta é a versão pronta para copiar e colar. Ela mostra o hostname, o diretório de trabalho, a branch do git e o nome do modelo. Cada campo tem um fallback, portanto até um objeto JSON vazio produz uma linha utilizável.
#!/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"Todas as leituras passam por field, que acrescenta // empty. Assim, uma chave renomeada ou removida produz uma string vazia, e a linha seguinte fornece um valor padrão. O diretório usa workspace.current_dir, depois cwd e, por fim, $PWD como fallback. A branch vem de git -C "$DIR", e não de um git isolado. Assim, a branch corresponde sempre ao diretório mostrado pela barra.
Guarde o ficheiro e torne-o executável:
chmod +x ~/.claude/statusline.shO bit de execução é obrigatório. O Claude Code executa o comando através de uma shell. Por isso, um script sem +x falha com Permission denied, não produz stdout e deixa a linha em branco sem erro visível.
jq analisa JSON na linha de comandos e não está instalado num servidor Ubuntu novo:
sudo apt update && sudo apt install -y jqDepois, aponte a configuração para o script usando o primeiro bloco settings.json acima.
Teste o script antes de confiar nele
Execute-o manualmente duas vezes. Primeiro, com um objeto de sessão normal:
echo '{"model":{"display_name":"Opus"},"workspace":{"current_dir":"/srv/api"},"session_id":"t1"}' | ~/.claude/statusline.shVocê obtém o nome do host, depois /srv/api e, por fim, Opus. Nenhuma ramificação aparece, porque /srv/api na sua máquina provavelmente não é um repositório git.
Em seguida, faça o teste de degradação, que é o que as pessoas ignoram:
echo '{}' | ~/.claude/statusline.shUm objeto vazio é o pior caso que uma alteração de esquema pode fornecer. A linha continua a imprimir o nome do host, o diretório atual de $PWD e a palavra claude no lugar do nome do modelo. Nada falha e nada imprime null. Um script que passa neste teste continua a funcionar quando um campo é renomeado, porque, para o script, um campo renomeado e um campo ausente são o mesmo evento.
O que deve ver
A linha de estado é apresentada na sua própria linha, acima dos indicadores de rodapé incorporados, e não os substitui. Numa configuração funcional, há uma linha com quatro elementos: o nome curto do anfitrião em ciano, o diretório de trabalho com o diretório pessoal reduzido a ~, o nome do branch a amarelo quando o diretório é um repositório git e, por fim, o nome do modelo com intensidade reduzida. O resultado deve ser semelhante a web-01 ~/api main Opus, com estas quatro partes coloridas.
A linha volta a executar o seu script quando uma sessão é iniciada, incluindo ao retomar uma sessão, quando chega uma nova mensagem do assistente, depois de /compact terminar, quando o modo de permissões muda, quando o modo vim é ativado ou desativado e a cada intervalo de refreshInterval, se definir um. As atualizações são agrupadas durante 300 ms, portanto uma sequência rápida de alterações executa o script uma única vez. A barra fica oculta durante o preenchimento automático, o menu de ajuda e os pedidos de permissões e volta a aparecer depois.
Por que o hostname vem primeiro
Quando mantém agentes em mais de um servidor, o terminal é a única coisa que indica onde está, e os terminais enganam. Abra uma segunda ligação ssh a partir de um painel do tmux e o título da janela muitas vezes mantém o nome antigo, porque o título é definido por uma shell que nunca soube que mudou de servidor. Deixe o Claude Code em execução numa sessão tmux desanexada numa VPS e volte a ligar-se um dia depois: nada no ecrã distingue o servidor de build do servidor de produção.
A linha de estado é diferente porque é renderizada pelo próprio Claude Code, por sessão, a partir dos dados mantidos por essa sessão. Não pode ser herdada do painel errado nem ficar desatualizada porque um prompt da shell não foi atualizado. O que ela mostra é o servidor onde o agente está a escrever os ficheiros.
Atribua uma cor própria a cada servidor para o reconhecer antes de ler o nome. Duas linhas, inseridas acima da atribuição LINE=:
CODE=$(printf '%s' "$HOST" | cksum | cut -d' ' -f1)
HOST_COLOR=$(printf '\033[%dm' "$((31 + CODE % 6))")Depois, use ${HOST_COLOR} no lugar de ${CYAN}. cksum calcula uma soma de verificação do hostname, por isso um determinado nome corresponde sempre à mesma cor no intervalo de 31 a 36, de vermelho a ciano. Copie o mesmo script para todos os servidores e cada um identifica-se.
O diretório também é importante pelo mesmo motivo. /srv/api e /srv/api-staging ficam separados por uma tecla num comando ssh, mas podem representar incidentes completamente diferentes. O modelo e o branch são os outros dois elementos que justificam o espaço: o modelo indica qual sessão retomou, e o branch indica se o agente está prestes a fazer commit em main.
Um ecrã pequeno torna tudo isto ainda mais importante, porque não há um título de janela disponível como alternativa. Se esse for o seu caso, consulte controlar o Claude Code a partir de um telefone.
Mantenha o script rápido
O seu script é executado em cada mensagem do assistente, e o Claude Code cancela uma execução em andamento quando chega uma nova atualização. Por isso, um script lento mostra texto desatualizado ou não mostra texto.
Cada chamada jq custa alguns milissegundos. git é a parte que fica lenta: git status num repositório grande, com a cache fria, demora centenas de milissegundos. O script acima evita git status de propósito e chama git branch --show-current, que lê .git/HEAD e retorna imediatamente.
Se adicionar algo mais pesado, armazene o resultado num ficheiro e atualize-o a cada poucos segundos. Use a sessão como chave do ficheiro:
CACHE="/tmp/statusline-$(field '.session_id')"Use session_id, não $$. $$ é o ID do processo do seu script. Ele muda em cada execução, portanto uma cache baseada nele nunca é reutilizada e o custo total é pago todas as vezes. session_id permanece estável durante toda a sessão e é diferente entre sessões. Assim, duas sessões do Claude Code em dois repositórios não conseguem ler o nome da branch armazenado em cache pela outra sessão. As sessões permanecem isoladas dessa forma por definição. Para uma sessão entregar trabalho a outra, é necessário executar uma ação deliberada. É para isso que serve enviar uma mensagem de uma sessão do Claude Code para outra.
Há mais uma limitação importante: tput cols não funciona dentro de um script de statusline. O Claude Code captura a saída em vez de ligar o seu script ao terminal, portanto não há largura disponível para medir. A partir da versão v2.1.153, o Claude Code define as variáveis de ambiente COLUMNS e LINES antes de executar o comando. Leia $COLUMNS quando precisar de decidir quanto texto imprimir.
Por que a linha de estado fica vazia
Não aparece absolutamente nada. Verifique o bit de execução com ls -l ~/.claude/statusline.sh e execute o script manualmente com a entrada de teste acima. Se ele imprimir uma linha no shell, mas não no Claude Code, comece por claude --debug, que regista o código de saída e o stderr da primeira execução da linha de estado na sessão.
O log de depuração indica Status line command skipped: workspace trust not accepted. A linha de estado executa um comando shell, por isso está sujeita ao mesmo mecanismo de confiança do workspace que os hooks. Enquanto não aceitar a caixa de diálogo de confiança desse diretório, o comando não será executado. Isto é comum numa VPS, onde cada novo clone fica num diretório que o Claude Code ainda não conhece. Reinicie o Claude Code nesse diretório e aceite a caixa de diálogo.
Tudo fica vazio e disableAllHooks está definido. "disableAllHooks": true em settings.json também desativa a linha de estado, porque utiliza o mesmo mecanismo de execução do shell. Remova-o ou defina-o como false.
A linha imprime null. Um seletor jq chegou a uma chave inexistente ou nula, e jq -r imprime null como os quatro caracteres null. Adicione // empty para texto e // 0 para números.
A linha fica vazia logo depois de editar o script. Um comando que termina com um código diferente de zero ou não imprime nada deixa a linha vazia. A causa habitual é uma linha final como [ -n "$BRANCH" ] && LINE="...", que termina com o código 1 quando o ramo está vazio e faz com que esse seja o código de saída de todo o script. Mantenha printf como última linha ou adicione exit 0.
Os códigos de escape aparecem como texto literal, por exemplo \e]8;; na barra. Use printf '%b' em vez de echo -e. Os links OSC 8 clicáveis também precisam de um terminal que os suporte, e o tmux ou o SSH podem remover essas sequências. Por isso, numa máquina remota, a cor simples é a opção mais segura.
O lado direito da linha fica cortado. As notificações do sistema e o contador de tokens do modo verboso partilham essa linha a partir da direita, e um terminal estreito não consegue acomodar os elementos sem sobreposição. Mantenha a saída curta. Para uma contabilização real da utilização, em vez de um número na barra, consulte como o Claude Code conta tokens.
FAQ
Onde fica a configuração da linha de estado do Claude Code?
Em settings.json, num bloco statusLine com type definido como "command" e command definido como um caminho de script ou um comando de shell. As configurações do utilizador ficam em ~/.claude/settings.json e aplicam-se a todos os projetos nessa máquina. As configurações do projeto ficam em .claude/settings.json dentro do repositório e têm precedência nesse diretório. As configurações são recarregadas automaticamente, mas uma alteração só fica visível no próximo gatilho de atualização, como a sua próxima mensagem.
Porque é que a minha linha de estado do Claude Code está vazia?
Quatro causas abrangem quase todos os casos. Falta ao script o bit de execução, por isso a shell devolve Permission denied e nada chega a stdout. O diálogo de confiança da área de trabalho nunca foi aceite, e claude --debug regista Status line command skipped: workspace trust not accepted. disableAllHooks é true, o que desativa a linha de estado com a mesma condição. Ou o script termina com um código diferente de zero, o que deixa a linha vazia. Teste-o primeiro manualmente: echo '{}' | ~/.claude/statusline.sh tem de apresentar algum conteúdo.
O JSON da linha de estado inclui o branch do git?
Não. O JSON contém o estado da sessão, como o modelo, os diretórios da área de trabalho, os números da janela de contexto e o custo. Não contém qualquer informação sobre o git. Um branch na sua barra vem do seu próprio script, que chama git branch --show-current. Passe o diretório do JSON com git -C "$DIR", para que o branch corresponda sempre ao diretório apresentado pela barra.
A linha de estado consome tokens ou torna a sessão mais lenta?
Não consome tokens, porque o script é executado localmente e o respetivo output nunca é enviado para o modelo. A velocidade depende de si. O comando é executado em cada mensagem do assistente, com um debounce de 300 ms, e o Claude Code cancela uma execução em curso quando chega uma nova atualização. Por isso, um script que demore um segundo completo apresenta texto desatualizado. Evite git status em repositórios grandes e coloque em cache os dados lentos num ficheiro indexado por session_id.
Como mostro uma linha de estado diferente em cada servidor?
Mantenha um único script e faça-o ler a máquina. O script acima apresenta $HOSTNAME, usando hostname -s como fallback. Assim, o mesmo ficheiro copiado para cada servidor identifica corretamente cada máquina, e o truque da cor baseada no checksum atribui uma cor própria a cada hostname. Se um servidor precisar de um layout diferente, coloque um bloco statusLine nas configurações do projeto do repositório em que trabalha nesse servidor, porque as configurações do projeto têm precedência sobre as configurações do utilizador nesse diretório.