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