# Technical Documentation: ThePrimeagen/99

> ℹ️ **Provenance:** Multi-Tier Ingestion · [CodeWiki Reference](https://codewiki.google/github.com/ThePrimeagen/99)

### Overview (`README.md`)

# 99
The AI client that Neovim deserves, built by those that still enjoy to code.

## IF YOU ARE HERE FROM [THE YT VIDEO](https://www.youtube.com/watch?v=ws9zR-UzwTE)
So many things have changed.  So please be careful!

## WARNING :: API CHANGES RIGHT NOW
It will happen that apis will disapear or be changed.  Sorry, this is an BETA product.

## Project Direction
This repo is meant to be my exploration grounds for using AI mixed with tradcoding.

I believe that hand coding is still very important and the best products i know
of today still do that (see opencode vs claude code)

## Warning
1. Prompts are temporary right now. they could be massively improved
2. Officially in beta, but api can still change.  unlikely at this point

# 99
The AI Neovim experience

## _99
99 is an agentic workflow that is meant to meld the current programmers ability
with the amazing powers of LLMs.  Instead of being a replacement, its meant to
augment the programmer.

As of now, the direction of 99 is to progress into agentic programming and surfacing
of information.  In the beginning and the original youtube video was about replacing
specific pieces of code.  The more i use 99 the more i realize the better use is
through `search` and `work`

### Basic Setup
```
/* Detailed source-code truncated for AI context efficiency. */
```

### Usage
I would highly recommend trying out `search` as its the direction the library is going

```lua
_99.search()
```

See search for more details

### Description
| Name | Type | Default Value |
| --- | --- | --- |
| `setup` | `fun(opts?: _99.Options): nil` | - |
| `search` | `fun(opts: _99.ops.SearchOpts): _99.TraceID` | - |
| `vibe` | `fun(opts?: _99.ops.Opts): _99.TraceID \| nil` | - |
| `open` | `fun(): nil` | - |
| `visual` | `fun(opts: _99.ops.Opts): _99.TraceID` | - |
| `view_logs` | `fun(): nil` | - |
| `stop_all_requests` | `fun(): nil` | - |
| `clear_previous_requests` | `fun(): nil` | - |
| `Extensions` | `_99.Extensions` | - |

### API

#### setup
Sets up _99.  Must be called for this library to work.  This is how we setup
in flight request spinners, set default values, get completion to work the
way you want it to.

#### search
Performs a search across your project with the prompt you provide and return out a list of
locations with notes that will be put into your quick fix list.

#### vibe
No description.

#### open
Opens a selection window for you to select the last interaction to open
and display its contents in a way that makes sense for its type.  For
search and vibe, it will open the qfix window.  For tutorial, it will open
the tutorial window.

#### visual
takes your current selection and sends that along with the prompt provided and replaces
your visual selection with the results

#### view_logs
views the most recent logs and setups the machine to view older and new logs
this is still pretty rough and will change in the near future

#### stop_all_requests
stops all in flight requests.  this means that the underlying process will
be killed (OpenCode) and any result will be discared

#### clear_previous_requests
clears all previous search and visual operations

#### Extensions
check out Worker for cool abstraction on search and vibe

## _99.Extensions.Worker
A persistent way to keep track of work.

this will likely be where the most change and focus goes into.  I would like
to take this into worktree territory and be able to swap between stuff super
slick.

Until then, it is going to be a single bit of work that you can provide
the description and then use search to find what is left that needs to be done.

### Description
| Name | Type | Default Value |
| --- | --- | --- |
| `set_work` | `fun(opts?: _99.WorkOpts): nil` | - |
| `search` | `fun(): nil` | - |

### API

#### set_work
will set the work for the project.  If opts provide a description then no
input capture of work description will be required

#### search
will use _99.search to find what is left to be done for this work item to be
considered done

## _99.Options
No description.

### Description
| Name | Type | Default Value |
| --- | --- | --- |
| `logger` | `_99.Logger.Options \| nil` | - |
| `model` | `string \| nil` | - |
| `in_flight_options` | `_99.InFlight.Opts \| nil` | - |
| `md_files` | `string[] \| nil` | - |
| `provider` | `_99.Providers.BaseProvider \| nil` | - |
| `display_errors` | `boolean \| nil` | - |
| `auto_add_skills` | `boolean \| nil` | - |
| `completion` | `_99.Completion \| nil` | - |
| `tmp_dir` | `string \| nil` | - |

### API

#### logger
No description.

#### model
No description.

#### in_flight_options
No description.

#### md_files
No description.

#### provider
No description.

#### display_errors
No description.

#### auto_add_skills
No description.

#### completion
No description.

#### tmp_dir
No description.

## _99.ops.Opts
The options that are used throughout all the interations with 99.  This
includes search, visual, and others

### Description
| Name | Type | Default Value |
| --- | --- | --- |
| `additional_prompt` | `string \| nil` | - |
| `additional_rules` | `_99.Agents.Rule[] \| nil` | - |

### API

#### additional_prompt
by providing `additional_prompt` you will not be required to provide a prompt.
this allows you to define actions based on remaps

```lua
remap("n", "<leader>9d", function()
  --- this function could be used to auto debug your project
  _99.search({
    additional_prompt = [[
run `make test` and debug the test failures and provide me a comprehensive set of steps where
the tests are breaking ]]
  })
end)
```

This would kick off a search job that will run your tests in the background.
the resulting failures would be diagnosed and search results would be transfered
into a quick fix list.

#### additional_rules
can be used to provide extra args.  If you have a skill called "cloudflare" you could
provide the rule for cloudflare and its context will be injected into your request

## _99.ops.SearchOpts
See `_99.opts.Opts` for more information.

There are no properties yet.  But i would like to tweek some behavior based on opts

### Description
| Name | Type | Default Value |
| --- | --- | --- |
| - | - | - |

### API
No properties.

## _99.WorkOpts
No description.

### Description
| Name | Type | Default Value |
| --- | --- | --- |
| `description` | `string \| nil` | - |

### API

#### description
No description.

## _99.Completion
No description.

### Description
| Name | Type | Default Value |
| --- | --- | --- |
| `source` | `"cmp" \| "blink" \| nil` | - |
| `custom_rules` | `string[]` | - |
| `files` | `_99.Files.Config?` | - |

### API

#### source
No description.

#### custom_rules
No description.

#### files
No description.

## _99.InFlight.Opts
this is pure a class for testing.   helps controls timings

### Description
| Name | Type | Default Value |
| --- | --- | --- |
| `throbber_opts` | `_99.Throbber.Opts \| nil` | - |
| `in_flight_interval` | `number \| nil` | - |
| `enable` | `boolean \| nil` | - |

### API

#### throbber_opts
options for the throbber in the top left

#### in_flight_interval
frequency in which the in-flight interval checks to see if it should be
displayed / removed

#### enable
defaults to true

## _99.Logger.Options
No description.

### Description
| Name | Type | Default Value |
| --- | --- | --- |
| `level` | `number?` | - |
| `type` | `"print" \| "void" \| "file" \| nil` | - |
| `path` | `string?` | - |
| `print_on_error` | `boolean \| nil` | - |
| `max_requests_cached` | `number \| nil` | - |

### API

#### level
No description.

#### type
No description.

#### path
No description.

#### print_on_error
No description.

#### max_requests_cached
No description.

## _99.Agents.Rule
No description.

### Description
| Name | Type | Default Value |
| --- | --- | --- |
| `name` | `string` | - |
| `path` | `string` | - |
| `absolute_path` | `string?` | - |

### API

#### name
No description.

#### path
No description.

#### absolute_path
No description.

## Completions

When prompting, you can reference rules and files to add context to your request.

- `#` references rules — type `#` in the prompt to autocomplete rule files from your configured rule directories
- `@` references files — type `@` to fuzzy-search project files. This will exclude files that are in .gitignore.

Referenced content is automatically resolved and injected into the AI context. Native completions work by default. For nvim-cmp or blink.cmp, set `source = "cmp"` or `source = "blink"`.

## Providers
99 supports multiple AI CLI backends. Set `provider` in your setup to switch. If you don't set `model`, the provider's default is used.

| Provider | CLI tool | Default model |
|---|---|---|
| `OpenCodeProvider` (default) | `opencode` | `opencode/claude-sonnet-4-5` |
| `ClaudeCodeProvider` | `claude` | `claude-sonnet-4-5` |
| `CursorAgentProvider` | `cursor-agent` | `sonnet-4.5` |
| `GeminiCLIProvider` | `gemini` | `auto` |

```lua
_99.setup({
    provider = _99.Providers.ClaudeCodeProvider,
    -- model is optional, overrides the provider's default
    model = "claude-sonnet-4-5",
})
```

## Extensions

### Telescope Model Selector

If you have [telescope.nvim](https://github.com/nvim-telescope/telescope.nvim) installed, you can switch models on the fly via the Telescope picker:

```lua
vim.keymap.set("n", "<leader>9m", function()
  require("99.extensions.telescope").select_model()
end)
```

The selected model is used for all subsequent requests in the current session.

### Telescope Provider Selector

Switch between providers (OpenCode, Claude, Cursor, Kiro) without restarting Neovim. Switching provider also resets the model to that provider's default.

```lua
vim.keymap.set("n", "<leader>9p", function()
  require("99.extensions.telescope").select_provider()
end)
```

### fzf-lua

If you use [fzf-lua](https://github.com/ibhagwan/fzf-lua) instead of telescope, the same pickers are available:

```lua
vim.keymap.set("n", "<leader>9m", function()
  require("99.extensions.fzf_lua").select_model()
end)

vim.keymap.set("n", "<leader>9p", function()
  require("99.extensions.fzf_lua").select_provider()
end)
```

## Reporting a bug

To report a bug, please provide the full running debug logs. This may require
a bit of back and forth.

Please do not request features. We will hold a public discussion on Twitch about
features, which will be a much better jumping point then a bunch of requests that i have to close down. If you do make a feature request ill just shut it down instantly.

### The logs
To get the _last_ run's logs execute `:lua require("99").view_logs()`.

### Dont forget
If there are secrets or other information in the logs you want to be removed make
sure that you delete the `query` printing. This will likely contain information you may not want to share.

---

### Overview (`docs/README.md`)

## GENERATED CODE
Yes, this is non hand artisanally crafted code, it may be a bit nuts

---

### Package.Json (`docs/package.json`)

{
  "name": "99-docs",
  "private": true,
  "type": "module",
  "scripts": {
    "generate": "node --experimental-strip-types src/generate.ts",
    "check": "node --experimental-strip-types src/generate.ts --check"
  }
}

---

### Tsconfig.Json (`docs/tsconfig.json`)

{
  "compilerOptions": {
    "target": "ES2022",
    "module": "NodeNext",
    "moduleResolution": "NodeNext",
    "strict": true,
    "noEmit": true,
    "skipLibCheck": true,
    "verbatimModuleSyntax": true
  },
  "include": ["src/**/*.ts"]
}

---

--- METRICS ---
- Files Extracted: 4
- Estimated Token Budget: ~2880 tokens
- Canonical Reference: https://codewiki.google/github.com/ThePrimeagen/99
