fix: exit 0 when stdout is not a TTY (winget headless validator)
The winget submission validator runs the binary with no arguments and no attached terminal. The bare `onde` code path immediately calls `enable_raw_mode()` / `EnterAlternateScreen`, which fail without a TTY and exit non-zero — triggering Validation-Executable-Error. Add a no-TTY guard (via `IsTerminal`) before `redirect_output()` so onde prints a usage summary and exits 0 when stdout is not a terminal. Also add explicit `--version`/`-V` and `--help`/`-h` handlers that work the same way, since the validator also exercises those flags. Closes #11 Co-Authored-By: siGit Code v1.6.7-acp <noreply@sigit.si>
Seto Elkahfi committed
Oct 11, 2026 at 22:26 UTC
42ebbe76a8c3c3a3ea7eaa125020df1fd34b5b1c
1 file changed
+37
-1
src/main.rs
+37
-1
@@ -13,7 +13,22 @@ mod project;
13
mod token;
14
mod ui;
15
16
-use std::io::Write;
16
+use std::io::{IsTerminal, Write};
17
+
18
+/// One-line usage, printed for `--help` and whenever `onde` is launched
19
+/// without an attached terminal.
20
+fn print_usage() {
21
+ println!(
22
+ "onde {}\n{}\n\n\
23
+ Usage:\n \
24
+ onde Launch the interactive terminal UI (requires a TTY)\n \
25
+ onde --mcp Run the MCP JSON-RPC server over stdio\n \
26
+ onde --version Print the version and exit\n \
27
+ onde --help Print this help and exit",
28
+ env!("CARGO_PKG_VERSION"),
29
+ env!("CARGO_PKG_DESCRIPTION"),
30
+ );
31
+}
32
33
/// Redirect **both** stdout and stderr to `~/.cache/onde/debug.log` before
34
/// ratatui takes over the alternate screen.
@@ -81,6 +96,27 @@ async fn main() -> anyhow::Result<()> {
96
return mcp::serve().await;
97
}
98
99
+ // Bare `--version`/`--help` must work without a terminal.
100
+ let args: Vec<String> = std::env::args().skip(1).collect();
101
+ if args.iter().any(|arg| arg == "--version" || arg == "-V") {
102
+ println!("onde {}", env!("CARGO_PKG_VERSION"));
103
+ return Ok(());
104
+ }
105
+ if args.iter().any(|arg| arg == "--help" || arg == "-h") {
106
+ print_usage();
107
+ return Ok(());
108
+ }
109
+
110
+ // The TUI needs a real terminal. When stdout isn't a TTY — e.g. the winget
111
+ // submission validator runs the executable headlessly with no arguments —
112
+ // enabling raw mode / the alternate screen below fails and the process
113
+ // exits non-zero (Validation-Executable-Error). Print usage and exit 0
114
+ // instead of launching ratatui into a pipe.
115
+ if !std::io::stdout().is_terminal() {
116
+ print_usage();
117
+ return Ok(());
118
+ }
119
+
120
let tty = redirect_output();
121
122
// Build a writer that targets the real terminal. When