From 60ec2aed9b263860f9559038c3b048ae2aa9f620 Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Thu, 9 Apr 2026 21:34:36 +0900 Subject: [PATCH] fix(cli): wire /tokens and /cache as aliases for /stats; implement /stats MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Dogfood found that /tokens and /cache had spec entries (resume_supported: true) but no parse arms in the command parser, resulting in: 'Unknown slash command: /tokens — Did you mean /tokens' (the suggestion engine found the spec entry but parsing always failed) Fix three things: 1. Add 'tokens' | 'cache' as aliases for 'stats' in the parse match so the commands actually resolve to SlashCommand::Stats 2. Implement SlashCommand::Stats in the REPL dispatch — previously fell through to 'Command registered but not yet implemented'. Now shows cumulative token usage for the session. 3. Implement SlashCommand::Stats in run_resume_command — previously returned 'unsupported resumed slash command'. Now emits: text: Cost / Input tokens / Output tokens / Cache create / Cache read json: {kind:stats, input_tokens, output_tokens, cache_*, total_tokens} 159 CLI tests pass, fmt clean. --- rust/crates/commands/src/lib.rs | 2 +- rust/crates/rusty-claude-cli/src/main.rs | 22 ++++++++++++++++++++-- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/rust/crates/commands/src/lib.rs b/rust/crates/commands/src/lib.rs index 82c544e..d68b486 100644 --- a/rust/crates/commands/src/lib.rs +++ b/rust/crates/commands/src/lib.rs @@ -1340,7 +1340,7 @@ pub fn validate_slash_command_input( validate_no_args(command, &args)?; SlashCommand::Upgrade } - "stats" => { + "stats" | "tokens" | "cache" => { validate_no_args(command, &args)?; SlashCommand::Stats } diff --git a/rust/crates/rusty-claude-cli/src/main.rs b/rust/crates/rusty-claude-cli/src/main.rs index fe5273f..5c4b809 100644 --- a/rust/crates/rusty-claude-cli/src/main.rs +++ b/rust/crates/rusty-claude-cli/src/main.rs @@ -2811,6 +2811,21 @@ fn run_resume_command( message: Some(render_doctor_report()?.render()), json: None, }), + SlashCommand::Stats => { + let usage = UsageTracker::from_session(session).cumulative_usage(); + Ok(ResumeCommandOutcome { + session: session.clone(), + message: Some(format_cost_report(usage)), + json: Some(serde_json::json!({ + "kind": "stats", + "input_tokens": usage.input_tokens, + "output_tokens": usage.output_tokens, + "cache_creation_input_tokens": usage.cache_creation_input_tokens, + "cache_read_input_tokens": usage.cache_read_input_tokens, + "total_tokens": usage.total_tokens(), + })), + }) + } SlashCommand::History { count } => { let limit = parse_history_count(count.as_deref()) .map_err(|error| -> Box { error.into() })?; @@ -2847,7 +2862,6 @@ fn run_resume_command( | SlashCommand::Logout | SlashCommand::Vim | SlashCommand::Upgrade - | SlashCommand::Stats | SlashCommand::Share | SlashCommand::Feedback | SlashCommand::Files @@ -3847,11 +3861,15 @@ impl LiveCli { self.print_prompt_history(count.as_deref()); false } + SlashCommand::Stats => { + let usage = UsageTracker::from_session(self.runtime.session()).cumulative_usage(); + println!("{}", format_cost_report(usage)); + false + } SlashCommand::Login | SlashCommand::Logout | SlashCommand::Vim | SlashCommand::Upgrade - | SlashCommand::Stats | SlashCommand::Share | SlashCommand::Feedback | SlashCommand::Files