mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-06-09 08:21:50 +00:00
Follow-on to the cron extraction in the same Phase 2 PR. Same pattern: per-group build_<name>_parser() functions with injected handlers, no main import. - subcommands/profile.py: build_profile_parser (190-line block out of main()). - subcommands/gateway.py: build_gateway_parser (gateway + proxy, 238-line block; they shared one inline section). Imports argparse for SUPPRESS defaults. - main(): two more inline blocks become single builder calls. Behavior-neutral: 'profile [sub] --help' and 'gateway/proxy [sub] --help' byte-identical to pre-extraction (diff-verified). main() now 2723 LOC (was 3297 at Phase 2 start); add_parser calls in main.py 179 -> 141. Validation: tests/hermes_cli/ 6476 passed / 0 failed under per-file process isolation; new builder unit tests cover subactions, aliases, dispatch, flags.
203 lines
7.4 KiB
Python
203 lines
7.4 KiB
Python
"""``hermes profile`` subcommand parser.
|
|
|
|
Extracted verbatim from ``hermes_cli/main.py:main()`` (god-file Phase 2).
|
|
Handler injected to avoid importing ``main``.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Callable
|
|
|
|
|
|
def build_profile_parser(subparsers, *, cmd_profile: Callable) -> None:
|
|
"""Attach the ``profile`` subcommand to ``subparsers``."""
|
|
# =========================================================================
|
|
# profile command
|
|
# =========================================================================
|
|
profile_parser = subparsers.add_parser(
|
|
"profile",
|
|
help="Manage profiles — multiple isolated Hermes instances",
|
|
)
|
|
profile_subparsers = profile_parser.add_subparsers(dest="profile_action")
|
|
|
|
profile_subparsers.add_parser("list", help="List all profiles")
|
|
profile_use = profile_subparsers.add_parser(
|
|
"use", help="Set sticky default profile"
|
|
)
|
|
profile_use.add_argument("profile_name", help="Profile name (or 'default')")
|
|
|
|
profile_create = profile_subparsers.add_parser(
|
|
"create", help="Create a new profile"
|
|
)
|
|
profile_create.add_argument(
|
|
"profile_name", help="Profile name (lowercase, alphanumeric)"
|
|
)
|
|
profile_create.add_argument(
|
|
"--clone",
|
|
action="store_true",
|
|
help="Copy config.yaml, .env, SOUL.md from active profile",
|
|
)
|
|
profile_create.add_argument(
|
|
"--clone-all",
|
|
action="store_true",
|
|
help="Full copy of active profile (all state)",
|
|
)
|
|
profile_create.add_argument(
|
|
"--clone-from",
|
|
metavar="SOURCE",
|
|
help="Source profile to clone from (default: active)",
|
|
)
|
|
profile_create.add_argument(
|
|
"--no-alias", action="store_true", help="Skip wrapper script creation"
|
|
)
|
|
profile_create.add_argument(
|
|
"--no-skills",
|
|
action="store_true",
|
|
help="Create an empty profile with no bundled skills (opts out of `hermes update` skill sync)",
|
|
)
|
|
profile_create.add_argument(
|
|
"--description",
|
|
default=None,
|
|
help="One- or two-sentence description of what this profile is good at. "
|
|
"Used by the kanban decomposer to route tasks based on role instead "
|
|
"of profile name alone. Skip and add later via `hermes profile describe`.",
|
|
)
|
|
|
|
profile_delete = profile_subparsers.add_parser("delete", help="Delete a profile")
|
|
profile_delete.add_argument("profile_name", help="Profile to delete")
|
|
profile_delete.add_argument(
|
|
"-y", "--yes", action="store_true", help="Skip confirmation prompt"
|
|
)
|
|
|
|
profile_describe = profile_subparsers.add_parser(
|
|
"describe",
|
|
help="Read or set a profile's description (used by the kanban orchestrator)",
|
|
)
|
|
profile_describe.add_argument(
|
|
"profile_name",
|
|
nargs="?",
|
|
default=None,
|
|
help="Profile to describe (omit + use --all --auto to sweep)",
|
|
)
|
|
profile_describe.add_argument(
|
|
"--text",
|
|
default=None,
|
|
help="Set description to this exact text (overwrites any existing description)",
|
|
)
|
|
profile_describe.add_argument(
|
|
"--auto",
|
|
action="store_true",
|
|
help="Auto-generate description via the auxiliary LLM "
|
|
"(uses auxiliary.profile_describer)",
|
|
)
|
|
profile_describe.add_argument(
|
|
"--overwrite",
|
|
action="store_true",
|
|
help="With --auto, replace user-authored descriptions too (default: only "
|
|
"fill in missing or previously-auto descriptions)",
|
|
)
|
|
profile_describe.add_argument(
|
|
"--all",
|
|
dest="all_missing",
|
|
action="store_true",
|
|
help="With --auto, run on every profile missing a description",
|
|
)
|
|
|
|
profile_show = profile_subparsers.add_parser("show", help="Show profile details")
|
|
profile_show.add_argument("profile_name", help="Profile to show")
|
|
|
|
profile_alias = profile_subparsers.add_parser(
|
|
"alias", help="Manage wrapper scripts"
|
|
)
|
|
profile_alias.add_argument("profile_name", help="Profile name")
|
|
profile_alias.add_argument(
|
|
"--remove", action="store_true", help="Remove the wrapper script"
|
|
)
|
|
profile_alias.add_argument(
|
|
"--name",
|
|
dest="alias_name",
|
|
metavar="NAME",
|
|
help="Custom alias name (default: profile name)",
|
|
)
|
|
|
|
profile_rename = profile_subparsers.add_parser("rename", help="Rename a profile")
|
|
profile_rename.add_argument("old_name", help="Current profile name")
|
|
profile_rename.add_argument("new_name", help="New profile name")
|
|
|
|
profile_export = profile_subparsers.add_parser(
|
|
"export", help="Export a profile to archive"
|
|
)
|
|
profile_export.add_argument("profile_name", help="Profile to export")
|
|
profile_export.add_argument(
|
|
"-o", "--output", default=None, help="Output file (default: <name>.tar.gz)"
|
|
)
|
|
|
|
profile_import = profile_subparsers.add_parser(
|
|
"import", help="Import a profile from archive"
|
|
)
|
|
profile_import.add_argument("archive", help="Path to .tar.gz archive")
|
|
profile_import.add_argument(
|
|
"--name",
|
|
dest="import_name",
|
|
metavar="NAME",
|
|
help="Profile name (default: inferred from archive)",
|
|
)
|
|
|
|
# ---------- Distribution subcommands (issue #20456) ----------
|
|
profile_install = profile_subparsers.add_parser(
|
|
"install",
|
|
help="Install a profile distribution from a git URL or local directory",
|
|
description=(
|
|
"Install a Hermes profile distribution. SOURCE can be a git URL "
|
|
"(github.com/user/repo, https://..., git@...) or a local "
|
|
"directory containing distribution.yaml at its root."
|
|
),
|
|
)
|
|
profile_install.add_argument(
|
|
"source",
|
|
help="Distribution source (git URL or local directory)",
|
|
)
|
|
profile_install.add_argument(
|
|
"--name", dest="install_name", metavar="NAME",
|
|
help="Override profile name (default: read from manifest)",
|
|
)
|
|
profile_install.add_argument(
|
|
"--alias", action="store_true",
|
|
help="Create a shell wrapper alias for the installed profile",
|
|
)
|
|
profile_install.add_argument(
|
|
"--force", action="store_true",
|
|
help="Overwrite an existing profile of the same name (user data preserved)",
|
|
)
|
|
profile_install.add_argument(
|
|
"-y", "--yes", action="store_true",
|
|
help="Skip manifest preview confirmation",
|
|
)
|
|
|
|
profile_update = profile_subparsers.add_parser(
|
|
"update",
|
|
help="Re-pull a distribution and apply updates (user data preserved)",
|
|
description=(
|
|
"Fetch the distribution from its recorded source and overwrite "
|
|
"distribution-owned files (SOUL.md, skills/, cron/, mcp.json). "
|
|
"User data (memories, sessions, auth, .env) is never touched. "
|
|
"config.yaml is preserved unless --force-config is passed."
|
|
),
|
|
)
|
|
profile_update.add_argument("profile_name", help="Profile to update")
|
|
profile_update.add_argument(
|
|
"--force-config", action="store_true",
|
|
help="Also overwrite config.yaml (normally preserved to keep user overrides)",
|
|
)
|
|
profile_update.add_argument(
|
|
"-y", "--yes", action="store_true",
|
|
help="Skip confirmation",
|
|
)
|
|
|
|
profile_info = profile_subparsers.add_parser(
|
|
"info",
|
|
help="Show a profile's distribution manifest (version, requirements, source)",
|
|
)
|
|
profile_info.add_argument("profile_name", help="Profile to inspect")
|
|
|
|
profile_parser.set_defaults(func=cmd_profile)
|