ralph_workflow/app/
plumbing.rs1use crate::agents::AgentRegistry;
18use crate::app::effect::{AppEffect, AppEffectHandler, AppEffectResult};
19use crate::config::Config;
20use crate::executor::ProcessExecutor;
21use crate::files::{
22 delete_commit_message_file, delete_commit_message_file_with_workspace,
23 read_commit_message_file, read_commit_message_file_with_workspace,
24 write_commit_message_file_with_workspace,
25};
26use crate::git_helpers::{
27 get_repo_root, git_add_all, git_commit, git_diff, git_snapshot, require_git_repo,
28};
29use crate::logger::Colors;
30use crate::logger::Logger;
31use crate::phases::generate_commit_message;
32use crate::pipeline::PipelineRuntime;
33use crate::pipeline::Timer;
34use crate::prompts::TemplateContext;
35use crate::workspace::Workspace;
36use std::env;
37use std::sync::Arc;
38
39pub struct CommitGenerationConfig<'a> {
44 pub config: &'a Config,
46 pub template_context: &'a TemplateContext,
48 pub workspace: &'a dyn crate::workspace::Workspace,
50 pub registry: &'a AgentRegistry,
52 pub logger: &'a Logger,
54 pub colors: Colors,
56 pub developer_agent: &'a str,
58 pub _reviewer_agent: &'a str,
60 pub executor: Arc<dyn ProcessExecutor>,
62}
63
64pub fn handle_show_commit_msg() -> anyhow::Result<()> {
72 require_git_repo()?;
73 let repo_root = get_repo_root()?;
74 env::set_current_dir(&repo_root)?;
75
76 match read_commit_message_file() {
77 Ok(msg) => {
78 println!("{msg}");
79 Ok(())
80 }
81 Err(e) => {
82 anyhow::bail!("Failed to read commit message: {e}");
83 }
84 }
85}
86
87pub fn handle_show_commit_msg_with_workspace(workspace: &dyn Workspace) -> anyhow::Result<()> {
100 match read_commit_message_file_with_workspace(workspace) {
101 Ok(msg) => {
102 println!("{msg}");
103 Ok(())
104 }
105 Err(e) => {
106 anyhow::bail!("Failed to read commit message: {e}");
107 }
108 }
109}
110
111pub fn handle_apply_commit(logger: &Logger, colors: Colors) -> anyhow::Result<()> {
125 require_git_repo()?;
126 let repo_root = get_repo_root()?;
127 env::set_current_dir(&repo_root)?;
128
129 let commit_msg = read_commit_message_file()?;
130
131 logger.info("Staging all changes...");
132 git_add_all()?;
133
134 if let Ok(status) = git_snapshot() {
136 if !status.is_empty() {
137 println!("{}Changes to commit:{}", colors.bold(), colors.reset());
138 for line in status.lines().take(20) {
139 println!(" {}{}{}", colors.dim(), line, colors.reset());
140 }
141 println!();
142 }
143 }
144
145 logger.info(&format!(
146 "Commit message: {}{}{}",
147 colors.cyan(),
148 commit_msg,
149 colors.reset()
150 ));
151
152 logger.info("Creating commit...");
153 if let Some(oid) = git_commit(&commit_msg, None, None, None)? {
157 logger.success(&format!("Commit created successfully: {oid}"));
158 if let Err(err) = delete_commit_message_file() {
160 logger.warn(&format!("Failed to delete commit-message.txt: {err}"));
161 }
162 } else {
163 logger.warn("Nothing to commit (working tree clean)");
164 }
165
166 Ok(())
167}
168
169pub fn handle_apply_commit_with_handler<H: AppEffectHandler>(
185 workspace: &dyn Workspace,
186 handler: &mut H,
187 logger: &Logger,
188 colors: Colors,
189) -> anyhow::Result<()> {
190 let commit_msg = read_commit_message_file_with_workspace(workspace)?;
191
192 logger.info("Staging all changes...");
193
194 match handler.execute(AppEffect::GitAddAll) {
197 AppEffectResult::Ok | AppEffectResult::Bool(true) => {}
198 AppEffectResult::Bool(false) => {
199 }
201 AppEffectResult::Error(e) => anyhow::bail!("Failed to stage changes: {e}"),
202 other => anyhow::bail!("Unexpected result from GitAddAll: {other:?}"),
203 }
204
205 logger.info(&format!(
206 "Commit message: {}{}{}",
207 colors.cyan(),
208 commit_msg,
209 colors.reset()
210 ));
211
212 logger.info("Creating commit...");
213
214 match handler.execute(AppEffect::GitCommit {
218 message: commit_msg,
219 user_name: None,
220 user_email: None,
221 }) {
222 AppEffectResult::String(oid) => {
223 logger.success(&format!("Commit created successfully: {oid}"));
224 if let Err(err) = delete_commit_message_file_with_workspace(workspace) {
226 logger.warn(&format!("Failed to delete commit-message.txt: {err}"));
227 }
228 Ok(())
229 }
230 AppEffectResult::Commit(crate::app::effect::CommitResult::Success(oid)) => {
231 logger.success(&format!("Commit created successfully: {oid}"));
232 if let Err(err) = delete_commit_message_file_with_workspace(workspace) {
234 logger.warn(&format!("Failed to delete commit-message.txt: {err}"));
235 }
236 Ok(())
237 }
238 AppEffectResult::Commit(crate::app::effect::CommitResult::NoChanges)
239 | AppEffectResult::Ok => {
240 logger.warn("Nothing to commit (working tree clean)");
242 Ok(())
243 }
244 AppEffectResult::Error(e) => anyhow::bail!("Failed to create commit: {e}"),
245 other => anyhow::bail!("Unexpected result from GitCommit: {other:?}"),
246 }
247}
248
249pub fn handle_generate_commit_msg(config: CommitGenerationConfig<'_>) -> anyhow::Result<()> {
268 config.logger.info("Generating commit message...");
269
270 let diff = git_diff()?;
272 if diff.trim().is_empty() {
273 config
274 .logger
275 .warn("No changes detected to generate a commit message for");
276 anyhow::bail!("No changes to commit");
277 }
278
279 let mut timer = Timer::new();
281
282 let executor_ref: &dyn ProcessExecutor = &*config.executor;
284 let mut runtime = PipelineRuntime {
285 timer: &mut timer,
286 logger: config.logger,
287 colors: &config.colors,
288 config: config.config,
289 executor: executor_ref,
290 executor_arc: Arc::clone(&config.executor),
291 workspace: config.workspace,
292 };
293
294 let result = generate_commit_message(
300 &diff,
301 config.registry,
302 &mut runtime,
303 config.developer_agent,
304 config.template_context,
305 config.workspace,
306 &std::collections::HashMap::new(), )
308 .map_err(|e| anyhow::anyhow!("Failed to generate commit message: {e}"))?;
309
310 if !result.success || result.message.trim().is_empty() {
311 anyhow::bail!("Commit message generation failed");
312 }
313
314 let commit_message = result.message;
315
316 config.logger.success("Commit message generated:");
317 println!();
318 println!(
319 "{}{}{}",
320 config.colors.cyan(),
321 commit_message,
322 config.colors.reset()
323 );
324 println!();
325
326 write_commit_message_file_with_workspace(config.workspace, &commit_message)?;
328
329 config
330 .logger
331 .info("Message saved to .agent/commit-message.txt");
332 config
333 .logger
334 .info("Run 'ralph --apply-commit' to create the commit");
335
336 Ok(())
337}