Skip to content

repl: handle Ctrl+C at the prompt on Windows instead of exiting - #38073

Open
robobun wants to merge 4 commits into
mainfrom
farm/c83da240/windows-repl-ctrl-c
Open

repl: handle Ctrl+C at the prompt on Windows instead of exiting#38073
robobun wants to merge 4 commits into
mainfrom
farm/c83da240/windows-repl-ctrl-c

Conversation

@robobun

@robobun robobun commented Aug 13, 2026

Copy link
Copy Markdown
Collaborator

Problem

  • On Windows, pressing Ctrl+C in bun repl while sitting at the prompt ends the process with STATUS_CONTROL_C_EXIT (0xC000013A). On POSIX the same keystroke clears the line, or prints (press Ctrl+C again to exit, or Ctrl+D) on an empty line.
  • Cause: setup_terminal() in src/runtime/cli/repl.rs sets ENABLE_PROCESSED_INPUT on the console input mode. With that flag conhost consumes Ctrl+C and raises a CTRL_C_EVENT instead of queueing the 0x03 byte, so read_key() never produces Key::CtrlC and handle_ctrl_c() is dead code on Windows. The REPL installs no console control handler, so the default one exits the process.
  • This is the "ctrl+c just quits bun repl" half of bun repl: way to break infinite loop #27558. The other half, breaking out of a running while (1) {}, is repl: interrupt a running evaluation with Ctrl+C #33411 and is not changed here, so this PR does not close that issue.
  • The flag combination was copied from the bun init / bun update -i prompts, where exiting on Ctrl+C is the intended behaviour (init_command.rs says so in a comment). The REPL wants the keystroke.

Fix

  • setup_terminal() clears ENABLE_PROCESSED_INPUT together with line input and echo, so Ctrl+C arrives as 0x03 and goes through the existing Key::CtrlC path, the same path POSIX raw mode (no ISIG) uses. This is also what libuv's raw mode does on Windows (uv_tty_set_mode only puts ENABLE_PROCESSED_INPUT in the normal mode word), so node's REPL and process.stdin.setRawMode(true) already run with it off.
  • evaluate_and_print() and evaluate_and_copy() hold a StdinModeGuard that sets the flag back for the duration of the evaluation (evaluate_raw is only reached from inside these two). Nothing reads stdin while JavaScript runs, so a Ctrl+C typed then would otherwise sit in the input buffer until the next prompt; with the flag set it still raises CTRL_C_EVENT and ends the process, which is what happens today and is the only way out of a hung evaluation on Windows. The guard restores the line editor's mode on every return path, and is skipped when setup_terminal() did not touch the console (stdin or stdout not a tty), so piped bun repl is unchanged.
  • POSIX code is untouched; the SIGINT handling around the promise wait is the same as before. repl: interrupt a running evaluation with Ctrl+C #33411 (interrupting a running evaluation) builds on CTRL_C_EVENT being raised during evaluation, which this change keeps.
  • Tests: test/js/bun/repl/repl.test.ts, new Bun REPL (Terminal) Ctrl+C block that runs on Windows too (the existing terminal block is still todoIf(isWindows)). Ctrl+C with pending input, on an empty line (hint, then exit 0 on the second press), and with a multiline input pending, which fail on an unfixed bun on both Windows configurations. A Windows-only test reads stdin's console mode through bun:ffi from inside an evaluation, once via .load (evaluate_and_print) and once via .copy <code> (evaluate_and_copy), asserting ENABLE_PROCESSED_INPUT is set each time; removing only the evaluate_and_copy guard makes its second assertion fail. Each of those waits also requires the prompt printed after the evaluation, which the REPL writes only after the guard has been dropped, and the test then sends Ctrl+C and expects the hint (the mode was restored; this step fails on an unfixed bun). A cross-platform test types a second line while the first is still evaluating and expects both results: input queued while the evaluation-time mode is in effect is delivered (SetConsoleMode does not discard queued input). The withTerminalRepl helper now matches on ANSI-stripped output, because ConPTY re-renders the REPL's output (the trailing space of > comes back as an erase sequence), and fails immediately with the exit code when the REPL exits while a test is waiting for output.
  • The evaluation test observes the mode flag instead of sending Ctrl+C during an evaluation: CI job trees run with Ctrl+C ignored (the per-process flag set by CREATE_NEW_PROCESS_GROUP / SetConsoleCtrlHandler(NULL, TRUE) is inherited by children), and a process that ignores Ctrl+C never receives CTRL_C_EVENT, so a test expecting the REPL to die during an evaluation hangs there. Reproduced locally by setting that flag in the process that runs bun test: the keystroke tests are unaffected, a kill-based test hangs, the mode-based test passes.
  • Verified on Windows Server 2019 x64 (build 17763) and Windows 11 24H2 arm64 (build 26100), the two Windows CI configurations: with USE_SYSTEM_BUN=1 (bun 1.4.0) the four Ctrl+C tests fail with REPL exited (code 58, signal null) before printing ... (58 is the low byte of 0xC000013A) and the type-ahead test passes; with the debug build all five pass, both normally and with Ctrl+C ignored in the test runner as in CI, across repeated runs. The full file passes on Windows x64 and Linux; CI on the previous revision was green on both Windows lanes.

