mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-10-20 05:15:31 +00:00
refactor(sync): put every Skill Sync verb under hermes sync; drop HSP naming
Encapsulates the feature behind one command for launch, and adopts the
official product name.
One command:
- `propose` moves from `hermes skills propose` to `hermes sync propose`, so
the whole feature is one command to learn and one to document. Its handler
moves from cmd_skills to cmd_sync accordingly.
- The `hermes sync` parser now documents both halves plainly: personal sync
across your devices, and sharing with your organisation. Added an examples
epilog; rewrote the verb help in user language ("Include a skill in your
sync" rather than "Opt a skill into sync").
- Every user-facing string that pointed at `hermes skills propose` now points
at `hermes sync propose` (8 sites, including the agent-visible guidance
returned by skill_manage and the org provenance header).
This also clears the way for #39343, which adds its own top-level `sync` for
git-repo profile backup — that feature nests under `skills`, this one owns
`sync`.
Naming:
- HSP / "Hermes Sync Protocol" is gone from prose, docstrings, and comments.
The feature is "Skill Sync".
- Public identifiers renamed: HSPClient -> SyncClient, HSPError -> SyncError,
HSPConflict -> SyncConflict, hsp_address -> wire_address, HSP_VERSION ->
WIRE_VERSION.
- The WIRE names are deliberately NOT renamed: the `hsp_version` capability
field and the `x-hsp-object-type` response header are set by the deployed
gateway-gateway sync plane (verified in src/sync/syncRouter.ts), so
renaming them client-side would break sync against a live server. A comment
at the version constant records why they differ from the product name.
- The version-mismatch error is now actionable ("this server speaks sync
version X, but this Hermes speaks Y — update Hermes to sync with it")
instead of leaking the protocol acronym.
Also fixes a wiring gap found on the way: the gateway housekeeping tick
pulled personal skills but never org skills — the same defect already fixed
for the CLI. Org pull now runs there too, gated on real org membership.
Tests: the jargon guard now also fails on a bare "HSP". The two tests that
asserted the old cross-command structure are replaced by three asserting the
new one (propose IS under sync, propose is NOT under skills, sync usage
lists it). 2294 passed / 0 failed across all 51 suites that import the
changed modules, via scripts/run_tests.sh.
Verified by running the real CLI: `hermes sync --help` lists all eight verbs,
`hermes skills --help` no longer mentions propose, `hermes sync propose
--help` parses, and `hermes sync status` still reports live org state.
This commit is contained in:
parent
981feb6730
commit
4f990ec09e
10 changed files with 240 additions and 230 deletions
|
|
@ -261,7 +261,9 @@ class TestOrgPullIsWiredIn:
|
|||
root / "hermes_cli" / "subcommands" / "sync.py",
|
||||
root / "hermes_cli" / "subcommands" / "skills.py",
|
||||
]
|
||||
banned = re.compile(r"\(M[12]\)|HSP/1|§[0-9]|DEV-PHASE|hsp-1-contract")
|
||||
banned = re.compile(
|
||||
r"\(M[12]\)|\bHSP\b|HSP/1|§[0-9]|DEV-PHASE|hsp-1-contract"
|
||||
)
|
||||
for path in targets:
|
||||
for i, line in enumerate(path.read_text(encoding="utf-8").split("\n"), 1):
|
||||
if "help=" in line or "description=" in line:
|
||||
|
|
@ -270,41 +272,41 @@ class TestOrgPullIsWiredIn:
|
|||
)
|
||||
|
||||
|
||||
class TestOrgSharingIsDiscoverable:
|
||||
"""`hermes sync` must point users at the org-sharing command.
|
||||
class TestSkillSyncIsOneCommand:
|
||||
"""Every Skill Sync verb lives under `hermes sync` for launch.
|
||||
|
||||
Without this, there is no path from "I want to share this with my team"
|
||||
to `hermes skills propose` — sync looks like the only sharing surface
|
||||
while being personal-only.
|
||||
The surface is deliberately encapsulated: one command to learn, one to
|
||||
document, and top-level `sync` stays free of skill-management verbs that
|
||||
belong elsewhere. `propose` in particular used to sit under `hermes
|
||||
skills`, which split one feature across two commands.
|
||||
"""
|
||||
|
||||
def test_sync_usage_block_mentions_propose(self):
|
||||
def _src(self, *parts):
|
||||
import pathlib
|
||||
|
||||
main_src = (
|
||||
pathlib.Path(__file__).resolve().parents[2]
|
||||
/ "hermes_cli"
|
||||
/ "main.py"
|
||||
return (
|
||||
pathlib.Path(__file__).resolve().parents[2].joinpath(*parts)
|
||||
).read_text(encoding="utf-8")
|
||||
usage_start = main_src.index(
|
||||
"usage: hermes sync <status|pull|push|now|enable|disable|device>"
|
||||
)
|
||||
usage_block = main_src[usage_start : usage_start + 1200]
|
||||
assert "hermes skills propose" in usage_block, (
|
||||
"`hermes sync` usage must point at the org-sharing command."
|
||||
|
||||
def test_propose_is_a_sync_subcommand(self):
|
||||
sync_src = self._src("hermes_cli", "subcommands", "sync.py")
|
||||
assert '"propose"' in sync_src, (
|
||||
"`propose` must be a `hermes sync` subcommand."
|
||||
)
|
||||
|
||||
def test_sync_parser_epilog_mentions_propose(self):
|
||||
import pathlib
|
||||
def test_propose_is_not_under_skills(self):
|
||||
skills_src = self._src("hermes_cli", "subcommands", "skills.py")
|
||||
assert '"propose"' not in skills_src, (
|
||||
"`propose` must NOT remain under `hermes skills` — Skill Sync is "
|
||||
"one command for launch."
|
||||
)
|
||||
|
||||
src = (
|
||||
pathlib.Path(__file__).resolve().parents[2]
|
||||
/ "hermes_cli"
|
||||
/ "subcommands"
|
||||
/ "sync.py"
|
||||
).read_text(encoding="utf-8")
|
||||
assert "hermes skills propose" in src, (
|
||||
"`hermes sync --help` must point at the org-sharing command."
|
||||
def test_sync_usage_lists_propose(self):
|
||||
main_src = self._src("hermes_cli", "main.py")
|
||||
usage_start = main_src.index("usage: hermes sync ")
|
||||
usage_block = main_src[usage_start : usage_start + 1400]
|
||||
assert "propose" in usage_block, (
|
||||
"`hermes sync` usage must list the propose verb."
|
||||
)
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue