====== Status line pro Claude Code ======
//Vytvořeno: **** | Aktualizováno: **~~LASTMOD~~**//
[[ai:platformy:claude-code:start|Claude Code]] umí podle [[https://code.claude.com/docs/en/statusline|oficiální dokumentace]] vykreslovat vlastní status line přes externí příkaz. Tohle je moje aktuální konfigurace: hlavní ''statusLine'' ukazuje stav top-level session a samostatná ''subagentStatusLine'' přidává přehled běžících subagentů v agent panelu.
===== Co z toho získám =====
* Hlavní status line ukazuje pracovní adresář, model, session ID, využití context window, 5h a 7d limity a absolutní spotřebu tokenů.
* Subagent status line ukazuje každého subagenta jako vlastní řádek: agent, model, krátký popis, stav, tokeny, trend tokenů, rychlost tokenů za sekundu, běh od startu a případně pracovní adresář.
* Model subagenta se do panelu dostává přes konvenci v ''description'', protože Claude Code ho ve vstupních datech pro ''subagentStatusLine'' neposílá jako samostatné pole.
* Popisky subagentů jsou čitelné i při paralelním běhu více agentů.
===== Zapojení do konfigurace =====
V ''~/.claude/settings.json'' jsou zapojené dva skripty:
"statusLine": {
"type": "command",
"command": "bash ~/.claude/statusline-command.sh"
},
"subagentStatusLine": {
"type": "command",
"command": "bash ~/.claude/subagent-statusline.sh"
}
Skripty čtou JSON ze standardního vstupu, parsují ho přes ''jq'' a vrací barevný výstup přes ANSI sekvence.
===== Hlavní status line =====
Hlavní ''statusLine'' je vázaná na top-level Claude Code session. Nepřepíná se podle aktivního subagenta a nedostává data o tom, který subagent zrovna běží. K tomu slouží samostatná ''subagentStatusLine''.
Výstup hlavní status line vypadá zhruba takto:
~/project │ Sonnet 4.6 | sid: │ ctx:55% │ 5h:4% ↺ 00:50 │ 7d:45% ↺ Po 08:00 │ 220k/400k
Barvy jsou rozdělené takto:
* cwd → cyan + bold
* model → magenta
* sid → dim
* ''ctx'', ''5h'' a ''7d'' → zelená pod 70 %, žlutá od 70 %, červená od 90 %
* token counter ''XYZk/TOTALk'' → červeně a tučně při překročení 200k, jinak dim
==== Skript ''statusline-command.sh'' ====
#!/usr/bin/env bash
# Claude Code status line
# Order: cwd | model | context used | 5h limit | 7d limit
input=$(cat)
# ── helpers ────────────────────────────────────────────────────────────────────
RESET='\033[0m'
BOLD='\033[1m'
DIM='\033[2m'
RED='\033[31m'
YELLOW='\033[33m'
GREEN='\033[32m'
CYAN='\033[36m'
MAGENTA='\033[35m'
WHITE='\033[37m'
# ── 1) cwd ─────────────────────────────────────────────────────────────────────
cwd=$(echo "$input" | jq -r '.workspace.project_dir // .workspace.current_dir // .cwd // ""')
# Show path relative to home dir
home_dir="$HOME"
if [[ "$cwd" == "$home_dir" ]]; then
short_cwd="~"
elif [[ "$cwd" == "$home_dir/"* ]]; then
short_cwd="~/${cwd#$home_dir/}"
else
short_cwd="$cwd"
fi
[ "$short_cwd" = "/" ] && short_cwd="/"
# ── 2) model ───────────────────────────────────────────────────────────────────
model_name=$(echo "$input" | jq -r '.model.display_name // .model.id // ""')
session_id=$(echo "$input" | jq -r '.session_id // .conversation_id // empty')
# ── rate limit helpers ─────────────────────────────────────────────────────────
five_pct=$(echo "$input" | jq -r '.rate_limits.five_hour.used_percentage // empty')
five_rst=$(echo "$input" | jq -r '.rate_limits.five_hour.resets_at // empty')
week_pct=$(echo "$input" | jq -r '.rate_limits.seven_day.used_percentage // empty')
week_rst=$(echo "$input" | jq -r '.rate_limits.seven_day.resets_at // empty')
fmt_reset_short() {
local v="$1"
[ -z "$v" ] && return
# Try as epoch first, then as ISO-8601 string
if [[ "$v" =~ ^[0-9]+$ ]]; then
date -d "@$v" +%H:%M 2>/dev/null || date -r "$v" +%H:%M 2>/dev/null || echo "?"
else
date -d "$v" +%H:%M 2>/dev/null || echo "?"
fi
}
fmt_reset_long() {
local v="$1"
[ -z "$v" ] && return
if [[ "$v" =~ ^[0-9]+$ ]]; then
date -d "@$v" +"%a %H:%M" 2>/dev/null || date -r "$v" +"%a %H:%M" 2>/dev/null || echo "?"
else
date -d "$v" +"%a %H:%M" 2>/dev/null || echo "?"
fi
}
# Choose colour based on used %
pct_color_used() {
local used="$1"
[ -z "$used" ] && echo "$WHITE" && return
local u
u=$(printf "%.0f" "$used")
if [ "$u" -ge 90 ]; then echo "$RED"
elif [ "$u" -ge 70 ]; then echo "$YELLOW"
else echo "$GREEN"
fi
}
# ── 3) context used ────────────────────────────────────────────────────────────
ctx_used_tokens=$(echo "$input" | jq -r '.context_window.total_input_tokens // empty')
ctx_window_size=$(echo "$input" | jq -r '.context_window.context_window_size // empty')
ctx_pct=$(echo "$input" | jq -r '.context_window.used_percentage // empty')
ctx_str=""
if [ -n "$ctx_pct" ]; then
ctx_int=$(printf "%.0f" "$ctx_pct")
if [ "$ctx_int" -ge 90 ]; then
ctx_str="$(printf "${RED}${BOLD}ctx:%s%%!${RESET}" "$ctx_int")"
elif [ "$ctx_int" -ge 70 ]; then
ctx_str="$(printf "${YELLOW}ctx:%s%%${RESET}" "$ctx_int")"
else
ctx_str="$(printf "${GREEN}ctx:%s%%${RESET}" "$ctx_int")"
fi
fi
# ── assemble line ──────────────────────────────────────────────────────────────
parts=()
# 1) cwd
parts+=("$(printf "${CYAN}${BOLD}%s${RESET}" "$short_cwd")")
# 2) model + session id
if [ -n "$model_name" ] || [ -n "$session_id" ]; then
parts+=("$(printf "${MAGENTA}%s${RESET}" "$model_name")${session_id:+ $(printf "${DIM}| sid:%s${RESET}" "$session_id")}")
fi
# 3) context used (always shown when data available)
[ -n "$ctx_str" ] && parts+=("$ctx_str")
# 4) 5h rate limit (only when data is available)
if [ -n "$five_pct" ]; then
five_int=$(printf "%.0f" "$five_pct")
col=$(pct_color_used "$five_pct")
rst=$(fmt_reset_short "$five_rst")
parts+=("$(printf "${col}5h:%s%%%s${RESET}" "$five_int" "${rst:+ ↺ $rst}")")
fi
# 5) 7d rate limit (only when data is available)
if [ -n "$week_pct" ]; then
week_int=$(printf "%.0f" "$week_pct")
col=$(pct_color_used "$week_pct")
rst=$(fmt_reset_long "$week_rst")
parts+=("$(printf "${col}7d:%s%%%s${RESET}" "$week_int" "${rst:+ ↺ $rst}")")
fi
# 6) token counter — červeně od 200k
if [ -n "$ctx_used_tokens" ] && [ -n "$ctx_window_size" ] && [ "$ctx_window_size" -gt 0 ]; then
ctx_used_k=$(python3 -c "print(round($ctx_used_tokens / 1000))")
ctx_total_k=$(python3 -c "print(round($ctx_window_size / 1000))")
if [ "$ctx_used_tokens" -gt 200000 ]; then
parts+=("$(printf "${RED}${BOLD}%sk/%sk${RESET}" "$ctx_used_k" "$ctx_total_k")")
else
parts+=("$(printf "${DIM}%sk/%sk${RESET}" "$ctx_used_k" "$ctx_total_k")")
fi
fi
# ── print ──────────────────────────────────────────────────────────────────────
# Join with separator " │ "
sep="$(printf "${DIM} │ ${RESET}")"
out=""
for p in "${parts[@]}"; do
[ -z "$out" ] && out="$p" || out="${out}${sep}${p}"
done
printf "%b\n" "$out"
===== Subagent status line =====
''subagentStatusLine'' dostává pole ''tasks'' a pro každý task musí vypsat jeden JSON řádek ve tvaru:
{"id":"","content":"<řádek s ANSI>"}
V reálných datech se ukázalo, že task obsahuje zejména pole ''cwd'', ''description'', ''id'', ''label'', ''startTime'', ''status'', ''tokenCount'', ''tokenSamples'' a ''type''. Pole ''type'' je jen obecná hodnota typu ''local_agent'', nikoli název použitého subagenta. Samostatné pole pro model ani název subagenta v datech není.
Proto se používá konvence v ''description'':
· ·
Příklad:
elite-prompt-architect · haiku · rešerše pricing
Skript tento popisek rozparsuje a zobrazí ho jako:
elite-prompt-architect │ haiku │ rešerše pricing │ running │ tok:8.1k ▁▄█ 579/s │ 14s
==== Pravidlo do globálního ''CLAUDE.md'' ====
Aby skript fungoval dobře i v dalších session, je potřeba dát do globálního ''CLAUDE.md'' pravidlo pro pojmenování dispatchů v Claude Code. Bez toho se sice subagent zobrazí, ale nebude zřejmé, který agent a model běží.
## `description` naming convention — Claude Code only
In **Claude Code**, format every dispatch `description` as:
```
· ·
```
Example: `elite-prompt-architect · haiku · rešerše pricing`
Why this exact shape: the `description` is the **only** human-readable string that
reaches the Claude Code subagent status-line panel — the task data exposes no
`name` or `model` field, and `subagent_type` isn't surfaced there either. Encoding
them into `description` is the only way the panel can show *which agent, on which
model, doing what*. That visibility matters most exactly when several subagents run
in parallel and you need to tell them apart at a glance.
- Separator is a spaced middot ` · ` — the status line splits the label on it.
- `` = whatever you pass in the `model` param (`haiku`/`sonnet`/`opus`/`fable`);
the status line renders it as a colored chip.
- Keep `` genuinely short — the panel truncates to terminal width.
This is purely a Claude Code convenience: it relies on the `subagentStatusLine`
setting parsing the ` · ` format. **OpenCode** has no such status line, so use a
plain, natural `description` there — do **not** force this format.
==== Skript ''subagent-statusline.sh'' ====
#!/usr/bin/env bash
# Claude Code subagent status line
# Renders one custom row per active subagent in the agent panel.
# Row: │ │ tok: │ │ [cwd] │
===== Poznámky k chování =====
* Hlavní ''statusLine'' a ''subagentStatusLine'' jsou oddělené. Hlavní řádek zůstává pro top-level session, subagenti se zobrazují v agent panelu.
* ''subagentStatusLine'' nedostává samostatně model ani skutečný ''subagent_type''. Proto je nutná konvence v ''description''.
* ''tokenSamples'' se používá pro sparkline trendu tokenů. Když vzorky neobsahují čas, rychlost se dopočítá jako průměr od ''startTime''.
* ''columns'' se používá pro ořez popisku, aby se řádky v úzkém terminálu nelámaly.
* ''cwd'' se zobrazí jen tehdy, když se liší od hlavní session.
* Skripty vyžadují ''jq'', ''python3'' a běžné unixové ''date''.
===== Zdroje =====
* [[https://code.claude.com/docs/en/statusline|Claude Code – Customize your status line (oficiální dokumentace)]]