mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-06-18 09:51:59 +00:00
fix(skills): surface list-modified hint on both update paths + disambiguate diff
Salvage follow-up to the cherry-picked feat/test commits: - W1: the unpack/install update path in main.py printed the '~ N user-modified (kept)' notice without the new 'hermes skills list-modified' hint that the git-pull path got. Mirror the hint to both sites so the count is actionable regardless of which update path runs. - W2: 'hermes skills diff <name>' (bundled-vs-stock) now shares the verb with the gateway write-approval 'diff <id>'. The gateway handler's docstring + truncation message pointed users to '/skills diff <id>' on the CLI, which now resolves a bundled skill by that name instead. Point at the pending JSON file and note the two diff commands are distinct. - Add an invariant test asserting every 'user-modified (kept)' notice in main.py carries the discovery hint (guards sibling drift).
This commit is contained in:
parent
481f0417d8
commit
6777916068
3 changed files with 62 additions and 4 deletions
|
|
@ -2214,7 +2214,9 @@ class GatewaySlashCommandsMixin:
|
|||
stranded).
|
||||
|
||||
``diff`` output is truncated for chat bubbles — the full diff lives in
|
||||
the CLI (``/skills diff <id>``) and the pending JSON file.
|
||||
the pending JSON file under ``~/.hermes/pending/skills/``. (Note this is
|
||||
the write-approval ``diff <id>``; the CLI also has an unrelated
|
||||
``hermes skills diff <name>`` that diffs a bundled skill vs stock.)
|
||||
"""
|
||||
from gateway.run import _hermes_home
|
||||
from hermes_cli.write_approval_commands import handle_pending_subcommand
|
||||
|
|
@ -2252,12 +2254,14 @@ class GatewaySlashCommandsMixin:
|
|||
"(Search/install are CLI-only.)")
|
||||
|
||||
# Chat bubbles can't hold a full skill diff — truncate and point at
|
||||
# the real review surfaces.
|
||||
# the real review surface. (Note: `hermes skills diff <name>` is a
|
||||
# *different* command — it diffs a bundled skill against its stock
|
||||
# version — so we point at the pending JSON file, not that command.)
|
||||
if args and args[0].lower() == "diff" and len(out) > 3000:
|
||||
pending_id = args[1] if len(args) > 1 else "<id>"
|
||||
out = (out[:3000]
|
||||
+ f"\n… (truncated — full diff: `/skills diff {pending_id}` "
|
||||
f"on the CLI, or ~/.hermes/pending/skills/{pending_id}.json)")
|
||||
+ "\n… (truncated — full diff in "
|
||||
f"~/.hermes/pending/skills/{pending_id}.json)")
|
||||
return out
|
||||
|
||||
async def _handle_fast_command(self, event: MessageEvent) -> str:
|
||||
|
|
|
|||
|
|
@ -6073,6 +6073,10 @@ def _update_via_zip(args):
|
|||
)
|
||||
if result.get("user_modified"):
|
||||
print(f" ~ {len(result['user_modified'])} user-modified (kept)")
|
||||
print(
|
||||
" → see them: hermes skills list-modified "
|
||||
"(diff/reset to resume updates)"
|
||||
)
|
||||
if result.get("cleaned"):
|
||||
print(f" − {len(result['cleaned'])} removed from manifest")
|
||||
if not result["copied"] and not result.get("updated"):
|
||||
|
|
|
|||
50
tests/hermes_cli/test_update_modified_notice.py
Normal file
50
tests/hermes_cli/test_update_modified_notice.py
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
"""Guard: every `hermes update` path that reports user-modified skills must
|
||||
also tell the user how to find them.
|
||||
|
||||
`hermes update` keeps (does not overwrite) bundled skills the user edited and
|
||||
prints a ``~ N user-modified (kept)`` count. There are two independent update
|
||||
code paths in ``hermes_cli/main.py`` that print this notice (the git-pull path
|
||||
in ``_cmd_update_impl`` and the unpack/install path). Both must point the user
|
||||
at ``hermes skills list-modified`` so the count is actionable — otherwise,
|
||||
depending on which path a user hits, they may never learn the discovery command
|
||||
exists.
|
||||
|
||||
This is an *invariant* test (the two sibling notices must agree), not a literal
|
||||
snapshot: it asserts the relationship "count line ⇒ discovery hint", so it
|
||||
keeps holding if the wording is reworded, as long as both sites stay in sync.
|
||||
"""
|
||||
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
import hermes_cli.main as main_mod
|
||||
|
||||
|
||||
_COUNT_RE = re.compile(r"user-modified \(kept\)")
|
||||
_HINT_RE = re.compile(r"hermes skills list-modified")
|
||||
|
||||
|
||||
def _source_lines() -> list[str]:
|
||||
return Path(main_mod.__file__).read_text(encoding="utf-8").splitlines()
|
||||
|
||||
|
||||
def test_every_user_modified_notice_points_at_list_modified():
|
||||
lines = _source_lines()
|
||||
count_sites = [i for i, ln in enumerate(lines) if _COUNT_RE.search(ln)]
|
||||
|
||||
# There are at least two such notices today; the bug was that only one of
|
||||
# them carried the discovery hint. Assert each is followed (within a small
|
||||
# window — the count print + the hint print) by the list-modified pointer.
|
||||
assert len(count_sites) >= 2, (
|
||||
"expected at least two 'user-modified (kept)' notices in main.py; "
|
||||
f"found {len(count_sites)}"
|
||||
)
|
||||
|
||||
for idx in count_sites:
|
||||
window = "\n".join(lines[idx : idx + 5])
|
||||
assert _HINT_RE.search(window), (
|
||||
"a 'user-modified (kept)' notice near line "
|
||||
f"{idx + 1} of main.py does not point users at "
|
||||
"`hermes skills list-modified` within the following lines — the "
|
||||
"two update paths have drifted apart again:\n" + window
|
||||
)
|
||||
Loading…
Add table
Add a link
Reference in a new issue