# How the Tutorial Operation Stores and Retrieves Learning Data in ThePrimeagen/99

> Discover how ThePrimeagen/99 tutorial operation stores and retrieves learning data in-memory using the __tutorials array. Learn about efficient data management.

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

---

**The tutorial operation in ThePrimeagen/99 stores learning data in-memory within the global `_99_state.__tutorials` array, appending structured tutorial objects upon successful LLM completion and exposing them via `require("99").__tutorials` for retrieval.**

ThePrimeagen/99 is a Neovim plugin that leverages LLMs to generate interactive learning content. Understanding how the tutorial operation stores and retrieves learning data reveals the plugin's state management architecture and enables developers to build custom integrations with persisted tutorial content.

## Architecture Overview

The tutorial operation treats learning requests as specialized **request entries** whose results persist in global state for later consultation. Unlike standard one-off LLM queries, tutorials generate structured data objects containing titles and markdown content arrays that survive for the duration of the Neovim session.

The flow traverses seven distinct stages: context creation, prompt composition, request initiation, provider execution, request finalization, data storage, and retrieval exposure.

## Step-by-Step Data Flow

### 1. Creating the Request Context

The process begins in [`lua/99/request-context.lua`](https://github.com/ThePrimeagen/99/blob/main/lua/99/request-context.lua) where `RequestContext.from_current_buffer` collects buffer metadata, model configuration, and temporary file paths:

```lua
local context = RequestContext.from_current_buffer(_99_state, trace_id)
context.operation = "tutorial"

```

The context captures:
- `tmp_file`: A random temporary file path for the LLM response
- `model`: The selected AI model configuration
- `md_file_names`: Additional markdown context files
- Per-request logging infrastructure

### 2. Composing the Tutorial Prompt

In [`lua/99/ops/make-prompt.lua`](https://github.com/ThePrimeagen/99/blob/main/lua/99/ops/make-prompt.lua), the `make_prompt` function injects user input into the static tutorial template defined in [`lua/99/prompt-settings.lua`](https://github.com/ThePrimeagen/99/blob/main/lua/99/prompt-settings.lua):

```lua
local prompt, refs = make_prompt(
    context,
    context._99.prompts.prompts.tutorial(),   -- static tutorial prompt
    opts
)
context:add_references(refs)

```

The resulting prompt combines the built-in tutorial instructions with user-specified additional context, while `make_prompt` returns any rule-derived references that augment the AI context.

### 3. Initiating the LLM Request

The `Request` object in [`lua/99/request/init.lua`](https://github.com/ThePrimeagen/99/blob/main/lua/99/request/init.lua) orchestrates provider interaction:

```lua
local request = Request.new(context)
request:add_prompt_content(prompt)
request:start(observer)

```

The `start` method writes the full prompt to `<tmp_file>-prompt` and invokes the provider's `make_request` method. The observer—created by `CleanUp.make_observer`—handles asynchronous completion callbacks.

### 4. Provider Execution and Response Streaming

[`lua/99/providers.lua`](https://github.com/ThePrimeagen/99/blob/main/lua/99/providers.lua) implements `BaseProvider:make_request`, which spawns the external LLM binary (e.g., `opencode run`):

The provider streams `stdout` and `stderr` to the observer in real-time. Upon process termination, `_retrieve_response` reads the temporary file containing the LLM's complete output.

### 5. Finalizing the Request and Storing Data

When the request completes, the observer's `on_complete` callback triggers `_99_state:finish_request` in [`lua/99/init.lua`](https://github.com/ThePrimeagen/99/blob/main/lua/99/init.lua):

```lua
local entry = self.__request_by_id[id]
entry.status = status
local data = entry.operation_data
if entry.status == "success" and data then
    if data.type == "tutorial" then
        table.insert(self.__tutorials, data)
    elseif data.type == "search" then
        table.insert(self.__searches, data)
    end
end

```

The `operation_data` field contains a structured object matching the `RequestEntry.Data.Tutorial` schema defined in [`lua/99/init.lua`](https://github.com/ThePrimeagen/99/blob/main/lua/99/init.lua):

```lua
--- @class _99.RequestEntry.Data.Tutorial
--- @field type "tutorial"
--- @field title string
--- @field content string[]

```

Upon successful completion, this object appends to `self.__tutorials`, an array residing in the singleton `_99_state` object.

## Data Structure and Storage Mechanism

### The Tutorial Object Schema

Tutorial data adheres to a strict type definition ensuring consistent access patterns:

- **type**: Literal string `"tutorial"` for type discrimination
- **title**: Human-readable string identifying the tutorial topic
- **content**: Array of strings representing markdown content lines

### In-Memory Storage Location

The storage mechanism relies on Lua module-level singletons rather than external databases or filesystem persistence:

```lua
-- Located in lua/99/init.lua
_99_state = _99_State.new()

```

The `__tutorials` array exists within this singleton, making it accessible globally through the module export:

```lua
local tutorials = require("99").__tutorials

```

Data persists only for the duration of the Neovim session. Terminating the editor destroys the Lua state and all stored tutorials.

## Retrieving Stored Tutorials

Accessing historical tutorial data requires importing the global state module:

```lua
local ninety = require("99")

-- List all stored tutorials
for i, tut in ipairs(ninety.__tutorials) do
    print(string.format("[#%d] %s", i, tut.title))
    print(table.concat(tut.content, "\n"))
    print("---")
end

```

The raw `__tutorials` array exposes the complete structured data, enabling custom UI components to render tutorial histories, implement search functionality, or export content to external formats.

## Clearing and Resetting Tutorial Data

The plugin provides explicit cleanup functionality for session management:

```lua
require("99"):clear_previous_requests()

```

This method, defined in [`lua/99/init.lua`](https://github.com/ThePrimeagen/99/blob/main/lua/99/init.lua) (lines 241-244), resets both tutorial and search storage:

```lua
function _99_State:clear_previous_requests()
    self.__request_by_id = {}
    self.__tutorials = {}
    self.__searches = {}
end

```

Invoking this function clears all learning data, effectively resetting the plugin's memory without restarting Neovim.

## Key Implementation Files

| File | Responsibility |
|------|----------------|
| [`lua/99/ops/tutorial.lua`](https://github.com/ThePrimeagen/99/blob/main/lua/99/ops/tutorial.lua) | Entrypoint for the tutorial operation – builds requests, wires cleanup, and starts execution. |
| [`lua/99/request/init.lua`](https://github.com/ThePrimeagen/99/blob/main/lua/99/request/init.lua) | Implements the `Request` object that drives the provider and manages request-specific state. |
| [`lua/99/request-context.lua`](https://github.com/ThePrimeagen/99/blob/main/lua/99/request-context.lua) | Captures buffer, model, temp file, markdown files, and assembles the full AI context. |
| [`lua/99/providers.lua`](https://github.com/ThePrimeagen/99/blob/main/lua/99/providers.lua) | Generic provider base that runs the external LLM binary and streams output. |
| [`lua/99/init.lua`](https://github.com/ThePrimeagen/99/blob/main/lua/99/init.lua) | Global singleton `_99_state` – stores `__tutorials`, tracks requests, and moves successful data into the tutorial store. |
| [`lua/99/prompt-settings.lua`](https://github.com/ThePrimeagen/99/blob/main/lua/99/prompt-settings.lua) | Defines the static tutorial prompt text merged with user input. |
| [`lua/99/ops/make-prompt.lua`](https://github.com/ThePrimeagen/99/blob/main/lua/99/ops/make-prompt.lua) | Composes the final prompt by injecting user content into operation-specific templates. |

## Summary

- The tutorial operation stores learning data **in-memory** within the global `_99_state.__tutorials` array, not on disk or external databases.
- Data persistence lasts only for the **Neovim session lifetime**, clearing automatically when the editor closes.
- The storage flow involves seven stages: context creation, prompt composition, request initiation, provider execution, request finalization, data appending to `__tutorials`, and global exposure via `require("99")`.
- Tutorial objects follow a strict schema with `type`, `title`, and `content` fields, ensuring consistent access patterns for UI components.
- Developers can manually clear stored tutorials using `clear_previous_requests()`, which resets the internal arrays without restarting Neovim.

## Frequently Asked Questions

### How long does tutorial data persist in the 99 plugin?

Tutorial data persists only for the duration of the current Neovim session. Because the storage mechanism uses Lua module-level singletons (`_99_state.__tutorials`) rather than filesystem persistence or databases, terminating the editor destroys all stored learning data automatically.

### What is the exact data structure of a stored tutorial object?

The tutorial object adheres to the `_99.RequestEntry.Data.Tutorial` class definition found in [`lua/99/init.lua`](https://github.com/ThePrimeagen/99/blob/main/lua/99/init.lua). It contains three fields: `type` (the literal string `"tutorial"`), `title` (a string describing the tutorial topic), and `content` (an array of strings representing markdown-formatted tutorial text).

### Can I access stored tutorials from my own Neovim Lua scripts?

Yes, the global state module exposes the `__tutorials` array publicly. You can access stored tutorials by requiring the main module: `local tutorials = require("99").__tutorials`. This returns the raw array of tutorial objects, allowing you to iterate, filter, or display the content in custom UI components.

### How do I clear the tutorial storage without restarting Neovim?

Invoke the `clear_previous_requests()` method on the 99 module: `require("99"):clear_previous_requests()`. This function, defined in [`lua/99/init.lua`](https://github.com/ThePrimeagen/99/blob/main/lua/99/init.lua), resets the internal `__tutorials` array to an empty table, effectively clearing all stored learning data while keeping your Neovim session active.