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:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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,
|
||||
|
||||
13
crates/g3-core/logs/errors/error_1768079503_unknown.json
Normal file
13
crates/g3-core/logs/errors/error_1768079503_unknown.json
Normal file
File diff suppressed because one or more lines are too long
13
crates/g3-core/logs/errors/error_1768081876_unknown.json
Normal file
13
crates/g3-core/logs/errors/error_1768081876_unknown.json
Normal file
File diff suppressed because one or more lines are too long
13
crates/g3-core/logs/errors/error_1768083039_unknown.json
Normal file
13
crates/g3-core/logs/errors/error_1768083039_unknown.json
Normal file
File diff suppressed because one or more lines are too long
13
crates/g3-core/logs/errors/error_1768085339_unknown.json
Normal file
13
crates/g3-core/logs/errors/error_1768085339_unknown.json
Normal file
File diff suppressed because one or more lines are too long
13
crates/g3-core/logs/errors/error_1768088342_unknown.json
Normal file
13
crates/g3-core/logs/errors/error_1768088342_unknown.json
Normal file
File diff suppressed because one or more lines are too long
13
crates/g3-core/logs/errors/error_1768088453_unknown.json
Normal file
13
crates/g3-core/logs/errors/error_1768088453_unknown.json
Normal file
File diff suppressed because one or more lines are too long
13
crates/g3-core/logs/errors/error_1768128158_unknown.json
Normal file
13
crates/g3-core/logs/errors/error_1768128158_unknown.json
Normal file
File diff suppressed because one or more lines are too long
13
crates/g3-core/logs/errors/error_1768128264_unknown.json
Normal file
13
crates/g3-core/logs/errors/error_1768128264_unknown.json
Normal file
File diff suppressed because one or more lines are too long
13
crates/g3-core/logs/errors/error_1768128473_unknown.json
Normal file
13
crates/g3-core/logs/errors/error_1768128473_unknown.json
Normal file
File diff suppressed because one or more lines are too long
13
crates/g3-core/logs/errors/error_1768128656_unknown.json
Normal file
13
crates/g3-core/logs/errors/error_1768128656_unknown.json
Normal file
File diff suppressed because one or more lines are too long
13
crates/g3-core/logs/errors/error_1768129035_unknown.json
Normal file
13
crates/g3-core/logs/errors/error_1768129035_unknown.json
Normal file
File diff suppressed because one or more lines are too long
13
crates/g3-core/logs/errors/error_1768129323_unknown.json
Normal file
13
crates/g3-core/logs/errors/error_1768129323_unknown.json
Normal file
File diff suppressed because one or more lines are too long
13
crates/g3-core/logs/errors/error_1768129339_unknown.json
Normal file
13
crates/g3-core/logs/errors/error_1768129339_unknown.json
Normal file
File diff suppressed because one or more lines are too long
13
crates/g3-core/logs/errors/error_1768129356_unknown.json
Normal file
13
crates/g3-core/logs/errors/error_1768129356_unknown.json
Normal file
File diff suppressed because one or more lines are too long
13
crates/g3-core/logs/errors/error_1768130842_unknown.json
Normal file
13
crates/g3-core/logs/errors/error_1768130842_unknown.json
Normal file
File diff suppressed because one or more lines are too long
13
crates/g3-core/logs/errors/error_1768131516_unknown.json
Normal file
13
crates/g3-core/logs/errors/error_1768131516_unknown.json
Normal file
File diff suppressed because one or more lines are too long
13
crates/g3-core/logs/errors/error_1768131530_unknown.json
Normal file
13
crates/g3-core/logs/errors/error_1768131530_unknown.json
Normal file
File diff suppressed because one or more lines are too long
13
crates/g3-core/logs/errors/error_1768131543_unknown.json
Normal file
13
crates/g3-core/logs/errors/error_1768131543_unknown.json
Normal file
File diff suppressed because one or more lines are too long
13
crates/g3-core/logs/errors/error_1768131797_unknown.json
Normal file
13
crates/g3-core/logs/errors/error_1768131797_unknown.json
Normal file
File diff suppressed because one or more lines are too long
13
crates/g3-core/logs/errors/error_1768131982_unknown.json
Normal file
13
crates/g3-core/logs/errors/error_1768131982_unknown.json
Normal file
File diff suppressed because one or more lines are too long
13
crates/g3-core/logs/errors/error_1768132117_unknown.json
Normal file
13
crates/g3-core/logs/errors/error_1768132117_unknown.json
Normal file
File diff suppressed because one or more lines are too long
13
crates/g3-core/logs/errors/error_1768132232_unknown.json
Normal file
13
crates/g3-core/logs/errors/error_1768132232_unknown.json
Normal file
File diff suppressed because one or more lines are too long
13
crates/g3-core/logs/errors/error_1768132268_unknown.json
Normal file
13
crates/g3-core/logs/errors/error_1768132268_unknown.json
Normal file
File diff suppressed because one or more lines are too long
13
crates/g3-core/logs/errors/error_1768132404_unknown.json
Normal file
13
crates/g3-core/logs/errors/error_1768132404_unknown.json
Normal file
File diff suppressed because one or more lines are too long
13
crates/g3-core/logs/errors/error_1768132454_unknown.json
Normal file
13
crates/g3-core/logs/errors/error_1768132454_unknown.json
Normal file
File diff suppressed because one or more lines are too long
13
crates/g3-core/logs/errors/error_1768132540_unknown.json
Normal file
13
crates/g3-core/logs/errors/error_1768132540_unknown.json
Normal file
File diff suppressed because one or more lines are too long
13
crates/g3-core/logs/errors/error_1768132553_unknown.json
Normal file
13
crates/g3-core/logs/errors/error_1768132553_unknown.json
Normal file
File diff suppressed because one or more lines are too long
13
crates/g3-core/logs/errors/error_1768133139_unknown.json
Normal file
13
crates/g3-core/logs/errors/error_1768133139_unknown.json
Normal file
File diff suppressed because one or more lines are too long
13
crates/g3-core/logs/errors/error_1768141273_unknown.json
Normal file
13
crates/g3-core/logs/errors/error_1768141273_unknown.json
Normal file
File diff suppressed because one or more lines are too long
13
crates/g3-core/logs/errors/error_1768141548_unknown.json
Normal file
13
crates/g3-core/logs/errors/error_1768141548_unknown.json
Normal file
File diff suppressed because one or more lines are too long
13
crates/g3-core/logs/errors/error_1768143784_unknown.json
Normal file
13
crates/g3-core/logs/errors/error_1768143784_unknown.json
Normal file
File diff suppressed because one or more lines are too long
13
crates/g3-core/logs/errors/error_1768143829_unknown.json
Normal file
13
crates/g3-core/logs/errors/error_1768143829_unknown.json
Normal file
File diff suppressed because one or more lines are too long
13
crates/g3-core/logs/errors/error_1768143855_unknown.json
Normal file
13
crates/g3-core/logs/errors/error_1768143855_unknown.json
Normal file
File diff suppressed because one or more lines are too long
13
crates/g3-core/logs/errors/error_1768143909_unknown.json
Normal file
13
crates/g3-core/logs/errors/error_1768143909_unknown.json
Normal file
File diff suppressed because one or more lines are too long
13
crates/g3-core/logs/errors/error_1768144572_unknown.json
Normal file
13
crates/g3-core/logs/errors/error_1768144572_unknown.json
Normal file
File diff suppressed because one or more lines are too long
13
crates/g3-core/logs/errors/error_1768145313_unknown.json
Normal file
13
crates/g3-core/logs/errors/error_1768145313_unknown.json
Normal file
File diff suppressed because one or more lines are too long
13
crates/g3-core/logs/errors/error_1768145517_unknown.json
Normal file
13
crates/g3-core/logs/errors/error_1768145517_unknown.json
Normal file
File diff suppressed because one or more lines are too long
13
crates/g3-core/logs/errors/error_1768146713_unknown.json
Normal file
13
crates/g3-core/logs/errors/error_1768146713_unknown.json
Normal file
File diff suppressed because one or more lines are too long
13
crates/g3-core/logs/errors/error_1768173587_unknown.json
Normal file
13
crates/g3-core/logs/errors/error_1768173587_unknown.json
Normal file
File diff suppressed because one or more lines are too long
13
crates/g3-core/logs/errors/error_1768175252_unknown.json
Normal file
13
crates/g3-core/logs/errors/error_1768175252_unknown.json
Normal file
File diff suppressed because one or more lines are too long
13
crates/g3-core/logs/errors/error_1768175509_unknown.json
Normal file
13
crates/g3-core/logs/errors/error_1768175509_unknown.json
Normal file
File diff suppressed because one or more lines are too long
13
crates/g3-core/logs/errors/error_1768184765_unknown.json
Normal file
13
crates/g3-core/logs/errors/error_1768184765_unknown.json
Normal file
File diff suppressed because one or more lines are too long
13
crates/g3-core/logs/errors/error_1768184965_unknown.json
Normal file
13
crates/g3-core/logs/errors/error_1768184965_unknown.json
Normal file
File diff suppressed because one or more lines are too long
13
crates/g3-core/logs/errors/error_1768193369_unknown.json
Normal file
13
crates/g3-core/logs/errors/error_1768193369_unknown.json
Normal file
File diff suppressed because one or more lines are too long
13
crates/g3-core/logs/errors/error_1768199392_unknown.json
Normal file
13
crates/g3-core/logs/errors/error_1768199392_unknown.json
Normal file
File diff suppressed because one or more lines are too long
13
crates/g3-core/logs/errors/error_1768200370_unknown.json
Normal file
13
crates/g3-core/logs/errors/error_1768200370_unknown.json
Normal file
File diff suppressed because one or more lines are too long
13
crates/g3-core/logs/errors/error_1768209302_unknown.json
Normal file
13
crates/g3-core/logs/errors/error_1768209302_unknown.json
Normal file
File diff suppressed because one or more lines are too long
@@ -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);
|
||||
|
||||
@@ -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 log_file_path = crate::get_session_file(session_id);
|
||||
let _ = config; // config no longer used but kept for API compatibility
|
||||
|
||||
// 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))
|
||||
};
|
||||
|
||||
// Use .g3/sessions/<session_id>/session.json path
|
||||
let log_file_path = crate::get_session_file(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 log_file_path = crate::get_session_file(session_id);
|
||||
let _ = config; // config no longer used but kept for API compatibility
|
||||
|
||||
// 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))
|
||||
};
|
||||
|
||||
// Use .g3/sessions/<session_id>/session.json path
|
||||
let log_file_path = crate::get_session_file(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 log_file_path = crate::get_session_file(session_id);
|
||||
let _ = config; // config no longer used but kept for API compatibility
|
||||
|
||||
// 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))
|
||||
};
|
||||
|
||||
// Use .g3/sessions/<session_id>/session.json path
|
||||
let log_file_path = crate::get_session_file(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"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
//! Path utilities for G3 session and workspace management.
|
||||
//!
|
||||
//! This module centralizes all path-related logic for:
|
||||
//! - TODO file location
|
||||
//! - Logs directory
|
||||
//! - TODO file location
|
||||
//! - 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.
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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 ===");
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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...");
|
||||
|
||||
@@ -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";
|
||||
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user