Remove legacy logs/ directory, consolidate all data under .g3/

This change removes the legacy logs/ directory and consolidates all
session data, error logs, and discovery files under the .g3/ directory.

New directory structure:
- .g3/sessions/<session_id>/session.json - session logs
- .g3/errors/ - error logs (was logs/errors/)
- .g3/background_processes/ - background process logs
- .g3/discovery/ - planner discovery files (was workspace/logs/)

Changes:
- paths.rs: Remove get_logs_dir()/logs_dir(), add get_errors_dir(),
  get_background_processes_dir(), get_discovery_dir()
- session.rs: Anonymous sessions now use .g3/sessions/anonymous_<ts>/
- error_handling.rs: Errors now saved to .g3/errors/
- project.rs: Remove logs_dir() and ensure_logs_dir() methods
- feedback_extraction.rs: Remove logs_dir field and fallback logic
- planner: Use .g3/ for workspace data and .g3/discovery/ for reports
- flock.rs: Look for session metrics in .g3/sessions/
- coach_feedback.rs: Remove fallback to logs/ path
- Update all tests to use new paths
- Update README.md and .gitignore
This commit is contained in:
Dhanji R. Prasanna
2026-01-12 18:20:08 +05:30
parent 43a5d27149
commit c2aa80647a
68 changed files with 744 additions and 159 deletions

3
.gitignore vendored
View File

@@ -23,8 +23,7 @@ target
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
# Session logs directory
logs/
# G3 session data directory
.g3/
# g3 artifacts

View File

@@ -60,7 +60,7 @@ G3 includes robust error handling with automatic retry logic:
- **Recoverable Error Detection**: Automatically identifies recoverable errors (rate limits, network issues, server errors, timeouts)
- **Exponential Backoff with Jitter**: Implements intelligent retry delays to avoid overwhelming services
- **Detailed Error Logging**: Captures comprehensive error context including stack traces, request/response data, and session information
- **Error Persistence**: Saves detailed error logs to `logs/errors/` for post-mortem analysis
- **Error Persistence**: Saves detailed error logs to `.g3/errors/` for post-mortem analysis
- **Graceful Degradation**: Non-recoverable errors are logged with full context before terminating
### Tool Call Duplicate Detection
@@ -316,12 +316,12 @@ G3 can interact with your computer's GUI for automation tasks:
## Session Logs
G3 automatically saves session logs for each interaction in the `logs/` directory. These logs contain:
G3 automatically saves session logs for each interaction in the `.g3/sessions/` directory. These logs contain:
- Complete conversation history
- Token usage statistics
- Timestamps and session status
The `logs/` directory is created automatically on first use and is excluded from version control.
The `.g3/` directory is created automatically on first use and is excluded from version control.
## Documentation Map

View File

