Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/Robitx/gp.nvim
Gp.nvim (GPT prompt) Neovim AI plugin: ChatGPT sessions & Instructable text/code operations & Speech to text [OpenAI]
https://github.com/Robitx/gp.nvim
ai chatgpt codeium copilot cursor gpt gpt-4 gpt4 llm lua neovim nvim openai plugin speech-to-text tabnine vim voice whisper
Last synced: about 1 month ago
JSON representation
Gp.nvim (GPT prompt) Neovim AI plugin: ChatGPT sessions & Instructable text/code operations & Speech to text [OpenAI]
- Host: GitHub
- URL: https://github.com/Robitx/gp.nvim
- Owner: Robitx
- License: mit
- Created: 2023-06-18T13:55:46.000Z (about 1 year ago)
- Default Branch: main
- Last Pushed: 2023-12-18T05:22:57.000Z (9 months ago)
- Last Synced: 2023-12-18T06:28:08.290Z (9 months ago)
- Topics: ai, chatgpt, codeium, copilot, cursor, gpt, gpt-4, gpt4, llm, lua, neovim, nvim, openai, plugin, speech-to-text, tabnine, vim, voice, whisper
- Language: Lua
- Homepage:
- Size: 322 KB
- Stars: 196
- Watchers: 6
- Forks: 20
- Open Issues: 4
-
Metadata Files:
- Readme: README.md
- Funding: .github/FUNDING.yml
- License: LICENSE
Awesome Lists containing this project
- awesome-neovim - Robitx/gp.nvim - ChatGPT like sessions and instructable text/code operations in your favorite editor. (AI / (requires Neovim 0.5))
- awesome-vim-llm-plugins - Robitx/gp.nvim
README
# Gp.nvim (GPT prompt) Neovim AI plugin
**ChatGPT like sessions, Instructable text/code operations, Speech to text and Image generation in your favorite editor.**
![]()
![]()
### Youtube demos
- [5-min-demo](https://www.youtube.com/watch?v=X-cT7s47PLo) (December 2023)
- [older-5-min-demo](https://www.youtube.com/watch?v=wPDcBnQgNCc) (screen capture, no sound)# Goals and Features
The goal is to extend Neovim with the **power of GPT models in a simple unobtrusive extensible way.**
Trying to keep things as native as possible - reusing and integrating well with the natural features of (Neo)vim.- **Streaming responses**
- no spinner wheel and waiting for the full answer
- response generation can be canceled half way through
- properly working undo (response can be undone with a single `u`)
- **Infinitely extensible** via hook functions specified as part of the config
- hooks have access to everything in the plugin and are automatically registered as commands
- see [5. Configuration](#5-configuration) and [Extend functionality](#extend-functionality) sections for details
- **Minimum dependencies** (`neovim`, `curl`, `grep` and optionally `sox`)
- zero dependencies on other lua plugins to minimize chance of breakage
- **ChatGPT like sessions**
- just good old neovim buffers formated as markdown with autosave and few buffer bound shortcuts
- last chat also quickly accessible via toggable popup window
- chat finder - management popup for searching, previewing, deleting and opening chat sessions
- **Instructable text/code operations**
- templating mechanism to combine user instructions, selections etc into the gpt query
- multimodal - same command works for normal/insert mode, with selection or a range
- many possible output targets - rewrite, prepend, append, new buffer, popup
- non interactive command mode available for common repetitive tasks implementable as simple hooks
(explain something in a popup window, write unit tests for selected code into a new buffer,
finish selected code based on comments in it, etc.)
- custom instructions per repository with `.gp.md` file
(instruct gpt to generate code using certain libs, packages, conventions and so on)
- **Speech to text support**
- a mouth is 2-4x faster than fingers when it comes to outputting words - use it where it makes sense
(dicating comments and notes, asking gpt questions, giving instructions for code operations, ..)
- **Image generation**
- be even less tempted to open the browser with the ability to generate images directly from Neovim# Install
## 1. Install the plugin
Snippets for your preferred package manager:
```lua
-- lazy.nvim
{
"robitx/gp.nvim",
config = function()
local conf = {
-- For customization, refer to Install > Configuration in the Documentation/Readme
}
require("gp").setup(conf)-- Setup shortcuts here (see Usage > Shortcuts in the Documentation/Readme)
end,
}
``````lua
-- packer.nvim
use({
"robitx/gp.nvim",
config = function()
local conf = {
-- For customization, refer to Install > Configuration in the Documentation/Readme
}
require("gp").setup(conf)-- Setup shortcuts here (see Usage > Shortcuts in the Documentation/Readme)
end,
})
``````lua
-- vim-plug
Plug 'robitx/gp.nvim'local conf = {
-- For customization, refer to Install > Configuration in the Documentation/Readme
}
require("gp").setup(conf)-- Setup shortcuts here (see Usage > Shortcuts in the Documentation/Readme)
```
## 2. OpenAI API keyMake sure you have OpenAI API key. [Get one here](https://platform.openai.com/account/api-keys) and use it in the [4. Configuration](#4-configuration). Also consider setting up [usage limits](https://platform.openai.com/account/billing/limits) so you won't get suprised at the end of the month.
The OpenAI API key can be passed to the plugin in multiple ways:
| Method | Example | Security Level |
| ------------------------- | -------------------------------------------------------------- | ------------------- |
| hardcoded string | `openai_api_key: "sk-...",` | Low |
| default env var | set `OPENAI_API_KEY` environment variable in shell config | Medium |
| custom env var | `openai_api_key = os.getenv("CUSTOM_ENV_NAME"),` | Medium |
| read from file | `openai_api_key = { "cat", "path_to_api_key" },` | Medium-High |
| password manager | `openai_api_key = { "bw", "get", "password", "OAI_API_KEY" },` | High |If `openai_api_key` is a table, Gp runs it asynchronously to avoid blocking Neovim (password managers can take a second or two).
## 3. Multiple providers
The following LLM providers are currently supported besides OpenAI:- [Ollama](https://github.com/ollama/ollama) for local/offline open-source models. The plugin assumes you have the Ollama service up and running with configured models available (the default Ollama agent uses Llama3).
- [GitHub Copilot](https://github.com/settings/copilot) with a Copilot license ([zbirenbaum/copilot.lua](https://github.com/zbirenbaum/copilot.lua) or [github/copilot.vim](https://github.com/github/copilot.vim) for autocomplete). You can access the underlying GPT-4 model without paying anything extra (essentially unlimited GPT-4 access).
- [Perplexity.ai](https://www.perplexity.ai/pro) Pro users have $5/month free API credits available (the default PPLX agent uses Mixtral-8x7b).
- [Anthropic](https://www.anthropic.com/api) to access Claude models, which currently outperform GPT-4 in some benchmarks.
- [Google Gemini](https://ai.google.dev/) with a quite generous free range but some geo-restrictions (EU).
- Any other "OpenAI chat/completions" compatible endpoint (Azure, LM Studio, etc.)Below is an example of the relevant configuration part enabling some of these. The `secret` field has the same capabilities as `openai_api_key` (which is still supported for compatibility).
```lua
providers = {
openai = {
endpoint = "https://api.openai.com/v1/chat/completions",
secret = os.getenv("OPENAI_API_KEY"),
},-- azure = {...},
copilot = {
endpoint = "https://api.githubcopilot.com/chat/completions",
secret = {
"bash",
"-c",
"cat ~/.config/github-copilot/hosts.json | sed -e 's/.*oauth_token...//;s/\".*//'",
},
},pplx = {
endpoint = "https://api.perplexity.ai/chat/completions",
secret = os.getenv("PPLX_API_KEY"),
},ollama = {
endpoint = "http://localhost:11434/v1/chat/completions",
},googleai = {
endpoint = "https://generativelanguage.googleapis.com/v1beta/models/{{model}}:streamGenerateContent?key={{secret}}",
secret = os.getenv("GOOGLEAI_API_KEY"),
},anthropic = {
endpoint = "https://api.anthropic.com/v1/messages",
secret = os.getenv("ANTHROPIC_API_KEY"),
},
},
```Each of these providers has some agents preconfigured. Below is an example of how to disable predefined ChatGPT3-5 agent and create a custom one. If the `provider` field is missing, OpenAI is assumed for backward compatibility.
```lua
agents = {
{
name = "ChatGPT3-5",
disable = true,
},
{
name = "MyCustomAgent",
provider = "copilot",
chat = true,
command = true,
model = { model = "gpt-4-turbo" },
system_prompt = "Answer any query with just: Sure thing..",
},
},```
## 4. Dependencies
The core plugin only needs `curl` installed to make calls to OpenAI API and `grep` for ChatFinder. So Linux, BSD and Mac OS should be covered.
Voice commands (`:GpWhisper*`) depend on `SoX` (Sound eXchange) to handle audio recording and processing:
- Mac OS: `brew install sox`
- Ubuntu/Debian: `apt-get install sox libsox-fmt-mp3`
- Arch Linux: `pacman -S sox`
- Redhat/CentOS: `yum install sox`
- NixOS: `nix-env -i sox`## 5. Configuration
Below is a linked snippet with the default values, but I suggest starting with minimal config possible (just `openai_api_key` if you don't have `OPENAI_API_KEY` env set up). Defaults change over time to improve things, options might get deprecated and so on - it's better to change only things where the default doesn't fit your needs.
https://github.com/Robitx/gp.nvim/blob/96af1aa8de0e1d2d72877569897ed081ab996c67/lua/gp/config.lua#L9-L596
# Usage
## Chat commands
#### `:GpChatNew`
Open a fresh chat in the current window. It can be either empty or include the visual selection or specified range as context. This command also supports subcommands for layout specification:
- `:GpChatNew vsplit` Open a fresh chat in a vertical split window.
- `:GpChatNew split` Open a fresh chat in a horizontal split window.
- `:GpChatNew tabnew` Open a fresh chat in a new tab.
- `:GpChatNew popup` Open a fresh chat in a popup window.#### `:GpChatPaste`
Paste the selection or specified range into the latest chat, simplifying the addition of code from multiple files into a single chat buffer. This command also supports subcommands for layout specification:
- `:GpChatPaste vsplit` Paste into the latest chat in a vertical split window.
- `:GpChatPaste split` Paste into the latest chat in a horizontal split window.
- `:GpChatPaste tabnew` Paste into the latest chat in a new tab.
- `:GpChatPaste popup` Paste into the latest chat in a popup window.#### `:GpChatToggle`
Open chat in a toggleable popup window, showing the last active chat or a fresh one with selection or a range as a context. This command also supports subcommands for layout specification:
- `:GpChatToggle vsplit` Toggle chat in a vertical split window.
- `:GpChatToggle split` Toggle chat in a horizontal split window.
- `:GpChatToggle tabnew` Toggle chat in a new tab.
- `:GpChatToggle popup` Toggle chat in a popup window.#### `:GpChatFinder`
Open a dialog to search through chats.
#### `:GpChatRespond`
Request a new GPT response for the current chat. Usin`:GpChatRespond N` request a new GPT response with only the last N messages as context, using everything from the end up to the Nth instance of `🗨:..` (N=1 is like asking a question in a new chat).
#### `:GpChatDelete`
Delete the current chat. By default requires confirmation before delete, which can be disabled in config using `chat_confirm_delete = false,`.
## Text/Code commands
#### `:GpRewrite`
Opens a dialog for entering a prompt. After providing prompt instructions into the dialog, the generated response replaces the current line in normal/insert mode, selected lines in visual mode, or the specified range (e.g., `:%GpRewrite` applies the rewrite to the entire buffer).
`:GpRewrite {prompt}` Executes directly with specified `{prompt}` instructions, bypassing the dialog. Suitable for mapping repetitive tasks to keyboard shortcuts or for automation using headless Neovim via terminal or shell scripts.
#### `:GpAppend`
Similar to `:GpRewrite`, but the answer is added after the current line, visual selection, or range.
#### `:GpPrepend`
Similar to `:GpRewrite`, but the answer is added before the current line, visual selection, or range.
#### `:GpEnew`
Similar to `:GpRewrite`, but the answer is added into a new buffer in the current window.
#### `:GpNew`
Similar to `:GpRewrite`, but the answer is added into a new horizontal split window.
#### `:GpVnew`
Similar to `:GpRewrite`, but the answer is added into a new vertical split window.
#### `:GpTabnew`
Similar to `:GpRewrite`, but the answer is added into a new tab.
#### `:GpPopup`
Similar to `:GpRewrite`, but the answer is added into a pop-up window.
#### `:GpImplement`
Example hook command to develop code from comments in a visual selection or specified range.
#### `:GpContext`
Provides custom context per repository:
- opens `.gp.md` file for a given repository in a toggable window.
- appends selection/range to the context file when used in visual/range mode.
- also supports subcommands for layout specification:- `:GpContext vsplit` Open `.gp.md` in a vertical split window.
- `:GpContext split` Open `.gp.md` in a horizontal split window.
- `:GpContext tabnew` Open `.gp.md` in a new tab.
- `:GpContext popup` Open `.gp.md` in a popup window.- refer to [Custom Instructions](#custom-instructions) for more details.
## Speech commands
#### `:GpWhisper` {lang?}
Transcription replaces the current line, visual selection or range in the current buffer. Use your mouth to ask a question in a chat buffer instead of writing it by hand, dictate some comments for the code, notes or even your next novel..
For the rest of the whisper commands, the transcription is used as an editable prompt for the equivalent non whisper command - `GpWhisperRewrite` dictates instructions for `GpRewrite` etc.
You can override the default language by setting {lang} with the 2 letter
shortname of your language (e.g. "en" for English, "fr" for French etc).#### `:GpWhisperRewrite`
Similar to `:GpRewrite`, but the prompt instruction dialog uses transcribed spoken instructions.
#### `:GpWhisperAppend`
Similar to `:GpAppend`, but the prompt instruction dialog uses transcribed spoken instructions for adding content after the current line, visual selection, or range.
#### `:GpWhisperPrepend`
Similar to `:GpPrepend`, but the prompt instruction dialog uses transcribed spoken instructions for adding content before the current line, selection, or range.
#### `:GpWhisperEnew`
Similar to `:GpEnew`, but the prompt instruction dialog uses transcribed spoken instructions for opening content in a new buffer within the current window.
#### `:GpWhisperNew`
Similar to `:GpNew`, but the prompt instruction dialog uses transcribed spoken instructions for opening content in a new horizontal split window.
#### `:GpWhisperVnew`
Similar to `:GpVnew`, but the prompt instruction dialog uses transcribed spoken instructions for opening content in a new vertical split window.
#### `:GpWhisperTabnew`
Similar to `:GpTabnew`, but the prompt instruction dialog uses transcribed spoken instructions for opening content in a new tab.
#### `:GpWhisperPopup`
Similar to `:GpPopup`, but the prompt instruction dialog uses transcribed spoken instructions for displaying content in a pop-up window.
## Agent commands
#### `:GpNextAgent`
Cycles between available agents based on the current buffer (chat agents if current buffer is a chat and command agents otherwise). The agent setting is persisted on disk across Neovim instances.
#### `:GpAgent`
Displays currently used agents for chat and command instructions.
#### `:GpAgent XY`
Choose a new agent based on its name, listing options based on the current buffer (chat agents if current buffer is a chat and command agents otherwise). The agent setting is persisted on disk across Neovim instances.
## Image commands
#### `:GpImage`
Opens a dialog for entering a prompt describing wanted images. When the generation is done it opens dialog for storing the image to the disk.
#### `:GpImageAgent`
Displays currently used image agent (configuration).
#### `:GpImageAgent XY`
Choose a new "image agent" based on its name. In the context of images, agent is basically a configuration for model, image size, quality and so on. The agent setting is persisted on disk across Neovim instances.
## Other commands
#### `:GpStop`
Stops all currently running responses and jobs.
#### `:GpInspectPlugin`
Inspects the GPT prompt plugin object in a new scratch buffer.
## GpDone autocommand
Commands like `GpRewrite`, `GpAppend` etc. run asynchronously and generate event `GpDone`, so you can define autocmd (like auto formating) to run when gp finishes:
```lua
vim.api.nvim_create_autocmd({ "User" }, {
pattern = {"GpDone"},
callback = function(event)
print("event fired:\n", vim.inspect(event))
-- local b = event.buf
-- DO something
end,
})
```## Custom instructions
By calling `:GpContext` you can make `.gp.md` markdown file in a root of a repository. Commands such as `:GpRewrite`, `:GpAppend` etc. will respect instructions provided in this file (works better with gpt4, gpt 3.5 doesn't always listen to system commands). For example:
```md
Use ‎C++17.
Use Testify library when writing Go tests.
Use Early return/Guard Clauses pattern to avoid excessive nesting.
...
```Here is [another example](https://github.com/Robitx/gp.nvim/blob/main/.gp.md).
## Scripting
`GpDone` event + `.gp.md` custom instructions provide a possibility to run gp.nvim using headless (neo)vim from terminal or shell script. So you can let gp run edits accross many files if you put it in a loop.
`test` file:
```
1
2
3
4
5
````.gp.md` file:
````
If user says hello, please respond with:```
Ahoy there!
```
````calling gp.nvim from terminal/script:
- register autocommand to save and quit nvim when Gp is done
- second jumps to occurrence of something I want to rewrite/append/prepend to (in this case number `3`)
- selecting the line
- calling gp.nvim acction```
$ nvim --headless -c "autocmd User GpDone wq" -c "/3" -c "normal V" -c "GpAppend hello there" test
```resulting `test` file:
```
1
2
3
Ahoy there!
4
5
```# Shortcuts
There are no default global shortcuts to mess with your own config. Below are examples for you to adjust or just use directly.
## Native
You can use the good old `vim.keymap.set` and paste the following after `require("gp").setup(conf)` call
(or anywhere you keep shortcuts if you want them at one place).```lua
local function keymapOptions(desc)
return {
noremap = true,
silent = true,
nowait = true,
desc = "GPT prompt " .. desc,
}
end-- Chat commands
vim.keymap.set({"n", "i"}, "c", "GpChatNew", keymapOptions("New Chat"))
vim.keymap.set({"n", "i"}, "t", "GpChatToggle", keymapOptions("Toggle Chat"))
vim.keymap.set({"n", "i"}, "f", "GpChatFinder", keymapOptions("Chat Finder"))vim.keymap.set("v", "c", ":'<,'>GpChatNew", keymapOptions("Visual Chat New"))
vim.keymap.set("v", "p", ":'<,'>GpChatPaste", keymapOptions("Visual Chat Paste"))
vim.keymap.set("v", "t", ":'<,'>GpChatToggle", keymapOptions("Visual Toggle Chat"))vim.keymap.set({ "n", "i" }, "", "GpChatNew split", keymapOptions("New Chat split"))
vim.keymap.set({ "n", "i" }, "", "GpChatNew vsplit", keymapOptions("New Chat vsplit"))
vim.keymap.set({ "n", "i" }, "", "GpChatNew tabnew", keymapOptions("New Chat tabnew"))vim.keymap.set("v", "", ":'<,'>GpChatNew split", keymapOptions("Visual Chat New split"))
vim.keymap.set("v", "", ":'<,'>GpChatNew vsplit", keymapOptions("Visual Chat New vsplit"))
vim.keymap.set("v", "", ":'<,'>GpChatNew tabnew", keymapOptions("Visual Chat New tabnew"))-- Prompt commands
vim.keymap.set({"n", "i"}, "r", "GpRewrite", keymapOptions("Inline Rewrite"))
vim.keymap.set({"n", "i"}, "a", "GpAppend", keymapOptions("Append (after)"))
vim.keymap.set({"n", "i"}, "b", "GpPrepend", keymapOptions("Prepend (before)"))vim.keymap.set("v", "r", ":'<,'>GpRewrite", keymapOptions("Visual Rewrite"))
vim.keymap.set("v", "a", ":'<,'>GpAppend", keymapOptions("Visual Append (after)"))
vim.keymap.set("v", "b", ":'<,'>GpPrepend", keymapOptions("Visual Prepend (before)"))
vim.keymap.set("v", "i", ":'<,'>GpImplement", keymapOptions("Implement selection"))vim.keymap.set({"n", "i"}, "gp", "GpPopup", keymapOptions("Popup"))
vim.keymap.set({"n", "i"}, "ge", "GpEnew", keymapOptions("GpEnew"))
vim.keymap.set({"n", "i"}, "gn", "GpNew", keymapOptions("GpNew"))
vim.keymap.set({"n", "i"}, "gv", "GpVnew", keymapOptions("GpVnew"))
vim.keymap.set({"n", "i"}, "gt", "GpTabnew", keymapOptions("GpTabnew"))vim.keymap.set("v", "gp", ":'<,'>GpPopup", keymapOptions("Visual Popup"))
vim.keymap.set("v", "ge", ":'<,'>GpEnew", keymapOptions("Visual GpEnew"))
vim.keymap.set("v", "gn", ":'<,'>GpNew", keymapOptions("Visual GpNew"))
vim.keymap.set("v", "gv", ":'<,'>GpVnew", keymapOptions("Visual GpVnew"))
vim.keymap.set("v", "gt", ":'<,'>GpTabnew", keymapOptions("Visual GpTabnew"))vim.keymap.set({"n", "i"}, "x", "GpContext", keymapOptions("Toggle Context"))
vim.keymap.set("v", "x", ":'<,'>GpContext", keymapOptions("Visual Toggle Context"))vim.keymap.set({"n", "i", "v", "x"}, "s", "GpStop", keymapOptions("Stop"))
vim.keymap.set({"n", "i", "v", "x"}, "n", "GpNextAgent", keymapOptions("Next Agent"))-- optional Whisper commands with prefix w
vim.keymap.set({"n", "i"}, "ww", "GpWhisper", keymapOptions("Whisper"))
vim.keymap.set("v", "ww", ":'<,'>GpWhisper", keymapOptions("Visual Whisper"))vim.keymap.set({"n", "i"}, "wr", "GpWhisperRewrite", keymapOptions("Whisper Inline Rewrite"))
vim.keymap.set({"n", "i"}, "wa", "GpWhisperAppend", keymapOptions("Whisper Append (after)"))
vim.keymap.set({"n", "i"}, "wb", "GpWhisperPrepend", keymapOptions("Whisper Prepend (before) "))vim.keymap.set("v", "wr", ":'<,'>GpWhisperRewrite", keymapOptions("Visual Whisper Rewrite"))
vim.keymap.set("v", "wa", ":'<,'>GpWhisperAppend", keymapOptions("Visual Whisper Append (after)"))
vim.keymap.set("v", "wb", ":'<,'>GpWhisperPrepend", keymapOptions("Visual Whisper Prepend (before)"))vim.keymap.set({"n", "i"}, "wp", "GpWhisperPopup", keymapOptions("Whisper Popup"))
vim.keymap.set({"n", "i"}, "we", "GpWhisperEnew", keymapOptions("Whisper Enew"))
vim.keymap.set({"n", "i"}, "wn", "GpWhisperNew", keymapOptions("Whisper New"))
vim.keymap.set({"n", "i"}, "wv", "GpWhisperVnew", keymapOptions("Whisper Vnew"))
vim.keymap.set({"n", "i"}, "wt", "GpWhisperTabnew", keymapOptions("Whisper Tabnew"))vim.keymap.set("v", "wp", ":'<,'>GpWhisperPopup", keymapOptions("Visual Whisper Popup"))
vim.keymap.set("v", "we", ":'<,'>GpWhisperEnew", keymapOptions("Visual Whisper Enew"))
vim.keymap.set("v", "wn", ":'<,'>GpWhisperNew", keymapOptions("Visual Whisper New"))
vim.keymap.set("v", "wv", ":'<,'>GpWhisperVnew", keymapOptions("Visual Whisper Vnew"))
vim.keymap.set("v", "wt", ":'<,'>GpWhisperTabnew", keymapOptions("Visual Whisper Tabnew"))
```## Whichkey
Or go more fancy by using [which-key.nvim](https://github.com/folke/which-key.nvim) plugin:
```lua
require("which-key").add({
-- VISUAL mode mappings
-- s, x, v modes are handled the same way by which_key
{
mode = { "v" },
nowait = true,
remap = false,
{ "", ":'<,'>GpChatNew tabnew", desc = "ChatNew tabnew" },
{ "", ":'<,'>GpChatNew vsplit", desc = "ChatNew vsplit" },
{ "", ":'<,'>GpChatNew split", desc = "ChatNew split" },
{ "a", ":'<,'>GpAppend", desc = "Visual Append (after)" },
{ "b", ":'<,'>GpPrepend", desc = "Visual Prepend (before)" },
{ "c", ":'<,'>GpChatNew", desc = "Visual Chat New" },
{ "g", group = "generate into new .." },
{ "ge", ":'<,'>GpEnew", desc = "Visual GpEnew" },
{ "gn", ":'<,'>GpNew", desc = "Visual GpNew" },
{ "gp", ":'<,'>GpPopup", desc = "Visual Popup" },
{ "gt", ":'<,'>GpTabnew", desc = "Visual GpTabnew" },
{ "gv", ":'<,'>GpVnew", desc = "Visual GpVnew" },
{ "i", ":'<,'>GpImplement", desc = "Implement selection" },
{ "n", "GpNextAgent", desc = "Next Agent" },
{ "p", ":'<,'>GpChatPaste", desc = "Visual Chat Paste" },
{ "r", ":'<,'>GpRewrite", desc = "Visual Rewrite" },
{ "s", "GpStop", desc = "GpStop" },
{ "t", ":'<,'>GpChatToggle", desc = "Visual Toggle Chat" },
{ "w", group = "Whisper" },
{ "wa", ":'<,'>GpWhisperAppend", desc = "Whisper Append" },
{ "wb", ":'<,'>GpWhisperPrepend", desc = "Whisper Prepend" },
{ "we", ":'<,'>GpWhisperEnew", desc = "Whisper Enew" },
{ "wn", ":'<,'>GpWhisperNew", desc = "Whisper New" },
{ "wp", ":'<,'>GpWhisperPopup", desc = "Whisper Popup" },
{ "wr", ":'<,'>GpWhisperRewrite", desc = "Whisper Rewrite" },
{ "wt", ":'<,'>GpWhisperTabnew", desc = "Whisper Tabnew" },
{ "wv", ":'<,'>GpWhisperVnew", desc = "Whisper Vnew" },
{ "ww", ":'<,'>GpWhisper", desc = "Whisper" },
{ "x", ":'<,'>GpContext", desc = "Visual GpContext" },
},-- NORMAL mode mappings
{
mode = { "n" },
nowait = true,
remap = false,
{ "", "GpChatNew tabnew", desc = "New Chat tabnew" },
{ "", "GpChatNew vsplit", desc = "New Chat vsplit" },
{ "