Chapter 13

Explore a Large Codebase in Layers

A large repository isn't put in the prompt. It's traversed as a funnel: structure, instructions, entry points, symbols, callers, tests, and only then the full text of selected files. Four search layers go from general to specific: the map (languages, packages, manifests, test directories), names (exact grep by error, route, type, schema), links (callers, imports, data ownership, nearest tests), and content (only files that actually change the decision are read in full).

The same bounded traversal from the companion project works here too.

TypeScript
  async function visit(directory: string): Promise<void> {
    const children = await readdir(directory, { withFileTypes: true });
    children.sort((left, right) => left.name.localeCompare(right.name));
    for (const child of children) {
      if (entries.length >= options.maxFiles) {
        truncated = true;
        return;
      }
      if (ignoredNames.has(child.name)) {
        continue;
      }
      const absolutePath = join(directory, child.name);
      if (child.isSymbolicLink()) {
        continue;
      }
      if (child.isDirectory()) {
        await visit(absolutePath);
        if (truncated) {
          return;
        }
        continue;
      }
      if (!child.isFile()) {
        continue;
      }
      await resolveExistingContained(root, absolutePath);
      const fileStats = await stat(absolutePath);
      const relativePath = normalizeRelativePath(relative(root, absolutePath));
      entries.push({
        path: relativePath,
        bytes: Math.min(fileStats.size, options.maxBytesPerFile),
        kind: classify(relativePath)
      });
      totalBytes += fileStats.size;
    }
  }

  await visit(root);
  return { root, entries, totalBytes, truncated };

By signal, an action and an expected artifact are chosen - this keeps the search bounded.

Signal · Agent action · Artifact

  • Error text - Exact search, then callers; A set of file:symbol
  • New capability - Find two existing analogs; A comparison of the pattern and differences
  • Cross-package change - Read package boundaries and contracts; A plan by packages
  • Flaky test - History, shared state, time, network; Hypotheses with verification commands

Separately, it matters that a product's index and a security boundary aren't the same thing. Cursor documents repository indexing and two ignore files, and the official page explicitly warns: .cursorignore limits AI-feature access, but terminal and MCP tools aren't blocked by this file, and .cursorindexingignore only excludes from the index. So secret protection must also live in permissions, sandbox, and the secret system itself.

A search summary needs verification. If a separate researcher or subagent returned a file list, the main executor should open the decisive fragments itself. A summary saves context but isn't proof of a line of code.

And a budget: set max files, max output, and a stop question - is enough found to name the root cause and the minimal scope. Otherwise the search turns into an endless architecture tour.

The map is ready. Time to change code - and a good patch is minimal and coherent.

Links