Files
g3/crates/g3-cli/src/utils.rs
Dhanji R. Prasanna eb6268641f Fix --safari flag being blocked by Chrome diagnostics
When --safari was passed, Chrome diagnostics were still running because
--chrome-headless defaults to true. This caused the CLI to hang while
running diagnostics for a browser that wouldn't be used.

Now skip Chrome diagnostics when --safari is explicitly set.
2026-01-17 09:20:21 +05:30

181 lines
5.9 KiB
Rust

//! Utility functions for G3 CLI.
use anyhow::Result;
use crossterm::style::{Color, ResetColor, SetForegroundColor};
use g3_config::Config;
use g3_core::ui_writer::UiWriter;
use g3_core::Agent;
use std::path::PathBuf;
use crate::cli_args::Cli;
use crate::simple_output::SimpleOutput;
/// Display context window progress bar.
pub fn display_context_progress<W: UiWriter>(agent: &Agent<W>, _output: &SimpleOutput) {
let context = agent.get_context_window();
let percentage = context.percentage_used();
// Ensure we start on a new line (previous response may not end with newline)
println!();
// Create 10 dots representing context fullness
let total_dots: usize = 10;
let filled_dots = ((percentage / 100.0) * total_dots as f32).round() as usize;
let empty_dots = total_dots.saturating_sub(filled_dots);
let filled_str = "".repeat(filled_dots);
let empty_str = "".repeat(empty_dots);
// Determine color based on percentage
let color = if percentage < 40.0 {
Color::Green
} else if percentage < 60.0 {
Color::Yellow
} else if percentage < 80.0 {
Color::Rgb {
r: 255,
g: 165,
b: 0,
} // Orange
} else {
Color::Red
};
// Format tokens as compact strings (e.g., "38.5k" instead of "38531")
let format_tokens = |tokens: u32| -> String {
if tokens >= 1_000_000 {
format!("{:.1}m", tokens as f64 / 1_000_000.0)
} else if tokens >= 1_000 {
let k = tokens as f64 / 1000.0;
if k >= 100.0 {
format!("{:.0}k", k)
} else {
format!("{:.1}k", k)
}
} else {
format!("{}", tokens)
}
};
// Print with colored dots (using print! directly to handle color codes)
print!(
"{}{}{}{} {}/{} ◉ | {:.0}%\n",
SetForegroundColor(color),
filled_str,
empty_str,
ResetColor,
format_tokens(context.used_tokens),
format_tokens(context.total_tokens),
percentage
);
}
/// Set up the workspace directory for autonomous mode.
/// Uses G3_WORKSPACE environment variable or defaults to ~/tmp/workspace.
pub fn setup_workspace_directory() -> Result<PathBuf> {
let workspace_dir = if let Ok(env_workspace) = std::env::var("G3_WORKSPACE") {
PathBuf::from(env_workspace)
} else {
// Default to ~/tmp/workspace
let home_dir = dirs::home_dir()
.ok_or_else(|| anyhow::anyhow!("Could not determine home directory"))?;
home_dir.join("tmp").join("workspace")
};
// Create the directory if it doesn't exist
if !workspace_dir.exists() {
std::fs::create_dir_all(&workspace_dir)?;
let output = SimpleOutput::new();
output.print(&format!(
"📁 Created workspace directory: {}",
workspace_dir.display()
));
}
Ok(workspace_dir)
}
/// Load configuration with CLI argument overrides applied.
///
/// This is the canonical function for loading config with CLI overrides.
/// All CLI entry points should use this to ensure consistent behavior.
pub fn load_config_with_cli_overrides(cli: &Cli) -> Result<Config> {
let mut config = Config::load_with_overrides(
cli.config.as_deref(),
cli.provider.clone(),
cli.model.clone(),
)?;
// Apply webdriver flag override
if cli.webdriver {
config.webdriver.enabled = true;
}
// Apply chrome-headless flag override
// Only apply chrome-headless if safari is not explicitly set
if cli.chrome_headless && !cli.safari {
config.webdriver.enabled = true;
config.webdriver.browser = g3_config::WebDriverBrowser::ChromeHeadless;
// Run Chrome diagnostics - only show output if there are issues
let report =
g3_computer_control::run_chrome_diagnostics(config.webdriver.chrome_binary.as_deref());
if !report.all_ok() {
println!("{}", report.format_report());
}
}
// Apply safari flag override
if cli.safari {
config.webdriver.enabled = true;
config.webdriver.browser = g3_config::WebDriverBrowser::Safari;
}
// Apply no-auto-compact flag override
if cli.manual_compact {
config.agent.auto_compact = false;
}
// Validate provider if specified
if let Some(ref provider) = cli.provider {
let valid_providers = ["anthropic", "databricks", "embedded", "openai"];
if !valid_providers.contains(&provider.as_str()) {
return Err(anyhow::anyhow!(
"Invalid provider '{}'. Valid options: {:?}",
provider,
valid_providers
));
}
}
Ok(config)
}
/// Initialize logging based on CLI verbosity settings.
pub fn initialize_logging(verbose: bool) {
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, EnvFilter};
let filter = if verbose {
EnvFilter::from_default_env()
.add_directive(format!("{}=debug", env!("CARGO_PKG_NAME")).parse().unwrap())
.add_directive("g3_core=debug".parse().unwrap())
.add_directive("g3_cli=debug".parse().unwrap())
.add_directive("g3_execution=debug".parse().unwrap())
.add_directive("g3_providers=debug".parse().unwrap())
} else {
EnvFilter::from_default_env()
.add_directive(format!("{}=info", env!("CARGO_PKG_NAME")).parse().unwrap())
.add_directive("g3_core=info".parse().unwrap())
.add_directive("g3_cli=info".parse().unwrap())
.add_directive("g3_execution=info".parse().unwrap())
.add_directive("g3_providers=info".parse().unwrap())
.add_directive("llama_cpp=off".parse().unwrap())
.add_directive("llama=off".parse().unwrap())
};
let _ = tracing_subscriber::registry()
.with(tracing_subscriber::fmt::layer())
.with(filter)
.try_init();
}