vv-agent 0.4.1

VectorVein agent runtime, SDK, CLI, tools, and workspace backends
Documentation
use std::path::{Path, PathBuf};

pub(super) fn path_exists(raw_path: &str, workspace: Option<&Path>) -> bool {
    let path = expand_user(raw_path);
    if path.exists() {
        return true;
    }
    if let Some(workspace) = workspace {
        if !path.is_absolute() {
            return workspace.join(&path).exists();
        }
    }
    false
}

pub(super) fn resolve_skill_path(raw_path: &str, workspace: Option<&Path>) -> PathBuf {
    let path = expand_user(raw_path);
    let path = if path.is_absolute() {
        path
    } else if let Some(workspace) = workspace {
        workspace.join(path)
    } else {
        path
    };
    let path = path.canonicalize().unwrap_or(path);
    if path.is_file()
        && path
            .file_name()
            .and_then(|name| name.to_str())
            .is_some_and(|name| name.eq_ignore_ascii_case("skill.md"))
    {
        return path.parent().map(Path::to_path_buf).unwrap_or(path);
    }
    path
}

pub(super) fn relative_location(skill_md: &Path, workspace: Option<&Path>) -> String {
    let normalized_skill_md = skill_md
        .canonicalize()
        .unwrap_or_else(|_| skill_md.to_path_buf());
    if let Some(workspace) = workspace {
        let normalized_workspace = workspace
            .canonicalize()
            .unwrap_or_else(|_| workspace.to_path_buf());
        if let Ok(relative) = normalized_skill_md.strip_prefix(normalized_workspace) {
            return relative.to_string_lossy().replace('\\', "/");
        }
    }
    normalized_skill_md.to_string_lossy().replace('\\', "/")
}

fn expand_user(raw_path: &str) -> PathBuf {
    if let Some(rest) = raw_path.strip_prefix("~/") {
        if let Some(home) = std::env::var_os("HOME") {
            return PathBuf::from(home).join(rest);
        }
    }
    PathBuf::from(raw_path)
}