Files
aiturk-hermes-ide/tests/hermes_cli/test_seeded_interactive_query.py
T

125 lines
4.3 KiB
Python

"""Seeded interactive ``-q`` behavior (Aug 2026).
On a real TTY, ``hermes chat -q "…"`` seeds a normal interactive session with
the prompt submitted literally as the first turn. Legacy answer-and-exit is
preserved for ``--oneshot``, ``-Q/--quiet``, and every non-TTY invocation
(kanban workers, cron, pipes, A2A). The seeded prompt bypasses slash-command
routing, ``!`` shell dispatch, and file-drop detection.
Context: Omarchy prompted-agent launches (basecamp/omarchy#8705) needed a
"start interactive, seeded with this prompt" mode with literal prompt
handling, like other coding agents.
"""
import sys
import types
import pytest
@pytest.fixture()
def cli_mod():
import cli
return cli
class TestShouldSeedInteractive:
def _tty(self, monkeypatch, cli_mod, stdin=True, stdout=True):
monkeypatch.setattr(
cli_mod.sys, "stdin", types.SimpleNamespace(isatty=lambda: stdin)
)
monkeypatch.setattr(
cli_mod.sys, "stdout", types.SimpleNamespace(isatty=lambda: stdout)
)
def test_tty_query_seeds_interactive(self, monkeypatch, cli_mod):
self._tty(monkeypatch, cli_mod)
assert cli_mod._should_seed_interactive("hi", None, quiet=False, oneshot=False)
def test_image_only_also_seeds(self, monkeypatch, cli_mod):
self._tty(monkeypatch, cli_mod)
assert cli_mod._should_seed_interactive(
None, "/tmp/x.png", quiet=False, oneshot=False
)
def test_oneshot_flag_forces_legacy(self, monkeypatch, cli_mod):
self._tty(monkeypatch, cli_mod)
assert not cli_mod._should_seed_interactive(
"hi", None, quiet=False, oneshot=True
)
def test_quiet_forces_legacy(self, monkeypatch, cli_mod):
self._tty(monkeypatch, cli_mod)
assert not cli_mod._should_seed_interactive(
"hi", None, quiet=True, oneshot=False
)
def test_non_tty_stdin_forces_legacy(self, monkeypatch, cli_mod):
self._tty(monkeypatch, cli_mod, stdin=False)
assert not cli_mod._should_seed_interactive(
"hi", None, quiet=False, oneshot=False
)
def test_non_tty_stdout_forces_legacy(self, monkeypatch, cli_mod):
self._tty(monkeypatch, cli_mod, stdout=False)
assert not cli_mod._should_seed_interactive(
"hi", None, quiet=False, oneshot=False
)
def test_no_query_no_image_never_seeds(self, monkeypatch, cli_mod):
self._tty(monkeypatch, cli_mod)
assert not cli_mod._should_seed_interactive(
None, None, quiet=False, oneshot=False
)
def test_isatty_failure_forces_legacy(self, monkeypatch, cli_mod):
def _boom():
raise OSError("no tty")
monkeypatch.setattr(
cli_mod.sys, "stdin", types.SimpleNamespace(isatty=_boom)
)
assert not cli_mod._should_seed_interactive(
"hi", None, quiet=False, oneshot=False
)
class TestSeededQueryMessage:
def test_str_returns_text(self, cli_mod):
msg = cli_mod._SeededQueryMessage("!echo pwned")
assert str(msg) == "!echo pwned"
assert msg.images == []
def test_images_are_copied(self, cli_mod):
imgs = ["/tmp/a.png"]
msg = cli_mod._SeededQueryMessage("hi", imgs)
assert msg.images == imgs
assert msg.images is not imgs
class TestChatParserOneshotFlag:
"""The chat subcommand's --oneshot must not collide with top-level -z."""
def _parse(self, argv):
from hermes_cli._parser import build_top_level_parser
parser, _subparsers, _chat = build_top_level_parser()
return parser.parse_args(argv)
def test_chat_oneshot_sets_distinct_dest(self):
args = self._parse(["chat", "-q", "hello", "--oneshot"])
assert args.oneshot_exit is True
# Top-level -z prompt dest untouched — dispatch sites check
# `args.oneshot` truthiness and would treat True as a prompt.
assert getattr(args, "oneshot", None) in (None, False)
def test_chat_without_oneshot_defaults_false(self):
args = self._parse(["chat", "-q", "hello"])
assert args.oneshot_exit is False
def test_top_level_oneshot_prompt_unaffected(self):
args = self._parse(["-z", "what is up"])
assert args.oneshot == "what is up"
assert getattr(args, "oneshot_exit", False) is False