diff --git a/crates/g3-cli/src/agent_mode.rs b/crates/g3-cli/src/agent_mode.rs index a6f5c0e..1bb9a39 100644 --- a/crates/g3-cli/src/agent_mode.rs +++ b/crates/g3-cli/src/agent_mode.rs @@ -181,6 +181,7 @@ pub async fn run_agent_mode( agents_content_opt, readme_content_opt, memory_content_opt, + &workspace_dir, ); // Create agent with custom system prompt diff --git a/crates/g3-cli/src/lib.rs b/crates/g3-cli/src/lib.rs index 87559f8..fac3dab 100644 --- a/crates/g3-cli/src/lib.rs +++ b/crates/g3-cli/src/lib.rs @@ -106,7 +106,7 @@ pub async fn run() -> Result<()> { let config = load_config_with_cli_overrides(&cli)?; // Combine AGENTS.md, README, and memory content - let combined_content = combine_project_content(agents_content, readme_content, memory_content); + let combined_content = combine_project_content(agents_content, readme_content, memory_content, &workspace_dir); run_console_mode(cli, config, project, combined_content, workspace_dir).await } diff --git a/crates/g3-cli/src/project_files.rs b/crates/g3-cli/src/project_files.rs index 3e26d00..d13318e 100644 --- a/crates/g3-cli/src/project_files.rs +++ b/crates/g3-cli/src/project_files.rs @@ -85,12 +85,17 @@ pub fn read_project_memory(workspace_dir: &Path) -> Option { /// Combine AGENTS.md, README, and memory content into a single string. /// /// Returns None if all inputs are None, otherwise joins non-None parts with double newlines. +/// Prepends the current working directory to help the LLM avoid path hallucinations. pub fn combine_project_content( agents_content: Option, readme_content: Option, memory_content: Option, + workspace_dir: &Path, ) -> Option { - let parts: Vec = [agents_content, readme_content, memory_content] + // Always include working directory to prevent LLM from hallucinating paths + let cwd_info = format!("📂 Working Directory: {}", workspace_dir.display()); + + let parts: Vec = [Some(cwd_info), agents_content, readme_content, memory_content] .into_iter() .flatten() .collect(); @@ -201,23 +206,37 @@ mod tests { #[test] fn test_combine_project_content_all_some() { + let workspace = std::path::PathBuf::from("/test/workspace"); let result = combine_project_content( Some("agents".to_string()), Some("readme".to_string()), Some("memory".to_string()), + &workspace, ); - assert_eq!(result, Some("agents\n\nreadme\n\nmemory".to_string())); + assert!(result.is_some()); + let content = result.unwrap(); + assert!(content.contains("📂 Working Directory: /test/workspace")); + assert!(content.contains("agents")); + assert!(content.contains("readme")); + assert!(content.contains("memory")); } #[test] fn test_combine_project_content_partial() { - let result = combine_project_content(None, Some("readme".to_string()), None); - assert_eq!(result, Some("readme".to_string())); + let workspace = std::path::PathBuf::from("/test/workspace"); + let result = combine_project_content(None, Some("readme".to_string()), None, &workspace); + assert!(result.is_some()); + let content = result.unwrap(); + assert!(content.contains("📂 Working Directory: /test/workspace")); + assert!(content.contains("readme")); } #[test] fn test_combine_project_content_all_none() { - let result = combine_project_content(None, None, None); - assert_eq!(result, None); + let workspace = std::path::PathBuf::from("/test/workspace"); + let result = combine_project_content(None, None, None, &workspace); + // Now always returns Some because we always include the working directory + assert!(result.is_some()); + assert!(result.unwrap().contains("📂 Working Directory: /test/workspace")); } }