Feature: Double Ctrl+C to Quit (Codex CLI-style interrupt handling) #6570

Open
opened 2026-02-16 18:04:37 -05:00 by yindo · 1 comment
Owner

Originally created by @ndycode on GitHub (Jan 17, 2026).

Summary

Add a double Ctrl+C to quit behavior inspired by Codex CLI. Instead of immediately terminating the session when Ctrl+C is pressed on an empty prompt, show a warning message and require a second Ctrl+C press within a timeout window to actually exit.


Current Behavior

Scenario Current Action
Ctrl+C with text in prompt Clears the input (works as expected)
Ctrl+C with empty prompt Immediately exits the CLI

Problem: Users can accidentally terminate their session by pressing Ctrl+C once when the prompt is empty. This is frustrating, especially during long conversations or when users instinctively press Ctrl+C to "cancel" even when there's nothing to cancel.


Proposed Behavior

Scenario Proposed Action
Ctrl+C with text in prompt Clears the input (unchanged)
1st Ctrl+C with empty prompt Show message: "Press Ctrl+C again to exit" + arm quit shortcut
2nd Ctrl+C within timeout Exit the CLI session
Timeout expires Reset state (next Ctrl+C shows warning again)

Visual Example

┌─────────────────────────────────────────────────────────┐
│  User presses Ctrl+C on empty prompt                    │
│                                                         │
│  > [empty prompt]                                       │
│                                                         │
│  ⚠️  Press Ctrl+C again to exit (or continue typing)   │
│                                                         │
│  [User presses Ctrl+C again within 1-2 seconds]         │
│  → CLI exits gracefully                                 │
└─────────────────────────────────────────────────────────┘

Implementation Reference

Codex CLI Pattern (Rust)

Codex CLI implements this via a time-bounded arming mechanism:

// Simplified from codex-rs/tui/src/chatwidget.rs
const QUIT_SHORTCUT_TIMEOUT: Duration = Duration::from_secs(1);

fn on_ctrl_c(&mut self) {
    // 1. Try to clear prompt first
    if self.bottom_pane.on_ctrl_c() == CancellationEvent::Handled {
        self.arm_quit_shortcut(key);
        return;
    }

    // 2. Check if shortcut is still armed (within timeout)
    if self.quit_shortcut_active_for(key) {
        self.request_quit_without_confirmation();
        return;
    }

    // 3. Arm the shortcut and show hint
    self.arm_quit_shortcut(key);
    self.bottom_pane.show_quit_shortcut_hint(key);
}

OpenCode Current Implementation

The relevant code is in:

  • Signal handling disabled: packages/opencode/src/cli/cmd/tui/app.tsx#L168 (exitOnCtrlC: false)
  • Current Ctrl+C logic: packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx#L798-L815

Current logic (simplified):

// If prompt has text → clear it
// If prompt is empty → exit immediately (app_exit)

Suggested Changes

  1. Add state to track "quit armed" status and timestamp
  2. Modify the empty-prompt Ctrl+C handler:
    • If not armed: arm + show warning message + set timestamp
    • If armed and within timeout: exit
    • If armed but timeout expired: reset and re-arm
  3. Add a visible hint in the UI (e.g., status bar or inline message)
  4. Suggested timeout: 1-2 seconds (Codex uses 1s)

Benefits

  1. Prevents accidental exits - No more losing context from instinctive Ctrl+C
  2. Familiar UX - Matches behavior users expect from Codex CLI and other modern CLIs
  3. Non-breaking - Users who want to exit just press twice quickly
  4. Consistent - Aligns with the existing "double escape to abort" pattern for session interruption

Acceptance Criteria

  • Pressing Ctrl+C with text in prompt clears the input (existing behavior)
  • Pressing Ctrl+C on empty prompt shows a warning message instead of exiting
  • Pressing Ctrl+C again within timeout (1-2s) exits the CLI
  • Warning message disappears after timeout or when user starts typing
  • Timeout duration is configurable (optional, nice-to-have)

Related

