code-graph-rag

repository·main·Indexed 25 days ago

https://github.com/vitali87/code-graph-rag

A tool for multi-language monorepos that parses codebases into structural knowledge graphs using Tree-sitter and Memgraph. It enables advanced Retrieval-Augmented Generation (RAG) to query, understand, and edit code based on AST structure. The library supports polyglot ingestion across 14 languages and provides an extensible system to add language support via ast-grep patterns. Version 0.0.517 includes comprehensive evaluation suites for structural containment, call attribution, inheritance, and retrieval accuracy.

Tokens
43.6K
Snippets
105
Records
264
Agent score
88%

What's inside code-graph-rag

  1. What is Code-Graph-RAG?

    main
    Code-Graph-RAG is a Retrieval-Augmented Generation (RAG) system designed for monorepos. It uses Tree-sitter to parse multi-language codebases and builds a knowledge graph in Memgraph. This allows users to perform natural language querying of codebase structures, relationships, and code snippets, as well as perform AST-based code editing and optimization.
  2. Optimize `should_skip_path` using string operations

    main

    The should_skip_path function is slow because it uses pathlib.Path.relative_to(), which creates intermediate objects on every call. Replacing pathlib with string operations provides significant speedups.

    Fix: Convert paths to strings at the boundary of should_skip_path and use str.removeprefix(), str.split("/"), and set membership testing instead of Path.relative_to() and Path.parts.

  3. Limitations of FLOWS_TO Analysis

    main

    The FLOWS_TO analysis is designed to be conservative and intra-procedural. Users should be aware of the following technical ceilings:

    • Intra-procedural: Value flow is tracked within a function body plus one level of argument/return hand-off. It is not path-sensitive; a 'kill' on one branch of an if/else drops taint conservatively for both.
    • Single-pass return propagation: A callee whose body is processed after its caller (common in cross-file analysis) may not yet be known to return a tainted value.
    • No SSA-level precision: The analysis uses direct I/O calls from the registry; it does not use Parameter nodes or SSA (Static Single Assignment) precision.
    • Language Support: The source/sink registry covers Python, JavaScript, TypeScript (including TSX), Go, Java, Rust, C, C++, and C#. Languages not in the registry will not emit I/O or flow edges until a table is added.
  4. Reduce logging overhead by suppressing debug logs in production

    main

    Processing debug() calls via Loguru consumes significant CPU time (approx. 5.9%) even when debug output is not displayed.

    Optimization Tasks:

    • Set the Loguru level to INFO or WARNING during GraphUpdater.run().
    • Wrap debug calls in guards: if logger.level <= DEBUG:.
    • Use lazy evaluation for expensive format strings: logger.opt(lazy=True).debug(lambda: ...).
  5. Optimize Call Resolution with Rust String Processing

    main

    Move call resolution logic into the Rust AST processing extension.

    Warning: Do NOT implement this as a standalone optimization. Call resolution is deeply interleaved with trie lookups, import map lookups, and AST node access. Attempting to extract only string processing would require marshalling massive amounts of context (import maps, trie state, class inheritance) across the FFI boundary for every call, resulting in negative net gains.

    Correct Strategy: Bundle this logic with the Rust AST extension so that call resolution happens entirely on the Rust side, eliminating FFI overhead during the resolution pass.

  6. Understand .cgrignore pattern syntax rules

    main

    Patterns in .cgrignore follow standard gitignore conventions:

    • *: Matches within a single path segment.
    • **: Matches across multiple path segments (cross-segment).
    • ?: Matches a single character.
    • Bare name (vendor): Matches a file or directory with that name at any depth.
    • Slash-anchored (docs/*.md, /generated): Patterns containing a slash are anchored to the repository root.
    • Trailing slash (build/): Matches directories only.
    • Negation (!bin/keep.py): Lines starting with ! un-ignore paths that would otherwise be skipped by default exclusions. Note that explicit excludes always take precedence over un-ignores.
    • Comments: Lines starting with # are ignored; blank lines are also ignored.
  7. Understand the Taint Analysis mental model

    main

    The data-flow analysis uses a taint analysis model to track how values move through code:

    1. Source: Where a value enters the program from the outside world (e.g., reading an environment variable or a file). A value from a source is considered tainted.
    2. Propagation: How the 'taint' travels through the program via assignments, function arguments, and return values.
    3. Sink: Where a tainted value leaves the program (e.g., writing to a file, a socket, or standard output).

    By representing these as graph edges, you can perform reachability queries such as: "Does any value from this specific source reach that specific sink?"

  8. Metavariable conventions for ast-grep patterns

    main

    When writing patterns in your YAML configuration, you must follow specific metavariable naming conventions to ensure the parser correctly identifies entities:

    • Definition patterns (functions, classes): You must capture the name using the exact metavariable $NAME.
    • Import patterns: You must capture the imported path using the exact metavariable $PATH. Note that surrounding quotes in the source code are automatically stripped from the captured value.
  9. Optimize `FunctionRegistryTrie.find_ending_with` performance

    main

    The FunctionRegistryTrie.find_ending_with() method currently suffers from a significant performance bottleneck because it falls back to a linear scan when the _simple_name_lookup index misses. This can be fixed by ensuring a complete suffix index is built.

    Fix: Build a complete suffix index in FunctionRegistryTrie by populating _simple_name_lookup for every insert. Ensure all insertion code paths (including __setitem__) update the index to eliminate the linear scan fallback.

  10. Pattern ordering in ast-grep configurations

    main

    Patterns in the functions, classes, and imports lists are evaluated in the order they are defined. The first pattern to match a source line claims it.

    To avoid incorrect matches, always place more specific patterns before more general ones.

    Example: If you have def self.build, the pattern def self.$NAME should come before def $NAME. If def $NAME were first, it would incorrectly capture self as the function name instead of build.

  11. Improve AST traversal efficiency via `build_local_variable_type_map` caching

    main

    The build_local_variable_type_map function causes redundant AST traversals (approx. 8.3% CPU usage) when multiple functions in the same file trigger independent traversals.

    Optimization Task: Implement memoization for this function. Use a cache key composed of (file_path, function_start_line, function_end_line) to return previously computed results. Ensure the cache is invalidated when files change via the existing incremental update system.

  12. Understand C retrieval limitations (Preprocessor Gap)

    main

    When evaluating C retrieval, be aware of the gap between cgr (using tree-sitter) and the libclang oracle caused by the C preprocessor:

    1. False Positives: cgr may emit calls located inside inactive conditional branches (e.g., code inside #ifdef blocks that are not defined). libclang correctly compiles these out.
    2. False Negatives: cgr may miss calls that only exist after macro expansion (e.g., macros that expand to function calls like jv_object_foreach or bison/flex API macros).