Background

  • Windows console input mode: every console input buffer has a flag word set with SetConsoleMode. ENABLE_LINE_INPUT / ENABLE_ECHO_INPUT are the equivalent of termios ICANON / ECHO; ENABLE_PROCESSED_INPUT is the equivalent of ISIG: while set, Ctrl+C is not delivered as input but turned into a CTRL_C_EVENT. ENABLE_VIRTUAL_TERMINAL_INPUT makes arrow keys and friends arrive as escape sequences, which is how the REPL decodes them.
  • CTRL_C_EVENT: conhost runs the process's console control handlers (SetConsoleCtrlHandler) on a new thread; if none claims the event, the default handler calls ExitProcess(STATUS_CONTROL_C_EXIT). The mode flag only affects keyboard Ctrl+C; GenerateConsoleCtrlEvent from another process still ends the REPL, just as kill -INT ends it on POSIX.
  • bun_sys::windows::StdinModeGuard: existing RAII helper that applies a set/unset pair to stdin's console mode and restores the previous mode on drop; inert when stdin is not a console. Already used by prompt(), bun init and bun update -i.
  • Bun.Terminal on Windows is a ConPTY. Writing \x03 to it goes through conhost's input path, so it is converted to CTRL_C_EVENT or delivered as a byte depending on the client's mode, exactly like a keypress; the unfixed REPL dies under it on both Windows versions above, which is what makes the tests fail before the fix.
Test runs

Windows Server 2019 x64, USE_SYSTEM_BUN=1 bun test test/js/bun/repl/repl.test.ts -t "Terminal\) Ctrl" (bun 1.4.0):

error: REPL exited (code 58, signal null) before printing ^C
(fail) Bun REPL (Terminal) Ctrl+C > Ctrl+C with pending input clears the line [41.89ms]
error: REPL exited (code 58, signal null) before printing press Ctrl+C again to exit
(fail) Bun REPL (Terminal) Ctrl+C > Ctrl+C on an empty line shows the exit hint; a second one exits [31.06ms]
error: REPL exited (code 58, signal null) before printing "undefined-checked"
(fail) Bun REPL (Terminal) Ctrl+C > Ctrl+C while a multiline input is pending discards it [45.53ms]
(pass) Bun REPL (Terminal) Ctrl+C > input typed while an evaluation is running is evaluated afterwards [267.10ms]
error: REPL exited (code 58, signal null) before printing press Ctrl+C again to exit
(fail) Bun REPL (Terminal) Ctrl+C > evaluations run with ENABLE_PROCESSED_INPUT set, the prompt without [76.85ms]
 1 pass
 4 fail

Same machine, debug build with this change, run from a parent that called SetConsoleCtrlHandler(NULL, TRUE) first (what a CI job tree looks like; the plain run is identical):

(pass) Bun REPL (Terminal) Ctrl+C > Ctrl+C with pending input clears the line [272.51ms]
(pass) Bun REPL (Terminal) Ctrl+C > Ctrl+C on an empty line shows the exit hint; a second one exits [231.80ms]
(pass) Bun REPL (Terminal) Ctrl+C > Ctrl+C while a multiline input is pending discards it [251.23ms]
(pass) Bun REPL (Terminal) Ctrl+C > input typed while an evaluation is running is evaluated afterwards [460.30ms]
(pass) Bun REPL (Terminal) Ctrl+C > evaluations run with ENABLE_PROCESSED_INPUT set, the prompt without [306.83ms]
 5 pass
 0 fail

Same machine, debug build with the evaluate_and_copy guard deleted (everything else unchanged): the mode test fails at its .copy assertion (Expected: 1, Received: 0), the .load assertion before it passes.

Windows 11 24H2 arm64: identical results in all configurations. Repeated runs of the whole block with the debug build under the Ctrl+C-ignored parent: 5/5 on Server 2019, 3/3 on Windows 11.

Full file: Windows x64 debug build 129 pass / 2 skip / 20 todo / 0 fail (previous revision); Linux debug build 150 pass / 1 skip / 0 fail (previous revision), the block itself re-run on Linux after the last change. cargo fmt --check and prettier clean. The cfg(windows) code was compiled by the x64 and arm64 Windows builds, the cfg(unix) side by the Linux build.

The exit code 58 in the messages above is Bun truncating the child's 32-bit status to one byte on Windows (node reports 3221225786); reported separately.

setup_terminal() set ENABLE_PROCESSED_INPUT on the console, so conhost
turned Ctrl+C into a CTRL_C_EVENT (default handler: exit the process
with STATUS_CONTROL_C_EXIT) instead of delivering the 0x03 byte that
read_key maps to Key::CtrlC. Clear the flag while line editing, as
libuv's raw mode does, and set it only for the duration of an
evaluation, where nothing reads stdin and Ctrl+C ending the process is
the only way out of a hung evaluation.

The terminal REPL tests match on ANSI-stripped output and fail as soon
as the REPL exits, so the Ctrl+C tests can run under ConPTY as well.
@coderabbitai

coderabbitai Bot commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

@robobun, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 38 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: f9bdcbb2-d255-445a-a8d2-e3eac980e106

📥 Commits

Reviewing files that changed from the base of the PR and between b7a0431 and 5d48e86.

📒 Files selected for processing (2)
  • src/runtime/cli/repl.rs
  • test/js/bun/repl/repl.test.ts

Comment @coderabbitai help to get the list of available commands.

@robobun

robobun commented Aug 13, 2026

Copy link
Copy Markdown
Collaborator Author
Updated 1:48 PM PT - Aug 13th, 2026

@robobun, your commit 5d48e86 has 1 failures in Build #94772 (All Failures):


🧪   To try this PR locally:

bunx bun-pr 38073

That installs a local version of the PR into your bun-38073 executable, so you can run:

bun-38073 --bun

@robobun

robobun commented Aug 13, 2026

Copy link
Copy Markdown
Collaborator Author

Status: reproduced and fixed, waiting on CI for 5d48e86.

Reproduced on Windows Server 2019 x64 and Windows 11 24H2 arm64 by running bun repl inside a Bun.Terminal (ConPTY) and writing \x03 at the prompt: bun 1.4.0 exits with STATUS_CONTROL_C_EXIT (shown as code 58) instead of printing ^C or the exit hint. Same with an unpatched debug build of main. The new tests in test/js/bun/repl/repl.test.ts fail the same way without the repl.rs change and pass with it on both machines.

Review so far: two automated LGTMs, comment-linter threads resolved (comments shortened in 70a7464), and a self-review pass whose points were addressed in the tests (5d48e86). #27558 is referenced in the description rather than closed (see the comments below).

@github-actions

Copy link
Copy Markdown
Contributor

Found 1 issue this PR may fix:

  1. bun repl: way to break infinite loop #27558 - Reports verbatim that on Windows "ctrl+c just quits bun repl" and asks for Node's two-press-to-exit behavior, which this PR implements at the prompt — note this is a partial fix: the issue's titled request (breaking out of while (1) {}) is explicitly left unchanged here and deferred to repl: interrupt a running evaluation with Ctrl+C #33411, so consider referencing rather than auto-closing it.

If this is helpful, copy the block below into the PR description to auto-close these issues on merge.

Fixes #27558

🤖 Generated with Claude Code

@robobun

robobun commented Aug 13, 2026

Copy link
Copy Markdown
Collaborator Author

