[KongchangAI]
· 3 min read· 1,779 words

RepoPilot: Using AST and Dependency Graphs to Help AI Truly Understand Codebases

RepoPilot: Using AST and Dependency Graphs to Help AI Truly Understand Codebases

RepoPilot builds repository structure awareness with AST and dependency graphs, then lets the LLM generate Q&A and tests based on targeted context.

RepoPilot is an AI tool for developers aimed at solving the dilemma of understanding unfamiliar large codebases. It doesn't stuff the entire repository into an LLM; instead, it first uses deterministic code analysis to understand repository architecture: extracting structured information via AST parsing, building a file-level dependency graph, and assembling targeted context from the selected file for the LLM to handle codebase Q&A and test generation. The system completely separates the code analysis pipeline from the AI generation pipeline. The author emphasizes that AI-generated tests still require human review, and private repository credential management is a key productization challenge.

Facing an unfamiliar repository—hundreds of files, deeply nested dependencies, almost zero documentation—this is a dilemma every developer knows well. While AI coding assistants have alleviated some of the pain, stuffing an entire repository into an LLM prompt is neither realistic nor efficient. RepoPilot takes a more structured approach: first use deterministic code analysis to understand the repository architecture, then let AI answer questions and generate tests based on this "map."

RepoPilot Project Overview

What Problem Does RepoPilot Solve

RepoPilot is an AI tool for developers, with its core capabilities centered around "repository awareness." Users can directly connect GitHub, GitLab, or Bitbucket repositories (including private repositories via OAuth), have the system complete indexing and dependency analysis, and then engage in codebase conversations or test generation.

Specific features include five items: repository integration, codebase indexing and dependency analysis, AI-driven codebase Q&A, context-aware unit and integration test generation, and generating test cases by describing business scenarios in natural language.

The author explicitly states that the goal is not to replace engineers' judgment, but to reduce the repetitive work required to understand codebases and build test foundations. This positioning is noteworthy—it places AI in the role of "lowering the cost of getting started" rather than "automatically delivering correct code."

Core Engineering Challenge: More Context Isn't Always Better

One of the biggest bottlenecks in AI-assisted development is context. Large repositories cannot be passed wholesale into LLM prompts; even if squeezed into the context window, providing every file to the model is often inefficient—token consumption rises, irrelevant information floods in, and the model struggles to focus on the specific implementation being discussed.

RepoPilot's solution is to base retrieval on the actual structural relationships of the code, rather than textual similarity. This leads to three key steps: AST parsing, dependency graph construction, and targeted context assembly.

Step One: Understanding Source Code Structure with AST Parsing

RepoPilot's indexing pipeline uses different strategies for different languages: JavaScript and TypeScript use the TypeScript compiler ecosystem (ts-morph), Python uses Python AST, and Java uses comment-aware source code scanning.

What the parser extracts is not plain text, but structured information: imports and exports, functions and classes, method signatures, relationships between source files, and relevant code snippets.

Take a TypeScript code example: checkout.ts imports PaymentService from ./payment.service, instantiates it in the checkout function, and calls its process method. Text-based search can only identify the class name, while the AST approach can reliably confirm the import relationship and the function structure containing that import. This difference is crucial when deciding "which related files should enter the AI request."

AST (Abstract Syntax Tree) is a tree-like representation of the grammatical structure of source code, where each node corresponds to a construct in the code (such as function declarations, classes, import statements). Unlike regex or text search, AST is generated by a parser according to the language grammar and can accurately distinguish semantic differences like "imported a class" versus "merely mentioned the class name." In RepoPilot, ts-morph wraps the TypeScript compiler API, allowing developers to programmatically traverse and query the AST; Python's built-in ast module similarly converts source code into an inspectable node tree. It is precisely through AST that the system can reliably extract import/export relationships, function signatures, and inter-file references, providing a reliable data foundation for subsequent dependency graph construction.

Step Two: Building the Repository Dependency Graph

After parsing is complete, RepoPilot builds a knowledge graph representing the internal relationships of the codebase. Conceptually, checkout.ts points to payment.service.ts via import, which in turn points to payment.gateway.ts.

With this graph, when a user requests test generation for checkout.ts, the system doesn't need to provide the entire repository. Instead, starting from the selected file, it traverses related dependencies and collects the context most likely to affect the implementation. This graph also lays the foundation for future impact analysis and architecture exploration capabilities.

Dependency Graph is a directed graph where nodes represent files or modules, and edges represent reference relationships between them (such as import, require). Unlike a simple call graph, a dependency graph focuses on coupling at the source code organization level: file A imports file B, forming an edge A→B. In RepoPilot, this graph is automatically generated from AST parsing results, enabling the system to start from any selected file and traverse its direct and indirect dependencies along the edges, thereby identifying the set of files "most likely to affect the current implementation." This graph-structure-based retrieval is more precise than textual similarity and also provides a computable foundation for subsequent impact analysis (which modules are affected by modifying a file).

Step Three: Assembling Context for the LLM

This is what the author considers a more interesting architectural decision: completely separating the code analysis pipeline from the AI generation pipeline. The overall flow is: Git repository → repository parser → dependency graph → context builder → prompt builder → LLM provider → generated tests or AI responses.

