vtcode_core/prompts/
system.rs

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