minor
This commit is contained in:
@@ -1,19 +1,19 @@
|
|||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use g3_config::Config;
|
use g3_config::Config;
|
||||||
use g3_core::{project::Project, Agent, ui_writer::UiWriter};
|
use g3_core::{project::Project, ui_writer::UiWriter, Agent};
|
||||||
use rustyline::error::ReadlineError;
|
use rustyline::error::ReadlineError;
|
||||||
use rustyline::DefaultEditor;
|
use rustyline::DefaultEditor;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use tokio_util::sync::CancellationToken;
|
use tokio_util::sync::CancellationToken;
|
||||||
use tracing::{error, info};
|
use tracing::{error, info};
|
||||||
|
|
||||||
|
mod retro_tui;
|
||||||
mod tui;
|
mod tui;
|
||||||
mod ui_writer_impl;
|
mod ui_writer_impl;
|
||||||
mod retro_tui;
|
use retro_tui::RetroTui;
|
||||||
use tui::SimpleOutput;
|
use tui::SimpleOutput;
|
||||||
use ui_writer_impl::{ConsoleUiWriter, RetroTuiWriter};
|
use ui_writer_impl::{ConsoleUiWriter, RetroTuiWriter};
|
||||||
use retro_tui::RetroTui;
|
|
||||||
|
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
#[command(name = "g3")]
|
#[command(name = "g3")]
|
||||||
@@ -91,14 +91,11 @@ pub async fn run() -> Result<()> {
|
|||||||
// In retro mode, we don't want any logging output to interfere with the TUI
|
// In retro mode, we don't want any logging output to interfere with the TUI
|
||||||
// We'll use a no-op subscriber
|
// We'll use a no-op subscriber
|
||||||
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, EnvFilter};
|
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, EnvFilter};
|
||||||
|
|
||||||
// Create a filter that suppresses ALL logs in retro mode
|
// Create a filter that suppresses ALL logs in retro mode
|
||||||
let filter = EnvFilter::from_default_env()
|
let filter = EnvFilter::from_default_env().add_directive("off".parse().unwrap()); // Turn off all logging
|
||||||
.add_directive("off".parse().unwrap()); // Turn off all logging
|
|
||||||
|
tracing_subscriber::registry().with(filter).init();
|
||||||
tracing_subscriber::registry()
|
|
||||||
.with(filter)
|
|
||||||
.init();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if !cli.retro {
|
if !cli.retro {
|
||||||
@@ -167,7 +164,7 @@ pub async fn run() -> Result<()> {
|
|||||||
if !cli.retro {
|
if !cli.retro {
|
||||||
info!("Starting interactive mode");
|
info!("Starting interactive mode");
|
||||||
}
|
}
|
||||||
|
|
||||||
if cli.retro {
|
if cli.retro {
|
||||||
// Use retro terminal UI
|
// Use retro terminal UI
|
||||||
run_interactive_retro(config, cli.show_prompt, cli.show_code).await?;
|
run_interactive_retro(config, cli.show_prompt, cli.show_code).await?;
|
||||||
@@ -185,22 +182,22 @@ pub async fn run() -> Result<()> {
|
|||||||
async fn run_interactive_retro(config: Config, show_prompt: bool, show_code: bool) -> Result<()> {
|
async fn run_interactive_retro(config: Config, show_prompt: bool, show_code: bool) -> Result<()> {
|
||||||
use crossterm::event::{self, Event, KeyCode, KeyModifiers};
|
use crossterm::event::{self, Event, KeyCode, KeyModifiers};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
// Set environment variable to suppress println in other crates
|
// Set environment variable to suppress println in other crates
|
||||||
std::env::set_var("G3_RETRO_MODE", "1");
|
std::env::set_var("G3_RETRO_MODE", "1");
|
||||||
|
|
||||||
// Initialize the retro terminal UI
|
// Initialize the retro terminal UI
|
||||||
let tui = RetroTui::start().await?;
|
let tui = RetroTui::start().await?;
|
||||||
|
|
||||||
// Create agent with RetroTuiWriter
|
// Create agent with RetroTuiWriter
|
||||||
let ui_writer = RetroTuiWriter::new(tui.clone());
|
let ui_writer = RetroTuiWriter::new(tui.clone());
|
||||||
let mut agent = Agent::new(config, ui_writer).await?;
|
let mut agent = Agent::new(config, ui_writer).await?;
|
||||||
|
|
||||||
// Display initial system messages
|
// Display initial system messages
|
||||||
tui.output("SYSTEM: G3 AI CODING AGENT ONLINE");
|
tui.output("SYSTEM: G3 AI CODING AGENT ONLINE");
|
||||||
tui.output("SYSTEM: READY FOR INPUT");
|
tui.output("SYSTEM: READY FOR INPUT");
|
||||||
tui.output("");
|
tui.output("");
|
||||||
|
|
||||||
// Display provider and model information
|
// Display provider and model information
|
||||||
match agent.get_provider_info() {
|
match agent.get_provider_info() {
|
||||||
Ok((provider, model)) => {
|
Ok((provider, model)) => {
|
||||||
@@ -210,26 +207,28 @@ async fn run_interactive_retro(config: Config, show_prompt: bool, show_code: boo
|
|||||||
tui.update_provider_info("ERROR", &e.to_string());
|
tui.update_provider_info("ERROR", &e.to_string());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tui.output("");
|
tui.output("");
|
||||||
tui.output("Type 'exit' or 'quit' to exit, use Up/Down arrows to scroll output");
|
|
||||||
tui.output("For multiline input: use \\ at the end of a line to continue");
|
|
||||||
tui.output("");
|
tui.output("");
|
||||||
|
|
||||||
// Track multiline input
|
// Track multiline input
|
||||||
let mut multiline_buffer = String::new();
|
let mut multiline_buffer = String::new();
|
||||||
let mut in_multiline = false;
|
let mut in_multiline = false;
|
||||||
let mut input_buffer = String::new();
|
let mut input_buffer = String::new();
|
||||||
|
|
||||||
// Main event loop
|
// Main event loop
|
||||||
loop {
|
loop {
|
||||||
// Update context window display
|
// Update context window display
|
||||||
let context = agent.get_context_window();
|
let context = agent.get_context_window();
|
||||||
tui.update_context(context.used_tokens, context.total_tokens, context.percentage_used());
|
tui.update_context(
|
||||||
|
context.used_tokens,
|
||||||
|
context.total_tokens,
|
||||||
|
context.percentage_used(),
|
||||||
|
);
|
||||||
|
|
||||||
// Update the displayed input buffer
|
// Update the displayed input buffer
|
||||||
tui.update_input(&input_buffer);
|
tui.update_input(&input_buffer);
|
||||||
|
|
||||||
// Poll for keyboard events
|
// Poll for keyboard events
|
||||||
if event::poll(Duration::from_millis(50))? {
|
if event::poll(Duration::from_millis(50))? {
|
||||||
if let Event::Key(key) = event::read()? {
|
if let Event::Key(key) = event::read()? {
|
||||||
@@ -245,7 +244,7 @@ async fn run_interactive_retro(config: Config, show_prompt: bool, show_code: boo
|
|||||||
KeyCode::Enter => {
|
KeyCode::Enter => {
|
||||||
if !input_buffer.is_empty() {
|
if !input_buffer.is_empty() {
|
||||||
let trimmed = input_buffer.trim_end();
|
let trimmed = input_buffer.trim_end();
|
||||||
|
|
||||||
// Check if line ends with backslash for continuation
|
// Check if line ends with backslash for continuation
|
||||||
if trimmed.ends_with('\\') {
|
if trimmed.ends_with('\\') {
|
||||||
// Remove the backslash and add to buffer
|
// Remove the backslash and add to buffer
|
||||||
@@ -257,7 +256,7 @@ async fn run_interactive_retro(config: Config, show_prompt: bool, show_code: boo
|
|||||||
tui.status("MULTILINE INPUT");
|
tui.status("MULTILINE INPUT");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we're in multiline mode and no backslash, this is the final line
|
// If we're in multiline mode and no backslash, this is the final line
|
||||||
let final_input = if in_multiline {
|
let final_input = if in_multiline {
|
||||||
multiline_buffer.push_str(&input_buffer);
|
multiline_buffer.push_str(&input_buffer);
|
||||||
@@ -269,24 +268,34 @@ async fn run_interactive_retro(config: Config, show_prompt: bool, show_code: boo
|
|||||||
} else {
|
} else {
|
||||||
input_buffer.clone()
|
input_buffer.clone()
|
||||||
};
|
};
|
||||||
|
|
||||||
input_buffer.clear();
|
input_buffer.clear();
|
||||||
|
|
||||||
let input = final_input.trim().to_string();
|
let input = final_input.trim().to_string();
|
||||||
if input.is_empty() {
|
if input.is_empty() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if input == "exit" || input == "quit" {
|
if input == "exit" || input == "quit" {
|
||||||
tui.exit();
|
tui.exit();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Execute the task
|
// Execute the task
|
||||||
tui.output(&format!("> {}", input));
|
tui.output(&format!("> {}", input));
|
||||||
tui.status("PROCESSING");
|
tui.status("PROCESSING");
|
||||||
|
|
||||||
match agent.execute_task_with_timing(&input, None, false, show_prompt, show_code, true).await {
|
match agent
|
||||||
|
.execute_task_with_timing(
|
||||||
|
&input,
|
||||||
|
None,
|
||||||
|
false,
|
||||||
|
show_prompt,
|
||||||
|
show_code,
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
{
|
||||||
Ok(response) => {
|
Ok(response) => {
|
||||||
tui.output(&response);
|
tui.output(&response);
|
||||||
tui.status("READY");
|
tui.status("READY");
|
||||||
@@ -326,22 +335,26 @@ async fn run_interactive_retro(config: Config, show_prompt: bool, show_code: boo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Small delay to prevent CPU spinning
|
// Small delay to prevent CPU spinning
|
||||||
tokio::time::sleep(Duration::from_millis(10)).await;
|
tokio::time::sleep(Duration::from_millis(10)).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
tui.output("SYSTEM: SHUTDOWN INITIATED");
|
tui.output("SYSTEM: SHUTDOWN INITIATED");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn run_interactive<W: UiWriter>(mut agent: Agent<W>, show_prompt: bool, show_code: bool) -> Result<()> {
|
async fn run_interactive<W: UiWriter>(
|
||||||
|
mut agent: Agent<W>,
|
||||||
|
show_prompt: bool,
|
||||||
|
show_code: bool,
|
||||||
|
) -> Result<()> {
|
||||||
let output = SimpleOutput::new();
|
let output = SimpleOutput::new();
|
||||||
|
|
||||||
output.print("");
|
output.print("");
|
||||||
output.print("🤖 G3 AI Coding Agent - Interactive Mode");
|
output.print("🤖 G3 AI Coding Agent - Interactive Mode");
|
||||||
output.print(
|
output.print(
|
||||||
"I solve problems by writing and executing code. Tell me what you need to accomplish!"
|
"I solve problems by writing and executing code. Tell me what you need to accomplish!",
|
||||||
);
|
);
|
||||||
output.print("");
|
output.print("");
|
||||||
|
|
||||||
@@ -389,7 +402,7 @@ async fn run_interactive<W: UiWriter>(mut agent: Agent<W>, show_prompt: bool, sh
|
|||||||
match readline {
|
match readline {
|
||||||
Ok(line) => {
|
Ok(line) => {
|
||||||
let trimmed = line.trim_end();
|
let trimmed = line.trim_end();
|
||||||
|
|
||||||
// Check if line ends with backslash for continuation
|
// Check if line ends with backslash for continuation
|
||||||
if trimmed.ends_with('\\') {
|
if trimmed.ends_with('\\') {
|
||||||
// Remove the backslash and add to buffer
|
// Remove the backslash and add to buffer
|
||||||
@@ -399,7 +412,7 @@ async fn run_interactive<W: UiWriter>(mut agent: Agent<W>, show_prompt: bool, sh
|
|||||||
in_multiline = true;
|
in_multiline = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we're in multiline mode and no backslash, this is the final line
|
// If we're in multiline mode and no backslash, this is the final line
|
||||||
if in_multiline {
|
if in_multiline {
|
||||||
multiline_buffer.push_str(&line);
|
multiline_buffer.push_str(&line);
|
||||||
@@ -407,35 +420,35 @@ async fn run_interactive<W: UiWriter>(mut agent: Agent<W>, show_prompt: bool, sh
|
|||||||
// Process the complete multiline input
|
// Process the complete multiline input
|
||||||
let input = multiline_buffer.trim().to_string();
|
let input = multiline_buffer.trim().to_string();
|
||||||
multiline_buffer.clear();
|
multiline_buffer.clear();
|
||||||
|
|
||||||
if input.is_empty() {
|
if input.is_empty() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add complete multiline to history
|
// Add complete multiline to history
|
||||||
rl.add_history_entry(&input)?;
|
rl.add_history_entry(&input)?;
|
||||||
|
|
||||||
if input == "exit" || input == "quit" {
|
if input == "exit" || input == "quit" {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process the multiline input
|
// Process the multiline input
|
||||||
execute_task(&mut agent, &input, show_prompt, show_code, &output).await;
|
execute_task(&mut agent, &input, show_prompt, show_code, &output).await;
|
||||||
} else {
|
} else {
|
||||||
// Single line input
|
// Single line input
|
||||||
let input = line.trim().to_string();
|
let input = line.trim().to_string();
|
||||||
|
|
||||||
if input.is_empty() {
|
if input.is_empty() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if input == "exit" || input == "quit" {
|
if input == "exit" || input == "quit" {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add to history
|
// Add to history
|
||||||
rl.add_history_entry(&input)?;
|
rl.add_history_entry(&input)?;
|
||||||
|
|
||||||
// Process the single line input
|
// Process the single line input
|
||||||
execute_task(&mut agent, &input, show_prompt, show_code, &output).await;
|
execute_task(&mut agent, &input, show_prompt, show_code, &output).await;
|
||||||
}
|
}
|
||||||
@@ -472,7 +485,13 @@ async fn run_interactive<W: UiWriter>(mut agent: Agent<W>, show_prompt: bool, sh
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn execute_task<W: UiWriter>(agent: &mut Agent<W>, input: &str, show_prompt: bool, show_code: bool, output: &SimpleOutput) {
|
async fn execute_task<W: UiWriter>(
|
||||||
|
agent: &mut Agent<W>,
|
||||||
|
input: &str,
|
||||||
|
show_prompt: bool,
|
||||||
|
show_code: bool,
|
||||||
|
output: &SimpleOutput,
|
||||||
|
) {
|
||||||
// Show thinking indicator immediately
|
// Show thinking indicator immediately
|
||||||
output.print("🤔 Thinking...");
|
output.print("🤔 Thinking...");
|
||||||
// Note: flush is handled internally by println
|
// Note: flush is handled internally by println
|
||||||
@@ -504,7 +523,7 @@ async fn execute_task<W: UiWriter>(agent: &mut Agent<W>, input: &str, show_promp
|
|||||||
// Enhanced error logging with detailed information
|
// Enhanced error logging with detailed information
|
||||||
error!("=== TASK EXECUTION ERROR ===");
|
error!("=== TASK EXECUTION ERROR ===");
|
||||||
error!("Error: {}", e);
|
error!("Error: {}", e);
|
||||||
|
|
||||||
// Log error chain
|
// Log error chain
|
||||||
let mut source = e.source();
|
let mut source = e.source();
|
||||||
let mut depth = 1;
|
let mut depth = 1;
|
||||||
@@ -513,14 +532,14 @@ async fn execute_task<W: UiWriter>(agent: &mut Agent<W>, input: &str, show_promp
|
|||||||
source = err.source();
|
source = err.source();
|
||||||
depth += 1;
|
depth += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Log additional context
|
// Log additional context
|
||||||
error!("Task input: {}", input);
|
error!("Task input: {}", input);
|
||||||
error!("Error type: {}", std::any::type_name_of_val(&e));
|
error!("Error type: {}", std::any::type_name_of_val(&e));
|
||||||
|
|
||||||
// Display user-friendly error message
|
// Display user-friendly error message
|
||||||
output.print(&format!("❌ Error: {}", e));
|
output.print(&format!("❌ Error: {}", e));
|
||||||
|
|
||||||
// If it's a stream error, provide helpful guidance
|
// If it's a stream error, provide helpful guidance
|
||||||
if e.to_string().contains("No response received") {
|
if e.to_string().contains("No response received") {
|
||||||
output.print("💡 This may be a temporary issue. Please try again or check the logs for more details.");
|
output.print("💡 This may be a temporary issue. Please try again or check the logs for more details.");
|
||||||
@@ -533,7 +552,11 @@ async fn execute_task<W: UiWriter>(agent: &mut Agent<W>, input: &str, show_promp
|
|||||||
|
|
||||||
fn display_context_progress<W: UiWriter>(agent: &Agent<W>, output: &SimpleOutput) {
|
fn display_context_progress<W: UiWriter>(agent: &Agent<W>, output: &SimpleOutput) {
|
||||||
let context = agent.get_context_window();
|
let context = agent.get_context_window();
|
||||||
output.print_context(context.used_tokens, context.total_tokens, context.percentage_used());
|
output.print_context(
|
||||||
|
context.used_tokens,
|
||||||
|
context.total_tokens,
|
||||||
|
context.percentage_used(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set up the workspace directory for autonomous mode
|
/// Set up the workspace directory for autonomous mode
|
||||||
@@ -570,15 +593,21 @@ async fn run_autonomous(
|
|||||||
max_turns: usize,
|
max_turns: usize,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let output = SimpleOutput::new();
|
let output = SimpleOutput::new();
|
||||||
|
|
||||||
output.print("🤖 G3 AI Coding Agent - Autonomous Mode");
|
output.print("🤖 G3 AI Coding Agent - Autonomous Mode");
|
||||||
output.print(&format!("📁 Using workspace: {}", project.workspace().display()));
|
output.print(&format!(
|
||||||
|
"📁 Using workspace: {}",
|
||||||
|
project.workspace().display()
|
||||||
|
));
|
||||||
|
|
||||||
// Check if requirements exist
|
// Check if requirements exist
|
||||||
if !project.has_requirements() {
|
if !project.has_requirements() {
|
||||||
output.print("❌ Error: requirements.md not found in workspace directory");
|
output.print("❌ Error: requirements.md not found in workspace directory");
|
||||||
output.print(" Please create a requirements.md file with your project requirements at:");
|
output.print(" Please create a requirements.md file with your project requirements at:");
|
||||||
output.print(&format!(" {}/requirements.md", project.workspace().display()));
|
output.print(&format!(
|
||||||
|
" {}/requirements.md",
|
||||||
|
project.workspace().display()
|
||||||
|
));
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -593,13 +622,16 @@ async fn run_autonomous(
|
|||||||
|
|
||||||
output.print("📋 Requirements loaded from requirements.md");
|
output.print("📋 Requirements loaded from requirements.md");
|
||||||
output.print("🔄 Starting coach-player feedback loop...");
|
output.print("🔄 Starting coach-player feedback loop...");
|
||||||
|
|
||||||
let mut turn = 1;
|
let mut turn = 1;
|
||||||
let mut coach_feedback = String::new();
|
let mut coach_feedback = String::new();
|
||||||
let mut implementation_approved = false;
|
let mut implementation_approved = false;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
output.print(&format!("\n=== TURN {}/{} - PLAYER MODE ===", turn, max_turns));
|
output.print(&format!(
|
||||||
|
"\n=== TURN {}/{} - PLAYER MODE ===",
|
||||||
|
turn, max_turns
|
||||||
|
));
|
||||||
|
|
||||||
// Player mode: implement requirements (with coach feedback if available)
|
// Player mode: implement requirements (with coach feedback if available)
|
||||||
let player_prompt = if coach_feedback.is_empty() {
|
let player_prompt = if coach_feedback.is_empty() {
|
||||||
@@ -615,7 +647,7 @@ async fn run_autonomous(
|
|||||||
};
|
};
|
||||||
|
|
||||||
output.print("🎯 Starting player implementation...");
|
output.print("🎯 Starting player implementation...");
|
||||||
|
|
||||||
// Execute player task and handle the result properly
|
// Execute player task and handle the result properly
|
||||||
match agent
|
match agent
|
||||||
.execute_task_with_timing(&player_prompt, None, false, show_prompt, show_code, true)
|
.execute_task_with_timing(&player_prompt, None, false, show_prompt, show_code, true)
|
||||||
@@ -631,7 +663,7 @@ async fn run_autonomous(
|
|||||||
// Continue to coach review even if player had an error
|
// Continue to coach review even if player had an error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Give some time for file operations to complete
|
// Give some time for file operations to complete
|
||||||
tokio::time::sleep(tokio::time::Duration::from_millis(500)).await;
|
tokio::time::sleep(tokio::time::Duration::from_millis(500)).await;
|
||||||
|
|
||||||
@@ -643,7 +675,10 @@ async fn run_autonomous(
|
|||||||
// Ensure coach agent is also in the workspace directory
|
// Ensure coach agent is also in the workspace directory
|
||||||
project.enter_workspace()?;
|
project.enter_workspace()?;
|
||||||
|
|
||||||
output.print(&format!("\n=== TURN {}/{} - COACH MODE ===", turn, max_turns));
|
output.print(&format!(
|
||||||
|
"\n=== TURN {}/{} - COACH MODE ===",
|
||||||
|
turn, max_turns
|
||||||
|
));
|
||||||
|
|
||||||
// Coach mode: critique the implementation
|
// Coach mode: critique the implementation
|
||||||
let coach_prompt = format!(
|
let coach_prompt = format!(
|
||||||
@@ -704,4 +739,4 @@ Keep your response concise and focused on actionable items.",
|
|||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user