`claw /status`, `claw /diff`, `claw /version`, `claw /doctor`,
`claw /sandbox` all returned interactive_only because the direct-slash
handler only had explicit arms for /help, /agents, /mcp, /skills.
Other resume-safe slash commands fell through to the generic
`Ok(Some(command)) => Err(interactive_only...)` arm.
Fix: add explicit routing arms for SlashCommand::Status, ::Diff,
::Version, ::Doctor, ::Sandbox in `parse_direct_slash_command`.
One new integration test: direct_slash_resume_safe_commands_route_correctly
(covers /version, /sandbox, /diff)
572+ tests pass.
`claw --output-format json mcp show` (no server name) previously emitted
error_kind:"unknown_mcp_action" — misleading because `show` IS a known
action; the problem is a missing required argument.
Fix:
- `render_mcp_report_json_for`: `Some("show")` arm now emits a dedicated
JSON response with error_kind:"missing_argument" + usage hint
- `classify_error_kind`: add classifier arm for "missing_argument:" prefix
One new test: mcp_show_missing_server_name_emits_missing_argument
572+ tests pass.
Commands like /commit, /pr, /issue, /bughunter, /ultraplan are
interactive-only and NOT resume-safe. Previously the generic
interactive_only error always suggested 'claw --resume SESSION.jsonl
/commit', which would just re-trigger interactive_only.
Fix: check commands::resume_supported_slash_commands() in the
SlashCommand::Ok(Some(cmd)) arm. Resume-safe commands get the full
--resume suggestion; non-resume-safe commands only say 'Start claw'.
Also update two existing unit tests whose assertions checked for the old
'interactive-only' substring (now 'interactive_only:' prefix).
Two new integration tests:
- non_resume_safe_interactive_only_hint_omits_resume_suggestion
- resume_safe_interactive_only_hint_includes_resume_suggestion
572 tests pass, 1 pre-existing worker_boot failure unrelated.
/approve, /yes, /deny, /no (and /y, /n) are valid REPL-only slash
commands. Outside the REPL they were falling through to
format_unknown_direct_slash_command -> error_kind:unknown_slash_command.
Fix: intercept them in the SlashCommand::Unknown arm and emit
interactive_only: prefix so classify_error_kind returns the correct kind.
One new test: approve_deny_outside_repl_emits_interactive_only (covers
/approve, /yes, /deny, /no)
572 tests pass, 1 pre-existing worker_boot failure unrelated.
Single-word all-alpha/dash tokens that don't match any known subcommand
now always emit command_not_found (with or without fuzzy suggestions).
Multi-word cases fall through to CliAction::Prompt (natural language
prompt passthrough like 'claw explain this' must still work). The
multi-word gap is documented as ROADMAP #826 (known limitation).
Tests:
- unknown_subcommand_json_emits_command_not_found (new)
- unknown_subcommand_text_emits_command_not_found_on_stderr (new)
- unknown_subcommand_typo_with_suggestions_json_emits_command_not_found (new)
- multi_word_unknown_subcommand_falls_through_to_prompt_826 (documents gap)
572 tests pass, 1 pre-existing worker_boot failure unrelated.
When looks_like_subcommand_typo fires on a single word with no close
fuzzy matches, the fallthrough reached CliAction::Prompt → provider
startup → misleading missing_credentials error.
Fix: always return Err with command_not_found: prefix from the typo
guard (with or without suggestions). Added command_not_found classifier
arm in classify_error_kind. Unified existing unknown_subcommand kind
under command_not_found in #825.
Three new regression tests in output_format_contract.rs:
- unknown_subcommand_json_emits_command_not_found
- unknown_subcommand_text_emits_command_not_found_on_stderr
- unknown_subcommand_typo_with_suggestions_json_emits_command_not_found
Updated pre-existing unit test assertion (starts_with → contains) and
classifier unit test (unknown_subcommand → command_not_found).
572 tests pass, 1 pre-existing worker_boot failure unrelated.
Add SUPPRESS_CONFIG_WARNINGS_STDERR AtomicBool flag in runtime/config.rs
and expose suppress_config_warnings_for_json_mode() via runtime crate.
In main.rs, scan raw argv for --output-format json before parse_args
and activate the flag so no settings-load warnings reach stderr on any
JSON-mode surface (status, sandbox, system-prompt, mcp list, skills list,
agents list, --resume /config*, etc.).
Text-mode surfaces are unaffected; prose deprecation warnings continue
to appear on stderr.
All 572+ tests pass (one pre-existing worker_boot failure unrelated).
* fix: route all JSON-mode abort envelopes to stdout (#819#820#823)
All handled errors in --output-format json mode now write the structured
abort envelope to stdout (rc=1) and keep stderr empty. Previously the
top-level error handler and resume_session JSON branches used eprintln!
which sent the envelope to stderr, breaking machine consumers that read
stdout for command payloads.
Surfaces fixed:
- Top-level abort handler (main.rs): export --session <missing>,
session <subcommand>, prompt (no text), unknown subcommand fallthrough,
flag errors, and all other run() failures
- resume_session JSON branches: session load errors, unsupported commands,
parse errors, command execution errors
Test changes: updated 24 failing contract tests to assert JSON envelopes
on stdout. Added stderr-clean assertions where appropriate. 70 contract
tests pass (was 68; 2 additional from regression coverage).
ROADMAP: #819 (export session-not-found), #820 (interactive_only class),
#823 (missing prompt)
* style: cargo fmt on main.rs after eprintln->println fix
* fix(tests): fmt + update compact_output test for stdout abort envelope routing
* fix(tests): update resume_slash_commands stub test for stdout envelope routing
JSON config output already carries collected config diagnostics in warnings[], so prose stderr emission must be reserved for text/local paths. Lazy permission-mode default resolution prevents an earlier config load from leaking the same deprecation before the JSON renderer runs.\n\nConstraint: ROADMAP #815 requires text mode to keep human stderr warnings while JSON config/list suppresses duplicate app-level config prose.\nRejected: Filtering all stderr in JSON mode | would hide cargo/compiler or unrelated diagnostics outside the app config warning path.\nConfidence: high\nScope-risk: narrow\nDirective: Keep load_collecting_warnings side-effect-free; use load() for human stderr emission.\nTested: cargo fmt; cargo test -p rusty-claude-cli --test output_format_contract config_json_reports_deprecations_structurally_without_stderr_duplicate_815; cargo test -p rusty-claude-cli --test output_format_contract; manual target/debug/claw JSON config fixture.\nNot-tested: cargo clippy -p rusty-claude-cli --all-targets -- -D warnings is blocked by pre-existing runtime dead_code/trident warnings.
Doctor help was already on the local help path in current source, but the exact #702 dogfood surface lacked a focused guard and the JSON help envelope was still too prose-oriented for wrappers. Strengthen the JSON contract while preserving text help.\n\nConstraint: Preserve unrelated dirty rust/Cargo.lock from prior #701 work.\nRejected: Starting runtime/provider/session to inspect doctor semantics | help must be local and credential-free.\nConfidence: high\nScope-risk: narrow\nDirective: Keep doctor help routed through parse_local_help_action and print_help_topic; do not call run_doctor for --help.\nTested: cargo test --manifest-path rust/Cargo.toml -p rusty-claude-cli --test output_format_contract doctor_help -- --nocapture; cargo test --manifest-path rust/Cargo.toml -p rusty-claude-cli --test output_format_contract help -- --nocapture; cargo fmt --manifest-path rust/Cargo.toml --all -- --check; cargo check --manifest-path rust/Cargo.toml -p rusty-claude-cli; timeout 5s cargo run -q --bin claw -- --output-format json doctor --help; timeout 5s cargo run -q --bin claw -- doctor --help.\nNot-tested: full workspace test suite.