1use crate::{DrivenConfig, Result};
4use dialoguer::{Confirm, MultiSelect, Select, theme::ColorfulTheme};
5use std::path::Path;
6
7#[derive(Debug)]
9pub struct InitCommand;
10
11impl InitCommand {
12 pub fn run(project_root: &Path, interactive: bool) -> Result<()> {
14 if interactive {
15 Self::run_interactive(project_root)
16 } else {
17 Self::run_default(project_root)
18 }
19 }
20
21 fn run_interactive(project_root: &Path) -> Result<()> {
22 let theme = ColorfulTheme::default();
23
24 println!();
25 println!("🚀 Welcome to Driven - AI-Assisted Development Orchestrator");
26 println!();
27
28 let editor_items = &[
30 "Cursor (.cursor/rules/)",
31 "GitHub Copilot (.github/copilot-instructions.md)",
32 "Windsurf (.windsurf/rules/)",
33 "Claude (CLAUDE.md)",
34 "Aider (.aider.conf.yml)",
35 "Cline (.clinerules)",
36 ];
37
38 let selected = MultiSelect::with_theme(&theme)
39 .with_prompt("Which AI editors do you use?")
40 .items(editor_items)
41 .defaults(&[true, true, false, false, false, false])
42 .interact()
43 .map_err(|e| crate::DrivenError::Cli(e.to_string()))?;
44
45 let mut config = DrivenConfig::default();
46 config.editors.cursor = selected.contains(&0);
47 config.editors.copilot = selected.contains(&1);
48 config.editors.windsurf = selected.contains(&2);
49 config.editors.claude = selected.contains(&3);
50 config.editors.aider = selected.contains(&4);
51 config.editors.cline = selected.contains(&5);
52
53 let template_items = &[
55 "Rust Workspace",
56 "TypeScript/JavaScript",
57 "Full-Stack (Rust + TypeScript)",
58 "CLI Tool",
59 "Library",
60 "Custom (start empty)",
61 ];
62
63 let template_idx = Select::with_theme(&theme)
64 .with_prompt("What type of project is this?")
65 .items(template_items)
66 .default(0)
67 .interact()
68 .map_err(|e| crate::DrivenError::Cli(e.to_string()))?;
69
70 config.templates.project = Some(match template_idx {
71 0 => "rust-workspace".to_string(),
72 1 => "typescript-monorepo".to_string(),
73 2 => "fullstack".to_string(),
74 3 => "cli-tool".to_string(),
75 4 => "library".to_string(),
76 _ => "custom".to_string(),
77 });
78
79 config.sync.watch = Confirm::with_theme(&theme)
81 .with_prompt("Enable auto-sync when rules change?")
82 .default(true)
83 .interact()
84 .map_err(|e| crate::DrivenError::Cli(e.to_string()))?;
85
86 Self::create_config(project_root, &config)?;
88
89 Self::create_initial_rules(project_root, &config)?;
91
92 super::print_success("Driven initialized successfully!");
93 println!();
94
95 println!("📋 Next steps:");
97 println!(" 1. Edit .driven/rules.drv to customize your AI rules");
98 println!(" 2. Run 'driven sync' to propagate rules to all editors");
99 println!(" 3. Run 'driven analyze' to generate context from your codebase");
100 println!();
101
102 Ok(())
103 }
104
105 fn run_default(project_root: &Path) -> Result<()> {
106 let config = DrivenConfig::default();
107 Self::create_config(project_root, &config)?;
108 Self::create_initial_rules(project_root, &config)?;
109
110 super::print_success("Driven initialized with default settings");
111 super::print_info("Run 'driven init -i' for interactive setup");
112
113 Ok(())
114 }
115
116 fn create_config(project_root: &Path, config: &DrivenConfig) -> Result<()> {
117 let config_dir = project_root.join(".driven");
118 std::fs::create_dir_all(&config_dir).map_err(|e| {
119 crate::DrivenError::Config(format!("Failed to create .driven directory: {}", e))
120 })?;
121
122 let config_path = config_dir.join("config.toml");
123 let toml = toml::to_string_pretty(config).map_err(|e| {
124 crate::DrivenError::Config(format!("Failed to serialize config: {}", e))
125 })?;
126
127 std::fs::write(&config_path, toml)
128 .map_err(|e| crate::DrivenError::Config(format!("Failed to write config: {}", e)))?;
129
130 Ok(())
131 }
132
133 fn create_initial_rules(project_root: &Path, config: &DrivenConfig) -> Result<()> {
134 let rules_path = project_root.join(&config.sync.source_of_truth);
135
136 if let Some(parent) = rules_path.parent() {
138 std::fs::create_dir_all(parent).map_err(|e| {
139 crate::DrivenError::Config(format!("Failed to create rules directory: {}", e))
140 })?;
141 }
142
143 let initial_rules = r#"# AI Development Rules
145
146## Persona
147
148You are an expert software engineer with deep knowledge of the project's tech stack.
149
150### Traits
151- Precise and detail-oriented
152- Proactive about edge cases
153- Explains reasoning clearly
154
155### Principles
156- Write idiomatic code
157- Prefer explicit over implicit
158- Test thoroughly
159
160## Standards
161
162### Style
163- Follow project's existing code style
164- Use consistent formatting
165
166### Naming
167- Use descriptive, meaningful names
168- Follow language conventions
169
170### Error Handling
171- Handle all error cases explicitly
172- Provide helpful error messages
173
174### Testing
175- Write tests for all new features
176- Maintain existing test coverage
177
178## Context
179
180### Focus
181- src/
182- tests/
183
184### Exclude
185- target/
186- node_modules/
187- .git/
188"#;
189
190 let rules_md_path = rules_path.with_extension("md");
191 std::fs::write(&rules_md_path, initial_rules)
192 .map_err(|e| crate::DrivenError::Config(format!("Failed to write rules: {}", e)))?;
193
194 Ok(())
195 }
196}