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        }
35    }
36
37    #[test]
38    fn prompt_includes_issue_details() {
39        let prompt = build_prompt(&sample_context()).unwrap();
40        assert!(prompt.contains("<issue_number>42</issue_number>"));
41        assert!(prompt.contains("<issue_title>Add retry logic</issue_title>"));
42        assert!(prompt.contains("Implement retry for API calls"));
43        assert!(prompt.contains("oven/issue-42-abcd1234"));
44        assert!(prompt.contains("PR: #99"));
45    }
46
47    #[test]
48    fn prompt_includes_scope_discipline() {
49        let prompt = build_prompt(&sample_context()).unwrap();
50        assert!(prompt.contains("Scope Discipline"));
51        assert!(prompt.contains("MUST NOT modify code outside the issue"));
52    }
53
54    #[test]
55    fn prompt_includes_verification_checklist() {
56        let prompt = build_prompt(&sample_context()).unwrap();
57        assert!(prompt.contains("Verification Checklist"));
58        assert!(prompt.contains("git diff main --stat"));
59    }
60
61    #[test]
62    fn prompt_includes_test_and_lint_commands() {
63        let prompt = build_prompt(&sample_context()).unwrap();
64        assert!(prompt.contains("cargo test"));
65        assert!(prompt.contains("cargo clippy"));
66    }
67
68    #[test]
69    fn prompt_without_test_command() {
70        let mut ctx = sample_context();
71        ctx.test_command = None;
72        let prompt = build_prompt(&ctx).unwrap();
73        assert!(!prompt.contains("cargo test"));
74    }
75
76    #[test]
77    fn prompt_includes_when_stuck_guidance() {
78        let prompt = build_prompt(&sample_context()).unwrap();
79        assert!(prompt.contains("When Stuck"));
80        assert!(prompt.contains("switch to a different strategy"));
81    }
82
83    #[test]
84    fn prompt_includes_commit_workflow() {
85        let prompt = build_prompt(&sample_context()).unwrap();
86        assert!(prompt.contains("Commit Workflow"));
87        assert!(prompt.contains("atomic conventional commits"));
88    }
89}