1pub mod agent;
2pub mod analyzer;
3pub mod auth; pub mod bedrock; pub mod cli;
6pub mod common;
7pub mod config;
8pub mod error;
9pub mod generator;
10pub mod handlers;
11pub mod telemetry; pub use analyzer::{ProjectAnalysis, analyze_project};
15use cli::Commands;
16pub use error::{IaCGeneratorError, Result};
17pub use generator::{generate_compose, generate_dockerfile, generate_terraform};
18pub use handlers::*;
19pub use telemetry::{TelemetryClient, TelemetryConfig, UserId}; pub const VERSION: &str = env!("CARGO_PKG_VERSION");
23
24pub async fn run_command(command: Commands) -> Result<()> {
25 match command {
26 Commands::Analyze {
27 path,
28 json,
29 detailed,
30 display,
31 only,
32 color_scheme,
33 } => {
34 match handlers::handle_analyze(path, json, detailed, display, only, color_scheme) {
35 Ok(_output) => Ok(()), Err(e) => Err(e),
37 }
38 }
39 Commands::Generate {
40 path,
41 output,
42 dockerfile,
43 compose,
44 terraform,
45 all,
46 dry_run,
47 force,
48 } => handlers::handle_generate(
49 path, output, dockerfile, compose, terraform, all, dry_run, force,
50 ),
51 Commands::Validate { path, types, fix } => handlers::handle_validate(path, types, fix),
52 Commands::Support {
53 languages,
54 frameworks,
55 detailed,
56 } => handlers::handle_support(languages, frameworks, detailed),
57 Commands::Dependencies {
58 path,
59 licenses,
60 vulnerabilities,
61 prod_only,
62 dev_only,
63 format,
64 } => handlers::handle_dependencies(
65 path,
66 licenses,
67 vulnerabilities,
68 prod_only,
69 dev_only,
70 format,
71 )
72 .await
73 .map(|_| ()),
74 Commands::Vulnerabilities {
75 path,
76 severity,
77 format,
78 output,
79 } => handlers::handle_vulnerabilities(path, severity, format, output).await,
80 Commands::Security {
81 path,
82 mode,
83 include_low,
84 no_secrets,
85 no_code_patterns,
86 no_infrastructure,
87 no_compliance,
88 frameworks,
89 format,
90 output,
91 fail_on_findings,
92 } => {
93 handlers::handle_security(
94 path,
95 mode,
96 include_low,
97 no_secrets,
98 no_code_patterns,
99 no_infrastructure,
100 no_compliance,
101 frameworks,
102 format,
103 output,
104 fail_on_findings,
105 )
106 .map(|_| ()) }
108 Commands::Tools { command } => handlers::handle_tools(command).await,
109 Commands::Chat {
110 path,
111 provider,
112 model,
113 query,
114 resume,
115 list_sessions: _, } => {
117 use agent::ProviderType;
118 use cli::ChatProvider;
119 use config::load_agent_config;
120
121 let project_path = path.canonicalize().unwrap_or(path);
122
123 if let Some(ref resume_arg) = resume {
125 use agent::persistence::{SessionSelector, format_relative_time};
126
127 let selector = SessionSelector::new(&project_path);
128 if let Some(session_info) = selector.resolve_session(resume_arg) {
129 let time = format_relative_time(session_info.last_updated);
130 println!(
131 "\nResuming session: {} ({}, {} messages)",
132 session_info.display_name, time, session_info.message_count
133 );
134 println!("Session ID: {}\n", session_info.id);
135
136 match selector.load_conversation(&session_info) {
138 Ok(record) => {
139 println!("--- Previous conversation ---");
141 for msg in record.messages.iter().take(5) {
142 let role = match msg.role {
143 agent::persistence::MessageRole::User => "You",
144 agent::persistence::MessageRole::Assistant => "AI",
145 agent::persistence::MessageRole::System => "System",
146 };
147 let preview = if msg.content.len() > 100 {
148 format!("{}...", &msg.content[..100])
149 } else {
150 msg.content.clone()
151 };
152 println!(" {}: {}", role, preview);
153 }
154 if record.messages.len() > 5 {
155 println!(" ... and {} more messages", record.messages.len() - 5);
156 }
157 println!("--- End of history ---\n");
158 }
160 Err(e) => {
161 eprintln!("Warning: Failed to load session history: {}", e);
162 }
163 }
164 } else {
165 eprintln!(
166 "Session '{}' not found. Use --list-sessions to see available sessions.",
167 resume_arg
168 );
169 return Ok(());
170 }
171 }
172
173 let agent_config = load_agent_config();
175
176 let (provider_type, effective_model) = match provider {
178 ChatProvider::Openai => (ProviderType::OpenAI, model),
179 ChatProvider::Anthropic => (ProviderType::Anthropic, model),
180 ChatProvider::Bedrock => (ProviderType::Bedrock, model),
181 ChatProvider::Ollama => {
182 eprintln!("Ollama support coming soon. Using OpenAI as fallback.");
183 (ProviderType::OpenAI, model)
184 }
185 ChatProvider::Auto => {
186 let saved_provider = match agent_config.default_provider.as_str() {
188 "openai" => ProviderType::OpenAI,
189 "anthropic" => ProviderType::Anthropic,
190 "bedrock" => ProviderType::Bedrock,
191 _ => ProviderType::OpenAI, };
193 let saved_model = if model.is_some() {
195 model
196 } else {
197 agent_config.default_model.clone()
198 };
199 (saved_provider, saved_model)
200 }
201 };
202
203 agent::session::ChatSession::load_api_key_to_env(provider_type);
206
207 if let Some(q) = query {
208 let response =
209 agent::run_query(&project_path, &q, provider_type, effective_model).await?;
210 println!("{}", response);
211 Ok(())
212 } else {
213 agent::run_interactive(&project_path, provider_type, effective_model).await?;
214 Ok(())
215 }
216 }
217 Commands::Auth { command } => {
218 use cli::AuthCommand;
219 use auth::credentials;
220 use auth::device_flow;
221
222 match command {
223 AuthCommand::Login { no_browser } => {
224 device_flow::login(no_browser).await.map_err(|e| {
225 error::IaCGeneratorError::Config(error::ConfigError::ParsingFailed(e.to_string()))
226 })
227 }
228 AuthCommand::Logout => {
229 credentials::clear_credentials().map_err(|e| {
230 error::IaCGeneratorError::Config(error::ConfigError::ParsingFailed(e.to_string()))
231 })?;
232 println!("✅ Logged out successfully. Credentials cleared.");
233 Ok(())
234 }
235 AuthCommand::Status => {
236 match credentials::get_auth_status() {
237 credentials::AuthStatus::NotAuthenticated => {
238 println!("❌ Not logged in.");
239 println!(" Run: sync-ctl auth login");
240 }
241 credentials::AuthStatus::Expired => {
242 println!("⚠️ Session expired.");
243 println!(" Run: sync-ctl auth login");
244 }
245 credentials::AuthStatus::Authenticated { email, expires_at } => {
246 println!("✅ Logged in");
247 if let Some(e) = email {
248 println!(" Email: {}", e);
249 }
250 if let Some(exp) = expires_at {
251 let now = std::time::SystemTime::now()
252 .duration_since(std::time::UNIX_EPOCH)
253 .map(|d| d.as_secs())
254 .unwrap_or(0);
255 if exp > now {
256 let remaining = exp - now;
257 let days = remaining / 86400;
258 let hours = (remaining % 86400) / 3600;
259 println!(" Expires in: {}d {}h", days, hours);
260 }
261 }
262 }
263 }
264 Ok(())
265 }
266 AuthCommand::Token { raw } => {
267 match credentials::get_access_token() {
268 Some(token) => {
269 if raw {
270 print!("{}", token);
271 } else {
272 println!("Access Token: {}", token);
273 }
274 Ok(())
275 }
276 None => {
277 eprintln!("Not authenticated. Run: sync-ctl auth login");
278 std::process::exit(1);
279 }
280 }
281 }
282 }
283 }
284 }
285}