#!/usr/bin/env bash
set -euo pipefail

# mosaic — Unified agent launcher and management CLI
#
# AGENTS.md is the global policy source for all agent sessions.
# The launcher injects a composed runtime contract (AGENTS + runtime reference).
#
# Usage:
#   mosaic claude [args...]       Launch Claude Code with runtime contract injected
#   mosaic opencode [args...]     Launch OpenCode with runtime contract injected
#   mosaic codex [args...]        Launch Codex with runtime contract injected
#   mosaic yolo <runtime> [args...]  Launch runtime in dangerous-permissions mode
#   mosaic --yolo <runtime> [args...]  Alias for yolo
#   mosaic init [args...]         Generate SOUL.md interactively
#   mosaic doctor [args...]       Health audit
#   mosaic sync [args...]         Sync skills
#   mosaic seq [subcommand]       sequential-thinking MCP management (check/fix/start)
#   mosaic bootstrap <path>       Bootstrap a repo
#   mosaic upgrade release         Upgrade installed Mosaic release
#   mosaic upgrade check           Check release upgrade status (no changes)
#   mosaic upgrade project [args]  Upgrade project-local stale files

MOSAIC_HOME="${MOSAIC_HOME:-$HOME/.config/mosaic}"
VERSION="0.1.0"

usage() {
  cat <<USAGE
mosaic $VERSION — Unified agent launcher

Usage: mosaic <command> [args...]

Agent Launchers:
  pi [args...]           Launch Pi with runtime contract injected (recommended)
  claude [args...]       Launch Claude Code with runtime contract injected
  opencode [args...]     Launch OpenCode with runtime contract injected
  codex [args...]        Launch Codex with runtime contract injected
  yolo <runtime> [args...]  Dangerous mode for claude|codex|opencode|pi
  --yolo <runtime> [args...] Alias for yolo

Management:
  init [args...]         Generate SOUL.md (agent identity contract)
  doctor [args...]       Audit runtime state and detect drift
  sync [args...]         Sync skills from canonical source
  seq [subcommand]       sequential-thinking MCP management:
                         check [--runtime <r>] [--strict]
                         fix [--runtime <r>]
                         start
  bootstrap <path>       Bootstrap a repo with Mosaic standards
  upgrade [mode] [args]  Upgrade release (default) or project files
  upgrade check          Check release upgrade status (no changes)
  release-upgrade [...]  Upgrade installed Mosaic release
  project-upgrade [...]  Clean up stale SOUL.md/CLAUDE.md in a project

PRD:
  prdy <subcommand>      PRD creation and validation
                         init    Create docs/PRD.md via guided runtime session
                         update  Update existing PRD via guided runtime session
                         validate Check PRD completeness (bash-only)
                         status  Quick PRD health check (one-liner)

Coordinator (r0):
  coord <subcommand>     Manual coordinator tools
                         init    Initialize a new mission
                         mission Show mission progress dashboard
                         status  Check agent session health
                         continue Generate continuation prompt
                         run     Generate context and launch selected runtime
                         resume  Crash recovery

Options:
  -h, --help             Show this help
  -v, --version          Show version

All arguments after the command are forwarded to the target CLI.
USAGE
}

# Pre-flight checks
check_mosaic_home() {
  if [[ ! -d "$MOSAIC_HOME" ]]; then
    echo "[mosaic] ERROR: ~/.config/mosaic not found." >&2
    echo "[mosaic] Install with: curl -sL https://git.mosaicstack.dev/mosaic/bootstrap/raw/branch/main/remote-install.sh | sh" >&2
    exit 1
  fi
}

check_agents_md() {
  if [[ ! -f "$MOSAIC_HOME/AGENTS.md" ]]; then
    echo "[mosaic] ERROR: ~/.config/mosaic/AGENTS.md not found." >&2
    echo "[mosaic] Re-run the installer: cd ~/src/mosaic-bootstrap && bash install.sh" >&2
    exit 1
  fi
}

check_soul() {
  if [[ ! -f "$MOSAIC_HOME/SOUL.md" ]]; then
    echo "[mosaic] SOUL.md not found. Running mosaic init..."
    "$MOSAIC_HOME/bin/mosaic-init"
  fi
}

check_runtime() {
  local cmd="$1"
  if ! command -v "$cmd" >/dev/null 2>&1; then
    echo "[mosaic] ERROR: '$cmd' not found in PATH." >&2
    echo "[mosaic] Install $cmd before launching." >&2
    exit 1
  fi
}

check_sequential_thinking() {
  local runtime="${1:-all}"
  local checker="$MOSAIC_HOME/bin/mosaic-ensure-sequential-thinking"
  if [[ ! -x "$checker" ]]; then
    echo "[mosaic] ERROR: sequential-thinking checker missing: $checker" >&2
    exit 1
  fi
  if ! "$checker" --check --runtime "$runtime" >/dev/null 2>&1; then
    echo "[mosaic] ERROR: sequential-thinking MCP is required but not configured." >&2
    echo "[mosaic] Fix config: $checker --runtime $runtime" >&2
    echo "[mosaic] Or run: mosaic seq fix --runtime $runtime" >&2
    echo "[mosaic] Manual server start: mosaic seq start" >&2
    exit 1
  fi
}

runtime_contract_path() {
  local runtime="$1"
  case "$runtime" in
    claude) echo "$MOSAIC_HOME/runtime/claude/RUNTIME.md" ;;
    codex) echo "$MOSAIC_HOME/runtime/codex/RUNTIME.md" ;;
    opencode) echo "$MOSAIC_HOME/runtime/opencode/RUNTIME.md" ;;
    pi) echo "$MOSAIC_HOME/runtime/pi/RUNTIME.md" ;;
    *)
      echo "[mosaic] ERROR: unsupported runtime '$runtime' for runtime contract." >&2
      exit 1
      ;;
  esac
}

