Skip to main content

ralph/cli/
tutorial.rs

1//! `ralph tutorial` command: Clap types and handler.
2//!
3//! Responsibilities:
4//! - Parse CLI arguments for the tutorial command.
5//! - Determine interactive vs non-interactive mode.
6//! - Delegate to the tutorial command implementation.
7//!
8//! Not handled here:
9//! - Actual tutorial phase logic (see `crate::commands::tutorial`).
10//! - Sandbox creation (see `crate::commands::tutorial::sandbox`).
11
12use anyhow::Result;
13use clap::Args;
14
15use crate::commands::tutorial as tutorial_cmd;
16
17/// Determine if both stdin and stdout are TTYs (interactive terminal).
18fn is_tty() -> bool {
19    atty::is(atty::Stream::Stdin) && atty::is(atty::Stream::Stdout)
20}
21
22/// Resolve interactive mode based on explicit flags and TTY detection.
23fn resolve_interactive_mode(explicit_non_interactive: bool) -> bool {
24    if explicit_non_interactive {
25        false
26    } else {
27        is_tty()
28    }
29}
30
31/// Handle the tutorial command.
32pub fn handle_tutorial(args: TutorialArgs) -> Result<()> {
33    let interactive = resolve_interactive_mode(args.non_interactive);
34
35    tutorial_cmd::run_tutorial(tutorial_cmd::TutorialOptions {
36        interactive,
37        keep_sandbox: args.keep_sandbox,
38    })
39}
40
41#[derive(Args)]
42#[command(
43    about = "Run interactive tutorial for Ralph onboarding",
44    after_long_help = "Examples:\n  ralph tutorial\n  ralph tutorial --keep-sandbox\n  ralph tutorial --non-interactive\n\nThe tutorial creates a temporary sandbox project and walks you through:\n  1. Initializing Ralph in a project\n  2. Creating your first task\n  3. Running a task (dry-run preview)\n  4. Reviewing the results\n\nUse --keep-sandbox to preserve the sandbox directory after the tutorial.\nUse --non-interactive for automated testing or CI environments."
45)]
46pub struct TutorialArgs {
47    /// Keep the sandbox directory after tutorial completion.
48    #[arg(long)]
49    pub keep_sandbox: bool,
50
51    /// Skip interactive prompts (for testing/CI).
52    #[arg(long)]
53    pub non_interactive: bool,
54}
55
56#[cfg(test)]
57mod tests {
58    use super::*;
59
60    #[test]
61    fn resolve_interactive_mode_explicit_non_interactive() {
62        let result = resolve_interactive_mode(true);
63        assert!(!result);
64    }
65
66    #[test]
67    fn resolve_interactive_mode_auto_detect() {
68        let result = resolve_interactive_mode(false);
69        // In test environment (non-TTY), should be false
70        // In TTY environment, would be true
71        assert_eq!(result, is_tty());
72    }
73}