Skip to main content

sparrow/onboarding/
mod.rs

1use crate::config::Config;
2
3pub mod enterprise;
4pub mod migration;
5pub mod setup_agent;
6pub mod wizard;
7
8// ─── User mode ─────────────────────────────────────────────────────────────────
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
11pub enum UserMode {
12    Beginner,
13    Expert,
14}
15
16impl Default for UserMode {
17    fn default() -> Self {
18        UserMode::Beginner
19    }
20}
21
22// ─── Onboarding engine ─────────────────────────────────────────────────────────
23
24pub struct Onboarding {
25    pub mode: UserMode,
26    pub lessons: Vec<Lesson>,
27}
28
29#[derive(Debug, Clone)]
30pub struct Lesson {
31    pub id: String,
32    pub title: String,
33    pub description: String,
34    pub command: String,
35    pub expected_output: String,
36}
37
38impl Default for Onboarding {
39    fn default() -> Self {
40        Self {
41            mode: UserMode::Beginner,
42            lessons: default_lessons(),
43        }
44    }
45}
46
47fn default_lessons() -> Vec<Lesson> {
48    vec![
49        Lesson {
50            id: "run".into(),
51            title: "Your First Run".into(),
52            description: "Execute a simple task and see Sparrow in action.".into(),
53            command: r#"run "Create a file called hello.txt with 'Hello Sparrow' inside""#.into(),
54            expected_output: "file created".into(),
55        },
56        Lesson {
57            id: "swarm".into(),
58            title: "The Swarm".into(),
59            description: "See Planner → Coder → Verifier work together.".into(),
60            command: r#"swarm "Add a comment to hello.txt explaining what it contains""#.into(),
61            expected_output: "Planner → Coder → Verifier".into(),
62        },
63        Lesson {
64            id: "autonomy".into(),
65            title: "Autonomy Dial".into(),
66            description: "Understand how Sparrow keeps you safe with graduated trust.".into(),
67            command: r#"run "Read hello.txt" --autonomy supervised"#.into(),
68            expected_output: "autonomy gate".into(),
69        },
70        Lesson {
71            id: "skills".into(),
72            title: "Self-Improving Skills".into(),
73            description: "Sparrow learns from experience and gets better over time.".into(),
74            command: "skills list".into(),
75            expected_output: "skill library".into(),
76        },
77        Lesson {
78            id: "gateway".into(),
79            title: "Everywhere You Are".into(),
80            description: "Continue your session from Telegram, Discord, or Slack.".into(),
81            command: "gateway status".into(),
82            expected_output: "messaging surfaces".into(),
83        },
84    ]
85}
86
87impl Onboarding {
88    /// Run interactive tutorial
89    pub fn run_interactive(&self) -> anyhow::Result<()> {
90        use std::io::{self, BufRead};
91
92        println!("═══ SPARROW LEARN ═══");
93        println!("5 interactive lessons to master Sparrow.\n");
94
95        for (i, lesson) in self.lessons.iter().enumerate() {
96            println!(
97                "── Lesson {}/{} : {} ──",
98                i + 1,
99                self.lessons.len(),
100                lesson.title
101            );
102            println!("{}", lesson.description);
103            println!("\n  sparrow {}", lesson.command);
104            println!("\nPress Enter to continue...");
105            io::stdin().lock().lines().next();
106            println!();
107        }
108
109        println!("═══ All lessons complete! ═══");
110        println!("Try: sparrow run 'your own task'");
111        println!("Help: sparrow --help\n");
112        Ok(())
113    }
114
115    /// Detect user mode from config or interactive choice
116    pub fn detect_mode(config: &Config) -> UserMode {
117        if config.defaults.autonomy == crate::event::AutonomyLevel::Supervised {
118            UserMode::Beginner
119        } else {
120            UserMode::Expert
121        }
122    }
123
124    /// Generate friendly error messages
125    pub fn friendly_error(error_type: &str, context: &str) -> String {
126        match error_type {
127            "no_provider" => format!(
128                "No API key configured.\n→ Run: sparrow auth add <provider>\n→ Or: sparrow setup\n→ Or set env: {}_API_KEY",
129                context.to_uppercase()
130            ),
131            "no_model" => format!(
132                "Model '{}' not available.\n→ List models: sparrow model --list\n→ Add one: edit ~/.config/sparrow/config.toml",
133                context
134            ),
135            "budget" => format!(
136                "Budget limit reached (${}).\n→ Increase: sparrow run --budget <amount>\n→ Or edit: config.toml [budget]",
137                context
138            ),
139            "sandbox" => format!(
140                "Operation blocked by sandbox: {}\n→ Check: sparrow doctor\n→ Change: config.toml [defaults] sandbox",
141                context
142            ),
143            "permission" => format!(
144                "Permission denied: {}\n→ In Supervised mode, mutating actions require approval.\n→ Use: --autonomy trusted\n→ Or: sparrow config --edit",
145                context
146            ),
147            _ => format!(
148                "Error: {}\n→ Run diagnostics: sparrow doctor\n→ Get help: sparrow --help\n→ Docs: https://github.com/ucav/Sparrow",
149                context
150            ),
151        }
152    }
153
154    /// Examples gallery
155    pub fn examples_gallery() -> Vec<(&'static str, &'static str)> {
156        vec![
157            ("run \"fix the failing auth test\"", "Fix a bug"),
158            ("run \"add a /health endpoint to the API\"", "Add a feature"),
159            ("run \"explain this codebase\" --local", "Analyze offline"),
160            ("swarm \"refactor the rate limiter\"", "Swarm review"),
161            (
162                "schedule \"run tests\" --cron \"0 */6 * * *\"",
163                "Schedule checks",
164            ),
165            ("skills list", "See learned skills"),
166            ("checkpoint list", "View safety net"),
167            ("replay <id>", "Replay any run"),
168        ]
169    }
170}