Add hqt tool CLI subcommand (tool + clone dispatch)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -121,6 +121,47 @@ def doctor():
|
||||
click.echo("No harnesses found on PATH")
|
||||
|
||||
|
||||
def _build_session_service():
|
||||
"""Wire a SessionService the same way HqtApp.on_mount does (for CLI use)."""
|
||||
from hqt.config import get_settings
|
||||
from hqt.db.engine import ensure_db, get_engine, get_session_factory
|
||||
from hqt.harnesses.registry import discover_harnesses
|
||||
from hqt.sessions.service import SessionService
|
||||
from hqt.tmux.manager import TmuxManager
|
||||
from hqt.tmux.runner import TmuxRunner
|
||||
|
||||
settings = get_settings()
|
||||
ensure_db(settings)
|
||||
factory = get_session_factory(get_engine(settings))
|
||||
runner = TmuxRunner(settings.tmux_path, settings.tui_session_name)
|
||||
return SessionService(factory, TmuxManager(runner), discover_harnesses())
|
||||
|
||||
|
||||
@main.command(name="tool")
|
||||
@click.argument("tool")
|
||||
@click.argument("window")
|
||||
def tool_cmd(tool, window):
|
||||
"""Run TOOL for the session in tmux WINDOW.
|
||||
|
||||
TOOL is nvim/lazygit/shell (opens a styled tool window) or "clone" (a fresh
|
||||
harness with the same project + model). WINDOW is the tmux window name (the
|
||||
hqt-<id> key, e.g. from #{window_name}).
|
||||
"""
|
||||
import asyncio
|
||||
|
||||
from hqt.errors import ServiceError
|
||||
|
||||
svc = _build_session_service()
|
||||
try:
|
||||
if tool == "clone":
|
||||
asyncio.run(svc.clone_session_for_window(window))
|
||||
else:
|
||||
asyncio.run(svc.open_tool_window_for_window(window, tool))
|
||||
except ServiceError as err:
|
||||
click.echo(str(err), err=True)
|
||||
raise SystemExit(1)
|
||||
|
||||
|
||||
@main.command(name="list")
|
||||
def list_cmd():
|
||||
"""List projects and sessions."""
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
from click.testing import CliRunner
|
||||
|
||||
from hqt import cli
|
||||
from hqt.config import Settings
|
||||
|
||||
|
||||
def test_tool_cmd_opens_tool_window(monkeypatch, tmp_path):
|
||||
monkeypatch.setattr(
|
||||
"hqt.config.get_settings", lambda: Settings(db_path=tmp_path / "t.db")
|
||||
)
|
||||
calls = {}
|
||||
|
||||
async def fake_for_window(self, window, tool):
|
||||
calls["tool"] = (window, tool)
|
||||
return "@9"
|
||||
|
||||
monkeypatch.setattr(
|
||||
"hqt.sessions.service.SessionService.open_tool_window_for_window",
|
||||
fake_for_window,
|
||||
)
|
||||
|
||||
result = CliRunner().invoke(cli.main, ["tool", "lazygit", "hqt-5"])
|
||||
|
||||
assert result.exit_code == 0, result.output
|
||||
assert calls["tool"] == ("hqt-5", "lazygit")
|
||||
|
||||
|
||||
def test_tool_cmd_clone_dispatches_to_clone(monkeypatch, tmp_path):
|
||||
monkeypatch.setattr(
|
||||
"hqt.config.get_settings", lambda: Settings(db_path=tmp_path / "t.db")
|
||||
)
|
||||
calls = {}
|
||||
|
||||
async def fake_clone(self, window):
|
||||
calls["clone"] = window
|
||||
return "RESULT"
|
||||
|
||||
monkeypatch.setattr(
|
||||
"hqt.sessions.service.SessionService.clone_session_for_window", fake_clone
|
||||
)
|
||||
|
||||
result = CliRunner().invoke(cli.main, ["tool", "clone", "hqt-5"])
|
||||
|
||||
assert result.exit_code == 0, result.output
|
||||
assert calls["clone"] == "hqt-5"
|
||||
|
||||
|
||||
def test_tool_cmd_reports_service_error(monkeypatch, tmp_path):
|
||||
from hqt.errors import ServiceError
|
||||
|
||||
monkeypatch.setattr(
|
||||
"hqt.config.get_settings", lambda: Settings(db_path=tmp_path / "t.db")
|
||||
)
|
||||
|
||||
async def boom(self, window, tool):
|
||||
raise ServiceError("'hqt-5' is not an hqt session window")
|
||||
|
||||
monkeypatch.setattr(
|
||||
"hqt.sessions.service.SessionService.open_tool_window_for_window", boom
|
||||
)
|
||||
|
||||
result = CliRunner().invoke(cli.main, ["tool", "nvim", "hqt-5"])
|
||||
|
||||
assert result.exit_code == 1
|
||||
assert "not an hqt session window" in result.output
|
||||
Reference in New Issue
Block a user