paceflow 0.2.4

Local-first CLI that turns AI coding session history and git metadata into engineering analytics.
Documentation
const LOCK_FILE_BASENAMES: &[&str] = &[
    "package-lock.json",
    "yarn.lock",
    "pnpm-lock.yaml",
    "npm-shrinkwrap.json",
    "Cargo.lock",
    "Gemfile.lock",
    "poetry.lock",
    "Pipfile.lock",
    "composer.lock",
    "go.sum",
    "mix.lock",
];

pub fn is_lock_file(rel_path: &str) -> bool {
    let basename = rel_path
        .rsplit(['/', '\\'])
        .next()
        .unwrap_or(rel_path)
        .trim();
    if basename.is_empty() {
        return false;
    }
    LOCK_FILE_BASENAMES
        .iter()
        .any(|name| basename.eq_ignore_ascii_case(name))
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn detects_each_known_lock_file_basename() {
        for name in LOCK_FILE_BASENAMES {
            assert!(is_lock_file(name), "expected `{name}` to be a lock file");
        }
    }

    #[test]
    fn detects_lock_files_in_nested_paths() {
        assert!(is_lock_file("frontend/package-lock.json"));
        assert!(is_lock_file("services/api/Cargo.lock"));
        assert!(is_lock_file("a/b/c/d/pnpm-lock.yaml"));
        assert!(is_lock_file("backend\\Pipfile.lock"));
    }

    #[test]
    fn matches_case_insensitively() {
        assert!(is_lock_file("CARGO.LOCK"));
        assert!(is_lock_file("Package-Lock.JSON"));
        assert!(is_lock_file("frontend/PNPM-LOCK.YAML"));
    }

    #[test]
    fn does_not_match_unrelated_paths() {
        assert!(!is_lock_file("src/main.rs"));
        assert!(!is_lock_file("unlock.rs"));
        assert!(!is_lock_file("lockfile_utils.go"));
        assert!(!is_lock_file("vendor/package-lock.json.bak"));
        assert!(!is_lock_file("docs/Cargo.lock.md"));
        assert!(!is_lock_file(""));
    }
}