Polish cost reporting into the shared console style

Reformat /cost for both live and resumed sessions so token accounting is presented in the same sectioned operator-console style as status, model, permissions, and config. This improves consistency across the command surface while preserving the same underlying usage metrics.

Constraint: Cost output must continue to reflect cumulative tracked usage only, without claiming real billing or currency totals
Rejected: Add dollar estimates | there is no authoritative pricing source wired into this CLI surface
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Keep /cost focused on raw token accounting until pricing metadata exists in the runtime layer
Tested: cargo fmt --manifest-path ./rust/Cargo.toml --all; cargo clippy --manifest-path ./rust/Cargo.toml --workspace --all-targets -- -D warnings; cargo test --manifest-path ./rust/Cargo.toml --workspace
Not-tested: Manual terminal UX review for very large cumulative token counts
This commit is contained in:
Yeachan-Heo
2026-03-31 21:02:24 +00:00
parent d9c5f60598
commit fa30059790

View File

@@ -311,6 +311,22 @@ fn format_permissions_switch_report(previous: &str, next: &str) -> String {
) )
} }
fn format_cost_report(usage: TokenUsage) -> String {
format!(
"Cost
Input tokens {}
Output tokens {}
Cache create {}
Cache read {}
Total tokens {}",
usage.input_tokens,
usage.output_tokens,
usage.cache_creation_input_tokens,
usage.cache_read_input_tokens,
usage.total_tokens(),
)
}
fn run_resume_command( fn run_resume_command(
session_path: &Path, session_path: &Path,
session: &Session, session: &Session,
@@ -383,14 +399,7 @@ fn run_resume_command(
let usage = UsageTracker::from_session(session).cumulative_usage(); let usage = UsageTracker::from_session(session).cumulative_usage();
Ok(ResumeCommandOutcome { Ok(ResumeCommandOutcome {
session: session.clone(), session: session.clone(),
message: Some(format!( message: Some(format_cost_report(usage)),
"cost: input_tokens={} output_tokens={} cache_creation_tokens={} cache_read_tokens={} total_tokens={}",
usage.input_tokens,
usage.output_tokens,
usage.cache_creation_input_tokens,
usage.cache_read_input_tokens,
usage.total_tokens(),
)),
}) })
} }
SlashCommand::Config => Ok(ResumeCommandOutcome { SlashCommand::Config => Ok(ResumeCommandOutcome {
@@ -620,14 +629,7 @@ impl LiveCli {
fn print_cost(&self) { fn print_cost(&self) {
let cumulative = self.runtime.usage().cumulative_usage(); let cumulative = self.runtime.usage().cumulative_usage();
println!( println!("{}", format_cost_report(cumulative));
"cost: input_tokens={} output_tokens={} cache_creation_tokens={} cache_read_tokens={} total_tokens={}",
cumulative.input_tokens,
cumulative.output_tokens,
cumulative.cache_creation_input_tokens,
cumulative.cache_read_input_tokens,
cumulative.total_tokens(),
);
} }
fn resume_session( fn resume_session(
@@ -1258,10 +1260,11 @@ fn print_help() {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::{ use super::{
format_model_report, format_model_switch_report, format_permissions_report, format_cost_report, format_model_report, format_model_switch_report,
format_permissions_switch_report, format_status_report, normalize_permission_mode, format_permissions_report, format_permissions_switch_report, format_status_report,
parse_args, render_init_claude_md, render_repl_help, resume_supported_slash_commands, normalize_permission_mode, parse_args, render_init_claude_md, render_repl_help,
status_context, CliAction, SlashCommand, StatusUsage, DEFAULT_MODEL, resume_supported_slash_commands, status_context, CliAction, SlashCommand, StatusUsage,
DEFAULT_MODEL,
}; };
use runtime::{ContentBlock, ConversationMessage, MessageRole}; use runtime::{ContentBlock, ConversationMessage, MessageRole};
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
@@ -1376,6 +1379,22 @@ mod tests {
); );
} }
#[test]
fn cost_report_uses_sectioned_layout() {
let report = format_cost_report(runtime::TokenUsage {
input_tokens: 20,
output_tokens: 8,
cache_creation_input_tokens: 3,
cache_read_input_tokens: 1,
});
assert!(report.contains("Cost"));
assert!(report.contains("Input tokens 20"));
assert!(report.contains("Output tokens 8"));
assert!(report.contains("Cache create 3"));
assert!(report.contains("Cache read 1"));
assert!(report.contains("Total tokens 32"));
}
#[test] #[test]
fn permissions_report_uses_sectioned_layout() { fn permissions_report_uses_sectioned_layout() {
let report = format_permissions_report("workspace-write"); let report = format_permissions_report("workspace-write");