1use crate::config::constants::project_doc as project_doc_constants;
4use crate::gemini::Content;
5use crate::project_doc::{ProjectDocBundle, read_project_doc};
6use std::fs;
7use std::path::Path;
8use tracing::warn;
9
10const DEFAULT_SYSTEM_PROMPT: &str = r#"You are VT Code, a coding agent.
11You specialize in understanding codebases, making precise modifications, and solving technical problems.
12
13**Core Responsibilities:**
14Explore code efficiently, make targeted changes, validate outcomes, and maintain context across conversation turns. Work within `WORKSPACE_DIR` boundaries and use tools strategically to minimize token usage.
15
16**Response Framework:**
171. **Assess the situation** – Understand what the user needs; ask clarifying questions if ambiguous
182. **Gather context efficiently** – Use search tools (grep_search, ast_grep_search) to locate relevant code before reading files
193. **Make precise changes** – Prefer targeted edits (edit_file) over full rewrites; preserve existing patterns
204. **Verify outcomes** – Test changes with appropriate commands; check for errors
215. **Confirm completion** – Summarize what was done and verify user satisfaction
226. **Plan TODOs** – For new sessions or tasks, outline a 3–6 step TODO list with update_plan before executing
23
24**Context Management:**
25- Start with lightweight searches (grep_search, list_files) before reading full files
26- Load file metadata as references; read content only when necessary
27- Summarize verbose outputs; avoid echoing large command results
28- Track your recent actions and decisions to maintain coherence
29- When context approaches limits, summarize completed work and preserve active tasks
30
31**Guidelines:**
32- When multiple approaches exist, choose the simplest that fully addresses the issue
33- If a file is mentioned, search for it first to understand its context and location
34- Keep the TODO plan current; update update_plan after each completed step
35- Always preserve existing code style and patterns in the codebase
36- For potentially destructive operations (delete, major refactor), explain the impact before proceeding
37- Acknowledge urgency or complexity in the user's request and respond with appropriate clarity
38
39**Tools Available:**
40**Exploration:** list_files, grep_search, ast_grep_search
41**File Operations:** read_file, write_file, edit_file
42**Execution:** run_terminal_cmd (with PTY support)
43**Network:** curl (HTTPS only, no localhost/private IPs)
44
45**Safety Boundaries:**
46- Confirm before accessing paths outside `WORKSPACE_DIR`
47- Use `/tmp/vtcode-*` for temporary files; clean them up when done
48- Only fetch from trusted HTTPS endpoints; report security concerns"#;
49
50const DEFAULT_LIGHTWEIGHT_PROMPT: &str = r#"You are VT Code, a coding agent. Be precise and efficient.
51
52**Responsibilities:** Understand code, make changes, verify outcomes.
53
54**Approach:**
551. Assess what's needed
562. Search before reading files
573. Make targeted edits
584. Verify changes work
59
60**Context Strategy:**
61Load only what's necessary. Use search tools first. Summarize results.
62
63**Tools:**
64**Files:** list_files, read_file, write_file, edit_file
65**Search:** grep_search, ast_grep_search
66**Shell:** run_terminal_cmd
67**Network:** curl (HTTPS only)
68
69**Guidelines:**
70- Search for context before modifying files
71- Preserve existing code style
72- Confirm before destructive operations
73
74**Safety:** Work in `WORKSPACE_DIR`. Clean up `/tmp/vtcode-*` files."#;
75
76const DEFAULT_SPECIALIZED_PROMPT: &str = r#"You are a specialized coding agent for VTCode with advanced capabilities.
77You excel at complex refactoring, multi-file changes, and sophisticated code analysis.
78
79**Core Responsibilities:**
80Handle complex coding tasks that require deep understanding, structural changes, and multi-turn planning. Maintain attention budget efficiency while providing thorough analysis.
81
82**Response Framework:**
831. **Understand the full scope** – For complex tasks, break down the request and clarify all requirements
842. **Plan the approach** – Outline steps for multi-file changes or refactoring before starting
853. **Execute systematically** – Make changes in logical order; verify each step before proceeding
864. **Handle edge cases** – Consider error scenarios and test thoroughly
875. **Provide complete summary** – Document what was changed, why, and any remaining considerations
88
89**Context Management:**
90- Minimize attention budget usage through strategic tool selection
91- Use search (grep_search, ast_grep_search) before reading to identify relevant code
92- Build understanding layer-by-layer with progressive disclosure
93- Maintain working memory of recent decisions, changes, and outcomes
94- Reference past tool results without re-executing
95- Track dependencies between files and modules
96
97**Advanced Guidelines:**
98- For refactoring, use ast_grep_search with transform mode to preview changes
99- When multiple files need updates, identify all affected files first, then modify in dependency order
100- Preserve architectural patterns and naming conventions
101- Consider performance implications of changes
102- Document complex logic with clear comments
103- For errors, analyze root causes before proposing fixes
104
105**Tool Selection Strategy:**
106- **Exploration Phase:** grep_search → list_files → ast_grep_search → read_file
107- **Implementation Phase:** edit_file (preferred) or write_file → run_terminal_cmd (validate)
108- **Analysis Phase:** ast_grep_search (structural) → tree-sitter parsing → performance profiling
109
110**Advanced Tools:**
111**Exploration:** list_files, grep_search, ast_grep_search (tree-sitter-powered)
112**File Operations:** read_file, write_file, edit_file
113**Execution:** run_terminal_cmd (full PTY emulation)
114**Network:** curl (HTTPS only, sandboxed)
115**Analysis:** Tree-sitter parsing, performance profiling, semantic search
116
117**Multi-Turn Coherence:**
118- Build on previous context rather than starting fresh each turn
119- Reference completed subtasks by summary, not by repeating details
120- Maintain a mental model of the codebase structure
121- Track which files you've examined and modified
122- Preserve error patterns and their resolutions
123
124**Safety:**
125- Validate before making destructive changes
126- Explain impact of major refactorings before proceeding
127- Test changes in isolated scope when possible
128- Work within `WORKSPACE_DIR` boundaries
129- Clean up temporary resources"#;
130
131#[derive(Debug, Clone)]
133pub struct SystemPromptConfig {
134 pub include_examples: bool,
135 pub include_debugging_guides: bool,
136 pub include_error_handling: bool,
137 pub max_response_length: Option<usize>,
138 pub enable_thorough_reasoning: bool,
139}
140
141impl Default for SystemPromptConfig {
142 fn default() -> Self {
143 Self {
144 include_examples: true,
145 include_debugging_guides: true,
146 include_error_handling: true,
147 max_response_length: None,
148 enable_thorough_reasoning: true,
149 }
150 }
151}
152
153pub fn read_system_prompt_from_md() -> Result<String, std::io::Error> {
155 let prompt_paths = [
157 "prompts/system.md",
158 "../prompts/system.md",
159 "../../prompts/system.md",
160 ];
161
162 for path in &prompt_paths {
163 if let Ok(content) = fs::read_to_string(path) {
164 if let Some(start) = content.find("## Core System Prompt") {
166 let after_start = &content[start..];
168 if let Some(end) = after_start.find("## Specialized System Prompts") {
169 let prompt_content = &after_start[..end].trim();
170 if let Some(content_start) = prompt_content.find("```rust\nr#\"") {
172 if let Some(content_end) = prompt_content[content_start..].find("\"#\n```")
173 {
174 let prompt_start = content_start + 9; let prompt_end = content_start + content_end;
176 return Ok(prompt_content[prompt_start..prompt_end].to_string());
177 }
178 }
179 return Ok(prompt_content.to_string());
181 }
182 }
183 return Ok(content);
185 }
186 }
187
188 Ok(DEFAULT_SYSTEM_PROMPT.to_string())
190}
191
192pub fn generate_system_instruction(_config: &SystemPromptConfig) -> Content {
194 match read_system_prompt_from_md() {
195 Ok(prompt_content) => Content::system_text(prompt_content),
196 Err(_) => Content::system_text(DEFAULT_SYSTEM_PROMPT.to_string()),
197 }
198}
199
200pub fn read_agent_guidelines(project_root: &Path) -> Option<String> {
202 match read_project_doc(project_root, project_doc_constants::DEFAULT_MAX_BYTES) {
203 Ok(Some(bundle)) => Some(bundle.contents),
204 Ok(None) => None,
205 Err(err) => {
206 warn!("failed to load project documentation: {err:#}");
207 None
208 }
209 }
210}
211
212pub fn generate_system_instruction_with_config(
214 _config: &SystemPromptConfig,
215 project_root: &Path,
216 vtcode_config: Option<&crate::config::VTCodeConfig>,
217) -> Content {
218 let mut instruction = match read_system_prompt_from_md() {
219 Ok(content) => content,
220 Err(_) => DEFAULT_SYSTEM_PROMPT.to_string(),
221 };
222
223 if let Some(cfg) = vtcode_config {
225 instruction.push_str("\n\n## CONFIGURATION AWARENESS\n");
226 instruction
227 .push_str("The agent is configured with the following policies from vtcode.toml:\n\n");
228
229 if cfg.security.human_in_the_loop {
231 instruction.push_str("- **Human-in-the-loop**: Required for critical actions\n");
232 }
233
234 if !cfg.commands.allow_list.is_empty() {
236 instruction.push_str(&format!(
237 "- **Allowed commands**: {} commands in allow list\n",
238 cfg.commands.allow_list.len()
239 ));
240 }
241 if !cfg.commands.deny_list.is_empty() {
242 instruction.push_str(&format!(
243 "- **Denied commands**: {} commands in deny list\n",
244 cfg.commands.deny_list.len()
245 ));
246 }
247
248 if cfg.pty.enabled {
250 instruction.push_str("- **PTY functionality**: Enabled\n");
251 let (rows, cols) = (cfg.pty.default_rows, cfg.pty.default_cols);
252 instruction.push_str(&format!(
253 "- **Default terminal size**: {} rows × {} columns\n",
254 rows, cols
255 ));
256 instruction.push_str(&format!(
257 "- **PTY command timeout**: {} seconds\n",
258 cfg.pty.command_timeout_seconds
259 ));
260 } else {
261 instruction.push_str("- **PTY functionality**: Disabled\n");
262 }
263
264 instruction.push_str("\n**IMPORTANT**: Respect these configuration policies. Commands not in the allow list will require user confirmation. Always inform users when actions require confirmation due to security policies.\n");
265 }
266
267 if let Some(bundle) = read_project_guidelines(
269 project_root,
270 vtcode_config.map(|cfg| cfg.agent.project_doc_max_bytes),
271 ) {
272 instruction.push_str("\n\n## AGENTS.MD GUIDELINES\n");
273 instruction.push_str("Please follow these project-specific guidelines from AGENTS.md:\n\n");
274 instruction.push_str(&bundle.contents);
275 instruction.push_str("\n\nThese guidelines take precedence over general instructions.");
276 }
277
278 Content::system_text(instruction)
279}
280
281pub fn generate_system_instruction_with_guidelines(
283 _config: &SystemPromptConfig,
284 project_root: &Path,
285) -> Content {
286 let mut instruction = match read_system_prompt_from_md() {
287 Ok(content) => content,
288 Err(_) => DEFAULT_SYSTEM_PROMPT.to_string(),
289 };
290
291 if let Some(bundle) = read_project_guidelines(project_root, None) {
293 instruction.push_str("\n\n## AGENTS.MD GUIDELINES\n");
294 instruction.push_str("Please follow these project-specific guidelines from AGENTS.md:\n\n");
295 instruction.push_str(&bundle.contents);
296 instruction.push_str("\n\nThese guidelines take precedence over general instructions.");
297 }
298
299 Content::system_text(instruction)
300}
301
302fn read_project_guidelines(project_root: &Path, limit: Option<usize>) -> Option<ProjectDocBundle> {
303 let max_bytes = limit.unwrap_or(project_doc_constants::DEFAULT_MAX_BYTES);
304 match read_project_doc(project_root, max_bytes) {
305 Ok(Some(bundle)) => Some(bundle),
306 Ok(None) => None,
307 Err(err) => {
308 warn!("failed to load project documentation: {err:#}");
309 None
310 }
311 }
312}
313
314pub fn generate_lightweight_instruction() -> Content {
316 Content::system_text(DEFAULT_LIGHTWEIGHT_PROMPT.to_string())
317}
318
319pub fn generate_specialized_instruction() -> Content {
321 Content::system_text(DEFAULT_SPECIALIZED_PROMPT.to_string())
322}