69 Commits

Author SHA1 Message Date
felixm 638d27bd21 feat: u restores archived sessions; x/u gated by current view 2026-06-11 12:29:24 -04:00
felixm 167695708b feat: A toggles the sessions column between active and archived
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-11 12:22:41 -04:00
felixm 6724d5e128 feat: archive session action bound to x
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-11 12:18:11 -04:00
felixm 8ee73ab8e3 feat: SessionList.set_mode swaps header for archived view
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-11 12:15:58 -04:00
felixm dfc7ffeaaf feat: h/l focus navigation between project and session columns
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-11 12:12:16 -04:00
felixm 2a56d6584b feat: add archive_session and unarchive_session to SessionService
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-11 12:08:23 -04:00
felixm c8380be72d feat: parameterize list_sessions with archived filter
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-11 12:05:19 -04:00
felixm fc95bf9830 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>
2026-06-11 11:07:33 -04:00
felixm 98a894f752 test: harden sandbox policy mapping across switch states
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-11 10:49:03 -04:00
felixm 78ea959788 feat: soft j/k field navigation in New Session dialog
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-11 10:43:00 -04:00
felixm d72f04dd89 style: unify New Session dialog focus highlight to accent
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-11 10:38:09 -04:00
felixm bafc664251 feat: reveal sandbox sub-options only when sandboxing is on
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-11 10:36:57 -04:00
felixm 5b4e800883 feat: add reset-windows entry to the tool palette
Hook the existing tmux window-index reset into the Alt+p tool palette,
special-cased like clone: selecting 'reset-windows' compacts the hqt
session's window indexes (keeping the home window at 0) via
TmuxManager.reset_window_indexes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-11 10:14:04 -04:00
felixm f5c97d1fe0 Merge branch 'tool-windows'
Add tool windows: tool registry, styled aux tmux windows
(TmuxRunner.new_aux_window / TmuxManager.open_aux_window),
SessionService tool-window + clone-for-window methods, and
hqt tool / hqt palette CLI subcommands (fzf tool launcher).

# Conflicts:
#	src/hqt/cli.py
#	src/hqt/tmux/manager.py
#	tests/test_cli.py
#	tests/test_sessions.py
2026-06-11 09:43:37 -04:00
felixm 75a0dc70bc test: verify delayed prompt injection
Fix pre-existing ty error in action_new_session by typing the sandbox
element as SandboxPolicy | None instead of object.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-11 09:08:54 -04:00
felixm 2e58b191fa feat: wire delayed prompt TUI actions
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-11 09:06:36 -04:00
felixm dfea5a5630 feat: show scheduled prompts in session rows
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-11 09:04:26 -04:00
felixm 7ebc3bf560 fix: preserve resume setup for scheduled dispatch 2026-06-11 08:50:54 -04:00
felixm 7109be8cbf test: cover schedule prompt modal validation 2026-06-11 08:48:35 -04:00
felixm ac7d57bfeb feat: add delayed prompt modal 2026-06-11 08:41:39 -04:00
felixm f10264f695 feat: dispatch due scheduled prompts 2026-06-11 08:40:16 -04:00
felixm 6c7e4e4682 Merge branch 'sandboxed-sessions' 2026-06-11 08:35:14 -04:00
felixm 488d76e88d Disable allow-rename on tool windows (match new_window)
nvim/lazygit emit OSC title sequences; without allow-rename off a tool
could overwrite the window name and scramble the styled @hqt_label.
Mirrors the window-option setup in new_window. Surfaced by final
integration review.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-11 08:28:07 -04:00
felixm 88a52d0e6d fix: handle scheduled prompts on session deletion 2026-06-11 08:26:33 -04:00
felixm 4169e7fc28 feat: schedule prompts in session service 2026-06-11 08:18:44 -04:00
felixm 5f29418186 fix: make sandboxed sessions usable for real git workflows
Address three integration gaps found in review:

- Bind ~/.gitconfig read-only in the sandbox base layer so `git commit`
  finds user.name/email inside the jail (spec §3).
- Bind a worktree session's repo common .git dir writable: the worktree's
  real gitdir lives at <repo>/.git/worktrees/<branch>, outside the bound
  cwd, so git was broken in sandboxed worktree sessions.
- doctor now gates on sandbox.is_available() (the single source of truth
  shared with the New Session dialog) rather than a raw shutil.which, so
  it agrees with the service on non-Linux platforms (spec §6).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-11 08:16:48 -04:00
felixm e35828d819 Add hqt palette CLI subcommand (fzf tool launcher)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-11 08:13:31 -04:00
felixm 60612065ff feat: persist scheduled prompts 2026-06-10 22:55:18 -04:00
felixm c574117c71 feat: report tmux prompt injection failures 2026-06-10 22:54:48 -04:00
felixm 09505f0758 fix: keep tmux focus during window index reset 2026-06-10 22:18:13 -04:00
felixm ee4bb4cbfa feat: expose tmux window index reset 2026-06-10 22:02:09 -04:00
felixm 666df40aca test: cover tmux window index reset 2026-06-10 21:51:16 -04:00
felixm 7d851dcb7e feat: doctor reports bubblewrap availability
Adds a non-fatal bubblewrap check to the doctor command that reports whether
the bwrap binary is available. This is necessary for sandboxed sessions and
the check is displayed alongside other system requirement checks.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 21:37:30 -04:00
felixm 8a304ba19e Add hqt tool CLI subcommand (tool + clone dispatch)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 21:35:57 -04:00
felixm 43fdfd61f1 Mirror create_session signature in clone test stub
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 21:33:37 -04:00
felixm f6ed53ce23 feat: sandbox toggles in New Session dialog, gated on bwrap availability
Adds sandbox_available param to NewSessionScreen, renders Switch/Select/
Switch widgets for sandbox on/off, filesystem access (rw/ro), and network
toggle. When bwrap is unavailable the switch is disabled and a warning label
is shown. The _policy_from staticmethod maps widget values to SandboxPolicy.
The result tuple gains SandboxPolicy | None as its fifth element. app.py
passes sandbox_available=sandbox.is_available() when opening the dialog and
unpacks the new element to forward sandbox= to create_session.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 21:33:34 -04:00
felixm 5265c8d4b0 Add SessionService.clone_session_for_window
Opens a fresh harness session with the same project, harness, and model
as the source window, creating a new sibling hqt-<id> conversation.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 21:31:42 -04:00
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 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 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