#!/usr/bin/env bash
#
#   BrainBoxx installer
#   ───────────────────
#   curl -fsSL https://get.brainboxx.app | bash
#   curl -fsSL https://get.brainboxx.app | bash -s -- --pair A2E5-XFV4-Y7U9
#
#   Installs the `bb` agent on this machine (macOS or Linux), sets it to start
#   on boot, and — if given a pairing code — connects it to your phone.
#
#   Env overrides (advanced):
#     BB_DIST_URL   where to fetch bb from        (default https://get.brainboxx.app)
#     BB_RELAY      relay websocket url            (default wss://relay.brainboxx.app/ws)
#     BB_FOLDER     Folder this machine exposes    (default: the script asks; headless
#                   installs pair with no Folder — add one later with `bb folder add`)
#     BB_HOME       install location               (default $HOME/.brainboxx)
#     BB_PREFIX     where the `bb` command goes     (default /usr/local, else ~/.local)
#
set -euo pipefail

# ── config ───────────────────────────────────────────────────────────────────
BB_DIST_URL="${BB_DIST_URL:-https://get.brainboxx.app}"
BB_RELAY="${BB_RELAY:-wss://relay.brainboxx.app/ws}"
BB_FOLDER="${BB_FOLDER:-}"
BB_HOME="${BB_HOME:-$HOME/.brainboxx}"
PAIR_CODE=""

while [ $# -gt 0 ]; do
  case "$1" in
    --pair)  PAIR_CODE="${2:-}"; shift 2 ;;
    --relay) BB_RELAY="${2:-}";  shift 2 ;;
    --folder) BB_FOLDER="${2:-}"; shift 2 ;;
    -h|--help)
      grep '^#' "$0" | sed 's/^# \{0,1\}//' | sed '/!\/usr\/bin/d'; exit 0 ;;
    *) echo "bb-install: unknown option: $1" >&2; exit 1 ;;
  esac
done

# ── pretty output ────────────────────────────────────────────────────────────
if [ -t 1 ] && [ -z "${NO_COLOR:-}" ]; then
  BOLD=$(printf '\033[1m'); DIM=$(printf '\033[2m'); RESET=$(printf '\033[0m')
  CYAN=$(printf '\033[36m'); GREEN=$(printf '\033[32m'); RED=$(printf '\033[31m')
  GOLD=$(printf '\033[38;5;214m')   # brand warm — the hexagon mark
else
  BOLD=; DIM=; RESET=; CYAN=; GREEN=; RED=; GOLD=
fi
step() { printf '%s→%s %s\n' "$CYAN" "$RESET" "$1"; }
ok()   { printf '%s✓%s %s\n' "$GREEN" "$RESET" "$1"; }
warn() { printf '\033[33m!\033[0m %s\n' "$1"; }
die()  { printf '%s✗ %s%s\n' "$RED" "$1" "$RESET" >&2; exit 1; }

# A *usable, interactive* controlling terminal. The /dev/tty node can exist yet fail to open
# (non-interactive ssh, cron, `curl | bash` with no tty). BUT some non-interactive hosts — notably
# GitHub Codespaces' postCreateCommand — DO have an openable /dev/tty with no human behind it, so a
# prompt's `read < /dev/tty` blocks forever. Treat known headless envs (Codespaces, CI) and an explicit
# BB_YES as no-tty so the install never stalls on a prompt.
have_tty() {
  [ -n "${BB_YES:-}" ] && return 1
  [ "${CODESPACES:-}" = "true" ] && return 1
  [ -n "${CI:-}" ] && return 1
  (exec < /dev/tty) 2>/dev/null
}

banner() {
  printf '\n%s' "$GOLD"
  printf '    ----\n'
  printf '  /      \\\n'
  printf ' /        \\\n'
  printf '|   X  X   |\n'
  printf ' \\        /\n'
  printf '  \\      /\n'
  printf '    ----%s\n\n' "$RESET"
  printf '%s BrainBoxx%s\n' "$BOLD" "$RESET"
  printf ' %sthe box that holds your brains%s\n\n' "$DIM" "$RESET"
}

trap 'die "install failed — see the last line above. Nothing was left running."' ERR

# ── prior install? ───────────────────────────────────────────────────────────
# Re-running is always safe: it updates bb in place and keeps the machine's identity, code
# and Folders. The only thing that needs a decision is a *different* pairing code.
CONFIG_FILE="$HOME/.brainboxx/config.json"
EXISTING_CODE=""; HAS_FOLDER=0
if [ -f "$CONFIG_FILE" ]; then
  EXISTING_CODE=$(sed -n 's/.*"machineCode": *"\([^"]*\)".*/\1/p' "$CONFIG_FILE" | head -1)
  # folderPaths is current; boxPaths still appears in configs the new agent has not migrated yet.
  # Folder entries are the only absolute-path strings in the config.
  if grep -q '"/' "$CONFIG_FILE"; then
    HAS_FOLDER=1
  fi
fi

check_existing() {
  [ -f "$CONFIG_FILE" ] || return 0
  ok "existing install found — updating it (identity, code and Folders survive)"
  if [ "$HAS_FOLDER" = 1 ]; then
    local kept
    kept=$(grep -o '"/[^"]*"' "$CONFIG_FILE" | tr -d '"' | tr '\n' ' ')
    step "keeping its Folders: ${kept:-—}(swap: bb folder add/rm <dir>)"
  fi
  [ -n "$EXISTING_CODE" ] || return 0
  [ -n "$PAIR_CODE" ] || return 0
  local new_lc old_lc
  new_lc=$(printf '%s' "$PAIR_CODE" | tr '[:upper:]' '[:lower:]')
  old_lc=$(printf '%s' "$EXISTING_CODE" | tr '[:upper:]' '[:lower:]')
  if [ "$new_lc" = "$old_lc" ]; then
    PAIR_CODE=""   # same fleet — nothing to re-pair
  elif have_tty; then
    {
      printf '\n  %s! This machine is already paired to a different fleet.%s\n' "$BOLD" "$RESET"
      printf '    current code: %s\n    new code:     %s\n' "$EXISTING_CODE" "$new_lc"
      printf '  Switch it to the new code? Its Folders and Brains stay put. [y/N]: '
    } > /dev/tty 2>/dev/null || return 0
    local reply=""
    IFS= read -r reply < /dev/tty || reply=""
    case "$reply" in
      y|Y|yes|YES) step "re-pairing: $EXISTING_CODE → $new_lc" ;;
      *) PAIR_CODE=""; ok "keeping the existing pairing ($EXISTING_CODE)" ;;
    esac
  else
    # Headless with an explicit --pair: the flag is the authorisation — but say it loudly.
    printf '  ! re-pairing this machine: %s → %s\n' "$EXISTING_CODE" "$new_lc"
  fi
}

# ── choose the first Folder ──────────────────────────────────────────────────
# BrainBoxx never invents a Folder. Interactive installs are asked (existing or new path,
# Enter accepts the suggestion); headless installs (cloud-init, provisioning) pair with no
# Folder unless --folder/BB_FOLDER was given — add one later with `bb folder add <dir>`.
choose_folder() {
  [ -n "$BB_FOLDER" ] && return 0
  [ "$HAS_FOLDER" = 1 ] && return 0   # re-run: the machine already has its Folders
  # GitHub Codespaces clone every repo under /workspaces — watch it so the repo is a Brain the moment
  # the box pairs, no manual `bb add` from a web terminal. (Applies to the app's spin-up too.)
  if [ "${CODESPACES:-}" = "true" ] && [ -d /workspaces ]; then BB_FOLDER=/workspaces; return 0; fi
  have_tty || return 0                # headless (no usable tty) → pair with no Folder
  {
    printf '\n  Where should this machine'\''s first Folder live? Your Brains go inside it.\n'
    printf '  Path — existing or new [%s/Brains], or "none" to add one later: ' "$HOME"
  } > /dev/tty 2>/dev/null || return 0
  local reply=""
  IFS= read -r reply < /dev/tty || reply=""
  case "$reply" in
    "")              BB_FOLDER="$HOME/Brains" ;;
    none|None|NONE)  BB_FOLDER="" ;;
    *)               BB_FOLDER="${reply/#\~/$HOME}" ;;
  esac
}

# ── detect platform ──────────────────────────────────────────────────────────
UNAME_S="$(uname -s)"
case "$UNAME_S" in
  Darwin) PLATFORM=macos ;;
  Linux)  PLATFORM=linux ;;
  *) die "unsupported OS: $UNAME_S (BrainBoxx runs on macOS and Linux)" ;;
