From 3db3dfa60de71af589399e4b9ef2b20db74fded4 Mon Sep 17 00:00:00 2001 From: Yeachan-Heo Date: Tue, 31 Mar 2026 20:43:56 +0000 Subject: [PATCH] Make model inspection and switching feel more like a real CLI surface Replace terse /model strings with sectioned model reports that show the active model and preserved session context, and use a structured switch report when the model changes. This keeps the behavior honest while making model management feel more intentional and Claude-like. Constraint: Model switching must preserve the current session and avoid adding any fake model catalog or validation layer Rejected: Add a hardcoded model list or aliases | would create drift with actual backend-supported model names Confidence: high Scope-risk: narrow Reversibility: clean Directive: Keep /model output informational and backend-agnostic unless the runtime gains authoritative model discovery 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 interactive switching across multiple real Anthropic model names --- rust/crates/rusty-claude-cli/src/main.rs | 72 ++++++++++++++++++++++-- 1 file changed, 66 insertions(+), 6 deletions(-) diff --git a/rust/crates/rusty-claude-cli/src/main.rs b/rust/crates/rusty-claude-cli/src/main.rs index e06095c..faa9639 100644 --- a/rust/crates/rusty-claude-cli/src/main.rs +++ b/rust/crates/rusty-claude-cli/src/main.rs @@ -269,6 +269,28 @@ struct StatusUsage { estimated_tokens: usize, } +fn format_model_report(model: &str, message_count: usize, turns: u32) -> String { + format!( + "Model + Current model {model} + Session messages {message_count} + Session turns {turns} + +Usage + Inspect current model with /model + Switch models with /model " + ) +} + +fn format_model_switch_report(previous: &str, next: &str, message_count: usize) -> String { + format!( + "Model updated + Previous {previous} + Current {next} + Preserved msgs {message_count}" + ) +} + fn run_resume_command( session_path: &Path, session: &Session, @@ -489,19 +511,38 @@ impl LiveCli { fn set_model(&mut self, model: Option) -> Result<(), Box> { let Some(model) = model else { - println!("Current model: {}", self.model); + println!( + "{}", + format_model_report( + &self.model, + self.runtime.session().messages.len(), + self.runtime.usage().turns(), + ) + ); return Ok(()); }; if model == self.model { - println!("Model already set to {model}."); + println!( + "{}", + format_model_report( + &self.model, + self.runtime.session().messages.len(), + self.runtime.usage().turns(), + ) + ); return Ok(()); } + let previous = self.model.clone(); let session = self.runtime.session().clone(); + let message_count = session.messages.len(); self.runtime = build_runtime(session, model.clone(), self.system_prompt.clone(), true)?; self.model.clone_from(&model); - println!("Switched model to {model}."); + println!( + "{}", + format_model_switch_report(&previous, &model, message_count) + ); Ok(()) } @@ -1193,9 +1234,10 @@ fn print_help() { #[cfg(test)] mod tests { use super::{ - format_status_report, normalize_permission_mode, parse_args, render_init_claude_md, - render_repl_help, resume_supported_slash_commands, status_context, CliAction, SlashCommand, - StatusUsage, DEFAULT_MODEL, + format_model_report, format_model_switch_report, format_status_report, + normalize_permission_mode, parse_args, render_init_claude_md, render_repl_help, + resume_supported_slash_commands, status_context, CliAction, SlashCommand, StatusUsage, + DEFAULT_MODEL, }; use runtime::{ContentBlock, ConversationMessage, MessageRole}; use std::path::{Path, PathBuf}; @@ -1310,6 +1352,24 @@ mod tests { ); } + #[test] + fn model_report_uses_sectioned_layout() { + let report = format_model_report("claude-sonnet", 12, 4); + assert!(report.contains("Model")); + assert!(report.contains("Current model claude-sonnet")); + assert!(report.contains("Session messages 12")); + assert!(report.contains("Switch models with /model ")); + } + + #[test] + fn model_switch_report_preserves_context_summary() { + let report = format_model_switch_report("claude-sonnet", "claude-opus", 9); + assert!(report.contains("Model updated")); + assert!(report.contains("Previous claude-sonnet")); + assert!(report.contains("Current claude-opus")); + assert!(report.contains("Preserved msgs 9")); + } + #[test] fn status_line_reports_model_and_token_totals() { let status = format_status_report(