Skip to main content

ai_agent/utils/
render_options.rs

1// Source: ~/claudecode/openclaudecode/src/utils/renderOptions.ts
2
3use std::sync::OnceLock;
4
5/// Cached stdin override - computed once per process.
6static STDIN_OVERRIDE: OnceLock<Option<std::fs::File>> = OnceLock::new();
7
8/// Gets a File for /dev/tty when stdin is piped.
9/// This allows interactive rendering even when stdin is a pipe.
10/// Result is cached for the lifetime of the process.
11fn get_stdin_override() -> Option<&'static std::fs::File> {
12    STDIN_OVERRIDE
13        .get_or_init(|| {
14            // No override needed if stdin is already a TTY
15            if atty::is(atty::Stream::Stdin) {
16                return None;
17            }
18
19            // Skip in CI environments (localized: CI)
20            if is_env_truthy(std::env::var("CI").ok()) {
21                return None;
22            }
23
24            // Skip if running MCP (input hijacking breaks MCP)
25            let args: Vec<String> = std::env::args().collect();
26            if args.iter().any(|a| a == "mcp") {
27                return None;
28            }
29
30            // No /dev/tty on Windows
31            if cfg!(target_os = "windows") {
32                return None;
33            }
34
35            // Try to open /dev/tty as an alternative input source
36            std::fs::File::open("/dev/tty").ok()
37        })
38        .as_ref()
39}
40
41/// Check if an environment variable is truthy.
42fn is_env_truthy(value: Option<String>) -> bool {
43    match value {
44        Some(v) => {
45            let v = v.to_lowercase();
46            v == "1" || v == "true" || v == "yes" || v == "on"
47        }
48        None => false,
49    }
50}
51
52/// Render options for the TUI.
53#[derive(Debug, Clone, Default)]
54pub struct RenderOptions {
55    /// Whether to exit on Ctrl+C.
56    pub exit_on_ctrl_c: bool,
57}
58
59/// Returns base render options, including stdin override when needed.
60/// Use this for all render calls to ensure piped input works correctly.
61pub fn get_base_render_options(exit_on_ctrl_c: bool) -> RenderOptions {
62    let _stdin = get_stdin_override();
63    RenderOptions { exit_on_ctrl_c }
64}
65
66#[cfg(test)]
67mod tests {
68    use super::*;
69
70    #[test]
71    fn test_is_env_truthy() {
72        assert!(is_env_truthy(Some("1".to_string())));
73        assert!(is_env_truthy(Some("true".to_string())));
74        assert!(!is_env_truthy(None));
75        assert!(!is_env_truthy(Some("0".to_string())));
76    }
77
78    #[test]
79    fn test_get_base_render_options() {
80        let opts = get_base_render_options(false);
81        assert!(!opts.exit_on_ctrl_c);
82
83        let opts = get_base_render_options(true);
84        assert!(opts.exit_on_ctrl_c);
85    }
86}