feat(skills): auto-deploy skill scripts to ~/.hermes/scripts/ on sync - #65
Conversation
- Add deploy_skill_scripts() to tools/skills_sync.py — copies .py files from skill scripts/ subdirectories to ~/.hermes/scripts/ when content has changed - Call deploy_skill_scripts() at end of sync_skills() so it runs on every Hermes startup - Move ugw-health-check.py into scripts/ subdirectory to match the convention used by other skills - Fixes UGW Health Monitor cron job: on next startup, the correct script (which reads gateway_state not report) overwrites the stale broken copy Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MnmdLAbTdbTo66Uc2tK31x
🔎 Lint report:
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c1b04ef607
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if dst.exists() and _file_md5(src) == _file_md5(dst): | ||
| continue # unchanged — skip | ||
| try: | ||
| import shutil | ||
| shutil.copy2(src, dst) |
There was a problem hiding this comment.
Replace broken install symlinks before copying
When a user followed the previous README's symlink install (~/.hermes/scripts/ugw-health-check.py -> ~/.hermes/skills/.../ugw-health-check.py), this move makes that symlink target disappear. Path.exists() is then false, and shutil.copy2(src, dst) follows the broken symlink and recreates ugw-health-check.py back inside the skill root, leaving the deployed path as a symlink and making the just-synced skill hash diverge from the manifest so subsequent syncs treat it as user-modified and stop updating it. Unlink or replace broken symlinks before copying into scripts_dir.
Useful? React with 👍 / 👎.
|
|
||
| # Scan both flat (skills_home/<skill>/scripts/) and | ||
| # category-nested (skills_home/<cat>/<skill>/scripts/) layouts. | ||
| for scripts_subdir in sorted(skills_home.rglob("scripts")): |
There was a problem hiding this comment.
Skip archived skills when deploying scripts
This recursive search includes ~/.hermes/skills/.archive/**/scripts, even though archived skills are intentionally excluded from skill loading. For users who archive a skill that has scripts/foo.py (or an old copy of a skill with the same script name), every startup can redeploy that archived script into ~/.hermes/scripts/ and potentially overwrite the active script. Reuse the existing excluded-path check before copying scripts from a discovered directory.
Useful? React with 👍 / 👎.
| continue # unchanged — skip | ||
| try: | ||
| import shutil | ||
| shutil.copy2(src, dst) |
There was a problem hiding this comment.
Avoid overwriting user-managed cron scripts
~/.hermes/scripts is the cron script workspace, so on any installation with an existing health_check.py, setup.py, or similarly named cron script, this unconditional copy replaces user code with a bundled skill helper at startup. Track deployed hashes or scope deployment to known managed filenames before overwriting files that were not previously deployed by this sync path.
Useful? React with 👍 / 👎.
| ```bash | ||
| # Copy (simple) | ||
| cp ~/.hermes/skills/devops/ugw-health-check/ugw-health-check.py ~/.hermes/scripts/ugw-health-check.py | ||
| cp ~/.hermes/skills/devops/ugw-health-check/scripts/ugw-health-check.py ~/.hermes/scripts/ugw-health-check.py |
There was a problem hiding this comment.
Update SKILL.md to point at scripts/ugw-health-check.py
This README now points at the moved file, but skills/devops/ugw-health-check/SKILL.md still tells the agent/user to run python3 ugw-health-check.py and copy ugw-health-check.py from the skill root. Since that file was deleted in this commit, following the loaded skill instructions fails with file-not-found even though the README is correct; update the SKILL.md run/install snippets to use scripts/ugw-health-check.py or the deployed ~/.hermes/scripts/ path.
Useful? React with 👍 / 👎.
| _scripts_home = HERMES_HOME / "scripts" | ||
| try: | ||
| deploy_skill_scripts(SKILLS_DIR, _scripts_home) |
There was a problem hiding this comment.
Avoid writing real ~/.hermes during skills_sync tests
HERMES_HOME is a module-level constant captured when tools.skills_sync is imported, but the tests in tests/tools/test_skills_sync.py only patch SKILLS_DIR/MANIFEST_FILE and import this module before the autouse fixture sets a per-test HERMES_HOME. With this new call, every sync_skills() test now creates ~/.hermes/scripts (and would copy any fixture scripts there) on the developer/CI home directory; derive the scripts dir from the patched SKILLS_DIR or expose it as a patchable module constant.
Useful? React with 👍 / 👎.
Problem
The UGW Health Monitor cron job runs
python3 ~/.hermes/scripts/ugw-health-check.pyand has been failing every run with:Root cause: the old script at
~/.hermes/scripts/ugw-health-check.pylooks for a"report"key ingateway_state.jsonthat was never written. The actual key is"gateway_state". The correct replacement script already exists in the repo atskills/devops/ugw-health-check/— butskills_sync.pyonly copies skill directories to~/.hermes/skills/, it never deployed scripts to~/.hermes/scripts/.Slack thread: https://mfc-nyc.slack.com/archives/C0BD8QBUSJF/p1782743855322599
Fix
tools/skills_sync.pyAdded
deploy_skill_scripts(skills_home, scripts_dir):scripts/subdirectory.pyfiles found to~/.hermes/scripts/chmod 0o755on deployed scriptssync_skills()(non-fatal, wrapped intry/except)On next Hermes startup,
sync_skills()will run → the correctugw-health-check.py(readinggateway_statenotreport) overwrites the stale broken copy → UGW Health Monitor passes.skills/devops/ugw-health-check/ugw-health-check.py→scripts/ugw-health-check.pyto match the convention used by all other skills (google-workspace,ocr-and-documents,powerpoint, etc.)Generated by Claude Code