syncable_cli/
lib.rs

1pub mod agent;
2pub mod analyzer;
3pub mod bedrock; // Inlined rig-bedrock with extended thinking fixes
4pub mod cli;
5pub mod common;
6pub mod config;
7pub mod error;
8pub mod generator;
9pub mod handlers;
10pub mod telemetry; // Add telemetry module
11
12// Re-export commonly used types and functions
13pub use analyzer::{ProjectAnalysis, analyze_project};
14use cli::Commands;
15pub use error::{IaCGeneratorError, Result};
16pub use generator::{generate_compose, generate_dockerfile, generate_terraform};
17pub use handlers::*;
18pub use telemetry::{TelemetryClient, TelemetryConfig, UserId}; // Re-export telemetry types
19
20/// The current version of the CLI tool
21pub const VERSION: &str = env!("CARGO_PKG_VERSION");
22
23pub async fn run_command(command: Commands) -> Result<()> {
24    match command {
25        Commands::Analyze {
26            path,
27            json,
28            detailed,
29            display,
30            only,
31            color_scheme,
32        } => {
33            match handlers::handle_analyze(path, json, detailed, display, only, color_scheme) {
34                Ok(_output) => Ok(()), // The output was already printed by display_analysis_with_return
35                Err(e) => Err(e),
36            }
37        }
38        Commands::Generate {
39            path,
40            output,
41            dockerfile,
42            compose,
43            terraform,
44            all,
45            dry_run,
46            force,
47        } => handlers::handle_generate(
48            path, output, dockerfile, compose, terraform, all, dry_run, force,
49        ),
50        Commands::Validate { path, types, fix } => handlers::handle_validate(path, types, fix),
51        Commands::Support {
52            languages,
53            frameworks,
54            detailed,
55        } => handlers::handle_support(languages, frameworks, detailed),
56        Commands::Dependencies {
57            path,
58            licenses,
59            vulnerabilities,
60            prod_only,
61            dev_only,
62            format,
63        } => handlers::handle_dependencies(
64            path,
65            licenses,
66            vulnerabilities,
67            prod_only,
68            dev_only,
69            format,
70        )
71        .await
72        .map(|_| ()),
73        Commands::Vulnerabilities {
74            path,
75            severity,
76            format,
77            output,
78        } => handlers::handle_vulnerabilities(path, severity, format, output).await,
79        Commands::Security {
80            path,
81            mode,
82            include_low,
83            no_secrets,
84            no_code_patterns,
85            no_infrastructure,
86            no_compliance,
87            frameworks,
88            format,
89            output,
90            fail_on_findings,
91        } => {
92            handlers::handle_security(
93                path,
94                mode,
95                include_low,
96                no_secrets,
97                no_code_patterns,
98                no_infrastructure,
99                no_compliance,
100                frameworks,
101                format,
102                output,
103                fail_on_findings,
104            )
105            .map(|_| ()) // Map Result<String> to Result<()>
106        }
107        Commands::Tools { command } => handlers::handle_tools(command).await,
108        Commands::Chat {
109            path,
110            provider,
111            model,
112            query,
113            resume,
114            list_sessions: _, // Handled in main.rs
115        } => {
116            use agent::ProviderType;
117            use cli::ChatProvider;
118            use config::load_agent_config;
119
120            let project_path = path.canonicalize().unwrap_or(path);
121
122            // Handle --resume flag
123            if let Some(ref resume_arg) = resume {
124                use agent::persistence::{SessionSelector, format_relative_time};
125
126                let selector = SessionSelector::new(&project_path);
127                if let Some(session_info) = selector.resolve_session(resume_arg) {
128                    let time = format_relative_time(session_info.last_updated);
129                    println!(
130                        "\nResuming session: {} ({}, {} messages)",
131                        session_info.display_name, time, session_info.message_count
132                    );
133                    println!("Session ID: {}\n", session_info.id);
134
135                    // Load the session
136                    match selector.load_conversation(&session_info) {
137                        Ok(record) => {
138                            // Display previous messages as context
139                            println!("--- Previous conversation ---");
140                            for msg in record.messages.iter().take(5) {
141                                let role = match msg.role {
142                                    agent::persistence::MessageRole::User => "You",
143                                    agent::persistence::MessageRole::Assistant => "AI",
144                                    agent::persistence::MessageRole::System => "System",
145                                };
146                                let preview = if msg.content.len() > 100 {
147                                    format!("{}...", &msg.content[..100])
148                                } else {
149                                    msg.content.clone()
150                                };
151                                println!("  {}: {}", role, preview);
152                            }
153                            if record.messages.len() > 5 {
154                                println!("  ... and {} more messages", record.messages.len() - 5);
155                            }
156                            println!("--- End of history ---\n");
157                            // TODO: Load history into conversation context
158                        }
159                        Err(e) => {
160                            eprintln!("Warning: Failed to load session history: {}", e);
161                        }
162                    }
163                } else {
164                    eprintln!(
165                        "Session '{}' not found. Use --list-sessions to see available sessions.",
166                        resume_arg
167                    );
168                    return Ok(());
169                }
170            }
171
172            // Load saved config for Auto mode
173            let agent_config = load_agent_config();
174
175            // Determine provider - use saved default if Auto
176            let (provider_type, effective_model) = match provider {
177                ChatProvider::Openai => (ProviderType::OpenAI, model),
178                ChatProvider::Anthropic => (ProviderType::Anthropic, model),
179                ChatProvider::Bedrock => (ProviderType::Bedrock, model),
180                ChatProvider::Ollama => {
181                    eprintln!("Ollama support coming soon. Using OpenAI as fallback.");
182                    (ProviderType::OpenAI, model)
183                }
184                ChatProvider::Auto => {
185                    // Load from saved config
186                    let saved_provider = match agent_config.default_provider.as_str() {
187                        "openai" => ProviderType::OpenAI,
188                        "anthropic" => ProviderType::Anthropic,
189                        "bedrock" => ProviderType::Bedrock,
190                        _ => ProviderType::OpenAI, // Fallback
191                    };
192                    // Use saved model if no explicit model provided
193                    let saved_model = if model.is_some() {
194                        model
195                    } else {
196                        agent_config.default_model.clone()
197                    };
198                    (saved_provider, saved_model)
199                }
200            };
201
202            // Load API key/credentials from config to environment
203            // This is essential for Bedrock bearer token auth!
204            agent::session::ChatSession::load_api_key_to_env(provider_type);
205
206            if let Some(q) = query {
207                let response =
208                    agent::run_query(&project_path, &q, provider_type, effective_model).await?;
209                println!("{}", response);
210                Ok(())
211            } else {
212                agent::run_interactive(&project_path, provider_type, effective_model).await?;
213                Ok(())
214            }
215        }
216    }
217}