#!/usr/bin/env bash set -euo pipefail # # session-status.sh — Check agent session health # # Usage: # session-status.sh [--project ] [--format table|json] # # Exit codes: # 0 = running # 2 = stale (recently died) # 3 = dead (no longer running) # 4 = no session SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" source "$SCRIPT_DIR/_lib.sh" # ─── Parse arguments ───────────────────────────────────────────────────────── PROJECT="." FORMAT="table" while [[ $# -gt 0 ]]; do case "$1" in --project) PROJECT="$2"; shift 2 ;; --format) FORMAT="$2"; shift 2 ;; -h|--help) echo "Usage: session-status.sh [--project ] [--format table|json]" exit 0 ;; *) echo "Unknown option: $1" >&2; exit 1 ;; esac done _require_jq runtime_cmd="$(coord_launch_command)" run_cmd="$(coord_run_command)" # ─── Check session lock ───────────────────────────────────────────────────── lock_data="" if ! lock_data="$(session_lock_read "$PROJECT")"; then # No active session — but check if a mission exists mp="$(mission_path "$PROJECT")" if [[ -f "$mp" ]]; then m_status="$(jq -r '.status // "inactive"' "$mp")" m_name="$(jq -r '.name // "unnamed"' "$mp")" m_id="$(jq -r '.mission_id // ""' "$mp")" m_total="$(jq '.milestones | length' "$mp")" m_done="$(jq '[.milestones[] | select(.status == "completed")] | length' "$mp")" m_current="$(jq -r '[.milestones[] | select(.status == "active" or .status == "pending")][0].name // "none"' "$mp")" # Task counts if TASKS.md exists task_json="$(count_tasks_md "$PROJECT")" t_total="$(echo "$task_json" | jq '.total')" t_done="$(echo "$task_json" | jq '.done')" t_pending="$(echo "$task_json" | jq '.pending')" t_inprog="$(echo "$task_json" | jq '.in_progress')" if [[ "$FORMAT" == "json" ]]; then jq -n \ --arg status "no-session" \ --arg mission_status "$m_status" \ --arg mission_name "$m_name" \ --arg mission_id "$m_id" \ --argjson milestones_total "$m_total" \ --argjson milestones_done "$m_done" \ --argjson tasks_total "$t_total" \ --argjson tasks_done "$t_done" \ '{ status: $status, mission: { status: $mission_status, name: $mission_name, id: $mission_id, milestones_total: $milestones_total, milestones_done: $milestones_done, tasks_total: $tasks_total, tasks_done: $tasks_done } }' else echo "" echo -e " ${C_DIM}No active agent session.${C_RESET}" echo "" # Mission info case "$m_status" in active) ms_color="${C_GREEN}ACTIVE${C_RESET}" ;; paused) ms_color="${C_YELLOW}PAUSED${C_RESET}" ;; completed) ms_color="${C_CYAN}COMPLETED${C_RESET}" ;; *) ms_color="${C_DIM}${m_status}${C_RESET}" ;; esac echo -e " ${C_BOLD}Mission:${C_RESET} $m_name" echo -e " ${C_CYAN}Status:${C_RESET} $ms_color" echo -e " ${C_CYAN}ID:${C_RESET} $m_id" echo -e " ${C_CYAN}Milestones:${C_RESET} $m_done / $m_total completed" [[ "$m_current" != "none" ]] && echo -e " ${C_CYAN}Current:${C_RESET} $m_current" if (( t_total > 0 )); then echo -e " ${C_CYAN}Tasks:${C_RESET} $t_done / $t_total done ($t_pending pending, $t_inprog in-progress)" fi echo "" if [[ "$m_status" == "active" || "$m_status" == "paused" ]]; then echo -e " ${C_BOLD}Next steps:${C_RESET}" echo " $run_cmd Auto-generate context and launch" echo " mosaic coord continue Generate continuation prompt" echo " $runtime_cmd Launch agent session" elif [[ "$m_status" == "completed" ]]; then echo -e " ${C_DIM}Mission completed. Start a new one with: mosaic coord init${C_RESET}" else echo -e " ${C_DIM}Initialize with: mosaic coord init --name \"Mission Name\"${C_RESET}" fi echo "" fi else if [[ "$FORMAT" == "json" ]]; then echo '{"status":"no-session","mission":null}' else echo "" echo -e " ${C_DIM}No active session.${C_RESET}" echo -e " ${C_DIM}No mission found.${C_RESET}" echo "" echo " Initialize with: mosaic coord init --name \"Mission Name\"" echo "" fi fi exit 4 fi # Parse lock session_id="$(echo "$lock_data" | jq -r '.session_id // "unknown"')" runtime="$(echo "$lock_data" | jq -r '.runtime // "unknown"')" pid="$(echo "$lock_data" | jq -r '.pid // 0')" started_at="$(echo "$lock_data" | jq -r '.started_at // ""')" milestone_id="$(echo "$lock_data" | jq -r '.milestone_id // ""')" # ─── Determine status ─────────────────────────────────────────────────────── status="unknown" exit_code=1 if is_pid_alive "$pid"; then status="running" exit_code=0 else # PID is dead — check how recently last_act="$(last_activity_time "$PROJECT")" now="$(epoch_now)" age=$(( now - last_act )) if (( age < STALE_THRESHOLD )); then status="stale" exit_code=2 elif (( age < DEAD_THRESHOLD )); then status="stale" exit_code=2 else status="dead" exit_code=3 fi fi # ─── Gather supplementary info ────────────────────────────────────────────── duration_secs=0 if [[ -n "$started_at" ]]; then start_epoch="$(iso_to_epoch "$started_at")" now="$(epoch_now)" duration_secs=$(( now - start_epoch )) fi last_act="$(last_activity_time "$PROJECT")" # Current milestone from mission.json current_ms="" if [[ -f "$(mission_path "$PROJECT")" ]]; then current_ms="$(current_milestone_id "$PROJECT")" if [[ -n "$current_ms" ]]; then ms_name="$(milestone_name "$PROJECT" "$current_ms")" [[ -n "$ms_name" ]] && current_ms="$current_ms ($ms_name)" fi fi # Next task from TASKS.md next_task="$(find_next_task "$PROJECT")" # ─── Output ────────────────────────────────────────────────────────────────── if [[ "$FORMAT" == "json" ]]; then jq -n \ --arg status "$status" \ --arg session_id "$session_id" \ --arg runtime "$runtime" \ --arg pid "$pid" \ --arg started_at "$started_at" \ --arg duration "$duration_secs" \ --arg milestone "$current_ms" \ --arg next_task "$next_task" \ --arg last_activity "$last_act" \ '{ status: $status, session_id: $session_id, runtime: $runtime, pid: ($pid | tonumber), started_at: $started_at, duration_seconds: ($duration | tonumber), milestone: $milestone, next_task: $next_task, last_activity_epoch: ($last_activity | tonumber) }' else # Color the status case "$status" in running) status_color="${C_GREEN}RUNNING${C_RESET}" ;; stale) status_color="${C_YELLOW}STALE${C_RESET}" ;; dead) status_color="${C_RED}DEAD${C_RESET}" ;; *) status_color="$status" ;; esac echo "" echo -e " Session Status: $status_color ($runtime)" echo -e " ${C_CYAN}Session ID:${C_RESET} $session_id" echo -e " ${C_CYAN}Started:${C_RESET} $started_at ($(format_duration "$duration_secs"))" echo -e " ${C_CYAN}PID:${C_RESET} $pid" [[ -n "$current_ms" ]] && echo -e " ${C_CYAN}Milestone:${C_RESET} $current_ms" [[ -n "$next_task" ]] && echo -e " ${C_CYAN}Next task:${C_RESET} $next_task" echo -e " ${C_CYAN}Last activity:${C_RESET} $(format_ago "$last_act")" echo "" if [[ "$status" == "stale" || "$status" == "dead" ]]; then echo -e " ${C_YELLOW}Session is no longer running.${C_RESET}" echo " Recovery: mosaic coord resume" echo " Continue: mosaic coord continue" echo "" fi fi exit "$exit_code"