← back to Terminal Status
verification/install/before/.claude/skills/color/scripts/paint-tab.sh
95 lines
#!/usr/bin/env bash
# paint-tab.sh <state> — tint THIS Claude session's iTerm tab by state.
# working | green -> SOLID green (Claude is busy) + stop any blinker
# waiting | purple -> BLINKING purple (turn handed back to Steve — answer
# needed). A background loop alternates purple<->default
# until the next `working` call kills it.
# solid-purple -> solid purple, no blink (internal/manual use)
# stop -> kill the blinker only
#
# Why slot-prefix matching: the color hook/bash subprocess runs detached (tty
# "??"), so walking process ancestry to find the tab pty is unreliable. And the
# inherited ITERM_SESSION_ID's UUID half goes STALE on an iTerm reattach. The
# w#t#p# (window/tab/pane) PREFIX is stable, so we match THAT against each live
# `claude` process's env to resolve the correct /dev/ttysNNN. Verified 2026-06-26.
set -u
GREEN=(114 244 52) # busy
PURPLE=(168 85 247) # awaiting Steve — bright phase
PURPLE_DIM=(84 42 124) # awaiting Steve — dim phase (PULSE, never blank)
BLINK_SEC="${PAINT_BLINK_SEC:-0.7}" # cadence of the idle pulse
state="${1:-waiting}"
# --- resolve THIS tab's pty by stable w#t#p# slot prefix --------------------
[[ -z "${ITERM_SESSION_ID:-}" ]] && exit 0
prefix="${ITERM_SESSION_ID%%:*}"
tty=""
for pid in $(pgrep -x claude 2>/dev/null); do
sid="$(ps eww -p "$pid" 2>/dev/null | grep -o 'ITERM_SESSION_ID=[^ ]*' | head -1 | cut -d= -f2-)"
[[ "${sid%%:*}" == "$prefix" ]] && tty="/dev/$(ps -o tty= -p "$pid" 2>/dev/null | tr -d ' ')"
done
[[ -n "$tty" && -w "$tty" ]] || exit 0
# Per-tab blinker pidfile, keyed by slot prefix so one tab never kills another's.
RUN_DIR="${HOME}/.claude/working-state"
mkdir -p "$RUN_DIR" 2>/dev/null || true
pidfile="${RUN_DIR}/blink-${prefix//[^A-Za-z0-9]/_}.pid"
write_rgb() { # write_rgb <tty> R G B
{ printf '\033]6;1;bg;red;brightness;%d\007' "$2"
printf '\033]6;1;bg;green;brightness;%d\007' "$3"
printf '\033]6;1;bg;blue;brightness;%d\007' "$4"
} > "$1" 2>/dev/null
}
reset_tint() { printf '\033]6;1;bg;*;default\007' > "$1" 2>/dev/null; }
stop_blink() {
[[ -f "$pidfile" ]] || return 0
kill "$(cat "$pidfile" 2>/dev/null)" 2>/dev/null || true
rm -f "$pidfile"
}
case "$state" in
green|working)
stop_blink
write_rgb "$tty" "${GREEN[@]}"
;;
purple|waiting)
# blinking purple: stop any prior blinker, then start a fresh detached loop.
# tty is resolved HERE (parent) and handed to the loop — once backgrounded
# the loop may be reparented and lose the ability to re-resolve it.
stop_blink
( trap 'exit 0' TERM INT
br="${PURPLE[0]}"; bg="${PURPLE[1]}"; bb="${PURPLE[2]}" # bright
dr="${PURPLE_DIM[0]}"; dg="${PURPLE_DIM[1]}"; db="${PURPLE_DIM[2]}" # dim
while :; do
# PULSE bright purple <-> dim purple — always a color, never blank.
{ printf '\033]6;1;bg;red;brightness;%d\007' "$br"
printf '\033]6;1;bg;green;brightness;%d\007' "$bg"
printf '\033]6;1;bg;blue;brightness;%d\007' "$bb"
} > "$tty" 2>/dev/null
sleep "$BLINK_SEC"
{ printf '\033]6;1;bg;red;brightness;%d\007' "$dr"
printf '\033]6;1;bg;green;brightness;%d\007' "$dg"
printf '\033]6;1;bg;blue;brightness;%d\007' "$db"
} > "$tty" 2>/dev/null
sleep "$BLINK_SEC"
done ) >/dev/null 2>&1 &
echo $! > "$pidfile"
disown 2>/dev/null || true
;;
solid-purple)
stop_blink
write_rgb "$tty" "${PURPLE[@]}"
;;
stop)
stop_blink
reset_tint "$tty"
;;
*)
write_rgb "$tty" 128 128 128
;;
esac
exit 0