#!/bin/bash

# omarchy:summary=Launch the default coding agent in a terminal
# omarchy:args=[--inline] [--pick] [--local] [--local-model <recipe>] [--agent <id>]
# omarchy:examples=omarchy agent | omarchy agent --inline | omarchy agent --local | omarchy agent --agent codex

inline=false
pick=false
use_local=false
local_model=""
agent_override=""

# Flags only. A bare prompt would shadow the subcommands under this group, so
# prompts go through omarchy-agent-prompt, which passes one here as --prompt.
while (($#)); do
  case "$1" in
    --inline)
      inline=true
      shift
      ;;
    --pick)
      pick=true
      shift
      ;;
    --local)
      use_local=true
      shift
      ;;
    --local-model)
      use_local=true
      local_model=${2:?--local-model needs a recipe id}
      shift 2
      ;;
    --agent)
      agent_override=${2:?--agent needs an agent id}
      shift 2
      ;;
    --prompt)
      prompt=${2:?--prompt needs a value}
      shift 2
      ;;
    *)
      echo "Unexpected argument: $1" >&2
      echo "To pass a prompt: omarchy agent prompt \"$*\"" >&2
      exit 1
      ;;
  esac
done

# Agents refuse to remember trust for $HOME, so launches from the keybinding or
# menu start in the work directory instead of re-asking on every session.
[[ $PWD == "$HOME" && -d $HOME/Work ]] && cd "$HOME/Work"

# --agent launches a given agent instead of the default: the panel's terminal
# button on each subscription opens the harness made for it, Codex for OpenAI.
if [[ -n $agent_override ]]; then
  agent=$agent_override
  if omarchy-cmd-missing "$agent"; then
    omarchy-notification-send "$(omarchy-i18n agents.harnessMissing "$agent")" "$(omarchy-i18n agents.harnessMissingBody "$agent")"
    exit 1
  fi
else
  agent=$(omarchy-default-agent)
fi

# Only agents with a launch-time overlay can be pointed at the local model. When
# the default can't, or none is set, the local model opens in pi, or OpenCode
# without pi: the agents panel launches it whether or not a default was chosen.
if [[ $use_local == "true" && $agent != "pi" && $agent != "opencode" ]]; then
  local_agent=""
  for candidate in pi opencode; do
    if omarchy-cmd-present "$candidate"; then
      local_agent=$candidate
      break
    fi
  done
  if [[ -z $local_agent ]]; then
    omarchy-notification-send -u critical "$(omarchy-i18n agents.localUnsupported)" "$(omarchy-i18n agents.localUnsupportedBody)"
    exit 1
  fi
  agent=$local_agent
fi

# Omarchy ships without a default, so there is nothing to launch until one is
# picked. --pick offers the choice instead of the error, which is what the
# keybinding wants: a keypress that opens nothing explains nothing.
if [[ -z $agent ]]; then
  [[ $pick == "true" ]] && exec omarchy-menu summon setup.default.agent
  echo "Choose default agent with: omarchy default agent <name>" >&2
  exit 1
fi

# OpenClaw's launcher sets OpenClaw up when the package became the seed before
# this user's OpenClaw moved, so with the package a missing command is its to fix.
if omarchy-cmd-missing "$agent" && ! { [[ $agent == "openclaw" ]] && omarchy-pkg-present openclaw; }; then
  echo "$agent is not installed. Choose an installed agent with: omarchy default agent <name>" >&2
  exit 1
fi

# Agents launched from the keybinding or menu run unattended, so each one starts
# with its own spelling of "don't stop to ask". Pi and Ori have none to skip.
case "$agent" in
opencode)
  command=(opencode --auto)
  [[ -n ${prompt:-} ]] && command+=(--prompt "$prompt")
  ;;
agy)
  command=(agy --dangerously-skip-permissions)
  [[ -n ${prompt:-} ]] && command+=(--prompt-interactive "$prompt")
  ;;
copilot)
  command=(copilot --allow-all)
  [[ -n ${prompt:-} ]] && command+=(--interactive "$prompt")
  ;;
crush)
  # --yolo belongs to the interactive command only; `crush run` never prompts.
  if [[ -n ${prompt:-} ]]; then
    command=(crush run "$prompt")
  else
    command=(crush --yolo)
  fi
  ;;
claude)
  command=(claude --permission-mode auto)
  [[ -n ${prompt:-} ]] && command+=(-- "$prompt")
  ;;
