-- .d8888b. 888888 888 888 -- d88P Y88b "88b 888 888 -- 888 888 888 888 888 -- 888 .d88b. 88888b. 88888b. .d88b. 888d888 888 .d88b. 88888b. 88888b. .d8888b 888888 .d88b. 88888b. .d88b. -- 888 d88''88b 888 '88b 888 '88b d88''88b 888P" 888 d88''88b 888 '88b 888 '88b 88K 888 d88''88b 888 '88b d8P Y8b -- 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 'Y8888b. 888 888 888 888 888 88888888 -- Y88b d88P Y88..88P 888 888 888 888 Y88..88P 888 88P Y88..88P 888 888 888 888 X88 Y88b. Y88..88P 888 888 Y8b. -- 'Y8888P" 'Y88P' 888 888 888 888 'Y88P' 888 888 'Y88P' 888 888 888 888 88888P' 'Y888 'Y88P' 888 888 'Y8888 -- .d88P -- .d88P" -- 888P" -- -- ------------------------------------------------------------------------------ -- General Settings -- ------------------------------------------------------------------------------ vim.opt.number = true vim.opt.relativenumber = true vim.opt.tabstop = 4 vim.opt.softtabstop = 4 vim.opt.shiftwidth = 4 vim.opt.expandtab = true vim.opt.splitbelow = true vim.opt.splitright = true vim.opt.scrolloff = 5 vim.opt.sidescrolloff = 5 -- vim.opt.cmdheight = 2 vim.opt.diffopt = "vertical,closeoff,context:6" vim.opt.clipboard = "unnamedplus" vim.opt.mouse = "nv" vim.opt.ignorecase = true vim.opt.smartcase = true vim.opt.spell = false vim.opt.list = true vim.opt.joinspaces = false vim.opt.breakindent = true vim.opt.undofile = true vim.opt.completeopt = "menuone,noselect" vim.opt.termguicolors = true vim.opt.guifont = "RobotoMono Nerd Font:h11" vim.opt.signcolumn = "yes" vim.opt.winborder = "rounded" vim.g.mapleader = " " vim.g.maplocalleader = "," vim.g.leader = " " -- ------------------------------------------------------------------------------ -- PlugIns -- ------------------------------------------------------------------------------ vim.pack.add({ { src = "https://github.com/vague2k/vague.nvim" }, { src = "https://github.com/mason-org/mason.nvim" }, { src = "https://github.com/neovim/nvim-lspconfig" }, { src = "https://github.com/stevearc/oil.nvim" }, { src = "https://github.com/nvim-telescope/telescope.nvim" }, { src = "https://github.com/nvim-lua/plenary.nvim" }, { src = "https://github.com/kdheepak/lazygit.nvim" }, { src = "https://github.com/tpope/vim-surround" }, { src = "https://github.com/tpope/vim-repeat" }, { src = "https://github.com/tommcdo/vim-lion" }, { src = "https://github.com/andythigpen/nvim-coverage" }, { src = "https://github.com/nvim-treesitter/nvim-treesitter" }, -- copilot deliberately off on every machine -- { src = "https://github.com/github/copilot.vim" }, -- ...and this is what replaced it: FIM completion off the local box. { src = "https://github.com/milanglacier/minuet-ai.nvim" }, }) require("mason").setup() require("oil").setup() require("telescope").setup({ pickers = { colorscheme = { enable_preview = true } } }) require("coverage").setup({ auto_reload = true, signs = { covered = { text = "█", texthl = "CoverageCovered" }, uncovered = { text = "█", texthl = "CoverageUncovered" }, partial = { text = "█", texthl = "CoveragePartial" }, }, }) require("nvim-treesitter").setup( { ensure_installed = { "lua", "python", "rust", "markdown", "markdown_inline", "julia" }, highlight = { enable = true }, }) vim.lsp.enable({ "lua_ls", "rust_analyzer", "julials", "pylsp", "black", "marksman", "ruff", "clangd" }) vim.lsp.config("rust_analyzer", { settings = { ["rust-analyzer"] = { cargo = { features = "all" }, } } }) -- ------------------------------------------------------------------------------ -- Local FIM autocomplete (minuet-ai -> LiteLLM -> llama-swap -> qwen3-coder-30b) -- ------------------------------------------------------------------------------ -- -- Replaces copilot, which is off on every machine deliberately. Nothing leaves -- the LAN: the model runs on mainframe's 3060 and `llm.rcjohnstone.com` has no -- public A record -- internal CA, and Caddy source-IP gates it to 10.0.0.0/24, -- 10.0.1.0/24 and the podman subnets. -- -- ⚠ ROUTE: `/v1/completions`, NOT `/infill`. Measured 2026-09-18 through the -- proxy: `/v1/completions` 200 with FIM tokens in a raw prompt, `/infill` 404 -- because LiteLLM does not expose it. `llama.vim` wants `/infill`, which would -- mean talking to llama-server directly and bypassing the virtual key -- and on -- the work laptop that key IS the policy boundary that makes Anthropic and PRC -- models 403. Not worth routing around for an editor plugin. -- -- ⚠ WHY qwen3-coder-30b: it is the best local FIM model by measurement (0.511 -- exact / 0.826 similarity against qwen3-next-80b's 0.302), and FIM is a -- VOCABULARY feature -- gpt-oss-20b and gemma3-12b have no FIM tokens and -- cannot do this at all. Its "skipped for capacity" verdict was about the 128k -- agentic floor and does not bind here; autocomplete needs a few thousand -- tokens of context, not 128k. -- -- ⚠ EXPECT AN OCCASIONAL MULTI-SECOND PAUSE. llama-swap runs `-np 1` with a -- ttl, so if another model was used in between, the next completion pays a -- full reload. Measured: ~10 s cold, 0.7-1.3 s warm. Accepted deliberately -- -- concurrent agentic work is not viable on this hardware anyway. -- ⚠ ALWAYS call setup(). The first version of this guarded the whole require() -- behind "is the key present", which meant that with no key the module never -- loaded, `:Minuet` was never registered, and nvim gave NO indication why -- -- the exact failure it hit in practice. `:Minuet` is created at module load, -- not inside setup(), and the plugin already notifies clearly when the key is -- missing. Let it do that job instead of silently doing nothing. -- -- The key resolves lazily and is cached: env var first, then rbw, matching the -- opencode convention of reading keys at launch rather than writing them to -- disk. `false` is cached for "looked and found nothing" so a locked rbw costs -- one probe per session rather than one per keystroke. local _minuet_key local function minuet_key() if _minuet_key ~= nil then return _minuet_key or nil end local k = vim.env.LITELLM_KEY_NVIM if k and k ~= "" then _minuet_key = k return k end if vim.fn.executable("rbw") == 1 then vim.fn.system({ "rbw", "unlocked" }) if vim.v.shell_error == 0 then -- ⚠ The work laptop MUST use the work key: it is enforced -- server-side to 403 Anthropic and PRC-origin models, and a -- personal key here would silently remove that guarantee for -- everything nvim sends. local host = vim.uv.os_gethostname() or "" local entry = host:match("sctfw004") and "litellm-key-work" or "litellm-key-personal" local out = vim.fn.system({ "rbw", "get", entry }) if vim.v.shell_error == 0 and vim.trim(out) ~= "" then _minuet_key = vim.trim(out) return _minuet_key end end end _minuet_key = false return nil end local minuet_base = vim.env.LITELLM_BASE if not minuet_base or minuet_base == "" then minuet_base = "https://llm.rcjohnstone.com" end require("minuet").setup({ provider = "openai_fim_compatible", -- Default is 'warn', which hides request failures. If completions stop -- appearing, set this to 'debug' and the reason shows up in `:messages`. notify = "warn", -- Ghost text: there is no completion engine here (no nvim-cmp, no blink) -- and virtual text is the copilot-shaped one. virtualtext = { auto_trigger_ft = { "python", "rust", "lua", "c", "cpp", "julia", "sh" }, keymap = { accept = "", accept_line = "", prev = "", next = "", dismiss = "", }, }, provider_options = { openai_fim_compatible = { -- A FUNCTION, not an env var name: this is what lets the key come -- from rbw when the shell did not export one. api_key = minuet_key, name = "litellm", end_point = minuet_base .. "/v1/completions", model = "qwen3-coder-30b", -- ⚠ KEEP THIS SMALL. Measured on the real path at 48: `def add` -- came back with `sub` and `mul` attached, so most of the latency -- was spent generating text that would be discarded. Raise it only -- if completions are being truncated mid-expression. optional = { max_tokens = 32, top_p = 0.9, stop = { "<|fim_pad|>", "<|endoftext|>", "\n\n" }, }, -- ⚠ THREE POSITIONAL ARGS, not a context table. minuet calls -- this as `template.prompt(before, after, opts)` -- the first -- version took `(ctx)` and died with "attempt to concatenate -- field 'lines_after' (a nil value)" on the first real -- completion, while loading perfectly cleanly. A config that -- loads is not a config that works. -- -- `suffix = false` because the whole FIM prompt is built here; -- letting minuet also send a `suffix` field would give the server -- two competing descriptions of the same gap. template = { prompt = function(before, after, _opts) return "<|fim_prefix|>" .. (before or "") .. "<|fim_suffix|>" .. (after or "") .. "<|fim_middle|>" end, suffix = false, }, }, }, }) -- Because "is it on, and if not why" should be answerable without reading Lua. vim.api.nvim_create_user_command("MinuetStatus", function() local k = minuet_key() local lines = { "endpoint: " .. minuet_base .. "/v1/completions", "model: qwen3-coder-30b", "key: " .. (k and ("found (" .. #k .. " chars)") or "NOT FOUND"), } if not k then table.insert(lines, "") table.insert(lines, "Set LITELLM_KEY_NVIM in ~/.secrets.zsh, or unlock rbw") table.insert(lines, "(`rbw unlock`) so it can be read from there.") end vim.notify(table.concat(lines, "\n"), k and vim.log.levels.INFO or vim.log.levels.WARN) end, { desc = "Show minuet FIM autocomplete status" }) vim.cmd("colorscheme vague") -- KEYMAPS -- ------------------------------------------------------------------------------- local builtin = require('telescope.builtin') vim.keymap.set("n", "f", vim.lsp.buf.format, { desc = "[f]ormat the current file" }) vim.keymap.set("n", "sr", builtin.oldfiles, { desc = "[s]earch [r]ecent files" }) vim.keymap.set("n", "sf", builtin.find_files, { desc = "[s]earch files" }) vim.keymap.set("n", "sw", builtin.grep_string, { desc = "[s]earch [w]ord with grep" }) vim.keymap.set("n", "sg", builtin.live_grep, { desc = "[s]earch with [g]rep" }) vim.keymap.set('n', "sh", builtin.help_tags, { desc = "[s]earch [h]elp" }) vim.keymap.set('n', "sc", builtin.colorscheme, { desc = "[s]earch [c]olorschemes" }) vim.keymap.set('i', "jk", "", { desc = "Escape, as in [j]ust [k]idding" }) vim.keymap.set('n', "o", ":Oil", { desc = "Run [O]il" }) vim.keymap.set('n', " ", ":nohlsearch", { desc = "Run [O]il" }) vim.keymap.set('n', "n", "nzz", { desc = "Move next search result to center of screen" }) vim.keymap.set('n', "N", "Nzz", { desc = "Move prev search result to center of screen" }) vim.keymap.set('n', "e", vim.diagnostic.open_float, { desc = "Open up the [e]rror" }) vim.keymap.set('n', "lg", ":LazyGitCurrentFile", { desc = "Open up [l]azy [g]it" }) vim.keymap.set('n', "ml", "0dwI ", { desc = "[M]erge lines" }) vim.keymap.set('n', "j", "gj", { desc = "Down visually" }) vim.keymap.set('n', "k", "gk", { desc = "Up visually" }) -- LSP KEYMAPS/CONFIG ------------------------------------------------------------------------------- -- Use LspAttach autocommand to only map the following keys -- after the language server attaches to the current buffer vim.api.nvim_create_autocmd('LspAttach', { group = vim.api.nvim_create_augroup('UserLspConfig', {}), callback = function(ev) -- Enable completion triggered by vim.bo[ev.buf].omnifunc = 'v:lua.vim.lsp.omnifunc' -- Buffer local mappings. -- See `:help vim.lsp.*` for documentation on any of the below functions local opts = { buffer = ev.buf } vim.keymap.set('n', 'gr', builtin.lsp_references, opts) vim.keymap.set('n', 'gd', vim.lsp.buf.definition, opts) vim.keymap.set('n', 'k', vim.lsp.buf.hover, opts) vim.keymap.set('n', 'gi', vim.lsp.buf.implementation, opts) vim.keymap.set('n', '', vim.lsp.buf.signature_help, opts) vim.keymap.set('n', 'wa', vim.lsp.buf.add_workspace_folder, opts) vim.keymap.set('n', 'wr', vim.lsp.buf.remove_workspace_folder, opts) vim.keymap.set('n', 'wl', function() print(vim.inspect(vim.lsp.buf.list_workspace_folders())) end, opts) vim.keymap.set('n', 'D', vim.lsp.buf.type_definition, opts) vim.keymap.set('n', 'rn', vim.lsp.buf.rename, opts) vim.keymap.set({ 'n', 'v' }, 'ca', vim.lsp.buf.code_action, opts) vim.keymap.set('n', 'gr', vim.lsp.buf.references, opts) vim.keymap.set('n', 'f', function() vim.lsp.buf.format { async = true } end, opts) end, })