diff --git a/crates/g3-cli/src/agent_mode.rs b/crates/g3-cli/src/agent_mode.rs index 9a0a903..5886b62 100644 --- a/crates/g3-cli/src/agent_mode.rs +++ b/crates/g3-cli/src/agent_mode.rs @@ -8,6 +8,7 @@ use g3_core::ui_writer::UiWriter; use g3_core::Agent; use crate::project_files::{combine_project_content, read_agents_config, read_project_memory, read_project_readme}; +use crate::language_prompts::{get_language_prompts_for_workspace, get_agent_language_prompts_for_workspace_with_langs}; use crate::simple_output::SimpleOutput; use crate::embedded_agents::load_agent_prompt; use crate::ui_writer_impl::ConsoleUiWriter; @@ -137,16 +138,38 @@ pub async fn run_agent_mode( "·" }; output.print(&format!( - " {} README | {} AGENTS.md | {} Memory", - readme_status, agents_status, memory_status + " {} README {} AGENTS.md {} Memory", + readme_status, agents_status, memory_status, )); + // Get language-specific prompts (same mechanism as normal mode) + let language_content = get_language_prompts_for_workspace(&workspace_dir); + + // Get agent+language-specific prompts (e.g., carmack.racket.md) and show which languages + let detected_langs = crate::language_prompts::detect_languages(&workspace_dir); + let agent_lang_content = if detected_langs.is_empty() { + None + } else { + let (content, matched_langs) = get_agent_language_prompts_for_workspace_with_langs(&workspace_dir, agent_name); + for lang in matched_langs { + output.print(&format!(" ✓ {}: {} language guidance", agent_name, lang)); + } + content + }; + + // Append agent+language-specific content to system prompt if available + let system_prompt = if let Some(agent_lang) = agent_lang_content { + format!("{}\n\n{}", system_prompt, agent_lang) + } else { + system_prompt + }; + // Combine all content for the agent's context let combined_content = combine_project_content( agents_content_opt, readme_content_opt, memory_content_opt, - crate::language_prompts::get_language_prompts_for_workspace(&workspace_dir), + language_content, &workspace_dir, ); diff --git a/crates/g3-cli/src/language_prompts.rs b/crates/g3-cli/src/language_prompts.rs index 6baf3c3..388e966 100644 --- a/crates/g3-cli/src/language_prompts.rs +++ b/crates/g3-cli/src/language_prompts.rs @@ -18,6 +18,13 @@ static LANGUAGE_PROMPTS: &[(&str, &[&str], &str)] = &[ ), ]; +/// Embedded agent-specific language prompts. +/// Format: (agent_name, language_name, prompt_content) +static AGENT_LANGUAGE_PROMPTS: &[(&str, &str, &str)] = &[ + // (agent_name, language_name, prompt_content) + ("carmack", "racket", include_str!("../../../prompts/langs/carmack.racket.md")), +]; + /// Detect languages present in the workspace by scanning for file extensions. /// Returns a list of detected language names. pub fn detect_languages(workspace_dir: &Path) -> Vec<&'static str> { @@ -118,6 +125,46 @@ pub fn list_available_languages() -> Vec<&'static str> { LANGUAGE_PROMPTS.iter().map(|(name, _, _)| *name).collect() } +/// Get agent-specific language prompt for a specific agent and language. +pub fn get_agent_language_prompt(agent_name: &str, lang: &str) -> Option<&'static str> { + AGENT_LANGUAGE_PROMPTS + .iter() + .find(|(agent, language, _)| *agent == agent_name && *language == lang) + .map(|(_, _, content)| *content) +} + +/// Get agent-specific language prompts for detected languages in the workspace. +/// Returns formatted content ready for injection into the agent's system prompt. +#[allow(dead_code)] +pub fn get_agent_language_prompts_for_workspace( + workspace_dir: &Path, + agent_name: &str, +) -> Option { + let (content, _) = get_agent_language_prompts_for_workspace_with_langs(workspace_dir, agent_name); + content +} + +/// Get agent-specific language prompts for detected languages in the workspace. +/// Returns both the formatted content and the list of languages that had matching prompts. +pub fn get_agent_language_prompts_for_workspace_with_langs( + workspace_dir: &Path, + agent_name: &str, +) -> (Option, Vec<&'static str>) { + let detected = detect_languages(workspace_dir); + let mut prompts = Vec::new(); + let mut matched_langs = Vec::new(); + + for lang in detected { + if let Some(content) = get_agent_language_prompt(agent_name, lang) { + prompts.push(content.to_string()); + matched_langs.push(lang); + } + } + + let content = if prompts.is_empty() { None } else { Some(prompts.join("\n\n---\n\n")) }; + (content, matched_langs) +} + #[cfg(test)] mod tests { use super::*; @@ -166,4 +213,29 @@ mod tests { assert!(content.contains("🔧 Language-Specific Guidance")); assert!(content.contains("raco")); } + + #[test] + fn test_carmack_racket_prompt_embedded() { + let prompt = get_agent_language_prompt("carmack", "racket"); + assert!(prompt.is_some()); + assert!(prompt.unwrap().contains("RACKET-SPECIFIC GUIDANCE")); + } + + #[test] + fn test_agent_language_prompt_not_found() { + let prompt = get_agent_language_prompt("nonexistent", "racket"); + assert!(prompt.is_none()); + } + + #[test] + fn test_get_agent_prompts_for_workspace() { + let temp_dir = TempDir::new().unwrap(); + let rkt_file = temp_dir.path().join("main.rkt"); + fs::write(&rkt_file, "#lang racket\n").unwrap(); + + let prompts = get_agent_language_prompts_for_workspace(temp_dir.path(), "carmack"); + assert!(prompts.is_some()); + let content = prompts.unwrap(); + assert!(content.contains("RACKET-SPECIFIC GUIDANCE")); + } }