grok)
  command=(grok --permission-mode bypassPermissions)
  [[ -n ${prompt:-} ]] && command+=(-- "$prompt")
  ;;
openclaw)
  # The launcher owns onboarding and the gateway dance: OpenClaw's terminal UI
  # must attach to the running gateway (the embedded `openclaw chat` refuses to
  # start while the gateway owns the state directory). It has no permission
  # prompts to skip, and --message seeds the session while keeping it
  # interactive.
  command=(omarchy-launch-openclaw --tui)
  [[ -n ${prompt:-} ]] && command+=(--message "$prompt")
  ;;
codex)
  command=(codex --approve-for-me)
  [[ -n ${prompt:-} ]] && command+=(-- "$prompt")
  ;;
cursor-agent)
  # --yolo covers commands only; the workspace trust dialog has its own flag.
  # A one-word prompt naming a subcommand (update, login, help) still runs that
  # subcommand after a bare --, so the agent subcommand is named outright, and
  # -- after it keeps a prompt starting with a dash from being read as an option.
  command=(cursor-agent --yolo --trust)
  [[ -n ${prompt:-} ]] && command+=(agent -- "$prompt")
  ;;
hermes)
  if [[ -n ${prompt:-} ]]; then
    command=(env -u HERMES_SESSION_SOURCE hermes chat --yolo --tui "--query=$prompt")
  else
    command=(hermes --yolo)
  fi
  ;;
muse)
  # --approval-mode never skips the tool prompts but keeps Muse's own sandbox.
  command=(muse --approval-mode never)
  [[ -n ${prompt:-} ]] && command+=(-- "$prompt")
  ;;
omp)
  command=(omp --auto-approve)
  [[ -n ${prompt:-} ]] && command+=(-- "$prompt")
  ;;
ori)
  # Ori is a harness launcher, and `ori code` is the agent it runs itself.
  # A prompt alone means one headless turn there, printed after the turn ends,
  # so --interactive is what seeds the session with it and keeps the window.
  command=(ori code)
  [[ -n ${prompt:-} ]] && command+=(--interactive --prompt "$prompt")
  ;;
pi)
  command=(pi)
  [[ -n ${prompt:-} ]] && command+=("$prompt")
  ;;
kimi)
  # --prompt is non-interactive print mode only; it cannot combine with --yolo.
  if [[ -n ${prompt:-} ]]; then
    command=(kimi --prompt "$prompt")
  else
    command=(kimi --yolo)
  fi
  ;;
qwen)
  # Bind the value so a prompt starting with a dash stays literal.
  command=(qwen --yolo)
  [[ -n ${prompt:-} ]] && command+=("--prompt-interactive=$prompt")
  ;;
dsh)
  # The browser UI has no prompt option, and the headless profile would run the
  # prompt with full access in a window that closes with its answer, so a
  # prompt goes to the clipboard below instead.
  command=(env DSH_PERMISSION_MODE=danger-full-access dsh web)
  prompt_via_clipboard="DeepSeek Harness"
  ;;
mcode)
  # The TUI has no no-approval flag; permission options belong to exec mode.
  command=(mcode)
  [[ -n ${prompt:-} ]] && command+=(-- "$prompt")
  ;;
kiro-cli)
  # A positional input seeds the interactive chat; -- keeps a prompt starting
  # with a dash literal.
  command=(kiro-cli chat --trust-all-tools)
  [[ -n ${prompt:-} ]] && command+=(-- "$prompt")
  ;;
freebuff)
  # Freebuff takes no starting prompt and asks no tool permissions, so a
  # prompt goes to the clipboard below instead.
  command=(freebuff)
  prompt_via_clipboard="Freebuff"
  ;;
kilo)
  # Kilo is built on OpenCode and shares its flags. Bind the prompt so one
  # starting with a dash stays literal.
  command=(kilo --auto)
  [[ -n ${prompt:-} ]] && command+=("--prompt=$prompt")
  ;;
devin)
  # A bare positional is a path that opens Devin Desktop, so the prompt
  # always follows --, which starts an interactive session with it.
  command=(devin --permission-mode dangerous --respect-workspace-trust false)
  [[ -n ${prompt:-} ]] && command+=(-- "$prompt")
  ;;
amp)
  # Amp takes its first interactive message from piped input; a positional
  # message is for --execute, which exits after one turn. The binary rides in
  # as $0 so a user install below can replace it.
  if [[ -n ${prompt:-} ]]; then
    command=(bash -c 'printf "%s\n" "$1" | exec "$0" --dangerously-allow-all' amp "$prompt")
  else
    command=(amp --dangerously-allow-all)
  fi
  ;;
