#!/usr/bin/env zsh
# clawdad — Multi-agent orchestration CLI for Codex-first project sessions
set -euo pipefail

# Resolve CLAWDAD_ROOT from the script's real location (follows symlinks)
_clawdad_self="${0:A}"
CLAWDAD_ROOT="${CLAWDAD_ROOT:-${_clawdad_self:h:h}}"

# Source libraries
source "$CLAWDAD_ROOT/lib/common.sh"
source "$CLAWDAD_ROOT/lib/log.sh"
source "$CLAWDAD_ROOT/lib/registry.sh"
source "$CLAWDAD_ROOT/lib/mailbox.sh"
source "$CLAWDAD_ROOT/lib/history.sh"
source "$CLAWDAD_ROOT/lib/dispatch.sh"
source "$CLAWDAD_ROOT/lib/watch.sh"

usage() {
  cat <<EOF
clawdad v$CLAWDAD_VERSION — Multi-agent orchestration for AI coding agents

Usage:
  clawdad <command> [options]

Requires: orp, jq, sqlite3, codex (Chimera optional)

Commands:
  init                          Initialize clawdad (~/.clawdad + verify ORP)
  register <path> [options]     Register a project (writes ORP tab)
  add-session <project> [opts]  Add another tracked session to a project bucket
  delegate <project> [--json]   Show the saved delegate brief/status for a project
  delegate-set <project> ...    Update the delegate brief text for a project
  go <project>                  Start autonomous delegate mode after ORP preflight
  delegate-run <project>        Start autonomous delegate mode for a project (--dry-run previews)
  supervise <project> --lane <laneId>
                                Opt into continuity orchestration for a delegate lane
  delegate-pause <project>      Pause autonomous delegate mode for a project
  delegate-reset <project>      Reset the delegate brief to the default template
  sessions-doctor [project]     Audit/repair stale sessions and delegate lanes
  codex <install|doctor> [proj] Install or audit Codex hooks, skills, plugin, and AGENTS integration
  watchtower <project>          Observe delegation read-only and index review cards
  feed <tail|search|review>     Read the delegation review feed
  track-session <project> <session-id>
                                Track an existing native Codex session in a project bucket
  rename-session <project> <session> <title>
                                Rename one tracked session inside a project bucket
  remove-session <project> <session>
                                Remove one tracked session inside a project bucket
  unregister <slug|path>        Remove a project (removes ORP tab)
  dispatch <slug|path> <msg>    Send a message to a spoke agent
  status [slug|path]            Show status of one or all projects
  sessions <slug|path>          List tracked sessions inside a project bucket
  use-session <project> <sel>   Select the active session for a project bucket
  list                          List registered projects
  read <slug|path>              Read the latest response from a spoke
  watch [options]               Monitor mailboxes for responses
  serve [options]               Run the secure HTTP listener entrypoint
  secure-bootstrap [options]    Write a Tailscale-first secure server setup
  secure-doctor [options]       Verify the secure self-hosted setup
  prod-doctor [options]         Verify the live Clawdad app runtime
  chimera-doctor [options]      Verify the local Chimera/Ollama provider lane
  codex-hook                    Internal Codex hook entrypoint
  gen-token [options]           Generate a bearer token for the listener
  print-launch-agent [opts]     Print a macOS launchd plist for clawdad serve
  install-launch-agent [opts]   Install a macOS launchd plist for clawdad serve
  print-systemd-unit [opts]     Print a Linux systemd user unit for clawdad serve
  install-systemd-unit [opts]   Install a Linux systemd user unit for clawdad serve
  help                          Show this help

Options for register:
  --slug <name>                 Custom slug (default: directory basename)
  --description <text>          Project description
  --provider <name>             AI provider: codex or chimera (default: codex)

Options for add-session:
  --slug <name>                 Custom session label
  --provider <name>             AI provider: codex or chimera (default: codex)

Options for track-session:
  --slug <name>                 Custom session label

Options for rename-session:
  <session>                     Existing session title or full session id
  <title>                       New user title for the session

Options for remove-session:
  <session>                     Existing session title or full session id

Options for dispatch:
  --file <path>                 Read message from file
  --session <session>           Target a tracked session without changing the active session
  --wait                        Block until response arrives
  --timeout <sec>               Optional max wait seconds (default: wait indefinitely)
  --model <model>               Override model for this dispatch
  --permission-mode <mode>      Permission mode (default: plan)

Options for delegate-set:
  --file <path>                 Read delegate brief text from a file
  --stdin                       Read delegate brief text from stdin
  --session <session>           Pin autonomous delegate work to a tracked Codex session by title or id
  --compute-reserve-percent <n> Pause delegate work when weekly Codex compute remaining is at or below n% (default: 10)
  --max-steps-per-run <n>       Deprecated; ignored unless CLAWDAD_ENABLE_DELEGATE_STEP_CAPS=1
  --watchtower-review-mode <mode> Control optional Watchtower review: off (default), log, or enforce
  --json                        Emit the saved delegate payload as JSON

Options for watch:
  --interval <sec>              Poll interval (default: 5)
  --daemon                      Run as tmux background daemon
  --stop                        Stop the watch daemon
  --once                        Single poll pass, then exit

Options for chimera-doctor:
  --json                        Emit machine-readable JSON
  --model <model>               Check a Chimera/Ollama model profile
  --ollama-base-url <url>        Check an explicit Ollama OpenAI-compatible base URL

Environment:
  CLAWDAD_HOME                  Config directory (default: ~/.clawdad)
  CLAWDAD_LOG                   Log file (default: ~/Library/Logs/clawdad.log)
  CLAWDAD_POLL_INTERVAL         Default poll interval (default: 5)
  CLAWDAD_PERMISSION_MODE       Default permission mode (default: plan)
  CLAWDAD_DEFAULT_PROVIDER      Default provider (default: codex)
  CLAWDAD_CHIMERA               Chimera CLI binary (default: chimera)
  CLAWDAD_CHIMERA_MODEL         Default Chimera model/profile (default: local)
  CLAWDAD_CHIMERA_LOCAL_OLLAMA_BASE_URL Local Ollama base URL for Mac fallback
  CLAWDAD_CHIMERA_4090_OLLAMA_BASE_URL  Ollama base URL for 4090/workstation profiles
  CLAWDAD_ORP_WORKSPACE         ORP workspace name (default: main)
  CLAWDAD_SERVER_HOST           Listener host (default: 127.0.0.1)
  CLAWDAD_SERVER_PORT           Listener port (default: 4477)
  CLAWDAD_SERVER_CONFIG_FILE    Listener config file (default: ~/.clawdad/server.json)
  CLAWDAD_SERVER_TOKEN_FILE     Listener bearer token file
  CLAWDAD_SERVER_DEFAULT_PROJECT Default project for remote dispatches
  CLAWDAD_SERVER_AUTH_MODE      Listener auth mode: token, tailscale, hybrid
  CLAWDAD_SERVER_ALLOWED_USERS  Comma-separated Tailscale login allowlist
  CLAWDAD_SERVER_REQUIRED_CAPABILITY Required Tailscale app capability
EOF
}

run_server_helper() {
  require_node
  export CLAWDAD_ROOT CLAWDAD_HOME CLAWDAD_LOG CLAWDAD_BIN_PATH="$_clawdad_self"
  "$CLAWDAD_NODE" "$CLAWDAD_ROOT/lib/server.mjs" "$@"
}

cmd_init() {
  require_jq
  require_orp
  registry_init
}

cmd_chimera_doctor() {
  require_node
  "$CLAWDAD_NODE" "$CLAWDAD_ROOT/lib/chimera-doctor.mjs" \
    --chimera-binary "$CLAWDAD_CHIMERA" \
    --model "$CLAWDAD_CHIMERA_MODEL" \
    "$@"
}

choose_provider_session_seed() {
  local project_path="$1" provider="$2"
  shift 2 || true

  local session_id="" session_seeded="true"

  if [[ "$provider" == "codex" ]]; then
    local saved_json
    if saved_json=$(registry_find_saved_codex_session_json "$project_path" "$@" 2>/dev/null); then
      session_id=$(echo "$saved_json" | "$CLAWDAD_JQ" -r 'select(.ok == true) | .sessionId // ""' 2>/dev/null || true)
    fi
    if [[ -n "$session_id" ]]; then
      session_seeded="true"
      echo "$session_id"$'\t'"$session_seeded"
      return 0
    fi
  fi

  session_id=$(gen_uuid)
  if [[ "$provider" == "codex" || "$provider" == "chimera" ]]; then
    session_seeded="false"
  fi
  echo "$session_id"$'\t'"$session_seeded"
}

