[FEATURE]: mini: align process controls with the main TUI (configurable exit, ctrl+z suspend)
Summary
opencode mini lacks the main TUI's process-level controls: exit is a hardcoded two-press on prompt.clear and there is no terminal.suspend in mini's command layer. The same binary therefore exposes two different exit semantics, and shell-integration wrappers driving mini programmatically can offer neither a one-press toggle nor Unix job-control suspend.
Environment
- opencode version: 2.0.21
- OS: macOS 27.2.0 (darwin arm64)
- Terminal: WezTerm (TERM=xterm-256color, COLORTERM=truecolor)
- Shell: /bin/zsh
- Install/channel: latest
- Active plugins: local server plugin (shell-history companion), billion-context, opencode-codex-memory
Use case
I maintain a zsh plugin that launches opencode mini -s <fixed-session> as a persistent shell assistant, toggled with a single keybinding from the shell prompt (summon → ask → return to shell, dozens of times a day). Two frictions:
Exit is two presses. Mini's
prompt.cleardoubles as exit: with an empty input it arms a 5-second exit timer and requires a second press. I can rebind the key viakeybinds, but the two-press behavior itself is not configurable —keybindschanges which key triggers it, not how many presses it takes.No suspend. Ctrl+Z is swallowed by mini's raw-mode input (the terminal driver never sees it, so the Unix suspend/fg path is unavailable), and mini's command table has no
terminal.suspendeither. Every mainstream TUI (vim, less, htop) and the opencode main TUI itself support ctrl+z suspend — mini is the odd one out.
Evidence (v2.0.21, source)
handleExitinpackages/tui/src/mini/footer.ts(~line 1017):if (next < 2) { armExitTimer(); return }— two-press is hardcoded; therequestExitHandlersetter (footer.ts:214/725) has no caller, so it cannot be injected externally.- The
/exitslash command bypasses two-press entirely (onExit: () => footer.close(), single press) — so the "no confirmation" behavior already exists in mini, it is just unreachable from the keymap. - Mini's keymap layer (
footer.view.tsx~550-600) registers onlyapp.clear,command.palette.show,variant.cycle,session.background,session.child.first. The main TUI'sterminal.suspendcommand (app.tsx:1127-1136,renderer.suspend()+ SIGTSTP + SIGCONT resume) is absent. - The
miniconfig group (packages/tui/src/config/index.tsx: thinking/tools/shell_output/turn_summary/footer/splash/work_spinner/mono/replay/replay_limit) has no exit or suspend keys.
Expected Behavior
Either (ideally both):
- Exit confirmation is configurable, e.g.
mini.exitConfirmation: false— mirroring the existingmini.replay/mini.replay_limitprecedent for behavior-divergence-by-config-key. - Mini exposes
terminal.suspend(ctrl+z), reusing the mechanism the main TUI already has — the renderer (@opentui) is shared, so this is bringing an existing command to the other frontend rather than building anything new.
Additional Context
- Main TUI exits with a single press (
app.exit: ctrl+c / ctrl+d /<leader>q) — so today one binary gives one-press exit in the full TUI and two-press exit in the minimal one. - #50379 already aligned mini's exit slash aliases with the main TUI; this issue is about the keybind path and suspend.
- Note that #50088 requests the opposite for the main TUI (add a debounced double ctrl+c there). Configurable confirmation would satisfy both directions.
- Workaround currently in use:
"keybinds": { "prompt.clear": "ctrl+c,ctrl+x" }so the summon key also exits mini — but it still takes two presses, and suspend has no workaround.
Source: anomalyco/opencode