refactor(g3-cli): break lib.rs into focused modules

Extract 7 modules from the 2966-line lib.rs:
- cli_args.rs (133 lines): CLI argument parsing with clap
- autonomous.rs (785 lines): coach-player feedback loop
- agent_mode.rs (284 lines): specialized agent execution
- accumulative.rs (343 lines): iterative requirements mode
- interactive.rs (851 lines): REPL with command handling
- task_execution.rs (212 lines): unified retry logic
- utils.rs (91 lines): display and workspace helpers

Key improvements:
- lib.rs reduced from 2966 to 415 lines (86% reduction)
- Eliminated duplicate retry logic between execute_task and execute_task_machine
- Each module has a single responsibility
- Easier to reason about and maintain

Agent: fowler
This commit is contained in:
Dhanji R. Prasanna
2026-01-12 05:35:08 +05:30
parent 14cc28d9ba
commit b9cdb99557
8 changed files with 2945 additions and 2797 deletions

View File

@@ -0,0 +1,91 @@
//! Utility functions for G3 CLI.
use anyhow::Result;
use crossterm::style::{Color, ResetColor, SetForegroundColor};
use g3_core::ui_writer::UiWriter;
use g3_core::Agent;
use std::path::PathBuf;
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();
// 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(machine_mode: bool) -> 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_with_mode(machine_mode);
output.print(&format!(
"📁 Created workspace directory: {}",
workspace_dir.display()
));
}
Ok(workspace_dir)
}