The managed-block marker and the docs have referred to `hermes codex-runtime migrate` all along, but no such CLI subcommand existed: the only way to run the ~/.codex/config.toml migration outside a chat session was to import the private hermes_cli.codex_runtime_plugin_migration.migrate (#79023). The new subcommand group (hermes_cli/subcommands/codex_runtime.py, registered like the other groups) calls the same migrate() the /codex-runtime slash command uses, on the selected profile home, with --dry-run (no write) and --json (full report incl. preserved_user_servers and errors); exit code 1 when the report has errors. Tests: one invariant for the same-name table (single header, valid TOML, user command kept, report lists the name) and one for the CLI dry-run/json path. Docs: conflict policy + command in the codex runtime guide and CLI reference.
56 lines
2.7 KiB
Python
56 lines
2.7 KiB
Python
"""``hermes codex-runtime`` — noninteractive counterpart of the ``/codex-runtime`` slash command.
|
|
|
|
``hermes codex-runtime migrate [--dry-run] [--json]`` runs the same ``~/.codex/config.toml``
|
|
migration the slash command triggers when the codex app-server runtime is enabled, on the
|
|
selected profile home (``hermes -p NAME codex-runtime migrate``), so automation no longer has to
|
|
import ``hermes_cli.codex_runtime_plugin_migration`` directly (issue #79023).
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import dataclasses
|
|
import json
|
|
|
|
|
|
def cmd_codex_runtime_migrate(args: argparse.Namespace) -> int:
|
|
"""Project Hermes MCP servers (+ codex plugins) into ~/.codex/config.toml; 1 on any error."""
|
|
from hermes_cli.codex_runtime_plugin_migration import migrate
|
|
from hermes_cli.config import load_config
|
|
|
|
report = migrate(load_config(), dry_run=args.dry_run)
|
|
if args.json:
|
|
payload = dataclasses.asdict(report)
|
|
payload["target_path"] = str(report.target_path)
|
|
print(json.dumps(payload, indent=2, sort_keys=True))
|
|
else:
|
|
print(report.summary())
|
|
return 1 if report.errors else 0
|
|
|
|
|
|
def build_codex_runtime_parser(subparsers) -> None:
|
|
"""Attach the ``codex-runtime`` subcommand (``migrate`` action) to ``subparsers``."""
|
|
parser = subparsers.add_parser(
|
|
"codex-runtime", help="Manage the optional codex app-server runtime (migrate MCP config)",
|
|
description="Noninteractive counterpart of the /codex-runtime slash command. Toggling the "
|
|
"runtime itself stays in the chat command (`/codex-runtime on|off`); `migrate` "
|
|
"re-projects Hermes' mcp_servers + installed codex plugins into the managed block of "
|
|
"~/.codex/config.toml for the selected profile.")
|
|
actions = parser.add_subparsers(dest="codex_runtime_action")
|
|
migrate_parser = actions.add_parser(
|
|
"migrate", help="Regenerate the hermes-managed block in codex's config.toml",
|
|
description="Idempotent: replaces the managed block, keeps user text verbatim, skips Hermes "
|
|
"servers whose name the user already declares outside the block, validates the result "
|
|
"as TOML before writing atomically.")
|
|
migrate_parser.add_argument(
|
|
"--dry-run", action="store_true", help="Report what would be written without touching config.toml")
|
|
migrate_parser.add_argument(
|
|
"--json", action="store_true", help="Print the migration report as JSON (for automation)")
|
|
migrate_parser.set_defaults(func=cmd_codex_runtime_migrate)
|
|
|
|
def _print_help(args): # noqa: ANN001 — bare `hermes codex-runtime` lists the actions
|
|
parser.print_help()
|
|
return 0
|
|
|
|
parser.set_defaults(func=_print_help)
|