From c760a49c474b42eb2eb78d509939930fc3744721 Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Wed, 27 May 2026 03:37:00 +0900 Subject: [PATCH] fix(#774): agents/plugins/mcp unknown-subcommand errors now include non-null hint --- ROADMAP.md | 2 ++ rust/crates/commands/src/lib.rs | 14 +++++++++++--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/ROADMAP.md b/ROADMAP.md index abb8938d..abf09408 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -7713,3 +7713,5 @@ Original filing (2026-04-18): the session emitted `SessionStart hook (completed) 772. **Slash command aliases bypassed `bare_slash_command_guidance` lookup** — dogfooded 2026-05-27 on `bf212b98`. `bare_slash_command_guidance()` only checked `spec.name == command_name`, not `spec.aliases`, so `claw yes`, `claw no`, `claw y`, `claw n`, `claw skill`, `claw cwd` all fell through (either to typo suggestions or `missing_credentials`). Should have returned `interactive_only:` guidance referencing the canonical form. Fix: (1) lookup changed to `spec.name == command_name || spec.aliases.contains(&command_name)`; (2) capture `canonical_name = slash_command.name`; (3) guidance strings updated to reference canonical form in remediation (e.g., `claw yes → /approve`, `claw n → /deny`, `claw skill → /skills`). 36 CLI contract tests pass. [SCOPE: claw-code] Source: Gaebal-gajae pinpoint on `bf212b98`, 2026-05-27. 773. **Config deprecation warnings only emitted as unstructured stderr text in `--output-format json` mode** — dogfooded 2026-05-27 on `212f0b2a`. `emit_config_warning_once()` always wrote to stderr regardless of output format, causing JSON-mode callers to receive an unexpected `warning: ...` text line on stderr before the JSON object. Callers had to implement ad-hoc stripping. Fix: added `ConfigLoader::load_collecting_warnings()` method that returns `(RuntimeConfig, Vec)` so callers can surface warnings structurally; `render_config_json()` now uses this and includes a `warnings: []` array in the config JSON envelope. Existing `load()` path unchanged (still emits to stderr for text-mode callers). 36 CLI contract tests pass. [SCOPE: claw-code] Source: Jobdori startup-friction probe on `212f0b2a`, 2026-05-27. + +774. **`claw agents bogus`, `claw plugins bogus`, `claw mcp bogus` returned `hint: null`** — dogfooded 2026-05-27 on `727a1ea4`. Three "unknown subcommand" envelopes had `error_kind` correctly set but `hint: null`: (1) `unknown_agents_subcommand` — both text and JSON handler emitted single-line error with inline remediation after `.`, no `\n`; (2) `unknown_plugins_action` — same, period-delimited remediation; (3) `unknown_mcp_action` — `render_mcp_usage_json` never included a `hint` field at all. Fixes: (1)+(2) added `\n` before remediation suffix in `commands/src/lib.rs`; (3) added `hint` field to `render_mcp_usage_json` pointing at supported actions. All three now return non-null `hint`. 36 CLI contract tests pass. [SCOPE: claw-code] Source: Jobdori envelope-consistency probe on `727a1ea4`, 2026-05-27. diff --git a/rust/crates/commands/src/lib.rs b/rust/crates/commands/src/lib.rs index e1712aed..58a27e09 100644 --- a/rust/crates/commands/src/lib.rs +++ b/rust/crates/commands/src/lib.rs @@ -2342,7 +2342,7 @@ pub fn handle_plugins_slash_command( reload_runtime: false, }), Some(other) => Err(PluginError::CommandFailed(format!( - "unknown_plugins_action: '{other}' is not a supported /plugins action. Use list, show, install, enable, disable, uninstall, or update." + "unknown_plugins_action: '{other}' is not a supported /plugins action.\nUse: list, show, install, enable, disable, uninstall, or update." ))), } } @@ -2406,7 +2406,7 @@ pub fn handle_agents_slash_command(args: Option<&str>, cwd: &Path) -> std::io::R Some(args) if is_help_arg(args) => Ok(render_agents_usage(None)), Some(args) => Err(std::io::Error::new( std::io::ErrorKind::InvalidInput, - format!("unknown agents subcommand: {args}. Supported: list, show, help"), + format!("unknown agents subcommand: {args}.\nSupported: list, show, help"), )), } } @@ -2477,7 +2477,7 @@ pub fn handle_agents_slash_command_json(args: Option<&str>, cwd: &Path) -> std:: Some(args) if is_help_arg(args) => Ok(render_agents_usage_json(None)), Some(args) => Err(std::io::Error::new( std::io::ErrorKind::InvalidInput, - format!("unknown agents subcommand: {args}. Supported: list, show, help"), + format!("unknown agents subcommand: {args}.\nSupported: list, show, help"), )), } } @@ -4173,12 +4173,20 @@ fn render_mcp_usage_json(unexpected: Option<&str>) -> Value { } else { Value::Null }; + // #774: add hint field so unknown_mcp_action errors have non-null hint parity + // with agents/plugins unknown-subcommand envelopes. + let hint: Value = if unexpected.is_some() { + json!("Use: list, show , or help") + } else { + Value::Null + }; json!({ "kind": "mcp", "action": "help", "ok": unexpected.is_none(), "status": if unexpected.is_some() { "error" } else { "ok" }, "error_kind": error_kind, + "hint": hint, "usage": { "slash_command": "/mcp [list|show |help]", "direct_cli": "claw mcp [list|show |help]",