How to Configure Custom SKILL.md Rules for AI Context in 99
To configure custom SKILL.md rules for AI context in 99, add your rule directories to the custom_rules list in the setup configuration, ensure each skill lives in its own subdirectory with a SKILL.md file, and trigger completion with the # character in any 99 prompt buffer.
ThePrimeagen's 99 is a Neovim plugin that enriches AI prompts with project-specific context through custom skill files. By configuring custom SKILL.md rules for AI context in 99, you can inject domain knowledge, coding standards, or framework-specific instructions directly into your AI workflow without leaving your editor.
Understanding the SKILL.md Workflow in 99
99 implements a three-stage pipeline for managing custom AI context:
- Discovery – During setup and refresh cycles, the plugin scans configured directories for
*/SKILL.mdpatterns usinghelpers.lsinlua/99/extensions/agents/helpers.lua. - Completion – When you type
#in a 99 prompt buffer, the completion provider queries the cached rule table and displays available skills with previews generated byhelpers.head. - Resolution – Upon sending the prompt,
get_rule_contentreads the full skill file, wraps it in XML-style tags (<skill_name>…</skill_name>), and injects it into the AI context.
All state management happens through _99_state.completion.custom_rules, initialized in lua/99/init.lua and refreshed via _99.State:refresh_rules.
Step-by-Step: Configure Custom SKILL.md Rules
1. Define Rule Directories in Setup
Configure the custom_rules option during plugin initialization in your Neovim configuration. In lua/99/init.lua, the _99.setup function expands paths and validates them before storing in global state.
require("99").setup({
completion = {
-- Directories containing skill subdirectories
custom_rules = {
"scratch/custom_rules/", -- Resolves to cwd/scratch/custom_rules/
vim.fn.expand("~/ai-skills/"), -- Absolute path with ~ expansion
},
source = "cmp", -- Use nvim-cmp for # completion
},
})
Paths support relative references, absolute paths, and ~ expansion for home directories.
2. Create the Directory Structure
Each skill requires its own subdirectory containing a SKILL.md file. The helpers.ls function in lua/99/extensions/agents/helpers.lua searches for both direct SKILL.md files and */SKILL.md patterns within configured directories.
scratch/custom_rules/
├─ vim/
│ └─ SKILL.md # Skill name: "vim"
├─ front-end/
│ └─ SKILL.md # Skill name: "front-end"
└─ backend/
└─ SKILL.md # Skill name: "backend"
Example SKILL.md content:
# vim
When generating code, prefer Vim motions for text manipulation.
Use registers for complex yank/paste operations.
Avoid arrow keys; use hjkl navigation.
3. Refresh and Verify Rules
After configuration changes, rules populate automatically during the next refresh cycle. The Agents.rules function in lua/99/extensions/agents/init.lua builds the rule table by aggregating results from all configured directories.
To verify your configuration programmatically:
local M = require("99.extensions.agents")
local rules = M.rules(_99_state)
print(vim.inspect(rules.custom))
-- Output: { "vim", "front-end", "backend" }
How 99 Discovers and Loads SKILL.md Files
The discovery mechanism relies on two core components in lua/99/extensions/agents/:
helpers.ls (lines 18-28): Expands directory globs to locate skill files. It handles both flat structures (SKILL.md in the root of the custom_rules folder) and nested structures (<skill>/SKILL.md).
Agents.rules (lines 29-38): Iterates through _99_state.completion.custom_rules, calls helpers.ls for each directory, and constructs a table containing name, path, and absolute_path for every discovered skill.
The system caches these results in the global state object, refreshing automatically when Neovim's working directory changes or when explicitly triggered via _99.State:refresh_rules.
Using Custom Rules in AI Prompts
Triggering Rule Completion
When editing a 99 prompt buffer, type # to trigger the completion menu. The completion provider in lua/99/extensions/agents/init.lua (lines 31-53) generates items by calling helpers.head on each rule file to extract the first few lines for the preview window.
- Label: Displays the skill name (directory name)
- Insert text: Inserts
#<relative_path_to_SKILL.md> - Documentation: Shows the header content of the skill file
Rule Resolution and Context Injection
When you submit the prompt, the get_rule_content function (lines 12-27 in lua/99/extensions/agents/init.lua) processes the inserted rule references:
- Parses the
#<path>pattern from the prompt text - Locates the corresponding
SKILL.mdfile using the cached rule table - Reads the entire file contents
- Wraps the content in XML-style tags:
<skill_name>…file contents…</skill_name> - Injects the wrapped content into the AI context before sending the request
This ensures the AI receives the full skill context without cluttering your visible prompt buffer with large text blocks.
Advanced Configuration Tips
Adding Rules at Runtime
You can dynamically extend your rule set without restarting Neovim:
-- Add a new directory to the configuration
table.insert(_99_state.completion.custom_rules, vim.fn.expand("~/new_skills/"))
-- Refresh the rule cache
_99_state:refresh_rules()
Multiple Directory Support
The custom_rules array accepts multiple paths, allowing you to organize skills by domain or share common rules across projects:
custom_rules = {
"project_specific/skills/",
vim.fn.expand("~/global_ai_skills/"),
"/opt/company_wide_rules/",
}
Summary
- Configure directories in
setup()using thecompletion.custom_rulesoption to declare where 99 should look for skill files. - Structure skills as
<skill_name>/SKILL.mdwithin those directories; 99 discovers them viahelpers.lsinlua/99/extensions/agents/helpers.lua. - Trigger completion by typing
#in any 99 prompt buffer; the completion provider inlua/99/extensions/agents/init.luasurfaces available skills. - Inject context automatically when sending prompts;
get_rule_contentwraps skill files in XML tags and inserts them into the AI context. - Refresh dynamically by calling
_99_state:refresh_rules()after modifyingcustom_rulesat runtime.
Frequently Asked Questions
Where does 99 store the custom rule configuration?
99 stores your custom rule directories in the global state object _99_state.completion.custom_rules, which is initialized during _99.setup() in lua/99/init.lua (lines 34-56). The actual skill files remain in your specified directories; 99 only caches references to their paths and metadata, not the file contents themselves.
Can I add new SKILL.md rules without restarting Neovim?
Yes. You can append new directories to _99_state.completion.custom_rules at runtime using table.insert(), then call _99_state:refresh_rules() to rebuild the rule cache. The Agents.rules function in lua/99/extensions/agents/init.lua will immediately pick up any new SKILL.md files in the added directories.
What format should the SKILL.md files follow?
SKILL.md files should follow standard Markdown syntax. The first few lines should contain a clear header or description, as helpers.head in lua/99/extensions/agents/helpers.lua extracts the beginning of the file for the completion preview. The full contents are injected verbatim into the AI context wrapped in <skill_name> XML tags, so structure the content as instructions or reference material for the AI.
How does 99 handle multiple custom_rules directories?
99 iterates through every path in the custom_rules array using Agents.rules in lua/99/extensions/agents/init.lua. For each directory, it calls helpers.ls to discover SKILL.md files. Rules from all directories are aggregated into a single table, with each skill identified by its directory name. You can organize skills by project, domain, or team by separating them into different directories in the configuration.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →