1use std::env;
2use std::path::{Path, PathBuf};
3
4pub fn project_root(cwd: Option<&str>) -> PathBuf {
9 if let Ok(val) = env::var("CLAUDE_PROJECT_DIR")
10 && !val.is_empty()
11 {
12 return canonicalize_or_raw(&val);
13 }
14 if let Some(c) = cwd.filter(|s| !s.is_empty()) {
15 return canonicalize_or_raw(c);
16 }
17 env::current_dir()
18 .and_then(|p| p.canonicalize())
19 .unwrap_or_else(|_| PathBuf::from("."))
20}
21
22pub fn resolve_file(project_dir: &Path, file_path: &str) -> Option<PathBuf> {
25 let project_dir = project_dir.canonicalize().ok()?;
26
27 let raw = Path::new(file_path);
28 let resolved = if raw.is_absolute() {
29 raw.to_path_buf()
30 } else {
31 project_dir.join(raw)
32 }
33 .canonicalize()
34 .ok()?;
35
36 resolved.starts_with(&project_dir).then_some(resolved)
37}
38
39pub fn read_text(path: &Path) -> Option<String> {
41 std::fs::read_to_string(path).ok()
42}
43
44fn canonicalize_or_raw(path: &str) -> PathBuf {
45 let p = PathBuf::from(path);
46 p.canonicalize().unwrap_or(p)
47}