feat: stop and rename a session from its tmux window

Add "stop" and "rename" to the Alt+p tool palette so a session can be
managed from inside its own harness window, mirroring the TUI's s/r keys:

- stop kills the harness window (stop_session_for_window).
- rename prompts for a nickname, pre-filled with the current one. The
  name is read from /dev/tty via readline (the fzf pipe leaves our stdin
  spent) and never passes through tmux's command parser, so there's
  nothing to quote-escape.

Extract require_session_id_for_window as the shared resolve-or-raise
guard behind the tool/stop/rename branches, dropping the duplicated
"not an hqt session window" check.

Also removes the now-shipped feature plans and specs under docs/.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-11 10:55:27 -04:00
parent ff357156d0
commit fc95bf9830
23 changed files with 243 additions and 9370 deletions
+22
View File
@@ -2073,6 +2073,15 @@ async def test_session_id_for_window_resolves_and_misses(service, db):
assert service.session_id_for_window("not-an-hqt-window") is None
@pytest.mark.asyncio
async def test_require_session_id_for_window_resolves_or_raises(service, db):
# The shared guard behind tool/stop/rename: id on a hit, ServiceError on a miss.
await service.create_session(project_id=1, harness_name="claude-code")
assert service.require_session_id_for_window("hqt-1") == 1
with pytest.raises(ServiceError):
service.require_session_id_for_window("not-an-hqt-window")
@pytest.mark.asyncio
async def test_open_tool_window_for_window_resolves_by_name(
service, db, tmux, monkeypatch
@@ -2128,3 +2137,16 @@ async def test_clone_session_for_window_reuses_project_harness_model(
async def test_clone_session_for_window_unknown_window_raises(service):
with pytest.raises(ServiceError):
await service.clone_session_for_window("not-an-hqt-window")
@pytest.mark.asyncio
async def test_stop_session_for_window_kills_by_name(service, db, tmux):
await service.create_session(project_id=1, harness_name="claude-code")
await service.stop_session_for_window("hqt-1")
tmux.kill.assert_called_once_with("hqt-1")
@pytest.mark.asyncio
async def test_stop_session_for_window_unknown_window_raises(service):
with pytest.raises(ServiceError):
await service.stop_session_for_window("not-an-hqt-window")