diff --git a/ROADMAP.md b/ROADMAP.md index ef7f7825..9c6ca0bf 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -7707,3 +7707,5 @@ Original filing (2026-04-18): the session emitted `SessionStart hook (completed) 769. **`claw session bogus` fell through to credential check instead of interactive-only guidance** — dogfooded 2026-05-27 on `b778d4e3` (tracked as #767). `claw session ` with more than one token bypassed `parse_single_word_command_alias` (which only fires for `rest.len()==1`) and had no match arm in `parse_args`, so `rest.join(" ")` became a prompt literal dispatched to `CliAction::Prompt`, hitting `missing_credentials` at the gate. Fix: added `"session"` match arm that emits `interactive_only:` error with `\n`-delimited hint referencing `--resume SESSION.jsonl /session` and REPL usage. Integration test `session_with_unknown_subcommand_returns_interactive_only_not_credentials_767` asserts `error_kind:interactive_only` + non-null hint for `bogus`, `nuke`, `delete-all`. 35 CLI contract tests pass. [SCOPE: claw-code] Source: Jobdori probe on `b778d4e3`, 2026-05-27. 770. **`claw cost/clear/memory/ultraplan/model` with trailing args fell to credential check** — dogfooded 2026-05-27 on `9e1be056`. Same fallthrough gap as #767/#769: these slash-only verbs had no multi-arg match arms, so `claw cost breakdown`, `claw clear --force`, `claw memory reset`, `claw ultraplan bogus`, `claw model opus extra` all became `CliAction::Prompt` literals, hitting `missing_credentials` at the gate. Fix: added `"cost"`, `"clear"`, `"memory"`, `"ultraplan"`, `"model" if rest.len() > 1` match arms, each returning `interactive_only:` + `\n`-delimited hint. Integration test `slash_only_verbs_with_args_return_interactive_only_not_credentials_770` asserts all five cases. 36 CLI contract tests pass. [SCOPE: claw-code] Source: Jobdori sweep on `9e1be056`, 2026-05-27. + +771. **`init extraarg` silently succeeded; `usage`/`stats`/`fork` with args fell to credential check** — dogfooded 2026-05-27 on `3a1d8838`. Two distinct gaps: (1) `claw init extraarg` returned `status:ok` with trailing positional ignored — `"init"` arm always returned `Ok(CliAction::Init)` regardless of `rest[1..]`; (2) `claw usage extra`, `claw stats extra`, `claw fork newbranch` had no match arms and fell to `CliAction::Prompt` + credential gate. Fixes: (1) added extra-arg check in `"init"` arm — rejects with `unexpected_extra_args:` prefix + `\n` usage hint; (2) added `"usage"`, `"stats"`, `"fork"` interactive-only arms. All four now return correct `error_kind` + non-null hint. 36 CLI contract tests pass. [SCOPE: claw-code] Source: Jobdori sweep on `3a1d8838`, 2026-05-27. diff --git a/rust/crates/rusty-claude-cli/src/main.rs b/rust/crates/rusty-claude-cli/src/main.rs index 7c5ce197..dd684425 100644 --- a/rust/crates/rusty-claude-cli/src/main.rs +++ b/rust/crates/rusty-claude-cli/src/main.rs @@ -1197,6 +1197,19 @@ fn parse_args(args: &[String]) -> Result { "interactive_only: `claw model` is a slash command.\nStart `claw` and run `/model [model-name]` inside the REPL." .to_string(), ), + // #771: usage/stats/fork are slash-only verbs with no multi-arg match arms + "usage" => Err( + "interactive_only: `claw usage` is a slash command.\nUse `claw --resume SESSION.jsonl /usage` or start `claw` and run `/usage`." + .to_string(), + ), + "stats" => Err( + "interactive_only: `claw stats` is a slash command.\nUse `claw --resume SESSION.jsonl /stats` or start `claw` and run `/stats`." + .to_string(), + ), + "fork" => Err( + "interactive_only: `claw fork` is a slash command.\nStart `claw` and run `/session fork [branch-name]` inside the REPL." + .to_string(), + ), "skills" => { let args = join_optional_args(&rest[1..]); if let Some(action) = args.as_deref() { @@ -1228,7 +1241,16 @@ fn parse_args(args: &[String]) -> Result { "system-prompt" => parse_system_prompt_args(&rest[1..], model, output_format), "acp" => parse_acp_args(&rest[1..], output_format), "login" | "logout" => Err(removed_auth_surface_error(rest[0].as_str())), - "init" => Ok(CliAction::Init { output_format }), + "init" => { + // #771: extra positional args to `init` were silently ignored — now rejected + if rest.len() > 1 { + let extra = rest[1..].join(" "); + return Err(format!( + "unexpected extra arguments after `claw init`: {extra}\nUsage: claw init [--cwd ] [--date ] [--session ]" + )); + } + Ok(CliAction::Init { output_format }) + } "export" => parse_export_args(&rest[1..], output_format), "prompt" => { let prompt = rest[1..].join(" ");