1use crate::{
2 core, doctor, hook_handlers, mcp_stdio, report, setup, shell,
3 status, token_report, tools, uninstall,
4};
5use anyhow::Result;
6
7pub fn run() {
8 let args: Vec<String> = std::env::args().collect();
9
10 if args.len() > 1 {
11 let rest = args[2..].to_vec();
12 let command = args[1].as_str();
13
14 if matches!(command, "gain" | "cep" | "dashboard" | "watch" | "heatmap" | "stats") {
15 super::exit_cloud_analytics_only(command);
16 }
17
18 match command {
19 "-c" | "exec" => {
20 let raw = rest.first().map(|a| a == "--raw").unwrap_or(false);
21 let cmd_args = if raw { &args[3..] } else { &args[2..] };
22 let command = if cmd_args.len() == 1 {
23 cmd_args[0].clone()
24 } else {
25 shell::join_command(cmd_args)
26 };
27 if std::env::var("NEBU_CTX_ACTIVE").is_ok()
28 || std::env::var("NEBU_CTX_DISABLED").is_ok()
29 {
30 passthrough(&command);
31 }
32 if raw {
33 std::env::set_var("NEBU_CTX_RAW", "1");
34 } else {
35 std::env::set_var("NEBU_CTX_COMPRESS", "1");
36 }
37 let code = shell::exec(&command);
38 core::stats::flush();
39 std::process::exit(code);
40 }
41 "-t" | "--track" => {
42 let cmd_args = &args[2..];
43 let command = if cmd_args.len() == 1 {
44 cmd_args[0].clone()
45 } else {
46 shell::join_command(cmd_args)
47 };
48 if std::env::var("NEBU_CTX_ACTIVE").is_ok()
49 || std::env::var("NEBU_CTX_DISABLED").is_ok()
50 {
51 passthrough(&command);
52 }
53 let code = shell::exec(&command);
54 core::stats::flush();
55 std::process::exit(code);
56 }
57 "shell" | "--shell" => {
58 shell::interactive();
59 return;
60 }
61 "gain" => {
62 if rest.iter().any(|a| a == "--reset") {
63 core::stats::reset_all();
64 println!("Stats reset. All token savings data cleared.");
65 return;
66 }
67 if rest.iter().any(|a| a == "--live" || a == "--watch") {
68 core::stats::gain_live();
69 return;
70 }
71 let model = rest.iter().enumerate().find_map(|(i, a)| {
72 if let Some(v) = a.strip_prefix("--model=") {
73 return Some(v.to_string());
74 }
75 if a == "--model" {
76 return rest.get(i + 1).cloned();
77 }
78 None
79 });
80 let period = rest
81 .iter()
82 .enumerate()
83 .find_map(|(i, a)| {
84 if let Some(v) = a.strip_prefix("--period=") {
85 return Some(v.to_string());
86 }
87 if a == "--period" {
88 return rest.get(i + 1).cloned();
89 }
90 None
91 })
92 .unwrap_or_else(|| "all".to_string());
93 let limit = rest
94 .iter()
95 .enumerate()
96 .find_map(|(i, a)| {
97 if let Some(v) = a.strip_prefix("--limit=") {
98 return v.parse::<usize>().ok();
99 }
100 if a == "--limit" {
101 return rest.get(i + 1).and_then(|v| v.parse::<usize>().ok());
102 }
103 None
104 })
105 .unwrap_or(10);
106
107 if rest.iter().any(|a| a == "--graph") {
108 println!("{}", core::stats::format_gain_graph());
109 } else if rest.iter().any(|a| a == "--daily") {
110 println!("{}", core::stats::format_gain_daily());
111 } else if rest.iter().any(|a| a == "--json") {
112 println!(
113 "{}",
114 tools::ctx_gain::handle(
115 "json",
116 Some(&period),
117 model.as_deref(),
118 Some(limit)
119 )
120 );
121 } else if rest.iter().any(|a| a == "--score") {
122 println!(
123 "{}",
124 tools::ctx_gain::handle("score", None, model.as_deref(), Some(limit))
125 );
126 } else if rest.iter().any(|a| a == "--cost") {
127 println!(
128 "{}",
129 tools::ctx_gain::handle("cost", None, model.as_deref(), Some(limit))
130 );
131 } else if rest.iter().any(|a| a == "--tasks") {
132 println!(
133 "{}",
134 tools::ctx_gain::handle("tasks", None, None, Some(limit))
135 );
136 } else if rest.iter().any(|a| a == "--agents") {
137 println!(
138 "{}",
139 tools::ctx_gain::handle("agents", None, None, Some(limit))
140 );
141 } else if rest.iter().any(|a| a == "--heatmap") {
142 println!(
143 "{}",
144 tools::ctx_gain::handle("heatmap", None, None, Some(limit))
145 );
146 } else if rest.iter().any(|a| a == "--wrapped") {
147 println!(
148 "{}",
149 tools::ctx_gain::handle(
150 "wrapped",
151 Some(&period),
152 model.as_deref(),
153 Some(limit)
154 )
155 );
156 } else if rest.iter().any(|a| a == "--pipeline") {
157 let stats_path = dirs::home_dir()
158 .unwrap_or_default()
159 .join(".nebu-ctx")
160 .join("pipeline_stats.json");
161 if let Ok(data) = std::fs::read_to_string(&stats_path) {
162 if let Ok(stats) =
163 serde_json::from_str::<core::pipeline::PipelineStats>(&data)
164 {
165 println!("{}", stats.format_summary());
166 } else {
167 println!("No pipeline stats available yet (corrupt data).");
168 }
169 } else {
170 println!(
171 "No pipeline stats available yet. Use MCP tools to generate data."
172 );
173 }
174 } else if rest.iter().any(|a| a == "--deep") {
175 println!(
176 "{}\n{}\n{}\n{}\n{}",
177 tools::ctx_gain::handle("report", None, model.as_deref(), Some(limit)),
178 tools::ctx_gain::handle("tasks", None, None, Some(limit)),
179 tools::ctx_gain::handle("cost", None, model.as_deref(), Some(limit)),
180 tools::ctx_gain::handle("agents", None, None, Some(limit)),
181 tools::ctx_gain::handle("heatmap", None, None, Some(limit))
182 );
183 } else {
184 println!("{}", core::stats::format_gain());
185 }
186 return;
187 }
188 "token-report" | "report-tokens" => {
189 let code = token_report::run_cli(&rest);
190 if code != 0 {
191 std::process::exit(code);
192 }
193 return;
194 }
195 "cep" => {
196 println!("{}", tools::ctx_gain::handle("score", None, None, Some(10)));
197 return;
198 }
199 "serve" => {
200 #[cfg(feature = "http-server")]
201 {
202 let mut cfg = crate::mcp_http::HttpServerConfig::default();
203 let mut i = 0;
204 while i < rest.len() {
205 match rest[i].as_str() {
206 "--host" | "-H" => {
207 i += 1;
208 if i < rest.len() {
209 cfg.host = rest[i].clone();
210 }
211 }
212 arg if arg.starts_with("--host=") => {
213 cfg.host = arg["--host=".len()..].to_string();
214 }
215 "--port" | "-p" => {
216 i += 1;
217 if i < rest.len() {
218 if let Ok(p) = rest[i].parse::<u16>() {
219 cfg.port = p;
220 }
221 }
222 }
223 arg if arg.starts_with("--port=") => {
224 if let Ok(p) = arg["--port=".len()..].parse::<u16>() {
225 cfg.port = p;
226 }
227 }
228 "--project-root" => {
229 i += 1;
230 if i < rest.len() {
231 cfg.project_root = std::path::PathBuf::from(&rest[i]);
232 }
233 }
234 arg if arg.starts_with("--project-root=") => {
235 cfg.project_root =
236 std::path::PathBuf::from(&arg["--project-root=".len()..]);
237 }
238 "--auth-token" => {
239 i += 1;
240 if i < rest.len() {
241 cfg.auth_token = Some(rest[i].clone());
242 }
243 }
244 arg if arg.starts_with("--auth-token=") => {
245 cfg.auth_token = Some(arg["--auth-token=".len()..].to_string());
246 }
247 "--stateful" => cfg.stateful_mode = true,
248 "--stateless" => cfg.stateful_mode = false,
249 "--json" => cfg.json_response = true,
250 "--sse" => cfg.json_response = false,
251 "--disable-host-check" => cfg.disable_host_check = true,
252 "--allowed-host" => {
253 i += 1;
254 if i < rest.len() {
255 cfg.allowed_hosts.push(rest[i].clone());
256 }
257 }
258 arg if arg.starts_with("--allowed-host=") => {
259 cfg.allowed_hosts
260 .push(arg["--allowed-host=".len()..].to_string());
261 }
262 "--max-body-bytes" => {
263 i += 1;
264 if i < rest.len() {
265 if let Ok(n) = rest[i].parse::<usize>() {
266 cfg.max_body_bytes = n;
267 }
268 }
269 }
270 arg if arg.starts_with("--max-body-bytes=") => {
271 if let Ok(n) = arg["--max-body-bytes=".len()..].parse::<usize>() {
272 cfg.max_body_bytes = n;
273 }
274 }
275 "--max-concurrency" => {
276 i += 1;
277 if i < rest.len() {
278 if let Ok(n) = rest[i].parse::<usize>() {
279 cfg.max_concurrency = n;
280 }
281 }
282 }
283 arg if arg.starts_with("--max-concurrency=") => {
284 if let Ok(n) = arg["--max-concurrency=".len()..].parse::<usize>() {
285 cfg.max_concurrency = n;
286 }
287 }
288 "--max-rps" => {
289 i += 1;
290 if i < rest.len() {
291 if let Ok(n) = rest[i].parse::<u32>() {
292 cfg.max_rps = n;
293 }
294 }
295 }
296 arg if arg.starts_with("--max-rps=") => {
297 if let Ok(n) = arg["--max-rps=".len()..].parse::<u32>() {
298 cfg.max_rps = n;
299 }
300 }
301 "--rate-burst" => {
302 i += 1;
303 if i < rest.len() {
304 if let Ok(n) = rest[i].parse::<u32>() {
305 cfg.rate_burst = n;
306 }
307 }
308 }
309 arg if arg.starts_with("--rate-burst=") => {
310 if let Ok(n) = arg["--rate-burst=".len()..].parse::<u32>() {
311 cfg.rate_burst = n;
312 }
313 }
314 "--request-timeout-ms" => {
315 i += 1;
316 if i < rest.len() {
317 if let Ok(n) = rest[i].parse::<u64>() {
318 cfg.request_timeout_ms = n;
319 }
320 }
321 }
322 arg if arg.starts_with("--request-timeout-ms=") => {
323 if let Ok(n) = arg["--request-timeout-ms=".len()..].parse::<u64>() {
324 cfg.request_timeout_ms = n;
325 }
326 }
327 "--help" | "-h" => {
328 eprintln!(
329 "Usage: nebu-ctx serve [--host H] [--port N] [--project-root DIR]\\n\\
330 \\n\\
331 Options:\\n\\
332 --host, -H Bind host (default: 127.0.0.1)\\n\\
333 --port, -p Bind port (default: 8080)\\n\\
334 --project-root Resolve relative paths against this root (default: cwd)\\n\\
335 --auth-token Require Authorization: Bearer <token> (required for non-loopback binds)\\n\\
336 --stateful/--stateless Streamable HTTP session mode (default: stateless)\\n\\
337 --json/--sse Response framing in stateless mode (default: json)\\n\\
338 --max-body-bytes Max request body size in bytes (default: 2097152)\\n\\
339 --max-concurrency Max concurrent requests (default: 32)\\n\\
340 --max-rps Max requests/sec (global, default: 50)\\n\\
341 --rate-burst Rate limiter burst (global, default: 100)\\n\\
342 --request-timeout-ms REST tool-call timeout (default: 30000)\\n\\
343 --allowed-host Add allowed Host header (repeatable)\\n\\
344 --disable-host-check Disable Host header validation (unsafe)"
345 );
346 return;
347 }
348 _ => {}
349 }
350 i += 1;
351 }
352
353 if cfg.auth_token.is_none() {
354 if let Ok(v) = std::env::var("NEBU_CTX_HTTP_TOKEN") {
355 if !v.trim().is_empty() {
356 cfg.auth_token = Some(v);
357 }
358 }
359 }
360
361 if let Err(e) = run_async(crate::mcp_http::serve(cfg)) {
362 eprintln!("HTTP server error: {e}");
363 std::process::exit(1);
364 }
365 return;
366 }
367 #[cfg(not(feature = "http-server"))]
368 {
369 eprintln!("nebu-ctx serve is not available in this build");
370 std::process::exit(1);
371 }
372 }
373 "proxy" => {
374 #[cfg(feature = "http-server")]
375 {
376 let sub = rest.first().map(|s| s.as_str()).unwrap_or("help");
377 match sub {
378 "start" => {
379 let port: u16 = rest
380 .iter()
381 .find_map(|p| {
382 p.strip_prefix("--port=").or_else(|| p.strip_prefix("-p="))
383 })
384 .and_then(|p| p.parse().ok())
385 .unwrap_or(4444);
386 let autostart = rest.iter().any(|a| a == "--autostart");
387 if autostart {
388 crate::proxy_autostart::install(port, false);
389 return;
390 }
391 if let Err(e) = run_async(crate::llm_proxy::start_proxy(port)) {
392 eprintln!("Proxy error: {e}");
393 std::process::exit(1);
394 }
395 }
396 "stop" => {
397 match ureq::get(&format!(
398 "http://127.0.0.1:{}/health",
399 rest.iter()
400 .find_map(|p| p.strip_prefix("--port="))
401 .and_then(|p| p.parse::<u16>().ok())
402 .unwrap_or(4444)
403 ))
404 .call()
405 {
406 Ok(_) => {
407 println!("Proxy is running. Use Ctrl+C or kill the process.");
408 }
409 Err(_) => {
410 println!("No proxy running on that port.");
411 }
412 }
413 }
414 "status" => {
415 let port: u16 = rest
416 .iter()
417 .find_map(|p| p.strip_prefix("--port="))
418 .and_then(|p| p.parse().ok())
419 .unwrap_or(4444);
420 match ureq::get(&format!("http://127.0.0.1:{port}/status")).call() {
421 Ok(resp) => {
422 let body =
423 resp.into_body().read_to_string().unwrap_or_default();
424 if let Ok(v) = serde_json::from_str::<serde_json::Value>(&body)
425 {
426 println!("nebu-ctx proxy status:");
427 println!(" Requests: {}", v["requests_total"]);
428 println!(" Compressed: {}", v["requests_compressed"]);
429 println!(" Tokens saved: {}", v["tokens_saved"]);
430 println!(
431 " Compression: {}%",
432 v["compression_ratio_pct"].as_str().unwrap_or("0.0")
433 );
434 } else {
435 println!("{body}");
436 }
437 }
438 Err(_) => {
439 println!("No proxy running on port {port}.");
440 println!("Start with: nebu-ctx proxy start");
441 }
442 }
443 }
444 _ => {
445 println!("Usage: nebu-ctx proxy <start|stop|status> [--port=4444]");
446 }
447 }
448 return;
449 }
450 #[cfg(not(feature = "http-server"))]
451 {
452 eprintln!("nebu-ctx proxy is not available in this build");
453 std::process::exit(1);
454 }
455 }
456 "init" => {
457 super::cmd_init(&rest);
458 return;
459 }
460 "setup" => {
461 let non_interactive = rest.iter().any(|a| a == "--non-interactive");
462 let yes = rest.iter().any(|a| a == "--yes" || a == "-y");
463 let fix = rest.iter().any(|a| a == "--fix");
464 let json = rest.iter().any(|a| a == "--json");
465
466 if non_interactive || fix || json || yes {
467 let opts = setup::SetupOptions {
468 non_interactive,
469 yes,
470 fix,
471 json,
472 };
473 match setup::run_setup_with_options(opts) {
474 Ok(report) => {
475 if json {
476 println!(
477 "{}",
478 serde_json::to_string_pretty(&report)
479 .unwrap_or_else(|_| "{}".to_string())
480 );
481 }
482 if !report.success {
483 std::process::exit(1);
484 }
485 }
486 Err(e) => {
487 eprintln!("{e}");
488 std::process::exit(1);
489 }
490 }
491 } else {
492 setup::run_setup();
493 }
494 return;
495 }
496 "bootstrap" => {
497 let json = rest.iter().any(|a| a == "--json");
498 let opts = setup::SetupOptions {
499 non_interactive: true,
500 yes: true,
501 fix: true,
502 json,
503 };
504 match setup::run_setup_with_options(opts) {
505 Ok(report) => {
506 if json {
507 println!(
508 "{}",
509 serde_json::to_string_pretty(&report)
510 .unwrap_or_else(|_| "{}".to_string())
511 );
512 }
513 if !report.success {
514 std::process::exit(1);
515 }
516 }
517 Err(e) => {
518 eprintln!("{e}");
519 std::process::exit(1);
520 }
521 }
522 return;
523 }
524 "status" => {
525 let code = status::run_cli(&rest);
526 if code != 0 {
527 std::process::exit(code);
528 }
529 return;
530 }
531 "read" => {
532 super::cmd_read(&rest);
533 return;
534 }
535 "diff" => {
536 super::cmd_diff(&rest);
537 return;
538 }
539 "grep" => {
540 super::cmd_grep(&rest);
541 return;
542 }
543 "find" => {
544 super::cmd_find(&rest);
545 return;
546 }
547 "ls" => {
548 super::cmd_ls(&rest);
549 return;
550 }
551 "deps" => {
552 super::cmd_deps(&rest);
553 return;
554 }
555 "discover" => {
556 super::cmd_discover(&rest);
557 return;
558 }
559 "filter" => {
560 super::cmd_filter(&rest);
561 return;
562 }
563 "graph" => {
564 let mut action = "build";
565 let mut path_arg: Option<&str> = None;
566 for arg in &rest {
567 if arg == "build" {
568 action = "build";
569 } else {
570 path_arg = Some(arg.as_str());
571 }
572 }
573 let root = path_arg
574 .map(String::from)
575 .or_else(|| {
576 std::env::current_dir()
577 .ok()
578 .map(|p| p.to_string_lossy().to_string())
579 })
580 .unwrap_or_else(|| ".".to_string());
581 match action {
582 "build" => {
583 let index = core::graph_index::load_or_build(&root);
584 println!(
585 "Graph built: {} files, {} edges",
586 index.files.len(),
587 index.edges.len()
588 );
589 }
590 _ => {
591 eprintln!("Usage: nebu-ctx graph [build] [path]");
592 }
593 }
594 return;
595 }
596 "session" => {
597 super::cmd_session();
598 return;
599 }
600 "wrapped" => {
601 super::cmd_wrapped(&rest);
602 return;
603 }
604 "sessions" => {
605 super::cmd_sessions(&rest);
606 return;
607 }
608 "benchmark" => {
609 super::cmd_benchmark(&rest);
610 return;
611 }
612 "config" => {
613 super::cmd_config(&rest);
614 return;
615 }
616 "cache" => {
617 super::cmd_cache(&rest);
618 return;
619 }
620 "theme" => {
621 super::cmd_theme(&rest);
622 return;
623 }
624 "tee" => {
625 super::cmd_tee(&rest);
626 return;
627 }
628 "terse" => {
629 super::cmd_terse(&rest);
630 return;
631 }
632 "slow-log" => {
633 super::cmd_slow_log(&rest);
634 return;
635 }
636
637 "doctor" => {
638 let code = doctor::run_cli(&rest);
639 if code != 0 {
640 std::process::exit(code);
641 }
642 return;
643 }
644 "gotchas" | "bugs" => {
645 super::cloud::cmd_gotchas(&rest);
646 return;
647 }
648 "buddy" | "pet" => {
649 super::cloud::cmd_buddy(&rest);
650 return;
651 }
652 "hook" => {
653 let action = rest.first().map(|s| s.as_str()).unwrap_or("help");
654 match action {
655 "rewrite" => hook_handlers::handle_rewrite(),
656 "redirect" => hook_handlers::handle_redirect(),
657 "copilot" => hook_handlers::handle_copilot(),
658 "codex-pretooluse" => hook_handlers::handle_codex_pretooluse(),
659 "codex-session-start" => hook_handlers::handle_codex_session_start(),
660 "rewrite-inline" => hook_handlers::handle_rewrite_inline(),
661 _ => {
662 eprintln!("Usage: nebu-ctx hook <rewrite|redirect|copilot|codex-pretooluse|codex-session-start|rewrite-inline>");
663 eprintln!(" Internal commands used by agent hooks (Claude, Cursor, Copilot, etc.)");
664 std::process::exit(1);
665 }
666 }
667 return;
668 }
669 "report-issue" | "report" => {
670 report::run(&rest);
671 return;
672 }
673 "uninstall" => {
674 uninstall::run();
675 return;
676 }
677 "cheat" | "cheatsheet" | "cheat-sheet" => {
678 super::cmd_cheatsheet();
679 return;
680 }
681 "connect" => {
682 super::cloud::cmd_connect(&rest);
683 return;
684 }
685 "disconnect" => {
686 super::cloud::cmd_disconnect();
687 return;
688 }
689 "bind" => {
690 super::cloud::cmd_bind();
691 return;
692 }
693 "--version" | "-V" => {
694 println!("nebu-ctx {}", env!("CARGO_PKG_VERSION"));
695 return;
696 }
697 "--help" | "-h" => {
698 print_help();
699 return;
700 }
701 "mcp" => {}
702 _ => {
703 eprintln!("nebu-ctx: unknown command '{}'\n", args[1]);
704 print_help();
705 std::process::exit(1);
706 }
707 }
708 }
709
710 if let Err(e) = run_mcp_server() {
711 eprintln!("nebu-ctx: {e}");
712 std::process::exit(1);
713 }
714}
715
716fn passthrough(command: &str) -> ! {
717 let (shell, flag) = shell::shell_and_flag();
718 let status = std::process::Command::new(&shell)
719 .arg(&flag)
720 .arg(command)
721 .env("NEBU_CTX_ACTIVE", "1")
722 .status()
723 .map(|s| s.code().unwrap_or(1))
724 .unwrap_or(127);
725 std::process::exit(status);
726}
727
728fn run_async<F: std::future::Future>(future: F) -> F::Output {
729 tokio::runtime::Runtime::new()
730 .expect("failed to create async runtime")
731 .block_on(future)
732}
733
734fn run_mcp_server() -> Result<()> {
735 use rmcp::ServiceExt;
736 use tracing_subscriber::EnvFilter;
737
738 std::env::set_var("NEBU_CTX_MCP_SERVER", "1");
739
740 let rt = tokio::runtime::Runtime::new()?;
741 rt.block_on(async {
742 tracing_subscriber::fmt()
743 .with_env_filter(EnvFilter::from_default_env())
744 .with_writer(std::io::stderr)
745 .init();
746
747 tracing::info!(
748 "nebu-ctx v{} MCP server starting",
749 env!("CARGO_PKG_VERSION")
750 );
751
752 let server = tools::create_server();
753 let transport =
754 mcp_stdio::HybridStdioTransport::new_server(tokio::io::stdin(), tokio::io::stdout());
755 let service = server.serve(transport).await?;
756 service.waiting().await?;
757
758 core::stats::flush();
759 core::mode_predictor::ModePredictor::flush();
760 core::feedback::FeedbackStore::flush();
761
762 Ok(())
763 })
764}
765
766fn print_help() {
767 println!(
768 "nebu-ctx {version} — Context Runtime for AI Agents
769
77090+ compression patterns | 46 MCP tools | Context Continuity Protocol
771
772USAGE:
773 nebu-ctx Start MCP server (stdio)
774 nebu-ctx serve Start MCP server (Streamable HTTP)
775 nebu-ctx -t \"command\" Track command (full output + stats, no compression)
776 nebu-ctx -c \"command\" Execute with compressed output (used by AI hooks)
777 nebu-ctx -c --raw \"command\" Execute without compression (full output)
778 nebu-ctx exec \"command\" Same as -c
779 nebu-ctx shell Interactive shell with compression
780
781COMMANDS:
782 token-report [--json] Token + memory report (project + session + CEP)
783 gain|cep|watch|dashboard|heatmap|stats Cloud-only analytics surfaces (not served locally)
784 serve [--host H] [--port N] MCP over HTTP (Streamable HTTP, local-first)
785 proxy start [--port=4444] API proxy: compress tool_results before LLM API
786 proxy status Show proxy statistics
787 cache [list|clear|stats] Show/manage file read cache
788 wrapped [--week|--month|--all] Savings report card (shareable)
789 sessions [list|show|cleanup] Manage CCP sessions (~/.nebu-ctx/sessions/)
790 benchmark run [path] [--json] Run real benchmark on project files
791 benchmark report [path] Generate shareable Markdown report
792 cheatsheet Command cheat sheet & workflow quick reference
793 setup One-command setup: shell + editor + verify
794 bootstrap Non-interactive setup + fix (zero-config)
795 status [--json] Show setup + MCP + rules status
796 init [--global] Install shell aliases (zsh/bash/fish/PowerShell)
797 init --agent <name> Configure MCP for specific editor/agent
798 read <file> [-m mode] Read file with compression
799 diff <file1> <file2> Compressed file diff
800 grep <pattern> [path] Search with compressed output
801 find <pattern> [path] Find files with compressed output
802 ls [path] Directory listing with compression
803 deps [path] Show project dependencies
804 discover Find uncompressed commands in shell history
805 filter [list|validate|init] Manage custom compression filters (~/.nebu-ctx/filters/)
806 session Show adoption statistics
807 config Show/edit configuration (~/.nebu-ctx/config.toml)
808 theme [list|set|export|import] Customize terminal colors and themes
809 tee [list|clear|show <file>|last] Manage output tee files (~/.nebu-ctx/tee/)
810 terse [off|lite|full|ultra] Set agent output verbosity (saves 25-65% output tokens)
811 slow-log [list|clear] Show/clear slow command log (~/.nebu-ctx/slow-commands.log)
812 gotchas [list|clear|export|stats] Bug Memory: view/manage auto-detected error patterns
813 buddy [show|stats|ascii|json] Token Guardian: your data-driven coding companion
814 doctor [--fix] [--json] Run diagnostics (and optionally repair)
815 uninstall Remove shell hook, MCP configs, and data directory
816
817SHELL HOOK PATTERNS (90+):
818 git status, log, diff, add, commit, push, pull, fetch, clone,
819 branch, checkout, switch, merge, stash, tag, reset, remote
820 docker build, ps, images, logs, compose, exec, network
821 npm/pnpm install, test, run, list, outdated, audit
822 cargo build, test, check, clippy
823 gh pr list/view/create, issue list/view, run list/view
824 kubectl get pods/services/deployments, logs, describe, apply
825 python pip install/list/outdated, ruff check/format, poetry, uv
826 linters eslint, biome, prettier, golangci-lint
827 builds tsc, next build, vite build
828 ruby rubocop, bundle install/update, rake test, rails test
829 tests jest, vitest, pytest, go test, playwright, rspec, minitest
830 iac terraform, make, maven, gradle, dotnet, flutter, dart
831 utils curl, grep/rg, find, ls, wget, env
832 data JSON schema extraction, log deduplication
833
834READ MODES:
835 auto Auto-select optimal mode (default)
836 full Full content (cached re-reads = 13 tokens)
837 map Dependency graph + API signatures
838 signatures tree-sitter AST extraction (18 languages)
839 task Task-relevant filtering (requires ctx_session task)
840 reference One-line reference stub (cheap cache key)
841 aggressive Syntax-stripped content
842 entropy Shannon entropy filtered
843 diff Changed lines only
844 lines:N-M Specific line ranges (e.g. lines:10-50,80)
845
846ENVIRONMENT:
847 NEBU_CTX_DISABLED=1 Bypass ALL compression + prevent shell hook from loading
848 NEBU_CTX_ENABLED=0 Prevent shell hook auto-start (nebu-ctx-on still works)
849 NEBU_CTX_RAW=1 Same as --raw for current command
850 NEBU_CTX_AUTONOMY=false Disable autonomous features
851 NEBU_CTX_COMPRESS=1 Force compression (even for excluded commands)
852
853OPTIONS:
854 --version, -V Show version
855 --help, -h Show this help
856
857EXAMPLES:
858 nebu-ctx -c \"git status\" Compressed git output
859 nebu-ctx -c \"kubectl get pods\" Compressed k8s output
860 nebu-ctx -c \"gh pr list\" Compressed GitHub CLI output
861 nebu-ctx token-report --json Machine-readable token + memory report
862 nebu-ctx wrapped Weekly savings report card
863 nebu-ctx wrapped --month Monthly savings report card
864 nebu-ctx sessions list List all CCP sessions
865 nebu-ctx sessions show Show latest session state
866 nebu-ctx discover Find missed savings in shell history
867 nebu-ctx setup One-command setup (shell + editors + verify)
868 nebu-ctx bootstrap Non-interactive setup + fix (zero-config)
869 nebu-ctx bootstrap --json Machine-readable bootstrap report
870 nebu-ctx init --global Install shell aliases (includes nebu-ctx-on/off/mode/status)
871 nebu-ctx-on Enable shell aliases in track mode (full output + stats)
872 nebu-ctx-off Disable all shell aliases
873 nebu-ctx-mode track Track mode: full output, stats recorded (default)
874 nebu-ctx-mode compress Compress mode: all output compressed (power users)
875 nebu-ctx-mode off Same as nebu-ctx-off
876 nebu-ctx-status Show whether compression is active
877 nebu-ctx init --agent pi Install Pi Coding Agent extension
878 nebu-ctx doctor Check PATH, config, MCP, and local edge health
879 nebu-ctx doctor --fix --json Repair + machine-readable report
880 nebu-ctx status --json Machine-readable current status
881 nebu-ctx read src/main.rs -m map
882 nebu-ctx grep \"pub fn\" src/
883 nebu-ctx deps .
884
885CLOUD SERVER:
886 connect [--endpoint <url>] [--token <token>] Save and validate a cloud connection
887 status Includes cloud connection status
888 bind Bind the current checkout to a canonical project
889 disconnect Remove the saved cloud connection
890
891TROUBLESHOOTING:
892 Commands broken? nebu-ctx-off (fixes current session)
893 Permanent fix? nebu-ctx uninstall (removes all hooks)
894 Manual fix? Edit ~/.zshrc, remove the \"nebu-ctx shell hook\" block
895 Binary missing? Aliases auto-fallback to original commands (safe)
896 Preview init? nebu-ctx init --global --dry-run
897
898WEBSITE: https://nebu-ctx.com
899GITHUB: https://github.com/MarkBovee/nebu-ctx
900",
901 version = env!("CARGO_PKG_VERSION"),
902 );
903}