feat: add --output-format json flag to goose run command (#5525)
Signed-off-by: sheikhlimon <sheikhlimon404@gmail.com> Signed-off-by: Sheikh Limon <sheikhlimon404@gmail.com>
This commit is contained in:
@@ -41,6 +41,7 @@ use goose::config::paths::Paths;
|
||||
use goose::conversation::message::{Message, MessageContent};
|
||||
use rand::{distributions::Alphanumeric, Rng};
|
||||
use rustyline::EditMode;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
use std::collections::HashMap;
|
||||
use std::path::PathBuf;
|
||||
@@ -50,6 +51,18 @@ use tokio;
|
||||
use tokio_util::sync::CancellationToken;
|
||||
use tracing::warn;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
struct JsonOutput {
|
||||
messages: Vec<Message>,
|
||||
metadata: JsonMetadata,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
struct JsonMetadata {
|
||||
total_tokens: Option<i32>,
|
||||
status: String,
|
||||
}
|
||||
|
||||
pub enum RunMode {
|
||||
Normal,
|
||||
Plan,
|
||||
@@ -66,6 +79,7 @@ pub struct CliSession {
|
||||
max_turns: Option<u32>,
|
||||
edit_mode: Option<EditMode>,
|
||||
retry_config: Option<RetryConfig>,
|
||||
output_format: String,
|
||||
}
|
||||
|
||||
// Cache structure for completion data
|
||||
@@ -120,6 +134,7 @@ pub async fn classify_planner_response(
|
||||
}
|
||||
|
||||
impl CliSession {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub async fn new(
|
||||
agent: Agent,
|
||||
session_id: String,
|
||||
@@ -128,6 +143,7 @@ impl CliSession {
|
||||
max_turns: Option<u32>,
|
||||
edit_mode: Option<EditMode>,
|
||||
retry_config: Option<RetryConfig>,
|
||||
output_format: String,
|
||||
) -> Self {
|
||||
let messages = SessionManager::get_session(&session_id, true)
|
||||
.await
|
||||
@@ -145,6 +161,7 @@ impl CliSession {
|
||||
max_turns,
|
||||
edit_mode,
|
||||
retry_config,
|
||||
output_format,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -760,6 +777,9 @@ impl CliSession {
|
||||
) -> Result<()> {
|
||||
let cancel_token_clone = cancel_token.clone();
|
||||
|
||||
// Cache the output format check to avoid repeated string comparisons in the hot loop
|
||||
let is_json_mode = self.output_format == "json";
|
||||
|
||||
let session_config = SessionConfig {
|
||||
id: self.session_id.clone(),
|
||||
schedule_id: self.scheduled_job_id.clone(),
|
||||
@@ -891,11 +911,16 @@ impl CliSession {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
self.messages.push(message.clone());
|
||||
|
||||
if interactive {output::hide_thinking()};
|
||||
let _ = progress_bars.hide();
|
||||
output::render_message(&message, self.debug);
|
||||
|
||||
// Don't render in JSON mode
|
||||
if !is_json_mode {
|
||||
output::render_message(&message, self.debug);
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(Ok(AgentEvent::McpNotification((_id, message)))) => {
|
||||
@@ -967,17 +992,21 @@ impl CliSession {
|
||||
// TODO: proper display for subagent notifications
|
||||
if interactive {
|
||||
let _ = progress_bars.hide();
|
||||
println!("{}", console::style(&formatted_message).green().dim());
|
||||
} else {
|
||||
if !is_json_mode {
|
||||
println!("{}", console::style(&formatted_message).green().dim());
|
||||
}
|
||||
} else if !is_json_mode {
|
||||
progress_bars.log(&formatted_message);
|
||||
}
|
||||
} else if let Some(ref notification_type) = message_notification_type {
|
||||
if notification_type == TASK_EXECUTION_NOTIFICATION_TYPE {
|
||||
if interactive {
|
||||
let _ = progress_bars.hide();
|
||||
print!("{}", formatted_message);
|
||||
std::io::stdout().flush().unwrap();
|
||||
} else {
|
||||
if !is_json_mode {
|
||||
print!("{}", formatted_message);
|
||||
std::io::stdout().flush().unwrap();
|
||||
}
|
||||
} else if !is_json_mode {
|
||||
print!("{}", formatted_message);
|
||||
std::io::stdout().flush().unwrap();
|
||||
}
|
||||
@@ -1056,7 +1085,29 @@ impl CliSession {
|
||||
}
|
||||
}
|
||||
}
|
||||
println!();
|
||||
|
||||
// Output JSON if requested
|
||||
if is_json_mode {
|
||||
let metadata = match SessionManager::get_session(&self.session_id, false).await {
|
||||
Ok(session) => JsonMetadata {
|
||||
total_tokens: session.total_tokens,
|
||||
status: "completed".to_string(),
|
||||
},
|
||||
Err(_) => JsonMetadata {
|
||||
total_tokens: None,
|
||||
status: "completed".to_string(),
|
||||
},
|
||||
};
|
||||
|
||||
let json_output = JsonOutput {
|
||||
messages: self.messages.messages().to_vec(),
|
||||
metadata,
|
||||
};
|
||||
|
||||
println!("{}", serde_json::to_string_pretty(&json_output)?);
|
||||
} else {
|
||||
println!();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user