← back to Terminal Status

verification/install/after/.claude/skills/allcolordots/allcolordots.sh

115 lines

#!/usr/bin/env bash
# /allcolordots — scan EVERY active terminal's color dot + label in one shot,
# and (new) present a clickable single-window ROSTER per color that JUMPS focus
# to any tab — the "gather my yellows in one place" navigator (path A).
#
# Status comes from the shared session-bound terminal-status engine.
# Labels and dot files are presentation; exact main agent ownership is required.
#
# Usage:
#   allcolordots.sh                 # grouped human table (orange > purple > yellow > green > pink)
#   allcolordots.sh --json          # JSON array: [{tty,color,label,live,pid}]  (navbar app)
#   allcolordots.sh --live          # table, but only ACTIVE (live) terminals
#   allcolordots.sh --yellow        # numbered ROSTER of just the 🟡 needs-direction tabs
#   allcolordots.sh --only <color>  # numbered roster of one color (yellow|orange|purple|green|pink)
#   allcolordots.sh jump <ttyNNN|N> # focus that tab (N = row# from the last roster)
set -u
set -o pipefail
# Emoji glob-matching (case *🟡*) needs a UTF-8 ctype; some launch contexts pass
# LC_CTYPE= empty which silently breaks the match -> every dot reads "none".
export LANG="${LANG:-en_US.UTF-8}" LC_ALL="en_US.UTF-8"
DOTS="$HOME/.claude/tab-dots"
ROSTER_CACHE="$DOTS/.last-roster"

color_of(){ case "$1" in *🟢*) echo green;; *🟡*) echo yellow;; *🟣*) echo purple;; *🟠*) echo orange;; *🩷*) echo pink;; *) echo none;; esac; }
emoji_of(){ case "$1" in orange) echo 🟠;; purple) echo 🟣;; yellow) echo 🟡;; green) echo 🟢;; pink) echo 🩷;; *) echo ⚪;; esac; }

# ---- JUMP: focus the tab whose tty matches (select + activate; NOT a reparent) ----
jump_to(){
  local arg="$1" tty=""
  # index into the last roster?
  if printf '%s' "$arg" | grep -qE '^[0-9]+$' && [ -f "$ROSTER_CACHE" ]; then
    tty="$(awk -F'\t' -v n="$arg" 'NR==n{print $1}' "$ROSTER_CACHE")"
  fi
  [ -z "$tty" ] && tty="$arg"
  case "$tty" in /dev/*) :;; ttys*) tty="/dev/$tty";; *) tty="/dev/$tty";; esac
  osascript 2>&1 <<OSA
tell application "iTerm2"
  repeat with w in windows
    repeat with t in tabs of w
      repeat with s in sessions of t
        if (tty of s) is "$tty" then
          select w
          select t
          select s
          activate
          return "jumped -> $tty"
        end if
      end repeat
    end repeat
  end repeat
  return "not found: $tty"
end tell
OSA
}

# ---- Shared state scanner: presentation never overrides canonical state ----
collect_rows(){
  python3 "$HOME/Projects/terminal-status/terminal_status.py" scan --tsv
}

# ---- roster of ONE color: numbered, jump-ready, caches order for `jump N` ----
print_roster(){
  local want="$1" rows; rows="$(collect_rows | awk -F'\t' -v c="$want" '$2==c')" || return $?
  : > "$ROSTER_CACHE"
  local e; e="$(emoji_of "$want")"
  local n; n="$(printf '%s\n' "$rows" | grep -c .)"
  echo "=== $e $want roster ($n) — jump with:  allcolordots.sh jump <#|tty> ==="
  [ "$n" = 0 ] && { echo "  (none)"; return 0; }
  local i=0
  printf '%s\n' "$rows" | while IFS=$'\t' read -r tty color live pid label; do
    i=$((i+1))
    printf '%s\t%s\t%s\n' "$tty" "$label" "$live" >> "$ROSTER_CACHE"
    local flag=""; [ "$live" = true ] && flag="  ·live"
    printf '  %2d) %-9s %s%s\n' "$i" "$tty" "$label" "$flag"
  done
}

# ---------------- dispatch ----------------
case "${1:-}" in
  jump)   shift; jump_to "${1:-}"; exit $? ;;
  --yellow) print_roster yellow; exit $? ;;
  --only)   print_roster "${2:-yellow}"; exit $? ;;
  --json)
    collect_rows | python3 -c '
import sys,json
out=[]
for line in sys.stdin:
    line=line.rstrip("\n")
    if not line: continue
    tty,color,live,pid,label=(line.split("\t")+["","","","",""])[:5]
    out.append({"tty":tty,"color":color,"live":live=="true","pid":pid,"label":label})
pri={"orange":0,"purple":1,"yellow":2,"green":3,"pink":4,"none":5}
out.sort(key=lambda r:(pri.get(r["color"],9), r["tty"]))
print(json.dumps(out))
'
    exit $? ;;
esac

# default / --live : grouped human table
LIVEONLY=0; [ "${1:-}" = "--live" ] && LIVEONLY=1
rows="$(collect_rows)" || exit $?
[ "$LIVEONLY" = 1 ] && rows="$(printf '%s\n' "$rows" | awk -F'\t' '$3=="true"')"
total="$(printf '%s\n' "$rows" | grep -c . )"
echo "=== active-terminal dots ($total) ==="
for c in orange purple yellow green pink none; do
  block="$(printf '%s\n' "$rows" | awk -F'\t' -v c="$c" '$2==c')"
  [ -z "$block" ] && continue
  n="$(printf '%s\n' "$block" | grep -c .)"
  echo "$(emoji_of "$c") ${c} ($n)"
  printf '%s\n' "$block" | while IFS=$'\t' read -r tty color live pid label; do
    flag=""; [ "$live" = true ] && flag="  ·live"
    printf '   %-9s %s%s\n' "$tty" "$label" "$flag"
  done
done