#!/usr/bin/env bash
# taskprio run.sh — the NEVER-STOP driver. v1.0 · https://taskprio.com/run.sh
#
# A single chat turn always ENDS (finite context); "never stop" is only real when a
# driver RE-INVOKES the agent when a turn ends. This is that driver: it relaunches the
# TaskPeace autopilot every time a session finishes — inside tmux (survives a closed
# window / disconnect) + caffeinate (Mac stays awake on AC) — forever, until YOU stop it.
#
#   curl -fsSL https://taskprio.com/run.sh | bash               # 1 session in the current dir
#   curl -fsSL https://taskprio.com/run.sh | bash -s -- 5       # 5 parallel sessions (self-divide the queue)
#   curl -fsSL https://taskprio.com/run.sh | bash -s -- 1 /path # 1 session in /path
#   curl -fsSL https://taskprio.com/run.sh | bash -s -- squad 3 # 3 pilots (P0-P5 roster order), each its OWN prompt
#   curl -fsSL https://taskprio.com/run.sh | bash -s -- stop    # graceful stop — the ONLY stop
#   curl -fsSL https://taskprio.com/run.sh | bash -s -- kill    # force-kill all sessions
#   curl -fsSL https://taskprio.com/run.sh | bash -s -- ls      # list running sessions
# (Saved locally you can also just: bash run.sh [N] [dir] | squad N [dir] | stop | kill | ls)
#
# Each session loops:  caffeinate -dimsu → claude -p "<prompt>" → on exit, re-poll/wait,
# relaunch — until the stop sentinel (~/.taskprio-stop) exists. Headless `-p` exits when a
# turn ends, so the loop re-fires it = true never-stop. The autopilot's HARD GATES (money /
# credentials / new accounts / publishing-as-you / destructive ops) still bind — it never
# auto-runs those. Requires: tmux + the `claude` CLI.
#
# Plain mode: every session fetches the same generic never-stop-prompt.txt (self-divides one
# queue). Squad mode: session i fetches pilots/<ROSTER[i]>.txt — a DISTINCT pilot identity per
# session (P0 ChiefPilot, P1 BountyPilot, P2 RankPilot, P3 CraftPilot, P4 SignalPilot, P5
# FoundryPilot — PILOT-SQUAD.md is the canonical spec). Each pilot prompt is self-contained: it
# fetches never-stop-prompt.txt itself as its base protocol, with its own overlay on top.
set -uo pipefail

PROMPT_URL="https://taskprio.com/never-stop-prompt.txt"
STOP="$HOME/.taskprio-stop"
RUNNER="$HOME/.taskprio-runner.sh"
PREFIX="tp"
# P0-P5, in roster order — the first N fly. Matches PILOT-SQUAD.md's "The roster" table and
# public/pilots/*.txt (the live-served single source of truth for each pilot's overlay).
ROSTER=(chiefpilot bountypilot rankpilot craftpilot signalpilot foundrypilot)
# Broad enough to match both plain-mode (tp, tp2, tp3…) and squad-mode (tp-chiefpilot,
# tp-bountypilot…) session names, so ls/stop/kill work uniformly across both modes.
_sessions(){ tmux ls 2>/dev/null | grep -oE "^${PREFIX}[0-9a-z-]*" || true; }

cmd="${1:-1}"
case "$cmd" in
  -h|--help|help) sed -n '2,34p' "$0" 2>/dev/null || echo "usage: run.sh [N] [dir] | squad N [dir] | stop | kill | ls"; exit 0;;
  ls)   s="$(_sessions)"; [ -n "$s" ] && echo "$s" || echo "no taskprio sessions running"; exit 0;;
  stop) : > "$STOP"
        for s in $(_sessions); do tmux send-keys -t "$s" C-c 2>/dev/null || true; done
        echo "stop requested — each session ends after its current run finishes (sentinel: $STOP)."
        echo "still running? 'bash run.sh kill' force-stops."; exit 0;;
  kill) rm -f "$STOP"
        for s in $(_sessions); do tmux kill-session -t "$s" && echo "killed $s"; done
        [ -z "$(_sessions)" ] && echo "all taskprio sessions stopped."; exit 0;;
  squad)
    N="${2:-}"
    DIR="${3:-$PWD}"
    case "$N" in ''|*[!0-9]*) echo "usage: run.sh squad N [dir]   (N = pilots to launch, roster is P0-P$((${#ROSTER[@]}-1)), i.e. max ${#ROSTER[@]})"; exit 1;; esac
    if [ "$N" -eq 0 ]; then echo "usage: run.sh squad N [dir]   (N must be >= 1)"; exit 1; fi
    if [ "$N" -gt "${#ROSTER[@]}" ]; then
      echo "squad: roster is P0-P$((${#ROSTER[@]}-1)) — only ${#ROSTER[@]} pilots defined. Launching all ${#ROSTER[@]} instead of $N (never dead-ends on an out-of-roster N)."
      N="${#ROSTER[@]}"
    fi
    [ -d "$DIR" ] || { echo "not a directory: $DIR"; exit 1; }
    command -v tmux   >/dev/null 2>&1 || { echo "tmux not found — install it first: brew install tmux"; exit 1; }
    command -v claude >/dev/null 2>&1 || { echo "claude (Claude Code) not on PATH — install Claude Code first"; exit 1; }
    rm -f "$STOP"
    started=0
    for i in $(seq 0 $((N - 1))); do
      pilot="${ROSTER[$i]}"
      s="${PREFIX}-${pilot}"
      if tmux has-session -t "$s" 2>/dev/null; then
        echo "$s already running (attach: tmux attach -t $s)"; continue
      fi
      RUNNER_I="$HOME/.taskprio-runner-${pilot}.sh"
      cat > "$RUNNER_I" <<RUNEOF
