← back to Terminal Status
verification/install/before/.claude/skills/orangedot/orangedot.sh
99 lines
#!/usr/bin/env bash
# /orangedot — mark a Claude session's iTerm2 tab ORANGE = "PASTE WAITING — Steve action required".
# A fixed SEMANTIC color (not /color's rotating golden-angle hue): orange always means
# "this session has handed Steve a `!` paste (or another one-step approval) and is waiting on it."
# Steve, 2026-09-02: "when we see pastes or paste this: use one of the color dots to notify terminal".
# Sibling of /pinkdot (pink = parked/idle) and /yellowdot (legacy idle). Lets Steve scan a wall of
# tabs and see exactly which windows are blocked on HIM (orange) vs parked (pink) vs working (/color).
#
# Mechanism (identical to pinkdot.sh / color.sh): the Claude Bash tool runs with NO controlling
# tty, so OSC escapes on stdout never reach the terminal. We resolve the `claude` process's own
# pty by walking process ancestry and write the OSC straight to /dev/ttysNNN. OSC color/title
# codes are non-destructive (no cursor moves) so this is glitch-safe.
#
# Usage:
# orangedot.sh # orange-dot THIS session (title "🟠 PASTE waiting")
# orangedot.sh "TK-11098 x3" # orange tint + custom title text (🟠 prefixed) — put the TK + count
# orangedot.sh --all # orange-dot EVERY live claude tab (rare; fleet-wide "all blocked on Steve")
# orangedot.sh --off # clear the tint on THIS session (paste was run → back to /color or /pinkdot)
set -u
# HARD RAIL (2026-09-08): a subagent has no tab of its own — its ancestry resolves to the
# PARENT session's pty, so painting would corrupt Steve's live pane (the input-field bug). No-op.
if [ -n "${CLAUDE_CODE_CHILD_SESSION:-}" ]; then
echo "/orangedot → subagent context — refusing to paint (would write to the parent session's live pane). no-op."
exit 0
fi
# Semantic orange — "action required from Steve". Distinct from pink (parked) and /color hues.
R=255; G=140; B=0
DOT="🟠"
# Smooth pulsate helper — ACTIVE state, so we pulse this color (Steve, 2026-09-04:
# "Pulsate the color whenever claude or codex is waiting for me or just running").
# Best-effort: if it's missing or the tty isn't writable, painting the solid color still succeeds.
PULSE="${HOME}/.claude/tab-dots/pulse.sh"
paint() { # $1 = tty_path, $2 = label(optional), $3 = off(1=reset)
local tp="$1" label="${2:-}" off="${3:-0}"
[[ -w "$tp" ]] || return 1
if [[ "$off" == "1" ]]; then
# Stop any active pulse FIRST (before the reset) so the pulse loop can't repaint after we clear.
[[ -f "$PULSE" ]] && bash "$PULSE" --stop "$tp" >/dev/null 2>&1 || true
{ printf '\033]6;1;bg;*;default\007'; printf '\033]1337;SetBadgeFormat=\a'; } > "$tp" 2>/dev/null
rm -f "${HOME}/.claude/tab-dots/$(basename "$tp").dot" 2>/dev/null
return 0
fi
local title="$DOT ${label:-PASTE waiting}"
# NOTE: no leading '*;default' reset — on iTerm2 it can swallow the color set (pinkdot bug,
# 2026-09-02). Set RGB directly and write BOTH title channels (]1; tab, ]2; window).
{
printf '\033]6;1;bg;red;brightness;%d\007' "$R"
printf '\033]6;1;bg;green;brightness;%d\007' "$G"
printf '\033]6;1;bg;blue;brightness;%d\007' "$B"
printf '\033]1;%s\007' "$title"
printf '\033]2;%s\007' "$title"
} > "$tp" 2>/dev/null
# Persist the dot so the 60s tab-ticket repainter PREFIXES it into the window title
# (visible even when the tab bar is hidden), and show an in-window iTerm2 BADGE.
mkdir -p "${HOME}/.claude/tab-dots"
printf '%s' "$title" > "${HOME}/.claude/tab-dots/$(basename "$tp").dot"
printf '\033]1337;SetBadgeFormat=%s\a' "$(printf '%s' "$title" | base64 | tr -d '\n')" > "$tp" 2>/dev/null
# ACTIVE state → start a smooth pulse of this same color (pulse.sh's per-tty pidfile replaces
# any existing pulse on this tty, so pulses never stack). Best-effort, never fatal.
[[ -f "$PULSE" ]] && bash "$PULSE" "$R" "$G" "$B" "$tp" >/dev/null 2>&1 || true
}
# --all: paint every live claude tab orange
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 "/orangedot --all → no live claude ttys"; exit 0; }
n=0
for tt in $ttys; do paint "/dev/$tt" "PASTE waiting" 0 && { echo " $tt → 🟠 PASTE waiting"; n=$((n+1)); }; done
echo "/orangedot --all → marked ${n} tab(s) orange (paste waiting on Steve)"
exit 0
fi
OFF=0; LABEL=""
[[ "${1:-}" == "--off" ]] && OFF=1 || LABEL="${1:-}"
# Resolve THIS session's controlling pty by walking ancestry (same as color.sh).
tty_path=""; p=$$
for _ in 1 2 3 4 5 6 7 8 9 10; do
line="$(ps -o ppid=,tty= -p "$p" 2>/dev/null)" || break
set -- $line
pp="${1:-}"; tt="${2:-}"
[[ -z "$pp" ]] && break
if [[ "$tt" == ttys* ]]; then tty_path="/dev/$tt"; break; fi
p="$pp"
done
if [[ -z "$tty_path" ]]; then
echo "/orangedot → could not resolve this session's tty (no ttysNNN in ancestry)"; exit 1
fi
if [[ "$OFF" == "1" ]]; then
paint "$tty_path" "" 1 && echo "/orangedot --off → cleared tint on $tty_path"
else
paint "$tty_path" "$LABEL" 0 && echo "/orangedot → $tty_path 🟠 ${LABEL:-PASTE waiting} rgb(${R},${G},${B})"
fi