TutorielIA

La barre d'état de Claude Code est un script. Ne la figez pas.

Configurer la barre d'état de Claude Code : JSON sur stdin, settings.json et les champs qui comptent. Un générateur copie un prompt, pas un script figé.

Manuel Hedinger
8 min de lecture

Je compacte trop tard. La limite de cinq heures, je la vois quand la réponse suivante est un mur payant. Quel modèle j'ai changé il y a trois messages, je l'ai déjà oublié. Tout ça est déjà dans la session. Claude Code ne le met juste pas là où je regarde.

C'est à ça que sert la barre d'état.

La réponse courte

La barre d'état est une ligne au-dessus des badges du pied. Claude Code lance une commande, lui envoie la session en cours en JSON sur stdin, et affiche ce que la commande écrit sur stdout. Ça ne consomme pas de tokens API. Ça ne remplace pas les badges. Ça masque en revanche la plupart des raccourcis clavier — esc to interrupt, ? for shortcuts, l'invite maintenir-espace-pour-parler. Si vous vivez avec, c'est le prix.

Trois voies. Décrivez la ligne avec /statusline et laissez Claude écrire le script dans ~/.claude/. Mettez vous-même un objet statusLine dans ~/.claude/settings.json. Ou composez la mise en page dans le générateur de barre d'état et collez le prompt. Le générateur n'émet pas de script. Un script publié dans un article est périmé dès qu'Anthropic ajoute un champ. Un prompt se lit contre la documentation que Claude Code a déjà.

Le tutoriel officiel est Customize your status line. Ce qui suit est la version que j'aurais voulu avoir : quoi afficher, quoi laisser, et les endroits qui échouent sans bruit.

Trois voies pour une barre d'état : la décrire, écrire settings.json, ou composer un prompt dans le générateur.

Demander à /statusline

Par exemple :

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

Claude génère un script sous ~/.claude/ et écrit les réglages. Approuvez les invites d'édition de fichier s'il les demande. C'est le bon premier geste si vous n'avez jamais eu de barre d'état et que le fonctionnement interne vous est encore égal.

Pour l'enlever plus tard, /statusline delete (ou clear, ou remove it) suffit. Ou supprimez vous-même le champ statusLine de settings.json.

Écrire les réglages

Les réglages utilisateur vivent dans ~/.claude/settings.json. Ceux du projet marchent aussi. La forme est petite :

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

type vaut "command". command est un chemin de script ou un bout de shell. Les réglages se rechargent à l'enregistrement ; vous ne redémarrez pas Claude Code.

padding est un espace horizontal supplémentaire en caractères, en plus de la gouttière intégrée. Défaut 0.

refreshInterval relance la commande toutes les N secondes, en plus des mises à jour déclenchées par des événements. Minimum 1. Mettez-le pour une horloge, ou quand des sous-agents en arrière-plan changent l'état git pendant que la session principale est idle. Laissez-le vide si seules les réponses vous intéressent.

hideVimModeIndicator masque le -- INSERT -- intégré sous le prompt. Activez-le quand votre script affiche déjà vim.mode, pour ne pas voir le mode deux fois.

La commande tourne dans un shell, donc une ligne suffit pour un premier contrôle :

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

Il vous faudra jq. Sous Windows, écrivez le chemin avec des barres obliques (C:/Users/you/.claude/statusline.sh). Git Bash traite les barres inverses comme des échappements et la commande échoue sans rien à l'écran.

Composez, puis laissez Claude écrire le fichier

Je n'archive pas de gist de statusline.sh. Le JSON sur stdin grossit. Le script de l'an dernier ignore rate_limits, pr.kind pour GitLab, et COLUMNS. Le suivi que je veux vraiment, c'est « now make the bar wider » ou « add the open PR ».

C'est pour ça que le générateur de barre d'état copie un prompt, pas un fichier. Vous glissez les segments voulus — répertoire, branche, modèle, contexte, les limites 5 h et 7 j, le coût, une horloge, les lignes changées, la PR, le worktree —, vous choisissez une palette, et vous collez le prompt anglais dans une session. Claude écrit le script, pointe settings.json dessus, et reste là pour le prochain ajustement.

Ça tourne dans le navigateur. Rien de ce que vous arrangez n'est envoyé quelque part.

Comment les données circulent

Claude Code sérialise la session en cours et l'envoie dans un tube. Votre commande imprime une ligne. Cette ligne est la rangée.

Claude Code écrit la session en JSON, votre commande imprime une ligne, la rangée apparaît au-dessus du pied. Pas de tokens.

La commande s'exécute une fois au démarrage d'une session, y compris à la reprise. Ensuite elle relance quand :

  • un nouveau message assistant arrive
  • /compact se termine
  • le mode de permission change
  • le mode vim bascule
  • vous changez command dans les réglages
  • un minuteur refreshInterval se déclenche, si vous en avez mis un

Les mises à jour sont regroupées à 300 ms. Un changement de command lui-même saute ce délai et part tout de suite. Si une nouvelle mise à jour arrive pendant que le script tourne encore, l'exécution en cours est annulée. Modifiez le script sur disque et le prochain déclenchement le reprend ; pas de rechargement en plus.

Les mises à jour par événements se taisent quand la session principale est idle, par exemple pendant qu'un coordinateur attend des sous-agents en arrière-plan. C'est le cas d'usage de refreshInterval.

Vous pouvez imprimer plusieurs lignes. Vous pouvez utiliser des couleurs ANSI. Vous pouvez envelopper du texte dans des séquences OSC 8 pour le rendre cliquable (Cmd-clic sur macOS, Ctrl-clic ailleurs) dans iTerm2, Kitty ou WezTerm. Terminal.app ne fait pas de liens cliquables. Si les séquences apparaissent en \e]8;; littéral, utilisez printf '%b', pas echo -e.

