ralph-coder 0.2.1

An agentic code generation CLI powered by multiple LLM backends
Documentation
/// GitHub CLI (`gh`) integration.
///
/// All functions require `gh` to be installed and authenticated.
/// Use `gh_available()` to gate before calling anything else.
use std::path::Path;

/// Returns true when `gh` is installed and the user is authenticated.
pub fn gh_available() -> bool {
    std::process::Command::new("gh")
        .args(["auth", "status"])
        .stdout(std::process::Stdio::null())
        .stderr(std::process::Stdio::null())
        .status()
        .map(|s| s.success())
        .unwrap_or(false)
}

/// Create a GitHub PR for the current branch.
///
/// `title` and `body` are required.  Pass `draft = true` for a draft PR.
/// `base` overrides the target branch (default: repo default branch).
pub async fn create_pr(
    title: &str,
    body: &str,
    draft: bool,
    base: Option<&str>,
    workspace: &Path,
) -> crate::errors::Result<String> {
    let mut cmd = tokio::process::Command::new("gh");
    cmd.args(["pr", "create", "--title", title, "--body", body]);
    if draft {
        cmd.arg("--draft");
    }
    if let Some(b) = base {
        cmd.args(["--base", b]);
    }
    cmd.current_dir(workspace);

    let out = cmd
        .output()
        .await
        .map_err(|e| crate::errors::RalphError::ToolFailed {
            tool: "create_pr".to_string(),
            message: e.to_string(),
        })?;

    if out.status.success() {
        Ok(String::from_utf8_lossy(&out.stdout).trim().to_string())
    } else {
        Err(crate::errors::RalphError::ToolFailed {
            tool: "create_pr".to_string(),
            message: String::from_utf8_lossy(&out.stderr).trim().to_string(),
        })
    }
}

/// Return a summary of recent CI runs for the current (or specified) branch.
pub async fn get_ci_status(
    branch: Option<&str>,
    workspace: &Path,
) -> crate::errors::Result<String> {
    let mut cmd = tokio::process::Command::new("gh");
    cmd.args(["run", "list", "--limit", "5"]);
    if let Some(b) = branch {
        cmd.args(["--branch", b]);
    }
    cmd.current_dir(workspace);

    let out = cmd
        .output()
        .await
        .map_err(|e| crate::errors::RalphError::ToolFailed {
            tool: "get_ci_status".to_string(),
            message: e.to_string(),
        })?;

    if !out.status.success() {
        return Err(crate::errors::RalphError::ToolFailed {
            tool: "get_ci_status".to_string(),
            message: String::from_utf8_lossy(&out.stderr).trim().to_string(),
        });
    }

    let text = String::from_utf8_lossy(&out.stdout).to_string();
    if text.trim().is_empty() {
        Ok("No recent CI runs found for this branch.".to_string())
    } else {
        Ok(text)
    }
}