llm-nvim
Neovim plugin for llm CLI
Links
README
From the repo.
llm-nvim
A Neovim plugin for integrating with Simon Willison's llm CLI tool.
Feature Demos
Model, Plugin and Key Management
https://github.com/user-attachments/assets/d8c9b2f8-4617-4534-9a64-05a2447d9380
LLM Chat
Feature List
- Unified LLM command interface (
:LLM) - Interactive prompting with fragments support
- Process selected text or entire files with LLMs
- Explain code in current buffer
- Support for custom models and system prompts
- API key management for multiple providers
- Fragment management (files, URLs, GitHub repos)
- Unified manager window (
:LLMConfig) with views for:- Models
- Plugins
- API Keys
- Fragments
- Markdown-formatted responses with syntax highlighting
- Asynchronous command execution
Requirements
- Neovim 0.7.0 or later
- Lua 5.2+ (Neovim bundles LuaJIT 2.1+ which is compatible)
- llm CLI tool installed (
pip install llmorbrew install llm)
Lua Compatibility Note
This plugin uses Lua 5.2+ APIs (table.unpack) for forward compatibility. Neovim bundles LuaJIT 2.1+ which provides these APIs, so no additional Lua installation is required.
Installation
Using vim-plug
Plug 'julwrites/llm-nvim'
Using packer.nvim
use 'julwrites/llm-nvim'
Using lazy.nvim
-- Example lazy.nvim configuration
return {
{
'julwrites/llm-nvim',
-- Optional: Specify dependencies if needed, e.g., for UI components
-- dependencies = { 'nvim-lua/plenary.nvim' },
config = function()
-- Configure the plugin
require('llm').setup({
-- Specify the default LLM model to use
model = 'gpt-4o', -- Or 'claude-3-haiku-20240307', 'llama3', etc.
-- Define a default system prompt (optional)
system_prompt = 'You are a helpful Neovim assistant.',
-- Disable default key mappings if you prefer to set your own
-- no_mappings = true,
-- Enable debug logging (optional)
-- debug = true,
-- Enable or disable automatic updates for the underlying `llm` CLI tool.
-- Defaults to `false`.
-- auto_update_cli = false,
-- Set the interval in days for checking for `llm` CLI updates.
-- Defaults to `7`.
-- auto_update_interval_days = 7,
})
-- Example custom key mappings (if no_mappings = true or for overrides)
-- vim.keymap.set('n', '<leader>lp', '<Plug>(llm-prompt)', { desc = "LLM Prompt" })
-- vim.keymap.set('v', '<leader>ls', '<Plug>(llm-selection)', { desc = "LLM Selection" })
-- vim.keymap.set('n', '<leader>lt', '<Plug>(llm-toggle)', { desc = "LLM Toggle Manager" })
end
}
}
Configuration
-- Setup with configuration options
require('llm').setup({
model = 'gpt-4o', -- Default model to use
system_prompt = 'You are a helpful assistant.', -- Default system prompt
no_mappings = false, -- Set to true to disable default mappings
debug = false, -- Set to true to enable debug output
auto_update_cli = false, -- Enable/disable CLI auto-updates (default: false)
auto_update_interval_days = 7, -- Interval in days for CLI update checks (default: 7)
})
-- Custom mappings
vim.keymap.set('n', '<leader>lp', '<Plug>(llm-prompt)')
vim.keymap.set('v', '<leader>ls', '<Plug>(llm-selection)')
vim.keymap.set('n', '<leader>le', '<Plug>(llm-explain)')
vim.keymap.set('n', '<leader>lm', '<Plug>(llm-models)') -- Note: <Plug>(llm-select-model) is deprecated
Automatic CLI Updates
The plugin includes a feature to automatically check for updates to the llm command-line tool upon startup.
- When enabled via the
auto_update_cli = truesetting, the plugin will check if the configuredauto_update_interval_dayshas passed since the last check. - If an update check is due, it will attempt to update the
llmCLI tool. The update mechanism tries common upgrade methods includinguv tool upgrade llm,pipx upgrade llm,pip install --upgrade llm(andpython -m pip install --upgrade llm), andbrew upgrade llmto keep the tool current. - This check runs asynchronously in the background to avoid impacting Neovim's startup time.
- You will receive a notification about the outcome of the update attempt (success or failure).
This helps ensure your llm tool stays up-to-date with the latest features and fixes.
Usage Examples
Commands
Unified LLM Command
:LLM {prompt}- Send prompt to LLM:LLM file [{prompt}]- Send current file's content with optional prompt:LLM selection [{prompt}]- Send visual selection with optional prompt:LLM explain- Explain current buffer's code:LLM fragments- Interactive prompt with fragment selection:LLM update- Manually trigger an update check for the underlyingllmCLI tool.
Unified Manager
:LLMConfig [view]- Open unified manager window- Optional views:
models,plugins,keys,fragments
- Optional views:
:LLMConfig models- Open Models view:LLMConfig plugins- Open Plugins view:LLMConfig keys- Open API Keys view:LLMConfig fragments- Open Fragments view
Basic Prompting
- Type
:LLM Write a short poem about Neovimand press Enter. - A new buffer will open with the LLM's response.
Working with Code
- Visually select a block of code.
- Type
:LLMWithSelection Refactor this code for clarityand press Enter. - The selected code and your prompt will be sent to the LLM.
Explaining Code
- Open a code file.
- Type
:LLMExplainand press Enter. - The LLM will explain the code in the current buffer.
Using the Unified Manager
- Type
:LLMConfigor press<leader>ll(default mapping). - The manager window opens, likely showing the Models view first.
- Press
Pto switch to the Plugins view. - Press
Kto switch to the API Keys view. - Navigate the list using
jandk. - Follow the instructions in the header for actions (e.g., press
sin the Models view to set a default model). - Press
qor<Esc>to close the manager.
Suggested Mappings
The plugin provides default key mappings that can be disabled with no_mappings = true. Here are the default mappings and some suggested alternatives:
-- Toggle unified manager
vim.keymap.set('n', '<leader>ll', '<Cmd>LLMConfig<CR>', { desc = "Toggle LLM Manager" })
-- Basic prompt
vim.keymap.set('n', '<leader>lp', '<Cmd>LLM<Space>', { desc = "LLM Prompt" })
-- Explain current buffer
vim.keymap.set('n', '<leader>le', '<Cmd>LLM explain<CR>', { desc = "Explain Code" })
-- Prompt with visual selection
vim.keymap.set('v', '<leader>ls', '<Cmd>LLM selection<CR>', { desc = "LLM Selection" })
-- Interactive fragments
vim.keymap.set('n', '<leader>lf', '<Cmd>LLM fragments<CR>', { desc = "LLM with Fragments" })
Development
Testing
The plugin includes a test suite using busted. To run the tests, you first need to install the test dependencies:
sudo luarocks install busted
sudo luarocks install luassert
Then, you can run the tests:
# Run all tests
make test
# Run a specific test file
make test file=init_spec.lua
Tests cover:
- Core functionality (prompts, code explanation)
- Model management
- Plugin management
- API key management
- Fragment management
Code Coverage
This project enforces a minimum code coverage of 70%. The CI/CD pipeline will fail if the coverage drops below this threshold. To run the coverage check locally, you first need to install the coverage dependencies:
sudo luarocks install luacov
sudo luarocks install luacov-console
Then, you can run the coverage check:
make coverage
This will run the tests with Luacov and print a coverage report to the console.
Troubleshooting
Lua Compatibility Errors
If you encounter errors like "attempt to call a nil value (global 'unpack')":
- Ensure you're using Neovim 0.7.0 or later
- Check your Lua version:
:lua print(_VERSION)in Neovim - Update Neovim if you're using an older version
- Report the issue with your Neovim and Lua versions if problems persist
The plugin requires Lua 5.2+ APIs which are provided by Neovim's bundled LuaJIT 2.1+.
Documentation
For more detailed documentation, including architecture and contribution guides, see the Documentation Directory.
License
Apache 2.0
Collected info
- ★ 9 stars
- Language: Lua
- Source updated: 9/23/2026
Config for your environment
Replace {MCP_ENDPOINT_URL} with this MCP’s endpoint URL (from its repo or docs above). No API key — you connect directly.
Tool
OS
Config file: ~/.cursor/mcp.json
{
"mcpServers": {
"mcp-server": {
"url": "{MCP_ENDPOINT_URL}"
}
}
}Paste into mcpServers in the config file. Restart Cursor after saving.
If this MCP is also published on mcpchannel.ai, you can subscribe from Browse and use the gateway config there instead.