cmd_register() {
  require_jq
  require_orp

  local project_path="" slug="" description="" provider="$CLAWDAD_DEFAULT_PROVIDER"

  # Parse arguments
  while (( $# > 0 )); do
    case "$1" in
      --slug) slug="$2"; shift 2 ;;
      --description) description="$2"; shift 2 ;;
      --provider) provider="$2"; shift 2 ;;
      -*) clawdad_error "Unknown option: $1"; return 1 ;;
      *) project_path="$1"; shift ;;
    esac
  done

  if [[ -z "$project_path" ]]; then
    clawdad_error "Usage: clawdad register <path> [--slug <name>] [--provider <codex|chimera>]"
    return 1
  fi

  # Validate provider
  require_provider "$provider"

  # Resolve to absolute path
  project_path=$(to_absolute_path "$project_path")
  if [[ ! -d "$project_path" ]]; then
    clawdad_error "Directory not found: $project_path"
    return 1
  fi

  # Default slug from basename
  [[ -z "$slug" ]] && slug="${project_path:t}"

  # Check for duplicates. If ORP already has this path but no Clawdad-tracked
  # session, upgrade that placeholder tab instead of forcing a remove/re-add.
  local upgrade_existing_tab=false
  if registry_has_path "$project_path"; then
    if registry_has_tracked_session_for_path "$project_path"; then
      clawdad_error "Project already registered: $project_path"
      clawdad_error "Use 'clawdad add-session $slug' to add another tracked session"
      return 1
    fi
    upgrade_existing_tab=true
  fi
  if registry_has_slug "$slug"; then
    if ! $upgrade_existing_tab || ! registry_has_untracked_slug_for_path "$project_path" "$slug"; then
      clawdad_error "Slug '$slug' already in use. Use --slug to choose a different name."
      return 1
    fi
  fi

  # Generate or adopt a provider session ID
  local session_id session_seeded seed_info
  seed_info=$(choose_provider_session_seed "$project_path" "$provider")
  session_id="${seed_info%%$'\t'*}"
  session_seeded="${seed_info#*$'\t'}"

  # Register in registry
  local write_mode="append"
  if $upgrade_existing_tab; then
    write_mode="update"
  fi
  registry_add "$project_path" "$session_id" "$slug" "$description" "$provider" "$session_seeded" "$write_mode"

  # Initialize mailbox
  mailbox_init "$project_path"

  if [[ "$provider" == "codex" && "$session_seeded" == "true" ]]; then
    clawdad_info "Registered: $slug -> $project_path (provider: $provider, adopted native session: $session_id)"
  else
    clawdad_info "Registered: $slug -> $project_path (provider: $provider, session: $session_id)"
  fi
}

choose_unique_session_slug() {
  local project_path="$1" provider="${2:-$CLAWDAD_DEFAULT_PROVIDER}" preferred="${3:-}"

  if [[ -n "$preferred" ]]; then
    local normalized
    normalized=$(print -r -- "$preferred" | tr '\r\n' '  ' | sed -E 's/[[:space:]]+/ /g; s/^ //; s/ $//')
    if [[ -n "$normalized" ]]; then
      if ! registry_has_slug "$normalized"; then
        echo "$normalized"
        return 0
      fi

      local preferred_suffix=2
      local preferred_candidate
      while true; do
        preferred_candidate="$normalized $preferred_suffix"
        if ! registry_has_slug "$preferred_candidate"; then
          echo "$preferred_candidate"
          return 0
        fi
        (( preferred_suffix += 1 ))
      done
    fi
  fi

  local base candidate suffix
  base="${project_path:t}"
  candidate="$base"
  if registry_has_slug "$candidate"; then
    candidate="$base ($provider)"
  fi

  if ! registry_has_slug "$candidate"; then
    echo "$candidate"
    return 0
  fi

  suffix=2
  while true; do
    candidate="$base ($provider $suffix)"
    if ! registry_has_slug "$candidate"; then
      echo "$candidate"
      return 0
    fi
    (( suffix += 1 ))
  done
}

