# Neovim Config Plugin management uses Neovim's native `vim.pack` (0.12+). No lazy.nvim. ## Layout - `init.lua` — entry point: `require("config")` then `require("plugins")` - `lua/config/*.lua` — core settings (options, keymaps, LSP, autocmds) - `lua/plugins/init.lua` — loads every plugin module in order, hosts `PackChanged` build hooks - `lua/plugins/.lua` — one self-contained module per plugin - `nvim-pack-lock.json` — lock file, auto-updated by `vim.pack`, commit it ## Plugin module structure Each `lua/plugins/.lua` is self-contained: it declares its own `vim.pack.add` specs (including shared deps like `nvim-lua/plenary.nvim`, `nvim-tree/nvim-web-devicons`, `MunifTanjim/nui.nvim` — repeated adds are idempotent), then runs setup/config code directly. All plugins load eagerly at startup; there is no lazy loading. Example: ```lua vim.pack.add({ { src = "https://github.com/owner/repo.nvim" }, }) require("repo").setup({ ... }) ``` ## Adding a new plugin 1. Create `lua/plugins/.lua` with a `vim.pack.add` spec and its config 2. Add `require("plugins.")` to `lua/plugins/init.lua` (theme first if it affects colorscheme/highlights) 3. Restart nvim — the plugin is cloned automatically on first startup ## Spec conventions - `version` — pin to a semver release: `version = vim.version.range("^1")`; use a branch name string (e.g. `version = "main"`) only when the plugin requires it (nvim-treesitter and its textobjects do) - `name` — set only when the repo name is undesirable (e.g. `catppuccin/nvim` is installed as `catppuccin`) - Shared deps listed explicitly in every module that needs them ## Build steps There is no `build` field. Post-install/update steps run from the `PackChanged` autocmd in `lua/plugins/init.lua`, keyed by plugin name. It must stay registered before the first `vim.pack.add` call, so keep new hooks in that file: ```lua -- in the PackChanged callback in lua/plugins/init.lua if name == "my-plugin" and (kind == "install" or kind == "update") then vim.system({ "make" }, { cwd = path }) end ``` Current hooks: `make` for telescope-fzf-native, parser `update` for nvim-treesitter. ## Useful commands `vim.pack` has no `:Pack*` user commands — everything goes through `:lua`. Plugin names are the installed directory names (e.g. `telescope-fzf-native.nvim`). - `:lua vim.pack.update()` — fetch and update all plugins to their pinned version (or latest branch), updating `nvim-pack-lock.json` - `:lua vim.pack.update({ "gitsigns.nvim" })` — update a single plugin - `:lua vim.pack.update(nil, { offline = true })` — update using cached refs, no network fetch - `:lua vim.pack.update(nil, { target = "lockfile" })` — restore all plugins to the revisions in `nvim-pack-lock.json` (rollback) - `:lua vim.pack.get()` — list all installed plugins with spec, path, revision, active status - `:lua vim.pack.add({ { src = "https://github.com/owner/repo.nvim" } })` — install/register a plugin on the fly (restart required to persist in config) - `:lua vim.pack.del("name")` — delete a plugin directory and its lock entry ## Updating / removing - Update everything: `:lua vim.pack.update()` (see commands above for variants) - Remove a plugin: delete its entry from `vim.pack.add` calls and its `lua/plugins/.lua` + require in `lua/plugins/init.lua`, then run `:lua vim.pack.del("name")` to delete the directory and lock entry ## Pitfalls - First startup installs missing plugins and asks for confirmation — expected behavior, not an error - The first `vim.pack.add` call in a session triggers installs, so `PackChanged` hooks must be registered in `lua/plugins/init.lua` before the requires - Only install actual Neovim plugins via `vim.pack` — CLI tools (e.g. `fd`, `ripgrep`) must not be added - Plugins already on disk are not re-checked against their spec on startup; run `vim.pack.update()` to sync revisions