Skip to content

feat(skills): auto-deploy skill scripts to ~/.hermes/scripts/ on sync - #65

Merged
github-actions[bot] merged 1 commit into
mainfrom
claude/ugw-skill-script-deploy
Jul 1, 2026
Merged

feat(skills): auto-deploy skill scripts to ~/.hermes/scripts/ on sync#65
github-actions[bot] merged 1 commit into
mainfrom
claude/ugw-skill-script-deploy

Conversation

@dizhaky

@dizhaky dizhaky commented Jul 1, 2026

Copy link
Copy Markdown
Owner

Problem

The UGW Health Monitor cron job runs python3 ~/.hermes/scripts/ugw-health-check.py and has been failing every run with:

CRITICAL Status: CRITICAL (report parse failed)

Root cause: the old script at ~/.hermes/scripts/ugw-health-check.py looks for a "report" key in gateway_state.json that was never written. The actual key is "gateway_state". The correct replacement script already exists in the repo at skills/devops/ugw-health-check/ — but skills_sync.py only 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.py

Added deploy_skill_scripts(skills_home, scripts_dir):

  • Scans all installed skills for a scripts/ subdirectory
  • Copies any .py files found to ~/.hermes/scripts/
  • Uses MD5 comparison — only writes when content has changed (fast on repeated startup calls)
  • Sets chmod 0o755 on deployed scripts
  • Called at the end of sync_skills() (non-fatal, wrapped in try/except)

On next Hermes startup, sync_skills() will run → the correct ugw-health-check.py (reading gateway_state not report) overwrites the stale broken copy → UGW Health Monitor passes.

skills/devops/ugw-health-check/

  • Moved ugw-health-check.pyscripts/ugw-health-check.py to match the convention used by all other skills (google-workspace, ocr-and-documents, powerpoint, etc.)
  • Updated README.md references to the new path

Generated by Claude Code

- 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
@dizhaky
dizhaky marked this pull request as ready for review July 1, 2026 23:14
@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown

🔎 Lint report: claude/ugw-skill-script-deploy vs origin/main

ruff

Total: 0 on HEAD, 0 on base (➖ 0)

🆕 New issues: none

✅ Fixed issues: none

Unchanged: 0 pre-existing issues carried over.

ty (type checker)

Total: 8607 on HEAD, 8607 on base (➖ 0)

🆕 New issues: none

✅ Fixed issues: none

Unchanged: 4576 pre-existing issues carried over.

Diagnostics are surfaced as warnings — this check never fails the build.

@github-actions
github-actions Bot merged commit 8bcf2c6 into main Jul 1, 2026
24 of 30 checks passed
@github-actions
github-actions Bot deleted the claude/ugw-skill-script-deploy branch July 1, 2026 23:15

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment thread tools/skills_sync.py
Comment on lines +355 to +359
if dst.exists() and _file_md5(src) == _file_md5(dst):
continue # unchanged — skip
try:
import shutil
shutil.copy2(src, dst)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

Comment thread tools/skills_sync.py

# 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")):

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

Comment thread tools/skills_sync.py
continue # unchanged — skip
try:
import shutil
shutil.copy2(src, dst)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

Comment thread tools/skills_sync.py
Comment on lines +310 to +312
_scripts_home = HERMES_HOME / "scripts"
try:
deploy_skill_scripts(SKILLS_DIR, _scripts_home)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants