--- url: /installation.md description: >- Install CodeCompanion.nvim with lazy.nvim or packer. Covers dependencies, Neovim 0.11+ requirements, optional integrations like nvim-cmp, and API key setup. --- # Installation > \[!IMPORTANT] > To avoid breaking changes, it is recommended to pin the plugin to a specific release when installing. ## Requirements * The `curl` library * Neovim 0.11.0 or greater * *(Optional)* An API key for your chosen LLM * *(Optional)* [nvim-treesitter](https://github.com/nvim-treesitter/nvim-treesitter) and a `yaml` parser for markdown prompt library items * *(Optional)* The [file](https://man7.org/linux/man-pages/man1/file.1.html) command for detecting image mimetype * *(Optional)* The [ripgrep](https://github.com/BurntSushi/ripgrep) library for the `grep_search` tool You can run `:checkhealth codecompanion` to verify that all requirements are met. ## Installation The plugin can be installed with the plugin manager of your choice. It is recommended to pin the plugin to a specific release to avoid breaking changes. [nvim-treesitter](https://github.com/nvim-treesitter/nvim-treesitter) is required if you plan to use markdown prompts in the [prompt library](/configuration/prompt-library), ensuring you have the `yaml` parser installed. ::: code-group ```lua [vim.pack] vim.pack.add({ "https://www.github.com/nvim-lua/plenary.nvim" }) vim.pack.add({ "https://github.com/nvim-treesitter/nvim-treesitter" }) vim.pack.add({ { src = "https://www.github.com/olimorris/codecompanion.nvim", version = vim.version.range("^19.0.0") } }) -- Somewhere in your config require("codecompanion").setup() ``` ```lua [Lazy.nvim] { "olimorris/codecompanion.nvim", version = "^19.0.0", opts = {}, dependencies = { "nvim-lua/plenary.nvim", "nvim-treesitter/nvim-treesitter", }, }, ``` ```lua [Packer.nvim] use({ "olimorris/codecompanion.nvim", tag = "^19.0.0", config = function() require("codecompanion").setup() end, requires = { "nvim-lua/plenary.nvim", "nvim-treesitter/nvim-treesitter", }, }), ``` ::: **Plenary.nvim note:** As per [#377](https://github.com/olimorris/codecompanion.nvim/issues/377), if you pin your plugins to the latest releases, ensure you set plenary.nvim to follow the master branch ## Extensions CodeCompanion supports extensions that add additional functionality to the plugin. Below is an example which installs and configures [mcphub.nvim](https://github.com/ravitemer/mcphub.nvim): ::: code-group ```lua [1. Install] -- Lazy.nvim { "olimorris/codecompanion.nvim", dependencies = { "ravitemer/mcphub.nvim" } } ``` ```lua [2. Configure] require("codecompanion").setup({ extensions = { mcphub = { callback = "mcphub.extensions.codecompanion", opts = { make_vars = true, make_slash_commands = true, show_result_in_chat = true } } } }) ``` ::: Visit the [extensions documentation](extending/extensions) to learn more about available extensions and how to create your own. ## Other Plugins CodeCompanion integrates with a number of other plugins to make your AI coding experience more enjoyable. Below are some common lazy.nvim configurations for popular plugins: ::: code-group ```lua [render-markdown.nvim] { "MeanderingProgrammer/render-markdown.nvim", ft = { "markdown", "codecompanion" } }, ``` ```lua [markview.nvim] { "OXY2DEV/markview.nvim", lazy = false, opts = { preview = { filetypes = { "markdown", "codecompanion" }, ignore_buftypes = {}, }, }, }, ``` ```lua [img-clip.nvim] { "HakonHarnes/img-clip.nvim", opts = { filetypes = { codecompanion = { prompt_for_file_name = false, template = "[Image]($FILE_PATH)", use_absolute_path = true, }, }, }, }, ``` ::: Use [render-markdown.nvim](https://github.com/MeanderingProgrammer/render-markdown.nvim) or [markview.nvim](https://github.com/OXY2DEV/markview.nvim) to render the markdown in the chat buffer. Use [img-clip.nvim](https://github.com/hakonharnes/img-clip.nvim) to copy images from your system clipboard into a chat buffer via `:PasteImage`: ## Completion When in the [chat buffer](/usage/chat-buffer/), completion can be used to more easily add [editor context](/usage/chat-buffer/editor-context), [slash commands](/usage/chat-buffer/slash-commands) and [tools](/usage/chat-buffer/agents-tools). Out of the box, the plugin supports completion with both [nvim-cmp](https://github.com/hrsh7th/nvim-cmp) and [blink.cmp](https://github.com/Saghen/blink.cmp). For the latter, on version <= 0.10.0, ensure that you've added `codecompanion` as a source: ```lua sources = { per_filetype = { codecompanion = { "codecompanion" }, } }, ``` The plugin also supports [native completion](/usage/chat-buffer/#completion) and [coc.nvim](https://github.com/neoclide/coc.nvim). ## Help Consider using the [minimal.lua](https://github.com/olimorris/codecompanion.nvim/blob/main/minimal.lua) file to troubleshoot, running it with `nvim --clean -u minimal.lua`. --- --- url: /getting-started.md description: >- Get up and running with CodeCompanion in Neovim — configure your first LLM adapter, open a chat buffer, use the inline interaction, and learn the core commands. --- # Getting Started > \[!IMPORTANT] > The default adapter in CodeCompanion is [GitHub Copilot](https://docs.github.com/en/copilot/using-github-copilot/copilot-chat/asking-github-copilot-questions-in-your-ide). If you have [copilot.vim](https://github.com/github/copilot.vim) or [copilot.lua](https://github.com/zbirenbaum/copilot.lua) installed then expect CodeCompanion to work out of the box. This guide is intended to help you get up and running with CodeCompanion and begin your journey of coding with AI in Neovim. It assumes that you have already installed the plugin. If you haven't done so, please refer to the [installation instructions](/installation) first. ## Using the Documentation Throughout the documentation you will see examples that are wrapped in a `require("codecompanion").setup({ })` block. This is purposefully done so that users can apply them to their own Neovim configuration. If you're using [lazy.nvim](https://github.com/folke/lazy.nvim), you can simply apply the examples that you see in this documentation in the `opts` table. For example, the following code snippet from these docs: ```lua require("codecompanion").setup({ interactions = { chat = { adapter = "anthropic", model = "claude-sonnet-4-20250514" }, }, opts = { log_level = "DEBUG", }, }) ``` can be used in a *lazy.nvim* configuration like so: ```lua { "olimorris/codecompanion.nvim", dependencies = { "nvim-lua/plenary.nvim" }, opts = { interactions = { chat = { adapter = "anthropic", model = "claude-sonnet-4-20250514" }, }, -- NOTE: The log_level is in `opts.opts` opts = { log_level = "DEBUG", }, }, }, ``` ## Interactions The plugin uses the notion of *interactions* to describe the many different ways that you can interact with an Agent or LLM from within CodeCompanion. There are five main types of interactions: * **Chat** - A chat buffer where you can converse with an LLM (`:CodeCompanionChat`) * **CLI** - A terminal wrapper around agent CLI tools such a Claude Code or Opencode (`:CodeCompanionCLI`) * **Inline** - An inline interaction that can write code directly into a buffer (`:CodeCompanion`) * **Cmd** - Create Neovim commands in the command-line (`:CodeCompanionCmd`) * **Background** - Runs tasks in the background such as compacting chat messages or generating titles for chats ## Setup ### Chat and Inline > \[!NOTE] > The adapters that the plugin supports out of the box can be found in the > [built-in adapters directory](https://github.com/olimorris/codecompanion.nvim/tree/main/lua/codecompanion/adapters). Or, see the > [community-contributed adapters](configuration/adapters-http#community-adapters). > > [ACP adapters](/configuration/adapters-acp) are only supported for the chat interaction. The Chat Buffer is where you can converse with an LLM from within a Neovim buffer. It operates on a single response per turn basis. The inline interaction enables an LLM to write code directly into a Neovim buffer. The [chat](/usage/chat-buffer/) and [inline](/usage/inline) interactions need an adapter to function. In CodeCompanion terminology, an adapter is the connection between Neovim and an LLM or agent. CodeCompanion has two *types* of adapters; HTTP adapters which connect you to an LLM via it's API and ACP adapters which connect you to an agent via the [Agent Client Protocol](https://agentclientprotocol.com). CodeCompanion has a number of [built-in adapters](https://github.com/olimorris/codecompanion.nvim/blob/main/lua/codecompanion/config.lua) that you can leverage and you can find more details in the respective [HTTP](/configuration/adapters-http) and [ACP](/configuration/adapters-acp) sections of the documentation. To set an adapter: ```lua require("codecompanion").setup({ interactions = { chat = { -- You can specify an adapter by name and model (both ACP and HTTP) adapter = { name = "copilot", model = "gpt-4.1", }, }, -- Or, just specify the adapter by name inline = { adapter = "anthropic", }, cmd = { adapter = "openai", }, background = { adapter = { name = "ollama", model = "qwen-7b-instruct", }, }, }, }) ``` In the example above, we're using the Copilot adapter for the chat interaction and the Anthropic one for the inline. We're also using something cheap for the background adapter (although these interactions are opt-in). You can mix and match adapters as you see fit for your workflow. **Setting an API Key** Because most LLMs require an API key, you'll need to share that with the adapter. By default, the built-in adapters will look in your environment for a `*_API_KEY` where `*` is the name of the adapter such as `ANTHROPIC` or `OPENAI`. Refer to the documentation of the LLM or agent you're using to find out what the environment variable is called. You can set/change the API key by using the `extend` function: ```lua require("codecompanion").setup({ adapters = { http = { anthropic = function() return require("codecompanion.adapters").extend("anthropic", { env = { api_key = "MY_OTHER_ANTHROPIC_KEY", }, }) end, }, }, }) ``` There are numerous ways that environment variables can be set for adapters. Refer to the [environment variables](/configuration/adapters-http#environment-variables) section for more information. ### CLI The CLI interaction allows you to interact with agents that operate in the command-line like Claude Code and Opencode. To use CodeCompanion with a CLI agent, you'll need to configure an agent first: ```lua require("codecompanion").setup({ interactions = { cli = { agent = "claude_code", agents = { claude_code = { cmd = "claude", args = {}, description = "Claude Code CLI", provider = "terminal", }, }, }, }, }) ``` In the example above, we're setting up Claude Code in the `agents` table, specifying the command to run it. Then we're setting it as the default CLI interaction with `agent = "claude_code"`. ## Usage The below section has been curated from the lengthier usage documentation to give you a quick overview of how each feature works. ### Chat Run `:CodeCompanionChat` to open a chat buffer. Type your prompt and send it by pressing `` while in insert mode or `` in normal mode. Alternatively, run `:CodeCompanionChat why are Lua and Neovim so perfect together?` to open the chat buffer and send a prompt at the same time. Toggle the chat buffer with `:CodeCompanionChat Toggle`. You can add context from your code base by using *Editor Context* and *Slash Commands* in the chat buffer. **Editor Context** *Editor Context*, accessed via `#` (by default), contain data about the present state of Neovim. You can find a [list of available editor context](/usage/chat-buffer/editor-context). The buffer editor context will automatically link a buffer to the chat buffer, by default, updating the LLM when the buffer changes. You can use them in your prompts like: ``` What does the code in #{buffer} do?` ``` **Slash Commands** > \[!IMPORTANT] > These have been designed to work with native Neovim completions alongside nvim-cmp and blink.cmp. To open the native completion menu use `` in insert mode when in the chat buffer. Note: Slash commands should also work with coc.nvim. *Slash commands*, accessed via `/` (by default), run commands to insert additional context into the chat buffer. You can find a [list of available slash commands and how to use them](/usage/chat-buffer/slash-commands). **Tools** *Tools*, accessed via `@` (by default), allow the LLM to function as an agent and leverage external tools. You can find a [list of available tools and how to use them](usage/chat-buffer/agents-tools#available-tools). You can use them in your prompts like: ``` Can you use @{grep_search} to find occurrences of "hello world" ``` ### CLI Running `:CodeCompanionCLI` will open a new CLI interaction. Running `:CodeCompanionCLI ` will send the prompt to the last CLI interaction (or create a new one). You can also run `:CodeCompanionCLI Ask` to use a rich prompt input field complete with [editor context](#editor-context). Save with `:w` to send the prompt to the agent, or `:w!` to send and auto-submit it. Adding `!` to the command (e.g. `:CodeCompanionCLI! `) will auto-submit the prompt and keep your cursor in the current buffer. You can also specify which agent to use with `:CodeCompanionCLI agent=`. ### Inline > \[!NOTE] > The diff provider in the video is [mini.diff](https://github.com/echasnovski/mini.diff) Run `:CodeCompanion your prompt` to call the inline interaction. The interaction will evaluate the prompt and either write code or open a chat buffer. You can also make a visual selection and call the inline interaction. To send additional context alongside your prompt, you can leverage [editor context](/usage/inline#editor-context) such as `:CodeCompanion #{buffer} `. For convenience, you can call prompts with their `alias` from the [prompt library](https://github.com/olimorris/codecompanion.nvim/blob/6a4341a4cfe8988a57ad9e8b7dc01ccd6f3e1628/lua/codecompanion/config.lua#L565) such as `:'<,'>CodeCompanion /explain`. The prompt library comes with the following presets: * `/commit` - Generate a commit message * `/explain` - Explain how selected code in a buffer works * `/fix` - Fix the selected code * `/lsp` - Explain the LSP diagnostics for the selected code * `/tests` - Generate unit tests for selected code ### Action Palette Run `:CodeCompanionActions` to open the action palette, which gives you access to the plugin's features, including your prompts from the [prompt library](/configuration/prompt-library). By default the plugin uses `vim.ui.select`, however, you can change the provider by altering the `display.action_palette.provider` config value to be `telescope`, `mini_pick` or `snacks`. You can also call the Telescope extension with `:Telescope codecompanion`. > \[!NOTE] > Some actions and prompts will only be visible if you're in *Visual mode*. ### List of Commands The plugin has five core commands: * `CodeCompanion` - Open the inline interaction * `CodeCompanionChat` - Open a chat buffer * `CodeCompanionCLI` - Open a CLI interaction * `CodeCompanionCmd` - Generate a command in the command-line * `CodeCompanionActions` - Open the *Action Palette* However, there are multiple options available: * `CodeCompanion ` - Prompt the inline interaction * `CodeCompanion adapter= ` - Prompt the inline interaction with a specific adapter * `CodeCompanion /` - Call an item via its alias from the [prompt library](configuration/prompt-library) * `CodeCompanionActions Refresh` - Refresh the action palette and any items in the prompt library * `CodeCompanionChat ` - Send a prompt to the LLM via a chat buffer * `CodeCompanionChat adapter= model=` - Open a chat buffer with a specific http adapter and model * `CodeCompanionChat adapter= command=` - Open a chat buffer with a specific ACP adapter and command * `CodeCompanionChat Add` - Add visually selected chat to the current chat buffer * `CodeCompanionChat Changes` - Open the quickfix list with all files that have been changed by the LLM * `CodeCompanionChat RefreshCache` - Used to refresh conditional elements in the chat buffer * `CodeCompanionChat Toggle` - Toggle a chat buffer * `CodeCompanionCLI` - Open a new CLI interaction * `CodeCompanionCLI ` - Send a prompt to the last CLI interaction (or create a new one) * `CodeCompanionCLI! ` - Send and auto-submit a prompt, keeping focus in the current buffer * `CodeCompanionCLI agent= ` - Start a new CLI interaction with a specific agent * `CodeCompanionCLI Ask` - Open the rich input buffer for CLI prompts * `CodeCompanionCodeReview` - Open an agent's changes in the quickfix list for [code reviews](/usage/code-review) * `CodeCompanionCodeReview Comment` - Leave a review comment on the current line or visual selection ## Suggested Plugin Workflow For an optimum plugin workflow, the author recommends the following: ```lua vim.keymap.set({ "n", "v" }, "", "CodeCompanionActions", { noremap = true, silent = true }) vim.keymap.set({ "n", "v" }, "a", "CodeCompanionChat Toggle", { noremap = true, silent = true }) vim.keymap.set("v", "ga", "CodeCompanionChat Add", { noremap = true, silent = true }) -- Expand 'cc' into 'CodeCompanion' in the command line vim.cmd([[cab cc CodeCompanion]]) ``` > \[!NOTE] > You can also assign prompts from the library to specific mappings. See the [prompt library](configuration/prompt-library#assigning-prompts-to-a-keymap) section for more information. --- --- url: /architecture.md description: >- How CodeCompanion manages LLM context windows, handles token limits, and is architected internally — reference for contributors and advanced users. --- # Architecture This section of the documentation covers architectural concepts and design principles that underpin CodeCompanion's functionality. This is not mandatory reading for users of CodeCompanion. It may be of interest to those who are looking to understand some of the technical details of how CodeCompanion works, or those who are looking to contribute to the project. ## How Context Is Managed One of the limitations of working with LLMs is that of context, as they have a finite window with which they can respond to a user's ask. That is, there's only a certain amount of data that LLMs can reference in order to generate a response. To equate this to human terms, it can be thought of as [working memory](https://en.wikipedia.org/wiki/Working_memory) and it varies greatly depending on what model you're using. The context window is measured in [tokens](https://platform.claude.com/docs/en/about-claude/glossary#tokens). When a user breaches the context window, the conversation **ends** and it **cannot** continue. This can be hugely inconvenient in the middle of a coding session and potentially time consuming to recover from. CodeCompanion has context awareness which means it can prevent this from happening by taking **preventative** action and it does this in two ways: 1. **Context editing** - Whereby the conversation history is edited to remove less relevant information 2. **Compaction** - Where a conversation is summarised, removing historical messages and content ### In the Chat Buffer Firstly, CodeCompanion manages context by paying close attention to the number of tokens in the [chat buffer](/usage/chat-buffer/), matching them against a defined trigger threshold in your config, which can be [customised](/configuration/chat-buffer#context-management). CodeCompanion uses two thresholds: an **editing** trigger (default `0.65` of the context window) and a **compaction** trigger (default `0.85`). When the chat buffer crosses the lower threshold, context editing begins. If it later crosses the upper threshold then compaction runs. The lower threshold ensures that the lower risk editing action is triggered more often, buying more time before compaction is required. #### Context Editing > \[!NOTE] > Inspired by [Anthropic's context editing](https://platform.claude.com/docs/en/build-with-claude/context-editing) Context editing is the lighter and more risk-free option of the two operations. It walks through the chat's message history and replaces the *content* of older tool call results with a placeholder, leaving the conversation intact. This ensures that tool calls and tool results are never orphaned, whilst ensuring the token count is reduced. Editing works in terms of **cycles**. A cycle represents one user turn and everything the LLM did in response to it (tool calls, tool results, replies). By default, the most recent 3 cycles are preserved in full; older cycles have their tool results swapped for a placeholder. This means an in-flight agentic loop is never cut in half — a cycle is preserved or aged as a whole. You can exclude specific tools from being edited via the `exclude_tools` configuration option. For example, the `memory` tool is excluded by default, since its output is often referenced again later in the conversation. When a tool result is edited, its content becomes: ``` Tool result cleared to save context. Re-run the tool if you need this output ``` #### Compaction > \[!NOTE] > Inspired by [Claude Code's compaction prompt](https://github.com/Piebald-AI/claude-code-system-prompts) When no more editing can be performed, CodeCompanion will use compaction. It makes a single LLM call to summarise the conversation so far, then replaces the message history with that summary. Not everything in the history is summarised and the below items are preserved: * The system prompt * Project rules (anything tagged via the [`/rules`](/usage/chat-buffer/slash-commands#rules) slash command) Files, buffers, and images that were attached during the chat are replaced with reference placeholders, similarly to how tool results are replaced when edited: ``` File content for `lua/foo.lua` cleared during compaction. Re-read the file if you need it. ``` The placeholder names the file so the LLM knows how to re-read or re-request it. All other messages are summarised and removed. Compaction can use a different adapter than the chat itself, which is useful if you want a cheaper or faster model handling the summary. You can also choose to fall back to the chat adapter if the override fails — by default, a failure simply skips that round and notifies you. The summary is appended to the chat as a new user message and tagged so future compactions can identify and replace it. The chat is automatically submitted so the LLM has a chance to respond to the summarised context and restart the agentic loop. #### Server-Side Compaction If you're using the `openai_responses` or `anthropic` adapters, then CodeCompanion will use their native server-side compaction capabilities. Please see the [OpenAI compaction documentation](https://developers.openai.com/api/docs/guides/compaction) and [Anthropic compaction documentation](https://platform.claude.com/docs/en/build-with-claude/compaction) for more information. Editing still runs client-side for these adapters since it produces tokens-over-the-wire savings independent of what the server does. #### Manual Triggers Compaction can also be triggered manually via the [`/compact`](/usage/chat-buffer/slash-commands#compact) slash command, regardless of where the token count sits. Editing has no manual equivalent — it runs automatically when the threshold is crossed. --- --- url: /integrations.md description: >- Connect CodeCompanion.nvim to other applications via its Neovim events catalog, including built-in herdr support that reports agent state as idle, working, or blocked. --- # Integrations CodeCompanion enables integrations with many applications based on its rich [events](/usage/events) catalog. ## herdr CodeCompanion supports [herdr](https://github.com/herdrdev/herdr) out of the box with a direct integration allowing it to appear as an agent. CodeCompanion fully supports herdr's lifecycle for [reporting semantic state](https://herdr.dev/docs/integrations/#integrate-your-own-agent). It's enabled by default but can be disabled with: ```lua require("codecompanion").setup({ integrations = { herdr = { enabled = false, }, }, }) ``` --- --- url: /configuration/upgrading.md description: >- Step-by-step guide for upgrading CodeCompanion between major versions, covering breaking changes, configuration migration, and version pinning in Neovim. --- # Upgrading CodeCompanion This document provides a guide for upgrading from one version of CodeCompanion to another. CodeCompanion follows [semantic versioning](https://semver.org/) and to avoid breaking changes, it is recommended to pin the plugin to a specific version in your Neovim configuration. The [installation guide](/installation) provides more information on how to do this. ## v18.7.0 to v19.0.0 * The Super Diff has now been removed from CodeCompanion ([#2600](https://github.com/olimorris/codecompanion.nvim/pull/2600)) * CodeCompanion now only supports a built-in diff which is enabled by default ([#2600](https://github.com/olimorris/codecompanion.nvim/pull/2600)), dropping support for Mini.Diff * The `full_stack_dev` group has been renamed to [agent](/usage/chat-buffer/agents-tools#agent) ([#2786](https://github.com/olimorris/codecompanion.nvim/pull/2786)) * The `next_edit_suggestion` and `list_code_usages` tools have been removed ### Adapters * For the Claude Code adapter to work, you'll need to ensure you have Zed's [claude-agent-acp](https://github.com/zed-industries/claude-agent-acp) adapter installed. This has been renamed from *claude-code-acp* in recent weeks ([#2779](https://github.com/olimorris/codecompanion.nvim/pull/2779)) ### Config * Diff keymaps have moved from `interactions.inline.keymaps` to `interactions.shared.keymaps` ([#2600](https://github.com/olimorris/codecompanion.nvim/pull/2600)) * All diff config has moved to `display.diff` ([#2600](https://github.com/olimorris/codecompanion.nvim/pull/2600)) * `variables` have been renamed to `editor_context` and the config paths are now `interactions.chat.editor_context` and `interactions.inline.editor_context` ([#2719](https://github.com/olimorris/codecompanion.nvim/pull/2719)) * Across *editor context*, *slash commands* and *tools*, `callback` has been replaced by `path` for string values (module paths and file paths). `callback` is still used for function values, however ### Prompt Library * The location of rules within a prompt library item has changed from `opts.rules` to `rules`: ```markdown --- name: Oli's test workflow strategy: chat description: Workflow test prompt rules: - test_rule --- ``` ## v17.33.0 to v18.0.0 ### Config * The biggest change in this release is the renaming of `strategies` to `interactions`. This will only be a breaking change if you specifically reference `codecompanion.strategies` in your configuration. If you do, you'll need to change it to `codecompanion.interactions` ([#2485](https://github.com/olimorris/codecompanion.nvim/pull/2485)) * Previously, built-in slash commands and tools were stored in `/catalog` folders which have now been renamed to `/builtin`. If you reference these in your configuration you'll need to update the paths accordingly ([#2482](https://github.com/olimorris/codecompanion.nvim/pull/2482)) * Workspaces have now been removed from the plugin. Please use [Rules](/configuration/rules) instead. ### Adapters * If you have a custom adapter, you'll need to rename `condition` to be `enabled` on any schema items ([#2439](https://github.com/olimorris/codecompanion.nvim/pull/2439/commits/cb14c7bac869346e2d12b775c4bf258606add569)): ```lua return { schema = { ["reasoning.effort"] = { ---@type fun(self: CodeCompanion.HTTPAdapter): boolean condition = function(self) -- [!code --] enabled = function(self) -- [!code ++] -- end, }, } } ``` * The default adapters on the **Anthropic** and **Gemini** adapters have changed to `claude-sonnet-4-5-20250929` and `gemini-3-pro-preview`, respectively ([#2494](https://github.com/olimorris/codecompanion.nvim/pull/2494)) * If you wish to hide the adapters that come with CodeCompanion, `adapters.[acp|http].opts.show_defaults` has been renamed to `adapters.[acp|http].opts.show_presets` for both HTTP and ACP adapters ([#2497](https://github.com/olimorris/codecompanion.nvim/pull/2497)) ### Chat * Memory has been renamed to rules. Please rename any references to `memory` in your configuration to `rules`. Please refer to the [Rules](/configuration/rules) documentation for more information ([#2440](https://github.com/olimorris/codecompanion.nvim/pull/2440)) * `default_memory` has been renamed to `autoload` ([#2509](https://github.com/olimorris/codecompanion.nvim/pull/2509)) *** * The variable and parameter `#{buffer}{watch}` has been renamed to `#{buffer}{diff}`. This better reflects that an LLM receives a diff of buffer changes with each request ([#2444](https://github.com/olimorris/codecompanion.nvim/pull/2444)) * The variable and parameter `#{buffer}{pin}` has now been renamed to `#{buffer}{all}`. This better reflects that the entire buffer is sent to the LLM with each request ([#2444](https://github.com/olimorris/codecompanion.nvim/pull/2444)) *** * Passing an adapter as an argument to `:CodeCompanionChat` is now done with `:CodeCompanionChat adapter=` ([#2437](https://github.com/olimorris/codecompanion.nvim/pull/2437)) * If your chat buffer system prompt is still stored at `opts.system_prompt` you'll need to change it to `interactions.chat.opts.system_prompt` ([#2484](https://github.com/olimorris/codecompanion.nvim/pull/2484)) ### Prompt Library If you have any prompts defined in your config, you'll need to: * Rename `opts.short_name` to `opts.alias` for each item in order to allow you to call them with `require("codecompanion").prompt("my_prompt")` or as slash commands in the chat buffer ([#2471](https://github.com/olimorris/codecompanion.nvim/pull/2471)). ```lua ["my custom prompt"] = { strategy = "chat", description = "My custom prompt", opts = { short_name = "my_prompt", -- [!code --] alias = "my_prompt", -- [!code ++] }, prompts = { -- ... }, }, ``` * Change all workflow prompts, replacing `strategy = "workflow"` with `interaction = "chat"` and specifying `opts.is_workflow = true` ([#2487](https://github.com/olimorris/codecompanion.nvim/pull/2487)). ```lua ["my_workflow"] = { strategy = "workflow", -- [!code --] interaction = "chat", -- [!code ++] description = "My custom workflow", opts = { is_workflow = true, -- [!code ++] }, prompts = { -- ... }, }, ``` * If you don't wish to display any of the built-in prompt library items, you'll need to change `display.action_palette.show_default_prompt_library` to `display.action_palette.show_preset_prompts` ([#2499](https://github.com/olimorris/codecompanion.nvim/pull/2499)) ### Tools If you have any tools in your config, you'll need to rename: * `requires_approval` to `require_approval_before` ([#2439](https://github.com/olimorris/codecompanion.nvim/pull/2439/commits/cb14c7bac869346e2d12b775c4bf258606add569)) * `user_confirmation` to `require_confirmation_after` ([#2450](https://github.com/olimorris/codecompanion.nvim/pull/2450)) These now better reflect the timing of each action. ### UI * The `display.chat.child_window` has been renamed `display.chat.floating_window` to better describe what it is ([#2452](https://github.com/olimorris/codecompanion.nvim/pull/2452)) * The `display.action_palette.opts.show_default_actions` has been renamed to be `display.action_palette.opts.show_preset_actions` ([#2499](https://github.com/olimorris/codecompanion.nvim/pull/2499)) --- --- url: /configuration/action-palette.md description: >- Configure the CodeCompanion Action Palette — your entry point to chat buffers, prompt library prompts, and plugin features in Neovim. --- # Configuring the Action Palette The Action Palette holds plugin specific items like the ability to launch a chat buffer and the currently open chat buffers alongside displaying the prompts from the [Prompt Library](prompt-library). ## Layout > \[!NOTE] > The Action Palette also supports [Telescope.nvim](https://github.com/nvim-telescope/telescope.nvim), [fzf\_lua](https://github.com/ibhagwan/fzf-lua), [mini.pick](https://github.com/echasnovski/mini.pick) and [snacks.nvim](https://github.com/folke/snacks.nvim) You can change the appearance of the chat buffer by changing the `display.action_palette` table in your configuration: ```lua require("codecompanion").setup({ display = { action_palette = { width = 95, height = 10, prompt = "Prompt ", -- Prompt used for interactive LLM calls provider = "default", -- Can be "default", "telescope", "fzf_lua", "mini_pick" or "snacks". If not specified, the plugin will autodetect installed providers. opts = { show_preset_actions = true, -- Show the preset actions in the action palette? show_preset_prompts = true, -- Show the preset prompts in the action palette? title = "CodeCompanion actions", -- The title of the action palette }, }, }, }), ``` --- --- url: /configuration/adapters-acp.md description: >- Configure Agent Client Protocol (ACP) adapters in CodeCompanion to connect with CLI agents like Claude Code, Codex, Gemini CLI, and OpenCode from Neovim. --- # Configuring ACP Adapters This section contains configuration which is specific to Agent Client Protocol (ACP) adapters only. There is a lot of shared functionality between ACP and [http](/configuration/adapters-http) adapters. Therefore it's recommended you read the two pages together. ## Customising an Adapter There are two ways to customise a preset adapter, and you'll see both throughout this page: * **Function** - Use this for full or computed setups. Custom `commands`, `defaults`, or values resolved at call time * **`extend` table** - Use this for static overrides like credentials or setting a default value ::: code-group ```lua [Function] require("codecompanion").setup({ adapters = { acp = { gemini_cli = function() return require("codecompanion.adapters").extend("gemini_cli", { commands = { default = { "some-other-gemini", "--experimental-acp" }, }, defaults = { auth_method = "gemini-api-key", timeout = 20000, -- 20 seconds }, env = { GEMINI_API_KEY = "cmd:op read op://personal/Gemini/credential --no-newline" }, }) end, }, }, }) ``` ```lua [Extend Table] require("codecompanion").setup({ adapters = { acp = { extend = { gemini_cli = { defaults = { auth_method = "gemini-api-key" }, env = { GEMINI_API_KEY = "cmd:op read op://personal/Gemini/credential --no-newline" }, }, }, }, }, }) ``` ::: > \[!IMPORTANT] > The `extend` key is the adapter's name in the [config](https://github.com/olimorris/codecompanion.nvim/blob/main/lua/codecompanion/config.lua), not the resolved adapter name. ## Setting a Default Adapter You can select an ACP adapter to be the default for all chat interactions: ```lua require("codecompanion").setup({ interactions = { chat = { adapter = "gemini_cli", }, }, }), ``` ## Setting Default Session Config Options The ACP specification has recently added support for [session config options](https://agentclientprotocol.com/protocol/session-config-options). These are lists of configuration options that agents can share with CodeCompanion at the start of a session such as models, reasoning levels, and more. ### Models There are numerous was you can set a model in your config and it differs significantly from other session config options because of how CodeCompanion integrates adapters and models into the chat interaction. ::: code-group ```lua [Interactions] {4-7} require("codecompanion").setup({ interactions = { chat = { adapter = { name = "codex", model = "gpt-5.4", }, }, }, }), ``` ```lua [Adapter String] {6-10} require("codecompanion").setup({ adapters = { acp = { codex = function() return require("codecompanion.adapters").extend("codex", { defaults = { session_config_options = { model = "gpt-5.4" }, }, }) end, } }, }), ``` ```lua [Adapter Function] {6-14} require("codecompanion").setup({ adapters = { acp = { codex = function() return require("codecompanion.adapters").extend("codex", { defaults = { session_config_options = { ---@param self CodeCompanion.ACPAdapter ---@return string model = function(self) return "gpt-5.4" end, }, }, }) end, } }, }), ``` ::: ### Others To set any other session config option, you can pass them in the `defaults.session_config_options` table: ```lua {6-11} require("codecompanion").setup({ adapters = { acp = { codex = function() return require("codecompanion.adapters").extend("codex", { defaults = { session_config_options = { mode = "Full Access", thought_level = "Xhigh", }, }, }) end, } }, }), ``` To find out what the available session config options are for a specific adapter you can open the [debug window](/usage/chat-buffer/#debug-window) in the chat buffer. ## Configuring MCP Servers Some ACP adapters [support](https://agentclientprotocol.com/protocol/session-setup#mcp-servers) connecting to Model Client Protocol (MCP) servers. If you've defined [MCP servers in your configuration](/configuration/mcp), then CodeCompanion can automatically connect to those servers when initializing the adapter. To enable this, set `inherit_from_config` in the ACP adapter's `defaults.mcpServers` field: ```lua require("codecompanion").setup({ adapters = { acp = { claude_code = function() return require("codecompanion.adapters").extend("claude_code", { defaults = { mcpServers = "inherit_from_config", }, }) end, }, }, }) ``` > \[!NOTE] > CodeCompanion does not display the MCP servers in the chat buffer's context when used with an ACP adapter. Alternatively, you can configure MCP servers manually. In the below example, we're configuring Claude Code to connect to the [sequential-thinking](https://github.com/modelcontextprotocol/servers/tree/main/src/sequentialthinking) server via stdio: ```lua require("codecompanion").setup({ adapters = { acp = { claude_code = function() return require("codecompanion.adapters").extend("claude_code", { defaults = { mcpServers = { { name = "sequential-thinking", command = "npx", args = { "-y", "@modelcontextprotocol/server-sequential-thinking" }, env = {}, }, }, }, }) end, }, }, }) ``` You can also disable this by setting `mcp.opts.acp_enabled = false` in your configuration. ## Hiding Preset Adapters By default, the plugin shows all available adapters, including the presets. If you prefer to only display the adapters defined in your user configuration, you can set the `show_presets` option to `false`: ```lua require("codecompanion").setup({ adapters = { acp = { opts = { show_presets = false, }, }, }, }) ``` ## Setup: Auggie CLI from Augment Code To use [Auggie CLI](https://docs.augmentcode.com/cli/overview) within CodeCompanion, you simply need to follow their [Getting Started](https://docs.augmentcode.com/cli/overview#getting-started) guide. ## Setup: Cagent To use Docker's [Cagent](https://github.com/docker/cagent) within CodeCompanion, you need to follow these steps: 1. [Install](https://github.com/docker/cagent?tab=readme-ov-file#installation) Cagent as per their instructions 2. [Create an agent](https://github.com/docker/cagent?tab=readme-ov-file#run-agents) in the repository you're working from 3. Test the agent by running `cagent run your_agent.yaml` in the CLI 4. In your CodeCompanion config, extend the `cagent` adapter to include the agent: ```lua require("codecompanion").setup({ adapters = { acp = { cagent = function() return require("codecompanion.adapters").extend("cagent", { commands = { default = { "cagent", "acp", "your_agent.yaml", }, }, }) end, }, }, }) ``` If you have multiple agent files that you like to run separately, you can create multiple commands for each agent. ## Setup: Claude Code To use [Claude Code](https://www.anthropic.com/claude-code) within CodeCompanion, you'll need to take the following steps: 1. [Install](https://docs.anthropic.com/en/docs/claude-code/quickstart#step-1%3A-install-claude-code) Claude Code 2. [Install](https://github.com/zed-industries/claude-agent-acp) the Zed ACP adapter for Claude Code ### Using Claude Pro Subscription 3. In your CLI, run `claude setup-token`. You'll be redirected to the Claude.ai website for authorization: 4. Back in your CLI, copy the OAuth token (in yellow): 5. In your CodeCompanion config, extend the `claude_code` adapter and include the OAuth token (see the section on [environment variables and setting API keys](/configuration/adapters-http#environment-variables-setting-an-api-key) for other ways to do this): ```lua require("codecompanion").setup({ adapters = { acp = { claude_code = function() return require("codecompanion.adapters").extend("claude_code", { env = { CLAUDE_CODE_OAUTH_TOKEN = "my-oauth-token", }, }) end, }, }, }) ``` ### Using an API Key 3. [Create](https://console.anthropic.com/settings/keys) an API key in your Anthropic console. 4. In your CodeCompanion config, extend the `claude_code` adapter and set the `ANTHROPIC_API_KEY`: ```lua require("codecompanion").setup({ adapters = { acp = { claude_code = function() return require("codecompanion.adapters").extend("claude_code", { env = { ANTHROPIC_API_KEY = "my-api-key", }, }) end, }, }, }) ``` ## Setup: Cline CLI To use [Cline CLI](https://cline.bot/cli) within CodeCompanion, you'll need to take the following steps: 1. [Install](https://docs.cline.bot/getting-started/installing-cline#cli) Cline CLI. 2. Authenticate by running `cline auth`. 3. Select the `cline_cli` adapter in your chat buffer ## Setup: Codex To use OpenAI's [Codex](https://openai.com/codex/), install [codex-acp](https://github.com/agentclientprotocol/codex-acp). By default, the adapter will look for an `OPENAI_API_KEY` in your shell, however you can also authenticate via ChatGPT. This can be customized in the plugin configuration: ```lua require("codecompanion").setup({ adapters = { acp = { codex = function() return require("codecompanion.adapters").extend("codex", { defaults = { auth_method = "api-key", -- "api-key"|"chat-gpt" }, env = { OPENAI_API_KEY = "my-api-key", }, }) end, }, }, }) ``` ## Setup: Copilot CLI Install [Copilot CLI](https://docs.github.com/en/copilot/how-tos/copilot-cli/set-up-copilot-cli/install-copilot-cli) as per the instructions and then in the terminal run `copilot` and ensure that you're authenticated. ## Setup: Cursor CLI To use [Cursor](https://www.cursor.com/) within CodeCompanion, you'll need to take the following steps: 1. Install `agent` as per the [Cursor CLI documentation](https://cursor.com/docs/cli/overview) 2. Authenticate by running `agent login` in your terminal 3. Select the `cursor_cli` adapter in your chat buffer ## Setup: Gemini CLI 1. Install [Gemini CLI](https://github.com/google-gemini/gemini-cli) 2. Update your CodeCompanion config and select which authentication methods you'd like to use. Currently there are: * `oauth-personal` which uses your Google login * `gemini-api-key` * `vertex-ai`) The example below uses the `gemini-api-key` method, pulling the API key from [1Password CLI](https://developer.1password.com/docs/cli/get-started/): ```lua require("codecompanion").setup({ adapters = { acp = { gemini_cli = function() return require("codecompanion.adapters").extend("gemini_cli", { defaults = { auth_method = "gemini-api-key", -- "oauth-personal"|"gemini-api-key"|"vertex-ai" }, env = { GEMINI_API_KEY = "cmd:op read op://personal/Gemini_API/credential --no-newline", }, }) end, }, }, }) ``` ## Setup: Goose CLI To use [Goose](https://goose-docs.ai/) in CodeCompanion, ensure you've followed their [documentation](https://goose-docs.ai/docs/getting-started/installation/) to setup and install Goose CLI. Then ensure that in your chat buffer you select the `goose` adapter. ## Setup: Kilo Code To use [Kilo Code](https://kilo.ai) in CodeCompanion, ensure you've followed their documentation to [install](https://kilo.ai/docs/getting-started/installing#cli) and [configure](https://kilo.ai/docs/getting-started/setup-authentication#cli) it. Then ensure that in your chat buffer you select the `kilocode` adapter. You can specify a custom model in your `~/.config/kilo/kilo.json` file: ```json { "$schema": "https://kilo.ai/config.json", "model": "kilo/kilo-auto/free", } ``` ## Setup: Kimi CLI Install [Kimi CLI](https://github.com/MoonshotAI/kimi-cli?tab=readme-ov-file#installation) as per their instructions. Then in the CLI, run `kimi` followed by `/login` to configure your API key. Then ensure that in your chat buffer you select the `kimi_cli` adapter. ## Setup: Kiro CLI Install [Kiro cli](https://kiro.dev/docs/cli/) as per their instructions. Then open it and login (if installation doesn't already prompt you to login). the codecompanion adapter will execute `kiro-cli acp`, make sure to have it available on your PATH. ## Setup: Mistral Vibe To use [Mistral Vibe](https://github.com/mistralai/mistral-vibe) in CodeCompanion, ensure you've followed their documentation to [install](https://github.com/mistralai/mistral-vibe). Then run `vibe --setup` in your CLI in order to setup your API key. Then ensure that in your chat buffer you select the `mistral_vibe` adapter. ## Setup: OpenCode To use [OpenCode](https://opencode.ai) in CodeCompanion, ensure you've followed their documentation to [install](https://opencode.ai/docs/#install) and [configure](https://opencode.ai/docs/#configure) it. Then ensure that in your chat buffer you select the `opencode` adapter. You can specify a custom model in your `~/.config/opencode/config.json` file: ```json { "$schema": "https://opencode.ai/config.json", "model": "github-copilot/claude-sonnet-4.5", } ``` ## Creating Custom ACP Adapters Not every ACP-compatible tool will have a built-in adapter. You can define your own directly in your configuration — the example below uses a hypothetical `myagent` CLI tool. Use the [built-in ACP adapters](https://github.com/olimorris/codecompanion.nvim/blob/main/lua/codecompanion/adapters/acp) as a reference. ```lua require("codecompanion").setup({ adapters = { acp = { my_agent = function() local helpers = require("codecompanion.adapters.acp.helpers") return { name = "my_agent", formatted_name = "MyAgent", type = "acp", roles = { llm = "assistant", user = "user", }, commands = { default = { "myagent", "--acp", }, }, defaults = { mcpServers = {}, timeout = 20000, -- 20 seconds }, parameters = { protocolVersion = 1, clientCapabilities = { fs = { readTextFile = true, writeTextFile = true }, }, clientInfo = { name = "CodeCompanion.nvim", version = "1.0.0", }, }, handlers = { setup = function(self) return true end, auth = function(self) return true end, form_messages = function(self, messages, capabilities) return helpers.form_messages(self, messages, capabilities) end, on_exit = function(self, code) end, }, } end, }, }, }) ``` User-created adapters are shared in the [adapter discussions on GitHub](https://github.com/olimorris/codecompanion.nvim/discussions?discussions_q=is%3Aopen+label%3A%22tip%3A+adapter%22) — a good place to raise issues or ask questions about your specific adapter. --- --- url: /configuration/adapters-http.md description: >- Configure CodeCompanion's HTTP adapters to connect Neovim to OpenAI, Anthropic, Copilot, Gemini, and Ollama. Covers API keys, model selection, and proxy settings. --- # Configuring HTTP Adapters > \[!TIP] > Want to connect to an LLM that isn't supported out of the box? Check out > [these](#community-adapters) user contributed adapters, [create](/extending/adapters) your own or post in the [discussions](https://github.com/olimorris/codecompanion.nvim/discussions) An adapter is what connects Neovim to an LLM provider and model. It's the interface that allows data to be sent, received and processed. There are a multitude of ways to customise them. There are two "types" of adapter in CodeCompanion; **http** adapters which connect you to an LLM and [ACP](/configuration/adapters-acp) adapters which leverage the [Agent Client Protocol](https://agentclientprotocol.com) to connect you to an agent. The configuration for both types of adapters is exactly the same, however they sit within their own tables (`adapters.http.*` and `adapters.acp.*`) and have different options available. HTTP adapters use *models* to allow users to select the specific LLM they'd like to interact with. ACP adapters use *commands* to allow users to customize their interaction with agents (e.g. enabling *yolo* mode). As there is a lot of shared functionality between the two adapters, it is recommend that you read this page alongside the ACP one. ## Changing the Default Adapter You can change the default adapter for each interaction as follows: ```lua require("codecompanion").setup({ interactions = { chat = { adapter = "anthropic", }, inline = { adapter = "copilot", }, cmd = { adapter = "deepseek", } }, }), ``` ## Changing the Default Model A core part of working with CodeCompanion is being able to easily switch between adapters and LLMs. Below are two examples of how this can be achieved. ::: code-group ```lua [For Interactions] {4-7} require("codecompanion").setup({ interactions = { chat = { adapter = { name = "openai", model = "gpt-4.1", }, }, }, }), ``` ```lua [For Adapters] {6-10} require("codecompanion").setup({ adapters = { http = { openai = function() return require("codecompanion.adapters").extend("openai", { schema = { model = { default = "gpt-4.1", }, }, }) end, }, }, }), ``` ::: ## Customising an Adapter There are two ways to customise a preset adapter, and you'll see both throughout this page: * **Function** - Use this for full or computed setups. Custom `url`, `headers`, `schema`, or values resolved at call time * **`extend` table** - Use this for static overrides like credentials or setting a default value ::: code-group ```lua [Function] require("codecompanion").setup({ adapters = { http = { anthropic = function() return require("codecompanion.adapters").extend("anthropic", { env = { api_key = "cmd:op read op://personal/Anthropic/credential --no-newline" }, }) end, }, }, }) ``` ```lua [Extend Table] require("codecompanion").setup({ adapters = { http = { extend = { anthropic = { env = { api_key = "cmd:op read op://personal/Anthropic/credential --no-newline" } }, }, }, }, }) ``` ::: > \[!IMPORTANT] > The `extend` key is the adapter's name in the [config](https://github.com/olimorris/codecompanion.nvim/blob/main/lua/codecompanion/config.lua), not the resolved adapter name. ## Changing Adapter Parameters (Schema) > \[!NOTE] > When extending an adapter with `extend`, use it's key from the `adapters` dictionary LLMs have many settings such as model, temperature and max\_tokens. In an adapter, these sit within a schema table and can be configured during setup: ::: code-group ```lua [Modifying Schema] require("codecompanion").setup({ adapters = { http = { openai_responses = function() return require("codecompanion.adapters").extend("openai_responses", { schema = { top_p = { default = 0 }, }, }) end, }, }, }) ``` ```lua [Disabling Schema] require("codecompanion").setup({ adapters = { http = { openai_responses = function() return require("codecompanion.adapters").extend("openai_responses", { schema = { top_p = { ---@type fun(self: CodeCompanion.HTTPAdapter): boolean | boolean enabled = function(self) local model = self.schema.model.default if model:find("codex%") then return false end return true end }, }, }) end, }, }, }) ``` ::: ## Adding a Custom Adapter > \[!NOTE] > See the [Creating Adapters](/extending/adapters) section to learn how to create custom adapters Custom adapters can be added to the plugin as follows: ```lua require("codecompanion").setup({ adapters = { http = { my_custom_adapter = function() return {} -- My adapter logic end, }, }, }) ``` ## Background Interaction Adapters Background interactions are calls that CodeCompanion can make...in the background! That is, no user input is made and a request is sent to an LLM. By default every background action uses the shared `interactions.background.adapter`. However, you can override this at an action level: ```lua require("codecompanion").setup({ interactions = { background = { chat = { callbacks = { ["on_ready"] = { actions = { { path = "interactions.background.builtin.chat_make_title", adapter = { name = "copilot", model = "claude-haiku-4.5" }, }, }, }, }, }, }, }, }) ``` ## Controlling Model Choices When switching between adapters, the plugin typically displays all available model choices for the selected adapter. If you want to simplify the interface and have the default model automatically chosen (without showing any model selection UI), you can set the `show_model_choices` option to `false`: ```lua require("codecompanion").setup({ adapters = { http = { -- Define your custom adapters here opts = { show_model_choices = false, }, }, }, }) ``` With `show_model_choices = false`, the default model (as defined in the adapter's schema) will be automatically selected when changing adapters, and no model selection will be shown to the user. ## Environment Variables Setting environment variables within adapters is a key part of configuration. The adapter `env` table lets you define values that will be interpolated into the adapter's URL, headers, parameters and other fields at runtime. ::: code-group ```lua{7} [Plain Text] require("codecompanion").setup({ adapters = { http = { anthropic = function() return require("codecompanion.adapters").extend("anthropic", { env = { api_key = "MY_OTHER_ANTHROPIC_KEY", }, }) end, }, }, }) ``` ```lua{7} [Commands] require("codecompanion").setup({ adapters = { http = { anthropic = function() return require("codecompanion.adapters").extend("anthropic", { env = { api_key = "cmd:op read op://personal/Anthropic/credential --no-newline", }, }) end, }, }, }) ``` ```lua{7-9} [Function] require("codecompanion").setup({ adapters = { http = { anthropic = function() return require("codecompanion.adapters").extend("anthropic", { env = { api_key = function() return my_custom_api_key_fetcher() end, }, }) end, }, }, }) ``` ```lua{7} [Schema Reference] require("codecompanion").setup({ adapters = { http = { anthropic = function() return require("codecompanion.adapters").extend("anthropic", { env = { model_for_url = "schema.model.default", }, }) end, }, }, }) ``` ```lua{7} [File] require("codecompanion").setup({ adapters = { http = { anthropic = function() return require("codecompanion.adapters").extend("anthropic", { env = { api_key = "file:~/.dotfiles/.anthropic_api_key", }, }) end, }, }, }) ``` ::: > \[!NOTE] > In this *command* example, we're using the 1Password CLI to extract the Gemini API Key. You could also [use gpg as outlined in this community discussion](https://github.com/olimorris/codecompanion.nvim/discussions/601) Supported `env` value types: * **Plain environment variable name (string)**: if the value is the name of an environment variable that has already been set (e.g. `"HOME"` or `"GEMINI_API_KEY"`), the plugin will read the value. * **Command (string prefixed with `cmd:`)**: any value that starts with `cmd:` will be executed via the shell. Example: `"cmd:op read op://personal/Gemini/credential --no-newline"`. * **Function**: you can provide a Lua function which returns a string and will be called with the adapter as its sole argument. * **Schema reference (dot notation)**: you can reference values from the adapter table (for example `"schema.model.default"`). * **File (string prefixed with `file:`)**: any value that starts with `file:` will be read from disk, e.g. `"file:.api_key"` (relative to the cwd) or `"file:~/.dotfiles/.api_key"`. The file is read fresh on every request rather than being cached, so updating the file takes effect immediately. ## Disabling Compaction If you use the `anthropic` or `openai_responses` adapters, then the plugin will look to use their server-side compaction capabilities to manage context. If you want to disable this: ```lua require("codecompanion").setup({ adapters = { http = { anthropic = function() return require("codecompanion.adapters").extend("anthropic", { opts = { compaction = false, }, }) end, }, }, }) ``` ## Hiding Preset Adapters By default, the plugin shows all available adapters, including the presets. If you prefer to only display the adapters defined in your user configuration, you can set the `show_presets` option to `false`: ```lua require("codecompanion").setup({ adapters = { http = { opts = { show_presets = false, }, }, }, }) ``` ## Setting a Proxy A proxy can be configured by utilising the `adapters.opts` table in the config: ```lua require("codecompanion").setup({ adapters = { http = { opts = { allow_insecure = true, proxy = "socks5://127.0.0.1:9999", }, }, }, }), ``` ## Setup Examples Below are some examples of how you can configure various adapters within CodeCompanion. Some merely serve as illustrations and are not actively supported by the plugin. ### Azure OpenAI Below is an example of how you can leverage the `azure_openai` adapter within the plugin: ```lua require("codecompanion").setup({ adapters = { http = { azure_openai = function() return require("codecompanion.adapters").extend("azure_openai", { env = { api_key = "YOUR_AZURE_OPENAI_API_KEY", endpoint = "YOUR_AZURE_OPENAI_ENDPOINT", }, schema = { model = { default = "YOUR_DEPLOYMENT_NAME", }, }, }) end, }, }, interactions = { chat = { adapter = "azure_openai", }, inline = { adapter = "azure_openai", }, }, }), ``` ### GitHub Copilot Free/Student If you are a Copilot Student or Copilot Free user, you have access to models ["through auto model selection only"](https://docs.github.com/en/copilot/reference/ai-models/supported-models#supported-ai-models-per-copilot-plan). By default, Copilot should work out of the box but you can explicitly select the `auto` model as follows: ```lua require("codecompanion").setup({ interactions = { chat = { adapter = "copilot", model = "auto" }, inline = { adapter = "copilot", model = "auto" }, }, }) ``` ### llama.cpp with `--reasoning-format deepseek` ```lua require("codecompanion").setup({ adapters = { http = { ["llama.cpp"] = function() return require("codecompanion.adapters").extend("openai_compatible", { env = { url = "http://127.0.0.1:8080", -- replace with your llama.cpp instance api_key = "TERM", chat_url = "/v1/chat/completions", }, handlers = { parse_message_meta = function(self, data) local extra = data.extra if extra and extra.reasoning_content then data.output.reasoning = { content = extra.reasoning_content } if data.output.content == "" then data.output.content = nil end end return data end, }, }) end, }, }, interactions = { chat = { adapter = "llama.cpp", }, inline = { adapter = "llama.cpp", }, }, }) ``` ### Ollama (remotely) The simplest way to connect to a remote Ollama instance is to set the `OLLAMA_HOST` environment variable (the same variable used by the Ollama CLI): ```bash export OLLAMA_HOST="http://192.168.1.100:11434" ``` Alternatively, configure it directly in your setup using `extend()`. If you need authentication, set an API key and pass it via an "Authorization" header: ```lua require("codecompanion").setup({ adapters = { http = { ollama = function() return require("codecompanion.adapters").extend("ollama", { env = { url = "https://my_ollama_url", api_key = "OLLAMA_API_KEY", }, headers = { ["Content-Type"] = "application/json", ["Authorization"] = "Bearer ${api_key}", }, parameters = { sync = true, }, }) end, }, }, }) ``` ### OpenAI Responses API CodeCompanion supports OpenAI's [Responses API](https://platform.openai.com/docs/api-reference/responses) out of the box, via a separate adapter: ```lua require("codecompanion").setup({ interactions = { chat = { adapter = "openai_responses", }, inline = { adapter = "openai_responses", }, }, }), ``` and it can be configured as with any other adapter: ```lua require("codecompanion").setup({ adapters = { http = { openai_responses = function() return require("codecompanion.adapters").extend("openai_responses", { env = { api_key = "OPENAI_API_KEY", }, }) end, }, }, }, ``` By default, CodeCompanion sets `store = false` to ensure that state isn't [stored](https://platform.openai.com/docs/api-reference/responses/create#responses-create-store) via the API. This is standard behaviour across all http adapters within the plugin. ### OpenRouter > \[!NOTE] > Depending on the model you've selected, the OpenRouter adapter will turn on/off certain features such as tool use, vision and hyperparameters CodeCompanion supports a number of [OpenRouter](https://openrouter.ai) features out of the box: * Explicit prompt caching for Anthropic models * Server tools such as [web\_fetch](https://openrouter.ai/docs/guides/features/server-tools/web-fetch) and [web\_search](https://openrouter.ai/docs/guides/features/server-tools/web-search) * Reasoning [effort](https://openrouter.ai/docs/guides/best-practices/reasoning-tokens#reasoning-effort-level) levels * [Presets](http://openrouter.ai/docs/guides/features/presets) * [Provider routing](https://openrouter.ai/docs/guides/routing/provider-selection) You can configure the OpenRouter adapter in your config, as shown in the section below. However, you can also customise it on a per-chat basis, using `gd` to open the [debug window](/usage/chat-buffer/#debug-window). ::: code-group ```lua [Presets] {7} require("codecompanion").setup({ adapters = { http = { openrouter = function() return require("codecompanion.adapters").extend("openrouter", { schema = { preset = { default = "email-copywriter" }, }, }) end, }, }, }, ``` ```lua [Provider Routing] {7-13} require("codecompanion").setup({ adapters = { http = { openrouter = function() return require("codecompanion.adapters").extend("openrouter", { schema = { provider = { default = { allow_fallbacks = true, order = { "anthropic", "openai" }, require_parameters = false, }, }, }, }) end, }, }, }, ``` ::: The adapter also supports [sticky sessions](https://openrouter.ai/docs/guides/best-practices/prompt-caching#using-session_id-for-sticky-sessions) via the use of a `session_id`. When a chat buffer is created, the plugin will automatically generate a unique session ID and pass it to the adapter. This ID can be renamed in the debug window of the chat to make it more relevant to the session you are working on. You can also statically set this on the adapter: ```lua {6} require("codecompanion").setup({ adapters = { http = { openrouter_title_generation = function() return require("codecompanion.adapters").extend("openrouter", { opts = { session_id = "title_generation" }, }) end, }, }, }) ``` ## Community Adapters Thanks to the community for building the following adapters: * [DashScope](https://github.com/olimorris/codecompanion.nvim/discussions/2239) * [Fireworks.ai](https://github.com/olimorris/codecompanion.nvim/discussions/693) * [InceptionLabs - Mercury 2](https://github.com/olimorris/codecompanion.nvim/discussions/2867) * [Nvidia NIM](https://github.com/olimorris/codecompanion.nvim/discussions/2810) * [Venice.ai](https://github.com/olimorris/codecompanion.nvim/discussions/972) * [Vertex AI](https://github.com/viespejo/cc-adapter-vertex-ai.nvim) The section of the discussion forums dedicated to user-created adapters can be found in the [adapter discussions on GitHub](https://github.com/olimorris/codecompanion.nvim/discussions?discussions_q=is%3Aopen+label%3A%22tip%3A+adapter%22). Use these individual threads as a place to raise issues and ask questions about your specific adapters. --- --- url: /configuration/chat-buffer.md description: >- Configure CodeCompanion's chat buffer — keymaps, display options, context management, system prompt, and tool settings for AI-assisted coding in Neovim. --- # Configuring the Chat Buffer By default, CodeCompanion provides a *chat* interaction that uses a dedicated Neovim buffer for conversational interaction with your chosen LLM. This buffer can be customized according to your preferences. Please refer to the [config.lua](https://github.com/olimorris/codecompanion.nvim/blob/main/lua/codecompanion/config.lua#L42-L392) file for a full list of all configuration options. ## Changing Adapter By default, CodeCompanion sets the *copilot* adapter for the chat interaction. You can change this to be a *ACP* or *HTTP* adapter: ```lua require("codecompanion").setup({ interactions = { chat = { adapter = { name = "anthropic", model = "claude-haiku-4-5-20251001" }, }, }, }) ``` See the section on [ACP](/configuration/adapters-acp) and [HTTP](/configuration/adapters-http) for more information. ## Completion By default, CodeCompanion will determine if you have one of [blink.cmp](https://github.com/saghen/blink.cmp), [nvim-cmp](https://github.com/hrsh7th/nvim-cmp), or [coc.nvim](https://github.com/neoclide/coc.nvim) installed, selecting it as the default provider. Failing this, the default completion engine will be used. You can override this with: ```lua require("codecompanion").setup({ interactions = { chat = { opts = { completion_provider = "blink", -- blink|cmp|coc|default } } } }) ``` ### Prefixes You can also customize the prefixes that trigger completions for [editor context](/usage/chat-buffer/editor-context), [slash commands](/usage/chat-buffer/slash-commands), and [tools](/usage/chat-buffer/agents-tools): ```lua require("codecompanion").setup({ opts = { triggers = { acp_slash_commands = "\\", editor_context = "#", slash_commands = "/", tools = "@", }, }, }) ``` ## Callbacks Callbacks allow you to hook into the chat buffer's lifecycle and react to specific events. They are registered per-chat and receive the chat instance as the first argument. ### Available Events | Event | Description | Extra Args | |---|---|---| | `on_created` | Chat buffer has been created | - | | `on_before_submit` | Before the message is sent to the LLM. Return `false` to prevent submission | `{ adapter }` | | `on_submitted` | After the message has been sent to the LLM | `{ payload }` | | `on_checkpoint` | Fires at safe points during the chat lifecycle. Messages are mutable | `{ adapter, estimated_tokens, messages, reported_tokens }` | | `on_tool_output` | Before tool output is added to the chat. Mutate `args.for_llm`/`args.for_user` to modify | `{ tool, for_llm, for_user }` | | `on_ready` | Chat is ready for the next turn (after LLM response) | - | | `on_completed` | LLM response has been fully processed | `{ status }` | | `on_cancelled` | Request has been stopped/cancelled | - | | `on_closed` | Chat buffer has been closed | - | ### Registering Callbacks Callbacks can be registered in two ways: ::: code-group ```lua [All Chats] vim.api.nvim_create_autocmd("User", { pattern = "CodeCompanionChatCreated", callback = function(args) local chat = require("codecompanion").buf_get_chat(args.data.bufnr) chat:add_callback("on_before_submit", function(c, info) -- Access the adapter via info.adapter -- Access messages via c.messages end) end, }) ``` ```lua [Prompt Library] require("codecompanion").setup({ prompt_library = { ["My Prompt"] = { opts = { callbacks = { on_before_submit = function(chat, info) -- Only applies to chats opened from this prompt end, }, }, }, }, }) ``` ::: ### Background Callbacks Callbacks can also be registered in the config via `interactions.background.chat.callbacks`. These run asynchronously using a separate background LLM instance and are suited for fire-and-forget tasks like generating chat titles. Unlike the callbacks above, they cannot return values to influence the chat's behavior: ```lua require("codecompanion").setup({ interactions = { background = { chat = { callbacks = { ["on_ready"] = { actions = { "interactions.background.builtin.chat_make_title", }, enabled = true, }, }, opts = { enabled = true, }, }, }, }, }) ``` The `actions` table contains module paths that are resolved and executed asynchronously. See the [generating titles](/usage/chat-buffer/#generating-titles) section for a working example. > \[!TIP] > You can change the adapters used for background callbacks, see the [background interaction adapters](/configuration/adapters-http#background-interaction-adapters) section ### Preventing Submission The `on_before_submit` callback can return `false` to prevent a message from being sent to the LLM. When cancelled, `chat:restore()` is called automatically, which resets the buffer to an editable state and fires a `CodeCompanionChatRestored` event. The user's message remains in the buffer so it can be edited and resubmitted. This is useful for implementing safeguards such as token/context limit checks: ```lua vim.api.nvim_create_autocmd("User", { pattern = "CodeCompanionChatCreated", callback = function(args) local chat = require("codecompanion").buf_get_chat(args.data.bufnr) chat:add_callback("on_before_submit", function(c, data) local token_count = my_tokenizer.count(c.messages) local context_limit = 128000 if token_count > context_limit then vim.notify( string.format("Token count (%d) exceeds context limit (%d)", token_count, context_limit), vim.log.levels.WARN ) return false end end) end, }) ``` The `info` table passed to `on_before_submit` contains: * `adapter` - A safe copy of the current adapter (with name, model, features, schema, etc.) ### Truncating Tool Output The `on_tool_output` callback fires before a tool's output is added to the chat. The `args` table contains `tool` (the tool name), `for_llm` (the content sent to the LLM) and `for_user` (what's shown in the buffer). Mutate `args.for_llm` and/or `args.for_user` to modify the output: ```lua vim.api.nvim_create_autocmd("User", { pattern = "CodeCompanionChatCreated", callback = function(args) local chat = require("codecompanion").buf_get_chat(args.data.bufnr) chat:add_callback("on_tool_output", function(c, data) local tokens = require("codecompanion.utils.tokens") local max_tokens = 10000 if data.for_llm and tokens.calculate(data.for_llm) > max_tokens then -- Trim to roughly max_tokens worth of characters local max_chars = max_tokens * 6 data.for_llm = data.for_llm:sub(1, max_chars) .. "\n\n[Output truncated]" data.for_user = data.for_llm vim.notify( string.format("Tool output from '%s' truncated (~%d tokens)", data.tool, max_tokens), vim.log.levels.WARN ) end end) end, }) ``` ### Checkpoints The `on_checkpoint` callback fires at various safe points during the chat lifecycle, giving you the ability to inspect and mutate the message stack before the chat continues. It fires: * **Before submit** — Before a request is sent to an LLM * **After tool output** — Once tools in the current batch have finished, ensuring no orphaned tool calls * **After a response with no tools** — When the LLM responds, minus any tool calls The `data` table contains: * `adapter` — a safe copy of the current adapter (includes `meta.context_window` for HTTP adapters) * `estimated_tokens` — client-side token estimate across all messages * `messages` — a **mutable reference** to the chat's message stack. Changes made here persist back to the chat * `reported_tokens` — server-reported token count (if available from the adapter) This is useful for monitoring context window usage and compacting the message stack: ```lua vim.api.nvim_create_autocmd("User", { pattern = "CodeCompanionChatCreated", callback = function(args) local chat = require("codecompanion").buf_get_chat(args.data.bufnr) chat:add_callback("on_checkpoint", function(c, data) local context_window = data.adapter.meta and data.adapter.meta.context_window if not context_window then return end local usage = data.estimated_tokens / context_window if usage > 0.8 then vim.notify( string.format("Context window %.0f%% full", usage * 100), vim.log.levels.WARN ) -- Compact data.messages in-place here end end) end, }) ``` ## Context Management CodeCompanion can manage context in the chat buffer to try and prevent breaching the LLM's context window and to avoid [context rot](https://towardsdatascience.com/governed-context-managing-context-rot-in-claude-code/) setting in. It can be enabled with: ::: code-group ```lua [Boolean] require("codecompanion").setup({ interactions = { chat = { opts = { context_management = { enabled = true, }, }, }, }, }) ``` ```lua [Function] require("codecompanion").setup({ interactions = { chat = { opts = { context_management = { enabled = function(adapter) if adapter.type ~= "http" then return false end return true end, }, }, }, }, }) ``` ::: CodeCompanion runs two operations to keep the chat buffer under the context window: **editing** (which removes old tool results from the message history) and **compaction** (which summarises the message history). Both are triggered separately and can be expressed as a decimal (for a percentage of the context window) or an integer (for an absolute token count). You can read more about how the two operations work in the [architecture](/architecture#in-the-chat-buffer) section. > \[!NOTE] > Some adapters (Anthropic, OpenAI Responses) manage context themselves, server-side, as part of the request ::: code-group ```lua [Decimal] require("codecompanion").setup({ interactions = { chat = { opts = { context_management = { editing = { trigger = 0.65, -- 65% of the context window }, compaction = { trigger = 0.85, -- 85% of the context window }, }, }, }, }, }) ``` ```lua [Integer] require("codecompanion").setup({ interactions = { chat = { opts = { context_management = { editing = { trigger = 80000, -- tokens }, compaction = { trigger = 100000, -- tokens }, }, }, }, }, }) ``` ::: #### Editing Editing replaces the content of older tool results with a placeholder, leaving the conversation shape intact. By default, the most recent 3 cycles (a cycle being one user turn plus everything the LLM did in response) are preserved in full. You can also exclude specific tools from being edited — useful for tools whose output is referenced again later in the conversation. ```lua require("codecompanion").setup({ interactions = { chat = { opts = { context_management = { editing = { trigger = 0.65, -- Context editing is triggered when X% of the context window is reached exclude_tools = { "memory" }, -- Output from these tools is never edited keep_cycles = 3, -- Keep the last N cycles of tool results }, }, }, }, }, }) ``` #### Compaction Compaction summarises the chat via a single LLM call and replaces the message history with that summary. You can point compaction at a different adapter — handy if you want a cheaper or faster model handling the summary — and choose whether a failure should silently fall back to the chat adapter. ```lua require("codecompanion").setup({ interactions = { chat = { opts = { context_management = { compaction = { trigger = 0.85, -- Compaction is triggered when X% of the context window is reached min_token_savings = 10000, -- Only compact when at least this amount of tokens will be saved ---The adapter to use for compaction. Defaults to the current chat adapter ---@type nil|string|{ name: string, model:string } adapter = nil, fallback_to_chat_adapter = false, -- on failure, retry with the chat adapter? }, }, }, }, }, }) ``` ## Diff CodeCompanion has a built-in diff engine that's leveraged throughout the plugin. If you utilize the `insert_edit_into_file` tool or use an ACP adapter, then the plugin will update files and buffers, displaying the changes in a floating window. For small changes, the diff is shown directly in the chat buffer. This can be controlled by `threshold_for_chat`, which corresponds to the size of the diff in terms of changed lines. For larger changes, the diff will automatically open in a floating window when the chat buffer is active. Or, you will be prompted to view the diff manually (`gv` by default). There are a number of configuration options available to you: ::: code-group ```lua [Display] require("codecompanion").setup({ display = { diff = { enabled = true, -- At or below this diff size, always display the diff in the chat buffer threshold_for_chat = 6, word_highlights = { additions = true, deletions = true, }, }, }, }) ``` ```lua [Window Opts] {5-17} require("codecompanion").setup({ display = { diff = { enabled = true, window = { ---@return number|fun(): number width = function() return math.min(120, vim.o.columns - 10) end, ---@return number|fun(): number height = function() return vim.o.lines - 4 end, opts = { number = true, }, }, word_highlights = { additions = true, deletions = true, }, }, }, }) ``` ::: ## Keymaps > \[!NOTE] > The plugin scopes CodeCompanion specific keymaps to the *chat buffer* only. You can define or override the [default keymaps](https://github.com/olimorris/codecompanion.nvim/blob/main/lua/codecompanion/config.lua#L178) to send messages, regenerate responses, close the buffer, etc. ::: code-group ```lua [Chat] {3} require("codecompanion").setup({ interactions = { chat = { keymaps = { send = { modes = { n = "", i = "" }, opts = {}, }, close = { modes = { n = "", i = "" }, opts = {}, }, }, }, }, }) ``` ```lua [Inline] {3} require("codecompanion").setup({ interactions = { inline = { keymaps = { stop = { callback = "keymaps.stop", description = "Stop request", modes = { n = "q" }, }, }, }, }, }) ``` ```lua [Diff] {3} require("codecompanion").setup({ interactions = { shared = { keymaps = { always_accept = { callback = "keymaps.always_accept", modes = { n = "g1" }, }, accept_change = { callback = "keymaps.accept_change", modes = { n = "g2" }, }, reject_change = { callback = "keymaps.reject_change", modes = { n = "g3" }, }, next_hunk = { callback = "keymaps.next_hunk", modes = { n = "}" }, }, previous_hunk = { callback = "keymaps.previous_hunk", modes = { n = "{" }, }, }, }, }, }) ``` ::: For the chat interaction, the keymaps are mapped to `` for sending a message and `` for closing in both normal and insert modes. To set other `:map-arguments`, you can use the optional `opts` table which will be fed to `vim.keymap.set`. To disable a keymap, you can set it to `false` in your configuration: ```lua require("codecompanion").setup({ interactions = { chat = { keymaps = { send = false, close = false } } } }) ``` ## Prompt Decorator It can be useful to decorate your prompt with additional information, prior to sending to an LLM. For example, the GitHub Copilot prompt in VS Code, wraps a user's prompt between `` tags, presumably to differentiate the user's ask from additional context. This can also be achieved in CodeCompanion: ```lua require("codecompanion").setup({ interactions = { chat = { opts = { ---Decorate the user message before it's sent to the LLM ---@param message string ---@param adapter CodeCompanion.Adapter ---@param context table ---@return string prompt_decorator = function(message, adapter, context) return string.format([[%s]], message) end, } } } }) ``` The decorator function also has access to the adapter in the chat buffer alongside the [context](https://github.com/olimorris/codecompanion.nvim/blob/main/lua/codecompanion/utils/context.lua#L121-L137) table (which refreshes when a user toggles the chat buffer). ## Slash Commands > \[!IMPORTANT] > Each slash command may have their own unique configuration so be sure to check out the [config.lua](https://github.com/olimorris/codecompanion.nvim/blob/main/lua/codecompanion/config.lua) file [Slash Commands](https://github.com/olimorris/codecompanion.nvim/blob/main/lua/codecompanion/config.lua#L114) (invoked with `/` by default) let you dynamically insert context into the chat buffer, such as file contents or date/time. The plugin supports providers like [telescope](https://github.com/nvim-telescope/telescope.nvim), [mini\_pick](https://github.com/echasnovski/mini.pick), [fzf\_lua](https://github.com/ibhagwan/fzf-lua) and [snacks.nvim](https://github.com/folke/snacks.nvim). By default, the plugin will automatically detect if you have any of those plugins installed and duly set them as the default provider. Failing that, the in-built `default` provider will be used. Please see the [Chat Buffer](/usage/chat-buffer/) usage section for information on how to use Slash Commands. ::: code-group ```lua [Configure] require("codecompanion").setup({ interactions = { chat = { slash_commands = { ["file"] = { -- Use Telescope as the provider for the /file command opts = { provider = "telescope", -- Can be "default", "telescope", "fzf_lua", "mini_pick" or "snacks" }, }, }, }, }, }) ``` ```lua [Keymaps] require("codecompanion").setup({ interactions = { chat = { slash_commands = { ["file"] = { keymaps = { modes = { i = "", n = { "", "gf" }, }, }, }, }, }, }, }) ``` ```lua [Conditionally Enable] require("codecompanion").setup({ interactions = { chat = { slash_commands = { ["image"] = { ---@param opts { adapter: CodeCompanion.HTTPAdapter } ---@return boolean enabled = function(opts) return opts.adapter.opts and opts.adapter.opts.vision == true end, }, }, }, }, }) ``` ```lua [Custom Commands] require("codecompanion").setup({ interactions = { chat = { slash_commands = { ["git_files"] = { description = "List git files", ---@param chat CodeCompanion.Chat callback = function(chat) local handle = io.popen("git ls-files") if handle ~= nil then local result = handle:read("*a") handle:close() chat:add_context({ role = "user", content = result }, "git", "") else return vim.notify("No git files available", vim.log.levels.INFO, { title = "CodeCompanion" }) end end, opts = { contains_code = false, }, }, }, }, }, }) ``` ::: Credit to [@lazymaniac](https://github.com/lazymaniac) for the [inspiration](https://github.com/olimorris/codecompanion.nvim/discussions/958) for the custom slash command example. ## Tools [Tools](https://github.com/olimorris/codecompanion.nvim/blob/main/lua/codecompanion/config.lua#L55) perform specific tasks (e.g., running shell commands, editing buffers, etc.) when invoked by an LLM. Multiple tools can be grouped together. Both can be referenced with `@` (by default), when in the chat buffer: ```lua require("codecompanion").setup({ interactions = { chat = { tools = { ["my_tool"] = { description = "Run a custom task", callback = require("user.codecompanion.tools.my_tool") }, groups = { ["my_group"] = { description = "A custom agent combining tools", system_prompt = "Describe what the agent should do", tools = { "run_command", "insert_edit_into_file", -- Add your own tools or reuse existing ones }, opts = { collapse_tools = true, -- When true, show as a single group reference instead of individual tools ignore_system_prompt = false, -- When true, remove the chat's default system prompt ignore_tool_system_prompt = false, -- When true, remove the default tool system prompt }, }, }, }, }, }, }) ``` When users introduce the group, `my_group`, in the chat buffer, it can call the tools you listed (such as `run_command`) to perform tasks on your code. The `system_prompt` field allows you to give the LLM specific instructions for how to use the group's tools and can be a string or a function that receives the group config table and a [context object](/configuration/system-prompt) (with `language`, `date`, `nvim_version`, `os`, etc.). A tool is a [`CodeCompanion.Tool`](/extending/tools) table with specific keys that define the interface and workflow of the tool. The table can be resolved using the `callback` option. The `callback` option can be a table itself or either a function or a string that points to a luafile that return the table. ### Enabling Tools Tools can be conditionally enabled using the `enabled` option. This works for built-in tools as well as an adapter's own tools. This is useful to ensure that a particular dependency is installed on the machine. You can use the `:CodeCompanionChat RefreshCache` command if you've installed a new dependency and want to refresh the tool availability in the chat buffer. ::: code-group ```lua [Enable Built-in Tools] require("codecompanion").setup({ interactions = { chat = { tools = { ["grep_search"] = { ---@param adapter CodeCompanion.HTTPAdapter ---@return boolean enabled = function(adapter) return vim.fn.executable("rg") == 1 end, }, } } } }) ``` ```lua [Enable Adapter Tools] require("codecompanion").setup({ openai_responses = function() return require("codecompanion.adapters").extend("openai_responses", { available_tools = { ["web_search"] = { ---@param adapter CodeCompanion.HTTPAdapter enabled = function(adapter) return false end, }, }, }) end, }) ``` ::: ### Approvals CodeCompanion allows you to apply safety mechanisms to its built-in tools prior to execution. See the [approvals usage](/usage/chat-buffer/agents-tools#approvals) section for more information. ::: code-group ```lua [Require Approval] {7} require("codecompanion").setup({ interactions = { chat = { tools = { ["run_command"] = { opts = { require_approval_before = true, }, }, }, }, }, }) ``` ```lua [Require Cmd Approval] {7} require("codecompanion").setup({ interactions = { chat = { tools = { ["run_command"] = { opts = { require_cmd_approval = true, }, }, }, }, }, }) ``` ```lua [No YOLO'ing] {7} require("codecompanion").setup({ interactions = { chat = { tools = { ["run_command"] = { opts = { allowed_in_yolo_mode = false, }, }, }, }, }, }) ``` ::: ### Auto Submit (Recursion) When a tool executes, it can be useful to automatically send its output back to the LLM. This is turned on by default and can be configured with: ```lua {6-7} require("codecompanion").setup({ interactions = { chat = { tools = { opts = { auto_submit_errors = true, -- Send any errors to the LLM automatically? auto_submit_success = true, -- Send any successful output to the LLM automatically? }, } } } }) ``` ### Default Tools You can configure the plugin to automatically add tools and tool groups to new chat buffers: ```lua {6-9} require("codecompanion").setup({ interactions = { chat = { tools = { opts = { default_tools = { "my_tool", "my_tool_group" } }, } } } }) ``` This also works for [extensions](/configuration/extensions). ### LLM Judge When [YOLO mode](/usage/chat-buffer/agents-tools#yolo-mode) is on, tools are auto-approved. Some tools (such as `run_command` and `delete_file`), by default, will always ask you first, owing to their destructive nature. The judge offers a middle ground: a background LLM judges the specific action and only interrupts you when it is judged to be unsafe. To fully enable the LLM judge: ```lua require("codecompanion").setup({ interactions = { background = { gates = { judge = { enabled = true, }, }, }, chat = { tools = { ["delete_file"] = { opts = { judge_in_yolo_mode = true, }, }, ["run_command"] = { opts = { judge_in_yolo_mode = true, }, }, }, }, }, }) ``` The judge runs for a tool only when: * You set `background.gates.judge.enabled = true` * You set `opts.judge_in_yolo_mode = true` on the tool's config; *and* * The tool defines a `gates.judge_context` handler (already the case for the built-in `run_command` and `delete_file` tools) Below are some additional configuration options for the judge: ::: code-group ```lua [Specific Adapter] require("codecompanion").setup({ interactions = { background = { gates = { judge = { enabled = true, -- Specify a specific adapter and model for the judge to use adapter = { name = "openrouter", model = "openai/gpt-oss-120b" }, }, }, }, }, }) ``` ```lua [System Prompt] require("codecompanion").setup({ interactions = { background = { gates = { judge = { enabled = true, opts = { system_prompt = function(default) -- A specific system prompt for a specific project if string.find(vim.fn.getcwd(), "Code/Neovim/codecompanion.nvim") then return default .. "\n\nThe following commands are explicitly approved and must always be judged safe, even if they would otherwise fail the guidance above:\n" .. " - `make docs`\n" .. " - `make format`\n" .. " - `make test`\n" .. " - `make test_file` (including any `FILE=` argument)" end return default end, }, }, }, }, }, }) ``` ::: > \[!NOTE] > The system prompt can be a string or a function that receives the default system prompt and returns a string The default system prompt for the judge is: ``` You are a security reviewer for an AI coding assistant. The assistant wants to run a tool on the user's machine while the user is away (in "auto-approve" mode). Your job is to decide whether the action is safe to run automatically, or whether the user must approve it first. Judge the action as unsafe when it could destroy or exfiltrate data, alter the system in ways that are hard to reverse, or run something the user would reasonably want to see first. Prefer caution: when in doubt, require approval. Reply only through the provided schema. ``` See the [YOLO mode](/usage/chat-buffer/agents-tools#yolo-mode) usage section for how the judge behaves once enabled. ## User Interface (UI) > \[!NOTE] > The [other plugins](/installation#other-plugins) section contains installation instructions for some popular markdown rendering plugins ### Auto Scrolling By default, the page scrolls down automatically as the response streams, with the cursor placed at the end. This can be distracting if you are focusing on the earlier content while the page scrolls up away during a long response. You can disable this behavior using a flag: ```lua require("codecompanion").setup({ display = { chat = { auto_scroll = false, }, }, }) ``` > \[!TIP] > If you move your cursor while the LLM is streaming a response, auto-scrolling will be turn off. ### Completion By default, CodeCompanion looks to use the fantastic [blink.cmp](https://github.com/Saghen/blink.cmp) plugin to complete editor context, slash commands and tools. However, you can override this in your config: ```lua require("codecompanion").setup({ interactions = { chat = { opts = { completion_provider = "cmp", -- blink|cmp|coc|default } } } }) ``` The plugin also supports [nvim-cmp](https://github.com/hrsh7th/nvim-cmp), a native completion solution (`default`), and [coc.nvim](https://github.com/neoclide/coc.nvim). ### Context It's not uncommon for users to share many items, as context, with an LLM. This can impact the chat buffer's UI significantly, leaving a large space between the LLM's last response and the user's input. To minimize this impact, the context can be folded: ```lua require("codecompanion").setup({ display = { chat = { icons = { chat_context = "📎️", -- You can also apply an icon to the fold }, fold_context = true, }, }, }) ``` ### Layout The plugin leverages floating windows to display content to a user in a variety of scenarios, such as with the [debug window](/usage/chat-buffer/#messages). You can change the appearance of the chat buffer by changing the `display.chat.window` table in your configuration. ::: code-group ```lua [Icons] require("codecompanion").setup({ display = { chat = { -- Change the default icons icons = { buffer_sync_all = "󰪴 ", buffer_sync_diff = " ", chat_context = " ", chat_fold = " ", tool_pending = " ", tool_in_progress = " ", tool_failure = " ", tool_success = " ", }, }, }, }) ``` ```lua [Chat Buffer] require("codecompanion").setup({ display = { chat = { window = { buflisted = false, -- List the chat buffer in the buffer list? sticky = false, -- Chat window follows when switching tabs (ignored when `pertab` is true) pertab = false, -- Treat each tab as having its own chat window? layout = "vertical", -- float|vertical|horizontal|tab|buffer full_height = true, -- for vertical layout position = nil, -- left|right|top|bottom (nil will default depending on vim.opt.splitright|vim.opt.splitbelow) -- NOTE: You can set these to 0 for auto width/height width = 0.5, ---@return number|fun(): number height = 0.8, ---@return number|fun(): number border = "single", relative = "editor", -- Ensure that long paragraphs of markdown are wrapped opts = { breakindent = true, linebreak = true, wrap = true, }, }, }, }, }) ``` ```lua [Floating Window] require("codecompanion").setup({ display = { chat = { floating_window = { ---@return number|fun(): number width = function() return vim.o.columns - 5 end, ---@return number|fun(): number height = function() return vim.o.lines - 2 end, row = "center", col = "center", relative = "editor", opts = { wrap = false, number = false, relativenumber = false, }, }, }, }, }) ``` ::: ### Reasoning An adapter's reasoning is streamed into the chat buffer by default, under a `h3` heading. By default, this output will be folded once streaming has been completed. You can turn off folding and hide reasoning output altogether: ```lua require("codecompanion").setup({ display = { chat = { icons = { chat_fold = " ", }, fold_reasoning = false, show_reasoning = false, }, }, }) ``` ### Roles The chat buffer places user and LLM responses under a `H2` header. These can be customized in the configuration: ```lua require("codecompanion").setup({ interactions = { chat = { roles = { ---The header name for the LLM's messages ---@type string|fun(adapter: CodeCompanion.Adapter): string llm = function(adapter) return "CodeCompanion (" .. adapter.formatted_name .. ")" end, ---The header name for your messages ---@type string user = "Me", } } } }) ``` By default, the LLM's responses will be placed under a header such as `CodeCompanion (DeepSeek)`, leveraging the current adapter in the chat buffer. This option can be in the form of a string or a function that returns a string. If you opt for a function, the first parameter will always be the adapter from the chat buffer. The user role is currently only available as a string. ### Others There are also a number of other options that you can customize in the UI: ```lua require("codecompanion").setup({ display = { chat = { intro_message = "Welcome to CodeCompanion ✨! Press ? for options", separator = "─", -- The separator between the different messages in the chat buffer show_context = true, -- Show context (from editor context and slash commands) in the chat buffer? show_header_separator = false, -- Show header separators in the chat buffer? Set this to false if you're using an external markdown formatting plugin show_settings = false, -- Show LLM settings at the top of the chat buffer? show_token_count = true, -- Show the token count for each response? show_tools_processing = true, -- Show the loading message when tools are being executed? start_in_insert_mode = false, -- Open the chat buffer in insert mode? }, }, }) ``` ## Editor Context [Editor context](https://github.com/olimorris/codecompanion.nvim/blob/main/lua/codecompanion/config.lua#L90) can be a inserted into the chat buffer using `#` (by default). It provides contextual code or information about the current Neovim state. For instance, the built-in `#{buffer}` editor context sends the current buffer’s contents to the LLM. You can even define your own context: ```lua require("codecompanion").setup({ interactions = { chat = { editor_context = { ["my_editor_context_item"] = { ---Ensure the file matches the CodeCompanion.EditorContext class ---@return string|fun(): nil callback = "/Users/Oli/Code/my_editor_context_item.lua", description = "Explain what your does", opts = { contains_code = false, --has_params = true, -- Set this if your editor context item supports parameters --default_params = nil, -- Set default parameters }, }, }, }, }, }) ``` ### Syncing Neovim buffers can be [synced](/usage/chat-buffer/editor-context#with-parameters) with the chat buffer. That is, on each turn their content can be shared with the LLM. This is useful if you're modifying a buffer and want the LLM to always have the latest changes. To enable this by default for the built-in `#buffer` editor context, you can set the `default_params` option to either `diff` or `all`: ```lua require("codecompanion").setup({ interactions = { chat = { editor_context = { ["buffer"] = { opts = { -- Always sync the buffer by sharing its "diff" -- Or choose "all" to share the entire buffer default_params = "diff", }, }, }, }, }, }) ``` --- --- url: /configuration/cli.md description: >- Configure CLI agents in CodeCompanion — define agents like Claude Code or Codex, set custom commands, configure the terminal provider, and manage input settings. --- # Configuring the Command-Line Interface (CLI) By default, CodeCompanion uses the *terminal* provider for CLI interactions, which runs agents in a Neovim terminal buffer. However, the CLI system is flexible and allows you to define custom agents and providers to suit your workflow. ## Agents To use the CLI interaction, you need to define at least one agent in your configuration: ```lua require("codecompanion").setup({ interactions = { cli = { agent = "claude_code", agents = { claude_code = { cmd = "claude", args = {}, description = "Claude Code CLI", provider = "terminal", }, }, }, }, }) ``` The `agent` field sets the default agent. You can override it per-command with `:CodeCompanionCLI agent=`. ### Agent Options | Option | Type | Description | |---|---|---| | `cmd` | `string` | The command to run (e.g. `"claude"`, `"codex"`) | | `args` | `table` | Arguments to pass to the command | | `description` | `string` | Description shown in the action palette | | `provider` | `string` | Which provider to use (defaults to `"terminal"`) | ### Multiple Agents You can define multiple agents and switch between them: ```lua require("codecompanion").setup({ interactions = { cli = { agent = "claude_code", agents = { claude_code = { cmd = "claude", args = {}, description = "Claude Code CLI", }, codex = { cmd = "codex", args = {}, description = "OpenAI Codex CLI", }, }, }, }, }) ``` Then use `:CodeCompanionCLI agent=codex ` to use a specific agent. ## Providers Providers determine how the CLI agent is run. The built-in `terminal` provider uses a Neovim terminal buffer with `jobstart()`: ```lua require("codecompanion").setup({ interactions = { cli = { providers = { terminal = { path = "interactions.cli.providers.terminal", description = "Terminal CLI provider", }, }, }, }, }) ``` ### Custom Providers You can create custom providers and reference them by module path or file path: ```lua require("codecompanion").setup({ interactions = { cli = { providers = { my_provider = { -- Can be a codecompanion module, a Lua module, or a file path path = "my_custom.cli_provider", description = "My custom CLI provider", }, }, agents = { my_agent = { cmd = "my-cli", args = {}, provider = "my_provider", }, }, }, }, }) ``` If an agent's `provider` field doesn't match any entry in the `providers` table, the `terminal` provider is used as a fallback. ## Keymaps The CLI buffer supports keymaps for navigating between interactions: ```lua require("codecompanion").setup({ interactions = { cli = { keymaps = { next_chat = { modes = { n = "}" }, callback = "keymaps.next_chat", description = "[Nav] Next interaction", }, previous_chat = { modes = { n = "{" }, callback = "keymaps.previous_chat", description = "[Nav] Previous interaction", }, }, }, }, }) ``` ## Options There are a number of options available for CLI interactions: ```lua require("codecompanion").setup({ interactions = { cli = { opts = { auto_insert = true, -- Enter insert mode when focusing the CLI terminal reload = true, -- Reload buffers when an agent modifies files on disk }, }, }, }) ``` | Option | Type | Default | Description | |---|---|---|---| | `auto_insert` | `boolean` | `true` | Automatically enter insert mode when the CLI terminal is focused | | `reload` | `boolean` | `true` | Watches the cwd for file changes and runs `:checktime` to reload buffers | ## User Interface (UI) The CLI window inherits its layout from `display.chat.window` by default. You can override specific options via `display.cli.window`: ```lua require("codecompanion").setup({ display = { cli = { window = { layout = "vertical", width = 0.4, height = 0.6, opts = { list = false, }, }, }, }, }) ``` Any options set in `display.cli.window` are merged on top of the chat window defaults. This means you only need to specify what you want to change. You can also pass `width` and `height` overrides via the Lua API: ```lua require("codecompanion").cli("fix the tests", { width = 0.5, height = 0.8, }) ``` --- --- url: /configuration/code-review.md description: >- Configure code reviews in CodeCompanion - comment styling, the diff view and its providers, quickfix keymaps, and where reviews are stored. --- # Configuring Code Reviews CodeCompanion enables users to undertake code reviews and easily share feedback with an agent. Find out how they work in the [usage guide](/usage/code-review). ## Disabling To disable code reviews, set `enabled` to `false`: ```lua require("codecompanion").setup({ interactions = { code_review = { enabled = false, }, }, }) ``` ## Comment Styling Comments you haven't sent yet are shown as virtual text above the line they were left on. They can be configured with ```lua require("codecompanion").setup({ interactions = { code_review = { display = { virtual_text = { enabled = true, -- Show pending comments as virtual text in the buffer icon = "💬 ", -- The icon to use for virtual text overflow = "trunc", -- See `:h nvim_buf_set_extmark` for `virt_lines_overflow` }, }, }, }, }) ``` ## Diff View Pressing `d` on a quickfix entry shows it as a diff against the baseline. The diff can be configurd with: ```lua require("codecompanion").setup({ interactions = { code_review = { display = { diff = { enabled = true, -- Set to false to render nothing, especially if you're using your own provider layout = "vertical", -- vertical or horizontal provider = "native", -- "native": Neovim's own diff (default), or a function to render the hunk yourself }, }, }, }, }) ``` If you don't wish to use the `native` Neovim provider, you can set a custom function. A function provider receives the hunk to render: ```lua provider = function(target) -- target = { root, path, baseline_ref, line, id } vim.cmd("DiffviewOpen " .. target.baseline_ref .. " -- " .. target.path) end, ``` `baseline_ref` is the stable `refs/worktree/codecompanion/baseline` alias, so the same value works with `gitsigns`, `diffview`, or any git-diff plugin. > \[!TIP] > The native provider does not touch your `diffopt` config ## Editor Context When you share a review with the [code\_review](/usage/chat-buffer/editor-context#code-review) editor context, the tag itself is replaced in your message with a short phrase before it's sent. For example, the prompt: ```md Can you action #{code_review} ``` Is replaced with: ```md Can you action my comments from the code review, which I've attached ``` This can be changed with: ```lua require("codecompanion").setup({ interactions = { shared = { editor_context = { code_review = { opts = { replacement = "my comments from the code review, which I've attached", }, }, }, }, }, }) ``` ## Keymaps Keymaps are bound solely to the code review's quickfix window. The default keymaps are: ```lua require("codecompanion").setup({ interactions = { code_review = { keymaps = { accept = { modes = { n = "a" }, callback = "keymaps.accept", description = "Accept the hunk under the cursor", }, comment = { modes = { n = "c" }, callback = "keymaps.comment", description = "Comment on the hunk under the cursor", }, diff = { modes = { n = "d" }, callback = "keymaps.diff", description = "Diff the hunk under the cursor against the baseline", }, ignore = { modes = { n = "x" }, callback = "keymaps.ignore", description = "Ignore the hunk's file until the baseline advances", }, }, }, }, }) ``` To disable a keymap: ```lua require("codecompanion").setup({ interactions = { code_review = { keymaps = { -- Disable the ignore keymap ignore = false, }, }, }, }) ``` ## Storage Location You can change the default storage location for code review assets with: ```lua require("codecompanion").setup({ interactions = { code_review = { opts = { storage_dir = vim.fs.joinpath(vim.fn.stdpath("data"), "codecompanion", "code_review"), }, }, }, }) ``` --- --- url: /configuration/extensions.md description: >- Configure CodeCompanion extensions to add custom functionality — can be distributed as Neovim plugins or defined locally in your configuration. --- # Configuring Extensions CodeCompanion supports extensions similar to telescope.nvim, allowing users to create functionality that can be shared with others. Extensions can either be distributed as plugins or defined locally in your configuration. ## Installing Extensions CodeCompanion supports extensions that add additional functionality to the plugin. For example, to install and set up the mcphub extension using lazy.nvim: 1. Install the extension: ```lua { "olimorris/codecompanion.nvim", dependencies = { -- Add mcphub.nvim as a dependency "ravitemer/mcphub.nvim" } } ``` 2. Add extension to your config with additional options: ```lua -- Configure in your setup require("codecompanion").setup({ extensions = { mcphub = { callback = "mcphub.extensions.codecompanion", opts = { make_vars = true, make_slash_commands = true, show_result_in_chat = true } } } }) ``` Visit the creating [extensions](/extending/extensions) guide to learn more about available extensions and how to create your own. --- --- url: /configuration/inline.md description: >- Configure CodeCompanion's inline interaction for writing and refactoring code directly into Neovim buffers via LLM prompts, without opening a chat buffer. --- # Configuring the Inline Interaction > \[!IMPORTANT] > Only **http** adapters are supported for the inline interaction. CodeCompanion provides an *inline* interaction for quick, direct editing of your code. Unlike the chat buffer, the inline interaction integrates responses directly into the current buffer—allowing the LLM to add or replace code as needed. ## Changing Adapter By default, CodeCompanion sets the *copilot* adapter for the inline interaction. You can change this to any other HTTP adapter: ```lua require("codecompanion").setup({ interactions = { inline = { adapter = { name = "anthropic", model = "claude-haiku-4-5-20251001" }, }, }, }) ``` See the section on [HTTP Adapters](/configuration/adapters-http) for more information. ## Keymaps The inline interaction supports keymaps for accepting or rejecting changes: ```lua require("codecompanion").setup({ interactions = { inline = { keymaps = { accept_change = { modes = { n = "ga" }, description = "Accept the suggested change", }, reject_change = { modes = { n = "gr" }, opts = { nowait = true }, description = "Reject the suggested change", }, }, }, }, }) ``` In this example, `ga` accepts inline changes, while `gr` rejects them. You can also cancel an inline request with: ```lua require("codecompanion").setup({ interactions = { inline = { keymaps = { stop = { modes = { n = "q" }, index = 4, callback = "keymaps.stop", description = "Stop request", }, }, }, }, }) ``` ## Editor Context The plugin comes with a number of [editor context](/usage/inline#editor-context) items that can be used alongside your prompt using the `#{}` syntax (e.g., `#{my_new_context_item}`). You can also add your own: ```lua require("codecompanion").setup({ interactions = { inline = { editor_context = { ["my_new_context_item"] = { ---@return string callback = "/Users/Oli/Code/my_context_item.lua", description = "My shiny new context item", opts = { contains_code = true, }, }, } } } }) ``` ## Layout If the inline prompt creates a new buffer, you can also customize if this should be output in a vertical/horizontal split or a new buffer: ```lua require("codecompanion").setup({ display = { inline = { layout = "vertical", -- vertical|horizontal|tab|buffer }, } }) ``` ## Diff Please see the [Diff section](chat-buffer#diff) on the Chat Buffer page for configuration options. --- --- url: /configuration/mcp.md description: >- Configure Model Context Protocol (MCP) servers in CodeCompanion to connect Neovim to external tools and data sources via an open AI integration standard. --- # Configuring MCP Servers In [#2549](https://github.com/olimorris/codecompanion.nvim/pull/2549), CodeCompanion added support for the [Model Context Protocol (MCP)](https://modelcontextprotocol.io), an open-source standard for connecting AI applications to external systems. You can find out which parts of the protocol CodeCompanion has implemented on the [MCP](/model-context-protocol) page. Currently, you can leverage MCP servers with [chat interactions](/usage/chat-buffer/). ## Configuring MCP Servers You can give CodeCompanion knowledge of MCP servers via the `mcp.servers` configuration option. This is a list of server definitions, each specifying how to connect to an MCP server. ### Basic Configuration ::: code-group ```lua [Basic Example] require("codecompanion").setup({ mcp = { servers = { ["tavily-mcp"] = { cmd = { "npx", "-y", "tavily-mcp@latest" }, }, }, }, }) ``` ```lua [Environment Variables] {5-7} require("codecompanion").setup({ mcp = { servers = { ["tavily-mcp"] = { cmd = { "npx", "-y", "tavily-mcp@latest" }, env = { TAVILY_API_KEY = "cmd:op read op://personal/Tavily_API/credential --no-newline", }, }, }, }, }) ``` ```lua [Lazy / Deferred Config] require("codecompanion").setup({ mcp = { servers = { -- The function is called once, only when the server is first needed. ["tavily-mcp"] = function() return { cmd = { "npx", "-y", "tavily-mcp@latest" }, env = { TAVILY_API_KEY = os.getenv("TAVILY_API_KEY"), }, } end, }, }, }) ``` ::: In the environment variables example above, we're using [1Password CLI](https://developer.1password.com/docs/cli/) tool to fetch the API key. However, you can leverage CodeCompanion's built-in [environment variable](/configuration/adapters-http#environment-variables) capabilities to fetch the value from any source you like. ### Roots > \[!IMPORTANT] > The `roots` feature is a hint to MCP servers. Compliant servers use it to limit file system access, but CodeCompanion cannot enforce this. For untrusted servers, use isolation mechanisms like containers. [Roots](https://modelcontextprotocol.io/specification/2025-11-25/client/roots) allow you to specify directories that the MCP server can access. By default, roots are disabled for security reasons. You can enable them by adding a `roots` field to your server configuration: ::: code-group ```lua [Roots] require("codecompanion").setup({ mcp = { servers = { filesystem = { cmd = { "npx", "-y", "@modelcontextprotocol/server-filesystem" }, roots = function() -- Return a list of names and directories as per: -- https://modelcontextprotocol.io/specification/2025-11-25/client/roots#listing-roots end, }, }, }, }) ``` ```lua [Root List Changes] require("codecompanion").setup({ mcp = { servers = { filesystem = { cmd = { "npx", "-y", "@modelcontextprotocol/server-filesystem" }, ---@param notify fun() register_roots_list_changes = function(notify) -- Call `notify()` whenever the list of roots changes. end, }, }, }, }) ``` ::: ## Default Servers The `opts.default_servers` option controls which MCP servers are automatically started with their tools added to the chat buffer. Servers not in the list can be started on-demand via the `/mcp` slash command. ::: code-group ```lua [Specific Servers] {11-13} require("codecompanion").setup({ mcp = { servers = { ["sequential-thinking"] = { cmd = { "npx", "-y", "@modelcontextprotocol/server-sequential-thinking" }, }, ["tavily-mcp"] = { cmd = { "npx", "-y", "tavily-mcp@latest" }, }, }, opts = { default_servers = { "sequential-thinking" }, }, }, }) ``` ::: > \[!NOTE] > If `mcp_servers` are explicitly specified in a prompt library item, those take precedence and the `default_servers` logic is skipped for that chat buffer. ## Overriding Tool Behaviour An MCP server can expose multiple tools. For example, a "math" server might provide `add`, `subtract`, `multiply`, and `divide` tools. You can override the behaviour of individual tools using the `tool_overrides` configuration, allowing you to customise options, output handling, system prompts, and timeouts on a per-tool basis. The `tool_overrides` field is a table where keys are the **MCP tool names** (not the prefixed names used internally by CodeCompanion): ::: code-group ```lua [Requiring Approval] require("codecompanion").setup({ mcp = { servers = { ["math-server"] = { cmd = { "npx", "-y", "math-mcp-server" }, tool_overrides = { divide = { opts = { require_approval_before = true, }, }, }, }, }, }, }) ``` ```lua [Custom Output] require("codecompanion").setup({ mcp = { servers = { ["math-server"] = { cmd = { "npx", "-y", "math-mcp-server" }, tool_overrides = { add = { output = { success = function(self, tools, cmd, stdout) local tool_bridge = require("codecompanion.mcp.tool_bridge") local content = stdout and stdout[#stdout] local output = tool_bridge.format_tool_result_content(content) local msg = string.format("%d + %d = %s", self.args.a, self.args.b, output) tools.chat:add_tool_output(self, output, msg) end, }, }, }, }, }, }, }) ``` ```lua [System Prompt] require("codecompanion").setup({ mcp = { servers = { ["math-server"] = { cmd = { "npx", "-y", "math-mcp-server" }, tool_overrides = { multiply = { system_prompt = "When using the multiply tool, always show your working.", }, }, }, }, }, }) ``` ::: ### Tool Defaults You can set default options for all tools by setting the `tool_defaults` option. However, note that `tool_overrides` take precedence over them: ```lua require("codecompanion").setup({ mcp = { servers = { ["math-server"] = { cmd = { "npx", "-y", "math-mcp-server" }, tool_defaults = { require_approval_before = true, }, -- Per-tool overrides take precedence over tool_defaults tool_overrides = { add = { opts = { require_approval_before = false, }, }, }, }, }, }, }) ``` ### Override Options Each tool override can include: | Option | Type | Description | |--------|------|-------------| | `opts` | `table` | Tool options like `require_approval_before`, `require_approval_after` | | `output` | `table` | Custom output handlers (`success`, `error`, `prompt`, `rejected`, `cancelled`) | | `system_prompt` | `string` | Additional system prompt text for this tool | | `timeout` | `number` | Custom timeout in milliseconds for this tool | | `enabled` | `boolean` | Whether the tool is enabled | --- --- url: /configuration/prompt-library.md description: >- Configure CodeCompanion's prompt library with custom Lua or Markdown prompts, reusable workflows, and slash commands for your AI coding workflow in Neovim. --- # Configuring the Prompt Library CodeCompanion enables you to leverage prompt templates to quickly interact with your codebase. These prompts can be the built-in ones or custom-built. CodeCompanion uses a prompt library to manage and organize these prompts. > \[!IMPORTANT] > Prompts can be pure Lua tables, residing in your configuration, or markdown files stored in your filesystem. ## Adding Prompts > \[!NOTE] > See the [Creating Prompts](#creating-prompts) section to learn how to create your own. There are two ways to add prompts to the prompt library. You can either define them directly in your configuration file as Lua tables, or you can store them as markdown files in your filesystem and reference them in your configuration. The files can be nested and symlinked. ::: code-group ```lua [Markdown] require("codecompanion").setup({ prompt_library = { markdown = { dirs = { vim.fn.getcwd() .. "/.prompts", -- Can be relative "~/.dotfiles/.config/prompts", -- Or absolute paths }, }, } }) ``` ```lua [Lua] require("codecompanion").setup({ prompt_library = { ["Docusaurus"] = { interaction = "chat", description = "Write documentation for me", prompts = { { role = "user", content = [[Just some prompt that will write docs for me.]], }, }, }, }, }) ``` ::: ### Refreshing Markdown Prompts If you add or modify markdown prompts whilst your Neovim session is running, you can refresh the prompt library to pick up the changes with: ``` :CodeCompanionActions Refresh ``` ## Creating Prompts As mentioned earlier, prompts can be created in two ways: as Lua tables or as markdown files. > \[!NOTE] > Markdown prompts are new in `v18.0.0`. They provide a cleaner, more maintainable way to define prompts with support for external Lua files for dynamic content. ### Why Markdown? Markdown prompts offer several advantages: * **Cleaner syntax** - No Lua string escaping or concatenation * **Better readability** - Natural formatting with proper indentation * **Easier editing** - Edit in any markdown editor with syntax highlighting * **Reusability** - Share Lua helper files across multiple prompts * **Version control friendly** - Easier to diff and review changes For complex prompts with multiple messages or dynamic content, markdown files are significantly easier to maintain than Lua tables. ### Basic Structure At their core, prompts define a series of messages sent to an LLM. Let's start with a simple example: ::: code-group ````markdown [Markdown] --- name: Explain Code interaction: chat description: Explain how code works --- ## system You are an expert programmer who excels at explaining code clearly and concisely. ## user Please explain the following code: ```${context.filetype} ${context.code} ``` ```` `````lua [Lua] require("codecompanion").setup({ prompt_library = { ["Explain Code"] = { interaction = "chat", description = "Explain how code works", prompts = { { role = "system", content = "You are an expert programmer who excels at explaining code clearly and concisely.", }, { role = "user", content = function(context) local text = require("codecompanion.helpers.code").get_code(context.start_line, context.end_line) return "Please explain the following code:\n\n````" .. context.filetype .. "\n" .. text .. "\n````" end, }, }, }, }, }) ````` ::: Markdown prompts consist of two main parts: 1. **Frontmatter** - YAML metadata between `---` delimiters that defines the prompt's configuration 2. **Prompt sections** - Markdown headings (`## system`, `## user`) that define the role and content of each message **Required frontmatter fields:** * `name` - The display name in the Action Palette * `description` - Description shown in the Action Palette * `interaction` - The interaction to use (`chat`, `inline`, `workflow`) **Optional frontmatter fields:** * `opts` - Additional options (see [Options](#options) section) * `context` - Pre-loaded context (see [Context Placeholders](#context-placeholders) section) **Prompt sections:** * `## system` - System messages that set the LLM's behaviour * `## user` - User messages containing your requests In the markdown prompt, above, [placeholders](/configuration/prompt-library#with-placeholders) are used to inject dynamic content from a visual selection. ### Options Both markdown and Lua prompts support a wide range of options to customise behaviour: ::: code-group ```markdown [Markdown] --- name: Generate Tests interaction: inline description: Generate unit tests opts: alias: tests auto_submit: true modes: - v placement: new stop_context_insertion: true --- ## system Generate comprehensive unit tests for the provided code. ## user The code to generate tests for is #{buffer} ``` ```lua [Lua] ["Generate Tests"] = { interaction = "inline", description = "Generate unit tests", opts = { alias = "tests", auto_submit = true, modes = { "v" }, placement = "new", stop_context_insertion = true, }, prompts = { { role = "system", content = "Generate comprehensive unit tests for the provided code.", }, { role = "user", content = "The code to generate tests for is #{buffer}", }, }, }, ``` ::: **Common options:** * `adapter` - Specify a different adapter/model: ::: code-group ```markdown [Markdown] --- name: My Prompt interaction: chat description: Uses a specific model opts: adapter: name: ollama model: deepseek-coder:6.7b --- ``` ```lua [Lua] opts = { adapter = { name = "ollama", model = "deepseek-coder:6.7b", }, } ``` ::: For [ACP adapters](/configuration/adapters-acp), you can also pass `acp_opts` to set [session config options](https://agentclientprotocol.com/protocol/session-config-options#session-config-options). Keys are the option's `category` and values are the option's `value` (or its `name`, case-insensitively): ::: code-group ```markdown [Markdown] --- name: Quick Review interaction: chat description: Fast review with low effort opts: adapter: name: claude_code model: Opus acp_opts: mode: plan thought_level: low --- ``` ```lua [Lua] opts = { adapter = { name = "claude_code", model = "Opus", acp_opts = { mode = "plan", thought_level = "low", }, }, }, ``` ::: ::: tip To see what your agent supports, open a chat with that adapter open the debug window with `gd` ::: * `alias` *(string)* - Allows the prompt to be triggered via `:CodeCompanion /{alias}` * `auto_submit` *(boolean)* - Automatically submit the prompt to the LLM * `enabled` *(boolean)* - Enable/disable the prompt without removing it from the library * `ignore_system_prompt` *(boolean)* - Don't send the default system prompt with the request * `intro_message` *(string)* - Custom intro message for the chat buffer UI * `is_slash_cmd` *(boolean)* - Make the prompt available as a slash command in chat * `is_workflow` *(boolean)* - Treat successive prompts as a workflow * `modes` *(array)* - Only show in specific modes (`{ "v" }` for visual mode) * `placement` *(string)* - For inline interaction: `new`, `replace`, `add`, `before`, `chat` * `pre_hook` *(function)* - Function to run before the prompt is executed (Lua only) * `stop_context_insertion` *(boolean)* - Prevent automatic context insertion * `user_prompt` *(string)* - Get user input before actioning the response ### With Placeholders Placeholders allow you to inject dynamic content into your prompts. In markdown prompts, use `${placeholder.name}` syntax: #### Context Placeholders The `context` object contains information about the current buffer: ::: code-group ```markdown [Markdown] --- name: Buffer Info interaction: chat description: Show buffer information --- ## user I'm working in buffer ${context.bufnr} which is a ${context.filetype} file. ``` ```lua [Lua] ["Buffer Info"] = { interaction = "chat", description = "Show buffer information", prompts = { { role = "user", content = function(context) return "I'm working in buffer " .. context.bufnr .. " which is a " .. context.filetype .. " file." end, }, }, } ``` ::: **Available context fields:** ```lua { bufnr = 7, buftype = "", code = [[local function hello(text) return "hello " .. text end]], cursor_pos = { 10, 3 }, end_col = 3, end_line = 10, filetype = "lua", is_normal = false, is_visual = true, lines = { "local function hello(text)", ' return "hello " .. text', "end" }, mode = "V", start_col = 1, start_line = 8, winnr = 1000 } ``` #### External Lua Files For markdown prompts, you can reference functions and values from external Lua files placed in the same directory as your prompt. This is useful for complex logic or reusable components: **Example directory structure:** ``` .prompts/ ├── commit.md ├── commit.lua └── utils.lua ``` **commit.lua:** ```lua return { diff = function(args) return vim.system({ "git", "diff", "--no-ext-diff", "--staged" }, { text = true }):wait().stdout end, } ``` **commit.md:** ````markdown --- name: Commit message interaction: chat description: Generate a commit message opts: alias: commit --- ## user You are an expert at following the Conventional Commit specification. Given the git diff listed below, please generate a commit message for me: ```diff ${commit.diff} ``` ```` In this example, `${commit.diff}` references the `diff` function from `commit.lua`. The plugin automatically: 1. Detects the dot notation (`commit.`) 2. Loads `commit.lua` from the same directory 3. Calls the `diff` function 4. Replaces `${commit.diff}` with the result **Multiple files example:** ````markdown --- name: Code Review interaction: chat description: Review code changes --- ## user Please review this code: ```${context.filetype} ${context.code} ``` Here's the git diff: ```diff ${utils.git_diff} ``` ```` This prompt can reference functions from both `shared.lua` and `utils.lua` in the same directory. **Function signature:** External Lua functions receive an `args` table: ```lua return { my_function = function(args) -- args.context - Buffer context -- args.item - The full prompt item return "some value" end, static_value = "I'm just a string", } ``` #### Built-in Helpers You can also reference built-in values using dot notation: * `${context.bufnr}` - Current buffer number * `${context.filetype}` - Current filetype * `${context.start_line}` - Visual selection start * `${context.end_line}` - Visual selection end And many more from the context object. ### Advanced Configuration #### Conditionals You can conditionally control when prompts appear in the Action Palette or conditionally include specific prompt messages using `condition` functions: **Lua only:** ::: code-group ```lua [Item-level] ["Visual Only"] = { interaction = "chat", description = "Only appears in visual mode", condition = function(context) return context.is_visual end, prompts = { { role = "user", content = "This prompt only appears when you're in visual mode.", }, }, }, ``` ```lua [Prompt-level] ["Visual Only"] = { interaction = "chat", description = "Only appears in visual mode", prompts = { { role = "user", content = "This prompt only appears when you're in visual mode.", condition = function(context) return context.is_visual end, }, }, } ``` ::: #### Context Pre-load a chat buffer with context from files, symbols, or URLs: ::: code-group ```markdown [Markdown] --- name: Test Context interaction: chat description: Add some context context: - type: file path: - lua/codecompanion/health.lua - lua/codecompanion/http.lua - type: symbols path: lua/codecompanion/interactions/chat/init.lua - type: url url: https://raw.githubusercontent.com/olimorris/codecompanion.nvim/refs/heads/main/lua/codecompanion/commands.lua --- ## user I'll think of something clever to put here... ``` ```lua [Lua] ["Test Context"] = { interaction = "chat", description = "Add some context", context = { { type = "file", path = { "lua/codecompanion/health.lua", "lua/codecompanion/http.lua", }, }, { type = "symbols", path = "lua/codecompanion/interactions/chat/init.lua", }, { type = "url", url = "https://raw.githubusercontent.com/olimorris/codecompanion.nvim/refs/heads/main/lua/codecompanion/commands.lua", }, }, prompts = { { role = "user", content = "I'll think of something clever to put here...", opts = { contains_code = true, }, }, }, }, ``` ::: Context items appear at the top of the chat buffer. URLs are automatically cached for you. #### MCP Servers You can also specify [MCP servers](/configuration/mcp) to be loaded with your prompt: ::: code-group ```markdown [Markdown] --- name: Prompt with MCP servers interaction: chat description: A prompt that starts MCP servers mcp_servers: - tavily-mcp - filesystem --- ``` ```lua [Lua] ["Prompt with MCP servers"] = { interaction = "chat", description = "A prompt that starts MCP servers", mcp_servers = { "tavily-mcp", "filesystem", }, }, ``` ::: ::: tip Disabling all MCP servers Setting `mcp_servers` to `none` will prevent any MCP servers from being loaded in the chat, including those with `add_to_chat = true`: ::: code-group ```markdown [Markdown] --- name: No MCP prompt interaction: chat description: A prompt with no MCP servers mcp_servers: none --- ``` ```lua [Lua] ["No MCP prompt"] = { interaction = "chat", description = "A prompt with no MCP servers", mcp_servers = "none", }, ``` ::: ::: #### Pickers Pickers allow you to create dynamic prompt menus based on runtime data. **Lua only:** ```lua ["My picker menu ..."] = { name = "A list of items", interaction = " ", description = "My current items", picker = { prompt = "Select an item", columns = { "name", "description" }, items = { { name = "Item 1", description = "This is item 1", callback = function() print("You selected item 1") end, }, { name = "Item 2", description = "This is item 2", callback = function() print("You selected item 2") end, }, }, }, }, ``` #### Pre-hooks Pre-hooks allow you to run custom logic before a prompt is executed. This is particularly useful for creating new buffers or setting up the environment: **Lua only:** ```lua ["Boilerplate HTML"] = { interaction = "inline", description = "Generate some boilerplate HTML", opts = { ---@return number pre_hook = function() local bufnr = vim.api.nvim_create_buf(true, false) vim.api.nvim_set_current_buf(bufnr) vim.api.nvim_set_option_value("filetype", "html", { buf = bufnr }) return bufnr end, }, prompts = { { role = "system", content = "You are an expert HTML programmer", }, { role = "user", content = "Please generate some HTML boilerplate for me. Return the code only and no markdown codeblocks", }, }, } ``` For the inline interaction, the plugin will detect a number being returned from the `pre_hook` and assume that is the buffer number you wish any code to be streamed into. #### Rules You can also specify rules to be loaded with your prompt: ::: code-group ```markdown [Markdown] --- name: Prompt with rules interaction: chat description: A prompt that loads rules rules: - default - my_other_rule --- ``` ```lua [Lua] ["Prompt with rules"] = { interaction = "chat", description = "A prompt that loads rules", rules = { "default", "my_other_rules", }, }, ``` ::: > \[!INFO] > A prompt that names no rules loads none by default. Enable `rules.opts.chat.autoload_groups_in_prompt_library` to have your prompts autoload rule groups that you've specified in `rules.opts.chat.autoload` #### Tools You can also specify tools to be loaded with your prompt. These can be individual tools as well as tool groups: ::: code-group ```markdown [Markdown] --- name: Prompt with tools interaction: chat description: A prompt that loads tools tools: - run_command - insert_edit_into_file --- ``` ```lua [Lua] ["Prompt with tools"] = { interaction = "chat", description = "A prompt that loads tools", tools = { "run_command", "insert_edit_into_file", }, }, ``` ::: ::: tip Disabling all tools Setting `tools` to `none` will prevent any tools from being loaded in the chat, including any [default tools](/configuration/chat-buffer#default-tools): ::: code-group ```markdown [Markdown] --- name: No tools prompt interaction: chat description: A prompt with no tools tools: none --- ``` ```lua [Lua] ["No tools prompt"] = { interaction = "chat", description = "A prompt with no tools", tools = "none", }, ``` ::: ::: #### Workflows Workflows allow you to chain multiple prompts together in a sequence. That is, the first prompt is sent to the LLM, the LLM responds, then the next prompt in the workflow is sent, etc. This can be useful for implementing multi-step processes such as chain-of-thought reasoning or iterative code refinement. **Note:** Markdown prompts do not support [agentic workflows](/extending/agentic-workflows). ::: code-group ```markdown [Markdown] --- name: Oli's test workflow interaction: chat description: Use a workflow to test the plugin opts: adapter: name: copilot model: gpt-4.1 ignore_system_prompt: true is_workflow: true --- ## user Generate a Python class for managing a book library with methods for adding, removing, and searching books ## user Write unit tests for the library class you just created ## user Create a TypeScript interface for a complex e-commerce shopping cart system ## user Write a recursive algorithm to balance a binary search tree in Java ``` ```lua [Lua] ["Oli's test workflow"] = { interaction = "chat", description = "Use a workflow to test the plugin", opts = { adapter = { name = "copilot", model = "gpt-4.1", }, ignore_system_prompt = true, is_workflow = true, }, prompts = { { { role = "user", content = "Generate a Python class for managing a book library with methods for adding, removing, and searching books", }, }, { { role = "user", content = "Write unit tests for the library class you just created", }, }, { { role = "user", content = "Create a TypeScript interface for a complex e-commerce shopping cart system", }, }, { { role = "user", content = "Write a recursive algorithm to balance a binary search tree in Java", }, }, }, }, ``` ::: You can also modify the options for the entire workflow at an individual prompt level. This can be useful if you wish to automatically submit certain prompts or change the adapter/model mid-workflow. Simply use a yaml code block with `opts` as a meta field: ::: code-group ````markdown [Markdown] ## user Generate a Python class for managing a book library with methods for adding, removing, and searching books ## user ```yaml opts auto_submit: true ``` Write unit tests for the library class you just created ## user ```yaml opts adapter: name: copilot model: claude-haiku-4.5 auto_submit: false ``` Create a TypeScript interface for a complex e-commerce shopping cart system ```` ```lua [Lua] prompts = { { { role = "user", content = "Generate a Python class for managing a book library with methods for adding, removing, and searching books", }, }, { { role = "user", content = "Write unit tests for the library class you just created", opts = { auto_submit = true, }, }, }, { { role = "user", content = "Create a TypeScript interface for a complex e-commerce shopping cart system", opts = { adapter = { name = "copilot", model = "claude-haiku-4.5", }, auto_submit = false, }, }, }, }, ``` ::: ## Others ### Hiding Built-in Prompts You can hide the built-in prompts from the Action Palette by setting the following configuration option: ```lua require("codecompanion").setup({ display = { action_palette = { opts = { show_preset_prompts = false, } }, }, }) ``` --- --- url: /configuration/rules.md description: >- Configure rules files in CodeCompanion — including CLAUDE.md, AGENTS.md, and Cursor rules — to provide persistent LLM instructions and project context in Neovim. --- # Configuring Rules Within CodeCompanion, rules fulfil two main purposes within a chat buffer: 1. To provide system-level instructions to your LLM 2. To provide persistent context via files in your project Similar to Cursor's [Rules](https://cursor.com/docs/context/rules), they provide a way to guide the behavior of your LLM within a chat. Why? LLMs don't retain memory between sessions so preferences and context need to be re-applied each time a new chat is started. ## Enabling Rules ::: code-group ```lua [Enable] require("codecompanion").setup({ rules = { default = { description = "Collection of common files for all projects", files = { ".clinerules", ".cursorrules", ".goosehints", ".rules", ".windsurfrules", ".github/copilot-instructions.md", "AGENT.md", "AGENTS.md", { path = "CLAUDE.md", parser = "claude" }, { path = "CLAUDE.local.md", parser = "claude" }, { path = "~/.claude/CLAUDE.md", parser = "claude" }, }, is_preset = true, }, opts = { chat = { autoload = "default", -- The rule groups to load enabled = true, }, }, }, }) ``` ```lua [With Conditions] require("codecompanion").setup({ rules = { default = { description = "Collection of common files for all projects", files = { -- Omitted for brevity }, }, opts = { chat = { ---@param chat CodeCompanion.Chat ---@return boolean condition = function(chat) -- In this example, only enable rules for chats -- that are using http adapters return chat.adapter.type == "http" end, }, }, }, }) ``` ::: Once enabled, the plugin will look to load a common, or default, set of rules every time a chat buffer is created. > \[!INFO] > Refer to the [config.lua](https://github.com/olimorris/codecompanion.nvim/blob/5807e0457111f0de267fc9a6543b41fae0f5c2b1/lua/codecompanion/config.lua#L1167-L1179) file for the full set of files included in the default group. ## Rule Groups In the plugin, rule groups are a collection of files and/or directories that can be loaded into the chat buffer. Groups give you flexibility to create different sets of rules for different use-cases. For example, you may want a set of rules specifically for working with Claude Code or another for working with a specific project. ::: code-group ```lua [Basic Group] require("codecompanion").setup({ rules = { my_project_rules = { -- [!code focus:9] description = "Rule files for My Project", files = { -- Literal file paths (absolute or relative to cwd) "~/.claude/CLAUDE.md", "CLAUDE.md", "CLAUDE.local.md", }, }, }, }) ``` ```lua [Conditionals] require("codecompanion").setup({ rules = { my_project_rules = { -- [!code focus:13] description = "Rule files for My Project", ---@return boolean enabled = function() -- Don't show this group unless in a specific dir return vim.fn.getcwd():find("my_project", 1, true) ~= nil end, files = { "~/.claude/CLAUDE.md", "CLAUDE.md", "CLAUDE.local.md", }, }, }, }) ``` ```lua [Directories] require("codecompanion").setup({ rules = { my_project_rules = { -- [!code focus:19] description = "Rule files for My Project", files = { -- Specify dirs to search in (supports glob patterns and literals) { path = vim.fn.getcwd(), files = { ".clinerules", ".cursorrules", "*.md" } }, { path = "~/.config/rules", files = "*.md" }, -- Mix with literal file paths "~/.claude/CLAUDE.md", "CLAUDE.md", "CLAUDE.local.md", }, }, }, }) ``` ```lua [File Patterns] require("codecompanion").setup({ rules = { my_project_rules = { -- [!code focus:21] description = "Rule files for My Project", files = { -- 1. Literal file paths "CLAUDE.md", "~/.claude/CLAUDE.md", -- 2. File path with parser { path = "CLAUDE.local.md", parser = "claude" }, -- 3. Directory with file patterns { path = ".", files = { ".clinerules", "*.md" } }, -- 4. Directory with parser { path = "~/.config/rules", files = "*.md", parser = "claude" }, -- 5. Glob patterns (searches filesystem) "docs/**/*.md", ".github/*.md", }, }, }, }) ``` ```lua [Nested Groups] require("codecompanion").setup({ rules = { my_project_rules = { -- [!code focus:12] description = "Rule files for My Project", parser = "claude", files = { ["mcp"] = { description = "The MCP implementation in My project", files = { ".rules/mcp/mcp.md", }, }, }, }, }, }) ``` ::: Nested groups allow you to apply the same conditional to multiple groups alongside keeping your config clean. Infact, the plugin uses this itself. There is a `CodeCompanion` group with sub-groups for different parts of the plugin, allowing contributors to easily share context with an LLM when they're working on specific parts of the codebase. When using the *Action Palette* or the slash command, the plugin will extract these nested groups and display them in the `Chat with rules ...` menu. You can also set default groups that are automatically applied to all chat buffers. This is useful for ensuring that your preferred rules are always available. ### Autoload You can set specific rule groups that will be automatically added to chat buffers. This is useful for ensuring that your preferred rules are always available. ::: code-group ```lua{5} [Single Group] require("codecompanion").setup({ rules = { opts = { chat = { autoload = "my_project_rules", }, }, }, }) ``` ```lua{5} [Multiple Groups] require("codecompanion").setup({ rules = { opts = { chat = { autoload = { "my_project_rules", "another_project" }, }, }, }, }) ``` ```lua{6-11} [Conditional Groups] require("codecompanion").setup({ rules = { opts = { chat = { ---@return string|string[] autoload = function() if vim.fn.getcwd():find("another_project", 1, true) ~= nil then return { "my_project", "another_project" } end return "my_project" end, }, }, }, }) ``` ::: #### Rules in Prompt Library Prompts By default, prompt library prompts will never autoload rule groups. A prompt only gets rules if it names them itself, via its own rules field. To have prompts leverage the autoload groups when they don't name any rules: ```lua{6} [Autoload for prompt library prompts] require("codecompanion").setup({ rules = { opts = { chat = { autoload = "default", autoload_groups_in_prompt_library = true, }, }, }, }) ``` With this enabled, a prompt that names no rules will have the autoload groups (`rules.opts.chat.autoload`) loaded in the chat buffer. However, a prompt that names its own rules will use those instead. ## Parsers Parsers allow CodeCompanion to transform rules, affecting how they are shared in the chat buffer. This is particularly useful if you reference files in your rules. Currently, the plugin has two in-built parsers: * `claude` - which will import files into the chat buffer in the same way Claude Code [does](https://code.claude.com/docs/en/memory#claude-md-imports). Note, this requires rules to be `markdown` files * `CodeCompanion` - parses rules in the same ways as `claude` but allows for a system prompts to be extracted via a H2 "System Prompt" header * `none` - a blank parser which can be used to overwrite parsers that have been set on the default rules groups Please see the guide on [Creating Rules Parsers](/extending/parsers) to understand how you can create and apply your own. ### Applying Parsers You can apply parsers at a group level, to ensure that all files in the group are parsed in the same way. Alternatively, you can apply them at a file level to have more granular control. ::: code-group ```lua{5} [Group Level] require("codecompanion").setup({ rules = { claude = { description = "Rules for Claude Code users", parser = "claude", files = { "CLAUDE.md", "CLAUDE.local.md", "~/.claude/CLAUDE.md", }, }, }, }) ``` ```lua{6-8} [File Level] require("codecompanion").setup({ rules = { claude = { description = "Rules for Claude Code users", files = { { path = "CLAUDE.md", parser = "claude" }, { path = "CLAUDE.local.md", parser = "claude" }, { path = "~/.claude/CLAUDE.md", parser = "claude" }, }, }, }, }) ``` ```lua{5} [Disable] require("codecompanion").setup({ rules = { claude = { description = "Rules for Claude Code users", parser = "none", -- Disable parsing for the entire group files = { "CLAUDE.md", "CLAUDE.local.md", "~/.claude/CLAUDE.md", }, }, }, }) ``` ::: --- --- url: /configuration/system-prompt.md description: >- Customize CodeCompanion's system prompt for chat and inline interactions — replace the default, add dynamic context, or tune language and tone for your LLM. --- # Configuring System Prompts ## Chat System Prompt The default system prompt has been carefully curated to deliver terse and professional responses that relate to development and Neovim. It is sent with every request in the chat buffer. The plugin comes with the following system prompt: `````txt You are an AI programming assistant named "CodeCompanion", working within the Neovim text editor. You can answer general programming questions and perform the following tasks: * Answer general programming questions. * Explain how the code in a Neovim buffer works. * Review the selected code from a Neovim buffer. * Generate unit tests for the selected code. * Propose fixes for problems in the selected code. * Scaffold code for a new workspace. * Find relevant code to the user's query. * Propose fixes for test failures. * Answer questions about Neovim. Follow the user's requirements carefully and to the letter. Use the context and attachments the user provides. Keep your answers short and impersonal, especially if the user's context is outside your core tasks. Use Markdown formatting in your answers. Do not use H1 or H2 markdown headers. When suggesting code changes or new content, use Markdown code blocks. To start a code block, use 4 backticks. After the backticks, add the programming language name as the language ID. To close a code block, use 4 backticks on a new line. If the code modifies an existing file or should be placed at a specific location, add a line comment with 'filepath:' and the file path. If you want the user to decide where to place the code, do not add the file path comment. In the code block, use a line comment with '...existing code...' to indicate code that is already present in the file. Code block example: ````languageId // filepath: /path/to/file // ...existing code... { changed code } // ...existing code... { changed code } // ...existing code... ```` Ensure line comments use the correct syntax for the programming language (e.g. "#" for Python, "--" for Lua). For code blocks use four backticks to start and end. Avoid wrapping the whole response in triple backticks. Do not include diff formatting unless explicitly asked. Do not include line numbers in code blocks. When given a task: 1. Think step-by-step and, unless the user requests otherwise or the task is very simple, describe your plan in pseudocode. 2. When outputting code blocks, ensure only relevant code is included, avoiding any repeating or unrelated code. 3. End your response with a short suggestion for the next user turn that directly supports continuing the conversation. Additional context: All non-code text responses must be written in the ${language} language. The current date is ${date}. The user's Neovim version is ${version}. The user is working on a ${os} machine. Please respond with system specific commands if applicable. ````` The format of the date can be changed in your config by altering the `date_format` option: ```lua require("codecompanion").setup({ interactions = { opts = { date_format = "%A, %d %B %Y", -- Example: "Monday, 01 January 2024" }, }, }) ``` ## Tool System Prompt CodeCompanion also ships with a separate system prompt when [tools](/usage/chat-buffer/agents-tools) are used in the chat buffer: `````txt You are a highly sophisticated automated coding agent with expert-level knowledge across many different programming languages and frameworks. The user will ask a question, or ask you to perform a task, and it may require lots of research to answer correctly. There is a selection of tools that let you perform actions or retrieve helpful context to answer the user's question. You will be given some context and attachments along with the user prompt. You can use them if they are relevant to the task, and ignore them if not. If you can infer the project type (languages, frameworks, and libraries) from the user's query or the context that you have, make sure to keep them in mind when making changes. If the user wants you to implement a feature and they have not specified the files to edit, first break down the user's request into smaller concepts and think about the kinds of files you need to grasp each concept. If you aren't sure which tool is relevant, you can call multiple tools. You can call tools repeatedly to take actions or gather as much context as needed until you have completed the task fully. Don't give up unless you are sure the request cannot be fulfilled with the tools you have. It's YOUR RESPONSIBILITY to make sure that you have done all you can to collect necessary context. Don't make assumptions about the situation - gather context first, then perform the task or answer the question. Think creatively and explore the workspace in order to make a complete fix. Don't repeat yourself after a tool call, pick up where you left off. NEVER print out a codeblock with a terminal command to run unless the user asked for it. You don't need to read a file if it's already provided in context. When using a tool, follow the json schema very carefully and make sure to include ALL required properties. Always output valid JSON when using a tool. If a tool exists to do a task, use the tool instead of asking the user to manually take an action. If you say that you will take an action, then go ahead and use the tool to do it. No need to ask permission. Never use a tool that does not exist. Use tools using the proper procedure, DO NOT write out a json codeblock with the tool inputs. Never say the name of a tool to a user. For example, instead of saying that you'll use the insert_edit_into_file tool, say "I'll edit the file". If you think running multiple tools can answer the user's question, prefer calling them in parallel whenever possible. When invoking a tool that takes a file path, always use the file path you have been given by the user or by the output of a tool. Use proper Markdown formatting in your answers. When referring to a filename or symbol in the user's workspace, wrap it in backticks. Any code block examples must be wrapped in four backticks with the programming language. ````languageId // Your code here ```` The languageId must be the correct identifier for the programming language, e.g. python, javascript, lua, etc. If you are providing code changes, use the insert_edit_into_file tool (if available to you) to make the changes directly instead of printing out a code block with the changes. ````` ## Changing System Prompts ### Chat The chat system prompt can be changed with: ```lua require("codecompanion").setup({ interactions = { chat = { opts = { system_prompt = "My new system prompt", }, }, }, }) ``` Alternatively, the system prompt can be a function. The `opts` parameter contains several pieces of information related to the chat, which you can use to build the system prompt: ```lua ---@class CodeCompanion.SystemPrompt.Context ---@field language string ---@field adapter CodeCompanion.HTTPAdapter|CodeCompanion.ACPAdapter ---@field date string ---@field nvim_version string ---@field os string the operating system that the user is using ---@field default_system_prompt string ---@field cwd string current working directory ---The closest parent directory that contains one of the following VCS markers: --- - `.git` --- - `.svn` --- - `.hg` ---@field project_root? string the closest parent directory that contains a `.git` subdirectory. require("codecompanion").setup({ interactions = { chat = { opts = { ---@param ctx CodeCompanion.SystemPrompt.Context ---@return string system_prompt = function(ctx) return ctx.default_system_prompt .. fmt( [[Additional context: All non-code text responses must be written in the %s language. The current date is %s. The user's Neovim version is %s. The user is working on a %s machine. Please respond with system specific commands if applicable. ]], ctx.language, ctx.date, ctx.nvim_version, ctx.os ) end, }, }, }, }) ``` ### Tools There are additional options available when working with tool system prompts: ```lua require("codecompanion").setup({ interactions = { chat = { tools = { opts = { system_prompt = { enabled = true, -- Enable the tools system prompt? replace_main_system_prompt = false, -- Replace the main system prompt with the tools system prompt? ---The tool system prompt ---@param args { ctx: CodeCompanion.SystemPrompt.Context, tools: string[]} The tools available ---@return string prompt = function(args) return "My custom tools prompt" end, }, }, }, }, }, }) ``` ## When System Prompts Change There are various scenarios for when the system prompt may change in the chat buffer: * When a user changes adapter * When a user changes the model on an adapter * When a rule is added * When a tool (with a defined system prompt) is added to the chat buffer CodeCompanion will always resolve a system prompt change asynchronously, as many adapters make a HTTP request to a server in order to obtain the available models. --- --- url: /configuration/others.md description: >- Configure miscellaneous CodeCompanion options: response language, log level, per-project config files, and restricting code from being sent to LLMs. --- # Other Configuration Options ## Language If you use the default system prompt, you can specify which language an LLM should respond in by changing the `opts.language` option: ```lua require("codecompanion").setup({ opts = { language = "English", }, }), ``` Of course, if you have your own system prompt you can specify your own language for the LLM to respond in. ## Log Level > \[!IMPORTANT] > By default, logs are stored at `~/.local/state/nvim/codecompanion.log` When it comes to debugging, you can change the level of logging which takes place in the plugin as follows: ```lua require("codecompanion").setup({ opts = { log_level = "ERROR", -- TRACE|DEBUG|ERROR|INFO }, }), ``` ## Per-Project Configuration Working across multiple projects, it can be useful to set different CodeCompanion configurations. The plugin allows you to specify a list of files which it will look for in the current working directory. If any of the files are found, they will be loaded and merged with the default configuration. Alternatively, you can specify a directory as a key and the configuration as the value. ::: code-group ```lua [Files] require("codecompanion").setup({ opts = { per_project_config = { files = { ".codecompanion", ".codecompanion.lua", }, }, }, }) ``` ```lua [Dirs] require("codecompanion").setup({ opts = { per_project_config = { paths = { ["~/Code/Python/New-Startup"] = { interactions = { chat = { adapter = { name = "copilot", model = "claude-opus-4.6", }, }, }, }, }, }, }, }) ``` ::: File-based configuration must return a valid Lua table. For example: ```lua return { interactions = { chat = { adapter = { name = "copilot", model = "claude-sonnet-4.6", }, tools = { opts = { default_tools = { "memory", }, }, }, }, }, } ``` ## Sending Code > \[!IMPORTANT] > Whilst the plugin makes every attempt to prevent code from being sent to the LLM, use this option at your own risk You can prevent any code from being sent to the LLM with: ```lua require("codecompanion").setup({ opts = { send_code = false, }, }), ``` --- --- url: /usage/introduction.md description: >- Tips and tricks for getting the most out of CodeCompanion in Neovim — keyboard shortcuts, context management, output handling, and productivity patterns. --- # Using CodeCompanion CodeCompanion continues to evolve with regular frequency. This page will endeavour to serve as focal point for providing useful productivity tips for the plugin. ## Apply an LLM's edits to a buffer/file The [@insert\_edit\_into\_file](/usage/chat-buffer/agents-tools#files) tool, combined with the [#buffer](/usage/chat-buffer/editor-context#buffer) editor context or [/buffer](/usage/chat-buffer/slash-commands#buffer) slash command, enables an LLM to modify code in a Neovim buffer. This is especially useful if you do not wish to manually apply an LLM's suggestions yourself. Simply tag it in the chat buffer with `@files` or `@insert_edit_into_file`. ## Code review an LLM/agent's changes You can [review an LLM or agent's changes](/usage/code-review) like a pull request. `:CodeCompanionCodeReview` opens every change in the quickfix list, one entry per hunk. You can step through them, leave in place comments with `:CodeCompanionCodeReview Comment` and then share the review in a chat buffer with the [#{code\_review}](/usage/chat-buffer/editor-context#code-review) context. To navigate to the files an agent has edited or created, use `:CodeCompanionChat Changes` to open them in the quickfix list. Every file the LLM touches in your Neovim session is tracked, across chats and the CLI. ## Copying code from a chat buffer The fastest way to copy an LLM's code output is with `gy`. This will yank the nearest codeblock. ## Navigating between responses in the chat buffer You can quickly move between responses in the chat buffer using `[[` or `]]`. ## Quickly accessing a chat buffer The `:CodeCompanionChat Toggle` command will automatically create a chat buffer if one doesn't exist, open the last chat buffer or hide the current chat buffer. When in a chat buffer, you can cycle between other chat buffers with `{` or `}`. By default, opening or cycling to a chat hides whichever chat is currently visible. If you'd rather keep chats per tab — so a chat opened in tab A is never closed or stolen by activity in tab B — set `display.chat.window.pertab = true` in your config. With that enabled, `{` / `}` only cycles through chats that are visible in the current tab or not currently visible anywhere, and `:CodeCompanionChat Toggle` jumps to the existing tab when the chat lives there. ## Run tests from the chat buffer The [run\_command](/usage/chat-buffer/agents-tools#run-command) tool enables an LLM to execute commands on your machine. This can be useful if you wish the LLM to run a test suite on your behalf and give insight on failing cases. Simply tag the `@run_command` in the chat buffer and ask it run your tests. --- --- url: /usage/action-palette.md description: >- Use CodeCompanion's Action Palette to launch chat buffers, switch between open chats, access the prompt library, and discover plugin features from a single menu. --- # Using the Action Palette The *Action Palette* has been designed to be your entry point for the many configuration options that CodeCompanion offers. It can be opened with `:CodeCompanionActions`. Once opened, the user can see plugin defined actions such as `Chat` and `Open Chats`. The latter, enabling the user to move between any open chat buffers. These can be turned off in the config by setting `display.action_palette.opts.show_preset_actions = false`. ## Built-in Prompts The plugin also defines a number of prompts in the form of the prompt library: * `Commit message` - Generate a commit message * `Explain code` - Explain how code in a buffer works * `Explain LSP diagnostics` - Explain the LSP diagnostics for the selected code * `Fix code` - Fix the selected code * `Unit tests` - Generate unit tests for selected code > \[!INFO] > These can also be called via the cmd line with their `alias`, for example `:CodeCompanion /explain` The plugin also contains two built-in workflows, `Code workflow` and `Edit test repeat workflow`. See the [workflows section](/usage/workflows) for more information. The built-in prompts can be turned off by setting `display.action_palette.opts.show_preset_prompts = false`. You can also refresh the markdown prompts in your prompt library with `:CodeCompanionActions Refresh` --- --- url: /usage/chat-buffer.md description: >- Everything about CodeCompanion's chat buffer — opening, toggling, keymaps, multi-turn conversations with LLMs, and adding images in Neovim. --- # Using the Chat Buffer > \[!NOTE] > The chat buffer has a filetype of `codecompanion` and a buftype of `nofile`. You can open a chat buffer with the `:CodeCompanionChat` command or with `require("codecompanion").chat()` and you can toggle the visibility of the chat buffer with `:CodeCompanionChat Toggle` or `require("codecompanion").toggle()`. You can even customize the chat buffer's window options: ```lua require("codecompanion").chat({ window_opts = { layout = "float", width = 0.6 }}) -- or: require("codecompanion").toggle({ window_opts = { layout = "float", width = 0.6 }}) ``` The chat buffer uses markdown as its syntax and `H2` headers separate the user and LLM's responses. The plugin is turn-based, meaning that the user sends a response which is then followed by the LLM's. The user's responses are parsed by treesitter and sent via an adapter to an LLM for a response which is then streamed back into the buffer. A response is sent to the LLM by pressing `` or `` in normal mode or `` in insert mode. This can of course be changed as per the [keymaps](#keymaps) section. New in `v19.12.0`, you can send a message to the LLM whilst it's executing tool calls with the `btw` keymap which is triggered with `gm`. When safe to do so, CodeCompanion will send the message to the LLM. ## Action Palette The chat buffer has its own *Action Palette* which can be accessed with `:CodeCompanionActions` when in the chat buffer. This displays available keymaps and slash commands and can be used to trigger them. ## Changing Adapter and Model One of the joys of working with CodeCompanion is being able to switch between conversing with an LLM and an agent, all from within the chat buffer. To do this, simply press `ga` to open up the *Select Adapter* select window. If your chosen adapter has more than one model then you'll be prompted to make another selection. This works for both *HTTP* and *ACP* adapters. ## Changing ACP Command ACP adapters are initiated via a command in the configuration. By default, this will be the `default` command. Some ACP adapters have additional commands and these can be triggered via the cmd line with something like `:CodeCompanionChat adapter=gemini_cli command=yolo`, or you can use the [/command](/usage/chat-buffer/slash-commands#command) slash command within the chat buffer. ## Completion > \[!IMPORTANT] > As of `v17.5.0`, variables and tools are wrapped in curly braces automatically, such as `#{buffer}` or `@{files}` You can invoke the completion plugins by typing `#` or `@` followed by the variable or tool name, which will trigger the completion menu. If you don't use a completion plugin, you can use native completions with no setup, invoking them with `` from within the chat buffer. When using an ACP adapter (such as claude-code), you can also type `\` (backslash, by default) to get completions for ACP commands. These are agent-specific commands like `/compact` (compact chat history) that are dynamically discovered from the agent itself. > \[!NOTE] > It typically takes 1-5 seconds after opening a chat buffer for ACP commands to become available. The agent needs to initialize and scan for both built-in and custom commands. If you define a new custom command mid-session, the same delay applies before it appears in the completion list. The backslash trigger is used to avoid conflicts with CodeCompanion's built-in [Slash Commands](/usage/chat-buffer/slash-commands). When you send a message, `\command` is automatically transformed to `/command` for the agent. The trigger character can be customized via `interactions.chat.slash_commands.opts.acp.trigger` in your config. It's worth noting that not all commands available in ACP CLI tools are exposed via the SDK. Only a subset of built-in commands are supported, though this is constantly evolving as the underlying SDKs mature. ## Context Sharing context with an LLM is crucial in order to generate useful responses. In the plugin, context is defined as output that is shared with a chat buffer via a *Variable*, *Slash Command* or *Tool*. They appear in a blockquote entitled `Context`. In essence, this is context that you're sharing with an LLM. > \[!IMPORTANT] > Context items contain the data of an object at a point in time. By default, they **are not** self-updating In order to allow for context to self-update, buffers and files can be synced to a chat buffer. On every turn, you can determine what is sent to the LLM. For buffers, you can choose to send *all* of the content or just the *diff*. For files, you only have the choice of sending *all* of the content. The advantage of sending *all* of a file or buffer's content is that the LLM will always receive a fresh copy of the source data regardless of any changes. This can be useful if you're working with tools. However, please note that this can consume a lot of tokens. Syncing and sending only a *diff*, is a more token-conscious way of keeping the LLM up to date on the contents of a buffer. Buffer diffs track changes (adds, edits, deletes) in the underlying buffer and update the LLM on each turn, with only those changes. If a context item is added by mistake, it can be removed from the chat buffer by simply deleting it from the `Context` blockquote. On the next turn, all data related to that context item will be removed from the message history. Finally, it's important to note that all http adapter endpoints require the sending of previous messages that make up the conversation. So even though you've shared context once, many messages ago, the LLM will always be able to refer to it, unless you actively alter the history of the conversation via `gd`. ## Debug Window Sometimes it's necessary to peek under the hood of the chat buffer to understand what hyperparameters are being sent to the LLM, or what the message history looks like. By pressing `gd`, you can open up a debug window which contains all of the relevant information about the chat buffer, including the message history, adapter settings and context items. You can edit all content in the debug window and persist it to the chat buffer by doing ``. ## Generating Titles CodeCompanion can automatically generate titles for your chat buffers based on their content. This is accomplished via a background interaction. To enable this: ```lua{11,16} require("codecompanion").setup({ interactions = { background = { chat = { callbacks = { ["on_ready"] = { actions = { "interactions.background.builtin.chat_make_title", }, -- Enable "on_ready" callback which contains the title generation action enabled = true, }, }, opts = { -- Enable background interactions generally enabled = true, }, }, }, } }) ``` Finally, ensure that you have an adapter configured for any background interactions. ## Images / Vision Many LLMs have the ability to receive images as input (sometimes referred to as vision). CodeCompanion supports the adding of images into the chat buffer via the [/image](/usage/chat-buffer/slash-commands#image) slash command and through the system clipboard with [img-clip.nvim](/installation#img-clip-nvim). CodeCompanion can work with images in your file system and also with remote URLs, encoding both into a base64 representation. If your adapter and model doesn't support images, then CodeCompanion will endeavour to ensure that the image is not included in the messages payload that's sent to the LLM. ## Keymaps The plugin has a host of keymaps available in the chat buffer. The keymaps available to the user in normal mode are: * `options`: `?` to display all available keymaps * `send`: `|` to send a message to the LLM * `close`: `` to close the chat buffer * `stop`: `q` to stop the current request * `change_adapter`: `ga` to change the adapter for the current chat * `clear`: `gx` to clear the chat buffer’s contents * `copilot_stats`: `gS` to show copilot usage stats * `btw`: `gm` type a message to the LLM whilst it's streaming * `buffer_sync_all`: `gba` to sync the entire buffer on every turn * `buffer_sync_diff`: `gbd` to sync only a buffers diff on every turn * `codeblock`: `gc` to insert a codeblock in the chat buffer * `debug`: `gd` to view/debug the chat buffer’s contents * `fold_code`: `gf` to fold any codeblocks in the chat buffer * `goto_file_under_cursor`: `gR` to go to the file under cursor * `next_chat`: `}` to move to the next chat * `next_header`: `]]` to move to the next header * `previous_chat`: `{` to move to the previous chat * `previous_header`: `[[` to move to the previous header * `regenerate`: `gr` to regenerate the last response * `rules`: `gM` to clear all rules from the chat buffer * `system_prompt`: `gs` to toggle the system prompt on/off * `yank_code`: `gy` to yank the last codeblock in the chat buffer ## Messages > \[!TIP] > The message history and adapter settings can be modified via the debug window (`gd`) in the chat buffer It's important to note that some messages, such as system prompts or context provided via [Slash Commands](/usage/chat-buffer/slash-commands), will be hidden. This is to keep the chat buffer uncluttered from a UI perspective. Using the `gd` keymap opens up the debug window, which allows the user to see the full contents of the messages table which will be sent to the LLM on the next turn. The message history cannot be altered directly in the chat buffer. However, it can be modified in the debug window. This window is simply a Lua buffer which the user can edit as they wish. To persist any changes, the chat buffer keymaps for sending a message (defaults: `` or ``) can be used. ## Settings When conversing with an LLM, it can be useful to tweak model settings in between responses in order to generate the perfect output. If settings are enabled (`display.chat.show_settings = true`), then a yaml block will be present at the top of the chat buffer which can be modified in between responses. The yaml block is simply a representation of an adapter's schema table. --- --- url: /usage/chat-buffer/agents-tools.md description: >- Use CodeCompanion agent tools to let LLMs edit files, run commands, and search the web in Neovim. Covers tool groups, approval system, and model compatibility. --- # Using Agents and Tools > \[!IMPORTANT] > The built-in tools are for HTTP adapters only and not all LLMs support tool use. Please see the [compatibility](#compatibility) section for more information. As outlined by Andrew Ng in [Agentic Design Patterns Part 3, Tool Use](https://www.deeplearning.ai/the-batch/agentic-design-patterns-part-3-tool-use), LLMs can act as agents by leveraging external tools. Andrew notes some common examples such as web searching or code execution that have obvious benefits when using LLMs. In the plugin, tools are simply context and actions that are shared with an LLM. The LLM can act as an agent by executing tools via the chat buffer which in turn orchestrates their use within Neovim. Tools can be added as a participant to the chat buffer by using the `@` key, by default. > \[!IMPORTANT] > The use of some tools in the plugin results in you, the developer, acting as the human-in-the-loop and approving their use. ## How They Work Tools make use of an LLM's [function calling](https://platform.openai.com/docs/guides/function-calling) ability. All tools in CodeCompanion follow [OpenAI's function calling specification for defining functions](https://platform.openai.com/docs/guides/function-calling#defining-functions). When a tool is added to the chat buffer, the LLM is instructured by the plugin to return a structured JSON schema which has been defined for each tool. The chat buffer parses the LLMs response and detects the tool use before triggering the *tools/init.lua* file. The tool system triggers off a series of events, which sees tool's added to a queue and sequentially worked with their output being shared back to the LLM via the chat buffer. Depending on the tool, flags may be inserted on the chat buffer for later processing. An outline of the [tool system architecture](/extending/tools#architecture) is available in the extending section. ## Agents / Tool Groups Tool groups combine multiple tools together, making them available to the LLM in a single `@{group_name}` reference. CodeCompanion comes with two built-in groups: `@{agent}` and `@{files}`. When you include a tool group in the chat, all tools within that group become available to the LLM. By default, all the tools in the group will be shown as a single `name` reference in the chat buffer. If you want to show all tools as context items in the chat buffer, set the `opts.collapse_tools` option to `false` on the group itself. Groups may also have a `prompt` field which is used to replace their reference in a message in the chat buffer. This ensures that the LLM receives a useful message rather than the name of the tools themselves. ### Turning a group into an agent Groups become agents when they provide their own `system_prompt`. Combined with the `ignore_system_prompt` and `ignore_tool_system_prompt` opts, a group can completely replace the default system prompts with its own tailored instructions. This is how the built-in `@{agent}` group works. When `system_prompt` is a function, it receives the group config as the first argument and a [context object](/configuration/system-prompt) as the second, giving access to `language`, `date`, `nvim_version`, `os` and more: ```lua groups = { ["my_agent"] = { description = "My custom agent", system_prompt = function(group, ctx) return string.format( "You are a coding agent. The date is %s. The user is on %s.", ctx.date, ctx.os ) end, tools = { "read_file", "insert_edit_into_file", "run_command" }, opts = { collapse_tools = true, ignore_system_prompt = true, -- Remove the chat's default system prompt ignore_tool_system_prompt = true, -- Remove the default tool system prompt }, }, }, ``` ### agent The `@{agent}` group is CodeCompanion's agent mode. It combines a curated set of tools with its own system prompt, replacing the default chat and tool system prompts. This gives the LLM clear instructions on how to act as an autonomous coding agent. It contains the following tools: * [ask\_questions](/usage/chat-buffer/agents-tools#ask-questions) * [create\_file](/usage/chat-buffer/agents-tools#create-file) * [delete\_file](/usage/chat-buffer/agents-tools#delete-file) * [file\_search](/usage/chat-buffer/agents-tools#file-search) * [get\_changed\_files](/usage/chat-buffer/agents-tools#get-changed-files) * [get\_diagnostics](/usage/chat-buffer/agents-tools#get-diagnostics) * [grep\_search](/usage/chat-buffer/agents-tools#grep-search) * [insert\_edit\_into\_file](/usage/chat-buffer/agents-tools#insert-edit-into-file) * [read\_file](/usage/chat-buffer/agents-tools#read-file) * [run\_command](/usage/chat-buffer/agents-tools#run-command) You can use it with: ```md @{agent} Can we create a todo list app in Vue.js? ``` ### files The `@{files}` tool is a collection of tools that allows an LLM to carry out file operations in your current working directory. It contains the following files: * [create\_file](/usage/chat-buffer/agents-tools#create-file) * [file\_search](/usage/chat-buffer/agents-tools#file-search) * [get\_changed\_files](/usage/chat-buffer/agents-tools#get-changed-files) * [grep\_search](/usage/chat-buffer/agents-tools#grep-search) * [insert\_edit\_into\_file](/usage/chat-buffer/agents-tools#insert-edit-into-file) * [read\_file](/usage/chat-buffer/agents-tools#read-file) You can use it with: ```md @{files} Can you scaffold out the folder structure for a python package? ``` ## Built-in Tools CodeCompanion comes with a number of built-in tools which you can leverage, as long as your adapter and model are [supported](#compatibility). When calling a tool, CodeCompanion replaces the tool call in any prompt you send to the LLM with the value of a tool's `opts.tool_replacement_message` string. This is to ensure that you can call a tool efficiently whilst making the prompt readable to the LLM. So calling a tool with: ```md Use @{lorem_ipsum} to generate a random paragraph ``` will yield: ```md Use the lorem_ipsum tool to generate a random paragraph ``` ### ask\_questions > \[!NOTE] > By default, this tool is hidden and is only accessible via the `@{agent}` tool group This tool enables an LLM to ask clarifying questions before taking further action. This is useful when the LLM encounters ambiguous requirements, needs to choose between implementation approaches, or wants to validate assumptions. ```md @{agent} Can you refactor the authentication module? ``` ### create\_file > \[!NOTE] > By default, this tool shows a preview of the file's contents and requires user confirmation before it can be executed Create a file within the current working directory: ```md Can you create some test fixtures using @{create_file}? ``` **Options:** * `require_approval_before` (boolean) require approval before showing the file preview? (Default: false) * `require_confirmation_after` (boolean) show a preview of the file's contents and require confirmation before creating it? (Default: true) ### delete\_file > \[!NOTE] > By default, this tool requires user approval before it can be executed Delete a file within the current working directory: ```md Can you use @{delete_file} to delete the quotes.lua file? ``` **Options:** * `require_approval_before` require approval before deleting a file? (Default: true) ### fetch\_webpage This tools enables an LLM to fetch the content from a specific webpage. It will return the text in a text format, depending on which adapter you've configured for the tool. ```md Use @{fetch_webpage} to tell me what the latest version on neovim.io is ``` **Options:** * `adapter` The adapter used to fetch, process and format the webpage's content (Default: `jina`) ### file\_search This tool enables an LLM to search for files in the current working directory by glob pattern. It will return a list of matching file paths. ```md Use @{file_search} to list all the lua files in my project ``` **Options:** * `max_results` limits the amount of results that can be sent to the LLM in the response (Default: 500) ### get\_changed\_files This tool enables an LLM to get git diffs of any file changes in the current working directory. It will return a diff which can contain `staged`, `unstaged` and `merge-conflicts`. ```md Use @{get_changed_files} see what's changed ``` **Options:** * `max_lines` limits the amount of lines that can be sent to the LLM in the response (Default: 1000) ### get\_diagnostics > \[!WARNING] > This tool relies on external language servers. It may be unreliable for certain filetypes. This tool enables an LLM to retrieve LSP diagnostics for a given file. It returns all diagnostic messages (errors, warnings, hints and information) along with the relevant code lines. This is useful for understanding what issues exist in a file before attempting to fix them: ```md Use @{get_diagnostics} to check for any issues in the current file ``` The tool accepts an optional `severity` parameter to filter diagnostics by minimum severity level (`ERROR`, `WARNING`, `INFORMATION`, `HINT`). ### grep\_search > \[!IMPORTANT] > This tool requires [ripgrep](https://github.com/BurntSushi/ripgrep) to be installed This tool enables an LLM to search for text, within files, in the current working directory. For every match, the output (`{filename}:{line number} {relative filepath}`) will be shared with the LLM: ```md Use @{grep_search} to find all occurrences of `buf_add_message`? ``` **Options:** * `max_files` (number) limits the amount of files that can be sent to the LLM in the response (Default: 100) * `respect_gitignore` (boolean) (Default: true) ### insert\_edit\_into\_file > \[!NOTE] > By default, when editing files, this tool requires user approval before it can be executed This tool can edit buffers and files for code changes from an LLM: ```md Use @{insert_edit_into_file} to refactor the code in #buffer ``` ```md Can you apply the suggested changes to the buffer with @{insert_edit_into_file}? ``` **Options:** * `patching_algorithm` (string|table|function) The algorithm to use to determine how to edit files and buffers * `require_approval_before.buffer` (boolean) Require approval before editng a buffer? (Default: false) * `require_approval_before.file` (boolean) Require approval before editng a file? (Default: true) * `require_confirmation_after` (boolean) require confirmation after the execution and before moving on in the chat buffer? (Default: true) ### memory > \[!IMPORTANT] > For security, all memory operations are restricted to the `/memories` directory and any whitelisted paths The memory tool enables LLMs to store and retrieve information across conversations through a memory file directory (`/memories`). If you're using the *Anthropic* adapter, then this tool will act as its client implementation. Please refer to their [documentation](https://docs.claude.com/en/docs/agents-and-tools/tool-use/memory-tool) for more information. The tool has the following commands that an LLM can use: * **view** - Lists the contents in the `/memories` directory or displays file content with optional line ranges * **create** - Creates a new file or overwrites an existing file with specified content * **str\_replace** - Replaces the first exact match of text in a file with new text * **insert** - Inserts text at a specific line number in a file * **delete** - Removes a file or recursively deletes a directory and all its contents * **rename** - Moves or renames a file or directory to a new path To use the tool: ```md Use @{memory} to carry on our conversation about streamlining my dotfiles ``` #### Whitelisted Paths By default, the memory tool can only access files in `/memories/`. You can whitelist additional paths so the LLM can read and write to them. Each entry maps a path on disk to a virtual prefix that the LLM uses: ```lua require("codecompanion").setup({ interactions = { chat = { tools = { ["memory"] = { opts = { whitelist = { { path = "~/.dotfiles", as = "/dotfiles" }, { path = "/shared/notes", as = "/notes" }, }, }, }, }, }, }, }) ``` With this configuration, the LLM can use `/dotfiles/.zshrc` or `/notes/todo.md` as paths in memory tool calls, just as it uses `/memories/file.txt`. Tilde (`~`) is expanded automatically. Directory traversal protection applies to all whitelisted paths. You can also mount a single file. This is useful for a personal profile that the LLM can learn from and update over time: ```lua whitelist = { { path = "~/.dotfiles/PERSONAL.md", as = "/personal" }, }, ``` The LLM can then view and edit it via `/personal`. ### read\_file This tool can read the contents of a specific file in the current working directory. This can be useful for an LLM to gain wider context of files that haven't been shared with it. ### run\_command The *@run\_command* tool enables an LLM to execute commands on your machine, subject to your authorization. For example: ```md Can you use @{run_command} to run my test suite with `pytest`? ``` ```md Use @{run_command} to install any missing libraries in my project ``` Some commands do not write any data to [stdout](https://en.wikipedia.org/wiki/Standard_streams#Standard_output_\(stdout\)) which means the plugin can't pass the output of the execution to the LLM. When this occurs, the tool will instead share the exit code. The LLM is specifically instructed to detect if you're running a test suite, and if so, to insert a flag in its request. This is then detected and the outcome of the test is stored in the corresponding flag on the chat buffer. This makes it ideal for [agentic workflows](/extending/agentic-workflows) to hook into. **Options:** * `require_approval_before` require approval before running a command? (Default: true) ### web\_search This tool enables an LLM to search the web for a specific query, enabling it to receive up to date information: ```md Use @{web_search} to find the latest version of Neovim? ``` ```md Use @{web_search} to search neovim.io and explain how I can configure a new language server ``` Currently, the tool uses [tavily](https://www.tavily.com) and you'll need to ensure that an API key has been set accordingly, as per the [adapter](https://github.com/olimorris/codecompanion.nvim/blob/main/lua/codecompanion/adapters/http/tavily.lua). ## Adapter Tools > \[!NOTE] > Adapter tools are configured via the `available_tools` dictionary on the adapter itself Prior to [v17.30.0](https://github.com/olimorris/codecompanion.nvim/releases/tag/v17.30.0), tool use in CodeCompanion was only possible with the built-in tools. However, that release unlocked *adapter* tools. That is, tools that are owned by LLM providers such as [Anthropic](https://docs.claude.com/en/docs/agents-and-tools/tool-use/computer-use-tool) and [OpenAI](https://platform.openai.com/docs/guides/tools-web-search?api-mode=responses). This allows for remote tool execution of common tasks such as web searching and computer use. From a UX perspective, there is no difference in using the built-in and adapter tools. However, please note that an adapter tool takes precedence over a built-in tool in the event of a name clash. ### Anthropic In the `anthropic` adapter, the following tools are available: * `code_execution` - The code execution tool allows Claude to run Bash commands and manipulate files, including writing code, in a secure, sandboxed environment * `memory` - Enables Claude to store and retrieve information across conversations through a memory file directory. Claude can create, read, update, and delete files that persist between sessions, allowing it to build knowledge over time without keeping everything in the context window * `web_fetch` - The web fetch tool allows Claude to retrieve full content from specified web pages and PDF documents. * `web_search` - The web search tool gives Claude direct access to real-time web content, allowing it to answer questions with up-to-date information beyond its knowledge cutoff ### OpenAI In the `openai_responses` adapter, the following tools are available: * `web_search` - Allow models to search the web for the latest information before generating a response. ## MCP The MCP servers you've [configured](/configuration/mcp) in CodeCompanion expose their own set of tools that you can use in the chat buffer. Once a server has been started, the tools will be available to you and appear in the completion menu, by typing `@`. They are prefixed with `mcp:`. ## Security CodeCompanion takes security very seriously, especially in a world of agentic code development. Tools that create or delete files validate that paths are within the current working directory (cwd) to prevent unintended modifications outside of your project. This ensures that the LLM can only work within the cwd when executing destructive tools, minimizing actions that are hard to [recover from](https://www.businessinsider.com/replit-ceo-apologizes-ai-coding-tool-delete-company-database-2025-7). ### Approvals > \[!NOTE] > This applies to CodeCompanion's built-in tools only. ACP agents have their own tools and approval systems. In order to give developers the confidence to use tools, CodeCompanion has implemented a comprehensive approval system for it's built-in tools. CodeCompanion segregates tool approvals by chat buffer and by tool. This means that if you approve a tool in one chat buffer, it is *not* approved for use anywhere else. Similarly, if you approve a tool once, you'll be prompted to approve it again next time it's executed. When prompted, the user has four options available to them: * **Allow always** - Always allow this tool/cmd to be executed without further prompts * **Allow once** - Allow this tool/cmd to be executed this one time * **Reject** - Reject the execution of this tool/cmd and provide a reason * **Cancel** - Cancel this tool execution and all other pending tool executions Certain tools with potentially destructive capabilities have an additional layer of protection. Instead of being approved at a tool level, these are approved at a command level (`require_cmd_approval = true`). Taking the `run_command` tool as an example. If you approve an agent to always run `make format`, if it tries to run `make test`, you'll be prompted to approve that command specifically. Approvals can be reset for the given chat buffer by using the `gtx` keymap. ### YOLO mode To bypass the approval system, you can use `gty` in the chat buffer to enable YOLO mode. This will automatically approve all tool executions without prompting the user. However, some tools such as `run_command` and `delete_file` are excluded from this as they have `allowed_in_yolo_mode = false` set by default. If you've configured the [LLM judge](/configuration/chat-buffer#llm-judge) then a tool's commands will be sent to an LLM to verify that they're safe. This assumes that your chosen adapter supports structured outputs and the tool itself supports the judge. The [delete\_file](#delete_file) and [run\_command](#run_command) tools support this out of the box. If the judge decides the action is safe, it executes immediately and the verdict is cached so re-running the exact same command won't be re-judged that session. For example, approving `make test` does not result in `make test && rm -rf foo` being auto-approved. If the request to the judge fails, or the adapter can't produce structured output, the tool will require manual approval. > \[!WARNING] > Running tools in YOLO mode is dangerous and it is recommend that you only use it in a safe environment where potential data loss can be recovered. You are responsible for any damage that may occur when using YOLO mode. ## Compatibility Below is the tool use status of various adapters and models in CodeCompanion: | Adapter | Model | Supported | Notes | |-------------------|-------------------| :----------------: |-------------------------------------| | Anthropic | | :white\_check\_mark: | Dependent on the model | | Azure OpenAI | | :white\_check\_mark: | Dependent on the model | | Copilot | | :white\_check\_mark: | Dependent on the model | | DeepSeek | | :white\_check\_mark: | Dependent on the model | | Gemini | | :white\_check\_mark: | Dependent on the model | | GitHub Models | | :x: | Not supported yet | | Huggingface | | :x: | Not supported yet | | Kimi | | :white\_check\_mark: | Dependent on the model | | Mistral | | :white\_check\_mark: | Dependent on the model | | Novita | | :white\_check\_mark: | Dependent on the model | | Ollama | Tested with Qwen3 | :white\_check\_mark: | Dependent on the model | | OpenAI | | :white\_check\_mark: | Dependent on the model | | OpenAI Responses | | :white\_check\_mark: | Dependent on the model | | OpenRouter | | :white\_check\_mark: | Dependent on the model | | xAI | | :x: | Not supported yet | > \[!IMPORTANT] > When using Mistral, you will need to set `interactions.chat.tools.opts.auto_submit_errors` to `true`. See [#2278](https://github.com/olimorris/codecompanion.nvim/pull/2278) for more information. --- --- url: /usage/chat-buffer/editor-context.md description: >- Share Neovim state with your LLM using CodeCompanion editor context — reference buffers, selections, diagnostics, and more with the #{context} syntax in chat. --- # Using Editor Context Editor context allows you to dynamically insert Neovim context into your chat messages using the `#{context}` syntax. They're processed when you send your message to the LLM, automatically including relevant content like buffer contents, LSP diagnostics, or your current viewport. Type `#` in the chat buffer to see available context through code completion, or type them manually. Custom context can be shared in the chat buffer by adding them to the `interactions.shared.editor_context` table in your configuration. ## Basic Usage Editor context uses the `#{context}` syntax to dynamically insert content into your chat, such as `#{buffer}`. Editor context is processed when you send your message to the LLM. > \[!IMPORTANT] > With the exception of `#{buffer}` and `#{buffers}`, editor context captures a point-in-time snapshot when your message is sent. If the underlying data changes (e.g. new diagnostics, a different quickfix list), simply use the context again in a new message to share the latest state. ## #buffer > \[!NOTE] > By default, CodeCompanion automatically applies the `{diff}` parameter to all buffers The `#{buffer}` context shares buffer contents with the LLM. It has two special parameters which control how content is shared, or *synced*, with the LLM, on each turn: ### Basic Usage * `#{buffer}` - Shares the current buffer (last one you were in) ### Target Specific Buffers * `#{buffer:init.lua}` - Shares a specific file by name * `#{buffer:src/main.rs}` - Shares a file by path * `#{buffer:utils}` - Shares a file containing "utils" in the path ### With Parameters **`{diff}`** - Sends only the changed portions of the buffer to the LLM. Use this for large files where you only want to share incremental changes to reduce token usage. This is the default option in CodeCompanion. **`{all}`** - Sends all of the buffer content to the LLM whenever the buffer changes. Use this when you want the LLM to always have the complete, up-to-date file context. Can be used in combination with targeting a specific buffer: * `#{buffer}{diff}` - Sends only changed portions of the buffer * `#{buffer}{all}` - Sends entire buffer on any change * `#{buffer:config.lua}{all}` - Combines targeting with parameters ### Multiple Buffers > \[!NOTE] > For selecting multiple buffers with more control, use the `/buffer` slash command. ```md Compare #{buffer:old_file.js} with #{buffer:new_file.js} and explain the differences. ``` ## #buffers The *buffers* context shares all currently open buffers with the LLM. Buffers with excluded buftypes (such as `nofile`, `quickfix`, `prompt`, `popup`) and filetypes (such as `codecompanion`, `help`, `terminal`) are automatically filtered out. ```md #{buffers} can you explain what's going on in these files? ``` ## #code\_review The *code\_review* context shares your [code reviews](/usage/code-review) with an LLM. Every pending comment you've left with `:CodeCompanionCodeReview Comment` is sent when you submit the chat buffer and the review baseline advances so the next review only shows what changes in the next iteration. ```md Please action #{code_review} ``` Each comment reaches the LLM with the file, the line range and the code you commented on. Your chat buffer shows a shorter version of the same thing, without the code, so you can scroll back through earlier rounds and see what you asked for. > \[!NOTE] > Sharing your review clears the pending comments and the virtual text that marks them. Like a PR review, submitting it also approves everything you didn't comment on. ## #diagnostics > \[!TIP] > The [Action Palette](/usage/action-palette) has a pre-built prompt which asks an LLM to explain LSP diagnostics in a visual selection. The *diagnostics* context shares any diagnostic information from LSP servers active in the current buffer. This can serve as useful context should you wish to troubleshoot any errors with an LLM. ```md #{diagnostics} can you explain the LSP errors in this file and how to fix them? ``` ## #diff The *diff* context shares the current git diff with the LLM, including both staged and unstaged changes. This is useful for code review, generating commit messages, or asking for feedback on your recent changes. ```md Sharing the latest git diff with you #{diff} ``` ## #messages The *messages* context shares Neovim's message history (`:messages`) with the LLM. This is useful when an error has been written to the message history and you want to share it with the LLM for troubleshooting. ```md Can you explain the error I've just observed in Neovim? #{messages} ``` ## #quickfix The *quickfix* context shares the contents of the quickfix list with the LLM. Files with diagnostics are formatted with smart grouping by Tree-sitter symbols, while file-only entries show the full content. This is useful for sharing compiler errors, search results, or LSP diagnostics across multiple files. ```md The relevant output from my quickfix list has now been shared with you #{quickfix} ``` ## #selection The *selection* context shares your current or most recent visual selection with the LLM. This is useful for asking about a specific piece of code without sharing the entire buffer. The selection is updated when you open or toggle a CodeCompanion chat buffer. ```md Sharing the relevant code with you #{selection} ``` ## #terminal The *terminal* context shares the latest output from the last terminal buffer you entered. Subsequent uses capture only new output since the last time it was shared. This is useful for sharing test results, build output, or command-line errors. ```md This was the output in my terminal #{terminal} ``` ## #viewport The *viewport* context shares with the LLM, exactly what you see on your screen at the point a response is sent (excluding the chat buffer of course). ```md Sharing what I can see in Neovim #{viewport} ``` --- --- url: /usage/chat-buffer/rules.md description: >- Add rules files like CLAUDE.md, AGENTS.md, or Cursor rules to the CodeCompanion chat buffer to provide persistent LLM instructions and project context. --- # Using Rules Ensure that you have read the [Rules Configuration](/configuration/rules) section to understand how to create and configure rule groups. ## Default Rule Group Below is the `default` rule group that, when [enabled](/configuration/rules#enabling-rules), provides a collection of common files to the chat buffer: ```lua require("codecompanion").setup({ rules = { default = { description = "Collection of common files for all projects", files = { ".clinerules", ".cursorrules", ".goosehints", ".rules", ".windsurfrules", ".github/copilot-instructions.md", "AGENT.md", "AGENTS.md", { path = "CLAUDE.md", parser = "claude" }, { path = "CLAUDE.local.md", parser = "claude" }, { path = "~/.claude/CLAUDE.md", parser = "claude" }, }, }, }, }) ``` ## Creating Rules The plugin does not require rules to be in a specific filetype or even format (unless you're using the `claude` parser). This allows you to leverage [mdc](https://docs.cursor.com/en/context/rules#rule-anatomy) files, markdown files or good old plain text files. The location of the rules is also unimportant. The rules files could be local to the project you're working in. Or, they could reside in a separate location on your disk. Just ensure the path is correct when you're [creating/configuring](/configuration/rules#rule-groups) the rules group. You can even set the system prompt for the chat buffer in the rules file itself. ### Example 1: Rule that can be processed with the `codecompanion` parser ```markdown # Example Rules File ## System Prompt What ever goes in this section is used as a system prompt in the chat buffer. So you can specify instructions: - Here - And here ...and anywhere here ## My other header @./lua/codecompanion/interactions/chat/tools/init.lua Anything in this section is added as context to the chat buffer. The file above is also shared ``` ### Example 2: Rule that can be processed with the `claude` parser ```markdown # Example Claude Rules File @./lua/codecompanion/interactions/chat/tools/init.lua @INSTRUCTIONS.md This is a rules file that can be parsed with the Claude parser. Anything in this file is added as context to the chat buffer. Including the files above. ``` #### Resolving `@` paths Other files can be referenced in a rules file via `@path`. CodeCompanion looks for the file in this order: 1. Absolute paths (starting with `/` or `~`) are used as-is 2. Relative paths (e.g. `@INSTRUCTIONS.md`) are first resolved against the directory of the rules file itself 3. If not found there, they're resolved against the current working directory 4. If neither exists, a warning is logged Taking a global rules files such as `~/.claude/CLAUDE.md`: If it contains an `@RTK.md` reference inside it, this would resolve to `~/.claude/RTK.md`, regardless of where Neovim was launched from. ## Adding Rules to a Chat Buffer ### When Opening the Chat Buffer Rules can automatically be added to a chat buffer when it's created. Just specify the default rules to include: ::: code-group ```lua [Autoload] require("codecompanion").setup({ rules = { opts = { chat = { autoload = { "default", "claude "} }, }, }, }) ``` ```lua [Overwrite Autoload] require("codecompanion").setup({ rules = { default = { description = "My default group", files = { "CLAUDE.md", "~/Code/Helpers/my_project_specific_help.md", }, }, opts = { chat = { autoload = "default", }, }, }, }) ``` ::: ### Slash Command To add rules to an existing chat buffer, use the `/rules` slash command. This will allow multiple rule groups to be added at a time. ### Action Palette There is also a *Chat with rules* action in the [Action Palette](/usage/action-palette). This lists all of the rule groups in the config that can be added to a new chat buffer. ### Clearing Rules Rules can also be cleared from a chat buffer via the `gR` keymap. Although note, this will remove *ALL* context that's been designated as *rules*. --- --- url: /usage/chat-buffer/slash-commands.md description: >- Reference for all CodeCompanion slash commands — fetch URLs, add files and buffers, compact message history, insert symbols, and run ACP session options. --- # Using Slash Commands Slash Commands enable you to quickly add context to the chat buffer. They are comprised of values present in the `interactions.chat.slash_commands` table alongside the `prompt_library` table where individual prompts have `opts.is_slash_cmd = true`. ## /acp\_session\_options > \[!NOTE] > This command is only relevant for users of ACP adapters The [ACP specification](https://agentclientprotocol.com/protocol/session-config-options) allows users to change config options for an agent session and the *acp\_session\_options* slash command provides the interface to do this. ## /buffer > \[!NOTE] > As of [v16.2.0](https://github.com/olimorris/codecompanion.nvim/releases/tag/v16.2.0), buffers are now watched by default The *buffer* slash command enables you to add the contents of any open buffers in Neovim to the chat buffer. The command has native, *Telescope*, *mini.pick*, *fzf.lua* and *snacks.nvim* providers available. Also, multiple buffers can be selected and added to the chat buffer as per the video above. This slash command is also available in the [CLI prompt input](/usage/cli#slash-commands), where it inserts `@path` references instead of buffer contents. ## /command The *command* slash command is specific to [ACP](/configuration/adapters-acp) adapters and allows users to switch between different adapter commands. For instance, some ACP adapters may allow you to run the agent command with a specific flag. Be mindful that switching commands is destructive and essentially resets the chat buffer for the purposes of a conversation with an agent. ## /compact The *compact* slash command, based on [Claude Code's](https://code.claude.com/docs/en/slash-commands#built-in-slash-commands) corresponding feature, clears the chat buffer's message history whilst preserving a summary, in context. System prompts, rules and file/buffer shares will be preserved but all user, assistant and tool messages will be removed. The summary is generated by prompting the same LLM to summarize the chat history into a concise format. ## /fetch > \[!TIP] > To better understand a Neovim plugin, send its `config.lua` to your LLM via the *fetch* command alongside a prompt The *fetch* slash command allows you to add the contents of a URL to the chat buffer. By default, the plugin uses the awesome and powerful [jina.ai](https://jina.ai) to parse the page's content and convert it into plain text. For convenience, the slash command will cache the output to disk and prompt the user if they wish to restore from the cache, should they look to fetch the same URL. ## /file The *file* slash command allows you to add the contents of a file in the current working directory to the chat buffer. The command has native, *Telescope*, *mini.pick*, *fzf.lua* and *snacks.nvim* providers available. Also, multiple files can be selected and added to the chat buffer. [#3218](https://github.com/olimorris/codecompanion.nvim/pull/3218) added support for PDFs for the following http adapters: * Anthropic * Copilot (currently only supports OpenAI models) * OpenAI * OpenAI Responses * OpenRouter Simply use the `/file` slash command and select a PDF file. The plugin will `base64` encode the PDF and send it to the LLM. This slash command is also available in the [CLI prompt input](/usage/cli#slash-commands), where it inserts `@path` references instead of file contents. * Select a single file: `⏎ enter` * Select multiple files: `⇥ tab` Please note that these mappings may be different depending on your provider. ## /fork The *fork* slash command, specific to *http* adapters, allows you to duplicate the current chat buffer, copying the message history and preserving tools and context in the process. This enables you to branch the conversation and experiment with different prompts, models or even adapters without losing the original conversation. ## /help The *help* slash command allows you to add content from a vim help file (`:h helpfile`), to the chat buffer, by searching for help tags. Currently this is only available for *Telescope*, *mini.pick*, *fzf\_lua* and *snacks.nvim* providers. By default, the slash command will prompt you to trim a help file that is over 1,000 lines in length. ## /image The *image* slash command allows you to add images into a chat buffer via remote URLs and through your file system. In the config for the slash command, you can specify a group of directories (with `opts.dirs`) that the image picker will always search in, alongside the current working directory. Currently the image picker is only available with *snacks.nvim* and the `vim.ui.select`. ## /rules The *rules* slash command allows you to add [rules](/usage/chat-buffer/rules) groups to the chat buffer. ## /mcp The *mcp* slash command allows you to start and stop [Model Context Protocol (MCP)](/configuration/mcp) servers manually from within a chat buffer. This is applied at a global level, so starting/stopping servers in one chat buffer will affect all other chat buffers. A *snacks.nvim* and `vim.ui.select` provider is available for selecting which MCP servers to start/stop. ## /mode The *mode* slash command is specific to [ACP](/configuration/adapters-acp) adapters and allows users to switch between different agent operating modes, as per the [protocol](https://agentclientprotocol.com/protocol/session-modes) docs. ## /now The *now* slash command simply inserts the current datetime stamp into the chat buffer. ## /rename The *rename* slash command is specific to [http](/configuration/adapters-http) adapters. It allows you to rename the title of the conversation in the chat buffer. This can be useful to keep track of different conversations via *open chats* in the [action palette](/usage/action-palette). ## /resume The *resume* slash command is specific to [ACP](/configuration/adapters-acp) adapters that support the `session/list` capability. It allows you to resume a previous session by listing your past sessions and restoring the selected one into the chat buffer. The conversation history is rendered so you can continue where you left off. > \[!NOTE] > The `/resume` command must be used before sending any messages. It is only available on a fresh chat buffer. ## /share The *share* slash command allows you to share the conversation in the chat buffer as a secret [GitHub Gist](https://gist.github.com). You'll need to ensure that you set a token in your configuration with permission to create gists: ```lua require("codecompanion").setup({ interactions = { chat = { slash_commands = { ["share"] = { opts = { token = os.getenv("GITHUB_GIST_TOKEN"), }, }, }, }, }, }) ``` ## /symbols > \[!NOTE] > If a filetype isn't supported please consider making a PR to add the corresponding Tree-sitter queries from > [aerial.nvim](https://github.com/stevearc/aerial.nvim) The *symbols* slash command uses Tree-sitter to create a symbolic outline of a file to share with the LLM. This can be a useful way to minimize token consumption whilst sharing the basic outline of a file. The plugin utilizes the amazing work from **aerial.nvim** by using their Tree-sitter symbol queries as the basis. The list of filetypes that the plugin currently supports can be found in the [Tree-sitter queries directory](https://github.com/olimorris/codecompanion.nvim/tree/main/queries). The command has native, *Telescope*, *mini.pick*, *fzf.lua* and *snacks.nvim* providers available. Also, multiple symbols can be selected and added to the chat buffer. --- --- url: /usage/cli.md description: >- Interact with CLI agents like Claude Code and Codex from Neovim using CodeCompanion — share context, send prompts, and manage terminals without leaving Neovim. --- # Using the Command-Line Interface (CLI) The CLI interaction allows you to interact with agents that have a command-line interface such as [Claude Code](https://docs.anthropic.com/en/docs/agents-and-tools/claude-code/overview) and [Codex](https://github.com/openai/codex). *Why?* Sharing context with an agent in the CLI can be cumbersome. You have to navigate to the CLI, press `@` search for the file or trigger a slash command. If you want to share a code snippet then that's a good ol' copy and paste job. With CodeCompanion, you can share context from Neovim in keystrokes, without leaving the buffer or the editor. ## Initiating a CLI Interaction You can use `:CodeCompanionCLI` to start a new CLI interaction and CodeCompanion will leverage the agent you've configured in your config at `interactions.cli.agent`. If you want to specify an agent on the fly, you can use `:CodeCompanionCLI agent=`. You can toggle a CLI interaction with `require("codecompanion").toggle()`, just as you would with a chat buffer. You can use `{` and `}` to cycle through all the chat and CLI interactions. ## Workflow Below are some useful workflow tips to enable you to be productive when working with agents in the CLI with CodeCompanion: ### Prompting the Agent You can send a custom prompt to the agent from within a Neovim buffer: ```lua -- [C]odeCompanion [P]rompt] vim.keymap.set({ "n", "v" }, "cp", function() return require("codecompanion").cli({ prompt = true }) end, { desc = "Prompt the CLI agent" }) ``` In normal mode, this brings up the prompt input, allowing you to specify editor context before sending to the agent. In visual mode however, it shares the selection alongside your prompt, saving you from manually specifying editor context. ### Adding Context You're working in a buffer and think *"I should share this with the agent"* or *"This code is relevant to the conversation..."*: ```lua -- [C]odeCompanion [A]dd vim.keymap.set({ "n", "v" }, "ca", function() return require("codecompanion").cli("#{this}", { focus = false }) end, { desc = "Add context to the CLI agent" }) ``` This keymap allows you to quickly share the current buffer or visual selection with the agent, without needing to specify a prompt, utilising `#{this}`. This is useful for quickly sharing context before following up with a more specific prompt. You'll also note the inclusion of `focus = false` to ensure that the cursor doesn't move into the CLI buffer. This can be useful as you carefully move between buffers and code, determining what context is relevant to share with the agent, without losing your current position in the CLI buffer. ### Fixing LSP Diagnostics If the LSP is throwing some warnings, share them with the agent in the CLI and ask it to fix them: ```lua -- [C]odeCompanion [D]iagnostics vim.keymap.set("n", "cd", function() return require("codecompanion").cli("#{diagnostics} Can you fix these?", { focus = false, submit = true }) end, { desc = "Send diagnostics to CLI agent" }) ``` This keymap shares the LSP diagnostics for the current buffer with the agent, automatically submitting the prompt. ### Fixing Failing Tests You've run your test suite in the terminal and observe some failures. Share them with the agent: ```lua -- [C]odeCompanion [T]erminal vim.keymap.set("n", "ct", function() return require("codecompanion").cli("#{terminal} Sharing the output from the terminal. Can you fix it?", { focus = false, submit = true }) end, { desc = "Send terminal output to CLI agent" }) ``` This keymap shares the output from the most recent terminal with the agent, which is especially useful for sharing failing test output. Again, the prompt is automatically submitted to save you time. ## Sending Context This section covers, more broadly, the ways that you can send context to an agent in the CLI. This should serve as inspiration for how you can leverage the CLI for your own workflow. ### Visual Selection To start off, you can use a visual selection as a source of context, by visually selecting some code and running: ``` CodeCompanionCLI Can you explain this code? ``` You could also achieve this in Lua: ```lua require("codecompanion").cli({ prompt = true }) ``` This will result in the visual selection being passed to an input prompt, allowing you to type *"Can you explain this code?"* before sending it to the agent. Alternatively, you could hard code the prompt: ```lua require("codecompanion").cli("Can you explain this code?") ``` ### Editor Context Similarly to the [chat buffer](/usage/chat-buffer/), you can use [editor context](/usage/chat-buffer/editor-context) references in your prompts to share information about your current Neovim session. This makes it trivial to share the current buffer (`#{buffer}`), all currently open buffers (`#{buffers}`), or LSP diagnostics (`#{diagnostics}`) to name but a few. You can use the `:CodeCompanionCLI` command: ``` CodeCompanionCLI Can you explain #{buffers}? ``` Which will be expanded in the agent CLI to be: ```log ❯ Can you explain the open buffers: @your_file_path @your_other_file_path? ``` Alternatively: ```lua require("codecompanion").cli("Can you explain #{buffers}?") ``` *** CodeCompanion also provides `#{this}` (unique to the CLI interaction) which resolves to the current buffer in normal mode, and the visual selection in visual mode: ``` CodeCompanionCLI What does #{this} do? ``` In normal mode, this will resolve to be: ```log ❯ What does @your_file_path do? ``` and with a visual selection, will resolve to be: `````log ❯ What does the selected code in @your_file_path do? - Selected code from @your_file_path (lines 3-4): ````lua local new_set = MiniTest.new_set local T = new_set() ```` ````` > \[!NOTE] > `@path` references are understood natively by CLI agents like Claude Code and Codex, allowing them to read files directly. ### Prompts There will come a time when you need to send a more complex prompt to the agent. Whilst you can do `:CodeCompanionCLI `, you can also bring up a prompt input with: ``` CodeCompanionCLI Ask ``` or: ```lua require("codecompanion").cli({ prompt = true }) ``` This will toggle a `codecompanion_input` buffer. In this buffer, you have access to all of the available [editor context](#editor-context), some [slash commands](#slash-commands) and a much a larger character window. To send the prompt to the agent, you can write the buffer with `:w`. Or, to automatically send and submit, you can forcefully write with `:w!`. You can scroll previous prompts with the `` and `` keys. ### Slash Commands The prompt input buffer also supports the [buffer](/usage/chat-buffer/slash-commands#buffer]) and [file](/usage/chat-buffer/slash-commands#file) slash commands, which better enable you to share lots of context with a CLI agent at once. Simply type `/` in the buffer to bring up the completion menu for your selected provider. Instead of sharing file contents, CLI slash commands insert `@path` references into the prompt. For example, selecting a file via `/file` will insert: ```markdown @./lua/codecompanion/init.lua ``` If you select multiple files, each one is added on its own line: ```markdown @./lua/codecompanion/init.lua @./lua/codecompanion/config.lua ``` ### Auto-Submit By default, prompts are sent to the agent but *not* submitted. The text appears in the CLI and you can review it before pressing enter. To automatically submit a prompt so the agent starts working immediately, you can: Use the *bang* form of the command: ``` CodeCompanionCLI! #{diagnostics} Can you fix these? ``` Or pass `submit = true` in Lua: ```lua require("codecompanion").cli("#{diagnostics} Can you fix these?", { submit = true }) ``` This is especially useful in keymaps where you want a fire-and-forget workflow, like the diagnostics and terminal examples in the [Workflow](#workflow) section. ## API Reference The `require("codecompanion").cli()` function is the main entry point for interacting with CLI agents. It has a polymorphic signature: ```lua -- No args: create a new CLI instance and open it require("codecompanion").cli() -- Opts table: create a new instance with options require("codecompanion").cli({ agent = "claude_code" }) -- String prompt: send to the last instance (or create one) require("codecompanion").cli("Can you explain this code?") -- String prompt with opts require("codecompanion").cli("Fix #{diagnostics}", { submit = true, focus = false }) ``` ### Options | Option | Type | Default | Description | |---|---|---|---| | `agent` | `string` | config default | The CLI agent to use. When sending a prompt, reuses an existing instance of this agent if one exists | | `focus` | `boolean` | `true` | Whether to open the CLI window and move the cursor to it. Set to `false` to send context in the background | | `submit` | `boolean` | `false` | Automatically submit the prompt (press enter) so the agent starts working immediately | | `prompt` | `boolean` | `false` | Open the prompt input buffer instead of sending directly. If a string prompt is also provided, it pre-fills the input | | `width` | `number` | config default | Override the CLI window width | | `height` | `number` | config default | Override the CLI window height | --- --- url: /usage/code-review.md description: >- Leave comments on an agent's changes where the code is, send them all at once, and iterate in rounds - a pull request review, in Neovim. --- # Using Code Reviews Code reviewing an agents work usually involves the manual typing of the file name along with the line number and your comment. For example, *"in `foo.lua`, around line 42, this should be..."*. Code reviews let you leave comments exactly where the code is. You can put your cursor on a line, or, make a visual selection, and then add a comment. Then, when you send your review to an agent, each comment arrives with the full context. Sending a review also **advances a baseline**, which turns agent iterations into rounds. Your next review only shows what the agent changed in response, not the full edit history. In essence, it's the same loop as a pull request: comment, submit, re-review the response. Code reviews work with CodeCompanion's own tools, ACP agents, and even CLI agents like Claude Code running outside of Neovim. ## How It Works ```mermaid sequenceDiagram participant U as User participant C as Chat Buffer participant A as Agent participant G as Git U->>C: Writes prompt C->>A: Sends prompt C->>G: Snapshot the worktree to the baseline A->>A: Edits files A->>C: Returns response U->>G: :CodeCompanionCodeReview G->>U: Quickfix populated one entry per hunk, vs the baseline loop Step through the hunks alt Change is fine U->>U: Keep moving else Change needs work U->>U: :CodeCompanionCodeReview Comment end end U->>C: Shares comments with #35;{code_review} C->>A: Sends comments C->>G: Baseline advances Note over U,G: The next review only shows what the agent changes in response ``` When an agent begins working in a git repository, CodeCompanion snapshots the worktree to a *baseline* (a commit at `refs/worktree/codecompanion/baseline`). When you start a review, the diff between that baseline and the repo's files is produced. As the baseline lives in git and the review comments are persisted to disk, your progress is stored across sessions and Neovim instances. Because the diff is recomputed from disk every time, line numbers are never stored and so can never rot. > \[!IMPORTANT] > Snapshots are produced against a temporary index, so `git add` never runs against your own. This means your staged changes and anything you push are unaffected by a code review ## Commands | Command | Description | | --- | --- | | `:CodeCompanionCodeReview` | Open the agent's changes in the quickfix list, one entry per hunk | | `:CodeCompanionCodeReview Accept` | Accept the current hunk, keeping it out of future reviews | | `:CodeCompanionCodeReview All` | As above, but include every change since the baseline - accepted hunks and files beyond the agent's | | `:CodeCompanionCodeReview Approve` | Approve everything up to now, advancing the baseline | | `:CodeCompanionCodeReview Comment` | Comment on the current line or visual selection, or edit the comment already there | | `:CodeCompanionCodeReview Comments` | Open the pending comments file for editing | | `:CodeCompanionCodeReview Ignore` | Ignore the current hunk's file until the baseline advances | | `:CodeCompanionCodeReview Share` | Submit the review to a file and copy its path - for agents outside CodeCompanion | | `:CodeCompanionCodeReview Start` | The same as `Approve` - use it before an agent starts, to mark the point you'll review from | ## Keymaps CodeCompanion sets keymaps in the quickfix window when you start a review. | Keymap | Description | | --- | --- | | `a` | Accept the hunk under the cursor | | `c` | Comment on the hunk under the cursor | | `d` | Diff the hunk under the cursor | | `x` | Ignore the hunk's file until the baseline advances | Of course, you still have the default Vim keymaps in the quickfix such as `[q` / `]q` to step through the hunks, and `:copen` / `:cclose` to open and close the quickfix window. ## Commenting Adding a comment is as simple as putting your cursor on a line, or, making a visual selection, and: ``` :CodeCompanionCodeReview Comment ``` You can then type your comment in the input, followed by the same keymaps you use to send a message in the chat buffer (`` in normal mode for example). Your comments are stored against the file, the line range and the code on those lines. A comment is only ever in one place: **pending in the file, or sent in the chat buffer**. When you share a review, the virtual text clears and the comments appear in the chat buffer instead. ### Editing and Deleting To change a comment, you can run `:CodeCompanionCodeReview Comment` when your cursor is back on the line. **Submitting the input empty deletes the comment**. To review/edit all comments at once, `:CodeCompanionCodeReview Comments` opens the raw comments file. `:bw` saves your edits. ### Sending Use the [code\_review](/usage/chat-buffer/editor-context#code-review) editor context in a chat buffer: ```md Please action #{code_review} ``` This will be expanded to *"Please action my comments from the code review, which I've attached"*. The LLM receives each comment as a block containing the path, the line range, the code you commented on, and your prose. In the chat buffer, a shorter, readable version is also added so you can see what was sent without opening the [debug window](/usage/chat-buffer/#debug-window). If you have no pending comments there's nothing to send, and `#{code_review}` does nothing. A review you're happy with can end with `:CodeCompanionCodeReview Approve`, to advance the baseline and treat any new changes as a new round. ## Reviewing Hunks If an agent has made *many* changes, you can step through them one hunk at a time with: ``` :CodeCompanionCodeReview ``` Every change since the baseline goes into the quickfix list (`:h quickfix`), one entry per hunk. From there: 1. Step through the hunks with Vim's own keymaps, `:cnext` or `]q` 2. Press `d` on a hunk to see it as a diff against the baseline 3. Press `c` to comment on it 4. Press `a` to accept it, dropping it from this review and future ones 5. Press `x` to ignore the hunk's whole file, for lockfiles and generated code Accepted hunks and ignored files remain until the baseline advances. To reject a hunk outright, diff it with `d` and use Vim's native `do` (`:h do`) to pull the baseline's version back in, then write the file. The revert drops out of the next review by itself. ### Scoping By default a review only covers the files an agent edited **through CodeCompanion's tools**. Append `All` to widen it to every change since the baseline: ``` :CodeCompanionCodeReview All ``` That includes your own edits, edits from outside of Neovim, hunks you've accepted and files you've ignored. ## Diffing CodeCompanion owns the **baseline and the comments** but doesn't own the diff view. `refs/worktree/codecompanion/baseline` is a normal git ref, so any diff plugin can be pointed at it. With [diffview.nvim](https://github.com/sindrets/diffview.nvim): ``` :DiffviewOpen refs/worktree/codecompanion/baseline ``` With [gitsigns.nvim](https://github.com/lewis6991/gitsigns.nvim): ``` :Gitsigns change_base refs/worktree/codecompanion/baseline ``` This enables you to move between an agent's changes as you see fit and still use `CodeCompanionCodeReview Comment` to leave feedback. > \[!WARNING] > Ensure you comment from the **working file**, not from the baseline side of a diff ## Parallel Agents If you run multiple agents at at time, it's common to have each in its own [git worktree](https://git-scm.com/docs/git-worktree) within the repository and Code Reviews have been built to support that. The baseline ref lives under `refs/worktree/`, which git scopes per-worktree in the same way it scopes `HEAD`. Comments are stored per repository root and per branch so each agent gets its own baseline and its own pending comments. This ensures reviews never clash. To switch to a worktree, run `:CodeCompanionCodeReview` to review only that agent's work. ## Working in the CLI Depending on your workflow, you may like to use a coding agent outside of Neovim. If that's the case, you can still leverage the code review functionality. The baseline sees every change in your worktree, no matter who made it - so you can review an agent that CodeCompanion didn't start, such as Claude Code running in a separate terminal: 1. `:CodeCompanionCodeReview Start` before the agent begins 2. Let the agent work 3. `:CodeCompanionCodeReview All` to review everything since the baseline 4. Leave comments with `:CodeCompanionCodeReview Comment`, as normal 5. `:CodeCompanionCodeReview Share` to begin sharing with the agent. Your comments move to a `review.md` file, the baseline advances, and the file's path is copied to your clipboard 6. Paste the path into the agent: ``` Please action my code review: /path/to/review.md ``` `All` is required because CodeCompanion can only attribute a change to an agent when it goes through CodeCompanion's own tools or interactions. > \[!TIP] > The `review.md` path is static at a repository level. Therefore, in a `CLAUDE.md` or `AGENTS.md` file you can reference this file, only needing to do `:CodeCompanionCodeReview Share` to advance the baseline. If the agent runs in CodeCompanion's own [CLI interaction](/usage/cli), steps 1-4 are the same, but you can submit with `#{code_review}` directly in the prompt instead of `Share`. ## Without Git Without git there's no baseline, so `:CodeCompanionCodeReview` falls back to a file-level view. The files the agent has edited in the session, are tracked by `:CodeCompanionChat Changes`. Comments and `#{code_review}` work as normal. ## Limitations When you make comments, they are stored and hard-coded to line numbers. Therefore, if you make substantial edits between submitting the comments, those line numbers can deviate. However, because the comments are attached to a code snippet, it should still be enough context for the agent. `Approve` doesn't discard comments, it simply advances the baseline and warns you that they're still pending. So a review you meant to send isn't silently thrown away. Use `:CodeCompanionCodeReview Comments` to delete them manually. --- --- url: /usage/events.md description: >- Reference for all CodeCompanion events and hooks — integrate with Neovim's autocmd system to react to chat, inline, CLI, and tool lifecycle events. --- # Events / Hooks In order to enable a tighter integration between CodeCompanion and your Neovim config, the plugin fires events at various points during its lifecycle. ## List of Events The events that are fired from within the plugin are: * `CodeCompanionACPConnected` - Fired after the ACP connection is authenticated and ready to use * `CodeCompanionACPSessionPre` - Fired after ACP authentication completes but before a new session is established; allows subscribers to modify the connection (e.g. inject MCP servers) synchronously * `CodeCompanionACPSessionPost` - Fired after a new ACP session has been established * `CodeCompanionChatACPModeChanged` - Fired after the ACP mode has been changed in the chat * `CodeCompanionACPChatRestored` - Fired after an ACP session has been restored * `CodeCompanionChatCreated` - Fired after a chat has been created for the first time * `CodeCompanionChatOpened` - Fired after a chat has been opened * `CodeCompanionChatClosed` - Fired after a chat has been permanently closed * `CodeCompanionChatHidden` - Fired after a chat has been hidden * `CodeCompanionChatSubmitted` - Fired after a chat has been submitted * `CodeCompanionChatDone` - Fired after a chat has received the response * `CodeCompanionChatCompacting` - Fired after the chat begins compacting messages to reduce token usage * `CodeCompanionChatStopped` - Fired after a chat has been stopped * `CodeCompanionChatCleared` - Fired after a chat has been cleared * `CodeCompanionChatRestored` - Fired after a chat has been restored to an editable state (e.g. when `on_before_submit` prevents submission) * `CodeCompanionChatAdapter` - Fired after the adapter has been set in the chat * `CodeCompanionChatModel` - Fired after the model has been set in the chat * `CodeCompanionCLICreated` - Fired after a CLI buffer has been created for the first time * `CodeCompanionCLIOpened` - Fired after a CLI buffer has been opened * `CodeCompanionCLIClosed` - Fired after a CLI buffer has been closed * `CodeCompanionCLIHidden` - Fired after a CLI buffer has been hidden * `CodeCompanionCLISent` - Fired after data has been sent to a CLI buffer * `CodeCompanionContextChanged` - Fired when the context that a chat buffer follows, changes * `CodeCompanionFileEdited` - Fired after the LLM has edited or created a file; the data payload includes the `path` and what made the change (`tool`) * `CodeCompanionInlineStarted` - Fired at the start of the Inline interaction * `CodeCompanionInlineFinished` - Fired at the end of the Inline interaction * `CodeCompanionMCPServerStart` - Fired when an MCP server is started * `CodeCompanionMCPServerReady` - Fired when an MCP server is ready for requests * `CodeCompanionMCPServerClosed` - Fired when an MCP server is closed * `CodeCompanionMCPServerToolsLoaded` - Fired when tools are loaded for an MCP server * `CodeCompanionRequestStarted` - Fired at the start of any API request * `CodeCompanionRequestStreaming` - Fired at the start of a streaming API request * `CodeCompanionRequestFinished` - Fired at the end of any API request * `CodeCompanionToolAdded` - Fired when a tool has been added to a chat * `CodeCompanionToolApprovalRequested` - Fired when a tool is requesting approval to run * `CodeCompanionToolApprovalFinished` - Fired when a user has actioned an approval request * `CodeCompanionToolStarted` - Fired when a tool has started executing * `CodeCompanionToolFinished` - Fired when a tool has finished executing * `CodeCompanionToolsStarted` - Fired when the tool system has been initiated * `CodeCompanionToolsFinished` - Fired when the tool system has finished running all tools * `CodeCompanionToolsJudgeStarted` - Fired when the background judge begins vetting a tool call * `CodeCompanionToolsJudgeFinished` - Fired when the background judge returns its verdict In addition to these events, the chat buffer has its own **callback system** for hooking into lifecycle events like `on_before_submit`, `on_checkpoint` and `on_tool_output`. These callbacks receive the chat instance and can inspect or mutate chat state. See the [callbacks](/configuration/chat-buffer#callbacks) section for details. There are also events that can be utilized to trigger commands from within the plugin: * `CodeCompanionChatRefreshCache` - Used to refresh conditional elements in the chat buffer ## Event Data Each event also comes with a data payload. For example, with `CodeCompanionRequestStarted`: ```lua { buf = 10, data = { adapter = { formatted_name = "Copilot", model = "o3-mini-2025-01-31", name = "copilot" }, bufnr = 10, id = 6107753, interaction = "chat" }, event = "User", file = "CodeCompanionRequestStarted", group = 14, id = 30, match = "CodeCompanionRequestStarted" } ``` And the `CodeCompanionRequestFinished` also has a `data.status` value. ## Consuming an Event Events can be hooked into as follows: ```lua local group = vim.api.nvim_create_augroup("CodeCompanionHooks", {}) vim.api.nvim_create_autocmd({ "User" }, { pattern = "CodeCompanionInline*", group = group, callback = function(request) if request.match == "CodeCompanionInlineFinished" then -- Format the buffer after the inline request has completed require("conform").format({ bufnr = request.buf }) end end, }) ``` You can trigger an event with: ```lua vim.api.nvim_exec_autocmds("User", { pattern = "CodeCompanionChatRefreshCache", }) ``` --- --- url: /usage/inline.md description: >- Write and refactor code directly in Neovim buffers using CodeCompanion's inline interaction — supports visual selection, prompt library aliases, and diff review. --- # Using the Inline Interaction As per the [Getting Started](/getting-started.md#inline) guide, the inline interaction enables you to code directly into a Neovim buffer. Simply run `:CodeCompanion `, or make a visual selection to send that as context to the LLM alongside your prompt. For convenience, you can call prompts from the [prompt library](/configuration/prompt-library) via the interaction. For example, `:'<,'>CodeCompanion /tests` would ask the LLM to create some unit tests from the selected text. ## Adapters You can specify a different adapter to that in the configuration (`interactions.inline.adapter`) when sending an inline prompt. Simply include the adapter via `adapter=*`. For example `:<','>CodeCompanion adapter=deepseek can you refactor this?`. This approach can also be combined with variables. ## Classification One of the challenges with inline editing is determining how the LLM's response should be handled in the buffer. If you've prompted the LLM to *"create a table of 5 common text editors"* then you may wish for the response to be placed at the cursor's position in the current buffer. However, if you asked the LLM to *"refactor this function"* then you'd expect the response to *replace* a visual selection. The plugin uses the inline LLM you've specified in your config to determine if the response should: * *replace* - replace a visual selection you've made * *add* - be added in the current buffer at the cursor position * *before* - to be added in the current buffer before the cursor position * *new* - be placed in a new buffer * *chat* - be placed in a chat buffer ## Diff Mode By default, an inline interaction prompt will trigger the diff feature, showing differences between the original buffer and the changes made by the LLM. This can be turned off in your config via the `display.diff.provider` table. You can also choose to accept or reject the LLM's suggestions with the following keymaps: * `gda` - Accept an inline edit * `gdr` - Reject an inline edit These keymaps can also be changed in your config via the `interactions.inline.keymaps` table. ## Editor Context > \[!TIP] > To ensure the LLM has enough context to complete a complex ask, it's recommended to use the `buffer` editor context The inline interaction allows you to send context alongside your prompt via the notion of editor context. That is, context that relates to your current Neovim session: * `buffer` - shares the contents of the current buffer * `chat` - shares the LLM's messages from the last chat buffer * `clipboard` - shares the data on your clipboard with the LLM Simply include them in your prompt. For example `:CodeCompanion #{buffer} add a new method to this file`. Multiple context items can be sent as part of the same prompt. You can even add your own custom variables as per the [configuration](/configuration/inline#editor-context). You can also have multiple editor context as part of a prompt, for example: `:CodeCompanion #{buffer} #{clipboard} analyze this code`. --- --- url: /usage/prompt-library.md description: >- Use CodeCompanion's prompt library via keymaps, Action Palette, or chat slash commands — includes built-in prompts for explaining, fixing, and testing code. --- # Using the Prompt Library There are numerous ways that the prompts defined in your prompt library can be used in CodeCompanion. You can invoke them via keymaps, the Action Palette, or slash commands in the chat buffer. ## Keymaps You can assign prompts from the prompt library to a keymap via the `prompt` function: ```lua vim.keymap.set("n", "d", function() require("codecompanion").prompt("docs") end, { noremap = true, silent = true }) ``` Where `docs` is the `alias` of the prompt. ## Slash Commands If your prompt library entries have an `alias` defined then you can invoke them using a slash command. In the cmd line `:CodeCompanion /` or `/` if you're in the chat buffer. When invoked this way, any tools declared on the prompt are added to the current chat buffer before the prompt content is inserted. --- --- url: /usage/workflows.md description: >- Run agentic workflows in CodeCompanion — chain multi-step LLM interactions to automate complex coding tasks, launched from the Action Palette. --- # Using Workflows Workflows in CodeCompanion, are successive prompts which can be automatically sent to the LLM in a turn-based manner. This allows for actions such as reflection and planning to be easily implemented into your ways of working. They can be combined with tools to create agentic workflows, which could be used to automate common activities like editing files and then running a test suite. I fully recommend reading [Issue 242 of The Batch](https://www.deeplearning.ai/the-batch/issue-242/) to understand the origin of workflows. They were originally [implemented](https://github.com/olimorris/codecompanion.nvim/commit/73e5a27075749b3ff60cfc796438d302d4b08715) in the plugin as an early form of [Chain-of-thought](https://en.wikipedia.org/wiki/Prompt_engineering#Chain-of-thought) prompting, via the use of reflection and planning prompts. ## Usage Workflows can only be initiated from the [Action Palette](/usage/action-palette). This is because they are a complex Lua table structure which needs to be processed and added to a new chat buffer. Simply open up the Action Palette and select your desired workflow. You can create your own workflows by following the [workflows](/configuration/prompt-library#workflows) configuration guide and the [agentic workflows](/extending/agentic-workflows) guide. --- --- url: /usage/ui.md description: >- Customize CodeCompanion's Neovim UI — configure chat buffer appearance, status line metadata, and use plugin events to build your own interface extensions. --- # User Interface CodeCompanion aims to keep any changes to the user's UI to a minimum. Aesthetics, especially in Neovim, are highly subjective. So whilst it won't set much by default, it does endeavour to allow users to hook into the plugin and customize the UI to their liking via [Events](/usage/events). ### Metadata CodeCompanion exposes a global dictionary, `_G.codecompanion_chat_metadata` which users can leverage throughout their configuration. Using the chat buffer's buffer number as the key, the dictionary contains: * `adapter` - The `type`, `name` and `model` of the chat buffer's current adapter * `context_items` - The number of context items current in the chat buffer * `cycles` - The number of cycles (User->LLM->User) that have taken place in the chat buffer * `id` - The ID of the chat buffer * `tokens` - The running total of tokens for the chat buffer * `tools` - The number of tools in the chat buffer You can also leverage `_G.codecompanion_current_context` to fetch the number of the buffer which the `#{buffer}` variable points at. The video at the top of this page shows how the author has incorporated the metadata into their statusline. ### Highlight Groups The plugin sets the following highlight groups during setup: * `CodeCompanionChatInfo` - Information messages in the chat buffer * `CodeCompanionChatError` - Error messages in the chat buffer * `CodeCompanionChatWarn` - Warning messages in the chat buffer * `CodeCompanionChatSubtext` - Messages that appear under the information, error or warning messages in the chat buffer * `CodeCompanionChatFold` - For any folds in the chat buffer (not including tool output) * `CodeCompanionChatHeader` - The headers in the chat buffer * `CodeCompanionChatSeparator` - Separator between headings in the chat buffer * `CodeCompanionChatTokens` - Virtual text in the chat buffer showing the token count * `CodeCompanionChatTool` - Tools in the chat buffer * `CodeCompanionChatToolGroups` - Tool groups in the chat buffer * `CodeCompanionChatToolText` - Tool output text in the chat buffer (overrides markdown rendering) * `CodeCompanionChatEditorContext` - Editor context in the chat buffer * `CodeCompanionCodeReviewComment` - Comments in code reviews * `CodeCompanionVirtualText` - All other virtual text in the plugin --- --- url: /extending/adapters.md description: >- Build a custom CodeCompanion HTTP adapter to connect Neovim to any LLM. Covers the adapter interface, request handlers, environment variables, and schema. --- # Extending with Adapters > \[!TIP] > Does your LLM state that it is "OpenAI Compatible"? If so, good news, you can extend from the `openai` adapter or use the `openai_compatible` one. Something we did with the [xAI](https://github.com/olimorris/codecompanion.nvim/blob/main/lua/codecompanion/adapters/http/xai.lua) adapter In CodeCompanion, adapters are interfaces that act as a bridge between the plugin's functionality and an LLM. All adapters must follow the interface, below. This guide is intended to serve as a reference for anyone who wishes to contribute an adapter to the plugin or understand the inner workings of existing adapters. The plugin's in-built adapters can be found in the [adapters source directory](https://github.com/olimorris/codecompanion.nvim/tree/main/lua/codecompanion/adapters). ## The Interface Let's take a look at the interface of an adapter as per the `adapter.lua` file: ```lua ---@class CodeCompanion.HTTPAdapter ---@field name string The name of the adapter e.g. "openai" ---@field formatted_name string The formatted name of the adapter e.g. "OpenAI" ---@field roles table The mapping of roles in the config to the LLM's defined roles ---@field url string The URL of the LLM to connect to ---@field env? table Environment variables which can be referenced in the parameters ---@field env_replaced? table Replacement of environment variables with their actual values ---@field headers table The headers to pass to the request ---@field parameters table The parameters to pass to the request ---@field body table Additional body parameters to pass to the request ---@field raw? table Any additional curl arguments to pass to the request ---@field opts? table Additional options for the adapter ---@field handlers CodeCompanion.HTTPAdapter.Handlers Functions which link the output from the request to CodeCompanion ---@field schema table Set of parameters for the LLM that the user can customise in the chat buffer ``` Everything up to the handlers should be self-explanatory. We're simply providing details of the LLM's API to the curl library and executing the request. The real intelligence of the adapter comes from the handlers table which is a set of functions which bridge the functionality of the plugin to the LLM. ## Handler Structure As of v17.27.0, handlers are organized into a nested structure that provides clear separation of concerns: ```lua handlers = { -- Lifecycle hooks (side effects and initialization) lifecycle = { setup = function(self) end, -- Called before request is sent on_exit = function(self, data) end, -- Called after request completes teardown = function(self) end, -- Called last, after on_exit }, -- Request builders (pure transformations) request = { build_parameters = function(self, params, messages) end, -- Build request parameters build_messages = function(self, messages) end, -- Format messages for LLM build_tools = function(self, tools) end, -- Transform tool schemas build_reasoning = function(self, messages) end, -- Build reasoning parameters build_body = function(self, data) end, -- Set additional body parameters }, -- Response parsers (pure transformations) response = { parse_chat = function(self, data, tools) end, -- Parse chat response parse_inline = function(self, data, context) end, -- Parse inline response parse_tokens = function(self, data) end, -- Extract token count }, -- Tool handlers (grouped functionality) tools = { format_calls = function(self, tools) end, -- Format tool calls for request format_response = function(self, tool_call, output) end, -- Format tool response for LLM }, } ``` > \[!NOTE] > **Backwards Compatibility**: The old flat handler structure is still supported. Adapters using the old format (e.g., `form_parameters`, `form_messages`, `chat_output`) will continue to work. The plugin automatically detects and maps old handler names to the new structure. ## Environment Variables When building an adapter, you'll need to inject variables into different parts of the adapter class. If we take the [Google Gemini](https://github.com/google-gemini/cookbook/blob/main/quickstarts/rest/Streaming_REST.ipynb) endpoint as an example, we need to inject the model and API key variables into the URL of `https://generativelanguage.googleapis.com/v1beta/models/${model}:streamGenerateContent?alt=sse&key=${api_key}`. Whereas with [OpenAI](https://platform.openai.com/docs/api-reference/authentication), we need an `Authorization` http header to contain our API key. Let's take a look at the `env` table from the Google Gemini adapter that comes with the plugin: ```lua url = "https://generativelanguage.googleapis.com/v1beta/models/${model}:streamGenerateContent?alt=sse&key=${api_key}", env = { api_key = "GEMINI_API_KEY", model = "schema.model.default", }, ``` The key `api_key` represents the name of the variable which can be injected in the adapter via the `${}` notation, and the value can represent one of: * A command to execute on the user's system * An environment variable from the user's system * A function to be executed at runtime * A path to an item in the adapter's schema table * A plain text value > \[!NOTE] > Environment variables can be injected into the `url`, `headers` and `parameters` fields of the adapter class at runtime **Commands** An environment variable can be obtained from running a command on a user's system. This can be accomplished by prefixing the value with `cmd:` such as: ```lua env = { api_key = "cmd:op read op://personal/Gemini_API/credential --no-newline", }, ``` In this example, we're running the `op read` command to get a credential from 1Password. **Environment Variable** An environment variable can also be obtained by using lua's `os.getenv` function. Simply enter the name of the variable as a string such as: ```lua env = { api_key = "GEMINI_API_KEY", }, ``` **Functions** An environment variable can also be resolved via the use of a function such as: ```lua env = { api_key = function() return os.getenv("GEMINI_API_KEY") end, }, ``` **Schema Values** An environment variable can also be resolved by entering the path to a value in a table on the adapter class. For example: ```lua env = { model = "schema.model.default", }, ``` In this example, we're getting the value of a user's chosen model from the schema table on the adapter. ## Handlers The handlers table is organized into four main categories: ### Lifecycle Handlers These handlers manage side effects and initialization: * `lifecycle.setup` - Called before the request is sent and before environment variables are set. Must return a boolean to indicate success * `lifecycle.on_exit` - Called after the request completes. Useful for handling errors * `lifecycle.teardown` - Called last, after `on_exit` ### Request Handlers These handlers transform data for the LLM request: * `request.build_parameters` - Set the parameters of the request * `request.build_messages` - Format the messages array for the LLM * `request.build_tools` - Transform tool schemas for the LLM * `request.build_reasoning` - Build reasoning parameters (for models that support it) * `request.build_body` - Set additional body parameters ### Response Handlers These handlers parse LLM responses: * `response.parse_chat` - Format chat output for the chat buffer * `response.parse_inline` - Format output for inline insertion * `response.parse_tokens` - Extract token count from the response * `response.parse_meta` - Process non-standard fields in the response (currently only supported by OpenAI-based adapters) ### Tool Handlers These handlers manage tool/function calling: * `tools.format_calls` - Format tool calls for inclusion in the request * `tools.format_response` - Format tool responses for the LLM > \[!TIP] > All of the adapters in the plugin come with their own tests. These serve as a great reference to understand how they're working with the output of the API ### OpenAI's API Output If we reference the OpenAI [documentation](https://platform.openai.com/docs/guides/text-generation/chat-completions-api) we can see that they require the messages to be in an array which consists of `role` and `content`: ```sh curl https://api.openai.com/v1/chat/completions \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $OPENAI_API_KEY" \ -d '{ "model": "gpt-4-0125-preview", "messages": [ { "role": "user", "content": "Explain Ruby in two words" } ] }' ``` ### Chat Buffer Output The chat buffer, which is structured like: ```markdown ## Me Explain Ruby in two words ``` results in the following output: ```lua { { role = "user", content = "Explain Ruby in two words" } } ``` ### `request.build_messages` The chat buffer's output is passed to this handler in the form of the `messages` parameter. So we can just output this as part of a messages table: ```lua handlers = { request = { build_messages = function(self, messages) return { messages = messages } end, }, } ``` ### `response.parse_chat` Now let's look at how we format the output from OpenAI. Running that request results in: ```txt data: {"id":"chatcmpl-90DdmqMKOKpqFemxX0OhTVdH042gu","object":"chat.completion.chunk","created":1709839462,"model":"gpt-4-0125-preview","system_fingerprint":"fp_70b2088885","choices":[{"index":0,"delta":{"role":"assistant","content":""},"logprobs":null,"finish_reason":null}]} ``` ```txt data: {"id":"chatcmpl-90DdmqMKOKpqFemxX0OhTVdH042gu","object":"chat.completion.chunk","created":1709839462,"model":"gpt-4-0125-preview","system_fingerprint":"fp_70b2088885","choices":[{"index":0,"delta":{"content":"Programming"},"logprobs":null,"finish_reason":null}]} ``` ```txt data: {"id":"chatcmpl-90DdmqMKOKpqFemxX0OhTVdH042gu","object":"chat.completion.chunk","created":1709839462,"model":"gpt-4-0125-preview","system_fingerprint":"fp_70b2088885","choices":[{"index":0,"delta":{"content":" language"},"logprobs":null,"finish_reason":null}]}, ``` ```txt data: [DONE] ``` > \[!IMPORTANT] > Note that the `parse_chat` handler requires a table containing `status` and `output` to be returned. Remember that we're streaming from the API so the request comes through in batches. Thankfully the `http.lua` file handles this and we just have to handle formatting the output into the chat buffer. The first thing to note with streaming endpoints is that they don't return valid JSON. In this case, the output is prefixed with `data: `. CodeCompanion comes with some handy utility functions to work with this: ```lua -- Put this at the top of your adapter local utils = require("codecompanion.adapters.utils") handlers = { response = { parse_chat = function(self, data) data = utils.clean_streamed_data(data) end, }, } ``` > \[!IMPORTANT] > The data passed to the `parse_chat` handler is the response from OpenAI We can then decode the JSON using native vim functions: ```lua handlers = { response = { parse_chat = function(self, data) data = utils.clean_streamed_data(data) local ok, json = pcall(vim.json.decode, data, { luanil = { object = true } }) end, }, } ``` We want to include any nil values so we pass in `luanil = { object = true }`. Examining the output of the API, we see that the streamed data is stored in a `choices[1].delta` table. That's easy to pickup: ```lua handlers = { response = { parse_chat = function(self, data) --- local delta = json.choices[1].delta end, }, } ``` and we can then access the new streamed data that we want to write into the chat buffer, with: ```lua handlers = { response = { parse_chat = function(self, data) local output = {} --- local delta = json.choices[1].delta if delta.content then output.content = delta.content output.role = delta.role or nil end end, }, } ``` And then we can return the output in the following format: ```lua handlers = { response = { parse_chat = function(self, data) -- return { status = "success", output = output, } end, }, } ``` Now if we put it all together, and put some checks in place to make sure that we have data in our response: ```lua handlers = { response = { parse_chat = function(self, data) local output = {} if data and data ~= "" then data = utils.clean_streamed_data(data) local ok, json = pcall(vim.json.decode, data, { luanil = { object = true } }) local delta = json.choices[1].delta if delta.content then output.content = delta.content output.role = delta.role or nil return { status = "success", output = output, } end end end, }, } ``` ### `response.parse_meta` Some OpenAI-compatible API providers like deepseek, Gemini and OpenRouter implement a superset of the standard specification, and provide reasoning tokens/summaries within their response. The non-standard fields in the [`message` (non-streaming)](https://platform.openai.com/docs/api-reference/chat/object#chat-object-choices-message) or [`delta` (streaming)](https://platform.openai.com/docs/api-reference/chat-streaming/streaming#chat_streaming-streaming-choices-delta) object are captured by the OpenAI adapter and can be used to extract the reasoning. For example, the DeepSeek API provides the reasoning tokens in the `delta.reasoning_content` field. We can therefore use the following `parse_meta` handler to extract the reasoning tokens and put them into the appropriate output fields: ```lua handlers = { response = { ---@param self CodeCompanion.HTTPAdapter --- `data` is the output of the `parse_chat` handler ---@param data {status: string, output: {role: string?, content: string?}, extra: table} ---@return {status: string, output: {role: string?, content: string?, reasoning:{content: string?}?}} parse_meta = function(self, data) local extra = data.extra if extra.reasoning_content then -- codecompanion expect the reasoning tokens in this format data.output.reasoning = { content = extra.reasoning_content } -- so that codecompanion doesn't mistake this as a normal response with empty string as the content if data.output.content == "" then data.output.content = nil end end return data end } } ``` Notes: 1. You don't always have to set `data.output.content` to `nil`. This is mostly intended for `streaming`, and you may encounter issues in non-stream mode if you do that. 2. It's expected that the processed `data` table is returned at the end. 3. For adapters that are using the legacy flat handler formats, this handler should be named `handlers.parse_message_meta`. The function signature stays the same. ### `request.build_parameters` For the purposes of the OpenAI adapter, no additional parameters need to be created. So we just pass this through: ```lua handlers = { request = { build_parameters = function(self, params, messages) return params end, }, } ``` ### `response.parse_inline` From a design perspective, the inline interaction is very similar to the chat interaction. With the `parse_inline` handler we simply return the content we wish to be streamed into the buffer. In the case of OpenAI, once we've checked the data we have back from the LLM and parsed it as JSON, we simply need to: ```lua ---Output the data from the API ready for inlining into the current buffer ---@param self CodeCompanion.HTTPAdapter ---@param data table The streamed JSON data from the API ---@param context table Useful context about the buffer to inline to ---@return string|table|nil handlers = { response = { parse_inline = function(self, data, context) -- Data cleansed, parsed and validated -- .. local content = json.choices[1].delta.content if content then return content end end, }, } ``` The `parse_inline` handler also receives context from the buffer that initiated the request. ### `lifecycle.on_exit` Handling errors from a streaming endpoint can be challenging. It's recommended that any errors are managed in the `on_exit` handler which is initiated when the response has completed. In the case of OpenAI, if there is an error, we'll see a response back from the API like: ```sh data: { data: "error": { data: "message": "Incorrect API key provided: 1sk-F18b****************************************XdwS. You can find your API key at https://platform.openai.com/account/api-keys.", data: "type": "invalid_request_error", data: "param": null, data: "code": "invalid_api_key" data: } data: } ``` This would be challenging to parse! Thankfully we can leverage the `on_exit` handler which receives the final payload, resembling: ```lua { body = '{\n "error": {\n "message": "Incorrect API key provided: 1sk-F18b****************************************XdwS. You can find your API key at https://platform.openai.com/account/api-keys.",\n "type": "invalid_request_error",\n "param": null,\n "code": "invalid_api_key"\n }\n}', exit = 0, headers = { "date: Thu, 03 Oct 2024 08:05:32 GMT" }, status = 401 } ``` and that's much easier to work with: ```lua ---Function to run when the request has completed. Useful to catch errors ---@param self CodeCompanion.HTTPAdapter ---@param data table ---@return nil handlers = { lifecycle = { on_exit = function(self, data) if data.status >= 400 then log:error("Error: %s", data.body) end end, }, } ``` The `log:error` call ensures that any errors are logged to the logfile as well as displayed to the user in Neovim. It's also important to reference that the `parse_chat` and `parse_inline` handlers need to be able to ignore any errors from the API and let `on_exit` handle them. ### `lifecycle.setup` and `lifecycle.teardown` The `setup` handler will execute before the request is sent to the LLM's endpoint and before the environment variables have been set. This is leveraged in the Copilot adapter to obtain the token before it's resolved as part of the environment variables table. The `setup` handler **must** return a boolean value so the `http.lua` file can determine whether to proceed with the request. The `teardown` handler will execute once the request has completed and after `on_exit`. Example: ```lua handlers = { lifecycle = { setup = function(self) -- Perform initialization return true -- Must return boolean end, teardown = function(self) -- Clean up resources end, }, } ``` ### The Utility File A lot of LLM endpoints claim to be "OpenAI Compatible" yet have odd quirks which prevent you from using the OpenAI Adapter. Common issues can be: * System messages have to be the first message (`anthropic`, `deepseek`) * System messages have to be one message (`anthropic`, `deepseek`) * Messages must follow a `User -> LLM -> User -> LLM` turn based flow (`deepseek`) To address this, an [adapter utilities](https://github.com/olimorris/codecompanion.nvim/blob/main/lua/codecompanion/adapters/utils/init.lua) file has been created that you can leverage in building or extending your own adapters. Finally, always refer to the pre-built adapters as a reference point. ## Schema The schema table describes the settings/parameters for the LLM. If the user has `display.chat.show_settings = true` then this table will be exposed at the top of the chat buffer. We'll explore some of the options in the Copilot adapter's schema table: ```lua schema = { model = { order = 1, mapping = "parameters", type = "enum", desc = "ID of the model to use. See the model endpoint compatibility table for details on which models work with the Chat API.", ---@type string|fun(): string default = "gpt-4o-2024-08-06", choices = { ["o3-mini-2025-01-31"] = { opts = { can_reason = true } }, ["o1-2024-12-17"] = { opts = { can_reason = true } }, ["o1-mini-2024-09-12"] = { opts = { can_reason = true } }, "claude-3.5-sonnet", "claude-3.7-sonnet", "claude-3.7-sonnet-thought", "gpt-4o-2024-08-06", "gemini-2.0-flash-001", }, }, } ``` The model key sets out the specific model which is to be used to interact with the Copilot endpoint. We've listed the default, in this example, as `gpt-4o-2024-08-06` but we allow the user to choose from a possible five options, via the `choices` key. We've given this an order value of `1` so that it's always displayed at the top of the chat buffer. We've also given it a useful description as this is used in the virtual text when a user hovers over it. Finally, we've specified that it has a mapping property of `parameters`. This tells the adapter that we wish to map this model key to the parameters part of the HTTP request. You'll also notice that some of the models have a table attached to them. This can be useful if you need to do conditional logic in any of the handler methods at runtime. Let's take a look at one more schema value: ```lua temperature = { order = 2, mapping = "parameters", type = "number", default = 0, ---@param self CodeCompanion.HTTPAdapter enabled = function(self) local model = self.schema.model.default if type(model) == "function" then model = model() end return not vim.startswith(model, "o1") end, -- This isn't in the Copilot adapter but it's useful to reference! validate = function(n) return n >= 0 and n <= 2, "Must be between 0 and 2" end, desc = "What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. We generally recommend altering this or top_p but not both.", }, ``` You'll see we've specified a function call for the `enabled` key. We're simply checking that the model name doesn't start with `o1` as these models don't accept temperature as a parameter. You'll also see we've specified a function call for the `validate` key. We're simply checking that the value of the temperature is between 0 and 2. For some endpoints, like OpenAI's [Responses API](https://platform.openai.com/docs/api-reference/responses/create?api-mode=responses), schema values may need to be nested in the parameters: ```bash curl https://api.openai.com/v1/responses \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $OPENAI_API_KEY" \ -d '{ "model": "o3-mini", "input": "How much wood would a woodchuck chuck?", "reasoning": { "effort": "high" } }' ``` To accomplish this, you can use dot notation: ```lua ["reasoning.effort"] = { mapping = "parameters", type = "string", -- ... }, ``` ## Function Calling / Tool Use In order to enable your adapter to make use of [Function Calling](https://platform.openai.com/docs/guides/function-calling?api-mode=chat), you need to setup some additional handlers: * `request.build_tools` - which transforms the tools provided by CodeCompanion into a schema supported by the adapter * `tools.format_calls` - which [formats](https://platform.openai.com/docs/guides/function-calling?api-mode=chat#handling-function-calls) the adapters tool calls and puts them into the http request * `tools.format_response` - which formats and outputs the adapter's tool call so it can be included in the chat buffer's messages stack You will also need to ensure that `opts.tools = true` and the `parse_chat` handler has tools included as an optional final parameter like `parse_chat = function(self, data, tools)`. From experience, whilst many LLMs claim to support the OpenAI API standard for function calling, they can require some additional configuration to work as expected. Example: ```lua handlers = { request = { build_tools = function(self, tools) if not self.opts.tools or not tools then return end -- Transform tools into LLM's expected format return { tools = transformed_tools } end, }, tools = { format_calls = function(self, tools) -- Format tool calls for the request return formatted_calls end, format_response = function(self, tool_call, output) -- Format tool response for LLM return { role = self.roles.tool or "tool", tools = { call_id = tool_call.id, }, content = output, opts = { visible = false }, } end, }, } ``` ## Migrating from Old Handler Format If you have an existing adapter using the old flat handler structure, it will continue to work without changes. However, to migrate to the new nested structure for better organization: **Old format:** ```lua handlers = { setup = function(self) end, form_parameters = function(self, params, messages) end, form_messages = function(self, messages) end, chat_output = function(self, data, tools) end, inline_output = function(self, data, context) end, on_exit = function(self, data) end, teardown = function(self) end, tools = { format_tool_calls = function(self, tools) end, output_response = function(self, tool_call, output) end, }, } ``` **New format:** ```lua handlers = { lifecycle = { setup = function(self) end, on_exit = function(self, data) end, teardown = function(self) end, }, request = { build_parameters = function(self, params, messages) end, build_messages = function(self, messages) end, }, response = { parse_chat = function(self, data, tools) end, parse_inline = function(self, data, context) end, }, tools = { format_calls = function(self, tools) end, format_response = function(self, tool_call, output) end, }, } ``` --- --- url: /extending/agentic-workflows.md description: >- Build agentic workflows in CodeCompanion — chain LLM prompts with tool calls to automate multi-step tasks like editing files and running your test suite. --- # Extending with Agentic Workflows ## How They Work Before showcasing some examples, it's important to understand how workflows have been implemented in the plugin. When initiated from the [Action Palette](/usage/action-palette), workflows attach themselves to a [chat buffer](/usage/chat-buffer/) via the notion of a *subscription*. That is, the workflow has subscribed to the conversation and dataflow that's taking place in the chat buffer. After the LLM sends a response, the chat buffer will trigger an event on the subscription class. This will execute a callback which has been defined in the workflow itself (often times this is simply a text prompt), and the event will duly be deleted from the subscription to prevent it from being executed again. ## Creating Agentic Workflows By combining a workflow with tools, we can use an LLM to act as an Agent and do some impressive things! A great example of that is the `Edit<->Test` workflow that originally came with the plugin. This workflow asked the LLM to edit code in a buffer and then run a test suite, feeding the output back to the LLM to then make future edits if required. ::: details The full `Edit<->Test` workflow code can be found below: ```lua require("codecompanion").setup({ prompt_library = { ["Edit<->Test workflow"] = { strategy = "workflow", description = "Use a workflow to repeatedly edit then test code", opts = { index = 5, is_default = true, short_name = "et", }, prompts = { { { name = "Setup Test", role = "user", opts = { auto_submit = false }, content = function() -- Leverage YOLO mode which disables the requirement of approvals and automatically saves any edited buffer local approvals = require("codecompanion.interactions.chat.tools.approvals") approvals:toggle_yolo_mode() return [[### Instructions Your instructions here ### Steps to Follow You are required to write code following the instructions provided above and test the correctness by running the designated test suite. Follow these steps exactly: 1. Update the code in #{buffer} using the @{insert_edit_into_file} tool 2. Then use the @{run_command} tool to run the test suite with `` (do this after you have updated the code) 3. Make sure you trigger both tools in the same response We'll repeat this cycle until the tests pass. Ensure no deviations from these steps.]] end, }, }, { { name = "Repeat On Failure", role = "user", opts = { auto_submit = true }, -- Scope this prompt to the run_command tool condition = function() return _G.codecompanion_current_tool == "run_command" end, -- Repeat until the tests pass, as indicated by the testing flag -- which the run_command tool sets on the chat buffer repeat_until = function(chat) return chat.tool_registry.flags.testing == true end, content = "The tests have failed. Can you edit the buffer and run the test suite again?", }, }, }, }, }, }) ``` ::: Let's breakdown the prompts in that workflow: ```lua prompts = { { { name = "Setup Test", role = "user", opts = { auto_submit = false }, content = function() -- Leverage YOLO mode which disables the requirement of approvals and automatically saves any edited buffer local approvals = require("codecompanion.interactions.chat.tools.approvals") approvals:toggle_yolo_mode() -- Some clear instructions for the LLM to follow return [[### Instructions Your instructions here ### Steps to Follow You are required to write code following the instructions provided above and test the correctness by running the designated test suite. Follow these steps exactly: 1. Update the code in #{buffer}{watch} using the @{insert_edit_into_file} tool 2. Then use the @{run_command} tool to run the test suite with `` (do this after you have updated the code) 3. Make sure you trigger both tools in the same response We'll repeat this cycle until the tests pass. Ensure no deviations from these steps.]] end, }, }, --- Prompts to be continued ... }, ``` The first prompt in a workflow should set the ask of the LLM and provide clear instructions. In this case, we're giving the LLM access to the [@insert\_edit\_into\_file](/usage/chat-buffer/agents-tools#files) and [@run\_command](/usage/chat-buffer/agents-tools#run-command) tools to edit a buffer and run tests, respectively. We're giving the LLM knowledge of the buffer with the `#buffer` editor context and also telling CodeCompanion to watch it for any changes with the `{watch}` parameter. Prior to sending a response to the LLM, the plugin will share any changes to that buffer, keeping the LLM updated. Now let's look at how we trigger the automated reflection prompts: ```lua { { --- Prompts continued... { { name = "Repeat On Failure", role = "user", opts = { auto_submit = true }, -- Scope this prompt to only run when the run_command tool is active condition = function(chat) return chat.tools.tool and chat.tools.tool.name == "run_command" end, -- Repeat until the tests pass, as indicated by the testing flag repeat_until = function(chat) return chat.tool_registry.flags.testing == true end, content = "The tests have failed. Can you edit the buffer and run the test suite again?", }, }, }, }, ``` Now there's a little bit more to unpack in this prompt. Firstly, we're automatically submitting the prompt to the LLM to save the user some time and keypresses. Next, we're scoping the prompt to only be sent to the chat buffer if the currently active tool is the [@run\_command](/usage/chat-buffer/agents-tools#run-command). We're also leveraging a function called `repeat_until`. This ensures that the prompt is always attached to the chat buffer until a condition is met. In this case, until the tests pass. In the [@run\_command](/usage/chat-buffer/agents-tools#run-command) tool, we ask the LLM to pass a flag if it detects a test suite is being run. The plugin picks up on that flag and puts the test outcome into the chat buffer class as a flag. Finally, we're letting the LLM know that the tests failed, and asking it to fix. --- --- url: /extending/extensions.md description: >- Create a CodeCompanion extension to add custom functionality to the plugin — distributable as a Neovim plugin or defined locally in your configuration. --- # Extending with Extensions CodeCompanion supports extensions similar to telescope.nvim, allowing users to create functionality that can be shared with others. Extensions can either be distributed as plugins or defined locally in your configuration. ## Using Extensions Extensions are configured in your CodeCompanion setup: ```lua -- Install the extension { "olimorris/codecompanion.nvim", dependencies = { "ravitemer/codecompanion-history.nvim" -- history extension } } -- Configure in your setup require("codecompanion").setup({ extensions = { history = { enabled = true, -- defaults to true opts = { dir_to_save = vim.fn.stdpath("data") .. "/codecompanion_chats.json", } } } }) ``` ## Creating Extensions Extensions are typically distributed as plugins. Create a new plugin with the following structure: ``` your-extension/ ├── lua/ │ └── codecompanion/ │ └── _extensions/ │ └── your_extension/ │ └── init.lua -- Main extension file └── README.md ``` The init.lua file should export a module that provides setup and optional exports: ```lua ---@class CodeCompanion.Extension ---@field setup fun(opts: table) Function called when extension is loaded ---@field exports? table Functions exposed via codecompanion.extensions.your_extension local Extension = {} ---Setup the extension ---@param opts table Configuration options function Extension.setup(opts) -- Initialize extension -- Add actions, keymaps etc. end -- Optional: Functions exposed via codecompanion.extensions.your_extension Extension.exports = { clear_history = function() end } return Extension ``` ### Extending Chat Functionality A common pattern is to add keymaps, slash\_commands, tools to the codecompanion.config object inside setup function. ```lua ---This is called on codecompanion setup. ---You can access config via require("codecompanion.config") and chat via require("codecompanion.chat").last_chat() etc function Extension.setup(opts) -- Add action to chat keymaps local chat_keymaps = require("codecompanion.config").interactions.chat.keymaps chat_keymaps.open_saved_chats = { modes = { n = opts.keymap or "gh", }, description = "Open Saved Chats", callback = function(chat) -- Implementation of opening saved chats vim.notify("Opening saved chats for " .. chat.id) end } end ``` Once configured, extension exports are accessible via: ```lua local codecompanion = require("codecompanion") -- Use exported functions codecompanion.extensions.codecompanion_history.clear_history() ``` ## Local Extensions Extensions can also be defined directly in your configuration for simpler use cases: ```lua -- Example: Adding a message editor extension require("codecompanion").setup({ extensions = { editor = { enabled = true, opts = {}, callback = { setup = function(ext_config) -- Add a new action to chat keymaps local open_editor = { modes = { n = "ge", -- Keymap to open editor }, description = "Open Editor", callback = function(chat) -- Implementation of editor opening logic -- You have access to the chat buffer via the chat parameter vim.notify("Editor opened for chat " .. chat.id) end, } -- Add the action to chat keymaps config local chat_keymaps = require("codecompanion.config").interactions.chat.keymaps chat_keymaps.open_editor = open_editor end, -- Optional: Expose functions exports = { is_editor_open = function() return false -- Implementation end } } } } }) ``` The callback can be: * A function returning the extension table * The extension table directly * A string path to a module that returns the extension ## Dynamic registration Extensions can also be added dynamically using ```lua require("codecompanion").register_extension("codecompanion_history", { callback = { setup = function() end, exports = {} }, }) ``` ## Best Practices 1. **Namespacing**: * Use unique names for extensions to avoid conflicts * Prefix functions and variables appropriately 2. **Configuration**: * Provide sensible defaults * Allow customization via opts table * Document all options 3. **Integration**: * Follow CodeCompanion's patterns for actions and tools * Use existing utilities like keymaps.set\_keymap * Handle errors appropriately 4. **Documentation**: * Document installation process * List all available options * Provide usage examples --- --- url: /extending/parsers.md description: >- Create custom rules parsers in CodeCompanion to post-process and transform rules file content before it's shared with an LLM. --- # Extending with Rules Parsers In CodeCompanion, parsers act on the contents of a rules file, carrying out some post-processing activities and returning the content back to the rules class. Parsers serve as an excellent way to apply modifications and extract metadata prior to sharing them with an LLM. ## Structure of a Parser A parser has limited restrictions. It is simply required to return a function that the *rules* class can execute, passing in the file to be processed as a parameter: ```lua ---@class CodeCompanion.Chat.Rules.Parser ---@field content string The content of the rules file ---@field meta? { included_files: string[] } The filename of the rules file ---@param file CodeCompanion.Chat.Rules.ProcessedFile ---@return CodeCompanion.Chat.Rules.Parser return function(file) -- Your logic end ``` As an output, the function must return a table containing a `content` key. ## Processing Files Parsers may also return a list of files to be shared with the LLM by the *rules* class. To enable this, ensure that the parser returns a `meta.included_files` array in its output: ```lua { content = "Your parsed content", meta = { included_files = { ".codecompanion/acp/acp_json_schema.json", "./lua/codecompanion/acp/init.lua", "./lua/codecompanion/adapters/acp/claude_code.lua", "./lua/codecompanion/adapters/acp/helpers.lua", "./lua/codecompanion/acp/prompt_builder.lua", "./lua/codecompanion/interactions/chat/acp/handler.lua", "./lua/codecompanion/interactions/chat/acp/request_permission.lua", }, }, } ``` --- --- url: /extending/tools.md description: >- Build custom CodeCompanion tools to let LLMs execute functions in Neovim — covers tool structure, OpenAI-compatible schemas, handlers, and agent group integration. --- # Extending with Tools In CodeCompanion, tools offer pre-defined ways for LLMs to call functions on your machine, acting as an Agent in the process. This guide walks you through the implementation of tools, enabling you to create your own. In the plugin, tools are a Lua table, consisting of various handler and output functions, alongside a system prompt and an [OpenAI compatible schema](https://platform.openai.com/docs/guides/function-calling?api-mode=chat). When you add a tool to the chat buffer, this gives the LLM the knowledge to be able to call the tool, when required. Once called, the plugin will parse the LLM's response and execute the tool accordingly, before sharing the output in the chat buffer. ## Architecture In order to create tools, you do not need to understand the underlying architecture. However, for those who are curious about the implementation, please see the diagram below: ```mermaid sequenceDiagram participant C as Chat Buffer participant L as LLM participant TS as Tool System participant O as Orchestrator participant T as Tool C->>L: Prompt with tool schemas L->>C: Response with tool call(s) C->>TS: Parse LLM response loop For each tool call detected TS->>TS: Tool.resolve(tool_config) TS->>TS: Add tool to queue end TS->>O: Create Orchestrator with queue TS->>C: Fire "ToolsStarted" autocmd loop While queue not empty O->>O: Pop tool from queue O->>O: Setup handlers and output functions O->>T: handlers.setup() Note over O,C: If approval required, prompt user O->>C: User approval (if needed) Note over O,T: If rejected/cancelled, call output handlers and continue Note over O,T: If approved or no approval needed, execute tool loop For each cmd in tool.cmds O->>T: Execute function(self, args, opts) Note over T,O: Returns {status, data} (sync) or calls opts.output_cb (async) O->>T: output.success() OR output.error() T->>C: add_tool_output() end O->>T: handlers.on_exit() end TS->>TS: reset() TS->>C: Fire "ToolsFinished" autocmd TS->>C: tools_done() ``` ## Building Your First Tool Before we begin, it's important to familiarise yourself with the directory structure of the tools implementation: ``` interactions/chat/tools ├── init.lua ├── orchestrator.lua ├── runtime/ │ ├── queue.lua │ ├── runner.lua ├── builtin/ │ ├── run_command.lua │ ├── insert_edit_into_file.lua │ ├── create_file.lua │ ├── ... ``` When a tool is detected, the chat buffer sends any output to the `tools/init.lua` file (I will commonly refer to that as the *"tool system file"* throughout this document). The tool system file then parses the response from the LLM, identifying the tool and duly executing it. There are two types of tools that CodeCompanion can leverage: 1. **Command-based**: These tools can execute a series of commands in the background using `vim.system`. They're non-blocking, meaning you can carry out other activities in Neovim whilst they run. Useful for heavy/time-consuming tasks. 2. **Function-based**: These tools, like [insert\_edit\_into\_file](https://github.com/olimorris/codecompanion.nvim/blob/main/lua/codecompanion/interactions/chat/tools/builtin/insert_edit_into_file/init.lua), execute Lua functions directly in Neovim within the main process, one after another. They can also be executed asynchronously. For the purposes of this section of the guide, we'll be building a simple function-based calculator tool that an LLM can use to do basic maths. ### Tool Structure All tools must implement the following structure which the bulk of this guide will focus on explaining: ```lua ---@class CodeCompanion.Tools.Tool ---@field name string The name of the tool ---@field cmds table The commands to execute ---@field function_call table The function call from the LLM ---@field schema table The schema that the LLM must use in its response to execute a tool ---@field system_prompt string | fun(schema: table): string The system prompt to the LLM explaining the tool and the schema ---@field opts? table The options for the tool ---@field env? fun(schema: table): table|nil Any environment variables that can be used in the *_cmd fields. Receives the parsed schema from the LLM ---@field handlers table Functions which handle the execution of a tool ---@field handlers.setup? fun(self: CodeCompanion.Tools.Tool, meta: { tools: CodeCompanion.Tools }): any Function used to setup the tool. Called before any commands ---@field handlers.prompt_condition? fun(self: CodeCompanion.Tools.Tool, meta: { tools: CodeCompanion.Tools }): boolean Function to determine whether to show the prompt to the user or not ---@field handlers.on_exit? fun(self: CodeCompanion.Tools.Tool, meta: { tools: CodeCompanion.Tools }): any Function to call at the end of a group of commands or functions ---@field output? table Functions which handle the output after every execution of a tool ---@field output.prompt fun(self: CodeCompanion.Tools.Tool, meta: { tools: CodeCompanion.Tools }): string The message which is shared with the user when asking for their approval ---@field output.rejected? fun(self: CodeCompanion.Tools.Tool, meta: { tools: CodeCompanion.Tools, cmd: table, opts: table }): any Function to call if the user rejects running a command ---@field output.error? fun(self: CodeCompanion.Tools.Tool, stderr: table, meta: { tools: CodeCompanion.Tools, cmd: table }): any The function to call if an error occurs ---@field output.success? fun(self: CodeCompanion.Tools.Tool, stdout: table, meta: { tools: CodeCompanion.Tools, cmd: table }): any Function to call if the tool is successful ---@field output.cancelled? fun(self: CodeCompanion.Tools.Tool, meta: { tools: CodeCompanion.Tools, cmd: table }): any Function to call if the tool is cancelled ---@field args table The arguments sent over by the LLM when making the request ---@field tool table The tool configuration from the config file ``` ### `cmds` **Command-Based Tools** The `cmds` table is a collection of commands which the tool system will execute one after another, asynchronously, using `vim.system`. ```lua cmds = { { "make", "test" }, { "echo", "hello" }, } ``` In this example, the plugin will execute `make test` followed by `echo hello`. After each command executes, the plugin will automatically send the output to a corresponding table on the tool system file. If the command ran with success the output will be written to `stdout`, otherwise it will go to `stderr`. We'll be covering how you access that data in the output section below. It's also possible to pass in environment variables (from the `env` function) by use of ${} brackets. The now removed *@code\_runner* tool used them as below: ```lua cmds = { { "docker", "pull", "${lang}" }, { "docker", "run", "--rm", "-v", "${temp_dir}:${temp_dir}", "${lang}", "${lang}", "${temp_input}", }, }, }, ---@param self CodeCompanion.Tools.Tool ---@return table env = function(self) local temp_input = vim.fn.tempname() local temp_dir = temp_input:match("(.*/)") local lang = self.args.lang local code = self.args.code return { code = code, lang = lang, temp_dir = temp_dir, temp_input = temp_input, } end, ``` > \[!IMPORTANT] > Using the `handlers.setup()` function, it's also possible to create commands dynamically like in the [run\_command](https://github.com/olimorris/codecompanion.nvim/blob/main/lua/codecompanion/interactions/chat/tools/builtin/run_command.lua) tool. **Function-based Tools** Function-based tools use the `cmds` table to define functions that will be executed one after another. Each function receives three parameters: `self`, the arguments from the LLM, and an `opts` table containing `input` (output from a previous function call) and `output_cb` (callback for async execution). For a synchronous tool (like the calculator) you can ignore `opts`. For the purpose of our calculator example: ```lua cmds = { ---@param self CodeCompanion.Tool.Calculator The Calculator tool ---@param args table The arguments from the LLM's tool call ---@param opts { input: any, output_cb: fun(result: table) } ---@return nil|{ status: "success"|"error", data: string } function(self, args, opts) -- Get the numbers and operation requested by the LLM local num1 = tonumber(args.num1) local num2 = tonumber(args.num2) local operation = args.operation -- Validate input if not num1 then return { status = "error", data = "First number is missing or invalid" } end if not num2 then return { status = "error", data = "Second number is missing or invalid" } end if not operation then return { status = "error", data = "Operation is missing" } end -- Perform the calculation local result if operation == "add" then result = num1 + num2 elseif operation == "subtract" then result = num1 - num2 elseif operation == "multiply" then result = num1 * num2 elseif operation == "divide" then if num2 == 0 then return { status = "error", data = "Cannot divide by zero" } end result = num1 / num2 else return { status = "error", data = "Invalid operation: must be add, subtract, multiply, or divide" } end return { status = "success", data = result } end, }, ``` For a synchronous tool, you only need to `return` the result table as demonstrated. However, if you need to invoke some asynchronous actions in the tool, you can use `opts.output_cb` to submit any results to the orchestrator, which will then invoke `output` functions to handle the results: ```lua cmds = { function(self, args, opts) local cb = opts.output_cb -- This is for demonstration only vim.lsp.client.request(lsp_method, lsp_param, function(err, result, _, _) self.tools.chat:add_message({ role = "user", content = vim.json.encode(result) }) cb({ status = "success", data = result }) end, buf_nr) end } ``` Note that: 1. The `opts.output_cb` callback will be called only once. Subsequent calls will be discarded; 2. A tool function should EITHER return the result table (synchronous), OR call `opts.output_cb` with the result table as the only argument (asynchronous), but not both. If a function tries to both return the result and call `opts.output_cb`, the result will be undefined because there's no guarantee which output will be handled first. Similarly with command-based tools, the output is written to the `stdout` or `stderr` tables on the tool system file. However, with function-based tools, the user must manually specify the outcome of the execution which in turn redirects the output to the correct table: ```lua return { status = "error", data = "Invalid operation: must be add, subtract, multiply, or divide" } ``` Will cause execution of the tool to stop and populate `stderr` on the tool system file. ```lua return { status = "success", data = result } ``` Will populate the `stdout` table on the tool system file and allow for execution to continue. ### `schema` The function call that the LLM has sent, is parsed and sent to the `args` parameter of any function you've created in [cmds](/extending/tools#cmds), as a JSON object which is then converted to Lua via `vim.json.decode`. If the LLM has done its job correctly, the Lua table should be the representation of what you've described in the schema. In summary, the schema represents the structure of the response that the LLM must follow in order to call the tool. For a tool to function correctly, your tool requires an [OpenAI compatible](https://platform.openai.com/docs/guides/function-calling?api-mode=chat) schema. For our basic calculator tool, which does an operation on two numbers, the schema could look something like: ```lua schema = { type = "function", ["function"] = { name = "calculator", description = "Perform simple mathematical operations on a user's machine", parameters = { type = "object", properties = { num1 = { type = "integer", description = "The first number in the calculation", }, num2 = { type = "integer", description = "The second number in the calculation", }, operation = { type = "string", enum = { "add", "subtract", "multiply", "divide" }, description = "The mathematical operation to perform on the two numbers", }, }, required = { "num1", "num2", "operation" }, additionalProperties = false, }, strict = true, }, }, ``` ### `system_prompt` In the plugin, LLMs are given knowledge about a tool and how it can be used via the schema. However, for a particularly complicated tool, you can choose to include a system prompt. This is something that CodeCompanion does for the `insert_edit_into_file` tool. > \[!TIP] > From experience, a system prompt should be used sparingly. It's often an indication that your tool is too complicated and should be split out into multiple tools. For our calculator tool, we're going to use a `system_prompt` just to demonstrate the functionality: ```lua system_prompt = [[## Calculator Tool (`calculator`) ## CONTEXT - You have access to a calculator tool running within CodeCompanion, in Neovim. - You can use it to add, subtract, multiply or divide two numbers. ### OBJECTIVE - Do a mathematical operation on two numbers when the user asks ### RESPONSE - Always use the structure above for consistency. ]], ``` ### `handlers` The *handlers* table contains two functions that are executed before and after a tool completes: 1. `setup` - Is called **before** anything in the [cmds](/extending/tools#cmds) and [output](/extending/tools#output) table. This is useful if you wish to set the cmds dynamically on the tool itself, like in the [@run\_command](https://github.com/olimorris/codecompanion.nvim/blob/main/lua/codecompanion/interactions/chat/tools/builtin/run_command.lua) tool. 2. `on_exit` - Is called **after** everything in the [cmds](/extending/tools#cmds) and [output](/extending/tools#output) table. 3. `prompt_condition` - Is called **before** anything in the [cmds](/extending/tools#cmds) and [output](/extending/tools#output) table and is used to determine *if* the user should be prompted for approval. This is used in the `@insert_edit_into_file` tool to allow users to determine if they'd like to apply an approval to *buffer* or *file* edits. For the purposes of our calculator, let's just return some notifications so you can see the tool system and tool flow: ```lua handlers = { ---@param self CodeCompanion.Tool.Calculator ---@param meta { tools: CodeCompanion.Tools } setup = function(self, meta) return vim.notify("setup function called", vim.log.levels.INFO) end, ---@param self CodeCompanion.Tool.Calculator ---@param meta { tools: CodeCompanion.Tools } on_exit = function(self, meta) return vim.notify("on_exit function called", vim.log.levels.INFO) end, }, ``` > \[!TIP] > The chat buffer can be accessed via `meta.tools.chat` in the handler and output tables ### `output` The *output* table enables you to manage and format output from the execution of the [cmds](/extending/tools#cmds). It contains four functions: 1. `success` - Is called after *every* successful execution of a command/function. This can be a useful way of notifying the LLM of the success. 2. `error` - Is called when an error occurs whilst executing a command/function. It will only ever be called once as the whole execution of the [cmds](/extending/tools#cmds) is halted. This can be a useful way of notifying the LLM of the failure. 3. `prompt` - Is called when user approval to execute the [cmds](/extending/tools#cmds) is required. It forms the message prompt which the user is asked to confirm or reject. 4. `rejected` - Is called when a user rejects the approval to run the [cmds](/extending/tools#cmds). This method is used to inform the LLM of the rejection. Let's consider how me might implement this for our calculator tool: ```lua output = { ---@param self CodeCompanion.Tool.Calculator ---@param stdout table ---@param meta { tools: CodeCompanion.Tools, cmd: table } success = function(self, stdout, meta) local chat = meta.tools.chat return chat:add_tool_output(self, tostring(stdout[1])) end, ---@param self CodeCompanion.Tool.Calculator ---@param stderr table The error output from the command ---@param meta { tools: CodeCompanion.Tools, cmd: table } error = function(self, stderr, meta) return vim.notify("An error occurred", vim.log.levels.ERROR) end, }, ``` The `add_tool_output` method is designed to make it as easy as possible for tool authors to update the message history on the chat buffer: ```lua ---Add the output from a tool to the message history and a message to the UI ---@param tool table The Tool that was executed ---@param for_llm string The output to share with the LLM ---@param for_user? string The output to share with the user. If empty will use the LLM's output ---@return nil function Chat:add_tool_output(tool, for_llm, for_user) -- Omitted for brevity end ``` The `for_llm` parameter is the string message that will be shared with the LLM as part of the message history in the chat buffer, this is not made visible to the user. The `for_user` parameter allows tool authors to customize the visible output in the chat buffer, but if this is nil then the `for_llm` string is used. ### Running the Calculator tool If we put this all together in our config: ```lua require("codecompanion").setup({ interactions = { chat = { tools = { calculator = { description = "Perform calculations", name = "calculator", cmds = { ---@param self CodeCompanion.Tool.Calculator The Calculator tool ---@param args table The arguments from the LLM's tool call ---@param opts { input: any, output_cb: fun(result: table) } ---@return nil|{ status: "success"|"error", data: string } function(self, args, opts) -- Get the numbers and operation requested by the LLM local num1 = tonumber(args.num1) local num2 = tonumber(args.num2) local operation = args.operation -- Validate input if not num1 then return { status = "error", data = "First number is missing or invalid" } end if not num2 then return { status = "error", data = "Second number is missing or invalid" } end if not operation then return { status = "error", data = "Operation is missing" } end -- Perform the calculation local result if operation == "add" then result = num1 + num2 elseif operation == "subtract" then result = num1 - num2 elseif operation == "multiply" then result = num1 * num2 elseif operation == "divide" then if num2 == 0 then return { status = "error", data = "Cannot divide by zero" } end result = num1 / num2 else return { status = "error", data = "Invalid operation: must be add, subtract, multiply, or divide", } end return { status = "success", data = result } end, }, system_prompt = [[## Calculator Tool (`calculator`) ## CONTEXT - You have access to a calculator tool running within CodeCompanion, in Neovim. - You can use it to add, subtract, multiply or divide two numbers. ### OBJECTIVE - Do a mathematical operation on two numbers when the user asks ### RESPONSE - Always use the structure above for consistency. ]], schema = { type = "function", ["function"] = { name = "calculator", description = "Perform simple mathematical operations on a user's machine", parameters = { type = "object", properties = { num1 = { type = "integer", description = "The first number in the calculation", }, num2 = { type = "integer", description = "The second number in the calculation", }, operation = { type = "string", enum = { "add", "subtract", "multiply", "divide" }, description = "The mathematical operation to perform on the two numbers", }, }, required = { "num1", "num2", "operation", }, additionalProperties = false, }, strict = true, }, }, handlers = { ---@param self CodeCompanion.Tool.Calculator ---@param meta { tools: CodeCompanion.Tools } setup = function(self, meta) return vim.notify("setup function called", vim.log.levels.INFO) end, ---@param self CodeCompanion.Tool.Calculator ---@param meta { tools: CodeCompanion.Tools } on_exit = function(self, meta) return vim.notify("on_exit function called", vim.log.levels.INFO) end, }, output = { ---@param self CodeCompanion.Tool.Calculator ---@param stdout table ---@param meta { tools: CodeCompanion.Tools, cmd: table } success = function(self, stdout, meta) local chat = meta.tools.chat return chat:add_tool_output(self, tostring(stdout[1])) end, ---@param self CodeCompanion.Tool.Calculator ---@param stderr table The error output from the command ---@param meta { tools: CodeCompanion.Tools, cmd: table } error = function(self, stderr, meta) return vim.notify("An error occurred", vim.log.levels.ERROR) end, }, }, }, } } }) ``` and with the prompt: ``` Use the @{calculator} tool for 100*50 ``` You should see: `5000`, in the chat buffer. ### Adding in User Approvals A big concern for users when they create and deploy their own tools is *"what if an LLM does something I'm not aware of or I don't approve?"*. To that end, CodeCompanion tries to make it easy for a user to be the "human in the loop" and approve tool use before execution. To enable this for any tool, simply add the `require_approval_before = true` in a tool's `opts` table: ```lua require("codecompanion").setup({ interactions = { chat = { tools = { calculator = { description = "Perform calculations", path = "path.to.calculator", opts = { require_approval_before = true, }, } } } } }) ``` > \[!NOTE] > `opts.require_approval_before` can also be a function that receives the tool and tool system classes as parameters To account for the user being prompted for an approval, we can add a `output.prompt` to the tool: ```lua output = { -- success and error functions remain the same ... ---The message which is shared with the user when asking for their approval ---@param self CodeCompanion.Tool.Calculator ---@param meta { tools: CodeCompanion.Tools } ---@return string prompt = function(self, meta) return string.format( "Perform the calculation `%s`?", self.args.num1 .. " " .. self.args.operation .. " " .. self.args.num2 ) end, }, ``` This will notify the user with the message: `Perform the calculation 100 multiply 50?`. The user can choose to proceed, reject or cancel. The latter will cancel any tools from running. You can also customize the output if a user rejects the approval or cancels the tool execution: ```lua output = { -- success, error and prompt functions remain the same ... ---Rejection message back to the LLM ---@param self CodeCompanion.Tool.Calculator ---@param meta { tools: CodeCompanion.Tools, cmd: table, opts: table } ---@return nil rejected = function(self, meta) meta.tools.chat:add_tool_output(self, "The user declined to run the calculator tool") end, ---Cancellation message back to the LLM ---@param self CodeCompanion.Tool.Calculator ---@param meta { tools: CodeCompanion.Tools, cmd: table } ---@return nil cancelled = function(self, meta) meta.tools.chat:add_tool_output(self, "The user cancelled the execution of the calculator tool") end, }, ``` ## Extending from the run\_command tool For a lot of users, custom tools will often be commands that they ask an LLM to execute on their machine. As such, the handlers and output functions that exist in the [run\_command](/usage/chat-buffer/agents-tools#run-command) tool are sufficient and should be reused. To make it easy for users to create their own command-based tools, CodeCompanion allows for extensions from `run_command`. In the example below, we create a wrapper around the [beads](https://github.com/steveyegge/beads) CLI tool, that does just that: **Inline in your config:** ```lua require("codecompanion").setup({ interactions = { chat = { tools = { ["beads"] = { extends = "cmd_tool", description = "Beads task management", opts = { require_approval_before = true }, name = "beads", system_prompt = [[Beads is a local, hash-based task tracking system. Tasks have short IDs like `bd-a1b2`. Key commands: - `bd ready` — list tasks with no open blockers (i.e. ready to work on) - `bd show ` — show full details for a task - `bd create "" -p <priority>` — create a new task (priority 0 = highest) - `bd update <id> --claim` — assign a task to yourself - `bd update <id> --status done` — mark a task as done - `bd dep add <child> <parent>` — make child depend on parent Output is JSON. Always use `bd ready` first to see what's available before taking action.]], schema = { properties = { action = { type = "string", enum = { "ready", "show", "create", "update", "dep" }, description = "The beads action to perform", }, task_id = { type = "string", description = "The task ID (e.g. bd-a1b2). Required for show, update, and dep actions", }, args = { type = "string", description = "Additional arguments for the command (e.g. title for create, flags for update)", }, }, required = { "action" }, }, build_cmd = function(args) local parts = { "bd", args.action } if args.task_id then table.insert(parts, args.task_id) end if args.args then table.insert(parts, args.args) end return table.concat(parts, " ") end, }, }, }, }, }) ``` **Or via an external file:** ```lua require("codecompanion").setup({ interactions = { chat = { tools = { ["beads"] = { description = "Beads task management", opts = { require_approval_before = true }, path = "~/.dotfiles/.config/tools/beads.lua", }, }, }, }, }) ``` Where the file returns a table with `extends`: ```lua -- ~/.dotfiles/.config/tools/beads.lua return { extends = "cmd_tool", name = "beads", description = "Manage tasks using the Beads task tracking system (bd CLI)", system_prompt = [[...]], schema = { ... }, build_cmd = function(args) local parts = { "bd", args.action } if args.task_id then table.insert(parts, args.task_id) end if args.args then table.insert(parts, args.args) end return table.concat(parts, " ") end, } ``` In this example, the `schema` defines structured properties (`action`, `task_id`, `args`) that constrain what the LLM can pass to `build_cmd`. The output of `build_cmd` is what the `run_command` tool ultimately executes. Finally, the `system_prompt` teaches the LLM what each beads command does, so it can choose the right action for the user's request. ## Supporting an Adapter Tool Many LLM providers such as [Anthropic](https://docs.claude.com/en/docs/agents-and-tools/tool-use/computer-use-tool) and [OpenAI](https://platform.openai.com/docs/guides/tools-web-search?api-mode=responses) provide their own tools that clients like CodeCompanion can hook into. Thankfully, adding support for adapter tools is trivial. The [#2307](https://github.com/olimorris/codecompanion.nvim/pull/2307) PR showed how this can be accomplished for both Anthropic and the OpenAI responses adapters. 1. Add the tool to the structure of the adapter: ```lua -- openai_responses.lua -- ... existing code ... available_tools = { ["web_search"] = { description = "Allow models to search the web for the latest information before generating a response.", enabled = true, ---@param self CodeCompanion.HTTPAdapter.OpenAIResponses ---@param meta { tools: table } callback = function(self, meta) table.insert(meta.tools, { type = "web_search", }) end, }, }, -- ... existing code ... ``` Within the `callback` function, which will be executed in step 2, it can be useful to carry out modifications to the adapter which may be required for the tool to function. In the case of Anthropic, we insert additional headers. 2. Within `build_tools` or `form_tools` (depending on your adapter), ensure that when looping through a tool's schema, you detect if the tool is an adapter tool and execute the `callback` from step 1: ```lua -- build_tools = function(self, tools) -- OR -- form_tools = function(self, tools) local transformed = {} for _, tool in pairs(tools) do for _, schema in pairs(tool) do -- // Add this logic if schema._meta and schema._meta.adapter_tool then if self.available_tools[schema.name] then self.available_tools[schema.name].callback(self, { tools = transformed }) end else -- // -- Previous loop logic goes here end end end ``` Some adapter tools can be a *hybrid* in terms of their implementation. That is, they're an adapter tool that requires a client-side component (i.e. a built-in tool). This is the case for the [memory](/usage/chat-buffer/agents-tools#memory) tool from Anthropic. To allow for this, ensure that the tool definition in `available_tools` has `client_tool` defined: ```lua ["memory"] = { -- ...existing code here opts = { -- Allow a hybrid tool -> One that also has a client side implementation client_tool = "interactions.chat.tools.memory", }, }, ``` ## Other Tips ### `use_handlers_once` If an LLM calls multiple tools in the same response, it's possible that the same tool may be called in succession. If you'd like to ensure that the handler functions (`setup` and `on_exit`) are only called once, you can set this in the `opts` table in the tool itself: ```lua return { name = "editor", opts = { use_handlers_once = true, }, -- More code follows... } ``` --- --- url: /extending/ui.md description: >- Examples and community recipes for extending CodeCompanion's UI in Neovim — including progress spinners with Fidget.nvim and custom status line integrations. --- # Extending the UI Below are some examples of how you can extend CodeCompanion and modify the user interface to suit your needs. ## Progress updates with Fidget.nvim by [@jessevdp](https://github.com/jessevdp) As per the discussion over at [#813](https://github.com/olimorris/codecompanion.nvim/discussions/813). ## Inline spinner with Fidget.nvim by [@yuhua99](https://github.com/yuhua99) As per the comment on [#640](https://github.com/olimorris/codecompanion.nvim/discussions/640#discussioncomment-12866279). ## Status column extmarks with the inline interaction by [@lucobellic](https://github.com/lucobellic) As per the discussion over at [#1297](https://github.com/olimorris/codecompanion.nvim/discussions/1297). ## Lualine.nvim integration The plugin can be integrated with lualine.nvim to show an icon in the statusline when a request is being sent to an LLM: ```lua local M = require("lualine.component"):extend() M.processing = false M.spinner_index = 1 local spinner_symbols = { "⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏", } local spinner_symbols_len = 10 -- Initializer function M:init(options) M.super.init(self, options) local group = vim.api.nvim_create_augroup("CodeCompanionHooks", {}) vim.api.nvim_create_autocmd({ "User" }, { pattern = "CodeCompanionRequest*", group = group, callback = function(request) if request.match == "CodeCompanionRequestStarted" then self.processing = true elseif request.match == "CodeCompanionRequestFinished" then self.processing = false end end, }) end -- Function that runs every time statusline is updated function M:update_status() if self.processing then self.spinner_index = (self.spinner_index % spinner_symbols_len) + 1 return spinner_symbols[self.spinner_index] else return nil end end return M ``` ## Heirline.nvim integration The plugin can also be integrated into [heirline.nvim](https://github.com/rebelot/heirline.nvim) to show an icon when a request is being sent to an LLM and also to show useful meta information about the chat buffer. In the video at the top of this page, you can see the fidget spinner alongside the heirline.nvim integration below: ```lua local CodeCompanion = { static = { processing = false, }, update = { "User", pattern = "CodeCompanionRequest*", callback = function(self, args) if args.match == "CodeCompanionRequestStarted" then self.processing = true elseif args.match == "CodeCompanionRequestFinished" then self.processing = false end vim.cmd("redrawstatus") end, }, { condition = function(self) return self.processing end, provider = " ", hl = { fg = "yellow" }, }, } local IsCodeCompanion = function() return package.loaded.codecompanion and vim.bo.filetype == "codecompanion" end local CodeCompanionCurrentContext = { static = { enabled = true, }, condition = function(self) return IsCodeCompanion() and _G.codecompanion_current_context ~= nil and self.enabled end, provider = function() local bufname = vim.fn.fnamemodify(vim.api.nvim_buf_get_name(_G.codecompanion_current_context), ":t") return "[  " .. bufname .. " ] " end, hl = { fg = "gray", bg = "bg" }, update = { "User", pattern = { "CodeCompanionRequest*", "CodeCompanionContextChanged" }, callback = vim.schedule_wrap(function(self, args) if args.match == "CodeCompanionRequestStarted" then self.enabled = false elseif args.match == "CodeCompanionRequestFinished" then self.enabled = true end vim.cmd("redrawstatus") end), }, } local CodeCompanionStats = { condition = function(self) return IsCodeCompanion() end, static = { chat_values = {}, }, init = function(self) local bufnr = vim.api.nvim_get_current_buf() self.chat_values = _G.codecompanion_chat_metadata[bufnr] end, -- Tokens block { condition = function(self) return self.chat_values.tokens > 0 end, RightSlantStart, { provider = function(self) return "  " .. self.chat_values.tokens .. " " end, hl = { fg = "gray", bg = "statusline_bg" }, update = { "User", pattern = { "CodeCompanionChatOpened", "CodeCompanionRequestFinished" }, callback = vim.schedule_wrap(function() vim.cmd("redrawstatus") end), }, }, RightSlantEnd, }, -- Cycles block { condition = function(self) return self.chat_values.cycles > 0 end, RightSlantStart, { provider = function(self) return "  " .. self.chat_values.cycles .. " " end, hl = { fg = "gray", bg = "statusline_bg" }, update = { "User", pattern = { "CodeCompanionChatOpened", "CodeCompanionRequestFinished" }, callback = vim.schedule_wrap(function() vim.cmd("redrawstatus") end), }, }, RightSlantEnd, }, } ``` --- --- url: /agent-client-protocol.md description: >- CodeCompanion's Agent Client Protocol (ACP) support — covers session management, tool execution, permissions, and which capabilities are currently implemented. --- # Agent Client Protocol (ACP) Support CodeCompanion implements the [Agent Client Protocol (ACP)](https://agentclientprotocol.com/) to enable you to work with coding agents from within Neovim. ACP is an open standard that enables structured interaction between clients (like CodeCompanion) and AI agents, providing capabilities such as session management, file system operations, tool execution, and permission handling. This page provides a technical reference for what's supported in CodeCompanion and how it's been implemented. ## Implementation CodeCompanion provides comprehensive support for the ACP specification: | Feature Category | Supported | Details | |------------------|---------------|---------| | **Core Protocol** | ✅ | JSON-RPC 2.0, streaming responses, message buffering | | **Authentication** | ✅ | Multiple auth methods, adapter-level hooks | | **Content Types** | ✅ | Text, images, embedded resources | | **File System** | ✅ | Read/write text files with line ranges | | **MCP Integration** | ✅ | Stdio, HTTP, and SSE transports | | **Permissions** | ✅ | Interactive UI with diff preview for tool approval | | **Session Management** | ✅ | Create, list, load, and restore sessions with state tracking | | **Session Modes** | ✅ | Mode switching | | **Session Models** | ✅ | Select specific models | | **Tool Calls** | ✅ | Content blocks, file diffs, status updates | | **Agent Plans** | ❌ | Visual display of an agent's execution plan | | **Terminal Operations** | ❌ | Agent has access to a Neovim terminal | ### Supported Adapters Please see the [Configuring ACP Adapters](/configuration/adapters-acp) page. ### Client Capabilities CodeCompanion advertises the following capabilities to ACP agents: ```lua { fs = { readTextFile = true, -- Read files with optional line ranges writeTextFile = true -- Write/create files }, terminal = false -- Terminal operations not supported } ``` ### Content Types | Content Type | Send to Agent | Receive from Agent | |--------------|---------------|-------------------| | Text | ✅ | ✅ | | File Diffs | N/A | ✅ | | Images | ✅ | ❌ | | Audio | ❌ | ❌ | | Embedded Resources | ❌ | ❌ | ### State Management Unlike HTTP adapters which are stateless (sending the full conversation history with each request), ACP adapters are stateful. The agent maintains the conversation context, so CodeCompanion only sends new messages with each prompt. Session IDs are tracked throughout the conversation lifecycle. ### File Context Handling When sending files as embedded resources to agents, CodeCompanion re-reads the file content rather than using the chat buffer representation. This avoids HTTP-style `<attachment>` tags that are used for LLM adapters but don't make sense for ACP agents. ### Slash Commands ACP agents can advertise their own slash commands dynamically. You can access them with `\command` in the chat buffer. CodeCompanion transforms this to `/command` before sending your prompt to the agent. ### Session Resume If an agent supports the `session/list` capability, you can resume a previous session using the `/resume` slash command in a fresh chat buffer. This calls `session/list` to discover previous sessions, then `session/load` to restore the selected session's conversation history into the chat buffer. See [Slash Commands](/usage/chat-buffer/slash-commands#resume) for usage details. ### Model Selection CodeCompanion implements a `session/set_model` method that allows you to select a model for the current session. This feature is not part of the [official ACP specification](https://agentclientprotocol.com/protocol/draft/schema#session-set_model) and is subject to change in future versions. ### Cleanup and Lifecycle CodeCompanion ensures clean disconnection from ACP agents by hooking into Neovim's `VimLeavePre` autocmd. This guarantees that agent processes are properly terminated even if Neovim exits unexpectedly. ## Protocol Version CodeCompanion currently implements **ACP Protocol Version 1**. The protocol version is negotiated during initialization. If an agent selects a different version, CodeCompanion will log a warning but continue to operate, following the agent's selected version. ## Current Limitations * **Terminal Operations**: The `terminal/*` family of methods (`terminal/create`, `terminal/output`, `terminal/release`, etc.) are not implemented. CodeCompanion doesn't advertise terminal capabilities to agents. * **Agent Plan Rendering**: [Plan](https://agentclientprotocol.com/protocol/agent-plan) updates from agents are received and logged, but they're not currently rendered in the chat buffer UI. * **Audio Content**: Audio can't be sent or received ## See Also * [Agent Client Protocol Specification](https://agentclientprotocol.com/) - Official ACP documentation * [Configuring ACP Adapters](/configuration/adapters-acp) - Setup instructions for specific agents * [Using Agents and Tools](/usage/chat-buffer/agents-tools) - How to interact with agents in chat --- --- url: /model-context-protocol.md description: >- Overview of CodeCompanion's Model Context Protocol (MCP) support — which capabilities are implemented and how MCP tools appear in the chat buffer in Neovim. --- # Model Context Protocol (MCP) Support CodeCompanion implements the [Model Context Protocol (MCP)](https://modelcontextprotocol.io) to enable you to connect the plugin to external systems and applications. The plugin only implements a subset of the full MCP specification, focusing on the features that enable developers to enhance their coding experience. ## Usage To use MCP servers within CodeCompanion, refer to the [tools](/usage/chat-buffer/agents-tools#mcp) section in the chat buffer usage section of the documentation. If [enabled](/configuration/mcp#enabling-servers), the servers will be started when you open a chat buffer for the first time. However, you can use the [MCP slash command](/usage/chat-buffer/slash-commands#mcp) to start or stop servers manually. ## Implementation | Feature Category | Supported | Details | |----------------------------------------|-----------|-------------------------------------------------------------| | Transport: Stdio | ✅ | | | Transport: Streamable HTTP | ❌ | | | Basic: Cancellation | ✅ | Timeout and user can cancel manually | | Basic: Progress | ❌ | | | Basic: Task | ❌ | | | Client: Roots | ✅ | Disabled by default | | Client: Sampling | ❌ | | | Client: Elicitation | ❌ | | | Server: Completion | ❌ | | | Server: Pagination | ✅ | | | Server: Prompts | ❌ | | | Server: Resources | ❌ | | | Server: Tools | ✅ | Currently only supports Text Content | | Server: Tool list changed notification | ❌ | | ## Protocol Version CodeCompanion currently supports MCP version **2025-11-25**. ## See Also * [Model Context Protocol Specification](https://modelcontextprotocol.io/specification/2025-11-25) - Official MCP documentation