⥠A blazingly fast Chat interface for ACP providers in Neovim
Agentic.nvim brings your AI assistant to Neovim through the implementation of the Agent Client Protocol (ACP).
You'll get the same results and performance as you would when using the ACP provider's official CLI directly from the terminal.
There're no hidden prompts or magic happening behind the scenes. Just a Chat interface, your colors, and your keymaps.
claude /login, or gemini auth login
once and, if they're working on your Terminal, they will work automatically
on Agentic./ in the prompt as the first character@ to trigger autocomplete for workspace files@file1.lua @file2.luaDefault, Auto Accept, Plan mode, etc... (depends on the provider)https://github.com/user-attachments/assets/4b33bb18-95f7-4fea-bc12-9a9208823911
https://github.com/user-attachments/assets/96a11aae-3095-46e7-86f1-ccc02d21c04f
https://github.com/user-attachments/assets/b6b43544-a91e-407f-834e-4b4de41259f8
@ to fuzzy find any file:https://github.com/user-attachments/assets/c6653a8b-20ef-49c8-b644-db0df1b342f0
We recommend using pnpmpnpm uses a constant, static global path, that's resilient to updates.
While npm loses global packages every time you change Node versions using
tools like nvm, fnm, etc...
You are free to chose any installation method you prefer!
| Provider | Install |
|---|---|
| claude-code-acp | pnpm add -g @zed-industries/claude-code-acp OR npm i -g @zed-industries/claude-code-acp OR Download binary |
| gemini-cli | pnpm add -g @google/gemini-cli OR npm i -g @google/gemini-cli OR brew install --cask gemini |
| codex-acp | pnpm add -g @zed-industries/codex-acp OR npm i -g @zed-industries/codex-acp OR Download binary |
| opencode | pnpm add -g opencode-ai OR npm i -g opencode-ai OR brew install opencode OR curl -fsSL https://opencode.ai/install | bash |
| cursor-agent | pnpm add -g @blowmage/cursor-agent-acp OR npm i -g @blowmage/cursor-agent-acp |
[!WARNING]
These install commands are here for convenience, please always refer to the official installation instructions from the respective ACP provider.
[!NOTE]
Why install ACP provider CLIs globally? shai-hulud should be reason enough. đ Pin your versions!
But frontend projects with strict package management policies will fail to start when usingnpx ...
{
"carlos-algms/agentic.nvim",
event = "VeryLazy",
opts = {
-- Available by default: "claude-acp" | "gemini-acp" | "codex-acp" | "opencode-acp" | "cursor-acp"
provider = "claude-acp", -- setting the name here is all you need to get started
},
-- these are just suggested keymaps; customize as desired
keys = {
{
"<C-\\>", function() require("agentic").toggle() end,
mode = { "n", "v", "i" },
desc = "Toggle Agentic Chat"
},
{
"<C-'>",
function() require("agentic").add_selection_or_file_to_context() end,
mode = { "n", "v" },
desc = "Add file or selection to Agentic to Context"
},
{
"<C-,>",
function() require("agentic").new_session() end,
mode = { "n", "v", "i" },
desc = "New Agentic Session"
},
},
}
You don't have to copy and paste anything from the default config, linking it
here for ease access and reference:
lua/agentic/config_default.lua.
You can customize the supported ACP providers by configuring the acp_providers
property:
[!NOTE]
You don't have to override anything or include these in your setup.
This is only needed if you want to customize existing providers.
{
acp_providers = {
-- Override existing provider (e.g., add API key)
-- Agentic.nvim don't require API keys, only add it if that's how you prefer to authenticate
["claude-acp"] = {
env = {
ANTHROPIC_API_KEY = os.getenv("ANTHROPIC_API_KEY"),
},
},
-- Example of how override the ACP command to suit your installation, if needed
["codex-acp"] = {
command = "~/.local/bin/codex-acp",
},
},
}
Provider Configuration Fields:
command (string) - The CLI command to execute (must be in PATH or absolute
path)args (table, optional) - Array of command-line argumentsenv (table, optional) - Environment variables to set for the processdefault_mode (string, optional) - Default mode ID to set on session creation
(e.g., "bypassPermissions", "plan")Notes: Customizing a provider only requires specifying the fields you want to change, not the entire configuration.
If you prefer a specific agent mode other than the provider's default, you can configure it per provider:
{
acp_providers = {
["claude-acp"] = {
-- Automatically switch to this mode when a new session starts
default_mode = "bypassPermissions",
},
},
}
The mode will only be set if it's available from the provider. Use <S-Tab> to
see available modes for your provider.
| Function | Description |
|---|---|
:lua require("agentic").toggle() |
Toggle chat sidebar |
:lua require("agentic").open() |
Open chat sidebar (keep open if already visible) |
:lua require("agentic").close() |
Close chat sidebar |
:lua require("agentic").add_selection() |
Add visual selection to context |
:lua require("agentic").add_file() |
Add current file to context |
:lua require("agentic").add_selection_or_file_to_context() |
Add selection (if any) or file to the context |
:lua require("agentic").new_session() |
Start new chat session, destroying and cleaning the current one |
:lua require("agentic").stop_generation() |
Stop current generation or tool execution (session stays active) |
Content-adding methods accept an optional opts table:
focus_prompt (boolean, default: true) - Whether to move cursor to
prompt input after opening the chatAvailable on: add_selection(opts), add_file(opts),
add_selection_or_file_to_context(opts)
Example:
-- Add selection without focusing the prompt
require("agentic").add_selection({ focus_prompt = false })
These keybindings are automatically set in Agentic buffers:
| Keybinding | Mode | Description |
|---|---|---|
<S-Tab> |
n/v/i | Switch agent mode (only available if provider supports modes) |
<CR> |
n | Submit prompt |
<C-s> |
n/v/i | Submit prompt |
q |
n | Close chat widget |
d |
n | Remove file or code selection at cursor |
d |
v | Remove multiple selected files or code selections |
You can customize the default keybindings by configuring the keymaps option in
your setup:
{
keymaps = {
-- Keybindings for ALL buffers in the widget (chat, prompt, code, files)
widget = {
close = "q", -- String for a single keybinding
change_mode = {
{
"<S-Tab>",
mode = { "i", "n", "v" }, -- Specify modes for this keybinding
},
},
},
-- Keybindings for the prompt buffer only
prompt = {
submit = {
"<CR>", -- Normal mode by default
{
"<C-s>",
mode = { "n", "v", "i" },
},
},
},
},
}
Keymap Configuration Format:
close = "q" - Simple keybinding (normal mode by default)submit = { "<CR>", "<C-s>" } - Multiple keybindings (normal mode
only){ "<C-s>", mode = { "i", "v" } } - Keybinding with
specific modesThe header text in the chat and prompt buffers will automatically update to show the appropriate keybinding for the current mode.
Type / in the Prompt buffer to see available slash commands with
auto-completion.
The /new command is always available to start a new session, other commands
are provided by your ACP provider.
You can reference and add files to the context by typing @ in the Prompt.
It will trigger the native Neovim completion menu with a list of all files in
the current workspace.
rg, fd, git ls-files, or lua globs as
fallback@file1.lua @file2.luaAgentic automatically includes environment and project information in the first message of each session:
This helps the AI Agent understand the context of the current project without having to run additional commands or grep through files, the goals is to reduce time for the first response.
Agentic.nvim uses custom highlight groups that you can override to match your colorscheme.
| Highlight Group | Purpose | Default |
|---|---|---|
AgenticDiffDelete |
Deleted lines in diff view | Links to DiffDelete |
AgenticDiffAdd |
Added lines in diff view | Links to DiffAdd |
AgenticDiffDeleteWord |
Word-level deletions in diff | bg=#9a3c3c, bold=true |
AgenticDiffAddWord |
Word-level additions in diff | bg=#155729, bold=true |
AgenticStatusPending |
Pending tool call status indicator | bg=#5f4d8f |
AgenticStatusCompleted |
Completed tool call status indicator | bg=#2d5a3d |
AgenticStatusFailed |
Failed tool call status indicator | bg=#7a2d2d |
AgenticCodeBlockFence |
The left border decoration on tool calls | Links to Directory |
AgenticTitle |
Window titles in sidebar | bg=#2787b0, fg=#000000, bold=true |
If any of these highlight exists, Agentic will use it instead of creating new ones.
If you're using lualine.nvim or similar statusline plugins, configure it to ignore Agentic windows to prevent conflicts with custom window decorations:
require('lualine').setup({
options = {
disabled_filetypes = {
statusline = { 'AgenticChat', 'AgenticInput', 'AgenticCode', 'AgenticFiles' },
winbar = { 'AgenticChat', 'AgenticInput', 'AgenticCode', 'AgenticFiles' },
}
}
})
This ensures that Agentic's custom window titles and statuslines render correctly without interference from your statusline plugin.
Verify your installation and dependencies:
:checkhealth agentic
This will check:
Enable debug logging to troubleshoot issues:
{
debug = true,
--- ... rest of config
}
View logs with :messages
View messages exchanged with the ACP provider in the log file at:
~/.cache/nvim/agentic_debug.logMIT License
Feel free to copy, modify, and distribute, just be a good samaritan and include
the the acknowledgments đ.
Agentic.nvim provides hooks that let you respond to key events during the chat lifecycle. These are useful for logging, notifications, analytics, or integrating with other plugins.
{
hooks = {
-- Called when the user submits a prompt
on_prompt_submit = function(data)
-- data.prompt: string - The user's prompt text
-- data.session_id: string - The ACP session ID
-- data.tab_page_id: number - The Neovim tabpage ID
vim.notify("Prompt submitted: " .. data.prompt:sub(1, 50))
end,
-- Called when the agent finishes responding
on_response_complete = function(data)
-- data.session_id: string - The ACP session ID
-- data.tab_page_id: number - The Neovim tabpage ID
-- data.success: boolean - Whether response completed without error
-- data.error: table|nil - Error details if failed
if data.success then
vim.notify("Agent finished!", vim.log.levels.INFO)
else
vim.notify("Agent error: " .. vim.inspect(data.error), vim.log.levels.ERROR)
end
end,
},
}