Model or dataset
piersolenski/wtf.nvim avatar
piersolenski/wtf.nvim

wtf.nvim: what does this diagnostic mean, and how do I fix it

Delicious diagnostic debugging in Neovim 🤤

607 stars16 forksLuaWTFPL

At a glance

What is it?
wtf.nvim is a Neovim plugin that takes the diagnostic on the line under your cursor, sends it to an AI provider or a web search, and offers an explanation or an applied fix without leaving the editor. It supports seven providers, three picker plugins and a set of commands that work in normal and visual mode.
Who is it for?
wtf.nvim suits a Neovim user who already lives in LSP diagnostics and wants a one keystroke route to an explanation or a patch rather than a copy and paste into a chat window, and the per provider model settings make it usable across several services.
Can I use it commercially?
Yes. WTFPL is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 143 days ago.
What is it written in?
Mainly Lua, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The plugin asks about the line you are on

wtf.nvim exists for a specific moment: a hint, a warning or an error has appeared in a buffer with language server support, and the message tells you that something is wrong without telling you what. The plugin sends the line's diagnostic messages along with contextual information, and the context matters as much as the message. The offending code, the file type and the severity level all travel with it, which is what lets a provider give an answer about your code rather than a generic description of the error class. It works with any language that has LSP support in Neovim, and there is no per-language configuration, because everything it knows comes from the diagnostic itself.

The project describes itself with a joke title, and the joke is the point of the plugin: a buffer full of red text and no idea what it means. Three things happen when you invoke it, and they are separate commands rather than one command with a flag.

Explain, fix, or search

The explain path sends the code and the diagnostic messages for a line, or for a visual range, to the configured provider, and the answer arrives with a solution, tailored to the code that produced the message. The fix path takes the same input and instead of describing the problem it attempts to change your code, which is the feature the documentation presents with the least ceremony and the most risk. The search path avoids the model entirely: instead of copying a message out of the buffer, the plugin opens a web search for it in Google, Stack Overflow and other engines, from inside the editor.

All of them accept extra instructions. The command that explains accepts them, the command that fixes accepts them, and the examples in the documentation show what that is for, such as telling the plugin that you are using Node.js, or asking for camel casing in a fix. This is the escape hatch for when the default prompt does not know enough about your project, and it is cheap to use because it is a single argument. The commands work in normal and visual mode, so you can select a range and ask about the whole block rather than a single line.

Seven providers behind one setting

The provider list is the practical feature: Anthropic, Copilot, DeepSeek, Gemini, Grok, Ollama and OpenAI, with Ollama being the local option. The choice is a single setting in the configuration, and a command exists to pick a provider at runtime, which is useful when you are moving between a paid hosted model and a local one depending on how sensitive the file is. Each provider entry takes its own API key and its own model id, so you can keep several configured at once and switch between them without editing your configuration file.

The other integration point is the picker. Three picker plugins are supported, Telescope, Snacks and FZF-lua, and the choice is a configuration option rather than a hard dependency, with the documentation asking you to pick one. The picker is used for history search, and the commands list includes a key that populates the quickfix list with previous chat history and another that greps that history through the picker, so the plugin keeps a record of your conversations and lets you search them later. That history lives on disk in a chat directory under your Neovim data path, which is worth knowing about if you keep sensitive code on the machine.

Installing it with lazy.nvim

The plugin needs two dependencies and a picker of your choice. The lazy.nvim specification wires the keys up in the usual way, and the diagnostic and fix commands are the first two entries:

lua
    {
      "<leader>wd",
      mode = { "n", "x" },
      function()
        require("wtf").diagnose()
      end,
      desc = "Debug diagnostic with AI",
    },

The rest of the specification covers the remaining actions, with a key for the web search, a key to pick the provider, a key that lists previous chat history in the quickfix list, a key that greps that history with the picker, and a key that yanks the diagnostic to the clipboard. The dependencies are plenary.nvim and nui.nvim, and one of the three picker plugins is listed as optional, with the comment asking you to pick exactly one since the history search needs it. A packer.nvim specification is given as well for people who have not moved to lazy, and it is much shorter, because the keys are not declared in the package manager specification there and the plugin is set up with a call to its setup function instead.

API keys belong in the environment

To use the AI features you need a key for the provider you chose, and the documentation shows them as environment variables, one block per provider:

sh
// Anthropic
export ANTHROPIC_API_KEY=your-api-key

// DeepSeek
export DEEPSEEK_API_KEY=your-api-key

// Gemini
export GEMINI_API_KEY=your-api-key

// Grok
export GROK_API_KEY=your-api-key

