feat(skills): find & diff user-modified bundled skills - #47802
Conversation
`hermes update` keeps (won't overwrite) bundled skills the user edited locally, but only printed a count — "~ N user-modified (kept)" — with no way to learn which skills, or see what changed. Reverting already existed (`hermes skills reset <name> [--restore]`); discovery and inspection did not. Add two CLI commands (zero model-tool footprint), reusing the manifest origin-hash that sync already maintains: - `hermes skills list-modified [--json]` — list the bundled skills whose on-disk copy diverges from the last-synced origin hash (the exact test the sync loop uses to decide what to skip). - `hermes skills diff <name>` — unified diff between the user's copy and the current bundled (stock) version, so the user can confirm what changed before reverting. Both are mirrored as `/skills list-modified` and `/skills diff`. The `hermes update` notice now points at `hermes skills list-modified`. Core helpers `list_user_modified_bundled_skills()` and `diff_bundled_skill()` live in tools/skills_sync.py alongside the existing reset logic.
Exercises the real sync pipeline (no mocked comparison logic): a pristine synced skill is not flagged; an edited one is listed and diffed (modified + added files); an unknown skill returns not-ok; and `reset --restore` clears the modified state so revert and discovery stay consistent.
|
Related: #28213 (Skill Lifecycle Transparency: |
|
Thanks for this @xxxigm — clean implementation and the detection logic correctly mirrors the sync engine's user-modified test (frontmatter-name + full category path, not dirname), which matters for the 36/240 bundled skills whose frontmatter name differs from their directory and the 36 that nest in deeper categories. I've salvaged your commits into #48286 (your authorship is preserved via cherry-pick). The salvage adds two small fixes found during review:
Closing in favor of #48286. Thanks again! |
…fied-diff feat(skills): find & diff user-modified bundled skills (salvage of #47802)
…ls-list-modified-diff feat(skills): find & diff user-modified bundled skills (salvage of NousResearch#47802)
…ls-list-modified-diff feat(skills): find & diff user-modified bundled skills (salvage of NousResearch#47802)
…ls-list-modified-diff feat(skills): find & diff user-modified bundled skills (salvage of NousResearch#47802)
…ls-list-modified-diff feat(skills): find & diff user-modified bundled skills (salvage of NousResearch#47802)
…ls-list-modified-diff feat(skills): find & diff user-modified bundled skills (salvage of NousResearch#47802)
Summary
Reported on Discord (Hana):
hermes updatesays certain skills were "modified by the user" and won't be updated, but there's no way to see which skills, diff them, or know they're recoverable. They end up stuck stale even though the edits were inadvertent.Verified against
main:hermes skills reset <name>(re-baseline, keep your copy) andhermes skills reset <name> --restore(replace with stock). ✅hermes updateonly prints~ N user-modified (kept), no names. ❌This PR closes the two gaps, reusing the manifest origin-hash that the sync engine already maintains (
~/.hermes/skills/.bundled_manifest). No new model tools — pure CLI surface (Footprint Ladder rung 1–2).Changes (commit 1)
tools/skills_sync.py:list_user_modified_bundled_skills()— returns the bundled skills whose on-disk copy diverges from the last-synced origin hash (the exact test the sync loop uses to decide what to skip).diff_bundled_skill(name)— unified diff of the user copy vs the current bundled (stock) copy, per file (modified / added / removed / binary).hermes skills list-modified [--json]andhermes skills diff <name>(parser + router + handlers), mirrored as/skills list-modifiedand/skills diff.hermes update's "user-modified" notice now points athermes skills list-modified.Test plan (commit 2)
scripts/run_tests.sh tests/tools/test_skills_list_modified_diff.py— 6 passed. Exercises the real sync pipeline (no mocked comparison): pristine → not flagged; edited → listed + diffed (modified & added files); unknown skill → not-ok;reset --restoreclears the modified state so revert and discovery agree.scripts/run_tests.sh tests/hermes_cli/test_skills_hub.py tests/hermes_cli/test_argparse_flag_propagation.py— 45 passed (no parser/routing regressions).