Skip to main content

codetether_agent/agent/
builtin.rs

1//! Built-in agent definitions
2
3use super::{AgentInfo, AgentMode};
4use std::path::Path;
5
6/// The default "build" agent - full access for development work
7pub fn build_agent() -> AgentInfo {
8    AgentInfo {
9        name: "build".to_string(),
10        description: Some("Full access agent for development work".to_string()),
11        mode: AgentMode::Primary,
12        native: true,
13        hidden: false,
14        model: None,
15        temperature: None,
16        top_p: None,
17        max_steps: Some(100),
18    }
19}
20
21/// The "plan" agent - read-only for analysis and exploration
22pub fn plan_agent() -> AgentInfo {
23    AgentInfo {
24        name: "plan".to_string(),
25        description: Some("Read-only agent for analysis and code exploration".to_string()),
26        mode: AgentMode::Primary,
27        native: true,
28        hidden: false,
29        model: None,
30        temperature: None,
31        top_p: None,
32        max_steps: Some(50),
33    }
34}
35
36/// The "explore" agent - fast codebase exploration
37pub fn explore_agent() -> AgentInfo {
38    AgentInfo {
39        name: "explore".to_string(),
40        description: Some("Fast agent for exploring codebases".to_string()),
41        mode: AgentMode::Subagent,
42        native: true,
43        hidden: false,
44        model: None,
45        temperature: None,
46        top_p: None,
47        max_steps: Some(20),
48    }
49}
50
51/// System prompt for the build agent
52#[allow(dead_code)]
53pub const BUILD_SYSTEM_PROMPT: &str = r#"You are an expert AI programming assistant called CodeTether Agent.
54
55You help users with software development tasks including:
56- Writing and editing code
57- Debugging and fixing issues
58- Implementing new features
59- Refactoring and improving code quality
60- Explaining code and concepts
61
62You have access to tools that let you:
63- Read and write files
64- Run shell commands
65- Search the codebase
66- List directories
67
68Always:
69- Be concise and helpful
70- Show your work by using tools
71- Explain what you're doing
72- Ask clarifying questions when needed
73- Follow best practices for the language/framework being used
74
75Current working directory: {cwd}
76"#;
77
78/// System prompt for the plan agent
79#[allow(dead_code)]
80pub const PLAN_SYSTEM_PROMPT: &str = r#"You are an expert AI assistant for code analysis and planning.
81
82Your role is to:
83- Explore and understand codebases
84- Analyze code structure and architecture
85- Plan changes and refactoring
86- Answer questions about the code
87
88You have read-only access to the codebase. You can:
89- Read files
90- Search the codebase
91- List directories
92- Run safe commands
93
94You should NOT modify any files. If the user wants changes, explain what should be changed and suggest switching to the build agent.
95
96Current working directory: {cwd}
97"#;
98
99/// System prompt for the explore agent
100///
101/// This constant is available for use when creating an explore agent programmatically.
102/// Currently used via the `explore_agent()` function which returns the AgentInfo.
103#[allow(dead_code)]
104pub const EXPLORE_SYSTEM_PROMPT: &str = r#"You are a fast, focused agent for codebase exploration.
105
106Your job is to quickly find relevant code and information. Use tools efficiently:
107- Use glob to find files by pattern
108- Use grep to search for text
109- Use list to see directory contents
110- Read files to get details
111
112Be thorough but fast. Return the most relevant results without unnecessary exploration.
113"#;
114
115/// Load AGENTS.md from the given directory or any parent directory.
116/// Returns the content and path if found.
117pub fn load_agents_md(start_dir: &Path) -> Option<(String, std::path::PathBuf)> {
118    let mut current = start_dir.to_path_buf();
119
120    loop {
121        let agents_path = current.join("AGENTS.md");
122        if agents_path.exists() {
123            if let Ok(content) = std::fs::read_to_string(&agents_path) {
124                return Some((content, agents_path));
125            }
126        }
127
128        // Try parent directory
129        if !current.pop() {
130            break;
131        }
132    }
133
134    None
135}
136
137/// Load all AGENTS.md files from the given directory up to the root.
138/// Returns a list of (content, path) tuples, from most specific (closest to start_dir) to least.
139pub fn load_all_agents_md(start_dir: &Path) -> Vec<(String, std::path::PathBuf)> {
140    let mut results = Vec::new();
141    let mut current = start_dir.to_path_buf();
142
143    loop {
144        let agents_path = current.join("AGENTS.md");
145        if agents_path.exists() {
146            if let Ok(content) = std::fs::read_to_string(&agents_path) {
147                results.push((content, agents_path));
148            }
149        }
150
151        // Try parent directory
152        if !current.pop() {
153            break;
154        }
155    }
156
157    results
158}
159
160/// Build a complete system prompt for the build agent, including AGENTS.md content if present.
161pub fn build_system_prompt(cwd: &Path) -> String {
162    let base_prompt = BUILD_SYSTEM_PROMPT.replace("{cwd}", &cwd.display().to_string());
163
164    // Load AGENTS.md files (closest first)
165    let agents_files = load_all_agents_md(cwd);
166
167    if agents_files.is_empty() {
168        return base_prompt;
169    }
170
171    // Build the AGENTS.md section - include all found files, closest last (takes precedence)
172    let mut agents_section = String::new();
173    agents_section.push_str("\n\n## Project Instructions (AGENTS.md)\n\n");
174    agents_section
175        .push_str("The following instructions were loaded from AGENTS.md files in the project.\n");
176    agents_section
177        .push_str("Follow these project-specific guidelines when working on this codebase.\n\n");
178
179    // Reverse so closest (most specific) comes last and takes precedence
180    for (content, path) in agents_files.iter().rev() {
181        agents_section.push_str(&format!("### From {}\n\n", path.display()));
182        agents_section.push_str(content);
183        agents_section.push_str("\n\n");
184    }
185
186    format!("{base_prompt}{agents_section}")
187}
188
189/// Build a complete system prompt for the plan agent, including AGENTS.md content if present.
190#[allow(dead_code)]
191pub fn build_plan_system_prompt(cwd: &Path) -> String {
192    let base_prompt = PLAN_SYSTEM_PROMPT.replace("{cwd}", &cwd.display().to_string());
193
194    // Load AGENTS.md files
195    let agents_files = load_all_agents_md(cwd);
196
197    if agents_files.is_empty() {
198        return base_prompt;
199    }
200
201    let mut agents_section = String::new();
202    agents_section.push_str("\n\n## Project Instructions (AGENTS.md)\n\n");
203
204    for (content, path) in agents_files.iter().rev() {
205        agents_section.push_str(&format!("### From {}\n\n", path.display()));
206        agents_section.push_str(content);
207        agents_section.push_str("\n\n");
208    }
209
210    format!("{base_prompt}{agents_section}")
211}