use std::path::{Path, PathBuf};
use std::sync::atomic::{AtomicBool, Ordering};
use crate::tools::path::{expand, home_dir};
pub const APP_NAME: &str = "yapi";
pub const PROJECT_DIR: &str = ".yapi";
pub const AGENT_DIR_ENV: &str = "YAPI_CODING_AGENT_DIR";
pub const SESSION_DIR_ENV: &str = "YAPI_CODING_AGENT_SESSION_DIR";
static OFFLINE: AtomicBool = AtomicBool::new(false);
pub const PROJECT_ONLY_ENV: &str = "YAPI_PROJECT_ONLY";
static PROJECT_ONLY: AtomicBool = AtomicBool::new(false);
pub fn set_project_only() {
PROJECT_ONLY.store(true, Ordering::Relaxed);
}
pub fn project_only() -> bool {
PROJECT_ONLY.load(Ordering::Relaxed)
}
const OFFLINE_ENV: [&str; 2] = ["PI_OFFLINE", "PI_SKIP_VERSION_CHECK"];
pub fn set_offline() {
OFFLINE.store(true, Ordering::Relaxed);
}
pub fn env_var(name: &str) -> Option<String> {
if OFFLINE.load(Ordering::Relaxed) && OFFLINE_ENV.contains(&name) {
return Some("1".to_owned());
}
std::env::var(name).ok()
}
pub fn env_set(name: &str) -> bool {
env_var(name).is_some_and(|value| !value.is_empty())
}
pub fn child_env() -> Vec<(&'static str, String)> {
let mut env = vec![
("PI_CODING_AGENT", "true".to_owned()),
("AI_AGENT", APP_NAME.to_owned()),
];
if OFFLINE.load(Ordering::Relaxed) {
env.extend(OFFLINE_ENV.map(|name| (name, "1".to_owned())));
}
if project_only() {
env.push((PROJECT_ONLY_ENV, "1".to_owned()));
}
env.extend(yapi_ai::http::proxy_env());
env
}
pub fn agent_dir() -> PathBuf {
match std::env::var(AGENT_DIR_ENV) {
Ok(dir) if !dir.is_empty() => PathBuf::from(expand(&dir)),
_ => home_dir().join(PROJECT_DIR).join("agent"),
}
}
pub fn default_session_dir(agent_dir: &Path, cwd: &Path) -> PathBuf {
let base = std::env::current_dir().unwrap_or_default();
let resolve = |path: &Path| crate::tools::path::resolve_lexically(&base, path);
let cwd = resolve(cwd);
let text = cwd.to_string_lossy();
let trimmed = text.trim_start_matches(['/', '\\']);
let safe: String = trimmed
.chars()
.map(|c| {
if matches!(c, '/' | '\\' | ':') {
'-'
} else {
c
}
})
.collect();
resolve(agent_dir)
.join("sessions")
.join(format!("--{safe}--"))
}
pub fn env_flag(name: &str) -> bool {
env_var(name).is_some_and(|value| {
value == "1" || matches!(value.to_lowercase().as_str(), "true" | "yes")
})
}
pub fn docs_dir(agent_dir: &Path) -> PathBuf {
agent_dir.join("docs")
}
pub fn bin_dir(agent_dir: &Path) -> PathBuf {
agent_dir.join("bin")
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn session_dirs_match_pi() {
assert_eq!(
default_session_dir(Path::new("/h/.yapi/agent"), Path::new("/home/user/project")),
PathBuf::from("/h/.yapi/agent/sessions/--home-user-project--")
);
}
}