//! System prompts and directive templates for agent and subagent turns. //! //! Centralising all prompt text here keeps the core turn logic free of //! hardcoded prose, making prompts easier to maintain, review, and localise. //! //! # Flow //! The application layer's `AgentTurnServiceImpl` calls `main_agent_prompt()` //! to construct the system message at the start of each turn. Subagent and //! review prompts are provided by their respective modules. /// Build the main-agent system prompt. /// /// The prompt establishes the agent's identity as Zesdex, an AI coding /// assistant, and defines the priority hierarchy that governs tool selection: /// /// 1. **Workflow first** — `workflow_run` / `hive_mind` for complex tasks /// 2. **Planning & TODOs** — `plan_enter` / `todowrite` for structural work /// 3. **Reasoning** — `seq_think` for deep analysis /// 4. **Tool execution** — direct tools for simple actions pub fn main_agent_prompt() -> String { "\ You are Zesdex, an AI coding assistant. You have access to various tools \ via native function calling to help the user. TOKEN BUDGET — BE EFFICIENT: - For simple/factual questions, answer directly. Do NOT call tools. - For complex or unfamiliar code tasks, call `explore_codebase` ONCE at the \ start to locate relevant code, then work from that context. - Keep tool usage minimal: prefer `grep`/`glob`/`read` for targeted lookups; \ avoid re-reading files you already have in context. - Keep responses concise; do not repeat tool output verbatim. CRITICAL DIRECTIVES & PRIORITY HIERARCHY: 1. WORKFLOW FIRST: For any multi-step, complex, or non-trivial task, \ you MUST prioritise using `workflow_run` (to construct and execute a \ multi-phase YAML workflow) or `hive_mind` (to orchestrate parallel \ autonomous agents). Workflows are your primary strategy. 2. PLANNING & TODOs: Use `plan_enter` to establish high-level \ architectural plans and `todowrite` to maintain granular task checklists. 3. REASONING: Use `seq_think` for deep step-by-step analysis. 4. TOOL EXECUTION: Execute individual tools (file edits, terminal commands) \ within or guided by your workflows. If an error occurs, analyse and fix it. VERIFY AFTER EDIT (CLAUDE-CODE STYLE): - After modifying code (edit/write), run the repo's check command via `bash` \ before ending the turn: `cargo check` / `cargo clippy` / `cargo test` for Rust, \ or the equivalent lint/test (`bun run lint && bun run test`, `npm test`, etc.) \ for other stacks. Pick the project's actual verify command (see PROJECT \ CONTEXT / AGENTS.md when present). - If the check fails, fix the errors you can see and re-run; only end the turn \ after the check passes or you cannot resolve a failure yourself (then report it \ explicitly). - Do NOT claim code compiles or works without running a real check. Respond conversationally, concisely, and helpfully." .to_string() } /// Build the main-agent system prompt including an injected block of project /// context (AGENTS.md / CLAUDE.md / project rules). /// /// Like Claude Code, which loads AGENTS.md at startup so the model starts with /// the repo's conventions, this wraps [`main_agent_prompt`] and appends a /// clearly-delimited `## PROJECT CONTEXT` section carrying the rules the user /// keeps next to their code. When `project_context` is empty the returned /// prompt is identical to [`main_agent_prompt`], so callers can fall back /// safely. pub fn main_agent_prompt_with_project_context(project_context: &str) -> String { let base = main_agent_prompt(); let context = project_context.trim(); if context.is_empty() { return base; } format!( "{base}\n\n\ ## PROJECT CONTEXT (repo rules — follow these conventions)\n\ {context}" ) } /// Build a subagent directive prompt. /// /// The directive is embedded in a system message that also communicates the /// current working directory and workspace root so the subagent can resolve /// paths correctly. pub fn subagent_directive(directive: &str, cwd: &str, ws_root: &str) -> String { format!( "\ You are a focused subagent. Current directory (PWD): {cwd} Workspace root: {ws_root} Your directive: {directive} Complete the directive autonomously using the tools available to you. \ Return your final answer when done." ) } /// Build a conversation-compaction prompt. /// /// The LLM is asked to produce a concise bulleted summary of the key /// requests, decisions, tools executed, and files modified. pub fn compaction_prompt() -> String { "\ You are a helpful assistant summarising conversation history. \ Provide a concise summary of the key user requests, decisions, tools \ executed, and modified files. Format as a clear bulleted list." .to_string() } // --------------------------------------------------------------------------- // Adaptive explore: directives // --------------------------------------------------------------------------- /// Directive for a single lightweight context-scout subagent. pub fn explore_scout_directive() -> String { "\ You are a codebase context scout. \ Given the workspace root, quickly locate the code that is most relevant \ to the user's request: \ 1. Run semantic_search once with the user's key terms. \ 2. Read up to the 3 most relevant files (use grep for symbols if needed). \ 3. Report a concise bullet list (max 15 bullets, under 1500 characters) of \ what you found and exactly where (file paths). \ Do NOT rebuild the index. Do NOT enumerate unrelated files. Be brief." .to_string() } /// Build a system note injected after repeated tool errors to steer the /// agent toward an alternative approach instead of retrying the same call. pub fn error_recovery_note(tool_name: &str, last_error: &str) -> String { format!( "\ [System note] The tool `{tool_name}` failed repeatedly with: \"{last_error}\". \ Try an alternative approach (verify paths, correct arguments, use a \ different tool, or finish without this tool). Do NOT retry the same call." ) } #[cfg(test)] mod tests { use super::*; #[test] fn main_prompt_is_non_empty() { let prompt = main_agent_prompt(); assert!(!prompt.is_empty()); assert!(prompt.contains("Zesdex")); assert!(prompt.contains("WORKFLOW FIRST")); } #[test] fn project_context_prompt_appends_context_and_keeps_base() { let base = main_agent_prompt(); let with_ctx = main_agent_prompt_with_project_context("## AGENTS.md\nUse cargo clippy."); assert!(with_ctx.contains("Zesdex"), "base prompt must be preserved"); assert!(with_ctx.contains("PROJECT CONTEXT")); assert!(with_ctx.contains("Use cargo clippy.")); assert!(with_ctx.contains(&base)); // The base section should appear before the context section. assert!(with_ctx.find("PROJECT CONTEXT").unwrap() > with_ctx.find("Zesdex").unwrap()); } #[test] fn empty_project_context_returns_base_prompt() { let base = main_agent_prompt(); assert_eq!(main_agent_prompt_with_project_context(""), base); assert_eq!(main_agent_prompt_with_project_context(" "), base); } #[test] fn subagent_directive_includes_directive_text() { let prompt = subagent_directive("test directive", "/home", "/home/project"); assert!(prompt.contains("test directive")); assert!(prompt.contains("/home")); assert!(prompt.contains("/home/project")); } #[test] fn explore_scout_directive_is_concise_and_mentions_tools() { let scout = explore_scout_directive(); assert!(scout.contains("scout")); assert!(scout.contains("semantic_search")); } #[test] fn error_recovery_note_suggests_alternative() { let note = error_recovery_note("read", "File not found"); assert!(note.contains("read")); assert!(note.contains("alternative")); } }