ricecoder_cli/commands/
init.rs1use super::Command;
4use crate::error::{CliError, CliResult};
5use crate::output::OutputStyle;
6use std::io::{self, Write};
7
8pub 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 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 #[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 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 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 fn run_wizard(&self, _path: &str) -> CliResult<(String, String, String)> {
96 self.show_welcome();
97
98 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 let project_description = self.prompt("Project description (optional)")?;
108
109 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 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 std::fs::create_dir_all(format!("{}/.agent", path)).map_err(CliError::Io)?;
147
148 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 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 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 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 self.show_getting_started(path);
255
256 Ok(())
257 }
258}