Referencing #27558 rather than closing it: this PR covers the "Ctrl+C quits the REPL" part of that issue (Ctrl+C at the prompt now clears the line, and two presses in a row exit), but the interruption of a running while (1) {} it asks for is #33411, which is unchanged by this PR. Added a note to the description.

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM — moves ENABLE_PROCESSED_INPUT from set to unset in the REPL's Windows console mode so Ctrl+C arrives as 0x03 at the prompt, and holds a StdinModeGuard during evaluation so Ctrl+C still ends a hung eval.

What was reviewed:

  • Verified StdinModeGuard::set restores the captured mode on drop and is inert when stdin isn't a console; the guard is gated on original_windows_mode.is_some() so piped bun repl is unchanged.
  • Checked evaluate_raw is only reached from inside evaluate_and_print/evaluate_and_copy, so the guard covers every interactive evaluation path (including .load and editor-mode Ctrl+D); eval_script (-e/-p) never runs setup_terminal so it needs no guard.
  • waitFor now matches on ANSI-stripped text and fails fast when the child exits — checked existing regex waits (/\u276f|> /, /\n\s*N\b/) still hold under stripping; the initial-prompt regex dropping the trailing space can't false-match the welcome banner.
Extended reasoning...

Overview

Two files: src/runtime/cli/repl.rs moves ENABLE_PROCESSED_INPUT from the set word to the unset word in setup_terminal()'s Windows branch, adds a #[cfg(windows)] processed_input_while_evaluating() helper that returns an Option<StdinModeGuard> re-enabling the flag, and binds that guard at the top of evaluate_and_print / evaluate_and_copy. A stale comment in enable_signals_during_wait is updated. test/js/bun/repl/repl.test.ts reworks waitFor to match on stripAnsi output, fail immediately when the child exits, and race the data-waiter against proc.exited; the old POSIX-only Ctrl+C test is replaced by a new cross-platform Bun REPL (Terminal) Ctrl+C describe block with four cases, one Windows-only. POSIX code paths are untouched.

Security risks

None. This is developer-tool terminal-mode plumbing. No parsing of untrusted input, no auth/crypto/permissions, no network. Clearing ENABLE_PROCESSED_INPUT while line-editing is exactly what libuv's raw mode does; Ctrl+Break and GenerateConsoleCtrlEvent still terminate the process.

Level of scrutiny

Low-to-moderate. The Rust change is a one-flag move plus an RAII guard using an existing in-tree helper (bun_sys::windows::StdinModeGuard, already used by prompt() / bun init). I confirmed UpdateStdioModeFlagsOpts derives Default (so unset is 0 in the guard), the guard's Drop restores the captured mode, and the original_windows_mode.is_some() gate makes it a no-op when setup_terminal() didn't touch the console. The test-helper change is the larger surface: I checked that switching waitFor to ANSI-stripped matching can only make existing text/regex patterns easier to satisfy (escape bytes between \n and a digit are removed) and can't false-match on the welcome banner.

Other factors

Callers audited: evaluate_raw is only reached from inside the two guarded functions, so every interactive evaluation (Enter, .load, .copy <expr>, editor Ctrl+D) is covered; eval_script runs in the non-interactive branch that never calls setup_terminal, so no guard needed there. The PR description documents fail-before / pass-after on both Windows CI configurations with repeated runs, and full-file passes on Windows x64 and Linux. The removed Ctrl+C cancels current input test is replaced by a stronger version in the new block. No prior human reviews or outstanding comments.

…ing the REPL

A process whose ancestors ignore Ctrl+C (CREATE_NEW_PROCESS_GROUP, as in a
CI job tree) never receives CTRL_C_EVENT, so a test expecting the REPL to
die during an evaluation hangs there. Read ENABLE_PROCESSED_INPUT from
inside a .load'ed evaluation instead, then check Ctrl+C at the following
prompt is handled again.
Comment thread src/runtime/cli/repl.rs Outdated
Comment thread src/runtime/cli/repl.rs Outdated
Comment thread src/runtime/cli/repl.rs Outdated
Comment thread src/runtime/cli/repl.rs
Comment thread src/runtime/cli/repl.rs
@robobun

robobun commented Aug 13, 2026

Copy link
Copy Markdown
Collaborator Author

