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