From 55bd3dbf57caae0518b5939740fbc4882dc39d74 Mon Sep 17 00:00:00 2001 From: Evan Alter Date: Thu, 1 Oct 2026 15:48:57 -0500 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20feat:=20=F0=9F=96=A5=EF=B8=8F=20tab?= =?UTF-8?q?status=20=E2=80=94=20set=20this=20tab's=20iTerm2=20Session=20St?= =?UTF-8?q?atus=20row=20in=20one=20command?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit iTerm2 3.7's Session Status panel can be set with the OSC 21337 escape, but that escape has to reach the tab's tty, and neither Claude Code's Bash tool nor its hooks have one (ps -o tty= answers ??). iTerm2 also ships `it2 set-status`, which goes through the API and needs no tty. The only friction is its mandatory --session, so tabstatus reads the id from $ITERM_SESSION_ID and passes everything else through. --status takes free text, not just the idle/working/waiting the help lists (verified live). Inside a Claude Code session iTerm2's own cc-status hook rewrites the row on the next event, so a manual status lasts about a turn. Co-Authored-By: Claude Opus 5.5 (1M context) --- dot_functions | 13 +++++++++++++ tests/tabstatus.bats | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) create mode 100644 tests/tabstatus.bats diff --git a/dot_functions b/dot_functions index 0f2f5db332..63f9fd7c71 100644 --- a/dot_functions +++ b/dot_functions @@ -2231,6 +2231,19 @@ function git-doctor() { print -r -- "configs: $(command git config --list --show-origin --name-only 2>/dev/null | command awk -F'\t' '$1 ~ /^file:/ {print substr($1, 6)}' | command awk '!seen[$0]++' | command paste -sd, - | command sed 's/,/, /g')"; } +# Set this tab's row in iTerm2's Session Status panel: +# tabstatus [detail] [--dot-color '#rrggbb' --text-color …] +# `tabstatus` alone clears it. Goes through `it2 set-status` (the API), not the +# OSC 21337 escape, because the escape needs the tab's tty and neither Claude +# Code's Bash tool nor its hooks have one. iTerm2's own cc-status hook rewrites +# the row on the next Claude Code event, so inside a session this lasts a turn. +function tabstatus() { + [[ -n $ITERM_SESSION_ID ]] || { print -ru2 -- "tabstatus: not inside an iTerm2 session"; return 1; } + local st=${1-} detail=${2-}; + (( $# )) && shift; (( $# )) && shift; + command it2 set-status --session "${ITERM_SESSION_ID#*:}" --status "$st" --detail "$detail" "$@" >/dev/null; +} + # Where iTerm2 is installed, or nothing. $ITERM2_APP names a non-standard # location and, when set, is used *exclusively* — an override that quietly # fell back to /Applications would make every answer below a lie about a diff --git a/tests/tabstatus.bats b/tests/tabstatus.bats new file mode 100644 index 0000000000..0e6b5288e3 --- /dev/null +++ b/tests/tabstatus.bats @@ -0,0 +1,32 @@ +#!/usr/bin/env bats + +# tabstatus in dot_functions fills in the session id `it2 set-status` demands. +# it2 is stubbed to record its argv; no test touches a real iTerm2. + +setup() { + STUB="$BATS_TEST_TMPDIR/bin" + mkdir -p "$STUB" + printf '#!/bin/sh\nprintf "%%s\\n" "$@" > "%s/argv"\n' "$BATS_TEST_TMPDIR" > "$STUB/it2" + chmod +x "$STUB/it2" + PATH="$STUB:$PATH" + DOTFUNCTIONS="$BATS_TEST_DIRNAME/../dot_functions" +} + +@test "tabstatus: passes the session uuid, status, detail and extra flags" { + run zsh -c "ITERM_SESSION_ID=w0t1p0:ABC-123; source '$DOTFUNCTIONS'; tabstatus 'reviewing PR' 'on CI' --dot-color '#66cccc'" + [ "$status" -eq 0 ] + [ "$(paste -sd'|' "$BATS_TEST_TMPDIR/argv")" = "set-status|--session|ABC-123|--status|reviewing PR|--detail|on CI|--dot-color|#66cccc" ] +} + +@test "tabstatus: no arguments clears status and detail" { + run zsh -c "ITERM_SESSION_ID=w0t1p0:ABC-123; source '$DOTFUNCTIONS'; tabstatus" + [ "$status" -eq 0 ] + [ "$(paste -sd'|' "$BATS_TEST_TMPDIR/argv")" = "set-status|--session|ABC-123|--status||--detail|" ] +} + +@test "tabstatus: outside iTerm2 is an error and never calls it2" { + run zsh -c "unset ITERM_SESSION_ID; source '$DOTFUNCTIONS'; tabstatus working" + [ "$status" -eq 1 ] + [[ "$output" == *"not inside an iTerm2 session"* ]] + [ ! -e "$BATS_TEST_TMPDIR/argv" ] +}