commandcode)
  # --yolo skips permission prompts and implies --trust. A positional prompt
  # seeds the interactive session; -- keeps one starting with a dash literal.
  command=(commandcode --yolo)
  [[ -n ${prompt:-} ]] && command+=(-- "$prompt")
  ;;
*)
  echo "Unsupported default agent: $agent" >&2
  exit 1
  ;;
esac

# --local points the agent at the model `omarchy agent local use` runs, through
# a launch-time overlay Omarchy owns, never by editing the agent's own config.
# Only agents with such an overlay can be pointed there.
if [[ $use_local == "true" ]]; then
  mapfile -t overlay < <(omarchy-agent-local overlay "$agent" ${local_model:+"$local_model"})
  (( ${#overlay[@]} == 3 )) || exit 1
  case "$agent" in
  opencode)
    # The shared background service never sees the overlay, so run a private one.
    model=$(jq -nc --arg model "${overlay[1]}/${overlay[2]}" '{model: $model}')
    command=(env "OPENCODE_CONFIG=${overlay[0]}" "OPENCODE_CONFIG_CONTENT=$model" "${command[@]}" --standalone)
    ;;
  pi)
    command+=(--extension "${overlay[0]}" --provider "${overlay[1]}" --model "${overlay[2]}")
    ;;
  esac
fi

# An agent with no way to take a starting prompt still opens, with the prompt
# on the clipboard to paste as its first message, so a prompted launch from the
# agents panel or a crash report is not lost.
if [[ -n ${prompt:-} && -n ${prompt_via_clipboard:-} ]]; then
  printf '%s' "$prompt" | wl-copy
  omarchy-notification-send -g 󰅇 "$(omarchy-i18n agents.promptOnClipboard "$prompt_via_clipboard")" \
    "$(omarchy-i18n agents.promptOnClipboardBody)"
fi

# omarchy-mise-install's wrappers run `mise use` and `mise x`, current ones on
# lines of their own and older ones after an inline variable; either way
# running one installs the tool it wraps. A symlink is the user's own install.
mise_wrapper() {
  [[ ! -L $1 ]] && head -c 4096 "$1" | grep -qaE 'mise (use|x) '
}

# The user's own install in ~/.local/bin, which omarchy-default-agent counts as
# installed, still wins over mise's lazy shim earlier on PATH, which would
# install and run mise's copy instead. Once mise has installed its own, a
# terminal runs that one, so the launcher does too. OpenClaw's command runs its
# launcher, so its prompt is the only argument that could match the name.
user_install="$HOME/.local/bin/$agent"
if [[ $agent != "openclaw" && -f $user_install && -x $user_install ]] &&
  ! mise_wrapper "$user_install" && ! MISE_OFFLINE=1 mise which "$agent" &>/dev/null; then
  for i in "${!command[@]}"; do
    if [[ ${command[i]} == "$agent" ]]; then
      command[i]=$user_install
      break
    fi
  done
fi

# A new session uses the current selection rather than its parent's account
# pin. Explicit provider homes still override the selection. Carry the new pin
# in the command, since terminals launched through uwsm don't inherit our env.
case "$agent" in
claude) home_env="CLAUDE_CONFIG_DIR" ;;
codex) home_env="CODEX_HOME" ;;
grok) home_env="GROK_HOME" ;;
*) home_env="" ;;
esac
if [[ -n $home_env ]]; then
  account_pin="OMARCHY_AGENT_${agent^^}_HOME"
  if [[ -v $account_pin && ${!home_env:-} == "${!account_pin}" ]]; then
    unset "$home_env"
  fi
  unset "$account_pin"
  if [[ -z ${!home_env:-} ]]; then
    home=$(omarchy-agent-account-home "$agent")
    account_env=(env -u "$home_env" "$account_pin=$home")
    [[ -n $home ]] && account_env+=("$home_env=$home")
    command=("${account_env[@]}" "${command[@]}")
  fi
fi

if [[ $inline == "true" ]]; then
  exec "${command[@]}"
else
  # A fixed app-id rather than the default org.omarchy.<binary>, so every agent
  # window shares one class for window rules and themes to single out.
  exec omarchy-launch-tui --app-id=org.omarchy.agent "${command[@]}"
fi
