A deep dive into how I built the ai_commit plugin for Neovim (nvim) using asynchronous system subprocesses, specialized prompt engineering, and external LLM APIs with zero heavy language wrappers.
Introduction & Repository
Writing meaningful, informative, and standardized git commit messages is one of those daily disciplines that keeps codebases healthy—but it's also a point of constant friction. Developers often write rushed commits like fix issue, wip, or refactor some stuff to move on quickly, which turns the git history into an uninformative timeline.
While there are many CLI utilities and heavy node-based wrappers that solve this problem externally, doing so requires switching context away from your editor. As an avid Neovim user, I wanted a solution integrated directly into my editing workspace.
The plugin is fully open-source and easy to integrate:
👉 Official GitHub Repository: https://github.com/klusekP/ai_commit
The goal was clear: select or stage files, type a simple Neovim command (like :AICommit), and let Neovim automatically request an intelligent, conventional-commit-formatted message based on the actual code diff and inject it right into Neovim's active commit buffer.
1. Core Plugin Architecture
To build a native Neovim plugin, we need to balance performance with user experience. A naive synchronous HTTP request would freeze Neovim completely during the network round-trip, resulting in a laggy editor.
Therefore, ai_commit uses a fully asynchronous subprocess model. Here is the operational pipeline:
-
Change Detection: The plugin verifies if there are staged changes by spawning
git diff --staged --name-onlyas an asynchronous background job. -
Diff Ingestion: If changes are staged, it executes
git diff --stagedto harvest the precise code additions and deletions. - Payload Construction: It bundles the raw diff with a highly tailored system prompt, ensuring the LLM formats the response strictly following the Conventional Commits specification.
-
Asynchronous Dispatch: Rather than relying on a heavy Python, Node, or Rust binary, Neovim spawns a non-blocking
curlprocess in the background. -
Buffer Injection: Once
curlexits with a successful response, Neovim processes the JSON, extracts the commit message, and populates either the active commit buffer or opens a floating menu for user review.
2. Technical Prerequisites & Dependencies
One of the core design goals of ai_commit was minimal bloat. To install and run the plugin, only a handful of ubiquitous system dependencies are required:
- Neovim (v0.8.0 or newer): Required for modern Lua APIs, job control spawning, and floating buffer manipulation.
-
Git CLI: The system must have
gitinstalled in the system PATH, as the plugin runs subprocesses to retrieve diff metadata. -
curl (System Utility): The plugin issues direct network requests using the standard
curlcommand-line tool. This avoids needing external Lua HTTP socket libraries. - Plenary.nvim (Optional but Standard): Used for elegant asynchronous job coordination and cleaner network requests. Most Neovim users already have Plenary as a dependency for Telescope.
- API Access Token: A valid API key for your chosen provider (such as OpenAI, Anthropic, Gemini, or a locally hosted Ollama instance running llama3).
3. Complete Installation & Configuration Guide
Integrating ai_commit into your Neovim configuration is simple and supports standard modern package managers.
Installation via lazy.nvim
Add this plugin declaration block directly to your plugins directory:
-- Neovim Plugin Integration using lazy.nvim
return {
"klusekP/ai_commit",
dependencies = { "nvim-lua/plenary.nvim" }, -- optional helper library
config = function()
require("ai_commit").setup({
provider = "openai", -- Options: "openai" | "gemini" | "ollama"
model = "gpt-4o-mini", -- Model alias to invoke
prompt_language = "en", -- Output language: "en" (English) or "pl" (Polish)
temperature = 0.2, -- Generative creativity scale
key_maps = {
generate = "<leader>ca", -- Custom keymap to trigger generation
}
})
end
}
4. Provider Setup & Custom Prompts
The plugin is highly versatile and lets you leverage cloud APIs or locally run LLMs with ease.
-
Authentication: The plugin reads your API tokens directly from your environment variables, keeping your secrets out of your public dotfile repository (e.g., loads
$OPENAI_API_KEYor$GEMINI_API_KEY). -
Ollama (Local Offline Mode): Set
provider = "ollama"and configure your local address (usuallyhttp://localhost:11434) to run completely offline without an internet connection. -
Polish Language Output: Setting
prompt_language = "pl"forces the system prompt to instruct the model to write clean, polish conventional commits, which is rare in other standard tools.
5. Command API & Keybindings
Once configured, the plugin exposes clean, native command-line bindings:
-
:AICommit- Triggers the asynchronous diff capture, calls the LLM, and populates the current buffer with the conventional commit message. - Keymaps configured under
key_maps.generate(such as<leader>ca) let you execute this workflow instantly with simple keystrokes.
This ensures a rapid cycle where you stage changes with your preferred git UI (like fugitive or neogit), open your commit message editor, hit your keymap, and immediately commit your changes.
Conclusion
By bypassing heavy external runtimes and leveraging Neovim's built-in asynchronous job control coupled with direct API endpoints, ai_commit provides a lightning-fast, zero-bloat workflow that keeps your git timeline pristine and readable.
Check out the repository, install it today, and start generating professional, structured commit messages in your preferred language!
🔗 Star the project on GitHub: https://github.com/klusekP/ai_commit
Top comments (4)
How do you handle large git diffs that exceed the LLM's context window while still generating accurate commit messages?
Just yesterday, I generated a commit message for a change set containing around 305 small modifications. I did not have issues with the accuracy or structure of the response, since that largely depends on how the prompt and diff-processing strategy are configured.
However, when using a local model-mlx-community/Qwen2.5-Coder-14B-Instruct-4bit—the generation time was noticeably longer than for smaller diffs. For very large changes, I would split the diff into logical chunks, summarize each chunk separately, and then generate the final commit message from those summaries rather than passing the entire raw diff at once.
In the future, I also plan to extend the plugin with project-specific skills that provide architectural, domain, and repository context. This should allow the model to understand the purpose of the changes more accurately and generate more precise commit messages without relying only on the diff itself.
I like the idea of project-specific skills. Do you think they'll have a bigger impact on commit quality than simply using a larger model?
I think project-specific skills will have a bigger impact. A larger model can better summarize a diff, but it still lacks project context.