Skip to main content

sparrow/onboarding/
mod.rs

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