esac

# sudo only if we're not root and it's available
if [ "$(id -u)" -eq 0 ]; then SUDO=""; elif command -v sudo >/dev/null 2>&1; then SUDO="sudo"; else SUDO=""; fi

# ── prerequisites: Node ≥ 18 and a C++ toolchain (node-pty compiles) ─────────
node_major() { node -p 'process.versions.node.split(".")[0]' 2>/dev/null || echo 0; }
have_node()  { command -v node >/dev/null 2>&1 && [ "$(node_major)" -ge 18 ]; }

ensure_node() {
  have_node && { ok "Node $(node -v) present"; return; }
  # nvm-managed boxes (GitHub Codespaces, many dev containers) pin an old `node` on PATH that a fresh
  # apt/dnf install can't override — so drive nvm directly when it's present.
  local d
  for d in "${NVM_DIR:-}" "$HOME/.nvm" "$HOME/nvm" /usr/local/share/nvm /usr/local/nvm; do
    { [ -n "$d" ] && [ -s "$d/nvm.sh" ]; } || continue
    step "installing Node.js via nvm…"
    # shellcheck disable=SC1090
    . "$d/nvm.sh" >/dev/null 2>&1
    nvm install 22 >/dev/null 2>&1 && nvm alias default 22 >/dev/null 2>&1 && nvm use 22 >/dev/null 2>&1
    have_node && { ok "Node $(node -v) present (via nvm)"; return; }
    break
  done
  step "installing Node.js…"
  if [ "$PLATFORM" = macos ]; then
    if command -v brew >/dev/null 2>&1; then brew install node >/dev/null
    else die "Node ≥18 is required. Install it (e.g. 'brew install node') and re-run."; fi
  else
    if command -v apt-get >/dev/null 2>&1; then
      curl -fsSL https://deb.nodesource.com/setup_20.x | $SUDO -E bash - >/dev/null
      $SUDO apt-get install -y nodejs >/dev/null
    elif command -v dnf >/dev/null 2>&1; then $SUDO dnf install -y nodejs >/dev/null
    else die "Node ≥18 is required and no supported package manager was found. Install Node and re-run."; fi
  fi
  have_node || die "Node install did not produce Node ≥18. If this box uses nvm, run 'nvm install 22 && nvm alias default 22', then re-run."
  ok "Node $(node -v) installed"
}

# Make sure the box can actually build node-pty — enough disk, and enough RAM (or swap).
# A full disk or an OOM-killed compiler must never reach the user as a stack trace.
ensure_resources() {
  [ "$PLATFORM" = linux ] || return 0

  # BB_HOME usually doesn't exist yet on a fresh install — create it so the `df` below has a real
  # path to measure. A missing path makes `df` fail, and under `set -e`/`pipefail` that failed
  # command-substitution would silently abort the whole install (bites first-time Linux users).
  mkdir -p "$BB_HOME"

  # Disk: need headroom for build tools + node-gyp headers + the compile (~1.2 GB).
  local freek; freek=$(df -Pk "$BB_HOME" 2>/dev/null | awk 'NR==2{print $4}' || true)
  if [ -n "$freek" ] && [ "$freek" -lt 1200000 ]; then
    die "Only $(( freek / 1024 )) MB of disk free — BrainBoxx needs ~1.2 GB to build. Free some space and re-run."
  fi

  # RAM: a C++ compile needs headroom. On a tiny box with no swap, cc1plus gets OOM-killed.
  # Quietly add a 2 GB swapfile so the build can't die — the #1 cause of a failed install.
  local ramk swapk
  ramk=$(awk '/^MemTotal:/{print $2}' /proc/meminfo 2>/dev/null)
  swapk=$(awk '/^SwapTotal:/{print $2}' /proc/meminfo 2>/dev/null)
  if [ "${ramk:-9999999}" -lt 1100000 ] && [ "${swapk:-0}" -lt 262144 ]; then
    step "low memory ($(( ${ramk:-0} / 1024 )) MB, no swap) — adding a 2 GB swapfile so the build isn't killed…"
    if { $SUDO fallocate -l 2G /swapfile 2>/dev/null || $SUDO dd if=/dev/zero of=/swapfile bs=1M count=2048 2>/dev/null; } \
       && $SUDO chmod 600 /swapfile && $SUDO mkswap /swapfile >/dev/null 2>&1 && $SUDO swapon /swapfile 2>/dev/null; then
      $SUDO sh -c 'grep -q "^/swapfile " /etc/fstab || echo "/swapfile none swap sw 0 0" >> /etc/fstab'
      ok "swap added (survives reboot)"
    else
      printf '  (could not add swap; if the build gets killed, add a swapfile and re-run)\n'
    fi
  fi
}

