Skip to content

Instantly share code, notes, and snippets.

@OutThisLife
Created July 23, 2026 18:12
Show Gist options
  • Select an option

  • Save OutThisLife/0c7407c203fee74e9b9bd2ab38e762d1 to your computer and use it in GitHub Desktop.

Select an option

Save OutThisLife/0c7407c203fee74e9b9bd2ab38e762d1 to your computer and use it in GitHub Desktop.
Hermes worktree-aware launchers (hermes / htui / hgui) — run Hermes from any git worktree without a full per-worktree reinstall

Hermes worktree-aware launchers (hermes / htui / hgui)

Run Hermes from any git worktree without a full per-worktree reinstall.

A git worktree only carries tracked files, so a fresh worktree has no node_modules (~1.3G) and no .venv. These launchers make a worktree runnable by sharing the main checkout's deps when they're byte-identical, and falling back to a local install (stamped, so it only reinstalls when the lockfile actually changes) when the branch bumps a dependency.

Command What it runs
hermes … Hermes CLI/TUI using the worktree's own ui-tui (or --dev for a from-source build)
htui … shortcut for hermes --tui --dev
hgui [path] the Electron desktop app (apps/desktop), with orphan-backend cleanup on exit

Install

Drop 55-hermes.zsh in ~/.zsh.d/ (or source it from your ~/.zshrc) and set your checkout root:

export DEV_ROOT="$HOME/Developer"                          # wherever your repos live
export HERMES_MAIN_CHECKOUT="$DEV_ROOT/hermes-agent"       # deps donor + venv source
export HERMES_GUI_DEPS_CHECKOUT="$DEV_ROOT/hermes-agent"   # optional; defaults to main

Requires the main checkout to have a real install once: cd $HERMES_MAIN_CHECKOUT && npm ci and a .venv (python -m venv .venv && .venv/bin/pip install -e .).

How it works

  • Deps get shared, not copied. When a worktree's package-lock.json is byte-identical to the main checkout's, node_modules is symlinked from main. Deleting a symlinked/idle node_modules is safe — the next run re-links it.
  • Divergent locks self-heal. If the branch bumped a dep, the launcher runs a local npm ci and stamps the lock hash, so it reinstalls only when the lock changes again.
  • Python reuses main's interpreter directly ($HERMES_MAIN_CHECKOUT/.venv/bin/python).
  • hgui cleans up after itself. Electron dev spawns ephemeral dashboard --port 0 backends + a vite server on 5174; a trap reaps them on Ctrl+C/exit so they don't leak.

Depends on a killport helper (kills whatever holds a port). Swap in your own or lsof -ti:PORT | xargs kill if you don't have one.

