mars-agents 0.4.8-rc.2

Agent package manager for .agents/ directories
Documentation
// qa-validated: harness-order-settings-audit

use std::collections::HashSet;

const HARNESS_BINARIES: &[(&str, &str)] = &[
    ("claude", "claude"),
    ("codex", "codex"),
    ("opencode", "opencode"),
    ("cursor", "cursor"),
    ("pi", "pi"),
    ("gemini", "gemini"),
];

const ORDERABLE_HARNESSES: &[&str] = &["claude", "codex", "opencode", "cursor", "pi"];

pub fn detect_installed_harnesses() -> HashSet<String> {
    HARNESS_BINARIES
        .iter()
        .filter(|(_, binary)| harness_binary_exists(binary))
        .map(|(name, _)| name.to_string())
        .collect()
}

fn harness_binary_exists(binary: &str) -> bool {
    if which::which(binary).is_ok() {
        return true;
    }

    #[cfg(windows)]
    {
        ["exe", "cmd", "bat"]
            .iter()
            .any(|ext| which::which(format!("{binary}.{ext}")).is_ok())
    }

    #[cfg(not(windows))]
    {
        false
    }
}

const PROVIDER_HARNESS_PREFERENCES: &[(&str, &[&str])] = &[
    ("anthropic", &["claude", "opencode", "gemini"]),
    ("openai", &["codex", "opencode"]),
    ("google", &["gemini", "opencode"]),
    ("meta", &["opencode"]),
    ("mistral", &["opencode"]),
    ("deepseek", &["opencode"]),
    ("cohere", &["opencode"]),
];

fn is_launch_bundle_harness(harness: &str) -> bool {
    ORDERABLE_HARNESSES.contains(&harness)
}

pub fn resolve_harness_for_provider(provider: &str, installed: &HashSet<String>) -> Option<String> {
    let provider_lower = provider.to_lowercase();
    PROVIDER_HARNESS_PREFERENCES
        .iter()
        .find(|(p, _)| *p == provider_lower)
        .and_then(|(_, prefs)| {
            prefs
                .iter()
                .find(|h| installed.contains(**h))
                .map(|h| h.to_string())
        })
}

pub fn harness_candidates_for_provider(provider: &str) -> Vec<String> {
    let provider_lower = provider.to_lowercase();
    PROVIDER_HARNESS_PREFERENCES
        .iter()
        .find(|(p, _)| *p == provider_lower)
        .map(|(_, prefs)| prefs.iter().map(|h| h.to_string()).collect())
        .unwrap_or_default()
}

pub fn preferred_harness_for_provider(provider: &str) -> Option<String> {
    harness_candidates_for_provider(provider).into_iter().next()
}

