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>
248 lines
9.5 KiB
Markdown
248 lines
9.5 KiB
Markdown
# 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
|
|
|
|
1. **Create workspace** at `~/dev/hqt/` with `uv init --lib --name hqt`
|
|
2. **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"`
|
|
3. **Create package structure** per spec: `src/hqt/` with all subdirectories
|
|
4. **Create `.hq/PROJECT.md`** for the new project
|
|
5. **Verify**: `uv run hqt --help` produces output
|
|
|
|
## Phase 2: Database Layer
|
|
|
|
SQLite models, engine, and schema management.
|
|
|
|
### Steps
|
|
|
|
1. **`src/hqt/config.py`** — Settings dataclass with paths:
|
|
- `db_path`: `~/.local/share/hqt/hqt.db`
|
|
- `config_dir`: `~/.config/hqt/`
|
|
- `skills_dir`: `~/.config/hqt/skills/`
|
|
- `tmux_path`: `tmux`
|
|
- `tui_session_name`: `hqt-main`
|
|
2. **`src/hqt/db/models.py`** — SQLAlchemy models: `Project`, `Harness`, `Session`, `McpServer`, `ProjectMcpServer`, `ProjectSkill`
|
|
3. **`src/hqt/db/session.py`** — Engine factory, session factory, `ensure_db()` (creates dir + tables)
|
|
4. **`src/hqt/db/migrate.py`** — Additive migration helper (add missing columns)
|
|
5. **Tests**: model creation, ensure_db on fresh + existing databases
|
|
6. **Verify**: `uv run pytest tests/test_db.py -q` passes
|
|
|
|
## Phase 3: TmuxRunner
|
|
|
|
The async tmux command abstraction. Largely ported from HQ.
|
|
|
|
### Steps
|
|
|
|
1. **`src/hqt/tmux/runner.py`** — Port `TmuxRunner` from HQ:
|
|
- `new_session(name, cwd, command, width, height)`
|
|
- `has_session(name) -> bool`
|
|
- `kill_session(name)`
|
|
- `send_text(name, text)`
|
|
- `send_enter(name)`
|
|
- `capture_pane(name) -> str`
|
|
- `switch_client(target)` ← NEW (not in HQ)
|
|
- `list_sessions() -> list[str]` ← NEW
|
|
2. **`src/hqt/tmux/manager.py`** — Higher-level session lifecycle:
|
|
- `spawn_session(session: Session, config: SpawnConfig) -> None`
|
|
- `resume_session(session: Session, config: SpawnConfig) -> None`
|
|
- `kill_session(session: Session) -> None`
|
|
- `attach_session(session: Session) -> None` (switch-client)
|
|
- `is_alive(session: Session) -> bool`
|
|
- `poll_all_sessions(sessions: list[Session]) -> dict[int, bool]`
|
|
3. **Tests**: mock `_exec` function, verify command construction
|
|
4. **Verify**: `uv run pytest tests/test_tmux.py -q` passes
|
|
|
|
## Phase 4: Harness Abstraction Layer
|
|
|
|
The configurator protocol and concrete implementations.
|
|
|
|
### Steps
|
|
|
|
1. **`src/hqt/harnesses/base.py`** — `HarnessConfigurator` protocol, `SpawnConfig`, `HarnessIdentity` dataclasses
|
|
2. **`src/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
|
|
3. **`src/hqt/harnesses/kiro.py`** — Kiro CLI configurator (stub for now):
|
|
- Basic spawn command
|
|
- MCP: Kiro-managed (no injection in v1)
|
|
4. **`src/hqt/harnesses/aider.py`** — Aider configurator:
|
|
- Spawn: `aider --model {model} --restore-chat-history`
|
|
- Skills: `--read {skill_file}` flags
|
|
- No MCP support
|
|
5. **`src/hqt/harnesses/generic.py`** — Fallback for unknown harnesses:
|
|
- Env-var based config only
|
|
6. **`src/hqt/harnesses/registry.py`** — Discover available harnesses (`shutil.which`), return configurator instances
|
|
7. **Tests**: each configurator produces correct SpawnConfig for given inputs
|
|
8. **Verify**: `uv run pytest tests/test_harnesses.py -q` passes
|
|
|
|
## Phase 5: Skills & MCP Services
|
|
|
|
Skill registry and MCP server management.
|
|
|
|
### Steps
|
|
|
|
1. **`src/hqt/skills/registry.py`** — Port from HQ: scan `skills_dir`, parse frontmatter, return `Skill` objects
|
|
2. **`src/hqt/skills/injection.py`** — Port from HQ: `wrap_prompt_with_skills()`
|
|
3. **`src/hqt/mcp/service.py`** — CRUD for MCP servers and project bindings (DB operations)
|
|
4. **`src/hqt/projects/service.py`** — Project CRUD: add, list, archive, git status reading
|
|
5. **Tests**: skill discovery with fixture directory, MCP CRUD, project CRUD
|
|
6. **Verify**: `uv run pytest tests/test_skills.py tests/test_mcp.py tests/test_projects.py -q` passes
|
|
|
|
## Phase 6: Session Service (Orchestration Core)
|
|
|
|
The service that ties together DB, tmux, and harness configurators.
|
|
|
|
### Steps
|
|
|
|
1. **`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
|
|
2. **Tests**: mock tmux manager, verify session lifecycle
|
|
3. **Verify**: `uv run pytest tests/test_sessions.py -q` passes
|
|
|
|
## Phase 7: Textual TUI
|
|
|
|
The user interface.
|
|
|
|
### Steps
|
|
|
|
1. **`src/hqt/tui/app.py`** — Textual `App` subclass:
|
|
- Bindings: q=quit, n=new session, a=add project, /=filter
|
|
- Compose: Header, ProjectList, SessionList, Footer
|
|
- Timer: poll tmux liveness every 3s
|
|
2. **`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
|
|
3. **`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)
|
|
4. **`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
|
|
5. **`src/hqt/tui/screens/add_project.py`** — Modal:
|
|
- Path input (with directory completion if feasible, or paste)
|
|
- Name override (optional)
|
|
6. **`src/hqt/tui/styles.tcss`** — Textual CSS for layout
|
|
7. **Integration test**: App mounts, projects display, key bindings respond
|
|
8. **Verify**: `uv run pytest tests/test_tui.py -q` passes (headless Textual testing)
|
|
|
|
## Phase 8: CLI Entry Point & tmux Bootstrap
|
|
|
|
Wire everything together.
|
|
|
|
### Steps
|
|
|
|
1. **`src/hqt/cli.py`** — Click CLI:
|
|
- `hqt` (default) — bootstrap into tmux + launch TUI
|
|
- `hqt list` — print projects/sessions table
|
|
- `hqt attach <id>` — switch to session
|
|
- `hqt new <project> <harness>` — create session non-interactively
|
|
- `hqt doctor` — check tmux, harness availability
|
|
2. **`src/hqt/__main__.py`** — `python -m hqt` support
|
|
3. **tmux bootstrap logic**:
|
|
```python
|
|
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()
|
|
```
|
|
4. **First-run setup**: suggest tmux keybinding, create config dir/skills dir
|
|
5. **Verify**: `hqt doctor` works, `hqt` launches TUI inside tmux
|
|
|
|
## Phase 9: Integration Testing & Polish
|
|
|
|
### Steps
|
|
|
|
1. **End-to-end test**: create project → create session → verify tmux session exists → attach → kill → verify dead
|
|
2. **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
|
|
3. **README.md** with quick start
|
|
4. **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)
|
|
|
|
1. 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.
|