1use std::path::Path;
6
7pub fn gh_available() -> bool {
9 std::process::Command::new("gh")
10 .args(["auth", "status"])
11 .stdout(std::process::Stdio::null())
12 .stderr(std::process::Stdio::null())
13 .status()
14 .map(|s| s.success())
15 .unwrap_or(false)
16}
17
18pub async fn create_pr(
23 title: &str,
24 body: &str,
25 draft: bool,
26 base: Option<&str>,
27 workspace: &Path,
28) -> crate::errors::Result<String> {
29 let mut cmd = tokio::process::Command::new("gh");
30 cmd.args(["pr", "create", "--title", title, "--body", body]);
31 if draft {
32 cmd.arg("--draft");
33 }
34 if let Some(b) = base {
35 cmd.args(["--base", b]);
36 }
37 cmd.current_dir(workspace);
38
39 let out = cmd
40 .output()
41 .await
42 .map_err(|e| crate::errors::RalphError::ToolFailed {
43 tool: "create_pr".to_string(),
44 message: e.to_string(),
45 })?;
46
47 if out.status.success() {
48 Ok(String::from_utf8_lossy(&out.stdout).trim().to_string())
49 } else {
50 Err(crate::errors::RalphError::ToolFailed {
51 tool: "create_pr".to_string(),
52 message: String::from_utf8_lossy(&out.stderr).trim().to_string(),
53 })
54 }
55}
56
57pub async fn get_ci_status(
59 branch: Option<&str>,
60 workspace: &Path,
61) -> crate::errors::Result<String> {
62 let mut cmd = tokio::process::Command::new("gh");
63 cmd.args(["run", "list", "--limit", "5"]);
64 if let Some(b) = branch {
65 cmd.args(["--branch", b]);
66 }
67 cmd.current_dir(workspace);
68
69 let out = cmd
70 .output()
71 .await
72 .map_err(|e| crate::errors::RalphError::ToolFailed {
73 tool: "get_ci_status".to_string(),
74 message: e.to_string(),
75 })?;
76
77 if !out.status.success() {
78 return Err(crate::errors::RalphError::ToolFailed {
79 tool: "get_ci_status".to_string(),
80 message: String::from_utf8_lossy(&out.stderr).trim().to_string(),
81 });
82 }
83
84 let text = String::from_utf8_lossy(&out.stdout).to_string();
85 if text.trim().is_empty() {
86 Ok("No recent CI runs found for this branch.".to_string())
87 } else {
88 Ok(text)
89 }
90}