ai-agent 0.13.4

Idiomatic agent sdk inspired by the claude code source leak
Documentation
//! Built-in agents - ported from ~/claudecode/openclaudecode/src/tools/AgentTool/builtInAgents.ts
//!
//! This module provides built-in agent definitions that ship with the SDK.

use crate::constants::env::ai;
use crate::skills::loader::HooksSettings;

/// Built-in agent type
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum BuiltInAgentType {
    GeneralPurpose,
    Explore,
    Plan,
    Verification,
    CodeGuide,
    StatuslineSetup,
}

impl BuiltInAgentType {
    pub fn as_str(&self) -> &'static str {
        match self {
            BuiltInAgentType::GeneralPurpose => "general-purpose",
            BuiltInAgentType::Explore => "explore",
            BuiltInAgentType::Plan => "plan",
            BuiltInAgentType::Verification => "verification",
            BuiltInAgentType::CodeGuide => "code-guide",
            BuiltInAgentType::StatuslineSetup => "statusline-setup",
        }
    }
}

/// Agent definition for built-in agents
#[derive(Debug, Clone)]
pub struct BuiltInAgent {
    /// Unique agent type identifier
    pub agent_type: BuiltInAgentType,
    /// Human-readable name
    pub name: String,
    /// Description
    pub description: String,
    /// System prompt
    pub get_system_prompt: fn() -> String,
    /// Optional skills to preload
    pub skills: Option<Vec<String>>,
    /// Optional hooks
    pub hooks: Option<HooksSettings>,
    /// Optional MCP servers
    pub mcp_servers: Option<Vec<String>>,
    /// Maximum turns (None = use default)
    pub max_turns: Option<u32>,
    /// Whether to omit AI.md
    pub omit_claude_md: bool,
}

impl BuiltInAgent {
    /// Get the system prompt for this agent
    pub fn get_system_prompt(&self) -> String {
        (self.get_system_prompt)()
    }
}

/// General Purpose Agent system prompt
fn general_purpose_system_prompt() -> String {
    r#"You are a general-purpose AI assistant helping with software development tasks.

Your goal is to assist the user with whatever task they present, combining your abilities as a coding, reasoning, and command-line assistant.

When working on code:
- Write clean, maintainable code
- Follow best practices for the language/framework
- Consider edge cases and error handling
- Write tests when appropriate

When you need more information to complete a task, ask the user clarifying questions.

Use the available tools to explore the codebase, read and write files, run commands, and accomplish the user's goals."#.to_string()
}

/// Explore Agent system prompt
fn explore_system_prompt() -> String {
    r#"You are an exploration agent. Your goal is to thoroughly investigate a codebase or problem area.

Your approach:
1. Start by understanding the overall structure
2. Identify key files and their purposes
3. Trace through important code paths
4. Look for patterns and conventions
5. Document your findings clearly

Be systematic and thorough. Use Glob, Grep, and Read tools to explore comprehensively.

Provide detailed findings with file paths and line numbers to help the user understand the codebase."#.to_string()
}

/// Plan Agent system prompt
fn plan_system_prompt() -> String {
    r#"You are a planning agent. Your goal is to design implementation plans for complex tasks.

Your approach:
1. Understand the requirements thoroughly
2. Break down into discrete steps
3. Identify dependencies and potential issues
4. Consider alternatives and trade-offs
5. Create a clear, actionable plan

When planning:
- Think step by step
- Consider edge cases
- Identify what needs to be tested
- Make sure the plan is practical and achievable

Present your plan in a clear format with numbered steps."#
        .to_string()
}

/// Verification Agent system prompt
fn verification_system_prompt() -> String {
    r#"You are a verification agent. Your goal is to verify that work is complete and correct.

Your approach:
1. Understand what was supposed to be accomplished
2. Check that all requirements are met
3. Run tests and verify they pass
4. Look for potential issues or bugs
5. Verify code quality and best practices

Be thorough - don't just check that code compiles, verify it actually works correctly.

When you find issues, be specific about what's wrong and suggest fixes."#
        .to_string()
}

/// Code Guide Agent system prompt
fn code_guide_system_prompt() -> String {
    r#"You are a code guide agent. Your goal is to help users understand and navigate Claude Code.

Your approach:
1. Answer questions about Claude Code features
2. Explain how to use tools effectively
3. Help with debugging and troubleshooting
4. Provide guidance on best practices
5. Explain Claude Code concepts and workflows

Be helpful and informative. Draw on your knowledge of Claude Code to assist users."#
        .to_string()
}

/// Statusline Setup Agent system prompt
fn statusline_setup_system_prompt() -> String {
    r#"You are a statusline setup agent. Your goal is to help configure the Claude Code statusline.

Your approach:
1. Check current statusline configuration
2. Understand available components
3. Help customize the statusline to user preferences
4. Ensure configuration is valid

Provide clear guidance on statusline options and help users get their ideal setup."#
        .to_string()
}

