4.1 KiB
Reset tmux window indexes
Date: 2026-06-11 Status: Approved
Goal
Add reusable functionality that compacts tmux window indexes inside the hqt
tmux session so keyboard shortcuts such as Alt+1, Alt+2, and Alt+3 stay
useful after windows have been closed.
This reset affects only tmux window indexes. It does not change database session
IDs, tmux_session_name, harness conversation IDs, nicknames, panes, or running
processes.
Behavior
The reset operation works on the configured hqt tmux session:
- The home/TUI window always ends at tmux index
0. - Every non-home window keeps its current name, pane, process, and content.
- Non-home windows are sorted by their current tmux index.
- Those non-home windows are moved into contiguous indexes starting at
1.
Example:
0: ⌂ HQT
1: hqt-foo
4: hqt-bar
9: hqt-baz
becomes:
0: ⌂ HQT
1: hqt-foo
2: hqt-bar
3: hqt-baz
If the home window is not already at 0, it is moved to 0 first, then the
remaining windows are compacted after it in their original index order.
Architecture
Add the reset at the tmux layer rather than the session or database layer. Existing app and service code targets windows by name, so changing tmux indexes should not disturb attach, stop, delete, status polling, or label syncing.
Runner
Add a low-level method on TmuxRunner, for example:
async def reset_window_indexes(self, home_window: str) -> bool:
...
The method should:
- List windows for
self.session_namewith#{window_id},#{window_index}, and#{window_name}. - Find the row whose name matches
home_window. - Move the home window to index
0if needed. - Sort all other windows by their original index.
- Move each non-home window to
1..Nin that order.
Use move-window with window_id as the source target so changing one index
cannot make a later move target the wrong window. The destination should target
the configured tmux session and explicit index.
The implementation should be idempotent: running it against an already compact session should return success without changing anything meaningful.
Manager
Add a thin delegate on TmuxManager, for example:
async def reset_window_indexes(self, home_window: str) -> bool:
return await self.runner.reset_window_indexes(home_window)
No SessionService change is needed for the initial implementation. This is a
tmux maintenance operation, not a session lifecycle operation.
Command Surface
Implement the reusable operation now and defer user-facing wiring. A command palette is planned separately, so this feature should expose a callable method that the future command can invoke.
Do not add a TUI keybinding or footer item in this change.
Error Handling
- If the configured tmux session does not exist or cannot be listed, return failure and leave the database untouched.
- If the home window is missing, return failure rather than guessing which
window should become index
0. - If a
move-windowcommand fails, return failure. The operation may have partially moved tmux windows before the failure; this is acceptable because tmux indexes are maintenance metadata and no persisted hqt state is changed. - Log tmux stderr for failed operations to aid debugging.
Testing
Add focused tests in tests/test_tmux.py:
- Missing home window returns failure and performs no moves.
- Already compact windows return success.
- Sparse indexes compact in current index order:
0 home, 1 foo, 4 bar, 9 bazmovesbarto2andbazto3. - Home initially not at
0is moved to0, and all other windows compact to1..Nin original order. - Moves target stable window identities, not mutable index-only targets.
The implementation must pass the project checks after code changes:
uv run ruff format src tests
uv run ruff check src tests
uv run ty check
Out of Scope
- Changing database primary keys.
- Changing
Session.tmux_session_name. - Changing harness session IDs or resume behavior.
- Adding command palette UI, keybindings, or footer labels.
- Enabling permanent tmux automatic renumbering.