use serde_json::json;
use beck::db::Db;
use beck::error::{CliError, Result};
use beck::paths;
const CHARS_PER_TOKEN: f64 = 4.0;
const PER_SKILL_FORMAT_OVERHEAD: i64 = 30;
fn commafy(n: i64) -> String {
let s = n.abs().to_string();
let mut out = String::new();
for (i, c) in s.chars().rev().enumerate() {
if i > 0 && i % 3 == 0 {
out.insert(0, ',');
}
out.insert(0, c);
}
if n < 0 {
out.insert(0, '-');
}
out
}
pub async fn handle(explain: bool, json_out: bool) -> Result<()> {
let db_path = paths::db_path()?;
if !db_path.exists() {
return Err(CliError::Validation(
"no database found. Run `beck sync` first.".into(),
));
}
let db = Db::open(&db_path).map_err(CliError::Other)?;
let count = db.count().map_err(CliError::Other)?;
let desc_bytes = db.description_bytes().map_err(CliError::Other)?;
let body_bytes = db.body_bytes().map_err(CliError::Other)?;
let baseline_bytes = desc_bytes + (count * PER_SKILL_FORMAT_OVERHEAD);
let baseline_tokens = (baseline_bytes as f64 / CHARS_PER_TOKEN).round() as i64;
let beck_session_tokens: i64 = 200;
let saved = baseline_tokens - beck_session_tokens;
let pct = if baseline_tokens > 0 {
(saved as f64 / baseline_tokens as f64 * 100.0).round()
} else {
0.0
};
if json_out {
let payload = json!({
"skills_indexed": count,
"baseline_tokens_per_turn": baseline_tokens,
"beck_tokens_per_turn": beck_session_tokens,
"tokens_saved_per_turn": saved,
"percent_saved": pct,
"description_bytes": desc_bytes,
"body_bytes": body_bytes,
});
println!(
"{}",
serde_json::to_string_pretty(&payload).unwrap_or_default()
);
} else {
let saved_h = commafy(saved);
let baseline_h = commafy(baseline_tokens);
let beck_h = commafy(beck_session_tokens);
println!("beck saves you ~{saved_h} tokens per agent turn ({pct}% of baseline)");
println!(" baseline inject-all: {baseline_h:>6} tokens");
println!(" beck MCP session: {beck_h:>6} tokens (flat)");
}
if explain {
println!();
println!("--- math ---");
println!("chars_per_token = {CHARS_PER_TOKEN}");
println!(
"per_skill_format_overhead = {PER_SKILL_FORMAT_OVERHEAD} (bytes of '- {{name}}: ' framing)"
);
println!("description_bytes = {desc_bytes}");
println!(
"body_bytes = {body_bytes} (not counted, beck only replaces the catalog metadata)"
);
println!(
"baseline_bytes = description_bytes + (skill_count * per_skill_format_overhead)"
);
println!(
" = {desc_bytes} + ({count} * {PER_SKILL_FORMAT_OVERHEAD}) = {baseline_bytes}"
);
println!(
"baseline_tokens = baseline_bytes / chars_per_token = {baseline_tokens}"
);
println!("beck_session_tokens = 200 (2 MCP tools * ~100 tokens each, flat)");
println!("tokens_saved_per_turn = baseline_tokens - beck_session_tokens = {saved}");
}
Ok(())
}