* Add Cursor CLI as a coding agent choice * Launch Cursor CLI through its agent subcommand with --trust Cursor CLI still dispatches a one-word prompt that names one of its subcommands (update, login, help) even after a bare --, so name the agent subcommand outright and pass the prompt behind -- there, where it also keeps a prompt starting with a dash from being read as an option. --yolo only auto-allows commands; the workspace trust dialog is skipped only by --trust, and a launcher that must not stop to ask needs both. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * Leave an official Cursor CLI install alone Cursor's own installer symlinks ~/.local/bin/cursor-agent, the same path the mise wrapper takes. The migration now installs the wrapper only when no cursor-agent command exists, and Remove Preinstalls deletes the path only when it holds the wrapper omarchy-mise-install wrote, the way the Hermes wrapper is handled. Selecting the agent treats an executable at that path other than the wrapper as the user's own install and skips mise, since the mise shims precede ~/.local/bin on PATH and a mise copy would only shadow it. The wrapper resolves through mise's registry, which lists cursor-agent from 2026.8.15 on. The tests write a real cursor-agent stub before Remove Preinstalls runs, cover the preinstall opt-out for the new migration, and check that a symlinked official install survives removal and selection alike while a dead file at the same path still installs. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * Give Cursor its brand mark and one name in the menu Add Cursor's mark to the Omarchy icon font as U+E90D and point the agent entry and both editor entries at it, so one brand is drawn one way across the menu. Label the agent entry "Cursor CLI", the name the command and the manual already use, and spell it the same in the migration and the tests. Append the manual row after the others at the standard width. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * Keep an official Cursor CLI install through a user re-provision User setup writes every mise wrapper unconditionally, which is fine on a fresh install but replaces the symlink Cursor's own installer leaves at the same path when omarchy-provision-user runs again with --force. Guard that one line the way the migration does. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> --------- Co-authored-by: John Cavanaugh <59479+cavanaug@users.noreply.github.com> Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
673 lines
29 KiB
Bash
673 lines
29 KiB
Bash
#!/bin/bash
|
|
|
|
set -euo pipefail
|
|
|
|
source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh"
|
|
|
|
test_tmp=$(mktemp -d)
|
|
trap 'rm -rf "$test_tmp"' EXIT
|
|
|
|
mock_bin="$test_tmp/bin"
|
|
test_home="$test_tmp/home"
|
|
agent_file="$test_home/.config/omarchy/defaults/agent"
|
|
notification_history="$test_tmp/notification-history"
|
|
agent_open_log="$test_tmp/agent-open"
|
|
launch_log="$test_tmp/launch"
|
|
inline_log="$test_tmp/inline"
|
|
mise_log="$test_tmp/mise"
|
|
mise_history="$test_tmp/mise-history"
|
|
stub_log="$test_tmp/stubs"
|
|
terminal_log="$test_tmp/terminal"
|
|
menu_log="$test_tmp/menu"
|
|
mkdir -p "$mock_bin" "$test_home"
|
|
|
|
cat >"$mock_bin/omarchy-notification-send" <<'SH'
|
|
#!/bin/bash
|
|
printf '%s\0' "$@" >>"$OMARCHY_TEST_NOTIFICATION_HISTORY"
|
|
SH
|
|
|
|
cat >"$mock_bin/omarchy-cmd-missing" <<'SH'
|
|
#!/bin/bash
|
|
[[ $1 == ${OMARCHY_TEST_MISSING_COMMAND:-} ]]
|
|
SH
|
|
|
|
cat >"$mock_bin/omarchy-launch-tui" <<'SH'
|
|
#!/bin/bash
|
|
printf '%s\0' "$@" >"$OMARCHY_TEST_AGENT_LAUNCH_LOG"
|
|
SH
|
|
|
|
cat >"$mock_bin/omarchy-launch-floating-terminal-with-presentation" <<'SH'
|
|
#!/bin/bash
|
|
printf '%s\0' "$@" >"$OMARCHY_TEST_AGENT_TERMINAL_LOG"
|
|
SH
|
|
|
|
cat >"$mock_bin/opencode" <<'SH'
|
|
#!/bin/bash
|
|
printf '%s\0' opencode "$@" >"$OMARCHY_TEST_AGENT_INLINE_LOG"
|
|
SH
|
|
|
|
cat >"$mock_bin/omarchy-mise-install" <<'SH'
|
|
#!/bin/bash
|
|
printf '%s\n' "$*" >>"$OMARCHY_TEST_STUB_LOG"
|
|
SH
|
|
|
|
cat >"$mock_bin/mise" <<'SH'
|
|
#!/bin/bash
|
|
printf '%s\0' "$@" >"$OMARCHY_TEST_MISE_LOG"
|
|
printf '%s\n' "$*" >>"$OMARCHY_TEST_MISE_HISTORY"
|
|
|
|
if [[ $1 == "where" ]]; then
|
|
[[ ${OMARCHY_TEST_AGENT_INSTALLED:-false} == "true" ]]
|
|
exit
|
|
fi
|
|
|
|
[[ ${OMARCHY_TEST_MISE_FAIL:-false} != "true" ]]
|
|
SH
|
|
|
|
cat >"$mock_bin/omarchy-menu" <<'SH'
|
|
#!/bin/bash
|
|
printf '%s\0' "$@" >"$OMARCHY_TEST_AGENT_MENU_LOG"
|
|
SH
|
|
|
|
cat >"$mock_bin/omarchy-test-noop" <<'SH'
|
|
#!/bin/bash
|
|
exit 0
|
|
SH
|
|
|
|
for command in gum hyprctl omarchy-webapp-remove-all omarchy-tui-remove-all omarchy-pkg-drop; do
|
|
ln -s omarchy-test-noop "$mock_bin/$command"
|
|
done
|
|
|
|
chmod +x "$mock_bin"/*
|
|
|
|
export HOME="$test_home"
|
|
export PATH="$mock_bin:$ROOT/bin:$PATH"
|
|
export OMARCHY_TEST_NOTIFICATION_HISTORY="$notification_history"
|
|
export OMARCHY_TEST_AGENT_OPEN_LOG="$agent_open_log"
|
|
export OMARCHY_TEST_AGENT_LAUNCH_LOG="$launch_log"
|
|
export OMARCHY_TEST_AGENT_INLINE_LOG="$inline_log"
|
|
export OMARCHY_TEST_MISE_LOG="$mise_log"
|
|
export OMARCHY_TEST_MISE_HISTORY="$mise_history"
|
|
export OMARCHY_TEST_STUB_LOG="$stub_log"
|
|
export OMARCHY_TEST_AGENT_TERMINAL_LOG="$terminal_log"
|
|
export OMARCHY_TEST_AGENT_MENU_LOG="$menu_log"
|
|
export OMARCHY_PATH="$ROOT"
|
|
|
|
grok_package="npm:@xai-official/grok"
|
|
omp_package="github:can1357/oh-my-pi"
|
|
crush_package="crush"
|
|
agy_package="antigravity-cli"
|
|
ori_package="github:OpenRouterLabs/ori-releases"
|
|
cursor_agent_package="cursor-agent"
|
|
|
|
assert_lazy_stub() {
|
|
local package=$1
|
|
local command=$2
|
|
|
|
: >"$mise_history"
|
|
"$ROOT/bin/omarchy-mise-install" "$package" "$command"
|
|
"$test_home/.local/bin/$command" --version
|
|
mapfile -t mise_calls <"$mise_history"
|
|
|
|
[[ ${mise_calls[0]} == "use -g --quiet $package" && ${mise_calls[1]} == "x $package -- $command --version" ]] ||
|
|
fail "$command lazy stub preserves its mise package"
|
|
}
|
|
|
|
assert_lazy_stub "$grok_package" grok
|
|
assert_lazy_stub "$omp_package" omp
|
|
assert_lazy_stub "$crush_package" crush
|
|
assert_lazy_stub "$ori_package" ori
|
|
assert_lazy_stub "$cursor_agent_package" cursor-agent
|
|
pass "custom agent lazy stubs preserve their mise packages"
|
|
|
|
OMARCHY_TEST_MISSING_COMMAND=cursor-agent source "$ROOT/install/user/mise.sh"
|
|
grep -Fx "$agy_package agy" "$stub_log" >/dev/null || fail "user setup creates the Antigravity lazy stub"
|
|
grep -Fx "$grok_package grok" "$stub_log" >/dev/null || fail "user setup creates the Grok lazy stub"
|
|
grep -Fx "$cursor_agent_package" "$stub_log" >/dev/null || fail "user setup creates the Cursor CLI lazy stub"
|
|
grep -Fx "$omp_package omp" "$stub_log" >/dev/null || fail "user setup creates the Oh My Pi lazy stub"
|
|
grep -Fx "$crush_package" "$stub_log" >/dev/null || fail "user setup creates the Crush lazy stub"
|
|
grep -Fx "$ori_package ori" "$stub_log" >/dev/null || fail "user setup creates the Ori lazy stub"
|
|
pass "user setup creates the custom agent lazy stubs"
|
|
|
|
: >"$stub_log"
|
|
source "$ROOT/install/user/mise.sh"
|
|
grep -Fx "$cursor_agent_package" "$stub_log" >/dev/null && fail "user setup replaces an existing cursor-agent command"
|
|
pass "user setup keeps an existing Cursor CLI install"
|
|
|
|
: >"$stub_log"
|
|
source "$ROOT/migrations/1785617047.sh" >/dev/null
|
|
grep -Fx "$omp_package omp" "$stub_log" >/dev/null || fail "Oh My Pi migration creates a working lazy stub"
|
|
|
|
: >"$stub_log"
|
|
source "$ROOT/migrations/1787342993.sh" >/dev/null
|
|
grep -Fx "$ori_package ori" "$stub_log" >/dev/null || fail "Ori migration creates a working lazy stub"
|
|
|
|
: >"$stub_log"
|
|
export OMARCHY_TEST_MISSING_COMMAND=cursor-agent
|
|
source "$ROOT/migrations/1788577553.sh" >/dev/null
|
|
unset OMARCHY_TEST_MISSING_COMMAND
|
|
grep -Fx "$cursor_agent_package" "$stub_log" >/dev/null || fail "Cursor CLI migration creates a working lazy stub"
|
|
|
|
: >"$stub_log"
|
|
source "$ROOT/migrations/1788577553.sh" >/dev/null
|
|
[[ ! -s $stub_log ]] || fail "Cursor CLI migration reinstalls an existing cursor-agent command"
|
|
pass "Cursor CLI migration preserves an existing Cursor CLI install"
|
|
|
|
: >"$stub_log"
|
|
source "$ROOT/migrations/1785846769.sh" >/dev/null
|
|
grep -Fx "$omp_package omp" "$stub_log" >/dev/null || fail "agent migration repairs the Oh My Pi lazy stub"
|
|
grep -Fx "$grok_package grok" "$stub_log" >/dev/null || fail "agent migration creates the Grok lazy stub"
|
|
grep -Fx "$crush_package" "$stub_log" >/dev/null || fail "agent migration creates the Crush lazy stub"
|
|
|
|
: >"$stub_log"
|
|
mkdir -p "$(dirname "$agent_file")"
|
|
printf '%s\n' gemini >"$agent_file"
|
|
"$ROOT/bin/omarchy-mise-install" gemini
|
|
export OMARCHY_TEST_MISSING_COMMAND=agy
|
|
source "$ROOT/migrations/1786719479.sh" >/dev/null
|
|
unset OMARCHY_TEST_MISSING_COMMAND
|
|
grep -Fx "$agy_package agy" "$stub_log" >/dev/null || fail "Antigravity migration creates its lazy stub"
|
|
[[ $(<"$agent_file") == "agy" ]] || fail "Antigravity migration replaces a Gemini default"
|
|
|
|
: >"$stub_log"
|
|
printf ' %s \n' gemini >"$agent_file"
|
|
export OMARCHY_TEST_MISSING_COMMAND=agy
|
|
source "$ROOT/migrations/1786719479.sh" >/dev/null
|
|
unset OMARCHY_TEST_MISSING_COMMAND
|
|
[[ $(<"$agent_file") == "agy" ]] ||
|
|
fail "Antigravity migration replaces a padded Gemini default the launcher would still read"
|
|
pass "Antigravity migration reads the default the way the launcher does"
|
|
|
|
for obsolete_form in 'mise use -g "gemini"' 'mise use -g --quiet "gemini"'; do
|
|
printf '#!/bin/bash\n%s || exit 1\n' "$obsolete_form" >"$test_home/.local/bin/gemini"
|
|
chmod +x "$test_home/.local/bin/gemini"
|
|
source "$ROOT/migrations/1786719479.sh" >/dev/null
|
|
[[ ! -e $test_home/.local/bin/gemini ]] ||
|
|
fail "Antigravity migration removes a wrapper built on [$obsolete_form]"
|
|
done
|
|
|
|
printf '#!/bin/bash\nexec /opt/gemini "$@"\n' >"$test_home/.local/bin/gemini"
|
|
chmod +x "$test_home/.local/bin/gemini"
|
|
source "$ROOT/migrations/1786719479.sh" >/dev/null
|
|
[[ -e $test_home/.local/bin/gemini ]] || fail "Antigravity migration leaves a hand-written gemini alone"
|
|
|
|
printf '#!/bin/bash\n# replaced: mise use -g --quiet "gemini"\nexec /opt/gemini "$@"\n' >"$test_home/.local/bin/gemini"
|
|
chmod +x "$test_home/.local/bin/gemini"
|
|
source "$ROOT/migrations/1786719479.sh" >/dev/null
|
|
[[ -e $test_home/.local/bin/gemini ]] ||
|
|
fail "Antigravity migration leaves a wrapper that only mentions the installer line"
|
|
rm -f "$test_home/.local/bin/gemini"
|
|
pass "Antigravity migration only removes the Gemini wrapper Omarchy wrote"
|
|
|
|
[[ -L "$test_home/.gemini/config/skills/omarchy" && $(readlink "$test_home/.gemini/config/skills/omarchy") == "$ROOT/default/agents/skills/omarchy" ]] ||
|
|
fail "Antigravity migration provisions the omarchy skill"
|
|
[[ -L "$test_home/.gemini/config/skills/diagnose-crash" && $(readlink "$test_home/.gemini/config/skills/diagnose-crash") == "$ROOT/default/agents/skills/diagnose-crash" ]] ||
|
|
fail "Antigravity migration provisions the diagnose-crash skill"
|
|
pass "Antigravity migration provisions Antigravity skills"
|
|
|
|
|
|
: >"$stub_log"
|
|
mkdir -p "$test_home/.local/state/omarchy"
|
|
touch "$test_home/.local/state/omarchy/preinstalls-removed"
|
|
export OMARCHY_TEST_MISSING_COMMAND=agy
|
|
source "$ROOT/migrations/1786719479.sh" >/dev/null
|
|
[[ ! -s $stub_log ]] || fail "Antigravity migration preserves removed preinstalls"
|
|
pass "Antigravity migration respects removed preinstalls"
|
|
|
|
: >"$stub_log"
|
|
printf '%s\n' gemini >"$agent_file"
|
|
source "$ROOT/migrations/1786719479.sh" >/dev/null
|
|
unset OMARCHY_TEST_MISSING_COMMAND
|
|
grep -Fx "$agy_package agy" "$stub_log" >/dev/null || fail "Antigravity migration installs the agent a Gemini default now names"
|
|
[[ $(<"$agent_file") == "agy" ]] || fail "Antigravity migration replaces a Gemini default after opt-out"
|
|
pass "Antigravity migration never leaves the default naming a missing agent"
|
|
|
|
: >"$stub_log"
|
|
rm "$test_home/.local/state/omarchy/preinstalls-removed"
|
|
source "$ROOT/migrations/1786719479.sh" >/dev/null
|
|
[[ ! -s $stub_log ]] || fail "Antigravity migration reinstalls an existing Antigravity command"
|
|
pass "Antigravity migration preserves an existing Antigravity install"
|
|
|
|
mkdir -p "$test_home/.local/state/omarchy"
|
|
touch "$test_home/.local/state/omarchy/preinstalls-removed"
|
|
"$ROOT/bin/omarchy-mise-install" oh-my-pi omp
|
|
: >"$stub_log"
|
|
source "$ROOT/migrations/1785617047.sh" >/dev/null
|
|
source "$ROOT/migrations/1785846769.sh" >/dev/null
|
|
source "$ROOT/migrations/1787342993.sh" >/dev/null
|
|
OMARCHY_TEST_MISSING_COMMAND=cursor-agent source "$ROOT/migrations/1788577553.sh" >/dev/null
|
|
[[ ! -s $stub_log ]] || fail "agent migrations respect the preinstall opt-out"
|
|
[[ ! -e $test_home/.local/bin/omp ]] || fail "agent migration removes the obsolete Oh My Pi wrapper after opt-out"
|
|
|
|
# The matcher has to catch a bare oh-my-pi wrapper from either generation of the
|
|
# installer, and leave a wrapper built on the fully qualified package alone.
|
|
for obsolete_form in 'mise use -g "oh-my-pi"' 'mise use -g --quiet "oh-my-pi"'; do
|
|
printf '#!/bin/bash\n%s || exit 1\n' "$obsolete_form" >"$test_home/.local/bin/omp"
|
|
chmod +x "$test_home/.local/bin/omp"
|
|
source "$ROOT/migrations/1785846769.sh" >/dev/null
|
|
[[ ! -e $test_home/.local/bin/omp ]] ||
|
|
fail "agent migration removes a wrapper built on [$obsolete_form]"
|
|
done
|
|
|
|
printf '#!/bin/bash\nmise use -g --quiet "%s" || exit 1\n' "$omp_package" >"$test_home/.local/bin/omp"
|
|
chmod +x "$test_home/.local/bin/omp"
|
|
source "$ROOT/migrations/1785846769.sh" >/dev/null
|
|
[[ -e $test_home/.local/bin/omp ]] ||
|
|
fail "agent migration keeps a wrapper built on $omp_package"
|
|
rm -f "$test_home/.local/bin/omp"
|
|
|
|
rm "$test_home/.local/state/omarchy/preinstalls-removed"
|
|
rm -f "$agent_file"
|
|
pass "agent migrations install working wrappers without overriding the preinstall opt-out"
|
|
|
|
touch "$test_home/.local/bin/agy" "$test_home/.local/bin/ori"
|
|
omarchy-remove-preinstalls >/dev/null
|
|
for command in agy omp ori grok crush cursor-agent; do
|
|
[[ ! -e $test_home/.local/bin/$command ]] || fail "Remove Preinstalls deletes the $command lazy stub"
|
|
done
|
|
pass "Remove Preinstalls deletes every optional agent lazy stub"
|
|
|
|
# Cursor's installer links the same path, so anything but the mise wrapper is
|
|
# the user's own install.
|
|
touch "$test_home/.local/bin/cursor-agent.official"
|
|
ln -s cursor-agent.official "$test_home/.local/bin/cursor-agent"
|
|
omarchy-remove-preinstalls >/dev/null
|
|
[[ -L $test_home/.local/bin/cursor-agent ]] || fail "Remove Preinstalls keeps an official Cursor CLI install"
|
|
rm -f "$test_home/.local/bin/cursor-agent" "$test_home/.local/bin/cursor-agent.official"
|
|
pass "Remove Preinstalls keeps an official Cursor CLI install"
|
|
|
|
[[ -z $(omarchy-default-agent) ]] || fail "default agent is unset until one is chosen"
|
|
pass "default agent is unset until one is chosen"
|
|
|
|
: >"$launch_log"
|
|
if omarchy-agent >"$test_tmp/no-agent-output" 2>&1; then
|
|
fail "agent launcher refuses to launch without a default"
|
|
fi
|
|
grep -Fq "Choose default agent with" "$test_tmp/no-agent-output" ||
|
|
fail "agent launcher explains that no default is set"
|
|
[[ ! -s $launch_log ]] || fail "agent launcher starts nothing without a default"
|
|
pass "agent launcher refuses to launch without a default"
|
|
|
|
# The keybinding uses --pick, where an error on stderr nobody sees would make
|
|
# the keypress look broken. It offers the choice instead.
|
|
: >"$launch_log"
|
|
: >"$menu_log"
|
|
omarchy-agent --pick
|
|
mapfile -d '' -t menu_args <"$menu_log"
|
|
[[ ${menu_args[*]} == "summon setup.default.agent" ]] ||
|
|
fail "--pick opens the agent defaults menu when none is set"
|
|
[[ ! -s $launch_log ]] || fail "--pick starts nothing when no agent is set"
|
|
pass "--pick opens the agent defaults menu when none is set"
|
|
|
|
source "$ROOT/default/bash/aliases"
|
|
[[ $(alias a) == "alias a='omarchy-agent --inline'" ]] ||
|
|
fail "terminal alias launches the default agent inline"
|
|
pass "terminal alias launches the default agent inline"
|
|
|
|
grep -Fq 'o.bind("SUPER + SHIFT + CTRL + A", "Agent", "omarchy-agent --pick")' \
|
|
"$ROOT/default/hypr/bindings/utilities.lua" ||
|
|
fail "agent launcher has a keyboard shortcut"
|
|
pass "agent launcher has a keyboard shortcut"
|
|
|
|
cat >"$mock_bin/omarchy-agent" <<'SH'
|
|
#!/bin/bash
|
|
printf '%s\0' omarchy-agent "$@" >"$OMARCHY_TEST_AGENT_OPEN_LOG"
|
|
SH
|
|
chmod +x "$mock_bin/omarchy-agent"
|
|
hash -r
|
|
|
|
declare -A expected_agents=(
|
|
[pi]="pi"
|
|
[omp]="omp"
|
|
[oh-my-pi]="omp"
|
|
[opencode]="opencode"
|
|
[open-code]="opencode"
|
|
[ori]="ori"
|
|
[openrouter]="ori"
|
|
[claude]="claude"
|
|
[claude-code]="claude"
|
|
[codex]="codex"
|
|
[crush]="crush"
|
|
[grok]="grok"
|
|
[agy]="agy"
|
|
[antigravity]="agy"
|
|
[antigravity-cli]="agy"
|
|
[gemini]="agy"
|
|
[gemini-cli]="agy"
|
|
[copilot]="copilot"
|
|
[github-copilot]="copilot"
|
|
[cursor]="cursor-agent"
|
|
[cursor-agent]="cursor-agent"
|
|
)
|
|
|
|
declare -A expected_packages=(
|
|
[pi]="pi"
|
|
[omp]="$omp_package"
|
|
[opencode]="opencode"
|
|
[ori]="$ori_package"
|
|
[claude]="claude"
|
|
[codex]="codex"
|
|
[crush]="$crush_package"
|
|
[grok]="$grok_package"
|
|
[agy]="$agy_package"
|
|
[copilot]="copilot"
|
|
[cursor-agent]="$cursor_agent_package"
|
|
)
|
|
|
|
for selection in "${!expected_agents[@]}"; do
|
|
expected=${expected_agents[$selection]}
|
|
: >"$agent_open_log"
|
|
OMARCHY_TEST_AGENT_INSTALLED=true omarchy-default-agent "$selection"
|
|
[[ $(omarchy-default-agent) == $expected ]] || fail "default agent canonicalizes $selection"
|
|
|
|
mapfile -d '' -t mise_args <"$mise_log"
|
|
[[ ${mise_args[0]} == "use" && ${mise_args[1]} == "-g" && ${mise_args[2]} == ${expected_packages[$expected]} ]] ||
|
|
fail "default agent installs $selection globally through mise"
|
|
|
|
mapfile -d '' -t agent_open_args <"$agent_open_log"
|
|
[[ ${#agent_open_args[@]} == 1 && ${agent_open_args[0]} == "omarchy-agent" ]] ||
|
|
fail "default agent opens $selection after selecting it"
|
|
done
|
|
pass "default agent selects and opens every supported provider and alias"
|
|
[[ -f $agent_file && ! -e $test_home/.local/state/omarchy/defaults/agent ]] ||
|
|
fail "default agent stores its selection in Omarchy user config"
|
|
pass "default agent stores its selection in Omarchy user config"
|
|
|
|
OMARCHY_TEST_AGENT_INSTALLED=true omarchy-default-agent pi
|
|
: >"$notification_history"
|
|
: >"$agent_open_log"
|
|
: >"$terminal_log"
|
|
omarchy-default-agent github-copilot
|
|
mapfile -d '' -t terminal_args <"$terminal_log"
|
|
[[ ${terminal_args[0]} == "omarchy-default-agent" && ${terminal_args[1]} == "--install" && ${terminal_args[2]} == "copilot" ]] ||
|
|
fail "missing agent installation opens in a terminal"
|
|
[[ ! -s $notification_history ]] || fail "missing agent installation skips notifications"
|
|
[[ ! -s $agent_open_log ]] || fail "missing agent installation waits to open the agent"
|
|
[[ $(omarchy-default-agent) == "pi" ]] || fail "missing agent installation waits to change the selection"
|
|
|
|
omarchy-default-agent --install github-copilot >"$test_tmp/install-output"
|
|
mapfile -d '' -t mise_args <"$mise_log"
|
|
[[ ${mise_args[0]} == "use" && ${mise_args[1]} == "-g" && ${mise_args[2]} == "copilot" ]] ||
|
|
fail "visible agent installation activates the provider globally through mise"
|
|
[[ $(omarchy-default-agent) == "copilot" ]] || fail "visible agent installation changes the selection after mise succeeds"
|
|
[[ ! -s $notification_history ]] || fail "visible agent installation leaves progress to the terminal"
|
|
[[ $(<"$test_tmp/install-output") == $'\033[2J\033[3J\033[H' ]] ||
|
|
fail "visible agent installation clears its terminal before opening the agent"
|
|
mapfile -d '' -t agent_open_args <"$agent_open_log"
|
|
[[ ${#agent_open_args[@]} == 2 && ${agent_open_args[0]} == "omarchy-agent" && ${agent_open_args[1]} == "--inline" ]] ||
|
|
fail "newly installed agent opens in the installation terminal"
|
|
pass "missing agents install visibly and open in the same terminal"
|
|
|
|
: >"$notification_history"
|
|
: >"$agent_open_log"
|
|
: >"$terminal_log"
|
|
OMARCHY_TEST_AGENT_INSTALLED=true omarchy-default-agent github-copilot
|
|
[[ ! -s $terminal_log ]] || fail "installed agent selection skips the terminal"
|
|
[[ ! -s $notification_history ]] || fail "installed agent selection skips notifications"
|
|
mapfile -d '' -t mise_args <"$mise_log"
|
|
[[ ${mise_args[0]} == "use" && ${mise_args[1]} == "-g" && ${mise_args[2]} == "copilot" ]] ||
|
|
fail "default agent still activates an installed provider globally through mise"
|
|
mapfile -d '' -t agent_open_args <"$agent_open_log"
|
|
[[ ${#agent_open_args[@]} == 1 && ${agent_open_args[0]} == "omarchy-agent" ]] ||
|
|
fail "installed agent opens in a new terminal after selection"
|
|
pass "installed agents select and open without notifications"
|
|
|
|
# Cursor's installer links the wrapper's path, and the mise shims precede
|
|
# ~/.local/bin, so a mise copy would shadow the user's own install.
|
|
touch "$test_home/.local/bin/cursor-agent.official"
|
|
chmod +x "$test_home/.local/bin/cursor-agent.official"
|
|
ln -s cursor-agent.official "$test_home/.local/bin/cursor-agent"
|
|
: >"$terminal_log"
|
|
: >"$mise_log"
|
|
: >"$agent_open_log"
|
|
omarchy-default-agent cursor-agent
|
|
[[ ! -s $terminal_log ]] || fail "an official Cursor CLI install needs no install terminal"
|
|
[[ ! -s $mise_log ]] || fail "an official Cursor CLI install is left to itself by mise"
|
|
[[ $(<"$agent_file") == "cursor-agent" ]] || fail "an official Cursor CLI install becomes the default"
|
|
mapfile -d '' -t agent_open_args <"$agent_open_log"
|
|
[[ ${#agent_open_args[@]} == 1 && ${agent_open_args[0]} == "omarchy-agent" ]] ||
|
|
fail "an official Cursor CLI install opens after selection"
|
|
rm -f "$test_home/.local/bin/cursor-agent" "$test_home/.local/bin/cursor-agent.official"
|
|
printf '%s\n' copilot >"$agent_file"
|
|
pass "selecting an official Cursor CLI install skips mise"
|
|
|
|
# A file nothing can run is not an install; the wrapper is still wanted.
|
|
touch "$test_home/.local/bin/cursor-agent"
|
|
: >"$terminal_log"
|
|
omarchy-default-agent cursor-agent
|
|
mapfile -d '' -t terminal_args <"$terminal_log"
|
|
[[ ${terminal_args[*]} == "omarchy-default-agent --install cursor-agent" ]] ||
|
|
fail "a dead file at the wrapper's path still installs Cursor CLI"
|
|
rm -f "$test_home/.local/bin/cursor-agent"
|
|
pass "a dead file at the wrapper's path does not pass for an install"
|
|
|
|
: >"$agent_open_log"
|
|
if omarchy-default-agent unsupported >"$test_tmp/invalid-output" 2>&1; then
|
|
fail "default agent rejects unsupported providers"
|
|
fi
|
|
grep -F "Usage: omarchy-default-agent" "$test_tmp/invalid-output" >/dev/null ||
|
|
fail "default agent explains supported providers"
|
|
[[ $(omarchy-default-agent) == "copilot" ]] || fail "invalid selection preserves the current default agent"
|
|
[[ ! -s $agent_open_log ]] || fail "invalid selection does not open an agent"
|
|
pass "default agent rejects unsupported providers without changing the selection"
|
|
|
|
: >"$notification_history"
|
|
: >"$agent_open_log"
|
|
if OMARCHY_TEST_MISE_FAIL=true omarchy-default-agent --install codex >"$test_tmp/install-failure-output" 2>&1; then
|
|
fail "default agent rejects a failed mise installation"
|
|
fi
|
|
[[ $(omarchy-default-agent) == "copilot" ]] || fail "failed installation preserves the current default agent"
|
|
grep -F "Could not install Codex with mise" "$test_tmp/install-failure-output" >/dev/null ||
|
|
fail "default agent reports a failed mise installation in the terminal"
|
|
[[ ! -s $notification_history ]] || fail "failed visible agent installation skips notifications"
|
|
[[ ! -s $agent_open_log ]] || fail "failed installation does not open an agent"
|
|
pass "default agent opens only after mise installs the provider"
|
|
|
|
: >"$notification_history"
|
|
: >"$agent_open_log"
|
|
if OMARCHY_TEST_AGENT_INSTALLED=true OMARCHY_TEST_MISE_FAIL=true omarchy-default-agent codex >"$test_tmp/setup-failure-output" 2>&1; then
|
|
fail "default agent rejects a failed mise activation"
|
|
fi
|
|
[[ $(omarchy-default-agent) == "copilot" ]] || fail "failed activation preserves the current default agent"
|
|
grep -F "Could not set Codex as the default coding agent" "$test_tmp/setup-failure-output" >/dev/null ||
|
|
fail "default agent reports a failed activation for an installed provider"
|
|
[[ ! -s $notification_history ]] || fail "failed activation skips notifications"
|
|
[[ ! -s $agent_open_log ]] || fail "failed activation does not open an agent"
|
|
pass "default agent reports mise failures without notifications"
|
|
|
|
rm "$mock_bin/omarchy-agent"
|
|
hash -r
|
|
|
|
assert_launched() {
|
|
local agent=$1
|
|
local description=$2
|
|
shift 2
|
|
# Every agent window launches under the same app-id, whichever agent is
|
|
# default, so window rules and themes see one class for all of them.
|
|
local expected=(--app-id=org.omarchy.agent "$@")
|
|
|
|
mapfile -d '' -t actual <"$launch_log"
|
|
|
|
(( ${#actual[@]} == ${#expected[@]} )) ||
|
|
fail "$agent launch $description" "expected: ${expected[*]}\nactual: ${actual[*]}"
|
|
|
|
for ((index = 0; index < ${#expected[@]}; index++)); do
|
|
case ${actual[$index]} in
|
|
"${expected[$index]}") ;;
|
|
*) fail "$agent launch $description" "expected: ${expected[*]}\nactual: ${actual[*]}" ;;
|
|
esac
|
|
done
|
|
}
|
|
|
|
assert_launch() {
|
|
local agent=$1
|
|
shift
|
|
|
|
printf '%s\n' "$agent" >"$agent_file"
|
|
omarchy-agent-prompt "Review this" project
|
|
assert_launched "$agent" "forwards the interactive prompt" "$@"
|
|
}
|
|
|
|
assert_bypass() {
|
|
local agent=$1
|
|
shift
|
|
|
|
printf '%s\n' "$agent" >"$agent_file"
|
|
omarchy-agent
|
|
assert_launched "$agent" "skips permission prompts" "$@"
|
|
}
|
|
|
|
assert_launch pi pi "Review this project"
|
|
assert_launch omp omp --auto-approve -- "Review this project"
|
|
assert_launch opencode opencode --auto --prompt "Review this project"
|
|
assert_launch ori ori code --interactive --prompt "Review this project"
|
|
assert_launch claude claude --permission-mode auto -- "Review this project"
|
|
assert_launch codex codex --approve-for-me -- "Review this project"
|
|
assert_launch crush crush run "Review this project"
|
|
assert_launch grok grok --permission-mode bypassPermissions -- "Review this project"
|
|
assert_launch cursor-agent cursor-agent --yolo --trust agent -- "Review this project"
|
|
assert_launch hermes env -u HERMES_SESSION_SOURCE hermes chat --yolo --tui "--query=Review this project"
|
|
assert_launch agy agy --dangerously-skip-permissions --prompt-interactive "Review this project"
|
|
assert_launch copilot copilot --allow-all --interactive "Review this project"
|
|
pass "agent launcher adapts initial prompts for every supported agent"
|
|
|
|
literal_hermes_prompt=$' --help !Crash /quit {$(touch must-not-run)}\ntrailing\\ '
|
|
printf '%s\n' "hermes" >"$agent_file"
|
|
omarchy-agent-prompt "$literal_hermes_prompt"
|
|
assert_launched hermes "binds its literal initial prompt" env -u HERMES_SESSION_SOURCE \
|
|
hermes chat --yolo --tui "--query=$literal_hermes_prompt"
|
|
pass "Hermes receives prompted launches as one literal query argument"
|
|
|
|
assert_bypass pi pi
|
|
assert_bypass omp omp --auto-approve
|
|
assert_bypass opencode opencode --auto
|
|
assert_bypass ori ori code
|
|
assert_bypass claude claude --permission-mode auto
|
|
assert_bypass codex codex --approve-for-me
|
|
assert_bypass crush crush --yolo
|
|
assert_bypass grok grok --permission-mode bypassPermissions
|
|
assert_bypass cursor-agent cursor-agent --yolo --trust
|
|
assert_bypass hermes hermes --yolo
|
|
assert_bypass agy agy --dangerously-skip-permissions
|
|
assert_bypass copilot copilot --allow-all
|
|
pass "agent launcher skips permission prompts for every supported agent"
|
|
|
|
printf '%s\n' "opencode" >"$agent_file"
|
|
omarchy-agent
|
|
mapfile -d '' -t launch_args <"$launch_log"
|
|
[[ ${launch_args[*]} == "--app-id=org.omarchy.agent opencode --auto" ]] ||
|
|
fail "agent launcher starts the selected agent without an initial prompt"
|
|
pass "agent launcher starts the selected agent without an initial prompt"
|
|
|
|
omarchy-agent-prompt --inline "Review this project"
|
|
mapfile -d '' -t inline_args <"$inline_log"
|
|
[[ ${inline_args[*]} == "opencode --auto --prompt Review this project" ]] ||
|
|
fail "inline agent launcher runs in the current terminal"
|
|
pass "inline agent launcher runs in the current terminal"
|
|
|
|
# The prompt route exists so the router can tell a prompt from a subcommand, so
|
|
# cover the public routes and not only the binaries behind them.
|
|
: >"$launch_log"
|
|
omarchy agent
|
|
mapfile -d '' -t launch_args <"$launch_log"
|
|
[[ ${launch_args[*]} == "--app-id=org.omarchy.agent opencode --auto" ]] ||
|
|
fail "omarchy agent routes to the launcher"
|
|
|
|
# With an agent chosen there is nothing to pick, so the keybinding launches.
|
|
: >"$launch_log"
|
|
: >"$menu_log"
|
|
omarchy-agent --pick
|
|
mapfile -d '' -t launch_args <"$launch_log"
|
|
[[ ${launch_args[*]} == "--app-id=org.omarchy.agent opencode --auto" ]] ||
|
|
fail "--pick launches once an agent is chosen"
|
|
[[ ! -s $menu_log ]] || fail "--pick opens no menu once an agent is chosen"
|
|
pass "--pick launches once an agent is chosen"
|
|
|
|
: >"$launch_log"
|
|
omarchy agent prompt "Review this project"
|
|
mapfile -d '' -t launch_args <"$launch_log"
|
|
[[ ${launch_args[*]} == "--app-id=org.omarchy.agent opencode --auto --prompt Review this project" ]] ||
|
|
fail "omarchy agent prompt routes the prompt to the launcher"
|
|
|
|
: >"$launch_log"
|
|
if omarchy agent Review this project >"$test_tmp/positional-output" 2>&1; then
|
|
fail "omarchy agent rejects a positional prompt"
|
|
fi
|
|
grep -F "omarchy agent prompt" "$test_tmp/positional-output" >/dev/null ||
|
|
fail "omarchy agent points a positional prompt at the prompt route"
|
|
[[ ! -s $launch_log ]] || fail "omarchy agent starts nothing for a positional prompt"
|
|
pass "omarchy agent keeps prompts on the prompt route"
|
|
|
|
printf '%s\n' "missing" >"$agent_file"
|
|
if OMARCHY_TEST_MISSING_COMMAND=missing omarchy-agent >"$test_tmp/missing-output" 2>&1; then
|
|
fail "agent launcher rejects a missing default command"
|
|
fi
|
|
grep -F "missing is not installed" "$test_tmp/missing-output" >/dev/null ||
|
|
fail "agent launcher explains when the default command is missing"
|
|
pass "agent launcher reports a missing default command"
|
|
|
|
# OpenClaw comes from its pacman package, not mise: choosing it must route
|
|
# through omarchy-install-openclaw-cli and never touch a mise environment.
|
|
cat >"$mock_bin/omarchy-pkg-present" <<'SH'
|
|
#!/bin/bash
|
|
[[ $1 == openclaw && ${OMARCHY_TEST_OPENCLAW_INSTALLED:-false} == "true" ]]
|
|
SH
|
|
cat >"$mock_bin/omarchy-pkg-add" <<'SH'
|
|
#!/bin/bash
|
|
printf '%s\n' "pkg-add $*" >>"$OMARCHY_TEST_STUB_LOG"
|
|
SH
|
|
cat >"$mock_bin/omarchy-launch-openclaw" <<'SH'
|
|
#!/bin/bash
|
|
printf '%s\0' omarchy-launch-openclaw "$@" >"$OMARCHY_TEST_AGENT_INLINE_LOG"
|
|
SH
|
|
cat >"$mock_bin/openclaw" <<'SH'
|
|
#!/bin/bash
|
|
exit 0
|
|
SH
|
|
chmod +x "$mock_bin/omarchy-pkg-present" "$mock_bin/omarchy-pkg-add" \
|
|
"$mock_bin/omarchy-launch-openclaw" "$mock_bin/openclaw"
|
|
|
|
: >"$launch_log"
|
|
: >"$terminal_log"
|
|
: >"$mise_history"
|
|
OMARCHY_TEST_OPENCLAW_INSTALLED=true omarchy-default-agent openclaw
|
|
read -r chosen <"$agent_file"
|
|
[[ $chosen == openclaw ]] || fail "choosing OpenClaw records it as the default agent"
|
|
mapfile -d '' -t launch_args <"$launch_log"
|
|
[[ ${launch_args[*]} == "--app-id=org.omarchy.agent omarchy-launch-openclaw --tui" ]] ||
|
|
fail "choosing OpenClaw launches its terminal UI"
|
|
[[ ! -s $terminal_log ]] || fail "an installed OpenClaw needs no install terminal"
|
|
! grep -q 'use -g openclaw' "$mise_history" || fail "OpenClaw never installs through mise"
|
|
pass "choosing OpenClaw uses the package and launches its terminal UI"
|
|
|
|
: >"$terminal_log"
|
|
OMARCHY_TEST_OPENCLAW_INSTALLED=false omarchy-default-agent openclaw
|
|
mapfile -d '' -t terminal_args <"$terminal_log"
|
|
[[ ${terminal_args[*]} == "omarchy-default-agent --install openclaw" ]] ||
|
|
fail "a missing OpenClaw routes through the install terminal"
|
|
pass "a missing OpenClaw routes through the install terminal"
|
|
|
|
: >"$stub_log"
|
|
: >"$inline_log"
|
|
OMARCHY_TEST_OPENCLAW_INSTALLED=false omarchy-default-agent --install openclaw >/dev/null
|
|
grep -Fx "pkg-add openclaw" "$stub_log" >/dev/null ||
|
|
fail "installing OpenClaw as default agent adds its package"
|
|
mapfile -d '' -t inline_args <"$inline_log"
|
|
[[ ${inline_args[*]} == "omarchy-launch-openclaw --tui" ]] ||
|
|
fail "installing OpenClaw as default agent hands over to its terminal UI"
|
|
pass "installing OpenClaw as default agent adds its package"
|
|
|
|
: >"$launch_log"
|
|
omarchy agent prompt "Review this project"
|
|
mapfile -d '' -t launch_args <"$launch_log"
|
|
# Element-wise: the prompt must travel as one argv entry, which a space-joined
|
|
# comparison could not tell apart from a prompt split into words.
|
|
[[ ${#launch_args[@]} == 5 &&
|
|
${launch_args[0]} == "--app-id=org.omarchy.agent" &&
|
|
${launch_args[1]} == "omarchy-launch-openclaw" &&
|
|
${launch_args[2]} == "--tui" &&
|
|
${launch_args[3]} == "--message" &&
|
|
${launch_args[4]} == "Review this project" ]] ||
|
|
fail "OpenClaw receives prompts through --message" "argv: ${launch_args[*]}"
|
|
pass "OpenClaw receives prompts through --message"
|