1use anyhow::Result;
2use colored::Colorize;
3use dialoguer::{Input, Select};
4use std::fs;
5use std::path::PathBuf;
6
7use crate::commands::config as config_cmd;
8use crate::commands::helpers::is_interactive;
9use crate::config::{Config, LLMConfig};
10use crate::storage::Storage;
11
12pub fn run(project_root: Option<PathBuf>, provider_arg: Option<String>) -> Result<()> {
13 let storage = Storage::new(project_root);
14
15 if storage.is_initialized() {
16 println!("{}", "✓ SCUD is already initialized".green());
17 return Ok(());
18 }
19
20 println!("{}", "Initializing SCUD...".blue());
21 println!();
22
23 let (provider, model) = if let Some(provider_name) = provider_arg {
24 let provider = provider_name.to_lowercase();
26 if !matches!(
27 provider.as_str(),
28 "xai" | "anthropic" | "openai" | "openrouter"
29 ) {
30 anyhow::bail!(
31 "Invalid provider: {}. Valid options: xai, anthropic, openai, openrouter",
32 provider
33 );
34 }
35 let model = Config::default_model_for_provider(&provider).to_string();
36 (provider, model)
37 } else if is_interactive() {
38 let providers = vec![
40 "xAI (Grok)",
41 "Anthropic (Claude)",
42 "OpenAI (GPT)",
43 "OpenRouter",
44 ];
45 let provider_selection = Select::new()
46 .with_prompt("Select your LLM provider")
47 .items(&providers)
48 .default(0)
49 .interact()?;
50
51 let provider = match provider_selection {
52 0 => "xai",
53 1 => "anthropic",
54 2 => "openai",
55 3 => "openrouter",
56 _ => "anthropic",
57 };
58
59 let suggested = Config::suggested_models_for_provider(provider);
61 let mut model_options: Vec<String> = suggested.iter().map(|s| s.to_string()).collect();
62 model_options.push("Custom (enter model name)".to_string());
63
64 let model_selection = Select::new()
65 .with_prompt("Select model (or choose Custom to enter any model)")
66 .items(&model_options)
67 .default(0)
68 .interact()?;
69
70 let model = if model_selection == model_options.len() - 1 {
71 Input::<String>::new()
73 .with_prompt("Enter model name")
74 .interact_text()?
75 } else {
76 suggested[model_selection].to_string()
77 };
78
79 (provider.to_string(), model)
80 } else {
81 let provider = "anthropic";
83 let model = Config::default_model_for_provider(provider);
84 (provider.to_string(), model.to_string())
85 };
86
87 let config = Config {
88 llm: LLMConfig {
89 provider,
90 model,
91 max_tokens: 4096,
92 },
93 };
94
95 storage.initialize_with_config(&config)?;
96
97 println!("\n{}", "SCUD initialized successfully!".green().bold());
98
99 println!("\n{}", "Installing SCUD agents and commands...".blue());
101 if let Err(e) = config_cmd::agents_add(Some(storage.project_root().to_path_buf()), None, true) {
102 println!(
103 "{}",
104 format!(" Could not install agents: {}", e).yellow()
105 );
106 println!(" You can install them later with: scud config agents add --all");
107 }
108
109 if let Err(e) = update_claude_md(&storage) {
111 println!(
112 "{}",
113 format!(" Could not update CLAUDE.md: {}", e).yellow()
114 );
115 }
116
117 println!("\n{}", "Configuration:".blue());
118 println!(" Provider: {}", config.llm.provider.yellow());
119 println!(" Model: {}", config.llm.model.yellow());
120 println!("\n{}", "Environment variable required:".blue());
121 println!(
122 " export {}=your-api-key",
123 config.api_key_env_var().yellow()
124 );
125 println!("\n{}", "Next steps:".blue());
126 println!(" 1. Set your API key environment variable");
127 println!(" 2. Run: scud tags");
128 println!(" 3. Create or import tasks, then use: /scud:next\n");
129
130 Ok(())
131}
132
133fn update_claude_md(storage: &Storage) -> Result<()> {
135 let claude_md_path = storage.project_root().join("CLAUDE.md");
136
137 let scud_section = r#"
138## SCUD Task Management
139
140This project uses SCUD for AI-driven task management.
141
142### Quick Start
143- `scud tags` - List available phases
144- `scud next` - Find next available task
145- `scud set-status <id> in-progress` - Claim a task
146- `scud view` - Open interactive task viewer
147
148### Slash Commands
149Use `/scud:` commands in Claude Code for task operations.
150"#;
151
152 let marker = "## SCUD Task Management";
153
154 if claude_md_path.exists() {
155 let content = fs::read_to_string(&claude_md_path)?;
156 if content.contains(marker) {
157 return Ok(()); }
159 let new_content = format!("{}\n{}", content.trim_end(), scud_section);
161 fs::write(&claude_md_path, new_content)?;
162 } else {
163 fs::write(&claude_md_path, scud_section.trim_start())?;
165 }
166
167 println!(" {} Updated CLAUDE.md with SCUD instructions", "✓".green());
168 Ok(())
169}