90944948f5
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>
9.5 KiB
9.5 KiB
HQ Terminal — Implementation Plan
Reference spec: .hq/2026-06-09-spec-hq-terminal.md
Phase 1: Project Scaffold
Set up the new workspace with tooling, dependencies, and basic structure.
Steps
- Create workspace at
~/dev/hqt/withuv init --lib --name hqt - Configure pyproject.toml:
- Python ≥ 3.12
- Dependencies:
textual>=2.0,sqlalchemy>=2.0,click>=8.0 - Dev dependencies:
pytest,pytest-asyncio,ruff - Entry point:
[project.scripts] hqt = "hqt.cli:main"
- Create package structure per spec:
src/hqt/with all subdirectories - Create
.hq/PROJECT.mdfor the new project - Verify:
uv run hqt --helpproduces output
Phase 2: Database Layer
SQLite models, engine, and schema management.
Steps
src/hqt/config.py— Settings dataclass with paths:db_path:~/.local/share/hqt/hqt.dbconfig_dir:~/.config/hqt/skills_dir:~/.config/hqt/skills/tmux_path:tmuxtui_session_name:hqt-main
src/hqt/db/models.py— SQLAlchemy models:Project,Harness,Session,McpServer,ProjectMcpServer,ProjectSkillsrc/hqt/db/session.py— Engine factory, session factory,ensure_db()(creates dir + tables)src/hqt/db/migrate.py— Additive migration helper (add missing columns)- Tests: model creation, ensure_db on fresh + existing databases
- Verify:
uv run pytest tests/test_db.py -qpasses
Phase 3: TmuxRunner
The async tmux command abstraction. Largely ported from HQ.
Steps
src/hqt/tmux/runner.py— PortTmuxRunnerfrom HQ:new_session(name, cwd, command, width, height)has_session(name) -> boolkill_session(name)send_text(name, text)send_enter(name)capture_pane(name) -> strswitch_client(target)← NEW (not in HQ)list_sessions() -> list[str]← NEW
src/hqt/tmux/manager.py— Higher-level session lifecycle:spawn_session(session: Session, config: SpawnConfig) -> Noneresume_session(session: Session, config: SpawnConfig) -> Nonekill_session(session: Session) -> Noneattach_session(session: Session) -> None(switch-client)is_alive(session: Session) -> boolpoll_all_sessions(sessions: list[Session]) -> dict[int, bool]
- Tests: mock
_execfunction, verify command construction - Verify:
uv run pytest tests/test_tmux.py -qpasses
Phase 4: Harness Abstraction Layer
The configurator protocol and concrete implementations.
Steps
src/hqt/harnesses/base.py—HarnessConfiguratorprotocol,SpawnConfig,HarnessIdentitydataclassessrc/hqt/harnesses/claude.py— Claude Code configurator:- UUID generation via
uuid5 - Spawn:
claude --session-id {uuid} --model {model} - Resume:
claude --resume {uuid} - MCP: write project
.mcp.json - Skills: write/append
CLAUDE.md - Ready/busy regexes from terminal profiles
- UUID generation via
src/hqt/harnesses/kiro.py— Kiro CLI configurator (stub for now):- Basic spawn command
- MCP: Kiro-managed (no injection in v1)
src/hqt/harnesses/aider.py— Aider configurator:- Spawn:
aider --model {model} --restore-chat-history - Skills:
--read {skill_file}flags - No MCP support
- Spawn:
src/hqt/harnesses/generic.py— Fallback for unknown harnesses:- Env-var based config only
src/hqt/harnesses/registry.py— Discover available harnesses (shutil.which), return configurator instances- Tests: each configurator produces correct SpawnConfig for given inputs
- Verify:
uv run pytest tests/test_harnesses.py -qpasses
Phase 5: Skills & MCP Services
Skill registry and MCP server management.
Steps
src/hqt/skills/registry.py— Port from HQ: scanskills_dir, parse frontmatter, returnSkillobjectssrc/hqt/skills/injection.py— Port from HQ:wrap_prompt_with_skills()src/hqt/mcp/service.py— CRUD for MCP servers and project bindings (DB operations)src/hqt/projects/service.py— Project CRUD: add, list, archive, git status reading- Tests: skill discovery with fixture directory, MCP CRUD, project CRUD
- Verify:
uv run pytest tests/test_skills.py tests/test_mcp.py tests/test_projects.py -qpasses
Phase 6: Session Service (Orchestration Core)
The service that ties together DB, tmux, and harness configurators.
Steps
src/hqt/sessions/service.py— Session lifecycle:create_session(project_id, harness_name, nickname, model, skill_names, mcp_ids) -> Session- Generates harness UUID via configurator
- Builds SpawnConfig
- Calls tmux manager to spawn
- Persists session row
resume_session(session_id) -> None- Loads session from DB
- Builds resume SpawnConfig with stored UUID
- Calls tmux manager to spawn
stop_session(session_id) -> None- Kills tmux session
- Updates DB (last_activity_at)
delete_session(session_id) -> None- Kills tmux if alive
- Removes DB row
attach_session(session_id) -> None- Calls tmux switch-client
list_sessions(project_id) -> list[SessionInfo]- Joins DB data with tmux liveness
- Tests: mock tmux manager, verify session lifecycle
- Verify:
uv run pytest tests/test_sessions.py -qpasses
Phase 7: Textual TUI
The user interface.
Steps
src/hqt/tui/app.py— TextualAppsubclass:- Bindings: q=quit, n=new session, a=add project, /=filter
- Compose: Header, ProjectList, SessionList, Footer
- Timer: poll tmux liveness every 3s
src/hqt/tui/widgets/project_list.py— ListView of projects:- Shows name, path, branch
- Selection updates session list
- Keybindings: Enter=select, a=add, d=archive
src/hqt/tui/widgets/session_list.py— ListView of sessions for selected project:- Shows harness icon, nickname, alive/dead status
- Keybindings: Enter=attach, n=new, d=delete, r=rename, R=resume (if dead)
src/hqt/tui/screens/new_session.py— Modal screen:- Harness selector (dropdown)
- Nickname input
- Model selector (populated from configurator.supported_models)
- Skills multi-select
- MCP servers multi-select
- Create / Cancel buttons
src/hqt/tui/screens/add_project.py— Modal:- Path input (with directory completion if feasible, or paste)
- Name override (optional)
src/hqt/tui/styles.tcss— Textual CSS for layout- Integration test: App mounts, projects display, key bindings respond
- Verify:
uv run pytest tests/test_tui.py -qpasses (headless Textual testing)
Phase 8: CLI Entry Point & tmux Bootstrap
Wire everything together.
Steps
src/hqt/cli.py— Click CLI:hqt(default) — bootstrap into tmux + launch TUIhqt list— print projects/sessions tablehqt attach <id>— switch to sessionhqt new <project> <harness>— create session non-interactivelyhqt doctor— check tmux, harness availability
src/hqt/__main__.py—python -m hqtsupport- tmux bootstrap logic:
def bootstrap(): if not in_tmux(): # Create hqt-main session with TUI as the command exec tmux new-session -s hqt-main "hqt --inside-tmux" else: # Already in tmux, just run the TUI run_tui() - First-run setup: suggest tmux keybinding, create config dir/skills dir
- Verify:
hqt doctorworks,hqtlaunches TUI inside tmux
Phase 9: Integration Testing & Polish
Steps
- End-to-end test: create project → create session → verify tmux session exists → attach → kill → verify dead
- Handle edge cases:
- tmux not installed → clear error
- Harness not found → show in UI, disable create
- DB locked → retry pattern
- Session name collision → fail gracefully
- README.md with quick start
- Verify: full test suite passes, manual smoke test with claude-code
Dependency Graph
Phase 1 (scaffold)
│
├── Phase 2 (database)
│ │
│ ├── Phase 5 (skills + MCP)
│ │ │
│ │ └──────┐
│ │ │
│ └── Phase 6 (session service) ◄── Phase 4 (harness abstraction)
│ │
├── Phase 3 (tmux runner) ──────────────┘
│ │
│ ▼
└── Phase 7 (TUI) ◄──────────── Phase 6
│
▼
Phase 8 (CLI + bootstrap)
│
▼
Phase 9 (integration + polish)
Estimated Effort
| Phase | Complexity | Approx. LOC |
|---|---|---|
| 1. Scaffold | Low | ~50 (config) |
| 2. Database | Low | ~150 |
| 3. TmuxRunner | Low | ~120 (mostly port) |
| 4. Harness abstraction | Medium | ~300 |
| 5. Skills + MCP | Low | ~150 (mostly port) |
| 6. Session service | Medium | ~200 |
| 7. TUI | Medium-High | ~400 |
| 8. CLI + bootstrap | Low | ~100 |
| 9. Integration | Medium | ~150 |
| Total | ~1,620 |
Implementation Order (sequential for a single agent)
- Phase 1 → 2. Phase 2 → 3. Phase 3 → 4. Phase 4 → 5. Phase 5 → 6. Phase 6 → 7. Phase 7 → 8. Phase 8 → 9. Phase 9
Each phase produces a working, testable increment. The TUI (Phase 7) is the largest single piece but depends on everything below it being solid.