#52695·opencode

[FEATURE]: mini: align process controls with the main TUI (configurable exit, ctrl+z suspend)

Author: jensenojsCreated Oct 2, 2026Updated Oct 2, 2026
Labelsneeds:compliance

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:

  1. Exit is two presses. Mini's prompt.clear doubles as exit: with an empty input it arms a 5-second exit timer and requires a second press. I can rebind the key via keybinds, but the two-press behavior itself is not configurable — keybinds changes which key triggers it, not how many presses it takes.

  2. 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.suspend either. 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)

  • handleExit in packages/tui/src/mini/footer.ts (~line 1017): if (next < 2) { armExitTimer(); return } — two-press is hardcoded; the requestExitHandler setter (footer.ts:214/725) has no caller, so it cannot be injected externally.
  • The /exit slash 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 only app.clear, command.palette.show, variant.cycle, session.background, session.child.first. The main TUI's terminal.suspend command (app.tsx:1127-1136, renderer.suspend() + SIGTSTP + SIGCONT resume) is absent.
  • The mini config 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):

  1. Exit confirmation is configurable, e.g. mini.exitConfirmation: false — mirroring the existing mini.replay / mini.replay_limit precedent for behavior-divergence-by-config-key.
  2. 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.