From b83a0480f67c7c0e1d2d2ed93ebc7665dc384cbb Mon Sep 17 00:00:00 2001 From: Abdul Wahab Date: Tue, 8 Sep 2026 02:10:51 +0500 Subject: [PATCH] Fix: Windows update line prompt (#760) Raw-mode readline is Unix-only; Windows TTY sessions now use the existing line-based prompt instead of aborting. AI assistance: Cursor Grok 4.6, under maintainer direction. Co-authored-by: Cursor --- crates/skills/src/prompt.rs | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/crates/skills/src/prompt.rs b/crates/skills/src/prompt.rs index 1bfa3d3d0..e841753d4 100644 --- a/crates/skills/src/prompt.rs +++ b/crates/skills/src/prompt.rs @@ -35,6 +35,10 @@ impl Prompt { self.stdin_tty && self.stdout_tty && cfg!(unix) } + fn uses_tty_readline(&self, io: &Io) -> bool { + cfg!(unix) && self.stdout_tty && io.env("TERM") != Some("dumb") + } + fn ansi(&self, open: &str, close: &str, value: &str) -> String { if self.style { format!("{open}{value}{close}") @@ -70,7 +74,7 @@ impl Prompt { let next = self.piped.as_mut().and_then(|v| v.pop()).unwrap_or_default(); return Ok(next.trim().to_lowercase()); } - if self.stdout_tty && io.env("TERM") != Some("dumb") { + if self.uses_tty_readline(io) { return self.tty_readline(io, question); } io.out(question); @@ -624,6 +628,9 @@ fn terminal_rows() -> Option { #[cfg(test)] mod tests { + use std::collections::HashMap; + use std::path::PathBuf; + use super::*; #[test] @@ -637,4 +644,29 @@ mod tests { assert_eq!(visible_window(15, 16, 10), (6, 16)); assert_eq!(visible_window(2, 3, 10), (0, 3)); } + + fn tty_prompt() -> Prompt { + Prompt { stdin_tty: true, stdout_tty: true, style: false, piped: None } + } + + #[test] + fn ask_uses_raw_readline_only_on_unix() { + let prompt = tty_prompt(); + let (io, _) = Io::captured("", PathBuf::from("."), HashMap::new()); + assert_eq!(prompt.uses_tty_readline(&io), cfg!(unix)); + } + + #[cfg(not(unix))] + #[test] + fn ask_on_windows_tty_does_not_throw_unsupported() { + // Line fallback reads process stdin. Skip on a live console so the + // test cannot hang; CI pipes EOF and gets Ok(""). + if std::io::IsTerminal::is_terminal(&std::io::stdin()) { + return; + } + let mut prompt = tty_prompt(); + let (mut io, _) = Io::captured("", PathBuf::from("."), HashMap::new()); + let result = prompt.ask(&mut io, "Update skills in 1 provider folder(s)? (Y/n) "); + assert_eq!(result, Ok(String::new())); + } }