Agent Skill — SKILL.md
neovim
Edit, debug, and validate Neovim Lua configurations, and develop, test, and release Neovim Lua plugins. Use for init.lua and plugin-manager work (vim.pack, lazy.nvim), LSP/completion wiring, treesitter, startup behavior, or for building plugins with plenary tests, GitHub Actions CI, vimdoc, and release automation.
Compatibility
Agent Skills-compatible coding agents.SKILL.md
Neovim
Use this skill for Neovim Lua work: a user's configuration (the config path, in references/neovim-config.md) or a plugin you are developing, testing, or releasing. It covers plugin structure, Lua module conventions, testing with plenary.nvim, CI with GitHub Actions, documentation with doc/ files, and release automation.
Use this skill when
- Editing, debugging, or validating a Neovim Lua config (
init.lua,lua/,after/, plugin specs, lockfiles, LSP/completion, treesitter, startup behavior), or a checkout loading the wrong config — followreferences/neovim-config.mdand validate withreferences/neovim-config-runtime-checks.md. - Creating a new Neovim plugin from scratch.
- Adding a feature, command, or keymap to an existing Neovim plugin.
- Writing or debugging tests for a Neovim plugin using plenary.nvim.
- Setting up CI for a Neovim plugin (linting with selene/stylua, testing with plenary, release automation).
- Generating or updating plugin documentation (
doc/<plugin>.txt). - Configuring release automation for a Neovim plugin (tags, changelogs, lazy.nvim compatibility).
- Debugging a runtime error in a Neovim plugin (Lua module loading, autocommands, user commands).
Do not use this skill when
- The task is general Lua scripting outside the Neovim plugin API.
- The main task is writing a README or documentation for a non-Neovim project — use
writing-and-editing.
Routing boundary
| Situation | Use this skill? | Route instead |
|---|---|---|
| New Neovim plugin, starting from scratch | Yes | — |
Adding a :MyCommand user command to an existing plugin |
Yes | — |
| Writing plenary tests for a plugin's Lua module | Yes | — |
| Setting up CI with selene, stylua, and plenary for a plugin repo | Yes | — |
Fixing init.lua config — LSP, keymaps, colorscheme, lazy.nvim specs |
Yes, config path | — |
Debugging why lazy.nvim loads the wrong plugin version |
Yes, config path | — |
Inputs to gather
Required before editing
- The plugin name and repository path.
- Whether the plugin uses
lua/<plugin>/init.luaorlua/<plugin>.luaentry point. - The existing test framework (plenary.nvim) and CI configuration.
Helpful if present
- The existing
Makefiletargets for lint, test, and doc generation. - The plugin's current release process (tags, changelogs, lazy.nvim compatibility).
- Any existing
doc/<plugin>.txtfile.
Only investigate if encountered
- Whether the plugin should use
vim.api.nvim_create_autocmdvsvim.cmd("autocmd ..."). - Whether the plugin needs
vim.treesitterintegration. - Whether the plugin should register with
lazy.nvim's lazy-loading hints.
First move
Before making any edit, list every file you plan to touch and cross-check that list against the request's explicit exclusions ("do not touch docs", "do not restructure modules/workflow", "do not vendor dependencies", etc.). Drop any file from the plan that isn't strictly necessary for the literal request.
If the plugin does not exist yet, scaffold the canonical structure:
lua/<name>/init.lua,plugin/,doc/,Makefile.If the plugin exists, identify the entry point and the specific feature or fix being made.
Check whether tests exist and CI is configured before adding new infrastructure.
Workflow
- Scaffold or locate structure — Use
lua/<plugin>/init.luaas the entry point, optionalplugin/autoload,doc/,tests/, and CI under.github/workflows/. Readreferences/plugin-structure.mdfor the full tree, entry-point and registration patterns. - Configuration defaults — Merge user opts with
vim.tbl_deep_extend("force", ...), keep defaults onM.config, makesetup()idempotent. Details and examples inreferences/plugin-structure.md. - Tests (plenary.nvim) — Add behavior-focused
*_spec.luaundertests/, run viamake testwith aminimal_init.luathat does not load the user's full config. Spec and Makefile patterns inreferences/plugin-structure.md. - CI — Lint with selene + stylua; test on stable and nightly Neovim with plenary checked out via its own
actions/checkoutstep to a../plenary.nvimpath (repository: nvim-lua/plenary.nvim,path: ../plenary.nvim) — fetched at CI time, never committed. Pointminimal_init.lua's runtimepath at that exact path so the workflow's checkout location and the test bootstrap stay in sync. Workflow shapes inreferences/plugin-structure.md. - Documentation — Maintain
doc/<plugin>.txtvimdoc; optionally generate HTML or convert from markdown with panvimdoc/lemmy-help. Templates inreferences/plugin-structure.md. - Release — Tag
v*releases via GitHub Actions; keeplua/anddoc/at repo root for lazy.nvim compatibility. Release workflow inreferences/plugin-structure.md.
Outputs
- A Neovim Lua plugin with the canonical
lua/,doc/, andtests/structure. - User commands and keymaps registered via the Neovim API.
- plenary.nvim tests covering each feature module.
- CI workflows for linting (selene + stylua) and testing (stable + nightly).
- Vimdoc help file in
doc/<plugin>.txt.
Guardrails
Must use
vim.api.nvim_create_user_commandandvim.keymap.setovervim.cmdstrings.Must provide sensible defaults for every configurable option.
Must make
setup()idempotent — safe to call multiple times.Must not load the user's full Neovim config during tests; use
minimal_init.lua.Must not restructure, rewrite, or replace existing modules, CI workflows, or docs that the request did not ask about — add the requested feature and stop.
Must treat any explicit user exclusion (e.g., "do not touch docs", "do not restructure the workflow", "do not vendor dependencies") as an absolute override of this skill's default workflow steps — even when a default step (like updating
doc/<plugin>.txtafter adding a command) would normally apply, skip it if the user excluded it.Must prefer the smallest possible diff for the literal request: add a new file/module where possible, and touch existing files only for the minimal registration point strictly required (e.g., one
require/one command registration line) rather than editing unrelated existing modules.Must not modify plugin source under
lua/<plugin>/when the request is scoped to CI, linting, or test-bootstrap only — CI-only work is limited to.github/workflows/,Makefilelint/test targets, andtests/minimal_init.lua(or an equivalent test bootstrap file). If the plugin truly lacks a hook needed to test it, surface that as a question instead of silently editing source.Must check
plenary.nvimout as a sibling of the plugin repo (../plenary.nvim), and point the test bootstrap's runtimepath at the same path, so the checkout and the bootstrap cannot disagree.Must distinguish "vendoring" (committing a dependency's source into the plugin repo's git history, e.g. a checked-in
plenary.nvim/directory or submodule) from "fetching at CI time" (an ephemeralactions/checkoutorgit clonestep inside a workflow run, nothing written to the repo). A CI workflow that clonesplenary.nvimas a job step is required for tests to run on a fresh runner and is not vendoring — do not skip this step when a request says "do not vendor test dependencies".Must make CI workflows self-sufficient for a fresh runner: explicitly install/checkout every tool the job needs (Neovim stable + nightly via an action like
rhysd/action-setup-vim,styluavia a dedicated action orcargo install stylua, andplenary.nvimvia a secondactions/checkoutstep withrepository: nvim-lua/plenary.nvimand an explicit siblingpath: ../plenary.nvim). Do not assume any tool or dependency is already present on the runner.Should keep each feature in its own
lua/<plugin>/<feature>.luamodule.Should test behavior, not internal implementation details.
Should run CI on both
stableandnightlyNeovim.May use
vim.treesitterAPIs when the plugin works with syntax trees.
Validation
Run
make lint(selene + stylua) and confirm no issues.Run
make testand confirm all plenary tests pass.For CI-only requests, verify the workflow itself installs/checks out every runtime dependency it needs (Neovim, stylua, plenary.nvim) as job steps rather than assuming any are pre-installed — a workflow that only passes because the dev machine already has these cached will fail on a fresh GitHub-hosted runner.
Confirm no dependency (e.g.
plenary.nvim) is committed into the repository's git history — checking it out as an ephemeral CI step is expected and is not vendoring.Open Neovim, run
:help <plugin>and confirm the docs render correctly.Open Neovim, run
:lua require("<plugin>").setup()and confirm no errors.Smoke test:
- should trigger: "Create a new Neovim plugin called
trailblazer.nvimthat adds a:Trailcommand." - should trigger: "Add a
highlightoption and test to my Neovim plugin." - should trigger: "Set up CI with selene, stylua, and plenary for this Neovim plugin repo."
- should trigger: "Fix the LSP configuration in my
init.lua." (config path) - should trigger: "Why does lazy.nvim load the wrong version of this plugin?" (config path)
- should trigger: "Create a new Neovim plugin called
Examples
- "Scaffold a new Neovim plugin called
glimpse.nvimthat previews file contents in a floating window." - "Add a
:Glimpsecommand and<leader>gpkeymap with plenary tests." - "Set up GitHub Actions CI with selene linting and plenary tests on stable and nightly Neovim."
Reference files
references/plugin-structure.md— plugin layout, config defaults, plenary tests, CI workflows, vimdoc, and release automationreferences/neovim-config.md— the config path: user-level Neovim configuration (init.lua, plugin managers, LSP wiring, startup validation).