Skip to content

Bug: get_module_summary returns files=0/functions=0/lines=0 on Windows (path_matches only checks / separator) #5

@MuBeiGe

Description

@MuBeiGe

Bug Report

Summary

get_module_summary returns files=0, functions=0, classes=0, lines=0 on Windows for any directory. external_deps incorrectly returns all workspace-wide external dependencies instead of module-scoped ones.

Environment

  • OS: Windows 10/11
  • Package: @astudioplus/codegraph-mcp 0.18.2–0.18.4
  • Graph size: 65,556 nodes, 2,615 CodeFiles

Root Cause

File: crates/codegraph-server/src/domain/module_summary.rs, function path_matches() (line ~217):

fn path_matches(path: &str, prefix: &str) -> bool {
    if prefix.is_empty() { return true; }
    if path == prefix { return true; }
    path.starts_with(prefix) && path[prefix.len()..].starts_with('/')  // <-- BUG
}

On Windows, the graph stores paths with backslash separators (via file_path.display().to_string()):

  • path = "C:\Users\...\agent\prompt_builder.py"
  • prefix = "C:\Users\...\agent"

path.starts_with(prefix) is true, but path[prefix.len()..] starts with \ not /, so the function returns false. Every CodeFile/Function/Class node is skipped, resulting in all-zero counts.

I verified this with a local Rust reproduction:

path_matches_original("C:\Users\...\agent\prompt_builder.py", "C:\Users\...\agent") => false
path_matches_fixed("C:\Users\...\agent\prompt_builder.py", "C:\Users\...\agent")    => true

Proposed Fix

Normalise both sides to forward slashes before comparing:

#[inline]
fn path_matches(path: &str, prefix: &str) -> bool {
    if prefix.is_empty() {
        return true;
    }
    let norm_path: String = path.replace('\\', "/");
    let norm_prefix: String = prefix.replace('\\', "/");
    if norm_path == norm_prefix {
        return true;
    }
    norm_path.starts_with(norm_prefix.as_str())
        && norm_path[norm_prefix.len()..].starts_with('/')
}

With corresponding Windows test cases:

#[test]
fn test_path_matches_windows_backslash() {
    assert!(path_matches(
        "C:\\Users\\me\\project\\src\\main.rs",
        "C:/Users/me/project/src"
    ));
}

#[test]
fn test_path_matches_windows_backslash_prefix() {
    assert!(path_matches(
        "C:\\Users\\me\\project\\src\\main.rs",
        "C:\\Users\\me\\project\\src"
    ));
}

#[test]
fn test_path_matches_windows_no_partial_dir() {
    assert!(!path_matches(
        "C:\\Users\\me\\project\\src_foo\\main.rs",
        "C:/Users/me/project/src"
    ));
}

Additional Issue: trim_end_matches('/') doesn't trim \

Line 64: let prefix = directory.trim_end_matches('/');

This also needs to trim backslash on Windows. Suggested fix:

let prefix = directory.trim_end_matches(|c| c == '/' || c == '\\');

Steps to Reproduce

  1. Install codegraph-mcp on Windows
  2. Index a workspace
  3. Call get_module_summary with any valid directory path:
    get_module_summary(path="C:\\Users\\me\\project\\src")
    
  4. Observe: files=0, total_functions=0, total_classes=0, total_lines=0

Expected Result

Non-zero counts matching the actual files/functions/classes in that directory.

Workaround

None currently available — the bug is in the compiled Rust binary.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions