1use crate::llm::LlmClient;
28use anyhow::Context;
29
30pub const SYSTEM_DEFAULT: &str = include_str!("../prompts/system_default.md");
35
36pub const CONTINUATION: &str = include_str!("../prompts/continuation.md");
39
40pub const SUBAGENT_EXPLORE: &str = include_str!("../prompts/subagent_explore.md");
46
47pub const SUBAGENT_PLAN: &str = include_str!("../prompts/subagent_plan.md");
49
50pub const SUBAGENT_CODE_REVIEW: &str = include_str!("../prompts/subagent_code_review.md");
52
53pub const SUBAGENT_TITLE: &str = include_str!("../prompts/subagent_title.md");
55
56pub const SUBAGENT_SUMMARY: &str = include_str!("../prompts/subagent_summary.md");
58
59pub const CONTEXT_COMPACT: &str = include_str!("../prompts/context_compact.md");
65
66pub const CONTEXT_SUMMARY_PREFIX: &str = include_str!("../prompts/context_summary_prefix.md");
68
69#[allow(dead_code)]
75pub const TITLE_GENERATE: &str = include_str!("../prompts/title_generate.md");
76
77pub const LLM_PLAN_SYSTEM: &str = include_str!("../prompts/llm_plan_system.md");
83
84pub const LLM_GOAL_EXTRACT_SYSTEM: &str = include_str!("../prompts/llm_goal_extract_system.md");
86
87pub const LLM_GOAL_CHECK_SYSTEM: &str = include_str!("../prompts/llm_goal_check_system.md");
89
90pub const PLAN_EXECUTE_GOAL: &str = include_str!("../prompts/plan_execute_goal.md");
96
97pub const PLAN_EXECUTE_STEP: &str = include_str!("../prompts/plan_execute_step.md");
99
100pub const PLAN_FALLBACK_STEP: &str = include_str!("../prompts/plan_fallback_step.md");
102
103pub const PLAN_PARALLEL_RESULTS: &str = include_str!("../prompts/plan_parallel_results.md");
105
106pub const TEAM_LEAD: &str = include_str!("../prompts/team_lead.md");
108
109pub const TEAM_REVIEWER: &str = include_str!("../prompts/team_reviewer.md");
111
112pub const SKILLS_CATALOG_HEADER: &str = include_str!("../prompts/skills_catalog_header.md");
114
115pub const BTW_SYSTEM: &str = include_str!("../prompts/btw_system.md");
124
125pub const AGENT_VERIFICATION: &str = include_str!("../prompts/agent_verification.md");
131
132pub const AGENT_VERIFICATION_RESTRICTIONS: &str =
134 include_str!("../prompts/agent_verification_restrictions.md");
135
136pub const INTENT_CLASSIFY_SYSTEM: &str = include_str!("../prompts/intent_classify_system.md");
142
143pub const SESSION_MEMORY_TEMPLATE: &str = include_str!("../prompts/system_session_memory.md");
149
150pub const PROMPT_SUGGESTION: &str = include_str!("../prompts/service_prompt_suggestion.md");
156
157pub const UNDERCOVER_INSTRUCTIONS: &str = include_str!("../prompts/undercover_instructions.md");
163
164use serde::{Deserialize, Serialize};
169
170#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
176pub enum PlanningMode {
177 #[default]
181 Auto,
182 Disabled,
184 Enabled,
186}
187
188impl PlanningMode {
189 pub fn should_plan(&self, message: &str) -> bool {
191 match self {
192 PlanningMode::Auto => AgentStyle::detect_from_message(message).requires_planning(),
193 PlanningMode::Enabled => true,
194 PlanningMode::Disabled => false,
195 }
196 }
197}
198
199#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
208pub enum AgentStyle {
209 #[default]
211 GeneralPurpose,
212 Plan,
215 Verification,
217 Explore,
220 CodeReview,
222}
223
224#[derive(Debug, Clone, Copy, PartialEq, Eq)]
226pub enum DetectionConfidence {
227 High,
229 Medium,
231 Low,
233}
234
235impl AgentStyle {
236 pub fn base_prompt(&self) -> &'static str {
238 match self {
239 AgentStyle::GeneralPurpose => SYSTEM_DEFAULT,
240 AgentStyle::Plan => SUBAGENT_PLAN,
241 AgentStyle::Verification => AGENT_VERIFICATION,
242 AgentStyle::Explore => SUBAGENT_EXPLORE,
243 AgentStyle::CodeReview => SYSTEM_DEFAULT, }
245 }
246
247 pub fn guidelines(&self) -> Option<&'static str> {
249 match self {
250 AgentStyle::GeneralPurpose => None,
251 AgentStyle::Plan => None, AgentStyle::Verification => None, AgentStyle::Explore => None, AgentStyle::CodeReview => Some(CODE_REVIEW_GUIDELINES),
255 }
256 }
257
258 pub fn description(&self) -> &'static str {
260 match self {
261 AgentStyle::GeneralPurpose => {
262 "General purpose coding agent for research and multi-step tasks"
263 }
264 AgentStyle::Plan => "Read-only planning and architecture analysis agent",
265 AgentStyle::Verification => "Adversarial verification specialist — tries to break code",
266 AgentStyle::Explore => "Fast read-only file search and codebase exploration agent",
267 AgentStyle::CodeReview => "Code review focused — analyzes quality and best practices",
268 }
269 }
270
271 pub fn builtin_agent_name(&self) -> &'static str {
273 match self {
274 AgentStyle::GeneralPurpose => "general",
275 AgentStyle::Plan => "plan",
276 AgentStyle::Verification => "verification",
277 AgentStyle::Explore => "explore",
278 AgentStyle::CodeReview => "review",
279 }
280 }
281
282 pub fn runtime_mode(&self) -> &'static str {
284 match self {
285 AgentStyle::GeneralPurpose => "general",
286 AgentStyle::Plan => "planning",
287 AgentStyle::Verification => "verification",
288 AgentStyle::Explore => "explore",
289 AgentStyle::CodeReview => "code_review",
290 }
291 }
292
293 pub fn requires_planning(&self) -> bool {
298 matches!(self, AgentStyle::Plan | AgentStyle::GeneralPurpose)
299 }
300
301 pub fn detect_with_confidence(message: &str) -> (Self, DetectionConfidence) {
308 let lower = message.to_lowercase();
309
310 if lower.contains("try to break")
314 || lower.contains("find vulnerabilities")
315 || lower.contains("adversarial")
316 || lower.contains("security audit")
317 {
318 return (AgentStyle::Verification, DetectionConfidence::High);
319 }
320
321 if lower.contains("help me plan")
323 || lower.contains("help me design")
324 || lower.contains("create a plan")
325 || lower.contains("implementation plan")
326 || lower.contains("step-by-step plan")
327 || lower.contains("帮我规划")
328 || lower.contains("帮我设计")
329 || lower.contains("架构设计")
330 || lower.contains("系统设计")
331 {
332 return (AgentStyle::Plan, DetectionConfidence::High);
333 }
334
335 if lower.contains("find all files")
337 || lower.contains("search for all")
338 || lower.contains("locate all")
339 {
340 return (AgentStyle::Explore, DetectionConfidence::High);
341 }
342
343 if lower.contains("verify")
347 || lower.contains("verification")
348 || lower.contains("break")
349 || lower.contains("debug")
350 || lower.contains("test")
351 || lower.contains("check if")
352 {
353 return (AgentStyle::Verification, DetectionConfidence::Medium);
354 }
355
356 if lower.contains("plan")
358 || lower.contains("design")
359 || lower.contains("architecture")
360 || lower.contains("approach")
361 || lower.contains("规划")
362 || lower.contains("设计")
363 || lower.contains("架构")
364 || lower.contains("方案")
365 {
366 return (AgentStyle::Plan, DetectionConfidence::Medium);
367 }
368
369 if lower.contains("find")
371 || lower.contains("search")
372 || lower.contains("where is")
373 || lower.contains("where's")
374 || lower.contains("locate")
375 || lower.contains("explore")
376 || lower.contains("look for")
377 {
378 return (AgentStyle::Explore, DetectionConfidence::Medium);
379 }
380
381 if lower.contains("review")
383 || lower.contains("code review")
384 || lower.contains("analyze")
385 || lower.contains("assess")
386 || lower.contains("quality")
387 || lower.contains("best practice")
388 {
389 return (AgentStyle::CodeReview, DetectionConfidence::Medium);
390 }
391
392 (AgentStyle::GeneralPurpose, DetectionConfidence::Low)
394 }
395
396 pub fn detect_from_message(message: &str) -> Self {
401 Self::detect_with_confidence(message).0
402 }
403
404 pub async fn detect_with_llm(llm: &dyn LlmClient, message: &str) -> anyhow::Result<Self> {
411 use crate::llm::Message;
412
413 let system = INTENT_CLASSIFY_SYSTEM;
414 let messages = vec![Message::user(message)];
415
416 let response = llm
417 .complete(&messages, Some(system), &[])
418 .await
419 .context("LLM intent classification failed")?;
420
421 let text = response.text().trim().to_lowercase();
422
423 let style = match text.as_str() {
424 "plan" => AgentStyle::Plan,
425 "explore" => AgentStyle::Explore,
426 "verification" => AgentStyle::Verification,
427 "codereview" | "code review" => AgentStyle::CodeReview,
428 _ => AgentStyle::GeneralPurpose,
429 };
430
431 Ok(style)
432 }
433}
434
435const CODE_REVIEW_GUIDELINES: &str = r#"## Code Review Focus
437
438When reviewing code, pay attention to:
439
4401. **Correctness** — Does the code do what it's supposed to do? Are edge cases handled?
4412. **Security** — Are there potential vulnerabilities (injection, auth bypass, data exposure)?
4423. **Performance** — Are there obvious inefficiencies (N+1 queries, unnecessary allocations)?
4434. **Maintainability** — Is the code readable? Are names clear? Is there appropriate documentation?
4445. **Best Practices** — Does it follow language/framework conventions? Are there anti-patterns?
445
446Be specific in your review. Quote the actual code you're referring to. Suggest concrete improvements with examples where possible.
447
448Remember: your job is to find issues, not to be nice. A code review that finds nothing is a missed opportunity."#;
449
450#[derive(Debug, Clone, Default)]
477pub struct SystemPromptSlots {
478 pub style: Option<AgentStyle>,
483
484 pub role: Option<String>,
489
490 pub guidelines: Option<String>,
494
495 pub response_style: Option<String>,
499
500 pub extra: Option<String>,
505}
506
507const DEFAULT_ROLE_LINE: &str = include_str!("../prompts/system_default_role_line.md");
509
510const DEFAULT_RESPONSE_FORMAT: &str = include_str!("../prompts/system_default_response_format.md");
512
513impl SystemPromptSlots {
514 pub fn build(&self) -> String {
522 self.build_with_style(self.style.unwrap_or_default())
523 }
524
525 pub fn build_with_message(&self, initial_message: &str) -> String {
530 let style = self
531 .style
532 .unwrap_or_else(|| AgentStyle::detect_from_message(initial_message));
533 self.build_with_style(style)
534 }
535
536 fn build_with_style(&self, style: AgentStyle) -> String {
538 let mut parts: Vec<String> = Vec::new();
539
540 let base_prompt = style.base_prompt().replace('\r', "");
543 let default_role_line = DEFAULT_ROLE_LINE.replace('\r', "");
544 let default_response_format = DEFAULT_RESPONSE_FORMAT.replace('\r', "");
545
546 let core = if let Some(ref role) = self.role {
550 if style == AgentStyle::GeneralPurpose {
551 let custom_role = format!(
552 "{}. You operate in an agentic loop: you\nthink, use tools, observe results, and keep working until the task is fully complete.",
553 role.trim_end_matches('.')
554 );
555 base_prompt.replace(&default_role_line, &custom_role)
556 } else {
557 format!("{}\n\n{}", role, base_prompt)
559 }
560 } else {
561 base_prompt
562 };
563
564 let core = if self.response_style.is_some() {
566 core.replace(&default_response_format, "")
567 .trim_end()
568 .to_string()
569 } else {
570 core.trim_end().to_string()
571 };
572
573 parts.push(core);
574
575 if let Some(ref style) = self.response_style {
577 parts.push(format!("## Response Format\n\n{}", style));
578 }
579
580 let style_guidelines = style.guidelines();
582 if style_guidelines.is_some() || self.guidelines.is_some() {
583 let mut guidelines_parts = Vec::new();
584 if let Some(sg) = style_guidelines {
585 guidelines_parts.push(sg.to_string());
586 }
587 if let Some(ref g) = self.guidelines {
588 guidelines_parts.push(g.clone());
589 }
590 parts.push(format!(
591 "## Guidelines\n\n{}",
592 guidelines_parts.join("\n\n")
593 ));
594 }
595
596 if let Some(ref extra) = self.extra {
598 parts.push(extra.clone());
599 }
600
601 parts.join("\n\n")
602 }
603
604 pub fn from_legacy(prompt: String) -> Self {
609 Self {
610 extra: Some(prompt),
611 ..Default::default()
612 }
613 }
614
615 pub fn is_empty(&self) -> bool {
617 self.style.is_none()
618 && self.role.is_none()
619 && self.guidelines.is_none()
620 && self.response_style.is_none()
621 && self.extra.is_none()
622 }
623
624 pub fn with_style(mut self, style: AgentStyle) -> Self {
626 self.style = Some(style);
627 self
628 }
629
630 pub fn with_role(mut self, role: impl Into<String>) -> Self {
632 self.role = Some(role.into());
633 self
634 }
635
636 pub fn with_guidelines(mut self, guidelines: impl Into<String>) -> Self {
638 self.guidelines = Some(guidelines.into());
639 self
640 }
641
642 pub fn with_response_style(mut self, style: impl Into<String>) -> Self {
644 self.response_style = Some(style.into());
645 self
646 }
647
648 pub fn with_extra(mut self, extra: impl Into<String>) -> Self {
650 self.extra = Some(extra.into());
651 self
652 }
653}
654
655pub fn render(template: &str, vars: &[(&str, &str)]) -> String {
661 let mut result = template.to_string();
662 for (key, value) in vars {
663 result = result.replace(&format!("{{{}}}", key), value);
664 }
665 result
666}
667
668#[cfg(test)]
669mod tests {
670 use super::*;
671
672 #[test]
673 fn test_all_prompts_loaded() {
674 assert!(!SYSTEM_DEFAULT.is_empty());
676 assert!(!CONTINUATION.is_empty());
677 assert!(!SUBAGENT_EXPLORE.is_empty());
678 assert!(!SUBAGENT_PLAN.is_empty());
679 assert!(!SUBAGENT_CODE_REVIEW.is_empty());
680 assert!(!SUBAGENT_TITLE.is_empty());
681 assert!(!SUBAGENT_SUMMARY.is_empty());
682 assert!(!CONTEXT_COMPACT.is_empty());
683 assert!(!TITLE_GENERATE.is_empty());
684 assert!(!LLM_PLAN_SYSTEM.is_empty());
685 assert!(!LLM_GOAL_EXTRACT_SYSTEM.is_empty());
686 assert!(!LLM_GOAL_CHECK_SYSTEM.is_empty());
687 assert!(!TEAM_LEAD.is_empty());
688 assert!(!TEAM_REVIEWER.is_empty());
689 assert!(!SKILLS_CATALOG_HEADER.is_empty());
690 assert!(!BTW_SYSTEM.is_empty());
691 assert!(!PLAN_EXECUTE_GOAL.is_empty());
692 assert!(!PLAN_EXECUTE_STEP.is_empty());
693 assert!(!PLAN_FALLBACK_STEP.is_empty());
694 assert!(!PLAN_PARALLEL_RESULTS.is_empty());
695 }
696
697 #[test]
698 fn test_render_template() {
699 let result = render(
700 PLAN_EXECUTE_GOAL,
701 &[("goal", "Build app"), ("steps", "1. Init")],
702 );
703 assert!(result.contains("Build app"));
704 assert!(!result.contains("{goal}"));
705 }
706
707 #[test]
708 fn test_render_multiple_placeholders() {
709 let template = "Goal: {goal}\nCriteria: {criteria}\nState: {current_state}";
710 let result = render(
711 template,
712 &[
713 ("goal", "Build a REST API"),
714 ("criteria", "- Endpoint works\n- Tests pass"),
715 ("current_state", "API is deployed"),
716 ],
717 );
718 assert!(result.contains("Build a REST API"));
719 assert!(result.contains("Endpoint works"));
720 assert!(result.contains("API is deployed"));
721 }
722
723 #[test]
724 fn test_subagent_prompts_contain_guidelines() {
725 assert!(SUBAGENT_EXPLORE.contains("Guidelines"));
726 assert!(SUBAGENT_EXPLORE.contains("read-only"));
727 assert!(SUBAGENT_PLAN.contains("Guidelines"));
728 assert!(SUBAGENT_PLAN.contains("read-only"));
729 }
730
731 #[test]
732 fn test_context_summary_prefix() {
733 assert!(CONTEXT_SUMMARY_PREFIX.contains("Context Summary"));
734 }
735
736 #[test]
739 fn test_slots_default_builds_system_default() {
740 let slots = SystemPromptSlots::default();
741 let built = slots.build();
742 assert!(built.contains("Core Behaviour"));
743 assert!(built.contains("Tool Usage Strategy"));
744 assert!(built.contains("Completion Criteria"));
745 assert!(built.contains("Response Format"));
746 assert!(built.contains("A3S Code"));
747 }
748
749 #[test]
750 fn test_slots_custom_role_replaces_default() {
751 let slots = SystemPromptSlots {
752 role: Some("You are a senior Python developer".to_string()),
753 ..Default::default()
754 };
755 let built = slots.build();
756 assert!(built.contains("You are a senior Python developer"));
757 assert!(!built.contains("You are A3S Code"));
758 assert!(built.contains("Core Behaviour"));
760 assert!(built.contains("Tool Usage Strategy"));
761 }
762
763 #[test]
764 fn test_slots_custom_guidelines_appended() {
765 let slots = SystemPromptSlots {
766 guidelines: Some("Always use type hints. Follow PEP 8.".to_string()),
767 ..Default::default()
768 };
769 let built = slots.build();
770 assert!(built.contains("## Guidelines"));
771 assert!(built.contains("Always use type hints"));
772 assert!(built.contains("Core Behaviour"));
773 }
774
775 #[test]
776 fn test_slots_custom_response_style_replaces_default() {
777 let slots = SystemPromptSlots {
778 response_style: Some("Be concise. Use bullet points.".to_string()),
779 ..Default::default()
780 };
781 let built = slots.build();
782 assert!(built.contains("Be concise. Use bullet points."));
783 assert!(!built.contains("emit tool calls, no prose"));
785 assert!(built.contains("Core Behaviour"));
787 }
788
789 #[test]
790 fn test_slots_extra_appended() {
791 let slots = SystemPromptSlots {
792 extra: Some("Remember: always write tests first.".to_string()),
793 ..Default::default()
794 };
795 let built = slots.build();
796 assert!(built.contains("Remember: always write tests first."));
797 assert!(built.contains("Core Behaviour"));
798 }
799
800 #[test]
801 fn test_slots_from_legacy() {
802 let slots = SystemPromptSlots::from_legacy("You are a helpful assistant.".to_string());
803 let built = slots.build();
804 assert!(built.contains("You are a helpful assistant."));
806 assert!(built.contains("Core Behaviour"));
807 assert!(built.contains("Tool Usage Strategy"));
808 }
809
810 #[test]
811 fn test_slots_all_slots_combined() {
812 let slots = SystemPromptSlots {
813 style: None,
814 role: Some("You are a Rust expert".to_string()),
815 guidelines: Some("Use clippy. No unwrap.".to_string()),
816 response_style: Some("Short answers only.".to_string()),
817 extra: Some("Project uses tokio.".to_string()),
818 };
819 let built = slots.build();
820 assert!(built.contains("You are a Rust expert"));
821 assert!(built.contains("Core Behaviour"));
822 assert!(built.contains("## Guidelines"));
823 assert!(built.contains("Use clippy"));
824 assert!(built.contains("Short answers only"));
825 assert!(built.contains("Project uses tokio"));
826 assert!(!built.contains("emit tool calls, no prose"));
828 }
829
830 #[test]
831 fn test_slots_is_empty() {
832 assert!(SystemPromptSlots::default().is_empty());
833 assert!(!SystemPromptSlots {
834 role: Some("test".to_string()),
835 ..Default::default()
836 }
837 .is_empty());
838 assert!(!SystemPromptSlots {
839 style: Some(AgentStyle::Plan),
840 ..Default::default()
841 }
842 .is_empty());
843 }
844
845 #[test]
848 fn test_agent_style_default_is_general_purpose() {
849 assert_eq!(AgentStyle::default(), AgentStyle::GeneralPurpose);
850 }
851
852 #[test]
853 fn test_agent_style_base_prompt() {
854 assert_eq!(AgentStyle::GeneralPurpose.base_prompt(), SYSTEM_DEFAULT);
855 assert_eq!(AgentStyle::Plan.base_prompt(), SUBAGENT_PLAN);
856 assert_eq!(AgentStyle::Explore.base_prompt(), SUBAGENT_EXPLORE);
857 assert_eq!(AgentStyle::Verification.base_prompt(), AGENT_VERIFICATION);
858 assert_eq!(AgentStyle::CodeReview.base_prompt(), SYSTEM_DEFAULT);
860 }
861
862 #[test]
863 fn test_agent_style_guidelines() {
864 assert!(AgentStyle::GeneralPurpose.guidelines().is_none());
865 assert!(AgentStyle::Plan.guidelines().is_none()); assert!(AgentStyle::Explore.guidelines().is_none());
867 assert!(AgentStyle::Verification.guidelines().is_none());
868 assert!(AgentStyle::CodeReview.guidelines().is_some());
869 assert!(AgentStyle::CodeReview
870 .guidelines()
871 .unwrap()
872 .contains("Correctness"));
873 }
874
875 #[test]
876 fn test_agent_style_builtin_agent_name_mapping() {
877 assert_eq!(AgentStyle::GeneralPurpose.builtin_agent_name(), "general");
878 assert_eq!(AgentStyle::Plan.builtin_agent_name(), "plan");
879 assert_eq!(AgentStyle::Explore.builtin_agent_name(), "explore");
880 assert_eq!(
881 AgentStyle::Verification.builtin_agent_name(),
882 "verification"
883 );
884 assert_eq!(AgentStyle::CodeReview.builtin_agent_name(), "review");
885 }
886
887 #[test]
888 fn test_agent_style_runtime_mode_mapping() {
889 assert_eq!(AgentStyle::GeneralPurpose.runtime_mode(), "general");
890 assert_eq!(AgentStyle::Plan.runtime_mode(), "planning");
891 assert_eq!(AgentStyle::Explore.runtime_mode(), "explore");
892 assert_eq!(AgentStyle::Verification.runtime_mode(), "verification");
893 assert_eq!(AgentStyle::CodeReview.runtime_mode(), "code_review");
894 }
895
896 #[test]
897 fn test_agent_style_detect_plan() {
898 assert_eq!(
899 AgentStyle::detect_from_message("Help me plan a new feature"),
900 AgentStyle::Plan
901 );
902 assert_eq!(
903 AgentStyle::detect_from_message("Design the architecture for this"),
904 AgentStyle::Plan
905 );
906 assert_eq!(
907 AgentStyle::detect_from_message("What's the implementation approach?"),
908 AgentStyle::Plan
909 );
910 assert_eq!(
911 AgentStyle::detect_from_message("帮我设计一个Agent as a Service 平台"),
912 AgentStyle::Plan
913 );
914 assert_eq!(
915 AgentStyle::detect_from_message("请给我一个系统设计方案"),
916 AgentStyle::Plan
917 );
918 }
919
920 #[test]
921 fn test_agent_style_detect_verification() {
922 assert_eq!(
923 AgentStyle::detect_from_message("Verify that this works correctly"),
924 AgentStyle::Verification
925 );
926 assert_eq!(
927 AgentStyle::detect_from_message("Test the login flow"),
928 AgentStyle::Verification
929 );
930 assert_eq!(
931 AgentStyle::detect_from_message("Check if the API handles edge cases"),
932 AgentStyle::Verification
933 );
934 }
935
936 #[test]
937 fn test_agent_style_detect_explore() {
938 assert_eq!(
939 AgentStyle::detect_from_message("Find all files related to auth"),
940 AgentStyle::Explore
941 );
942 assert_eq!(
943 AgentStyle::detect_from_message("Where is the user model defined?"),
944 AgentStyle::Explore
945 );
946 assert_eq!(
947 AgentStyle::detect_from_message("Search for password hashing code"),
948 AgentStyle::Explore
949 );
950 }
951
952 #[test]
953 fn test_agent_style_detect_code_review() {
954 assert_eq!(
955 AgentStyle::detect_from_message("Review the PR changes"),
956 AgentStyle::CodeReview
957 );
958 assert_eq!(
959 AgentStyle::detect_from_message("Analyze this code for best practices"),
960 AgentStyle::CodeReview
961 );
962 assert_eq!(
963 AgentStyle::detect_from_message("Assess code quality"),
964 AgentStyle::CodeReview
965 );
966 }
967
968 #[test]
969 fn test_agent_style_detect_default_is_general_purpose() {
970 assert_eq!(
972 AgentStyle::detect_from_message("Implement the new feature"),
973 AgentStyle::GeneralPurpose
974 );
975 assert_eq!(
977 AgentStyle::detect_from_message("Write code for the API"),
978 AgentStyle::GeneralPurpose
979 );
980 }
981
982 #[test]
983 fn test_build_with_message_auto_detects_style() {
984 let slots = SystemPromptSlots::default();
985 let built = slots.build_with_message("Help me plan a new feature");
986 assert!(built.contains("planning agent") || built.contains("READ-ONLY"));
988 }
989
990 #[test]
991 fn test_build_with_message_explicit_style_overrides() {
992 let slots = SystemPromptSlots {
993 style: Some(AgentStyle::Verification),
994 ..Default::default()
995 };
996 let built = slots.build_with_message("Help me plan a new feature");
997 assert!(built.contains("verification specialist") || built.contains("try to break"));
999 }
1000
1001 #[test]
1002 fn test_build_with_message_plan_style() {
1003 let slots = SystemPromptSlots::default();
1004 let built = slots.build_with_message("Design the system architecture");
1005 assert!(built.contains("planning agent") || built.contains("READ-ONLY"));
1006 }
1007
1008 #[test]
1009 fn test_build_with_message_explore_style() {
1010 let slots = SystemPromptSlots::default();
1011 let built = slots.build_with_message("Find all authentication files");
1012 assert!(built.contains("exploration agent") || built.contains("explore"));
1013 }
1014
1015 #[test]
1016 fn test_build_with_message_code_review_style() {
1017 let slots = SystemPromptSlots::default();
1018 let built = slots.build_with_message("Review this code");
1019 assert!(built.contains("Correctness") || built.contains("Code Review"));
1021 }
1022
1023 #[test]
1024 fn test_builder_methods() {
1025 let slots = SystemPromptSlots::default()
1026 .with_style(AgentStyle::Plan)
1027 .with_role("You are a Python expert")
1028 .with_guidelines("Use type hints")
1029 .with_response_style("Be brief")
1030 .with_extra("Additional instructions");
1031
1032 assert_eq!(slots.style, Some(AgentStyle::Plan));
1033 assert_eq!(slots.role, Some("You are a Python expert".to_string()));
1034 assert_eq!(slots.guidelines, Some("Use type hints".to_string()));
1035 assert_eq!(slots.response_style, Some("Be brief".to_string()));
1036 assert_eq!(slots.extra, Some("Additional instructions".to_string()));
1037
1038 let built = slots.build();
1039 assert!(built.contains("Python expert"));
1040 assert!(built.contains("Use type hints"));
1041 assert!(built.contains("Be brief"));
1042 assert!(built.contains("Additional instructions"));
1043 }
1044
1045 #[test]
1046 fn test_code_review_guidelines_appended() {
1047 let slots = SystemPromptSlots {
1048 style: Some(AgentStyle::CodeReview),
1049 ..Default::default()
1050 };
1051 let built = slots.build();
1052 assert!(built.contains("Correctness"));
1053 assert!(built.contains("Security"));
1054 assert!(built.contains("Performance"));
1055 assert!(built.contains("Maintainability"));
1056 }
1057}