// OpenAI
export OPENAI_API_KEY=your-api-key

The keys can also be set or overridden in the plugin configuration, but the project says environment variables are recommended, and the reason is not hard to guess: a key in a shell profile stays out of a configuration file that ends up in a dotfiles repository or on a screen share. The configuration alternative takes a literal string or a function that reads the environment, so you can keep the indirection if you prefer.

The configuration surface in one block

Everything is configured through a single table passed to the setup function, and the first entries deal with where things live and how they look:

lua
{
  -- Directory for storing chat files
  chat_dir = vim.fn.stdpath("data"):gsub("/$", "") .. "/wtf/chats",
  -- Default AI popup type
  popup_type = "popup" | "horizontal" | "vertical",
  -- The default provider
  provider = "anthropic" | "copilot" | "deepseek" | "gemini" | "grok" | "ollama" | "openai",
  -- Configure providers
  providers = {
    anthropic = {
      -- An alternative way to set your API key
      api_key = "32lkj23sdjke223ksdlfk" | function() os.getenv("API_KEY") end,
      -- Your preferred model
      model_id = "claude-3-5-sonnet-20241022",
    },

The rest of the table covers the response language, any additional instructions to append to every request, the default search engine, the picker used for history search, a pair of hooks for request start and request finish, and a winhighlight string for custom colours. The language and additional instructions settings are the two that change the output most, since they are prepended to every request the plugin makes.

How the plugin is tested

The test setup shows how a plugin that calls language models is validated. The Makefile defines a test target that first tries to run a setup script for a local Ollama instance and prints that Ollama is ready, and if the setup fails it prints that Ollama is not available and skips those tests instead of failing the run. The model used locally is set by a variable that defaults to tinyllama, chosen because it is small enough to pull quickly on a developer machine. The test suite itself runs headless Neovim with the plugin disabled, a minimal init file, and the Plenary busted directory runner.

The rest of the tooling is conventional for a Lua project: stylua for formatting with a configuration file in the repository root, luacheck for linting the lua directory, and a scripts directory holding the Ollama setup and cleanup scripts that the Makefile makes executable before the run. The last push was on 2026-06-02 and the repository publishes no tagged releases, and the licence is the WTFPL, which is about as permissive as a licence gets and is a reasonable choice for a plugin of this size.

Editorial conclusion

wtf.nvim suits a Neovim user who already lives in LSP diagnostics and wants a one keystroke route to an explanation or a patch rather than a copy and paste into a chat window, and the per provider model settings make it usable across several services. It is the wrong choice for an offline or air gapped setup unless you run Ollama yourself, because the default paths send your code to a hosted provider, and it is the wrong choice if you want the fix reviewed by a human before it lands. Before adopting it, decide which provider you trust with source code and set the key as an environment variable rather than in your config file, since the project recommends that, and read the configuration options for language and additional instructions, since they change every answer the plugin gives you. The licence is WTFPL and the repository publishes no tagged releases, so pin a commit if you want the behaviour to stay put.

Frequently asked questions

How do I install wtf.nvim?

The project documents a lazy.nvim specification and a packer.nvim specification. Either way you need plenary.nvim and nui.nvim, and one of the three supported pickers, Telescope, Snacks or FZF-lua, for the history search feature.

Which AI providers does wtf.nvim support?

Anthropic, Copilot, DeepSeek, Gemini, Grok, Ollama and OpenAI. The default provider is a configuration setting, each provider has its own API key and model id, and a command exists to pick one at runtime.

How do I set the API key for my provider?

The documentation shows the keys as environment variables such as ANTHROPIC_API_KEY, DEEPSEEK_API_KEY, GEMINI_API_KEY, GROK_API_KEY and OPENAI_API_KEY, and says environment variables are recommended. You can also set or override the key in the configuration, as a literal string or a function that reads the environment.

What is the difference between :Wtf and :WtfFix?

Both send the code and the diagnostic messages for a line or visual range to the provider and both accept extra instructions. The difference is the outcome: Wtf explains the issue, while WtfFix attempts to fix it instead of describing it.

Which search engines can wtf.nvim use for a diagnostic?

The default search engine is a configuration option and can be set to google, duck_duck_go, stack_overflow, github, phind or perplexity. The plugin can also override it per invocation by passing an option to the search command.

Official sources

  1. Issues
  2. License: WTFPL
  3. piersolenski/wtf.nvim on GitHub
  4. README
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/piersolenski-wtf-nvim.svg)](https://hysenlabs.com/projects/piersolenski-wtf-nvim)

Was this useful?