ensure_build_tools() {
  if [ "$PLATFORM" = macos ]; then
    xcode-select -p >/dev/null 2>&1 || die "Xcode Command Line Tools are required (run 'xcode-select --install', then re-run)."
  elif command -v apt-get >/dev/null 2>&1 && ! command -v g++ >/dev/null 2>&1; then
    step "installing build tools…"
    # Wait out any apt lock (unattended-upgrades etc.) rather than failing instantly.
    local waited=0
    while $SUDO fuser /var/lib/dpkg/lock-frontend >/dev/null 2>&1; do
      [ "$waited" -ge 90 ] && break
      [ "$waited" -eq 0 ] && printf '  waiting for another apt process to finish…\n'
      sleep 3; waited=$(( waited + 3 ))
    done
    $SUDO apt-get update -y >/dev/null 2>&1 || true
    $SUDO apt-get install -y python3 make g++ ca-certificates >/dev/null 2>&1 \
      || die "couldn't install build tools via apt — check the box has free disk and no stuck apt process, then re-run."
  fi
  ok "build toolchain ready"
}

# ── fetch + install bb ───────────────────────────────────────────────────────
install_bb() {
  step "downloading bb…"
  TMP="$(mktemp -d)"; trap 'rm -rf "$TMP"' EXIT
  curl -fsSL "$BB_DIST_URL/bb-latest.tar.gz" -o "$TMP/bb.tar.gz" \
    || die "could not download bb from $BB_DIST_URL"
  rm -rf "$BB_HOME/app"; mkdir -p "$BB_HOME/app"
  tar -xzf "$TMP/bb.tar.gz" -C "$BB_HOME/app"
  ok "unpacked to $BB_HOME/app"

  step "building (this compiles the terminal engine)…"
  if ! ( cd "$BB_HOME/app" && npm install --omit=dev --no-audit --no-fund ) >"$TMP/build.log" 2>&1; then
    if grep -qiE 'Killed|out of memory|cannot allocate' "$TMP/build.log"; then
      die "the build ran out of memory (the compiler was killed). Add a 1–2 GB swapfile and re-run — that fixes it on small boxes."
    elif grep -qiE 'ENOSPC|no space left' "$TMP/build.log"; then
      die "the build ran out of disk space. Free some space and re-run."
    fi
    printf '%s\n' "$(tail -6 "$TMP/build.log")"
    die "the terminal engine failed to build (last lines above) — usually low memory (add swap) or missing build tools."
  fi
  ok "dependencies built"

  # put `bb` on PATH — prefer a writable system bin, fall back to ~/.local/bin
  local entry="$BB_HOME/app/index.js"; chmod +x "$entry"
  local prefix="${BB_PREFIX:-/usr/local}"
  if [ -w "$prefix/bin" ] || { [ -n "$SUDO" ] && [ -d "$prefix/bin" ]; }; then
    ${SUDO} ln -sf "$entry" "$prefix/bin/bb"; BB_BIN="$prefix/bin/bb"
  else
    mkdir -p "$HOME/.local/bin"; ln -sf "$entry" "$HOME/.local/bin/bb"; BB_BIN="$HOME/.local/bin/bb"
    case ":$PATH:" in *":$HOME/.local/bin:"*) : ;; *) NEEDS_PATH="$HOME/.local/bin" ;; esac
  fi
  ok "installed the ${BOLD}bb${RESET} command → $BB_BIN"
}

