mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-06-01 07:01:41 +00:00
Fix CLI verbose tool progress config fallback
This commit is contained in:
parent
d97c324473
commit
6a1aa420e7
5 changed files with 84 additions and 10 deletions
4
cli.py
4
cli.py
|
|
@ -2819,7 +2819,7 @@ class HermesCLI:
|
||||||
api_key: str = None,
|
api_key: str = None,
|
||||||
base_url: str = None,
|
base_url: str = None,
|
||||||
max_turns: int = None,
|
max_turns: int = None,
|
||||||
verbose: bool = False,
|
verbose: Optional[bool] = None,
|
||||||
compact: bool = False,
|
compact: bool = False,
|
||||||
resume: str = None,
|
resume: str = None,
|
||||||
checkpoints: bool = False,
|
checkpoints: bool = False,
|
||||||
|
|
@ -14439,7 +14439,7 @@ def main(
|
||||||
api_key: str = None,
|
api_key: str = None,
|
||||||
base_url: str = None,
|
base_url: str = None,
|
||||||
max_turns: int = None,
|
max_turns: int = None,
|
||||||
verbose: bool = False,
|
verbose: Optional[bool] = None,
|
||||||
quiet: bool = False,
|
quiet: bool = False,
|
||||||
compact: bool = False,
|
compact: bool = False,
|
||||||
list_tools: bool = False,
|
list_tools: bool = False,
|
||||||
|
|
|
||||||
|
|
@ -269,7 +269,11 @@ def build_top_level_parser():
|
||||||
help="Inference provider (default: auto). Built-in or a user-defined name from `providers:` in config.yaml.",
|
help="Inference provider (default: auto). Built-in or a user-defined name from `providers:` in config.yaml.",
|
||||||
)
|
)
|
||||||
chat_parser.add_argument(
|
chat_parser.add_argument(
|
||||||
"-v", "--verbose", action="store_true", help="Verbose output"
|
"-v",
|
||||||
|
"--verbose",
|
||||||
|
action="store_true",
|
||||||
|
default=argparse.SUPPRESS,
|
||||||
|
help="Verbose output",
|
||||||
)
|
)
|
||||||
chat_parser.add_argument(
|
chat_parser.add_argument(
|
||||||
"-Q",
|
"-Q",
|
||||||
|
|
|
||||||
|
|
@ -1454,7 +1454,7 @@ def _launch_tui(
|
||||||
provider: Optional[str] = None,
|
provider: Optional[str] = None,
|
||||||
toolsets: object = None,
|
toolsets: object = None,
|
||||||
skills: object = None,
|
skills: object = None,
|
||||||
verbose: bool = False,
|
verbose: Optional[bool] = None,
|
||||||
quiet: bool = False,
|
quiet: bool = False,
|
||||||
query: Optional[str] = None,
|
query: Optional[str] = None,
|
||||||
image: Optional[str] = None,
|
image: Optional[str] = None,
|
||||||
|
|
@ -1763,7 +1763,7 @@ def cmd_chat(args):
|
||||||
provider=getattr(args, "provider", None),
|
provider=getattr(args, "provider", None),
|
||||||
toolsets=getattr(args, "toolsets", None),
|
toolsets=getattr(args, "toolsets", None),
|
||||||
skills=getattr(args, "skills", None),
|
skills=getattr(args, "skills", None),
|
||||||
verbose=getattr(args, "verbose", False),
|
verbose=getattr(args, "verbose", None),
|
||||||
quiet=getattr(args, "quiet", False),
|
quiet=getattr(args, "quiet", False),
|
||||||
query=getattr(args, "query", None),
|
query=getattr(args, "query", None),
|
||||||
image=getattr(args, "image", None),
|
image=getattr(args, "image", None),
|
||||||
|
|
@ -1783,7 +1783,7 @@ def cmd_chat(args):
|
||||||
"provider": getattr(args, "provider", None),
|
"provider": getattr(args, "provider", None),
|
||||||
"toolsets": args.toolsets,
|
"toolsets": args.toolsets,
|
||||||
"skills": getattr(args, "skills", None),
|
"skills": getattr(args, "skills", None),
|
||||||
"verbose": args.verbose,
|
"verbose": getattr(args, "verbose", None),
|
||||||
"quiet": getattr(args, "quiet", False),
|
"quiet": getattr(args, "quiet", False),
|
||||||
"query": args.query,
|
"query": args.query,
|
||||||
"image": getattr(args, "image", None),
|
"image": getattr(args, "image", None),
|
||||||
|
|
@ -13811,7 +13811,7 @@ Examples:
|
||||||
("model", None),
|
("model", None),
|
||||||
("provider", None),
|
("provider", None),
|
||||||
("toolsets", None),
|
("toolsets", None),
|
||||||
("verbose", False),
|
("verbose", None),
|
||||||
("worktree", False),
|
("worktree", False),
|
||||||
]:
|
]:
|
||||||
if not hasattr(args, attr):
|
if not hasattr(args, attr):
|
||||||
|
|
@ -13826,7 +13826,7 @@ Examples:
|
||||||
("model", None),
|
("model", None),
|
||||||
("provider", None),
|
("provider", None),
|
||||||
("toolsets", None),
|
("toolsets", None),
|
||||||
("verbose", False),
|
("verbose", None),
|
||||||
("resume", None),
|
("resume", None),
|
||||||
("continue_last", None),
|
("continue_last", None),
|
||||||
("worktree", False),
|
("worktree", False),
|
||||||
|
|
|
||||||
|
|
@ -14,9 +14,10 @@ sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
|
||||||
|
|
||||||
# Module-level reference to the cli module (set by _make_cli on first call)
|
# Module-level reference to the cli module (set by _make_cli on first call)
|
||||||
_cli_mod = None
|
_cli_mod = None
|
||||||
|
_UNSET = object()
|
||||||
|
|
||||||
|
|
||||||
def _make_cli(tool_progress="all"):
|
def _make_cli(tool_progress="all", verbose=_UNSET):
|
||||||
"""Create a HermesCLI instance with minimal mocking."""
|
"""Create a HermesCLI instance with minimal mocking."""
|
||||||
global _cli_mod
|
global _cli_mod
|
||||||
_clean_config = {
|
_clean_config = {
|
||||||
|
|
@ -54,7 +55,9 @@ def _make_cli(tool_progress="all"):
|
||||||
_cli_mod = mod
|
_cli_mod = mod
|
||||||
with patch.object(mod, "get_tool_definitions", return_value=[]), \
|
with patch.object(mod, "get_tool_definitions", return_value=[]), \
|
||||||
patch.dict(mod.__dict__, {"CLI_CONFIG": _clean_config}):
|
patch.dict(mod.__dict__, {"CLI_CONFIG": _clean_config}):
|
||||||
return mod.HermesCLI()
|
if verbose is _UNSET:
|
||||||
|
return mod.HermesCLI()
|
||||||
|
return mod.HermesCLI(verbose=verbose)
|
||||||
|
|
||||||
|
|
||||||
class TestToolProgressScrollback:
|
class TestToolProgressScrollback:
|
||||||
|
|
@ -168,6 +171,20 @@ class TestToolProgressScrollback:
|
||||||
|
|
||||||
mock_print.assert_not_called()
|
mock_print.assert_not_called()
|
||||||
|
|
||||||
|
def test_verbose_mode_config_enables_cli_verbose_by_default(self):
|
||||||
|
"""Config-only display.tool_progress=verbose should enable verbose output."""
|
||||||
|
cli = _make_cli(tool_progress="verbose")
|
||||||
|
|
||||||
|
assert cli.tool_progress_mode == "verbose"
|
||||||
|
assert cli.verbose is True
|
||||||
|
|
||||||
|
def test_explicit_non_verbose_argument_still_overrides_verbose_config(self):
|
||||||
|
"""An explicit non-verbose value should keep overriding the config fallback."""
|
||||||
|
cli = _make_cli(tool_progress="verbose", verbose=False)
|
||||||
|
|
||||||
|
assert cli.tool_progress_mode == "verbose"
|
||||||
|
assert cli.verbose is False
|
||||||
|
|
||||||
def test_pending_info_stores_on_started(self):
|
def test_pending_info_stores_on_started(self):
|
||||||
"""tool.started stores args for later use by tool.completed."""
|
"""tool.started stores args for later use by tool.completed."""
|
||||||
cli = _make_cli(tool_progress="all")
|
cli = _make_cli(tool_progress="all")
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,59 @@ def _build_parser():
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
|
|
||||||
|
class TestChatVerboseArg:
|
||||||
|
"""Verify chat --verbose preserves config fallback when absent."""
|
||||||
|
|
||||||
|
def test_chat_without_verbose_leaves_attribute_unset(self):
|
||||||
|
from hermes_cli._parser import build_top_level_parser
|
||||||
|
|
||||||
|
parser, _subparsers, _chat_parser = build_top_level_parser()
|
||||||
|
args = parser.parse_args(["chat"])
|
||||||
|
|
||||||
|
assert not hasattr(args, "verbose")
|
||||||
|
|
||||||
|
def test_chat_verbose_sets_attribute_true(self):
|
||||||
|
from hermes_cli._parser import build_top_level_parser
|
||||||
|
|
||||||
|
parser, _subparsers, _chat_parser = build_top_level_parser()
|
||||||
|
args = parser.parse_args(["chat", "--verbose"])
|
||||||
|
|
||||||
|
assert args.verbose is True
|
||||||
|
|
||||||
|
def test_cmd_chat_forwards_none_when_verbose_is_absent(self, monkeypatch):
|
||||||
|
import types
|
||||||
|
import sys
|
||||||
|
|
||||||
|
import hermes_cli.main as main_mod
|
||||||
|
from hermes_cli._parser import build_top_level_parser
|
||||||
|
|
||||||
|
parser, _subparsers, chat_parser = build_top_level_parser()
|
||||||
|
chat_parser.set_defaults(func=main_mod.cmd_chat)
|
||||||
|
args = parser.parse_args(["chat"])
|
||||||
|
captured = {}
|
||||||
|
fake_cli = types.ModuleType("cli")
|
||||||
|
|
||||||
|
def fake_main(**kwargs):
|
||||||
|
captured.update(kwargs)
|
||||||
|
|
||||||
|
setattr(fake_cli, "main", fake_main)
|
||||||
|
fake_banner = types.ModuleType("hermes_cli.banner")
|
||||||
|
setattr(fake_banner, "prefetch_update_check", lambda: None)
|
||||||
|
fake_skills_sync = types.ModuleType("tools.skills_sync")
|
||||||
|
setattr(fake_skills_sync, "sync_skills", lambda quiet=True: None)
|
||||||
|
|
||||||
|
monkeypatch.setitem(sys.modules, "cli", fake_cli)
|
||||||
|
monkeypatch.setitem(sys.modules, "hermes_cli.banner", fake_banner)
|
||||||
|
monkeypatch.setitem(sys.modules, "tools.skills_sync", fake_skills_sync)
|
||||||
|
monkeypatch.setattr(main_mod, "_has_any_provider_configured", lambda: True)
|
||||||
|
monkeypatch.setattr(main_mod, "_pin_kanban_board_env", lambda: None)
|
||||||
|
|
||||||
|
main_mod.cmd_chat(args)
|
||||||
|
|
||||||
|
assert captured["quiet"] is False
|
||||||
|
assert "verbose" not in captured
|
||||||
|
|
||||||
|
|
||||||
class TestYoloEnvVar:
|
class TestYoloEnvVar:
|
||||||
"""Verify --yolo sets HERMES_YOLO_MODE regardless of flag position.
|
"""Verify --yolo sets HERMES_YOLO_MODE regardless of flag position.
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue