mirror of
https://github.com/instructkr/claw-code.git
synced 2026-04-08 00:54:49 +08:00
feat: b5-cost-tracker — batch 5 upstream parity
This commit is contained in:
@@ -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<String> = 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 <path>|help|<skill> [args]]",
|
||||
);
|
||||
message.push_str("\n Usage: /skills [list|install <path>|help|<skill> [args]]");
|
||||
return Err(message);
|
||||
}
|
||||
}
|
||||
@@ -4297,7 +4297,7 @@ mod tests {
|
||||
|
||||
// then
|
||||
assert!(error.contains("Usage: /teleport <symbol-or-path>"));
|
||||
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"));
|
||||
}
|
||||
|
||||
|
||||
@@ -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/<session-id>.jsonl".to_string(),
|
||||
" Resume latest /resume latest".to_string(),
|
||||
" Browse sessions /session list".to_string(),
|
||||
|
||||
Reference in New Issue
Block a user