# ── pair + autostart ─────────────────────────────────────────────────────────
configure_bb() {
  step "pairing this machine…"
  if [ -n "$BB_FOLDER" ]; then
    mkdir -p "$BB_FOLDER"   # user-chosen path — the one creation the installer is allowed
    if [ -n "$PAIR_CODE" ]; then "$BB_BIN" pair --code "$PAIR_CODE" --relay "$BB_RELAY" --folder "$BB_FOLDER"
    else "$BB_BIN" pair --relay "$BB_RELAY" --folder "$BB_FOLDER"; fi
  else
    if [ -n "$PAIR_CODE" ]; then "$BB_BIN" pair --code "$PAIR_CODE" --relay "$BB_RELAY"
    else "$BB_BIN" pair --relay "$BB_RELAY"; fi
  fi
  [ -n "$PAIR_CODE" ] && ok "paired with code $PAIR_CODE"
  # Autostart via systemd only if this box actually runs it. Dev containers / Codespaces don't (no PID-1
  # systemd), so fall back to a plain background start — and be honest that it won't come back by itself
  # after a reboot (a Codespace is ephemeral anyway).
  if [ -d /run/systemd/system ]; then
    step "enabling start-on-boot…"
    if "$BB_BIN" install; then ok "bb is running and will survive reboot"
    else warn "systemd setup failed — starting bb in the background instead"; start_bb_background; fi
  else
    step "starting bb (no systemd here)…"
    start_bb_background
  fi
}

# Run the daemon detached, for boxes without systemd. A box like this has NO process supervisor, so if
# bb ever exits — its own stall-watchdog SIGKILL (which assumes launchd/systemd will relaunch it), a
# crash, an OOM — nothing brings it back and the phone is stuck "reconnecting" until the box restarts.
# So we run it under a tiny respawn loop: the supervisor a Codespace is missing. The daemon's
# single-instance guard makes a double-start safe, and it writes its own pid file we poll on below.
start_bb_background() {
  local sup="$BB_HOME/supervise.sh"
  cat > "$sup" <<SUP
#!/bin/sh
# BrainBoxx daemon supervisor — respawn bb if it dies (no systemd on this box). Single-instance:
# postCreate and postStart both launch this on the first boot, and it can be re-run on resume, so the
# lock stops two respawn loops from fighting (each would evict the other's bb → oscillation). flock is
# auto-released when this process dies (survives a Codespace hard-stop cleanly); the pidfile fallback
# clears a stale lock by liveness check.
if command -v flock >/dev/null 2>&1; then
  exec 9>"$BB_HOME/.superlock"
  flock -n 9 || exit 0
else
  P="$BB_HOME/supervisor.pid"
  [ -f "\$P" ] && kill -0 "\$(cat "\$P" 2>/dev/null)" 2>/dev/null && exit 0
  echo \$\$ > "\$P"
fi
while true; do
  "$BB_BIN" start --relay "$BB_RELAY" </dev/null >>"$BB_HOME/bb.log" 2>&1
  echo "[supervisor] bb exited, respawning in 2s" >>"$BB_HOME/bb.log"
  sleep 2
done
SUP
  chmod +x "$sup"
  # Fully detach the supervisor (setsid + </dev/null) so it isn't a child of a lifecycle command that
  # waits on it — without this, a Codespace postCreateCommand hangs forever on the never-exiting loop.
  if command -v setsid >/dev/null 2>&1; then
    setsid "$sup" </dev/null >/dev/null 2>&1 &
  else
    nohup "$sup" </dev/null >/dev/null 2>&1 &
  fi
  local pid tries=0
  while [ "$tries" -lt 12 ]; do
    pid=$(cat "$BB_HOME/daemon.pid" 2>/dev/null || echo "")
    { [ -n "$pid" ] && kill -0 "$pid" 2>/dev/null; } && break
    tries=$((tries + 1)); sleep 0.5
  done
  if [ -n "$pid" ] && kill -0 "$pid" 2>/dev/null; then
    ok "bb started (pid $pid) — supervised, so it self-heals if it ever exits while the box is up"
  else
    die "bb didn't stay up — see $BB_HOME/bb.log"
  fi
}

