wsx-core 0.21.0

Composable project, Git worktree, terminal runtime, hook, and model primitives for wsx.
Documentation
use super::IntegrationTarget;
use std::io;
use std::path::{Path, PathBuf};

fn home() -> io::Result<PathBuf> {
    std::env::var_os("HOME")
        .filter(|v| !v.is_empty())
        .map(PathBuf::from)
        .or_else(dirs::home_dir)
        .ok_or_else(|| io::Error::other("cannot determine home directory"))
}
fn expanded(value: std::ffi::OsString) -> io::Result<PathBuf> {
    let p = PathBuf::from(value);
    let Some(s) = p.to_str() else { return Ok(p) };
    if s == "~" {
        return home();
    }
    if let Some(rest) = s.strip_prefix("~/") {
        return Ok(home()?.join(rest));
    }
    Ok(p)
}
fn env_or(var: &str, default: &[&str]) -> io::Result<PathBuf> {
    if let Some(v) = std::env::var_os(var).filter(|v| !v.is_empty()) {
        return expanded(v);
    }
    let mut p = home()?;
    for part in default {
        p.push(part);
    }
    Ok(p)
}

pub(crate) fn root(target: IntegrationTarget) -> io::Result<PathBuf> {
    Ok(match target {
        IntegrationTarget::Pi => env_or("PI_CODING_AGENT_DIR", &[".pi", "agent"])?,
        IntegrationTarget::Omp => {
            if let Some(v) = std::env::var_os("PI_CODING_AGENT_DIR").filter(|v| !v.is_empty()) {
                expanded(v)?
            } else {
                home()?
                    .join(
                        std::env::var_os("PI_CONFIG_DIR")
                            .filter(|v| !v.is_empty())
                            .unwrap_or_else(|| ".omp".into()),
                    )
                    .join("agent")
            }
        }
        IntegrationTarget::Claude => env_or("CLAUDE_CONFIG_DIR", &[".claude"])?,
        IntegrationTarget::Codex => env_or("CODEX_HOME", &[".codex"])?,
        IntegrationTarget::Copilot => env_or("COPILOT_HOME", &[".copilot"])?,
        IntegrationTarget::Devin => {
            if let Some(v) = std::env::var_os("XDG_CONFIG_HOME").filter(|v| !v.is_empty()) {
                expanded(v)?.join("devin")
            } else {
                home()?.join(".config/devin")
            }
        }
        IntegrationTarget::Droid => home()?.join(".factory"),
        IntegrationTarget::Kimi => env_or("KIMI_CODE_HOME", &[".kimi-code"])?,
        IntegrationTarget::Opencode => home()?.join(".config/opencode"),
        IntegrationTarget::Kilo => home()?.join(".config/kilo"),
        IntegrationTarget::Hermes => env_or("HERMES_HOME", &[".hermes"])?,
        IntegrationTarget::Qodercli => env_or("QODER_CONFIG_DIR", &[".qoder"])?,
        IntegrationTarget::Qwen => env_or("QWEN_HOME", &[".qwen"])?,
        IntegrationTarget::Cursor => env_or("CURSOR_CONFIG_DIR", &[".cursor"])?,
        IntegrationTarget::Mastracode => home()?.join(".mastracode"),
        IntegrationTarget::AntigravityCli => {
            env_or("ANTIGRAVITY_CLI_CONFIG_DIR", &[".gemini", "config"])?
        }
        IntegrationTarget::Grok => {
            if let Some(v) = std::env::var_os("GROK_CONFIG_DIR").filter(|v| !v.is_empty()) {
                expanded(v)?
            } else {
                env_or("GROK_HOME", &[".grok"])?
            }
        }
    })
}

pub(crate) fn asset_path(target: IntegrationTarget) -> io::Result<PathBuf> {
    Ok(asset_path_in(&root(target)?, target))
}

pub(crate) fn asset_path_in(root: &Path, target: IntegrationTarget) -> PathBuf {
    match target {
        IntegrationTarget::Pi => root.join("extensions/wsx-agent-status.ts"),
        IntegrationTarget::Omp => root.join("extensions/wsx-omp-agent-status.ts"),
        IntegrationTarget::Claude => root.join("hooks/wsx-agent-status.sh"),
        IntegrationTarget::Codex => root.join("wsx-agent-status.sh"),
        IntegrationTarget::Copilot => root.join("hooks/wsx-agent-status.sh"),
        IntegrationTarget::Devin => root.join("wsx-agent-status.sh"),
        IntegrationTarget::Droid => root.join("hooks/wsx-agent-status.sh"),
        IntegrationTarget::Kimi => root.join("hooks/wsx-agent-status.sh"),
        IntegrationTarget::Opencode => root.join("plugins/wsx-agent-status.js"),
        IntegrationTarget::Kilo => root.join("plugin/wsx-agent-status.js"),
        IntegrationTarget::Hermes => root.join("plugins/wsx-agent-status/__init__.py"),
        IntegrationTarget::Qodercli => root.join("hooks/wsx-agent-status.sh"),
        IntegrationTarget::Qwen => root.join("hooks/wsx-agent-status.sh"),
        IntegrationTarget::Cursor => root.join("wsx-agent-status.sh"),
        IntegrationTarget::Mastracode => root.join("hooks/wsx-agent-status.sh"),
        IntegrationTarget::AntigravityCli => root.join("hooks/wsx-agent-status.sh"),
        IntegrationTarget::Grok => root.join("hooks/wsx-agent-status.sh"),
    }
}