1use anyhow::Result;
13use clap::Args;
14
15use crate::commands::tutorial as tutorial_cmd;
16
17fn is_tty() -> bool {
19 atty::is(atty::Stream::Stdin) && atty::is(atty::Stream::Stdout)
20}
21
22fn resolve_interactive_mode(explicit_non_interactive: bool) -> bool {
24 if explicit_non_interactive {
25 false
26 } else {
27 is_tty()
28 }
29}
30
31pub 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 #[arg(long)]
49 pub keep_sandbox: bool,
50
51 #[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 assert_eq!(result, is_tty());
72 }
73}