A minimal, portable Neovim config for editing code by hand in the age of coding agents - a surgical instrument, not an IDE.
Startuptime: ~95ms (opening a file, 26 of 34 plugins loaded)
Based on the actual CPU time of the Neovim process till UIEnter (:Lazy profile).
Most Neovim configurations pretend like they are being used to get actual work done. This one is - but the work has changed.
Most code is now generated by agents (I use Claude Code and Codex), so Neovim's job is no longer to be an IDE you write all your code in. What makes it good today is that it's a surgical instrument: fast, precise and predictable for reading, navigating and making exact edits by hand - reviewing what an agent wrote, fixing the one line it got wrong, untangling a diff.
A surgical instrument only stays precise if it's kept that way, so this config is intentionally:
- Minimal - every plugin has to earn its place for hand-editing. Neovim built-ins win over plugins, and anything an agent does better - running tests, REST calls, database queries, large refactors, AI completion - stays out.
- Cross-platform and portable - nothing tied to one machine, codebase or stack. A fresh install bootstraps itself, and every tool beyond the basics is optional and skipped when it's missing.
Everything here comes from daily use - if something breaks or slows me down, it gets fixed or removed.
- 34 plugins across 25 plugin files
- Neovim 0.12 built-ins first - LSP keymaps and floats,
gccommenting (JSX-aware through tree-sitter),:Undotree,an/inincremental selection, LSP document colors - Navigation - snacks.nvim picker (files, grep, buffers, LSP symbols, diagnostics, GitHub issues and PRs), editable directory buffers via canola.nvim, Harpoon marks
- 21 language servers configured, each one starts only when its binary is installed (globally or in the project's
node_modules/.bin) - Format on save with conform.nvim (biome, prettier, stylua, ruff, rustfmt, shfmt, taplo)
- Linting with nvim-lint - missing linters are skipped, and project-convention linters (commitlint, vale) only run when the project has their config
- Completion with blink.cmp - LSP, friendly-snippets and personal snippets
- Git - gitsigns hunks and blame, lazygit
- Agent-friendly - files changed on disk reload automatically in open buffers
TypeScript/JavaScript, React/Vue/Astro, Python, Rust, Lua, Bash, HTML/CSS/Tailwind, Markdown, TOML, YAML, JSON, Docker, GraphQL, Prisma, and Graphviz DOT.
- Neovim >= 0.12
- git, unzip
- ripgrep - used by the snacks.nvim picker for live grep
- Nerd Font - provides icons used throughout the UI (
vim.g.have_nerd_fontis set totrue) - Clipboard tool -
pbcopy/pbpaste(macOS),xclip/xsel(Linux), orwin32yank(Windows)
Everything below is skipped gracefully when it's missing:
- tree-sitter CLI and a C compiler - compile tree-sitter parsers
- Node.js & npm - most LSP servers and formatters installed through Mason
- cargo - builds blink.cmp's native fuzzy matcher (falls back to the Lua matcher)
- lazygit - git UI, integrated via snacks.nvim (
<leader>lg) - fd - faster file finding for the picker
- mise - its shims are prepended to
PATHwhen present - Language runtimes - Deno, Python 3, Rust toolchain for those languages
-
Clone this repository:
git clone https://github.com/ph1losof/nvim ~/.config/nvim -
Open Neovim - lazy.nvim installs the plugins, and Mason installs the core tools (
lua-language-server,stylua,shellcheck):nvim
-
Install the LSP servers, formatters and linters you need on this machine - all of them (a custom command defined in this config) or just the ones you use:
:MasonInstallAll :MasonInstall pyright ruff
-
Check that the external tools are available:
:checkhealth config
Leader key is Space. Neovim's default keymaps all work (K, grn, gra, grr, gri, grt, gO, gc, [d/]d, [q/]q, ...), these are the additions. Press <leader> to browse the rest in which-key.
| Key | Description |
|---|---|
<leader>ff |
Find files |
<leader>fw |
Live grep |
<leader><space> |
Open buffers |
<leader>fr |
Resume last picker |
<leader>fh |
Help pages |
<leader>ft |
TODO comments |
<C-n> |
Directory of the current file |
<leader>h1–h4 |
Jump to Harpoon mark 1–4 |
<leader>ha / <leader>hm |
Add buffer to / show Harpoon |
| Key | Description |
|---|---|
<leader>gd |
Go to definition |
<leader>gr |
References |
<leader>gI |
Implementations |
<leader>gt |
Type definition |
<leader>rn |
Rename |
<leader>ca |
Code action |
<leader>ol |
Document symbols (location list) |
<leader>fsd / <leader>fsw |
Document / workspace symbols picker |
<leader>d |
Line diagnostics |
<leader>tg / <leader>tt |
Workspace / buffer diagnostics |
<leader>oi |
Organize imports (TypeScript) |
| Key | Description |
|---|---|
<leader>lg |
Lazygit |
<leader>lf |
Lazygit current file history |
]c / [c |
Next / previous hunk |
<leader>hs / <leader>hr |
Stage / reset hunk |
<leader>hp |
Preview hunk |
<leader>hb |
Blame line |
<leader>fgp / <leader>fgi |
GitHub PRs / issues |
| Key | Description |
|---|---|
<leader>/ |
Toggle comment |
<leader><tab> / <bs> |
Grow / shrink tree-sitter selection |
<leader>m |
Split / join code block (treesj) |
H J K L (visual) |
Move selection |
<leader>sr |
Search & replace across files (grug-far) |
S |
Replace word under cursor |
<leader>p (visual) |
Paste without overwriting the register |
U |
Redo |
+ / - |
Increment / decrement number |
<leader>u |
Undotree |
<leader>yfr |
Copy relative file path |
<leader>z / <leader>Z |
Zen mode / zoom window |
~/.config/nvim/
├── init.lua # Entry point (leader key, loads config modules)
├── lua/
│ ├── config/
│ │ ├── lazy.lua # lazy.nvim bootstrap and setup
│ │ ├── options.lua # Vim options and diagnostics
│ │ ├── mappings.lua # Global keymaps
│ │ ├── autocmds.lua # Autocommands
│ │ └── health.lua # :checkhealth config
│ └── plugins/ # 25 plugin spec files (one per plugin/group)
├── snippets/ # Personal VS Code-style snippets
└── lazy-lock.json # Plugin version lockfile
To the agents. Claude Code and Codex run next to Neovim (tmux pane or a separate terminal) and handle tests, API calls, database queries and large refactors, while Neovim is where I read and edit code by hand. Keeping those tools out is what keeps this config small, fast and the same on every machine.
Neovim 0.11 and 0.12 ship most of what used to need a plugin: LSP keymaps and UI, commenting, undotree, incremental selection, bracket motions, document colors. Built-ins need no install step, don't break on plugin updates, and behave the same everywhere - exactly what a portable config wants.
From a distro to my own config. When I started using Neovim, my starting point was NvChad - that's why some keybindings still feel NvChad-like. The move off it went through kickstart.nvim, which this repo was born from in September 2024. Over time I rewrote nearly everything from scratch, keeping only a few good parts. Huge thanks to kickstart for getting me started in a non-distro direction.
The batteries-included era. Through late 2024 and 2025 the config grew into a full IDE. snacks.nvim replaced telescope and a pile of small plugins, blink.cmp replaced nvim-cmp (after a couple of false starts), and every workflow I had got its own plugin: .env files (I ended up writing ecolog, shelter and cellmode for that), REST requests with kulala, tests with neotest, databases with dadbod, npm versions with package-info, and even patches for plugins that didn't quite do what I needed. A lot of those choices were inspired by LazyVim - whenever I add a plugin, I still look up how the LazyVim repo configures it first. It's a great reference for well-thought-out defaults.
AI moves into the editor. AI followed the same path. Completion went from Supermaven to Copilot to Codeium and back to Supermaven. Chat and inline edits went from avante.nvim to codecompanion.nvim, claudecode.nvim and OpenCode, and finally ThePrimeagen's 99. Each one was better than the last, and each one was bolting an agent onto an editor. At its peak the README proudly said: 70 plugins, 20 LSPs.
Agents got good. Then agentic coding got good enough that bolting it on stopped making sense. In February 2026 I removed claudecode.nvim and started running Claude Code in a tmux pane next to Neovim, and Codex joined it there. With every model release the agents got better at exactly what all those plugins were for: writing the code, running the tests, calling the APIs, querying the databases, doing the large refactors. What was left for Neovim was reading what they wrote and making precise edits by hand.
Neovim caught up too. Meanwhile Neovim kept absorbing what used to need plugins. 0.10 brought built-in commenting. 0.11 brought vim.lsp.config/vim.lsp.enable, default LSP keymaps, unimpaired-style bracket motions and winborder. 0.12 added :Undotree, tree-sitter incremental selection and LSP document colors. On the language side, the native TypeScript 7 server made typescript-tools and its patches unnecessary.
The surgical instrument. So in October 2026 the config went through its biggest cleanup yet: 71 plugins down to 34, built-ins wherever possible, every stack-specific tool handed over to the agents, and every external tool made optional so it runs the same on any machine. It's not a step back - Neovim just has a different job now.
Thanks to the Neovim core team for built-ins that made half of this config unnecessary, to folke for lazy.nvim, snacks.nvim and which-key that hold the rest together, to saghen for blink.cmp, to stevearc for conform.nvim, to lewis6991 for gitsigns, to ThePrimeagen for harpoon (and for 99, which closed out the AI-in-the-editor era), and to every plugin author whose work has ever been in this config - including the ones that aren't here anymore.