diff --git a/ROADMAP.md b/ROADMAP.md index 8856d1aa..4e635166 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -7671,3 +7671,5 @@ Original filing (2026-04-18): the session emitted `SessionStart hook (completed) 751. **ROADMAP #750 has no regression test: `claw prompt --output-format json` no-arg `error_kind` and `hint` could silently regress** — confirmed by gaebal-gajae on `ac925ed4`. Fix: add `prompt_no_arg_json_error_kind_750` test asserting nonzero exit, `error_kind:"missing_prompt"`, non-empty `hint` mentioning `claw prompt` or `echo`. Source: gaebal-gajae dogfood on `ac925ed4`, 2026-05-26. 752. **`claw --output-format json ` returned `hint: null` for all `cli_parse` errors when an unrecognized positional arg was supplied** — dogfooded 2026-05-26 on `ddc71b56`. Generic `unrecognized argument` format string had no `\n` so `split_error_hint` emitted null hint (only the `--json` special-case added a hint). Fix: add else-branch appending `\nRun `claw --help` for usage.` to the generic arm. Affected surfaces: `sandbox`, `doctor`, `version`, and any other subcommand routing through the same unrecognized-arg path. Source: Jobdori dogfood on `ddc71b56`, 2026-05-26. + +753. **`claw --output-format json -p` (no prompt arg) returned `error_kind:"unknown"` and `hint: null`** — parity gap with #750/#751 which fixed the explicit `prompt` verb. Identified by gaebal-gajae on `ddc71b56`. Fix: same `missing_prompt:` prefix + newline usage hint as #750. Regression guard: `short_p_flag_no_arg_json_error_kind_753` asserting nonzero exit, `error_kind:"missing_prompt"`, non-empty hint mentioning `claw -p` or `claw prompt`. Source: gaebal-gajae dogfood on `ddc71b56`, 2026-05-26. diff --git a/rust/crates/rusty-claude-cli/src/main.rs b/rust/crates/rusty-claude-cli/src/main.rs index 5cb4bdc5..ddae2664 100644 --- a/rust/crates/rusty-claude-cli/src/main.rs +++ b/rust/crates/rusty-claude-cli/src/main.rs @@ -861,7 +861,8 @@ fn parse_args(args: &[String]) -> Result { // Claw Code compat: -p "prompt" = one-shot prompt let prompt = args[index + 1..].join(" "); if prompt.trim().is_empty() { - return Err("-p requires a prompt string".to_string()); + // #753: same missing_prompt shape as claw prompt (no arg) fix in #750 + return Err("missing_prompt: -p requires a prompt string.\nUsage: claw -p or claw prompt ".to_string()); } return Ok(CliAction::Prompt { prompt, 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 24ebef17..8d4d762f 100644 --- a/rust/crates/rusty-claude-cli/tests/output_format_contract.rs +++ b/rust/crates/rusty-claude-cli/tests/output_format_contract.rs @@ -1504,6 +1504,53 @@ fn prompt_no_arg_json_error_kind_750() { ); } +#[test] +fn short_p_flag_no_arg_json_error_kind_753() { + // #753: `claw --output-format json -p` (no prompt) must emit error_kind:"missing_prompt" + // and non-empty hint. Before #753 it returned error_kind:"unknown" + hint:null. + // Parity with #750 which fixed the explicit `prompt` verb. + use std::process::Command; + let root = unique_temp_dir("short-p-no-arg"); + fs::create_dir_all(&root).expect("temp dir"); + let bin = env!("CARGO_BIN_EXE_claw"); + + let output = Command::new(bin) + .current_dir(&root) + .args(["--output-format", "json", "-p"]) + .output() + .expect("claw -p should run"); + assert!( + !output.status.success(), + "claw -p with no arg must exit non-zero" + ); + let stdout = String::from_utf8_lossy(&output.stdout); + let raw = if stdout.trim().starts_with('{') { + stdout.trim().to_string() + } else { + String::from_utf8_lossy(&output.stderr) + .lines() + .filter(|l| l.starts_with('{')) + .collect::>() + .join("") + }; + let parsed: serde_json::Value = serde_json::from_str(&raw).unwrap_or_else(|_| { + panic!("claw -p (no arg) --output-format json must emit valid JSON; got: {raw}") + }); + assert_eq!( + parsed["error_kind"], "missing_prompt", + "claw -p no-arg must have error_kind:missing_prompt (#753); got: {parsed}" + ); + let hint = parsed["hint"].as_str().unwrap_or(""); + assert!( + !hint.is_empty(), + "claw -p no-arg hint must be non-empty (#753)" + ); + assert!( + hint.contains("claw -p") || hint.contains("claw prompt"), + "hint should mention 'claw -p' or 'claw prompt': {hint}" + ); +} + #[test] fn bare_slash_command_hint_745() { // #747/#745: claw --output-format json must return non-null hint.