| name | hermes-safe-auto-upgrade |
| description | Set up Hermes to safely self-upgrade on a schedule with pre-upgrade backup commits, push/tag safeguards, and user notifications before and after any real upgrade. |
| version | 1.0.0 |
| author | Hermes Agent |
| license | MIT |
| metadata | {"hermes":{"tags":["hermes","self-upgrade","cron","git","notifications","github","feishu"],"related_skills":["hermes-agent"]}} |
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
~/.hermes and ~/.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
upstream is missing, do not guess a new official remote URL; first verify whether the repo only has a known-safe origin and whether that is the only confirmable source
~/.hermes/hermes-agent
- common official update source:
origin
- common backup remote:
fork
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
~/.hermes has upstream
- whether any candidate official repo can be safely confirmed from local config,
gh repo view, or other grounded evidence
Real findings from validation:
~/.hermes may have no upstream at all
- the existing
origin for ~/.hermes may be a private fork with parent = 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-agent may be left on a feature branch; automatic upgrade must remain blocked until it is back on main
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
gh auth
- 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
main branch
- fetch from
upstream and rebase onto upstream/main
- verify, restore branch/stash, and fail-closed on any verification failure
- 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 pending
upgrade — 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 * * 5 with timezone: America/Los_Angeles
- set
recover_missed: true for 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-agent being on a feature/chore branch is a valid block condition; do not auto-switch branches silently
- missing
upstream on ~/.hermes is 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_RUNNING state
- upstream divergence is classified as
UPSTREAM_DIVERGED and 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/config and live remotes/branches for both repos.
- Use
gh repo view on any known remote to determine whether it is a fork, whether it has a parent, and what its default branch is.
- If
~/.hermes has no safely confirmable official upstream, update the script's RepoSpec for hermes_core to use the known-good existing remote (for example origin) as upstream_remote rather than adding an unverified remote.
- If
~/.hermes/hermes-agent is not on main, fix the branch precondition before re-running probe.
- Re-run
python3 ~/.hermes/scripts/auto_safe_upgrade.py probe and treat its structured JSON plus exit code as the validation result.
- A non-zero business-status exit (for example
10 meaning update pending) is not the same as a crash.
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
probe runs and returns structured JSON
- the cron job exists and has the expected Friday 18:00
America/Los_Angeles schedule
- notification target is valid
- any blocking prerequisite is explicitly surfaced (for example missing
upstream or non-main branch)
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 update
10: one or more repos are update_pending (non-fatal business status)
- non-10/0 non-zero: execution failure; report error text directly
- Read
~/.hermes/state/auto_safe_upgrade_summary.json first for the current cycle report, then ~/.hermes/state/auto_safe_upgrade_last.json and ~/.hermes/state/auto_safe_upgrade_history.jsonl for command details/history
- Verify schedule freshness in
~/.hermes/profiles/hermes-home/cron/jobs.json and check next_run_at
Common blocking pattern to expect in real environments:
- If the run appears "not triggered," check whether
next_run_at is still in the future.
- If
upgrade fails immediately, check for dirty worktree blocks:
working_tree_clean must be true unless running with --force-upgrade-with-stash.
- If no work is possible on
hermes_agent because drift is large, output may show risk_level: high; in this wrapper design the scheduled cycle uses --force-upgrade-with-stash and records any failure in the state machine.
- Confirm
~/.hermes/hermes-agent is on main and has upstream; 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_at and why it may legitimately make it appear idle