bctx 0.1.16

bctx CLI — intercept CLI commands and compress output for LLM coding agents
use std::io::{self, Read};

use weave::lenses::{apply_stack, clarity::ClarityLens, narrow::NarrowLens, LensContext};

pub fn handle(budget: usize, file: Option<String>, json: bool) -> anyhow::Result<()> {
    let mut text = String::new();
    if let Some(ref path) = file {
        text = std::fs::read_to_string(path)
            .map_err(|e| anyhow::anyhow!("bctx compress: cannot read '{path}': {e}"))?;
    } else {
        io::stdin().read_to_string(&mut text)?;
    }

    if text.trim().is_empty() {
        if json {
            println!(
                "{}",
                serde_json::json!({
                    "compressed": "",
                    "tokens_before": 0,
                    "tokens_after": 0,
                    "savings_pct": 0.0,
                })
            );
        }
        return Ok(());
    }

    let ctx = LensContext::new(budget);
    let stack: Vec<Box<dyn weave::lenses::Lens>> =
        vec![Box::new(ClarityLens), Box::new(NarrowLens)];
    let output = apply_stack(&stack, &text, &ctx);

    if json {
        println!(
            "{}",
            serde_json::json!({
                "compressed": output.content,
                "tokens_before": output.tokens_before,
                "tokens_after": output.tokens_after,
                "savings_pct": output.savings_pct(),
            })
        );
    } else {
        print!("{}", output.content);
        eprintln!(
            "[bctx: {}{} tokens, {}% saved]",
            output.tokens_before,
            output.tokens_after,
            output.savings_pct().round() as u64,
        );
    }
    Ok(())
}