Originally created by @ndycode on GitHub (Jan 17, 2026). ## Summary Add a **double Ctrl+C to quit** behavior inspired by [Codex CLI](https://github.com/openai/codex). Instead of immediately terminating the session when Ctrl+C is pressed on an empty prompt, show a warning message and require a second Ctrl+C press within a timeout window to actually exit. --- ## Current Behavior | Scenario | Current Action | |----------|----------------| | Ctrl+C with text in prompt | ✅ Clears the input (works as expected) | | Ctrl+C with empty prompt | ❌ **Immediately exits the CLI** | **Problem**: Users can accidentally terminate their session by pressing Ctrl+C once when the prompt is empty. This is frustrating, especially during long conversations or when users instinctively press Ctrl+C to "cancel" even when there's nothing to cancel. --- ## Proposed Behavior | Scenario | Proposed Action | |----------|-----------------| | Ctrl+C with text in prompt | Clears the input (unchanged) | | **1st Ctrl+C with empty prompt** | Show message: `"Press Ctrl+C again to exit"` + arm quit shortcut | | **2nd Ctrl+C within timeout** | Exit the CLI session | | **Timeout expires** | Reset state (next Ctrl+C shows warning again) | ### Visual Example ``` ┌─────────────────────────────────────────────────────────┐ │ User presses Ctrl+C on empty prompt │ │ │ │ > [empty prompt] │ │ │ │ ⚠️ Press Ctrl+C again to exit (or continue typing) │ │ │ │ [User presses Ctrl+C again within 1-2 seconds] │ │ → CLI exits gracefully │ └─────────────────────────────────────────────────────────┘ ``` --- ## Implementation Reference ### Codex CLI Pattern (Rust) Codex CLI implements this via a **time-bounded arming mechanism**: ```rust // Simplified from codex-rs/tui/src/chatwidget.rs const QUIT_SHORTCUT_TIMEOUT: Duration = Duration::from_secs(1); fn on_ctrl_c(&mut self) { // 1. Try to clear prompt first if self.bottom_pane.on_ctrl_c() == CancellationEvent::Handled { self.arm_quit_shortcut(key); return; } // 2. Check if shortcut is still armed (within timeout) if self.quit_shortcut_active_for(key) { self.request_quit_without_confirmation(); return; } // 3. Arm the shortcut and show hint self.arm_quit_shortcut(key); self.bottom_pane.show_quit_shortcut_hint(key); } ``` ### OpenCode Current Implementation The relevant code is in: - **Signal handling disabled**: `packages/opencode/src/cli/cmd/tui/app.tsx#L168` (`exitOnCtrlC: false`) - **Current Ctrl+C logic**: `packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx#L798-L815` Current logic (simplified): ```typescript // If prompt has text → clear it // If prompt is empty → exit immediately (app_exit) ``` ### Suggested Changes 1. Add state to track "quit armed" status and timestamp 2. Modify the empty-prompt Ctrl+C handler: - If not armed: arm + show warning message + set timestamp - If armed and within timeout: exit - If armed but timeout expired: reset and re-arm 3. Add a visible hint in the UI (e.g., status bar or inline message) 4. Suggested timeout: **1-2 seconds** (Codex uses 1s) --- ## Benefits 1. **Prevents accidental exits** - No more losing context from instinctive Ctrl+C 2. **Familiar UX** - Matches behavior users expect from Codex CLI and other modern CLIs 3. **Non-breaking** - Users who want to exit just press twice quickly 4. **Consistent** - Aligns with the existing "double escape to abort" pattern for session interruption --- ## Acceptance Criteria - [ ] Pressing Ctrl+C with text in prompt clears the input (existing behavior) - [ ] Pressing Ctrl+C on empty prompt shows a warning message instead of exiting - [ ] Pressing Ctrl+C again within timeout (1-2s) exits the CLI - [ ] Warning message disappears after timeout or when user starts typing - [ ] Timeout duration is configurable (optional, nice-to-have) --- ## Related - Codex CLI implementation: https://github.com/openai/codex/blob/main/codex-rs/tui/src/chatwidget.rs#L4017 - OpenCode already uses similar pattern for session abort: double-escape within 5 seconds
Author
Owner

@ndycode commented on GitHub (Jan 17, 2026):

Update: Windows Ctrl+C Behavior — Root Cause Identified

After further investigation, the behavior described in this feature request has a deeper root cause on Windows that must be addressed first.


The Problem

On Windows, Ctrl+C sends a CTRL_C_EVENT at the OS level. Bun currently does not suppress this event, so the process is terminated by the OS before OpenCode's TUI layer (@opentui/solid) can intercept and handle the keypress.

This means:

  • exitOnCtrlC: false in app.tsx:168 has no effect on Windows
  • The input_clear keybind (ctrl+c) in prompt/index.tsx:798-815 never executes
  • Users experience immediate termination regardless of prompt state

