1pub mod commands;
2pub mod mcp;
3pub mod output;
4pub mod welcome;
5
6use clap::{Parser, Subcommand};
7
8#[derive(Parser)]
9#[command(
10 name = "chub",
11 version,
12 about = "chub — agent-agnostic context, tracking, and cost analytics for AI-assisted development"
13)]
14struct Cli {
15 #[arg(long, global = true)]
17 json: bool,
18
19 #[command(subcommand)]
20 command: Commands,
21}
22
23#[derive(Subcommand)]
24enum Commands {
25 Build(commands::build::BuildArgs),
27 Search(commands::search::SearchArgs),
29 List(commands::search::SearchArgs),
31 Get(commands::get::GetArgs),
33 Update(commands::update::UpdateArgs),
35 Cache(commands::cache::CacheArgs),
37 Annotate(commands::annotate::AnnotateArgs),
39 Feedback(commands::feedback::FeedbackArgs),
41 Mcp,
43
44 Init(commands::init::InitArgs),
47 Pin(commands::pin::PinArgs),
49 Profile(commands::profile::ProfileArgs),
51 Detect(commands::detect::DetectArgs),
53 AgentConfig(commands::agent_config::AgentConfigArgs),
55 Check(commands::check::CheckArgs),
57 Context(commands::context_cmd::ContextArgs),
59 Stats(commands::stats::StatsArgs),
61 Serve(commands::serve::ServeArgs),
63 Bundle(commands::bundle::BundleArgs),
65 Snapshot(commands::snapshot::SnapshotArgs),
67 Telemetry(commands::telemetry_cmd::TelemetryArgs),
69 Track(commands::track::TrackArgs),
71}
72
73pub async fn run() {
74 let cli = Cli::parse();
75
76 if matches!(cli.command, Commands::Mcp) {
78 if let Err(e) = mcp::server::run_mcp_server().await {
79 eprintln!("[chub-mcp] Fatal: {}", e);
80 std::process::exit(1);
81 }
82 return;
83 }
84
85 welcome::show_welcome_if_needed(cli.json);
86
87 match cli.command {
89 Commands::Build(args) => {
90 if let Err(e) = commands::build::run(args, cli.json) {
91 output::error(&e.to_string(), cli.json);
92 std::process::exit(1);
93 }
94 return;
95 }
96 Commands::Update(args) => {
97 if let Err(e) = commands::update::run(args, cli.json).await {
98 output::error(&e.to_string(), cli.json);
99 std::process::exit(1);
100 }
101 return;
102 }
103 Commands::Cache(args) => {
104 commands::cache::run(args, cli.json);
105 return;
106 }
107 Commands::Annotate(args) => {
108 commands::annotate::run(args, cli.json).await;
109 return;
110 }
111 Commands::Init(args) => {
112 commands::init::run(args, cli.json);
113 return;
114 }
115 Commands::Pin(args) => {
116 commands::pin::run(args, cli.json);
117 return;
118 }
119 Commands::Profile(args) => {
120 commands::profile::run(args, cli.json);
121 return;
122 }
123 Commands::AgentConfig(args) => {
124 commands::agent_config::run(args, cli.json);
125 return;
126 }
127 Commands::Check(args) => {
128 commands::check::run(args, cli.json);
129 return;
130 }
131 Commands::Context(args) => {
132 commands::context_cmd::run(args, cli.json);
133 return;
134 }
135 Commands::Stats(args) => {
136 commands::stats::run(args, cli.json);
137 return;
138 }
139 Commands::Serve(args) => {
140 if let Err(e) = commands::serve::run(args, cli.json).await {
141 output::error(&e.to_string(), cli.json);
142 std::process::exit(1);
143 }
144 return;
145 }
146 Commands::Bundle(args) => {
147 commands::bundle::run(args, cli.json);
148 return;
149 }
150 Commands::Snapshot(args) => {
151 commands::snapshot::run(args, cli.json);
152 return;
153 }
154 Commands::Telemetry(args) => {
155 commands::telemetry_cmd::run(args, cli.json);
156 return;
157 }
158 Commands::Track(args) => {
159 commands::track::run(args, cli.json).await;
160 return;
161 }
162 _ => {}
163 }
164
165 if let Err(e) = chub_core::fetch::ensure_registry().await {
167 output::error(
168 &format!(
169 "Registry not available: {}. Run `chub update` to refresh.",
170 e
171 ),
172 cli.json,
173 );
174 std::process::exit(1);
175 }
176
177 let merged = chub_core::registry::load_merged();
178
179 match cli.command {
180 Commands::Search(args) | Commands::List(args) => {
181 commands::search::run(args, cli.json, &merged);
182 }
183 Commands::Get(args) => {
184 if let Err(e) = commands::get::run(args, cli.json, &merged).await {
185 output::error(&e.to_string(), cli.json);
186 std::process::exit(1);
187 }
188 }
189 Commands::Feedback(args) => {
190 commands::feedback::run(args, cli.json, Some(&merged)).await;
191 }
192 Commands::Detect(args) => {
193 commands::detect::run(args, cli.json, &merged);
194 }
195 Commands::Build(_)
197 | Commands::Update(_)
198 | Commands::Cache(_)
199 | Commands::Annotate(_)
200 | Commands::Init(_)
201 | Commands::Pin(_)
202 | Commands::Profile(_)
203 | Commands::AgentConfig(_)
204 | Commands::Check(_)
205 | Commands::Context(_)
206 | Commands::Stats(_)
207 | Commands::Serve(_)
208 | Commands::Bundle(_)
209 | Commands::Snapshot(_)
210 | Commands::Telemetry(_)
211 | Commands::Track(_)
212 | Commands::Mcp => unreachable!(),
213 }
214}