# How 99's #rules Autocomplete System Works Internally: A Deep Dive into the Completion Framework

> Explore the internal workings of 99s #rules autocomplete system. Discover how its completion framework leverages SKILL.md files and LLM prompts for efficient rule discovery and injection.

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

---

**The #rules autocomplete system in 99 uses a provider-based completion framework that triggers on the `#` character, discovers custom rules from SKILL.md files, and injects rule content into LLM prompts via pattern matching and resolution callbacks.**

The #rules autocomplete system is a core feature of ThePrimeagen's 99 plugin that streamlines prompt engineering by allowing users to reference external rule files using the `#` trigger. This system is built on a modular completion framework located in [`lua/99/extensions/completions.lua`](https://github.com/ThePrimeagen/99/blob/main/lua/99/extensions/completions.lua) that manages provider registration, trigger detection, and content resolution.

## The Completion Provider Architecture

The autocomplete functionality is implemented as a **completion provider** within the agents extension. Each provider declares a trigger character, item generation logic, validation, and resolution capabilities.

### Trigger Characters and Provider Structure

In [`lua/99/extensions/agents/init.lua`](https://github.com/ThePrimeagen/99/blob/main/lua/99/extensions/agents/init.lua), the `completion_provider` function returns a structured table that defines how the `#` trigger behaves:

```lua
function M.completion_provider(_99)
  return {
    trigger   = "#",
    name      = "rules",
    get_items = function() … end,
    is_valid  = function(token) return M.is_rule(_99.rules, token) end,
    resolve   = function(token) … end,
  }
end

```

The **trigger** field specifies that typing `#` activates this provider. The **get_items** callback generates the completion list, while **is_valid** checks if a token corresponds to an actual rule, and **resolve** fetches the rule's full content.

### Registration with the Completion Manager

The provider registers itself with the central completion system in [`lua/99/extensions/completions.lua`](https://github.com/ThePrimeagen/99/blob/main/lua/99/extensions/completions.lua):

```lua
Completions.register(Agents.completion_provider(_99))

```

This inserts the provider into the internal `providers` array, making it available for trigger detection and item generation throughout the editing session.

## Rule Discovery and Indexing

Before autocomplete can suggest rules, the system must discover and index available rule files from the filesystem.

### Scanning Custom Rule Directories

Rule discovery happens at startup via [`lua/99/init.lua`](https://github.com/ThePrimeagen/99/blob/main/lua/99/init.lua). The system creates a global state that includes `state.completion.custom_rules`, which contains paths to user-defined rule directories.

The `Agents.rules` function in [`lua/99/extensions/agents/init.lua`](https://github.com/ThePrimeagen/99/blob/main/lua/99/extensions/agents/init.lua) iterates over these paths:

```lua
self.rules = Agents.rules(self)

```

For each path in `custom_rules`, the system calls `helpers.ls` to scan for directories containing rule definitions.

### Parsing SKILL.md Files

The `helpers.ls` function in [`lua/99/extensions/agents/helpers.lua`](https://github.com/ThePrimeagen/99/blob/main/lua/99/extensions/agents/helpers.lua) handles the actual filesystem traversal. It expands paths and looks for [`SKILL.md`](https://github.com/ThePrimeagen/99/blob/main/SKILL.md) files or directories containing them:

```lua
{
  name = "my_rule",
  path = "rules/my_rule",
  absolute_path = "/abs/.../SKILL.md"
}

```

Each discovered rule becomes an object containing its directory name, relative path, and absolute path to the skill definition.

### Building the Rule Index

The discovered rules are stored in `state.rules.custom` as a list. Additionally, a name-to-rule mapping (`by_name`) is constructed for O(1) lookups during validation and resolution phases.

## Generating LSP-Style Completion Items

When the user types `#`, the completion provider generates LSP-compatible completion items that Neovim can display in the popup menu.

### Constructing Completion Objects

The `get_items` callback in the rules provider iterates over the indexed rules and builds LSP completion items:

```lua
{
  label        = rule.name,
  insertText   = "#" .. rule.path,
  filterText   = "#" .. rule.name,
  kind         = 12,
  documentation= { kind = "markdown", value = docs },
  detail       = "Rule: " .. rule.path,
}

```

The **label** displays the rule name, while **insertText** determines what gets inserted into the buffer (including the `#` prefix). The **filterText** enables searching by name, and **kind** categorizes the item as a value type (12).

### Preview Extraction with helpers.head

To provide helpful context in the completion popup, the system uses `helpers.head` from [`lua/99/extensions/agents/helpers.lua`](https://github.com/ThePrimeagen/99/blob/main/lua/99/extensions/agents/helpers.lua). This function reads the first few lines of the [`SKILL.md`](https://github.com/ThePrimeagen/99/blob/main/SKILL.md) file and includes them in the `documentation` field as markdown content.

## Resolving #rules Tokens in Prompts

The autocomplete system doesn't just insert text—it ensures that when a prompt is sent to the LLM, the rule content is properly injected.

### Pattern Matching and Token Extraction

When processing a prompt, `Completions.parse` in [`lua/99/extensions/completions.lua`](https://github.com/ThePrimeagen/99/blob/main/lua/99/extensions/completions.lua) scans the text for registered trigger patterns:

```lua
local pattern = provider.trigger:gsub("([%%%^%$%(%)%.%[%]%*%+%-%?])","%%%1") .. "%S+"
for word in prompt_text:gmatch(pattern) do
  local token = word:sub(#provider.trigger + 1)
  if provider.is_valid(token) then
    local content = provider.resolve(token)
    if content then table.insert(refs, {content = content}) end
  end
end

```

The pattern escapes special regex characters from the trigger (here `#`) and matches any non-whitespace sequence following it. The extracted **token** (the text after `#`) is validated against the rule index.

### Content Injection and Tag Wrapping

For valid tokens, the resolver fetches the rule content:

```lua
local rule = M.get_rule_by_path(_99.rules, token)
return M.get_rule_content(rule)

```

The `get_rule_content` function reads the [`SKILL.md`](https://github.com/ThePrimeagen/99/blob/main/SKILL.md) file and wraps it in XML-style tags:

```lua
"<" .. rule.name .. ">\n" .. file_content .. "\n</" .. rule.name .. ">"

```

This structured format helps the LLM identify and utilize the rule context within the prompt.

## End-to-End Workflow

Understanding the complete flow clarifies how the components interact:

1. **Startup**: [`lua/99/init.lua`](https://github.com/ThePrimeagen/99/blob/main/lua/99/init.lua) loads custom rule paths and calls `Agents.rules()`, which scans directories via `helpers.ls()` and stores results in `state.rules.custom`.

2. **Registration**: The agents extension registers its provider with `Completions.register()`, enabling the `#` trigger.

3. **Completion**: When the user types `#`, Neovim queries `provider.get_items()`, which builds LSP items from the indexed rules using metadata extracted by `helpers.head()`.

4. **Validation**: Upon prompt submission, `Completions.parse()` extracts tokens using regex pattern matching, validates them via `provider.is_valid()`, and resolves content through `provider.resolve()`.

5. **Injection**: The resolved content, wrapped in XML-style tags, is inserted into the prompt sent to the LLM.

## Practical Implementation Examples

**Adding a custom rule directory:**

```lua
require("99").setup({
  completion = {
    custom_rules = { "~/.config/nvim/99/rules" },
  },
})

```

**Triggering completion in a buffer:**

1. Type `#` in any buffer.
2. Select a rule from the popup (e.g., `#git_commit`).
3. The buffer now contains the token `#git_commit`.

**Programmatically resolving a rule:**

```lua
local content = require("99.extensions.agents").completion_provider(state)
                 .resolve("git_commit")

-- Returns: "<git_commit>\n<file contents>\n</git_commit>"

```

**Using the parsed references programmatically:**

```lua
local prompt = "Please follow #git_commit guidelines."
local refs   = require("99.extensions.completions").parse(prompt)

for _, ref in ipairs(refs) do
  print(ref.content)   -- Full rule content with XML tags
end

```

## Key Files and Their Responsibilities

| File | Role |
|------|------|
| [`lua/99/extensions/completions.lua`](https://github.com/ThePrimeagen/99/blob/main/lua/99/extensions/completions.lua) | Core completion manager; registers providers, builds trigger patterns, parses prompts. |
| [`lua/99/extensions/agents/init.lua`](https://github.com/ThePrimeagen/99/blob/main/lua/99/extensions/agents/init.lua) | Implements the `#` provider: discovers rules, builds LSP items, validates and resolves tokens. |
| [`lua/99/extensions/agents/helpers.lua`](https://github.com/ThePrimeagen/99/blob/main/lua/99/extensions/agents/helpers.lua) | Helpers for path normalization, rule discovery (`ls`), and preview extraction (`head`). |
| [`lua/99/init.lua`](https://github.com/ThePrimeagen/99/blob/main/lua/99/init.lua) | Bootstrap: creates global state, loads custom rules, registers the agents provider. |
| [`lua/99/test/completions_spec.lua`](https://github.com/ThePrimeagen/99/blob/main/lua/99/test/completions_spec.lua) | Test suite confirming that `#` completions are registered and parsed correctly. |

## Summary

- The `#rules` autocomplete system uses a **provider-based architecture** centered in [`lua/99/extensions/completions.lua`](https://github.com/ThePrimeagen/99/blob/main/lua/99/extensions/completions.lua).
- Rule discovery happens at startup via `Agents.rules()`, which scans `custom_rules` paths for [`SKILL.md`](https://github.com/ThePrimeagen/99/blob/main/SKILL.md) files using `helpers.ls()`.
- The completion provider registers the `#` trigger and generates **LSP-compatible completion items** with metadata extracted by `helpers.head()`.
- When prompts are processed, `Completions.parse()` uses regex pattern matching to extract tokens, validates them against the rule index, and resolves content by reading [`SKILL.md`](https://github.com/ThePrimeagen/99/blob/main/SKILL.md) files and wrapping them in XML-style tags.
- This separation of concerns between discovery, presentation, and resolution makes the system modular and extensible.

## Frequently Asked Questions

### How does 99 discover custom rules for the # autocomplete?

During initialization in [`lua/99/init.lua`](https://github.com/ThePrimeagen/99/blob/main/lua/99/init.lua), the system reads paths from `state.completion.custom_rules` and invokes `Agents.rules()`. This function iterates over each path and calls `helpers.ls()` to scan for directories containing [`SKILL.md`](https://github.com/ThePrimeagen/99/blob/main/SKILL.md) files. Each valid discovery is stored as a rule object in `state.rules.custom` with `name`, `path`, and `absolute_path` fields.

### What file format does 99 expect for rule definitions?

The system expects rules to be defined in files named [`SKILL.md`](https://github.com/ThePrimeagen/99/blob/main/SKILL.md) located within directories. When a rule is resolved, the provider reads the entire contents of this file and wraps it in XML-style tags using the rule's directory name (e.g., `<rule_name>\n...content...\n</rule_name>`) before injecting it into the LLM prompt.

### How does the completion provider integrate with Neovim's LSP?

The provider generates LSP-compatible completion items in the `get_items()` callback, setting standard fields such as `label` (rule name), `insertText` (the `#` prefixed path), `filterText` (searchable text), `kind` (12 for Value), and `documentation` (markdown preview). Neovim's built-in completion engine consumes these items directly, displaying them when the user types the `#` trigger character.

### Can I use the #rules system programmatically outside of autocomplete?

Yes. You can manually resolve rules or parse prompts using the extension's API. For example, call `require("99.extensions.agents").completion_provider(state).resolve("rule_name")` to fetch the wrapped content directly, or use `require("99.extensions.completions").parse(prompt_text)` to extract and resolve all `#` tokens from a string programmatically.