refactor(cli): derive relaunch flag table from argparse introspection

Pull the top-level + chat parser construction out of main() into
hermes_cli/_parser.py so relaunch.py can introspect parser._actions to
discover which flags exist and whether they take values, instead of
maintaining a parallel hand-rolled (flag, takes_value) tuple list.

- _parser.py: build_top_level_parser() returns (parser, subparsers,
  chat_parser); side-effect-free import.
- main.py: ~290 lines of inline parser construction collapsed to a
  helper call. Other subparsers stay inline (dispatch is bound to
  module-level cmd_* functions).
- test_ignore_user_config_flags.py: brittle inspect.getsource grep
  replaced with proper parser introspection.
- test_relaunch.py: introspection sanity tests added.
- _parser._inherited_flag(parser, ...): wraps parser.add_argument and
  sets action.inherit_on_relaunch = True. Used in place of
  parser.add_argument for the 25 flags (top-level + chat) that need to
  carry over.
- _parser.PRE_ARGPARSE_INHERITED_FLAGS: holds --profile/-p, which
  isn't on argparse (consumed earlier by main._apply_profile_override).
- relaunch.py: drops _CRITICAL_DESTS and _PRE_ARGPARSE_FLAGS; the table
  builder now filters by getattr(action, "inherit_on_relaunch", False).
  Renames _CRITICAL_FLAGS_TABLE / _build_critical_flag_table /
  _extract_critical_flags / preserve_critical= → _INHERITED_FLAGS_TABLE
  / _build_inherited_flag_table / _extract_inherited_flags /
  preserve_inherited= so the terminology is consistent.
This commit is contained in:
ethernet
2026-04-29 17:30:19 -04:00
parent 883de8f9a1
commit 27c03fbd99
5 changed files with 473 additions and 357 deletions

View File

@@ -224,22 +224,21 @@ class TestArgparseFlagsRegistered:
assert args.ignore_rules is True
def test_main_py_registers_both_flags(self):
"""E2E: the real hermes_cli/main.py parser accepts both flags.
"""E2E: the real hermes parser accepts both flags."""
from hermes_cli._parser import build_top_level_parser
We invoke the real argparse tree builder from hermes_cli.main.
"""
import hermes_cli.main as hm
parser, _subparsers, chat_parser = build_top_level_parser()
top_dests = {a.dest for a in parser._actions}
chat_dests = {a.dest for a in chat_parser._actions}
assert "ignore_user_config" in top_dests
assert "ignore_rules" in top_dests
assert "ignore_user_config" in chat_dests
assert "ignore_rules" in chat_dests
# hm has a helper that builds the argparse tree inside main().
# We can extract it by catching the SystemExit on --help.
# Simpler: just grep the source for the flag strings. Both approaches
# are brittle; we use a combined test.
import inspect
src = inspect.getsource(hm)
assert '"--ignore-user-config"' in src, \
"chat subparser must register --ignore-user-config"
assert '"--ignore-rules"' in src, \
"chat subparser must register --ignore-rules"
# And the cmd_chat env-var wiring must be present
import inspect
import hermes_cli.main as hm
src = inspect.getsource(hm)
assert "HERMES_IGNORE_USER_CONFIG" in src
assert "HERMES_IGNORE_RULES" in src