Skip to main content

oven_cli/agents/
implementer.rs

1use 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}