Hermes Safe Auto Upgrade
Use this skill when a user wants Hermes to periodically check for official updates and automatically upgrade itself safely, while preserving local work and notifying the user before and after a real upgrade.
When to use
- Hermes is installed from git under
~/.hermesand~/.hermes/hermes-agent - The user wants scheduled update checks (for example every Friday at 18:00 in
America/Los_Angeles) - The user wants automatic backup of local changes before upgrade
- The user wants a pre-upgrade notification when a new version is detected and a final result notification after completion
Required safety model
Treat the workflow as a two-repo system, but validate the actual remotes on disk before assuming the ideal layout:
~/.hermes- preferred official update source:
upstream - common backup remote:
origin - if
upstreamis missing, do not guess a new official remote URL; first verify whether the repo only has a known-safeoriginand whether that is the only confirmable source
- preferred official update source:
~/.hermes/hermes-agent- common official update source:
origin - common backup remote:
fork
- common official update source:
Only allow automatic upgrade on branch main.
If a repo is not on main, a required remote is missing, git has an in-progress merge/rebase/cherry-pick state, push/tag fails, or verification fails, stop and report the blocking reason.
Proven workflow
1. Inspect prerequisites first
Check these live rather than assuming them:
gh auth status- git remotes for both repos
- current branch for both repos
git status -sb- whether
~/.hermeshasupstream - whether any candidate official repo can be safely confirmed from local config,
gh repo view, or other grounded evidence
Real findings from validation:
~/.hermesmay have noupstreamat all- the existing
originfor~/.hermesmay be a private fork withparent = null - if an official upstream cannot be safely confirmed, prefer reconfiguring the automation to use the known-good existing remote rather than inventing a speculative
upstream ~/.hermes/hermes-agentmay be left on a feature branch; automatic upgrade must remain blocked until it is back onmain
2. Implement a dedicated wrapper script
Create a script such as:
~/.hermes/scripts/auto_safe_upgrade.py
The wrapper should add the missing safety layer around the built-in Hermes upgrade path:
- acquire a lock file to prevent re-entry
- verify
ghauth - verify both repos and required remotes
- fetch upstream refs/tags
- detect whether updates are pending
- if no update is pending, exit quietly
- if update is pending:
- in older designs, commit/push/tag may be part of backup strategy
- in the current Hermes-home script, the operator-safe path is typically:
- stash local changes with
git stash --include-untracked - switch/ensure
mainbranch - fetch from
upstreamand rebase ontoupstream/main - verify, restore branch/stash, and fail-closed on any verification failure
- stash local changes with
- if verification fails, rollback and report that rollback happened
- write JSON/JSONL history under
~/.hermes/state/
Recommended files:
- history log:
~/.hermes/state/auto_safe_upgrade_history.jsonl - summary snapshot:
~/.hermes/state/auto_safe_upgrade_summary.json(current dashboard/cron truth) - last result:
~/.hermes/state/auto_safe_upgrade_last.json(last command detail; may be stale) - lock file:
~/.hermes/state/auto_safe_upgrade.lock - current state:
~/.hermes/state/auto_safe_upgrade_state.json - state history:
~/.hermes/state/auto_safe_upgrade_state_history.jsonl - repo failure backoff:
~/.hermes/state/auto_safe_upgrade_failures.json
3. Split the script into two modes
Use:
probe— check prerequisites and whether updates are pendingupgrade— run the allowed precondition repairs, perform upgrade/verification, and rollback on failure
This enables a cron task to notify only when a real upgrade is about to happen.
4. Notification pattern
Do not notify on every successful no-op check.
Instead:
- Run
probe - If no update is pending, finish quietly unless there is an error worth reporting
- If any repo reports
update_pending, immediately send the user a message like:- detected a new Hermes version
- auto-upgrade is about to start
- local changes will be backed up, pushed, and tagged first
- a final result message will follow
- Run
upgrade - Send a second message with:
- overall success/failure
- per-repo status
- whether backup commit/push/tag happened
- whether verification passed
- whether rollback happened or what blocked the run
Cron job pattern
Create a cron job with a self-contained script invocation that:
- runs
python3 ~/.hermes/scripts/auto_safe_upgrade.py cycle --repo all --force-upgrade-with-stash - lets the script run probe first and upgrade only when updates are pending
- parses the JSON output injected by the cron script runner
- sends a final message only when an upgrade succeeds or fails
For messaging targets, discover the available destination first with send_message(action='list'), then use the exact returned target (for example a Feishu DM target).
Recommended deterministic schedule:
0 18 * * 5withtimezone: America/Los_Angeles- set
recover_missed: truefor this job so Hermes runs one recovery cycle after downtime instead of silently skipping a missed Friday window; recovery is bounded so very old missed windows are fast-forwarded
Important implementation notes
~/.hermes/hermes-agentbeing on a feature/chore branch is a valid block condition; do not auto-switch branches silently- missing
upstreamon~/.hermesis a common blocker and should be reported explicitly - after three consecutive failures for the same repo, the wrapper marks that repo
upgrade_blocked, then allows one recheck after the configured re-evaluation window - the wrapper uses a cycle-bound expiring lock to avoid overlapping manual and scheduled cycles and to recover stale
UPGRADE_RUNNINGstate - upstream divergence is classified as
UPSTREAM_DIVERGEDand fails closed for manual policy review - keep user-facing notifications concise and actionable
- keep the cron prompt self-contained because cron runs have no access to current chat context
- prefer reporting exact block reasons over attempting risky repair during scheduled runs
Repair guidance for prerequisite failures
When probe fails on prerequisites, prefer grounded repair over blind configuration:
- Inspect
~/.hermes/.git/configand live remotes/branches for both repos. - Use
gh repo viewon any known remote to determine whether it is a fork, whether it has aparent, and what its default branch is. - If
~/.hermeshas no safely confirmable officialupstream, update the script'sRepoSpecforhermes_coreto use the known-good existing remote (for exampleorigin) asupstream_remoterather than adding an unverified remote. - If
~/.hermes/hermes-agentis not onmain, fix the branch precondition before re-running probe. - Re-run
python3 ~/.hermes/scripts/auto_safe_upgrade.py probeand treat its structured JSON plus exit code as the validation result.- A non-zero business-status exit (for example
10meaning update pending) is not the same as a crash.
- A non-zero business-status exit (for example
This approach was validated in practice: the blocker on ~/.hermes was removed by repointing hermes_core.upstream_remote from upstream to the only confirmed remote, and the hermes-agent blocker was removed by returning it to main before re-running probe.
After setup, verify all of the following:
- the script exists and is executable
proberuns and returns structured JSON- the cron job exists and has the expected Friday 18:00
America/Los_Angelesschedule - notification target is valid
- any blocking prerequisite is explicitly surfaced (for example missing
upstreamor non-mainbranch)
Runtime triage when user says "not working"
When this exact behavior is reported, run these checks in order before changing files:
python3 ~/.hermes/scripts/auto_safe_upgrade.py probe- Inspect exit code:
0: no blocker, no pending update10: one or more repos areupdate_pending(non-fatal business status)- non-10/0 non-zero: execution failure; report error text directly
- Read
~/.hermes/state/auto_safe_upgrade_summary.jsonfirst for the current cycle report, then~/.hermes/state/auto_safe_upgrade_last.jsonand~/.hermes/state/auto_safe_upgrade_history.jsonlfor command details/history - Verify schedule freshness in
~/.hermes/profiles/hermes-home/cron/jobs.jsonand checknext_run_at
Common blocking pattern to expect in real environments:
- If the run appears "not triggered," check whether
next_run_atis still in the future. - If
upgradefails immediately, check for dirty worktree blocks:working_tree_cleanmust be true unless running with--force-upgrade-with-stash.
- If no work is possible on
hermes_agentbecause drift is large, output may showrisk_level: high; in this wrapper design the scheduled cycle uses--force-upgrade-with-stashand records any failure in the state machine. - Confirm
~/.hermes/hermes-agentis onmainand hasupstream; missing/invalid remotes should be surfaced as blockers.
In this codebase, a practical recovery sequence is often:
python3 ~/.hermes/scripts/auto_safe_upgrade.py cycle --repo all --dry-run --force-upgrade-with-stash- Review planned actions and blockers
- If accepted, run
python3 ~/.hermes/scripts/auto_safe_upgrade.py cycle --repo all --force-upgrade-with-stash
Good final report contents
When reporting setup status back to the user, include:
- script path
- cron job name and id
- schedule
- whether pre/post notifications are wired in
- the exact current blocker, if any
- the next concrete remediation step
- if relevant,
next_run_atand why it may legitimately make it appear idle