The context builder is responsible for identifying and collecting relevant code, while the prompt builder transforms it into task-specific instructions. The prompt structures required for generating unit tests and answering architecture questions are completely different. Separating responsibilities makes the system easier to extend, debug, and maintain, and also allows AI providers to evolve independently of the repository analysis pipeline.

The separation of context builder and prompt builder is essentially a decoupling of the two stages: "retrieval" and "generation." The context builder is only responsible for selecting and trimming relevant code snippets from the repository based on the dependency graph and task type, outputting a structured context object; the prompt builder receives this object, applies templates for specific tasks (such as generating unit tests or answering architecture questions), and transforms it into the final instructions sent to the LLM. The benefits are: when switching LLM providers, only the prompt builder needs adjustment; when improving repository analysis logic, only the context builder needs modification—the two don't interfere with each other. At the same time, different tasks can reuse the same context by simply changing the prompt template, reducing the cost of repeated retrieval.

Step Four: Generating Tests from Real Code Context

Test generation is one of RepoPilot's primary use cases. Take a function responsible for validating orders, checking inventory, and processing payments—a useful test suite needs to cover far more than just the happy path: valid orders, invalid input, insufficient inventory, payment failure, dependency exceptions, boundary conditions.

By providing relevant implementation and dependency context, RepoPilot attempts to generate more meaningful test suggestions than "generic prompts containing only function names." The author also acknowledges that generated tests still require developer review and execution—AI-generated tests are not automatically proof of correctness, but they can significantly reduce the cost of establishing an initial test baseline.

Step Five: Describing Test Scenarios in Business Language

RepoPilot also supports describing tests in the language of business requirements. For example, entering "generate tests for the checkout flow where payment succeeds but inventory reservation fails" will generate corresponding test code based on the scenario description and repository context.

This is especially useful for developers who have clearly defined the behavior to verify but haven't yet located all the implementation details, and it also bridges the gap between business-level requirements and actual source code behavior.

Business-language-driven test generation differs fundamentally from traditional "generate tests for a specified file" in terms of input form: the former takes a natural language description of a behavior scenario (such as "payment succeeds but inventory reservation fails"), and the system needs to first map keywords in the scenario to relevant entities in the repository (payment service, inventory service, checkout flow), then locate implementation code using the dependency graph, and finally generate tests covering that scenario. This requires the system to have certain semantic understanding and entity linking capabilities, not just code structure analysis. For developers, this approach reduces the cost of converting from requirements documents to test code, and is especially suitable for building test skeletons before implementation details are fully determined.

Tech Stack and Architectural Trade-offs

The frontend uses Vue 3 and TypeScript, the backend is Python with FastAPI, code analysis relies on the TypeScript Compiler API / ts-morph, repository intelligence is handled by the dependency graph and context builder, AI integration has a provider abstraction for LLM services, authentication uses Auth0, source code integration covers GitHub, GitLab, and Bitbucket, and background processing uses asynchronous tasks and workers.

The core architectural requirement is modularity—the author doesn't want the repository parser, context builder, and LLM integration merged into one hard-to-maintain monolith.

Five Lessons from the Build Process

First, better context is as important as better prompts. Prompt engineering is useful, but accurately locating the right source files and relationships is equally critical.

Second, deterministic code analysis and generative AI are complementary. AST provides structural information, and LLM helps interpret that information and generate useful output—the two need not replace each other.

Third, AI-generated tests must be verified. Generated code may contain incorrect assumptions, incomplete mocks, or invalid assertions; human review and execution are indispensable.

Fourth, private repositories make security issues more complex. Repository credentials need to be handled carefully: scoped access, secure storage, and preventing accidental leakage in logs or prompts.

Fifth, building a product is different from building a prototype. Authentication, quotas, fault handling, deployment, observability, and user onboarding quickly become just as important as the AI features themselves.

Future Directions

RepoPilot is still evolving. The author lists exploration directions including: better semantic retrieval for large repositories, more advanced dependency and impact analysis, test quality assessment, support for more languages and frameworks, and smarter repository-aware development workflows.

Production preview execution is deliberately excluded from the initial public version; the author wants to first validate security, infrastructure, and operational requirements. The current focus remains on repository intelligence and AI-assisted testing.

One-line Summary

RepoPilot first builds repository structure awareness with AST and dependency graphs, then lets the LLM generate Q&A and tests based on targeted context.

Paragraph Summary

RepoPilot is an AI tool for developers aimed at solving the dilemma of understanding unfamiliar large codebases. Instead of stuffing the entire repository into an LLM, it first uses deterministic code analysis to understand the repository architecture: extracting structured information such as imports/exports and function signatures through AST parsing, then building a file-level dependency graph, and finally traversing dependencies starting from the selected file to assemble targeted context for the LLM to perform codebase Q&A and test generation. The system completely separates the code analysis pipeline from the AI generation pipeline, with the context builder and prompt builder each handling their own responsibilities. The author emphasizes that AI-generated tests still require human review, and that credential management for private repositories is a key productization challenge.

#RepoPilot#AST parsing#dependency graph#AI codebase understanding#context-aware test generation
Share:

Related articles