A lightweight, extensible chat plugin for Neovim with AI integration. Chat with AI assistants directly in your editor using a clean, floating window interface.
- ✨ Features
- 📦 Installation
- ⚙️ Usage
- 🤖 Providers
- Tools
- Available Tools
- Third-party Tools
- How to Use Tools
- Custom Tools
- 🔍 Picker Integration
- 📣 Self-Promotion
- 💬 Feedback
- 📄 License
- Multiple AI Providers: Built-in support for DeepSeek, GitHub AI, Moonshot, OpenRouter, Qwen, SiliconFlow, Tencent, and more
- Custom Provider Support: Easily add your own AI providers via Lua modules
- Tool Call Integration: Built-in tools for file operations (
@read_file,@find_files) with custom tool support - Floating Window Interface: Clean, non-intrusive chat interface
- Session Management: Run multiple parallel sessions with independent AI models, each maintaining separate conversation histories and configurations
- Picker Integration: Seamless integration with picker.nvim
- Streaming Responses: Real-time AI responses with cancellation support
- Lightweight: Pure Lua implementation with minimal dependencies
Using nvim-plug:
require('plug').add({
{
'wsdjeg/chat.nvim',
depends = { {
'wsdjeg/job.nvim',
} },
opt = {
provider = 'deepseek',
api_key = {
deepseek = 'xxxxx',
github = 'xxxxx',
},
width = 0.8, -- 80% of vim.o.columns
height = 0.8, -- 80% of vim.o.lines
border = 'rounded',
-- default allowed_path is empty string, which means no files is allowed.
-- this also can be a table of string.
allowed_path = '',
},
},
})chat.nvim provides several commands to manage your AI conversations.
The main command is :Chat, which opens the chat window.
You can also navigate between sessions using the following commands.
| Command | Description |
|---|---|
:Chat |
Open the chat window with the current session |
:Chat new |
Start a new chat session |
:Chat prev |
Switch to the previous chat session |
:Chat next |
Switch to the next chat session |
:Chat delete |
Delete current session and create new empty session |
:Chat clear |
Clear all messages in current session |
:Chat cd <dir> |
Change current session cwd, open chat window |
chat.nvim supports running multiple chat sessions simultaneously, with each session operating independently:
- Independent Model Selection: Each session can use a different AI model (e.g., Session A with DeepSeek, Session B with GitHub AI)
- Separate Contexts: Sessions maintain their own conversation history, working directory, and settings
- Quick Switching: Use
:Chat prevand:Chat nextto navigate between active sessions - Isolated Workflows: Perfect for comparing model responses or working on multiple projects simultaneously
Workflow Example:
- Start a session with DeepSeek:
:Chat new(then select DeepSeek model) - Switch to GitHub AI for a different task:
:Chat new(select GitHub model) - Toggle between sessions:
:Chat prev/:Chat next - Each session preserves its unique context and conversation flow
-
Start a new conversation:
:Chat newThis creates a fresh session and opens the chat window.
-
Resume a previous conversation:
:Chat prevCycles backward through your saved sessions.
-
Switch to the next conversation:
:Chat nextCycles forward through your saved sessions.
-
Open or forced to the chat window:
:Chat
This command will not change current sessions.
-
Delete current session:
:Chat deleteCycles to next session or create a new session if current session is latest one.
-
Change the working directory of current session:
:Chat cd ../picker.nvim/
If the current session is in progress, the working directory will not be changed, and a warning message will be printed.
-
Clear messages in current session:
:Chat clearIf the current session is in progress, a warning message will be printed, and current session will not be cleared. This command also will forced to chat window.
-
Work with multiple parallel sessions:
" Start first session with DeepSeek :Chat new " Select DeepSeek as provider and choose a model " Start second session with GitHub AI :Chat new " Select GitHub as provider and choose a model " Switch between sessions :Chat prev " Go to first session :Chat next " Go to second session
This enables simultaneous conversations with different AI assistants for different tasks.
All sessions are automatically saved and can be resumed later. For more advanced session management, see the Picker Integration section below.
Note: The plugin is currently in active development phase. Key bindings may change and may reflect the author's personal preferences. Configuration options for customizing key bindings are planned for future releases.
The following key bindings are available in the Input window:
| Mode | Key Binding | Description |
|---|---|---|
Normal |
<Enter> |
Send message |
Normal |
q |
Close chat window |
Normal |
<Tab> |
Switch between input and result windows |
Normal |
Ctrl-C |
Cancel current request |
Normal |
Ctrl-N |
Open new session |
Normal |
r |
Retry last cancelled request |
Normal |
alt-h |
previous chat session |
Normal |
alt-l |
next chat session |
Normal |
<Leaer>fr |
run :Picker chat |
Normal |
<Leaer>fp |
run :Picker chat_provider |
Normal |
<Leader>fm |
run :Picker chat_model |
The following key bindings are available in the Result window:
| Mode | Key Binding | Description |
|---|---|---|
Normal |
q |
Close chat window |
Normal |
<Tab> |
Switch between input and result windows |
deepseek- DeepSeek AIgithub- GitHub AImoonshot- Moonshot AIopenrouter- OpenRouterqwen- Alibaba Cloud Qwensiliconflow- SiliconFlowtencent- Tencent Hunyuanbigmodel- BigModel AIvolcengine- Volcengine AI
chat.nvim also supports custom provider, just create lua/chat/providers/<provider_name>.lua, this lua module
should provides two functions request and available_models,
here is an example for using free_chatgpt_api
file: ~/.config/nvim/lua/chat/provides/free_chatgpt_api.lua
local M = {}
local job = require('job')
local sessions = require('chat.sessions')
local config = require('chat.config')
function M.available_models()
return {
'gpt-4o-mini',
}
end
function M.request(requestObj)
local cmd = {
'curl',
'-s',
'https://free.v36.cm/v1/chat/completions',
'-H',
'Content-Type: application/json',
'-H',
'Authorization: Bearer ' .. config.config.api_key.free_chatgpt,
'-X',
'POST',
'@-',
}
local body = vim.json.encode({
model = sessions.get_session_model(opt.session),
messages = opt.messages,
thinking = {
type = 'enabled',
},
stream = true,
stream_options = { include_usage = true },
tools = require('chat.tools').available_tools(),
})
local jobid = job.start(cmd, {
on_stdout = opt.on_stdout,
on_stderr = opt.on_stderr,
on_exit = opt.on_exit,
})
job.send(jobid, body)
job.send(jobid, nil)
sessions.set_session_jobid(opt.session, jobid)
return jobid
end
return Mchat.nvim supports tool call functionality, allowing the AI assistant to interact with your filesystem, manage memories, and perform other operations during conversations. Tools are invoked using the @tool_name syntax directly in your messages.
Reads the content of a file and makes it available to the AI assistant.
Usage:
@read_file <filepath>
Examples:
@read_file ./src/main.lua- Read a Lua file in the current directory@read_file /etc/hosts- Read a system file using absolute path@read_file ../config.json- Read a file from a parent directory
Advanced Usage with Line Ranges:
@read_file ./src/main.lua line_start=10 line_to=20
Notes:
- File paths can be relative to the current working directory or absolute
- Supports line range selection with
line_startandline_toparameters - Line numbers are 1-indexed (first line is line 1)
- If
line_startis not specified, defaults to line 1 - If
line_tois not specified, defaults to last line - The AI will receive the file content for context
- This is particularly useful for code review, debugging, or analyzing configuration files
Finds files in the current working directory that match a given pattern.
Usage:
@find_files <pattern>
Examples:
@find_files *.lua- Find all Lua files in the current directory@find_files **/*.md- Recursively find all Markdown files@find_files src/**/*.js- Find JavaScript files in thesrcdirectory and its subdirectories@find_files README*- Find files starting with "README"
Notes:
- The pattern follows Vim's
globpathsyntax - Searches are limited to the current working directory
- Returns a list of found files, with one file path per line
- Returns a message if no files are found based on the given pattern
- File searching is restricted by the
allowed_pathconfiguration setting
Advanced text search tool using ripgrep (rg) to search text content in directories with regex support, file type filtering, exclusion patterns, and other advanced features.
Usage:
@search_text <pattern> [options]
Basic Examples:
@search_text "function.*test"- Search for regex pattern in current directory@search_text "TODO:" --file-types "*.lua"- Search TODO comments in Lua files@search_text "error" --context-lines 2- Search for "error" with 2 lines of context
Advanced Usage with JSON Parameters:
For more complex searches, you can provide a JSON object with multiple parameters:
@search_text {"pattern": "function.*test", "directory": "./src", "file_types": ["*.lua", "*.vim"], "ignore_case": true, "max_results": 50}
Parameters:
| Parameter | Type | Description |
|---|---|---|
pattern |
string | Required. Text pattern to search for (supports regex) |
directory |
string | Directory path to search in (default: current working directory) |
ignore_case |
boolean | Whether to ignore case (default: false) |
regex |
boolean | Whether to use regex (default: true) |
max_results |
integer | Maximum number of results (default: 100) |
context_lines |
integer | Number of context lines to show around matches (default: 0) |
whole_word |
boolean | Whether to match whole words only (default: false) |
file_types |
array | File type filter, e.g., ["*.py", "*.md", "*.txt"] |
exclude_patterns |
array | Exclude file patterns, e.g., ["*.log", "tmp/*"] |
More Examples:
-
Case-insensitive search:
@search_text {"pattern": "config", "ignore_case": true} -
Search with file type filtering:
@search_text {"pattern": "function", "file_types": ["*.lua", "*.vim"]} -
Search with context and exclusions:
@search_text {"pattern": "FIXME", "context_lines": 3, "exclude_patterns": ["*.log", "node_modules/*"]} -
Whole word matching:
@search_text {"pattern": "test", "whole_word": true}
Notes:
- Uses ripgrep (rg) for fast, powerful text searching
- Supports full regex syntax for complex pattern matching
- Search is restricted by the
allowed_pathconfiguration setting - Returns matching lines with file paths and line numbers
- If no matches are found, returns an informative message
- Particularly useful for code analysis, debugging, and finding references
Extract long-term memories from conversation text, focusing ONLY on factual information and habitual patterns. Filters out subjective feelings, temporary states, and irrelevant chatter.
Usage:
@extract_memory <parameters>
Examples:
@extract_memory text="Python的GIL是全局解释器锁,我习惯用Vim写代码" category="fact"@extract_memory text="我每天早晨6点起床锻炼,通常下午3点喝咖啡" category="preference"
Parameters:
| Parameter | Type | Description |
|---|---|---|
text |
string | Text to analyze for memory extraction |
memories |
array | Pre-extracted memories array (alternative to text parameter) |
category |
string | Suggested category: "fact", "preference", "skill", or "event" |
Category Definitions:
- fact: Verifiable objective facts, data, definitions, rules
- preference: Personal habits, routine behaviors, regular practices
- skill: Technical abilities and knowledge
- event: Specific events and occurrences
Notes:
- Extracts only persistent and reusable information
- Automatically detects categories based on keywords
- Supports both raw text analysis and pre-processed memories
- Memory system must be enabled in chat.nvim configuration
Retrieve relevant information from long-term memory and add to current conversation. Automatically extracts keywords if no query is provided.
Usage:
@recall_memory <parameters>
Examples:
@recall_memory query="vim configuration"@recall_memory query="programming tips" limit=8@recall_memory(automatically extracts keywords from current conversation)
Parameters:
| Parameter | Type | Description |
|---|---|---|
query |
string | Search query (optional, auto-extracted if not provided) |
limit |
integer | Number of results (default: 5, maximum: 10) |
all_sessions |
boolean | Search all sessions instead of just current (default: false) |
Notes:
- Returns formatted memory list that AI can reference for responses
- Searches across categories and content
- Shows timestamps and contextual information
- Memory system must be enabled in chat.nvim configuration
- Useful for maintaining context across conversations
Read a prompt file and set it as the current session's system prompt.
Usage:
@set_prompt <filepath>
Examples:
@set_prompt ./AGENTS.md@set_prompt ./prompts/code_review.txt@set_prompt ~/.config/chat.nvim/default_prompt.md
Parameters:
| Parameter | Type | Description |
|---|---|---|
filepath |
string | Path to prompt file |
Notes:
- Updates the current session's system prompt with file content
- File must be within the
allowed_pathconfigured in chat.nvim - Useful for switching between different agent roles or task-specific prompts
- Supports relative and absolute paths
Create new zettelkasten notes, provided by zettelkasten.nvim.
Usage:
@zettelkasten_create <parameters>
Parameters:
| Parameter | Type | Description |
|---|---|---|
title |
string | The title of zettelkasten note |
content |
string | The note body of zettelkasten |
tags |
array | Optional tags for the note (max 3) |
Notes:
- Creates a new zettelkasten note with specified title and content
- Tags should be in English and limited to 3 to avoid synonyms
- Integration with zettelkasten.nvim plugin
Retrieve zettelkasten notes by tags, provided by zettelkasten.nvim.
Usage:
@zettelkasten_get <tags>
Parameters:
| Parameter | Type | Description |
|---|---|---|
tags |
array | Tags to search for (e.g., ["programming", "vim"]) |
Notes:
- Returns JSON object containing matching notes
- Each note includes
file_nameandtitlefields - Tags should be in English
- Integration with zettelkasten.nvim plugin
-
Direct invocation: Include the tool call directly in your message:
Can you review this code? @read_file ./my_script.lua -
Multiple tools: Combine multiple tools in a single message:
Compare these two configs: @read_file ./config1.json @read_file ./config2.json -
Natural integration: The tool calls can be embedded naturally within your questions:
What's wrong with this function? @read_file ./utils.lua -
Memory management: Use memory tools for context-aware conversations:
Based on what we discussed earlier about Vim: @recall_memory query="vim"
The AI assistant will process the tool calls, execute the specified operations, and incorporate their results into its response. This enables more context-aware assistance without needing to manually copy-paste file contents or repeat previous information.
chat.nvim also supports custom tools. Users can create lua/chat/tools/<tool_name>.lua file in their Neovim runtime path.
This module should provide at least two functions: scheme() and <tool_name> function. The scheme() function returns a table describing the tool's schema (name, description, parameters). The <tool_name> function is the actual implementation that will be called when the tool is invoked.
The tools.lua module automatically discovers all tools in the lua/chat/tools/ directory and provides an available_tools() function to list them, and a call(func, arguments) function to invoke a specific tool.
Here is an example for a get_weather tool:
local M = {}
---@param action { city: string, unit?: string }
function M.get_weather(action)
if not action.city or action.city == '' then
return {
error = 'City name is required for weather information.',
}
end
local unit = action.unit or 'celsius'
local valid_units = { 'celsius', 'fahrenheit' }
if not vim.tbl_contains(valid_units, unit) then
return {
error = 'Unit must be either "celsius" or "fahrenheit".',
}
end
-- Simulate fetching weather data (in a real implementation, you would call an API here)
local temperature = math.random(15, 35) -- Random temperature between 15°C and 35°C
local conditions = { 'Sunny', 'Cloudy', 'Rainy', 'Partly Cloudy', 'Windy' }
local condition = conditions[math.random(1, #conditions)]
-- Convert temperature if needed
if unit == 'fahrenheit' then
temperature = math.floor((temperature * 9/5) + 32)
end
return {
content = string.format(
'Weather in %s:\n- Temperature: %d°%s\n- Condition: %s\n- Humidity: %d%%\n- Wind Speed: %d km/h',
action.city,
temperature,
unit == 'celsius' and 'C' or 'F',
condition,
math.random(40, 90),
math.random(5, 25)
),
}
end
function M.scheme()
return {
type = 'function',
['function'] = {
name = 'get_weather',
description = 'Get weather information for a specific city. Use @get_weather {city: "City Name"} to get weather details.',
parameters = {
type = 'object',
properties = {
city = {
type = 'string',
description = 'City name for weather information',
},
unit = {
type = 'string',
description = 'Temperature unit: "celsius" or "fahrenheit"',
enum = { 'celsius', 'fahrenheit' },
},
},
required = { 'city' },
},
},
}
end
return Mchat.nvim provides built-in picker sources for seamless integration with picker.nvim. These sources allow you to quickly access and manage your chat sessions, providers, and models.
Note: The chat picker source displays all your active sessions, allowing quick switching between parallel conversations with different models.
Available Sources:
-
chat- Search through your chat history sessions -
chat_provider- Switch between different AI providers -
chat_model- Select available models for the current provider
Like this plugin? Star the repository on GitHub.
Love this plugin? Follow me on GitHub.
If you encounter any bugs or have suggestions, please file an issue in the issue tracker.
This project is licensed under the GPL-3.0 License.



