
./claude-ps で起動。
#!/usr/bin/env bash set -euo pipefail # claude-ps: Monitor Claude Code sessions running in tmux panes HOME_DIR="$HOME" WATCH_INTERVAL=1 # Colors BOLD='\033[1m' GREEN='\033[32m' YELLOW='\033[33m' DIM='\033[2m' RESET='\033[0m' usage() { echo "Usage: claude-ps [-w|--watch [INTERVAL]]" echo " -w, --watch [SEC] Watch mode (default: ${WATCH_INTERVAL}s interval)" exit 0 } check_deps() { if ! command -v tmux &>/dev/null; then echo "error: tmux is not installed" >&2 exit 1 fi if ! tmux list-sessions &>/dev/null 2>&1; then echo "error: no tmux sessions found" >&2 exit 1 fi } parse_args() { watch_mode=false while [[ $# -gt 0 ]]; do case "$1" in -w|--watch) watch_mode=true if [[ "${2:-}" =~ ^[0-9]+$ ]]; then WATCH_INTERVAL="$2" shift fi shift ;; -h|--help) usage ;; *) echo "Unknown option: $1" >&2 usage ;; esac done } is_claude_pane() { local cmd="$1" title="$2" [[ "$cmd" =~ ^[0-9]+\.[0-9]+\.[0-9]+ ]] || [[ "$title" == *"Claude Code"* ]] } # Detect status from pane_title icon # ✳ (e29cb3) / ✻ (e29cbb) = idle, Braille spinner (e2a080-e2a3bf) = running detect_claude_status() { local title="$1" local icon_hex icon_hex=$(echo -n "$title" | head -c 3 | xxd -p) if [[ "$icon_hex" == "e29cb3" || "$icon_hex" == "e29cbb" ]]; then echo "idle" elif [[ "$icon_hex" == e2a0* || "$icon_hex" == e2a1* || "$icon_hex" == e2a2* || "$icon_hex" == e2a3* ]]; then echo "running" else echo "unknown" fi } extract_task() { local title="$1" local task task=$(echo "$title" | sed $'s/^[\xe2\xa0\x80-\xe2\xa3\xbf\xe2\x9c\xb3\xe2\x9c\xbb\xe2\x8f\xb5\xe2\x8f\xb6\xe2\x8f\xb7\xe2\x8f\xb8] *//') task="${task#Claude Code}" task="${task# - }" if [[ ${#task} -gt 45 ]]; then task="${task:0:42}..." fi echo "$task" } # List Claude panes as pipe-delimited lines: dir|status|task get_list_claude_panes() { while IFS='|' read -r _pane cmd dir title; do if ! is_claude_pane "$cmd" "$title"; then continue fi local status short_dir task status=$(detect_claude_status "$title") short_dir="$(basename "$dir")" task=$(extract_task "$title") echo "${status}|${task}|${short_dir}" done < <(tmux list-panes -a -F "#{session_name}:#{window_index}.#{pane_index}|#{pane_current_command}|#{pane_current_path}|#{pane_title}") } render() { # header printf "${BOLD} %-45s %s${RESET}\n" "TASK" "DIR" printf "%s\n" "$(printf '%.0s─' {1..60})" # panes local found=0 while IFS='|' read -r status task short_dir; do found=$((found + 1)) local status_icon status_color case "$status" in running) status_icon="●"; status_color="$GREEN" ;; idle) status_icon="○"; status_color="$YELLOW" ;; *) status_icon="?"; status_color="$DIM" ;; esac printf "${status_color}%s${RESET} %-45s ${DIM}%s${RESET}\n" \ "$status_icon" "$task" "$short_dir" done < <(get_list_claude_panes) if [[ $found -eq 0 ]]; then echo "No Claude Code sessions found in tmux." fi # footer printf "\n${DIM}Updated: $(date '+%H:%M:%S')${RESET}" } watch_loop() { trap 'tput cnorm; tput clear; exit 0' INT TERM tput civis tput clear while true; do local buf buf=$(render) tput home printf "${DIM}claude-ps (every ${WATCH_INTERVAL}s, Ctrl-C to stop)${RESET}\n\n" printf '%s' "$buf" tput ed sleep "$WATCH_INTERVAL" done } # --- Main --- check_deps parse_args "$@" if $watch_mode; then watch_loop else render echo fi


