# How 99 Builds the Request Context for AI Providers

> Learn how 99 builds the request context for AI providers in three stages: initializing from Neovim, enriching with code and docs, and serializing into a prompt file.

- Repository: [ThePrimeagen/99](https://github.com/theprimeagen/99)
- Tags: internals
- Published: 2026-02-16

---

**99 constructs a comprehensive `RequestContext` in three distinct stages—initializing from the current Neovim buffer, enriching it with code references and markdown documentation, and serializing the result into a prompt file—before dispatching to providers like `OpenCodeProvider` or `ClaudeCodeProvider`.**

The 99 plugin for Neovim orchestrates AI-assisted coding workflows by meticulously assembling a request context that captures your complete editing environment. Understanding how 99 builds this context reveals the mechanism by which it gathers buffer metadata, project documentation, and code selections to generate deterministic, richly-contextualized AI prompts.

## Stage 1: Initializing from the Current Buffer

The construction process begins in [`lua/99/request-context.lua`](https://github.com/ThePrimeagen/99/blob/main/lua/99/request-context.lua) with the `RequestContext.from_current_buffer` method (lines 26‑53). This static factory method captures the essential metadata from your current editing session:

- **Buffer identification**: The current buffer number via `vim.api.nvim_get_current_buf()`
- **File path**: Absolute path resolution through `vim.api.nvim_buf_get_name()`
- **Filetype normalization**: Conversion of compound types like `typescriptreact` to `typescript`
- **Temporary file generation**: A unique scratch file created via `utils.random_file()` for provider output
- **Model selection**: The AI model specified in the global state
- **Documentation index**: The set of markdown filenames (`.md`) configured by the user

```lua
local ctx = require("99.request-context").from_current_buffer(state, request_id)

```

At this stage, the context object contains the structural foundation but lacks the actual content references and documentation text that will guide the AI model.

## Stage 2: Populating the AI Context

Once initialized, the context undergoes enrichment through three primary mechanisms defined in [`lua/99/request-context.lua`](https://github.com/ThePrimeagen/99/blob/main/lua/99/request-context.lua): reference aggregation, markdown ingestion, and range extraction.

### Adding Code References

The `add_references` method (lines 74‑80) appends raw content extracted from Treesitter queries or LSP symbols to the `ai_context` array. These references provide the AI with specific code definitions relevant to the current operation:

```lua
-- refs is an array of {content = "..."} tables from Treesitter analysis
ctx:add_references(refs)

```

### Ingesting Markdown Documentation

The `_read_md_files` method (lines 82‑108) implements a lazy directory traversal that searches for user-configured markdown files. Starting from the current file's directory and walking upward to the project root (`vim.uv.cwd()`), it reads the contents of each discovered `.md` file and appends them to the context:

- Searches for files specified in `state.md_files`
- Traverses parent directories until reaching the working directory root
- Reads file contents and adds them to the `ai_context` array

### Capturing Visual Selections

When operating on a specific code range, the context captures precise location data. If `ctx.range` is set (typically from a visual selection), the `finalize` method incorporates:
- **File location tags**: XML-like markers from [`prompt-settings.lua`](https://github.com/ThePrimeagen/99/blob/main/prompt-settings.lua) indicating the file path and line range
- **Range text**: The raw selected text extracted from the buffer

## Stage 3: Serializing and Handing Off to the Provider

The final stage transforms the enriched context into a concrete prompt file and dispatches it to the AI provider. This orchestration occurs in [`lua/99/request/init.lua`](https://github.com/ThePrimeagen/99/blob/main/lua/99/request/init.lua) within the `Request:start` method (lines 19‑32).

### Finalizing the Context

The `finalize` method (lines 136‑146 in [`request-context.lua`](https://github.com/ThePrimeagen/99/blob/main/request-context.lua)) performs the last assembly steps:
1. **Appends the MustObey block**: Inserts instructions telling the model where to write its output (the temporary file location)
2. **Concatenates all context entries**: Joins the `ai_context` array into the final prompt structure

### Writing the Prompt File

The `Request` class writes the serialized context to a temporary file with the `-prompt` suffix:

```lua
local prompt = table.concat(self._content, "\n")
-- Written to <temp_file>-prompt

```

### Provider Dispatch

Finally, the request invokes the selected provider's `make_request` method. By default, 99 uses `OpenCodeProvider`, though `ClaudeCodeProvider` and others are available:

```lua
provider:make_request(prompt, self, observer)

```

The provider reads the prompt file, executes the AI model, and streams responses back through the observer callbacks (`on_start`, `on_stdout`, `on_stderr`, `on_complete`).

## Key Files in the Context Pipeline

| File | Role |
|------|------|
| [`lua/99/request-context.lua`](https://github.com/ThePrimeagen/99/blob/main/lua/99/request-context.lua) | Defines `RequestContext` class with `from_current_buffer`, `add_references`, `_read_md_files`, and `finalize` methods. |
| [`lua/99/request/init.lua`](https://github.com/ThePrimeagen/99/blob/main/lua/99/request/init.lua) | Orchestrates the request lifecycle via `Request:new()` and `Request:start()`, handling serialization and provider dispatch. |
| [`lua/99/prompt-settings.lua`](https://github.com/ThePrimeagen/99/blob/main/lua/99/prompt-settings.lua) | Supplies XML-style tags (`<Location>`, `<TEMP_FILE>`, etc.) embedded into the context for model instruction. |
| [`lua/99/providers.lua`](https://github.com/ThePrimeagen/99/blob/main/lua/99/providers.lua) | Defines the provider interface; implementations like `OpenCodeProvider` consume the generated prompt files. |
| [`lua/99/utils.lua`](https://github.com/ThePrimeagen/99/blob/main/lua/99/utils.lua) | Provides `random_file()` utility for generating unique temporary file paths used throughout the context. |

## Summary

- **Three-stage pipeline**: 99 builds request context through initialization (`from_current_buffer`), enrichment (`add_references`, `_read_md_files`), and serialization (`finalize`).
- **Comprehensive metadata**: The context captures buffer state, file paths, normalized filetypes, project documentation, code references, and visual selection ranges.
- **Deterministic output**: The `finalize` method ensures the model receives explicit instructions via the MustObey block, directing output to a specific temporary file location.
- **Provider agnostic**: The serialized prompt is written to disk before `make_request` dispatches to any provider (`OpenCodeProvider`, `ClaudeCodeProvider`, etc.), ensuring reproducible AI interactions.

## Frequently Asked Questions

### What information does 99 capture from the current buffer?

99 captures the buffer number, absolute file path, normalized filetype (converting variants like `typescriptreact` to `typescript`), and the specific AI model configured in your state. This occurs in `RequestContext.from_current_buffer` within [`lua/99/request-context.lua`](https://github.com/ThePrimeagen/99/blob/main/lua/99/request-context.lua) (lines 26‑53), which also generates a unique temporary file path for the provider's output.

### How does 99 handle project documentation?

The plugin lazily ingests markdown files configured in `state.md_files` through the `_read_md_files` method (lines 82‑108 in [`lua/99/request-context.lua`](https://github.com/ThePrimeagen/99/blob/main/lua/99/request-context.lua)). This function traverses upward from the current file's directory to the project root (`vim.uv.cwd()`), reading the contents of any discovered `.md` files and appending them to the `ai_context` array for the model to reference.

### What happens if I have a visual selection active?

When a visual range is present, 99 appends precise location metadata and the raw selected text to the context. During `finalize` (lines 136‑146 in [`lua/99/request-context.lua`](https://github.com/ThePrimeagen/99/blob/main/lua/99/request-context.lua)), the system inserts XML-style tags from [`prompt-settings.lua`](https://github.com/ThePrimeagen/99/blob/main/prompt-settings.lua) indicating the file location and range boundaries, followed by the actual text content of the selection, ensuring the model understands exactly which code you're referencing.

### Which AI providers does 99 support?

99 supports multiple providers through a common interface defined in [`lua/99/providers.lua`](https://github.com/ThePrimeagen/99/blob/main/lua/99/providers.lua). The default implementation is `OpenCodeProvider`, though `ClaudeCodeProvider` and others are available. Regardless of the provider, the interaction follows the same pattern: the `Request:start` method writes the serialized context to a temporary file, then calls `provider:make_request(prompt, self, observer)` to execute the AI command and stream results back through observer callbacks.