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        /// Output compressed JSON for AI agent consumption (implies --json)
69        #[arg(long)]
70        agent: bool,
71    },
72
73    /// Generate IaC files for a project
74    Generate {
75        /// Path to the project directory to analyze
76        #[arg(value_name = "PROJECT_PATH")]
77        path: PathBuf,
78
79        /// Output directory for generated files
80        #[arg(short, long, value_name = "OUTPUT_DIR")]
81        output: Option<PathBuf>,
82
83        /// Generate Dockerfile
84        #[arg(long)]
85        dockerfile: bool,
86
87        /// Generate Docker Compose file
88        #[arg(long)]
89        compose: bool,
90
91        /// Generate Terraform configuration
92        #[arg(long)]
93        terraform: bool,
94
95        /// Generate all supported IaC files
96        #[arg(long, conflicts_with_all = ["dockerfile", "compose", "terraform"])]
97        all: bool,
98
99        /// Perform a dry run without creating files
100        #[arg(long)]
101        dry_run: bool,
102
103        /// Overwrite existing files
104        #[arg(long)]
105        force: bool,
106    },
107
108    /// Validate existing IaC files against best practices
109    Validate {
110        /// Path to the directory containing IaC files
111        #[arg(value_name = "PATH")]
112        path: PathBuf,
113
114        /// Types of files to validate
115        #[arg(long, value_delimiter = ',')]
116        types: Option<Vec<String>>,
117
118        /// Fix issues automatically where possible
119        #[arg(long)]
120        fix: bool,
121
122        /// Output compressed JSON for AI agent consumption (implies --json)
123        #[arg(long)]
124        agent: bool,
125    },
126
127    /// Show supported languages and frameworks
128    Support {
129        /// Show only languages
130        #[arg(long)]
131        languages: bool,
132
133        /// Show only frameworks
134        #[arg(long)]
135        frameworks: bool,
136
137        /// Show detailed information
138        #[arg(short, long)]
139        detailed: bool,
140    },
141
142    /// Analyze project dependencies in detail
143    Dependencies {
144        /// Path to the project directory to analyze
145        #[arg(value_name = "PROJECT_PATH")]
146        path: PathBuf,
147
148        /// Show license information for dependencies
149        #[arg(long)]
150        licenses: bool,
151
152        /// Check for known vulnerabilities
153        #[arg(long)]
154        vulnerabilities: bool,
155
156        /// Show only production dependencies
157        #[arg(long, conflicts_with = "dev_only")]
158        prod_only: bool,
159
160        /// Show only development dependencies
161        #[arg(long, conflicts_with = "prod_only")]
162        dev_only: bool,
163
164        /// Output format
165        #[arg(long, value_enum, default_value = "table")]
166        format: OutputFormat,
167
168        /// Output compressed JSON for AI agent consumption (implies --json)
169        #[arg(long)]
170        agent: bool,
171    },
172
173    /// Check dependencies for known vulnerabilities
174    Vulnerabilities {
175        /// Check vulnerabilities in a specific path
176        #[arg(default_value = ".")]
177        path: PathBuf,
178
179        /// Show only vulnerabilities with severity >= threshold
180        #[arg(long, value_enum)]
181        severity: Option<SeverityThreshold>,
182
183        /// Output format
184        #[arg(long, value_enum, default_value = "table")]
185        format: OutputFormat,
186
187        /// Export report to file
188        #[arg(long)]
189        output: Option<PathBuf>,
190
191        /// Output compressed JSON for AI agent consumption (implies --json)
192        #[arg(long)]
193        agent: bool,
194    },
195
196    /// Perform comprehensive security analysis
197    Security {
198        /// Path to the project directory to analyze
199        #[arg(value_name = "PROJECT_PATH", default_value = ".")]
200        path: PathBuf,
201
202        /// Security scan mode (lightning, fast, balanced, thorough, paranoid)
203        #[arg(long, value_enum, default_value = "thorough")]
204        mode: SecurityScanMode,
205
206        /// Include low severity findings
207        #[arg(long)]
208        include_low: bool,
209
210        /// Skip secrets detection
211        #[arg(long)]
212        no_secrets: bool,
213
214        /// Skip code pattern analysis
215        #[arg(long)]
216        no_code_patterns: bool,
217
218        /// Skip infrastructure analysis (not implemented yet)
219        #[arg(long, hide = true)]
220        no_infrastructure: bool,
221
222        /// Skip compliance checks (not implemented yet)
223        #[arg(long, hide = true)]
224        no_compliance: bool,
225
226        /// Compliance frameworks to check (not implemented yet)
227        #[arg(long, value_delimiter = ',', hide = true)]
228        frameworks: Vec<String>,
229
230        /// Output format
231        #[arg(long, value_enum, default_value = "table")]
232        format: OutputFormat,
233
234        /// Export report to file
235        #[arg(long)]
236        output: Option<PathBuf>,
237
238        /// Exit with error code on security findings
239        #[arg(long)]
240        fail_on_findings: bool,
241
242        /// Output compressed JSON for AI agent consumption (implies --json)
243        #[arg(long)]
244        agent: bool,
245    },
246
247    /// Manage vulnerability scanning tools
248    Tools {
249        #[command(subcommand)]
250        command: ToolsCommand,
251    },
252
253    /// Analyze Kubernetes manifests for resource optimization opportunities
254    Optimize {
255        /// Path to Kubernetes manifests (file or directory)
256        #[arg(value_name = "PATH", default_value = ".")]
257        path: PathBuf,
258
259        /// Connect to a live Kubernetes cluster for metrics-based recommendations
260        /// Uses current kubeconfig context, or specify a context name
261        #[arg(long, short = 'k', value_name = "CONTEXT", default_missing_value = "current", num_args = 0..=1)]
262        cluster: Option<String>,
263
264        /// Prometheus URL for historical metrics (e.g., http://localhost:9090)
265        #[arg(long, value_name = "URL")]
266        prometheus: Option<String>,
267
268        /// Target namespace(s) for cluster analysis (comma-separated, or * for all)
269        #[arg(long, short = 'n', value_name = "NAMESPACE")]
270        namespace: Option<String>,
271
272        /// Analysis period for historical metrics (e.g., 7d, 30d)
273        #[arg(long, short = 'p', default_value = "7d")]
274        period: String,
275
276        /// Minimum severity to report (critical, warning, info)
277        #[arg(long, short = 's')]
278        severity: Option<String>,
279
280        /// Minimum waste percentage threshold (0-100)
281        #[arg(long, short = 't')]
282        threshold: Option<u8>,
283
284        /// Safety margin percentage for recommendations (default: 20)
285        #[arg(long)]
286        safety_margin: Option<u8>,
287
288        /// Include info-level suggestions
289        #[arg(long)]
290        include_info: bool,
291
292        /// Include system namespaces (kube-system, etc.)
293        #[arg(long)]
294        include_system: bool,
295
296        /// Output format (table, json, yaml)
297        #[arg(long, value_enum, default_value = "table")]
298        format: OutputFormat,
299
300        /// Write report to file
301        #[arg(long, short = 'o')]
302        output: Option<PathBuf>,
303
304        /// Generate fix suggestions
305        #[arg(long)]
306        fix: bool,
307
308        /// Apply fixes to manifest files (requires --fix or --full with live cluster)
309        #[arg(long, requires = "fix")]
310        apply: bool,
311
312        /// Preview changes without applying (dry-run mode)
313        #[arg(long)]
314        dry_run: bool,
315
316        /// Backup directory for original files before applying fixes
317        #[arg(long, value_name = "DIR")]
318        backup_dir: Option<PathBuf>,
319
320        /// Minimum confidence threshold for auto-apply (0-100, default: 70)
321        #[arg(long, default_value = "70")]
322        min_confidence: u8,
323
324        /// Cloud provider for cost estimation (aws, gcp, azure, onprem)
325        #[arg(long, value_name = "PROVIDER")]
326        cloud_provider: Option<String>,
327
328        /// Region for cloud pricing (e.g., us-east-1, us-central1)
329        #[arg(long, value_name = "REGION", default_value = "us-east-1")]
330        region: String,
331
332        /// Run comprehensive analysis (includes kubelint security checks and helmlint validation)
333        #[arg(long, short = 'f')]
334        full: bool,
335
336        /// Output compressed JSON for AI agent consumption (implies --json)
337        #[arg(long)]
338        agent: bool,
339    },
340
341    /// Retrieve stored output from a previous --agent command
342    Retrieve {
343        /// Reference ID (e.g., "security_a1b2c3d4") or "latest" for most recent
344        #[arg(value_name = "REF_ID")]
345        ref_id: Option<String>,
346
347        /// Filter query (e.g., "severity:critical", "file:path", "section:frameworks")
348        #[arg(long, short = 'q')]
349        query: Option<String>,
350
351        /// List all stored outputs
352        #[arg(long)]
353        list: bool,
354
355        /// Maximum number of results to return (default: 20)
356        #[arg(long, short = 'l', default_value = "20")]
357        limit: usize,
358
359        /// Number of results to skip (for pagination)
360        #[arg(long, default_value = "0")]
361        offset: usize,
362    },
363
364    /// Start an interactive AI chat session to analyze and understand your project
365    Chat {
366        /// Path to the project directory (default: current directory)
367        #[arg(value_name = "PROJECT_PATH", default_value = ".")]
368        path: PathBuf,
369
370        /// LLM provider to use (uses saved preference by default)
371        #[arg(long, value_enum, default_value = "auto")]
372        provider: ChatProvider,
373
374        /// Model to use (e.g., gpt-4o, claude-3-5-sonnet-latest, llama3.2)
375        #[arg(long)]
376        model: Option<String>,
377
378        /// Run a single query instead of interactive mode
379        #[arg(long)]
380        query: Option<String>,
381
382        /// Resume a previous session (accepts: "latest", session number, or UUID)
383        #[arg(long, short = 'r')]
384        resume: Option<String>,
385
386        /// List available sessions for this project and exit
387        #[arg(long)]
388        list_sessions: bool,
389
390        /// Start AG-UI server for frontend connectivity (SSE/WebSocket)
391        #[arg(long)]
392        ag_ui: bool,
393
394        /// AG-UI server port (default: 9090)
395        #[arg(long, default_value = "9090", requires = "ag_ui")]
396        ag_ui_port: u16,
397    },
398
399    /// Authenticate with the Syncable platform
400    Auth {
401        #[command(subcommand)]
402        command: AuthCommand,
403    },
404
405    /// Manage Syncable projects
406    Project {
407        #[command(subcommand)]
408        command: ProjectCommand,
409    },
410
411    /// Manage Syncable organizations
412    Org {
413        #[command(subcommand)]
414        command: OrgCommand,
415    },
416
417    /// Manage environments within a project
418    Env {
419        #[command(subcommand)]
420        command: EnvCommand,
421    },
422
423    /// Deploy services to the Syncable platform (launches wizard by default)
424    Deploy {
425        /// Path to the project directory (default: current directory)
426        #[arg(value_name = "PROJECT_PATH", default_value = ".")]
427        path: PathBuf,
428
429        #[command(subcommand)]
430        command: Option<DeployCommand>,
431    },
432
433    /// Run as dedicated AG-UI agent server (headless mode for containers)
434    Agent {
435        /// Path to the project directory
436        #[arg(value_name = "PROJECT_PATH", default_value = ".")]
437        path: PathBuf,
438
439        /// Port for AG-UI server
440        #[arg(long, short, default_value = "9090")]
441        port: u16,
442
443        /// Host address to bind to
444        #[arg(long, default_value = "127.0.0.1")]
445        host: String,
446
447        /// LLM provider to use
448        #[arg(long, value_enum, default_value = "auto")]
449        provider: ChatProvider,
450
451        /// Model to use
452        #[arg(long)]
453        model: Option<String>,
454    },
455}
456
457#[derive(Subcommand)]
458pub enum ToolsCommand {
459    /// Check which vulnerability scanning tools are installed
460    Status {
461        /// Output format
462        #[arg(long, value_enum, default_value = "table")]
463        format: OutputFormat,
464
465        /// Check tools for specific languages only
466        #[arg(long, value_delimiter = ',')]
467        languages: Option<Vec<String>>,
468    },
469
470    /// Install missing vulnerability scanning tools
471    Install {
472        /// Install tools for specific languages only
473        #[arg(long, value_delimiter = ',')]
474        languages: Option<Vec<String>>,
475
476        /// Also install OWASP Dependency Check (large download)
477        #[arg(long)]
478        include_owasp: bool,
479
480        /// Perform a dry run to show what would be installed
481        #[arg(long)]
482        dry_run: bool,
483
484        /// Skip confirmation prompts
485        #[arg(short, long)]
486        yes: bool,
487    },
488
489    /// Verify that installed tools are working correctly
490    Verify {
491        /// Test tools for specific languages only
492        #[arg(long, value_delimiter = ',')]
493        languages: Option<Vec<String>>,
494
495        /// Show detailed verification output
496        #[arg(short, long)]
497        detailed: bool,
498    },
499
500    /// Show tool installation guides for manual setup
501    Guide {
502        /// Show guide for specific languages only
503        #[arg(long, value_delimiter = ',')]
504        languages: Option<Vec<String>>,
505
506        /// Show platform-specific instructions
507        #[arg(long)]
508        platform: Option<String>,
509    },
510}
511
512/// Authentication subcommands for the Syncable platform
513#[derive(Subcommand)]
514pub enum AuthCommand {
515    /// Log in to Syncable (opens browser for authentication)
516    Login {
517        /// Don't open browser automatically
518        #[arg(long)]
519        no_browser: bool,
520    },
521
522    /// Log out and clear stored credentials
523    Logout,
524
525    /// Show current authentication status
526    Status,
527
528    /// Print current access token (for scripting)
529    Token {
530        /// Print raw token without formatting
531        #[arg(long)]
532        raw: bool,
533    },
534}
535
536/// Project management subcommands
537#[derive(Subcommand)]
538pub enum ProjectCommand {
539    /// List projects in the current organization
540    List {
541        /// Organization ID to list projects from (uses current org if not specified)
542        #[arg(long)]
543        org_id: Option<String>,
544
545        /// Output format
546        #[arg(long, value_enum, default_value = "table")]
547        format: OutputFormat,
548    },
549
550    /// Select a project to work with
551    Select {
552        /// Project ID to select
553        id: String,
554    },
555
556    /// Show current organization and project context
557    Current,
558
559    /// Show details of a project
560    Info {
561        /// Project ID (uses current project if not specified)
562        id: Option<String>,
563    },
564}
565
566/// Organization management subcommands
567#[derive(Subcommand)]
568pub enum OrgCommand {
569    /// List organizations you belong to
570    List {
571        /// Output format
572        #[arg(long, value_enum, default_value = "table")]
573        format: OutputFormat,
574    },
575
576    /// Select an organization to work with
577    Select {
578        /// Organization ID to select
579        id: String,
580    },
581}
582
583/// Environment management subcommands
584#[derive(Subcommand)]
585pub enum EnvCommand {
586    /// List environments in the current project
587    List {
588        /// Output format
589        #[arg(long, value_enum, default_value = "table")]
590        format: OutputFormat,
591    },
592
593    /// Select an environment to work with
594    Select {
595        /// Environment ID to select
596        id: String,
597    },
598}
599
600/// Deployment subcommands
601#[derive(Subcommand)]
602pub enum DeployCommand {
603    /// Launch interactive deployment wizard
604    Wizard {
605        /// Path to the project directory (default: current directory)
606        #[arg(value_name = "PROJECT_PATH", default_value = ".")]
607        path: PathBuf,
608    },
609
610    /// Create a new environment for the current project
611    NewEnv,
612
613    /// Check deployment status
614    Status {
615        /// The deployment task ID (from deploy command output)
616        task_id: String,
617
618        /// Watch for status updates (poll until complete)
619        #[arg(short, long)]
620        watch: bool,
621    },
622
623    /// Preview deployment recommendation (non-interactive, JSON output for agents)
624    Preview {
625        /// Path to project or service subdirectory
626        #[arg(value_name = "PATH", default_value = ".")]
627        path: PathBuf,
628
629        /// Override service name (default: derived from directory name)
630        #[arg(long)]
631        service_name: Option<String>,
632
633        /// Override cloud provider (gcp, hetzner, azure)
634        #[arg(long)]
635        provider: Option<String>,
636
637        /// Override region
638        #[arg(long)]
639        region: Option<String>,
640
641        /// Override machine type
642        #[arg(long)]
643        machine_type: Option<String>,
644
645        /// Override detected port
646        #[arg(long)]
647        port: Option<u16>,
648
649        /// Make service publicly accessible
650        #[arg(long)]
651        public: bool,
652    },
653
654    /// Deploy a service non-interactively (for agents and CI/CD)
655    Run {
656        /// Path to project or service subdirectory
657        #[arg(value_name = "PATH", default_value = ".")]
658        path: PathBuf,
659
660        /// Override service name (default: derived from directory name)
661        #[arg(long)]
662        service_name: Option<String>,
663
664        /// Cloud provider (gcp, hetzner, azure)
665        #[arg(long)]
666        provider: Option<String>,
667
668        /// Region
669        #[arg(long)]
670        region: Option<String>,
671
672        /// Machine type
673        #[arg(long)]
674        machine_type: Option<String>,
675
676        /// Port to expose
677        #[arg(long)]
678        port: Option<u16>,
679
680        /// Make service publicly accessible
681        #[arg(long)]
682        public: bool,
683
684        /// CPU allocation (for GCP/Azure, e.g. "1000m", "2")
685        #[arg(long)]
686        cpu: Option<String>,
687
688        /// Memory allocation (for GCP/Azure, e.g. "512Mi", "2Gi")
689        #[arg(long)]
690        memory: Option<String>,
691
692        /// Min instances/replicas
693        #[arg(long)]
694        min_instances: Option<i32>,
695
696        /// Max instances/replicas
697        #[arg(long)]
698        max_instances: Option<i32>,
699
700        /// Environment variable as KEY=VALUE (non-secret, repeatable)
701        #[arg(long = "env", value_name = "KEY=VALUE")]
702        env_vars: Vec<String>,
703
704        /// Secret key name (user prompted in terminal for value, repeatable)
705        #[arg(long = "secret")]
706        secrets: Vec<String>,
707
708        /// Load environment variables from a .env file
709        #[arg(long)]
710        env_file: Option<PathBuf>,
711    },
712}
713
714#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum)]
715pub enum OutputFormat {
716    Table,
717    Json,
718}
719
720#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum)]
721pub enum DisplayFormat {
722    /// Compact matrix/dashboard view (modern, easy to scan)
723    Matrix,
724    /// Detailed vertical view (legacy format with all details)
725    Detailed,
726    /// Brief summary only
727    Summary,
728}
729
730#[derive(Clone, Copy, Debug, ValueEnum)]
731pub enum ColorScheme {
732    /// Auto-detect terminal background (default)
733    Auto,
734    /// Dark background terminal colors
735    Dark,
736    /// Light background terminal colors
737    Light,
738}
739
740#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum)]
741pub enum SeverityThreshold {
742    Low,
743    Medium,
744    High,
745    Critical,
746}
747
748#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum)]
749pub enum SecurityScanMode {
750    /// Lightning fast scan - critical files only (.env, configs)
751    Lightning,
752    /// Fast scan - smart sampling with priority patterns
753    Fast,
754    /// Balanced scan - good coverage with performance optimizations (recommended)
755    Balanced,
756    /// Thorough scan - comprehensive analysis of all files
757    Thorough,
758    /// Paranoid scan - most comprehensive including low-severity findings
759    Paranoid,
760}
761
762#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum, Default)]
763pub enum ChatProvider {
764    /// OpenAI (GPT-4o, GPT-4, etc.)
765    Openai,
766    /// Anthropic (Claude 3)
767    Anthropic,
768    /// AWS Bedrock (Claude via AWS)
769    Bedrock,
770    /// Ollama (local LLM, no API key needed)
771    Ollama,
772    /// Use saved default from config file
773    #[default]
774    Auto,
775}
776
777impl Cli {
778    /// Initialize logging based on verbosity level
779    pub fn init_logging(&self) {
780        if self.quiet {
781            return;
782        }
783
784        let level = match self.verbose {
785            0 => log::LevelFilter::Warn,
786            1 => log::LevelFilter::Info,
787            2 => log::LevelFilter::Debug,
788            _ => log::LevelFilter::Trace,
789        };
790
791        env_logger::Builder::from_default_env()
792            .filter_level(level)
793            .init();
794    }
795}