fix: #136 support --output-format json with --compact flag

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
YeonGyu-Kim 2026-04-21 14:47:15 +09:00
parent 21adae9570
commit a8beca1463
2 changed files with 82 additions and 0 deletions

View File

@ -3870,6 +3870,7 @@ impl LiveCli {
compact: bool,
) -> Result<(), Box<dyn std::error::Error>> {
match output_format {
CliOutputFormat::Json if compact => self.run_prompt_compact_json(input),
CliOutputFormat::Text if compact => self.run_prompt_compact(input),
CliOutputFormat::Text => self.run_turn(input),
CliOutputFormat::Json => self.run_prompt_json(input),
@ -3889,6 +3890,32 @@ impl LiveCli {
Ok(())
}
fn run_prompt_compact_json(&mut self, input: &str) -> Result<(), Box<dyn std::error::Error>> {
let (mut runtime, hook_abort_monitor) = self.prepare_turn_runtime(false)?;
let mut permission_prompter = CliPermissionPrompter::new(self.permission_mode);
let result = runtime.run_turn(input, Some(&mut permission_prompter));
hook_abort_monitor.stop();
let summary = result?;
self.replace_runtime(runtime)?;
self.persist_session()?;
println!(
"{}",
json!({
"message": final_assistant_text(&summary),
"compact": true,
"model": self.model,
"usage": {
"input_tokens": summary.usage.input_tokens,
"output_tokens": summary.usage.output_tokens,
"cache_creation_input_tokens": summary.usage.cache_creation_input_tokens,
"cache_read_input_tokens": summary.usage.cache_read_input_tokens,
},
})
);
Ok(())
}
fn run_prompt_json(&mut self, input: &str) -> Result<(), Box<dyn std::error::Error>> {
let (mut runtime, hook_abort_monitor) = self.prepare_turn_runtime(false)?;
let mut permission_prompter = CliPermissionPrompter::new(self.permission_mode);

View File

@ -5,6 +5,7 @@ use std::sync::atomic::{AtomicU64, Ordering};
use std::time::{SystemTime, UNIX_EPOCH};
use mock_anthropic_service::{MockAnthropicService, SCENARIO_PREFIX};
use serde_json::Value;
static TEMP_COUNTER: AtomicU64 = AtomicU64::new(0);
@ -125,6 +126,60 @@ fn compact_flag_streaming_text_only_emits_final_message_text() {
fs::remove_dir_all(&workspace).expect("workspace cleanup should succeed");
}
#[test]
fn compact_flag_with_json_output_emits_structured_json() {
let runtime = tokio::runtime::Runtime::new().expect("tokio runtime should build");
let server = runtime
.block_on(MockAnthropicService::spawn())
.expect("mock service should start");
let base_url = server.base_url();
let workspace = unique_temp_dir("compact-json");
let config_home = workspace.join("config-home");
let home = workspace.join("home");
fs::create_dir_all(&workspace).expect("workspace should exist");
fs::create_dir_all(&config_home).expect("config home should exist");
fs::create_dir_all(&home).expect("home should exist");
let prompt = format!("{SCENARIO_PREFIX}streaming_text");
let output = run_claw(
&workspace,
&config_home,
&home,
&base_url,
&[
"--model",
"sonnet",
"--permission-mode",
"read-only",
"--output-format",
"json",
"--compact",
&prompt,
],
);
assert!(
output.status.success(),
"compact json run should succeed
stdout:
{}
stderr:
{}",
String::from_utf8_lossy(&output.stdout),
String::from_utf8_lossy(&output.stderr),
);
let stdout = String::from_utf8(output.stdout).expect("stdout should be utf8");
let parsed: Value = serde_json::from_str(&stdout).expect("compact json stdout should parse");
assert_eq!(parsed["message"], "Mock streaming says hello from the parity harness.");
assert_eq!(parsed["compact"], true);
assert_eq!(parsed["model"], "claude-sonnet-4-6");
assert!(parsed["usage"].is_object());
fs::remove_dir_all(&workspace).expect("workspace cleanup should succeed");
}
fn run_claw(
cwd: &std::path::Path,
config_home: &std::path::Path,