Skip to main content

ralph/
prompts.rs

1//! Prompt template loading, rendering, and validation utilities.
2//!
3//! Responsibilities:
4//! - Expose a minimal public prompt API for integration tests and external callers.
5//! - Keep prompt composition and registry details internal to the crate.
6//!
7//! Not handled:
8//! - CLI argument parsing or queue mutation.
9//! - Direct access to prompt registry internals (see `prompts_internal`).
10//!
11//! Invariants/assumptions:
12//! - Public exports here are intentional and minimal.
13
14use crate::cli::scan::ScanMode;
15use crate::contracts::{Config, ProjectType, ScanPromptVersion};
16use crate::prompts_internal;
17use anyhow::Result;
18use std::path::Path;
19
20pub const REPOPROMPT_REQUIRED_INSTRUCTION: &str =
21    prompts_internal::util::REPOPROMPT_REQUIRED_INSTRUCTION;
22pub const REPOPROMPT_CONTEXT_BUILDER_PLANNING_INSTRUCTION: &str =
23    prompts_internal::util::REPOPROMPT_CONTEXT_BUILDER_PLANNING_INSTRUCTION;
24pub const PHASE3_COMPLETION_GUIDANCE_FINAL: &str =
25    prompts_internal::iteration::PHASE3_COMPLETION_GUIDANCE_FINAL;
26
27pub(crate) const ITERATION_CONTEXT_REFINEMENT: &str =
28    prompts_internal::iteration::ITERATION_CONTEXT_REFINEMENT;
29pub(crate) const ITERATION_COMPLETION_BLOCK: &str =
30    prompts_internal::iteration::ITERATION_COMPLETION_BLOCK;
31pub(crate) const PHASE3_COMPLETION_GUIDANCE_NONFINAL: &str =
32    prompts_internal::iteration::PHASE3_COMPLETION_GUIDANCE_NONFINAL;
33
34pub(crate) fn prompts_reference_readme(repo_root: &Path) -> Result<bool> {
35    prompts_internal::prompts_reference_readme(repo_root)
36}
37
38pub(crate) fn load_scan_prompt(
39    repo_root: &Path,
40    version: ScanPromptVersion,
41    mode: ScanMode,
42) -> Result<String> {
43    prompts_internal::scan::load_scan_prompt(repo_root, version, mode)
44}
45
46pub(crate) fn render_scan_prompt(
47    template: &str,
48    focus: &str,
49    mode: ScanMode,
50    version: ScanPromptVersion,
51    project_type: ProjectType,
52    config: &Config,
53) -> Result<String> {
54    prompts_internal::scan::render_scan_prompt(template, focus, mode, version, project_type, config)
55}
56
57pub(crate) fn load_task_builder_prompt(repo_root: &Path) -> Result<String> {
58    prompts_internal::task_builder::load_task_builder_prompt(repo_root)
59}
60
61pub(crate) fn load_task_decompose_prompt(repo_root: &Path) -> Result<String> {
62    prompts_internal::task_decompose::load_task_decompose_prompt(repo_root)
63}
64
65pub(crate) fn render_task_builder_prompt(
66    template: &str,
67    user_request: &str,
68    hint_tags: &str,
69    hint_scope: &str,
70    project_type: ProjectType,
71    config: &Config,
72) -> Result<String> {
73    prompts_internal::task_builder::render_task_builder_prompt(
74        template,
75        user_request,
76        hint_tags,
77        hint_scope,
78        project_type,
79        config,
80    )
81}
82
83#[allow(clippy::too_many_arguments)]
84pub(crate) fn render_task_decompose_prompt(
85    template: &str,
86    source_mode: &str,
87    source_request: &str,
88    source_task_json: &str,
89    attach_target_json: &str,
90    max_depth: u8,
91    max_children: usize,
92    max_nodes: usize,
93    child_policy: crate::commands::task::DecompositionChildPolicy,
94    with_dependencies: bool,
95    project_type: ProjectType,
96    config: &Config,
97) -> Result<String> {
98    prompts_internal::task_decompose::render_task_decompose_prompt(
99        template,
100        source_mode,
101        source_request,
102        source_task_json,
103        attach_target_json,
104        max_depth,
105        max_children,
106        max_nodes,
107        child_policy,
108        with_dependencies,
109        project_type,
110        config,
111    )
112}
113
114pub(crate) fn load_task_updater_prompt(repo_root: &Path) -> Result<String> {
115    prompts_internal::task_updater::load_task_updater_prompt(repo_root)
116}
117
118pub(crate) fn render_task_updater_prompt(
119    template: &str,
120    task_id: &str,
121    project_type: ProjectType,
122    config: &Config,
123) -> Result<String> {
124    prompts_internal::task_updater::render_task_updater_prompt(
125        template,
126        task_id,
127        project_type,
128        config,
129    )
130}
131
132/// Internal compatibility helper for merge-conflict prompt rendering.
133#[allow(dead_code)]
134pub(crate) fn load_merge_conflict_prompt(repo_root: &Path) -> Result<String> {
135    prompts_internal::merge_conflicts::load_merge_conflict_prompt(repo_root)
136}
137
138/// Internal compatibility helper for merge-conflict prompt rendering.
139#[allow(dead_code)]
140pub(crate) fn render_merge_conflict_prompt(
141    template: &str,
142    conflict_files: &[String],
143    config: &Config,
144) -> Result<String> {
145    prompts_internal::merge_conflicts::render_merge_conflict_prompt(
146        template,
147        conflict_files,
148        config,
149    )
150}
151
152pub(crate) fn wrap_with_repoprompt_requirement(prompt: &str, required: bool) -> String {
153    prompts_internal::util::wrap_with_repoprompt_requirement(prompt, required)
154}
155
156pub(crate) fn wrap_with_instruction_files(
157    repo_root: &Path,
158    prompt: &str,
159    config: &Config,
160) -> Result<String> {
161    prompts_internal::util::wrap_with_instruction_files(repo_root, prompt, config)
162}
163
164pub(crate) fn instruction_file_warnings(repo_root: &Path, config: &Config) -> Vec<String> {
165    prompts_internal::util::instruction_file_warnings(repo_root, config)
166}
167
168pub(crate) fn load_worker_prompt(repo_root: &Path) -> Result<String> {
169    prompts_internal::worker::load_worker_prompt(repo_root)
170}
171
172pub(crate) fn render_worker_prompt(
173    template: &str,
174    task_id: &str,
175    project_type: ProjectType,
176    config: &Config,
177) -> Result<String> {
178    prompts_internal::worker::render_worker_prompt(template, task_id, project_type, config)
179}
180
181pub fn load_worker_phase1_prompt(repo_root: &Path) -> Result<String> {
182    prompts_internal::worker_phases::load_worker_phase1_prompt(repo_root)
183}
184
185pub fn load_worker_phase2_prompt(repo_root: &Path) -> Result<String> {
186    prompts_internal::worker_phases::load_worker_phase2_prompt(repo_root)
187}
188
189pub fn load_worker_phase2_handoff_prompt(repo_root: &Path) -> Result<String> {
190    prompts_internal::worker_phases::load_worker_phase2_handoff_prompt(repo_root)
191}
192
193pub fn load_worker_phase3_prompt(repo_root: &Path) -> Result<String> {
194    prompts_internal::worker_phases::load_worker_phase3_prompt(repo_root)
195}
196
197pub fn load_worker_single_phase_prompt(repo_root: &Path) -> Result<String> {
198    prompts_internal::worker_phases::load_worker_single_phase_prompt(repo_root)
199}
200
201#[allow(clippy::too_many_arguments)]
202pub(crate) fn render_worker_phase1_prompt(
203    template: &str,
204    base_prompt: &str,
205    iteration_context: &str,
206    task_refresh_instruction: &str,
207    task_id: &str,
208    total_phases: u8,
209    plan_path: &str,
210    repoprompt_plan_required: bool,
211    repoprompt_tool_injection: bool,
212    config: &Config,
213) -> Result<String> {
214    prompts_internal::worker_phases::render_worker_phase1_prompt(
215        template,
216        base_prompt,
217        iteration_context,
218        task_refresh_instruction,
219        task_id,
220        total_phases,
221        plan_path,
222        repoprompt_plan_required,
223        repoprompt_tool_injection,
224        config,
225    )
226}
227
228#[allow(clippy::too_many_arguments)]
229pub(crate) fn render_worker_phase2_prompt(
230    template: &str,
231    base_prompt: &str,
232    plan: &str,
233    checklist: &str,
234    iteration_context: &str,
235    iteration_completion_block: &str,
236    task_id: &str,
237    total_phases: u8,
238    repoprompt_tool_injection: bool,
239    config: &Config,
240) -> Result<String> {
241    prompts_internal::worker_phases::render_worker_phase2_prompt(
242        template,
243        base_prompt,
244        plan,
245        checklist,
246        iteration_context,
247        iteration_completion_block,
248        task_id,
249        total_phases,
250        repoprompt_tool_injection,
251        config,
252    )
253}
254
255#[allow(clippy::too_many_arguments)]
256pub(crate) fn render_worker_phase2_handoff_prompt(
257    template: &str,
258    base_prompt: &str,
259    plan: &str,
260    checklist: &str,
261    iteration_context: &str,
262    iteration_completion_block: &str,
263    task_id: &str,
264    total_phases: u8,
265    repoprompt_tool_injection: bool,
266    config: &Config,
267) -> Result<String> {
268    prompts_internal::worker_phases::render_worker_phase2_handoff_prompt(
269        template,
270        base_prompt,
271        plan,
272        checklist,
273        iteration_context,
274        iteration_completion_block,
275        task_id,
276        total_phases,
277        repoprompt_tool_injection,
278        config,
279    )
280}
281
282#[allow(clippy::too_many_arguments)]
283pub(crate) fn render_worker_phase3_prompt(
284    template: &str,
285    base_prompt: &str,
286    review_body: &str,
287    phase2_final: &str,
288    task_id: &str,
289    checklist: &str,
290    iteration_context: &str,
291    iteration_completion_block: &str,
292    phase3_completion_guidance: &str,
293    total_phases: u8,
294    repoprompt_tool_injection: bool,
295    config: &Config,
296) -> Result<String> {
297    prompts_internal::worker_phases::render_worker_phase3_prompt(
298        template,
299        base_prompt,
300        review_body,
301        phase2_final,
302        task_id,
303        checklist,
304        iteration_context,
305        iteration_completion_block,
306        phase3_completion_guidance,
307        total_phases,
308        repoprompt_tool_injection,
309        config,
310    )
311}
312
313#[allow(clippy::too_many_arguments)]
314pub(crate) fn render_worker_single_phase_prompt(
315    template: &str,
316    base_prompt: &str,
317    checklist: &str,
318    iteration_context: &str,
319    iteration_completion_block: &str,
320    task_id: &str,
321    repoprompt_tool_injection: bool,
322    config: &Config,
323) -> Result<String> {
324    prompts_internal::worker_phases::render_worker_single_phase_prompt(
325        template,
326        base_prompt,
327        checklist,
328        iteration_context,
329        iteration_completion_block,
330        task_id,
331        repoprompt_tool_injection,
332        config,
333    )
334}
335
336pub fn load_completion_checklist(repo_root: &Path) -> Result<String> {
337    prompts_internal::review::load_completion_checklist(repo_root)
338}
339
340pub fn load_phase2_handoff_checklist(repo_root: &Path) -> Result<String> {
341    prompts_internal::review::load_phase2_handoff_checklist(repo_root)
342}
343
344pub fn load_iteration_checklist(repo_root: &Path) -> Result<String> {
345    prompts_internal::review::load_iteration_checklist(repo_root)
346}
347
348pub fn render_completion_checklist(
349    template: &str,
350    task_id: &str,
351    config: &Config,
352    parallel_worker_mode: bool,
353) -> Result<String> {
354    prompts_internal::review::render_completion_checklist(
355        template,
356        task_id,
357        config,
358        parallel_worker_mode,
359    )
360}
361
362pub fn render_phase2_handoff_checklist(template: &str, config: &Config) -> Result<String> {
363    prompts_internal::review::render_phase2_handoff_checklist(template, config)
364}
365
366pub fn render_iteration_checklist(
367    template: &str,
368    task_id: &str,
369    config: &Config,
370) -> Result<String> {
371    prompts_internal::review::render_iteration_checklist(template, task_id, config)
372}
373
374pub(crate) fn load_code_review_prompt(repo_root: &Path) -> Result<String> {
375    prompts_internal::review::load_code_review_prompt(repo_root)
376}
377
378pub(crate) fn render_code_review_prompt(
379    template: &str,
380    task_id: &str,
381    project_type: ProjectType,
382    config: &Config,
383) -> Result<String> {
384    prompts_internal::review::render_code_review_prompt(template, task_id, project_type, config)
385}