ricecoder_cli/commands/
init.rs

1// Initialize a new ricecoder project with interactive setup wizard
2
3use super::Command;
4use crate::error::{CliError, CliResult};
5use crate::output::OutputStyle;
6use std::io::{self, Write};
7
8/// Initialize a new ricecoder project
9pub struct InitCommand {
10    pub project_path: Option<String>,
11    pub interactive: bool,
12}
13
14impl InitCommand {
15    pub fn new(project_path: Option<String>) -> Self {
16        Self {
17            project_path,
18            interactive: true,
19        }
20    }
21
22    pub fn with_interactive(mut self, interactive: bool) -> Self {
23        self.interactive = interactive;
24        self
25    }
26
27    /// Prompt user for input
28    fn prompt(&self, question: &str) -> CliResult<String> {
29        let style = OutputStyle::default();
30        print!("{}", style.prompt(question));
31        io::stdout().flush().map_err(CliError::Io)?;
32
33        let mut input = String::new();
34        io::stdin()
35            .read_line(&mut input)
36            .map_err(CliError::Io)?;
37
38        Ok(input.trim().to_string())
39    }
40
41    /// Prompt user for yes/no
42    #[allow(dead_code)]
43    fn prompt_yes_no(&self, question: &str) -> CliResult<bool> {
44        loop {
45            let response = self.prompt(&format!("{} (y/n): ", question))?;
46            match response.to_lowercase().as_str() {
47                "y" | "yes" => return Ok(true),
48                "n" | "no" => return Ok(false),
49                _ => println!("Please enter 'y' or 'n'"),
50            }
51        }
52    }
53
54    /// Show welcome message
55    fn show_welcome(&self) {
56        let style = OutputStyle::default();
57        println!("{}", style.section("Welcome to RiceCoder"));
58        println!();
59        println!("This wizard will help you set up a new RiceCoder project.");
60        println!();
61        println!("{}",
62            style.list_item("Create project configuration")
63        );
64        println!("{}", style.list_item("Set up AI provider"));
65        println!("{}", style.list_item("Configure storage"));
66        println!();
67    }
68
69    /// Show getting started guide
70    fn show_getting_started(&self, project_path: &str) {
71        let style = OutputStyle::default();
72        println!();
73        println!("{}", style.section("Getting Started"));
74        println!();
75        println!("Your project is ready! Here are the next steps:");
76        println!();
77        println!("{}", style.numbered_item(1, "Navigate to your project"));
78        println!("   cd {}", project_path);
79        println!();
80        println!("{}", style.numbered_item(2, "Start the interactive chat"));
81        println!("   rice chat");
82        println!();
83        println!("{}", style.numbered_item(3, "Generate code from specifications"));
84        println!("   rice gen --spec my-spec.md");
85        println!();
86        println!("{}", style.section("Learn More"));
87        println!();
88        println!("{}", style.link("Documentation", "https://ricecoder.dev/docs"));
89        println!("{}", style.link("Examples", "https://ricecoder.dev/examples"));
90        println!("{}", style.link("Troubleshooting", "https://ricecoder.dev/docs/troubleshooting"));
91        println!();
92    }
93
94    /// Interactive setup wizard
95    fn run_wizard(&self, _path: &str) -> CliResult<(String, String, String)> {
96        self.show_welcome();
97
98        // Project name
99        let project_name = self.prompt("Project name")?;
100        let project_name = if project_name.is_empty() {
101            "My Project".to_string()
102        } else {
103            project_name
104        };
105
106        // Project description
107        let project_description = self.prompt("Project description (optional)")?;
108
109        // Provider selection
110        println!();
111        println!("Available AI providers:");
112        println!("  1. OpenAI (GPT-4, GPT-3.5)");
113        println!("  2. Anthropic (Claude)");
114        println!("  3. Local (Ollama)");
115        println!("  4. Other");
116        println!();
117
118        let provider = loop {
119            let choice = self.prompt("Select provider (1-4)")?;
120            match choice.as_str() {
121                "1" => break "openai".to_string(),
122                "2" => break "anthropic".to_string(),
123                "3" => break "ollama".to_string(),
124                "4" => break "other".to_string(),
125                _ => println!("Please enter 1, 2, 3, or 4"),
126            }
127        };
128
129        Ok((project_name, project_description, provider))
130    }
131}
132
133impl Command for InitCommand {
134    fn execute(&self) -> CliResult<()> {
135        let path = self.project_path.as_deref().unwrap_or(".");
136        let style = OutputStyle::default();
137
138        // Run interactive wizard if enabled
139        let (project_name, project_description, provider) = if self.interactive {
140            self.run_wizard(path)?
141        } else {
142            ("My Project".to_string(), String::new(), "openai".to_string())
143        };
144
145        // Create .agent/ directory structure
146        std::fs::create_dir_all(format!("{}/.agent", path)).map_err(CliError::Io)?;
147
148        // Create default configuration
149        let config_content = format!(
150            r#"# RiceCoder Project Configuration
151# This file configures ricecoder for your project
152
153[project]
154name = "{}"
155description = "{}"
156
157[providers]
158default = "{}"
159
160[storage]
161mode = "merged"
162
163# For more configuration options, see:
164# https://ricecoder.dev/docs/configuration
165"#,
166            project_name, project_description, provider
167        );
168
169        std::fs::write(format!("{}/.agent/ricecoder.toml", path), config_content)
170            .map_err(CliError::Io)?;
171
172        // Create example spec file
173        let example_spec = r#"# Example Specification
174
175## Overview
176
177This is an example specification for RiceCoder. You can use this as a template
178for your own specifications.
179
180## Requirements
181
182### Requirement 1
183
184**User Story:** As a user, I want to do something, so that I can achieve a goal.
185
186#### Acceptance Criteria
187
1881. WHEN I do something THEN the system SHALL do something else
1892. WHEN I do another thing THEN the system SHALL respond appropriately
190
191## Design
192
193### Architecture
194
195Describe your architecture here.
196
197### Data Models
198
199Describe your data models here.
200
201## Tasks
202
203- [ ] Task 1: Implement feature
204- [ ] Task 2: Write tests
205- [ ] Task 3: Document
206
207For more information, see: https://ricecoder.dev/docs/specifications
208"#;
209
210        std::fs::write(format!("{}/.agent/example-spec.md", path), example_spec)
211            .map_err(CliError::Io)?;
212
213        // Create README
214        let readme = format!(
215            r#"# {}
216
217{}
218
219## Getting Started
220
2211. Configure your AI provider in `.agent/ricecoder.toml`
2222. Create a specification file (see `example-spec.md`)
2233. Run `rice gen --spec your-spec.md` to generate code
224
225## Documentation
226
227- [RiceCoder Documentation](https://ricecoder.dev/docs)
228- [Configuration Guide](https://ricecoder.dev/docs/configuration)
229- [Specification Guide](https://ricecoder.dev/docs/specifications)
230
231## Support
232
233- [GitHub Issues](https://github.com/ricecoder/ricecoder/issues)
234- [Discussions](https://github.com/ricecoder/ricecoder/discussions)
235- [Troubleshooting](https://ricecoder.dev/docs/troubleshooting)
236"#,
237            project_name, project_description
238        );
239
240        std::fs::write(format!("{}/README.md", path), readme)
241            .map_err(CliError::Io)?;
242
243        // Print success message
244        println!();
245        println!("{}", style.success(&format!("Initialized ricecoder project at {}", path)));
246        println!();
247        println!("Created files:");
248        println!("{}", style.list_item(".agent/ricecoder.toml - Project configuration"));
249        println!("{}", style.list_item(".agent/example-spec.md - Example specification"));
250        println!("{}", style.list_item("README.md - Project documentation"));
251        println!();
252
253        // Show getting started guide
254        self.show_getting_started(path);
255
256        Ok(())
257    }
258}