pub struct HarnessCandidateResolution {
    pub harness: Option<String>,
    pub source: Option<&'static str>,
    pub harness_order_position: Option<usize>,
    pub warnings: Vec<String>,
    pub harness_order_failure: Option<HarnessOrderFailure>,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum HarnessOrderFailure {
    Empty,
    NoneInstalled { valid_candidates: Vec<String> },
}

pub fn resolve_harness_from_candidates(
    provider: Option<&str>,
    settings_harness_order: Option<&[String]>,
    installed: &HashSet<String>,
) -> HarnessCandidateResolution {
    let mut warnings = Vec::new();

    if let Some(order) = settings_harness_order {
        if order.is_empty() {
            return HarnessCandidateResolution {
                harness: None,
                source: None,
                harness_order_position: None,
                warnings,
                harness_order_failure: Some(HarnessOrderFailure::Empty),
            };
        }

        let mut valid_candidates = Vec::new();
        for candidate in order {
            let normalized = candidate.trim().to_lowercase();
            if !ORDERABLE_HARNESSES.contains(&normalized.as_str()) {
                warnings.push(format!(
                    "settings.harness_order contains unrecognized harness `{candidate}`; skipping (valid: {})",
                    ORDERABLE_HARNESSES.join(", ")
                ));
                continue;
            }

            valid_candidates.push(normalized.clone());
            if installed.contains(&normalized) {
                return HarnessCandidateResolution {
                    harness: Some(normalized),
                    source: Some("config-order"),
                    harness_order_position: Some(valid_candidates.len() - 1),
                    warnings,
                    harness_order_failure: None,
                };
            }
        }
        return HarnessCandidateResolution {
            harness: None,
            source: None,
            harness_order_position: None,
            warnings,
            harness_order_failure: (!valid_candidates.is_empty())
                .then_some(HarnessOrderFailure::NoneInstalled { valid_candidates }),
        };
    }

    let harness =
        provider.and_then(|value| resolve_launch_bundle_harness_for_provider(value, installed));
    HarnessCandidateResolution {
        source: harness.as_ref().map(|_| "provider"),
        harness,
        harness_order_position: None,
        warnings,
        harness_order_failure: None,
    }
}

fn resolve_launch_bundle_harness_for_provider(
    provider: &str,
    installed: &HashSet<String>,
) -> Option<String> {
    let provider_lower = provider.to_lowercase();
    PROVIDER_HARNESS_PREFERENCES
        .iter()
        .find(|(p, _)| *p == provider_lower)
        .and_then(|(_, prefs)| {
            prefs
                .iter()
                .find(|h| is_launch_bundle_harness(h) && installed.contains(**h))
                .map(|h| h.to_string())
        })
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn resolve_harness_anthropic_with_claude() {
        let installed: HashSet<String> = ["claude"].iter().map(|s| s.to_string()).collect();
        assert_eq!(
            resolve_harness_for_provider("anthropic", &installed),
            Some("claude".to_string())
        );
    }

    #[test]
    fn resolve_harness_anthropic_falls_back_to_opencode() {
        let installed: HashSet<String> = ["opencode"].iter().map(|s| s.to_string()).collect();
        assert_eq!(
            resolve_harness_for_provider("anthropic", &installed),
            Some("opencode".to_string())
        );
    }

    #[test]
    fn resolve_harness_none_installed() {
        let installed: HashSet<String> = HashSet::new();
        assert_eq!(resolve_harness_for_provider("anthropic", &installed), None);
    }

    #[test]
    fn resolve_harness_unknown_provider() {
        let installed: HashSet<String> = ["claude"].iter().map(|s| s.to_string()).collect();
        assert_eq!(
            resolve_harness_for_provider("unknown-provider", &installed),
            None
        );
    }

    #[test]
    fn resolve_harness_case_insensitive_provider() {
        let installed: HashSet<String> = ["claude"].iter().map(|s| s.to_string()).collect();
        assert_eq!(
            resolve_harness_for_provider("Anthropic", &installed),
            Some("claude".to_string())
        );
    }

    #[test]
    fn candidates_for_known_provider() {
        let candidates = harness_candidates_for_provider("openai");
        assert_eq!(candidates, vec!["codex", "opencode"]);
    }

    #[test]
    fn candidates_for_anthropic_include_gemini() {
        let candidates = harness_candidates_for_provider("anthropic");
        assert_eq!(candidates, vec!["claude", "opencode", "gemini"]);
    }

    #[test]
    fn candidates_for_unknown_provider() {
        let candidates = harness_candidates_for_provider("unknown");
        assert!(candidates.is_empty());
    }

    #[test]
    fn preferred_harness_for_provider_uses_first_candidate() {
        assert_eq!(
            preferred_harness_for_provider("openai"),
            Some("codex".to_string())
        );
        assert_eq!(preferred_harness_for_provider("unknown"), None);
    }

    #[test]
    fn resolve_harness_from_order_selects_first_installed_and_sets_source() {
        let installed: HashSet<String> = ["opencode"].iter().map(|s| s.to_string()).collect();
        let order = vec![
            "pi".to_string(),
            "opencode".to_string(),
            "codex".to_string(),
        ];
        let resolved = resolve_harness_from_candidates(None, Some(&order), &installed);
        assert_eq!(resolved.harness, Some("opencode".to_string()));
        assert_eq!(resolved.source, Some("config-order"));
        assert_eq!(resolved.harness_order_position, Some(1));
        assert!(resolved.warnings.is_empty());
    }

    #[test]
    fn resolve_harness_from_order_warns_for_unrecognized_entries() {
        let installed: HashSet<String> = ["codex"].iter().map(|s| s.to_string()).collect();
        let order = vec!["unknown-harness".to_string(), "codex".to_string()];
        let resolved = resolve_harness_from_candidates(None, Some(&order), &installed);
        assert_eq!(resolved.harness, Some("codex".to_string()));
        assert_eq!(resolved.source, Some("config-order"));
        assert_eq!(resolved.harness_order_position, Some(0));
        assert!(resolved.warnings.iter().any(|warning| {
            warning.contains("settings.harness_order contains unrecognized harness")
        }));
    }

    #[test]
    fn resolve_harness_from_order_all_invalid() {
        let installed: HashSet<String> = ["codex"].iter().map(|s| s.to_string()).collect();
        let order = vec!["bogus".to_string(), "also-bogus".to_string()];
        let resolved = resolve_harness_from_candidates(None, Some(&order), &installed);
        assert_eq!(resolved.harness, None);
        assert_eq!(resolved.source, None);
        assert_eq!(resolved.harness_order_position, None);
        assert_eq!(resolved.harness_order_failure, None);
        assert_eq!(resolved.warnings.len(), 2);
        assert!(resolved.warnings.iter().all(|warning| {
            warning.contains("settings.harness_order contains unrecognized harness")
        }));
    }

    #[test]
    fn resolve_harness_from_order_warns_for_empty_order() {
        let installed: HashSet<String> = ["codex"].iter().map(|s| s.to_string()).collect();
        let order: Vec<String> = Vec::new();
        let resolved = resolve_harness_from_candidates(None, Some(&order), &installed);
        assert_eq!(resolved.harness, None);
        assert_eq!(resolved.source, None);
        assert_eq!(
            resolved.harness_order_failure,
            Some(HarnessOrderFailure::Empty)
        );
        assert!(resolved.warnings.is_empty());
    }

    #[test]
    fn resolve_harness_from_order_warns_when_none_installed() {
        let installed: HashSet<String> = ["claude"].iter().map(|s| s.to_string()).collect();
        let order = vec!["pi".to_string(), "opencode".to_string()];
        let resolved = resolve_harness_from_candidates(None, Some(&order), &installed);
        assert_eq!(resolved.harness, None);
        assert_eq!(resolved.source, None);
        assert_eq!(
            resolved.harness_order_failure,
            Some(HarnessOrderFailure::NoneInstalled {
                valid_candidates: vec!["pi".to_string(), "opencode".to_string()],
            })
        );
        assert!(resolved.warnings.is_empty());
    }

    #[test]
    fn resolve_harness_from_candidates_uses_provider_when_order_unset() {
        let installed: HashSet<String> = ["opencode"].iter().map(|s| s.to_string()).collect();
        let resolved = resolve_harness_from_candidates(Some("openai"), None, &installed);
        assert_eq!(resolved.harness, Some("opencode".to_string()));
        assert_eq!(resolved.source, Some("provider"));
        assert!(resolved.warnings.is_empty());
        assert_eq!(resolved.harness_order_failure, None);
    }

    #[test]
    fn resolve_harness_from_candidates_provider_skips_non_launch_bundle_harnesses() {
        let installed: HashSet<String> = ["gemini"].iter().map(|s| s.to_string()).collect();
        let resolved = resolve_harness_from_candidates(Some("anthropic"), None, &installed);
        assert_eq!(resolved.harness, None);
        assert_eq!(resolved.source, None);
        assert!(resolved.warnings.is_empty());
        assert_eq!(resolved.harness_order_failure, None);
    }
}