cmd_add_session() {
  require_jq
  require_orp

  local input="" slug="" provider="$CLAWDAD_DEFAULT_PROVIDER"

  while (( $# > 0 )); do
    case "$1" in
      --slug) slug="$2"; shift 2 ;;
      --provider) provider="$2"; shift 2 ;;
      -*) clawdad_error "Unknown option: $1"; return 1 ;;
      *) input="$1"; shift ;;
    esac
  done

  if [[ -z "$input" ]]; then
    clawdad_error "Usage: clawdad add-session <project> [--slug <name>] [--provider <codex|chimera>]"
    return 1
  fi

  require_provider "$provider"

  local project_path=""
  if [[ "$input" == /* ]]; then
    local resolved_input
    resolved_input=$(to_absolute_path "$input")
    if [[ -n "$resolved_input" ]] && [[ -d "$resolved_input" ]] && registry_has_path "$resolved_input"; then
      project_path="$resolved_input"
    fi
  fi

  if [[ -z "$project_path" ]]; then
    project_path=$(resolve_project "$input") || {
      clawdad_error "Project '$input' is not tracked yet. Use 'clawdad register <path>' first."
      return 1
    }
  fi

  if ! registry_has_path "$project_path"; then
    clawdad_error "Project '$project_path' is not tracked yet. Use 'clawdad register <path>' first."
    return 1
  fi

  [[ -z "$slug" ]] && slug=$(choose_unique_session_slug "$project_path" "$provider")

  if registry_has_slug "$slug"; then
    clawdad_error "Slug '$slug' already in use. Use --slug to choose a different name."
    return 1
  fi

  local -a excludes
  if [[ "$provider" == "codex" ]]; then
    while IFS= read -r existing_id; do
      [[ -n "$existing_id" ]] || continue
      excludes+=("$existing_id")
    done < <(registry_codex_tracked_session_ids_for_path "$project_path")
  fi

  local session_id session_seeded seed_info
  seed_info=$(choose_provider_session_seed "$project_path" "$provider" "${excludes[@]:-}")
  session_id="${seed_info%%$'\t'*}"
  session_seeded="${seed_info#*$'\t'}"

  registry_add "$project_path" "$session_id" "$slug" "" "$provider" "$session_seeded"
  mailbox_init "$project_path"

  if [[ "$provider" == "codex" && "$session_seeded" == "true" ]]; then
    clawdad_info "Added session: $slug -> $project_path (provider: $provider, adopted native session: $session_id)"
  else
    clawdad_info "Added session: $slug -> $project_path (provider: $provider, session: $session_id)"
  fi
}

cmd_track_session() {
  require_jq
  require_orp

  local input="" session_id="" slug=""

  while (( $# > 0 )); do
    case "$1" in
      --slug) slug="$2"; shift 2 ;;
      -*) clawdad_error "Unknown option: $1"; return 1 ;;
      *)
        if [[ -z "$input" ]]; then
          input="$1"
        elif [[ -z "$session_id" ]]; then
          session_id="$1"
        else
          clawdad_error "Usage: clawdad track-session <project> <session-id> [--slug <name>]"
          return 1
        fi
        shift
        ;;
    esac
  done

  if [[ -z "$input" || -z "$session_id" ]]; then
    clawdad_error "Usage: clawdad track-session <project> <session-id> [--slug <name>]"
    return 1
  fi

  local project_path
  project_path=$(resolve_project "$input") || return 1

  if registry_session_exists "$project_path" "$session_id"; then
    clawdad_error "Session '$session_id' is already tracked for $project_path"
    return 1
  fi

  local sessions_json session_json
  sessions_json=$(registry_list_saved_codex_sessions_json "$project_path" "0") || return 1
  session_json=$(echo "$sessions_json" | "$CLAWDAD_JQ" -c --arg session "$session_id" '
    select(.ok == true)
    | (.sessions // [])
    | map(select(.sessionId == $session))
    | .[0] // empty
  ')

  if [[ -z "$session_json" ]]; then
    clawdad_error "No saved native Codex session '$session_id' was found for $project_path"
    return 1
  fi

  if [[ -z "$slug" ]]; then
    local title_hint
    title_hint=$(echo "$session_json" | "$CLAWDAD_JQ" -r '.titleHint // .preview // ""')
    slug=$(choose_unique_session_slug "$project_path" "codex" "$title_hint")
  fi

  if registry_has_slug "$slug"; then
    clawdad_error "Slug '$slug' already in use. Use --slug to choose a different name."
    return 1
  fi

  registry_add "$project_path" "$session_id" "$slug" "" "codex" "true"
  mailbox_init "$project_path"
  clawdad_info "Tracked native session: $slug -> $project_path (provider: codex, session: $session_id)"
}

cmd_unregister() {
  require_jq

  local input="${1:-}"
  local clean=false
  [[ "${2:-}" == "--clean" ]] && clean=true

  if [[ -z "$input" ]]; then
    clawdad_error "Usage: clawdad unregister <slug|path> [--clean]"
    return 1
  fi

  local project_path
  project_path=$(resolve_project "$input") || return 1

  local session_json="" session_count
  session_count=$(registry_session_count "$project_path")
  if [[ "$input" != /* ]]; then
    session_json=$(registry_session_json "$project_path" "$input" 2>/dev/null || true)
  fi

  local slug="" session_id=""
  if [[ -n "$session_json" ]]; then
    slug=$(echo "$session_json" | "$CLAWDAD_JQ" -r '.title // ""')
    session_id=$(echo "$session_json" | "$CLAWDAD_JQ" -r '.resumeSessionId // ""')
    registry_remove "$project_path" "$session_id" "$slug"
  else
    if [[ "$session_count" =~ ^[0-9]+$ ]] && (( session_count > 1 )); then
      clawdad_error "Project bucket '$project_path' has multiple tracked sessions."
      clawdad_error "Unregister by exact session title or session id instead."
      return 1
    fi
    slug=$(registry_field "$project_path" "slug")
    registry_remove "$project_path"
  fi

  if $clean; then
    rm -rf "$project_path/.clawdad"
    clawdad_info "Removed .clawdad/ from $project_path"
  fi

  if [[ -n "$session_id" ]]; then
    clawdad_info "Unregistered session: $slug ($project_path, $session_id)"
  else
    clawdad_info "Unregistered: $slug ($project_path)"
  fi
}

cmd_remove_session() {
  require_jq
  require_orp

  local input="${1:-}" selector="${2:-}"
  if [[ -z "$input" || -z "$selector" ]]; then
    clawdad_error "Usage: clawdad remove-session <project> <session-title|session-id>"
    return 1
  fi

  local project_path=""
  if [[ "$input" == /* ]]; then
    local resolved_input
    resolved_input=$(to_absolute_path "$input")
    if [[ -n "$resolved_input" ]] && [[ -d "$resolved_input" ]] && registry_has_path "$resolved_input"; then
      project_path="$resolved_input"
    fi
  fi

  if [[ -z "$project_path" ]]; then
    project_path=$(resolve_project "$input") || return 1
  fi

  local session_json
  session_json=$(registry_session_json "$project_path" "$selector") || {
    clawdad_error "No tracked session '$selector' found for $project_path"
    return 1
  }

  local slug session_id
  slug=$(echo "$session_json" | "$CLAWDAD_JQ" -r '.title // ""')
  session_id=$(echo "$session_json" | "$CLAWDAD_JQ" -r '.resumeSessionId // ""')

  if [[ -z "$session_id" ]]; then
    clawdad_error "Session '$selector' has no resumable session id; nothing was removed"
    return 1
  fi

  registry_remove "$project_path" "$session_id" "$slug"
  registry_sync_sessions_for_project "$project_path" >/dev/null 2>&1 || true

  clawdad_info "Removed session: $slug ($project_path, $session_id)"
}

cmd_dispatch() {
  require_jq

  local input="" message="" file="" wait=false timeout="$CLAWDAD_DISPATCH_TIMEOUT"
  local model="" permission_mode="$CLAWDAD_PERMISSION_MODE" session_selector=""
  local attachment_manifest=""

  # Parse arguments
  while (( $# > 0 )); do
    case "$1" in
      --file) file="$2"; shift 2 ;;
      --session) session_selector="$2"; shift 2 ;;
      --wait) wait=true; shift ;;
      --timeout) timeout="$2"; shift 2 ;;
      --model) model="$2"; shift 2 ;;
      --permission-mode) permission_mode="$2"; shift 2 ;;
      --attachment-manifest) attachment_manifest="$2"; shift 2 ;;
      --stdin) message=$(cat); shift ;;
      -*)
        clawdad_error "Unknown option: $1"; return 1 ;;
      *)
        if [[ -z "$input" ]]; then
          input="$1"
        elif [[ -z "$message" ]]; then
          message="$1"
        else
          message="$message $1"
        fi
        shift
        ;;
    esac
  done

  if [[ -z "$input" ]]; then
    clawdad_error "Usage: clawdad dispatch <slug|path> <message>"
    return 1
  fi

  # Read from file if specified
  if [[ -n "$file" ]]; then
    if [[ ! -f "$file" ]]; then
      clawdad_error "File not found: $file"
      return 1
    fi
    message=$(cat "$file")
  fi

  if [[ -z "$message" ]]; then
    clawdad_error "No message provided"
    return 1
  fi

  if [[ -n "$attachment_manifest" && ! -f "$attachment_manifest" ]]; then
    clawdad_error "Attachment manifest not found: $attachment_manifest"
    return 1
  fi

  if [[ -n "$timeout" ]]; then
    if [[ ! "$timeout" =~ ^[0-9]+$ ]]; then
      clawdad_error "Timeout must be a non-negative integer"
      return 1
    fi
    if (( timeout == 0 )); then
      timeout=""
    fi
  fi

  local project_path
  project_path=$(resolve_project "$input") || return 1

  local persist_active="true"
  if [[ -n "$session_selector" ]]; then
    persist_active="false"
  fi

  dispatch_to_spoke "$project_path" "$message" "$permission_mode" "$model" "$session_selector" "$persist_active" "$attachment_manifest"

  if $wait; then
    local slug
    slug=$(registry_field "$project_path" "slug")
    if [[ -n "$timeout" ]]; then
      clawdad_info "Waiting for response (timeout: ${timeout}s)..."
    else
      clawdad_info "Waiting for response..."
    fi

    local elapsed=0
    local poll_interval="$CLAWDAD_POLL_INTERVAL"
    while true; do
      local mailbox_status
      mailbox_status=$(mailbox_read_status "$project_path")
      if [[ "$mailbox_status" == "completed" ]]; then
        echo ""
        mailbox_read_response "$project_path"
        return 0
      fi
      if [[ "$mailbox_status" == "failed" ]]; then
        echo ""
        mailbox_read_response "$project_path"
        return 1
      fi

      local sleep_for="$poll_interval"
      if [[ -n "$timeout" ]]; then
        if (( elapsed >= timeout )); then
          break
        fi
        if (( elapsed + sleep_for > timeout )); then
          sleep_for=$((timeout - elapsed))
        fi
        (( sleep_for > 0 )) || break
      fi

      sleep "$sleep_for"
      elapsed=$((elapsed + sleep_for))
      printf "."
    done

    local mailbox_status
    mailbox_status=$(mailbox_read_status "$project_path")
    if [[ "$mailbox_status" == "completed" ]]; then
      echo ""
      mailbox_read_response "$project_path"
      return 0
    fi
    if [[ "$mailbox_status" == "failed" ]]; then
      echo ""
      mailbox_read_response "$project_path"
      return 1
    fi

    echo ""
    if [[ -n "$timeout" ]]; then
      clawdad_warn "Timed out after ${timeout}s. Check with: clawdad status $slug"
    else
      clawdad_warn "Waiting ended before a response arrived. Check with: clawdad status $slug"
    fi
    return 1
  fi
}

cmd_status() {
  require_jq
  require_orp

  local input="${1:-}" json=false
  [[ "$input" == "--json" ]] && { json=true; input=""; }

  if [[ -n "$input" ]]; then
    local project_path
    project_path=$(resolve_project "$input") || return 1

    if $json; then
      registry_get "$project_path"
    else
      local _slug _prov _st _last _cnt
      _slug=$(registry_field "$project_path" "slug")
      _prov=$(registry_field "$project_path" "provider")
      _st=$(registry_field "$project_path" "status")
      _last=$(registry_field "$project_path" "last_dispatch")
      _cnt=$(registry_field "$project_path" "dispatch_count")

      printf "%-24s %-8s %-12s last: %-20s dispatches: %s\n" \
        "$_slug" "${_prov:-codex}" "${_st:-idle}" "${_last:-never}" "${_cnt:-0}"
    fi
  else
    if $json; then
      _orp_tabs_json
    else
      local tabs_json
      tabs_json=$(_orp_tabs_json) || {
        clawdad_error "Failed to read ORP workspace '$CLAWDAD_ORP_WORKSPACE'"
        return 1
      }

      local tab_count
      tab_count=$(echo "$tabs_json" | "$CLAWDAD_JQ" '.tabCount // 0')

      if (( tab_count == 0 )); then
        echo "No projects in ORP workspace '$CLAWDAD_ORP_WORKSPACE'. Use 'clawdad register <path>' to add one."
        return 0
      fi

      printf "%-24s %-8s %-12s %-22s %s\n" "SLUG" "PROVIDER" "STATUS" "LAST DISPATCH" "COUNT"

      local _tab_lines _slug _prov _path _st _last _cnt
      _tab_lines=$(echo "$tabs_json" | "$CLAWDAD_JQ" -r '.tabs[] | "\(.title // (.path | split("/") | last))\t\(.resumeTool // "none")\t\(.path)"')

      echo "$_tab_lines" | while IFS=$'\t' read -r _slug _prov _path; do
          _st=$(state_field "$_path" "status")
          _last=$(state_field "$_path" "last_dispatch")
          _cnt=$(state_field "$_path" "dispatch_count")
          printf "%-24s %-8s %-12s %-22s %s\n" \
            "$_slug" "${_prov:-none}" "${_st:-idle}" "${_last:-never}" "${_cnt:-0}"
        done
    fi
  fi
}

cmd_sessions() {
  require_jq
  require_orp

  local input="${1:-}" json=false
  if [[ "$input" == "--json" ]]; then
    json=true
    input="${2:-}"
  elif [[ "${2:-}" == "--json" ]]; then
    json=true
  fi

  if [[ -z "$input" ]]; then
    clawdad_error "Usage: clawdad sessions <slug|path> [--json]"
    return 1
  fi

  local project_path sessions_json
  project_path=$(resolve_project "$input") || return 1
  sessions_json=$(registry_list_sessions_json "$project_path")

  if $json; then
    echo "$sessions_json"
    return 0
  fi

  local session_count
  session_count=$(echo "$sessions_json" | "$CLAWDAD_JQ" 'length')
  if (( session_count == 0 )); then
    echo "No tracked sessions for $project_path."
    return 0
  fi

  printf "%-6s %-24s %-8s %-10s %-12s %s\n" "ACTIVE" "SLUG" "PROVIDER" "STATUS" "DISPATCHES" "SESSION ID"
  echo "$sessions_json" | "$CLAWDAD_JQ" -r '.[] | [
    (if .active then "*" else "-" end),
    .slug,
    .provider,
    .status,
    (.dispatchCount | tostring),
    .sessionId
  ] | @tsv' | while IFS=$'\t' read -r active slug provider session_status dispatches session_id; do
    printf "%-6s %-24s %-8s %-10s %-12s %s\n" \
      "$active" "$slug" "$provider" "$session_status" "$dispatches" "$session_id"
  done
}

cmd_use_session() {
  require_jq
  require_orp

  local input="${1:-}" selector="${2:-}"
  if [[ -z "$input" || -z "$selector" ]]; then
    clawdad_error "Usage: clawdad use-session <project> <session-title|session-id>"
    return 1
  fi

  local project_path
  project_path=$(resolve_project "$input") || return 1

  local session_id
  session_id=$(registry_select_session "$project_path" "$selector") || return 1

  local slug provider
  slug=$(registry_session_field "$project_path" "$session_id" "title")
  provider=$(registry_session_field "$project_path" "$session_id" "resumeTool")

  clawdad_info "Active session: $slug (${provider:-$CLAWDAD_DEFAULT_PROVIDER}, $session_id)"
}

cmd_rename_session() {
  require_jq
  require_orp

  local input="${1:-}" selector="${2:-}"
  shift 2 2>/dev/null || true
  local new_title="$*"

  if [[ -z "$input" || -z "$selector" || -z "${new_title// }" ]]; then
    clawdad_error "Usage: clawdad rename-session <project> <session-title|session-id> <new-title>"
    return 1
  fi

  local project_path
  project_path=$(resolve_project "$input") || return 1

  local session_id provider
  session_id=$(registry_session_field "$project_path" "$selector" "resumeSessionId") || return 1
  provider=$(registry_session_field "$project_path" "$selector" "resumeTool")

  registry_rename_session "$project_path" "$selector" "$new_title" || return 1
  clawdad_info "Renamed session: ${new_title} (${provider:-$CLAWDAD_DEFAULT_PROVIDER}, $session_id)"
}

cmd_list() {
  require_jq
  require_orp

  local mode="full"
  [[ "${1:-}" == "--paths" ]] && mode="paths"
  [[ "${1:-}" == "--slugs" ]] && mode="slugs"

  local tabs_json
  tabs_json=$(_orp_tabs_json) || {
    clawdad_error "Failed to read ORP workspace '$CLAWDAD_ORP_WORKSPACE'"
    return 1
  }

  case "$mode" in
    paths)
      echo "$tabs_json" | "$CLAWDAD_JQ" -r '.tabs[].path'
      ;;
    slugs)
      echo "$tabs_json" | "$CLAWDAD_JQ" -r '.tabs[] | .title // (.path | split("/") | last)'
      ;;
    full)
      local entries
      entries=$(echo "$tabs_json" | "$CLAWDAD_JQ" -r '.tabs[] | "\(.title // (.path | split("/") | last))\t\(.resumeTool // "none")\t\(.path)"')
      if [[ -z "$entries" ]]; then
        echo "No projects in ORP workspace '$CLAWDAD_ORP_WORKSPACE'."
        return 0
      fi
      printf "%-24s %-8s %s\n" "SLUG" "PROVIDER" "PATH"
      echo "$entries" | while IFS=$'\t' read -r slug provider path; do
        printf "%-24s %-8s %s\n" "$slug" "$provider" "$path"
      done
      ;;
  esac
}

cmd_read() {
  require_jq

  local input="${1:-}" raw=false
  [[ "${2:-}" == "--raw" ]] && raw=true

  if [[ -z "$input" ]]; then
    clawdad_error "Usage: clawdad read <slug|path>"
    return 1
  fi

  local project_path
  project_path=$(resolve_project "$input") || return 1

  if $raw; then
    mailbox_read_response_raw "$project_path"
  else
    mailbox_read_response "$project_path"
  fi
}

cmd_watch() {
  if [[ "${1:-}" != "" && "${1:-}" != --* ]]; then
    run_server_helper watchtower "$@"
    return
  fi

  require_jq

  local interval="$CLAWDAD_POLL_INTERVAL" daemon=false stop=false once=false

  while (( $# > 0 )); do
    case "$1" in
      --interval) interval="$2"; shift 2 ;;
      --daemon) daemon=true; shift ;;
      --stop) stop=true; shift ;;
      --once) once=true; shift ;;
      *) clawdad_error "Unknown option: $1"; return 1 ;;
    esac
  done

  if $stop; then
    watch_daemon_stop
    return 0
  fi

  if $daemon; then
    watch_daemon_start
    return 0
  fi

  if $once; then
    watch_poll_once
    return 0
  fi

  watch_loop "$interval"
}

cmd_serve() {
  run_server_helper serve "$@"
}

cmd_secure_bootstrap() {
  run_server_helper secure-bootstrap "$@"
}

cmd_secure_doctor() {
  run_server_helper secure-doctor "$@"
}

cmd_prod_doctor() {
  run_server_helper prod-doctor "$@"
}

cmd_gen_token() {
  run_server_helper gen-token "$@"
}

cmd_delegate() {
  run_server_helper delegate "$@"
}

cmd_delegate_set() {
  run_server_helper delegate-set "$@"
}

cmd_delegate_run() {
  run_server_helper delegate-run "$@"
}

cmd_supervise() {
  run_server_helper supervise "$@"
}

cmd_go() {
  run_server_helper go "$@"
}

cmd_watchtower() {
  run_server_helper watchtower "$@"
}

cmd_feed() {
  run_server_helper feed "$@"
}

cmd_delegate_pause() {
  run_server_helper delegate-pause "$@"
}

cmd_delegate_reset() {
  run_server_helper delegate-reset "$@"
}

cmd_sessions_doctor() {
  run_server_helper sessions-doctor "$@"
}

cmd_codex() {
  run_server_helper codex "$@"
}

cmd_codex_hook() {
  run_server_helper codex-hook "$@"
}

cmd_print_launch_agent() {
  run_server_helper print-launch-agent "$@"
}

cmd_install_launch_agent() {
  run_server_helper install-launch-agent "$@"
}

cmd_print_systemd_unit() {
  run_server_helper print-systemd-unit "$@"
}

cmd_install_systemd_unit() {
  run_server_helper install-systemd-unit "$@"
}

# Main dispatch
main() {
  local cmd="${1:-help}"
  shift 2>/dev/null || true

  case "$cmd" in
    init)       cmd_init "$@" ;;
    register)   cmd_register "$@" ;;
    add-session) cmd_add_session "$@" ;;
    delegate)   cmd_delegate "$@" ;;
    delegate-set) cmd_delegate_set "$@" ;;
    go)         cmd_go "$@" ;;
    delegate-run|delegate-start) cmd_delegate_run "$@" ;;
    supervise)  cmd_supervise "$@" ;;
    delegate-pause|delegate-stop) cmd_delegate_pause "$@" ;;
    delegate-reset) cmd_delegate_reset "$@" ;;
    sessions-doctor|session-doctor) cmd_sessions_doctor "$@" ;;
    codex)      cmd_codex "$@" ;;
    codex-hook) cmd_codex_hook "$@" ;;
    watchtower) cmd_watchtower "$@" ;;
    feed)       cmd_feed "$@" ;;
    track-session) cmd_track_session "$@" ;;
    rename-session) cmd_rename_session "$@" ;;
    remove-session) cmd_remove_session "$@" ;;
    unregister) cmd_unregister "$@" ;;
    dispatch)   cmd_dispatch "$@" ;;
    status)     cmd_status "$@" ;;
    sessions)   cmd_sessions "$@" ;;
    use-session) cmd_use_session "$@" ;;
    list)       cmd_list "$@" ;;
    read)       cmd_read "$@" ;;
    watch)      cmd_watch "$@" ;;
    serve)      cmd_serve "$@" ;;
    secure-bootstrap) cmd_secure_bootstrap "$@" ;;
    secure-doctor) cmd_secure_doctor "$@" ;;
    prod-doctor) cmd_prod_doctor "$@" ;;
    chimera-doctor) cmd_chimera_doctor "$@" ;;
    gen-token)  cmd_gen_token "$@" ;;
    print-launch-agent) cmd_print_launch_agent "$@" ;;
    install-launch-agent) cmd_install_launch_agent "$@" ;;
    print-systemd-unit) cmd_print_systemd_unit "$@" ;;
    install-systemd-unit) cmd_install_systemd_unit "$@" ;;
    help|--help|-h) usage ;;
    version|--version|-v) echo "clawdad v$CLAWDAD_VERSION" ;;
    *)
      clawdad_error "Unknown command: $cmd"
      usage
      return 1
      ;;
  esac
}

main "$@"
