279 lines
13 KiB
Bash
Executable File
279 lines
13 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
# Claude Unified Status
|
|
# Shows all sessions across all projects with summary stats
|
|
|
|
# Helper function to get Claude session info directly from Claude's data
|
|
get_claude_info() {
|
|
local TMUX_SESSION="$1"
|
|
|
|
# Get the TTY for this tmux session
|
|
local TTY=$(tmux list-panes -t "$TMUX_SESSION" -F '#{pane_tty}' 2>/dev/null | head -1)
|
|
[ -z "$TTY" ] && return 1
|
|
|
|
# Find the Claude PID running on that TTY
|
|
local TTY_SHORT="${TTY#/dev/}"
|
|
local CLAUDE_PID=$(ps -eo pid,tty,comm 2>/dev/null | awk -v tty="$TTY_SHORT" '$2 == tty && $3 == "claude" {print $1; exit}')
|
|
[ -z "$CLAUDE_PID" ] && return 1
|
|
|
|
# Get the working directory of that process
|
|
local CWD=$(lsof -p "$CLAUDE_PID" 2>/dev/null | awk '$4 == "cwd" {print $NF}')
|
|
[ -z "$CWD" ] && return 1
|
|
|
|
# Get current git branch from that directory
|
|
local BRANCH=$(git -C "$CWD" rev-parse --abbrev-ref HEAD 2>/dev/null || echo "")
|
|
|
|
# Convert to Claude's project path format and find active session
|
|
local PROJECT_PATH=$(echo "$CWD" | tr '/_' '--')
|
|
local PROJECT_DIR="$HOME/.claude/projects/$PROJECT_PATH"
|
|
|
|
local SESSION_ID=""
|
|
local SUMMARY=""
|
|
|
|
if [ -d "$PROJECT_DIR" ]; then
|
|
# Find the most recently modified session file (exclude agent- files)
|
|
local ACTIVE_SESSION_FILE=$(ls -t "$PROJECT_DIR"/*.jsonl 2>/dev/null | grep -v "agent-" | head -1)
|
|
if [ -n "$ACTIVE_SESSION_FILE" ]; then
|
|
SESSION_ID=$(basename "$ACTIVE_SESSION_FILE" .jsonl)
|
|
# Extract the most recent summary from the session file
|
|
SUMMARY=$(grep '"type":"summary"' "$ACTIVE_SESSION_FILE" 2>/dev/null | tail -1 | jq -r '.summary // empty' 2>/dev/null)
|
|
fi
|
|
fi
|
|
|
|
# Output as key=value pairs (properly quoted for eval)
|
|
printf 'claude_branch=%q\n' "$BRANCH"
|
|
printf 'claude_session_id=%q\n' "$SESSION_ID"
|
|
printf 'claude_summary=%q\n' "$SUMMARY"
|
|
}
|
|
|
|
echo "╔══════════════════════════════════════════════════════════════════════════════╗"
|
|
echo "║ CLAUDE ORCHESTRATOR STATUS ║"
|
|
echo "╚══════════════════════════════════════════════════════════════════════════════╝"
|
|
echo ""
|
|
|
|
# Count sessions
|
|
INTEGRATOR_COUNT=$(tmux list-sessions -F '#{session_name}' 2>/dev/null | grep -c "^integrator-" || echo 0)
|
|
SPLITLY_COUNT=$(tmux list-sessions -F '#{session_name}' 2>/dev/null | grep -c "^splitly-" || echo 0)
|
|
AO_COUNT=$(tmux list-sessions -F '#{session_name}' 2>/dev/null | grep -c "^ao-" || echo 0)
|
|
TOTAL=$((INTEGRATOR_COUNT + SPLITLY_COUNT + AO_COUNT))
|
|
|
|
# Count empty checkouts
|
|
INTEGRATOR_EMPTY=0
|
|
for i in {0..9}; do
|
|
count=$(tmux list-sessions -F '#{session_name}' 2>/dev/null | grep -c "^integrator-${i}-" 2>/dev/null || true)
|
|
count=${count:-0}
|
|
count=$(echo "$count" | tr -d '[:space:]')
|
|
[ "$count" -eq 0 ] 2>/dev/null && ((INTEGRATOR_EMPTY++))
|
|
done
|
|
|
|
SPLITLY_EMPTY=0
|
|
for i in {0..12}; do
|
|
count=$(tmux list-sessions -F '#{session_name}' 2>/dev/null | grep -c "^splitly-${i}-" 2>/dev/null || true)
|
|
count=${count:-0}
|
|
count=$(echo "$count" | tr -d '[:space:]')
|
|
[ "$count" -eq 0 ] 2>/dev/null && ((SPLITLY_EMPTY++))
|
|
done
|
|
|
|
echo "📊 SUMMARY: $TOTAL active sessions"
|
|
echo " Integrator: $INTEGRATOR_COUNT sessions, $INTEGRATOR_EMPTY empty checkouts"
|
|
echo " Splitly: $SPLITLY_COUNT sessions, $SPLITLY_EMPTY empty checkouts"
|
|
echo " AO: $AO_COUNT sessions"
|
|
echo ""
|
|
|
|
# Show integrator sessions
|
|
echo "┌──────────────────────────────────────────────────────────────────────────────┐"
|
|
echo "│ INTEGRATOR │"
|
|
echo "└──────────────────────────────────────────────────────────────────────────────┘"
|
|
|
|
if [ "$INTEGRATOR_COUNT" -eq 0 ]; then
|
|
echo " (no active sessions)"
|
|
else
|
|
# Get session details from the session data files
|
|
for session in $(tmux list-sessions -F '#{session_name}' 2>/dev/null | grep "^integrator-" | sort); do
|
|
activity=$(tmux display-message -t "$session" -p '#{session_activity}' 2>/dev/null)
|
|
now=$(date +%s)
|
|
if [ -n "$activity" ]; then
|
|
diff=$((now - activity))
|
|
if [ $diff -lt 60 ]; then
|
|
last_active="${diff}s ago"
|
|
elif [ $diff -lt 3600 ]; then
|
|
last_active="$((diff / 60))m ago"
|
|
elif [ $diff -lt 86400 ]; then
|
|
last_active="$((diff / 3600))h ago"
|
|
else
|
|
last_active="$((diff / 86400))d ago"
|
|
fi
|
|
else
|
|
last_active="-"
|
|
fi
|
|
|
|
data_file="$HOME/.integrator-sessions/$session"
|
|
meta_branch=$(grep "^branch=" "$data_file" 2>/dev/null | cut -d'=' -f2-)
|
|
meta_status=$(grep "^status=" "$data_file" 2>/dev/null | cut -d'=' -f2-)
|
|
meta_summary=$(grep "^summary=" "$data_file" 2>/dev/null | cut -d'=' -f2- | cut -c1-60)
|
|
|
|
# Get live Claude session info
|
|
eval "$(get_claude_info "$session" 2>/dev/null)"
|
|
|
|
echo " 📍 $session ($last_active)"
|
|
|
|
# Show branch - prefer live, show if different from metadata
|
|
if [ -n "$claude_branch" ]; then
|
|
if [ -n "$meta_branch" ] && [ "$claude_branch" != "$meta_branch" ]; then
|
|
echo " Branch: $claude_branch (meta: $meta_branch)"
|
|
else
|
|
echo " Branch: $claude_branch"
|
|
fi
|
|
elif [ -n "$meta_branch" ]; then
|
|
echo " Branch: $meta_branch (stale)"
|
|
fi
|
|
|
|
# Show summary - prefer Claude's auto-generated, fall back to metadata
|
|
if [ -n "$claude_summary" ]; then
|
|
echo " Claude: ${claude_summary:0:65}"
|
|
fi
|
|
if [ -n "$meta_summary" ]; then
|
|
echo " Status: ${meta_summary}..."
|
|
fi
|
|
|
|
# Show session ID if available (useful for resuming)
|
|
[ -n "$claude_session_id" ] && echo " Session: ${claude_session_id:0:8}..."
|
|
done
|
|
fi
|
|
|
|
echo ""
|
|
|
|
# Show splitly sessions
|
|
echo "┌──────────────────────────────────────────────────────────────────────────────┐"
|
|
echo "│ SPLITLY / SAFESPLIT │"
|
|
echo "└──────────────────────────────────────────────────────────────────────────────┘"
|
|
|
|
if [ "$SPLITLY_COUNT" -eq 0 ]; then
|
|
echo " (no active sessions)"
|
|
else
|
|
for session in $(tmux list-sessions -F '#{session_name}' 2>/dev/null | grep "^splitly-" | sort); do
|
|
activity=$(tmux display-message -t "$session" -p '#{session_activity}' 2>/dev/null)
|
|
now=$(date +%s)
|
|
if [ -n "$activity" ]; then
|
|
diff=$((now - activity))
|
|
if [ $diff -lt 60 ]; then
|
|
last_active="${diff}s ago"
|
|
elif [ $diff -lt 3600 ]; then
|
|
last_active="$((diff / 60))m ago"
|
|
elif [ $diff -lt 86400 ]; then
|
|
last_active="$((diff / 3600))h ago"
|
|
else
|
|
last_active="$((diff / 86400))d ago"
|
|
fi
|
|
else
|
|
last_active="-"
|
|
fi
|
|
|
|
data_file="$HOME/.splitly-sessions/$session"
|
|
meta_branch=$(grep "^branch=" "$data_file" 2>/dev/null | cut -d'=' -f2-)
|
|
meta_status=$(grep "^status=" "$data_file" 2>/dev/null | cut -d'=' -f2-)
|
|
meta_summary=$(grep "^summary=" "$data_file" 2>/dev/null | cut -d'=' -f2- | cut -c1-60)
|
|
|
|
# Get live Claude session info
|
|
eval "$(get_claude_info "$session" 2>/dev/null)"
|
|
|
|
echo " 📍 $session ($last_active)"
|
|
|
|
# Show branch - prefer live, show if different from metadata
|
|
if [ -n "$claude_branch" ]; then
|
|
if [ -n "$meta_branch" ] && [ "$claude_branch" != "$meta_branch" ]; then
|
|
echo " Branch: $claude_branch (meta: $meta_branch)"
|
|
else
|
|
echo " Branch: $claude_branch"
|
|
fi
|
|
elif [ -n "$meta_branch" ]; then
|
|
echo " Branch: $meta_branch (stale)"
|
|
fi
|
|
|
|
# Show summary - prefer Claude's auto-generated, fall back to metadata
|
|
if [ -n "$claude_summary" ]; then
|
|
echo " Claude: ${claude_summary:0:65}"
|
|
fi
|
|
if [ -n "$meta_summary" ]; then
|
|
echo " Status: ${meta_summary}..."
|
|
fi
|
|
|
|
# Show session ID if available (useful for resuming)
|
|
[ -n "$claude_session_id" ] && echo " Session: ${claude_session_id:0:8}..."
|
|
done
|
|
fi
|
|
|
|
echo ""
|
|
|
|
# Show ao sessions
|
|
echo "┌──────────────────────────────────────────────────────────────────────────────┐"
|
|
echo "│ AGENT-ORCHESTRATOR │"
|
|
echo "└──────────────────────────────────────────────────────────────────────────────┘"
|
|
|
|
if [ "$AO_COUNT" -eq 0 ]; then
|
|
echo " (no active sessions)"
|
|
else
|
|
for session in $(tmux list-sessions -F '#{session_name}' 2>/dev/null | grep "^ao-" | sort); do
|
|
activity=$(tmux display-message -t "$session" -p '#{session_activity}' 2>/dev/null)
|
|
now=$(date +%s)
|
|
if [ -n "$activity" ]; then
|
|
diff=$((now - activity))
|
|
if [ $diff -lt 60 ]; then
|
|
last_active="${diff}s ago"
|
|
elif [ $diff -lt 3600 ]; then
|
|
last_active="$((diff / 60))m ago"
|
|
elif [ $diff -lt 86400 ]; then
|
|
last_active="$((diff / 3600))h ago"
|
|
else
|
|
last_active="$((diff / 86400))d ago"
|
|
fi
|
|
else
|
|
last_active="-"
|
|
fi
|
|
|
|
data_file="$HOME/.ao-sessions/$session"
|
|
meta_branch=$(grep "^branch=" "$data_file" 2>/dev/null | cut -d'=' -f2-)
|
|
meta_status=$(grep "^status=" "$data_file" 2>/dev/null | cut -d'=' -f2-)
|
|
meta_summary=$(grep "^summary=" "$data_file" 2>/dev/null | cut -d'=' -f2- | cut -c1-60)
|
|
|
|
# Get live Claude session info
|
|
eval "$(get_claude_info "$session" 2>/dev/null)"
|
|
|
|
echo " 📍 $session ($last_active)"
|
|
|
|
# Show branch - prefer live, show if different from metadata
|
|
if [ -n "$claude_branch" ]; then
|
|
if [ -n "$meta_branch" ] && [ "$claude_branch" != "$meta_branch" ]; then
|
|
echo " Branch: $claude_branch (meta: $meta_branch)"
|
|
else
|
|
echo " Branch: $claude_branch"
|
|
fi
|
|
elif [ -n "$meta_branch" ]; then
|
|
echo " Branch: $meta_branch (stale)"
|
|
fi
|
|
|
|
# Show summary - prefer Claude's auto-generated, fall back to metadata
|
|
if [ -n "$claude_summary" ]; then
|
|
echo " Claude: ${claude_summary:0:65}"
|
|
fi
|
|
if [ -n "$meta_summary" ]; then
|
|
echo " Status: ${meta_summary}..."
|
|
fi
|
|
|
|
# Show session ID if available (useful for resuming)
|
|
[ -n "$claude_session_id" ] && echo " Session: ${claude_session_id:0:8}..."
|
|
done
|
|
fi
|
|
|
|
echo ""
|
|
echo "┌──────────────────────────────────────────────────────────────────────────────┐"
|
|
echo "│ QUICK ACTIONS │"
|
|
echo "└──────────────────────────────────────────────────────────────────────────────┘"
|
|
echo " ~/claude-spawn i [linear-url] - New integrator session in new iTerm2 tab"
|
|
echo " ~/claude-spawn s [issue-num] - New splitly session in new iTerm2 tab"
|
|
echo " ~/claude-spawn ao [linear-id] - New agent-orchestrator session"
|
|
echo " ~/claude-integrator-session cleanup"
|
|
echo " ~/claude-splitly-session cleanup"
|
|
echo " ~/claude-ao-session cleanup"
|
|
echo ""
|