From 684360b011d725d15fdcc3448ea5a6e3cb8e2a5c Mon Sep 17 00:00:00 2001 From: Signal-Six Date: Tue, 18 Aug 2026 14:45:15 +0000 Subject: [PATCH 01/12] Add Hermes as a selectable default agent Hermes (Nous Research) joins the default agent list: it installs through the same lazy mise stubs as the other harnesses (pipx:hermes-agent), appears under Setup > Defaults > Agent, and launches unattended through the agent hotkey, the omarchy agent prompt route, and crash diagnosis. Crash handling needs no changes of its own: omarchy-agent-crash already hands the diagnose-crash skill to whichever agent is the default, so selecting Hermes routes crash toasts and "omarchy agent crash " to it directly. Unlike the other harnesses, Hermes has no "don't stop to ask" mode, so it launches in its normal approval mode: one-shot prompts go through "hermes -z " (the same route OpenCode uses), and interactive launches stay plain. Users who want fully unattended runs can pass --yolo themselves. Like the other harnesses, Hermes needs one-time setup after install (provider credentials, via hermes setup). The provision loop and a new migration also link the shipped agent skills into ~/.hermes/skills, where Hermes discovers global skills, so diagnose-crash and the Omarchy skill are first-class for it. --- bin/omarchy-agent | 7 +++++++ bin/omarchy-default-agent | 6 ++++-- bin/omarchy-provision-user | 3 ++- bin/omarchy-remove-preinstalls | 3 ++- default/omarchy/omarchy-menu.jsonc | 1 + install/user/mise.sh | 1 + manual/17-ai.md | 1 + migrations/1787063342.sh | 14 ++++++++++++++ test/shell.d/default-agent-test.sh | 8 +++++++- test/shell.d/menu-test.sh | 3 ++- 10 files changed, 41 insertions(+), 6 deletions(-) create mode 100644 migrations/1787063342.sh diff --git a/bin/omarchy-agent b/bin/omarchy-agent index 964254e8110..ef3d745345e 100755 --- a/bin/omarchy-agent +++ b/bin/omarchy-agent @@ -62,6 +62,13 @@ agy) command=(agy --dangerously-skip-permissions) [[ -n ${prompt:-} ]] && command+=(--prompt-interactive "$prompt") ;; +hermes) + # Hermes has no unattended flag in the spirit of the other harnesses' + # "don't stop to ask" modes, so it launches in its normal approval mode. + # One-shot prompts go through -z, which still asks before dangerous work. + command=(hermes) + [[ -n ${prompt:-} ]] && command+=(-z "$prompt") + ;; copilot) command=(copilot --allow-all) [[ -n ${prompt:-} ]] && command+=(--interactive "$prompt") diff --git a/bin/omarchy-default-agent b/bin/omarchy-default-agent index 3ff91358565..6730bb76fc4 100755 --- a/bin/omarchy-default-agent +++ b/bin/omarchy-default-agent @@ -1,7 +1,7 @@ #!/bin/bash # omarchy:summary=Set and launch the default coding agent -# omarchy:args=[pi|omp|opencode|claude|codex|grok|agy|copilot|crush] +# omarchy:args=[pi|omp|opencode|claude|codex|grok|agy|copilot|crush|hermes] # omarchy:examples=omarchy default agent | omarchy default agent codex | omarchy default agent claude installing=false @@ -32,9 +32,11 @@ codex) agent="codex"; name="Codex" ;; crush) agent="crush"; name="Crush" ;; grok) agent="grok"; name="Grok"; agent_package="npm:@xai-official/grok" ;; agy | antigravity | antigravity-cli | gemini | gemini-cli) agent="agy"; name="Antigravity"; agent_package="antigravity-cli" ;; +hermes | nous-hermes) agent="hermes"; name="Hermes"; agent_package="pipx:hermes-agent" ;; 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-provision-user b/bin/omarchy-provision-user index 57024b5e79a..770171f4de6 100755 --- a/bin/omarchy-provision-user +++ b/bin/omarchy-provision-user @@ -84,7 +84,7 @@ fi # Dev-aware skill symlinks. Cannot live in /etc/skel because OMARCHY_PATH may # point at a dev checkout (omarchy dev link) where the target differs. # Loops every skill directory, so shipping a new one needs no edit here. -mkdir -p ~/.agents/skills ~/.claude/skills ~/.codex/skills ~/.pi/agent/skills ~/.gemini/config/skills +mkdir -p ~/.agents/skills ~/.claude/skills ~/.codex/skills ~/.pi/agent/skills ~/.gemini/config/skills ~/.hermes/skills for skill in "$OMARCHY_PATH"/default/agents/skills/*/; do skill=${skill%/} name=${skill##*/} @@ -93,6 +93,7 @@ for skill in "$OMARCHY_PATH"/default/agents/skills/*/; do ln -sfn "$skill" ~/.codex/skills/"$name" ln -sfn "$skill" ~/.pi/agent/skills/"$name" ln -sfn "$skill" ~/.gemini/config/skills/"$name" + ln -sfn "$skill" ~/.hermes/skills/"$name" done mkdir -p ~/Downloads ~/Pictures ~/Videos ~/.config/gtk-3.0 diff --git a/bin/omarchy-remove-preinstalls b/bin/omarchy-remove-preinstalls index 6b2c6ed15d5..e9ba92a38f1 100755 --- a/bin/omarchy-remove-preinstalls +++ b/bin/omarchy-remove-preinstalls @@ -15,7 +15,8 @@ if gum confirm "Are you sure you want to remove all preinstalled web apps, TUI w # Remove mise stubs rm -f ~/.local/bin/codex ~/.local/bin/claude ~/.local/bin/agy ~/.local/bin/copilot \ ~/.local/bin/gh ~/.local/bin/opencode ~/.local/bin/playwright ~/.local/bin/playwright-cli ~/.local/bin/pi \ - ~/.local/bin/omp ~/.local/bin/grok ~/.local/bin/crush ~/.local/bin/ghui ~/.local/bin/hunk + ~/.local/bin/omp ~/.local/bin/grok ~/.local/bin/crush ~/.local/bin/ghui ~/.local/bin/hunk \ + ~/.local/bin/hermes omarchy-pkg-drop \ aether \ diff --git a/default/omarchy/omarchy-menu.jsonc b/default/omarchy/omarchy-menu.jsonc index e204da693df..7cd993d7e50 100644 --- a/default/omarchy/omarchy-menu.jsonc +++ b/default/omarchy/omarchy-menu.jsonc @@ -141,6 +141,7 @@ "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.agent.grok": {"icon":"","iconFont":"omarchy","label":"Grok","checked":"[[ \"$(omarchy-default-agent)\" == \"grok\" ]]","action":"omarchy-default-agent grok"}, + "setup.default.agent.hermes": {"icon":"󰚩","label":"Hermes","checked":"[[ \"$(omarchy-default-agent)\" == \"hermes\" ]]","action":"omarchy-default-agent hermes"}, "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.pi": {"icon":"","iconFont":"omarchy","label":"Pi","checked":"[[ \"$(omarchy-default-agent)\" == \"pi\" ]]","action":"omarchy-default-agent pi"}, diff --git a/install/user/mise.sh b/install/user/mise.sh index 6653cec485a..adee744a18a 100644 --- a/install/user/mise.sh +++ b/install/user/mise.sh @@ -9,6 +9,7 @@ omarchy-mise-install npm:playwright playwright omarchy-mise-install pi omarchy-mise-install github:can1357/oh-my-pi omp omarchy-mise-install npm:@xai-official/grok grok +omarchy-mise-install pipx:hermes-agent hermes omarchy-mise-install npm:@kitlangton/ghui ghui omarchy-mise-install aqua:modem-dev/hunk hunk omarchy-mise-install github:basecamp/hey-cli hey diff --git a/manual/17-ai.md b/manual/17-ai.md index a0fc17f4485..fe91619cfbd 100644 --- a/manual/17-ai.md +++ b/manual/17-ai.md @@ -13,6 +13,7 @@ Omarchy treats AI coding agents as first-class citizens, but it doesn't pick a f | `grok` | Grok CLI from xAI | | `pi` | [Mario Zechner's Pi](https://github.com/badlogic/pi-mono) | | `omp` | [Oh My Pi](https://github.com/can1357/oh-my-pi) | +| `hermes` | [Hermes](https://hermes-agent.nousresearch.com) | To wrap an additional CLI the same way, run `omarchy-mise-install [command-name]`. The stubs are kept current along with everything else mise manages when you run `omarchy update` (or the `mup` alias). diff --git a/migrations/1787063342.sh b/migrations/1787063342.sh new file mode 100644 index 00000000000..ac290d4aae6 --- /dev/null +++ b/migrations/1787063342.sh @@ -0,0 +1,14 @@ +echo "Install Hermes lazy mise stub" + +if [[ ! -f $HOME/.local/state/omarchy/preinstalls-removed ]]; then + omarchy-mise-install pipx:hermes-agent hermes +fi + +echo "Link Omarchy skills into the Hermes skill directory" + +mkdir -p ~/.hermes/skills +for skill in "$OMARCHY_PATH"/default/agents/skills/*/; do + skill=${skill%/} + name=${skill##*/} + ln -sfn "$skill" ~/.hermes/skills/"$name" +done diff --git a/test/shell.d/default-agent-test.sh b/test/shell.d/default-agent-test.sh index 0445106da69..b680cc15107 100644 --- a/test/shell.d/default-agent-test.sh +++ b/test/shell.d/default-agent-test.sh @@ -233,8 +233,9 @@ rm -f "$agent_file" pass "agent migrations install working wrappers without overriding the preinstall opt-out" touch "$test_home/.local/bin/agy" +touch "$test_home/.local/bin/hermes" omarchy-remove-preinstalls >/dev/null -for command in agy omp grok crush; do +for command in agy omp grok crush hermes; 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" @@ -297,6 +298,8 @@ declare -A expected_agents=( [gemini-cli]="agy" [copilot]="copilot" [github-copilot]="copilot" + [hermes]="hermes" + [nous-hermes]="hermes" ) declare -A expected_packages=( @@ -309,6 +312,7 @@ declare -A expected_packages=( [grok]="$grok_package" [agy]="$agy_package" [copilot]="copilot" + [hermes]="pipx:hermes-agent" ) for selection in "${!expected_agents[@]}"; do @@ -452,6 +456,7 @@ assert_launch crush crush run "Review this project" assert_launch grok grok --permission-mode bypassPermissions -- "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" +assert_launch hermes hermes -z "Review this project" pass "agent launcher adapts initial prompts for every supported agent" assert_bypass pi pi @@ -463,6 +468,7 @@ assert_bypass crush crush --yolo assert_bypass grok grok --permission-mode bypassPermissions assert_bypass agy agy --dangerously-skip-permissions assert_bypass copilot copilot --allow-all +assert_bypass hermes hermes pass "agent launcher skips permission prompts for every supported agent" printf '%s\n' "opencode" >"$agent_file" diff --git a/test/shell.d/menu-test.sh b/test/shell.d/menu-test.sh index 6cf5165032b..04c0812fbe2 100644 --- a/test/shell.d/menu-test.sh +++ b/test/shell.d/menu-test.sh @@ -218,6 +218,7 @@ const expectedAgents = { claude: { icon: '󰛄', label: 'Claude' }, codex: { icon: '\ue905', iconFont: 'omarchy', label: 'Codex' }, grok: { icon: '\ue904', iconFont: 'omarchy', label: 'Grok' }, + hermes: { icon: '\u{f06a9}', label: 'Hermes' }, copilot: { icon: '', label: 'Copilot' }, crush: { icon: '󰋑', label: 'Crush' }, } @@ -238,7 +239,7 @@ assertDeepEqual( defaultItems .filter(item => item.parent === 'setup.default.agent') .map(item => item.label), - ['Antigravity', 'Claude', 'Codex', 'Copilot', 'Crush', 'Grok', 'omp', 'OpenCode', 'Pi'], + ['Antigravity', 'Claude', 'Codex', 'Copilot', 'Crush', 'Grok', 'Hermes', 'omp', 'OpenCode', 'Pi'], 'menu sorts coding agents alphabetically' ) const expectedDefaults = { From f411ba9111e2b3e4839831d164e40c40dca42ac7 Mon Sep 17 00:00:00 2001 From: Signal-Six Date: Wed, 19 Aug 2026 18:14:15 +0000 Subject: [PATCH 02/12] Use $HOME and document unguarded skill symlinks in Hermes migration Addresses Copilot review comments on migrations/1787063342.sh: - Consistently use $HOME for the skills directory path (matches bin/omarchy-provision-user and sibling migrations) - Document why the skill symlinks are intentionally unguarded: crash diagnosis is a core feature, and the other agent skill symlinks are created unconditionally as well --- migrations/1787063342.sh | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/migrations/1787063342.sh b/migrations/1787063342.sh index ac290d4aae6..0c0d15a2f73 100644 --- a/migrations/1787063342.sh +++ b/migrations/1787063342.sh @@ -6,9 +6,13 @@ fi echo "Link Omarchy skills into the Hermes skill directory" -mkdir -p ~/.hermes/skills +# Skill symlinks are deliberately unguarded: crash diagnosis is a core +# feature, not an optional preinstall, and the other agent skill symlinks +# (bin/omarchy-provision-user, migrations/1786539345.sh) are created +# unconditionally as well. +mkdir -p "$HOME/.hermes/skills" for skill in "$OMARCHY_PATH"/default/agents/skills/*/; do skill=${skill%/} name=${skill##*/} - ln -sfn "$skill" ~/.hermes/skills/"$name" + ln -sfn "$skill" "$HOME/.hermes/skills/$name" done From 09bb7ed770737bebda778b769dbee379d3641548 Mon Sep 17 00:00:00 2001 From: Talos Date: Sat, 22 Aug 2026 00:17:56 -0600 Subject: [PATCH 03/12] Fix Hermes installation and launch semantics --- bin/omarchy-agent | 11 +-- bin/omarchy-default-agent | 25 +++++-- bin/omarchy-install-hermes-cli | 69 +++++++++++++++++++ bin/omarchy-remove-preinstalls | 2 +- install/user/mise.sh | 2 +- manual/17-ai.md | 10 +-- migrations/1787063342.sh | 4 +- test/shell.d/default-agent-test.sh | 97 +++++++++++++++++++++++++-- test/shell.d/hermes-installer-test.sh | 90 +++++++++++++++++++++++++ test/shell.d/menu-test.sh | 2 +- test/shell.d/provision-user-test.sh | 12 ++-- 11 files changed, 292 insertions(+), 32 deletions(-) create mode 100755 bin/omarchy-install-hermes-cli create mode 100755 test/shell.d/hermes-installer-test.sh diff --git a/bin/omarchy-agent b/bin/omarchy-agent index ef3d745345e..f0d159f7b79 100755 --- a/bin/omarchy-agent +++ b/bin/omarchy-agent @@ -51,8 +51,10 @@ if omarchy-cmd-missing "$agent"; then 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 has no approval prompts. +# Prompt launches run unattended, so each agent gets its own spelling of +# "don't stop to ask". Plain Hermes is the exception: its interactive launch +# remains approval-gated, while its one-shot prompt mode bypasses approvals. +# Pi has no approval prompts. case "$agent" in opencode) command=(opencode --auto) @@ -63,9 +65,8 @@ agy) [[ -n ${prompt:-} ]] && command+=(--prompt-interactive "$prompt") ;; hermes) - # Hermes has no unattended flag in the spirit of the other harnesses' - # "don't stop to ask" modes, so it launches in its normal approval mode. - # One-shot prompts go through -z, which still asks before dangerous work. + # Plain Hermes stays interactive and approval-gated. A supplied prompt uses + # one-shot mode, which Hermes documents as auto-bypassing approvals. command=(hermes) [[ -n ${prompt:-} ]] && command+=(-z "$prompt") ;; diff --git a/bin/omarchy-default-agent b/bin/omarchy-default-agent index 6730bb76fc4..2274c6cb047 100755 --- a/bin/omarchy-default-agent +++ b/bin/omarchy-default-agent @@ -32,7 +32,7 @@ codex) agent="codex"; name="Codex" ;; crush) agent="crush"; name="Crush" ;; grok) agent="grok"; name="Grok"; agent_package="npm:@xai-official/grok" ;; agy | antigravity | antigravity-cli | gemini | gemini-cli) agent="agy"; name="Antigravity"; agent_package="antigravity-cli" ;; -hermes | nous-hermes) agent="hermes"; name="Hermes"; agent_package="pipx:hermes-agent" ;; +hermes | nous-hermes) agent="hermes"; name="Hermes" ;; copilot | github-copilot) agent="copilot"; name="GitHub Copilot" ;; *) echo "Usage: omarchy-default-agent " @@ -43,11 +43,22 @@ esac agent_package=${agent_package:-$agent} -if [[ $installing == "false" ]] && ! mise where "$agent_package" &>/dev/null; then - exec omarchy-launch-floating-terminal-with-presentation omarchy-default-agent --install "$agent" +if [[ $installing == "false" ]]; then + if [[ $agent == "hermes" ]]; then + if ! omarchy-install-hermes-cli --check; then + exec omarchy-launch-floating-terminal-with-presentation omarchy-default-agent --install "$agent" + fi + elif ! mise where "$agent_package" &>/dev/null; then + exec omarchy-launch-floating-terminal-with-presentation omarchy-default-agent --install "$agent" + fi fi -if ! mise use -g "$agent_package"; then +if [[ $agent == "hermes" ]]; then + if [[ $installing == "true" ]] && ! omarchy-install-hermes-cli; then + echo "Could not install Hermes" >&2 + exit 1 + fi +elif ! mise use -g "$agent_package"; then if [[ $installing == "true" ]]; then echo "Could not install $name with mise" >&2 else @@ -61,7 +72,11 @@ printf '%s\n' "$agent" >"$agent_file" if [[ $installing == "true" ]]; then printf '\033[2J\033[3J\033[H' - exec omarchy-agent --inline + if [[ $agent == "hermes" ]]; then + exec hermes setup + else + exec omarchy-agent --inline + fi else exec omarchy-agent fi diff --git a/bin/omarchy-install-hermes-cli b/bin/omarchy-install-hermes-cli new file mode 100755 index 00000000000..87254036eb3 --- /dev/null +++ b/bin/omarchy-install-hermes-cli @@ -0,0 +1,69 @@ +#!/bin/bash + +# omarchy:summary=Install Hermes CLI through its supported upstream installer +# omarchy:args=[--check|--stub] + +hermes_command="$HOME/.local/bin/hermes" +stub_marker="# omarchy-hermes-installer-stub" + +is_installer_stub() { + [[ -f $hermes_command ]] && grep -Fxq "$stub_marker" "$hermes_command" +} + +is_installed() { + omarchy-cmd-present hermes && ! is_installer_stub +} + +case "${1:-}" in +--check) + is_installed + exit + ;; +--stub) + if is_installed; then + exit 0 + fi + + mkdir -p "$HOME/.local/bin" + rm -f "$hermes_command" + cat >"$hermes_command" <<'STUB' +#!/bin/bash +# omarchy-hermes-installer-stub +omarchy-install-hermes-cli || exit 1 +exec "$HOME/.local/bin/hermes" "$@" +STUB + chmod +x "$hermes_command" + exit 0 + ;; +"") ;; +*) + echo "Usage: omarchy-install-hermes-cli [--check|--stub]" >&2 + exit 1 + ;; +esac + +# A real Hermes install owns its updates through `hermes update`. Omarchy only +# bootstraps missing installations and never turns a normal system update into +# an unrequested Hermes update. +if is_installed; then + exit 0 +fi + +installer=$(mktemp) +trap 'rm -f "$installer"' EXIT + +if ! curl -fsSL https://hermes-agent.nousresearch.com/install.sh -o "$installer"; then + echo "Could not download the official Hermes installer" >&2 + exit 1 +fi + +if ! bash "$installer" --skip-setup --non-interactive; then + echo "The official Hermes installer failed" >&2 + exit 1 +fi + +hash -r +if [[ ! -x $hermes_command ]] || is_installer_stub; then + echo "Hermes installer finished without creating $hermes_command" >&2 + exit 1 +fi diff --git a/bin/omarchy-remove-preinstalls b/bin/omarchy-remove-preinstalls index e9ba92a38f1..3d2488105f4 100755 --- a/bin/omarchy-remove-preinstalls +++ b/bin/omarchy-remove-preinstalls @@ -12,7 +12,7 @@ if gum confirm "Are you sure you want to remove all preinstalled web apps, TUI w touch ~/.local/state/omarchy/preinstalls-removed hyprctl reload - # Remove mise stubs + # Remove lazy launchers rm -f ~/.local/bin/codex ~/.local/bin/claude ~/.local/bin/agy ~/.local/bin/copilot \ ~/.local/bin/gh ~/.local/bin/opencode ~/.local/bin/playwright ~/.local/bin/playwright-cli ~/.local/bin/pi \ ~/.local/bin/omp ~/.local/bin/grok ~/.local/bin/crush ~/.local/bin/ghui ~/.local/bin/hunk \ diff --git a/install/user/mise.sh b/install/user/mise.sh index adee744a18a..cf7e8cccfa9 100644 --- a/install/user/mise.sh +++ b/install/user/mise.sh @@ -9,7 +9,7 @@ omarchy-mise-install npm:playwright playwright omarchy-mise-install pi omarchy-mise-install github:can1357/oh-my-pi omp omarchy-mise-install npm:@xai-official/grok grok -omarchy-mise-install pipx:hermes-agent hermes +omarchy-install-hermes-cli --stub omarchy-mise-install npm:@kitlangton/ghui ghui omarchy-mise-install aqua:modem-dev/hunk hunk omarchy-mise-install github:basecamp/hey-cli hey diff --git a/manual/17-ai.md b/manual/17-ai.md index fe91619cfbd..456a36af9a4 100644 --- a/manual/17-ai.md +++ b/manual/17-ai.md @@ -1,6 +1,6 @@ # AI -Omarchy treats AI coding agents as first-class citizens, but it doesn't pick a favorite for you. Instead, every major coding-agent CLI comes pre-wired as a lazy-loaded launcher. The launchers are tiny [mise](https://mise.jdx.dev/)-managed stubs in `~/.local/bin/`, so nothing is downloaded until the first time you actually run one. Invoke any of these and authenticate when prompted: +Omarchy treats AI coding agents as first-class citizens, but it doesn't pick a favorite for you. Instead, every major coding-agent CLI comes pre-wired as a lazy-loaded launcher in `~/.local/bin/`, so nothing is downloaded until the first time you actually run one. Most are tiny [mise](https://mise.jdx.dev/)-managed stubs; Hermes uses its supported upstream installer instead. Invoke any of these and authenticate when prompted: | Command | Agent | |------------|------------------------------------------------------------------| @@ -15,13 +15,13 @@ Omarchy treats AI coding agents as first-class citizens, but it doesn't pick a f | `omp` | [Oh My Pi](https://github.com/can1357/oh-my-pi) | | `hermes` | [Hermes](https://hermes-agent.nousresearch.com) | -To wrap an additional CLI the same way, run `omarchy-mise-install [command-name]`. The stubs are kept current along with everything else mise manages when you run `omarchy update` (or the `mup` alias). +Most agent commands are lazy mise wrappers. To wrap another CLI the same way, run `omarchy-mise-install [command-name]`; those stubs stay current along with mise during `omarchy update` (or the `mup` alias). Hermes is the exception because its PyPI package is unsupported upstream: its lazy stub invokes Hermes' official Linux installer with setup deferred, and later updates belong to `hermes update`. ### The default agent -Pick your default agent with `omarchy default agent ` or under _Setup > Defaults > Agent_ in the Omarchy Menu (`Super + Space`). If the agent isn't installed yet, picking it installs it first. A fresh Omarchy will invite you to make this choice with a one-time notification. +Pick your default agent with `omarchy default agent ` or under _Setup > Defaults > Agent_ in the Omarchy Menu (`Super + Space`). If the agent isn't installed yet, picking it installs it first. Hermes then opens `hermes setup` for its one-time provider configuration. A fresh Omarchy will invite you to make this choice with a one-time notification. -Once you've chosen, `Super + Shift + Ctrl + A` launches the default agent in a dedicated terminal window (or brings up the picker if you haven't chosen yet). You can also launch it straight into a task with `omarchy agent prompt "Review this project"`. Agents launched this way run unattended in their respective don't-stop-to-ask modes, so be ready for them to actually do things! And since agents refuse to remember trust for your home directory, launches from `$HOME` start in `~/Work` instead. +Once you've chosen, `Super + Shift + Ctrl + A` launches the default agent in a dedicated terminal window (or brings up the picker if you haven't chosen yet). You can also launch it straight into a task with `omarchy agent prompt "Review this project"`. Prompt launches run unattended in each agent's don't-stop-to-ask mode, so be ready for them to actually do things; in Hermes, `-z` one-shot mode auto-bypasses approvals. An ordinary interactive Hermes launch stays approval-gated. And since agents refuse to remember trust for your home directory, launches from `$HOME` start in `~/Work` instead. There are terminal shortcuts too: `a` runs the default agent inline in the current terminal, while `c`, `cx`, and `cy` start OpenCode, Claude Code, and Codex directly (again in their auto-approving modes). Theme changes sync to the agents as well: Claude Code, Pi, and OpenCode all follow along when you switch the Omarchy theme. @@ -47,6 +47,6 @@ Omarchy recommends two ways of running local LLM models: LM Studio and Ollama. L ### The Omarchy Skill -Agent skills help AI use specific tools in a specific way, and Omarchy ships with a default skill for tailoring the system. Like tweaking your Hyprland config, adjusting the bar, or even creating a new theme from scratch. It's symlinked into the skill directories for Claude Code (`~/.claude/skills`), Codex (`~/.codex/skills`), Pi (`~/.pi/agent/skills`), Antigravity (`~/.gemini/config/skills`), and the generic `~/.agents/skills` location, so most harnesses pick it up automatically. +Agent skills help AI use specific tools in a specific way, and Omarchy ships with a default skill for tailoring the system. Like tweaking your Hyprland config, adjusting the bar, or even creating a new theme from scratch. It's symlinked into the skill directories for Claude Code (`~/.claude/skills`), Codex (`~/.codex/skills`), Pi (`~/.pi/agent/skills`), Antigravity (`~/.gemini/config/skills`), Hermes (`~/.hermes/skills`), and the generic `~/.agents/skills` location, so most harnesses pick it up automatically. But you should treat this skill as experimental. Different models will use it to different effect. It's best to run in plan mode first, so you have an idea of what the agent would like to change. And then be ready to rollback changes or even invoking `omarchy reinstall configs`, if the agent makes a mess of everything. diff --git a/migrations/1787063342.sh b/migrations/1787063342.sh index 0c0d15a2f73..3f7a1199b0b 100644 --- a/migrations/1787063342.sh +++ b/migrations/1787063342.sh @@ -1,7 +1,7 @@ -echo "Install Hermes lazy mise stub" +echo "Install Hermes lazy official-installer stub" if [[ ! -f $HOME/.local/state/omarchy/preinstalls-removed ]]; then - omarchy-mise-install pipx:hermes-agent hermes + omarchy-install-hermes-cli --stub fi echo "Link Omarchy skills into the Hermes skill directory" diff --git a/test/shell.d/default-agent-test.sh b/test/shell.d/default-agent-test.sh index b680cc15107..d4016bc1d60 100644 --- a/test/shell.d/default-agent-test.sh +++ b/test/shell.d/default-agent-test.sh @@ -17,6 +17,8 @@ inline_log="$test_tmp/inline" mise_log="$test_tmp/mise" mise_history="$test_tmp/mise-history" stub_log="$test_tmp/stubs" +hermes_install_log="$test_tmp/hermes-install" +hermes_log="$test_tmp/hermes" terminal_log="$test_tmp/terminal" menu_log="$test_tmp/menu" mkdir -p "$mock_bin" "$test_home" @@ -51,6 +53,21 @@ cat >"$mock_bin/omarchy-mise-install" <<'SH' printf '%s\n' "$*" >>"$OMARCHY_TEST_STUB_LOG" SH +cat >"$mock_bin/omarchy-install-hermes-cli" <<'SH' +#!/bin/bash +if [[ ${1:-} == "--check" ]]; then + [[ ${OMARCHY_TEST_MISSING_COMMAND:-} != "hermes" ]] + exit +fi +printf '%s\0' "$@" >"$OMARCHY_TEST_HERMES_INSTALL_LOG" +[[ ${OMARCHY_TEST_HERMES_INSTALL_FAIL:-false} != "true" ]] +SH + +cat >"$mock_bin/hermes" <<'SH' +#!/bin/bash +printf '%s\0' "$@" >"$OMARCHY_TEST_HERMES_LOG" +SH + cat >"$mock_bin/mise" <<'SH' #!/bin/bash printf '%s\0' "$@" >"$OMARCHY_TEST_MISE_LOG" @@ -89,6 +106,8 @@ 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_HERMES_INSTALL_LOG="$hermes_install_log" +export OMARCHY_TEST_HERMES_LOG="$hermes_log" export OMARCHY_TEST_AGENT_TERMINAL_LOG="$terminal_log" export OMARCHY_TEST_AGENT_MENU_LOG="$menu_log" export OMARCHY_PATH="$ROOT" @@ -121,8 +140,35 @@ grep -Fx "$agy_package agy" "$stub_log" >/dev/null || fail "user setup creates t 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" +mapfile -d '' -t hermes_install_args <"$hermes_install_log" +[[ ${hermes_install_args[*]} == "--stub" ]] || fail "user setup creates the supported Hermes lazy stub" pass "user setup creates the custom agent lazy stubs" +: >"$hermes_install_log" +HOME="$test_home" OMARCHY_PATH="$ROOT" bash -euo pipefail "$ROOT/migrations/1787063342.sh" >/dev/null +mapfile -d '' -t hermes_install_args <"$hermes_install_log" +[[ ${hermes_install_args[*]} == "--stub" ]] || fail "Hermes migration creates the supported lazy stub under temporary HOME" +for skill in omarchy diagnose-crash; do + link="$test_home/.hermes/skills/$skill" + [[ -L $link && $(readlink "$link") == "$ROOT/default/agents/skills/$skill" ]] || + fail "Hermes migration provisions the $skill skill" +done +pass "Hermes migration installs its stub and both shipped skills" + +: >"$hermes_install_log" +HOME="$test_home" OMARCHY_PATH="$ROOT" bash -euo pipefail "$ROOT/migrations/1787063342.sh" >/dev/null +mapfile -d '' -t hermes_install_args <"$hermes_install_log" +[[ ${hermes_install_args[*]} == "--stub" ]] || fail "Hermes migration rerun remains idempotent" +pass "Hermes migration is idempotent" + +: >"$hermes_install_log" +mkdir -p "$test_home/.local/state/omarchy" +touch "$test_home/.local/state/omarchy/preinstalls-removed" +HOME="$test_home" OMARCHY_PATH="$ROOT" bash -euo pipefail "$ROOT/migrations/1787063342.sh" >/dev/null +[[ ! -s $hermes_install_log ]] || fail "Hermes migration respects removed preinstalls" +pass "Hermes migration respects removed preinstalls" +rm "$test_home/.local/state/omarchy/preinstalls-removed" + : >"$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" @@ -312,18 +358,22 @@ declare -A expected_packages=( [grok]="$grok_package" [agy]="$agy_package" [copilot]="copilot" - [hermes]="pipx:hermes-agent" ) +: >"$hermes_install_log" 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" + if [[ $expected == "hermes" ]]; then + [[ ! -s $hermes_install_log ]] || fail "installed Hermes selection does not rerun its installer" + else + 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" + fi mapfile -d '' -t agent_open_args <"$agent_open_log" [[ ${#agent_open_args[@]} == 1 && ${agent_open_args[0]} == "omarchy-agent" ]] || @@ -359,6 +409,35 @@ mapfile -d '' -t agent_open_args <"$agent_open_log" fail "newly installed agent opens in the installation terminal" pass "missing agents install visibly and open in the same terminal" +: >"$terminal_log" +OMARCHY_TEST_MISSING_COMMAND=hermes omarchy-default-agent hermes +mapfile -d '' -t terminal_args <"$terminal_log" +[[ ${terminal_args[*]} == "omarchy-default-agent --install hermes" ]] || + fail "missing Hermes installation opens in a terminal" + +: >"$hermes_install_log" +: >"$hermes_log" +OMARCHY_TEST_MISSING_COMMAND=hermes omarchy-default-agent --install hermes >"$test_tmp/hermes-install-output" +mapfile -d '' -t hermes_install_args <"$hermes_install_log" +[[ ${#hermes_install_args[@]} == 1 && -z ${hermes_install_args[0]} ]] || fail "Hermes uses its dedicated official installer" +mapfile -d '' -t hermes_args <"$hermes_log" +[[ ${hermes_args[*]} == "setup" ]] || fail "new Hermes install opens first-run setup" +[[ $(omarchy-default-agent) == "hermes" ]] || fail "successful Hermes install changes the selection" +pass "Hermes installs through the supported route and opens first-run setup" + +printf '%s\n' copilot >"$agent_file" +: >"$hermes_install_log" +: >"$hermes_log" +if OMARCHY_TEST_MISSING_COMMAND=hermes OMARCHY_TEST_HERMES_INSTALL_FAIL=true \ + omarchy-default-agent --install hermes >"$test_tmp/hermes-install-failure-output" 2>&1; then + fail "default agent rejects a failed Hermes installation" +fi +[[ $(omarchy-default-agent) == "copilot" ]] || fail "failed Hermes installation preserves the current default" +[[ ! -s $hermes_log ]] || fail "failed Hermes installation does not start setup" +grep -F "Could not install Hermes" "$test_tmp/hermes-install-failure-output" >/dev/null || + fail "default agent reports a failed Hermes installation" +pass "default agent preserves state when the Hermes installer fails" + : >"$notification_history" : >"$agent_open_log" : >"$terminal_log" @@ -457,7 +536,7 @@ assert_launch grok grok --permission-mode bypassPermissions -- "Review this proj assert_launch agy agy --dangerously-skip-permissions --prompt-interactive "Review this project" assert_launch copilot copilot --allow-all --interactive "Review this project" assert_launch hermes hermes -z "Review this project" -pass "agent launcher adapts initial prompts for every supported agent" +pass "agent launcher adapts prompts, with Hermes one-shot auto-bypassing approvals" assert_bypass pi pi assert_bypass omp omp --auto-approve @@ -468,8 +547,12 @@ assert_bypass crush crush --yolo assert_bypass grok grok --permission-mode bypassPermissions assert_bypass agy agy --dangerously-skip-permissions assert_bypass copilot copilot --allow-all -assert_bypass hermes hermes -pass "agent launcher skips permission prompts for every supported agent" +pass "agent launcher skips permission prompts for unattended interactive agents" + +printf '%s\n' hermes >"$agent_file" +omarchy-agent +assert_launched hermes "keeps ordinary interactive approval prompts" hermes +pass "Hermes interactive launch remains approval-gated" printf '%s\n' "opencode" >"$agent_file" omarchy-agent diff --git a/test/shell.d/hermes-installer-test.sh b/test/shell.d/hermes-installer-test.sh new file mode 100755 index 00000000000..2913703fbf9 --- /dev/null +++ b/test/shell.d/hermes-installer-test.sh @@ -0,0 +1,90 @@ +#!/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" +curl_log="$test_tmp/curl" +installer_log="$test_tmp/installer" +mkdir -p "$mock_bin" "$test_home/.local/bin" + +cat >"$mock_bin/curl" <<'SH' +#!/bin/bash +printf '%s\0' "$@" >"$OMARCHY_TEST_CURL_LOG" +[[ ${OMARCHY_TEST_CURL_FAIL:-false} != "true" ]] || exit 22 +while (($#)); do + if [[ $1 == "-o" ]]; then + cp "$OMARCHY_TEST_INSTALLER" "$2" + exit 0 + fi + shift +done +exit 2 +SH + +cat >"$test_tmp/installer" <<'SH' +#!/bin/bash +printf '%s\0' "$@" >"$OMARCHY_TEST_INSTALLER_LOG" +mkdir -p "$HOME/.hermes/hermes-agent/.git" +[[ ${OMARCHY_TEST_INSTALLER_FAIL:-false} != "true" ]] || exit 1 +printf '#!/bin/bash\n' >"$HOME/.local/bin/hermes" +chmod +x "$HOME/.local/bin/hermes" +SH + +chmod +x "$mock_bin/curl" "$test_tmp/installer" + +export HOME="$test_home" +export PATH="$test_home/.local/bin:$mock_bin:$ROOT/bin:/usr/bin" +export OMARCHY_TEST_CURL_LOG="$curl_log" +export OMARCHY_TEST_INSTALLER_LOG="$installer_log" +export OMARCHY_TEST_INSTALLER="$test_tmp/installer" + +ln -s "$test_home/.hermes/missing-hermes" "$test_home/.local/bin/hermes" +omarchy-install-hermes-cli --stub +grep -Fxq '# omarchy-hermes-installer-stub' "$test_home/.local/bin/hermes" || + fail "Hermes preinstall is a supported-installer lazy stub" +pass "Hermes preinstall creates a supported-installer lazy stub" + +"$test_home/.local/bin/hermes" --version +mapfile -d '' -t installer_args <"$installer_log" +[[ ${installer_args[*]} == "--skip-setup --non-interactive" ]] || + fail "Hermes lazy stub invokes the official installer noninteractively" "${installer_args[*]}" +mapfile -d '' -t curl_args <"$curl_log" +[[ ${curl_args[*]} == "-fsSL https://hermes-agent.nousresearch.com/install.sh -o "* ]] || + fail "Hermes installer downloads the Tier-1 installer to a file" "${curl_args[*]}" +pass "Hermes lazy stub invokes the Tier-1 installer with noninteractive setup skipped" + +: >"$curl_log" +omarchy-install-hermes-cli +[[ ! -s $curl_log ]] || fail "Hermes installer rerun leaves an installed Hermes to its self-updater" +pass "Hermes installer rerun leaves updates to hermes update" + +rm "$test_home/.local/bin/hermes" +: >"$curl_log" +if OMARCHY_TEST_CURL_FAIL=true omarchy-install-hermes-cli >"$test_tmp/download-failure" 2>&1; then + fail "Hermes installer reports download failure" +fi +[[ ! -e $test_home/.local/bin/hermes ]] || fail "download failure creates no Hermes command" +pass "Hermes installer fails cleanly when the official script cannot be downloaded" + +: >"$curl_log" +if OMARCHY_TEST_INSTALLER_FAIL=true omarchy-install-hermes-cli >"$test_tmp/install-failure" 2>&1; then + fail "Hermes installer reports official installer failure" +fi +[[ -d $test_home/.hermes/hermes-agent/.git ]] || fail "failed installer preserves upstream partial state for its recovery path" +[[ ! -e $test_home/.local/bin/hermes ]] || fail "failed installer exposes no broken Hermes command" +pass "Hermes installer preserves partial state but exposes no broken command" + +cat >"$test_tmp/installer" <<'SH' +#!/bin/bash +exit 0 +SH +if omarchy-install-hermes-cli >"$test_tmp/missing-command" 2>&1; then + fail "Hermes installer verifies the public command after upstream success" +fi +pass "Hermes installer rejects success without an installed command" diff --git a/test/shell.d/menu-test.sh b/test/shell.d/menu-test.sh index 04c0812fbe2..cf7bf6dc766 100644 --- a/test/shell.d/menu-test.sh +++ b/test/shell.d/menu-test.sh @@ -233,7 +233,7 @@ assert( && !entry.when && entry.checked.includes(`== \"${agent}\"`) }), - 'menu exposes every mise-installable coding agent with its own glyph under Defaults > Agent' + 'menu exposes every installable coding agent with its own glyph under Defaults > Agent' ) assertDeepEqual( defaultItems diff --git a/test/shell.d/provision-user-test.sh b/test/shell.d/provision-user-test.sh index b598242e2fd..4d1e441b5fd 100755 --- a/test/shell.d/provision-user-test.sh +++ b/test/shell.d/provision-user-test.sh @@ -26,10 +26,12 @@ HOME="$test_tmp/home" PATH="$mock_bin:$ROOT/bin:$PATH" OMARCHY_PATH="$ROOT" \ OMARCHY_INSTALL="$test_tmp/install" bash "$ROOT/bin/omarchy-provision-user" >/dev/null || fail "omarchy-provision-user finishes" -for skill in omarchy diagnose-crash; do - link="$test_tmp/home/.gemini/config/skills/$skill" - [[ -L $link && $(readlink "$link") == "$ROOT/default/agents/skills/$skill" ]] || - fail "omarchy-provision-user provisions the $skill skill for Antigravity" +for agent_dir in .gemini/config/skills .hermes/skills; do + for skill in omarchy diagnose-crash; do + link="$test_tmp/home/$agent_dir/$skill" + [[ -L $link && $(readlink "$link") == "$ROOT/default/agents/skills/$skill" ]] || + fail "omarchy-provision-user provisions the $skill skill in $agent_dir" + done done -pass "omarchy-provision-user provisions Antigravity skills" +pass "omarchy-provision-user provisions both shipped skills for Antigravity and Hermes" From 22c6b3b5a6818a4958f7bc156bae94e5100e8d49 Mon Sep 17 00:00:00 2001 From: Talos Date: Sat, 22 Aug 2026 00:23:13 -0600 Subject: [PATCH 04/12] Preserve user-owned Hermes launchers --- bin/omarchy-remove-preinstalls | 11 ++++++++--- test/shell.d/default-agent-test.sh | 21 +++++++++++++++++++-- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/bin/omarchy-remove-preinstalls b/bin/omarchy-remove-preinstalls index 3d2488105f4..99b784cb4af 100755 --- a/bin/omarchy-remove-preinstalls +++ b/bin/omarchy-remove-preinstalls @@ -12,11 +12,16 @@ if gum confirm "Are you sure you want to remove all preinstalled web apps, TUI w touch ~/.local/state/omarchy/preinstalls-removed hyprctl reload - # Remove lazy launchers + # Remove lazy launchers. Hermes stops being Omarchy-owned as soon as its + # marked stub replaces itself with the upstream installer's real launcher. + hermes_launcher="$HOME/.local/bin/hermes" + if [[ -f $hermes_launcher ]] && grep -Fxq '# omarchy-hermes-installer-stub' "$hermes_launcher"; then + rm -f "$hermes_launcher" + fi + rm -f ~/.local/bin/codex ~/.local/bin/claude ~/.local/bin/agy ~/.local/bin/copilot \ ~/.local/bin/gh ~/.local/bin/opencode ~/.local/bin/playwright ~/.local/bin/playwright-cli ~/.local/bin/pi \ - ~/.local/bin/omp ~/.local/bin/grok ~/.local/bin/crush ~/.local/bin/ghui ~/.local/bin/hunk \ - ~/.local/bin/hermes + ~/.local/bin/omp ~/.local/bin/grok ~/.local/bin/crush ~/.local/bin/ghui ~/.local/bin/hunk omarchy-pkg-drop \ aether \ diff --git a/test/shell.d/default-agent-test.sh b/test/shell.d/default-agent-test.sh index d4016bc1d60..d7bf711ff93 100644 --- a/test/shell.d/default-agent-test.sh +++ b/test/shell.d/default-agent-test.sh @@ -279,12 +279,29 @@ rm -f "$agent_file" pass "agent migrations install working wrappers without overriding the preinstall opt-out" touch "$test_home/.local/bin/agy" -touch "$test_home/.local/bin/hermes" +printf '#!/bin/bash\n# omarchy-hermes-installer-stub\n' >"$test_home/.local/bin/hermes" +chmod +x "$test_home/.local/bin/hermes" omarchy-remove-preinstalls >/dev/null for command in agy omp grok crush hermes; 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" +pass "Remove Preinstalls deletes the marked Hermes lazy stub" + +mkdir -p "$test_home/.hermes/hermes-agent/venv/bin" +printf '#!/bin/bash\nexit 0\n' >"$test_home/.hermes/hermes-agent/venv/bin/hermes" +chmod +x "$test_home/.hermes/hermes-agent/venv/bin/hermes" +ln -s "$test_home/.hermes/hermes-agent/venv/bin/hermes" "$test_home/.local/bin/hermes" +omarchy-remove-preinstalls >/dev/null +[[ -L $test_home/.local/bin/hermes ]] || fail "Remove Preinstalls preserves the official Hermes launcher" +pass "Remove Preinstalls preserves the official Hermes launcher" + +rm "$test_home/.local/bin/hermes" +printf '#!/bin/bash\nprintf user-hermes\n' >"$test_home/.local/bin/hermes" +chmod +x "$test_home/.local/bin/hermes" +omarchy-remove-preinstalls >/dev/null +grep -Fxq 'printf user-hermes' "$test_home/.local/bin/hermes" || + fail "Remove Preinstalls preserves an unrelated Hermes launcher" +pass "Remove Preinstalls preserves an unrelated Hermes launcher" [[ -z $(omarchy-default-agent) ]] || fail "default agent is unset until one is chosen" pass "default agent is unset until one is chosen" From 6180ba51bf9ed636af125bb60c5a52ff8020c5d7 Mon Sep 17 00:00:00 2001 From: Talos Date: Sat, 22 Aug 2026 00:35:00 -0600 Subject: [PATCH 05/12] Preserve hidden Hermes launchers --- bin/omarchy-install-hermes-cli | 6 +++++- test/shell.d/default-agent-test.sh | 1 + test/shell.d/hermes-installer-test.sh | 15 +++++++++++++++ 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/bin/omarchy-install-hermes-cli b/bin/omarchy-install-hermes-cli index 87254036eb3..d955e1eb02a 100755 --- a/bin/omarchy-install-hermes-cli +++ b/bin/omarchy-install-hermes-cli @@ -20,7 +20,11 @@ case "${1:-}" in exit ;; --stub) - if is_installed; then + if [[ -e $hermes_command ]]; then + if ! is_installer_stub; then + exit 0 + fi + elif [[ ! -L $hermes_command ]] && is_installed; then exit 0 fi diff --git a/test/shell.d/default-agent-test.sh b/test/shell.d/default-agent-test.sh index d7bf711ff93..da3be369064 100644 --- a/test/shell.d/default-agent-test.sh +++ b/test/shell.d/default-agent-test.sh @@ -135,6 +135,7 @@ assert_lazy_stub "$omp_package" omp assert_lazy_stub "$crush_package" crush pass "custom agent lazy stubs preserve their mise packages" +: >"$hermes_install_log" 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" diff --git a/test/shell.d/hermes-installer-test.sh b/test/shell.d/hermes-installer-test.sh index 2913703fbf9..1b7f062bef6 100755 --- a/test/shell.d/hermes-installer-test.sh +++ b/test/shell.d/hermes-installer-test.sh @@ -44,12 +44,27 @@ export OMARCHY_TEST_CURL_LOG="$curl_log" export OMARCHY_TEST_INSTALLER_LOG="$installer_log" export OMARCHY_TEST_INSTALLER="$test_tmp/installer" +printf '#!/bin/bash\nprintf real-hermes\n' >"$test_home/.local/bin/hermes" +chmod +x "$test_home/.local/bin/hermes" +PATH="$mock_bin:$ROOT/bin:/usr/bin" omarchy-install-hermes-cli --stub +grep -Fxq 'printf real-hermes' "$test_home/.local/bin/hermes" || + fail "Hermes stub setup preserves an exact-path real launcher outside PATH" +pass "Hermes stub setup preserves an exact-path real launcher outside PATH" + +rm "$test_home/.local/bin/hermes" ln -s "$test_home/.hermes/missing-hermes" "$test_home/.local/bin/hermes" omarchy-install-hermes-cli --stub grep -Fxq '# omarchy-hermes-installer-stub' "$test_home/.local/bin/hermes" || fail "Hermes preinstall is a supported-installer lazy stub" pass "Hermes preinstall creates a supported-installer lazy stub" +printf '# stale marked stub\n' >>"$test_home/.local/bin/hermes" +omarchy-install-hermes-cli --stub +if grep -Fxq '# stale marked stub' "$test_home/.local/bin/hermes"; then + fail "Hermes stub setup replaces a marked Omarchy stub" +fi +pass "Hermes stub setup replaces a marked Omarchy stub" + "$test_home/.local/bin/hermes" --version mapfile -d '' -t installer_args <"$installer_log" [[ ${installer_args[*]} == "--skip-setup --non-interactive" ]] || From 16cba45659e9bf8c715cee7de88978330764c6ae Mon Sep 17 00:00:00 2001 From: JARVIS Date: Sat, 22 Aug 2026 03:13:42 -0600 Subject: [PATCH 06/12] Keep Hermes one-shot results visible --- bin/omarchy-agent | 15 ++++++-- bin/omarchy-agent-hermes-prompt | 25 +++++++++++++ test/shell.d/agent-hermes-prompt-test.sh | 46 ++++++++++++++++++++++++ test/shell.d/default-agent-test.sh | 11 ++++-- 4 files changed, 92 insertions(+), 5 deletions(-) create mode 100755 bin/omarchy-agent-hermes-prompt create mode 100755 test/shell.d/agent-hermes-prompt-test.sh diff --git a/bin/omarchy-agent b/bin/omarchy-agent index f0d159f7b79..b056b41d112 100755 --- a/bin/omarchy-agent +++ b/bin/omarchy-agent @@ -66,9 +66,18 @@ agy) ;; hermes) # Plain Hermes stays interactive and approval-gated. A supplied prompt uses - # one-shot mode, which Hermes documents as auto-bypassing approvals. - command=(hermes) - [[ -n ${prompt:-} ]] && command+=(-z "$prompt") + # one-shot mode, which auto-bypasses approvals. New terminal launches go + # through a small holder so the final answer stays visible; --inline returns + # it directly to the terminal the user already has open. + if [[ -n ${prompt:-} ]]; then + if [[ $inline == "true" ]]; then + command=(hermes -z "$prompt") + else + command=(omarchy-agent-hermes-prompt "$prompt") + fi + else + command=(hermes) + fi ;; copilot) command=(copilot --allow-all) diff --git a/bin/omarchy-agent-hermes-prompt b/bin/omarchy-agent-hermes-prompt new file mode 100755 index 00000000000..ebf6b60e9f0 --- /dev/null +++ b/bin/omarchy-agent-hermes-prompt @@ -0,0 +1,25 @@ +#!/bin/bash + +# omarchy:summary=Show a Hermes one-shot result until the user closes it +# omarchy:args= +# omarchy:hidden=true + +set -u + +if (($# != 1)); then + echo "Usage: omarchy-agent-hermes-prompt " >&2 + exit 1 +fi + +status=0 +hermes -z "$1" || status=$? + +printf '\n' +if ((status == 0)); then + echo "Hermes finished. Press Enter to close." +else + echo "Hermes exited with status $status. Press Enter to close." >&2 +fi +read -r + +exit "$status" diff --git a/test/shell.d/agent-hermes-prompt-test.sh b/test/shell.d/agent-hermes-prompt-test.sh new file mode 100755 index 00000000000..a9853e9a202 --- /dev/null +++ b/test/shell.d/agent-hermes-prompt-test.sh @@ -0,0 +1,46 @@ +#!/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" +hermes_log="$test_tmp/hermes-args" +output="$test_tmp/output" +mkdir -p "$mock_bin" + +cat >"$mock_bin/hermes" <<'SH' +#!/bin/bash +printf '%s\0' "$@" >"$OMARCHY_TEST_HERMES_LOG" +printf 'Portal crashed in its rendering worker.\n' +SH +chmod +x "$mock_bin/hermes" + +export PATH="$mock_bin:$ROOT/bin:$PATH" +export OMARCHY_TEST_HERMES_LOG="$hermes_log" + +coproc DIAGNOSIS { omarchy-agent-hermes-prompt "Crash facts" >"$output" 2>&1; } +diagnosis_pid=$DIAGNOSIS_PID + +for _ in $(seq 1 100); do + grep -Fq 'Portal crashed in its rendering worker.' "$output" && break + sleep 0.01 +done + +grep -Fq 'Portal crashed in its rendering worker.' "$output" || + fail "Hermes crash diagnosis prints its result" +kill -0 "$diagnosis_pid" 2>/dev/null || + fail "Hermes crash diagnosis keeps the terminal open after the result" +grep -Fq 'Press Enter to close' "$output" || + fail "Hermes crash diagnosis explains how to close the result" + +printf '\n' >&"${DIAGNOSIS[1]}" +wait "$diagnosis_pid" + +mapfile -d '' -t hermes_args <"$hermes_log" +[[ ${hermes_args[*]} == "-z Crash facts" ]] || + fail "Hermes crash diagnosis invokes one-shot mode with the complete prompt" "${hermes_args[*]}" +pass "Hermes one-shot diagnosis remains visible until Enter" diff --git a/test/shell.d/default-agent-test.sh b/test/shell.d/default-agent-test.sh index da3be369064..73e89a3205c 100644 --- a/test/shell.d/default-agent-test.sh +++ b/test/shell.d/default-agent-test.sh @@ -553,8 +553,8 @@ assert_launch crush crush run "Review this project" assert_launch grok grok --permission-mode bypassPermissions -- "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" -assert_launch hermes hermes -z "Review this project" -pass "agent launcher adapts prompts, with Hermes one-shot auto-bypassing approvals" +assert_launch hermes omarchy-agent-hermes-prompt "Review this project" +pass "agent launcher adapts prompts and keeps Hermes one-shot results visible" assert_bypass pi pi assert_bypass omp omp --auto-approve @@ -572,6 +572,13 @@ omarchy-agent assert_launched hermes "keeps ordinary interactive approval prompts" hermes pass "Hermes interactive launch remains approval-gated" +: >"$hermes_log" +omarchy-agent-prompt --inline "Review this project" +mapfile -d '' -t hermes_args <"$hermes_log" +[[ ${hermes_args[*]} == "-z Review this project" ]] || + fail "inline Hermes prompt returns its one-shot result to the current terminal" +pass "inline Hermes prompts remain direct one-shot commands" + printf '%s\n' "opencode" >"$agent_file" omarchy-agent mapfile -d '' -t launch_args <"$launch_log" From df0844785616a76689f62ddd883c2bf27d8bf4f4 Mon Sep 17 00:00:00 2001 From: JARVIS Date: Sat, 22 Aug 2026 13:42:29 -0600 Subject: [PATCH 07/12] Accept typed follow-ups in Hermes crash diagnosis --- bin/omarchy-agent-hermes-prompt | 34 +++++++++++++++++++----- test/shell.d/agent-hermes-prompt-test.sh | 30 ++++++++++++++++----- 2 files changed, 50 insertions(+), 14 deletions(-) diff --git a/bin/omarchy-agent-hermes-prompt b/bin/omarchy-agent-hermes-prompt index ebf6b60e9f0..f563aa1a23d 100755 --- a/bin/omarchy-agent-hermes-prompt +++ b/bin/omarchy-agent-hermes-prompt @@ -1,6 +1,6 @@ #!/bin/bash -# omarchy:summary=Show a Hermes one-shot result until the user closes it +# omarchy:summary=Show a Hermes one-shot result and accept typed follow-ups # omarchy:args= # omarchy:hidden=true @@ -12,14 +12,34 @@ if (($# != 1)); then fi status=0 -hermes -z "$1" || status=$? +session_id="" -printf '\n' -if ((status == 0)); then - echo "Hermes finished. Press Enter to close." -else +# The first call uses single-query mode with quiet output and the same +# auto-bypass approval semantics as `hermes -z`. Unlike -z, it prints the +# session id, which is how a typed follow-up resumes the same conversation. +output=$(hermes chat -q "$1" -Q --yolo 2>&1) +status=$? +printf '%s\n' "$output" + +session_id=$(printf '%s\n' "$output" | sed -n 's/^session_id: //p' | tail -1) + +while true; do + printf 'Ask a follow-up, or press Enter to close: ' + read -r followup || { printf '\n'; break; } + [[ -n $followup ]] || break + + if [[ -n $session_id ]]; then + output=$(hermes chat -q "$followup" -Q --yolo --resume "$session_id" 2>&1) + else + output=$(hermes chat -q "$followup" -Q --yolo 2>&1) + fi + status=$? + printf '%s\n' "$output" +done + +if ((status != 0)); then echo "Hermes exited with status $status. Press Enter to close." >&2 + read -r || true fi -read -r exit "$status" diff --git a/test/shell.d/agent-hermes-prompt-test.sh b/test/shell.d/agent-hermes-prompt-test.sh index a9853e9a202..8f17bcb6f8f 100755 --- a/test/shell.d/agent-hermes-prompt-test.sh +++ b/test/shell.d/agent-hermes-prompt-test.sh @@ -14,8 +14,12 @@ mkdir -p "$mock_bin" cat >"$mock_bin/hermes" <<'SH' #!/bin/bash -printf '%s\0' "$@" >"$OMARCHY_TEST_HERMES_LOG" -printf 'Portal crashed in its rendering worker.\n' +printf '%s\0' "$@" >>"$OMARCHY_TEST_HERMES_LOG" +if [[ " $* " == *" --resume "* ]]; then + printf 'Follow-up answer.\n' +else + printf 'session_id: mock-session-123\nPortal crashed in its rendering worker.\n' +fi SH chmod +x "$mock_bin/hermes" @@ -34,13 +38,25 @@ grep -Fq 'Portal crashed in its rendering worker.' "$output" || fail "Hermes crash diagnosis prints its result" kill -0 "$diagnosis_pid" 2>/dev/null || fail "Hermes crash diagnosis keeps the terminal open after the result" -grep -Fq 'Press Enter to close' "$output" || - fail "Hermes crash diagnosis explains how to close the result" +grep -Fq 'Ask a follow-up, or press Enter to close' "$output" || + fail "Hermes crash diagnosis invites a typed follow-up" + +printf 'why did it crash?\n' >&"${DIAGNOSIS[1]}" + +for _ in $(seq 1 100); do + grep -Fq 'Follow-up answer.' "$output" && break + sleep 0.01 +done + +grep -Fq 'Follow-up answer.' "$output" || + fail "Hermes crash diagnosis answers a typed follow-up" +kill -0 "$diagnosis_pid" 2>/dev/null || + fail "Hermes crash diagnosis stays open after the follow-up" printf '\n' >&"${DIAGNOSIS[1]}" wait "$diagnosis_pid" mapfile -d '' -t hermes_args <"$hermes_log" -[[ ${hermes_args[*]} == "-z Crash facts" ]] || - fail "Hermes crash diagnosis invokes one-shot mode with the complete prompt" "${hermes_args[*]}" -pass "Hermes one-shot diagnosis remains visible until Enter" +[[ ${hermes_args[*]} == "chat -q Crash facts -Q --yolo chat -q why did it crash? -Q --yolo --resume mock-session-123" ]] || + fail "Hermes crash diagnosis resumes the same session for follow-ups" "${hermes_args[*]}" +pass "Hermes crash diagnosis accepts typed follow-ups in the same session" From 59f0bc27832ba1a44207d565890925361cc67a3a Mon Sep 17 00:00:00 2001 From: JARVIS Date: Sat, 22 Aug 2026 13:43:50 -0600 Subject: [PATCH 08/12] Explain the diagnosis wait before Hermes one-shot runs --- bin/omarchy-agent-hermes-prompt | 5 +++++ test/shell.d/agent-hermes-prompt-test.sh | 8 ++++++++ 2 files changed, 13 insertions(+) diff --git a/bin/omarchy-agent-hermes-prompt b/bin/omarchy-agent-hermes-prompt index f563aa1a23d..1e3dbd76f31 100755 --- a/bin/omarchy-agent-hermes-prompt +++ b/bin/omarchy-agent-hermes-prompt @@ -14,6 +14,11 @@ fi status=0 session_id="" +# One-line heads-up before the first call: a full diagnosis takes minutes and +# Hermes one-shot mode prints nothing until it finishes, so an unexplained +# blank window reads as a hang. This line is what makes the wait legible. +echo "Diagnosing with Hermes — this may take a few minutes." + # The first call uses single-query mode with quiet output and the same # auto-bypass approval semantics as `hermes -z`. Unlike -z, it prints the # session id, which is how a typed follow-up resumes the same conversation. diff --git a/test/shell.d/agent-hermes-prompt-test.sh b/test/shell.d/agent-hermes-prompt-test.sh index 8f17bcb6f8f..0059185ea9f 100755 --- a/test/shell.d/agent-hermes-prompt-test.sh +++ b/test/shell.d/agent-hermes-prompt-test.sh @@ -29,6 +29,14 @@ export OMARCHY_TEST_HERMES_LOG="$hermes_log" coproc DIAGNOSIS { omarchy-agent-hermes-prompt "Crash facts" >"$output" 2>&1; } diagnosis_pid=$DIAGNOSIS_PID +for _ in $(seq 1 100); do + grep -Fq 'may take a few minutes' "$output" && break + sleep 0.01 +done + +grep -Fq 'may take a few minutes' "$output" || + fail "Hermes crash diagnosis explains that the run takes a while" + for _ in $(seq 1 100); do grep -Fq 'Portal crashed in its rendering worker.' "$output" && break sleep 0.01 From 3b5acb0419f7b38c14a1152f61b34d0237241c73 Mon Sep 17 00:00:00 2001 From: JARVIS Date: Sat, 22 Aug 2026 13:44:01 -0600 Subject: [PATCH 09/12] Document follow-up questions in crash diagnosis --- manual/17-ai.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/manual/17-ai.md b/manual/17-ai.md index 456a36af9a4..eb1eb53fba7 100644 --- a/manual/17-ai.md +++ b/manual/17-ai.md @@ -35,6 +35,8 @@ Left-click the bar icon for the panel, right-click to launch your default agent. Omarchy watches systemd-coredump for process crashes. When something segfaults, you'll get a "Process crashed" notification — click it, and the crash is handed to your default agent along with Omarchy's diagnose-crash skill, which walks the agent through establishing the facts from the core dump and deciding whether the crash is worth reporting upstream. You can also run it by hand against any PID from `coredumpctl list` with `omarchy agent crash `. +The diagnosis runs in a dedicated terminal. A full investigation takes a few minutes, and the window says so before Hermes goes quiet — the answer appears when it's done. The window then stays open: type a follow-up question to keep asking about the same crash in the same conversation, or press Enter to close. + The watching is on by default. Turn it off under _Trigger > Toggle > Crash Capture_ (or with `omarchy toggle crash-capture`) and the notifications stop; `omarchy agent crash ` still works by hand. ### Desktop apps From 767dd7a5ca7bed0c2b1f702d445854e663fcb30e Mon Sep 17 00:00:00 2001 From: JARVIS Date: Sat, 22 Aug 2026 15:04:45 -0600 Subject: [PATCH 10/12] Stream Hermes diagnosis progress live --- bin/omarchy-agent-hermes-prompt | 30 +++++++++++++++--------- manual/17-ai.md | 2 +- test/shell.d/agent-hermes-prompt-test.sh | 3 ++- 3 files changed, 22 insertions(+), 13 deletions(-) diff --git a/bin/omarchy-agent-hermes-prompt b/bin/omarchy-agent-hermes-prompt index 1e3dbd76f31..8b1d6e2f045 100755 --- a/bin/omarchy-agent-hermes-prompt +++ b/bin/omarchy-agent-hermes-prompt @@ -19,14 +19,24 @@ session_id="" # blank window reads as a hang. This line is what makes the wait legible. echo "Diagnosing with Hermes — this may take a few minutes." -# The first call uses single-query mode with quiet output and the same -# auto-bypass approval semantics as `hermes -z`. Unlike -z, it prints the -# session id, which is how a typed follow-up resumes the same conversation. -output=$(hermes chat -q "$1" -Q --yolo 2>&1) -status=$? -printf '%s\n' "$output" +# The first call uses single-query mode with the same auto-bypass approval +# semantics as `hermes -z`. Output is teed so Hermes' live progress (agent +# startup, reasoning, tool activity) streams to the window instead of being +# buffered until the end, and the session id is captured for follow-ups. +run_turn() { + local question=$1 + shift + local tmp + tmp=$(mktemp) + hermes chat -q "$question" --yolo "$@" 2>&1 | tee "$tmp" + status=${PIPESTATUS[0]} + if [[ -z $session_id ]]; then + session_id=$(sed -n -E 's/^(session_id|Session):[[:space:]]*([A-Za-z0-9_-]+).*/\2/p' "$tmp" | tail -1) + fi + rm -f "$tmp" +} -session_id=$(printf '%s\n' "$output" | sed -n 's/^session_id: //p' | tail -1) +run_turn "$1" while true; do printf 'Ask a follow-up, or press Enter to close: ' @@ -34,12 +44,10 @@ while true; do [[ -n $followup ]] || break if [[ -n $session_id ]]; then - output=$(hermes chat -q "$followup" -Q --yolo --resume "$session_id" 2>&1) + run_turn "$followup" --resume "$session_id" else - output=$(hermes chat -q "$followup" -Q --yolo 2>&1) + run_turn "$followup" fi - status=$? - printf '%s\n' "$output" done if ((status != 0)); then diff --git a/manual/17-ai.md b/manual/17-ai.md index eb1eb53fba7..e27eedfe532 100644 --- a/manual/17-ai.md +++ b/manual/17-ai.md @@ -35,7 +35,7 @@ Left-click the bar icon for the panel, right-click to launch your default agent. Omarchy watches systemd-coredump for process crashes. When something segfaults, you'll get a "Process crashed" notification — click it, and the crash is handed to your default agent along with Omarchy's diagnose-crash skill, which walks the agent through establishing the facts from the core dump and deciding whether the crash is worth reporting upstream. You can also run it by hand against any PID from `coredumpctl list` with `omarchy agent crash `. -The diagnosis runs in a dedicated terminal. A full investigation takes a few minutes, and the window says so before Hermes goes quiet — the answer appears when it's done. The window then stays open: type a follow-up question to keep asking about the same crash in the same conversation, or press Enter to close. +The diagnosis runs in a dedicated terminal. A full investigation takes a few minutes, and the window says so up front — Hermes' live progress (agent startup, reasoning, tool activity) streams to the window while it works, and the full report appears when it's done. The window then stays open: type a follow-up question to keep asking about the same crash in the same conversation, or press Enter to close. The watching is on by default. Turn it off under _Trigger > Toggle > Crash Capture_ (or with `omarchy toggle crash-capture`) and the notifications stop; `omarchy agent crash ` still works by hand. diff --git a/test/shell.d/agent-hermes-prompt-test.sh b/test/shell.d/agent-hermes-prompt-test.sh index 0059185ea9f..dfbfb6e0a65 100755 --- a/test/shell.d/agent-hermes-prompt-test.sh +++ b/test/shell.d/agent-hermes-prompt-test.sh @@ -18,6 +18,7 @@ printf '%s\0' "$@" >>"$OMARCHY_TEST_HERMES_LOG" if [[ " $* " == *" --resume "* ]]; then printf 'Follow-up answer.\n' else + printf 'Initializing agent...\n' printf 'session_id: mock-session-123\nPortal crashed in its rendering worker.\n' fi SH @@ -65,6 +66,6 @@ printf '\n' >&"${DIAGNOSIS[1]}" wait "$diagnosis_pid" mapfile -d '' -t hermes_args <"$hermes_log" -[[ ${hermes_args[*]} == "chat -q Crash facts -Q --yolo chat -q why did it crash? -Q --yolo --resume mock-session-123" ]] || +[[ ${hermes_args[*]} == "chat -q Crash facts --yolo chat -q why did it crash? --yolo --resume mock-session-123" ]] || fail "Hermes crash diagnosis resumes the same session for follow-ups" "${hermes_args[*]}" pass "Hermes crash diagnosis accepts typed follow-ups in the same session" From 50df8918b9b450a8a0c15e0cf5c8c8a62c328502 Mon Sep 17 00:00:00 2001 From: JARVIS Date: Sat, 22 Aug 2026 15:11:26 -0600 Subject: [PATCH 11/12] Harden diagnosis holder against test race and temp leaks --- bin/omarchy-agent-hermes-prompt | 16 +++++++++++----- test/shell.d/agent-hermes-prompt-test.sh | 6 ++++++ 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/bin/omarchy-agent-hermes-prompt b/bin/omarchy-agent-hermes-prompt index 8b1d6e2f045..0f3d46c70ad 100755 --- a/bin/omarchy-agent-hermes-prompt +++ b/bin/omarchy-agent-hermes-prompt @@ -13,6 +13,12 @@ fi status=0 session_id="" +tmp_file="" + +cleanup() { + [[ -n $tmp_file ]] && rm -f "$tmp_file" +} +trap cleanup EXIT # One-line heads-up before the first call: a full diagnosis takes minutes and # Hermes one-shot mode prints nothing until it finishes, so an unexplained @@ -26,14 +32,14 @@ echo "Diagnosing with Hermes — this may take a few minutes." run_turn() { local question=$1 shift - local tmp - tmp=$(mktemp) - hermes chat -q "$question" --yolo "$@" 2>&1 | tee "$tmp" + tmp_file=$(mktemp) + hermes chat -q "$question" --yolo "$@" 2>&1 | tee "$tmp_file" status=${PIPESTATUS[0]} if [[ -z $session_id ]]; then - session_id=$(sed -n -E 's/^(session_id|Session):[[:space:]]*([A-Za-z0-9_-]+).*/\2/p' "$tmp" | tail -1) + session_id=$(sed -n -E 's/^(session_id|Session):[[:space:]]*([A-Za-z0-9_-]+).*/\2/p' "$tmp_file" | tail -1) fi - rm -f "$tmp" + rm -f "$tmp_file" + tmp_file="" } run_turn "$1" diff --git a/test/shell.d/agent-hermes-prompt-test.sh b/test/shell.d/agent-hermes-prompt-test.sh index dfbfb6e0a65..bfb79602237 100755 --- a/test/shell.d/agent-hermes-prompt-test.sh +++ b/test/shell.d/agent-hermes-prompt-test.sh @@ -47,6 +47,12 @@ grep -Fq 'Portal crashed in its rendering worker.' "$output" || fail "Hermes crash diagnosis prints its result" kill -0 "$diagnosis_pid" 2>/dev/null || fail "Hermes crash diagnosis keeps the terminal open after the result" + +for _ in $(seq 1 100); do + grep -Fq 'Ask a follow-up, or press Enter to close' "$output" && break + sleep 0.01 +done + grep -Fq 'Ask a follow-up, or press Enter to close' "$output" || fail "Hermes crash diagnosis invites a typed follow-up" From 63ff41dfd0f6b9d029ae5c555c01b3542281f9b5 Mon Sep 17 00:00:00 2001 From: angel mogollon Date: Fri, 28 Aug 2026 23:44:56 -0600 Subject: [PATCH 12/12] Link Omarchy skills into existing Hermes profiles Folds in the existing-profile loop from #8611: provision-user and the Hermes migration symlink shipped skills into each existing ~/.hermes/profiles//skills without creating profiles, with test and doc coverage. --- bin/omarchy-provision-user | 7 +++++++ docs/file-layout.md | 5 +++-- manual/17-ai.md | 2 +- migrations/1787063342.sh | 7 +++++++ test/shell.d/provision-user-test.sh | 6 +++--- 5 files changed, 21 insertions(+), 6 deletions(-) diff --git a/bin/omarchy-provision-user b/bin/omarchy-provision-user index 770171f4de6..3bcbf7a8f27 100755 --- a/bin/omarchy-provision-user +++ b/bin/omarchy-provision-user @@ -94,6 +94,13 @@ for skill in "$OMARCHY_PATH"/default/agents/skills/*/; do ln -sfn "$skill" ~/.pi/agent/skills/"$name" ln -sfn "$skill" ~/.gemini/config/skills/"$name" ln -sfn "$skill" ~/.hermes/skills/"$name" + if [[ -d ~/.hermes/profiles ]]; then + for profile in ~/.hermes/profiles/*/; do + [[ -d $profile ]] || continue + mkdir -p "$profile/skills" + ln -sfn "$skill" "$profile/skills/$name" + done + fi done mkdir -p ~/Downloads ~/Pictures ~/Videos ~/.config/gtk-3.0 diff --git a/docs/file-layout.md b/docs/file-layout.md index 2a9d965bf89..08516524308 100644 --- a/docs/file-layout.md +++ b/docs/file-layout.md @@ -198,11 +198,12 @@ Runs once per user. It does **not** copy `~/.config/**`, `~/.bashrc`, `flags.lua`, or the nautilus extensions — `/etc/skel` already seeded those. It only does the things `/etc/skel` can't: -- Skill symlinks `~/.{agents,claude,codex,pi/agent}/skills/` → +- Skill symlinks `~/.{agents,claude,codex,pi/agent,hermes}/skills/` (and each `~/.hermes/profiles/*/skills/`) → `$OMARCHY_PATH/default/agents/skills/`, looping over every skill directory there (currently `omarchy` and `diagnose-crash`) so new skills need no edit. Symlinks (not copies) so `omarchy dev link` against a dev - checkout repoints them correctly. + checkout repoints them correctly. Hermes profile dirs are only linked when + they already exist — provision does not create Hermes profiles. - `xdg-user-dirs-update` (Templates/Public/Desktop folded back into `$HOME`) and `~/.config/gtk-3.0/bookmarks` (needs `$HOME` expansion). - Hyprland's package-owned default input reads `XKBLAYOUT` / `XKBVARIANT` diff --git a/manual/17-ai.md b/manual/17-ai.md index 3def7260c76..9f3e5bc0e30 100644 --- a/manual/17-ai.md +++ b/manual/17-ai.md @@ -52,6 +52,6 @@ Omarchy recommends two ways of running local LLM models: LM Studio and Ollama. L ### The Omarchy Skill -Agent skills help AI use specific tools in a specific way, and Omarchy ships with a default skill for tailoring the system. Like tweaking your Hyprland config, adjusting the bar, or even creating a new theme from scratch. It's symlinked into the skill directories for Claude Code (`~/.claude/skills`), Codex (`~/.codex/skills`), Pi (`~/.pi/agent/skills`), Antigravity (`~/.gemini/config/skills`), Hermes (`~/.hermes/skills`), and the generic `~/.agents/skills` location, so most harnesses pick it up automatically. +Agent skills help AI use specific tools in a specific way, and Omarchy ships with a default skill for tailoring the system. Like tweaking your Hyprland config, adjusting the bar, or even creating a new theme from scratch. It's symlinked into the skill directories for Claude Code (`~/.claude/skills`), Codex (`~/.codex/skills`), Pi (`~/.pi/agent/skills`), Antigravity (`~/.gemini/config/skills`), Hermes (`~/.hermes/skills` and each `~/.hermes/profiles/*/skills`), and the generic `~/.agents/skills` location, so most harnesses pick it up automatically. But you should treat this skill as experimental. Different models will use it to different effect. It's best to run in plan mode first, so you have an idea of what the agent would like to change. And then be ready to rollback changes or even invoking `omarchy reinstall configs`, if the agent makes a mess of everything. diff --git a/migrations/1787063342.sh b/migrations/1787063342.sh index 3f7a1199b0b..bfa737aa5ef 100644 --- a/migrations/1787063342.sh +++ b/migrations/1787063342.sh @@ -15,4 +15,11 @@ for skill in "$OMARCHY_PATH"/default/agents/skills/*/; do skill=${skill%/} name=${skill##*/} ln -sfn "$skill" "$HOME/.hermes/skills/$name" + if [[ -d $HOME/.hermes/profiles ]]; then + for profile in "$HOME"/.hermes/profiles/*/; do + [[ -d $profile ]] || continue + mkdir -p "$profile/skills" + ln -sfn "$skill" "$profile/skills/$name" + done + fi done diff --git a/test/shell.d/provision-user-test.sh b/test/shell.d/provision-user-test.sh index 4d1e441b5fd..cf34656c76a 100755 --- a/test/shell.d/provision-user-test.sh +++ b/test/shell.d/provision-user-test.sh @@ -8,7 +8,7 @@ test_tmp=$(mktemp -d) trap 'rm -rf "$test_tmp"' EXIT mock_bin="$test_tmp/bin" -mkdir -p "$mock_bin" "$test_tmp/home" +mkdir -p "$mock_bin" "$test_tmp/home" "$test_tmp/home/.hermes/profiles/james" for command in xdg-user-dirs-update xdg-settings xdg-mime; do printf '#!/bin/bash\nexit 0\n' >"$mock_bin/$command" @@ -26,7 +26,7 @@ HOME="$test_tmp/home" PATH="$mock_bin:$ROOT/bin:$PATH" OMARCHY_PATH="$ROOT" \ OMARCHY_INSTALL="$test_tmp/install" bash "$ROOT/bin/omarchy-provision-user" >/dev/null || fail "omarchy-provision-user finishes" -for agent_dir in .gemini/config/skills .hermes/skills; do +for agent_dir in .gemini/config/skills .hermes/skills .hermes/profiles/james/skills; do for skill in omarchy diagnose-crash; do link="$test_tmp/home/$agent_dir/$skill" [[ -L $link && $(readlink "$link") == "$ROOT/default/agents/skills/$skill" ]] || @@ -34,4 +34,4 @@ for agent_dir in .gemini/config/skills .hermes/skills; do done done -pass "omarchy-provision-user provisions both shipped skills for Antigravity and Hermes" +pass "omarchy-provision-user provisions both shipped skills for Antigravity, Hermes, and Hermes profiles"