use std::io::Read;
use crate::core::cache::SessionCache;
use crate::core::compress_preview;
use crate::tools::{CrpMode, ctx_compress};
pub(crate) fn cmd_compress(args: &[String]) {
if args.first().map(String::as_str) == Some("diff") {
cmd_compress_diff(&args[1..]);
return;
}
let signatures = args.iter().any(|a| a == "--signatures" || a == "-s");
let json = args.iter().any(|a| a == "--json");
#[cfg(unix)]
{
#[cfg(unix)]
if let Some(out) = crate::daemon_client::try_daemon_tool_call_blocking_text(
"ctx_compress",
Some(serde_json::json!({
"include_signatures": signatures
})),
) {
if json {
let payload = serde_json::json!({ "output": out });
println!(
"{}",
serde_json::to_string_pretty(&payload).unwrap_or_else(|_| out.clone())
);
} else {
println!("{out}");
}
return;
}
}
let cache = build_cli_cache();
let out = ctx_compress::handle(&cache, signatures, CrpMode::Off);
if json {
let payload = serde_json::json!({ "output": out });
println!(
"{}",
serde_json::to_string_pretty(&payload).unwrap_or_else(|_| out.clone())
);
} else {
println!("{out}");
}
}
fn cmd_compress_diff(args: &[String]) {
let json = args.iter().any(|a| a == "--json");
let shell_cmd = flag_value(args, "--shell");
let target = first_positional(args);
let (content, ext) = match target.as_deref() {
None | Some("-") => {
let mut buf = String::new();
if std::io::stdin().read_to_string(&mut buf).is_err() {
eprintln!("compress diff: failed to read stdin");
std::process::exit(1);
}
(buf, None)
}
Some(path) => match std::fs::read_to_string(path) {
Ok(c) => (c, compress_preview::ext_of(path)),
Err(e) => {
eprintln!("compress diff: cannot read {path}: {e}");
std::process::exit(1);
}
},
};
let preview = match shell_cmd {
Some(cmd) => compress_preview::preview_shell(&cmd, &content),
None => compress_preview::preview_read(&content, ext.as_deref()),
};
if json {
let payload = serde_json::json!({
"pipeline": preview.pipeline.label(),
"original_tokens": preview.original_tokens,
"compressed_tokens": preview.compressed_tokens,
"saved_tokens": preview.saved_tokens(),
"saved_pct": preview.saved_pct(),
"original_bytes": preview.original_bytes(),
"compressed_bytes": preview.compressed_bytes(),
"diff": preview.diff(),
});
println!(
"{}",
serde_json::to_string_pretty(&payload).unwrap_or_else(|_| preview.render())
);
} else {
println!("{}", preview.render());
}
}
fn flag_value(args: &[String], flag: &str) -> Option<String> {
let prefix = format!("{flag}=");
let mut it = args.iter();
while let Some(a) = it.next() {
if a == flag {
return it.next().cloned();
}
if let Some(v) = a.strip_prefix(&prefix) {
return Some(v.to_string());
}
}
None
}
fn first_positional(args: &[String]) -> Option<String> {
let mut skip_next = false;
for a in args {
if skip_next {
skip_next = false;
continue;
}
if a == "--shell" {
skip_next = true;
continue;
}
if a.starts_with("--") {
continue;
}
return Some(a.clone());
}
None
}
fn build_cli_cache() -> SessionCache {
let mut cache = SessionCache::new();
if let Some(session) = crate::core::session::SessionState::load_latest() {
for ft in &session.files_touched {
if let Ok(content) = std::fs::read_to_string(&ft.path) {
cache.store(&ft.path, &content);
}
}
}
cache
}