Note: This is an experimental plugin, not intended for production use. If you want something featureful and actively maintained, use avante.nvim or CodeCompanion. This exists as a learning project and a starting point if you want to build your own.
Simple inline code generation plugin for nvim, which uses OpenCode as a backend.
This plugin is designed for single-buffer, in-place edits:
- Generate a function from a signature
- Add documentation or comments
- Fix a bug in a specific block
- Refactor a small section of code
It is not intended for:
- Multi-file refactoring
- Large-scale codebase changes
- Complex implementations spanning multiple files
For those use cases, use OpenCode directly, your editor's refactoring tools, or other agentic coding assistants.
- Annotate your code with an instruction
// @ai <instruction>comment in your code, trigger the plugin with:InlineRun, and the plugin will replace it with the OpenCode response.
- I wanted to build my first nvim plugin.
- I wanted the same model-agnostic, on-demand workflow from OpenCode inside the editor.
- Intentionally minimal: no always-on features, no inline completion—just a lightweight tool to help form a clearer mental model of the codebase.
sequenceDiagram
participant N as nvim
participant I as inline.nvim
participant O as opencode
N->>I: :InlineRun
I->>I: find @ai comment
I->>I: build prompt
I->>O: POST /session
O-->>I: session_id
I->>O: POST /message
O-->>I: REPLACE response
I->>N: update buffer
- I tested this with nvim
v0.11.1and OpenCodev1.0.223. - You want to have OpenCode server running (
opencode serve --port 4096) -- or in a custom port.
{
"haapjari/inline.nvim",
config = function()
-- optional: override provider/model (defaults to OpenCode's config)
-- provider = "anthropic",
-- model = "claude-sonnet-4-5",
-- per-filetype agent mapping
agents = {
go = "go", -- this is my custom agent, you can omit this entirely
},
})
end,
keys = {
{ "<leader>ai", "<cmd>InlineRun<cr>", desc = "run inline ai" },
},
cmd = { "InlineRun", "InlineStatus", "InlineConfig", "InlineCancel", "InlineValidateConfig" },
}If you encounter issues with a new release, you can pin to a specific version:
{
"haapjari/inline.nvim",
tag = "v0.0.1", -- pin to specific version
-- or: commit = "abc123", -- pin to specific commit
config = function()
require("inline").setup({})
end,
}require("inline").setup({
-- OpenCode server details
host = "127.0.0.1", -- default
port = 4096, -- default
-- model (optional - uses OpenCode's configured defaults if omitted)
provider = "anthropic",
model = "claude-sonnet-4-5",
-- default agent (used when no filetype mapping matches)
agent = "build", -- default
-- per-filetype agent mapping (see "Agents" section below)
agents = {
go = "go",
python = "python",
},
-- keymap (false to disable, string to customize)
keymap = "<leader>ai", -- default
-- custom prompt template (nil uses built-in default)
prompt = "~/.config/nvim/my-inline-prompt.md",
})Note: Agent configuration is entirely optional. If you omit the
agentstable, all requests use the defaultbuildagent which works out of the box.
The agents table maps nvim filetypes to OpenCode agent names. When you run :InlineRun, the plugin checks the current buffer's filetype and uses the corresponding agent.
agents = {
go = "go", -- *.go files use the "go" agent
python = "python", -- *.py files use the "python" agent
rust = "rust", -- *.rs files use the "rust" agent
},How it Works:
- You run
:InlineRunin a Go file - Plugin detects filetype is
go - Looks up
agents["go"]→ finds"go" - Sends the request to OpenCode with
agent = "go" - If no mapping exists, falls back to the
agentdefault ("build")
Override Per-Request: Use :InlineRun agent=<name> to bypass the filetype mapping for a single request.
OpenCode comes with these agents out of the box:
| Agent | Mode | Description |
|---|---|---|
build |
primary | Default agent with all tools enabled |
plan |
primary | Analysis and planning without making changes |
general |
subagent | General-purpose research and multi-step tasks |
explore |
subagent | Fast codebase exploration and file searching |
The agent name in your agents table must exist in your OpenCode configuration. Define agents in ~/.config/opencode/opencode.json (global) or .opencode/opencode.json (per-project).
Example: Define a go agent in your opencode.json:
{
"agent": {
"go": {
"model": "anthropic/claude-sonnet-4-5",
"prompt": "{file:./prompts/go.md}",
"tools": { "bash": true },
"permission": {
"edit": "allow",
"bash": {
"*": "deny",
"go test *": "allow",
"go build *": "allow"
}
}
}
}
}Then create your prompt at ~/.config/opencode/prompts/go.md:
You are a Go expert. Follow these conventions:
- Use standard library when possible
- Format with gofumpt
- No globals, prefer dependency injection
- Error wrapping with fmt.Errorf("context: %w", err)Now when you run :InlineRun in a Go file, it uses your go agent with its custom prompt and permissions. This is a handy way to create agents, with as much autonomy - but restrict critical stuff - like deleting or removing stuff from the file system.
See OpenCode Agents documentation for more configuration options.
- You can customize the prompt by pointing to a markdown file:
require("inline").setup({
prompt = "~/.config/nvim/prompt.md",
})The prompt uses %s placeholders (in order):
- filename
- filetype
- buffer content (with line numbers)
- line number of @ai comment
- instruction text
- See:
prompts/default.mdfor the default template.
| Command | Description |
|---|---|
:InlineRun |
run ai for the nearest @ai comment |
:InlineRun agent=<name> |
override default or configured agent for this request |
:InlineStatus |
check opencode server status |
:InlineConfig |
show current configuration |
:InlineCancel |
cancel current request |
:InlineValidateConfig |
validate configuration against models.dev |
Run :InlineValidateConfig to validate your configuration with good taste. This checks:
- Local config (host, port, timeout ranges)
- OpenCode server health
- Provider name against models.dev registry
- Model name against provider's model list
flowchart TD
A[InlineValidateConfig] --> B{Local Config Valid?}
B -->|No| C[Report Errors]
B -->|Yes| D{OpenCode Server Healthy?}
D -->|No| E[Report Server Error]
D -->|Yes| F{Provider Configured?}
F -->|No| G[Skip Provider Check]
F -->|Yes| H[Fetch models.dev]
H --> I{Provider Found?}
I -->|No| J[Warn + Suggest Similar]
I -->|Yes| K{Model Configured?}
K -->|No| L[Valid]
K -->|Yes| M{Model Found?}
M -->|No| N[Warn Model Unknown]
M -->|Yes| L
G --> L
Note: Validation is on-demand only. Nothing runs automatically.
- The plugin automatically detects your language's comment syntax using vim's
commentstringoption. Thecommentstringsetting (see:help commentstring) defines the format for comments in each filetype, allowing the plugin to recognize@aidirectives regardless of language:
| Language | Format |
|---|---|
| Go, C, JS, TS | // @ai ... |
| Python, Bash | # @ai ... |
| Lua | -- @ai ... |
- Non-Blocking Requests.
- Smart Range Detection.
- Spinner Animation.
- Multiple Concurrent Requests.
- Buffer Change Detection - you can edit your file at the same time, while ai is processing.
- Custom Prompt Templates.
Releases are created locally using the Makefile:
# auto-increment patch version (0.0.1 -> 0.0.2)
make release
# for minor/major bumps, edit VERSION file first
echo "0.1.0" > VERSION # or "1.0.0" for major
make releaseThe release target will:
- Bump patch version (or use VERSION if manually edited)
- Update version in
lua/inline/init.lua - Commit, tag, and push to main
- Create GitHub release with auto-generated notes
MIT.
