Skip to main content

syncable_cli/
cli.rs

1use clap::{Parser, Subcommand, ValueEnum};
2use std::path::PathBuf;
3
4#[derive(Parser)]
5#[command(name = "sync-ctl")]
6#[command(version = env!("CARGO_PKG_VERSION"))]
7#[command(about = "Generate Infrastructure as Code from your codebase")]
8#[command(
9    long_about = "A powerful CLI tool that analyzes your codebase and automatically generates optimized Infrastructure as Code configurations including Dockerfiles, Docker Compose files, and Terraform configurations"
10)]
11pub struct Cli {
12    #[command(subcommand)]
13    pub command: Commands,
14
15    /// Path to configuration file
16    #[arg(short, long, global = true, value_name = "FILE")]
17    pub config: Option<PathBuf>,
18
19    /// Enable verbose logging (-v for info, -vv for debug, -vvv for trace)
20    #[arg(short, long, global = true, action = clap::ArgAction::Count)]
21    pub verbose: u8,
22
23    /// Suppress all output except errors
24    #[arg(short, long, global = true)]
25    pub quiet: bool,
26
27    /// Output in JSON format where applicable
28    #[arg(long, global = true)]
29    pub json: bool,
30
31    /// Clear the update check cache and force a new check
32    #[arg(long, global = true)]
33    pub clear_update_cache: bool,
34
35    /// Disable telemetry data collection
36    #[arg(long, global = true)]
37    pub disable_telemetry: bool,
38}
39
40#[derive(Subcommand)]
41pub enum Commands {
42    /// Analyze a project and display detected components
43    Analyze {
44        /// Path to the project directory to analyze
45        #[arg(value_name = "PROJECT_PATH")]
46        path: PathBuf,
47
48        /// Output analysis results in JSON format
49        #[arg(short, long)]
50        json: bool,
51
52        /// Show detailed analysis information (legacy format)
53        #[arg(short, long, conflicts_with = "display")]
54        detailed: bool,
55
56        /// Display format for analysis results
57        #[arg(long, value_enum, default_value = "matrix")]
58        display: Option<DisplayFormat>,
59
60        /// Only analyze specific aspects (languages, frameworks, dependencies)
61        #[arg(long, value_delimiter = ',')]
62        only: Option<Vec<String>>,
63
64        /// Color scheme for terminal output (auto-detect, dark, light)
65        #[arg(long, value_enum, default_value = "auto")]
66        color_scheme: Option<ColorScheme>,
67    },
68
69    /// Generate IaC files for a project
70    Generate {
71        /// Path to the project directory to analyze
72        #[arg(value_name = "PROJECT_PATH")]
73        path: PathBuf,
74
75        /// Output directory for generated files
76        #[arg(short, long, value_name = "OUTPUT_DIR")]
77        output: Option<PathBuf>,
78
79        /// Generate Dockerfile
80        #[arg(long)]
81        dockerfile: bool,
82
83        /// Generate Docker Compose file
84        #[arg(long)]
85        compose: bool,
86
87        /// Generate Terraform configuration
88        #[arg(long)]
89        terraform: bool,
90
91        /// Generate all supported IaC files
92        #[arg(long, conflicts_with_all = ["dockerfile", "compose", "terraform"])]
93        all: bool,
94
95        /// Perform a dry run without creating files
96        #[arg(long)]
97        dry_run: bool,
98
99        /// Overwrite existing files
100        #[arg(long)]
101        force: bool,
102    },
103
104    /// Validate existing IaC files against best practices
105    Validate {
106        /// Path to the directory containing IaC files
107        #[arg(value_name = "PATH")]
108        path: PathBuf,
109
110        /// Types of files to validate
111        #[arg(long, value_delimiter = ',')]
112        types: Option<Vec<String>>,
113
114        /// Fix issues automatically where possible
115        #[arg(long)]
116        fix: bool,
117    },
118
119    /// Show supported languages and frameworks
120    Support {
121        /// Show only languages
122        #[arg(long)]
123        languages: bool,
124
125        /// Show only frameworks
126        #[arg(long)]
127        frameworks: bool,
128
129        /// Show detailed information
130        #[arg(short, long)]
131        detailed: bool,
132    },
133
134    /// Analyze project dependencies in detail
135    Dependencies {
136        /// Path to the project directory to analyze
137        #[arg(value_name = "PROJECT_PATH")]
138        path: PathBuf,
139
140        /// Show license information for dependencies
141        #[arg(long)]
142        licenses: bool,
143
144        /// Check for known vulnerabilities
145        #[arg(long)]
146        vulnerabilities: bool,
147
148        /// Show only production dependencies
149        #[arg(long, conflicts_with = "dev_only")]
150        prod_only: bool,
151
152        /// Show only development dependencies
153        #[arg(long, conflicts_with = "prod_only")]
154        dev_only: bool,
155
156        /// Output format
157        #[arg(long, value_enum, default_value = "table")]
158        format: OutputFormat,
159    },
160
161    /// Check dependencies for known vulnerabilities
162    Vulnerabilities {
163        /// Check vulnerabilities in a specific path
164        #[arg(default_value = ".")]
165        path: PathBuf,
166
167        /// Show only vulnerabilities with severity >= threshold
168        #[arg(long, value_enum)]
169        severity: Option<SeverityThreshold>,
170
171        /// Output format
172        #[arg(long, value_enum, default_value = "table")]
173        format: OutputFormat,
174
175        /// Export report to file
176        #[arg(long)]
177        output: Option<PathBuf>,
178    },
179
180    /// Perform comprehensive security analysis
181    Security {
182        /// Path to the project directory to analyze
183        #[arg(value_name = "PROJECT_PATH", default_value = ".")]
184        path: PathBuf,
185
186        /// Security scan mode (lightning, fast, balanced, thorough, paranoid)
187        #[arg(long, value_enum, default_value = "thorough")]
188        mode: SecurityScanMode,
189
190        /// Include low severity findings
191        #[arg(long)]
192        include_low: bool,
193
194        /// Skip secrets detection
195        #[arg(long)]
196        no_secrets: bool,
197
198        /// Skip code pattern analysis
199        #[arg(long)]
200        no_code_patterns: bool,
201
202        /// Skip infrastructure analysis (not implemented yet)
203        #[arg(long, hide = true)]
204        no_infrastructure: bool,
205
206        /// Skip compliance checks (not implemented yet)
207        #[arg(long, hide = true)]
208        no_compliance: bool,
209
210        /// Compliance frameworks to check (not implemented yet)
211        #[arg(long, value_delimiter = ',', hide = true)]
212        frameworks: Vec<String>,
213
214        /// Output format
215        #[arg(long, value_enum, default_value = "table")]
216        format: OutputFormat,
217
218        /// Export report to file
219        #[arg(long)]
220        output: Option<PathBuf>,
221
222        /// Exit with error code on security findings
223        #[arg(long)]
224        fail_on_findings: bool,
225    },
226
227    /// Manage vulnerability scanning tools
228    Tools {
229        #[command(subcommand)]
230        command: ToolsCommand,
231    },
232
233    /// Analyze Kubernetes manifests for resource optimization opportunities
234    Optimize {
235        /// Path to Kubernetes manifests (file or directory)
236        #[arg(value_name = "PATH", default_value = ".")]
237        path: PathBuf,
238
239        /// Connect to a live Kubernetes cluster for metrics-based recommendations
240        /// Uses current kubeconfig context, or specify a context name
241        #[arg(long, short = 'k', value_name = "CONTEXT", default_missing_value = "current", num_args = 0..=1)]
242        cluster: Option<String>,
243
244        /// Prometheus URL for historical metrics (e.g., http://localhost:9090)
245        #[arg(long, value_name = "URL")]
246        prometheus: Option<String>,
247
248        /// Target namespace(s) for cluster analysis (comma-separated, or * for all)
249        #[arg(long, short = 'n', value_name = "NAMESPACE")]
250        namespace: Option<String>,
251
252        /// Analysis period for historical metrics (e.g., 7d, 30d)
253        #[arg(long, short = 'p', default_value = "7d")]
254        period: String,
255
256        /// Minimum severity to report (critical, warning, info)
257        #[arg(long, short = 's')]
258        severity: Option<String>,
259
260        /// Minimum waste percentage threshold (0-100)
261        #[arg(long, short = 't')]
262        threshold: Option<u8>,
263
264        /// Safety margin percentage for recommendations (default: 20)
265        #[arg(long)]
266        safety_margin: Option<u8>,
267
268        /// Include info-level suggestions
269        #[arg(long)]
270        include_info: bool,
271
272        /// Include system namespaces (kube-system, etc.)
273        #[arg(long)]
274        include_system: bool,
275
276        /// Output format (table, json, yaml)
277        #[arg(long, value_enum, default_value = "table")]
278        format: OutputFormat,
279
280        /// Write report to file
281        #[arg(long, short = 'o')]
282        output: Option<PathBuf>,
283
284        /// Generate fix suggestions
285        #[arg(long)]
286        fix: bool,
287
288        /// Apply fixes to manifest files (requires --fix or --full with live cluster)
289        #[arg(long, requires = "fix")]
290        apply: bool,
291
292        /// Preview changes without applying (dry-run mode)
293        #[arg(long)]
294        dry_run: bool,
295
296        /// Backup directory for original files before applying fixes
297        #[arg(long, value_name = "DIR")]
298        backup_dir: Option<PathBuf>,
299
300        /// Minimum confidence threshold for auto-apply (0-100, default: 70)
301        #[arg(long, default_value = "70")]
302        min_confidence: u8,
303
304        /// Cloud provider for cost estimation (aws, gcp, azure, onprem)
305        #[arg(long, value_name = "PROVIDER")]
306        cloud_provider: Option<String>,
307
308        /// Region for cloud pricing (e.g., us-east-1, us-central1)
309        #[arg(long, value_name = "REGION", default_value = "us-east-1")]
310        region: String,
311
312        /// Run comprehensive analysis (includes kubelint security checks and helmlint validation)
313        #[arg(long, short = 'f')]
314        full: bool,
315    },
316
317    /// Start an interactive AI chat session to analyze and understand your project
318    Chat {
319        /// Path to the project directory (default: current directory)
320        #[arg(value_name = "PROJECT_PATH", default_value = ".")]
321        path: PathBuf,
322
323        /// LLM provider to use (uses saved preference by default)
324        #[arg(long, value_enum, default_value = "auto")]
325        provider: ChatProvider,
326
327        /// Model to use (e.g., gpt-4o, claude-3-5-sonnet-latest, llama3.2)
328        #[arg(long)]
329        model: Option<String>,
330
331        /// Run a single query instead of interactive mode
332        #[arg(long)]
333        query: Option<String>,
334
335        /// Resume a previous session (accepts: "latest", session number, or UUID)
336        #[arg(long, short = 'r')]
337        resume: Option<String>,
338
339        /// List available sessions for this project and exit
340        #[arg(long)]
341        list_sessions: bool,
342
343        /// Start AG-UI server for frontend connectivity (SSE/WebSocket)
344        #[arg(long)]
345        ag_ui: bool,
346
347        /// AG-UI server port (default: 9090)
348        #[arg(long, default_value = "9090", requires = "ag_ui")]
349        ag_ui_port: u16,
350    },
351
352    /// Authenticate with the Syncable platform
353    Auth {
354        #[command(subcommand)]
355        command: AuthCommand,
356    },
357
358    /// Manage Syncable projects
359    Project {
360        #[command(subcommand)]
361        command: ProjectCommand,
362    },
363
364    /// Manage Syncable organizations
365    Org {
366        #[command(subcommand)]
367        command: OrgCommand,
368    },
369
370    /// Manage environments within a project
371    Env {
372        #[command(subcommand)]
373        command: EnvCommand,
374    },
375
376    /// Deploy services to the Syncable platform (launches wizard by default)
377    Deploy {
378        /// Path to the project directory (default: current directory)
379        #[arg(value_name = "PROJECT_PATH", default_value = ".")]
380        path: PathBuf,
381
382        #[command(subcommand)]
383        command: Option<DeployCommand>,
384    },
385
386    /// Run as dedicated AG-UI agent server (headless mode for containers)
387    Agent {
388        /// Path to the project directory
389        #[arg(value_name = "PROJECT_PATH", default_value = ".")]
390        path: PathBuf,
391
392        /// Port for AG-UI server
393        #[arg(long, short, default_value = "9090")]
394        port: u16,
395
396        /// Host address to bind to
397        #[arg(long, default_value = "127.0.0.1")]
398        host: String,
399
400        /// LLM provider to use
401        #[arg(long, value_enum, default_value = "auto")]
402        provider: ChatProvider,
403
404        /// Model to use
405        #[arg(long)]
406        model: Option<String>,
407    },
408}
409
410#[derive(Subcommand)]
411pub enum ToolsCommand {
412    /// Check which vulnerability scanning tools are installed
413    Status {
414        /// Output format
415        #[arg(long, value_enum, default_value = "table")]
416        format: OutputFormat,
417
418        /// Check tools for specific languages only
419        #[arg(long, value_delimiter = ',')]
420        languages: Option<Vec<String>>,
421    },
422
423    /// Install missing vulnerability scanning tools
424    Install {
425        /// Install tools for specific languages only
426        #[arg(long, value_delimiter = ',')]
427        languages: Option<Vec<String>>,
428
429        /// Also install OWASP Dependency Check (large download)
430        #[arg(long)]
431        include_owasp: bool,
432
433        /// Perform a dry run to show what would be installed
434        #[arg(long)]
435        dry_run: bool,
436
437        /// Skip confirmation prompts
438        #[arg(short, long)]
439        yes: bool,
440    },
441
442    /// Verify that installed tools are working correctly
443    Verify {
444        /// Test tools for specific languages only
445        #[arg(long, value_delimiter = ',')]
446        languages: Option<Vec<String>>,
447
448        /// Show detailed verification output
449        #[arg(short, long)]
450        detailed: bool,
451    },
452
453    /// Show tool installation guides for manual setup
454    Guide {
455        /// Show guide for specific languages only
456        #[arg(long, value_delimiter = ',')]
457        languages: Option<Vec<String>>,
458
459        /// Show platform-specific instructions
460        #[arg(long)]
461        platform: Option<String>,
462    },
463}
464
465/// Authentication subcommands for the Syncable platform
466#[derive(Subcommand)]
467pub enum AuthCommand {
468    /// Log in to Syncable (opens browser for authentication)
469    Login {
470        /// Don't open browser automatically
471        #[arg(long)]
472        no_browser: bool,
473    },
474
475    /// Log out and clear stored credentials
476    Logout,
477
478    /// Show current authentication status
479    Status,
480
481    /// Print current access token (for scripting)
482    Token {
483        /// Print raw token without formatting
484        #[arg(long)]
485        raw: bool,
486    },
487}
488
489/// Project management subcommands
490#[derive(Subcommand)]
491pub enum ProjectCommand {
492    /// List projects in the current organization
493    List {
494        /// Organization ID to list projects from (uses current org if not specified)
495        #[arg(long)]
496        org_id: Option<String>,
497
498        /// Output format
499        #[arg(long, value_enum, default_value = "table")]
500        format: OutputFormat,
501    },
502
503    /// Select a project to work with
504    Select {
505        /// Project ID to select
506        id: String,
507    },
508
509    /// Show current organization and project context
510    Current,
511
512    /// Show details of a project
513    Info {
514        /// Project ID (uses current project if not specified)
515        id: Option<String>,
516    },
517}
518
519/// Organization management subcommands
520#[derive(Subcommand)]
521pub enum OrgCommand {
522    /// List organizations you belong to
523    List {
524        /// Output format
525        #[arg(long, value_enum, default_value = "table")]
526        format: OutputFormat,
527    },
528
529    /// Select an organization to work with
530    Select {
531        /// Organization ID to select
532        id: String,
533    },
534}
535
536/// Environment management subcommands
537#[derive(Subcommand)]
538pub enum EnvCommand {
539    /// List environments in the current project
540    List {
541        /// Output format
542        #[arg(long, value_enum, default_value = "table")]
543        format: OutputFormat,
544    },
545
546    /// Select an environment to work with
547    Select {
548        /// Environment ID to select
549        id: String,
550    },
551}
552
553/// Deployment subcommands
554#[derive(Subcommand)]
555pub enum DeployCommand {
556    /// Launch interactive deployment wizard
557    Wizard {
558        /// Path to the project directory (default: current directory)
559        #[arg(value_name = "PROJECT_PATH", default_value = ".")]
560        path: PathBuf,
561    },
562
563    /// Create a new environment for the current project
564    NewEnv,
565
566    /// Check deployment status
567    Status {
568        /// The deployment task ID (from deploy command output)
569        task_id: String,
570
571        /// Watch for status updates (poll until complete)
572        #[arg(short, long)]
573        watch: bool,
574    },
575}
576
577#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum)]
578pub enum OutputFormat {
579    Table,
580    Json,
581}
582
583#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum)]
584pub enum DisplayFormat {
585    /// Compact matrix/dashboard view (modern, easy to scan)
586    Matrix,
587    /// Detailed vertical view (legacy format with all details)
588    Detailed,
589    /// Brief summary only
590    Summary,
591}
592
593#[derive(Clone, Copy, Debug, ValueEnum)]
594pub enum ColorScheme {
595    /// Auto-detect terminal background (default)
596    Auto,
597    /// Dark background terminal colors
598    Dark,
599    /// Light background terminal colors
600    Light,
601}
602
603#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum)]
604pub enum SeverityThreshold {
605    Low,
606    Medium,
607    High,
608    Critical,
609}
610
611#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum)]
612pub enum SecurityScanMode {
613    /// Lightning fast scan - critical files only (.env, configs)
614    Lightning,
615    /// Fast scan - smart sampling with priority patterns
616    Fast,
617    /// Balanced scan - good coverage with performance optimizations (recommended)
618    Balanced,
619    /// Thorough scan - comprehensive analysis of all files
620    Thorough,
621    /// Paranoid scan - most comprehensive including low-severity findings
622    Paranoid,
623}
624
625#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum, Default)]
626pub enum ChatProvider {
627    /// OpenAI (GPT-4o, GPT-4, etc.)
628    Openai,
629    /// Anthropic (Claude 3)
630    Anthropic,
631    /// AWS Bedrock (Claude via AWS)
632    Bedrock,
633    /// Ollama (local LLM, no API key needed)
634    Ollama,
635    /// Use saved default from config file
636    #[default]
637    Auto,
638}
639
640impl Cli {
641    /// Initialize logging based on verbosity level
642    pub fn init_logging(&self) {
643        if self.quiet {
644            return;
645        }
646
647        let level = match self.verbose {
648            0 => log::LevelFilter::Warn,
649            1 => log::LevelFilter::Info,
650            2 => log::LevelFilter::Debug,
651            _ => log::LevelFilter::Trace,
652        };
653
654        env_logger::Builder::from_default_env()
655            .filter_level(level)
656            .init();
657    }
658}