Bare -r/--resume now opens a numbered picker merging jsonl (source of truth) with the xiaohe hosted-session index, deduped by cli_session_id and sorted by real content activity (not jsonl mtime, which the xiaohe projection re-touches). -l gains a Src column (xiaohe/cli); -r <id> and -c stay passthrough. Adds read-only xiaohe_sessions data layer + tests.
282 lines
11 KiB
Python
282 lines
11 KiB
Python
"""Tests for standalone myclaude / mykimi / mycodex / myhermes / mycursor entrypoints."""
|
|
|
|
from pathlib import Path
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
from click.testing import CliRunner
|
|
|
|
from myagents.entrypoints import (
|
|
claude_cli,
|
|
codex_cli,
|
|
cursor_cli,
|
|
hermes_cli,
|
|
kimi_cli,
|
|
)
|
|
|
|
class TestMyclaudeEntrypoint:
|
|
"""``myclaude`` standalone entrypoint."""
|
|
|
|
def test_runs_claude(self) -> None:
|
|
runner = CliRunner()
|
|
with (
|
|
patch("myagents.launcher.shutil.which", return_value="/usr/bin/claude"),
|
|
patch("myagents.launcher.subprocess.run") as mock_run,
|
|
):
|
|
mock_run.return_value = MagicMock(returncode=0)
|
|
result = runner.invoke(claude_cli, [])
|
|
assert result.exit_code == 0
|
|
assert mock_run.call_args[0][0] == [
|
|
"/usr/bin/claude",
|
|
"--dangerously-skip-permissions",
|
|
]
|
|
|
|
def test_version_shows_myclaude(self) -> None:
|
|
runner = CliRunner()
|
|
result = runner.invoke(claude_cli, ["--version"])
|
|
assert result.exit_code == 0
|
|
assert "myclaude" in result.output
|
|
|
|
def test_passthrough(self, tmp_path: Path) -> None:
|
|
"""``--resume <id>`` still passes through to the native CLI."""
|
|
runner = CliRunner()
|
|
with (
|
|
patch("myagents.launcher.shutil.which", return_value="/usr/bin/claude"),
|
|
patch("myagents.launcher.subprocess.run") as mock_run,
|
|
):
|
|
mock_run.return_value = MagicMock(returncode=0)
|
|
result = runner.invoke(
|
|
claude_cli, ["--cwd", str(tmp_path), "--resume", "abc123"]
|
|
)
|
|
assert result.exit_code == 0
|
|
args = mock_run.call_args[0][0]
|
|
assert args[-2:] == ["--resume", "abc123"]
|
|
|
|
def test_bare_resume_opens_picker(self, tmp_path: Path) -> None:
|
|
"""Bare ``--resume`` opens the unified picker instead of the CLI."""
|
|
runner = CliRunner()
|
|
with (
|
|
patch("myagents.launcher.shutil.which", return_value="/usr/bin/claude"),
|
|
patch("myagents.launcher.subprocess.run") as mock_run,
|
|
):
|
|
mock_run.return_value = MagicMock(returncode=0)
|
|
result = runner.invoke(claude_cli, ["--cwd", str(tmp_path), "--resume"])
|
|
assert result.exit_code == 0
|
|
mock_run.assert_not_called()
|
|
|
|
|
|
class TestMykimiEntrypoint:
|
|
"""``mykimi`` standalone entrypoint."""
|
|
|
|
def test_runs_kimi(self) -> None:
|
|
runner = CliRunner()
|
|
with (
|
|
patch("myagents.launcher.shutil.which", return_value="/usr/bin/kimi"),
|
|
patch("myagents.launcher.subprocess.run") as mock_run,
|
|
):
|
|
mock_run.return_value = MagicMock(returncode=0)
|
|
result = runner.invoke(kimi_cli, [])
|
|
assert result.exit_code == 0
|
|
assert mock_run.call_args[0][0] == ["/usr/bin/kimi"]
|
|
|
|
def test_version_shows_mykimi(self) -> None:
|
|
runner = CliRunner()
|
|
result = runner.invoke(kimi_cli, ["--version"])
|
|
assert result.exit_code == 0
|
|
assert "mykimi" in result.output
|
|
|
|
def test_passthrough(self, tmp_path: Path) -> None:
|
|
runner = CliRunner()
|
|
with (
|
|
patch("myagents.launcher.shutil.which", return_value="/usr/bin/kimi"),
|
|
patch("myagents.launcher.subprocess.run") as mock_run,
|
|
):
|
|
mock_run.return_value = MagicMock(returncode=0)
|
|
result = runner.invoke(kimi_cli, ["--cwd", str(tmp_path), "--resume"])
|
|
assert result.exit_code == 0
|
|
assert "--resume" in mock_run.call_args[0][0]
|
|
|
|
|
|
class TestMycodexEntrypoint:
|
|
"""``mycodex`` standalone entrypoint."""
|
|
|
|
def test_runs_codex(self) -> None:
|
|
runner = CliRunner()
|
|
with (
|
|
patch("myagents.launcher.shutil.which", return_value="/usr/bin/codex"),
|
|
patch("myagents.launcher.subprocess.run") as mock_run,
|
|
):
|
|
mock_run.return_value = MagicMock(returncode=0)
|
|
result = runner.invoke(codex_cli, [])
|
|
assert result.exit_code == 0
|
|
assert mock_run.call_args[0][0] == ["/usr/bin/codex", "--dangerously-bypass-approvals-and-sandbox"]
|
|
|
|
def test_version_shows_mycodex(self) -> None:
|
|
runner = CliRunner()
|
|
result = runner.invoke(codex_cli, ["--version"])
|
|
assert result.exit_code == 0
|
|
assert "mycodex" in result.output
|
|
|
|
def test_passthrough(self, tmp_path: Path) -> None:
|
|
runner = CliRunner()
|
|
with (
|
|
patch("myagents.launcher.shutil.which", return_value="/usr/bin/codex"),
|
|
patch("myagents.launcher.subprocess.run") as mock_run,
|
|
):
|
|
mock_run.return_value = MagicMock(returncode=0)
|
|
result = runner.invoke(codex_cli, ["--cwd", str(tmp_path), "-r", "abc123"])
|
|
assert result.exit_code == 0
|
|
assert mock_run.call_args[0][0] == ["/usr/bin/codex", "--dangerously-bypass-approvals-and-sandbox", "resume", "abc123"]
|
|
|
|
|
|
class TestMyhermesEntrypoint:
|
|
"""``myhermes`` standalone entrypoint."""
|
|
|
|
def test_runs_hermes(self) -> None:
|
|
runner = CliRunner()
|
|
with (
|
|
patch("myagents.launcher.shutil.which", return_value="/usr/bin/hermes"),
|
|
patch("myagents.launcher.subprocess.run") as mock_run,
|
|
):
|
|
mock_run.return_value = MagicMock(returncode=0)
|
|
result = runner.invoke(hermes_cli, [])
|
|
assert result.exit_code == 0
|
|
assert mock_run.call_args[0][0] == ["/usr/bin/hermes", "--yolo"]
|
|
|
|
def test_version_shows_myhermes(self) -> None:
|
|
runner = CliRunner()
|
|
result = runner.invoke(hermes_cli, ["--version"])
|
|
assert result.exit_code == 0
|
|
assert "myhermes" in result.output
|
|
|
|
def test_passthrough(self, tmp_path: Path) -> None:
|
|
runner = CliRunner()
|
|
with (
|
|
patch("myagents.launcher.shutil.which", return_value="/usr/bin/hermes"),
|
|
patch("myagents.launcher.subprocess.run") as mock_run,
|
|
):
|
|
mock_run.return_value = MagicMock(returncode=0)
|
|
result = runner.invoke(
|
|
hermes_cli, ["--cwd", str(tmp_path), "--resume", "abc123"]
|
|
)
|
|
assert result.exit_code == 0
|
|
assert mock_run.call_args[0][0][-3:] == [
|
|
"--resume",
|
|
"abc123",
|
|
"--no-restore-cwd",
|
|
]
|
|
|
|
|
|
class TestMycursorEntrypoint:
|
|
"""``mycursor`` standalone entrypoint."""
|
|
|
|
def test_runs_agent(self) -> None:
|
|
runner = CliRunner()
|
|
with (
|
|
patch(
|
|
"myagents.launcher.shutil.which", return_value="/usr/bin/agent"
|
|
),
|
|
patch("myagents.launcher.subprocess.run") as mock_run,
|
|
):
|
|
mock_run.return_value = MagicMock(returncode=0)
|
|
result = runner.invoke(cursor_cli, [])
|
|
assert result.exit_code == 0
|
|
assert mock_run.call_args[0][0] == ["/usr/bin/agent", "--force"]
|
|
|
|
def test_version_shows_mycursor(self) -> None:
|
|
runner = CliRunner()
|
|
result = runner.invoke(cursor_cli, ["--version"])
|
|
assert result.exit_code == 0
|
|
assert "mycursor" in result.output
|
|
|
|
def test_passthrough(self, tmp_path: Path) -> None:
|
|
runner = CliRunner()
|
|
with (
|
|
patch(
|
|
"myagents.launcher.shutil.which", return_value="/usr/bin/agent"
|
|
),
|
|
patch("myagents.launcher.subprocess.run") as mock_run,
|
|
):
|
|
mock_run.return_value = MagicMock(returncode=0)
|
|
result = runner.invoke(
|
|
cursor_cli, ["--cwd", str(tmp_path), "-r", "abc123"]
|
|
)
|
|
assert result.exit_code == 0
|
|
assert mock_run.call_args[0][0][-2:] == ["--resume", "abc123"]
|
|
|
|
|
|
class TestTmuxOption:
|
|
"""``--tmux`` / ``-t`` wraps the backend in an attachable tmux session."""
|
|
|
|
def _which(self, name: str) -> str:
|
|
return f"/usr/bin/{name}"
|
|
|
|
def test_tmux_wraps_command(self, tmp_path: Path) -> None:
|
|
runner = CliRunner()
|
|
with (
|
|
patch("myagents.launcher.shutil.which", side_effect=self._which),
|
|
patch("myagents.launcher.subprocess.run") as mock_run,
|
|
patch.dict("os.environ", {}, clear=False) as env,
|
|
):
|
|
env.pop("TMUX", None)
|
|
mock_run.return_value = MagicMock(returncode=0)
|
|
result = runner.invoke(claude_cli, ["--cwd", str(tmp_path), "--tmux"])
|
|
assert result.exit_code == 0
|
|
argv = mock_run.call_args[0][0]
|
|
assert argv[:3] == ["/usr/bin/tmux", "new-session", "-A"]
|
|
assert "-c" in argv and str(tmp_path) in argv
|
|
assert argv[-1] == "/usr/bin/claude --dangerously-skip-permissions"
|
|
|
|
def test_tmux_session_name_deterministic(self, tmp_path: Path) -> None:
|
|
from myagents.launcher import _tmux_session_name
|
|
|
|
first = _tmux_session_name("claude", tmp_path)
|
|
second = _tmux_session_name("claude", tmp_path)
|
|
other = _tmux_session_name("kimi", tmp_path)
|
|
assert first == second
|
|
assert first != other
|
|
assert first.startswith("myclaude-")
|
|
|
|
def test_tmux_with_passthrough(self, tmp_path: Path) -> None:
|
|
runner = CliRunner()
|
|
with (
|
|
patch("myagents.launcher.shutil.which", side_effect=self._which),
|
|
patch("myagents.launcher.subprocess.run") as mock_run,
|
|
patch.dict("os.environ", {}, clear=False) as env,
|
|
):
|
|
env.pop("TMUX", None)
|
|
mock_run.return_value = MagicMock(returncode=0)
|
|
result = runner.invoke(
|
|
claude_cli, ["--cwd", str(tmp_path), "-t", "-r", "sess-1"]
|
|
)
|
|
assert result.exit_code == 0
|
|
argv = mock_run.call_args[0][0]
|
|
assert argv[0] == "/usr/bin/tmux"
|
|
assert argv[-1].endswith("-r sess-1")
|
|
|
|
def test_tmux_skipped_when_already_inside(self, tmp_path: Path) -> None:
|
|
runner = CliRunner()
|
|
with (
|
|
patch("myagents.launcher.shutil.which", side_effect=self._which),
|
|
patch("myagents.launcher.subprocess.run") as mock_run,
|
|
patch.dict("os.environ", {"TMUX": "/tmp/tmux-501/default,1,0"}),
|
|
):
|
|
mock_run.return_value = MagicMock(returncode=0)
|
|
result = runner.invoke(claude_cli, ["--cwd", str(tmp_path), "-t"])
|
|
assert result.exit_code == 0
|
|
argv = mock_run.call_args[0][0]
|
|
assert argv[0] == "/usr/bin/claude"
|
|
|
|
def test_tmux_missing_binary(self, tmp_path: Path) -> None:
|
|
runner = CliRunner()
|
|
|
|
def _which(name: str) -> str | None:
|
|
return None if name == "tmux" else f"/usr/bin/{name}"
|
|
|
|
with (
|
|
patch("myagents.launcher.shutil.which", side_effect=_which),
|
|
patch.dict("os.environ", {}, clear=False) as env,
|
|
):
|
|
env.pop("TMUX", None)
|
|
result = runner.invoke(claude_cli, ["--cwd", str(tmp_path), "-t"])
|
|
assert result.exit_code == 127
|