use crate::output_fmt;
use crate::types::Output;
use agent_first_data::{OutputFormat, OutputTo};
use serde_json::Value;
use std::io::Write;
use std::sync::OnceLock;
static OUTPUT_TO: OnceLock<OutputTo> = OnceLock::new();
pub fn install_output_to_from_raw(raw_args: &[String]) -> Result<(), String> {
let mut requested: Option<OutputTo> = None;
let mut event_stream = false;
let mut top_level = true;
let mut i = 1usize;
while i < raw_args.len() {
let arg = raw_args[i].as_str();
if arg == "--" {
break;
}
if let Some(value) = arg.strip_prefix("--output-to=") {
requested = Some(OutputTo::parse(value)?);
i += 1;
continue;
}
if arg == "--output-to" {
let value = raw_args.get(i + 1).ok_or_else(|| {
"--output-to requires a value: expected split, stdout, or stderr".to_string()
})?;
requested = Some(OutputTo::parse(value)?);
i += 2;
continue;
}
if !top_level {
i += 1;
continue;
}
if arg == "--stream-rows" || arg == "--mode=pipe" {
event_stream = true;
i += 1;
continue;
}
if arg == "--mode" {
if raw_args.get(i + 1).is_some_and(|value| value == "pipe") {
event_stream = true;
}
i += 2;
continue;
}
if crate::cli::top_level_arg_consumes_two_values(arg) {
i += if arg.contains('=') { 2 } else { 3 };
continue;
}
if crate::cli::top_level_arg_consumes_value(arg) {
i += if arg.contains('=') { 1 } else { 2 };
continue;
}
if arg.starts_with('-') {
i += 1;
continue;
}
top_level = false;
i += 1;
}
let output_to = match (requested, event_stream) {
(Some(OutputTo::Split), true) => {
return Err(
"--output-to split cannot be combined with --mode pipe or --stream-rows: an ordered event stream must stay on one stream. Use --output-to stdout (the streaming default) or --output-to stderr"
.to_string(),
);
}
(Some(explicit), _) => explicit,
(None, true) => OutputTo::Stdout,
(None, false) => OutputTo::Split,
};
let _ = OUTPUT_TO.set(output_to);
Ok(())
}
pub fn output_to() -> OutputTo {
OUTPUT_TO.get().copied().unwrap_or(OutputTo::Split)
}
pub fn emit_cli_error(
msg: &str,
hint: Option<&str>,
format: OutputFormat,
) -> Result<(), agent_first_data::CliEmitterError> {
let mut emitter =
agent_first_data::CliEmitter::from_output_to(output_to(), format).with_strict_protocol();
let event = agent_first_data::json_error(crate::protocol::error_code::INVALID_REQUEST, msg)
.hint_if_some(hint)
.build()
.map_err(agent_first_data::CliEmitterError::Build)?;
emitter.emit(event)
}
pub fn emit_value(
value: Value,
format: OutputFormat,
) -> Result<(), agent_first_data::CliEmitterError> {
let mut emitter =
agent_first_data::CliEmitter::from_output_to(output_to(), format).with_strict_protocol();
emitter.emit_validated_value(value)
}
pub fn emit_output(
out: &Output,
format: OutputFormat,
) -> Result<(), agent_first_data::CliEmitterError> {
output_fmt::emit_process_output(out, format, output_to())
}
#[allow(clippy::disallowed_methods)]
pub fn write_result_text(text: &str) -> std::io::Result<()> {
let mut writer: Box<dyn Write> = match output_to() {
OutputTo::Stderr => Box::new(std::io::stderr()),
OutputTo::Split | OutputTo::Stdout => Box::new(std::io::stdout()),
};
writer.write_all(text.as_bytes())?;
writer.flush()
}