Skip to content

[Bug]: hermes skills reset <name> without --restore returns misleading success message for user-modified bundled skills #29856

@MeasyZero

Description

@MeasyZero

Bug Description

When a user modifies a bundled skill (e.g. edits the SKILL.md in ~/.hermes/skills/), sync_skills() detects the hash mismatch and correctly marks it as user-modified, skipping future updates. However, hermes skills reset <name> (without --restore) claims to resolve this but actually does nothing for truly modified skills.

Two misleading messages are involved:

  1. reset_bundled_skill() returns: "Future hermes update runs will re-baseline against your current copy and accept upstream changes." — This is false when the skill was actually modified. The next sync_skills() call hits the not_in_manifest + dest.exists() + dir_hash(dest) != bundled_hash branch, which skips the skill without writing a manifest entry. The skill remains permanently skipped for future updates.

  2. sync_skills() prints: "yours was kept. Run hermes skills reset {name} to replace it with the bundled version." — But reset without --restore does not replace it. The user gets a circular suggestion: run reset → it says "yours was kept, run reset."

Steps to Reproduce

  1. Start Hermes. A bundled skill foo is synced to ~/.hermes/skills/foo/.
  2. Edit ~/.hermes/skills/foo/SKILL.md so the user hash diverges from the origin hash.
  3. Next launch: ~ foo (user-modified, skipping) is printed.
  4. Run hermes skills reset foo (no --restore). Output says "Cleared manifest entry for 'foo'. Future hermes update runs will re-baseline..."
  5. Next launch: ~ foo (user-modified, skipping) again. Nothing changed.

Expected Behavior

hermes skills reset <name> without --restore should either:

  • (a) Actually unblock the skill so future bundled updates can come in (write the current bundled hash into the manifest so the next sync can detect bundled-vs-user divergence properly), or
  • (b) Honestly tell the user "Your local copy is preserved and will never receive bundled updates. Use --restore to revert to the shipped version."

Actual Behavior

The command returns a success message that is demonstrably false for user-modified skills. The skill remains permanently stuck in "user-modified" limbo, and the command output implies a resolution that never happens.

Affected Component

Skills (skill loading, skill hub, skill guard)

Root Cause Analysis

Two locations in tools/skills_sync.py:

1. Lines 409-411reset_bundled_skill() non---restore path:

action = "manifest_cleared"
message = (
    f"Cleared manifest entry for '{name}'. Future `hermes update` runs "
    f"will re-baseline against your current copy and accept upstream changes."
)

This is incorrect when the skill was modified. Clearing the manifest causes sync_skills() to treat it as a new skill conflict (lines 204-226), which skips the skill without writing a manifest entry when _dir_hash(dest) != bundled_hash. The next sync repeats the same loop.

2. Lines 222-225sync_skills() new-skill-conflict path:

print(
    f"  ⚠ {skill_name}: bundled version shipped but you "
    f"already have a local skill by this name — yours "
    f"was kept. Run `hermes skills reset {skill_name}` "
    f"to replace it with the bundled version."
)

Suggesting reset is circular—reset without --restore cannot replace anything for modified skills. The correct suggestion should be reset --restore.

Proposed Fix

Option A (honest messaging, minimal fix): Fix the two messages to be accurate about what happens. The reset_bundled_skill() message should say something like: "Cleared manifest entry for '{name}'. If you modified this skill, your local copy is preserved and will not receive upstream updates. Use --restore to revert to the shipped version." The sync_skills() message should suggest hermes skills reset --restore instead of plain reset.

Option B (behavioral fix): Have reset without --restore record the current bundled hash in the manifest, so the next sync sees user_hash != origin_hash and processes it through the existing user-modified path (which at least warns predictably) rather than falling through to the new-skill-conflict path each time. This makes the behavior consistent and understandable.

Option C (full fix): Combine A + B, and add a --preserve-mine / --accept-upstream flag on reset to let the user choose explicitly.

Related Issues

Operating System

macOS 15.x

Hermes Version

Latest main

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Medium — degraded but workaround existscomp/cliCLI entry point, hermes_cli/, setup wizardtool/skillsSkills system (list, view, manage)type/bugSomething isn't working

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions