← back to Terminal Status

verification/install/before/.claude/hooks/dot-floor.sh

41 lines

#!/usr/bin/env bash
# dot-floor.sh — Stop-hook DOT FLOOR: guarantee this session's iTerm2 tab always shows a dot.
#
# Fires at the end of every turn (settings.json Stop hook). It is a SAFETY NET, not a judge:
#   • If this tab already has a dot set (any color) → do NOTHING. The model's deliberate
#     /dot choice (green/yellow/purple/orange/pink) is respected and never overridden here.
#   • If this tab has NO dot (blank) → paint GREEN ("WORKING"). This closes the "no dot" gap
#     (the exact bug greendot was born for) without trying to auto-guess the semantic color —
#     purple/yellow are persistent semantic states only the model can judge, so a per-turn
#     heuristic must NOT touch them.
#
# Zero model cost (no block / no extra turn). Always exits 0 — a Stop hook must never break the
# session. Resolves the tab the same way the dot scripts do: walk process ancestry to the ttysNNN
# (the hook runs as a child of the `claude` process, so the walk reaches the controlling pty).
set -u

# Drain stdin (Stop hook delivers JSON: session_id, transcript_path, stop_hook_active, cwd).
# We don't need it for the floor — read-and-ignore so the pipe never blocks.
cat >/dev/null 2>&1 || true

# Resolve THIS session's controlling pty via ancestry (identical walk to greendot.sh / current.sh).
tty_path=""; p=$$
for _ in 1 2 3 4 5 6 7 8 9 10 11 12; do
  line="$(ps -o ppid=,tty= -p "$p" 2>/dev/null)" || break
  set -- $line
  pp="${1:-}"; tt="${2:-}"
  [ -z "$pp" ] && break
  case "$tt" in ttys*) tty_path="/dev/$tt"; break;; esac
  p="$pp"
done
[ -z "$tty_path" ] && exit 0   # no ttysNNN in ancestry (launchd/pipe context) → nothing to paint

dotfile="${HOME}/.claude/tab-dots/$(basename "$tty_path").dot"

# FLOOR: only act when the tab is blank (no dot file, or empty). Respect any existing dot.
if [ ! -s "$dotfile" ]; then
  bash "${HOME}/.claude/skills/greendot/greendot.sh" >/dev/null 2>&1 || true
fi

exit 0