The refusals that leave a self-installed Hermes alone came before the command was replaced only for a finished runtime. An unfinished one was still bootstrapped over a git status that could not be read, since a failed status read as a clean tree, and a half-built app beside it, or an edit the Linux runtime patch could not land on, was found only after the user's command had been saved aside and replaced and main switched. All three are asked first now, in both states, so a run that is going to stop leaves the launcher, the checkout and ~/.local/bin as they were.
Co-Authored-By: Codex XHigh <noreply@openai.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Hermes is only ever installed through the app. Choosing it as the default agent used to stand aside for a hermes that worked but came from somewhere else, and to refuse one that predated seeded sessions; both left the machine on a Hermes that was not the app's, which is the one Omarchy prepares for in-app updates and hands the theme to. Now --check answers only for the app's own Hermes, and --now installs the app whatever answered to hermes before, saving the previous command aside as it always did for the app path. The desktop installer no longer adds the package itself, since the shared install does.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Whether the environment mise built is gone was read by searching the global listing for the prefix `"pipx:hermes-agent`, so a tool that merely starts the same way, `pipx:hermes-agent-tools` say, read as the retired one still requested. Removing the real one changed nothing, the recheck failed again, and the migration stayed pending on every update. The key is matched whole now, with or without its options.
Co-Authored-By: Codex XHigh <noreply@openai.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The wait before clearing a stale shallow.lock looked for a git whose command line began with `git`, so one started as `/usr/bin/git`, which is what a caller that resolved it with `which` runs, was never seen: its lock, once a minute old, would have been cleared under it and a second fetch started against the same shallow file. The path is allowed only ahead of the name, at the start of the command line, so a process that merely names git in an argument, an editor opened on /usr/bin/git from inside the runtime say, is not waited for.
Co-Authored-By: Codex XHigh <noreply@openai.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Upstream's installer runs `npx playwright install chromium` for the browser tools, and npx asks before fetching a package it does not have. Over ssh, with no terminal, it goes ahead; in the floating terminal the menu opens for choosing Hermes it printed "Ok to proceed? (y)" and waited, so an install a user had every reason to walk away from sat there until someone typed y. The mise-built Hermes this replaces never asked anything. `--skip-setup` already answers the wizard the same way, and the app's own bootstrap never had a terminal to ask in.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Choosing Hermes as the default agent built it through mise: a pipx environment with no checkout, so `hermes update` had nothing to move, and the only Hermes that could update itself was the one Hermes Desktop set up. Both paths now run the same setup. omarchy-install-hermes-cli installs the hermes-desktop package and runs upstream's installer from it, pinned to the packaged release and started on main, exactly as Install > AI did; omarchy-install-ai-hermes is that plus opening the app. The terminal, the default agent and the app share one runtime, and it updates itself.
--check answers whether --now has anything left to do, not merely whether a hermes runs: choosing Hermes from the menu asks first and opens a terminal only on a no, so a yes has to mean no minutes-long step would run where nobody can see it. With the app installed that means the runtime's own command, its completion marker and the seeded packaged app; a finished runtime whose command is gone, somebody else's, or its own but unable to run gets it back from upstream's path stage without bootstrapping again. Either way the command has to be the one PATH finds, because omarchy-agent runs bare `hermes` and Omarchy puts mise's shims ahead of ~/.local/bin; a command in the way is named rather than installed over. The modes are named outright because the app's launcher used to call this command with no arguments to reconcile a mise copy; a default of --now would turn every launch into an install. --check still refuses to run the retired wrapper, since running it built Hermes through mise, and a machine whose migration is pending can still have it on PATH.
Provisioning no longer writes the wrapper, Remove Preinstalls no longer looks for it, and the wrapper, the environment it built and what proves them Omarchy's are known to the installer alone: --retire-mise is the migration's whole job, and --now runs the same removal once the runtime installer has saved the wrapper aside, so a user who chose Hermes before their migration ran is not left with mise's shim answering `hermes`. Only the wrapper proves the environment is Omarchy's, at its path or in that saved copy, so the environment goes first and the wrapper last, judged by mise neither having it installed nor still requesting it; a removal that leaves either behind, or a listing that cannot be read, mise missing included, stops with the commands to finish by hand and leaves the migration pending. The migration that once installed the wrapper is kept as a no-op for late updaters, and one whose default agent was Hermes is told to choose it again.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Codex XHigh <noreply@openai.com>
A failed --remove told the user to run it again, but that advice could
never work: rm -f has usually taken the marked stub by the time the
failure is judged, and a rerun that finds nothing it owns succeeds
without touching the mise environment it was asked to finish removing.
Spell out the three commands that complete the job by hand instead.
Also correct the story the probe comment told: chat never lost
--oneshot in v0.20 -- no released Hermes defined it there. It lived at
the top level until v0.21 added chat's own, so the old probe was keyed
to a flag no release ever carried under chat, and every install read
as not ready. Recorded straight so a future hermes-desktop bump to
v0.21+, which would make the old probe pass on the desktop path alone,
cannot read as the fix.
Findings from omarchybot's review (Opus 5, with Codex at xhigh).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
Two review follow-ups. The probe counted any mention of --tui/--query in
the help as support -- Hermes already writes "With --tui:" into --dev's
description, so a release that dropped the option while keeping the
prose would still read as ready. A flag now counts only when followed by
a shape argparse prints after a definition: the usage bracket, the gap
before same-line help text, an uppercase metavar, or the line end. Not
probed by parsing a real invocation on purpose -- a release that ignores
unknown arguments would turn the probe into a live session.
And the teardown trusted its commands: a stub rm that failed aborted
Remove Hermes under set -e before any ~/.hermes handling, while mise
failures vanished into || true. --remove now attempts every step, then
judges by what is left -- the marked stub still present, or mise still
resolving the tool -- and Remove Hermes tolerates the failure until the
runtime is handled, then carries it in its exit code.
Findings from the same codex review at xhigh, verified and proven by
mutation before landing.
Co-Authored-By: Codex <noreply@openai.com>
Co-Authored-By: Claude <noreply@anthropic.com>
The teardown removed the Omarchy tool spec from mise unconditionally,
so removing Hermes Desktop could destroy a mise environment the user
had built against the same spec while sparing their wrapper -- the very
command the removal claims to preserve, broken behind its back. The
whole teardown now turns on the marked stub, as replacement already
does; the desktop takeover keeps its own bargain, where a second Hermes
goes whoever built it and the app still provides the command after.
Also close the probe over underscores -- _ continues a flag name just
as - does, so --tui_mode no longer answers for --tui -- and pin the
gaps review found in the tests: each flag must match on its own (either
grep could be deleted before without a failure), a foreign wrapper's
mise environment must survive --remove, and Remove Hermes must tear
down the CLI in the interrupted-install case, not only after the app's
runtime landed.
Findings from an independent codex review at xhigh, each verified
against the source and proven by mutation before landing.
Co-Authored-By: Codex <noreply@openai.com>
Co-Authored-By: Claude <noreply@anthropic.com>
Remove Hermes dropped the desktop package and its ~/.hermes runtime but
never touched the mise CLI, on the assumption the install-time handoff
had already removed it. A CLI the app never superseded -- an interrupted
install, or the terminal CLI from before the app existed -- was left
stranded on PATH after uninstall.
Add a --remove mode to omarchy-install-hermes-cli that performs the same
teardown the desktop takeover already does (mise rm -g + mise uninstall,
and the marked stub), and call it from omarchy-remove-ai-hermes. The tool
spec and ownership marker stay defined in one place, so the takeover and
teardown paths cannot drift. Scoped to what Omarchy owns: a Hermes the
user installed themselves is left alone.
Co-Authored-By: Claude <noreply@anthropic.com>
Review follow-up on the readiness probe. The two greps were fixed-string
substring matches, so a future release listing only --tui-theme or
--query-log while dropping the bare --tui/--query omarchy-agent passes
would read as ready -- the same false verdict inverted. Anchor both to a
flag boundary.
Add a regression case pinning that a substring-only help is rejected,
and one exercising --check through a mise-installed hermes in both
capability directions: the desktop and foreign cases only covered their
own wrappers, and the mise path is what a machine without the app runs.
Co-Authored-By: Claude <noreply@anthropic.com>
Hermes v0.20 removed chat's --oneshot flag, which hermes_prompt_ready
used as its capability marker. A fully bootstrapped Hermes Desktop
install then read as not ready: --check failed forever, the default
agent flow looped back into the installer, and --now dead-ended with
"Launch Hermes Desktop once to finish installing it" on a machine
where it already had.
Probe for --tui and --query instead: the flags omarchy-agent actually
passes to seed an interactive session, rather than one that merely
shipped alongside them.
Hermes now keeps chat queries interactive and literal to TUI control syntax, so launch it directly and let its own session flow replace the local one-shot, usage-file, and resume bridge.
Gate installation on the capability added with native interactive queries, preserve unowned mise environments, and keep the unprompted launch path unchanged.
Co-Authored-By: Codex XHigh <noreply@openai.com>
Bind option-looking prompts to one-shot mode, require a successful completed usage report before resuming, replay the prompt after first-run setup, and reject Hermes runtimes that lack the session-report capability.
Co-Authored-By: Codex XHigh <noreply@openai.com>
Three files spelled out the line that marks ~/.local/bin/hermes as Omarchy's:
the installer that writes it, Remove Preinstalls, and the migration. Two of
them were copies, and a change to what ownership means would have left them
matching a line nobody writes any more -- Remove Preinstalls quietly sweeping
nothing, the migration mistaking Omarchy's own wrapper for a stranger's.
omarchy-install-hermes-cli --owns answers it now, and the other two ask. The
installer's own metadata was also a flag behind: --check has been there since
this landed and was never listed.
A test pins the marker to one file, so a second copy fails rather than drifts.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
desktop_hermes_ready decided from a marker file and a text match, while a
hermes the user installed themselves had to answer --version before it counted.
The marker says the app's install once landed, not that it is still there, so a
runtime deleted afterwards left --check reporting success: the default agent
records Hermes, skips the install terminal, and the launch fails.
It now runs the command, on the same 15 second budget the app itself uses. The
path match is a plain string for the same reason it is in the remover -- the
dot in ~/.hermes would otherwise claim a wrapper pointing at ~/xhermes.
foreign_hermes_runs never tested foreignness, only that the command runs, so it
is hermes_runs now and both callers share it.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Codex XHigh <noreply@openai.com>
The stub exports UV_PYTHON so mise builds Hermes against 3.13, which Hermes
requires and Arch's Python is past. Exported, it survived the exec into Hermes
itself and reached every command the agent shells out to. Hermes is a coding
agent that runs commands in the user's own repositories, so a `uv venv` or
`uv sync` there resolved 3.13 as well: on a project declaring
requires-python >=3.14, uv warns that the interpreter contradicts it and builds
the venv anyway.
Dropping it at the handover keeps the pin over the install, where it belongs.
mise x resolves the tool it already installed without it.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Codex XHigh <noreply@openai.com>
Hermes joins Install > AI as a desktop app, sits beside it under Remove > AI,
and becomes a choice in the default-agent list. The CLI installs through
omarchy-install-hermes-cli rather than a bare `mise use`, so its interpreter
is pinned before mise builds it.
Rebased onto quattro. Ori claimed U+E909 in #7709 while this branch was open,
so the Hermes mark moves to U+E90A in the icon font, the menu entries, the
font README, and the charset the menu test pins. The glyph outline itself is
unchanged; it is spliced in beside Ori rather than over it.
Co-Authored-By: witcheer <witcheer.eth@gmail.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SySdB3RtCA8BNv6Am246BP