chub_cli/commands/
agent_config.rs1use clap::{Args, Subcommand};
2use owo_colors::OwoColorize;
3
4use chub_core::team::agent_config;
5
6use crate::output;
7
8#[derive(Args)]
9pub struct AgentConfigArgs {
10 #[command(subcommand)]
11 command: AgentConfigCommand,
12}
13
14#[derive(Subcommand)]
15pub enum AgentConfigCommand {
16 Generate,
18 Sync,
20 Diff,
22}
23
24pub fn run(args: AgentConfigArgs, json: bool) {
25 match args.command {
26 AgentConfigCommand::Generate | AgentConfigCommand::Sync => run_sync(json),
27 AgentConfigCommand::Diff => run_diff(json),
28 }
29}
30
31fn run_sync(json: bool) {
32 match agent_config::sync_configs() {
33 Ok(results) => {
34 if json {
35 let items: Vec<serde_json::Value> = results
36 .iter()
37 .map(|r| {
38 serde_json::json!({
39 "target": r.target,
40 "filename": r.filename,
41 "action": format!("{:?}", r.action).to_lowercase(),
42 })
43 })
44 .collect();
45 println!(
46 "{}",
47 serde_json::to_string_pretty(&serde_json::json!({
48 "results": items,
49 }))
50 .unwrap_or_default()
51 );
52 } else {
53 if results.is_empty() {
54 eprintln!(
55 "{}",
56 "No targets configured in agent_rules.targets.".yellow()
57 );
58 return;
59 }
60 for r in &results {
61 let action = match r.action {
62 agent_config::SyncAction::Created => "created".green().to_string(),
63 agent_config::SyncAction::Updated => "updated".yellow().to_string(),
64 agent_config::SyncAction::Unchanged => "unchanged".dimmed().to_string(),
65 agent_config::SyncAction::Unknown => {
66 format!(
67 "{} (known: {})",
68 "unknown target".red(),
69 agent_config::Target::all_target_names().join(", ")
70 )
71 }
72 };
73 eprintln!(" {} {}", r.filename.bold(), action);
74 }
75 }
76 }
77 Err(e) => {
78 output::error(&e.to_string(), json);
79 std::process::exit(1);
80 }
81 }
82}
83
84fn run_diff(json: bool) {
85 match agent_config::diff_configs() {
86 Ok(diffs) => {
87 if json {
88 let items: Vec<serde_json::Value> = diffs
89 .iter()
90 .map(|(filename, new_content, existing)| {
91 serde_json::json!({
92 "filename": filename,
93 "exists": existing.is_some(),
94 "would_change": existing.as_ref().map(|e| e != new_content).unwrap_or(true),
95 })
96 })
97 .collect();
98 println!(
99 "{}",
100 serde_json::to_string_pretty(&serde_json::json!({ "diffs": items }))
101 .unwrap_or_default()
102 );
103 } else {
104 if diffs.is_empty() {
105 eprintln!("{}", "No targets configured.".yellow());
106 return;
107 }
108 for (filename, new_content, existing) in &diffs {
109 match existing {
110 None => {
111 eprintln!(" {} {}", filename.bold(), "(new file)".green());
112 for line in new_content.lines().take(10) {
113 eprintln!(" + {}", line.green());
114 }
115 if new_content.lines().count() > 10 {
116 eprintln!(" {} more lines...", "...".dimmed());
117 }
118 }
119 Some(old) if old != new_content => {
120 eprintln!(" {} {}", filename.bold(), "(changed)".yellow());
121 }
122 Some(_) => {
123 eprintln!(" {} {}", filename.bold(), "(unchanged)".dimmed());
124 }
125 }
126 }
127 }
128 }
129 Err(e) => {
130 output::error(&e.to_string(), json);
131 std::process::exit(1);
132 }
133 }
134}