Pushed 70a7464, which only shortens the three comments the comment linter flagged (replied on each thread; the flag choice itself is the standard raw console mode, not a workaround, so the two remaining short comments stay). 497ff09 before it replaced the Windows-only evaluation test, see the description. CodeRabbit has been rate limited on every push so far, so there is no review from it to address.

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM — targeted Windows console-mode fix with RAII guard and thorough tests.

What was reviewed:

  • setup_terminal now clears ENABLE_PROCESSED_INPUT so Ctrl+C reaches Key::CtrlC; verified StdinModeGuard::set/Drop semantics restore the line-editor mode, and the original_windows_mode.is_some() gate keeps piped stdin untouched.
  • Guard placement covers both interactive evaluation entry points (evaluate_and_print, evaluate_and_copy); .load/.editor route through the former, evaluate_raw is only reached under a live guard, and eval_script (non-tty -e/-p) is correctly untouched.
  • waitFor refactor: ANSI-stripped matching and proc.exited race are strict improvements; checked existing terminal tests' patterns still match post-strip and that the loosened prompt regex has no false positive in the welcome banner.
Extended reasoning...

Overview

Two files: src/runtime/cli/repl.rs moves ENABLE_PROCESSED_INPUT from the set to the unset word in the Windows setup_terminal path, adds processed_input_while_evaluating() returning an Option<StdinModeGuard> that re-enables the flag while JS runs, and holds that guard in evaluate_and_print / evaluate_and_copy. test/js/bun/repl/repl.test.ts hardens the withTerminalRepl helper (ANSI-strip before match, fail immediately if the child exits mid-wait, race the data promise against proc.exited), moves the old Ctrl+C test out of the todoIf(isWindows) block into a new cross-platform Ctrl+C describe with four cases, and adds a Windows-only FFI test that reads stdin's console mode during .load evaluation to pin the guard.

Security risks

None. No untrusted input parsing, no auth/crypto/permissions. The console-mode calls go through the existing bun_sys::windows helpers already used by bun init / prompt(). The FFI in the test is read-only (GetConsoleMode).

Level of scrutiny

