mirror of
https://github.com/herdrdev/herdr.git
synced 2026-09-22 00:01:06 +00:00
157 lines
4.6 KiB
Rust
157 lines
4.6 KiB
Rust
use std::io;
|
|
use std::path::PathBuf;
|
|
#[cfg(test)]
|
|
use std::sync::{Mutex, MutexGuard, OnceLock};
|
|
|
|
use portable_pty::CommandBuilder;
|
|
|
|
pub(crate) const HERDR_PANE_ID_ENV_VAR: &str = "HERDR_PANE_ID";
|
|
pub(crate) const HERDR_TAB_ID_ENV_VAR: &str = "HERDR_TAB_ID";
|
|
pub(crate) const HERDR_WORKSPACE_ID_ENV_VAR: &str = "HERDR_WORKSPACE_ID";
|
|
|
|
pub(crate) const PI_CODING_AGENT_DIR_ENV_VAR: &str = "PI_CODING_AGENT_DIR";
|
|
pub(crate) const CLAUDE_CONFIG_DIR_ENV_VAR: &str = "CLAUDE_CONFIG_DIR";
|
|
pub(crate) const CODEX_HOME_ENV_VAR: &str = "CODEX_HOME";
|
|
pub(crate) const KIMI_CODE_HOME_ENV_VAR: &str = "KIMI_CODE_HOME";
|
|
pub(crate) const COPILOT_HOME_ENV_VAR: &str = "COPILOT_HOME";
|
|
pub(crate) const QODERCLI_CONFIG_DIR_ENV_VAR: &str = "QODER_CONFIG_DIR";
|
|
pub(crate) const CURSOR_CONFIG_DIR_ENV_VAR: &str = "CURSOR_CONFIG_DIR";
|
|
|
|
pub(crate) fn apply_pane_base_env(cmd: &mut CommandBuilder) {
|
|
cmd.env(crate::api::SOCKET_PATH_ENV_VAR, crate::api::socket_path());
|
|
}
|
|
|
|
pub(crate) fn pi_extension_dir() -> io::Result<PathBuf> {
|
|
Ok(
|
|
config_dir_from_env_or_home(PI_CODING_AGENT_DIR_ENV_VAR, &[".pi", "agent"])?
|
|
.join("extensions"),
|
|
)
|
|
}
|
|
|
|
pub(crate) fn omp_extension_dir() -> io::Result<PathBuf> {
|
|
Ok(
|
|
config_dir_from_env_or_home(PI_CODING_AGENT_DIR_ENV_VAR, &[".omp", "agent"])?
|
|
.join("extensions"),
|
|
)
|
|
}
|
|
|
|
pub(crate) fn claude_dir() -> io::Result<PathBuf> {
|
|
config_dir_from_env_or_home(CLAUDE_CONFIG_DIR_ENV_VAR, &[".claude"])
|
|
}
|
|
|
|
pub(crate) fn codex_dir() -> io::Result<PathBuf> {
|
|
config_dir_from_env_or_home(CODEX_HOME_ENV_VAR, &[".codex"])
|
|
}
|
|
|
|
pub(crate) fn kimi_dir() -> io::Result<PathBuf> {
|
|
config_dir_from_env_or_home(KIMI_CODE_HOME_ENV_VAR, &[".kimi-code"])
|
|
}
|
|
|
|
pub(crate) fn copilot_dir() -> io::Result<PathBuf> {
|
|
config_dir_from_env_or_home(COPILOT_HOME_ENV_VAR, &[".copilot"])
|
|
}
|
|
|
|
pub(crate) fn devin_dir() -> io::Result<PathBuf> {
|
|
if let Some(value) = std::env::var_os("XDG_CONFIG_HOME").filter(|value| !value.is_empty()) {
|
|
return expand_tilde_path(PathBuf::from(value)).map(|path| path.join("devin"));
|
|
}
|
|
|
|
Ok(home_dir()?.join(".config").join("devin"))
|
|
}
|
|
|
|
pub(crate) fn droid_dir() -> io::Result<PathBuf> {
|
|
Ok(home_dir()?.join(".factory"))
|
|
}
|
|
|
|
pub(crate) fn config_dir_from_env_or_home(
|
|
env_var: &str,
|
|
home_relative_segments: &[&str],
|
|
) -> io::Result<PathBuf> {
|
|
if let Some(value) = std::env::var_os(env_var).filter(|value| !value.is_empty()) {
|
|
return expand_tilde_path(PathBuf::from(value));
|
|
}
|
|
|
|
let mut path = home_dir()?;
|
|
for segment in home_relative_segments {
|
|
path.push(segment);
|
|
}
|
|
Ok(path)
|
|
}
|
|
|
|
pub(crate) fn expand_tilde_path(path: PathBuf) -> io::Result<PathBuf> {
|
|
let Some(raw) = path.to_str() else {
|
|
return Ok(path);
|
|
};
|
|
|
|
if raw == "~" {
|
|
return home_dir();
|
|
}
|
|
|
|
if let Some(rest) = raw
|
|
.strip_prefix("~/")
|
|
.or_else(|| raw.strip_prefix("~\\"))
|
|
.or_else(|| raw.strip_prefix('~'))
|
|
{
|
|
return Ok(home_dir()?.join(rest));
|
|
}
|
|
|
|
Ok(path)
|
|
}
|
|
|
|
pub(crate) fn opencode_dir() -> io::Result<PathBuf> {
|
|
Ok(home_dir()?.join(".config/opencode"))
|
|
}
|
|
|
|
pub(crate) fn kilo_dir() -> io::Result<PathBuf> {
|
|
Ok(home_dir()?.join(".config/kilo"))
|
|
}
|
|
|
|
pub(crate) fn hermes_dir() -> io::Result<PathBuf> {
|
|
Ok(home_dir()?.join(".hermes"))
|
|
}
|
|
|
|
pub(crate) fn hermes_plugin_dir() -> io::Result<PathBuf> {
|
|
Ok(hermes_dir()?
|
|
.join("plugins")
|
|
.join(super::HERMES_PLUGIN_INSTALL_NAME))
|
|
}
|
|
|
|
pub(crate) fn qodercli_dir() -> io::Result<PathBuf> {
|
|
config_dir_from_env_or_home(QODERCLI_CONFIG_DIR_ENV_VAR, &[".qoder"])
|
|
}
|
|
|
|
pub(crate) fn cursor_dir() -> io::Result<PathBuf> {
|
|
config_dir_from_env_or_home(CURSOR_CONFIG_DIR_ENV_VAR, &[".cursor"])
|
|
}
|
|
|
|
pub(crate) fn home_dir() -> io::Result<PathBuf> {
|
|
if let Some(home) = std::env::var_os("HOME").filter(|value| !value.is_empty()) {
|
|
return Ok(PathBuf::from(home));
|
|
}
|
|
|
|
#[cfg(windows)]
|
|
{
|
|
if let Some(profile) = std::env::var_os("USERPROFILE").filter(|value| !value.is_empty()) {
|
|
return Ok(PathBuf::from(profile));
|
|
}
|
|
if let (Some(drive), Some(path)) = (
|
|
std::env::var_os("HOMEDRIVE").filter(|value| !value.is_empty()),
|
|
std::env::var_os("HOMEPATH").filter(|value| !value.is_empty()),
|
|
) {
|
|
let mut home = PathBuf::from(drive);
|
|
home.push(path);
|
|
return Ok(home);
|
|
}
|
|
}
|
|
|
|
Err(io::Error::other(
|
|
"home directory is not set; cannot locate home directory",
|
|
))
|
|
}
|
|
|
|
#[cfg(test)]
|
|
pub(crate) fn integration_env_lock() -> MutexGuard<'static, ()> {
|
|
static LOCK: OnceLock<Mutex<()>> = OnceLock::new();
|
|
LOCK.get_or_init(|| Mutex::new(())).lock().unwrap()
|
|
}
|