# In a Codespace, pre-install the repo's agent so opening a Brain from the phone Just Works — no
# round-trip "install it?" prompt and no waiting. We detect the agent the same way the daemon does
# (CLAUDE.md/.claude → claude, GEMINI.md/.gemini → gemini), defaulting to Claude when there's no marker
# (Codex/Grok have none — the app still offers to install on first open). Runs before configure_bb so
# the agent is ready the moment the machine appears. Best-effort throughout.
install_codespace_agent() {
  [ "${CODESPACES:-}" = "true" ] || return 0
  local repo kind=claude pkg
  for repo in /workspaces/*/; do
    [ -d "$repo" ] || continue
    if [ -e "${repo}.claude" ] || [ -e "${repo}CLAUDE.md" ]; then kind=claude; break; fi
    if [ -e "${repo}.gemini" ] || [ -e "${repo}GEMINI.md" ]; then kind=gemini; break; fi
  done
  command -v "$kind" >/dev/null 2>&1 && { ok "$kind present"; return 0; }
  case "$kind" in
    gemini) pkg="@google/gemini-cli" ;;
    *)      pkg="@anthropic-ai/claude-code"; kind=claude ;;
  esac
  step "installing the ${kind} agent…"
  if npm install -g "$pkg" >/dev/null 2>&1; then ok "${kind} ready"
  else warn "couldn't pre-install ${kind} — the app will offer to install it on first open"; fi
}

# Pre-load your Claude login so a fresh cloud box comes up already signed in — no /login, no browser
# round-trip. The app captures your creds the first time you sign in on any box, keeps them in the
# phone's Keychain, and injects them here as the CLAUDE_CREDENTIALS secret (base64 of
# ~/.claude/.credentials.json). Codespace-only; best-effort — a missing/expired blob just falls back to
# the one-tap sign-in the daemon drives, so it's never worse than today.
restore_claude_login() {
  [ "${CODESPACES:-}" = "true" ] || return 0
  [ -n "${CLAUDE_CREDENTIALS:-}" ] || return 0
  mkdir -p "$HOME/.claude"
  if printf '%s' "$CLAUDE_CREDENTIALS" | base64 -d > "$HOME/.claude/.credentials.json" 2>/dev/null && [ -s "$HOME/.claude/.credentials.json" ]; then
    chmod 600 "$HOME/.claude/.credentials.json"; ok "Claude login carried over — already signed in"
  else
    rm -f "$HOME/.claude/.credentials.json"; warn "couldn't apply the carried Claude login — you'll sign in once"
  fi
}

# ── run ──────────────────────────────────────────────────────────────────────
banner
step "installing on ${BOLD}${PLATFORM}${RESET}"
check_existing
choose_folder
[ -n "$BB_FOLDER" ] && step "Folder → $BB_FOLDER"
ensure_node
ensure_resources
ensure_build_tools
install_bb
install_codespace_agent
restore_claude_login
configure_bb
# The `claude` shell override was dropped — BrainBoxx doesn't touch your tools; run `bb claude` for
# continuity. Sweep up any helper an older install left in the shell rc so it stops intercepting.
for _rc in "$HOME/.zshrc" "$HOME/.bashrc" "$HOME/.bash_profile" "$HOME/.profile"; do
  [ -f "$_rc" ] || continue
  sed -i.bbbak '/# >>> brainboxx claude helper >>>/,/# <<< brainboxx claude helper <<</d' "$_rc" 2>/dev/null && rm -f "$_rc.bbbak"
done
trap - ERR

printf '\n%s✓ BrainBoxx is live on this machine.%s\n' "$GREEN$BOLD" "$RESET"
if [ -n "$BB_FOLDER" ]; then
  printf '  Its Folder is %s%s%s — Brains you create from the phone land there.\n' "$BOLD" "$BB_FOLDER" "$RESET"
  printf '  %sWatch another? cd into it and run  bb add%s\n' "$DIM" "$RESET"
elif [ "$HAS_FOLDER" != 1 ]; then
  printf '  No Folder yet — cd into your projects folder and run:  %sbb add%s\n' "$BOLD" "$RESET"
fi
printf '  %sRun  bb claude  in a Brain'\''s folder to start a joinable session.%s\n' "$DIM" "$RESET"
if [ -z "$PAIR_CODE" ]; then
  CODE=$("$BB_BIN" code 2>/dev/null | tr '[:lower:]' '[:upper:]')
  printf '  Get the BrainBoxx app and enter this code:  %s%s%s\n' "$BOLD$GREEN" "$CODE" "$RESET"
fi
if [ -n "${NEEDS_PATH:-}" ]; then
  # shellcheck disable=SC2016  # literal $PATH is intentional — user pastes this into their shell
  printf '  %sAdd to your shell so `bb` is found:%s  export PATH="%s:$PATH"\n' "$DIM" "$RESET" "$NEEDS_PATH"
fi
printf '\n'
