diff --git a/docs/superpowers/plans/2026-07-23-conpty-interactive-sessions.md b/docs/superpowers/plans/2026-07-23-conpty-interactive-sessions.md new file mode 100644 index 00000000..97b84222 --- /dev/null +++ b/docs/superpowers/plans/2026-07-23-conpty-interactive-sessions.md @@ -0,0 +1,88 @@ +# Plan — ConPTY Interactive Sessions + +Spec: `docs/superpowers/specs/2026-07-23-conpty-interactive-sessions-design.md` +Date: 2026-07-23 + +Execution: subagent-driven-development, sonnet model, TDD where meaningful, +build + test + commit per task. Stage files explicitly by path (never +`git add -A`). Terminal rendering is visual — flagged for the user's visual pass. + +## Task 0 — Spike: embed a ConPTY terminal running `claude` + +Not TDD; a throwaway proof. Add a temporary window/view that embeds each +candidate control and launches `claude` in a known worktree. + +- Evaluate **SvcSystems.UI.Terminal** and **Iciclecreek.Avalonia.Terminal**. +- Acceptance: the real `claude` TUI renders correctly — colors, resize/reflow, + and a live permission prompt is usable; input reaches the CLI. +- Output: pick one library; note the control API (start with cwd/exe/args/env, + process-exited event, dispose/kill). Record the decision in the spec. +- Remove the throwaway harness before Task 1 (or keep as a manual dev sample, + not wired into the app). + +**Stop for the user's visual verification of the spike before continuing.** + +## Task 1 — Worker: interactive launch-spec endpoint + +- Add a Worker service/hub method that, given a taskId, prepares the worktree + (session-skills seeding, agent files, MCP config, env — reuse the autonomous + run prep path) and returns a `LaunchSpec { cwd, exe, args, env }`. +- Reuse `WindowsTerminalLauncher.BuildResumeCommand` for exe/args. +- Guards mirror `ResumeTaskInTerminal` (not Running/Queued, persisted SessionId, + worktree Active/Kept). Never-run task → spec without `--resume` (fresh start). +- Tests (Worker.Tests, real SQLite/git): guard cases, spec contents for a + resumable task, fresh-start case. No real `claude` in tests. + +## Task 2 — Worker: ad-hoc launch-spec + +- Method to build a `LaunchSpec` for a free session in a given directory: + MCP config + env set up, no task/session-skills seeding. +- Tests: env/MCP presence, arbitrary cwd. + +## Task 3 — UI: terminal host control + view model + +- Wrap the chosen library in an app control/view (e.g. `InteractiveTerminalView` + + `InteractiveTerminalViewModel`) that starts from a `LaunchSpec` and exposes + running/exited state. +- `IWorkerClient`: add methods to fetch the task and ad-hoc launch specs; wire + the SignalR client + hub method. +- Update hand-rolled `IWorkerClient`/hub fakes in BOTH test projects. +- Tests: view model starts/stops lifecycle with a fake terminal backend; + fake worker returns a spec. + +## Task 4 — Command Center: host interactive panes + entry points + +- `MonitorPaneView`: autonomous panes keep the streamed log; interactive panes + host the terminal control. +- Entry points: "Open interactive session" from a task (task-based) and a + "New session" action (ad-hoc, pick directory). +- Layout toggle: focus (tabs) ↔ overview (grid); reuse/extend the existing + `UniformGrid` column logic for the grid mode. +- Tests: view-model level (pane kind selection, layout toggle state). Rendering + is a visual-pass item. + +## Task 5 — Remove the streaming interactive stack + +Only after Tasks 1–4 land and the terminal path works. + +- Worker: delete `StreamingClaudeSession`, `InteractiveSessionService`, + interactive `WorkerHub` methods + broadcast events, DI registrations. + Verify `LiveSessionRegistry` / `IdleSessionReaper` usage first; remove only if + unreferenced. +- UI: remove composer bits on `TaskMonitorViewModel`, the composer/queued portion + of `SessionTerminalView`, `IWorkerClient` interactive methods. +- Update fakes and delete now-dead tests. Full build + all test projects green. + +## Task 6 — Docs + +- Update `docs/open.md` with visual-verification items (spike render, terminal + resize/focus, grid vs tabs). +- Update affected per-project `CLAUDE.md` (Worker interactive removal, UI new + terminal host). + +## Verification gates + +- After Task 0: user visual pass on the spike. +- After Task 4: user visual pass on Command Center (task + ad-hoc, tabs + grid, + permission prompt round-trip). +- Never claim the terminal UI works without the user running it. diff --git a/docs/superpowers/specs/2026-07-23-conpty-interactive-sessions-design.md b/docs/superpowers/specs/2026-07-23-conpty-interactive-sessions-design.md new file mode 100644 index 00000000..1078f00e --- /dev/null +++ b/docs/superpowers/specs/2026-07-23-conpty-interactive-sessions-design.md @@ -0,0 +1,118 @@ +# ConPTY Interactive Sessions — Design + +Date: 2026-07-23 +Status: Approved (design), implementation not started + +## Problem + +The current in-app interactive session path streams `stream-json` from a +`claude` process spawned **in the Worker** and renders it as a chat log with a +composer. It does not surface permission requests, AskUser questions, and other +TUI-native interactions well — the rendering is a partial reimplementation of +what the real Claude Code TUI already does. We want full fidelity for +interactive work without rebuilding the TUI. + +## Decision + +**Hybrid execution model:** + +- **Autonomous queue tasks** (`Status=Queued`, picked by the queue): unchanged. + Headless `stream-json`, full orchestration (status flow, diff, review, merge). +- **Interactive sessions**: an **embedded ConPTY terminal** running the real + `claude` CLI, rendered in the **UI process**. Full TUI fidelity (permission + prompts, questions, colors, everything the standalone CLI does). Detached from + the review/merge/status machinery — these are a manual cockpit. + +This is the third direction for interactive (external `wt` terminal → streaming +chat → embedded ConPTY). The streaming interactive stack is **removed**, not run +in parallel — accepted as discarded work in exchange for one interactive path +and full fidelity. + +## Architecture + +### Process location + +ConPTY terminal controls render in-process and spawn their child (`claude`) as a +child of the host process. Therefore interactive sessions move **out of the +Worker and into the UI process**. They no longer flow over SignalR. This mirrors +the existing `ResumeTaskInTerminal` behavior (launch real `claude`), but embedded +instead of via external `wt.exe`. + +### Worktree preparation (task-based sessions) + +Interactive task sessions get the **same worktree preparation as autonomous +runs**: session-skills seeding, agent files, MCP config, environment. The Worker +performs the prep and returns a launch spec to the UI: + +``` +LaunchSpec { + cwd: string // worktree path + exe: string // resolved claude executable / shell + args: string[] // e.g. --resume + env: Dictionary +} +``` + +The command construction reuses the existing logic in +`WindowsTerminalLauncher.BuildResumeCommand`. Guards mirror +`ResumeTaskInTerminal`: task not Running/Queued, a persisted SessionId exists, +worktree exists and is Active/Kept. + +### Free / ad-hoc sessions + +In addition to task-based sessions, the user can open an ad-hoc terminal in a +chosen directory (no task). These also get MCP config + env set up so the +`claudedo` tools are available, but no per-level session-skills seeding tied to a +task. + +### Terminal host control + +Embed a ConPTY-capable Avalonia terminal control in the UI. Candidates: + +- **SvcSystems.UI.Terminal** (IvanJosipovic) — explicit ConPTY, full-screen TUI + render + resize reflow, selection/mouse reporting. +- **Iciclecreek.Avalonia.Terminal** — XTerm.NET + Porta.Pty, clean + `TerminalControl` embed API. + +Selection is resolved by a **spike** (first plan step): the acceptance criterion +is that the real `claude` TUI renders correctly inside the control — colors, +resize/reflow, and a live permission prompt are usable. + +### Command Center layout + +- `MonitorPaneView` keeps the streamed log for autonomous tasks. +- Interactive panes host the terminal control instead of the log+composer. +- Layout is **toggleable**: focus mode (tabs, one session large) ↔ overview mode + (grid, several sessions at once). + +## Removals + +Worker: +- `StreamingClaudeSession`, `InteractiveSessionService` +- `WorkerHub` interactive methods: `OpenInteractiveTerminal`, + `SendInteractiveMessage`, `RemoveQueuedInteractiveMessage`, + `StopInteractiveSession`, `InterruptInteractiveSession` +- Broadcast events: `InteractiveSessionStarted/Ended`, `InteractiveQueueChanged`, + `InteractiveMessageSent` +- `IdleSessionReaper` and `LiveSessionRegistry` **iff** unused elsewhere + (verify during implementation — do not delete blindly). + +UI: +- Composer on `TaskMonitorViewModel`: `ComposerDraft`, `SubmitComposerCommand`, + `InterruptInteractiveCommand`, `StopInteractiveCommand`, `QueuedMessages`, + `IsInteractiveLive`. +- The composer + queued-messages portion of `SessionTerminalView` (the log + portion stays for autonomous panes). +- `IWorkerClient` interactive methods. + +## Open items (implementation time) + +- Whether `LiveSessionRegistry` is referenced outside the interactive path. +- Exact env dictionary handed to the ConPTY control (parity with the autonomous + run environment). +- Session-id availability for a never-run task (no `--resume` → start fresh). + +## Non-goals + +- No screen-scraping of terminal output back into task status/diff/review. +- No change to the autonomous queue execution path.