When more than one rollout matches the project cwd within the capture
window, return None and warn instead of guessing the earliest. The worst
case is now a kept placeholder id, never a wrong one. Part of the P2
capture-race fix.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Reject non-existent or non-directory paths with ServiceError (already
surfaced as a TUI notification), and store the resolved absolute path.
Closes the P2 finding where bad paths became dead sessions later.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Add worktree support to SessionService via the hqt.git.worktree module:
- create_session(worktree_branch=...): creates the worktree before the row
(ServiceError → no row, no spawn); spawn/capture use the worktree path;
blank nickname defaults to the branch name.
- _session_path / _repo_path_for_worktree helpers route every harness-facing
path through the worktree when set, with a parent.parent repo fallback when
the project row is gone.
- attach_session rung 0: recreate a missing worktree from its branch, else
raise "delete the session".
- delete_session(remove_worktree=, force=): remove worktree before deleting
the row; removal failure keeps the row.
- worktree_state_for(session_id): None for plain/missing sessions, else the
module's WorktreeState.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Add worktree_path and worktree_branch nullable columns to the Session
model, with a migration v2 that ALTERs the existing sessions table.
Tests cover fresh DB stamping at v2, v1→v2 upgrade path, and row-level
defaults (None when unset, populated when provided).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
TUI:
- VimListView: j/k now move down/up in the project and session lists
(arrow keys still work); used by both list widgets.
tmux status bar:
- Widen the gap between window cells via a 2-space window-status-separator
(WINDOW_STATUS_SEPARATOR).
- Fix the current-window cell toggling between padded " 1: name " and
unpadded "1: name". The per-window status format is static, but
set_window_label re-asserted it on every 3s poll — letting a competing
writer (e.g. a stale TUI process running older code with a differently
padded format) flip the cell each cycle. The format is now owned by a
single write-once path (new_window at creation, apply_theme for existing
windows); the poll updates only the dynamic @hqt_label.
Also lands pre-staged scaffolding: AGENTS.md quality gate, TODO.md backlog,
and ty dev dependency.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Style the hqt session's status bar with the same Frappé palette the TUI
uses: blue session badge, Mantle background, peach current-window
highlight, muted inactive windows, styled clock/date. The TUI window
shows just its house glyph (no index/flag/text). Applied from the TUI's
on_mount since the os.execvp bootstrap can't run follow-up tmux commands.
All options are session-scoped (never -g), so the user's other tmux
sessions and personal config are untouched. Window-scoped options
(current-window highlight, pane borders, mode style) have no session
scope in tmux, so they're painted per-window on every existing window in
apply_theme and on each new harness window in new_window — otherwise a
window spawned after mount would render with no peach highlight.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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>