Skip to content

Commit 4c1dcae

Browse files
leogrpoiana
authored andcommitted
fix(plugin): canonicalize missing working directories
Resolve the cwd through its nearest existing ancestor, just like target file paths. This keeps symlinked path namespaces consistent when the cwd does not exist yet, including macOS /tmp resolving to /private/tmp, and prevents inside-cwd writes from being misclassified as outside. Signed-off-by: Leonardo Grasso <me@leonardograsso.com>
1 parent ffd2395 commit 4c1dcae

1 file changed

Lines changed: 26 additions & 3 deletions

File tree

  • plugins/coding-agents-plugin/src

plugins/coding-agents-plugin/src/event.rs

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -474,13 +474,15 @@ fn normalize_separators(path: String) -> String {
474474
}
475475
}
476476

477-
/// Resolve a single path: canonicalize if possible, otherwise lexically normalize.
477+
/// Resolve a single path, including through its nearest existing ancestor.
478478
fn resolve_path(raw: &str) -> String {
479479
if raw.is_empty() {
480480
return String::new();
481481
}
482-
// Try filesystem canonicalization first (resolves symlinks).
483-
if let Ok(resolved) = std::fs::canonicalize(raw) {
482+
// The cwd itself may not exist yet. Resolve any existing symlinked
483+
// ancestor so cwd and file paths use the same canonical namespace (for
484+
// example, /tmp and /private/tmp on macOS).
485+
if let Some(resolved) = canonicalize_allow_missing(Path::new(raw)) {
484486
return normalize_separators(resolved.to_string_lossy().into_owned());
485487
}
486488
// Fallback: lexical normalization only.
@@ -818,6 +820,27 @@ mod tests {
818820
assert_eq!(resolve_path(""), "");
819821
}
820822

823+
#[cfg(unix)]
824+
#[test]
825+
fn resolve_path_canonicalizes_symlinked_ancestor_for_missing_suffix() {
826+
use std::os::unix::fs::symlink;
827+
828+
let root = std::env::temp_dir().join(format!(
829+
"prempti-resolve-cwd-symlink-{}",
830+
std::process::id()
831+
));
832+
let target = root.join("target");
833+
let link = root.join("link");
834+
let _ = std::fs::remove_dir_all(&root);
835+
std::fs::create_dir_all(&target).unwrap();
836+
symlink(&target, &link).unwrap();
837+
838+
let resolved = resolve_path(&link.join("missing/project").to_string_lossy());
839+
assert_eq!(PathBuf::from(resolved), target.join("missing/project"));
840+
841+
std::fs::remove_dir_all(&root).unwrap();
842+
}
843+
821844
#[test]
822845
fn resolve_file_path_joins_relative_to_cwd() {
823846
// Use a non-existent cwd so we exercise the lexical path.

0 commit comments

Comments
 (0)