From 5b4e800883b5fea9e7a0c395efdf49fdb10ae978 Mon Sep 17 00:00:00 2001 From: Felix Martin Date: Thu, 11 Jun 2026 10:14:04 -0400 Subject: [PATCH] feat: add reset-windows entry to the tool palette Hook the existing tmux window-index reset into the Alt+p tool palette, special-cased like clone: selecting 'reset-windows' compacts the hqt session's window indexes (keeping the home window at 0) via TmuxManager.reset_window_indexes. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/hqt/cli.py | 16 ++++++++++++---- tests/test_cli.py | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 4 deletions(-) diff --git a/src/hqt/cli.py b/src/hqt/cli.py index 84625fb..a4b8c16 100644 --- a/src/hqt/cli.py +++ b/src/hqt/cli.py @@ -158,9 +158,10 @@ def _build_session_service(): 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- key, e.g. from #{window_name}). + TOOL is nvim/lazygit/shell (opens a styled tool window), "clone" (a fresh + harness with the same project + model), or "reset-windows" (compact the hqt + session's tmux window indexes, keeping the home window at 0). WINDOW is the + tmux window name (the hqt- key, e.g. from #{window_name}). """ import asyncio @@ -170,6 +171,13 @@ def tool_cmd(tool, window): try: if tool == "clone": asyncio.run(svc.clone_session_for_window(window)) + elif tool == "reset-windows": + from hqt.config import get_settings + + home_window = get_settings().tui_window_name + if not asyncio.run(svc.tmux.reset_window_indexes(home_window)): + click.echo("hqt: failed to reset window indexes", err=True) + raise SystemExit(1) else: asyncio.run(svc.open_tool_window_for_window(window, tool)) except ServiceError as err: @@ -177,7 +185,7 @@ def tool_cmd(tool, window): raise SystemExit(1) -PALETTE_ENTRIES = ["nvim", "lazygit", "shell", "clone"] +PALETTE_ENTRIES = ["nvim", "lazygit", "shell", "clone", "reset-windows"] # fzf colors matching the Catppuccin Frappé status bar / the Alt+o switcher. _PALETTE_FZF_COLORS = ( diff --git a/tests/test_cli.py b/tests/test_cli.py index c31b6e0..7f8569b 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -75,6 +75,41 @@ def test_tool_cmd_clone_dispatches_to_clone(monkeypatch, tmp_path): assert calls["clone"] == "hqt-5" +def test_tool_cmd_reset_windows_dispatches_to_manager(monkeypatch, tmp_path): + monkeypatch.setattr( + "hqt.config.get_settings", lambda: Settings(db_path=tmp_path / "t.db") + ) + calls = {} + + async def fake_reset(self, home_window): + calls["home"] = home_window + return True + + monkeypatch.setattr("hqt.tmux.manager.TmuxManager.reset_window_indexes", fake_reset) + + result = CliRunner().invoke(cli.main, ["tool", "reset-windows", "hqt-5"]) + + assert result.exit_code == 0, result.output + # Compacts against the configured home/TUI window, not the trigger window. + assert calls["home"] == Settings(db_path=tmp_path / "t.db").tui_window_name + + +def test_tool_cmd_reset_windows_failure_reports_error(monkeypatch, tmp_path): + monkeypatch.setattr( + "hqt.config.get_settings", lambda: Settings(db_path=tmp_path / "t.db") + ) + + async def fake_reset(self, home_window): + return False + + monkeypatch.setattr("hqt.tmux.manager.TmuxManager.reset_window_indexes", fake_reset) + + result = CliRunner().invoke(cli.main, ["tool", "reset-windows", "hqt-5"]) + + assert result.exit_code == 1 + assert "reset window indexes" in result.output + + def test_tool_cmd_reports_service_error(monkeypatch, tmp_path): from hqt.errors import ServiceError @@ -115,6 +150,7 @@ def test_palette_cmd_shows_popup_for_session_window(monkeypatch, tmp_path): popup_cmd = argv[-1] assert "fzf" in popup_cmd assert "nvim" in popup_cmd and "clone" in popup_cmd + assert "reset-windows" in popup_cmd # the window is baked into the command for `hqt tool {} ` assert "hqt-5" in popup_cmd