mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-09-11 22:53:00 +00:00
fix: resolve @path references relative to workflow file, not CWD
`normalize_logical_path()` silently dropped leading `..` components because `PathBuf::pop()` on an empty buffer is a no-op. For user-global workflows (~/.fabro/workflows/) invoked from an unrelated CWD, the manifest builder produces logical paths with leading `..` segments, but the BundleFileResolver normalized them differently during lookup — stripping the `..` — causing a key mismatch and leaving `@` references unresolved. Preserve `..` when there is no normal component to collapse. Closes #175 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
cb0c39ee91
commit
c7851f4e08
2 changed files with 69 additions and 2 deletions
|
|
@ -362,7 +362,11 @@ fn normalize_logical_path(current_dir: &Path, reference: &str) -> Option<PathBuf
|
|||
Component::CurDir => {}
|
||||
Component::Normal(part) => normalized.push(part),
|
||||
Component::ParentDir => {
|
||||
normalized.pop();
|
||||
if normalized.file_name().is_some() {
|
||||
normalized.pop();
|
||||
} else {
|
||||
normalized.push("..");
|
||||
}
|
||||
}
|
||||
Component::RootDir | Component::Prefix(_) => return None,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,7 +50,16 @@ pub(crate) fn normalize_logical_path(current_dir: &Path, reference: &str) -> Opt
|
|||
Component::CurDir => {}
|
||||
Component::Normal(part) => normalized.push(part),
|
||||
Component::ParentDir => {
|
||||
normalized.pop();
|
||||
// If the last component is a normal name, collapse it.
|
||||
// Otherwise (empty path or trailing `..`), preserve the
|
||||
// `..` so that leading parent-dir segments survive — these
|
||||
// occur when a bundled workflow lives outside the CWD
|
||||
// (e.g. user-global workflows under ~/.fabro/).
|
||||
if normalized.file_name().is_some() {
|
||||
normalized.pop();
|
||||
} else {
|
||||
normalized.push("..");
|
||||
}
|
||||
}
|
||||
Component::RootDir | Component::Prefix(_) => return None,
|
||||
}
|
||||
|
|
@ -150,4 +159,58 @@ mod tests {
|
|||
let resolver = BundleFileResolver::new(HashMap::new());
|
||||
assert!(resolver.resolve(Path::new("."), "missing.md").is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn normalize_preserves_leading_parent_dir() {
|
||||
// When a bundled workflow lives outside the CWD (e.g. ~/.fabro/),
|
||||
// the manifest builder produces logical paths with leading `..`
|
||||
// segments. The normalizer must preserve these so that the lookup
|
||||
// key matches the stored key.
|
||||
assert_eq!(
|
||||
normalize_logical_path(
|
||||
Path::new("../.fabro/workflows/demo"),
|
||||
"prompts/hello.md"
|
||||
),
|
||||
Some(PathBuf::from(
|
||||
"../.fabro/workflows/demo/prompts/hello.md"
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn normalize_preserves_multiple_leading_parent_dirs() {
|
||||
assert_eq!(
|
||||
normalize_logical_path(Path::new("../../shared/workflows"), "file.md"),
|
||||
Some(PathBuf::from("../../shared/workflows/file.md"))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn normalize_collapses_mid_path_parent_dir() {
|
||||
// ../foo/../bar should normalize to ../bar
|
||||
assert_eq!(
|
||||
normalize_logical_path(Path::new("../foo"), "../bar/file.md"),
|
||||
Some(PathBuf::from("../bar/file.md"))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bundle_resolver_resolves_outside_cwd_paths() {
|
||||
// Reproduces the user-global workflow scenario from issue #175:
|
||||
// files are keyed with leading `..` because the workflow lives
|
||||
// outside the CWD.
|
||||
let resolver = BundleFileResolver::new(HashMap::from([(
|
||||
PathBuf::from("../.fabro/workflows/demo/prompts/hello.md"),
|
||||
"prompt content".to_string(),
|
||||
)]));
|
||||
|
||||
let resolved = resolver
|
||||
.resolve(
|
||||
Path::new("../.fabro/workflows/demo"),
|
||||
"prompts/hello.md",
|
||||
)
|
||||
.expect("file should resolve for out-of-CWD workflow");
|
||||
|
||||
assert_eq!(resolved.content, "prompt content");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue