Repository navigation
feat: show teammate model in the agent panel - #14
Conversation
Add subagent-statusline.sh, a subagentStatusLine command that rewrites each running teammate's row in the agent panel as "↳ 🐝 <name> · <glyph> <model> · ↓ <tok> tok". The model is read from the teammate's own transcript (the panel payload has no model field) and mapped to a per-family glyph (Opus/Sonnet/Haiku/Fable). Only actively-running teammates show; finished and idle rows are hidden. statusline.sh's daily auto-update now refreshes both scripts, and install.sh installs both and prints the subagentStatusLine snippet. Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: GitButler <gitbutler@gitbutler.com>
The Status Line Example now shows the four-line main block and the agent-panel teammate rows together, so the difference is visible at a glance. Drop the duplicate example from the Subagent rows section and link back to it. Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: GitButler <gitbutler@gitbutler.com>
There was a problem hiding this comment.
Code Review
This pull request introduces a new subagent-statusline.sh script to display active subagent/teammate models and token usage in the Claude Code agent panel, along with corresponding installer and auto-update integrations. The review feedback focuses on performance optimizations and robust error handling under set -e. Key recommendations include optimizing transcript scanning by only reading the tail of the file, replacing external awk calls with pure Bash arithmetic for token formatting, declaring loop variables as local, and safely capturing command substitution exit codes to prevent premature script termination.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| grep -oE '"model":"claude[^"]*"' "$file" 2>/dev/null \ | ||
| | tail -1 \ | ||
| | sed -E 's/.*"model":"//; s/"$//' || true |
There was a problem hiding this comment.
Since this script runs on every panel refresh tick, scanning the entire teammate transcript file with grep can cause noticeable terminal lag as the transcript grows large. Since the model ID is repeated in recent messages, optimize this by only scanning the last 50 lines of the file using tail.
| grep -oE '"model":"claude[^"]*"' "$file" 2>/dev/null \ | |
| | tail -1 \ | |
| | sed -E 's/.*"model":"//; s/"$//' || true | |
| tail -n 50 "$file" 2>/dev/null \ | |
| | grep -oE '"model":"claude[^"]*"' \ | |
| | tail -1 \ | |
| | sed -E 's/.*"model":"//; s/"$//' || true |
References
- In shell scripts running with 'set -e', guard command substitutions or pipelines that may fail with '|| true' to prevent script termination, provided that an empty result is safely handled by fallback logic.
| transcript_path=$(printf '%s' "$stdin_data" | jq -r '.transcript_path // ""' 2>/dev/null || echo "") | ||
| columns=$(printf '%s' "$stdin_data" | jq -r '.columns // 80' 2>/dev/null || echo 80) | ||
| [[ "$columns" =~ ^[0-9]+$ ]] || columns=80 |
There was a problem hiding this comment.
Capture command substitution exit codes using the || status=$? pattern to prevent unintended script termination on command failure under set -e.
| transcript_path=$(printf '%s' "$stdin_data" | jq -r '.transcript_path // ""' 2>/dev/null || echo "") | |
| columns=$(printf '%s' "$stdin_data" | jq -r '.columns // 80' 2>/dev/null || echo 80) | |
| [[ "$columns" =~ ^[0-9]+$ ]] || columns=80 | |
| status=0 | |
| transcript_path=$(printf '%s' "$stdin_data" | jq -r '.transcript_path // ""' 2>/dev/null) || status=$? | |
| columns=$(printf '%s' "$stdin_data" | jq -r '.columns // 80' 2>/dev/null) || status=$? | |
| [[ "$columns" =~ ^[0-9]+$ ]] || columns=80 |
References
- When using 'set -e' in shell scripts, capture command substitution exit codes using the 'var=$(command) || status=$?' pattern to prevent unintended script termination on command failure.
| local first rest | ||
| first=$(printf '%s' "${family:0:1}" | tr '[:lower:]' '[:upper:]') |
There was a problem hiding this comment.
Under set -euo pipefail, command substitutions can cause unintended script termination if any command in the pipeline fails. Capture the exit code using the || status=$? pattern to prevent this.
| local first rest | |
| first=$(printf '%s' "${family:0:1}" | tr '[:lower:]' '[:upper:]') | |
| local first rest status=0 | |
| first=$(printf '%s' "${family:0:1}" | tr '[:lower:]' '[:upper:]') || status=$? |
References
- When using 'set -e' in shell scripts, capture command substitution exit codes using the 'var=$(command) || status=$?' pattern to prevent unintended script termination on command failure.
| fmt_tokens() { | ||
| local n="$1" | ||
| [[ "$n" =~ ^[0-9]+$ ]] || n=0 | ||
| if (( n >= 1000 )); then | ||
| awk -v n="$n" 'BEGIN { printf "%.1fk", n/1000 }' | ||
| else | ||
| printf '%s' "$n" | ||
| fi | ||
| } |
There was a problem hiding this comment.
Spawning an external awk process on every panel refresh tick inside a loop can be a performance bottleneck. Implement token formatting and rounding entirely in pure Bash, which is significantly faster and avoids process forks.
| fmt_tokens() { | |
| local n="$1" | |
| [[ "$n" =~ ^[0-9]+$ ]] || n=0 | |
| if (( n >= 1000 )); then | |
| awk -v n="$n" 'BEGIN { printf "%.1fk", n/1000 }' | |
| else | |
| printf '%s' "$n" | |
| fi | |
| } | |
| fmt_tokens() { | |
| local n="$1" | |
| [[ "$n" =~ ^[0-9]+$ ]] || n=0 | |
| if (( n >= 1000 )); then | |
| local rounded=$(( (n + 50) / 100 )) | |
| printf '%d.%dk' $(( rounded / 10 )) $(( rounded % 10 )) | |
| else | |
| printf '%s' "$n" | |
| fi | |
| } |
| tasks_tsv=$(printf '%s' "$stdin_data" | jq -r ' | ||
| .tasks // [] | ||
| | .[] | ||
| | [ .id, (.status // ""), (.label // .description // ""), ((.tokenCount // 0) | tostring) ] | ||
| | @tsv | ||
| ' 2>/dev/null || true) |
There was a problem hiding this comment.
Capture command substitution exit codes using the || status=$? pattern to prevent unintended script termination on command failure under set -e.
| tasks_tsv=$(printf '%s' "$stdin_data" | jq -r ' | |
| .tasks // [] | |
| | .[] | |
| | [ .id, (.status // ""), (.label // .description // ""), ((.tokenCount // 0) | tostring) ] | |
| | @tsv | |
| ' 2>/dev/null || true) | |
| status=0 | |
| tasks_tsv=$(printf '%s' "$stdin_data" | jq -r ' | |
| .tasks // [] | |
| | .[] | |
| | [ .id, (.status // ""), (.label // .description // ""), ((.tokenCount // 0) | tostring) ] | |
| | @tsv | |
| ' 2>/dev/null) || status=$? |
References
- When using 'set -e' in shell scripts, capture command substitution exit codes using the 'var=$(command) || status=$?' pattern to prevent unintended script termination on command failure.
| raw_model=$(model_of "$id") | ||
| if [[ -n "$raw_model" ]]; then | ||
| glyph=$(model_glyph "$raw_model") | ||
| if pretty=$(model_pretty "$raw_model"); then | ||
| if [[ -n "$glyph" ]]; then | ||
| content="${content} · ${glyph} ${pretty}" | ||
| else | ||
| content="${content} · ${pretty}" | ||
| fi | ||
| fi | ||
| fi | ||
|
|
||
| content="${content} · ↓ $(fmt_tokens "$tok") tok" |
There was a problem hiding this comment.
Declare loop variables as local to prevent scope leakage, and capture command substitution exit codes using the || status=$? pattern to adhere to the general rules and prevent unintended script termination under set -e.
| raw_model=$(model_of "$id") | |
| if [[ -n "$raw_model" ]]; then | |
| glyph=$(model_glyph "$raw_model") | |
| if pretty=$(model_pretty "$raw_model"); then | |
| if [[ -n "$glyph" ]]; then | |
| content="${content} · ${glyph} ${pretty}" | |
| else | |
| content="${content} · ${pretty}" | |
| fi | |
| fi | |
| fi | |
| content="${content} · ↓ $(fmt_tokens "$tok") tok" | |
| local raw_model="" glyph="" pretty="" formatted_tok="" status=0 | |
| raw_model=$(model_of "$id") || status=$? | |
| if [[ -n "$raw_model" ]]; then | |
| glyph=$(model_glyph "$raw_model") || status=$? | |
| pretty=$(model_pretty "$raw_model") || status=$? | |
| if [[ $status -eq 0 && -n "$pretty" ]]; then | |
| if [[ -n "$glyph" ]]; then | |
| content="${content} · ${glyph} ${pretty}" | |
| else | |
| content="${content} · ${pretty}" | |
| fi | |
| fi | |
| fi | |
| formatted_tok=$(fmt_tokens "$tok") || status=$? | |
| content="${content} · ↓ ${formatted_tok} tok" |
References
- When using 'set -e' in shell scripts, capture command substitution exit codes using the 'var=$(command) || status=$?' pattern to prevent unintended script termination on command failure.
There was a problem hiding this comment.
Pull request overview
Adds a dedicated subagentStatusLine command to improve Claude Code’s agent panel by showing each running teammate’s model (with optional glyphs), and updates install/auto-update flows so both statusline scripts stay current.
Changes:
- Introduces
subagent-statusline.shto render per-subagent rows including model family/version and compact token counts. - Extends
statusline.shdaily auto-update to refresh bothstatusline.shandsubagent-statusline.sh. - Updates
install.shandREADME.mdto install/document the optionalsubagentStatusLineconfiguration.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| subagent-statusline.sh | New script that parses tasks + subagent transcripts to render model-aware agent panel rows. |
| statusline.sh | Auto-update now refreshes both the main and subagent statusline scripts. |
| install.sh | Installer now installs both scripts and prints config snippets for both. |
| README.md | Documents the new subagent rows feature and updated install/config instructions. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| grep -oE '"model":"claude[^"]*"' "$file" 2>/dev/null \ | ||
| | tail -1 \ | ||
| | sed -E 's/.*"model":"//; s/"$//' || true |
| if curl -sSL "${BASE_URL}/${name}" -o "$tmp"; then | ||
| cp "$tmp" "${INSTALL_DIR}/${name}" | ||
| chmod +x "${INSTALL_DIR}/${name}" | ||
| echo " Installed successfully." |
| model_of() { | ||
| local id="$1" file="${subagents_dir}/agent-${id}.jsonl" | ||
| [[ -f "$file" ]] || return 0 | ||
| grep -oE '"model":"claude[^"]*"' "$file" 2>/dev/null \ | ||
| | tail -1 \ | ||
| | sed -E 's/.*"model":"//; s/"$//' || true |
| columns=$(printf '%s' "$stdin_data" | jq -r '.columns // 80' 2>/dev/null || echo 80) | ||
| [[ "$columns" =~ ^[0-9]+$ ]] || columns=80 |
| grep -oE '"model":"claude[^"]*"' "$file" 2>/dev/null \ | ||
| | tail -1 \ | ||
| | sed -E 's/.*"model":"//; s/"$//' || true |
| echo "Installing ${name} to ${INSTALL_DIR}..." | ||
| local tmp | ||
| tmp=$(mktemp) | ||
| if curl -sSL "${BASE_URL}/${name}" -o "$tmp"; then |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 42250e150a
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| # Last assistant model recorded in a teammate's transcript. Empty if the file is | ||
| # missing or no assistant turn has landed yet (a just-spawned agent). | ||
| model_of() { | ||
| local id="$1" file="${subagents_dir}/agent-${id}.jsonl" |
There was a problem hiding this comment.
Resolve teammate sessions before reading the model
When the row represents an Agent Teams teammate, .tasks[].id is the panel/task id while the teammate is a separate Claude session whose session id is recorded in the team config; its transcript is not <lead-session>/subagents/agent-$id.jsonl. With this hard-coded path [[ -f "$file" ]] returns false, so raw_model stays empty and the model never appears for the teammate rows this feature is meant to improve. Please map teammate rows to their session transcript and only use the subagents path for classic subagents.
Useful? React with 👍 / 👎.
What
Background teammates show up as rows in Claude Code's agent panel, but the default row (
name · description · token count) never says which model a teammate is running: a Sonnet teammate looks the same as an Opus one.This adds
subagent-statusline.sh, asubagentStatusLinecommand that rewrites each running teammate's row:↳ 🐝marks a spawned worker nested under the main session.Glyphs: 🦉 Opus · 🔮 Sonnet · 🍃 Haiku · 📖 Fable/Mythos. A model outside these families keeps its row without a glyph.
Also
statusline.sh's once-a-day auto-update now refreshes both scripts, so the subagent script never goes stale after install.install.shinstalls both scripts and prints thesubagentStatusLinesnippet.Testing
Verified live through the configured command against real captured payloads:
Full model matrix (Opus, Sonnet, Haiku, Fable, plus legacy Haiku 3.5) and the edge cases all pass: unknown family keeps the row without a glyph, a completed teammate's row is hidden, and sub-1k token counts render raw. All three scripts pass
bash -n.Note: existing sessions load
subagentStatusLineat startup, so restart or open a new session to pick it up.🤖 Generated with Claude Code
https://claude.ai/code/session_01EHAoGEYEfzyhPRHSQ1XkDZ