#!/usr/bin/env bash
# MindrianOS statusline resolver  (lives in the plugin; runs the real logic)
# ----------------------------------
# Execs the context-monitor script from the *active* installed version of
# mos@mindrian-marketplace, regardless of which cache directory Claude Code has
# materialized. ~/.claude/settings.json points at the deployed dispatcher shim
# (~/.claude/statusline-mos == scripts/statusline-mos-dispatch), which finds an
# installed plugin version and exec's this script. So this file is the one place
# the statusline logic lives -- a fix here ships in the plugin and reaches users
# on next session with no re-stamp of the deployment surface.
#
# Plugin-root resolution is delegated to lib/core/active-plugin-root.cjs (the
# single source of truth across the plugin: MINDRIAN_OS_ROOT ->
# installed_plugins.json [temporal truth: which install is actually active] ->
# newest marketplace-cache mos/<version>/ -> legacy clone -> not-found). If that
# resolver is not present next to this script -- e.g. this is an older deployed
# copy of the wrapper -- we fall back to a pre-release-tolerant scan of the
# marketplace cache.
#
# History: created 2026-04-14 after v1.10.4/v1.10.5 shipped to the marketplace
# cache but a fresh session still rendered the v1.10.0 legacy statusline (settings
# pointed at the legacy path). Then 2026-05-12: the fallback's pure-semver anchor
# rejected `-beta.N` dirs, so a box with 1.12.0 + 1.13.0-beta.9 picked 1.12.0;
# regex widened and resolution moved to the shared module.

set -euo pipefail

PLUGIN_ROOT=""

# 1. Canonical resolver, if it's sitting next to this script.
SELF_DIR="$(cd "$(dirname "$0")" 2>/dev/null && pwd || true)"
if command -v node >/dev/null 2>&1 && [ -n "${SELF_DIR}" ] && [ -f "${SELF_DIR}/../lib/core/active-plugin-root.cjs" ]; then
  PLUGIN_ROOT="$(node "${SELF_DIR}/../lib/core/active-plugin-root.cjs" 2>/dev/null || true)"
fi

# 2. Fallback: newest version dir in the marketplace cache. Directory names look
#    like "1.12.0", "1.13.0-beta.9", "1.12.5.1"; sort -V orders them correctly
#    (1.12.0 < 1.13.0-beta.9 < 1.13.0).
if [ -z "${PLUGIN_ROOT}" ]; then
  CACHE_ROOT="${HOME}/.claude/plugins/cache/mindrian-marketplace/mos"
  if [ -d "${CACHE_ROOT}" ]; then
    LATEST_VERSION="$(ls -1 "${CACHE_ROOT}" 2>/dev/null | grep -E '^[0-9]+(\.[0-9]+)+(-[A-Za-z0-9.]+)?$' | sort -V | tail -1)"
    if [ -n "${LATEST_VERSION}" ]; then
      PLUGIN_ROOT="${CACHE_ROOT}/${LATEST_VERSION}"
    fi
  fi
fi

if [ -z "${PLUGIN_ROOT}" ]; then
  # Plugin not installed / not resolvable: emit nothing so Claude Code's
  # default statusline renders.
  exit 0
fi

CONTEXT_MONITOR="${PLUGIN_ROOT}/scripts/context-monitor"

if [ ! -x "${CONTEXT_MONITOR}" ] && [ ! -f "${CONTEXT_MONITOR}" ]; then
  exit 0
fi

# Export MINDRIAN_OS_ROOT so context-monitor (and the focus probe below) compute
# everything from the same version this wrapper resolved.
export MINDRIAN_OS_ROOT="${PLUGIN_ROOT}"

# Phase 109-02: compute active session focus and emit a 🎯 focus glyph prefix
# when getActiveFocus returns non-null. The focus_segment is best-effort: any
# error in the navigation API or sqlite handle silently degrades to an empty
# prefix (Canon Part 8 LOCAL-only by construction; statusline must never
# block or throw). The 🎯 glyph is shared with the Phase 106-02 JTBD broadcast
# per the amended fence in tests/test-context-monitor-d02-broadcast.cjs (see
# the Phase 109-02 amendment block); auto-focus rule 1 makes the two signals
# semantically correlated. The prefix prints BEFORE exec so the statusline
# composes as: <focus_segment><context-monitor stdout>.
focus_segment=""
if [ -n "${MINDRIAN_ROOM:-}" ] && [ -n "${session_id:-}" ]; then
  focus_data=$(MINDRIAN_OS_ROOT="${MINDRIAN_OS_ROOT}" \
    MINDRIAN_ROOM="${MINDRIAN_ROOM}" \
    SESSION_ID="${session_id}" \
    node -e "
      const path = require('node:path');
      try {
        const root = process.env.MINDRIAN_OS_ROOT || '';
        const room = process.env.MINDRIAN_ROOM;
        const sess = process.env.SESSION_ID;
        const { openRoomDb } = require(path.join(root, 'lib', 'core', 'room-db.cjs'));
        const focus = require(path.join(root, 'lib', 'core', 'navigation', 'focus.cjs'));
        const db = openRoomDb(room);
        const f = focus.getActiveFocus(db, sess);
        try { db.close(); } catch (_) { /* ignore */ }
        if (f) process.stdout.write(JSON.stringify(f));
      } catch (_) { /* statusline never throws */ }
    " 2>/dev/null)
  if [ -n "$focus_data" ]; then
    focus_short=$(printf '%s' "$focus_data" | node -e "
      let buf = '';
      process.stdin.on('data', (d) => { buf += d; });
      process.stdin.on('end', () => {
        try {
          const f = JSON.parse(buf);
          const id = (f.focusNodeId || '').split(':').slice(1).join(':') || (f.focusNodeId || '');
          const trimmed = id.length > 24 ? id.slice(0, 21) + '...' : id;
          process.stdout.write((f.focusType || 'focus') + ':' + trimmed);
        } catch (_) { /* ignore */ }
      });
    " 2>/dev/null)
    if [ -n "$focus_short" ]; then
      focus_segment="🎯 ${focus_short} "
    fi
  fi
fi

if [ -n "$focus_segment" ]; then
  printf '%s' "$focus_segment"
fi

exec node "${CONTEXT_MONITOR}"
