Pure-Lua Neovim plugin for Cursor-style next-edit predictions using local LLMs.
Lua
146
11 commits
updated Feb 1, 2026
██████╗ ██╗ ██╗███╗ ██╗██╗ ██╗ ███████╗██████╗ ██╗████████╗
██╔══██╗██║ ██║████╗ ██║██║ ██╔╝ ██╔════╝██╔══██╗██║╚══██╔══╝
██████╔╝██║ ██║██╔██╗ ██║█████╔╝ █████╗ ██║ ██║██║ ██║
██╔══██╗██║ ██║██║╚██╗██║██╔═██╗ ██╔══╝ ██║ ██║██║ ██║
██████╔╝███████╗██║██║ ╚████║██║ ██╗ ███████╗██████╔╝██║ ██║
╚═════╝ ╚══════╝╚═╝╚═╝ ╚═══╝╚═╝ ╚═╝ ╚══════╝╚═════╝ ╚═╝ ╚═╝
Pure-Lua Neovim plugin for Cursor-style next-edit predictions using local LLMs.
⚠️ Alpha — Expect breaking changes and rapid iteration ⚠️
blink-edit.nvim predicts your next edit and renders it as ghost text. It sends context-aware prompts to a local LLM and shows predictions inline.
<Tab>, reject with <Esc> in insert mode.{
"BlinkResearchLabs/blink-edit.nvim",
config = function()
require("blink-edit").setup({
llm = {
provider = "sweep",
backend = "openai",
url = "http://localhost:8000",
model = "sweep",
},
})
end,
}
use({
"BlinkResearchLabs/blink-edit.nvim",
config = function()
require("blink-edit").setup({
llm = {
provider = "sweep",
backend = "openai",
url = "http://localhost:8000",
model = "sweep",
},
})
end,
})
require("blink-edit").setup({
llm = {
provider = "sweep",
backend = "openai",
url = "http://localhost:8000",
model = "sweep",
},
})
Model name:
llm.modelmust match an ID returned by your backend (/v1/models). Examples:
- ✅
sweepai/sweep-next-edit-1.5B(if/v1/modelsreturns the HF name)- ✅
/home/.../sweep-next-edit-1.5b.q8_0.v2.gguf(llama.cpp can expose the full path)- ✅
sweep(if you served an alias like--served-model-name sweep)- ❌
sweepwhen/v1/modelsonly exposes the full path
Start typing in Insert mode. Predictions will appear as ghost text.
Control:
<Tab> to Accept the next hunk<C-j> to Accept Line (line-by-line through visible changes)<C-]> to Clear (dismiss without leaving insert mode)<Esc> to Reject and exit insert mode:BlinkEditStatus to check server healthDefault provider. Uses <|file_sep|> delimiters for context sections. Works best with the Sweep Next-Edit model.
llm = { provider = "sweep", backend = "openai", url = "http://localhost:8000", model = "sweep" }
Instruction-style prompt with editable region markers. Supports next-edit mode only.
llm = { provider = "zeta", backend = "openai", url = "http://localhost:8000", model = "zeta" }
Template-based provider. Exists but is not validated. Avoid in production until tested.
Uses /v1/completions. Works with llama.cpp, vLLM, text-generation-webui, and other OpenAI-compatible servers.
Uses /api/generate. Works with any model available through Ollama. See Running a Local Model for setup.
require("blink-edit").setup({
llm = {
backend = "openai", -- "openai" | "ollama"
provider = "sweep", -- "sweep" | "zeta" | "generic"
url = "http://localhost:8000",-- Model server URL
model = "sweep", -- Model name sent to backend
temperature = 0.0, -- Sampling temperature (0 = deterministic)
max_tokens = 512, -- Max tokens to generate
timeout_ms = 5000, -- Request timeout in ms
},
context = {
enabled = true, -- Master switch for context collection
lines_before = nil, -- Lines before cursor (nil = provider default)
lines_after = nil, -- Lines after cursor (nil = provider default)
max_tokens = 512, -- Token budget for context
selection = {
enabled = true, -- Include visual selection in context
max_lines = 10, -- Max lines from selection
},
lsp = {
enabled = true, -- Fetch LSP references for cursor symbol
max_definitions = 2, -- Max definition locations
max_references = 2, -- Max reference locations
timeout_ms = 100, -- LSP request timeout
},
same_file = {
enabled = true, -- Include surrounding lines from same file
max_lines_before = 20, -- Lines above the window
max_lines_after = 20, -- Lines below the window
},
history = {
enabled = false, -- Include recent edit history
max_items = 5, -- Number of history entries
max_tokens = 512, -- Token budget for history
max_files = 2, -- Max files in history
global = true, -- Share history across buffers
},
},
ui = {
progress = true, -- Show "thinking..." indicator
suppress_lsp_floats = true, -- Hide LSP floats while prediction visible
},
prefetch = {
enabled = false, -- Speculative prefetch (uses extra tokens)
strategy = "n-1", -- Prefetch when one hunk remains
},
normal_mode = {
enabled = false, -- Trigger predictions on idle in normal mode
debounce_ms = 200, -- Normal-mode debounce (ms)
},
debounce_ms = 100, -- Delay (ms) before sending prediction request
keymaps = {
insert = {
accept = "<Tab>", -- Accept next hunk
accept_line = "<C-j>", -- Accept next visible line (line-by-line)
clear = "<C-]>", -- Dismiss ghost text (stay in insert mode)
reject = "<Esc>", -- Reject and exit insert mode
},
normal = {
accept = "<Tab>", -- Accept next hunk (when normal_mode.enabled)
accept_line = "<C-j>", -- Accept next visible line (when normal_mode.enabled)
},
},
})
| Key | Action | Description |
|---|---|---|
<Tab> | Accept | Accept the next predicted hunk |
<C-j> | Accept Line | Accept next visible line (line-by-line) |
<C-]> | Clear | Dismiss prediction (stay in insert mode) |
<Esc> | Reject | Reject prediction and exit insert mode |
normal_mode.enabled = true)| Key | Action | Description |
|---|---|---|
<Tab> | Accept | Accept the next predicted hunk |
<C-j> | Accept Line | Accept next visible line (line-by-line) |
Note: Normal mode only maps accept/accept_line; when a prediction is visible, pressing
<Esc>clears it without adding a persistent mapping.
Note: After you start line-by-line accepts with
<C-j>,<Tab>accepts the remaining visible lines.
Note: If you use
blink.cmpornvim-cmp, the<Tab>keymap automatically checks if the completion menu is visible before accepting predictions.
Customization:
Keymaps are organized by mode and can be customized or disabled (set to nil):
require("blink-edit").setup({
keymaps = {
insert = {
accept = "<C-y>", -- Use Ctrl+y to accept
accept_line = "<C-j>", -- Keep default
clear = nil, -- Disable clear keymap
reject = "<C-n>", -- Use Ctrl+n to reject
},
normal = {
accept = "<C-y>", -- Match insert mode binding
accept_line = nil, -- Disable in normal mode
},
},
})
| Command | Description |
|---|---|
:BlinkEditStatus | Show status popup with health, config, and state |
:BlinkEditEnable | Enable predictions |
:BlinkEditDisable | Disable predictions |
:BlinkEditToggle | Toggle predictions on/off |
require("blink-edit").setup(opts) -- Initialize with config
require("blink-edit").enable() -- Enable predictions
require("blink-edit").disable() -- Disable predictions
require("blink-edit").toggle() -- Toggle predictions
require("blink-edit").trigger() -- Manually trigger a prediction
require("blink-edit").accept() -- Accept next hunk
require("blink-edit").accept_line() -- Accept next visible line (line-by-line)
require("blink-edit").clear() -- Clear prediction (stay in insert mode)
require("blink-edit").reject() -- Reject prediction
require("blink-edit").status() -- Get status table
require("blink-edit").health_check() -- Check backend health
:BlinkEditStatus opens a popup showing:
Press r to refresh health, q or <Esc> to close.
:BlinkEditStatus to check if the server is healthy.:LspInfo to verify an LSP is attached to the buffer.:lua vim.print(vim.lsp.buf_request_sync(...)).context.max_tokens or context.lsp.timeout_ms in your config.We check for blink.cmp/nvim-cmp visibility, but you can change keymaps.insert.accept (and keymaps.normal.accept for normal mode) if needed.
vim.g.blink_edit_debug = true -- Summary logs
vim.g.blink_edit_debug = 2 -- Verbose (prompts + responses)
A 1.5B parameter model optimized for next-edit prediction.
Using llama-server:
# Download and run directly
llama-server -hf sweepai/sweep-next-edit-1.5b --port 8000
# Or download manually first
huggingface-cli download sweepai/sweep-next-edit-1.5b \
sweep-next-edit-1.5b.q8_0.v2.gguf --local-dir ./models
llama-server -m ./models/sweep-next-edit-1.5b.q8_0.v2.gguf --port 8000
Config:
llm = { provider = "sweep", backend = "openai", url = "http://localhost:8000", model = "sweep" }
A 7B parameter model from Zed Industries, fine-tuned from Qwen2.5-Coder-7B.
Using vLLM (Recommended):
vllm serve zed-industries/zeta --served-model-name zeta --port 8000
# With optimizations (requires compatible GPU)
vllm serve zed-industries/zeta --served-model-name zeta --port 8000 \
--enable-prefix-caching --quantization="fp8"
Config:
llm = { provider = "zeta", backend = "openai", url = "http://localhost:8000", model = "zeta" }
Using Ollama:
ollama pull lennyerik/zeta
# Runs on port 11434 by default
Config for Ollama:
llm = { provider = "zeta", backend = "ollama", url = "http://localhost:11434", model = "lennyerik/zeta" }
Issues and PRs are welcome! This is an alpha project — your feedback helps shape the future of local AI coding in Neovim.
MIT — see LICENSE.
10 commits
1 commits
Lua
100.0%
Pure-Lua Neovim plugin for Cursor-style next-edit predictions using local LLMs.
Lua
146
11 commits
updated Feb 1, 2026
██████╗ ██╗ ██╗███╗ ██╗██╗ ██╗ ███████╗██████╗ ██╗████████╗
██╔══██╗██║ ██║████╗ ██║██║ ██╔╝ ██╔════╝██╔══██╗██║╚══██╔══╝
██████╔╝██║ ██║██╔██╗ ██║█████╔╝ █████╗ ██║ ██║██║ ██║
██╔══██╗██║ ██║██║╚██╗██║██╔═██╗ ██╔══╝ ██║ ██║██║ ██║
██████╔╝███████╗██║██║ ╚████║██║ ██╗ ███████╗██████╔╝██║ ██║
╚═════╝ ╚══════╝╚═╝╚═╝ ╚═══╝╚═╝ ╚═╝ ╚══════╝╚═════╝ ╚═╝ ╚═╝
Pure-Lua Neovim plugin for Cursor-style next-edit predictions using local LLMs.
⚠️ Alpha — Expect breaking changes and rapid iteration ⚠️
blink-edit.nvim predicts your next edit and renders it as ghost text. It sends context-aware prompts to a local LLM and shows predictions inline.
<Tab>, reject with <Esc> in insert mode.{
"BlinkResearchLabs/blink-edit.nvim",
config = function()
require("blink-edit").setup({
llm = {
provider = "sweep",
backend = "openai",
url = "http://localhost:8000",
model = "sweep",
},
})
end,
}
use({
"BlinkResearchLabs/blink-edit.nvim",
config = function()
require("blink-edit").setup({
llm = {
provider = "sweep",
backend = "openai",
url = "http://localhost:8000",
model = "sweep",
},
})
end,
})
require("blink-edit").setup({
llm = {
provider = "sweep",
backend = "openai",
url = "http://localhost:8000",
model = "sweep",
},
})
Model name:
llm.modelmust match an ID returned by your backend (/v1/models). Examples:
- ✅
sweepai/sweep-next-edit-1.5B(if/v1/modelsreturns the HF name)- ✅
/home/.../sweep-next-edit-1.5b.q8_0.v2.gguf(llama.cpp can expose the full path)- ✅
sweep(if you served an alias like--served-model-name sweep)- ❌
sweepwhen/v1/modelsonly exposes the full path
Start typing in Insert mode. Predictions will appear as ghost text.
Control:
<Tab> to Accept the next hunk<C-j> to Accept Line (line-by-line through visible changes)<C-]> to Clear (dismiss without leaving insert mode)<Esc> to Reject and exit insert mode:BlinkEditStatus to check server healthDefault provider. Uses <|file_sep|> delimiters for context sections. Works best with the Sweep Next-Edit model.
llm = { provider = "sweep", backend = "openai", url = "http://localhost:8000", model = "sweep" }
Instruction-style prompt with editable region markers. Supports next-edit mode only.
llm = { provider = "zeta", backend = "openai", url = "http://localhost:8000", model = "zeta" }
Template-based provider. Exists but is not validated. Avoid in production until tested.
Uses /v1/completions. Works with llama.cpp, vLLM, text-generation-webui, and other OpenAI-compatible servers.
Uses /api/generate. Works with any model available through Ollama. See Running a Local Model for setup.
require("blink-edit").setup({
llm = {
backend = "openai", -- "openai" | "ollama"
provider = "sweep", -- "sweep" | "zeta" | "generic"
url = "http://localhost:8000",-- Model server URL
model = "sweep", -- Model name sent to backend
temperature = 0.0, -- Sampling temperature (0 = deterministic)
max_tokens = 512, -- Max tokens to generate
timeout_ms = 5000, -- Request timeout in ms
},
context = {
enabled = true, -- Master switch for context collection
lines_before = nil, -- Lines before cursor (nil = provider default)
lines_after = nil, -- Lines after cursor (nil = provider default)
max_tokens = 512, -- Token budget for context
selection = {
enabled = true, -- Include visual selection in context
max_lines = 10, -- Max lines from selection
},
lsp = {
enabled = true, -- Fetch LSP references for cursor symbol
max_definitions = 2, -- Max definition locations
max_references = 2, -- Max reference locations
timeout_ms = 100, -- LSP request timeout
},
same_file = {
enabled = true, -- Include surrounding lines from same file
max_lines_before = 20, -- Lines above the window
max_lines_after = 20, -- Lines below the window
},
history = {
enabled = false, -- Include recent edit history
max_items = 5, -- Number of history entries
max_tokens = 512, -- Token budget for history
max_files = 2, -- Max files in history
global = true, -- Share history across buffers
},
},
ui = {
progress = true, -- Show "thinking..." indicator
suppress_lsp_floats = true, -- Hide LSP floats while prediction visible
},
prefetch = {
enabled = false, -- Speculative prefetch (uses extra tokens)
strategy = "n-1", -- Prefetch when one hunk remains
},
normal_mode = {
enabled = false, -- Trigger predictions on idle in normal mode
debounce_ms = 200, -- Normal-mode debounce (ms)
},
debounce_ms = 100, -- Delay (ms) before sending prediction request
keymaps = {
insert = {
accept = "<Tab>", -- Accept next hunk
accept_line = "<C-j>", -- Accept next visible line (line-by-line)
clear = "<C-]>", -- Dismiss ghost text (stay in insert mode)
reject = "<Esc>", -- Reject and exit insert mode
},
normal = {
accept = "<Tab>", -- Accept next hunk (when normal_mode.enabled)
accept_line = "<C-j>", -- Accept next visible line (when normal_mode.enabled)
},
},
})
| Key | Action | Description |
|---|---|---|
<Tab> | Accept | Accept the next predicted hunk |
<C-j> | Accept Line | Accept next visible line (line-by-line) |
<C-]> | Clear | Dismiss prediction (stay in insert mode) |
<Esc> | Reject | Reject prediction and exit insert mode |
normal_mode.enabled = true)| Key | Action | Description |
|---|---|---|
<Tab> | Accept | Accept the next predicted hunk |
<C-j> | Accept Line | Accept next visible line (line-by-line) |
Note: Normal mode only maps accept/accept_line; when a prediction is visible, pressing
<Esc>clears it without adding a persistent mapping.
Note: After you start line-by-line accepts with
<C-j>,<Tab>accepts the remaining visible lines.
Note: If you use
blink.cmpornvim-cmp, the<Tab>keymap automatically checks if the completion menu is visible before accepting predictions.
Customization:
Keymaps are organized by mode and can be customized or disabled (set to nil):
require("blink-edit").setup({
keymaps = {
insert = {
accept = "<C-y>", -- Use Ctrl+y to accept
accept_line = "<C-j>", -- Keep default
clear = nil, -- Disable clear keymap
reject = "<C-n>", -- Use Ctrl+n to reject
},
normal = {
accept = "<C-y>", -- Match insert mode binding
accept_line = nil, -- Disable in normal mode
},
},
})
| Command | Description |
|---|---|
:BlinkEditStatus | Show status popup with health, config, and state |
:BlinkEditEnable | Enable predictions |
:BlinkEditDisable | Disable predictions |
:BlinkEditToggle | Toggle predictions on/off |
require("blink-edit").setup(opts) -- Initialize with config
require("blink-edit").enable() -- Enable predictions
require("blink-edit").disable() -- Disable predictions
require("blink-edit").toggle() -- Toggle predictions
require("blink-edit").trigger() -- Manually trigger a prediction
require("blink-edit").accept() -- Accept next hunk
require("blink-edit").accept_line() -- Accept next visible line (line-by-line)
require("blink-edit").clear() -- Clear prediction (stay in insert mode)
require("blink-edit").reject() -- Reject prediction
require("blink-edit").status() -- Get status table
require("blink-edit").health_check() -- Check backend health
:BlinkEditStatus opens a popup showing:
Press r to refresh health, q or <Esc> to close.
:BlinkEditStatus to check if the server is healthy.:LspInfo to verify an LSP is attached to the buffer.:lua vim.print(vim.lsp.buf_request_sync(...)).context.max_tokens or context.lsp.timeout_ms in your config.We check for blink.cmp/nvim-cmp visibility, but you can change keymaps.insert.accept (and keymaps.normal.accept for normal mode) if needed.
vim.g.blink_edit_debug = true -- Summary logs
vim.g.blink_edit_debug = 2 -- Verbose (prompts + responses)
A 1.5B parameter model optimized for next-edit prediction.
Using llama-server:
# Download and run directly
llama-server -hf sweepai/sweep-next-edit-1.5b --port 8000
# Or download manually first
huggingface-cli download sweepai/sweep-next-edit-1.5b \
sweep-next-edit-1.5b.q8_0.v2.gguf --local-dir ./models
llama-server -m ./models/sweep-next-edit-1.5b.q8_0.v2.gguf --port 8000
Config:
llm = { provider = "sweep", backend = "openai", url = "http://localhost:8000", model = "sweep" }
A 7B parameter model from Zed Industries, fine-tuned from Qwen2.5-Coder-7B.
Using vLLM (Recommended):
vllm serve zed-industries/zeta --served-model-name zeta --port 8000
# With optimizations (requires compatible GPU)
vllm serve zed-industries/zeta --served-model-name zeta --port 8000 \
--enable-prefix-caching --quantization="fp8"
Config:
llm = { provider = "zeta", backend = "openai", url = "http://localhost:8000", model = "zeta" }
Using Ollama:
ollama pull lennyerik/zeta
# Runs on port 11434 by default
Config for Ollama:
llm = { provider = "zeta", backend = "ollama", url = "http://localhost:11434", model = "lennyerik/zeta" }
Issues and PRs are welcome! This is an alpha project — your feedback helps shape the future of local AI coding in Neovim.
MIT — see LICENSE.
10 commits
1 commits
Lua
100.0%