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
3 changes: 1 addition & 2 deletions crates/goose-cli/src/session/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -187,8 +187,7 @@ async fn offer_extension_debugging_help(
// Create a debugging prompt with context about the extension failure
let debug_prompt = format!(
"I'm having trouble starting an extension called '{}'. Here's the error I encountered:\n\n{}\n\nCan you help me diagnose what might be wrong and suggest how to fix it? Please consider common issues like:\n- Missing dependencies or tools\n- Configuration problems\n- Network connectivity (for remote extensions)\n- Permission issues\n- Path or environment variable problems",
extension_name,
error_message
extension_name, error_message
);

// Create a minimal agent for debugging
Expand Down
11 changes: 8 additions & 3 deletions crates/goose-cli/src/session/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,9 @@ pub async fn classify_planner_response(
message_text: String,
provider: Arc<dyn Provider>,
) -> Result<PlannerResponseType> {
let prompt = format!("The text below is the output from an AI model which can either provide a plan or list of clarifying questions. Based on the text below, decide if the output is a \"plan\" or \"clarifying questions\".\n---\n{message_text}");
let prompt = format!(
"The text below is the output from an AI model which can either provide a plan or list of clarifying questions. Based on the text below, decide if the output is a \"plan\" or \"clarifying questions\".\n---\n{message_text}"
);

let message = Message::user().with_text(&prompt);
let model_config = provider.get_model_config();
Expand Down Expand Up @@ -628,6 +630,7 @@ impl CliSession {

let _provider = self.agent.provider().await?;

println!();
output::run_status_hook("thinking");
output::show_thinking();
let start_time = Instant::now();
Expand Down Expand Up @@ -1751,7 +1754,9 @@ fn display_log_notification(
let _ = progress_bars.hide();
}
if !is_json_mode {
print!("{}", formatted_message);
for line in formatted_message.lines() {
println!(" {}", console::style(line).dim());
}
std::io::stdout().flush().unwrap();
}
} else if ntype == "shell_output" {
Expand All @@ -1766,7 +1771,7 @@ fn display_log_notification(
let _ = progress_bars.hide();
}
if !is_json_mode {
println!("{}", formatted_message);
println!(" {}", console::style(formatted_message).dim());
}
}
}
Expand Down
52 changes: 47 additions & 5 deletions crates/goose-cli/src/session/output.rs
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ pub fn render_message(message: &Message, debug: bool) {
},
MessageContent::Text(text) => print_markdown(&text.text, theme),
MessageContent::ToolRequest(req) => render_tool_request(req, theme, debug),
MessageContent::ToolResponse(resp) => render_tool_response(resp, theme, debug),
MessageContent::ToolResponse(resp) => render_tool_response(resp, debug),
MessageContent::Image(image) => {
println!("Image: [data: {}, type: {}]", image.data, image.mime_type);
}
Expand Down Expand Up @@ -295,7 +295,7 @@ pub fn render_message_streaming(
}
MessageContent::ToolResponse(resp) => {
flush_markdown_buffer(buffer, theme);
render_tool_response(resp, theme, debug);
render_tool_response(resp, debug);
}
MessageContent::ActionRequired(action) => {
flush_markdown_buffer(buffer, theme);
Expand Down Expand Up @@ -491,7 +491,7 @@ fn render_tool_request(req: &ToolRequest, theme: Theme, debug: bool) {
}
}

fn render_tool_response(resp: &ToolResponse, theme: Theme, debug: bool) {
fn render_tool_response(resp: &ToolResponse, debug: bool) {
let config = Config::global();

match &resp.tool_result {
Expand Down Expand Up @@ -519,11 +519,52 @@ fn render_tool_response(resp: &ToolResponse, theme: Theme, debug: bool) {
if debug {
println!("{:#?}", content);
} else if let Some(text) = content.as_text() {
print_markdown(&text.text, theme);
print_tool_output(&text.text);
}
}
}
Err(e) => print_markdown(&e.to_string(), theme),
Err(e) => {
println!(" {}", style(e.to_string()).red().dim());
}
}
}

fn print_tool_output(text: &str) {
if text.is_empty() {
return;
}
if !std::io::stdout().is_terminal() {
print!("{}", text);
return;
}
let max_lines = if get_show_full_tool_output() {
Comment thread
michaelneale marked this conversation as resolved.
usize::MAX
} else {
20
Comment on lines +540 to +543
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Don't truncate tool output in headless TTY sessions

This truncates long tool responses whenever stdout is a terminal, but CliSession::process_message()/headless() still render through render_message_streaming() with interactive = false. As a result, a normal goose run '…' launched from a terminal now drops everything past 20 lines even though there is no input loop where the user could recover the hidden lines, so long shell/read outputs and stack traces become irretrievable in common non-interactive runs.

Useful? React with 👍 / 👎.

};
let lines: Vec<&str> = text.lines().collect();
if lines.len() <= max_lines {
for line in &lines {
println!(" {}", style(line).dim());
}
} else {
Comment thread
michaelneale marked this conversation as resolved.
let head = max_lines / 2;
let tail = max_lines - head;
for line in &lines[..head] {
println!(" {}", style(line).dim());
}
println!(
" {}",
style(format!(
"... ({} lines hidden, /toggle to show all)",
lines.len() - head - tail
Comment on lines +558 to +560
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Show the real command for expanding truncated tool output

The truncation banner tells users to run /toggle, but the command parser and help text only expose /r (crates/goose-cli/src/session/input.rs:311 and :414). In interactive sessions the advertised escape hatch for hidden lines will therefore be rejected, so users who hit truncation are pointed at a command that does not exist.

Useful? React with 👍 / 👎.

))
.dim()
.italic()
);
for line in &lines[lines.len() - tail..] {
println!(" {}", style(line).dim());
}
}
}

Expand Down Expand Up @@ -904,6 +945,7 @@ fn print_tool_header(call: &CallToolRequestParams) {
)
};
println!();
println!(" {}", style("─".repeat(40)).dim());
println!("{}", tool_header);
}

