mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-31 19:16:29 +00:00
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.
99 lines
4.1 KiB
Python
99 lines
4.1 KiB
Python
"""``hermes sync`` subcommand parser — Skill Sync.
|
|
|
|
Cloned from ``hermes_cli/subcommands/cron.py`` — same injected-handler shape
|
|
(``func=cmd_sync``) so this module does not import ``main`` (cycle avoidance).
|
|
|
|
Skill Sync covers two surfaces, both under this one command for launch:
|
|
|
|
Personal — your own skills, across your own devices:
|
|
hermes sync status show gate/opt-in/head state
|
|
hermes sync pull pull and materialize opted-in skills
|
|
hermes sync push push opted-in skills
|
|
hermes sync now reconcile: pull then push
|
|
hermes sync enable <skill> opt a skill into sync
|
|
hermes sync disable <skill> opt a skill out of sync
|
|
hermes sync device [--name] show or set this device's label
|
|
|
|
Organisation — skills shared with your team:
|
|
hermes sync propose <skill> share a skill with your organisation
|
|
|
|
Sync is INERT unless the resolved Nous token carries the access-gate claim
|
|
AND a sync base URL is configured. The commands report that state rather than
|
|
failing opaquely.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
from typing import Callable
|
|
|
|
|
|
def build_sync_parser(subparsers, *, cmd_sync: Callable) -> None:
|
|
"""Attach the ``sync`` subcommand (and its sub-actions) to ``subparsers``."""
|
|
sync_parser = subparsers.add_parser(
|
|
"sync",
|
|
help="Skill Sync — sync your skills across devices and with your team",
|
|
description=(
|
|
"Skill Sync keeps your skills with you. Personal sync moves your "
|
|
"own skills between your devices; if you belong to an "
|
|
"organisation, you also get its shared skills and can propose "
|
|
"your own back to the team."
|
|
),
|
|
epilog=(
|
|
"Examples:\n"
|
|
" hermes sync status what is synced, and from where\n"
|
|
" hermes sync enable my-skill include a skill in your sync\n"
|
|
" hermes sync now pull, then push\n"
|
|
" hermes sync propose my-skill share a skill with your team\n"
|
|
),
|
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
)
|
|
sync_sub = sync_parser.add_subparsers(dest="sync_command")
|
|
|
|
sync_sub.add_parser("status", help="Show what is synced, and from where")
|
|
sync_sub.add_parser(
|
|
"pull", help="Pull your synced skills (and your organisation's)"
|
|
)
|
|
sync_sub.add_parser("push", help="Push your opted-in skills")
|
|
sync_sub.add_parser("now", help="Reconcile now: pull then push")
|
|
|
|
enable = sync_sub.add_parser("enable", help="Include a skill in your sync")
|
|
enable.add_argument("skill", help="Skill name (frontmatter name / directory name)")
|
|
|
|
disable = sync_sub.add_parser("disable", help="Exclude a skill from your sync")
|
|
disable.add_argument("skill", help="Skill name (frontmatter name / directory name)")
|
|
|
|
device = sync_sub.add_parser(
|
|
"device",
|
|
help="Show or set this device's label (shown in the sync console)",
|
|
)
|
|
device.add_argument(
|
|
"--name",
|
|
dest="device_name",
|
|
default=None,
|
|
help="Set a human-friendly label for this device (e.g. \"Ben's Laptop\"). "
|
|
"Omit to print the current label.",
|
|
)
|
|
|
|
# Org-shared skills. A member's submission becomes a proposal an admin
|
|
# reviews; an admin's merges straight into the shared set. Accounts that
|
|
# aren't in a shared organisation are told so plainly.
|
|
propose = sync_sub.add_parser(
|
|
"propose",
|
|
help="Share a skill with your organisation",
|
|
description=(
|
|
"Submit one of your skills to your organisation's shared set. If "
|
|
"you are an admin it is added directly; otherwise it becomes a "
|
|
"proposal for an admin to review. Accounts that aren't part of a "
|
|
"shared organisation don't have this workflow."
|
|
),
|
|
)
|
|
propose.add_argument("name", help="Skill name to share")
|
|
propose.add_argument(
|
|
"-m",
|
|
"--message",
|
|
default=None,
|
|
help="Optional message describing the change",
|
|
)
|
|
|
|
sync_parser.set_defaults(func=cmd_sync)
|