La barra de estado de Claude Code es un script. No la congeles.
Cómo configurar la barra de estado de Claude Code: JSON por stdin, settings.json y los campos que importan. Un generador copia un prompt, no un script congelado.
Manuel Hedinger
Compacto demasiado tarde. El límite de cinco horas lo noto cuando la siguiente respuesta es un muro de pago. Qué modelo cambié hace tres mensajes, ya lo he olvidado. Todo eso está ya en la sesión. Claude Code simplemente no lo pone donde miro.
Para eso está la barra de estado.
La respuesta corta
La barra de estado es una fila encima de las insignias del pie. Claude Code
ejecuta un comando, le pasa la sesión actual como JSON por stdin y pinta lo
que el comando escribe en stdout. No gasta tokens de API. No sustituye las
insignias. Sí oculta la mayoría de los atajos de teclado — esc to interrupt,
? for shortcuts, el aviso de mantener espacio para hablar — así que si vives
de ellos, ese es el precio.
Hay tres caminos. Describe la línea con /statusline y deja que Claude
escriba el script en ~/.claude/. Pon tú un objeto statusLine en
~/.claude/settings.json. O monta el diseño en el
generador de barra de estado y pega el
prompt. El generador no emite un script. Un script publicado en un post se
queda viejo en cuanto Anthropic añade un campo. Un prompt se lee contra la
documentación que Claude Code ya tiene.
La guía oficial es Customize your status line. Lo que sigue es la versión que me habría gustado tener: qué mostrar, qué dejar quieto y las partes que fallan en silencio.

Preguntar a /statusline
Algo así:
/statusline show model name and context percentage with a progress barClaude genera un script en ~/.claude/ y escribe los ajustes. Confirma los
prompts de edición de archivo si te los pide. Es el primer paso correcto si
nunca has tenido una barra de estado y todavía no te importa cómo funciona por
dentro.
Para quitarla después, /statusline delete (o clear, o remove it)
sirve. O borra tú el campo statusLine de settings.json.
Escribir los ajustes
Los ajustes de usuario viven en ~/.claude/settings.json. Los del proyecto
también valen. La forma es pequeña:
{
"statusLine": {
"type": "command",
"command": "~/.claude/statusline.sh",
"padding": 2
}
}type es "command". command es la ruta de un script o un fragmento de
shell. Los ajustes se recargan al guardar; no reinicias Claude Code.
padding es espacio horizontal extra en caracteres, encima del margen
incorporado. Por defecto 0.
refreshInterval vuelve a ejecutar el comando cada N segundos, además de las
actualizaciones por eventos. Mínimo 1. Ponlo para un reloj, o cuando
subagentes en segundo plano cambian el estado de git mientras la sesión
principal está idle. Déjalo sin poner si solo te importan las respuestas.
hideVimModeIndicator oculta el -- INSERT -- de fábrica bajo el prompt.
Actívalo cuando tu script ya imprime vim.mode, para no ver el modo dos
veces.
El comando corre en una shell, así que para el primer check basta una línea:
{
"statusLine": {
"type": "command",
"command": "jq -r '\"[\\(.model.display_name)] \\(.context_window.used_percentage // 0)% context\"'"
}
}Vas a necesitar jq. En Windows, escribe la ruta con
barras inclinadas (C:/Users/you/.claude/statusline.sh). Git Bash trata las
barras invertidas como escapes y el comando falla sin nada en pantalla.
Compónlo, y que Claude escriba el archivo
No guardo un gist de statusline.sh. El JSON de stdin crece. El script del
año pasado no conoce rate_limits, ni pr.kind para GitLab, ni COLUMNS.
El seguimiento que de verdad quiero es «now make the bar wider» o «add the
open PR».
Por eso el generador de barra de estado copia un prompt, no un archivo. Arrastras los segmentos que quieres — directorio, rama, modelo, contexto, los límites de 5 horas y 7 días, coste, un reloj, líneas cambiadas, el PR, el worktree —, eliges una paleta y pegas el prompt en inglés en una sesión. Claude escribe el script, apunta settings.json hacia él y sigue ahí para el siguiente ajuste.
Corre en el navegador. Nada de lo que colocas se sube a ningún sitio.
Cómo se mueven los datos
Claude Code serializa la sesión en curso y la mete por un pipe. Tu comando imprime una línea. Esa línea es la fila.

El comando corre una vez al arrancar una sesión, también al reanudarla. Después vuelve a correr cuando:
- llega un mensaje nuevo del asistente
- termina
/compact - cambia el modo de permisos
- se conmuta el modo vim
- cambias
commanden los ajustes - dispara un temporizador
refreshInterval, si lo pusiste
Las actualizaciones se agrupan a 300ms. Un cambio en command se salta esa
espera y corre al momento. Si llega una actualización nueva mientras el script
sigue en marcha, se cancela la ejecución en vuelo. Editas el script en disco
y el siguiente disparo lo recoge; no hay recarga extra.
Las actualizaciones por eventos se callan cuando la sesión principal está
idle, por ejemplo mientras un coordinador espera a subagentes en segundo
plano. Para eso está refreshInterval.
Puedes imprimir varias líneas. Puedes usar colores ANSI. Puedes envolver texto
en secuencias OSC 8 para que sea clicable (Cmd-clic en macOS, Ctrl-clic en
el resto) en iTerm2, Kitty o WezTerm. Terminal.app no hace enlaces clicables.
Si las secuencias aparecen como \e]8;; literal, usa printf '%b', no
echo -e.
No llames a tput cols dentro del script. Claude Code captura stdout en vez
de engancharte al terminal, así que la detección de ancho desde dentro del
proceso va a ciegas. Lee COLUMNS y LINES. Se asignan antes de correr el
comando, a partir de la versión 2.1.153.
La fila se oculta durante el autocompletado, el menú de ayuda y los prompts de permiso. Fuera de pantalla completa, las notificaciones comparten la misma fila y te recortan en un terminal estrecho.
Qué merece la pena mostrar
El JSON es grande. En una fila de 80 columnas la mayor parte es ruido.
Contexto. context_window.used_percentage es el campo. Se calcula solo
con tokens de entrada (input_tokens + cache_creation_input_tokens + cache_read_input_tokens). Los tokens de salida no entran. Si calculas el
porcentaje tú a partir de current_usage, usa la misma fórmula o no
coincidirás con /context. current_usage es null antes de la primera
llamada a la API, y otra vez después de /compact hasta que la siguiente
respuesta lo rellena. Lo mismo para los campos de porcentaje al principio de
una sesión. Fallback en jq: // 0.
La ventana por defecto es 200k tokens, o 1M en modelos de contexto extendido.
exceeds_200k_tokens es un umbral fijo, no «la ventana está llena».
Coste. cost.total_cost_usd es una estimación en el cliente. No es tu
factura. Vuelve a 0 $ con /clear (antes de v2.1.211 se arrastraba, y
confundía). total_duration_ms es reloj de pared desde que arrancó la
sesión; total_api_duration_ms es el tiempo esperando a la API.
Límites de ritmo. rate_limits.five_hour y rate_limits.seven_day
existen para Claude.ai Pro/Max después de la primera respuesta de la API.
Cada ventana puede faltar por su cuenta. La ausencia es «no dibujes el
segmento», no 0 %.
Git. En el JSON no hay git.branch. Ejecutas git tú. Eso es lento en
un repo grande, y el script corre a menudo, así que cachea. Usa session_id
en el nombre del archivo de caché, no $$ ni os.getpid(): cambian en cada
invocación y la caché nunca acierta. Sesiones concurrentes en repos distintos
no deben compartir archivo.
workspace.git_worktree está puesto cuando estás en un worktree enlazado.
worktree.* es otro objeto, presente solo durante una sesión worktree de
Claude Code.
Lo demás que uso de verdad. model.display_name más effort.level.
workspace.current_dir (el mismo valor que cwd; prefiere el campo
anidado). pr.number y pr.review_state cuando hay un PR o un merge request
de GitLab abierto en la rama. session_name si nombraste la sesión; el
nombre por defecto al estilo my-app-3f no lo rellena. vim.mode si usas el
modo vim.
Un campo ausente no es lo mismo que un campo en null. Trata los dos.
Un primer script
Bash, en macOS y Linux. Hazlo ejecutable (chmod +x ~/.claude/statusline.sh)
y apunta command hacia él.
#!/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"Pruébalo sin abrir Claude Code:
echo '{"model":{"display_name":"Opus"},"workspace":{"current_dir":"/home/user/project"},"context_window":{"used_percentage":25},"session_id":"test"}' | ./statusline.shMantén corta la línea impresa. La barra no es ancha, y en un terminal estrecho las notificaciones de la derecha se la comen.
Cuando se queda en blanco
La lista de siempre, en el orden en que yo miro:
- ¿El script es ejecutable y escribe en stdout, no en stderr?
- ¿Aceptaste el diálogo de confianza del workspace? Hasta entonces la fila
se queda vacía y
claude --debugregistra que se saltó el comando. - En Windows, ¿Git Bash se comió una barra invertida en la ruta?
- ¿Estás mirando nulos antes de la primera respuesta? Fallbacks, y espera un turno.
- ¿Alguien puso
disableAllHooks? Fuera de ajustes gestionados eso desactiva una barra de usuario.allowManagedHooksOnlyen ajustes de la organización ignora la tuya en silencio.
claude --debug registra el código de salida y el stderr de la primera
invocación de una sesión. Pedirle a Claude que ejecute el comando
statusLine contra tu archivo de ajustes también saca el error antes que
quedarte mirando una fila vacía.
Si después de varios mensajes ves -- o valores vacíos, reinicia una vez. Si
los enlaces OSC 8 se pintan como texto, mira el terminal y prueba
FORCE_HYPERLINK=1 claude.
Lo que yo hago correr
Tres filas. Directorio y rama a la izquierda, un reloj a la derecha. Modelo a la izquierda de la fila dos, contexto a la derecha. El límite de 5 horas en la fila tres, el de 7 días enfrente. Es el preset «Mine» del generador. Me importa compactar antes de que la ventana se llene, y el tope semanal antes del viernes por la tarde.
Sigo sin meter el script en el repo. Cuando quiero otro segmento pego un prompt nuevo. En el generador decido el diseño. Claude Code escribe el archivo.
Si quieres que el pie crezca insignias clicables cuando aparece un ID en la
conversación, eso es otro ajuste: footerLinksRegexes. Sin script.
Si una barra de estado te está dando guerra y no quieres pasar la noche con códigos ANSI, escríbeme. A veces la respuesta es una línea de jq de 40 caracteres.