mirror of
https://github.com/aljazceru/goose.git
synced 2026-01-07 00:14:23 +01:00
feat(cli): add --debug flag to goose session / run (#1564)
This commit is contained in:
@@ -83,8 +83,14 @@ async fn run_eval(
|
||||
let requirements = evaluation.required_extensions();
|
||||
|
||||
// Create session with error capture
|
||||
let base_session =
|
||||
build_session(None, false, requirements.external, requirements.builtin).await;
|
||||
let base_session = build_session(
|
||||
None,
|
||||
false,
|
||||
requirements.external,
|
||||
requirements.builtin,
|
||||
false,
|
||||
)
|
||||
.await;
|
||||
|
||||
let bench_session = Arc::new(Mutex::new(BenchSession::new(base_session)));
|
||||
let bench_session_clone = bench_session.clone();
|
||||
|
||||
@@ -90,6 +90,14 @@ enum Command {
|
||||
)]
|
||||
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
|
||||
#[arg(
|
||||
long = "with-extension",
|
||||
@@ -157,6 +165,14 @@ enum Command {
|
||||
)]
|
||||
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
|
||||
#[arg(
|
||||
long = "with-extension",
|
||||
@@ -285,6 +301,7 @@ async fn main() -> Result<()> {
|
||||
Some(Command::Session {
|
||||
identifier,
|
||||
resume,
|
||||
debug,
|
||||
extension,
|
||||
builtin,
|
||||
}) => {
|
||||
@@ -293,6 +310,7 @@ async fn main() -> Result<()> {
|
||||
resume,
|
||||
extension,
|
||||
builtin,
|
||||
debug,
|
||||
)
|
||||
.await;
|
||||
|
||||
@@ -306,6 +324,7 @@ async fn main() -> Result<()> {
|
||||
interactive,
|
||||
identifier,
|
||||
resume,
|
||||
debug,
|
||||
extension,
|
||||
builtin,
|
||||
}) => {
|
||||
@@ -332,6 +351,7 @@ async fn main() -> Result<()> {
|
||||
resume,
|
||||
extension,
|
||||
builtin,
|
||||
debug,
|
||||
)
|
||||
.await;
|
||||
setup_logging(session.session_file().file_stem().and_then(|s| s.to_str()))?;
|
||||
@@ -410,7 +430,7 @@ async fn main() -> Result<()> {
|
||||
return Ok(());
|
||||
} else {
|
||||
// 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()))?;
|
||||
let _ = session.interactive(None).await;
|
||||
return Ok(());
|
||||
|
||||
@@ -15,6 +15,7 @@ pub async fn build_session(
|
||||
resume: bool,
|
||||
extensions: Vec<String>,
|
||||
builtins: Vec<String>,
|
||||
debug: bool,
|
||||
) -> Session {
|
||||
// Load config and get provider/model
|
||||
let config = Config::global();
|
||||
@@ -92,7 +93,7 @@ pub async fn build_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
|
||||
for extension_str in extensions {
|
||||
|
||||
@@ -36,6 +36,7 @@ pub struct Session {
|
||||
session_file: PathBuf,
|
||||
// Cache for completion data - using std::sync for thread safety without async
|
||||
completion_cache: Arc<std::sync::RwLock<CompletionCache>>,
|
||||
debug: bool, // New field for debug mode
|
||||
}
|
||||
|
||||
// Cache structure for completion data
|
||||
@@ -56,7 +57,7 @@ impl CompletionCache {
|
||||
}
|
||||
|
||||
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) {
|
||||
Ok(msgs) => msgs,
|
||||
Err(e) => {
|
||||
@@ -70,6 +71,7 @@ impl Session {
|
||||
messages,
|
||||
session_file,
|
||||
completion_cache: Arc::new(std::sync::RwLock::new(CompletionCache::new())),
|
||||
debug,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -393,7 +395,7 @@ impl Session {
|
||||
}
|
||||
|
||||
if msg.role == mcp_core::Role::User {
|
||||
output::render_message(&msg);
|
||||
output::render_message(&msg, self.debug);
|
||||
}
|
||||
self.messages.push(msg);
|
||||
}
|
||||
@@ -461,7 +463,7 @@ impl Session {
|
||||
session::persist_messages(&self.session_file, &self.messages, None).await?;
|
||||
|
||||
if interactive {output::hide_thinking()};
|
||||
output::render_message(&message);
|
||||
output::render_message(&message, self.debug);
|
||||
if interactive {output::show_thinking()};
|
||||
}
|
||||
}
|
||||
@@ -547,7 +549,7 @@ impl Session {
|
||||
// No need for description update here
|
||||
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 {
|
||||
// An interruption occurred outside of a tool request-response.
|
||||
if let Some(last_msg) = self.messages.last() {
|
||||
@@ -562,13 +564,19 @@ impl Session {
|
||||
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,
|
||||
);
|
||||
}
|
||||
Some(_) => {
|
||||
// A real users message
|
||||
self.messages.pop();
|
||||
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"),
|
||||
}
|
||||
|
||||
@@ -96,14 +96,14 @@ pub fn hide_thinking() {
|
||||
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();
|
||||
|
||||
for content in &message.content {
|
||||
match content {
|
||||
MessageContent::Text(text) => print_markdown(&text.text, theme),
|
||||
MessageContent::ToolRequest(req) => render_tool_request(req, theme),
|
||||
MessageContent::ToolResponse(resp) => render_tool_response(resp, theme),
|
||||
MessageContent::ToolRequest(req) => render_tool_request(req, theme, debug),
|
||||
MessageContent::ToolResponse(resp) => render_tool_response(resp, theme, debug),
|
||||
MessageContent::Image(image) => {
|
||||
println!("Image: [data: {}, type: {}]", image.data, image.mime_type);
|
||||
}
|
||||
@@ -126,18 +126,18 @@ pub fn render_message(message: &Message) {
|
||||
println!();
|
||||
}
|
||||
|
||||
fn render_tool_request(req: &ToolRequest, theme: Theme) {
|
||||
fn render_tool_request(req: &ToolRequest, theme: Theme, debug: bool) {
|
||||
match &req.tool_call {
|
||||
Ok(call) => match call.name.as_str() {
|
||||
"developer__text_editor" => render_text_editor_request(call),
|
||||
"developer__shell" => render_shell_request(call),
|
||||
_ => render_default_request(call),
|
||||
"developer__text_editor" => render_text_editor_request(call, debug),
|
||||
"developer__shell" => render_shell_request(call, debug),
|
||||
_ => render_default_request(call, debug),
|
||||
},
|
||||
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();
|
||||
|
||||
match &resp.tool_result {
|
||||
@@ -157,12 +157,14 @@ fn render_tool_response(resp: &ToolResponse, theme: Theme) {
|
||||
if content
|
||||
.priority()
|
||||
.is_some_and(|priority| priority < min_priority)
|
||||
|| content.priority().is_none()
|
||||
|| (content.priority().is_none() && !debug)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -266,7 +268,7 @@ pub fn render_builtin_error(names: &str, error: &str) {
|
||||
println!();
|
||||
}
|
||||
|
||||
fn render_text_editor_request(call: &ToolCall) {
|
||||
fn render_text_editor_request(call: &ToolCall, debug: bool) {
|
||||
print_tool_header(call);
|
||||
|
||||
// Print path first with special formatting
|
||||
@@ -274,7 +276,7 @@ fn render_text_editor_request(call: &ToolCall) {
|
||||
println!(
|
||||
"{}: {}",
|
||||
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());
|
||||
}
|
||||
}
|
||||
print_params(&Value::Object(other_args), 0);
|
||||
print_params(&Value::Object(other_args), 0, debug);
|
||||
}
|
||||
println!();
|
||||
}
|
||||
|
||||
fn render_shell_request(call: &ToolCall) {
|
||||
fn render_shell_request(call: &ToolCall, debug: bool) {
|
||||
print_tool_header(call);
|
||||
|
||||
match call.arguments.get("command") {
|
||||
Some(Value::String(s)) => {
|
||||
println!("{}: {}", style("command").dim(), style(s).green());
|
||||
}
|
||||
_ => print_params(&call.arguments, 0),
|
||||
_ => print_params(&call.arguments, 0, debug),
|
||||
}
|
||||
println!();
|
||||
}
|
||||
|
||||
fn render_default_request(call: &ToolCall) {
|
||||
fn render_default_request(call: &ToolCall, debug: bool) {
|
||||
print_tool_header(call);
|
||||
print_params(&call.arguments, 0);
|
||||
print_params(&call.arguments, 0, debug);
|
||||
println!();
|
||||
}
|
||||
|
||||
@@ -342,7 +344,7 @@ fn print_markdown(content: &str, theme: Theme) {
|
||||
const MAX_STRING_LENGTH: usize = 40;
|
||||
const INDENT: &str = " ";
|
||||
|
||||
fn print_params(value: &Value, depth: usize) {
|
||||
fn print_params(value: &Value, depth: usize, debug: bool) {
|
||||
let indent = INDENT.repeat(depth);
|
||||
|
||||
match value {
|
||||
@@ -351,17 +353,17 @@ fn print_params(value: &Value, depth: usize) {
|
||||
match val {
|
||||
Value::Object(_) => {
|
||||
println!("{}{}:", indent, style(key).dim());
|
||||
print_params(val, depth + 1);
|
||||
print_params(val, depth + 1, debug);
|
||||
}
|
||||
Value::Array(arr) => {
|
||||
println!("{}{}:", indent, style(key).dim());
|
||||
for item in arr.iter() {
|
||||
println!("{}{}- ", indent, INDENT);
|
||||
print_params(item, depth + 2);
|
||||
print_params(item, depth + 2, debug);
|
||||
}
|
||||
}
|
||||
Value::String(s) => {
|
||||
if s.len() > MAX_STRING_LENGTH {
|
||||
if !debug && s.len() > MAX_STRING_LENGTH {
|
||||
println!("{}{}: {}", indent, style(key).dim(), style("...").dim());
|
||||
} else {
|
||||
println!("{}{}: {}", indent, style(key).dim(), style(s).green());
|
||||
@@ -382,11 +384,11 @@ fn print_params(value: &Value, depth: usize) {
|
||||
Value::Array(arr) => {
|
||||
for (i, item) in arr.iter().enumerate() {
|
||||
println!("{}{}.", indent, i + 1);
|
||||
print_params(item, depth + 1);
|
||||
print_params(item, depth + 1, debug);
|
||||
}
|
||||
}
|
||||
Value::String(s) => {
|
||||
if s.len() > MAX_STRING_LENGTH {
|
||||
if !debug && s.len() > MAX_STRING_LENGTH {
|
||||
println!(
|
||||
"{}{}",
|
||||
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);
|
||||
|
||||
// First try to convert to ~ if it's in home directory
|
||||
@@ -485,9 +492,17 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_short_paths_unchanged() {
|
||||
assert_eq!(shorten_path("/usr/bin"), "/usr/bin");
|
||||
assert_eq!(shorten_path("/a/b/c"), "/a/b/c");
|
||||
assert_eq!(shorten_path("file.txt"), "file.txt");
|
||||
assert_eq!(shorten_path("/usr/bin", false), "/usr/bin");
|
||||
assert_eq!(shorten_path("/a/b/c", false), "/a/b/c");
|
||||
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]
|
||||
@@ -499,13 +514,13 @@ mod tests {
|
||||
env::set_var("HOME", "/Users/testuser");
|
||||
|
||||
assert_eq!(
|
||||
shorten_path("/Users/testuser/documents/file.txt"),
|
||||
shorten_path("/Users/testuser/documents/file.txt", false),
|
||||
"~/documents/file.txt"
|
||||
);
|
||||
|
||||
// A path that starts similarly to home but isn't in home
|
||||
assert_eq!(
|
||||
shorten_path("/Users/testuser2/documents/file.txt"),
|
||||
shorten_path("/Users/testuser2/documents/file.txt", false),
|
||||
"/Users/testuser2/documents/file.txt"
|
||||
);
|
||||
|
||||
@@ -521,7 +536,8 @@ mod tests {
|
||||
fn test_long_path_shortening() {
|
||||
assert_eq!(
|
||||
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"
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user