Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 11 additions & 6 deletions rust/crates/rusty-claude-cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,10 @@ fn main() {
let (short_reason, inline_hint) = split_error_hint(&message);
// #781: fall back to a kind-derived hint when the message has no \n-delimited hint
let hint = inline_hint.or_else(|| fallback_hint_for_error_kind(kind).map(String::from));
eprintln!(
// #819/#820/#823: JSON mode error envelopes must go to stdout so machine
// consumers can parse failures from stdout byte 0 (parity with all
// non-interactive command guards that already use println! / to_stdout).
println!(
"{}",
serde_json::json!({
"type": "error",
Expand Down Expand Up @@ -3401,7 +3404,9 @@ fn resume_session(session_path: &Path, commands: &[String], output_format: CliOu
// #787: fall back to kind-derived hint when message has no \n delimiter
let hint =
inline_hint.or_else(|| fallback_hint_for_error_kind(kind).map(String::from));
eprintln!(
// #819: JSON mode resume errors go to stdout for parity with other
// non-interactive command guards.
println!(
"{}",
serde_json::json!({
"kind": kind,
Expand Down Expand Up @@ -3459,7 +3464,7 @@ fn resume_session(session_path: &Path, commands: &[String], output_format: CliOu
.unwrap_or("");
if STUB_COMMANDS.contains(&cmd_root) {
if output_format == CliOutputFormat::Json {
eprintln!(
println!(
"{}",
serde_json::json!({
"kind": "unsupported_command",
Expand All @@ -3482,7 +3487,7 @@ fn resume_session(session_path: &Path, commands: &[String], output_format: CliOu
Ok(Some(command)) => command,
Ok(None) => {
if output_format == CliOutputFormat::Json {
eprintln!(
println!(
"{}",
serde_json::json!({
"kind": "unsupported_resumed_command",
Expand All @@ -3502,7 +3507,7 @@ fn resume_session(session_path: &Path, commands: &[String], output_format: CliOu
}
Err(error) => {
if output_format == CliOutputFormat::Json {
eprintln!(
println!(
"{}",
serde_json::json!({
"kind": "cli_parse",
Expand Down Expand Up @@ -3552,7 +3557,7 @@ fn resume_session(session_path: &Path, commands: &[String], output_format: CliOu
// #787: fall back to kind-derived hint when error has no \n delimiter
let hint = inline_hint
.or_else(|| fallback_hint_for_error_kind(error_kind).map(String::from));
eprintln!(
println!(
"{}",
serde_json::json!({
"kind": error_kind,
Expand Down
12 changes: 7 additions & 5 deletions rust/crates/rusty-claude-cli/tests/compact_output.rs
Original file line number Diff line number Diff line change
Expand Up @@ -266,13 +266,15 @@ fn compact_subcommand_json_help_fails_fast_when_stdin_closed() {
!output.status.success(),
"compact json help should fail non-zero"
);
// #819/#820/#823: JSON abort envelopes route to stdout
let stderr = String::from_utf8(output.stderr).expect("stderr should be utf8");
assert!(
output.stdout.is_empty(),
"compact json help should not start a prompt/spinner on stdout: {}",
String::from_utf8_lossy(&output.stdout)
stderr.trim().is_empty() || !stderr.trim_start().starts_with('{'),
"compact json help should not emit JSON envelope to stderr (#819/#820/#823): {stderr}"
);
let stderr = String::from_utf8(output.stderr).expect("stderr should be utf8");
let parsed: Value = serde_json::from_str(stderr.trim()).expect("stderr should be JSON error");
let stdout = String::from_utf8(output.stdout).expect("stdout should be utf8");
let parsed: Value =
serde_json::from_str(stdout.trim()).expect("stdout should be JSON error envelope");
assert_eq!(parsed["status"], "error");
assert_eq!(parsed["error_kind"], "interactive_only");
assert_eq!(parsed["action"], "abort");
Expand Down
Loading
Loading