frigg 0.3.2

Local-first MCP server for code understanding.
Documentation
use std::path::Path;

use crate::domain::PathClass;
use crate::languages::{LanguageCapability, supported_language_for_path};

pub(crate) fn classify_repository_path(relative_path: &str) -> PathClass {
    let normalized = relative_path.trim_start_matches("./");
    let components = normalized
        .split('/')
        .filter(|component| !component.is_empty())
        .collect::<Vec<_>>();
    if components.iter().any(|component| {
        matches!(
            *component,
            "benches" | "bench" | "examples" | "example" | "tests" | "test"
        )
    }) {
        PathClass::Support
    } else if is_roc_platform_source_path(normalized)
        || normalized == "bootstrap/app.php"
        || normalized.starts_with("app/")
        || normalized.starts_with("bootstrap/")
        || normalized.starts_with("routes/")
        || normalized.starts_with("database/migrations/")
        || normalized.starts_with("database/seeders/")
        || normalized.starts_with("database/factories/")
    {
        PathClass::Runtime
    } else if normalized.starts_with("resources/views/") {
        PathClass::Support
    } else if components.contains(&"src") || is_repo_root_supported_source_file(normalized) {
        PathClass::Runtime
    } else {
        PathClass::Project
    }
}

fn is_repo_root_supported_source_file(relative_path: &str) -> bool {
    !relative_path.contains('/')
        && supported_language_for_path(Path::new(relative_path), LanguageCapability::SymbolCorpus)
            .is_some()
}

fn is_roc_platform_source_path(relative_path: &str) -> bool {
    relative_path.ends_with(".roc")
        && (relative_path.starts_with("platform/") || relative_path.contains("/platform/"))
}

pub(crate) fn repository_path_class(relative_path: &str) -> &'static str {
    classify_repository_path(relative_path).as_str()
}

pub(crate) fn repository_path_class_rank(path_class: &str) -> u8 {
    PathClass::from_label(path_class)
        .map(PathClass::rank)
        .unwrap_or(3)
}

#[cfg(test)]
mod tests {
    use crate::domain::PathClass;

    use super::{classify_repository_path, repository_path_class};

    #[test]
    fn repository_path_class_treats_laravel_runtime_surfaces_as_runtime() {
        assert_eq!(
            repository_path_class("app/Livewire/Dashboard.php"),
            "runtime"
        );
        assert_eq!(repository_path_class("bootstrap/app.php"), "runtime");
        assert_eq!(repository_path_class("routes/web.php"), "runtime");
        assert_eq!(
            repository_path_class("database/migrations/2014_10_12_000000_create_users_table.php"),
            "runtime"
        );
        assert_eq!(
            repository_path_class("database/seeders/DatabaseSeeder.php"),
            "runtime"
        );
        assert_eq!(
            repository_path_class("database/factories/UserFactory.php"),
            "runtime"
        );
    }

    #[test]
    fn classify_repository_path_returns_typed_classes() {
        assert_eq!(
            classify_repository_path("crates/cli/src/mcp/server.rs"),
            PathClass::Runtime
        );
        assert_eq!(
            classify_repository_path("crates/cli/examples/server.rs"),
            PathClass::Support
        );
        assert_eq!(classify_repository_path("Cargo.toml"), PathClass::Project);
    }

    #[test]
    fn repository_path_class_treats_laravel_views_and_tests_as_support() {
        assert_eq!(
            repository_path_class("resources/views/auth/login.blade.php"),
            "support"
        );
        assert_eq!(repository_path_class("tests/DuskTestCase.php"), "support");
    }

    #[test]
    fn repository_path_class_treats_repo_root_supported_source_files_as_runtime() {
        assert_eq!(repository_path_class("main.py"), "runtime");
        assert_eq!(repository_path_class("index.tsx"), "runtime");
        assert_eq!(repository_path_class("main.go"), "runtime");
        assert_eq!(repository_path_class("Main.kt"), "runtime");
        assert_eq!(repository_path_class("init.lua"), "runtime");
        assert_eq!(repository_path_class("Main.roc"), "runtime");
        assert_eq!(repository_path_class("config.nims"), "runtime");
    }

    #[test]
    fn repository_path_class_treats_roc_platform_surfaces_as_runtime() {
        assert_eq!(repository_path_class("platform/main.roc"), "runtime");
        assert_eq!(repository_path_class("platform/Arg.roc"), "runtime");
    }
}