/// All built-in agents
pub fn get_built_in_agents() -> Vec<BuiltInAgent> {
    vec![
        BuiltInAgent {
            agent_type: BuiltInAgentType::GeneralPurpose,
            name: "General Purpose".to_string(),
            description: "A general-purpose agent for any task".to_string(),
            get_system_prompt: general_purpose_system_prompt,
            skills: None,
            hooks: None,
            mcp_servers: None,
            max_turns: Some(50),
            omit_claude_md: false,
        },
        BuiltInAgent {
            agent_type: BuiltInAgentType::Explore,
            name: "Explore".to_string(),
            description: "Explore and understand a codebase".to_string(),
            get_system_prompt: explore_system_prompt,
            skills: Some(vec!["superpowers:brainstorming".to_string()]),
            hooks: None,
            mcp_servers: None,
            max_turns: Some(20),
            omit_claude_md: false,
        },
        BuiltInAgent {
            agent_type: BuiltInAgentType::Plan,
            name: "Plan".to_string(),
            description: "Create implementation plans".to_string(),
            get_system_prompt: plan_system_prompt,
            skills: Some(vec!["superpowers:writing-plans".to_string()]),
            hooks: None,
            mcp_servers: None,
            max_turns: Some(15),
            omit_claude_md: false,
        },
        BuiltInAgent {
            agent_type: BuiltInAgentType::Verification,
            name: "Verification".to_string(),
            description: "Verify work is complete and correct".to_string(),
            get_system_prompt: verification_system_prompt,
            skills: Some(vec![
                "superpowers:verification-before-completion".to_string(),
                "simplify".to_string(),
            ]),
            hooks: None,
            mcp_servers: None,
            max_turns: Some(25),
            omit_claude_md: false,
        },
        BuiltInAgent {
            agent_type: BuiltInAgentType::CodeGuide,
            name: "Code Guide".to_string(),
            description: "Help with Claude Code features".to_string(),
            get_system_prompt: code_guide_system_prompt,
            skills: None,
            hooks: None,
            mcp_servers: None,
            max_turns: Some(10),
            omit_claude_md: true,
        },
        BuiltInAgent {
            agent_type: BuiltInAgentType::StatuslineSetup,
            name: "Statusline Setup".to_string(),
            description: "Configure Claude Code statusline".to_string(),
            get_system_prompt: statusline_setup_system_prompt,
            skills: None,
            hooks: None,
            mcp_servers: None,
            max_turns: Some(5),
            omit_claude_md: true,
        },
    ]
}

/// Get a built-in agent by type
pub fn get_built_in_agent(agent_type: BuiltInAgentType) -> Option<BuiltInAgent> {
    get_built_in_agents()
        .into_iter()
        .find(|a| a.agent_type == agent_type)
}

/// Get agent by type name (e.g., "general-purpose", "explore")
pub fn get_agent_by_type_name(type_name: &str) -> Option<BuiltInAgent> {
    let agent_type = match type_name {
        "general-purpose" | "general" => Some(BuiltInAgentType::GeneralPurpose),
        "explore" => Some(BuiltInAgentType::Explore),
        "plan" => Some(BuiltInAgentType::Plan),
        "verification" | "verify" => Some(BuiltInAgentType::Verification),
        "code-guide" | "guide" => Some(BuiltInAgentType::CodeGuide),
        "statusline-setup" | "statusline" => Some(BuiltInAgentType::StatuslineSetup),
        _ => None,
    };
    agent_type.and_then(get_built_in_agent)
}

/// Check if Explore/Plan agents are enabled (feature-gated in TypeScript)
pub fn are_explore_plan_agents_enabled() -> bool {
    // In Rust SDK, always enabled by default
    // Can be disabled via environment variable in production use
    std::env::var(ai::DISABLE_EXPLORE_PLAN_AGENTS)
        .map(|v| v != "true")
        .unwrap_or(true)
}

/// Get all enabled built-in agents
pub fn get_enabled_built_in_agents() -> Vec<BuiltInAgent> {
    let mut agents = get_built_in_agents();

    // Filter based on feature flags
    if !are_explore_plan_agents_enabled() {
        agents.retain(|a| {
            a.agent_type != BuiltInAgentType::Explore && a.agent_type != BuiltInAgentType::Plan
        });
    }

    agents
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_get_built_in_agents() {
        let agents = get_built_in_agents();
        assert!(!agents.is_empty());
    }

    #[test]
    fn test_get_agent_by_type_name() {
        let agent = get_agent_by_type_name("explore");
        assert!(agent.is_some());
        assert_eq!(agent.unwrap().agent_type, BuiltInAgentType::Explore);
    }

    #[test]
    fn test_get_nonexistent_agent() {
        let agent = get_agent_by_type_name("nonexistent");
        assert!(agent.is_none());
    }

    #[test]
    fn test_agent_system_prompt() {
        let agent = get_built_in_agent(BuiltInAgentType::Explore).unwrap();
        let prompt = agent.get_system_prompt();
        assert!(!prompt.is_empty());
    }

    #[test]
    fn test_are_explore_plan_agents_enabled_default() {
        // By default should be enabled
        assert!(are_explore_plan_agents_enabled());
    }
}