96 Commits

Author SHA1 Message Date
felixm 3da3fc6c4c Polish tool-window methods: error format, logging, async test
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 21:30:06 -04:00
felixm e54cbb96b6 feat: wrap sandboxed sessions in bwrap on spawn and resume
- Import `hqt.sandbox` module and `SandboxPolicy` in service.py
- Add `_sandbox_policy` helper to decode persisted JSON policy from a session row
- Add `_wrap_command` helper that calls `sandbox.is_available` / `sandbox.wrap` and raises `ServiceError` when bwrap is unavailable for a sandboxed session
- Add `sandbox: SandboxPolicy | None = None` parameter to `create_session`; persist as `sandbox_json`, pass `sandboxed=` to `build_spawn_config`, wrap command before spawning
- Update `_get_resume_config` to pass `sandboxed=` to `build_resume_config` and wrap the resulting command
- Update rung-2 path in `_respawn_with_fallback` to pass `sandboxed=` and wrap the fresh-spawn command
- Add three new tests covering: sandboxed create wraps command, unsandboxed create does not wrap, and ServiceError on missing bwrap

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 21:28:22 -04:00
felixm e588c62fd4 Add SessionService tool-window + window-resolution methods
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 21:25:39 -04:00
felixm 080ddd9364 feat: add Session.sandbox_json column and migration
Adds a nullable sandbox_json TEXT column to the sessions table via a new
version-3 migration. Updates LATEST_VERSION to 3 and adjusts existing
migration tests to account for the new column and version number.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 21:23:45 -04:00
felixm 115d0ffc01 Add TmuxManager.open_aux_window delegate
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 21:22:53 -04:00
felixm 7835511f0a Test new_aux_window cleanup when set-option fails
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 21:21:33 -04:00
felixm 4c36385045 feat: configurators declare sandbox skip-permission flags and binds
Add sandbox_skip_permission_flags class attr and sandbox_binds() method
to HarnessConfigurator ABC; thread sandboxed: bool = False through all
build_spawn_config / build_resume_config signatures. Claude and Codex
append their respective skip-permission flags when sandboxed=True; Kiro
and Generic accept the param but add no flags.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 21:20:50 -04:00
felixm 9db79c68f4 feat: sandbox.wrap builds bwrap argv from policy and binds
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 21:16:44 -04:00
felixm 9896521fa9 Add TmuxRunner.new_aux_window for styled tool windows
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 21:16:37 -04:00
felixm 3130d95cc5 feat: sandbox primitives — Bind, SandboxPolicy, is_available
Core sandbox infrastructure for isolating harness sessions. Bind defines
read-only/writable path bindings, SandboxPolicy specifies filesystem and
network isolation modes, and is_available() is the single source of truth
for bubblewrap availability across the application.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 21:15:30 -04:00
felixm 7c543beffe Add tool registry for tool windows
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 21:12:23 -04:00
felixm b680112c9c chore: ignore .worktrees directory
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 21:10:02 -04:00
felixm e74a6e6eb8 Merge branch 'main' into worktree-sessions 2026-06-10 21:08:02 -04:00
felixm dff53144c9 Merge branch 'main' into worktree-sessions
# Conflicts:
#	src/hqt/sessions/service.py
#	tests/test_sessions.py
2026-06-10 21:07:20 -04:00
felixm 897f6591dc docs: implementation plan for bubblewrap-sandboxed sessions
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 21:05:16 -04:00
felixm fb92ccd1ef chore: clear resolved P2 findings from TODO
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 21:04:22 -04:00
felixm f796590746 test: use real directories in TUI/integration project tests
ProjectService now rejects non-existent paths (path-validation feature), so
the TUI and integration tests must seed projects with real tmp_path dirs.
Completes the path-validation test updates beyond test_services.py.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 21:01:54 -04:00
felixm d78d8ee149 test: add isolated real-tmux smoke tests
Drive TmuxRunner against a throwaway tmux server (isolated via TMUX_TMPDIR)
so the tests never touch the user's live tmux. Register the 'tmux' pytest marker.
Skips when no tmux binary is present.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 20:56:58 -04:00
felixm 8bfbead34f feat: show focused pane title in tmux status-right
Lead status-right with the focused pane's terminal title (truncated to 21
cols) and widen status-right-length to 64, restoring the preview tmux shows by
default.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 20:56:37 -04:00
felixm 5c63d0fbec docs: spec + plan for Alt+p tool palette (nvim/lazygit/shell/clone)
Adds a single tmux Alt+p fzf palette that opens nvim/lazygit/shell as styled
tool windows for the current session's project, or clones a fresh harness with
the same project+model. Bridges via run-shell (expands #{window_name}) into a
new 'hqt palette' CLI subcommand, since display-popup does not format-expand its
command (verified on tmux 3.6b).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 20:56:33 -04:00
felixm cabac16fd4 docs: design spec for bubblewrap-sandboxed sessions
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 20:54:09 -04:00
felixm ace537e3d6 fix: worktree session UI safety and visibility (review)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 20:49:12 -04:00
felixm 6ed598b443 fix: serialize Codex spawn->capture with a lock
Hold an asyncio.Lock across the spawn->capture window for harnesses that
capture a session id, so two concurrent hqt starts in the same project
never produce overlapping capture windows. Completes the P2 capture-race
fix alongside the ambiguity guard.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 20:47:35 -04:00
felixm f6a90e8f4d fix: refuse ambiguous Codex session-id capture
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>
2026-06-10 20:43:52 -04:00
felixm 9efa270422 feat: worktree session UI (dialog checkbox, confirm delete, list marker)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 20:41:11 -04:00
felixm 1b79530214 feat: validate project paths at create/update
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>
2026-06-10 20:40:24 -04:00
felixm 0cf147ee25 fix: robust worktree repo-path derivation and branch typing (review)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 20:28:25 -04:00
felixm 61f1a07ba7 feat: worktree-aware session lifecycle in SessionService
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>
2026-06-10 20:21:37 -04:00
felixm ad7f9f87aa docs: implementation plan for P2 fixes
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 20:08:54 -04:00
felixm ca0d028a87 docs: design spec for P2 fixes
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 20:08:54 -04:00
felixm 860f1b59bd feat: worktree columns on sessions + migration v2
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>
2026-06-10 20:03:38 -04:00
felixm 682c6a3f74 docs: implementation plan for P2 fixes
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 20:01:06 -04:00
felixm cc49859b08 fix: harden git worktree module (review findings)
- is_git_repo: check out.strip() == "true" (bare repos / .git dirs exit 0 with "false")
- _validate_branch: new shared helper calls check-ref-format; used in all three
  public branch-taking functions to block option-injection (--detach, --all, etc.)
- _ensure_worktrees_excluded: prepend newline when existing content lacks trailing newline
- WorktreeState: make frozen=True (codebase precedent)
- create_worktree invalid-branch ServiceError now includes git stderr
- remove_worktree: log.debug for silently-swallowed branch -d / worktree prune failures
- worktree_state docstring: document git-failure → clean-state behaviour
- Tests: 9 new regression tests; raw subprocess.run commit calls replaced with _commit helper

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 19:59:16 -04:00
felixm 0e7d8ff35b docs: design spec for P2 fixes
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 19:56:26 -04:00
felixm bb142d05e4 feat: git worktree module
Adds hqt.git.worktree with slugify, is_git_repo, WorktreeState,
create_worktree, add_worktree_for_branch, worktree_state, and
remove_worktree; includes 25 real-git integration tests.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 19:51:45 -04:00
felixm 685f39baec docs: worktree-sessions spec and implementation plan
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 19:48:20 -04:00
felixm c29d862774 chore: close out P1 findings
Resolve all three P1 items: per-operation DB sessions, ServiceError
user-facing failures, and user_version schema migrations. Add the
implementation plan doc.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 19:31:58 -04:00
felixm 98ea8a1a5c feat: factory-based DB wiring and ServiceError notifications in TUI
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 19:31:06 -04:00
felixm 208a9028e7 refactor: per-operation DB sessions and ServiceError in SessionService
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 19:16:15 -04:00
felixm c93135b338 refactor: per-operation DB sessions in Project/Mcp services
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 19:10:09 -04:00
felixm 5109609b8d feat: add ServiceError and detached-safe session factory
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 19:05:40 -04:00
felixm 8870b8f3ef feat: add user_version-based schema migrations
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 19:02:53 -04:00
felixm a7851e7007 Satisfy project quality gates 2026-06-10 18:23:52 -04:00
felixm 9379e1523c Add vim list nav, fix window-title padding toggle, widen title spacing
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>
2026-06-10 17:43:47 -04:00
felixm 5eea909029 Theme tmux status bar to match Catppuccin Frappé TUI
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>
2026-06-10 17:26:34 -04:00
felixm 90944948f5 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>
2026-06-10 17:06:57 -04:00