#!/usr/bin/env bash
STOP="$STOP"
URL="https://taskprio.com/pilots/${pilot}.txt"
until [ -f "\$STOP" ]; do
  P="\$(curl -fsSL "\$URL" 2>/dev/null)"
  if [ -z "\$P" ]; then sleep 30; continue; fi   # transient fetch failure → wait, retry (don't quit)
  claude --dangerously-skip-permissions -p "\$P" || true
  [ -f "\$STOP" ] && break
  sleep 5                                          # brief gap, then re-fire — the re-invocation driver
done
echo "taskprio: ${pilot} stopped (sentinel \$STOP present)"
RUNEOF
      chmod +x "$RUNNER_I"
      tmux new-session -d -s "$s" -c "$DIR" "caffeinate -dimsu bash '$RUNNER_I'"
      echo "started $s — P$i $pilot — in $DIR   (watch: tmux attach -t $s — detach with Ctrl-b d)"
      started=$((started + 1))
    done
    echo ""
    echo "SQUAD active — $started pilot(s), each fetching its own prompt from taskprio.com/pilots/*.txt:"
    for i in $(seq 0 $((N - 1))); do echo "  P$i ${ROSTER[$i]}"; done
    echo "It relaunches forever; the ONLY stop is yours:"
    echo "  bash run.sh stop   (graceful — finishes the current run, then exits)"
    echo "  bash run.sh kill   (force-stop now)"
    exit 0;;
esac

N="$cmd"
case "$N" in ''|*[!0-9]*) echo "usage: run.sh [N] [dir] | stop | kill | ls   ('-h' for help)"; exit 1;; esac
DIR="${2:-$PWD}"
command -v tmux   >/dev/null 2>&1 || { echo "tmux not found — install it first: brew install tmux"; exit 1; }
command -v claude >/dev/null 2>&1 || { echo "claude (Claude Code) not on PATH — install Claude Code first"; exit 1; }
[ -d "$DIR" ] || { echo "not a directory: $DIR"; exit 1; }
rm -f "$STOP"   # a fresh launch clears any prior stop sentinel

# Self-contained per-session runner: STOP + URL are baked in now; the prompt is fetched
# FRESH each iteration (so you always run the latest), and the curl/loop run at runtime.
cat > "$RUNNER" <<RUNEOF
#!/usr/bin/env bash
STOP="$STOP"
URL="$PROMPT_URL"
until [ -f "\$STOP" ]; do
  P="\$(curl -fsSL "\$URL" 2>/dev/null)"
  if [ -z "\$P" ]; then sleep 30; continue; fi   # transient fetch failure → wait, retry (don't quit)
  claude --dangerously-skip-permissions -p "\$P" || true
  [ -f "\$STOP" ] && break
  sleep 5                                          # brief gap, then re-fire — the re-invocation driver
done
echo "taskprio: stopped (sentinel \$STOP present)"
RUNEOF
chmod +x "$RUNNER"

started=0
for i in $(seq 1 "$N"); do
  s="$PREFIX"; [ "$i" -gt 1 ] && s="$PREFIX$i"
  if tmux has-session -t "$s" 2>/dev/null; then
    echo "$s already running (attach: tmux attach -t $s)"; continue
  fi
  tmux new-session -d -s "$s" -c "$DIR" "caffeinate -dimsu bash '$RUNNER'"
  echo "started $s in $DIR   (watch: tmux attach -t $s — detach with Ctrl-b d)"
  started=$((started+1))
done
echo ""
echo "NEVER-STOP active — $started session(s). It relaunches forever; the ONLY stop is yours:"
echo "  bash run.sh stop   (graceful — finishes the current run, then exits)"
echo "  bash run.sh kill   (force-stop now)"
