oven_cli/agents/
implementer.rs1use anyhow::{Context, Result};
2use askama::Template;
3
4use super::AgentContext;
5
6#[derive(Template)]
7#[template(path = "implementer.txt")]
8struct ImplementerPrompt<'a> {
9 ctx: &'a AgentContext,
10}
11
12pub fn build_prompt(ctx: &AgentContext) -> Result<String> {
13 let tmpl = ImplementerPrompt { ctx };
14 tmpl.render().context("rendering implementer template")
15}
16
17#[cfg(test)]
18mod tests {
19 use super::*;
20
21 fn sample_context() -> AgentContext {
22 AgentContext {
23 issue_number: 42,
24 issue_title: "Add retry logic".to_string(),
25 issue_body: "Implement retry for API calls".to_string(),
26 branch: "oven/issue-42-abcd1234".to_string(),
27 pr_number: Some(99),
28 test_command: Some("cargo test".to_string()),
29 lint_command: Some("cargo clippy".to_string()),
30 review_findings: None,
31 cycle: 1,
32 target_repo: None,
33 issue_source: "github".to_string(),
34 base_branch: "main".to_string(),
35 }
36 }
37
38 #[test]
39 fn prompt_includes_issue_details() {
40 let prompt = build_prompt(&sample_context()).unwrap();
41 assert!(prompt.contains("<issue_number>42</issue_number>"));
42 assert!(prompt.contains("<issue_title>Add retry logic</issue_title>"));
43 assert!(prompt.contains("Implement retry for API calls"));
44 assert!(prompt.contains("oven/issue-42-abcd1234"));
45 assert!(prompt.contains("PR: #99"));
46 }
47
48 #[test]
49 fn prompt_includes_scope_discipline() {
50 let prompt = build_prompt(&sample_context()).unwrap();
51 assert!(prompt.contains("Scope Discipline"));
52 assert!(prompt.contains("MUST NOT modify code outside the issue"));
53 }
54
55 #[test]
56 fn prompt_includes_verification_checklist() {
57 let prompt = build_prompt(&sample_context()).unwrap();
58 assert!(prompt.contains("Verification Checklist"));
59 assert!(prompt.contains("git diff main --stat"));
60 }
61
62 #[test]
63 fn prompt_includes_test_and_lint_commands() {
64 let prompt = build_prompt(&sample_context()).unwrap();
65 assert!(prompt.contains("cargo test"));
66 assert!(prompt.contains("cargo clippy"));
67 }
68
69 #[test]
70 fn prompt_without_test_command() {
71 let mut ctx = sample_context();
72 ctx.test_command = None;
73 let prompt = build_prompt(&ctx).unwrap();
74 assert!(!prompt.contains("cargo test"));
75 }
76
77 #[test]
78 fn prompt_includes_when_stuck_guidance() {
79 let prompt = build_prompt(&sample_context()).unwrap();
80 assert!(prompt.contains("When Stuck"));
81 assert!(prompt.contains("switch to a different strategy"));
82 }
83
84 #[test]
85 fn prompt_includes_commit_workflow() {
86 let prompt = build_prompt(&sample_context()).unwrap();
87 assert!(prompt.contains("Commit Workflow"));
88 assert!(prompt.contains("atomic conventional commits"));
89 }
90}