Suppress ansi with pipes (#3775)

Co-authored-by: Douwe Osinga <douwe@squareup.com>
This commit is contained in:
Douwe Osinga
2025-08-05 11:37:02 +02:00
committed by GitHub
parent 97e508b3a1
commit bef7622123
5 changed files with 51 additions and 28 deletions
+2
View File
@@ -59,3 +59,5 @@ do_not_version/
/ui/desktop/src/bin/goose-scheduler-executor /ui/desktop/src/bin/goose-scheduler-executor
/ui/desktop/src/bin/goose /ui/desktop/src/bin/goose
/.env /.env
/working_dir
Generated
+7 -5
View File
@@ -3379,6 +3379,7 @@ dependencies = [
name = "goose-cli" name = "goose-cli"
version = "1.1.0" version = "1.1.0"
dependencies = [ dependencies = [
"anstream",
"anyhow", "anyhow",
"async-trait", "async-trait",
"axum 0.8.1", "axum 0.8.1",
@@ -3396,6 +3397,7 @@ dependencies = [
"goose-mcp", "goose-mcp",
"http 1.2.0", "http 1.2.0",
"indicatif", "indicatif",
"is-terminal",
"jsonschema", "jsonschema",
"mcp-client", "mcp-client",
"mcp-core", "mcp-core",
@@ -3631,9 +3633,9 @@ checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
[[package]] [[package]]
name = "hermit-abi" name = "hermit-abi"
version = "0.4.0" version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c"
[[package]] [[package]]
name = "hex" name = "hex"
@@ -4246,11 +4248,11 @@ checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130"
[[package]] [[package]]
name = "is-terminal" name = "is-terminal"
version = "0.4.15" version = "0.4.16"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e19b23d53f35ce9f56aebc7d1bb4e6ac1e9c0db7ac85c8d1760c04379edced37" checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9"
dependencies = [ dependencies = [
"hermit-abi 0.4.0", "hermit-abi 0.5.2",
"libc", "libc",
"windows-sys 0.59.0", "windows-sys 0.59.0",
] ]
+2
View File
@@ -56,6 +56,8 @@ http = "1.0"
webbrowser = "1.0" webbrowser = "1.0"
indicatif = "0.17.11" indicatif = "0.17.11"
tokio-util = "0.7.15" tokio-util = "0.7.15"
is-terminal = "0.4.16"
anstream = "0.6.18"
[target.'cfg(target_os = "windows")'.dependencies] [target.'cfg(target_os = "windows")'.dependencies]
winapi = { version = "0.3", features = ["wincred"] } winapi = { version = "0.3", features = ["wincred"] }
+5 -4
View File
@@ -1,5 +1,6 @@
use std::collections::HashMap; use std::collections::HashMap;
use anstream::println;
use console::style; use console::style;
use goose::recipe::{Recipe, BUILT_IN_RECIPE_DIR_PARAM}; use goose::recipe::{Recipe, BUILT_IN_RECIPE_DIR_PARAM};
@@ -81,16 +82,16 @@ pub fn missing_parameters_command_line(missing_params: Vec<String>) -> String {
} }
pub fn print_recipe_info(recipe: &Recipe, params: Vec<(String, String)>) { pub fn print_recipe_info(recipe: &Recipe, params: Vec<(String, String)>) {
println!( eprintln!(
"{} {}", "{} {}",
style("Loading recipe:").green().bold(), style("Loading recipe:").green().bold(),
style(&recipe.title).green() style(&recipe.title).green()
); );
println!("{} {}", style("Description:").bold(), &recipe.description); eprintln!("{} {}", style("Description:").bold(), &recipe.description);
if !params.is_empty() { if !params.is_empty() {
println!("{}", style("Parameters used to load this recipe:").bold()); eprintln!("{}", style("Parameters used to load this recipe:").bold());
print_parameters_with_values(params.into_iter().collect()); print_parameters_with_values(params.into_iter().collect());
} }
println!(); eprintln!();
} }
+35 -19
View File
@@ -1,3 +1,4 @@
use anstream::println;
use bat::WrappingMode; use bat::WrappingMode;
use console::{style, Color}; use console::{style, Color};
use goose::config::Config; use goose::config::Config;
@@ -11,11 +12,10 @@ use rmcp::model::PromptArgument;
use serde_json::Value; use serde_json::Value;
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::HashMap; use std::collections::HashMap;
use std::io::{Error, Write}; use std::io::{Error, IsTerminal, Write};
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::sync::Arc; use std::sync::Arc;
use std::time::Duration; use std::time::Duration;
// Re-export theme for use in main // Re-export theme for use in main
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub enum Theme { pub enum Theme {
@@ -135,11 +135,15 @@ thread_local! {
} }
pub fn show_thinking() { pub fn show_thinking() {
THINKING.with(|t| t.borrow_mut().show()); if std::io::stdout().is_terminal() {
THINKING.with(|t| t.borrow_mut().show());
}
} }
pub fn hide_thinking() { pub fn hide_thinking() {
THINKING.with(|t| t.borrow_mut().hide()); if std::io::stdout().is_terminal() {
THINKING.with(|t| t.borrow_mut().hide());
}
} }
pub fn is_showing_thinking() -> bool { pub fn is_showing_thinking() -> bool {
@@ -147,11 +151,13 @@ pub fn is_showing_thinking() -> bool {
} }
pub fn set_thinking_message(s: &String) { pub fn set_thinking_message(s: &String) {
THINKING.with(|t| { if std::io::stdout().is_terminal() {
if let Some(spinner) = t.borrow_mut().spinner.as_mut() { THINKING.with(|t| {
spinner.set_message(s); if let Some(spinner) = t.borrow_mut().spinner.as_mut() {
} spinner.set_message(s);
}); }
});
}
} }
pub fn render_message(message: &Message, debug: bool) { pub fn render_message(message: &Message, debug: bool) {
@@ -166,7 +172,9 @@ pub fn render_message(message: &Message, debug: bool) {
println!("Image: [data: {}, type: {}]", image.data, image.mime_type); println!("Image: [data: {}, type: {}]", image.data, image.mime_type);
} }
MessageContent::Thinking(thinking) => { MessageContent::Thinking(thinking) => {
if std::env::var("GOOSE_CLI_SHOW_THINKING").is_ok() { if std::env::var("GOOSE_CLI_SHOW_THINKING").is_ok()
&& std::io::stdout().is_terminal()
{
println!("\n{}", style("Thinking:").dim().italic()); println!("\n{}", style("Thinking:").dim().italic());
print_markdown(&thinking.thinking, theme); print_markdown(&thinking.thinking, theme);
} }
@@ -190,6 +198,10 @@ pub fn render_text(text: &str, color: Option<Color>, dim: bool) {
} }
pub fn render_text_no_newlines(text: &str, color: Option<Color>, dim: bool) { pub fn render_text_no_newlines(text: &str, color: Option<Color>, dim: bool) {
if !std::io::stdout().is_terminal() {
println!("{}", text);
return;
}
let mut styled_text = style(text); let mut styled_text = style(text);
if dim { if dim {
styled_text = styled_text.dim(); styled_text = styled_text.dim();
@@ -472,14 +484,18 @@ pub fn env_no_color() -> bool {
} }
fn print_markdown(content: &str, theme: Theme) { fn print_markdown(content: &str, theme: Theme) {
bat::PrettyPrinter::new() if std::io::stdout().is_terminal() {
.input(bat::Input::from_bytes(content.as_bytes())) bat::PrettyPrinter::new()
.theme(theme.as_str()) .input(bat::Input::from_bytes(content.as_bytes()))
.colored_output(env_no_color()) .theme(theme.as_str())
.language("Markdown") .colored_output(env_no_color())
.wrapping_mode(WrappingMode::NoWrapping(true)) .language("Markdown")
.print() .wrapping_mode(WrappingMode::NoWrapping(true))
.unwrap(); .print()
.unwrap();
} else {
print!("{}", content);
}
} }
const INDENT: &str = " "; const INDENT: &str = " ";
@@ -768,7 +784,7 @@ pub async fn display_cost_usage(
) { ) {
if let Some(cost) = estimate_cost_usd(provider, model, input_tokens, output_tokens).await { if let Some(cost) = estimate_cost_usd(provider, model, input_tokens, output_tokens).await {
use console::style; use console::style;
println!( eprintln!(
"Cost: {} USD ({} tokens: in {}, out {})", "Cost: {} USD ({} tokens: in {}, out {})",
style(format!("${:.4}", cost)).cyan(), style(format!("${:.4}", cost)).cyan(),
input_tokens + output_tokens, input_tokens + output_tokens,