...

/

Mastering Context: Codebase Indexing and @-References

Mastering Context: Codebase Indexing and @-References

Learn how Cursor AI indexes your codebase for deep understanding and how to use @-references to provide the AI with precise context for any task.

In our previous lessons, we established that Cursor’s “AI-first” architecture is its key differentiator. The foundation of that architecture is its ability to understand the entire context of a project. An AI assistant’s effectiveness is directly proportional to the quality of the context it is given. Without proper context, even the most powerful AI is simply making educated guesses, leading to generic, unhelpful suggestions that feel disconnected from the specific needs of our project.

Press + to interact

This lesson delves into the two primary mechanisms Cursor uses to manage context: automated codebase indexing and manual context direction with @-references. Perfecting these tools is arguably the single most important step in transitioning from a casual user to a power user. By learning how to provide the AI with perfect context, we can unlock more accurate, relevant, and powerful assistance for complex engineering tasks. This transforms the AI from a simple coding aid into a true coding collaborator, and assistant.

How codebase indexing creates a project “Map”

When we open a project folder in Cursor for the first time, the editor automatically begins a process called codebase indexing. This is not a simple file listing or a keyword search index. Instead, Cursor is building a sophisticated semantic graph of our entire project, in the form of a rich, interconnected map of its logic.

Here is what happens during this process:

  1. Parsing the code: Cursor recursively scans every file in our project, parsing the abstract syntax tree (AST) to understand the code’s structure, not just its text. This allows it to differentiate between a variable name and a comment, for instance.

  2. Identifying symbols: It identifies all the key symbols, functions, classes, variables, interfaces, etc., and their definitions. It catalogs where each symbol is declared and what its signature is.

  3. Mapping relationships: Most importantly, it maps the relationships between these symbols. It understands that functionA calls functionB, that classC inherits from classD, and that a change in api/types.ts will affect a component in components/UserProfile.tsx. This graph represents the logical flow and dependency structure of the application.

The result is a comprehensive “map” of our codebase that the AI can consult. When we ask a question or give a command, the AI uses this map to find the most relevant pieces of information, even if they are spread across dozens of files. This is what enables Cursor to perform complex, multi-file operations with a high degree of accuracy and to answer high-level questions like, “Where is user authentication handled in this project?”

Managing the index in large projects

For large, enterprise-scale projects with thousands of files, managing the index is important for performance and accuracy.

  • Automatic indexing: By default, Cursor will index any new folder we open. We can find the status of the index in Settings > Features > Codebase Indexing. It is a good habit to glance at this when starting a session to ensure the index is ready.

  • Manual refresh: If we make significant changes to the project structure, such as renaming a major directory or pulling in a large new feature branch, we may want to trigger a manual re-index to ensure the AI’s map is up-to-date. Working with an outdated index can lead to the AI making suggestions based on obsolete code structures.

  • Ignoring files (.cursorignore): To improve performance and reduce noise, we can tell Cursor to ignore certain files or directories during indexing. Cursor automatically respects the project’s .gitignore file. We can add more exclusions by creating a .cursorignore file in the project’s root directory. This is useful for excluding build artifacts (dist, build), dependency folders (node_modules), large asset folders (images), or log files (*.log).

Mastering @-References: Directing the AI’s attention

If codebase indexing provides the AI with a map of the entire city, @-references are how we give it precise turn-by-turn directions. The @ symbol is a powerful command that allows us to manually inject specific context into our prompts, ensuring the AI focuses on exactly what matters for the task at hand. This is a form of active context management.

Press + to interact

Applying context in the UI

Here is how we can add context using Cursor UI:

  • The @ command in chat: As we type in the chat panel, simply typing @ will bring up an auto-complete menu, allowing us to search for and select files, folders, and symbols from our indexed codebase. This makes providing context fast and intuitive.

Press + to interact
The '@Add Context' menu provides multiple ways to give the AI specific context, including files, Git history, and web search results
The '@Add Context' menu provides multiple ways to give the AI specific context, including files, Git history, and web search results
  • Codebase indexing controls: In Settings > Features > Indexing & Docs, we have a dashboard where we can see the indexing status, trigger a manual re-index, and configure ignored files. It is a good practice to check this panel when starting a new large project to ensure the AI has a complete, and accurate picture of our code.

Press + to interact
The ‘Indexing & Docs’ settings panel allows us to monitor the codebase indexing status and configure what files are included
The ‘Indexing & Docs’ settings panel allows us to monitor the codebase indexing status and configure what files are included

Example

Let’s walk through a practical example. Suppose we have a simple emailService.ts file in our project:

