From ad11b479c36c7e1d2445fe47ba9b96ea244bece5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 17 Mar 2026 05:53:44 +0000 Subject: [PATCH 1/6] Initial plan From 01cf0f2726721ab9e1f49eab778f3ee181d6a597 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 17 Mar 2026 06:02:22 +0000 Subject: [PATCH 2/6] Support AGENTS.md instructions in ReAct system prompt Co-authored-by: assapir <4030466+assapir@users.noreply.github.com> --- src/prompts/react.rs | 50 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/src/prompts/react.rs b/src/prompts/react.rs index 79d0b56..778aeb5 100644 --- a/src/prompts/react.rs +++ b/src/prompts/react.rs @@ -1,4 +1,5 @@ use crate::thinker::ToolDescription; +use std::fs; const INTRO: &str = "You are Golem, an AI agent that solves tasks using a ReAct loop.\n\nCRITICAL: Your entire response must be a single JSON object. No prose, no explanation, no markdown — just JSON."; @@ -35,12 +36,20 @@ const RULES: &[&str] = &[ ]; pub fn build_react_system_prompt(tools: &[ToolDescription]) -> String { - build_react_system_prompt_with_session(tools, false) + build_react_system_prompt_with_session_and_agents(tools, false, load_agents_md()) } pub fn build_react_system_prompt_with_session( tools: &[ToolDescription], has_session_history: bool, +) -> String { + build_react_system_prompt_with_session_and_agents(tools, has_session_history, load_agents_md()) +} + +fn build_react_system_prompt_with_session_and_agents( + tools: &[ToolDescription], + has_session_history: bool, + agents_md: Option, ) -> String { let mut prompt = String::with_capacity(1024); @@ -53,6 +62,12 @@ pub fn build_react_system_prompt_with_session( prompt.push('\n'); } + if let Some(agents_md) = agents_md { + prompt.push_str("\nRepository instructions (AGENTS.md):\n"); + prompt.push_str(&sanitize_agents_md(&agents_md)); + prompt.push('\n'); + } + // Tool list if !tools.is_empty() { prompt.push_str("\nAvailable tools:\n"); @@ -81,6 +96,22 @@ pub fn build_react_system_prompt_with_session( prompt } +fn load_agents_md() -> Option { + for path in ["AGENTS.md", "agents.md"] { + if let Ok(content) = fs::read_to_string(path) { + let trimmed = content.trim(); + if !trimmed.is_empty() { + return Some(trimmed.to_string()); + } + } + } + None +} + +fn sanitize_agents_md(content: &str) -> String { + content.replace("```", "'''") +} + #[cfg(test)] mod tests { use super::*; @@ -187,4 +218,21 @@ mod tests { let prompt = build_react_system_prompt(&[]); assert!(!prompt.contains("prior tasks")); } + + #[test] + fn includes_agents_md_when_present() { + let prompt = build_react_system_prompt_with_session_and_agents( + &[], + false, + Some("# AGENTS\n```bash\nfollow repo instructions\n```".to_string()), + ); + assert!(prompt.contains("Repository instructions (AGENTS.md):")); + assert!(prompt.contains("# AGENTS\n'''bash\nfollow repo instructions\n'''")); + } + + #[test] + fn omits_agents_md_section_when_missing() { + let prompt = build_react_system_prompt_with_session_and_agents(&[], false, None); + assert!(!prompt.contains("Repository instructions (AGENTS.md):")); + } } From 507f446a0096bc17cda8040eb31fc7b72211294e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 17 Mar 2026 06:08:11 +0000 Subject: [PATCH 3/6] Optimize AGENTS.md prompt loading and finalize tests Co-authored-by: assapir <4030466+assapir@users.noreply.github.com> --- src/prompts/react.rs | 35 ++++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/src/prompts/react.rs b/src/prompts/react.rs index 778aeb5..44020d5 100644 --- a/src/prompts/react.rs +++ b/src/prompts/react.rs @@ -1,5 +1,6 @@ use crate::thinker::ToolDescription; use std::fs; +use std::sync::OnceLock; const INTRO: &str = "You are Golem, an AI agent that solves tasks using a ReAct loop.\n\nCRITICAL: Your entire response must be a single JSON object. No prose, no explanation, no markdown — just JSON."; @@ -35,6 +36,8 @@ const RULES: &[&str] = &[ "When you have enough information, respond with the answer format.", ]; +static AGENTS_MD_CACHE: OnceLock> = OnceLock::new(); + pub fn build_react_system_prompt(tools: &[ToolDescription]) -> String { build_react_system_prompt_with_session_and_agents(tools, false, load_agents_md()) } @@ -49,7 +52,7 @@ pub fn build_react_system_prompt_with_session( fn build_react_system_prompt_with_session_and_agents( tools: &[ToolDescription], has_session_history: bool, - agents_md: Option, + agents_md: Option<&str>, ) -> String { let mut prompt = String::with_capacity(1024); @@ -63,8 +66,9 @@ fn build_react_system_prompt_with_session_and_agents( } if let Some(agents_md) = agents_md { - prompt.push_str("\nRepository instructions (AGENTS.md):\n"); - prompt.push_str(&sanitize_agents_md(&agents_md)); + prompt.push('\n'); + prompt.push_str("Repository instructions (AGENTS.md):\n"); + prompt.push_str(&sanitize_agents_md(agents_md)); prompt.push('\n'); } @@ -96,16 +100,21 @@ fn build_react_system_prompt_with_session_and_agents( prompt } -fn load_agents_md() -> Option { - for path in ["AGENTS.md", "agents.md"] { - if let Ok(content) = fs::read_to_string(path) { - let trimmed = content.trim(); - if !trimmed.is_empty() { - return Some(trimmed.to_string()); +fn load_agents_md() -> Option<&'static str> { + AGENTS_MD_CACHE + .get_or_init(|| { + // Prefer AGENTS.md when both are present, then fall back to agents.md. + for path in ["AGENTS.md", "agents.md"] { + if let Ok(content) = fs::read_to_string(path) { + let trimmed = content.trim(); + if !trimmed.is_empty() { + return Some(trimmed.to_string()); + } + } } - } - } - None + None + }) + .as_deref() } fn sanitize_agents_md(content: &str) -> String { @@ -224,7 +233,7 @@ mod tests { let prompt = build_react_system_prompt_with_session_and_agents( &[], false, - Some("# AGENTS\n```bash\nfollow repo instructions\n```".to_string()), + Some("# AGENTS\n```bash\nfollow repo instructions\n```"), ); assert!(prompt.contains("Repository instructions (AGENTS.md):")); assert!(prompt.contains("# AGENTS\n'''bash\nfollow repo instructions\n'''")); From f218ba3868687bf4caf8220842323ef71d08e814 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 17 Mar 2026 06:14:20 +0000 Subject: [PATCH 4/6] Support CLAUDE/GEMINI/copilot instruction files Co-authored-by: assapir <4030466+assapir@users.noreply.github.com> --- src/prompts/react.rs | 45 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 35 insertions(+), 10 deletions(-) diff --git a/src/prompts/react.rs b/src/prompts/react.rs index 44020d5..c5be6f8 100644 --- a/src/prompts/react.rs +++ b/src/prompts/react.rs @@ -36,17 +36,21 @@ const RULES: &[&str] = &[ "When you have enough information, respond with the answer format.", ]; -static AGENTS_MD_CACHE: OnceLock> = OnceLock::new(); +static AGENT_INSTRUCTIONS_CACHE: OnceLock> = OnceLock::new(); pub fn build_react_system_prompt(tools: &[ToolDescription]) -> String { - build_react_system_prompt_with_session_and_agents(tools, false, load_agents_md()) + build_react_system_prompt_with_session_and_agents(tools, false, load_agent_instructions()) } pub fn build_react_system_prompt_with_session( tools: &[ToolDescription], has_session_history: bool, ) -> String { - build_react_system_prompt_with_session_and_agents(tools, has_session_history, load_agents_md()) + build_react_system_prompt_with_session_and_agents( + tools, + has_session_history, + load_agent_instructions(), + ) } fn build_react_system_prompt_with_session_and_agents( @@ -67,7 +71,7 @@ fn build_react_system_prompt_with_session_and_agents( if let Some(agents_md) = agents_md { prompt.push('\n'); - prompt.push_str("Repository instructions (AGENTS.md):\n"); + prompt.push_str("Repository agent instructions:\n"); prompt.push_str(&sanitize_agents_md(agents_md)); prompt.push('\n'); } @@ -100,11 +104,12 @@ fn build_react_system_prompt_with_session_and_agents( prompt } -fn load_agents_md() -> Option<&'static str> { - AGENTS_MD_CACHE +fn load_agent_instructions() -> Option<&'static str> { + AGENT_INSTRUCTIONS_CACHE .get_or_init(|| { - // Prefer AGENTS.md when both are present, then fall back to agents.md. - for path in ["AGENTS.md", "agents.md"] { + // Ordered by precedence. Keep AGENTS first, then common single-file + // alternatives used by other agents. + for path in instruction_file_candidates() { if let Ok(content) = fs::read_to_string(path) { let trimmed = content.trim(); if !trimmed.is_empty() { @@ -117,6 +122,18 @@ fn load_agents_md() -> Option<&'static str> { .as_deref() } +fn instruction_file_candidates() -> &'static [&'static str] { + &[ + "AGENTS.md", + "agents.md", + "CLAUDE.md", + "claude.md", + "GEMINI.md", + "gemini.md", + ".github/copilot-instructions.md", + ] +} + fn sanitize_agents_md(content: &str) -> String { content.replace("```", "'''") } @@ -235,13 +252,21 @@ mod tests { false, Some("# AGENTS\n```bash\nfollow repo instructions\n```"), ); - assert!(prompt.contains("Repository instructions (AGENTS.md):")); + assert!(prompt.contains("Repository agent instructions:")); assert!(prompt.contains("# AGENTS\n'''bash\nfollow repo instructions\n'''")); } #[test] fn omits_agents_md_section_when_missing() { let prompt = build_react_system_prompt_with_session_and_agents(&[], false, None); - assert!(!prompt.contains("Repository instructions (AGENTS.md):")); + assert!(!prompt.contains("Repository agent instructions:")); + } + + #[test] + fn instruction_candidates_include_other_agent_files() { + let files = instruction_file_candidates(); + assert!(files.contains(&"CLAUDE.md")); + assert!(files.contains(&"GEMINI.md")); + assert!(files.contains(&".github/copilot-instructions.md")); } } From d729e6624107a347ee2ddb3d8f44622a0a8baa17 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 17 Mar 2026 06:18:26 +0000 Subject: [PATCH 5/6] Support additional agent instruction files in prompt loader Co-authored-by: assapir <4030466+assapir@users.noreply.github.com> --- src/prompts/react.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/prompts/react.rs b/src/prompts/react.rs index c5be6f8..4466a47 100644 --- a/src/prompts/react.rs +++ b/src/prompts/react.rs @@ -107,9 +107,11 @@ fn build_react_system_prompt_with_session_and_agents( fn load_agent_instructions() -> Option<&'static str> { AGENT_INSTRUCTIONS_CACHE .get_or_init(|| { - // Ordered by precedence. Keep AGENTS first, then common single-file - // alternatives used by other agents. + // Ordered by precedence. Keep AGENTS/agents first, then common + // single-file alternatives used by other agents. for path in instruction_file_candidates() { + // Missing/unreadable files are expected in many repos; ignore and + // continue scanning fallback candidates. if let Ok(content) = fs::read_to_string(path) { let trimmed = content.trim(); if !trimmed.is_empty() { @@ -123,6 +125,8 @@ fn load_agent_instructions() -> Option<&'static str> { } fn instruction_file_candidates() -> &'static [&'static str] { + // Explicit filename variants keep behavior predictable across platforms and + // avoid case-insensitive filesystem assumptions. &[ "AGENTS.md", "agents.md", @@ -135,6 +139,8 @@ fn instruction_file_candidates() -> &'static [&'static str] { } fn sanitize_agents_md(content: &str) -> String { + // Keep sanitization minimal: strip markdown code fences so we preserve the + // prompt's no-fences constraint while leaving other text unchanged. content.replace("```", "'''") } From 7452dd87bde3990a025a6716975f4f5a863d1102 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 17 Mar 2026 06:19:22 +0000 Subject: [PATCH 6/6] Use static instruction-file candidate list Co-authored-by: assapir <4030466+assapir@users.noreply.github.com> --- src/prompts/react.rs | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/src/prompts/react.rs b/src/prompts/react.rs index 4466a47..22d086d 100644 --- a/src/prompts/react.rs +++ b/src/prompts/react.rs @@ -37,6 +37,17 @@ const RULES: &[&str] = &[ ]; static AGENT_INSTRUCTIONS_CACHE: OnceLock> = OnceLock::new(); +// Explicit filename variants keep behavior predictable across platforms and +// avoid case-insensitive filesystem assumptions. +static INSTRUCTION_FILE_CANDIDATES: &[&str] = &[ + "AGENTS.md", + "agents.md", + "CLAUDE.md", + "claude.md", + "GEMINI.md", + "gemini.md", + ".github/copilot-instructions.md", +]; pub fn build_react_system_prompt(tools: &[ToolDescription]) -> String { build_react_system_prompt_with_session_and_agents(tools, false, load_agent_instructions()) @@ -109,7 +120,7 @@ fn load_agent_instructions() -> Option<&'static str> { .get_or_init(|| { // Ordered by precedence. Keep AGENTS/agents first, then common // single-file alternatives used by other agents. - for path in instruction_file_candidates() { + for path in INSTRUCTION_FILE_CANDIDATES { // Missing/unreadable files are expected in many repos; ignore and // continue scanning fallback candidates. if let Ok(content) = fs::read_to_string(path) { @@ -124,20 +135,6 @@ fn load_agent_instructions() -> Option<&'static str> { .as_deref() } -fn instruction_file_candidates() -> &'static [&'static str] { - // Explicit filename variants keep behavior predictable across platforms and - // avoid case-insensitive filesystem assumptions. - &[ - "AGENTS.md", - "agents.md", - "CLAUDE.md", - "claude.md", - "GEMINI.md", - "gemini.md", - ".github/copilot-instructions.md", - ] -} - fn sanitize_agents_md(content: &str) -> String { // Keep sanitization minimal: strip markdown code fences so we preserve the // prompt's no-fences constraint while leaving other text unchanged. @@ -270,7 +267,7 @@ mod tests { #[test] fn instruction_candidates_include_other_agent_files() { - let files = instruction_file_candidates(); + let files = INSTRUCTION_FILE_CANDIDATES; assert!(files.contains(&"CLAUDE.md")); assert!(files.contains(&"GEMINI.md")); assert!(files.contains(&".github/copilot-instructions.md"));