Files
tkmind_go/crates/goose-mcp/src/developer/paths.rs
T
2026-02-11 17:43:18 +00:00

116 lines
3.2 KiB
Rust

use crate::subprocess::SubprocessExt;
use anyhow::Result;
use std::env;
use std::path::PathBuf;
use tokio::process::Command;
use tokio::sync::OnceCell;
static SHELL_PATH_DIRS: OnceCell<Result<Vec<PathBuf>, anyhow::Error>> = OnceCell::const_new();
pub async fn get_shell_path_dirs() -> Result<&'static Vec<PathBuf>> {
let result = SHELL_PATH_DIRS
.get_or_init(|| async {
get_shell_path_async()
.await
.map(|path| env::split_paths(&path).collect())
})
.await;
match result {
Ok(dirs) => Ok(dirs),
Err(e) => Err(anyhow::anyhow!(
"Failed to get shell PATH directories: {}",
e
)),
}
}
async fn get_shell_path_async() -> Result<String> {
let shell = env::var("SHELL").unwrap_or_else(|_| {
if cfg!(windows) {
"cmd".to_string()
} else {
"/bin/bash".to_string()
}
});
if cfg!(windows) {
get_windows_path_async(&shell).await
} else {
get_unix_path_async(&shell).await
}
.or_else(|e| {
tracing::warn!(
"Failed to get PATH from shell ({}), falling back to current PATH",
e
);
env::var("PATH").map_err(|_| anyhow::anyhow!("No PATH variable available"))
})
}
async fn get_unix_path_async(shell: &str) -> Result<String> {
let output = Command::new(shell)
.args(["-l", "-i", "-c", "echo $PATH"])
.output()
.await
.map_err(|e| anyhow::anyhow!("Failed to execute shell command: {}", e))?;
if !output.status.success() {
let stderr = String::from_utf8_lossy(&output.stderr);
return Err(anyhow::anyhow!("Shell command failed: {}", stderr));
}
let path = String::from_utf8(output.stdout)
.map_err(|e| anyhow::anyhow!("Invalid UTF-8 in shell output: {}", e))?
.trim()
.to_string();
if path.is_empty() {
return Err(anyhow::anyhow!("Shell returned empty PATH"));
}
Ok(path)
}
async fn get_windows_path_async(shell: &str) -> Result<String> {
let shell_name = std::path::Path::new(shell)
.file_stem()
.and_then(|s| s.to_str())
.unwrap_or("cmd");
let output = match shell_name {
"pwsh" | "powershell" => {
Command::new(shell)
.args(["-NoLogo", "-Command", "$env:PATH"])
.set_no_window()
.output()
.await
}
_ => {
Command::new(shell)
.args(["/c", "echo %PATH%"])
.set_no_window()
.output()
.await
}
};
let output = output.map_err(|e| anyhow::anyhow!("Failed to execute shell command: {}", e))?;
if !output.status.success() {
let stderr = String::from_utf8_lossy(&output.stderr);
return Err(anyhow::anyhow!("Shell command failed: {}", stderr));
}
let path = String::from_utf8(output.stdout)
.map_err(|e| anyhow::anyhow!("Invalid UTF-8 in shell output: {}", e))?
.trim()
.to_string();
if path.is_empty() {
return Err(anyhow::anyhow!("Shell returned empty PATH"));
}
Ok(path)
}