← back to Terminal Status
verification/install/before/.claude/skills/greendot/greendot.sh
62 lines
#!/usr/bin/env bash
# /greendot — mark a Claude session's iTerm2 tab GREEN = "WORKING — running or monitoring, nothing needed from Steve".
# A fixed SEMANTIC color (not /color's rotating golden-angle hue): green always means
# "this session is WORKING or monitoring — nothing is needed from Steve." The default resting state.
# Steve, 2026-09-02: every window always shows exactly one dot; green closes the "no dot" gap.
# Sibling of /yellowdot (needs direction), /purpledot (gated memo), /orangedot (paste in terminal),
# /pinkdot (parked).
#
# Mechanism (shared with all dot painters via paint-lib.sh): the Claude Bash tool runs with NO
# controlling tty, so OSC escapes on stdout never reach the terminal. We resolve the OWNING `claude`
# process's pty by ancestry and write the OSC straight to /dev/ttysNNN — in a SINGLE atomic write so
# it can never be torn mid-sequence. Two hard safety rails (see paint-lib.sh header for the proven
# root cause): (1) refuse to paint from a subagent — a subagent's ancestry resolves to the PARENT
# session's pty, so painting would corrupt Steve's live pane; (2) no background pulse loop — the old
# rapid-fire loop is what leaked the OSC payload ("1;bg;green;brightness;0") into the Claude input.
#
# Usage:
# greendot.sh # green-dot THIS session (title "🟢 WORKING")
# greendot.sh "TK-11098 x3" # green tint + custom title text (🟢 prefixed) — put the TK + count
# greendot.sh --all # green-dot EVERY live claude tab (rare)
# greendot.sh --off # clear the tint on THIS session
set -u
source "${HOME}/.claude/skills/colordots/paint-lib.sh"
# HARD RAIL: a subagent has no tab of its own; painting would write to the parent session's
# live pane (the input-field-pollution bug). Refuse and no-op.
if tabdots_is_subagent; then
echo "/greendot → subagent context (CLAUDE_CODE_CHILD_SESSION set) — refusing to paint (would write to the parent session's live pane). no-op."
exit 0
fi
# Semantic green — "working / monitoring, nothing needed".
R=0; G=200; B=83
DOT="🟢"
# --all: paint every live claude tab green (one atomic write each; no pulse loop).
if [ "${1:-}" = "--all" ]; then
ttys="$(ps -axo tty=,command= 2>/dev/null \
| awk '$1 ~ /^ttys/ && $0 ~ /claude/ && $0 !~ /grep/ {print $1}' | sort -u)"
[ -z "$ttys" ] && { echo "/greendot --all → no live claude ttys"; exit 0; }
n=0
for tt in $ttys; do tabdots_paint "/dev/$tt" "$R" "$G" "$B" "$DOT WORKING" 0 && { echo " $tt → 🟢 WORKING"; n=$((n+1)); }; done
echo "/greendot --all → marked ${n} tab(s) green (working)"
exit 0
fi
OFF=0; LABEL=""
[ "${1:-}" = "--off" ] && OFF=1 || LABEL="${1:-}"
tty_path="$(tabdots_resolve_tty)" || {
echo "/greendot → could not resolve this session's tty (no ttysNNN in ancestry)"; exit 1
}
if [ "$OFF" = "1" ]; then
tabdots_paint "$tty_path" "$R" "$G" "$B" "" 1 && echo "/greendot --off → cleared tint on $tty_path"
else
title="$DOT ${LABEL:-WORKING}"
tabdots_paint "$tty_path" "$R" "$G" "$B" "$title" 0 \
&& echo "/greendot → $tty_path 🟢 ${LABEL:-WORKING} rgb(${R},${G},${B})"
fi