feat(cli): add --debug flag to goose session / run (#1564)

This commit is contained in:
Kalvin C
2025-03-06 18:58:25 -08:00
committed by GitHub
parent 798d657e7e
commit 7b37ab0b52
5 changed files with 91 additions and 40 deletions
+8 -2
View File
@@ -83,8 +83,14 @@ async fn run_eval(
let requirements = evaluation.required_extensions(); let requirements = evaluation.required_extensions();
// Create session with error capture // Create session with error capture
let base_session = let base_session = build_session(
build_session(None, false, requirements.external, requirements.builtin).await; None,
false,
requirements.external,
requirements.builtin,
false,
)
.await;
let bench_session = Arc::new(Mutex::new(BenchSession::new(base_session))); let bench_session = Arc::new(Mutex::new(BenchSession::new(base_session)));
let bench_session_clone = bench_session.clone(); let bench_session_clone = bench_session.clone();
+21 -1
View File
@@ -90,6 +90,14 @@ enum Command {
)] )]
resume: bool, resume: bool,
/// Enable debug output mode
#[arg(
long,
help = "Enable debug output mode with full content and no truncation",
long_help = "When enabled, shows complete tool responses without truncation and full paths."
)]
debug: bool,
/// Add stdio extensions with environment variables and commands /// Add stdio extensions with environment variables and commands
#[arg( #[arg(
long = "with-extension", long = "with-extension",
@@ -157,6 +165,14 @@ enum Command {
)] )]
resume: bool, resume: bool,
/// Enable debug output mode
#[arg(
long,
help = "Enable debug output mode with full content and no truncation",
long_help = "When enabled, shows complete tool responses without truncation and full paths."
)]
debug: bool,
/// Add stdio extensions with environment variables and commands /// Add stdio extensions with environment variables and commands
#[arg( #[arg(
long = "with-extension", long = "with-extension",
@@ -285,6 +301,7 @@ async fn main() -> Result<()> {
Some(Command::Session { Some(Command::Session {
identifier, identifier,
resume, resume,
debug,
extension, extension,
builtin, builtin,
}) => { }) => {
@@ -293,6 +310,7 @@ async fn main() -> Result<()> {
resume, resume,
extension, extension,
builtin, builtin,
debug,
) )
.await; .await;
@@ -306,6 +324,7 @@ async fn main() -> Result<()> {
interactive, interactive,
identifier, identifier,
resume, resume,
debug,
extension, extension,
builtin, builtin,
}) => { }) => {
@@ -332,6 +351,7 @@ async fn main() -> Result<()> {
resume, resume,
extension, extension,
builtin, builtin,
debug,
) )
.await; .await;
setup_logging(session.session_file().file_stem().and_then(|s| s.to_str()))?; setup_logging(session.session_file().file_stem().and_then(|s| s.to_str()))?;
@@ -410,7 +430,7 @@ async fn main() -> Result<()> {
return Ok(()); return Ok(());
} else { } else {
// Run session command by default // Run session command by default
let mut session = build_session(None, false, vec![], vec![]).await; let mut session = build_session(None, false, vec![], vec![], false).await;
setup_logging(session.session_file().file_stem().and_then(|s| s.to_str()))?; setup_logging(session.session_file().file_stem().and_then(|s| s.to_str()))?;
let _ = session.interactive(None).await; let _ = session.interactive(None).await;
return Ok(()); return Ok(());
+2 -1
View File
@@ -15,6 +15,7 @@ pub async fn build_session(
resume: bool, resume: bool,
extensions: Vec<String>, extensions: Vec<String>,
builtins: Vec<String>, builtins: Vec<String>,
debug: bool,
) -> Session { ) -> Session {
// Load config and get provider/model // Load config and get provider/model
let config = Config::global(); let config = Config::global();
@@ -92,7 +93,7 @@ pub async fn build_session(
}; };
// Create new session // Create new session
let mut session = Session::new(agent, session_file.clone()); let mut session = Session::new(agent, session_file.clone(), debug);
// Add extensions if provided // Add extensions if provided
for extension_str in extensions { for extension_str in extensions {
+14 -6
View File
@@ -36,6 +36,7 @@ pub struct Session {
session_file: PathBuf, session_file: PathBuf,
// Cache for completion data - using std::sync for thread safety without async // Cache for completion data - using std::sync for thread safety without async
completion_cache: Arc<std::sync::RwLock<CompletionCache>>, completion_cache: Arc<std::sync::RwLock<CompletionCache>>,
debug: bool, // New field for debug mode
} }
// Cache structure for completion data // Cache structure for completion data
@@ -56,7 +57,7 @@ impl CompletionCache {
} }
impl Session { impl Session {
pub fn new(agent: Box<dyn Agent>, session_file: PathBuf) -> Self { pub fn new(agent: Box<dyn Agent>, session_file: PathBuf, debug: bool) -> Self {
let messages = match session::read_messages(&session_file) { let messages = match session::read_messages(&session_file) {
Ok(msgs) => msgs, Ok(msgs) => msgs,
Err(e) => { Err(e) => {
@@ -70,6 +71,7 @@ impl Session {
messages, messages,
session_file, session_file,
completion_cache: Arc::new(std::sync::RwLock::new(CompletionCache::new())), completion_cache: Arc::new(std::sync::RwLock::new(CompletionCache::new())),
debug,
} }
} }
@@ -393,7 +395,7 @@ impl Session {
} }
if msg.role == mcp_core::Role::User { if msg.role == mcp_core::Role::User {
output::render_message(&msg); output::render_message(&msg, self.debug);
} }
self.messages.push(msg); self.messages.push(msg);
} }
@@ -461,7 +463,7 @@ impl Session {
session::persist_messages(&self.session_file, &self.messages, None).await?; session::persist_messages(&self.session_file, &self.messages, None).await?;
if interactive {output::hide_thinking()}; if interactive {output::hide_thinking()};
output::render_message(&message); output::render_message(&message, self.debug);
if interactive {output::show_thinking()}; if interactive {output::show_thinking()};
} }
} }
@@ -547,7 +549,7 @@ impl Session {
// No need for description update here // No need for description update here
session::persist_messages(&self.session_file, &self.messages, None).await?; session::persist_messages(&self.session_file, &self.messages, None).await?;
output::render_message(&Message::assistant().with_text(&prompt)); output::render_message(&Message::assistant().with_text(&prompt), self.debug);
} else { } else {
// An interruption occurred outside of a tool request-response. // An interruption occurred outside of a tool request-response.
if let Some(last_msg) = self.messages.last() { if let Some(last_msg) = self.messages.last() {
@@ -562,13 +564,19 @@ impl Session {
session::persist_messages(&self.session_file, &self.messages, None) session::persist_messages(&self.session_file, &self.messages, None)
.await?; .await?;
output::render_message(&Message::assistant().with_text(prompt)); output::render_message(
&Message::assistant().with_text(prompt),
self.debug,
);
} }
Some(_) => { Some(_) => {
// A real users message // A real users message
self.messages.pop(); self.messages.pop();
let prompt = "Interrupted before the model replied and removed the last message."; let prompt = "Interrupted before the model replied and removed the last message.";
output::render_message(&Message::assistant().with_text(prompt)); output::render_message(
&Message::assistant().with_text(prompt),
self.debug,
);
} }
None => panic!("No content in last message"), None => panic!("No content in last message"),
} }
+46 -30
View File
@@ -96,14 +96,14 @@ pub fn hide_thinking() {
THINKING.with(|t| t.borrow_mut().hide()); THINKING.with(|t| t.borrow_mut().hide());
} }
pub fn render_message(message: &Message) { pub fn render_message(message: &Message, debug: bool) {
let theme = get_theme(); let theme = get_theme();
for content in &message.content { for content in &message.content {
match content { match content {
MessageContent::Text(text) => print_markdown(&text.text, theme), MessageContent::Text(text) => print_markdown(&text.text, theme),
MessageContent::ToolRequest(req) => render_tool_request(req, theme), MessageContent::ToolRequest(req) => render_tool_request(req, theme, debug),
MessageContent::ToolResponse(resp) => render_tool_response(resp, theme), MessageContent::ToolResponse(resp) => render_tool_response(resp, theme, debug),
MessageContent::Image(image) => { MessageContent::Image(image) => {
println!("Image: [data: {}, type: {}]", image.data, image.mime_type); println!("Image: [data: {}, type: {}]", image.data, image.mime_type);
} }
@@ -126,18 +126,18 @@ pub fn render_message(message: &Message) {
println!(); println!();
} }
fn render_tool_request(req: &ToolRequest, theme: Theme) { fn render_tool_request(req: &ToolRequest, theme: Theme, debug: bool) {
match &req.tool_call { match &req.tool_call {
Ok(call) => match call.name.as_str() { Ok(call) => match call.name.as_str() {
"developer__text_editor" => render_text_editor_request(call), "developer__text_editor" => render_text_editor_request(call, debug),
"developer__shell" => render_shell_request(call), "developer__shell" => render_shell_request(call, debug),
_ => render_default_request(call), _ => render_default_request(call, debug),
}, },
Err(e) => print_markdown(&e.to_string(), theme), Err(e) => print_markdown(&e.to_string(), theme),
} }
} }
fn render_tool_response(resp: &ToolResponse, theme: Theme) { fn render_tool_response(resp: &ToolResponse, theme: Theme, debug: bool) {
let config = Config::global(); let config = Config::global();
match &resp.tool_result { match &resp.tool_result {
@@ -157,12 +157,14 @@ fn render_tool_response(resp: &ToolResponse, theme: Theme) {
if content if content
.priority() .priority()
.is_some_and(|priority| priority < min_priority) .is_some_and(|priority| priority < min_priority)
|| content.priority().is_none() || (content.priority().is_none() && !debug)
{ {
continue; continue;
} }
if let mcp_core::content::Content::Text(text) = content { if debug {
println!("{:#?}", content);
} else if let mcp_core::content::Content::Text(text) = content {
print_markdown(&text.text, theme); print_markdown(&text.text, theme);
} }
} }
@@ -266,7 +268,7 @@ pub fn render_builtin_error(names: &str, error: &str) {
println!(); println!();
} }
fn render_text_editor_request(call: &ToolCall) { fn render_text_editor_request(call: &ToolCall, debug: bool) {
print_tool_header(call); print_tool_header(call);
// Print path first with special formatting // Print path first with special formatting
@@ -274,7 +276,7 @@ fn render_text_editor_request(call: &ToolCall) {
println!( println!(
"{}: {}", "{}: {}",
style("path").dim(), style("path").dim(),
style(shorten_path(path)).green() style(shorten_path(path, debug)).green()
); );
} }
@@ -286,26 +288,26 @@ fn render_text_editor_request(call: &ToolCall) {
other_args.insert(k.clone(), v.clone()); other_args.insert(k.clone(), v.clone());
} }
} }
print_params(&Value::Object(other_args), 0); print_params(&Value::Object(other_args), 0, debug);
} }
println!(); println!();
} }
fn render_shell_request(call: &ToolCall) { fn render_shell_request(call: &ToolCall, debug: bool) {
print_tool_header(call); print_tool_header(call);
match call.arguments.get("command") { match call.arguments.get("command") {
Some(Value::String(s)) => { Some(Value::String(s)) => {
println!("{}: {}", style("command").dim(), style(s).green()); println!("{}: {}", style("command").dim(), style(s).green());
} }
_ => print_params(&call.arguments, 0), _ => print_params(&call.arguments, 0, debug),
} }
println!(); println!();
} }
fn render_default_request(call: &ToolCall) { fn render_default_request(call: &ToolCall, debug: bool) {
print_tool_header(call); print_tool_header(call);
print_params(&call.arguments, 0); print_params(&call.arguments, 0, debug);
println!(); println!();
} }
@@ -342,7 +344,7 @@ fn print_markdown(content: &str, theme: Theme) {
const MAX_STRING_LENGTH: usize = 40; const MAX_STRING_LENGTH: usize = 40;
const INDENT: &str = " "; const INDENT: &str = " ";
fn print_params(value: &Value, depth: usize) { fn print_params(value: &Value, depth: usize, debug: bool) {
let indent = INDENT.repeat(depth); let indent = INDENT.repeat(depth);
match value { match value {
@@ -351,17 +353,17 @@ fn print_params(value: &Value, depth: usize) {
match val { match val {
Value::Object(_) => { Value::Object(_) => {
println!("{}{}:", indent, style(key).dim()); println!("{}{}:", indent, style(key).dim());
print_params(val, depth + 1); print_params(val, depth + 1, debug);
} }
Value::Array(arr) => { Value::Array(arr) => {
println!("{}{}:", indent, style(key).dim()); println!("{}{}:", indent, style(key).dim());
for item in arr.iter() { for item in arr.iter() {
println!("{}{}- ", indent, INDENT); println!("{}{}- ", indent, INDENT);
print_params(item, depth + 2); print_params(item, depth + 2, debug);
} }
} }
Value::String(s) => { Value::String(s) => {
if s.len() > MAX_STRING_LENGTH { if !debug && s.len() > MAX_STRING_LENGTH {
println!("{}{}: {}", indent, style(key).dim(), style("...").dim()); println!("{}{}: {}", indent, style(key).dim(), style("...").dim());
} else { } else {
println!("{}{}: {}", indent, style(key).dim(), style(s).green()); println!("{}{}: {}", indent, style(key).dim(), style(s).green());
@@ -382,11 +384,11 @@ fn print_params(value: &Value, depth: usize) {
Value::Array(arr) => { Value::Array(arr) => {
for (i, item) in arr.iter().enumerate() { for (i, item) in arr.iter().enumerate() {
println!("{}{}.", indent, i + 1); println!("{}{}.", indent, i + 1);
print_params(item, depth + 1); print_params(item, depth + 1, debug);
} }
} }
Value::String(s) => { Value::String(s) => {
if s.len() > MAX_STRING_LENGTH { if !debug && s.len() > MAX_STRING_LENGTH {
println!( println!(
"{}{}", "{}{}",
indent, indent,
@@ -408,7 +410,12 @@ fn print_params(value: &Value, depth: usize) {
} }
} }
fn shorten_path(path: &str) -> String { fn shorten_path(path: &str, debug: bool) -> String {
// In debug mode, return the full path
if debug {
return path.to_string();
}
let path = Path::new(path); let path = Path::new(path);
// First try to convert to ~ if it's in home directory // First try to convert to ~ if it's in home directory
@@ -485,9 +492,17 @@ mod tests {
#[test] #[test]
fn test_short_paths_unchanged() { fn test_short_paths_unchanged() {
assert_eq!(shorten_path("/usr/bin"), "/usr/bin"); assert_eq!(shorten_path("/usr/bin", false), "/usr/bin");
assert_eq!(shorten_path("/a/b/c"), "/a/b/c"); assert_eq!(shorten_path("/a/b/c", false), "/a/b/c");
assert_eq!(shorten_path("file.txt"), "file.txt"); assert_eq!(shorten_path("file.txt", false), "file.txt");
}
#[test]
fn test_debug_mode_returns_full_path() {
assert_eq!(
shorten_path("/very/long/path/that/would/normally/be/shortened", true),
"/very/long/path/that/would/normally/be/shortened"
);
} }
#[test] #[test]
@@ -499,13 +514,13 @@ mod tests {
env::set_var("HOME", "/Users/testuser"); env::set_var("HOME", "/Users/testuser");
assert_eq!( assert_eq!(
shorten_path("/Users/testuser/documents/file.txt"), shorten_path("/Users/testuser/documents/file.txt", false),
"~/documents/file.txt" "~/documents/file.txt"
); );
// A path that starts similarly to home but isn't in home // A path that starts similarly to home but isn't in home
assert_eq!( assert_eq!(
shorten_path("/Users/testuser2/documents/file.txt"), shorten_path("/Users/testuser2/documents/file.txt", false),
"/Users/testuser2/documents/file.txt" "/Users/testuser2/documents/file.txt"
); );
@@ -521,7 +536,8 @@ mod tests {
fn test_long_path_shortening() { fn test_long_path_shortening() {
assert_eq!( assert_eq!(
shorten_path( shorten_path(
"/vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv/long/path/with/many/components/file.txt" "/vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv/long/path/with/many/components/file.txt",
false
), ),
"/v/l/p/w/m/components/file.txt" "/v/l/p/w/m/components/file.txt"
); );