Expand Down
34 changes: 9 additions & 25 deletions crates/goose-cli/src/session/task_execution_display/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,6 @@ use std::sync::atomic::{AtomicBool, Ordering};
#[cfg(test)]
mod tests;

const CLEAR_SCREEN: &str = "\x1b[2J\x1b[H";
const MOVE_TO_PROGRESS_LINE: &str = "\x1b[4;1H";
const CLEAR_TO_EOL: &str = "\x1b[K";
const CLEAR_BELOW: &str = "\x1b[J";
pub const TASK_EXECUTION_NOTIFICATION_TYPE: &str = "task_execution";

static INITIAL_SHOWN: AtomicBool = AtomicBool::new(false);
Expand Down Expand Up @@ -85,18 +81,14 @@ fn format_tasks_update_from_event(event: &TaskExecutionNotificationEvent) -> Str
let mut display = String::new();

if !INITIAL_SHOWN.swap(true, Ordering::SeqCst) {
display.push_str(CLEAR_SCREEN);
display.push_str("🎯 Task Execution Dashboard\n");
display.push_str("═══════════════════════════\n\n");
Comment on lines 83 to 85
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reset the task dashboard header for later executions

The header is now emitted only while INITIAL_SHOWN is false, but after removing the cursor-reposition refresh path nothing preserves that header across separate task executions. Because TasksComplete never clears the flag, the second subrecipe/subagent execution in the same CLI session appends a new block that starts at 📊 Progress with no 🎯 Task Execution Dashboard label, which makes later task runs hard to identify.

Useful? React with 👍 / 👎.

} else {
display.push_str(MOVE_TO_PROGRESS_LINE);
}

display.push_str(&format!(
"📊 Progress: {} total | ⏳ {} pending | 🏃 {} running | ✅ {} completed | ❌ {} failed",
"📊 Progress: {} total | ⏳ {} pending | 🏃 {} running | ✅ {} completed | ❌ {} failed\n\n",
stats.total, stats.pending, stats.running, stats.completed, stats.failed
));
display.push_str(&format!("{}\n\n", CLEAR_TO_EOL));

let mut sorted_tasks = tasks.clone();
sorted_tasks.sort_by(|a, b| a.id.cmp(&b.id));
Expand All @@ -105,7 +97,6 @@ fn format_tasks_update_from_event(event: &TaskExecutionNotificationEvent) -> Str
display.push_str(&format_task_display(&task));
}

display.push_str(CLEAR_BELOW);
display
} else {
String::new()
Expand Down Expand Up @@ -155,48 +146,41 @@ fn format_task_display(task: &TaskInfo) -> String {
};

task_display.push_str(&format!(
"{} {} ({}){}\n",
status_icon, task.task_name, task.task_type, CLEAR_TO_EOL
"{} {} ({})\n",
status_icon, task.task_name, task.task_type
));

if !task.task_metadata.is_empty() {
task_display.push_str(&format!(
" 📋 Parameters: {}{}\n",
task.task_metadata, CLEAR_TO_EOL
));
task_display.push_str(&format!(" 📋 Parameters: {}\n", task.task_metadata));
}

if let Some(duration_secs) = task.duration_secs {
task_display.push_str(&format!(" ⏱️ {:.1}s{}\n", duration_secs, CLEAR_TO_EOL));
task_display.push_str(&format!(" ⏱️ {:.1}s\n", duration_secs));
}

if matches!(task.status, TaskStatus::Running) && !task.current_output.trim().is_empty() {
let processed_output = process_output_for_display(&task.current_output);
if !processed_output.is_empty() {
task_display.push_str(&format!(" 💬 {}{}\n", processed_output, CLEAR_TO_EOL));
task_display.push_str(&format!(" 💬 {}\n", processed_output));
}
}

if matches!(task.status, TaskStatus::Completed) {
if let Some(result_data) = &task.result_data {
let result_preview = format_result_data_for_display(result_data);
if !result_preview.is_empty() {
task_display.push_str(&format!(" 📄 {}{}\n", result_preview, CLEAR_TO_EOL));
task_display.push_str(&format!(" 📄 {}\n", result_preview));
}
}
}

if matches!(task.status, TaskStatus::Failed) {
if let Some(error) = &task.error {
let error_preview = safe_truncate(error, 80);
task_display.push_str(&format!(
" ⚠️ {}{}\n",
error_preview.replace('\n', " "),
CLEAR_TO_EOL
));
task_display.push_str(&format!(" ⚠️ {}\n", error_preview.replace('\n', " ")));
}
}

task_display.push_str(&format!("{}\n", CLEAR_TO_EOL));
task_display.push('\n');
task_display
}
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ fn test_format_tasks_update_from_event() {

let result2 = format_tasks_update_from_event(&event);
assert!(!result2.contains("🎯 Task Execution Dashboard"));
assert!(result2.contains(MOVE_TO_PROGRESS_LINE));
assert!(result2.contains("📊 Progress:"));
}

#[test]
Expand Down
Loading