stynx-code 3.6.2

stynx-code — interactive AI coding assistant
pub fn git_branch() -> Option<String> {
    let mut dir = std::env::current_dir().ok()?;
    loop {
        let head = dir.join(".git").join("HEAD");
        if head.exists() {
            let content = std::fs::read_to_string(head).ok()?;
            let content = content.trim();
            if let Some(branch) = content.strip_prefix("ref: refs/heads/") {
                return Some(branch.to_string());
            }
            if content.len() >= 7 {
                return Some(format!("({})", &content[..7]));
            }
            return None;
        }
        match dir.parent() {
            Some(p) => dir = p.to_path_buf(),
            None => return None,
        }
    }
}

pub fn is_git_repo(cwd: &str) -> bool {
    std::process::Command::new("git")
        .args(["rev-parse", "--is-inside-work-tree"])
        .current_dir(cwd)
        .output()
        .map(|o| o.status.success())
        .unwrap_or(false)
}

pub fn git_status_snapshot(cwd: &str) -> Option<String> {
    let run = |args: &[&str]| -> String {
        std::process::Command::new("git")
            .args(args)
            .current_dir(cwd)
            .output()
            .map(|o| String::from_utf8_lossy(&o.stdout).trim().to_string())
            .unwrap_or_default()
    };

    let branch = run(&["rev-parse", "--abbrev-ref", "HEAD"]);
    if branch.is_empty() {
        return None;
    }

    let main_branch = ["main", "master", "develop"]
        .iter()
        .find(|&&b| {
            std::process::Command::new("git")
                .args(["show-ref", "--verify", &format!("refs/heads/{b}")])
                .current_dir(cwd)
                .output()
                .map(|o| o.status.success())
                .unwrap_or(false)
        })
        .map(|s| s.to_string())
        .unwrap_or_else(|| branch.clone());

    let status = {
        let raw = run(&["--no-optional-locks", "status", "--short"]);
        if raw.len() > 2000 {
            format!("{}\n... (truncated)", &raw[..2000])
        } else if raw.is_empty() {
            "(clean)".to_string()
        } else {
            raw
        }
    };

    let log = run(&["--no-optional-locks", "log", "--oneline", "-n", "5"]);
    let user = run(&["config", "user.name"]);

    let mut parts = vec![
        "This is the git status at the start of the conversation. Note that this status is a snapshot in time, and will not update during the conversation.".to_string(),
        format!("Current branch: {branch}"),
        format!("Main branch (you will usually use this for PRs): {main_branch}"),
    ];
    if !user.is_empty() {
        parts.push(format!("Git user: {user}"));
    }
    parts.push(format!("Status:\n{status}"));
    if !log.is_empty() {
        parts.push(format!("Recent commits:\n{log}"));
    }

    Some(parts.join("\n\n"))
}