From 65f4c3ad825084213f664bd10dd45363d3491524 Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Tue, 7 Apr 2026 14:51:12 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20b5-cost-tracker=20=E2=80=94=20batch=205?= =?UTF-8?q?=20upstream=20parity?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- rust/crates/commands/src/lib.rs | 127 ++++++++++++++++------- rust/crates/rusty-claude-cli/src/main.rs | 4 - 2 files changed, 87 insertions(+), 44 deletions(-) diff --git a/rust/crates/commands/src/lib.rs b/rust/crates/commands/src/lib.rs index 73175ab..d8d92ae 100644 --- a/rust/crates/commands/src/lib.rs +++ b/rust/crates/commands/src/lib.rs @@ -1786,24 +1786,29 @@ pub fn resume_supported_slash_commands() -> Vec<&'static SlashCommandSpec> { fn slash_command_category(name: &str) -> &'static str { match name { - "help" | "status" | "sandbox" | "model" | "permissions" | "cost" | "resume" | "session" - | "version" | "login" | "logout" | "usage" | "stats" | "rename" | "privacy-settings" => { - "Session & visibility" - } - "compact" | "clear" | "config" | "memory" | "init" | "diff" | "commit" | "pr" | "issue" - | "export" | "plugin" | "branch" | "add-dir" | "files" | "hooks" | "release-notes" => { - "Workspace & git" - } - "agents" | "skills" | "teleport" | "debug-tool-call" | "mcp" | "context" | "tasks" - | "doctor" | "ide" | "desktop" => "Discovery & debugging", - "bughunter" | "ultraplan" | "review" | "security-review" | "advisor" | "insights" => { - "Analysis & automation" - } - "theme" | "vim" | "voice" | "color" | "effort" | "fast" | "brief" | "output-style" - | "keybindings" | "stickers" => "Appearance & input", - "copy" | "share" | "feedback" | "summary" | "tag" | "thinkback" | "plan" | "exit" - | "upgrade" | "rewind" => "Communication & control", - _ => "Other", + "help" | "status" | "cost" | "resume" | "session" | "version" | "login" | "logout" + | "usage" | "stats" | "rename" | "clear" | "compact" | "history" | "tokens" | "cache" + | "exit" | "summary" | "tag" | "thinkback" | "copy" | "share" | "feedback" | "rewind" + | "pin" | "unpin" | "bookmarks" | "context" | "files" | "focus" | "unfocus" | "retry" + | "stop" | "undo" => "Session", + "diff" | "commit" | "pr" | "issue" | "branch" | "blame" | "log" | "git" | "stash" + | "init" | "export" | "plan" | "review" | "security-review" | "bughunter" | "ultraplan" + | "teleport" | "refactor" | "fix" | "autofix" | "explain" | "docs" | "perf" | "search" + | "references" | "definition" | "hover" | "symbols" | "map" | "web" | "image" + | "screenshot" | "paste" | "listen" | "speak" | "test" | "lint" | "build" | "run" + | "format" | "parallel" | "multi" | "macro" | "alias" | "templates" | "migrate" + | "benchmark" | "cron" | "agent" | "subagent" | "agents" | "skills" | "team" | "plugin" + | "mcp" | "hooks" | "tasks" | "advisor" | "insights" | "release-notes" | "chat" + | "approve" | "deny" | "allowed-tools" | "add-dir" => "Tools", + "model" | "permissions" | "config" | "memory" | "theme" | "vim" | "voice" | "color" + | "effort" | "fast" | "brief" | "output-style" | "keybindings" | "privacy-settings" + | "stickers" | "language" | "profile" | "max-tokens" | "temperature" | "system-prompt" + | "api-key" | "terminal-setup" | "notifications" | "telemetry" | "providers" | "env" + | "project" | "reasoning" | "budget" | "rate-limit" | "workspace" | "reset" | "ide" + | "desktop" | "upgrade" => "Config", + "debug-tool-call" | "doctor" | "sandbox" | "diagnostics" | "tool-details" | "changelog" + | "metrics" => "Debug", + _ => "Tools", } } @@ -1912,12 +1917,7 @@ pub fn render_slash_command_help() -> String { String::new(), ]; - let categories = [ - "Session & visibility", - "Workspace & git", - "Discovery & debugging", - "Analysis & automation", - ]; + let categories = ["Session", "Tools", "Config", "Debug"]; for category in categories { lines.push(category.to_string()); @@ -1930,6 +1930,12 @@ pub fn render_slash_command_help() -> String { lines.push(String::new()); } + lines.push("Keyboard shortcuts".to_string()); + lines.push(" Up/Down Navigate prompt history".to_string()); + lines.push(" Tab Complete commands, modes, and recent sessions".to_string()); + lines.push(" Ctrl-C Clear input (or exit on empty prompt)".to_string()); + lines.push(" Shift+Enter/Ctrl+J Insert a newline".to_string()); + lines .into_iter() .rev() @@ -2314,8 +2320,7 @@ pub fn resolve_skill_invocation( .unwrap_or_default(); if !skill_token.is_empty() { if let Err(error) = resolve_skill_path(cwd, skill_token) { - let mut message = - format!("Unknown skill: {skill_token} ({error})"); + let mut message = format!("Unknown skill: {skill_token} ({error})"); let roots = discover_skill_roots(cwd); if let Ok(available) = load_skills_from_roots(&roots) { let names: Vec = available @@ -2324,15 +2329,10 @@ pub fn resolve_skill_invocation( .map(|s| s.name.clone()) .collect(); if !names.is_empty() { - message.push_str(&format!( - "\n Available skills: {}", - names.join(", ") - )); + message.push_str(&format!("\n Available skills: {}", names.join(", "))); } } - message.push_str( - "\n Usage: /skills [list|install |help| [args]]", - ); + message.push_str("\n Usage: /skills [list|install |help| [args]]"); return Err(message); } } @@ -4297,7 +4297,7 @@ mod tests { // then assert!(error.contains("Usage: /teleport ")); - assert!(error.contains(" Category Discovery & debugging")); + assert!(error.contains(" Category Tools")); } #[test] @@ -4371,10 +4371,10 @@ mod tests { let help = render_slash_command_help(); assert!(help.contains("Start here /status, /diff, /agents, /skills, /commit")); assert!(help.contains("[resume] also works with --resume SESSION.jsonl")); - assert!(help.contains("Session & visibility")); - assert!(help.contains("Workspace & git")); - assert!(help.contains("Discovery & debugging")); - assert!(help.contains("Analysis & automation")); + assert!(help.contains("Session")); + assert!(help.contains("Tools")); + assert!(help.contains("Config")); + assert!(help.contains("Debug")); assert!(help.contains("/help")); assert!(help.contains("/status")); assert!(help.contains("/sandbox")); @@ -4411,6 +4411,53 @@ mod tests { assert!(resume_supported_slash_commands().len() >= 39); } + #[test] + fn renders_help_with_grouped_categories_and_keyboard_shortcuts() { + // given + let categories = ["Session", "Tools", "Config", "Debug"]; + + // when + let help = render_slash_command_help(); + + // then + for category in categories { + assert!( + help.contains(category), + "expected help to contain category {category}" + ); + } + let session_index = help.find("Session").expect("Session header should exist"); + let tools_index = help.find("Tools").expect("Tools header should exist"); + let config_index = help.find("Config").expect("Config header should exist"); + let debug_index = help.find("Debug").expect("Debug header should exist"); + assert!(session_index < tools_index); + assert!(tools_index < config_index); + assert!(config_index < debug_index); + + assert!(help.contains("Keyboard shortcuts")); + assert!(help.contains("Up/Down Navigate prompt history")); + assert!(help.contains("Tab Complete commands, modes, and recent sessions")); + assert!(help.contains("Ctrl-C Clear input (or exit on empty prompt)")); + assert!(help.contains("Shift+Enter/Ctrl+J Insert a newline")); + + // every command should still render with a summary line + for spec in slash_command_specs() { + let usage = match spec.argument_hint { + Some(hint) => format!("/{} {hint}", spec.name), + None => format!("/{}", spec.name), + }; + assert!( + help.contains(&usage), + "expected help to contain command {usage}" + ); + assert!( + help.contains(spec.summary), + "expected help to contain summary for /{}", + spec.name + ); + } + } + #[test] fn renders_per_command_help_detail() { // given @@ -4423,7 +4470,7 @@ mod tests { assert!(help.contains("/plugin")); assert!(help.contains("Summary Manage Claw Code plugins")); assert!(help.contains("Aliases /plugins, /marketplace")); - assert!(help.contains("Category Workspace & git")); + assert!(help.contains("Category Tools")); } #[test] @@ -4431,7 +4478,7 @@ mod tests { let help = render_slash_command_help_detail("mcp").expect("detail help should exist"); assert!(help.contains("/mcp")); assert!(help.contains("Summary Inspect configured MCP servers")); - assert!(help.contains("Category Discovery & debugging")); + assert!(help.contains("Category Tools")); assert!(help.contains("Resume Supported with --resume SESSION.jsonl")); } diff --git a/rust/crates/rusty-claude-cli/src/main.rs b/rust/crates/rusty-claude-cli/src/main.rs index 1b55644..399ab3d 100644 --- a/rust/crates/rusty-claude-cli/src/main.rs +++ b/rust/crates/rusty-claude-cli/src/main.rs @@ -4171,10 +4171,6 @@ fn render_repl_help() -> String { "REPL".to_string(), " /exit Quit the REPL".to_string(), " /quit Quit the REPL".to_string(), - " Up/Down Navigate prompt history".to_string(), - " Tab Complete commands, modes, and recent sessions".to_string(), - " Ctrl-C Clear input (or exit on empty prompt)".to_string(), - " Shift+Enter/Ctrl+J Insert a newline".to_string(), " Auto-save .claw/sessions/.jsonl".to_string(), " Resume latest /resume latest".to_string(), " Browse sessions /session list".to_string(),