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 = "DevOps CLI toolbox for AI coding agents and developers")]
8#[command(
9 long_about = "Analyze tech stacks, scan for security issues and CVEs, validate IaC files, optimize Kubernetes resources, and deploy to cloud providers. Works standalone or through AI coding agent skills (Claude Code, Codex, Gemini CLI, Cursor, Windsurf)."
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 /// [DEPRECATED] Start an interactive AI chat session. Use AI coding agent skills instead.
365 #[command(hide = true)]
366 Chat {
367 /// Path to the project directory (default: current directory)
368 #[arg(value_name = "PROJECT_PATH", default_value = ".")]
369 path: PathBuf,
370
371 /// LLM provider to use (uses saved preference by default)
372 #[arg(long, value_enum, default_value = "auto")]
373 provider: ChatProvider,
374
375 /// Model to use (e.g., gpt-4o, claude-3-5-sonnet-latest, llama3.2)
376 #[arg(long)]
377 model: Option<String>,
378
379 /// Run a single query instead of interactive mode
380 #[arg(long)]
381 query: Option<String>,
382
383 /// Resume a previous session (accepts: "latest", session number, or UUID)
384 #[arg(long, short = 'r')]
385 resume: Option<String>,
386
387 /// List available sessions for this project and exit
388 #[arg(long)]
389 list_sessions: bool,
390
391 /// Start AG-UI server for frontend connectivity (SSE/WebSocket)
392 #[arg(long)]
393 ag_ui: bool,
394
395 /// AG-UI server port (default: 9090)
396 #[arg(long, default_value = "9090", requires = "ag_ui")]
397 ag_ui_port: u16,
398 },
399
400 /// Authenticate with the Syncable platform
401 Auth {
402 #[command(subcommand)]
403 command: AuthCommand,
404 },
405
406 /// Manage Syncable projects
407 Project {
408 #[command(subcommand)]
409 command: ProjectCommand,
410 },
411
412 /// Manage Syncable organizations
413 Org {
414 #[command(subcommand)]
415 command: OrgCommand,
416 },
417
418 /// Manage environments within a project
419 Env {
420 #[command(subcommand)]
421 command: EnvCommand,
422 },
423
424 /// Deploy services to the Syncable platform (launches wizard by default)
425 Deploy {
426 /// Path to the project directory (default: current directory)
427 #[arg(value_name = "PROJECT_PATH", default_value = ".")]
428 path: PathBuf,
429
430 #[command(subcommand)]
431 command: Option<DeployCommand>,
432 },
433
434 /// [DEPRECATED] Run as dedicated AG-UI agent server. Use AI coding agent skills instead.
435 #[command(hide = true)]
436 Agent {
437 /// Path to the project directory
438 #[arg(value_name = "PROJECT_PATH", default_value = ".")]
439 path: PathBuf,
440
441 /// Port for AG-UI server
442 #[arg(long, short, default_value = "9090")]
443 port: u16,
444
445 /// Host address to bind to
446 #[arg(long, default_value = "127.0.0.1")]
447 host: String,
448
449 /// LLM provider to use
450 #[arg(long, value_enum, default_value = "auto")]
451 provider: ChatProvider,
452
453 /// Model to use
454 #[arg(long)]
455 model: Option<String>,
456 },
457}
458
459#[derive(Subcommand)]
460pub enum ToolsCommand {
461 /// Check which vulnerability scanning tools are installed
462 Status {
463 /// Output format
464 #[arg(long, value_enum, default_value = "table")]
465 format: OutputFormat,
466
467 /// Check tools for specific languages only
468 #[arg(long, value_delimiter = ',')]
469 languages: Option<Vec<String>>,
470 },
471
472 /// Install missing vulnerability scanning tools
473 Install {
474 /// Install tools for specific languages only
475 #[arg(long, value_delimiter = ',')]
476 languages: Option<Vec<String>>,
477
478 /// Also install OWASP Dependency Check (large download)
479 #[arg(long)]
480 include_owasp: bool,
481
482 /// Perform a dry run to show what would be installed
483 #[arg(long)]
484 dry_run: bool,
485
486 /// Skip confirmation prompts
487 #[arg(short, long)]
488 yes: bool,
489 },
490
491 /// Verify that installed tools are working correctly
492 Verify {
493 /// Test tools for specific languages only
494 #[arg(long, value_delimiter = ',')]
495 languages: Option<Vec<String>>,
496
497 /// Show detailed verification output
498 #[arg(short, long)]
499 detailed: bool,
500 },
501
502 /// Show tool installation guides for manual setup
503 Guide {
504 /// Show guide for specific languages only
505 #[arg(long, value_delimiter = ',')]
506 languages: Option<Vec<String>>,
507
508 /// Show platform-specific instructions
509 #[arg(long)]
510 platform: Option<String>,
511 },
512}
513
514/// Authentication subcommands for the Syncable platform
515#[derive(Subcommand)]
516pub enum AuthCommand {
517 /// Log in to Syncable (opens browser for authentication)
518 Login {
519 /// Don't open browser automatically
520 #[arg(long)]
521 no_browser: bool,
522 },
523
524 /// Log out and clear stored credentials
525 Logout,
526
527 /// Show current authentication status
528 Status,
529
530 /// Print current access token (for scripting)
531 Token {
532 /// Print raw token without formatting
533 #[arg(long)]
534 raw: bool,
535 },
536}
537
538/// Project management subcommands
539#[derive(Subcommand)]
540pub enum ProjectCommand {
541 /// List projects in the current organization
542 List {
543 /// Organization ID to list projects from (uses current org if not specified)
544 #[arg(long)]
545 org_id: Option<String>,
546
547 /// Output format
548 #[arg(long, value_enum, default_value = "table")]
549 format: OutputFormat,
550 },
551
552 /// Select a project to work with
553 Select {
554 /// Project ID to select
555 id: String,
556 },
557
558 /// Show current organization and project context
559 Current,
560
561 /// Show details of a project
562 Info {
563 /// Project ID (uses current project if not specified)
564 id: Option<String>,
565 },
566}
567
568/// Organization management subcommands
569#[derive(Subcommand)]
570pub enum OrgCommand {
571 /// List organizations you belong to
572 List {
573 /// Output format
574 #[arg(long, value_enum, default_value = "table")]
575 format: OutputFormat,
576 },
577
578 /// Select an organization to work with
579 Select {
580 /// Organization ID to select
581 id: String,
582 },
583}
584
585/// Environment management subcommands
586#[derive(Subcommand)]
587pub enum EnvCommand {
588 /// List environments in the current project
589 List {
590 /// Output format
591 #[arg(long, value_enum, default_value = "table")]
592 format: OutputFormat,
593 },
594
595 /// Select an environment to work with
596 Select {
597 /// Environment ID to select
598 id: String,
599 },
600}
601
602/// Deployment subcommands
603#[derive(Subcommand)]
604pub enum DeployCommand {
605 /// Launch interactive deployment wizard
606 Wizard {
607 /// Path to the project directory (default: current directory)
608 #[arg(value_name = "PROJECT_PATH", default_value = ".")]
609 path: PathBuf,
610 },
611
612 /// Create a new environment for the current project
613 NewEnv,
614
615 /// Check deployment status
616 Status {
617 /// The deployment task ID (from deploy command output)
618 task_id: String,
619
620 /// Watch for status updates (poll until complete)
621 #[arg(short, long)]
622 watch: bool,
623 },
624
625 /// Preview deployment recommendation (non-interactive, JSON output for agents)
626 Preview {
627 /// Path to project or service subdirectory
628 #[arg(value_name = "PATH", default_value = ".")]
629 path: PathBuf,
630
631 /// Override service name (default: derived from directory name)
632 #[arg(long)]
633 service_name: Option<String>,
634
635 /// Override cloud provider (gcp, hetzner, azure)
636 #[arg(long)]
637 provider: Option<String>,
638
639 /// Override region
640 #[arg(long)]
641 region: Option<String>,
642
643 /// Override machine type
644 #[arg(long)]
645 machine_type: Option<String>,
646
647 /// Override detected port
648 #[arg(long)]
649 port: Option<u16>,
650
651 /// Make service publicly accessible
652 #[arg(long)]
653 public: bool,
654 },
655
656 /// Deploy a service non-interactively (for agents and CI/CD)
657 Run {
658 /// Path to project or service subdirectory
659 #[arg(value_name = "PATH", default_value = ".")]
660 path: PathBuf,
661
662 /// Override service name (default: derived from directory name)
663 #[arg(long)]
664 service_name: Option<String>,
665
666 /// Cloud provider (gcp, hetzner, azure)
667 #[arg(long)]
668 provider: Option<String>,
669
670 /// Region
671 #[arg(long)]
672 region: Option<String>,
673
674 /// Machine type
675 #[arg(long)]
676 machine_type: Option<String>,
677
678 /// Port to expose
679 #[arg(long)]
680 port: Option<u16>,
681
682 /// Make service publicly accessible
683 #[arg(long)]
684 public: bool,
685
686 /// CPU allocation (for GCP/Azure, e.g. "1000m", "2")
687 #[arg(long)]
688 cpu: Option<String>,
689
690 /// Memory allocation (for GCP/Azure, e.g. "512Mi", "2Gi")
691 #[arg(long)]
692 memory: Option<String>,
693
694 /// Min instances/replicas
695 #[arg(long)]
696 min_instances: Option<i32>,
697
698 /// Max instances/replicas
699 #[arg(long)]
700 max_instances: Option<i32>,
701
702 /// Environment variable as KEY=VALUE (non-secret, repeatable)
703 #[arg(long = "env", value_name = "KEY=VALUE")]
704 env_vars: Vec<String>,
705
706 /// Secret key name (user prompted in terminal for value, repeatable)
707 #[arg(long = "secret")]
708 secrets: Vec<String>,
709
710 /// Load environment variables from a .env file
711 #[arg(long)]
712 env_file: Option<PathBuf>,
713 },
714}
715
716#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum)]
717pub enum OutputFormat {
718 Table,
719 Json,
720}
721
722#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum)]
723pub enum DisplayFormat {
724 /// Compact matrix/dashboard view (modern, easy to scan)
725 Matrix,
726 /// Detailed vertical view (legacy format with all details)
727 Detailed,
728 /// Brief summary only
729 Summary,
730}
731
732#[derive(Clone, Copy, Debug, ValueEnum)]
733pub enum ColorScheme {
734 /// Auto-detect terminal background (default)
735 Auto,
736 /// Dark background terminal colors
737 Dark,
738 /// Light background terminal colors
739 Light,
740}
741
742#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum)]
743pub enum SeverityThreshold {
744 Low,
745 Medium,
746 High,
747 Critical,
748}
749
750#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum)]
751pub enum SecurityScanMode {
752 /// Lightning fast scan - critical files only (.env, configs)
753 Lightning,
754 /// Fast scan - smart sampling with priority patterns
755 Fast,
756 /// Balanced scan - good coverage with performance optimizations (recommended)
757 Balanced,
758 /// Thorough scan - comprehensive analysis of all files
759 Thorough,
760 /// Paranoid scan - most comprehensive including low-severity findings
761 Paranoid,
762}
763
764#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum, Default)]
765pub enum ChatProvider {
766 /// OpenAI (GPT-4o, GPT-4, etc.)
767 Openai,
768 /// Anthropic (Claude 3)
769 Anthropic,
770 /// AWS Bedrock (Claude via AWS)
771 Bedrock,
772 /// Ollama (local LLM, no API key needed)
773 Ollama,
774 /// Use saved default from config file
775 #[default]
776 Auto,
777}
778
779impl Cli {
780 /// Initialize logging based on verbosity level
781 pub fn init_logging(&self) {
782 if self.quiet {
783 return;
784 }
785
786 let level = match self.verbose {
787 0 => log::LevelFilter::Warn,
788 1 => log::LevelFilter::Info,
789 2 => log::LevelFilter::Debug,
790 _ => log::LevelFilter::Trace,
791 };
792
793 env_logger::Builder::from_default_env()
794 .filter_level(level)
795 .init();
796 }
797}