Neovim Plugin
sonny-neovim is an AI code completion plugin for Neovim. Unlike the desktop and
VS Code clients, it does not attach to the shared supervisor: it runs its own
sonny-daemon and focuses on inline completion rather than agent sessions.
What it does
- Ghost text suggestions — completions appear inline as you type.
- Multiple LLM providers — OpenAI, Anthropic, Ollama, Gemini, Groq, Azure, Bedrock.
- Treesitter integration — uses Neovim’s built-in
vim.treesitterto extract functions, types, and variables, plus the exact construct at the cursor (for examplereturn_statement) and its ancestors, injected into the completion for structural awareness. - Built-in Neovim feature support — running LSP clients (
vim.lsp), diagnostics (vim.diagnostic), quickfix and loclist state, and buffer/version info, injected as an## Editor Contextsection. - Agent instruction files — reads your repo’s
AGENTS.md/CLAUDE.md(provider-aware) and injects them into every completion. - Codebase conventions — injects
.editorconfigstyle settings so generated code matches the project. - Language best practices — per-language guidance for Go, Python, JS/TS, Rust, Lua and others is baked into completions.
- LSP integration — gathers document symbols and diagnostics from language servers (gopls, pyright, tsserver, rust-analyzer, clangd, …).
- Documentation lookup — fetches docs from
go doc,pydoc, and more. - Linter integration — golangci-lint, ESLint, Pylint, Clippy, and more.
- Pre-commit support — parses and runs pre-commit hooks.
Requirements
- Neovim 0.8+
- Go 1.27+ (for building the daemon)
- Network access to
devnw.dev/llm(the backend module), using your Go credentials in~/.netrc
Installation
The Neovim integration is commercial software. Install the package provided
through your authorized CodePros distribution channel, then ensure the daemon
is on your PATH or set daemon_path in setup().
See Licensing and access
.
Configuration
require('sonny').setup({
-- Daemon
daemon_path = 'sonny-daemon', -- path to daemon binary
pack_file = nil, -- optional: path to a recipe YAML
request_timeout_s = 300, -- fail outstanding requests after this long
-- LLM
provider = 'openai',
model = 'gpt-5-mini',
-- Completion behaviour
auto_trigger = true,
trigger_delay_ms = 150, -- debounce
trigger_min_chars = 3,
max_tokens = 256,
num_suggestions = 1,
-- Context gathering
include_related_files = true,
include_open_buffers = true,
use_treesitter = true,
max_related_files = 5,
max_lines_per_related = 50,
max_context_lines = 100,
use_lsp = true,
-- Editor context
include_lsp_context = true, -- running clients + capabilities + folders
include_diagnostics = true, -- vim.diagnostic at cursor + buffer counts
include_quickfix = true,
include_nvim_context = true,
disabled_filetypes = {
'TelescopePrompt', 'neo-tree', 'NvimTree', 'lazy', 'help',
},
mcp_servers = {}, -- see lua/sonny/config.lua for examples
debug = false,
})
max_context_lines is the budget that matters most in practice: it caps the
total context lines sent to the model, so raising max_related_files beyond what
that budget allows just changes what gets evicted, not how much you send.
Keybindings
| Key | Mode | Action |
|---|---|---|
<Tab> | Insert | Accept full suggestion |
<C-Right> | Insert | Accept next word |
<M-Right> | Insert | Accept to end of line |
<C-]> | Insert | Next suggestion |
<C-[> | Insert | Previous suggestion |
<Esc> | Insert | Dismiss suggestions |
<C-Space> | Insert | Manual trigger |
<leader>cc | Normal | Toggle plugin |
All are configurable through keymaps, and :SonnyRebindKeys refreshes them
from config without restarting Neovim.
Commands
| Command | Description |
|---|---|
:SonnyEnable | Enable the plugin |
:SonnyDisable | Disable the plugin |
:SonnyToggle | Toggle the plugin on/off |
:SonnyStatus | Show plugin status |
:SonnyComplete | Trigger completion manually |
:SonnyPack <path> | Switch recipe |
:SonnyProvider <provider> [model] | Switch LLM provider |
:SonnyModel <model> | Switch LLM model |
:SonnyDoc [symbol] | Look up documentation |
:SonnyDocSearch <query> | Search documentation |
:SonnyLint [output] | Run linter (diagnostics/quickfix/loclist) |
:SonnyLintFix | Run linter with auto-fix |
:SonnyLintConfig | Show linter configuration |
:SonnyPreCommit [action] | Pre-commit hooks (info/run/all) |
:SonnyEditorConfig | Show EditorConfig settings |
:SonnyRebindKeys | Refresh keybindings from config |
:SonnyMCP [status|tools|connect|disconnect] [server] | MCP server management |
:SonnyMCPCall <server> <tool> [json_args] | Call an MCP tool |
Lua API
local sonny = require('sonny')
-- Core
sonny.complete() -- request completion
sonny.accept() -- accept current suggestion
sonny.accept_partial('word') -- accept next word
sonny.accept_partial('line') -- accept to end of line
sonny.next_suggestion()
sonny.prev_suggestion()
sonny.dismiss()
sonny.has_suggestion()
-- Configuration
sonny.enable(); sonny.disable(); sonny.toggle(); sonny.status()
sonny.set_pack(path)
sonny.set_provider(name, model)
sonny.set_model(name)
sonny.rebind_keys(keymaps)
-- Documentation
sonny.fetch_doc(symbol, callback)
sonny.search_docs(query, callback)
sonny.doc_at_cursor()
-- Linting
sonny.lint(output)
sonny.lint_fix()
sonny.run_linter(opts, callback)
sonny.get_linter_config(opts, callback)
sonny.run_precommit(opts, callback)
sonny.get_editorconfig(opts, callback)
Source of truth
sonny-neovim/README.mdsonny-neovim/lua/sonny/config.lua(defaults and MCP examples)