diff --git a/bin/omarchy-default-agent b/bin/omarchy-default-agent index 7a4e8a27..cd6582c6 100755 --- a/bin/omarchy-default-agent +++ b/bin/omarchy-default-agent @@ -1,7 +1,7 @@ #!/bin/bash # omarchy:summary=Set the default coding agent used by omarchy-launch-agent -# omarchy:args=[pi|omp|opencode|claude|codex|grok|gemini|copilot] +# omarchy:args=[pi|omp|opencode|claude|codex|grok|gemini|copilot|crush] # omarchy:examples=omarchy default agent | omarchy default agent codex | omarchy default agent claude agent_file="$HOME/.local/state/omarchy/defaults/agent" @@ -21,11 +21,12 @@ omp | oh-my-pi) agent="omp"; name="Oh My Pi"; agent_package="oh-my-pi" ;; opencode | open-code) agent="opencode"; name="OpenCode" ;; claude | claude-code) agent="claude"; name="Claude Code" ;; codex) agent="codex"; name="Codex" ;; +crush) agent="crush"; name="Crush" ;; grok) agent="grok"; name="Grok"; agent_package="npm:@xai-official/grok" ;; gemini | gemini-cli) agent="gemini"; name="Gemini" ;; copilot | github-copilot) agent="copilot"; name="GitHub Copilot" ;; *) - echo "Usage: omarchy-default-agent " + echo "Usage: omarchy-default-agent " exit 1 ;; esac diff --git a/bin/omarchy-launch-agent b/bin/omarchy-launch-agent index 54950bf6..aef81821 100755 --- a/bin/omarchy-launch-agent +++ b/bin/omarchy-launch-agent @@ -41,6 +41,13 @@ copilot) command=(copilot) fi ;; +crush) + if [[ -n ${prompt:-} ]]; then + command=(crush run "$prompt") + else + command=(crush) + fi + ;; pi | omp | claude | codex | grok) command=("$agent") [[ -n ${prompt:-} ]] && command+=(-- "$prompt") diff --git a/default/omarchy/omarchy-menu.jsonc b/default/omarchy/omarchy-menu.jsonc index 0c5d424c..cf901d2e 100644 --- a/default/omarchy/omarchy-menu.jsonc +++ b/default/omarchy/omarchy-menu.jsonc @@ -124,14 +124,15 @@ "setup.direct-boot": {"icon":"","label":"Direct Boot","action":"omarchy-launch-floating-terminal-with-presentation omarchy-setup-direct-boot"}, "setup.default": {"icon":"","label":"Defaults","aliases":["default","defaults"]}, "setup.default.agent": {"icon":"󰚩","label":"Agent"}, - "setup.default.agent.pi": {"icon":"󰚩","label":"Pi","checked":"[[ \"$(omarchy-default-agent)\" == \"pi\" ]]","action":"omarchy-default-agent pi"}, - "setup.default.agent.omp": {"icon":"󰚩","label":"Oh My Pi","checked":"[[ \"$(omarchy-default-agent)\" == \"omp\" ]]","action":"omarchy-default-agent omp"}, - "setup.default.agent.opencode": {"icon":"󰚩","label":"OpenCode","checked":"[[ \"$(omarchy-default-agent)\" == \"opencode\" ]]","action":"omarchy-default-agent opencode"}, - "setup.default.agent.claude": {"icon":"󰚩","label":"Claude Code","checked":"[[ \"$(omarchy-default-agent)\" == \"claude\" ]]","action":"omarchy-default-agent claude"}, - "setup.default.agent.codex": {"icon":"󰚩","label":"Codex","checked":"[[ \"$(omarchy-default-agent)\" == \"codex\" ]]","action":"omarchy-default-agent codex"}, - "setup.default.agent.grok": {"icon":"󰚩","label":"Grok","checked":"[[ \"$(omarchy-default-agent)\" == \"grok\" ]]","action":"omarchy-default-agent grok"}, - "setup.default.agent.gemini": {"icon":"󰚩","label":"Gemini","checked":"[[ \"$(omarchy-default-agent)\" == \"gemini\" ]]","action":"omarchy-default-agent gemini"}, - "setup.default.agent.copilot": {"icon":"󰚩","label":"GitHub Copilot","checked":"[[ \"$(omarchy-default-agent)\" == \"copilot\" ]]","action":"omarchy-default-agent copilot"}, + "setup.default.agent.pi": {"icon":"󰏿","label":"Pi","checked":"[[ \"$(omarchy-default-agent)\" == \"pi\" ]]","action":"omarchy-default-agent pi"}, + "setup.default.agent.omp": {"icon":"󰣪","label":"omp","checked":"[[ \"$(omarchy-default-agent)\" == \"omp\" ]]","action":"omarchy-default-agent omp"}, + "setup.default.agent.opencode": {"icon":"󰕪","label":"OpenCode","checked":"[[ \"$(omarchy-default-agent)\" == \"opencode\" ]]","action":"omarchy-default-agent opencode"}, + "setup.default.agent.claude": {"icon":"󰛄","label":"Claude","checked":"[[ \"$(omarchy-default-agent)\" == \"claude\" ]]","action":"omarchy-default-agent claude"}, + "setup.default.agent.codex": {"icon":"󰞵","label":"Codex","checked":"[[ \"$(omarchy-default-agent)\" == \"codex\" ]]","action":"omarchy-default-agent codex"}, + "setup.default.agent.grok": {"icon":"󰰱","label":"Grok","checked":"[[ \"$(omarchy-default-agent)\" == \"grok\" ]]","action":"omarchy-default-agent grok"}, + "setup.default.agent.gemini": {"icon":"󰫢","label":"Gemini","checked":"[[ \"$(omarchy-default-agent)\" == \"gemini\" ]]","action":"omarchy-default-agent gemini"}, + "setup.default.agent.copilot": {"icon":"","label":"Copilot","checked":"[[ \"$(omarchy-default-agent)\" == \"copilot\" ]]","action":"omarchy-default-agent copilot"}, + "setup.default.agent.crush": {"icon":"󰋑","label":"Crush","checked":"[[ \"$(omarchy-default-agent)\" == \"crush\" ]]","action":"omarchy-default-agent crush"}, "setup.default.browser": {"icon":"","label":"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.chrome": {"icon":"󰊯","label":"Chrome","when":"omarchy-cmd-present google-chrome-stable","checked":"[[ \"$(omarchy-default-browser)\" == \"chrome\" ]]","action":"omarchy-default-browser chrome"}, @@ -215,7 +216,6 @@ "install.ai.dictation": {"icon":"","label":"Dictation","when":"! omarchy-pkg-present voxtype-bin","action":"omarchy-launch-floating-terminal-with-presentation omarchy-voxtype-install"}, "install.ai.lm-studio": {"icon":"󱚤","label":"LM Studio","when":"! omarchy-pkg-present lmstudio-bin","action":"omarchy-install-app 'LM Studio' lmstudio-bin"}, "install.ai.ollama": {"icon":"󱚤","label":"Ollama","when":"! omarchy-cmd-present ollama","action":"if omarchy-cmd-present nvidia-smi; then ollama_pkg=ollama-cuda; elif omarchy-cmd-present rocminfo; then ollama_pkg=ollama-rocm; else ollama_pkg=ollama; fi; omarchy-install-app Ollama \"$ollama_pkg\""}, - "install.ai.crush": {"icon":"󱚤","label":"Crush","when":"! omarchy-pkg-present crush-bin","action":"omarchy-install-app Crush crush-bin"}, "install.gaming.steam": {"icon":"","label":"Steam","when":"! omarchy-pkg-present steam","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-gaming-steam"}, "install.gaming.retroarch": {"icon":"󰯉","label":"RetroArch","when":"! omarchy-pkg-present retroarch","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-gaming-retroarch"}, "install.gaming.minecraft": {"icon":"󰍳","label":"Minecraft","when":"! omarchy-pkg-present minecraft-launcher","action":"omarchy-install-and-launch Minecraft minecraft-launcher minecraft-launcher"}, diff --git a/install/user/mise.sh b/install/user/mise.sh index e5467395..d863e564 100644 --- a/install/user/mise.sh +++ b/install/user/mise.sh @@ -1,5 +1,6 @@ omarchy-mise-install codex omarchy-mise-install claude +omarchy-mise-install crush omarchy-mise-install gemini omarchy-mise-install gh omarchy-mise-install copilot diff --git a/migrations/1785633225.sh b/migrations/1785633225.sh index 657ba855..70c6e9f5 100644 --- a/migrations/1785633225.sh +++ b/migrations/1785633225.sh @@ -1,3 +1,4 @@ -echo "Install Grok via mise wrapper" +echo "Install Grok and Crush via mise wrappers" omarchy-mise-install npm:@xai-official/grok grok +omarchy-mise-install crush diff --git a/test/shell.d/default-agent-test.sh b/test/shell.d/default-agent-test.sh index 91da13a8..dcdd0014 100644 --- a/test/shell.d/default-agent-test.sh +++ b/test/shell.d/default-agent-test.sh @@ -64,6 +64,7 @@ export OMARCHY_TEST_STUB_LOG="$stub_log" grok_package="npm:@xai-official/grok" omp_package="oh-my-pi" +crush_package="crush" assert_lazy_stub() { local package=$1 @@ -80,11 +81,13 @@ assert_lazy_stub() { assert_lazy_stub "$grok_package" grok assert_lazy_stub "$omp_package" omp +assert_lazy_stub "$crush_package" crush pass "custom agent lazy stubs preserve their mise packages" source "$ROOT/install/user/mise.sh" grep -Fx "$grok_package grok" "$stub_log" >/dev/null || fail "user setup creates the Grok 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" pass "user setup creates the custom agent lazy stubs" [[ $(omarchy-default-agent) == "opencode" ]] || fail "default agent falls back to OpenCode" @@ -99,6 +102,7 @@ declare -A expected_agents=( [claude]="claude" [claude-code]="claude" [codex]="codex" + [crush]="crush" [grok]="grok" [gemini]="gemini" [gemini-cli]="gemini" @@ -112,6 +116,7 @@ declare -A expected_packages=( [opencode]="opencode" [claude]="claude" [codex]="codex" + [crush]="$crush_package" [grok]="$grok_package" [gemini]="gemini" [copilot]="copilot" @@ -188,6 +193,7 @@ assert_launch omp omp -- "Review this project" assert_launch opencode opencode --prompt "Review this project" assert_launch claude claude -- "Review this project" assert_launch codex codex -- "Review this project" +assert_launch crush crush run "Review this project" assert_launch grok grok -- "Review this project" assert_launch gemini gemini --prompt-interactive "Review this project" assert_launch copilot copilot --interactive "Review this project" diff --git a/test/shell.d/menu-test.sh b/test/shell.d/menu-test.sh index 4a5b0864..5ed5db98 100644 --- a/test/shell.d/menu-test.sh +++ b/test/shell.d/menu-test.sh @@ -167,16 +167,30 @@ assertEqual( defaultItems.findIndex(item => item.id === 'setup.input') + 1, 'menu lists Direct Boot immediately below Input' ) +const expectedAgents = { + pi: { icon: '󰏿', label: 'Pi' }, + omp: { icon: '󰣪', label: 'omp' }, + opencode: { icon: '󰕪', label: 'OpenCode' }, + claude: { icon: '󰛄', label: 'Claude' }, + codex: { icon: '󰞵', label: 'Codex' }, + grok: { icon: '󰰱', label: 'Grok' }, + gemini: { icon: '󰫢', label: 'Gemini' }, + copilot: { icon: '', label: 'Copilot' }, + crush: { icon: '󰋑', label: 'Crush' }, +} assert( - ['pi', 'omp', 'opencode', 'claude', 'codex', 'grok', 'gemini', 'copilot'].every(agent => { + Object.entries(expectedAgents).every(([agent, expected]) => { const entry = defaultById[`setup.default.agent.${agent}`] return entry + && entry.icon === expected.icon + && entry.label === expected.label && entry.action === `omarchy-default-agent ${agent}` && !entry.when && entry.checked.includes(`== \"${agent}\"`) }), - 'menu exposes every mise-installable coding agent under Defaults > Agent' + 'menu exposes every mise-installable coding agent with its own glyph under Defaults > Agent' ) +assert(!defaultById['install.ai.crush'], 'menu removes Crush from Install > AI') assert( defaultById['setup.security.passwordless-sudo'].action.includes('omarchy-sudo-passwordless'), 'menu places Passwordless Sudo under Setup > Security'