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(".lean-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: lean-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 "stats" => {
617 super::cmd_stats(&rest);
618 return;
619 }
620 "cache" => {
621 super::cmd_cache(&rest);
622 return;
623 }
624 "theme" => {
625 super::cmd_theme(&rest);
626 return;
627 }
628 "tee" => {
629 super::cmd_tee(&rest);
630 return;
631 }
632 "terse" => {
633 super::cmd_terse(&rest);
634 return;
635 }
636 "slow-log" => {
637 super::cmd_slow_log(&rest);
638 return;
639 }
640 "update" | "--self-update" => {
641 core::updater::run(&rest);
642 return;
643 }
644 "doctor" => {
645 let code = doctor::run_cli(&rest);
646 if code != 0 {
647 std::process::exit(code);
648 }
649 return;
650 }
651 "gotchas" | "bugs" => {
652 super::cloud::cmd_gotchas(&rest);
653 return;
654 }
655 "buddy" | "pet" => {
656 super::cloud::cmd_buddy(&rest);
657 return;
658 }
659 "hook" => {
660 let action = rest.first().map(|s| s.as_str()).unwrap_or("help");
661 match action {
662 "rewrite" => hook_handlers::handle_rewrite(),
663 "redirect" => hook_handlers::handle_redirect(),
664 "copilot" => hook_handlers::handle_copilot(),
665 "codex-pretooluse" => hook_handlers::handle_codex_pretooluse(),
666 "codex-session-start" => hook_handlers::handle_codex_session_start(),
667 "rewrite-inline" => hook_handlers::handle_rewrite_inline(),
668 _ => {
669 eprintln!("Usage: nebu-ctx hook <rewrite|redirect|copilot|codex-pretooluse|codex-session-start|rewrite-inline>");
670 eprintln!(" Internal commands used by agent hooks (Claude, Cursor, Copilot, etc.)");
671 std::process::exit(1);
672 }
673 }
674 return;
675 }
676 "report-issue" | "report" => {
677 report::run(&rest);
678 return;
679 }
680 "uninstall" => {
681 uninstall::run();
682 return;
683 }
684 "cheat" | "cheatsheet" | "cheat-sheet" => {
685 super::cmd_cheatsheet();
686 return;
687 }
688 "login" => {
689 super::cloud::cmd_login(&rest);
690 return;
691 }
692 "register" => {
693 super::cloud::cmd_register(&rest);
694 return;
695 }
696 "forgot-password" => {
697 super::cloud::cmd_forgot_password(&rest);
698 return;
699 }
700 "sync" => {
701 super::cloud::cmd_sync();
702 return;
703 }
704 "contribute" => {
705 super::cloud::cmd_contribute();
706 return;
707 }
708 "cloud" | "server" => {
709 super::cloud::cmd_cloud(&rest);
710 return;
711 }
712 "upgrade" => {
713 super::cloud::cmd_upgrade();
714 return;
715 }
716 "--version" | "-V" => {
717 println!("{}", core::integrity::origin_line());
718 return;
719 }
720 "--help" | "-h" => {
721 print_help();
722 return;
723 }
724 "mcp" => {}
725 _ => {
726 eprintln!("nebu-ctx: unknown command '{}'\n", args[1]);
727 print_help();
728 std::process::exit(1);
729 }
730 }
731 }
732
733 if let Err(e) = run_mcp_server() {
734 eprintln!("nebu-ctx: {e}");
735 std::process::exit(1);
736 }
737}
738
739fn passthrough(command: &str) -> ! {
740 let (shell, flag) = shell::shell_and_flag();
741 let status = std::process::Command::new(&shell)
742 .arg(&flag)
743 .arg(command)
744 .env("NEBU_CTX_ACTIVE", "1")
745 .status()
746 .map(|s| s.code().unwrap_or(1))
747 .unwrap_or(127);
748 std::process::exit(status);
749}
750
751fn run_async<F: std::future::Future>(future: F) -> F::Output {
752 tokio::runtime::Runtime::new()
753 .expect("failed to create async runtime")
754 .block_on(future)
755}
756
757fn run_mcp_server() -> Result<()> {
758 use rmcp::ServiceExt;
759 use tracing_subscriber::EnvFilter;
760
761 std::env::set_var("NEBU_CTX_MCP_SERVER", "1");
762
763 let rt = tokio::runtime::Runtime::new()?;
764 rt.block_on(async {
765 tracing_subscriber::fmt()
766 .with_env_filter(EnvFilter::from_default_env())
767 .with_writer(std::io::stderr)
768 .init();
769
770 tracing::info!(
771 "nebu-ctx v{} MCP server starting",
772 env!("CARGO_PKG_VERSION")
773 );
774
775 let server = tools::create_server();
776 let transport =
777 mcp_stdio::HybridStdioTransport::new_server(tokio::io::stdin(), tokio::io::stdout());
778 let service = server.serve(transport).await?;
779 service.waiting().await?;
780
781 core::stats::flush();
782 core::mode_predictor::ModePredictor::flush();
783 core::feedback::FeedbackStore::flush();
784
785 Ok(())
786 })
787}
788
789fn print_help() {
790 println!(
791 "nebu-ctx {version} — Context Runtime for AI Agents
792
79390+ compression patterns | 46 MCP tools | Context Continuity Protocol
794
795USAGE:
796 nebu-ctx Start MCP server (stdio)
797 nebu-ctx serve Start MCP server (Streamable HTTP)
798 nebu-ctx -t \"command\" Track command (full output + stats, no compression)
799 nebu-ctx -c \"command\" Execute with compressed output (used by AI hooks)
800 nebu-ctx -c --raw \"command\" Execute without compression (full output)
801 nebu-ctx exec \"command\" Same as -c
802 nebu-ctx shell Interactive shell with compression
803
804COMMANDS:
805 token-report [--json] Token + memory report (project + session + CEP)
806 gain|cep|watch|dashboard|heatmap|stats Cloud-only analytics surfaces (not served locally)
807 serve [--host H] [--port N] MCP over HTTP (Streamable HTTP, local-first)
808 proxy start [--port=4444] API proxy: compress tool_results before LLM API
809 proxy status Show proxy statistics
810 cache [list|clear|stats] Show/manage file read cache
811 wrapped [--week|--month|--all] Savings report card (shareable)
812 sessions [list|show|cleanup] Manage CCP sessions (~/.lean-ctx/sessions/)
813 benchmark run [path] [--json] Run real benchmark on project files
814 benchmark report [path] Generate shareable Markdown report
815 cheatsheet Command cheat sheet & workflow quick reference
816 setup One-command setup: shell + editor + verify
817 bootstrap Non-interactive setup + fix (zero-config)
818 status [--json] Show setup + MCP + rules status
819 init [--global] Install shell aliases (zsh/bash/fish/PowerShell)
820 init --agent <name> Configure MCP for specific editor/agent
821 read <file> [-m mode] Read file with compression
822 diff <file1> <file2> Compressed file diff
823 grep <pattern> [path] Search with compressed output
824 find <pattern> [path] Find files with compressed output
825 ls [path] Directory listing with compression
826 deps [path] Show project dependencies
827 discover Find uncompressed commands in shell history
828 filter [list|validate|init] Manage custom compression filters (~/.lean-ctx/filters/)
829 session Show adoption statistics
830 config Show/edit configuration (~/.nebu-ctx/config.toml)
831 theme [list|set|export|import] Customize terminal colors and themes
832 tee [list|clear|show <file>|last] Manage output tee files (~/.lean-ctx/tee/)
833 terse [off|lite|full|ultra] Set agent output verbosity (saves 25-65% output tokens)
834 slow-log [list|clear] Show/clear slow command log (~/.lean-ctx/slow-commands.log)
835 update [--check] Self-update nebu-ctx binary from GitHub Releases
836 gotchas [list|clear|export|stats] Bug Memory: view/manage auto-detected error patterns
837 buddy [show|stats|ascii|json] Token Guardian: your data-driven coding companion
838 doctor [--fix] [--json] Run diagnostics (and optionally repair)
839 uninstall Remove shell hook, MCP configs, and data directory
840
841SHELL HOOK PATTERNS (90+):
842 git status, log, diff, add, commit, push, pull, fetch, clone,
843 branch, checkout, switch, merge, stash, tag, reset, remote
844 docker build, ps, images, logs, compose, exec, network
845 npm/pnpm install, test, run, list, outdated, audit
846 cargo build, test, check, clippy
847 gh pr list/view/create, issue list/view, run list/view
848 kubectl get pods/services/deployments, logs, describe, apply
849 python pip install/list/outdated, ruff check/format, poetry, uv
850 linters eslint, biome, prettier, golangci-lint
851 builds tsc, next build, vite build
852 ruby rubocop, bundle install/update, rake test, rails test
853 tests jest, vitest, pytest, go test, playwright, rspec, minitest
854 iac terraform, make, maven, gradle, dotnet, flutter, dart
855 utils curl, grep/rg, find, ls, wget, env
856 data JSON schema extraction, log deduplication
857
858READ MODES:
859 auto Auto-select optimal mode (default)
860 full Full content (cached re-reads = 13 tokens)
861 map Dependency graph + API signatures
862 signatures tree-sitter AST extraction (18 languages)
863 task Task-relevant filtering (requires ctx_session task)
864 reference One-line reference stub (cheap cache key)
865 aggressive Syntax-stripped content
866 entropy Shannon entropy filtered
867 diff Changed lines only
868 lines:N-M Specific line ranges (e.g. lines:10-50,80)
869
870ENVIRONMENT:
871 NEBU_CTX_DISABLED=1 Bypass ALL compression + prevent shell hook from loading
872 NEBU_CTX_ENABLED=0 Prevent shell hook auto-start (nebu-ctx-on still works)
873 NEBU_CTX_RAW=1 Same as --raw for current command
874 NEBU_CTX_AUTONOMY=false Disable autonomous features
875 NEBU_CTX_COMPRESS=1 Force compression (even for excluded commands)
876
877OPTIONS:
878 --version, -V Show version
879 --help, -h Show this help
880
881EXAMPLES:
882 nebu-ctx -c \"git status\" Compressed git output
883 nebu-ctx -c \"kubectl get pods\" Compressed k8s output
884 nebu-ctx -c \"gh pr list\" Compressed GitHub CLI output
885 nebu-ctx token-report --json Machine-readable token + memory report
886 nebu-ctx wrapped Weekly savings report card
887 nebu-ctx wrapped --month Monthly savings report card
888 nebu-ctx sessions list List all CCP sessions
889 nebu-ctx sessions show Show latest session state
890 nebu-ctx discover Find missed savings in shell history
891 nebu-ctx setup One-command setup (shell + editors + verify)
892 nebu-ctx bootstrap Non-interactive setup + fix (zero-config)
893 nebu-ctx bootstrap --json Machine-readable bootstrap report
894 nebu-ctx init --global Install shell aliases (includes nebu-ctx-on/off/mode/status)
895 nebu-ctx-on Enable shell aliases in track mode (full output + stats)
896 nebu-ctx-off Disable all shell aliases
897 lean-ctx-mode track Track mode: full output, stats recorded (default)
898 lean-ctx-mode compress Compress mode: all output compressed (power users)
899 lean-ctx-mode off Same as nebu-ctx-off
900 nebu-ctx-status Show whether compression is active
901 lean-ctx init --agent pi Install Pi Coding Agent extension
902 nebu-ctx doctor Check PATH, config, MCP, and local edge health
903 nebu-ctx doctor --fix --json Repair + machine-readable report
904 nebu-ctx status --json Machine-readable current status
905 lean-ctx read src/main.rs -m map
906 lean-ctx grep \"pub fn\" src/
907 lean-ctx deps .
908
909CLOUD:
910 cloud connect [--endpoint <url>] [--token <token>] Save and validate a cloud connection
911 cloud status Show cloud connection status
912 cloud bind Bind the current checkout to a canonical project
913 cloud disconnect Remove the saved cloud connection
914
915TROUBLESHOOTING:
916 Commands broken? nebu-ctx-off (fixes current session)
917 Permanent fix? nebu-ctx uninstall (removes all hooks)
918 Manual fix? Edit ~/.zshrc, remove the \"nebu-ctx shell hook\" block
919 Binary missing? Aliases auto-fallback to original commands (safe)
920 Preview init? nebu-ctx init --global --dry-run
921
922WEBSITE: https://nebu-ctx.com
923GITHUB: https://github.com/MarkBovee/nebu-ctx
924",
925 version = env!("CARGO_PKG_VERSION"),
926 );
927}