Medium. This is an interactive-CLI UX fix, Windows-only for the native change and POSIX code is byte-identical. It changes process-global console state, which is the sort of thing that can leak — I checked that the StdinModeGuard restores the pre-guard mode on drop (so the line editor's mode word comes back on every early return), and that the guard is skipped when setup_terminal never ran (original_windows_mode.is_none()), so piped bun repl and bun repl -e/-p are unaffected. UpdateStdioModeFlagsOpts derives Default, so unset is 0 in the guard call.

Other factors

  • The PR description documents USE_SYSTEM_BUN=1 failures and debug-build passes on both Windows CI targets, with repeated runs, and explains why the evaluation test observes the mode flag instead of sending Ctrl+C (CI inherits Ctrl+C-ignored). Full file passes on Linux and Windows.
  • The comment-cop threads were addressed in 70a7464 (comments trimmed) and resolved; no outstanding human review.
  • The test-helper change to strip ANSI before matching is applied to all callers, but every existing waitFor pattern in the file matches on plain text or \u276f, which survive stripping; the loosened initial-prompt regex /\u276f|>/ cannot false-match the welcome banner.
  • I confirmed evaluate_raw has no other callers, .load goes through evaluate_and_print, and eval_script (the non-interactive path) does not need the guard because setup_terminal never runs for it.

The mode test waited for the evaluation's result, which is printed while
the guard is still alive, and then sent Ctrl+C; wait for the prompt that
follows instead, since it is printed after the guard has been dropped.
Read the mode through .copy as well, so both guard sites are covered, and
check that a line typed while an evaluation is running is evaluated
afterwards.
@robobun

robobun commented Aug 13, 2026

Copy link
Copy Markdown
Collaborator Author

Pushed 5d48e86 (tests only, after a self-review pass on the previous revision):

  • The Windows mode test waited for the evaluation's result and then sent Ctrl+C, but the result is printed while the evaluation-time console mode is still in effect, so the keystroke raced the restore. It now waits for the prompt that follows each evaluation, which the REPL prints only after the mode has been restored.
  • The same test also reads the mode through .copy <code>, so the guard in evaluate_and_copy is covered as well as the one in evaluate_and_print (deleting only the .copy guard makes that assertion fail).
  • New cross-platform test: a line typed while an evaluation is still running is evaluated afterwards, which is the property the per-evaluation mode switch relies on.

Verified on both Windows configurations with and without Ctrl+C ignored in the parent process; the description's test section and runs are updated. The CodeRabbit comments on this PR are rate-limit notices, there is no review from it to address.

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I reviewed this PR and didn't find any bugs. The console-mode flip and the RAII guard around evaluation look correct and match libuv's raw-mode word, and the new tests are designed to fail on the unfixed build. Because it changes Windows console-mode handling and reworks the shared waitFor helper that every terminal REPL test goes through, a human look would still be worthwhile.

Checked: StdinModeGuard captures and restores the pre-evaluation mode on every return path of evaluate_and_print/evaluate_and_copy (including the nested evaluate_raw fallback), and is skipped when setup_terminal didn't touch the console (piped stdin). The waitFor change to match on ANSI-stripped output is safe for the existing POSIX-only terminal tests — none of their patterns depend on escape sequences, and the cursor now consistently tracks the stripped length.

Extended reasoning...

Overview

Two files. In src/runtime/cli/repl.rs (Windows-only paths): setup_terminal() moves ENABLE_PROCESSED_INPUT from the set word to unset, so Ctrl+C at the prompt arrives as 0x03 and reaches the existing Key::CtrlC handler instead of triggering the default CTRL_C_EVENT exit; a new processed_input_while_evaluating() returns a StdinModeGuard (existing in-tree RAII helper) that re-enables the flag for the duration of evaluate_and_print() / evaluate_and_copy(), preserving today's exit-on-Ctrl+C during a hung evaluation. POSIX code is untouched. In test/js/bun/repl/repl.test.ts: the withTerminalRepl helper now strips ANSI before matching (ConPTY re-renders output), fails fast when the child exits mid-wait, and races the data-waiter against proc.exited; a new Bun REPL (Terminal) Ctrl+C block runs on Windows too and covers pending-input, empty-line double-press, multiline, type-ahead during eval, and a Windows-only test that reads the console mode via bun:ffi from inside .load and .copy to pin both guard sites.

Security risks

None. This is interactive REPL terminal-mode handling; no untrusted input parsing, auth, or network surface is touched.

Level of scrutiny

Medium. The Rust diff is small (~20 lines) and uses an existing helper, but Windows console-mode semantics and ConPTY behavior are subtle and easy to regress. I verified StdinModeGuard::set reads the current mode, applies the delta, and restores the captured mode on drop, so every return path from the two evaluate functions (early transform failure, exception, promise reject/pending, success) puts the line editor's mode back. The guard is gated on original_windows_mode.is_some(), which is only set when setup_terminal ran to completion (both stdin and stdout are TTYs), so piped bun repl and repl -e/-p are unaffected. evaluate_raw is only reached from inside the two guarded functions, so it inherits the guard. The one behavior nuance — user JS that changes the console mode during an evaluation is now overwritten by the guard's Drop — is the same trade-off the POSIX side already makes around promise waits, and the REPL needs its raw mode back to read the next keystroke.

Other factors

The test-helper change affects every withTerminalRepl caller (including the ~25 existing POSIX-only tests in the todoIf(isWindows) block): matching now happens on ANSI-stripped text and cursor tracks the stripped length. I checked the existing patterns — none rely on escape sequences, and the ones that call stripAnsi on the returned slice are idempotent. The initial-prompt regex was loosened from /❯|> / to /❯|>/ to accommodate ConPTY rendering the trailing space as an erase; the welcome banner contains neither character so it can't false-match. The new type-ahead test's 200 ms busy loop is wall-clock in the child and the test synchronizes on printed output, so if the send lands late the test still passes (weaker, not flaky). The Windows-only mode test observes the flag rather than sending Ctrl+C during eval, which the description explains is required because CI job trees inherit an ignored-Ctrl+C flag; that reasoning checks out. All comment-cop threads are resolved. CI on the head commit was still building when I reviewed, and I can't run the Windows lane myself — that plus the shared-helper edit is why I'm leaving this for a human to confirm rather than approving.

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant