devist 0.6.0

Project bootstrap CLI for AI-assisted development. Spin up new projects from templates, manage backends, and keep your codebase comprehensible.
use anyhow::{anyhow, Result};
use std::path::Path;
use std::process::{Command, Stdio};

/// Run a shell command in the given directory, inheriting stdin/stdout/stderr.
/// Returns Ok if the command succeeded, Err otherwise.
pub fn run_in(dir: &Path, command: &str) -> Result<()> {
    let status = build_shell_command(command)
        .current_dir(dir)
        .stdin(Stdio::inherit())
        .stdout(Stdio::inherit())
        .stderr(Stdio::inherit())
        .status()
        .map_err(|e| anyhow!("Failed to spawn `{}`: {}", command, e))?;

    if !status.success() {
        return Err(anyhow!(
            "Command failed (exit code {}): {}",
            status.code().unwrap_or(-1),
            command
        ));
    }
    Ok(())
}

/// Run a shell command and capture its output (no inheritance).
/// Used for quiet operations like `supabase stop`.
pub fn run_in_quiet(dir: &Path, command: &str) -> Result<String> {
    let output = build_shell_command(command)
        .current_dir(dir)
        .output()
        .map_err(|e| anyhow!("Failed to spawn `{}`: {}", command, e))?;

    if !output.status.success() {
        let stderr = String::from_utf8_lossy(&output.stderr);
        return Err(anyhow!("Command failed: {}\n{}", command, stderr.trim()));
    }

    Ok(String::from_utf8_lossy(&output.stdout).to_string())
}

fn build_shell_command(command: &str) -> Command {
    let mut cmd = if cfg!(target_os = "windows") {
        let mut c = Command::new("cmd");
        c.args(["/C", command]);
        c
    } else {
        let mut c = Command::new("sh");
        c.args(["-c", command]);
        c
    };
    // Make sure subprocesses inherit a sane PATH
    if let Ok(path) = std::env::var("PATH") {
        cmd.env("PATH", path);
    }
    cmd
}