@@ -63,7 +63,7 @@ pub struct Cli {
#[arg(long, value_name = "MODEL")]
pub model: Option<String>,
/// Disable log file creation (no logs/ directory or session logs)
/// Disable session log file creation (no .g3/sessions/ or error logs)
#[arg(long)]
pub quiet: bool,

View File

@@ -54,12 +54,7 @@ pub fn extract_from_logs(
/// Resolve the log file path, trying new path first then falling back to old.
fn resolve_log_path(session_id: &str) -> std::path::PathBuf {
let new_path = g3_core::get_session_file(session_id);
if new_path.exists() {
new_path
} else {
Path::new("logs").join(format!("g3_session_{}.json", session_id))
}
g3_core::get_session_file(session_id)
}
/// Extract feedback from a session log file.

View File

@@ -4,15 +4,17 @@ use tempfile::TempDir;
#[test]
fn test_extract_coach_feedback_with_timing_message() {
// Create a temporary directory for logs
// Create a temporary directory for session logs
let temp_dir = TempDir::new().unwrap();
let logs_dir = temp_dir.path().join("logs");
fs::create_dir(&logs_dir).unwrap();
let sessions_dir = temp_dir.path().join(".g3").join("sessions");
fs::create_dir_all(&sessions_dir).unwrap();
// Create a mock session log with the problematic conversation history
// where timing message appears after the tool result
let session_id = "test_session_123";
let log_file_path = logs_dir.join(format!("g3_session_{}.json", session_id));
let session_dir = sessions_dir.join(session_id);
fs::create_dir_all(&session_dir).unwrap();
let log_file_path = session_dir.join("session.json");
let log_content = json!({
"session_id": session_id,
@@ -93,11 +95,13 @@ fn test_extract_coach_feedback_with_timing_message() {
fn test_extract_only_final_output_tool_results() {
// Test that we only extract tool results from final_output, not from other tools
let temp_dir = TempDir::new().unwrap();
let logs_dir = temp_dir.path().join("logs");
fs::create_dir(&logs_dir).unwrap();
let sessions_dir = temp_dir.path().join(".g3").join("sessions");
fs::create_dir_all(&sessions_dir).unwrap();
let session_id = "test_session_final_output_only";
let log_file_path = logs_dir.join(format!("g3_session_{}.json", session_id));
let session_dir = sessions_dir.join(session_id);
fs::create_dir_all(&session_dir).unwrap();
let log_file_path = session_dir.join("session.json");
let log_content = json!({
"session_id": session_id,
@@ -184,14 +188,16 @@ fn test_extract_only_final_output_tool_results() {
#[test]
fn test_extract_coach_feedback_without_timing_message() {
// Create a temporary directory for logs
// Create a temporary directory for session logs
let temp_dir = TempDir::new().unwrap();
let logs_dir = temp_dir.path().join("logs");
fs::create_dir(&logs_dir).unwrap();
let sessions_dir = temp_dir.path().join(".g3").join("sessions");
fs::create_dir_all(&sessions_dir).unwrap();
// Test the case where there's no timing message (backward compatibility)
let session_id = "test_session_456";
let log_file_path = logs_dir.join(format!("g3_session_{}.json", session_id));
let session_dir = sessions_dir.join(session_id);
fs::create_dir_all(&session_dir).unwrap();
let log_file_path = session_dir.join("session.json");
let log_content = json!({
"session_id": session_id,
@@ -256,11 +262,13 @@ fn test_extract_coach_feedback_without_timing_message() {
fn test_extract_coach_feedback_with_multiple_tool_results() {
// Test that we get the LAST tool result when there are multiple
let temp_dir = TempDir::new().unwrap();
let logs_dir = temp_dir.path().join("logs");
fs::create_dir(&logs_dir).unwrap();
let sessions_dir = temp_dir.path().join(".g3").join("sessions");
fs::create_dir_all(&sessions_dir).unwrap();
let session_id = "test_session_789";
let log_file_path = logs_dir.join(format!("g3_session_{}.json", session_id));
let session_dir = sessions_dir.join(session_id);
fs::create_dir_all(&session_dir).unwrap();
let log_file_path = session_dir.join("session.json");
let log_content = json!({
"session_id": session_id,

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -129,8 +129,7 @@ impl ErrorContext {
return;
}
let base_logs_dir = crate::logs_dir();
let logs_dir = base_logs_dir.join("errors");
let logs_dir = crate::paths::get_errors_dir();
if !logs_dir.exists() {
if let Err(e) = std::fs::create_dir_all(&logs_dir) {
error!("Failed to create error logs directory: {}", e);

View File

@@ -8,10 +8,9 @@
//!
//! Used by both autonomous mode (g3-cli) and planning mode (g3-planner).
use crate::{logs_dir, Agent, TaskResult};
use crate::{Agent, TaskResult};
use crate::ui_writer::UiWriter;
use serde_json::Value;
use std::path::PathBuf;
use tracing::{debug, warn};
/// Result of feedback extraction with source information
@@ -60,8 +59,6 @@ impl ExtractedFeedback {
pub struct FeedbackExtractionConfig {
/// Whether to print debug information
pub verbose: bool,
/// Custom logs directory (overrides default)
pub logs_dir: Option<PathBuf>,
/// Default feedback message if extraction fails
pub default_feedback: String,
}
@@ -70,7 +67,6 @@ impl Default for FeedbackExtractionConfig {
fn default() -> Self {
Self {
verbose: false,
logs_dir: None,
default_feedback: "The implementation needs review. Please ensure all requirements are met and the code compiles without errors.".to_string(),
}
}
@@ -149,17 +145,10 @@ fn try_extract_last_assistant_message(
session_id: &str,
config: &FeedbackExtractionConfig,
) -> Option<String> {
// Try new .g3/sessions/<session_id>/session.json path first
let _ = config; // config no longer used but kept for API compatibility
// Use .g3/sessions/<session_id>/session.json path
let log_file_path = crate::get_session_file(session_id);
// Fall back to old logs/ path if new path doesn't exist
let log_file_path = if log_file_path.exists() {
log_file_path
} else {
let logs_path = config.logs_dir.clone().unwrap_or_else(logs_dir);
logs_path.join(format!("g3_session_{}.json", session_id))
};
if !log_file_path.exists() {
debug!("Session log file not found: {:?}", log_file_path);
return None;
@@ -214,17 +203,10 @@ fn try_extract_from_session_log(
session_id: &str,
config: &FeedbackExtractionConfig,
) -> Option<String> {
// Try new .g3/sessions/<session_id>/session.json path first
let _ = config; // config no longer used but kept for API compatibility
// Use .g3/sessions/<session_id>/session.json path
let log_file_path = crate::get_session_file(session_id);
// Fall back to old logs/ path if new path doesn't exist
let log_file_path = if log_file_path.exists() {
log_file_path
} else {
let logs_path = config.logs_dir.clone().unwrap_or_else(logs_dir);
logs_path.join(format!("g3_session_{}.json", session_id))
};
if !log_file_path.exists() {
debug!("Session log file not found: {:?}", log_file_path);
return None;
@@ -358,17 +340,10 @@ fn try_extract_from_conversation_history(
session_id: &str,
config: &FeedbackExtractionConfig,
) -> Option<String> {
// Try new .g3/sessions/<session_id>/session.json path first
let _ = config; // config no longer used but kept for API compatibility
// Use .g3/sessions/<session_id>/session.json path
let log_file_path = crate::get_session_file(session_id);
// Fall back to old logs/ path if new path doesn't exist
let log_file_path = if log_file_path.exists() {
log_file_path
} else {
let logs_path = config.logs_dir.clone().unwrap_or_else(logs_dir);
logs_path.join(format!("g3_session_{}.json", session_id))
};
if !log_file_path.exists() {
return None;
}
@@ -652,7 +627,6 @@ mod tests {
fn test_feedback_extraction_config_default() {
let config = FeedbackExtractionConfig::default();
assert!(!config.verbose);
assert!(config.logs_dir.is_none());
assert!(config.default_feedback.contains("review"));
}
}

View File

@@ -53,10 +53,11 @@ use std::time::{Duration, Instant};
use tokio_util::sync::CancellationToken;
use tracing::{debug, error, warn};
// Re-export path utilities for backward compatibility
// Re-export path utilities
pub use paths::{
G3_WORKSPACE_PATH_ENV, ensure_session_dir, get_context_summary_file, get_g3_dir, get_logs_dir,
get_session_file, get_session_logs_dir, get_session_todo_path, get_thinned_dir, logs_dir,
G3_WORKSPACE_PATH_ENV, ensure_session_dir, get_context_summary_file, get_g3_dir,
get_session_file, get_session_logs_dir, get_session_todo_path, get_thinned_dir,
get_errors_dir, get_background_processes_dir, get_discovery_dir,
};
use paths::get_todo_path;
@@ -291,7 +292,7 @@ impl<W: UiWriter> Agent<W> {
working_dir: None,
background_process_manager: std::sync::Arc::new(
background_process::BackgroundProcessManager::new(
paths::get_logs_dir().join("background_processes")
paths::get_background_processes_dir()
)),
pending_images: Vec::new(),
is_agent_mode: false,

View File

@@ -2,7 +2,7 @@
//!
//! This module centralizes all path-related logic for:
//! - TODO file location
//! - Logs directory
//! - Error logs directory
//! - Session directories and files
//! - Thinned content storage
@@ -33,22 +33,29 @@ pub fn get_session_todo_path(session_id: &str) -> PathBuf {
get_session_logs_dir(session_id).join("todo.g3.md")
}
/// Get the path to the logs directory.
/// Get the path to the errors directory.
///
/// Checks for G3_WORKSPACE_PATH environment variable first (used by planning mode),
/// then falls back to "logs" in the current directory.
pub fn get_logs_dir() -> PathBuf {
if let Ok(workspace_path) = std::env::var(G3_WORKSPACE_PATH_ENV) {
PathBuf::from(workspace_path).join("logs")
} else {
std::env::current_dir().unwrap_or_default().join("logs")
}
/// Returns `.g3/errors/` in the workspace or current directory.
pub fn get_errors_dir() -> PathBuf {
get_g3_dir().join("errors")
}
/// Public accessor for the logs directory path (for use by submodules).
/// Alias for `get_logs_dir()` for backward compatibility.
pub fn logs_dir() -> PathBuf {
get_logs_dir()
/// Get the path to the background processes directory.
///
/// Returns `.g3/background_processes/` in the workspace or current directory.
pub fn get_background_processes_dir() -> PathBuf {
get_g3_dir().join("background_processes")
}
/// Get the path to the discovery logs directory (for planner mode).
///
/// Returns `.g3/discovery/` in the workspace or current directory.
pub fn get_discovery_dir() -> PathBuf {
if let Ok(workspace_path) = std::env::var(G3_WORKSPACE_PATH_ENV) {
PathBuf::from(workspace_path).join(".g3").join("discovery")
} else {
get_g3_dir().join("discovery")
}
}
/// Get the base .g3 directory path.

View File

@@ -127,18 +127,4 @@ impl Project {
std::env::set_current_dir(&self.workspace_dir)?;
Ok(())
}
/// Get the logs directory for the project
pub fn logs_dir(&self) -> PathBuf {
self.workspace_dir.join("logs")
}
/// Ensure the logs directory exists
pub fn ensure_logs_dir(&self) -> Result<()> {
let logs_dir = self.logs_dir();
if !logs_dir.exists() {
std::fs::create_dir_all(&logs_dir)?;
}
Ok(())
}
}

View File

@@ -5,7 +5,7 @@
//! operations from the Agent, keeping the Agent as a thin orchestrator.
use crate::context_window::ContextWindow;
use crate::paths::{ensure_session_dir, get_context_summary_file, get_g3_dir, get_logs_dir, get_session_file};
use crate::paths::{ensure_session_dir, get_context_summary_file, get_g3_dir, get_session_file};
use g3_providers::MessageRole;
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
@@ -90,7 +90,7 @@ pub fn generate_session_id(description: &str, agent_name: Option<&str>) -> Strin
/// Save the context window to a session file.
///
/// If session_id is provided, saves to `.g3/sessions/<session_id>/session.json`.
/// Otherwise, falls back to `logs/g3_context_<timestamp>.json`.
/// Otherwise, saves to `.g3/sessions/anonymous_<timestamp>/session.json`.
pub fn save_context_window(
session_id: Option<&str>,
context_window: &ContextWindow,
@@ -110,13 +110,13 @@ pub fn save_context_window(
}
get_session_file(id)
} else {
// Fallback to old logs/ directory for sessions without ID
let logs_dir = get_logs_dir();
if let Err(e) = std::fs::create_dir_all(&logs_dir) {
error!("Failed to create logs directory: {}", e);
// Create anonymous session for sessions without ID
let anonymous_id = format!("anonymous_{}", timestamp);
if let Err(e) = ensure_session_dir(&anonymous_id) {
error!("Failed to create anonymous session directory: {}", e);
return;
}
logs_dir.join(format!("g3_context_{}.json", timestamp))
get_session_file(&anonymous_id)
};
let context_data = serde_json::json!({
@@ -252,8 +252,8 @@ pub fn log_error_to_session(
.unwrap_or_default()
.as_secs();
let logs_dir = get_logs_dir();
let filename = logs_dir.join(format!("g3_session_{}.json", session_id));
// Use the new .g3/sessions/<session_id>/session.json path
let filename = get_session_file(session_id);
// Read existing session log
let mut session_data: serde_json::Value = if filename.exists() {

View File

@@ -29,7 +29,7 @@ echo "Done!"
fs::set_permissions(&script_path, fs::Permissions::from_mode(0o755)).unwrap();
}
let log_dir = test_dir.join("logs");
let log_dir = test_dir.join(".g3").join("background_processes");
let manager = BackgroundProcessManager::new(log_dir);
println!("\n=== Background Process Demo ===");

View File

@@ -53,7 +53,7 @@ done
fs::set_permissions(&script_path, fs::Permissions::from_mode(0o755)).unwrap();
}
let log_dir = test_dir.join("logs");
let log_dir = test_dir.join(".g3").join("background_processes");
let manager = BackgroundProcessManager::new(log_dir);
// Start the process
@@ -116,7 +116,7 @@ sleep 30
fs::set_permissions(&script_path, fs::Permissions::from_mode(0o755)).unwrap();
}
let log_dir = test_dir.join("logs");
let log_dir = test_dir.join(".g3").join("background_processes");
let manager = BackgroundProcessManager::new(log_dir);
// Start a process
@@ -151,7 +151,7 @@ sleep 30
let _ = fs::remove_dir_all(&test_dir);
fs::create_dir_all(&test_dir).unwrap();
let log_dir = test_dir.join("logs");
let log_dir = test_dir.join(".g3").join("background_processes");
let manager = BackgroundProcessManager::new(log_dir);
// Getting a process that doesn't exist should return None

View File

@@ -91,7 +91,7 @@ fn test_save_and_load_continuation() {
"save_load_test".to_string(),
None,
Some("Test summary content".to_string()),
"/logs/g3_session_save_load_test.json".to_string(),
"/.g3/sessions/save_load_test/session.json".to_string(),
35.5,
Some("- [ ] Pending task".to_string()),
temp_dir.path().to_string_lossy().to_string(),
@@ -321,9 +321,9 @@ fn test_has_valid_continuation_with_existing_session_log() {
let (temp_dir, original_dir) = setup_test_env();
// Create a fake session log file
let logs_dir = temp_dir.path().join("logs");
fs::create_dir_all(&logs_dir).expect("Failed to create logs dir");
let session_log_path = logs_dir.join("g3_session_valid_test.json");
let session_dir = temp_dir.path().join(".g3").join("sessions").join("valid_test");
fs::create_dir_all(&session_dir).expect("Failed to create session dir");
let session_log_path = session_dir.join("session.json");
fs::write(&session_log_path, "{}").expect("Failed to write session log");
// Create a continuation pointing to the existing session log

View File

@@ -735,8 +735,8 @@ async fn run_segment(
segment_status.errors += 1;
}
// Try to extract metrics from session log if available
let log_dir = segment_dir.join("logs");
// Try to extract metrics from session log if available (check .g3/sessions/)
let log_dir = segment_dir.join(".g3").join("sessions");
if log_dir.exists() {
if let Ok(entries) = std::fs::read_dir(&log_dir) {
for entry in entries.flatten() {

View File

@@ -66,7 +66,7 @@ pub async fn get_initial_discovery_messages(
// Step 1: Run explore_codebase to get the codebase report
let codebase_report = explore_codebase(codebase_path);
// Write the codebase report to logs directory
// Write the codebase report to discovery directory
write_code_report(&codebase_report)?;
// Step 2: Build the prompt with the codebase report appended
@@ -112,7 +112,7 @@ pub async fn get_initial_discovery_messages(
shell_commands.len()
));
// Write the discovery commands to logs directory
// Write the discovery commands to discovery directory
write_discovery_commands(&shell_commands)?;
// Step 6: Format as tool messages
@@ -194,21 +194,21 @@ pub fn extract_summary(response: &str) -> Option<String> {
}
}
/// Write the codebase report to logs directory
/// Write the codebase report to discovery directory
fn write_code_report(report: &str) -> Result<()> {
// Get logs directory from workspace path or current dir
let logs_dir = if let Ok(workspace_path) = std::env::var("G3_WORKSPACE_PATH") {
std::path::PathBuf::from(workspace_path).join("logs")
// Get discovery directory from workspace path or current dir
let discovery_dir = if let Ok(workspace_path) = std::env::var("G3_WORKSPACE_PATH") {
std::path::PathBuf::from(workspace_path).join(".g3").join("discovery")
} else {
std::env::current_dir().unwrap_or_default().join("logs")
std::env::current_dir().unwrap_or_default().join(".g3").join("discovery")
};
// Ensure logs directory exists
fs::create_dir_all(&logs_dir)?;
// Ensure discovery directory exists
fs::create_dir_all(&discovery_dir)?;
// Generate timestamp in same format as tool_calls log
let timestamp = Local::now().format("%Y%m%d_%H%M%S").to_string();
let filename = logs_dir.join(format!("code_report_{}.log", timestamp));
let filename = discovery_dir.join(format!("code_report_{}.log", timestamp));
// Write the report to file
let mut file = OpenOptions::new()
@@ -223,21 +223,21 @@ fn write_code_report(report: &str) -> Result<()> {
Ok(())
}
/// Write the discovery commands to logs directory
/// Write the discovery commands to discovery directory
fn write_discovery_commands(commands: &[String]) -> Result<()> {
// Get logs directory from workspace path or current dir
let logs_dir = if let Ok(workspace_path) = std::env::var("G3_WORKSPACE_PATH") {
std::path::PathBuf::from(workspace_path).join("logs")
// Get discovery directory from workspace path or current dir
let discovery_dir = if let Ok(workspace_path) = std::env::var("G3_WORKSPACE_PATH") {
std::path::PathBuf::from(workspace_path).join(".g3").join("discovery")
} else {
std::env::current_dir().unwrap_or_default().join("logs")
std::env::current_dir().unwrap_or_default().join(".g3").join("discovery")
};
// Ensure logs directory exists
fs::create_dir_all(&logs_dir)?;
// Ensure discovery directory exists
fs::create_dir_all(&discovery_dir)?;
// Generate timestamp in same format as tool_calls log
let timestamp = Local::now().format("%Y%m%d_%H%M%S").to_string();
let filename = logs_dir.join(format!("discovery_commands_{}.log", timestamp));
let filename = discovery_dir.join(format!("discovery_commands_{}.log", timestamp));
// Write the commands to file
let mut file = OpenOptions::new()

View File

@@ -324,15 +324,13 @@ pub async fn call_refinement_llm_with_tools(
let ui_writer = PlannerUiWriter::new();
// CRITICAL FIX: Use the actual workspace directory, NOT codepath!
// The workspace is where logs should be written (e.g., /tmp/g3_test_workspace)
// The workspace is where session data should be written (e.g., /tmp/g3_test_workspace)
// The codepath is where the source code lives (e.g., ~/RustroverProjects/g3)
// Previous bug: was using codepath as workspace, causing logs to go to wrong location
let workspace_path = std::path::PathBuf::from(workspace);
let project = Project::new(workspace_path.clone());
project.ensure_workspace_exists()?;
project.enter_workspace()?;
project.ensure_logs_dir()?;
// Create agent - not autonomous mode, just regular agent with tools
let mut agent = Agent::new_with_readme_and_quiet(
planner_config,

View File

@@ -777,13 +777,13 @@ pub async fn run_planning_mode(
// Set G3_WORKSPACE_PATH environment variable EARLY for all logging
std::env::set_var("G3_WORKSPACE_PATH", workspace_dir.display().to_string());
// Create logs directory and verify it exists
let logs_dir = workspace_dir.join("logs");
if !logs_dir.exists() {
fs::create_dir_all(&logs_dir)
.context("Failed to create logs directory")?;
// Create .g3 directory and verify it exists
let g3_dir = workspace_dir.join(".g3");
if !g3_dir.exists() {
fs::create_dir_all(&g3_dir)
.context("Failed to create .g3 directory")?;
}
print_msg(&format!("📁 Logs directory: {}", logs_dir.display()));
print_msg(&format!("📁 G3 directory: {}", g3_dir.display()));
// Create the LLM provider for planning
print_msg("🔧 Initializing planner provider...");

View File

@@ -6,22 +6,22 @@ use std::path::Path;
#[test]
fn test_log_files_created() {
// This test verifies that the logging functions work correctly
// by checking that files can be created in the logs directory
// by checking that files can be created in the discovery directory
// Clean up any existing test logs
let _ = fs::remove_dir_all("logs");
// Clean up any existing test discovery dir
let _ = fs::remove_dir_all(".g3/discovery");
// Create logs directory
fs::create_dir_all("logs").expect("Failed to create logs directory");
// Create discovery directory
fs::create_dir_all(".g3/discovery").expect("Failed to create discovery directory");
// Verify directory exists
assert!(Path::new("logs").exists());
assert!(Path::new("logs").is_dir());
assert!(Path::new(".g3/discovery").exists());
assert!(Path::new(".g3/discovery").is_dir());
// Test writing a code report
let test_report = "Test codebase report\nLine 2\nLine 3";
let timestamp = chrono::Local::now().format("%Y%m%d_%H%M%S").to_string();
let report_filename = format!("logs/code_report_{}.log", timestamp);
let report_filename = format!(".g3/discovery/code_report_{}.log", timestamp);
fs::write(&report_filename, test_report).expect("Failed to write code report");
assert!(Path::new(&report_filename).exists());
@@ -30,7 +30,7 @@ fn test_log_files_created() {
assert_eq!(content, test_report);
// Test writing discovery commands
let commands_filename = format!("logs/discovery_commands_{}.log", timestamp);
let commands_filename = format!(".g3/discovery/discovery_commands_{}.log", timestamp);
let test_commands =
"# Discovery Commands\n# Generated by g3-planner\n\nls -la\ncat README.md\n";

View File

@@ -82,7 +82,6 @@ fn test_extracted_feedback_approval_detection() {
fn test_feedback_extraction_config_default() {
let config = FeedbackExtractionConfig::default();
assert!(!config.verbose);
assert!(config.logs_dir.is_none());
assert!(config.default_feedback.contains("review"));
}
@@ -90,14 +89,9 @@ fn test_feedback_extraction_config_default() {
fn test_feedback_extraction_config_custom() {
let config = FeedbackExtractionConfig {
verbose: true,
logs_dir: Some(std::path::PathBuf::from("/tmp/test_logs")),
default_feedback: "Custom fallback message for testing".to_string(),
};
assert!(config.verbose);
assert_eq!(
config.logs_dir,
Some(std::path::PathBuf::from("/tmp/test_logs"))
);
assert!(config.default_feedback.contains("Custom fallback"));
}

File diff suppressed because one or more lines are too long