build_runtime_prompt() {
  local runtime="$1"
  local runtime_file
  runtime_file="$(runtime_contract_path "$runtime")"
  if [[ ! -f "$runtime_file" ]]; then
    echo "[mosaic] ERROR: runtime contract not found: $runtime_file" >&2
    exit 1
  fi

  # Inject active mission context FIRST so the agent sees it immediately
  local mission_file=".mosaic/orchestrator/mission.json"
  if [[ -f "$mission_file" ]] && command -v jq &>/dev/null; then
    local m_status
    m_status="$(jq -r '.status // "inactive"' "$mission_file" 2>/dev/null)"
    if [[ "$m_status" == "active" || "$m_status" == "paused" ]]; then
      local m_name m_id m_count m_completed
      m_name="$(jq -r '.name // "unnamed"' "$mission_file")"
      m_id="$(jq -r '.mission_id // ""' "$mission_file")"
      m_count="$(jq '.milestones | length' "$mission_file")"
      m_completed="$(jq '[.milestones[] | select(.status == "completed")] | length' "$mission_file")"

      cat <<MISSION_EOF
# ACTIVE MISSION — HARD GATE (Read Before Anything Else)

An active orchestration mission exists in this project. This is a BLOCKING requirement.

**Mission:** $m_name
**ID:** $m_id
**Status:** $m_status
**Milestones:** $m_completed / $m_count completed

## MANDATORY — Before ANY Response to the User

You MUST complete these steps before responding to any user message, including simple greetings:

1. Read \`~/.config/mosaic/guides/ORCHESTRATOR-PROTOCOL.md\` (mission lifecycle protocol)
2. Read \`docs/MISSION-MANIFEST.md\` for full mission scope, milestones, and success criteria
3. Read the latest scratchpad in \`docs/scratchpads/\` for session history, decisions, and corrections
4. Read \`docs/TASKS.md\` for current task state (what is done, what is next)
5. After reading all four, acknowledge the mission state to the user before proceeding

If the user gives a task, execute it within the mission context. If no task is given, present mission status and ask how to proceed.

MISSION_EOF
    fi
  fi

  # Inject PRD status so the agent knows requirements state
  local prd_file="docs/PRD.md"
  if [[ -f "$prd_file" ]]; then
    local prd_sections=0
    local prd_assumptions=0
    for entry in "Problem Statement|^#{2,3} .*(problem statement|objective)" \
                 "Scope / Non-Goals|^#{2,3} .*(scope|non.goal|out of scope|in.scope)" \
                 "User Stories / Requirements|^#{2,3} .*(user stor|stakeholder|user.*requirement)" \
                 "Functional Requirements|^#{2,3} .*functional requirement" \
                 "Non-Functional Requirements|^#{2,3} .*non.functional" \
                 "Acceptance Criteria|^#{2,3} .*acceptance criteria" \
                 "Technical Considerations|^#{2,3} .*(technical consideration|constraint|dependenc)" \
                 "Risks / Open Questions|^#{2,3} .*(risk|open question)" \
                 "Success Metrics / Testing|^#{2,3} .*(success metric|test|verification)" \
                 "Milestones / Delivery|^#{2,3} .*(milestone|delivery|scope version)"; do
      local pattern="${entry#*|}"
      grep -qiE "$pattern" "$prd_file" 2>/dev/null && prd_sections=$((prd_sections + 1))
    done
    prd_assumptions=$(grep -c 'ASSUMPTION:' "$prd_file" 2>/dev/null || echo 0)

    local prd_status="ready"
    (( prd_sections < 10 )) && prd_status="incomplete ($prd_sections/10 sections)"

    cat <<PRD_EOF

# PRD Status

- **File:** docs/PRD.md
- **Status:** $prd_status
- **Assumptions:** $prd_assumptions

PRD_EOF
  fi

  cat <<'EOF'
# Mosaic Launcher Runtime Contract (Hard Gate)

This contract is injected by `mosaic` launch and is mandatory.

First assistant response MUST start with exactly one mode declaration line:
1. Orchestration mission: `Now initiating Orchestrator mode...`
2. Implementation mission: `Now initiating Delivery mode...`
3. Review-only mission: `Now initiating Review mode...`

No tool call or implementation step may occur before that first line.

Mosaic hard gates OVERRIDE runtime-default caution for routine delivery operations.
For required push/merge/issue-close/release actions, execute without routine confirmation prompts.

EOF

  cat "$MOSAIC_HOME/AGENTS.md"

  if [[ -f "$MOSAIC_HOME/USER.md" ]]; then
    printf '\n\n# User Profile\n\n'
    cat "$MOSAIC_HOME/USER.md"
  fi

  if [[ -f "$MOSAIC_HOME/TOOLS.md" ]]; then
    printf '\n\n# Machine Tools\n\n'
    cat "$MOSAIC_HOME/TOOLS.md"
  fi

  printf '\n\n# Runtime-Specific Contract\n\n'
  cat "$runtime_file"
}

# Ensure runtime contract is present at the runtime's native config path.
# Used for runtimes that do not support CLI prompt injection.
ensure_runtime_config() {
  local runtime="$1"
  local dst="$2"
  local tmp
  tmp="$(mktemp)"
  mkdir -p "$(dirname "$dst")"
  build_runtime_prompt "$runtime" > "$tmp"
  if ! cmp -s "$tmp" "$dst" 2>/dev/null; then
    mv "$tmp" "$dst"
  else
    rm -f "$tmp"
  fi
}

# Detect active mission and return an initial prompt if one exists.
# Sets MOSAIC_MISSION_PROMPT as a side effect.
_detect_mission_prompt() {
  MOSAIC_MISSION_PROMPT=""
  local mission_file=".mosaic/orchestrator/mission.json"
  if [[ -f "$mission_file" ]] && command -v jq &>/dev/null; then
    local m_status
    m_status="$(jq -r '.status // "inactive"' "$mission_file" 2>/dev/null)"
    if [[ "$m_status" == "active" || "$m_status" == "paused" ]]; then
      local m_name
      m_name="$(jq -r '.name // "unnamed"' "$mission_file")"
      MOSAIC_MISSION_PROMPT="Active mission detected: ${m_name}. Read the mission state files and report status."
    fi
  fi
}

# Write a session lock if an active mission exists in the current directory.
# Called before exec so $$ captures the PID that will become the agent process.
_write_launcher_session_lock() {
  local runtime="$1"
  local mission_file=".mosaic/orchestrator/mission.json"
  local lock_file=".mosaic/orchestrator/session.lock"

  # Only write lock if mission exists and is active
  [[ -f "$mission_file" ]] || return 0
  command -v jq &>/dev/null || return 0

  local m_status
  m_status="$(jq -r '.status // "inactive"' "$mission_file" 2>/dev/null)"
  [[ "$m_status" == "active" || "$m_status" == "paused" ]] || return 0

  local session_id
  session_id="${runtime}-$(date +%Y%m%d-%H%M%S)-$$"

  jq -n \
    --arg sid "$session_id" \
    --arg rt "$runtime" \
    --arg pid "$$" \
    --arg ts "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
    --arg pp "$(pwd)" \
    --arg mid "" \
    '{
      session_id: $sid,
      runtime: $rt,
      pid: ($pid | tonumber),
      started_at: $ts,
      project_path: $pp,
      milestone_id: $mid
    }' > "$lock_file"
}

# Clean up session lock on exit (covers normal exit + signals).
# Registered via trap after _write_launcher_session_lock succeeds.
_cleanup_session_lock() {
  rm -f ".mosaic/orchestrator/session.lock" 2>/dev/null
}

# Launcher functions
launch_claude() {
  check_mosaic_home
  check_agents_md
  check_soul
  check_runtime "claude"
  check_sequential_thinking "claude"

  _check_resumable_session

  # Claude supports --append-system-prompt for direct injection
  local runtime_prompt
  runtime_prompt="$(build_runtime_prompt "claude")"

  # If active mission exists and no user prompt was given, inject initial prompt
  _detect_mission_prompt
  _write_launcher_session_lock "claude"
  trap _cleanup_session_lock EXIT INT TERM
  if [[ -n "$MOSAIC_MISSION_PROMPT" && $# -eq 0 ]]; then
    echo "[mosaic] Launching Claude Code (active mission detected)..."
    exec claude --append-system-prompt "$runtime_prompt" "$MOSAIC_MISSION_PROMPT"
  else
    echo "[mosaic] Launching Claude Code..."
    exec claude --append-system-prompt "$runtime_prompt" "$@"
  fi
}

launch_opencode() {
  check_mosaic_home
  check_agents_md
  check_soul
  check_runtime "opencode"
  check_sequential_thinking "opencode"

  _check_resumable_session

  # OpenCode reads from ~/.config/opencode/AGENTS.md
  ensure_runtime_config "opencode" "$HOME/.config/opencode/AGENTS.md"
  _write_launcher_session_lock "opencode"
  trap _cleanup_session_lock EXIT INT TERM
  echo "[mosaic] Launching OpenCode..."
  exec opencode "$@"
}

launch_codex() {
  check_mosaic_home
  check_agents_md
  check_soul
  check_runtime "codex"
  check_sequential_thinking "codex"

  _check_resumable_session

  # Codex reads from ~/.codex/instructions.md
  ensure_runtime_config "codex" "$HOME/.codex/instructions.md"
  _detect_mission_prompt
  _write_launcher_session_lock "codex"
  trap _cleanup_session_lock EXIT INT TERM
  if [[ -n "$MOSAIC_MISSION_PROMPT" && $# -eq 0 ]]; then
    echo "[mosaic] Launching Codex (active mission detected)..."
    exec codex "$MOSAIC_MISSION_PROMPT"
  else
    echo "[mosaic] Launching Codex..."
    exec codex "$@"
  fi
}

launch_pi() {
  check_mosaic_home
  check_agents_md
  check_soul
  check_runtime "pi"
  # Pi has native thinking levels — no sequential-thinking gate required

  _check_resumable_session

  local runtime_prompt
  runtime_prompt="$(build_runtime_prompt "pi")"

  # Build skill args from Mosaic skills directories (canonical + local)
  local -a skill_args=()
  for skills_root in "$MOSAIC_HOME/skills" "$MOSAIC_HOME/skills-local"; do
    [[ -d "$skills_root" ]] || continue
    for skill_dir in "$skills_root"/*/; do
      [[ -f "${skill_dir}SKILL.md" ]] && skill_args+=(--skill "$skill_dir")
    done
  done

  # Load Mosaic extension if present
  local -a ext_args=()
  local mosaic_ext="$MOSAIC_HOME/runtime/pi/mosaic-extension.ts"
  [[ -f "$mosaic_ext" ]] && ext_args=(--extension "$mosaic_ext")

  _detect_mission_prompt
  _write_launcher_session_lock "pi"
  trap _cleanup_session_lock EXIT INT TERM
  if [[ -n "$MOSAIC_MISSION_PROMPT" && $# -eq 0 ]]; then
    echo "[mosaic] Launching Pi (active mission detected)..."
    exec pi --append-system-prompt "$runtime_prompt" \
       "${skill_args[@]}" "${ext_args[@]}" "$MOSAIC_MISSION_PROMPT"
  else
    echo "[mosaic] Launching Pi..."
    exec pi --append-system-prompt "$runtime_prompt" \
       "${skill_args[@]}" "${ext_args[@]}" "$@"
  fi
}

launch_yolo() {
  if [[ $# -eq 0 ]]; then
    echo "[mosaic] ERROR: yolo requires a runtime (claude|codex|opencode|pi)." >&2
    echo "[mosaic] Example: mosaic yolo claude" >&2
    exit 1
  fi

  local runtime="$1"
  shift

  case "$runtime" in
    claude)
      check_mosaic_home
      check_agents_md
      check_soul
      check_runtime "claude"
      check_sequential_thinking "claude"

      # Claude uses an explicit dangerous permissions flag.
      local runtime_prompt
      runtime_prompt="$(build_runtime_prompt "claude")"

      _detect_mission_prompt
      _write_launcher_session_lock "claude"
      trap _cleanup_session_lock EXIT INT TERM
      if [[ -n "$MOSAIC_MISSION_PROMPT" && $# -eq 0 ]]; then
        echo "[mosaic] Launching Claude Code in YOLO mode (active mission detected)..."
        exec claude --dangerously-skip-permissions --append-system-prompt "$runtime_prompt" "$MOSAIC_MISSION_PROMPT"
      else
        echo "[mosaic] Launching Claude Code in YOLO mode (dangerous permissions enabled)..."
        exec claude --dangerously-skip-permissions --append-system-prompt "$runtime_prompt" "$@"
      fi
      ;;
    codex)
      check_mosaic_home
      check_agents_md
      check_soul
      check_runtime "codex"
      check_sequential_thinking "codex"

      # Codex reads instructions.md from ~/.codex and supports a direct dangerous flag.
      ensure_runtime_config "codex" "$HOME/.codex/instructions.md"
      _detect_mission_prompt
      _write_launcher_session_lock "codex"
      trap _cleanup_session_lock EXIT INT TERM
      if [[ -n "$MOSAIC_MISSION_PROMPT" && $# -eq 0 ]]; then
        echo "[mosaic] Launching Codex in YOLO mode (active mission detected)..."
        exec codex --dangerously-bypass-approvals-and-sandbox "$MOSAIC_MISSION_PROMPT"
      else
        echo "[mosaic] Launching Codex in YOLO mode (dangerous permissions enabled)..."
        exec codex --dangerously-bypass-approvals-and-sandbox "$@"
      fi
      ;;
    opencode)
      check_mosaic_home
      check_agents_md
      check_soul
      check_runtime "opencode"
      check_sequential_thinking "opencode"

      # OpenCode defaults to allow-all permissions unless user config restricts them.
      ensure_runtime_config "opencode" "$HOME/.config/opencode/AGENTS.md"
      _write_launcher_session_lock "opencode"
      trap _cleanup_session_lock EXIT INT TERM
      echo "[mosaic] Launching OpenCode in YOLO mode..."
      exec opencode "$@"
      ;;
    pi)
      # Pi has no permission restrictions — yolo is identical to normal launch
      launch_pi "$@"
      ;;
    *)
      echo "[mosaic] ERROR: Unsupported yolo runtime '$runtime'. Use claude|codex|opencode|pi." >&2
      exit 1
      ;;
  esac
}

# Delegate to existing scripts
run_init() {
  # Prefer wizard if Node.js and bundle are available
  local wizard_bin="$MOSAIC_HOME/dist/mosaic-wizard.mjs"
  if command -v node >/dev/null 2>&1 && [[ -f "$wizard_bin" ]]; then
    exec node "$wizard_bin" "$@"
  fi
  # Fallback to legacy bash wizard
  check_mosaic_home
  exec "$MOSAIC_HOME/bin/mosaic-init" "$@"
}

run_doctor() {
  check_mosaic_home
  exec "$MOSAIC_HOME/bin/mosaic-doctor" "$@"
}

run_sync() {
  check_mosaic_home
  exec "$MOSAIC_HOME/bin/mosaic-sync-skills" "$@"
}

run_seq() {
  check_mosaic_home
  local checker="$MOSAIC_HOME/bin/mosaic-ensure-sequential-thinking"
  local action="${1:-check}"

  case "$action" in
    check)
      shift || true
      exec "$checker" --check "$@"
      ;;
    fix|apply)
      shift || true
      exec "$checker" "$@"
      ;;
    start)
      shift || true
      check_runtime "npx"
      echo "[mosaic] Starting sequential-thinking MCP server..."
      exec npx -y @modelcontextprotocol/server-sequential-thinking "$@"
      ;;
    *)
      echo "[mosaic] ERROR: Unknown seq subcommand '$action'." >&2
      echo "[mosaic] Use: mosaic seq check|fix|start" >&2
      exit 1
      ;;
  esac
}

run_coord() {
  check_mosaic_home
  local runtime="claude"
  local runtime_flag=""
  local yolo_flag=""
  local -a coord_args=()

  while [[ $# -gt 0 ]]; do
    case "$1" in
      --claude|--codex|--pi)
        local selected_runtime="${1#--}"
        if [[ -n "$runtime_flag" ]] && [[ "$runtime" != "$selected_runtime" ]]; then
          echo "[mosaic] ERROR: --claude, --codex, and --pi are mutually exclusive for 'mosaic coord'." >&2
          exit 1
        fi
        runtime="$selected_runtime"
        runtime_flag="$1"
        shift
        ;;
      --yolo)
        yolo_flag="--yolo"
        shift
        ;;
      *)
        coord_args+=("$1")
        shift
        ;;
    esac
  done

  local subcmd="${coord_args[0]:-help}"
  if (( ${#coord_args[@]} > 1 )); then
    set -- "${coord_args[@]:1}"
  else
    set --
  fi

  local tool_dir="$MOSAIC_HOME/tools/orchestrator"

  case "$subcmd" in
    status|session)
      MOSAIC_COORD_RUNTIME="$runtime" exec bash "$tool_dir/session-status.sh" "$@"
      ;;
    init)
      MOSAIC_COORD_RUNTIME="$runtime" exec bash "$tool_dir/mission-init.sh" "$@"
      ;;
    mission|progress)
      MOSAIC_COORD_RUNTIME="$runtime" exec bash "$tool_dir/mission-status.sh" "$@"
      ;;
    continue|next)
      MOSAIC_COORD_RUNTIME="$runtime" exec bash "$tool_dir/continue-prompt.sh" "$@"
      ;;
    run|start)
      MOSAIC_COORD_RUNTIME="$runtime" exec bash "$tool_dir/session-run.sh" ${yolo_flag:+"$yolo_flag"} "$@"
      ;;
    smoke|test)
      MOSAIC_COORD_RUNTIME="$runtime" exec bash "$tool_dir/smoke-test.sh" "$@"
      ;;
    resume|recover)
      MOSAIC_COORD_RUNTIME="$runtime" exec bash "$tool_dir/session-resume.sh" "$@"
      ;;
    help|*)
      cat <<COORD_USAGE
mosaic coord — r0 manual coordinator tools

Commands:
  init --name <name> [opts]   Initialize a new mission
  mission [--project <path>]  Show mission progress dashboard
  status [--project <path>]   Check agent session health
  continue [--project <path>] Generate continuation prompt for next session
  run [--project <path>]      Generate context and launch selected runtime
  smoke                       Run orchestration behavior smoke checks
  resume [--project <path>]   Crash recovery (detect dirty state, generate fix)

Runtime:
  --claude                    Use Claude runtime hints/prompts (default)
  --codex                     Use Codex runtime hints/prompts
  --pi                        Use Pi runtime hints/prompts
  --yolo                      Launch runtime in dangerous/skip-permissions mode (run only)

Examples:
  mosaic coord init --name "Security Fix" --milestones "Critical,High,Medium"
  mosaic coord mission
  mosaic coord --codex mission
  mosaic coord --pi run
  mosaic coord continue --copy
  mosaic coord run
  mosaic coord run --codex
  mosaic coord --yolo run
  mosaic coord smoke
  mosaic coord continue --codex --copy

COORD_USAGE
      ;;
  esac
}

# Resume advisory — prints warning if active mission or stale session detected
_check_resumable_session() {
  local mission_file=".mosaic/orchestrator/mission.json"
  local lock_file=".mosaic/orchestrator/session.lock"

  command -v jq &>/dev/null || return 0

  if [[ -f "$lock_file" ]]; then
    local pid
    pid="$(jq -r '.pid // 0' "$lock_file" 2>/dev/null)"
    if [[ -n "$pid" ]] && [[ "$pid" != "0" ]] && ! kill -0 "$pid" 2>/dev/null; then
      # Stale lock from a dead session — clean it up
      rm -f "$lock_file"
      echo "[mosaic] Cleaned up stale session lock (PID $pid no longer running)."
      echo ""
    fi
  elif [[ -f "$mission_file" ]]; then
    local status
    status="$(jq -r '.status // "inactive"' "$mission_file" 2>/dev/null)"
    if [[ "$status" == "active" ]]; then
      echo "[mosaic] Active mission detected. Generate continuation prompt with:"
      echo "[mosaic]   mosaic coord continue"
      echo ""
    fi
  fi
}

run_prdy() {
  check_mosaic_home
  local runtime="claude"
  local runtime_flag=""
  local -a prdy_args=()

  while [[ $# -gt 0 ]]; do
    case "$1" in
      --claude|--codex|--pi)
        local selected_runtime="${1#--}"
        if [[ -n "$runtime_flag" ]] && [[ "$runtime" != "$selected_runtime" ]]; then
          echo "[mosaic] ERROR: --claude, --codex, and --pi are mutually exclusive for 'mosaic prdy'." >&2
          exit 1
        fi
        runtime="$selected_runtime"
        runtime_flag="$1"
        shift
        ;;
      *)
        prdy_args+=("$1")
        shift
        ;;
    esac
  done

  local subcmd="${prdy_args[0]:-help}"
  if (( ${#prdy_args[@]} > 1 )); then
    set -- "${prdy_args[@]:1}"
  else
    set --
  fi

  local tool_dir="$MOSAIC_HOME/tools/prdy"

  case "$subcmd" in
    init)
      MOSAIC_PRDY_RUNTIME="$runtime" exec bash "$tool_dir/prdy-init.sh" "$@"
      ;;
    update)
      MOSAIC_PRDY_RUNTIME="$runtime" exec bash "$tool_dir/prdy-update.sh" "$@"
      ;;
    validate|check)
      MOSAIC_PRDY_RUNTIME="$runtime" exec bash "$tool_dir/prdy-validate.sh" "$@"
      ;;
    status)
      exec bash "$tool_dir/prdy-status.sh" "$@"
      ;;
    help|*)
      cat <<PRDY_USAGE
mosaic prdy — PRD creation and validation tools

Commands:
  init [--project <path>] [--name <feature>]   Create docs/PRD.md via guided runtime session
  update [--project <path>]                    Update existing docs/PRD.md via guided runtime session
  validate [--project <path>]                  Check PRD completeness against Mosaic guide (bash-only)
  status [--project <path>] [--format short|json]  Quick PRD health check (one-liner)

Runtime:
  --claude                    Use Claude runtime (default)
  --codex                     Use Codex runtime
  --pi                        Use Pi runtime

Examples:
  mosaic prdy init --name "User Authentication"
  mosaic prdy update
  mosaic prdy --pi init --name "User Authentication"
  mosaic prdy --codex init --name "User Authentication"
  mosaic prdy validate

Output location: docs/PRD.md (per Mosaic PRD guide)

PRDY_USAGE
      ;;
  esac
}

run_bootstrap() {
  check_mosaic_home
  exec "$MOSAIC_HOME/bin/mosaic-bootstrap-repo" "$@"
}

run_release_upgrade() {
  check_mosaic_home
  exec "$MOSAIC_HOME/bin/mosaic-release-upgrade" "$@"
}

run_project_upgrade() {
  check_mosaic_home
  exec "$MOSAIC_HOME/bin/mosaic-upgrade" "$@"
}

run_upgrade() {
  check_mosaic_home

  # Default: upgrade installed release
  if [[ $# -eq 0 ]]; then
    run_release_upgrade
  fi

  case "$1" in
    release)
      shift
      run_release_upgrade "$@"
      ;;
    check)
      shift
      run_release_upgrade --dry-run "$@"
      ;;
    project)
      shift
      run_project_upgrade "$@"
      ;;

    # Backward compatibility for historical project-upgrade usage.
    --all|--root)
      run_project_upgrade "$@"
      ;;
    --dry-run|--ref|--keep|--overwrite|-y|--yes)
      run_release_upgrade "$@"
      ;;
    -*)
      run_release_upgrade "$@"
      ;;
    *)
      run_project_upgrade "$@"
      ;;
  esac
}

# Main router
if [[ $# -eq 0 ]]; then
  usage
  exit 0
fi

command="$1"
shift

case "$command" in
  pi)         launch_pi "$@" ;;
  claude)     launch_claude "$@" ;;
  opencode)   launch_opencode "$@" ;;
  codex)      launch_codex "$@" ;;
  yolo|--yolo) launch_yolo "$@" ;;
  init)       run_init "$@" ;;
  doctor)     run_doctor "$@" ;;
  sync)       run_sync "$@" ;;
  seq)        run_seq "$@" ;;
  bootstrap)  run_bootstrap "$@" ;;
  prdy)       run_prdy "$@" ;;
  coord)      run_coord "$@" ;;
  upgrade)    run_upgrade "$@" ;;
  release-upgrade) run_release_upgrade "$@" ;;
  project-upgrade) run_project_upgrade "$@" ;;
  help|-h|--help)  usage ;;
  version|-v|--version) echo "mosaic $VERSION" ;;
  *)
    echo "[mosaic] Unknown command: $command" >&2
    echo "[mosaic] Run 'mosaic --help' for usage." >&2
    exit 1
    ;;
esac
