codegpt-ng is a minimalist plugin for neovim that provides commands to interact with AI backend. The focus is around code related usages. So code completion, refactorings, generating docs, etc.
This is a fork of the original CodeGPT repository from github user @dpayne. All credit goes to him for the initial work.
This fork does the following:
- Full support for Ollama API
- New table-based configuration instead of global variables
- Streaming mode for real-time popup responses
- Refactored for idiomatic Lua and neovim plugin style
- Simplified command system with explicit configuration
- Tests with plenary library
Although this fork introduces breaking changes and a complete rewrite, I've tried to preserve the original project's minimalist ethos — a tool that connects to AI APIs without getting in the way. The goal remains to provide simple, code-focused interactions that stay lightweight and unobtrusive, letting developers leverage AI power while maintaining control over their workflow.
- The plugins 'plenary' and 'nui' are also required.
Installing with Lazy.
{
"blob42/codegpt-ng.nvim",
dependencies = {
'nvim-lua/plenary.nvim',
'MunifTanjim/nui.nvim',
},
opts = {
-- API configuration
api = {
provider = "openai", -- or "Ollama", "Azure", etc.
openai_api_key = os.getenv("OPENAI_API_KEY"),
chat_completions_url = "https://api.openai.com/v1/chat/completions",
},
-- Command defaults
commands = {
completion = {
model = "gpt-3.5-turbo",
user_message_template = "This is a template...",
callback_type = "replace_lines",
},
tests = {
language_instructions = { java = "Use TestNG framework" },
},
-- Add custom commands here
},
-- UI configuration
ui = {
popup_type = "popup", -- or "horizontal", "vertical"
text_popup_filetype = "markdown",
commands = {
quit = "q",
use_as_output = "<c-o>",
},
popup_options = {
relative = "editor",
position = "50%",
size = { width = "80%", height = "80%" },
},
popup_border = { style = "rounded" },
popup_window_options = { wrap = true, number = true },
},
-- Status hooks
hooks = {
request_started = function() vim.cmd("hi StatusLine ctermfg=yellow") end,
request_finished = function() vim.cmd("hi StatusLine ctermfg=NONE") end,
},
-- Other options
clear_visual_selection = true,
}
}
Installing with Packer.
use({
"blob42/codegpt-ng.nvim",
requires = {
"MunifTanjim/nui.nvim",
"nvim-lua/plenary.nvim",
},
config = function()
require("codegpt").setup({
-- Configuration here
})
end
})
The top-level command is :Chat
. The behavior is different depending on whether text is selected and/or arguments are passed.
You can also use :VChat
to temporary enforce the vertical layout.
:Chat
with text selection will trigger thecompletion
command, ChatGPT will try to complete the selected code snippet.
:Chat some instructions
with text selection and command args will invoke thecode_edit
command.
:Chat <command>
if there is only one argument and that argument matches a command, it will invoke that command with the given text selection. In the below example:Chat tests
will attempt to write units for the selected code.
:Chat hello world
without any text selection will trigger thechat
command.
A full list of predefined commands are below
command | input | Description |
---|---|---|
completion | text selection | Will ask ChatGPT to complete the selected code. |
code_edit | text selection and command args | Will ask ChatGPT to apply the given instructions (the command args) to the selected code. |
explain | text selection | Will ask ChatGPT to explain the selected code. |
question | text selection | Will pass the commands args to ChatGPT and return the answer in a text popup. |
debug | text selection | Will pass the code selection to ChatGPT analyze it for bugs, the results will be in a text popup. |
doc | text selection | Will ask ChatGPT to document the selected code. |
opt | text selection | Will ask ChatGPT to optimize the selected code. |
tests | text selection | Will ask ChatGPT to write unit tests for the selected code. |
chat | command args | Will pass the given command args to ChatGPT and return the response in a popup. |
require("codegpt").setup({
api = {
provider = "openai", -- or "Ollama", "Azure", etc.
openai_api_key = os.getenv("OPENAI_API_KEY"),
chat_completions_url = "https://api.openai.com/v1/chat/completions",
},
commands = {
-- Command defaults
},
ui = {
-- UI configuration
},
hooks = {
-- Status hooks
},
clear_visual_selection = true,
})
The configuration table commands
can be used to override command configurations.
commands = {
completion = {
model = "gpt-3.5-turbo",
user_message_template = "This is a template...",
callback_type = "replace_lines",
},
tests = {
language_instructions = { java = "Use TestNG framework" },
}
}
Custom commands can be added to the commands
table.
commands = {
modernize = {
user_message_template = "Modernize the code...",
language_instructions = { cpp = "..." }
}
}
ui = {
popup_type = "popup", -- or "horizontal", "vertical"
text_popup_filetype = "markdown",
commands = {
quit = "q",
use_as_output = "<c-o>",
},
popup_options = {
relative = "editor",
position = "50%",
size = { width = "80%", height = "80%" },
},
popup_border = { style = "rounded" },
popup_window_options = { wrap = true, number = true },
}
hooks = {
request_started = function() vim.cmd("hi StatusLine ctermfg=yellow") end,
request_finished = function() vim.cmd("hi StatusLine ctermfg=NONE") end,
}
The system_message_template
and user_message_template
can contain template macros. For example:
macro | description |
---|---|
{{filetype}} |
The filetype of the current buffer. |
{{text_selection}} |
The selected text in the current buffer. |
{{language}} |
The name of the programming language in the current buffer. |
{{command_args}} |
Everything passed to the command as an argument, joined with spaces. |
{{language_instructions}} |
The found value in the language_instructions map. |
name | Description |
---|---|
replace_lines | replaces the current lines with the response. If no text is selected it will insert the response at the cursor. |
text_popup | Will display the result in a text popup window. |
code_popup | Will display the results in a popup window with the filetype set to the filetype of the current buffer |
require("codegpt").setup({
api = {
provider = "openai",
openai_api_key = os.getenv("OPENAI_API_KEY"),
},
models = {
ollama = {
default = 'qwen3:4b',
['qwen3:4b'] = {
max_tokens = 8192,
temperature = 0.8,
append_string = '/no_think',
},
},
commands = {
tests = {
language_instructions = { java = "Use TestNG framework" },
},
doc = {
language_instructions = { python = "Use Google style docstrings" },
max_tokens = 1024,
},
code_edit = {
system_message_template = "You are {{language}} developer.",
user_message_template = "I have the following {{language}} code: ```{{filetype}}\n{{text_selection}}```\nEdit the above code. {{language_instructions}}",
callback_type = "code_popup",
},
modernize = {
user_message_template = "I have the following {{language}} code: ```{{filetype}}\n{{text_selection}}```\nModernize the above code. Use current best practices. Only return the code snippet and comments. {{language_instructions}}",
language_instructions = {
cpp = "Use modern C++ syntax. Use auto where possible. Do not import std. Use trailing return type. Use the c++11, c++14, c++17, and c++20 standards where applicable.",
},
}
},
ui = {
popup_type = "popup",
text_popup_filetype = "markdown",
commands = {
quit = "q",
use_as_output = "<c-o>",
},
popup_options = {
relative = "editor",
position = "50%",
size = { width = "80%", height = "80%" },
},
popup_border = { style = "rounded" },
popup_window_options = { wrap = true, number = true },
},
hooks = {
request_started = function() vim.cmd("hi StatusLine ctermfg=yellow") end,
request_finished = function() vim.cmd("hi StatusLine ctermfg=NONE") end,
},
clear_visual_selection = true,
})