Evidence: There's also a global keyboard handler in app.tsx:659-662 that calls handleExit() unconditionally on Ctrl+C, but this is secondary — on Windows, the process dies before this handler even runs.


Related Issues

Issue Description
#7074 Umbrella issue for Windows CTRL_C_EVENT handling
#7079 input_clear has no effect
#6189 Windows: Ctrl+C auto exits terminal
#7957 Ctrl+C should not exit OpenCode

Upstream Fix in Progress

PR: oven-sh/bun#25876
Status: Open, mergeable, 3 reviews, passing checks
Author: @Hona

The PR adds:

  1. Windows-specific CTRL_C_EVENT absorption in c-bindings.cpp
  2. Stdin raw-mode state tracking (Bun__setStdinRawMode)
  3. Active subprocess counting to prevent parent from killing children
  4. Pending Ctrl+C queue so TUI apps can handle the event in userland

Once merged, OpenCode needs to:

  1. Bump Bun dependency to the version containing the fix
  2. Verify input_clear works as expected on Windows
  3. Then implement the "double Ctrl+C to quit" behavior described in this issue

Suggested Implementation Order

  1. Wait for oven-sh/bun#25876 to merge
  2. Update Bun dependency — this alone should fix #7074, #7079, [FEATURE]: Option to hide environment variable prefix in bash command display (#6189)
  3. Remove the unconditional exit handler in app.tsx:659-662 (or gate it properly)
  4. Implement double-press logic as described in the original issue above

Current Workarounds (Windows)

Action Keybind
Exit Ctrl+D or <leader>q (Ctrl+X → q)
Interrupt AI Escape (double-tap within 5s to abort)
@ndycode commented on GitHub (Jan 17, 2026): ## Update: Windows Ctrl+C Behavior — Root Cause Identified After further investigation, the behavior described in this feature request has a deeper root cause on Windows that must be addressed first. --- ### The Problem On Windows, `Ctrl+C` sends a `CTRL_C_EVENT` at the OS level. Bun currently does not suppress this event, so the process is terminated by the OS **before** OpenCode's TUI layer (`@opentui/solid`) can intercept and handle the keypress. This means: - `exitOnCtrlC: false` in `app.tsx:168` has no effect on Windows - The `input_clear` keybind (`ctrl+c`) in `prompt/index.tsx:798-815` never executes - Users experience immediate termination regardless of prompt state **Evidence**: There's also a global keyboard handler in `app.tsx:659-662` that calls `handleExit()` unconditionally on `Ctrl+C`, but this is secondary — on Windows, the process dies before this handler even runs. --- ### Related Issues | Issue | Description | |-------|-------------| | [#7074](https://github.com/anomalyco/opencode/issues/7074) | Umbrella issue for Windows `CTRL_C_EVENT` handling | | [#7079](https://github.com/anomalyco/opencode/issues/7079) | `input_clear` has no effect | | [#6189](https://github.com/anomalyco/opencode/issues/6189) | Windows: Ctrl+C auto exits terminal | | [#7957](https://github.com/anomalyco/opencode/issues/7957) | Ctrl+C should not exit OpenCode | --- ### Upstream Fix in Progress **PR**: [oven-sh/bun#25876](https://github.com/oven-sh/bun/pull/25876) **Status**: Open, mergeable, 3 reviews, passing checks **Author**: @Hona The PR adds: 1. Windows-specific `CTRL_C_EVENT` absorption in `c-bindings.cpp` 2. Stdin raw-mode state tracking (`Bun__setStdinRawMode`) 3. Active subprocess counting to prevent parent from killing children 4. Pending Ctrl+C queue so TUI apps can handle the event in userland Once merged, OpenCode needs to: 1. Bump Bun dependency to the version containing the fix 2. Verify `input_clear` works as expected on Windows 3. Then implement the "double Ctrl+C to quit" behavior described in this issue --- ### Suggested Implementation Order 1. **Wait for [oven-sh/bun#25876](https://github.com/oven-sh/bun/pull/25876) to merge** 2. **Update Bun dependency** — this alone should fix #7074, #7079, #6189 3. **Remove the unconditional exit handler** in `app.tsx:659-662` (or gate it properly) 4. **Implement double-press logic** as described in the original issue above --- ### Current Workarounds (Windows) | Action | Keybind | |--------|---------| | Exit | `Ctrl+D` or `<leader>q` (Ctrl+X → q) | | Interrupt AI | `Escape` (double-tap within 5s to abort) |
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: anomalyco/opencode#6570