Initial commit: hqt — HQ Terminal TUI
TUI for orchestrating AI coding harness sessions (Claude Code, Codex, Kiro, etc.) via tmux. Click CLI bootstraps a Textual TUI over ProjectService/SessionService backed by SQLite, spawning harness sessions as tmux windows through TmuxManager. Includes recent fixes: - Visible Tab focus highlight on dialog OK/Cancel buttons - Auto-select first project on launch - Auto-select first session + per-project session-selection memory - tmux new-window targets an explicit free index, fixing "index N in use" failures (broken spawn/attach in attached sessions) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
# Simplify session attach + add rename
|
||||
|
||||
**Date:** 2026-06-10
|
||||
**Status:** Approved
|
||||
|
||||
## Goal
|
||||
|
||||
Streamline session handling with two changes:
|
||||
|
||||
1. Remove the redundant Resume (`Shift+R`) binding — attach already auto-resumes.
|
||||
2. Add a Rename action (`r`) to relabel the selected session.
|
||||
|
||||
## Background
|
||||
|
||||
Each session row carries two names:
|
||||
|
||||
- `tmux_session_name` (`hqt-{id}`) — the stable internal identifier used to
|
||||
target the tmux window and as the unique DB key. Never user-facing.
|
||||
- `nickname` — an optional display label shown in the session list and in the
|
||||
tmux window label (falls back to `tmux_session_name` when unset).
|
||||
|
||||
`attach_session()` (`src/hqt/sessions/service.py`) already handles every window
|
||||
state: alive → switch to it; dead pane or missing window → respawn via the
|
||||
fallback ladder (`_respawn_with_fallback`) then attach. The `resume_session()`
|
||||
method and its `R,shift+r` binding only duplicate that respawn-then-attach path,
|
||||
so Resume is dead weight.
|
||||
|
||||
## Change 1 — Remove the Resume binding (deletion only)
|
||||
|
||||
- **`src/hqt/tui/app.py`**: delete the `Binding("R,shift+r", "resume_session",
|
||||
"Resume")` entry and the `action_resume_session()` method.
|
||||
- **`src/hqt/sessions/service.py`**: delete the `resume_session()` method.
|
||||
|
||||
Keep `_respawn_with_fallback()` — `attach_session()` depends on it. Attach
|
||||
behavior is unchanged; Enter becomes the single way into a session, resuming
|
||||
transparently when the window is dead or gone.
|
||||
|
||||
## Change 2 — Add Rename (`r`)
|
||||
|
||||
### Service
|
||||
|
||||
New synchronous method on `SessionService` (pure DB write — no tmux call):
|
||||
|
||||
```python
|
||||
def rename_session(self, session_id: int, nickname: str | None) -> None:
|
||||
sess = self.db.get(Session, session_id)
|
||||
sess.nickname = nickname or None # empty -> None -> label falls back to tmux name
|
||||
self.db.commit()
|
||||
```
|
||||
|
||||
The next 3-second poll (`sync_window_labels()`) reads the updated `nickname` and
|
||||
refreshes the tmux window label automatically — no immediate label push needed.
|
||||
|
||||
A small `get_session(session_id) -> Session | None` accessor is added so the app
|
||||
layer can read the current nickname to prefill the dialog without reaching into
|
||||
`db` directly.
|
||||
|
||||
### Modal
|
||||
|
||||
New `RenameSessionScreen(ModalScreen[str | None])` in
|
||||
`src/hqt/tui/screens/rename_session.py`, mirroring `ProjectFormScreen`:
|
||||
|
||||
- Titled dialog with a single `Input` prefilled with the current nickname.
|
||||
- OK / Cancel buttons in a `.dialog-actions` row.
|
||||
- OK dismisses with the stripped input value; Cancel dismisses with `None`.
|
||||
|
||||
### App wiring (`src/hqt/tui/app.py`)
|
||||
|
||||
- Add `Binding("r", "rename_session", "Rename")`.
|
||||
- `action_rename_session()`:
|
||||
- Get the selected session id; warn ("Select a session first") if none.
|
||||
- Load the session via `get_session()` to read its current nickname.
|
||||
- Push `RenameSessionScreen` prefilled with that nickname.
|
||||
- On a non-`None` dismiss, call `rename_session()` then `_refresh_sessions()`.
|
||||
|
||||
## Testing
|
||||
|
||||
- `rename_session` sets `nickname`; an empty string clears it to `None`.
|
||||
- Remove or update any test referencing `resume_session` or the Resume binding.
|
||||
- Pilot test: press `r`, type a name, confirm, assert the session's nickname /
|
||||
displayed label updates.
|
||||
|
||||
## Out of scope
|
||||
|
||||
- No change to `tmux_session_name`.
|
||||
- No new harness logic.
|
||||
- No DB migration (reuses the existing `nickname` column).
|
||||
Reference in New Issue
Block a user