diff --git a/crates/g3-cli/src/interactive.rs b/crates/g3-cli/src/interactive.rs index 835b4f1..c0f14bf 100644 --- a/crates/g3-cli/src/interactive.rs +++ b/crates/g3-cli/src/interactive.rs @@ -338,16 +338,16 @@ async fn handle_command( Ok(true) } "/compact" => { - output.print("g3: compacting session ..."); + output.print_g3_progress("compacting session"); match agent.force_compact().await { Ok(true) => { - output.print("g3: compacting session ... done"); + output.print_g3_status("compacting session", "done"); } Ok(false) => { - output.print("g3: compacting session ... failed"); + output.print_g3_status("compacting session", "failed"); } Err(e) => { - output.print(&format!("g3: compacting session ... error: {}", e)); + output.print_g3_status("compacting session", &format!("error: {}", e)); } } Ok(true) diff --git a/crates/g3-cli/src/simple_output.rs b/crates/g3-cli/src/simple_output.rs index e6bd99b..0c1b1b4 100644 --- a/crates/g3-cli/src/simple_output.rs +++ b/crates/g3-cli/src/simple_output.rs @@ -1,3 +1,5 @@ +use crossterm::style::{Attribute, Color, ResetColor, SetAttribute, SetForegroundColor}; + /// Simple output helper for printing messages #[derive(Clone)] pub struct SimpleOutput; @@ -14,6 +16,49 @@ impl SimpleOutput { pub fn print_smart(&self, message: &str) { println!("{}", message); } + + /// Print a g3 status message with colored tag and status + /// Format: "g3: ... [status]" + /// - "g3:" is bold green + /// - "done" status is normal + /// - "failed" and "error" statuses are red + pub fn print_g3_status(&self, message: &str, status: &str) { + let status_colored = match status { + s if s.starts_with("error") || s == "failed" => { + format!( + "{}[{}]{}", + SetForegroundColor(Color::Red), + status, + ResetColor + ) + } + _ => format!("[{}]", status), + }; + + println!( + "{}{}g3:{}{} {} ... {}", + SetAttribute(Attribute::Bold), + SetForegroundColor(Color::Green), + ResetColor, + SetAttribute(Attribute::Reset), + message, + status_colored + ); + } + + /// Print a g3 status message in progress (no status yet) + /// Format: "g3: ..." + /// - "g3:" is bold green + pub fn print_g3_progress(&self, message: &str) { + println!( + "{}{}g3:{}{} {} ...", + SetAttribute(Attribute::Bold), + SetForegroundColor(Color::Green), + ResetColor, + SetAttribute(Attribute::Reset), + message + ); + } } impl Default for SimpleOutput { diff --git a/crates/g3-cli/src/ui_writer_impl.rs b/crates/g3-cli/src/ui_writer_impl.rs index 7b40515..48ff3b9 100644 --- a/crates/g3-cli/src/ui_writer_impl.rs +++ b/crates/g3-cli/src/ui_writer_impl.rs @@ -211,6 +211,41 @@ impl UiWriter for ConsoleUiWriter { println!("{}", message); } + fn print_g3_progress(&self, message: &str) { + use crossterm::style::{Attribute, Color, ResetColor, SetAttribute, SetForegroundColor}; + println!( + "{}{}g3:{}{} {} ...", + SetAttribute(Attribute::Bold), + SetForegroundColor(Color::Green), + ResetColor, + SetAttribute(Attribute::Reset), + message + ); + } + + fn print_g3_status(&self, message: &str, status: &str) { + use crossterm::style::{Attribute, Color, ResetColor, SetAttribute, SetForegroundColor}; + let status_colored = if status.starts_with("error") || status == "failed" { + format!( + "{}[{}]{}", + SetForegroundColor(Color::Red), + status, + ResetColor + ) + } else { + format!("[{}]", status) + }; + println!( + "{}{}g3:{}{} {} ... {}", + SetAttribute(Attribute::Bold), + SetForegroundColor(Color::Green), + ResetColor, + SetAttribute(Attribute::Reset), + message, + status_colored + ); + } + fn print_context_thinning(&self, message: &str) { // Animated highlight for context thinning // Use bright cyan/green with a quick flash animation diff --git a/crates/g3-core/src/lib.rs b/crates/g3-core/src/lib.rs index 09dad58..56e64ae 100644 --- a/crates/g3-core/src/lib.rs +++ b/crates/g3-core/src/lib.rs @@ -892,12 +892,14 @@ impl Agent { // Check if we need to do 90% auto-compaction if self.pending_90_compaction { - self.ui_writer - .print_context_status("\ng3: compacting session ...\n"); - if let Err(e) = self.force_compact().await { - warn!("Failed to auto-compact at 90%: {}", e); - } else { - self.ui_writer.println(""); + self.ui_writer.println(""); + self.ui_writer.print_g3_progress("compacting session"); + match self.force_compact().await { + Ok(true) => self.ui_writer.print_g3_status("compacting session", "done"), + Ok(false) => self.ui_writer.print_g3_status("compacting session", "failed"), + Err(e) => { + self.ui_writer.print_g3_status("compacting session", &format!("error: {}", e)); + } } self.pending_90_compaction = false; } @@ -1071,12 +1073,11 @@ impl Agent { self.ui_writer.print_context_thinning(&thin_summary); if !self.context_window.should_compact() { - self.ui_writer - .print_context_status("g3: thinning resolved capacity issue\n"); + self.ui_writer.print_g3_status("thinning", "resolved"); return Ok(false); } - self.ui_writer - .print_context_status("g3: thinning insufficient, compacting ...\n"); + self.ui_writer.print_g3_status("thinning", "insufficient"); + self.ui_writer.print_g3_progress("compacting session"); } // Compaction still needed @@ -1086,10 +1087,10 @@ impl Agent { use crate::compaction::{perform_compaction, CompactionConfig}; - self.ui_writer.print_context_status(&format!( - "\ng3: compacting session ({}%) ...", - self.context_window.percentage_used() as u32 - )); + self.ui_writer.println(""); + self.ui_writer.print_g3_progress( + &format!("compacting session ({}%)", self.context_window.percentage_used() as u32) + ); let provider_name = self.providers.get(None)?.name().to_string(); let latest_user_msg = request @@ -1115,14 +1116,13 @@ impl Agent { .await?; if result.success { - self.ui_writer - .print_context_status("g3: compacting session ... done\n"); + self.ui_writer.print_g3_status("compacting session", "done"); self.compaction_events.push(result.chars_saved); request.messages = self.context_window.conversation_history.clone(); return Ok(true); } - self.ui_writer.print_context_status("⚠️ Unable to compact context. Consider starting a new session if you continue to see errors.\n"); + self.ui_writer.print_g3_status("compacting session", "failed"); Err(anyhow::anyhow!( "Context window at capacity and compaction failed. Please start a new session." )) diff --git a/crates/g3-core/src/ui_writer.rs b/crates/g3-core/src/ui_writer.rs index f6e293c..8dc347a 100644 --- a/crates/g3-core/src/ui_writer.rs +++ b/crates/g3-core/src/ui_writer.rs @@ -17,6 +17,16 @@ pub trait UiWriter: Send + Sync { /// Print a context window status message fn print_context_status(&self, message: &str); + /// Print a g3-style status message in progress + /// Format: "g3: ..." + /// - "g3:" should be bold green + fn print_g3_progress(&self, message: &str); + + /// Print a g3-style status message with completion status + /// Format: "g3: ... [status]" + /// - "g3:" should be bold green, "failed"/"error" status should be red + fn print_g3_status(&self, message: &str, status: &str); + /// Print a context thinning success message with highlight and animation fn print_context_thinning(&self, message: &str); @@ -124,6 +134,8 @@ impl UiWriter for NullUiWriter { fn print_inline(&self, _message: &str) {} fn print_system_prompt(&self, _prompt: &str) {} fn print_context_status(&self, _message: &str) {} + fn print_g3_progress(&self, _message: &str) {} + fn print_g3_status(&self, _message: &str, _status: &str) {} fn print_context_thinning(&self, _message: &str) {} fn print_tool_header(&self, _tool_name: &str, _tool_args: Option<&serde_json::Value>) {} fn print_tool_arg(&self, _key: &str, _value: &str) {} diff --git a/crates/g3-core/tests/todo_staleness_test.rs b/crates/g3-core/tests/todo_staleness_test.rs index 5f786ba..b93a6a1 100644 --- a/crates/g3-core/tests/todo_staleness_test.rs +++ b/crates/g3-core/tests/todo_staleness_test.rs @@ -52,6 +52,8 @@ impl UiWriter for MockUiWriter { .unwrap() .push(format!("STATUS: {}", message)); } + fn print_g3_progress(&self, _message: &str) {} + fn print_g3_status(&self, _message: &str, _status: &str) {} fn print_context_thinning(&self, _message: &str) {} fn print_tool_header(&self, _tool_name: &str, _tool_args: Option<&serde_json::Value>) {} fn print_tool_arg(&self, _key: &str, _value: &str) {} diff --git a/crates/g3-planner/src/llm.rs b/crates/g3-planner/src/llm.rs index 5a3fb04..0bbd004 100644 --- a/crates/g3-planner/src/llm.rs +++ b/crates/g3-planner/src/llm.rs @@ -233,6 +233,14 @@ impl g3_core::ui_writer::UiWriter for PlannerUiWriter { fn print_context_status(&self, message: &str) { println!("📊 {}", message); } + + fn print_g3_progress(&self, message: &str) { + println!("g3: {} ...", message); + } + + fn print_g3_status(&self, message: &str, status: &str) { + println!("g3: {} ... [{}]", message, status); + } fn print_context_thinning(&self, message: &str) { println!("🗜️ {}", message);