1use crate::logger::Colors;
7use crate::logger::Loggable;
8
9pub struct PipelineSummary {
13 pub total_time: String,
15 pub dev_runs_completed: usize,
17 pub dev_runs_total: usize,
19 pub review_runs: usize,
21 pub changes_detected: usize,
23 pub isolation_mode: bool,
25 pub verbose: bool,
27 pub review_summary: Option<ReviewSummary>,
29}
30
31pub struct ReviewSummary {
33 pub summary: String,
35 pub unresolved_count: usize,
37 pub blocking_count: usize,
39 pub detailed_breakdown: Option<String>,
41 pub samples: Vec<String>,
43}
44
45pub fn print_welcome_banner(colors: Colors, developer_agent: &str, reviewer_agent: &str) {
55 println!();
56 println!(
57 "{}{}╭────────────────────────────────────────────────────────────╮{}",
58 colors.bold(),
59 colors.cyan(),
60 colors.reset()
61 );
62 println!(
63 "{}{}│{} {}{}🤖 Ralph{} {}─ PROMPT-driven agent orchestrator{} {}{}│{}",
64 colors.bold(),
65 colors.cyan(),
66 colors.reset(),
67 colors.bold(),
68 colors.white(),
69 colors.reset(),
70 colors.dim(),
71 colors.reset(),
72 colors.bold(),
73 colors.cyan(),
74 colors.reset()
75 );
76 println!(
77 "{}{}│{} {}{} × {} pipeline for autonomous development{} {}{}│{}",
78 colors.bold(),
79 colors.cyan(),
80 colors.reset(),
81 colors.dim(),
82 developer_agent,
83 reviewer_agent,
84 colors.reset(),
85 colors.bold(),
86 colors.cyan(),
87 colors.reset()
88 );
89 println!(
90 "{}{}╰────────────────────────────────────────────────────────────╯{}",
91 colors.bold(),
92 colors.cyan(),
93 colors.reset()
94 );
95 println!();
96}
97
98pub fn print_final_summary<L: Loggable>(colors: Colors, summary: &PipelineSummary, logger: &L) {
109 logger.header("Pipeline Complete", crate::logger::Colors::green);
110
111 println!();
112 println!(
113 "{}{}📊 Summary{}",
114 colors.bold(),
115 colors.white(),
116 colors.reset()
117 );
118 println!(
119 "{}──────────────────────────────────{}",
120 colors.dim(),
121 colors.reset()
122 );
123 println!(
124 " {}⏱{} Total time: {}{}{}",
125 colors.cyan(),
126 colors.reset(),
127 colors.bold(),
128 summary.total_time,
129 colors.reset()
130 );
131 println!(
132 " {}🔄{} Dev runs: {}{}{}/{}",
133 colors.blue(),
134 colors.reset(),
135 colors.bold(),
136 summary.dev_runs_completed,
137 colors.reset(),
138 summary.dev_runs_total
139 );
140 println!(
141 " {}🔍{} Review runs: {}{}{}",
142 colors.magenta(),
143 colors.reset(),
144 colors.bold(),
145 summary.review_runs,
146 colors.reset()
147 );
148 println!(
149 " {}📝{} Changes detected: {}{}{}",
150 colors.green(),
151 colors.reset(),
152 colors.bold(),
153 summary.changes_detected,
154 colors.reset()
155 );
156
157 if let Some(ref review) = summary.review_summary {
159 print_review_summary(colors, summary.verbose, review);
160 }
161 println!();
162
163 print_output_files(colors, summary.isolation_mode);
164
165 logger.success("Ralph pipeline completed successfully!");
167
168 if summary.review_runs > 0 {
170 logger.info(&format!("Completed {} review run(s)", summary.review_runs));
171 }
172 if summary.changes_detected > 0 {
173 logger.info(&format!("Detected {} change(s)", summary.changes_detected));
174 }
175 if summary.isolation_mode {
176 logger.info("Running in isolation mode");
177 }
178
179 if let Some(ref review) = summary.review_summary {
181 if review.unresolved_count > 0 {
182 logger.warn(&format!(
183 "{} unresolved issue(s) remaining",
184 review.unresolved_count
185 ));
186 }
187 if review.blocking_count > 0 {
188 logger.error(&format!(
189 "{} blocking issue(s) unresolved",
190 review.blocking_count
191 ));
192 }
193 }
194}
195
196fn print_review_summary(colors: Colors, verbose: bool, review: &ReviewSummary) {
198 if review.unresolved_count == 0 && review.blocking_count == 0 {
200 println!(
201 " {}✓{} Review result: {}{}{}",
202 colors.green(),
203 colors.reset(),
204 colors.bold(),
205 review.summary,
206 colors.reset()
207 );
208 return;
209 }
210
211 println!(
213 " {}🔎{} Review summary: {}{}{}",
214 colors.yellow(),
215 colors.reset(),
216 colors.bold(),
217 review.summary,
218 colors.reset()
219 );
220
221 if review.unresolved_count > 0 {
223 println!(
224 " {}⚠{} Unresolved: {}{}{} issues remaining",
225 colors.red(),
226 colors.reset(),
227 colors.bold(),
228 review.unresolved_count,
229 colors.reset()
230 );
231 }
232
233 if verbose {
235 if let Some(ref breakdown) = review.detailed_breakdown {
236 println!(" {}📊{} Breakdown:", colors.dim(), colors.reset());
237 for line in breakdown.lines() {
238 println!(" {}{}{}", colors.dim(), line.trim(), colors.reset());
239 }
240 }
241 if !review.samples.is_empty() {
243 println!(
244 " {}🧾{} Unresolved samples:",
245 colors.dim(),
246 colors.reset()
247 );
248 for s in &review.samples {
249 println!(" {}- {}{}", colors.dim(), s, colors.reset());
250 }
251 }
252 }
253
254 if review.blocking_count > 0 {
256 println!(
257 " {}🚨{} BLOCKING: {}{}{} critical/high issues unresolved",
258 colors.red(),
259 colors.reset(),
260 colors.bold(),
261 review.blocking_count,
262 colors.reset()
263 );
264 }
265}
266
267fn print_output_files(colors: Colors, isolation_mode: bool) {
269 println!(
270 "{}{}📁 Output Files{}",
271 colors.bold(),
272 colors.white(),
273 colors.reset()
274 );
275 println!(
276 "{}──────────────────────────────────{}",
277 colors.dim(),
278 colors.reset()
279 );
280 println!(
281 " → {}PROMPT.md{} Goal definition",
282 colors.cyan(),
283 colors.reset()
284 );
285 println!(
286 " → {}.agent/STATUS.md{} Current status",
287 colors.cyan(),
288 colors.reset()
289 );
290 if !isolation_mode {
292 println!(
293 " → {}.agent/ISSUES.md{} Review findings",
294 colors.cyan(),
295 colors.reset()
296 );
297 println!(
298 " → {}.agent/NOTES.md{} Progress notes",
299 colors.cyan(),
300 colors.reset()
301 );
302 }
303 println!(
304 " → {}.agent/logs/{} Detailed logs",
305 colors.cyan(),
306 colors.reset()
307 );
308 println!();
309}