# Hermes worktree-aware launcher.
export HERMES_MAIN_CHECKOUT="$DEV_ROOT/hermes-agent"
_hermes_root() {
local root
root="$(git rev-parse --show-toplevel 2>/dev/null)" || return 1
[[ -f "$root/hermes_cli/main.py" && -d "$root/ui-tui" ]] || return 1
print -r "$root"
}
hermes() {
local root arg use_tui_dir
use_tui_dir=1
root="$(_hermes_root)" || {
command hermes "$@"
return
}
for arg in "$@"; do
case "$arg" in
--dev|--dev=*)
use_tui_dir=0
break
;;
esac
done
# ui-tui is a root workspace, so its deps track the root lock. Link main's
# tree only when locks match; a stale link to divergent deps is repaired.
if [[ -L "$root/ui-tui/node_modules" ]] && ! _hermes_locks_match "$root" "$HERMES_MAIN_CHECKOUT"; then
rm -f "$root/ui-tui/node_modules"
fi
if [[ ! -e "$root/ui-tui/node_modules" ]]; then
if _hermes_locks_match "$root" "$HERMES_MAIN_CHECKOUT" && [[ -d "$HERMES_MAIN_CHECKOUT/ui-tui/node_modules" ]]; then
ln -s "$HERMES_MAIN_CHECKOUT/ui-tui/node_modules" "$root/ui-tui/node_modules"
else
_hermes_local_install "$root" || true
fi
fi
if (( use_tui_dir )); then
PYTHONPATH="$root" \
HERMES_TUI_DIR="$root/ui-tui" \
"$HERMES_MAIN_CHECKOUT/.venv/bin/python" -m hermes_cli.main "$@"
else
# Force-disable prebuilt override for --dev flow.
env -u HERMES_TUI_DIR \
PYTHONPATH="$root" \
"$HERMES_MAIN_CHECKOUT/.venv/bin/python" -m hermes_cli.main "$@"
fi
}
htui() {
hermes --tui --dev "$@"
}
_hermes_npm_root_ok() {
local root="$1"
[[ -f "${root%/}/node_modules/vite/package.json" ]]
}
# Symlink node_modules from deps checkout when missing or a broken partial install.
_hermes_link_node_modules() {
local target="$1" source="$2"
local dest="${target%/}/node_modules" src="${source%/}/node_modules"
[[ -d "$src" ]] || return 1
if [[ -L "$dest" ]]; then
return 0
fi
if [[ -e "$dest" ]]; then
if _hermes_npm_root_ok "$target"; then
return 0
fi
if _hermes_npm_root_ok "$source"; then
rm -rf "$dest"
else
return 1
fi
fi
ln -s "$src" "$dest"
}
# A deps checkout only stands in for another when their locks are byte-identical.
_hermes_locks_match() {
cmp -s "${1%/}/package-lock.json" "${2%/}/package-lock.json"
}
# Real (non-symlinked) install in the worktree, stamped against its own lock so
# we reinstall only when the lock actually changes.
_hermes_local_install() {
local root="${1%/}" stamp hash
stamp="$root/node_modules/.hgui-lock"
hash="${$(shasum "$root/package-lock.json" 2>/dev/null)%% *}"
if [[ -d "$root/node_modules" && ! -L "$root/node_modules" \
&& -f "$stamp" && "$(<$stamp)" == "$hash" ]]; then
return 0
fi
echo "hgui: worktree deps diverge from deps checkout; installing locally (npm ci)…" >&2
[[ -L "$root/node_modules" ]] && rm -f "$root/node_modules"
[[ -L "$root/apps/desktop/node_modules" ]] && rm -f "$root/apps/desktop/node_modules"
[[ -L "$root/ui-tui/node_modules" ]] && rm -f "$root/ui-tui/node_modules"
( cd "$root" && npm ci ) || return 1
print -r -- "$hash" > "$stamp"
}
_hermes_resolve_checkout() {
local arg="$1" root
if [[ -n "$arg" ]]; then
root="$(cd "$arg" 2>/dev/null && pwd)" || return 1
[[ -f "$root/hermes_cli/main.py" ]] || return 1
print -r "$root"
return 0
fi
_hermes_root
}
# Desktop dev spawns ephemeral `dashboard --port 0` backends; Electron often
# survives a Ctrl+C through concurrently without reaping them.
_hermes_gui_cleanup() {
local root="$1" pid cmdline dashboard_pids vite_pids
if [[ -n "$root" ]]; then
pkill -TERM -f "${root}/apps/desktop/node_modules/electron/dist/Electron" 2>/dev/null
pkill -TERM -f "wait-on http://127.0.0.1:5174" 2>/dev/null
fi
dashboard_pids=("${(@f)$(pgrep -f 'hermes_cli\.main.*dashboard' 2>/dev/null)}")
for pid in $dashboard_pids; do
cmdline="$(ps -p "$pid" -o command= 2>/dev/null)" || continue
[[ "$cmdline" == *"--port 0"* || "$cmdline" == *"--port=0"* ]] || continue
kill -TERM "$pid" 2>/dev/null
done
if lsof -t -i:5174 >/dev/null 2>&1; then
vite_pids=("${(@f)$(lsof -t -i:5174 2>/dev/null)}")
for pid in $vite_pids; do kill -TERM "$pid" 2>/dev/null; done
sleep 0.5
killport 5174
fi
sleep 0.5
if [[ -n "$root" ]]; then
pkill -KILL -f "${root}/apps/desktop/node_modules/electron/dist/Electron" 2>/dev/null
fi
for pid in $dashboard_pids; do
cmdline="$(ps -p "$pid" -o command= 2>/dev/null)" || continue
[[ "$cmdline" == *"--port 0"* || "$cmdline" == *"--port=0"* ]] || continue
kill -KILL "$pid" 2>/dev/null
done
}
hgui() {
local root deps desktop deps_desktop py arg cleanup_done=0
arg="$1"
root="$(_hermes_resolve_checkout "$arg")" || {
if [[ -n "$arg" ]]; then
echo "hgui: $arg is not a Hermes checkout" >&2
else
echo "hgui: not inside a Hermes checkout (usage: hgui [path])" >&2
fi
return 1
}
deps="${HERMES_GUI_DEPS_CHECKOUT:-$HERMES_MAIN_CHECKOUT}"
desktop="$root/apps/desktop"
deps_desktop="$deps/apps/desktop"
if [[ ! -d "$desktop" ]]; then
echo "hgui: $root does not have apps/desktop" >&2
return 1
fi
if [[ ! -d "$deps_desktop" ]]; then
echo "hgui: set HERMES_GUI_DEPS_CHECKOUT to a checkout with apps/desktop deps" >&2
return 1
fi
# Link main's tree only when this worktree's lock matches; otherwise a branch
# that bumps a dep would silently run against stale packages. On divergence,
# install locally instead.
if _hermes_locks_match "$root" "$deps"; then
_hermes_link_node_modules "$desktop" "$deps_desktop" || true
_hermes_link_node_modules "$root" "$deps" || true
elif ! _hermes_local_install "$root"; then
echo "hgui: dependency install failed" >&2
return 1
fi
if ! _hermes_npm_root_ok "$root"; then
echo "hgui: run once: cd $deps && npm ci" >&2
return 1
fi
py="$HERMES_MAIN_CHECKOUT/.venv/bin/python"
[[ -x "$py" ]] || py="$(command -v python3)"
# Vite dev server is fixed at 5174; clear a stale session from another hgui.
if lsof -t -i:5174 >/dev/null 2>&1; then
echo "hgui: stopping process on port 5174" >&2
killport 5174
fi
_hgui_cleanup_trap() {
(( cleanup_done )) && return
cleanup_done=1
_hermes_gui_cleanup "$root"
}
trap '_hgui_cleanup_trap' INT TERM EXIT
(
cd "$desktop" || exit 1
export PATH="$root/node_modules/.bin:$PATH"
HERMES_DESKTOP_HERMES_ROOT="$root" \
HERMES_DESKTOP_PYTHON="$py" \
HERMES_DESKTOP_IGNORE_EXISTING=1 \
HERMES_DESKTOP_CWD="$root" \
npm run dev
)
local rc=$?
trap - INT TERM EXIT
_hgui_cleanup_trap
return $rc
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment