Skip to content

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

Closed

Description

@ndycode

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

Activity

  1. ndycode commented on Jan 17, 2026

    @ndycode
    Author

    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 [Windows] CTRL_C_EVENT not handled by bun (umbrella issue) #7074, input_clear has no effect #7079, In the Windows system, pressing Ctrl+C will automatically exit the terminal #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)
  2. thdxr commented on Mar 25, 2026

    @thdxr
    Member

    To stay organized issues are automatically closed after 90 days of no activity. If the issue is still relevant please open a new one.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions