1use crate::llm::LlmClient;
19use anyhow::Context;
20
21pub const SYSTEM_DEFAULT: &str = include_str!("../prompts/common/system_default.md");
26
27pub const CONTINUATION: &str = include_str!("../prompts/common/continuation.md");
30
31pub const AGENT_EXPLORE: &str = include_str!("../prompts/agents/explore.md");
37
38pub const AGENT_PLAN: &str = include_str!("../prompts/agents/plan.md");
40
41pub const AGENT_CODE_REVIEW: &str = include_str!("../prompts/agents/code_review.md");
43
44pub const CONTEXT_COMPACT: &str = include_str!("../prompts/common/context_compact.md");
50
51pub const CONTEXT_SUMMARY_PREFIX: &str =
53 include_str!("../prompts/common/context_summary_prefix.md");
54
55pub const LLM_PLAN_SYSTEM: &str = include_str!("../prompts/planning/llm_plan_system.md");
61
62pub const LLM_GOAL_EXTRACT_SYSTEM: &str =
64 include_str!("../prompts/planning/llm_goal_extract_system.md");
65
66pub const LLM_GOAL_CHECK_SYSTEM: &str =
68 include_str!("../prompts/planning/llm_goal_check_system.md");
69
70pub const PRE_ANALYSIS_SYSTEM: &str = include_str!("../prompts/analysis/pre_analysis_system.md");
72
73pub const PLAN_EXECUTE_GOAL: &str = include_str!("../prompts/planning/plan_execute_goal.md");
79
80pub const PLAN_EXECUTE_STEP: &str = include_str!("../prompts/planning/plan_execute_step.md");
82
83pub const PLAN_FALLBACK_STEP: &str = include_str!("../prompts/planning/plan_fallback_step.md");
85
86pub const SKILLS_CATALOG_HEADER: &str = include_str!("../prompts/common/skills_catalog_header.md");
88
89pub const BTW_SYSTEM: &str = include_str!("../prompts/common/btw_system.md");
98
99pub const AGENT_VERIFICATION: &str = include_str!("../prompts/agents/verification.md");
105
106pub const INTENT_CLASSIFY_SYSTEM: &str =
112 include_str!("../prompts/analysis/intent_classify_system.md");
113
114use serde::{Deserialize, Serialize};
119
120#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
126pub enum PlanningMode {
127 #[default]
131 Auto,
132 Disabled,
134 Enabled,
136}
137
138impl PlanningMode {
139 pub fn should_plan(&self, message: &str) -> bool {
144 match self {
145 PlanningMode::Auto => AgentStyle::detect_from_message(message).requires_planning(),
146 PlanningMode::Enabled => true,
147 PlanningMode::Disabled => false,
148 }
149 }
150}
151
152#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
161pub enum AgentStyle {
162 #[default]
164 GeneralPurpose,
165 Plan,
168 Verification,
170 Explore,
173 CodeReview,
175}
176
177#[derive(Debug, Clone, Copy, PartialEq, Eq)]
179pub enum DetectionConfidence {
180 High,
182 Medium,
184 Low,
186}
187
188impl AgentStyle {
189 pub fn base_prompt(&self) -> &'static str {
191 match self {
192 AgentStyle::GeneralPurpose => SYSTEM_DEFAULT,
193 AgentStyle::Plan => AGENT_PLAN,
194 AgentStyle::Verification => AGENT_VERIFICATION,
195 AgentStyle::Explore => AGENT_EXPLORE,
196 AgentStyle::CodeReview => AGENT_CODE_REVIEW,
197 }
198 }
199
200 pub fn guidelines(&self) -> Option<&'static str> {
202 match self {
203 AgentStyle::GeneralPurpose => None,
204 AgentStyle::Plan => None, AgentStyle::Verification => None, AgentStyle::Explore => None, AgentStyle::CodeReview => None,
208 }
209 }
210
211 pub fn description(&self) -> &'static str {
213 match self {
214 AgentStyle::GeneralPurpose => {
215 "General purpose coding agent for research and multi-step tasks"
216 }
217 AgentStyle::Plan => "Read-only planning and architecture analysis agent",
218 AgentStyle::Verification => "Adversarial verification specialist — tries to break code",
219 AgentStyle::Explore => "Fast read-only file search and codebase exploration agent",
220 AgentStyle::CodeReview => "Code review focused — analyzes quality and best practices",
221 }
222 }
223
224 pub fn builtin_agent_name(&self) -> &'static str {
226 match self {
227 AgentStyle::GeneralPurpose => "general",
228 AgentStyle::Plan => "plan",
229 AgentStyle::Verification => "verification",
230 AgentStyle::Explore => "explore",
231 AgentStyle::CodeReview => "review",
232 }
233 }
234
235 pub fn runtime_mode(&self) -> &'static str {
237 match self {
238 AgentStyle::GeneralPurpose => "general",
239 AgentStyle::Plan => "planning",
240 AgentStyle::Verification => "verification",
241 AgentStyle::Explore => "explore",
242 AgentStyle::CodeReview => "code_review",
243 }
244 }
245
246 pub fn requires_planning(&self) -> bool {
251 matches!(self, AgentStyle::Plan)
252 }
253
254 pub fn detect_with_confidence(message: &str) -> (Self, DetectionConfidence) {
260 if message
264 .chars()
265 .any(|c| ('\u{4e00}'..='\u{9fff}').contains(&c))
266 {
267 return (AgentStyle::GeneralPurpose, DetectionConfidence::Low);
268 }
269
270 let lower = message.to_lowercase();
271
272 if lower.contains("try to break")
276 || lower.contains("find vulnerabilities")
277 || lower.contains("adversarial")
278 || lower.contains("security audit")
279 {
280 return (AgentStyle::Verification, DetectionConfidence::High);
281 }
282
283 if lower.contains("help me plan")
285 || lower.contains("help me design")
286 || lower.contains("create a plan")
287 || lower.contains("implementation plan")
288 || lower.contains("step-by-step plan")
289 {
290 return (AgentStyle::Plan, DetectionConfidence::High);
291 }
292
293 if lower.contains("find all files")
295 || lower.contains("search for all")
296 || lower.contains("locate all")
297 {
298 return (AgentStyle::Explore, DetectionConfidence::High);
299 }
300
301 if lower.contains("verify")
305 || lower.contains("verification")
306 || lower.contains("break")
307 || lower.contains("debug")
308 || lower.contains("test")
309 || lower.contains("check if")
310 {
311 return (AgentStyle::Verification, DetectionConfidence::Medium);
312 }
313
314 if lower.contains("plan")
316 || lower.contains("design")
317 || lower.contains("architecture")
318 || lower.contains("approach")
319 {
320 return (AgentStyle::Plan, DetectionConfidence::Medium);
321 }
322
323 if lower.contains("find")
325 || lower.contains("search")
326 || lower.contains("where is")
327 || lower.contains("where's")
328 || lower.contains("locate")
329 || lower.contains("explore")
330 || lower.contains("look for")
331 {
332 return (AgentStyle::Explore, DetectionConfidence::Medium);
333 }
334
335 if lower.contains("review")
337 || lower.contains("code review")
338 || lower.contains("analyze")
339 || lower.contains("assess")
340 || lower.contains("quality")
341 || lower.contains("best practice")
342 {
343 return (AgentStyle::CodeReview, DetectionConfidence::Medium);
344 }
345
346 (AgentStyle::GeneralPurpose, DetectionConfidence::Low)
348 }
349
350 pub fn detect_from_message(message: &str) -> Self {
356 Self::detect_with_confidence(message).0
357 }
358
359 pub async fn detect_with_llm(llm: &dyn LlmClient, message: &str) -> anyhow::Result<Self> {
366 use crate::llm::Message;
367
368 let system = INTENT_CLASSIFY_SYSTEM;
369 let messages = vec![Message::user(message)];
370
371 let response = llm
372 .complete(&messages, Some(system), &[])
373 .await
374 .context("LLM intent classification failed")?;
375
376 let text = response.text().trim().to_lowercase();
377
378 let style = match text.as_str() {
379 "plan" => AgentStyle::Plan,
380 "explore" => AgentStyle::Explore,
381 "verification" => AgentStyle::Verification,
382 "codereview" | "code review" => AgentStyle::CodeReview,
383 _ => AgentStyle::GeneralPurpose,
384 };
385
386 Ok(style)
387 }
388}
389
390#[derive(Debug, Clone, Default)]
417pub struct SystemPromptSlots {
418 pub style: Option<AgentStyle>,
423
424 pub role: Option<String>,
429
430 pub guidelines: Option<String>,
434
435 pub response_style: Option<String>,
439
440 pub extra: Option<String>,
442}
443
444const DEFAULT_ROLE_LINE: &str = include_str!("../prompts/common/system_default_role_line.md");
446
447const DEFAULT_RESPONSE_FORMAT: &str =
449 include_str!("../prompts/common/system_default_response_format.md");
450
451impl SystemPromptSlots {
452 pub fn build(&self) -> String {
460 self.build_with_style(self.style.unwrap_or_default())
461 }
462
463 pub fn build_with_message(&self, initial_message: &str) -> String {
468 let style = self
469 .style
470 .unwrap_or_else(|| AgentStyle::detect_from_message(initial_message));
471 self.build_with_style(style)
472 }
473
474 fn build_with_style(&self, style: AgentStyle) -> String {
476 let mut parts: Vec<String> = Vec::new();
477
478 let base_prompt = style.base_prompt().replace('\r', "");
481 let default_role_line = DEFAULT_ROLE_LINE.replace('\r', "");
482 let default_response_format = DEFAULT_RESPONSE_FORMAT.replace('\r', "");
483
484 let core = if let Some(ref role) = self.role {
488 if style == AgentStyle::GeneralPurpose {
489 let custom_role = format!(
490 "{}. You operate in an agentic loop: inspect, act with tools, observe results, and continue until the user's request is genuinely complete.",
491 role.trim_end_matches('.')
492 );
493 base_prompt.replace(&default_role_line, &custom_role)
494 } else {
495 format!("{}\n\n{}", role, base_prompt)
497 }
498 } else {
499 base_prompt
500 };
501
502 let core = if self.response_style.is_some() {
504 core.replace(&default_response_format, "")
505 .trim_end()
506 .to_string()
507 } else {
508 core.trim_end().to_string()
509 };
510
511 parts.push(core);
512
513 if let Some(ref style) = self.response_style {
515 parts.push(format!("## Response Format\n\n{}", style));
516 }
517
518 let style_guidelines = style.guidelines();
520 if style_guidelines.is_some() || self.guidelines.is_some() {
521 let mut guidelines_parts = Vec::new();
522 if let Some(sg) = style_guidelines {
523 guidelines_parts.push(sg.to_string());
524 }
525 if let Some(ref g) = self.guidelines {
526 guidelines_parts.push(g.clone());
527 }
528 parts.push(format!(
529 "## Guidelines\n\n{}",
530 guidelines_parts.join("\n\n")
531 ));
532 }
533
534 if let Some(ref extra) = self.extra {
536 parts.push(extra.clone());
537 }
538
539 parts.join("\n\n")
540 }
541
542 pub fn is_empty(&self) -> bool {
544 self.style.is_none()
545 && self.role.is_none()
546 && self.guidelines.is_none()
547 && self.response_style.is_none()
548 && self.extra.is_none()
549 }
550
551 pub fn with_style(mut self, style: AgentStyle) -> Self {
553 self.style = Some(style);
554 self
555 }
556
557 pub fn with_role(mut self, role: impl Into<String>) -> Self {
559 self.role = Some(role.into());
560 self
561 }
562
563 pub fn with_guidelines(mut self, guidelines: impl Into<String>) -> Self {
565 self.guidelines = Some(guidelines.into());
566 self
567 }
568
569 pub fn with_response_style(mut self, style: impl Into<String>) -> Self {
571 self.response_style = Some(style.into());
572 self
573 }
574
575 pub fn with_extra(mut self, extra: impl Into<String>) -> Self {
577 self.extra = Some(extra.into());
578 self
579 }
580}
581
582pub fn render(template: &str, vars: &[(&str, &str)]) -> String {
588 let mut result = template.to_string();
589 for (key, value) in vars {
590 result = result.replace(&format!("{{{}}}", key), value);
591 }
592 result
593}
594
595#[cfg(test)]
596mod tests {
597 use super::*;
598
599 #[test]
600 fn test_all_prompts_loaded() {
601 assert!(!SYSTEM_DEFAULT.is_empty());
603 assert!(!CONTINUATION.is_empty());
604 assert!(!AGENT_EXPLORE.is_empty());
605 assert!(!AGENT_PLAN.is_empty());
606 assert!(!AGENT_CODE_REVIEW.is_empty());
607 assert!(!CONTEXT_COMPACT.is_empty());
608 assert!(!LLM_PLAN_SYSTEM.is_empty());
609 assert!(!LLM_GOAL_EXTRACT_SYSTEM.is_empty());
610 assert!(!LLM_GOAL_CHECK_SYSTEM.is_empty());
611 assert!(!SKILLS_CATALOG_HEADER.is_empty());
612 assert!(!BTW_SYSTEM.is_empty());
613 assert!(!PLAN_EXECUTE_GOAL.is_empty());
614 assert!(!PLAN_EXECUTE_STEP.is_empty());
615 assert!(!PLAN_FALLBACK_STEP.is_empty());
616 }
617
618 #[test]
619 fn test_render_template() {
620 let result = render(
621 PLAN_EXECUTE_GOAL,
622 &[("goal", "Build app"), ("steps", "1. Init")],
623 );
624 assert!(result.contains("Build app"));
625 assert!(!result.contains("{goal}"));
626 }
627
628 #[test]
629 fn test_render_multiple_placeholders() {
630 let template = "Goal: {goal}\nCriteria: {criteria}\nState: {current_state}";
631 let result = render(
632 template,
633 &[
634 ("goal", "Build a REST API"),
635 ("criteria", "- Endpoint works\n- Tests pass"),
636 ("current_state", "API is deployed"),
637 ],
638 );
639 assert!(result.contains("Build a REST API"));
640 assert!(result.contains("Endpoint works"));
641 assert!(result.contains("API is deployed"));
642 }
643
644 #[test]
645 fn test_delegated_agent_prompts_contain_guidelines() {
646 assert!(AGENT_EXPLORE.contains("Guidelines"));
647 assert!(AGENT_EXPLORE.contains("read-only"));
648 assert!(AGENT_PLAN.contains("Guidelines"));
649 assert!(AGENT_PLAN.contains("read-only"));
650 }
651
652 #[test]
653 fn test_context_summary_prefix() {
654 assert!(CONTEXT_SUMMARY_PREFIX.contains("Context Summary"));
655 }
656
657 #[test]
660 fn test_slots_default_builds_system_default() {
661 let slots = SystemPromptSlots::default();
662 let built = slots.build();
663 assert!(built.contains("Core Behaviour"));
664 assert!(built.contains("Tool Usage Strategy"));
665 assert!(built.contains("Completion Criteria"));
666 assert!(built.contains("Response Format"));
667 assert!(built.contains("A3S Code"));
668 }
669
670 #[test]
671 fn test_slots_custom_role_replaces_default() {
672 let slots = SystemPromptSlots {
673 role: Some("You are a senior Python developer".to_string()),
674 ..Default::default()
675 };
676 let built = slots.build();
677 assert!(built.contains("You are a senior Python developer"));
678 assert!(!built.contains("You are A3S Code"));
679 assert!(built.contains("Core Behaviour"));
681 assert!(built.contains("Tool Usage Strategy"));
682 }
683
684 #[test]
685 fn test_slots_custom_guidelines_appended() {
686 let slots = SystemPromptSlots {
687 guidelines: Some("Always use type hints. Follow PEP 8.".to_string()),
688 ..Default::default()
689 };
690 let built = slots.build();
691 assert!(built.contains("## Guidelines"));
692 assert!(built.contains("Always use type hints"));
693 assert!(built.contains("Core Behaviour"));
694 }
695
696 #[test]
697 fn test_slots_custom_response_style_replaces_default() {
698 let slots = SystemPromptSlots {
699 response_style: Some("Be concise. Use bullet points.".to_string()),
700 ..Default::default()
701 };
702 let built = slots.build();
703 assert!(built.contains("Be concise. Use bullet points."));
704 assert!(!built.contains("keep progress notes brief and useful"));
706 assert!(built.contains("Core Behaviour"));
708 }
709
710 #[test]
711 fn test_slots_extra_appended() {
712 let slots = SystemPromptSlots {
713 extra: Some("Remember: always write tests first.".to_string()),
714 ..Default::default()
715 };
716 let built = slots.build();
717 assert!(built.contains("Remember: always write tests first."));
718 assert!(built.contains("Core Behaviour"));
719 }
720
721 #[test]
722 fn test_slots_with_extra() {
723 let slots = SystemPromptSlots::default().with_extra("You are a helpful assistant.");
724 let built = slots.build();
725 assert!(built.contains("You are a helpful assistant."));
726 assert!(built.contains("Core Behaviour"));
727 assert!(built.contains("Tool Usage Strategy"));
728 }
729
730 #[test]
731 fn test_slots_all_slots_combined() {
732 let slots = SystemPromptSlots {
733 style: None,
734 role: Some("You are a Rust expert".to_string()),
735 guidelines: Some("Use clippy. No unwrap.".to_string()),
736 response_style: Some("Short answers only.".to_string()),
737 extra: Some("Project uses tokio.".to_string()),
738 };
739 let built = slots.build();
740 assert!(built.contains("You are a Rust expert"));
741 assert!(built.contains("Core Behaviour"));
742 assert!(built.contains("## Guidelines"));
743 assert!(built.contains("Use clippy"));
744 assert!(built.contains("Short answers only"));
745 assert!(built.contains("Project uses tokio"));
746 assert!(!built.contains("keep progress notes brief and useful"));
748 }
749
750 #[test]
751 fn test_slots_is_empty() {
752 assert!(SystemPromptSlots::default().is_empty());
753 assert!(!SystemPromptSlots {
754 role: Some("test".to_string()),
755 ..Default::default()
756 }
757 .is_empty());
758 assert!(!SystemPromptSlots {
759 style: Some(AgentStyle::Plan),
760 ..Default::default()
761 }
762 .is_empty());
763 }
764
765 #[test]
768 fn test_agent_style_default_is_general_purpose() {
769 assert_eq!(AgentStyle::default(), AgentStyle::GeneralPurpose);
770 }
771
772 #[test]
773 fn test_agent_style_base_prompt() {
774 assert_eq!(AgentStyle::GeneralPurpose.base_prompt(), SYSTEM_DEFAULT);
775 assert_eq!(AgentStyle::Plan.base_prompt(), AGENT_PLAN);
776 assert_eq!(AgentStyle::Explore.base_prompt(), AGENT_EXPLORE);
777 assert_eq!(AgentStyle::Verification.base_prompt(), AGENT_VERIFICATION);
778 assert_eq!(AgentStyle::CodeReview.base_prompt(), AGENT_CODE_REVIEW);
779 }
780
781 #[test]
782 fn test_agent_style_guidelines() {
783 assert!(AgentStyle::GeneralPurpose.guidelines().is_none());
784 assert!(AgentStyle::Plan.guidelines().is_none()); assert!(AgentStyle::Explore.guidelines().is_none());
786 assert!(AgentStyle::Verification.guidelines().is_none());
787 assert!(AgentStyle::CodeReview.guidelines().is_none());
788 }
789
790 #[test]
791 fn test_agent_style_builtin_agent_name_mapping() {
792 assert_eq!(AgentStyle::GeneralPurpose.builtin_agent_name(), "general");
793 assert_eq!(AgentStyle::Plan.builtin_agent_name(), "plan");
794 assert_eq!(AgentStyle::Explore.builtin_agent_name(), "explore");
795 assert_eq!(
796 AgentStyle::Verification.builtin_agent_name(),
797 "verification"
798 );
799 assert_eq!(AgentStyle::CodeReview.builtin_agent_name(), "review");
800 }
801
802 #[test]
803 fn test_agent_style_runtime_mode_mapping() {
804 assert_eq!(AgentStyle::GeneralPurpose.runtime_mode(), "general");
805 assert_eq!(AgentStyle::Plan.runtime_mode(), "planning");
806 assert_eq!(AgentStyle::Explore.runtime_mode(), "explore");
807 assert_eq!(AgentStyle::Verification.runtime_mode(), "verification");
808 assert_eq!(AgentStyle::CodeReview.runtime_mode(), "code_review");
809 }
810
811 #[test]
812 fn test_agent_style_detect_plan() {
813 assert_eq!(
814 AgentStyle::detect_from_message("Help me plan a new feature"),
815 AgentStyle::Plan
816 );
817 assert_eq!(
818 AgentStyle::detect_from_message("Design the architecture for this"),
819 AgentStyle::Plan
820 );
821 assert_eq!(
822 AgentStyle::detect_from_message("What's the implementation approach?"),
823 AgentStyle::Plan
824 );
825 }
826
827 #[test]
828 fn test_agent_style_detect_verification() {
829 assert_eq!(
830 AgentStyle::detect_from_message("Verify that this works correctly"),
831 AgentStyle::Verification
832 );
833 assert_eq!(
834 AgentStyle::detect_from_message("Test the login flow"),
835 AgentStyle::Verification
836 );
837 assert_eq!(
838 AgentStyle::detect_from_message("Check if the API handles edge cases"),
839 AgentStyle::Verification
840 );
841 }
842
843 #[test]
844 fn test_agent_style_detect_explore() {
845 assert_eq!(
846 AgentStyle::detect_from_message("Find all files related to auth"),
847 AgentStyle::Explore
848 );
849 assert_eq!(
850 AgentStyle::detect_from_message("Where is the user model defined?"),
851 AgentStyle::Explore
852 );
853 assert_eq!(
854 AgentStyle::detect_from_message("Search for password hashing code"),
855 AgentStyle::Explore
856 );
857 }
858
859 #[test]
860 fn test_agent_style_detect_code_review() {
861 assert_eq!(
862 AgentStyle::detect_from_message("Review the PR changes"),
863 AgentStyle::CodeReview
864 );
865 assert_eq!(
866 AgentStyle::detect_from_message("Analyze this code for best practices"),
867 AgentStyle::CodeReview
868 );
869 assert_eq!(
870 AgentStyle::detect_from_message("Assess code quality"),
871 AgentStyle::CodeReview
872 );
873 }
874
875 #[test]
876 fn test_agent_style_detect_default_is_general_purpose() {
877 assert_eq!(
879 AgentStyle::detect_from_message("Implement the new feature"),
880 AgentStyle::GeneralPurpose
881 );
882 assert_eq!(
884 AgentStyle::detect_from_message("Write code for the API"),
885 AgentStyle::GeneralPurpose
886 );
887 }
888
889 #[test]
890 fn test_build_with_message_auto_detects_style() {
891 let slots = SystemPromptSlots::default();
892 let built = slots.build_with_message("Help me plan a new feature");
893 assert!(built.contains("planning agent") || built.contains("READ-ONLY"));
895 }
896
897 #[test]
898 fn test_build_with_message_explicit_style_overrides() {
899 let slots = SystemPromptSlots {
900 style: Some(AgentStyle::Verification),
901 ..Default::default()
902 };
903 let built = slots.build_with_message("Help me plan a new feature");
904 assert!(built.contains("adversarial verification specialist"));
906 }
907
908 #[test]
909 fn test_build_with_message_plan_style() {
910 let slots = SystemPromptSlots::default();
911 let built = slots.build_with_message("Design the system architecture");
912 assert!(built.contains("planning agent") || built.contains("READ-ONLY"));
913 }
914
915 #[test]
916 fn test_build_with_message_explore_style() {
917 let slots = SystemPromptSlots::default();
918 let built = slots.build_with_message("Find all authentication files");
919 assert!(built.contains("exploration agent") || built.contains("explore"));
920 }
921
922 #[test]
923 fn test_build_with_message_code_review_style() {
924 let slots = SystemPromptSlots::default();
925 let built = slots.build_with_message("Review this code");
926 assert!(built.contains("code review agent"));
927 assert!(built.contains("regressions"));
928 }
929
930 #[test]
931 fn test_builder_methods() {
932 let slots = SystemPromptSlots::default()
933 .with_style(AgentStyle::Plan)
934 .with_role("You are a Python expert")
935 .with_guidelines("Use type hints")
936 .with_response_style("Be brief")
937 .with_extra("Additional instructions");
938
939 assert_eq!(slots.style, Some(AgentStyle::Plan));
940 assert_eq!(slots.role, Some("You are a Python expert".to_string()));
941 assert_eq!(slots.guidelines, Some("Use type hints".to_string()));
942 assert_eq!(slots.response_style, Some("Be brief".to_string()));
943 assert_eq!(slots.extra, Some("Additional instructions".to_string()));
944
945 let built = slots.build();
946 assert!(built.contains("Python expert"));
947 assert!(built.contains("Use type hints"));
948 assert!(built.contains("Be brief"));
949 assert!(built.contains("Additional instructions"));
950 }
951
952 #[test]
953 fn test_code_review_guidelines_appended() {
954 let slots = SystemPromptSlots {
955 style: Some(AgentStyle::CodeReview),
956 ..Default::default()
957 };
958 let built = slots.build();
959 assert!(built.contains("code review agent"));
960 assert!(built.contains("correctness"));
961 assert!(built.contains("regressions"));
962 assert!(built.contains("security"));
963 }
964
965 #[test]
966 fn test_prompts_do_not_reference_removed_surfaces() {
967 let prompts = [
968 SYSTEM_DEFAULT,
969 AGENT_VERIFICATION,
970 PRE_ANALYSIS_SYSTEM,
971 AGENT_EXPLORE,
972 AGENT_PLAN,
973 AGENT_CODE_REVIEW,
974 SKILLS_CATALOG_HEADER,
975 CONTINUATION,
976 ]
977 .join("\n")
978 .to_lowercase();
979
980 for removed in [
981 "orchestrator",
982 "plugin",
983 "agentic_search",
984 "agentic_parse",
985 "agentic-search",
986 "agentic-parse",
987 "manage_skill",
988 "claude.md",
989 ] {
990 assert!(
991 !prompts.contains(removed),
992 "prompt still references removed surface: {removed}"
993 );
994 }
995
996 assert!(SYSTEM_DEFAULT.contains("program"));
997 assert!(SYSTEM_DEFAULT.contains("task"));
998 assert!(SYSTEM_DEFAULT.contains("parallel_task"));
999 assert!(SYSTEM_DEFAULT.contains("AHP"));
1000 }
1001}