diff --git a/rust/crates/commands/src/lib.rs b/rust/crates/commands/src/lib.rs index d4f1770673..9a2c798ef6 100644 --- a/rust/crates/commands/src/lib.rs +++ b/rust/crates/commands/src/lib.rs @@ -2420,6 +2420,13 @@ pub fn handle_skills_slash_command_json(args: Option<&str>, cwd: &Path) -> std:: pub fn classify_skills_slash_command(args: Option<&str>) -> SkillSlashDispatch { match normalize_optional_args(args) { None | Some("list" | "help" | "-h" | "--help") => SkillSlashDispatch::Local, + Some(args) + if args + .split_whitespace() + .any(|part| matches!(part, "-h" | "--help")) => + { + SkillSlashDispatch::Local + } Some(args) if args == "install" || args.starts_with("install ") => { SkillSlashDispatch::Local } diff --git a/rust/crates/rusty-claude-cli/src/main.rs b/rust/crates/rusty-claude-cli/src/main.rs index dbdbd07b64..2fae6a8b6a 100644 --- a/rust/crates/rusty-claude-cli/src/main.rs +++ b/rust/crates/rusty-claude-cli/src/main.rs @@ -10142,6 +10142,47 @@ mod tests { allow_broad_cwd: false, } ); + assert_eq!( + parse_args(&[ + "skills".to_string(), + "list".to_string(), + "--help".to_string(), + "--output-format".to_string(), + "json".to_string(), + ]) + .expect("skills list help json should stay local"), + CliAction::Skills { + args: Some("list --help".to_string()), + output_format: CliOutputFormat::Json, + } + ); + assert_eq!( + parse_args(&[ + "skills".to_string(), + "show".to_string(), + "--help".to_string(), + "--output-format=json".to_string(), + ]) + .expect("skills show help json should stay local"), + CliAction::Skills { + args: Some("show --help".to_string()), + output_format: CliOutputFormat::Json, + } + ); + assert_eq!( + parse_args(&[ + "skills".to_string(), + "missing-skill".to_string(), + "--help".to_string(), + "--output-format".to_string(), + "json".to_string(), + ]) + .expect("missing skill help json should stay local"), + CliAction::Skills { + args: Some("missing-skill --help".to_string()), + output_format: CliOutputFormat::Json, + } + ); assert_eq!( parse_args(&["agents".to_string(), "--help".to_string()]) .expect("agents help should parse"), diff --git a/rust/crates/rusty-claude-cli/tests/output_format_contract.rs b/rust/crates/rusty-claude-cli/tests/output_format_contract.rs index 9fbbdcb00c..6ed76ce658 100644 --- a/rust/crates/rusty-claude-cli/tests/output_format_contract.rs +++ b/rust/crates/rusty-claude-cli/tests/output_format_contract.rs @@ -64,6 +64,33 @@ fn acp_guidance_emits_json_when_requested() { .contains("discoverability alias")); } +#[test] +fn skills_subcommand_help_json_is_bounded_and_static() { + let root = unique_temp_dir("skills-help-json"); + fs::create_dir_all(&root).expect("temp dir should exist"); + + for args in [ + &["skills", "list", "--help", "--output-format", "json"][..], + &["skills", "install", "--help", "--output-format", "json"][..], + &["skills", "show", "--help", "--output-format", "json"][..], + &[ + "skills", + "missing-skill", + "--help", + "--output-format", + "json", + ][..], + ] { + let help = assert_json_command(&root, args); + assert_eq!(help["kind"], "skills"); + assert_eq!(help["action"], "help"); + assert!(help["usage"]["direct_cli"] + .as_str() + .expect("direct CLI usage") + .contains("claw skills")); + } +} + #[test] fn inventory_commands_emit_structured_json_when_requested() { let root = unique_temp_dir("inventory-json");