diff --git a/ROADMAP.md b/ROADMAP.md index 84a390c0..3c4dbaeb 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -7605,3 +7605,5 @@ Original filing (2026-04-18): the session emitted `SessionStart hook (completed) 718. **`claw plugins show ` unimplemented — unlike `agents show` and `skills show`, `/plugins show` returned `unknown_plugins_action` error** — dogfooded 2026-05-26 on `8d80f2ff`. Fix: added `show/info/describe` arm to `handle_plugins_slash_command` that filters installed plugins by name; `print_plugins` JSON path filters `payload.plugins` when action is `show/info/describe` and emits `{kind:"plugin", action:"show", status:"error", error_kind:"plugin_not_found", requested:""}` for missing names. Updated error message in catch-all to name `show` as supported. Source: Jobdori dogfood on `8d80f2ff`, 2026-05-26. 719. **`plugins list ` silently returned all plugins instead of filtering — unlike `agents list ` and `skills list ` which do substring filter** — dogfooded 2026-05-26 on `556a598f`. `claw plugins list nonexistent-filter-xyz --output-format json` returned both installed plugins. Fix: `handle_plugins_slash_command` `list` arm now treats `target` as a substring filter; `print_plugins` JSON path applies `target` filter for `list` action. Source: Jobdori dogfood on `556a598f`, 2026-05-26. + +720. **`claw help agents` (and `help skills|plugins|mcp|config|diff|sandbox|doctor|etc.`) errored with `cli_parse: unrecognized argument` instead of routing to the subsystem's help** — dogfooded 2026-05-26 on `fe2b13a4`. `claw help agents --output-format json` returned `{status:"error", error_kind:"cli_parse"}`. The `is_diagnostic` guard for `"help"` verb rejected any trailing non-flag argument. Fix: when `verb == "help"` and exactly one topic argument follows, match it against all known `LocalHelpTopic` variants (including new `Agents`, `Skills`, `Plugins`, `Mcp`, `Config`, `Diff`) and route to `HelpTopic`; `agents` and `skills` delegate to their subsystem usage JSON in `print_help_topic`. Unknown topics fall through to generic `Help`. Source: Jobdori dogfood on `fe2b13a4`, 2026-05-26. diff --git a/rust/crates/rusty-claude-cli/src/main.rs b/rust/crates/rusty-claude-cli/src/main.rs index af4209d5..5186a1a9 100644 --- a/rust/crates/rusty-claude-cli/src/main.rs +++ b/rust/crates/rusty-claude-cli/src/main.rs @@ -690,6 +690,13 @@ enum LocalHelpTopic { SystemPrompt, DumpManifests, BootstrapPlan, + // #720: subsystem help topics so `claw help agents` etc. route to usage JSON + Agents, + Skills, + Plugins, + Mcp, + Config, + Diff, } #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -923,6 +930,12 @@ fn parse_args(args: &[String]) -> Result { "system-prompt" => Some(LocalHelpTopic::SystemPrompt), "dump-manifests" => Some(LocalHelpTopic::DumpManifests), "bootstrap-plan" => Some(LocalHelpTopic::BootstrapPlan), + "agents" | "agent" => Some(LocalHelpTopic::Agents), + "skills" | "skill" => Some(LocalHelpTopic::Skills), + "plugins" | "plugin" | "marketplace" => Some(LocalHelpTopic::Plugins), + "mcp" => Some(LocalHelpTopic::Mcp), + "config" => Some(LocalHelpTopic::Config), + "diff" => Some(LocalHelpTopic::Diff), _ => None, }; if let Some(topic) = topic { @@ -1259,6 +1272,39 @@ fn parse_single_word_command_alias( // "doctor --help -h" is valid, routed to parse_local_help_action() instead return None; } + // #720: `claw help ` — when the verb is "help" and exactly one + // non-flag argument follows, try to route to the topic's handler. + if verb == "help" && rest.len() == 2 { + let topic_name = rest[1].as_str(); + let topic = match topic_name { + "status" => Some(LocalHelpTopic::Status), + "sandbox" => Some(LocalHelpTopic::Sandbox), + "doctor" => Some(LocalHelpTopic::Doctor), + "acp" => Some(LocalHelpTopic::Acp), + "init" => Some(LocalHelpTopic::Init), + "state" => Some(LocalHelpTopic::State), + "export" => Some(LocalHelpTopic::Export), + "version" => Some(LocalHelpTopic::Version), + "system-prompt" => Some(LocalHelpTopic::SystemPrompt), + "dump-manifests" => Some(LocalHelpTopic::DumpManifests), + "bootstrap-plan" => Some(LocalHelpTopic::BootstrapPlan), + "agents" | "agent" => Some(LocalHelpTopic::Agents), + "skills" | "skill" => Some(LocalHelpTopic::Skills), + "plugins" | "plugin" | "marketplace" => Some(LocalHelpTopic::Plugins), + "mcp" => Some(LocalHelpTopic::Mcp), + "config" => Some(LocalHelpTopic::Config), + "diff" => Some(LocalHelpTopic::Diff), + _ => None, + }; + if let Some(t) = topic { + return Some(Ok(CliAction::HelpTopic { + topic: t, + output_format, + })); + } + // Unknown topic: fall through to generic help. + return Some(Ok(CliAction::Help { output_format })); + } // Unrecognized suffix like "--json" let mut msg = format!( "unrecognized argument `{}` for subcommand `{}`", @@ -1272,6 +1318,40 @@ fn parse_single_word_command_alias( return Some(Err(msg)); } + // #720: `claw help ` — when `help` is the verb and a topic follows, + // try to route to the topic's help handler instead of erroring. + if rest.len() == 2 && rest[0] == "help" { + let topic_name = rest[1].as_str(); + let topic = match topic_name { + "status" => Some(LocalHelpTopic::Status), + "sandbox" => Some(LocalHelpTopic::Sandbox), + "doctor" => Some(LocalHelpTopic::Doctor), + "acp" => Some(LocalHelpTopic::Acp), + "init" => Some(LocalHelpTopic::Init), + "state" => Some(LocalHelpTopic::State), + "export" => Some(LocalHelpTopic::Export), + "version" => Some(LocalHelpTopic::Version), + "system-prompt" => Some(LocalHelpTopic::SystemPrompt), + "dump-manifests" => Some(LocalHelpTopic::DumpManifests), + "bootstrap-plan" => Some(LocalHelpTopic::BootstrapPlan), + "agents" | "agent" => Some(LocalHelpTopic::Agents), + "skills" | "skill" => Some(LocalHelpTopic::Skills), + "plugins" | "plugin" | "marketplace" => Some(LocalHelpTopic::Plugins), + "mcp" => Some(LocalHelpTopic::Mcp), + "config" => Some(LocalHelpTopic::Config), + "diff" => Some(LocalHelpTopic::Diff), + _ => None, + }; + if let Some(t) = topic { + return Some(Ok(CliAction::HelpTopic { + topic: t, + output_format, + })); + } + // Unknown topic falls through to the generic help action. + return Some(Ok(CliAction::Help { output_format })); + } + if rest.len() != 1 { return None; } @@ -7303,6 +7383,40 @@ fn render_help_topic(topic: LocalHelpTopic) -> String { Formats text (default), json Related claw doctor · claw status" .to_string(), + LocalHelpTopic::Agents => commands::handle_agents_slash_command( + Some("--help"), + &env::current_dir().unwrap_or_default(), + ) + .unwrap_or_else(|_| "agents help unavailable".to_string()), + LocalHelpTopic::Skills => commands::handle_skills_slash_command( + Some("--help"), + &env::current_dir().unwrap_or_default(), + ) + .unwrap_or_else(|_| "skills help unavailable".to_string()), + LocalHelpTopic::Plugins => "Plugins + Usage claw plugins [list|show |install |enable |disable |uninstall ] + Purpose manage lifecycle of plugins that extend tool and hook capabilities + Formats text (default), json + Related /plugins · claw plugins --help" + .to_string(), + LocalHelpTopic::Mcp => "MCP Servers + Usage claw mcp [list|show ] [--output-format ] + Purpose inspect configured MCP servers and their connection status + Formats text (default), json + Related /mcp · claw mcp list" + .to_string(), + LocalHelpTopic::Config => "Config + Usage claw config [section] [--output-format ] + Purpose show effective runtime configuration (model, hooks, plugins, env) + Formats text (default), json + Related /config · claw doctor" + .to_string(), + LocalHelpTopic::Diff => "Diff + Usage claw diff [--output-format ] + Purpose show the diff of changes relative to the expected base commit + Formats text (default), json + Related /diff · ROADMAP #148" + .to_string(), } } @@ -7319,6 +7433,12 @@ fn local_help_topic_command(topic: LocalHelpTopic) -> &'static str { LocalHelpTopic::SystemPrompt => "system-prompt", LocalHelpTopic::DumpManifests => "dump-manifests", LocalHelpTopic::BootstrapPlan => "bootstrap-plan", + LocalHelpTopic::Agents => "agents", + LocalHelpTopic::Skills => "skills", + LocalHelpTopic::Plugins => "plugins", + LocalHelpTopic::Mcp => "mcp", + LocalHelpTopic::Config => "config", + LocalHelpTopic::Diff => "diff", } } @@ -7386,6 +7506,29 @@ fn print_help_topic( topic: LocalHelpTopic, output_format: CliOutputFormat, ) -> Result<(), Box> { + let cwd = env::current_dir().unwrap_or_default(); + // For subsystem topics in JSON mode, delegate to the subsystem's usage JSON. + if output_format == CliOutputFormat::Json { + match topic { + LocalHelpTopic::Agents => { + let json = commands::handle_agents_slash_command_json(Some("--help"), &cwd) + .unwrap_or_else( + |_| serde_json::json!({"kind":"agents","action":"help","status":"error"}), + ); + println!("{}", serde_json::to_string_pretty(&json)?); + return Ok(()); + } + LocalHelpTopic::Skills => { + let json = commands::handle_skills_slash_command_json(Some("--help"), &cwd) + .unwrap_or_else( + |_| serde_json::json!({"kind":"skills","action":"help","status":"error"}), + ); + println!("{}", serde_json::to_string_pretty(&json)?); + return Ok(()); + } + _ => {} + } + } match output_format { CliOutputFormat::Text => println!("{}", render_help_topic(topic)), CliOutputFormat::Json => println!(