N'appelez pas tput cols dans le script. Claude Code capture stdout au lieu de vous attacher au terminal, donc la détection de largeur depuis l'intérieur du processus est aveugle. Lisez COLUMNS et LINES. Ils sont positionnés avant l'exécution, à partir de la version 2.1.153.

La rangée se masque pendant l'autocomplétion, le menu d'aide et les invites de permission. Hors plein écran, les notifications partagent la même rangée et vous tronquent sur un terminal étroit.

Ce qui vaut d'être affiché

Le JSON est gros. Sur une rangée de 80 colonnes, l'essentiel est du bruit.

Contexte. context_window.used_percentage est le champ à prendre. Il se calcule uniquement à partir des tokens d'entrée (input_tokens + cache_creation_input_tokens + cache_read_input_tokens). Les tokens de sortie n'y sont pas. Si vous calculez le pourcentage vous-même à partir de current_usage, utilisez la même formule, sinon vous serez en désaccord avec /context. current_usage vaut null avant le premier appel API, et de nouveau après /compact jusqu'à ce que la réponse suivante le remplisse. Idem pour les champs de pourcentage en début de session. Repli en jq : // 0.

La fenêtre par défaut fait 200k tokens, ou 1M sur les modèles à contexte étendu. exceeds_200k_tokens est un seuil fixe, pas « la fenêtre est pleine ».

Coût. cost.total_cost_usd est une estimation côté client. Ce n'est pas votre facture. Elle revient à 0 $ sur /clear (avant v2.1.211 elle se reportait, ce qui prêtait à confusion). total_duration_ms est l'horloge murale depuis le début de la session ; total_api_duration_ms est le temps passé à attendre l'API.

Limites de débit. rate_limits.five_hour et rate_limits.seven_day existent pour Claude.ai Pro/Max après la première réponse API. Chaque fenêtre peut manquer de son côté. L'absence veut dire « ne dessine pas le segment », pas 0 %.

Git. Il n'y a pas de git.branch dans le JSON. Vous lancez git vous-même. C'est lent dans un gros dépôt, et le script tourne souvent, donc mettez en cache. Utilisez session_id dans le nom du fichier de cache, pas $$ ni os.getpid() : ils changent à chaque invocation et le cache ne touche jamais. Des sessions concurrentes dans des dépôts différents ne doivent pas partager un fichier.

workspace.git_worktree est renseigné quand vous êtes dans un worktree lié. worktree.* est un autre objet, présent seulement pendant une session worktree de Claude Code.

Le reste que j'utilise vraiment. model.display_name plus effort.level. workspace.current_dir (même valeur que cwd ; préférez le champ imbriqué). pr.number et pr.review_state quand une PR ou une merge request GitLab est ouverte sur la branche. session_name si vous avez nommé la session ; le nom par défaut du style my-app-3f ne le remplit pas. vim.mode si vous utilisez le mode vim.

Un champ absent n'est pas la même chose qu'un champ à null. Traitez les deux.

Un premier script

Bash, sur macOS et Linux. Rendez-le exécutable (chmod +x ~/.claude/statusline.sh) et pointez command dessus.

#!/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"

Testez sans ouvrir Claude Code :

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

Gardez la ligne imprimée courte. La barre n'est pas large, et sur un terminal étroit les notifications à droite la mangent.

Quand elle reste vide

La liste habituelle, dans l'ordre où je vérifie :

  1. Le script est-il exécutable, et écrit-il sur stdout plutôt que stderr ?
  2. Avez-vous accepté la boîte de dialogue de confiance du workspace ? Tant que non, la rangée reste vide et claude --debug journalise que la commande a été ignorée.
  3. Sous Windows, une barre inverse dans le chemin a-t-elle été avalée par Git Bash ?
  4. Regardez-vous des nulls avant la première réponse ? Des replis, puis attendre un tour.
  5. Quelqu'un a-t-il mis disableAllHooks ? Hors réglages gérés, ça désactive une barre utilisateur. allowManagedHooksOnly dans les réglages de l'organisation ignore la vôtre sans prévenir.

claude --debug journalise le code de sortie et le stderr de la première invocation d'une session. Demander à Claude d'exécuter la commande statusLine contre votre fichier de réglages fait aussi apparaître l'erreur plus vite que de fixer une rangée vide.

Si après plusieurs messages vous voyez -- ou des valeurs vides, redémarrez une fois. Si les liens OSC 8 s'affichent en texte, vérifiez le terminal, puis essayez FORCE_HYPERLINK=1 claude.

Ce que je fais tourner vraiment

Trois rangées. Répertoire et branche à gauche, une horloge à droite. Modèle à gauche de la rangée deux, contexte à droite. La limite 5 h sur la rangée trois, la limite 7 j en face. C'est le préréglage « Mine » du générateur. Je tiens à compacter avant que la fenêtre soit pleine, et au plafond hebdomadaire avant vendredi après-midi.

Je ne versionne toujours pas le script. Quand je veux un autre segment, je colle un nouveau prompt. Dans le générateur je décide de la mise en page. Claude Code écrit le fichier.

Si vous voulez que le pied fasse pousser des badges cliquables quand un ID apparaît dans la conversation, c'est un autre réglage : footerLinksRegexes. Pas de script.

Si une barre d'état vous résiste et que vous préférez ne pas passer la soirée sur des codes ANSI, écrivez-moi. Parfois la réponse est une ligne jq de 40 caractères.