// src/services/emailService.ts
export class EmailService {
/**
* Sends an email to a recipient.
* @returns true if the email was sent successfully, false otherwise.
*/
send(recipient: string, subject: string, body: string): boolean {
if (!recipient || !subject || !body) {
console.error("Missing required email parameters.");
return false;
}
// In a real application, this would use an email API
console.log(`Sending email to ${recipient} with subject "${subject}"`);
return true;
}
}
A simple TypeScript service file that we will use as context for the AI

Now, we want to generate a unit test file for this service. Instead of copy-pasting the code into the chat, we can use an @-reference to provide perfect context. Our prompt would be:

Prompt: “Read the contents of @src/services/emailService.ts and generate a complete Jest test suite for it. Ensure you cover the success case and the failure case for missing parameters.”

By referencing the file directly, we give the AI the full context it needs. Cursor will read the file and generate a new, complete test file like this:

import { EmailService } from './emailService';
describe('EmailService', () => {
let emailService: EmailService;
beforeEach(() => {
emailService = new EmailService();
});
it('should send email successfully with valid parameters', () => {
const result = emailService.send('test@example.com', 'Test Subject', 'Test Body');
expect(result).toBe(true);
});
it('should fail to send email if recipient is missing', () => {
const result = emailService.send('', 'Test Subject', 'Test Body');
expect(result).toBe(false);
});
it('should fail to send email if subject is missing', () => {
const result = emailService.send('test@example.com', '', 'Test Body');
expect(result).toBe(false);
});
it('should fail to send email if body is missing', () => {
const result = emailService.send('test@example.com', 'Test Subject', '');
expect(result).toBe(false);
});
});
The complete and accurate Jest test suite generated by the AI after being given the source file as context

A screenshot from Cursor for this example is shown below:

Press + to interact
Adding a file as context in Cursor
Adding a file as context in Cursor

This example demonstrates how providing precise context with @-references leads to high-quality, complete, and accurate code generation.

There are several types of @-references we can use:

Referencing files and folders (@file and @folder)

This is the most common use case. We can explicitly tell the AI to read the contents of one or more files or even entire folders before generating a response. This is far more effective than copy-pasting code into the chat.

  • Example 1 (single file): We are creating a new test file for an existing service. Our prompt could be:

Prompt: “Read the contents of @services/paymentService.ts and generate a complete Jest test suite for it in a new file named paymentService_test.ts.”

  • Example 2 (multiple files): We want to modify a component’s style. Our prompt could be:

Prompt: “Using @components/Button.tsx and @styles/buttons.css, modify the component to add a new ‘danger’ variant with a red background.”

By referencing the files directly, we ensure the AI has the full source code to work with, leading to more accurate and complete results.

Referencing symbols (@symbol)

For more targeted questions, we can reference a specific function or class within a file. This is more efficient than referencing the entire file, as it uses fewer tokens and provides a more focused context to the AI.

  • Example: We need to understand a complex function. Let’s test this with the following prompt.

Prompt: “Explain the logic of the @calculateTax function in the billing.js file.”

This tells the AI to focus its analysis on that specific symbol, providing a more detailed and relevant explanation than if we had asked about the entire file.

Referencing web pages (@Web)

Modern software development often requires consulting external documentation. The @Web reference allows the AI to search the internet and use that information as context. This is a critical feature for working with new technologies or APIs.

  • Example 1 (specific API): We are using a new feature from the Stripe API. Let’s test this with a prompt:

Prompt: “Consult the official Stripe API documentation using @Web for creating a Payment Intent, then write a Python function that implements it.”

  • Example 2 (general concept): We want to improve our code based on best practices.

Prompt: “Use @Web to find an article on best practices for accessibility in React forms, then refactor @components/ContactForm.tsx to apply those principles.”

This is incredibly powerful because it allows the AI to work with the most up-to-date information, overcoming the knowledge cut-off inherent in its training data.

Conclusion

We have now explored the engine that powers Cursor’s advanced capabilities: its deep contextual understanding of our code. Codebase indexing provides the AI with a comprehensive map automatically, and @-references give us the power to direct its focus with precision. The developer’s role shifts from holding all context mentally to curating skillfully, and providing the right context to the AI for the task at hand.

By combining these tools, we move from simply asking generic questions to having a structured, context-rich dialogue with our AI partner. This skill is fundamental to all the advanced workflows we will cover next, from precision prompt engineering to large-scale architectural refactoring. In our next lesson, we will address the critical topics of privacy and security, ensuring we can leverage these powerful tools responsibly.