From c65a7d50c10eae97df94a626e6bdccbc1bab7171 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Mon, 29 Jun 2026 15:37:09 -0400 Subject: [PATCH] feat(tui): integrate composer picker --- packages/cli/AGENTS.md | 10 +-- packages/tui/src/config/keybind.ts | 2 +- packages/tui/src/routes/session/index.tsx | 86 ++++++++++++++--------- 3 files changed, 57 insertions(+), 41 deletions(-) diff --git a/packages/cli/AGENTS.md b/packages/cli/AGENTS.md index c7215121e52..209f03d623f 100644 --- a/packages/cli/AGENTS.md +++ b/packages/cli/AGENTS.md @@ -8,7 +8,7 @@ ```bash # From packages/cli: local V2 TUI -termctrl start opencode-v2-dev --host opentui --cols 112 --rows 34 -- bun dev --standalone +termctrl start opencode-v2-dev --host opentui --cols 112 --rows 34 -- bun dev # Released legacy TUI behavior reference termctrl start opencode-legacy --host opentui --cols 112 --rows 34 -- bunx opencode-ai@latest @@ -23,12 +23,12 @@ termctrl save opencode-legacy --format png --out /tmp/opencode/legacy.png ## Interactive debugging - This package is the V2 CLI adapter. Run its `dev` script when testing the TUI; do not use the repository-root `bun dev`, which launches the legacy `packages/opencode` CLI. -- Run commands from `packages/cli`. Use `bun dev --standalone` for most debugging so the TUI starts with a private V2 server instead of depending on the background service. +- Run commands from `packages/cli`. Use `bun dev` for most debugging so the TUI starts with a private V2 server. - Use `termctrl` for interactive checks instead of starting the TUI as a blocking foreground process. It provides a real PTY, handles OpenTUI's host handshake, and can save reviewable screenshots. - Use a dedicated session name and do not reuse or kill an unrelated session. ```bash -termctrl start opencode-v2-dev --host opentui --cols 112 --rows 34 -- bun dev --standalone +termctrl start opencode-v2-dev --host opentui --cols 112 --rows 34 -- bun dev termctrl wait opencode-v2-dev "Ask anything" --timeout 20000 termctrl show opencode-v2-dev ``` @@ -56,7 +56,7 @@ termctrl show opencode-v2-dev ``` - Source changes may require restarting the process. Use `termctrl restart opencode-v2-dev` rather than assuming the running TUI reloaded the change. -- To exercise background-service behavior, omit `--standalone`. Service lifecycle commands are available through `bun dev service start`, `bun dev service status`, and `bun dev service stop`. +- To exercise background-service behavior, use `bun dev service start`, `bun dev service status`, and `bun dev service stop`. - Always clean up the Terminal Control session when the check is complete: ```bash @@ -85,7 +85,7 @@ bun dev api --param key=value ```bash termctrl start opencode-v2-debug --host opentui --cols 112 --rows 34 -- \ - bun run --inspect=ws://localhost:6499/ src/index.ts --standalone + bun run --inspect=ws://localhost:6499/ src/index.ts ``` - Use `--inspect-wait` or `--inspect-brk` when execution must pause until the debugger attaches. diff --git a/packages/tui/src/config/keybind.ts b/packages/tui/src/config/keybind.ts index 4dbf8727500..878ed340937 100644 --- a/packages/tui/src/config/keybind.ts +++ b/packages/tui/src/config/keybind.ts @@ -99,7 +99,7 @@ export const Definitions = { session_toggle_timestamps: keybind("none", "Toggle message timestamps"), session_toggle_generic_tool_output: keybind("none", "Toggle generic tool output"), session_queued_prompts: keybind("q", "Manage queued prompts"), - session_child_first: keybind("down", "Go to first child session"), + session_child_first: keybind("down", "Toggle subagent picker"), session_child_cycle: keybind("right", "Go to next child session"), session_child_cycle_reverse: keybind("left", "Go to previous child session"), session_parent: keybind("up", "Go to parent session"), diff --git a/packages/tui/src/routes/session/index.tsx b/packages/tui/src/routes/session/index.tsx index 3c521a6b1e9..ee6f4809ef3 100644 --- a/packages/tui/src/routes/session/index.tsx +++ b/packages/tui/src/routes/session/index.tsx @@ -16,6 +16,7 @@ import { import path from "node:path" import { mkdir, writeFile } from "node:fs/promises" import { useRoute, useRouteData } from "../../context/route" +import { createStore } from "solid-js/store" import { useProject } from "../../context/project" import { useData } from "../../context/data" import { SplitBorder } from "../../ui/border" @@ -46,7 +47,7 @@ import { DialogSessionRename } from "../../component/dialog-session-rename" import { TodoItem } from "../../component/todo-item" import { DialogMessage } from "./dialog-message" import { Sidebar } from "./sidebar" -import { SubagentFooter } from "./subagent-footer.tsx" +import { Composer } from "./composer" import { filetype } from "../../util/filetype" import parsers from "../../parsers-config" import { errorMessage } from "../../util/error" @@ -177,7 +178,10 @@ export function Session() { if (session()?.parentID) return [] return data.session.question.list(route.sessionID) ?? [] }) - const visible = createMemo(() => !session()?.parentID && permissions().length === 0 && questions().length === 0) + const [composer, setComposer] = createStore({ + open: false, + tab: undefined as string | undefined, + }) const disabled = createMemo(() => permissions().length > 0 || questions().length > 0) const pending = createMemo(() => { @@ -764,11 +768,14 @@ export function Session() { run: () => unavailable("Backgrounding subagents"), }, { - title: "Go to child session", + title: "Toggle subagent picker", value: "session.child.first", category: "Session", - hidden: true, - run: () => unavailable("Child session discovery"), + run: () => { + if (composer.open || session()?.parentID) setComposer("open", false) + else setComposer("open", true) + dialog.clear() + }, }, { title: "Go to parent session", @@ -836,6 +843,7 @@ export function Session() { // snap to bottom when session changes createEffect(on(() => route.sessionID, toBottom)) + createEffect(on(() => route.sessionID, () => setComposer("open", false))) return ( @@ -898,37 +906,45 @@ export function Session() { - 0}> - - - 0}> - - - - - - - - setComposer("open", false)} + /> + + + {null} + + 0}> + + + 0}> + + + + { - toBottom() - }} - sessionID={route.sessionID} - right={} - /> - - + > + { + toBottom() + }} + sessionID={route.sessionID} + right={} + /> + + +