#!/usr/bin/env bash
# pi-king — open the cross-project session dashboard.
#
# Deliberately does NOT set PI_CODING_AGENT_DIR. An earlier version pointed the
# hub at a private agent directory to get a quiet startup; because that variable
# is per-process, the hub and the sessions it spawned then computed different
# paths for their shared state and could not see each other. The dashboard was
# silently empty. Quiet startup now comes from a PROJECT settings file in the
# hub directory, which Pi resolves from cwd independently of the agent dir, so
# your own Pi configuration is left completely untouched.
set -uo pipefail

# The directory you invoked from, preserved across the cd below so that "new
# session" prefills where you actually are rather than where the hub lives.
export PI_KING_CWD="${PI_KING_CWD:-$PWD}"

# Resolve symlinks so the hub directory is found via the real script location,
# not via whatever alias invoked it. `readlink -f` is not portable to older
# macOS, hence the loop.
SELF="${BASH_SOURCE[0]}"
while [ -L "$SELF" ]; do
  LINK="$(readlink "$SELF")"
  case "$LINK" in
    /*) SELF="$LINK" ;;
    *)  SELF="$(dirname "$SELF")/$LINK" ;;
  esac
done
# Absolute path: launchd runs the plist with cwd=/ and the install case
# bakes SELF into the plist's ProgramArguments.
SELF="$(cd "$(dirname "$SELF")" && pwd)/$(basename "$SELF")"
HUB_DIR="$(cd "$(dirname "$SELF")/.." && pwd)/hub"
EXTENSION="$(cd "$(dirname "$SELF")/.." && pwd)/src/index.ts"

# Detached hub daemon (launchd KeepAlive agent com.stanz.pi-king-hub). Owns
# marker polling, injection, banner, and session-window restore 24/7 — the
# interactive hub exits on tmux attach, this one never does. Plain node, not
# a pi process (2026-08-10): the daemon never renders a TUI or reads a
# keypress, so the entire pi runtime -- model catalog, tool/extension
# registration, pi-tui -- was pure overhead. Measured live: pi-process
# daemon 3.57% CPU at rest after the lazy-rows fix (was 6.3-10.5% before
# it); plain node daemon 0.00% at rest, same restore/poll/inject behavior.
case "${1:-}" in
  --daemon)
    shift
    DAEMON="$(cd "$(dirname "$SELF")/.." && pwd)/scripts/hub-daemon.ts"
    exec node --experimental-strip-types "$DAEMON" "$@"
    ;;
  --patch-tui|patch-tui)
    shift
    PATCH_TOOL="$(cd "$(dirname "$SELF")/.." && pwd)/tools/patch-pi-tui.mjs"
    exec node "$PATCH_TOOL" "$@"
    ;;
  --patch-acp|patch-acp)
    shift
    PATCH_TOOL="$(cd "$(dirname "$SELF")/.." && pwd)/tools/patch-acp.mjs"
    exec node "$PATCH_TOOL" "$@"
    ;;
  --daemon-install)
    PLIST="$HOME/Library/LaunchAgents/com.stanz.pi-king-hub.plist"
    mkdir -p "$HOME/Library/LaunchAgents" "$HOME/.pi/king"
    cat > "$PLIST" <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>Label</key>
  <string>com.stanz.pi-king-hub</string>
  <key>ProgramArguments</key>
  <array>
    <string>/bin/zsh</string>
    <string>-lc</string>
    <string>exec "$SELF" --daemon</string>
  </array>
  <key>KeepAlive</key>
  <true/>
  <key>RunAtLoad</key>
  <true/>
  <key>ProcessType</key>
  <string>Background</string>
  <key>EnvironmentVariables</key>
  <dict>
    <key>PATH</key>
    <string>/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin</string>
  </dict>
  <key>StandardOutPath</key>
  <string>$HOME/.pi/king/hub.log</string>
  <key>StandardErrorPath</key>
  <string>$HOME/.pi/king/hub.log</string>
</dict>
</plist>
EOF
    launchctl bootout "gui/$(id -u)/com.stanz.pi-king-hub" 2>/dev/null || true
    launchctl bootstrap "gui/$(id -u)" "$PLIST"
    echo "pi-king hub daemon installed + started. Log: ~/.pi/king/hub.log"
    exit 0
    ;;
  --daemon-uninstall)
    launchctl bootout "gui/$(id -u)/com.stanz.pi-king-hub" 2>/dev/null || true
    rm -f "$HOME/Library/LaunchAgents/com.stanz.pi-king-hub.plist"
    echo "pi-king hub daemon stopped + removed."
    exit 0
    ;;
  --daemon-status)
    if launchctl print "gui/$(id -u)/com.stanz.pi-king-hub" >/dev/null 2>&1; then
      launchctl print "gui/$(id -u)/com.stanz.pi-king-hub" | grep -E 'state|pid|last exit' || true
      echo "--- hub.log tail ---"
      tail -5 "$HOME/.pi/king/hub.log" 2>/dev/null || true
    else
      echo "pi-king hub daemon is NOT loaded."
      exit 1
    fi
    exit 0
    ;;
esac

ACTION_FILE="$(mktemp -t pi-king-action)"
export PI_KING_ACTION_FILE="$ACTION_FILE"

cleanup() {
  rm -f "$ACTION_FILE"
  # Pi prints startup lines to the main buffer before its TUI takes over, so
  # they linger after exit; clear them so quitting returns a clean prompt.
  [ -t 1 ] && printf '\033[H\033[2J\033[3J'
  return 0
}
trap cleanup EXIT

# tmux is a hard requirement for the part people install this for. The
# dashboard itself works without it — it just has nothing to list and cannot
# background anything — so this warns rather than exits, and the attach path
# below fails with an explanation instead of "command not found".
if ! command -v tmux >/dev/null 2>&1; then
  echo "pi-king: tmux is not installed." >&2
  echo "  Sessions cannot be backgrounded or reattached without it." >&2
  echo "  macOS: brew install tmux     Debian/Ubuntu: apt install tmux" >&2
  echo >&2
fi

cd "$HUB_DIR" || exit 1

while true; do
  : > "$ACTION_FILE"

  # Loads ONLY this package's extension. The hub is a launcher: it lists
  # sessions and hands the terminal to tmux, and it never calls a model, so
  # every other extension is startup cost for nothing. Measured 2026-08-09 on
  # this machine: bare boot 0.44s, full normal-session package set 1.60s, and
  # almost all of that ~1.16s is two packages (@quintinshaw/pi-dynamic-workflows
  # +0.91s, pi-hermes-memory +0.55s) — NOT a router extension, and not this
  # extension, which costs +0.01s despite its size. Sessions STARTED from here
  # are unaffected: they are separate processes and load the full configuration.
  if [ -f "$EXTENSION" ]; then
    pi --no-session --no-tools --no-context-files --no-skills        --no-extensions --extension "$EXTENSION" --agents-hub "$@"
  else
    # Installed some other way: fall back to loading normally rather than
    # starting with no dashboard at all.
    pi --no-session --no-tools --no-context-files --no-skills --agents-hub "$@"
  fi

  # No action written => the user quit the dashboard. Done.
  [ -s "$ACTION_FILE" ] || break

  # tmuxName is the RAW first line of the action file, not JSON. The file's
  # second line carries the full JSON action for anything that wants it; the
  # name itself must never be JSON-escaped, because a session name containing
  # a double quote (e.g. a pi session renamed to "foo bar" issue) would then
  # arrive as \"... and sed extraction stops at that first quote, leaving a
  # stray backslash as the attach target (observed live as `can't find
  # session: \`).
  target="$(head -1 "$ACTION_FILE")"
  [ -n "$target" ] || break

  # tmux owns the terminal exclusively here; blocks until the user detaches.
  if ! command -v tmux >/dev/null 2>&1; then
    echo "pi-king: cannot attach to '$target' — tmux is not installed." >&2
    break
  fi
  tmux attach-session -t "$target"
done
