Reshape the agent launcher into omarchy agent (#6757)

* Reshape the agent launcher into omarchy agent

omarchy-launch-agent becomes omarchy-agent, with prompts on omarchy-agent-prompt
rather than the bare route: `omarchy agent` is both a command and a group, so a
positional prompt there would shadow any subcommand under it. The launcher takes
flags only and points at `omarchy agent prompt` when handed one.

Every agent window now launches under a fixed org.omarchy.agent app-id instead of
omarchy-launch-tui's default of org.omarchy.<binary>, so one rule floats them all
whichever agent is default.

Omarchy also stops picking an agent for you. omarchy-default-agent prints nothing
until one is chosen, leaving every entry under Setup > Defaults > Agent unchecked,
and a first-run invitation offers to take you there.

* Wordsmith

* Cover the agent routes and the invitation

The route split is the point of the change, so exercise `omarchy agent`,
`omarchy agent prompt`, and a rejected positional prompt through the router
rather than only the binaries behind them.

The invitation gets the same treatment as the Voxtype and fingerprint ones: it
notifies once, opens the agent defaults menu, and leaves both the notification
and the marker alone for anyone who already chose an agent.

* Offer the agent choice from the keybinding

Super + Shift + Ctrl + A now runs `omarchy-agent --pick`, which opens Setup >
Defaults > Agent when nothing is chosen yet. A keypress that writes to stderr
and opens nothing just looks broken.

* Reach existing installs with the agent invitation

first-run installs the invitation hook, and existing accounts marked it complete
long ago, so they would never see it -- while being the accounts most likely to
need it, since the old getter returned opencode implicitly and most have no
agent recorded at all. Post-update hooks run later in the same update, so the
invitation arrives without waiting for another one.

* Say what the Defaults submenus set

Setup > Defaults lists Agent, Browser, Terminal, Editor, but the header inside
each repeated the same bare word, which reads as a category rather than a
setting -- and says nothing at all when the menu is summoned straight into it.
The list keeps its short labels; the headers now name the setting.
This commit is contained in:
David Heinemeier Hansson
2026-08-12 17:56:19 +02:00
committed by GitHub
parent b97a1480dc
commit 9502b81f3b
12 changed files with 240 additions and 45 deletions
+40 -13
View File
@@ -1,15 +1,35 @@
#!/bin/bash #!/bin/bash
# omarchy:summary=Launch the default coding agent in a terminal # omarchy:summary=Launch the default coding agent in a terminal
# omarchy:args=[--inline] [prompt...] # omarchy:args=[--inline] [--pick]
# omarchy:examples=omarchy launch agent | omarchy launch agent "Review this project" # omarchy:examples=omarchy agent | omarchy agent --inline
if [[ ${1:-} == "--inline" ]]; then inline=false
inline=true pick=false
shift
else # Flags only. A bare prompt would shadow the subcommands under this group, so
inline=false # prompts go through omarchy-agent-prompt, which passes one here as --prompt.
fi while (($#)); do
case "$1" in
--inline)
inline=true
shift
;;
--pick)
pick=true
shift
;;
--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 # 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. # menu start in the work directory instead of re-asking on every session.
@@ -17,13 +37,18 @@ fi
agent=$(omarchy-default-agent) agent=$(omarchy-default-agent)
if omarchy-cmd-missing "$agent"; then # Omarchy ships without a default, so there is nothing to launch until one is
echo "$agent is not installed. Choose an installed agent with: omarchy default agent <name>" >&2 # 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 exit 1
fi fi
if (($# > 0)); then if omarchy-cmd-missing "$agent"; then
prompt="$*" echo "$agent is not installed. Choose an installed agent with: omarchy default agent <name>" >&2
exit 1
fi fi
# Agents launched from the keybinding or menu run unattended, so each one starts # Agents launched from the keybinding or menu run unattended, so each one starts
@@ -74,5 +99,7 @@ esac
if [[ $inline == "true" ]]; then if [[ $inline == "true" ]]; then
exec "${command[@]}" exec "${command[@]}"
else else
exec omarchy-launch-tui "${command[@]}" # A fixed app-id rather than the default org.omarchy.<binary>, so every agent
# window shares one class for default/hypr/apps/agent.lua to float.
exec omarchy-launch-tui --app-id=org.omarchy.agent "${command[@]}"
fi fi
+23
View File
@@ -0,0 +1,23 @@
#!/bin/bash
# omarchy:summary=Launch the default coding agent with a prompt
# omarchy:args=[--inline] <prompt...>
# omarchy:examples=omarchy agent prompt "Review this project"
# Prompts live here rather than on `omarchy agent`, where a bare prompt would
# shadow the subcommands under that group.
set -euo pipefail
inline=()
if [[ ${1:-} == "--inline" ]]; then
inline=(--inline)
shift
fi
if (($# == 0)); then
echo "Usage: omarchy agent prompt [--inline] <prompt...>" >&2
exit 1
fi
exec omarchy-agent "${inline[@]}" --prompt "$*"
+5 -3
View File
@@ -17,7 +17,9 @@ if (($# == 0)); then
read -r agent <"$agent_file" read -r agent <"$agent_file"
fi fi
[[ -n $agent ]] && echo "$agent" || echo "opencode" # Silent when unset rather than defaulting: Omarchy picks no agent for you, so
# the menu leaves every entry unchecked until one is chosen.
[[ -n ${agent:-} ]] && echo "$agent"
exit 0 exit 0
fi fi
@@ -57,7 +59,7 @@ printf '%s\n' "$agent" >"$agent_file"
if [[ $installing == "true" ]]; then if [[ $installing == "true" ]]; then
printf '\033[2J\033[3J\033[H' printf '\033[2J\033[3J\033[H'
exec omarchy-launch-agent --inline exec omarchy-agent --inline
else else
exec omarchy-launch-agent exec omarchy-agent
fi fi
+2
View File
@@ -72,6 +72,8 @@ run_first_run_step "install Voxtype post-update hook" \
omarchy-hook-install post-update "$OMARCHY_PATH/install/user/first-run/install-voxtype.hook" omarchy-hook-install post-update "$OMARCHY_PATH/install/user/first-run/install-voxtype.hook"
run_first_run_step "install fingerprint setup post-update hook" \ run_first_run_step "install fingerprint setup post-update hook" \
omarchy-hook-install post-update "$OMARCHY_PATH/install/user/first-run/setup-fingerprint.hook" omarchy-hook-install post-update "$OMARCHY_PATH/install/user/first-run/setup-fingerprint.hook"
run_first_run_step "install agent setup post-update hook" \
omarchy-hook-install post-update "$OMARCHY_PATH/install/user/first-run/setup-agent.hook"
run_first_run_step "enable user systemd units" \ run_first_run_step "enable user systemd units" \
bash "$OMARCHY_PATH/install/user/first-run/enable-user-units.sh" bash "$OMARCHY_PATH/install/user/first-run/enable-user-units.sh"
+1 -1
View File
@@ -43,7 +43,7 @@ alias ...='cd ../..'
alias ....='cd ../../..' alias ....='cd ../../..'
# Tools # Tools
alias a='omarchy-launch-agent --inline' alias a='omarchy-agent --inline'
alias c='opencode --auto' alias c='opencode --auto'
alias cx='printf "\033[2J\033[3J\033[H" && claude --permission-mode bypassPermissions' alias cx='printf "\033[2J\033[3J\033[H" && claude --permission-mode bypassPermissions'
alias cy='codex -s danger-full-access -a never' alias cy='codex -s danger-full-access -a never'
+3
View File
@@ -0,0 +1,3 @@
-- omarchy-agent gives every agent terminal the same app-id, so this
-- floats the keybinding, the menu, and a crash diagnosis alike.
o.window("org\\.omarchy\\.agent", { float = true, center = true, size = { 1200, 800 } })
+1 -1
View File
@@ -93,7 +93,7 @@ o.bind("SUPER + CTRL + ALT + T", "Show time", "omarchy-notification-time")
o.bind("SUPER + CTRL + ALT + B", "Show battery remaining", "omarchy-notification-battery") o.bind("SUPER + CTRL + ALT + B", "Show battery remaining", "omarchy-notification-battery")
o.bind("SUPER + CTRL + ALT + W", "Toggle weather", "omarchy-notification-weather") o.bind("SUPER + CTRL + ALT + W", "Toggle weather", "omarchy-notification-weather")
o.bind("SUPER + SHIFT + CTRL + A", "Agent", "omarchy-launch-agent") o.bind("SUPER + SHIFT + CTRL + A", "Agent", "omarchy-agent --pick")
o.bind("SUPER + CTRL + A", "Audio", "omarchy-shell shell toggle omarchy.audio") o.bind("SUPER + CTRL + A", "Audio", "omarchy-shell shell toggle omarchy.audio")
o.bind("SUPER + CTRL + B", "Bluetooth", "omarchy-shell shell toggle omarchy.bluetooth") o.bind("SUPER + CTRL + B", "Bluetooth", "omarchy-shell shell toggle omarchy.bluetooth")
o.bind("SUPER + CTRL + D", "Display", "omarchy-shell shell toggle omarchy.monitor") o.bind("SUPER + CTRL + D", "Display", "omarchy-shell shell toggle omarchy.monitor")
+4 -4
View File
@@ -128,7 +128,7 @@
"setup.network.dns.custom": {"icon":"","label":"Custom","checked":"[[ \"$(omarchy-dns)\" == \"Custom\" ]]","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-dns Custom'"}, "setup.network.dns.custom": {"icon":"","label":"Custom","checked":"[[ \"$(omarchy-dns)\" == \"Custom\" ]]","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-dns Custom'"},
"setup.network.qr": {"icon":"󰐲","label":"QR Code","aliases":["wifi-qr"],"when":"[[ $(omarchy-network-status) == wifi* ]]","action":"omarchy-shell shell summon omarchy.wifiqr"}, "setup.network.qr": {"icon":"󰐲","label":"QR Code","aliases":["wifi-qr"],"when":"[[ $(omarchy-network-status) == wifi* ]]","action":"omarchy-shell shell summon omarchy.wifiqr"},
"setup.default": {"icon":"","label":"Defaults","aliases":["default","defaults"]}, "setup.default": {"icon":"","label":"Defaults","aliases":["default","defaults"]},
"setup.default.agent": {"icon":"󰚩","label":"Agent"}, "setup.default.agent": {"icon":"󰚩","label":"Agent","title":"Default Agent"},
"setup.default.agent.claude": {"icon":"󰛄","label":"Claude","checked":"[[ \"$(omarchy-default-agent)\" == \"claude\" ]]","action":"omarchy-default-agent claude"}, "setup.default.agent.claude": {"icon":"󰛄","label":"Claude","checked":"[[ \"$(omarchy-default-agent)\" == \"claude\" ]]","action":"omarchy-default-agent claude"},
"setup.default.agent.codex": {"icon":"","iconFont":"omarchy","label":"Codex","checked":"[[ \"$(omarchy-default-agent)\" == \"codex\" ]]","action":"omarchy-default-agent codex"}, "setup.default.agent.codex": {"icon":"","iconFont":"omarchy","label":"Codex","checked":"[[ \"$(omarchy-default-agent)\" == \"codex\" ]]","action":"omarchy-default-agent codex"},
"setup.default.agent.copilot": {"icon":"","label":"Copilot","checked":"[[ \"$(omarchy-default-agent)\" == \"copilot\" ]]","action":"omarchy-default-agent copilot"}, "setup.default.agent.copilot": {"icon":"","label":"Copilot","checked":"[[ \"$(omarchy-default-agent)\" == \"copilot\" ]]","action":"omarchy-default-agent copilot"},
@@ -138,7 +138,7 @@
"setup.default.agent.omp": {"icon":"","iconFont":"omarchy","label":"omp","checked":"[[ \"$(omarchy-default-agent)\" == \"omp\" ]]","action":"omarchy-default-agent omp"}, "setup.default.agent.omp": {"icon":"","iconFont":"omarchy","label":"omp","checked":"[[ \"$(omarchy-default-agent)\" == \"omp\" ]]","action":"omarchy-default-agent omp"},
"setup.default.agent.opencode": {"icon":"","iconFont":"omarchy","label":"OpenCode","checked":"[[ \"$(omarchy-default-agent)\" == \"opencode\" ]]","action":"omarchy-default-agent opencode"}, "setup.default.agent.opencode": {"icon":"","iconFont":"omarchy","label":"OpenCode","checked":"[[ \"$(omarchy-default-agent)\" == \"opencode\" ]]","action":"omarchy-default-agent opencode"},
"setup.default.agent.pi": {"icon":"","iconFont":"omarchy","label":"Pi","checked":"[[ \"$(omarchy-default-agent)\" == \"pi\" ]]","action":"omarchy-default-agent pi"}, "setup.default.agent.pi": {"icon":"","iconFont":"omarchy","label":"Pi","checked":"[[ \"$(omarchy-default-agent)\" == \"pi\" ]]","action":"omarchy-default-agent pi"},
"setup.default.browser": {"icon":"","label":"Browser"}, "setup.default.browser": {"icon":"","label":"Browser","title":"Default Browser"},
"setup.default.browser.chromium": {"icon":"","label":"Chromium","when":"omarchy-cmd-present chromium","checked":"[[ \"$(omarchy-default-browser)\" == \"chromium\" ]]","action":"omarchy-default-browser chromium"}, "setup.default.browser.chromium": {"icon":"","label":"Chromium","when":"omarchy-cmd-present chromium","checked":"[[ \"$(omarchy-default-browser)\" == \"chromium\" ]]","action":"omarchy-default-browser chromium"},
"setup.default.browser.chrome": {"icon":"󰊯","label":"Chrome","when":"omarchy-cmd-present google-chrome-stable","checked":"[[ \"$(omarchy-default-browser)\" == \"chrome\" ]]","action":"omarchy-default-browser chrome"}, "setup.default.browser.chrome": {"icon":"󰊯","label":"Chrome","when":"omarchy-cmd-present google-chrome-stable","checked":"[[ \"$(omarchy-default-browser)\" == \"chrome\" ]]","action":"omarchy-default-browser chrome"},
"setup.default.browser.brave": {"icon":"󰖟","label":"Brave","when":"omarchy-cmd-present brave","checked":"[[ \"$(omarchy-default-browser)\" == \"brave\" ]]","action":"omarchy-default-browser brave"}, "setup.default.browser.brave": {"icon":"󰖟","label":"Brave","when":"omarchy-cmd-present brave","checked":"[[ \"$(omarchy-default-browser)\" == \"brave\" ]]","action":"omarchy-default-browser brave"},
@@ -146,12 +146,12 @@
"setup.default.browser.edge": {"icon":"󰇩","label":"Edge","when":"omarchy-cmd-present microsoft-edge-stable","checked":"[[ \"$(omarchy-default-browser)\" == \"edge\" ]]","action":"omarchy-default-browser edge"}, "setup.default.browser.edge": {"icon":"󰇩","label":"Edge","when":"omarchy-cmd-present microsoft-edge-stable","checked":"[[ \"$(omarchy-default-browser)\" == \"edge\" ]]","action":"omarchy-default-browser edge"},
"setup.default.browser.firefox": {"icon":"","label":"Firefox","when":"omarchy-cmd-present firefox","checked":"[[ \"$(omarchy-default-browser)\" == \"firefox\" ]]","action":"omarchy-default-browser firefox"}, "setup.default.browser.firefox": {"icon":"","label":"Firefox","when":"omarchy-cmd-present firefox","checked":"[[ \"$(omarchy-default-browser)\" == \"firefox\" ]]","action":"omarchy-default-browser firefox"},
"setup.default.browser.zen": {"icon":"󰖟","label":"Zen","when":"omarchy-cmd-present zen-browser","checked":"[[ \"$(omarchy-default-browser)\" == \"zen\" ]]","action":"omarchy-default-browser zen"}, "setup.default.browser.zen": {"icon":"󰖟","label":"Zen","when":"omarchy-cmd-present zen-browser","checked":"[[ \"$(omarchy-default-browser)\" == \"zen\" ]]","action":"omarchy-default-browser zen"},
"setup.default.terminal": {"icon":"","label":"Terminal"}, "setup.default.terminal": {"icon":"","label":"Terminal","title":"Default Terminal"},
"setup.default.terminal.alacritty": {"icon":"","label":"Alacritty","when":"omarchy-cmd-present alacritty","checked":"[[ \"$(omarchy-default-terminal)\" == \"alacritty\" ]]","action":"omarchy-default-terminal alacritty"}, "setup.default.terminal.alacritty": {"icon":"","label":"Alacritty","when":"omarchy-cmd-present alacritty","checked":"[[ \"$(omarchy-default-terminal)\" == \"alacritty\" ]]","action":"omarchy-default-terminal alacritty"},
"setup.default.terminal.foot": {"icon":"","label":"Foot","when":"omarchy-cmd-present foot","checked":"[[ \"$(omarchy-default-terminal)\" == \"foot\" ]]","action":"omarchy-default-terminal foot"}, "setup.default.terminal.foot": {"icon":"","label":"Foot","when":"omarchy-cmd-present foot","checked":"[[ \"$(omarchy-default-terminal)\" == \"foot\" ]]","action":"omarchy-default-terminal foot"},
"setup.default.terminal.ghostty": {"icon":"","label":"Ghostty","when":"omarchy-cmd-present ghostty","checked":"[[ \"$(omarchy-default-terminal)\" == \"ghostty\" ]]","action":"omarchy-default-terminal ghostty"}, "setup.default.terminal.ghostty": {"icon":"","label":"Ghostty","when":"omarchy-cmd-present ghostty","checked":"[[ \"$(omarchy-default-terminal)\" == \"ghostty\" ]]","action":"omarchy-default-terminal ghostty"},
"setup.default.terminal.kitty": {"icon":"","label":"Kitty","when":"omarchy-cmd-present kitty","checked":"[[ \"$(omarchy-default-terminal)\" == \"kitty\" ]]","action":"omarchy-default-terminal kitty"}, "setup.default.terminal.kitty": {"icon":"","label":"Kitty","when":"omarchy-cmd-present kitty","checked":"[[ \"$(omarchy-default-terminal)\" == \"kitty\" ]]","action":"omarchy-default-terminal kitty"},
"setup.default.editor": {"icon":"","label":"Editor"}, "setup.default.editor": {"icon":"","label":"Editor","title":"Default Editor"},
"setup.default.editor.neovim": {"icon":"","label":"Neovim","when":"omarchy-cmd-present nvim","checked":"[[ \"$(omarchy-default-editor)\" == \"nvim\" ]]","action":"omarchy-default-editor nvim"}, "setup.default.editor.neovim": {"icon":"","label":"Neovim","when":"omarchy-cmd-present nvim","checked":"[[ \"$(omarchy-default-editor)\" == \"nvim\" ]]","action":"omarchy-default-editor nvim"},
"setup.default.editor.vscode": {"icon":"","label":"VSCode","when":"omarchy-cmd-present code","checked":"[[ \"$(omarchy-default-editor)\" == \"code\" ]]","action":"omarchy-default-editor code"}, "setup.default.editor.vscode": {"icon":"","label":"VSCode","when":"omarchy-cmd-present code","checked":"[[ \"$(omarchy-default-editor)\" == \"code\" ]]","action":"omarchy-default-editor code"},
"setup.default.editor.cursor": {"icon":"","label":"Cursor","when":"omarchy-cmd-present cursor","checked":"[[ \"$(omarchy-default-editor)\" == \"cursor\" ]]","action":"omarchy-default-editor cursor"}, "setup.default.editor.cursor": {"icon":"","label":"Cursor","when":"omarchy-cmd-present cursor","checked":"[[ \"$(omarchy-default-editor)\" == \"cursor\" ]]","action":"omarchy-default-editor cursor"},
+11
View File
@@ -0,0 +1,11 @@
#!/bin/bash
set -e
# Omarchy ships no default agent, so invite once rather than picking one. An
# agent already chosen means the invitation has nothing to offer.
if [[ -z $(omarchy-default-agent) ]] && omarchy-done ensure agent-setup-invitation; then
omarchy-notification-send -u critical -g 󰚩 "Set your default agent" \
"Let your favorite agent help with Omarchy." \
--exec "omarchy menu summon setup.default.agent"
fi
+10
View File
@@ -0,0 +1,10 @@
echo "Invite existing installs to pick a default agent"
# The invitation is installed by first-run, which existing accounts have already
# marked complete, so they would never receive it. They are also the accounts
# most likely to need it: the old getter returned opencode implicitly, so most
# have no agent recorded at all.
#
# Post-update hooks run later in this same update, so the invitation appears
# without waiting for another one. The hook decides whether to notify.
omarchy-hook-install post-update "$OMARCHY_PATH/install/user/first-run/setup-agent.hook"
+61
View File
@@ -0,0 +1,61 @@
#!/bin/bash
source "$(dirname "$0")/base-test.sh"
test_home=$(mktemp -d)
test_bin=$(mktemp -d)
log_file=$(mktemp)
hook_path="$test_home/.config/omarchy/hooks/post-update.d/setup-agent.hook"
cleanup() {
rm -rf "$test_home" "$test_bin"
rm -f "$log_file"
}
trap cleanup EXIT
mkdir -p "$(dirname "$hook_path")"
cat >"$test_bin/omarchy-notification-send" <<'EOF'
#!/bin/bash
echo notification >>"$TEST_LOG"
while (($# > 0)); do
[[ $1 == "--exec" ]] && echo "exec:$2" >>"$TEST_LOG"
shift
done
EOF
chmod +x "$test_bin/omarchy-notification-send"
run_invitation_hook() {
cp "$ROOT/install/user/first-run/setup-agent.hook" "$hook_path"
HOME="$test_home" PATH="$test_bin:$ROOT/bin:$PATH" TEST_LOG="$log_file" bash "$hook_path"
}
run_invitation_hook
[[ -f $test_home/.local/state/omarchy/done/agent-setup-invitation ]] || fail "agent invitation records completion"
[[ -f $hook_path ]] || fail "agent invitation keeps its hook installed"
[[ $(grep -c '^notification$' "$log_file") -eq 1 ]] || fail "agent invitation sends one notification"
grep -qx 'exec:omarchy menu summon setup.default.agent' "$log_file" ||
fail "agent invitation opens the agent defaults menu"
run_invitation_hook
[[ $(grep -c '^notification$' "$log_file") -eq 1 ]] || fail "completed agent invitation does not notify again"
pass "agent invitation only runs once"
# Someone who already chose an agent has nothing to be invited to, and must not
# burn the marker either -- otherwise clearing the choice later leaves them with
# no invitation and no default.
fresh_home=$(mktemp -d)
mkdir -p "$fresh_home/.config/omarchy/defaults"
printf 'claude\n' >"$fresh_home/.config/omarchy/defaults/agent"
: >"$log_file"
cp "$ROOT/install/user/first-run/setup-agent.hook" "$hook_path"
HOME="$fresh_home" PATH="$test_bin:$ROOT/bin:$PATH" TEST_LOG="$log_file" bash "$hook_path"
[[ ! -s $log_file ]] || fail "agent invitation stays quiet when a default is already set"
[[ ! -f $fresh_home/.local/state/omarchy/done/agent-setup-invitation ]] ||
fail "agent invitation leaves its marker unset when a default is already set"
rm -rf "$fresh_home"
pass "agent invitation skips anyone who already chose an agent"
+79 -23
View File
@@ -18,6 +18,7 @@ mise_log="$test_tmp/mise"
mise_history="$test_tmp/mise-history" mise_history="$test_tmp/mise-history"
stub_log="$test_tmp/stubs" stub_log="$test_tmp/stubs"
terminal_log="$test_tmp/terminal" terminal_log="$test_tmp/terminal"
menu_log="$test_tmp/menu"
mkdir -p "$mock_bin" "$test_home" mkdir -p "$mock_bin" "$test_home"
cat >"$mock_bin/omarchy-notification-send" <<'SH' cat >"$mock_bin/omarchy-notification-send" <<'SH'
@@ -63,6 +64,11 @@ fi
[[ ${OMARCHY_TEST_MISE_FAIL:-false} != "true" ]] [[ ${OMARCHY_TEST_MISE_FAIL:-false} != "true" ]]
SH 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' cat >"$mock_bin/omarchy-test-noop" <<'SH'
#!/bin/bash #!/bin/bash
exit 0 exit 0
@@ -84,6 +90,7 @@ export OMARCHY_TEST_MISE_LOG="$mise_log"
export OMARCHY_TEST_MISE_HISTORY="$mise_history" export OMARCHY_TEST_MISE_HISTORY="$mise_history"
export OMARCHY_TEST_STUB_LOG="$stub_log" export OMARCHY_TEST_STUB_LOG="$stub_log"
export OMARCHY_TEST_AGENT_TERMINAL_LOG="$terminal_log" export OMARCHY_TEST_AGENT_TERMINAL_LOG="$terminal_log"
export OMARCHY_TEST_AGENT_MENU_LOG="$menu_log"
grok_package="npm:@xai-official/grok" grok_package="npm:@xai-official/grok"
omp_package="github:can1357/oh-my-pi" omp_package="github:can1357/oh-my-pi"
@@ -140,30 +147,44 @@ for command in omp grok crush; do
done done
pass "Remove Preinstalls deletes every optional agent lazy stub" pass "Remove Preinstalls deletes every optional agent lazy stub"
[[ $(omarchy-default-agent) == "opencode" ]] || fail "default agent falls back to OpenCode" [[ -z $(omarchy-default-agent) ]] || fail "default agent is unset until one is chosen"
pass "default agent falls back to OpenCode" pass "default agent is unset until one is chosen"
omarchy-launch-agent : >"$launch_log"
mapfile -d '' -t launch_args <"$launch_log" if omarchy-agent >"$test_tmp/no-agent-output" 2>&1; then
[[ ${launch_args[*]} == "opencode --auto" ]] || fail "agent launcher refuses to launch without a default"
fail "agent launcher falls back to OpenCode before a default is selected" fi
pass "agent launcher falls back to OpenCode before a default is selected" 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" source "$ROOT/default/bash/aliases"
[[ $(alias a) == "alias a='omarchy-launch-agent --inline'" ]] || [[ $(alias a) == "alias a='omarchy-agent --inline'" ]] ||
fail "terminal alias launches the default agent inline" fail "terminal alias launches the default agent inline"
pass "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-launch-agent")' \ grep -Fq 'o.bind("SUPER + SHIFT + CTRL + A", "Agent", "omarchy-agent --pick")' \
"$ROOT/default/hypr/bindings/utilities.lua" || "$ROOT/default/hypr/bindings/utilities.lua" ||
fail "agent launcher has a keyboard shortcut" fail "agent launcher has a keyboard shortcut"
pass "agent launcher has a keyboard shortcut" pass "agent launcher has a keyboard shortcut"
cat >"$mock_bin/omarchy-launch-agent" <<'SH' cat >"$mock_bin/omarchy-agent" <<'SH'
#!/bin/bash #!/bin/bash
printf '%s\0' omarchy-launch-agent "$@" >"$OMARCHY_TEST_AGENT_OPEN_LOG" printf '%s\0' omarchy-agent "$@" >"$OMARCHY_TEST_AGENT_OPEN_LOG"
SH SH
chmod +x "$mock_bin/omarchy-launch-agent" chmod +x "$mock_bin/omarchy-agent"
hash -r hash -r
declare -A expected_agents=( declare -A expected_agents=(
@@ -206,7 +227,7 @@ for selection in "${!expected_agents[@]}"; do
fail "default agent installs $selection globally through mise" fail "default agent installs $selection globally through mise"
mapfile -d '' -t agent_open_args <"$agent_open_log" mapfile -d '' -t agent_open_args <"$agent_open_log"
[[ ${#agent_open_args[@]} == 1 && ${agent_open_args[0]} == "omarchy-launch-agent" ]] || [[ ${#agent_open_args[@]} == 1 && ${agent_open_args[0]} == "omarchy-agent" ]] ||
fail "default agent opens $selection after selecting it" fail "default agent opens $selection after selecting it"
done done
pass "default agent selects and opens every supported provider and alias" pass "default agent selects and opens every supported provider and alias"
@@ -235,7 +256,7 @@ mapfile -d '' -t mise_args <"$mise_log"
[[ $(<"$test_tmp/install-output") == $'\033[2J\033[3J\033[H' ]] || [[ $(<"$test_tmp/install-output") == $'\033[2J\033[3J\033[H' ]] ||
fail "visible agent installation clears its terminal before opening the agent" fail "visible agent installation clears its terminal before opening the agent"
mapfile -d '' -t agent_open_args <"$agent_open_log" mapfile -d '' -t agent_open_args <"$agent_open_log"
[[ ${#agent_open_args[@]} == 2 && ${agent_open_args[0]} == "omarchy-launch-agent" && ${agent_open_args[1]} == "--inline" ]] || [[ ${#agent_open_args[@]} == 2 && ${agent_open_args[0]} == "omarchy-agent" && ${agent_open_args[1]} == "--inline" ]] ||
fail "newly installed agent opens in the installation terminal" fail "newly installed agent opens in the installation terminal"
pass "missing agents install visibly and open in the same terminal" pass "missing agents install visibly and open in the same terminal"
@@ -249,7 +270,7 @@ mapfile -d '' -t mise_args <"$mise_log"
[[ ${mise_args[0]} == "use" && ${mise_args[1]} == "-g" && ${mise_args[2]} == "copilot" ]] || [[ ${mise_args[0]} == "use" && ${mise_args[1]} == "-g" && ${mise_args[2]} == "copilot" ]] ||
fail "default agent still activates an installed provider globally through mise" fail "default agent still activates an installed provider globally through mise"
mapfile -d '' -t agent_open_args <"$agent_open_log" mapfile -d '' -t agent_open_args <"$agent_open_log"
[[ ${#agent_open_args[@]} == 1 && ${agent_open_args[0]} == "omarchy-launch-agent" ]] || [[ ${#agent_open_args[@]} == 1 && ${agent_open_args[0]} == "omarchy-agent" ]] ||
fail "installed agent opens in a new terminal after selection" fail "installed agent opens in a new terminal after selection"
pass "installed agents select and open without notifications" pass "installed agents select and open without notifications"
@@ -287,14 +308,16 @@ grep -F "Could not set Codex as the default coding agent" "$test_tmp/setup-failu
[[ ! -s $agent_open_log ]] || fail "failed activation does not open an agent" [[ ! -s $agent_open_log ]] || fail "failed activation does not open an agent"
pass "default agent reports mise failures without notifications" pass "default agent reports mise failures without notifications"
rm "$mock_bin/omarchy-launch-agent" rm "$mock_bin/omarchy-agent"
hash -r hash -r
assert_launched() { assert_launched() {
local agent=$1 local agent=$1
local description=$2 local description=$2
shift 2 shift 2
local expected=("$@") # Every agent window launches under the same app-id, whichever agent is
# default, so default/hypr/apps/agent.lua can float them all.
local expected=(--app-id=org.omarchy.agent "$@")
mapfile -d '' -t actual <"$launch_log" mapfile -d '' -t actual <"$launch_log"
@@ -312,7 +335,7 @@ assert_launch() {
shift shift
printf '%s\n' "$agent" >"$agent_file" printf '%s\n' "$agent" >"$agent_file"
omarchy-launch-agent "Review this" project omarchy-agent-prompt "Review this" project
assert_launched "$agent" "forwards the interactive prompt" "$@" assert_launched "$agent" "forwards the interactive prompt" "$@"
} }
@@ -321,7 +344,7 @@ assert_bypass() {
shift shift
printf '%s\n' "$agent" >"$agent_file" printf '%s\n' "$agent" >"$agent_file"
omarchy-launch-agent omarchy-agent
assert_launched "$agent" "skips permission prompts" "$@" assert_launched "$agent" "skips permission prompts" "$@"
} }
@@ -348,20 +371,53 @@ assert_bypass copilot copilot --allow-all
pass "agent launcher skips permission prompts for every supported agent" pass "agent launcher skips permission prompts for every supported agent"
printf '%s\n' "opencode" >"$agent_file" printf '%s\n' "opencode" >"$agent_file"
omarchy-launch-agent omarchy-agent
mapfile -d '' -t launch_args <"$launch_log" mapfile -d '' -t launch_args <"$launch_log"
[[ ${launch_args[*]} == "opencode --auto" ]] || [[ ${launch_args[*]} == "--app-id=org.omarchy.agent opencode --auto" ]] ||
fail "agent launcher starts the selected agent without an initial prompt" fail "agent launcher starts the selected agent without an initial prompt"
pass "agent launcher starts the selected agent without an initial prompt" pass "agent launcher starts the selected agent without an initial prompt"
omarchy-launch-agent --inline "Review this project" omarchy-agent-prompt --inline "Review this project"
mapfile -d '' -t inline_args <"$inline_log" mapfile -d '' -t inline_args <"$inline_log"
[[ ${inline_args[*]} == "opencode --auto --prompt Review this project" ]] || [[ ${inline_args[*]} == "opencode --auto --prompt Review this project" ]] ||
fail "inline agent launcher runs in the current terminal" fail "inline agent launcher runs in the current terminal"
pass "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" printf '%s\n' "missing" >"$agent_file"
if OMARCHY_TEST_MISSING_COMMAND=missing omarchy-launch-agent >"$test_tmp/missing-output" 2>&1; then if OMARCHY_TEST_MISSING_COMMAND=missing omarchy-agent >"$test_tmp/missing-output" 2>&1; then
fail "agent launcher rejects a missing default command" fail "agent launcher rejects a missing default command"
fi fi
grep -F "missing is not installed" "$test_tmp/missing-output" >/dev/null || grep -F "missing is not installed" "$test_tmp/missing-output" >/dev/null ||