An LLM assistant for Neovim.
Supports: OpenAI, Copilot and OpenRouter (both OpenAI Chat Completions and Responses), anthropic (native API), Gemini and ZAI.
Screen.Recording.2025-10-08.at.21.30.32.mov
Screencast.from.2025-09-14.23-23-03.mp4
Screen.Recording.2024-10-11.at.15.42.20.mov
Screen.Recording.2025-09-16.at.15.49.16_compressed.mp4
- Neovim >= 0.11
- curl
- Access to OpenAI API, Copilot or Gemini
- Install using a Lazy:
{
"isaksamsten/sia.nvim",
opts = {},
dependencies = {
{
"rickhowe/diffchar.vim",
keys = {
{ "[z", "<Plug>JumpDiffCharPrevStart", desc = "Previous diff", silent = true },
{ "]z", "<Plug>JumpDiffCharNextStart", desc = "Next diff", silent = true },
{ "do", "<Plug>GetDiffCharPair", desc = "Obtain diff", silent = true },
{ "dp", "<Plug>PutDiffCharPair", desc = "Put diff", silent = true },
},
},
},
}- get an OpenAI API key and add it to your environment as
OPENAI_API_KEY, enable Copilot (use the vim plugin to set it up) or add Gemini API key to your environment asGEMINI_API_KEY.
Sia can be customized both globally (in your Neovim config) and per-project
(using .sia/config.json).
Configure Sia in your init.lua:
require("sia").setup({
-- Model defaults
defaults = {
model = "openai/gpt-4.1", -- Main model for conversations
fast_model = "openai/gpt-4.1-mini", -- Fast model for quick tasks
plan_model = "openai/o3-mini", -- Model for planning and reasoning
temperature = 0.3, -- Creativity level (0-1)
-- UI behavior
ui = {
diff = {
enable = true, -- Enable diff system for change tracking
show_signs = true, -- Show signs in the gutter for changes
char_diff = true, -- Show character-level diffs
},
approval = {
use_vim_ui = false, -- Use Vim's built-in input/select for approvals
show_preview = true, -- Show preview in approval prompts
async = false, -- Queue approvals in background (non-blocking)
},
},
-- File operations
file_ops = {
trash = true, -- Move deleted files to trash
create_dirs_on_rename = true, -- Create directories when renaming
restrict_to_project_root = true, -- Restrict file operations to project
},
-- Default context settings
context = {
max_tool = 30, -- Maximum tool calls before pruning occurs
exclude = {}, -- Tool names to exclude from pruning
clear_input = false, -- Whether to clear tool input parameters during pruning
keep = 20, -- Number of recent tool calls to keep after pruning
},
},
-- Add custom actions (see Customizing Actions below)
actions = {
-- Your custom actions here
}
})sia.nvim emits the following autocommands:
SiaUsageReport: when the number of tokens are knownSiaStart: query has been submittedSiaComplete: the query is completedSiaError: on errors in the LLM
Normal Mode
:Sia [query]- Sends the query and opens a chat view with the response.:Sia [query](from a conversation) - Continues the conversation with the new query.:Sia /prompt [query]- Executes the prompt with the optional additional query.:Sia! [query]- Sends the query and inserts the response directly into the buffer.
Ranges
Any range is supported. For example:
:'<,'>Sia! [query]- Sends the selected lines along with the query and diffs the response.:'<,'>Sia [query]- Sends the selected lines and query, opening a chat with the response.:'<,'>Sia /prompt [query]- Executes the prompt with the extra query for the selected range.
Examples
:%Sia fix the test function- Opens a chat with a suggested fix for the test function.:Sia write snake in pygame- Opens a chat with the generated answer for the query.:Sia /doc numpydoc- Documents the function or class under the cursor using the numpydoc format.
Sia provides real-time cost tracking and token usage monitoring in the chat window's status bar (winbar). This helps you track API costs and token consumption during conversations.
Screen.Recording.2025-11-10.at.15.52.32.mov
For OpenRouter or other custom models, you can add pricing information to your model configuration:
require("sia").setup({
models = {
["openrouter/custom-model"] = {
"openrouter",
"provider/model-name",
pricing = { input = 3.00, output = 15.00 }, -- Per 1M tokens in USD
cache_multiplier = { read = 0.1, write = 1.25 } -- Optional: cache pricing multipliers
},
}
})Cache pricing multipliers:
read: Multiplier for cached tokens read from cachewrite: Multiplier for cache creation tokens (only Anthropic is currently supported)
Providers with built-in cache multipliers (Anthropic, OpenAI) will
automatically apply these. For custom models, specify cache_multiplier
in the model configuration.
Enable by setting defaults.chat.show_stats = true.
require("sia").setup({
defaults = {
chat = {
show_stats = true,
render_stats = function(win, stats)
-- stats table contains:
-- - left: string (optional, e.g., "7d" for Copilot days remaining)
-- - bar: { percent: number, icon: string, text: string } (optional)
-- - right: string (optional, e.g., token count)
end
}
}
})Sia supports three primary interaction modes that determine how the AI assistant responds to your queries. Each mode is optimized for different workflows:
Usage: :Sia [query] or :Sia /prompt [query]
Chat mode opens a conversational interface where you can interact with the AI assistant. The assistant can use tools (read files, search code, execute commands) and provide explanations, suggestions, and guidance. This mode is ideal for:
- Exploratory conversations about your codebase
- Getting explanations and suggestions
- Multi-step problem solving where the AI needs to gather information
- Code reviews and architectural discussions
The chat window persists across queries, maintaining conversation history and allowing you to build on previous exchanges.
After the AI makes suggestions, you can navigate through changes with
]c/[c] and accept/reject them individually with ga/gx or in bulk with
:SiaAccept!/:SiaReject!. See the Change Management
section for more details.
Usage: :Sia! [query] (without a range)
Insert mode generates text and inserts it directly at the cursor position. The AI's response is inserted as-is without any conversational wrapper. This mode is ideal for:
- Code generation at the current cursor position
- Writing boilerplate code
- Generating documentation or comments
- Quick text generation tasks
The AI is instructed to output only the content to be inserted, without explanations or markdown formatting.
Screen.Recording.2025-10-14.at.23.47.53.mov
Usage: :'<,'>Sia! [query] (with a range or visual selection)
Diff mode shows AI-suggested changes in a side-by-side diff view. The assistant analyzes your selected code and proposes modifications, which you can then accept or reject selectively. This mode is ideal for:
- Refactoring existing code
- Fixing bugs with suggested patches
- Applying style or formatting changes
- Making targeted improvements to selected code
Screen.Recording.2025-10-14.at.23.48.34.mov
- Use chat mode when you need to explore, discuss, or get guidance
- Use insert mode when you want generated code at your cursor
- Use diff mode when you want to modify existing code with AI suggestions
You can customize the default behavior and create custom actions that use any of these modes. See Customizing Actions for details.
Sia comes with a comprehensive set of tools that enable the AI assistant to interact with your codebase and development environment:
- read - Read file contents with optional line ranges and limits (up to 2000 lines by default, with line number display)
- write - Write complete file contents to create new files or overwrite existing ones (ideal for large changes >50% of file content)
- edit - Make precise targeted edits using search and replace with fuzzy matching and context validation
- rename_file - Rename or move files within the project with automatic buffer updates
- remove_file - Safely delete files with optional trash functionality
(moves to
.sia_trashby default)
- grep - Fast content search using ripgrep with regex support and glob patterns (max 100 results, sorted by file modification time)
- glob - Find files matching patterns using
fd(supports*.lua,**/*.py, etc.) with hidden file options - workspace - Show currently visible files with line ranges, cursor positions, and background buffers
- show_locations - Create navigable quickfix lists for multiple locations (supports error/warning/info/note types)
- get_diagnostics - Retrieve diagnostics with severity levels and source information
- bash - Execute shell commands in persistent sessions with security restrictions and output truncation (8000 char limit)
- fetch - Retrieve and convert web content to markdown using pandoc, with AI-powered content analysis
- lsp - Interact with Language Server Protocol servers for code intelligence
- task - Launch autonomous agents with access to read-only tools (glob, grep, read) for complex search tasks
- compact_conversation - Intelligently summarize and compact conversation history when topics change
- history - Access and search saved conversation history (see Conversation History below)
- write_todos - Create and manage todo lists for tracking multi-step tasks (add new todos, update status, or clear completed items)
- read_todos - Read the current todo list with IDs, descriptions, and status for each item
The assistant combines these tools intelligently to handle complex development workflows, from simple file edits to multi-file refactoring, debugging, and project analysis.
Sia includes a flexible approval system that allows you to control how tool operations are confirmed. You can choose between blocking (traditional) and non-blocking (async) approval modes.
Screen.Recording.2025-10-27.at.15.09.18.mov
When enabled with ui.approval.async = true, tool approval requests are queued
in the background without interrupting your workflow. This allows you to:
- Continue working while approvals accumulate
- Batch process approvals when you're ready
- Maintain focus on editing without constant interruptions
How it works:
-
Queued notifications: When a tool needs approval, a notification appears in a floating window at the top of your screen:
󱇥 [conversation-name] Execute bash command 'git status'The notification uses the
SiaApproveorSiaApproveWarnhighlight group (both linked toStatusLineby default). -
Process approvals: When you're ready, use one of these functions:
require("sia.approval").prompt()- Shows the full approval promptrequire("sia.approval").accept()- Auto-accepts without showing promptrequire("sia.approval").decline()- Auto-declines without showing promptrequire("sia.approval").preview()- Preview without showing prompt
All our functions will show a picker when multiple approvals are pending, allowing you to select which one to process. The difference is in the default action for a single pending approval.
Customizing Notifications:
By default, approval notifications are shown in a non-focusable floating window at the top of the editor. Sia provides built-in notifiers you can choose from, or you can provide your own custom notifier.
Built-in notifiers:
require("sia.approval").floating_notifier()- Non-focusable floating window at top (default)require("sia.approval").winbar_notifier()- Shows in the current window's winbar
Example using winbar:
require("sia").setup({
defaults = {
ui = {
approval = {
async = {
enable = true,
notifier = require("sia.approval").winbar_notifier(),
},
},
},
},
})Custom notifiers:
The notifier must implement the sia.ApprovalNotifier interface:
show(conversation_name, msg, total)- Show/update the notification. Called whenever the message changes (both initially and on updates).clear()- Clear/dismiss the notification
Example using nvim-notify:
require("sia").setup({
defaults = {
ui = {
approval = {
async = {
enable = true,
notifier = (function()
local notif_id = nil
return {
show = function(_, msg, _)
notif_id = vim.notify(msg, vim.log.levels.INFO, {
title = "Sia Approval",
timeout = false,
replace = notif_id, -- Replace if exists, create if not
})
end,
clear = function()
if notif_id then
vim.notify("", vim.log.levels.INFO, {
timeout = 0,
replace = notif_id
})
notif_id = nil
end
end,
}
end)(),
},
},
},
},
})- Suggested keybindings:
keys = { { "<Leader>ac", mode = "n", function() require("sia.approval").prompt() end, desc = "Confirm pending tool" }, { "<Leader>ay", mode = "n", function() require("sia.approval").accept() end, desc = "Accept pending tool" }, { "<Leader>an", mode = "n", function() require("sia.approval").decline() end, desc = "Decline pending tool" }, }
Configuration example:
require("sia").setup({
defaults = {
ui = {
approval = {
use_vim_ui = false, -- Use custom preview UI
show_preview = true, -- Show detailed preview in prompts
async = {
enable = true, -- Enable non-blocking approval mode
-- notifier = { ... } -- Optional: customize notification display
},
},
},
}
})Traditional (Blocking) Mode:
If you prefer immediate prompts (the default behavior), keep async = false.
Tool operations will show an approval prompt immediately and wait for your
response before continuing.
Create .sia/config.json in your project root to customize Sia for that
specific project:
{
"model": "copilot/gpt-5-mini",
"fast_model": {
"name": "openai/gpt-4.1-mini",
"temperature": 0.1
},
"plan_model": "openai/o3-mini",
"auto_continue": true,
"action": {
"insert": "custom_insert_action",
"diff": "custom_diff_action",
"chat": "custom_chat_action"
},
"context": {
"max_tool": 50,
"exclude": ["grep", "glob"],
"clear_input": false,
"keep": 10
},
"permission": {
"allow": {
"bash": {
"arguments": {
"command": ["^git diff", "^git status$", "^uv build"]
}
},
"edit": {
"arguments": {
"target_file": [".*%.lua$", ".*%.py$", ".*%.js$"]
}
}
},
"deny": {
"bash": {
"arguments": {
"command": ["rm -rf", "sudo"]
}
},
"remove_file": {
"arguments": {
"path": [".*important.*", ".*config.*"]
}
}
},
"ask": {
"write": {
"arguments": {
"path": ["^(\\.md$)@!.*"]
}
}
}
},
"risk": {
"bash": {
"arguments": {
"command": [
{ "pattern": "^ls", "level": "safe" },
{ "pattern": "^cat", "level": "safe" },
{ "pattern": "rm", "level": "warn" }
]
}
}
}
}model: Override the default model for this project. Can be specified as:- String:
"openai/gpt-4.1"(uses default settings) - Object:
{ "name": "openai/gpt-4.1", "temperature": 0.7 }(override model-specific parameters like temperature, pricing, or provider-specific options)
- String:
fast_model/plan_model: Override the fast/plan models. Same format asmodel.models: Override parameters for specific models by name (e.g.,{ "openai/gpt-5.1": { "reasoning_effort": "medium" } }).auto_continue: Automatically continue execution when tools are cancelled (default: false)action: Override default actions for different modes (insert,diff,chat)context: Project-specific context management (tool pruning behavior)permission: Fine-grained tool access control (see Permission System below)risk: Configure risk levels for visual feedback and auto-confirm behavior (see Risk Level System below)
The permission system uses Vim regex patterns (with very magic mode \v)
to control tool access:
Rule Precedence (in order):
- Deny rules: Block operations immediately without confirmation
- Ask rules: Require user confirmation before proceeding
- Allow rules: Auto-approve operations that match all configured patterns
Rule Structure:
- Each tool permission must have an
argumentsfield arguments: Object mapping parameter names to pattern arrayschoice(allow rules only): Auto-selection index for multi-choice prompts (default: 1)
Pattern Format:
Patterns are Vim regex strings using very magic mode (\v):
- Simple pattern strings:
"^git status$","^ls","\\.lua$" - Multiple patterns in an array are OR'd together:
["^git status", "^git diff", "^git log"] - Use negative lookahead for exclusions:
"^(rm|sudo)@!.*"(matches anything NOT starting with rm or sudo)
Pattern Matching:
- Patterns use Vim regex syntax with
\v(very magic mode) - Multiple patterns in an array are OR'd together
- All configured argument patterns must match for the rule to apply
nilarguments are treated as empty strings ("")- Non-string arguments are converted to strings with
tostring() - See
:help vim.regex()for full syntax details
Auto-approve safe git commands:
{
"permission": {
"allow": {
"bash": {
"arguments": {
"command": ["^git status$", "^git diff", "^git log"]
}
}
}
}
}Restrict file operations to source code:
{
"permission": {
"allow": {
"edit": {
"arguments": {
"target_file": ["src/.*\\.(js|ts|py)$"]
}
}
},
"deny": {
"remove_file": {
"arguments": {
"path": [".*\\.(config|env)"]
}
}
}
}
}This system provides fine-grained control over AI assistant capabilities while maintaining security and preventing accidental destructive operations.
The risk level system provides visual feedback and control over how tool operations are presented in the async approval UI. Unlike the permission system (which controls whether operations require confirmation), the risk system lets you mark operations as safe, informational, or risky. By default, the risk system is used together with the async approval system to highlight tool differently.
Risk Levels:
safe- Low-risk operations (by default, displayed withSiaApproveSafehighlight)info- Standard operations (by default, displayed withSiaApproveInfohighlight)warn- High-risk operations (by default, displayed withSiaApproveWarnhighlight)
How it works:
- Each tool has a default risk level (usually
"info") - Your
riskconfig can escalate or de-escalate operations based on patterns - Multiple matching patterns → highest risk level wins
- Auto-confirm only applies when resolved risk level ≤
info
Configuration format:
{
"risk": {
"tool_name": {
"arguments": {
"parameter_name": [
{ "pattern": "vim_regex_pattern", "level": "safe|info|warn" }
]
}
}
}
}Highlight safe commands:
{
"risk": {
"bash": {
"arguments": {
"command": [
{ "pattern": "^ls", "level": "safe" },
{ "pattern": "^cat", "level": "safe" },
{ "pattern": "^echo", "level": "safe" },
{ "pattern": "^git status", "level": "safe" },
{ "pattern": "^git diff", "level": "safe" }
]
}
}
}
}Highlight dangerous commands:
{
"risk": {
"bash": {
"arguments": {
"command": [{ "pattern": "\\brm\\b", "level": "warn" }]
}
},
"remove_file": {
"arguments": {
"path": [{ "pattern": "\\.(env|config)$", "level": "warn" }]
}
}
}
}Control how Sia manages conversation history and tool call pruning:
- Tool pruning: Use
context.max_toolto set when pruning occurs andcontext.keepto control how many recent tool calls are retained - Pruning exclusions: Use
context.excludeto specify tool names that should never be pruned (e.g.,["grep", "glob"]) - Input parameter clearing: Use
context.clear_inputto also remove tool input parameters during pruning
When a user cancels a tool operation, Sia normally asks "Continue?
(Y/n/[a]lways)". Setting auto_continue: true bypasses this prompt and
automatically continues execution. This is useful for automated workflows where
you want the AI to keep working even if individual operations are cancelled.
The action configuration allows you to override the default actions for different interaction modes:
insert: Action used when calling:Sia!(insert mode)diff: Action used when calling:Sia!with a range (diff mode)chat: Action used when calling:Sia(chat mode)
Each field should reference an action name defined in your global configuration. This allows you to customize the behavior, system prompts, tools, and models used for different types of interactions on a per-project basis.
Example: Use a specialized action for writing in a specific project:
{
"action": {
"chat": "prose"
}
}Sia maintains persistent memory across conversations using the .sia/memory/ directory in your project root. This allows the AI assistant to:
- Track progress on complex tasks: Remember what it has tried, what worked, and what didn't
- Learn from iterations: Build on previous attempts and avoid repeating mistakes
- Resume after interruption: Continue work seamlessly even if the conversation or Neovim session ends
- Document decisions: Keep a record of architectural choices, bug fixes, and implementation details
How it works:
- The assistant checks
.sia/memory/at the start of each conversation - It reads relevant memory files to understand previous progress
- As work progresses, it updates memory files with new findings and status
- Memory files use markdown format for human readability
You can safely view, edit, or delete files in .sia/memory/ - they're meant to be human-readable and editable. The assistant will adapt to any changes you make.
Note: Add .sia/ to your .gitignore if you don't want to commit memory files to version control, or commit them if you want to share context with your team.
Screen.Recording.2025-10-31.at.15.24.12.mov
When working on complex tasks, Sia can create and manage a todo list to track progress. This helps you stay organized and gives visibility into multi-step workflows.
How it works:
When you ask Sia to work on a task with multiple steps, it will automatically:
- Break down the task into concrete, actionable todos
- Update status as it completes each step (pending → current → done)
- Track progress throughout the conversation
Viewing todos:
Todos are shown in a floating status window that appears automatically when they're being used. You can also check the current todo list at any time by asking Sia "what's the status?" or "show todos".
Collaborative todos:
The todo list is collaborative - you can manually update todo statuses at any time, and Sia will respect your changes. This is useful if you want to:
- Skip a step that's no longer needed
- Mark something as done that you completed yourself
- Reprioritize what Sia should work on next
Todos help Sia stay focused on your goals and make it easier to resume work after interruptions or context switches.
Sia can save and retrieve conversation history, allowing the AI assistant to learn from past interactions and build on previous work.
Saving conversations:
Use :SiaSave in any chat window to save the current conversation to
.sia/history/. The conversation is saved as a structured JSON file that
includes:
- User and assistant messages from the conversation
- Conversation metadata (name, creation date, model used)
- Automatically generated table of contents with topic summaries
- Optional semantic embeddings for intelligent search (requires
embedding_modelconfiguration)
Accessing saved conversations:
The AI assistant can use the history tool to access past conversations in three ways:
- Browse all conversations - View a table of contents for all saved conversations with topics and date ranges
- View specific messages - Retrieve exact messages by index from a particular conversation
- Semantic search - Search across all conversations using natural language queries (requires embeddings)
Configuration:
To enable semantic search across conversation history, configure an embedding model:
require("sia").setup({
defaults = {
embedding_model = "openai/text-embedding-3-small",
}
})Use cases:
- Learn from past solutions: "Find how we implemented authentication in previously"
- Resume old work: "What did we discuss about the database migration?"
- Reference decisions: "Search for conversations about API design choices"
- Build on previous knowledge: The assistant can reference past successful approaches
Note: Add .sia/history/ to your .gitignore if you don't want to commit
conversation history, or commit it if you want to share knowledge with your
team.
You can define custom agents that the AI can invoke using the task tool. Agents
are specialized assistants with specific capabilities, tools, and system prompts
tailored for particular tasks.
Creating Custom Agents:
- Create a
.sia/agents/directory in your project root - Add agent definition files as markdown with JSON frontmatter
- Agents become automatically available to the AI assistant
Agent File Format:
---
{
"description": "Brief description of what this agent does",
"tools": ["tool1", "tool2", "tool3"],
"model": "openai/gpt-4.1-mini",
"require_confirmation": false,
}
---
System prompt for the agent goes here.Frontmatter Fields:
-
description(required): A clear, concise description of the agent's purpose- This is shown to the AI when it lists available agents
- Example:
"Searches through code to find specific patterns, functions, or implementations"
-
tools(required): Array of tool names the agent can use- Available tools:
glob,grep,read,bash,fetch, etc. The tools must be defined insetup({..}). - Example:
["glob", "grep", "read"]
- Available tools:
-
model(optional): Override the model for this agent- Defaults to
fast_modelif not specified - Example:
"openai/gpt-4.1-mini"
- Defaults to
-
require_confirmation(optional): Whether tool operations need user approval- Default:
true(requires approval) - Set to
falsefor read-only agents that should work autonomously
- Default:
How Agents Work:
- The AI assistant can use the
tasktool to list available agents - When it identifies a task that matches an agent's description, it launches that agent
- The agent runs autonomously in the background with its own conversation
- Progress updates appear in the tasks window
- Results are integrated back into the main conversation
Viewing Running Agents:
You can view and track running agents in the tasks window:
- In chat: Press
t(or your configured binding) to toggle the tasks window - Programmatically: Call
require("sia").tasks("toggle")
Tips:
- Keep agent system prompts focused and specific
- Use
require_confirmation: falsefor read-only agents to avoid interruptions - Choose appropriate tool sets for each agent's purpose
- Name agents descriptively (e.g.,
code-searcher,test-runner,doc-finder) - You can organize agents in subdirectories:
.sia/agents/code/searcher.mdbecomes agent namecode/searcher
Sia supports running multiple conversations simultaneously, each maintaining its own independent view of file changes.
How it works:
When conversation A makes changes to a file using the edit/write/insert tools:
- Conversation A (which made the changes) continues to see the original content it read, allowing it to understand what changed and continue its work coherently
- Conversation B (which also read the same file) sees that content invalidated with "History pruned... read again if needed", prompting it to refresh if that file is relevant to its task
Example:
- Both conversations A and B read
auth.py - Conversation A refactors a function in
auth.py - Conversation A still sees the original content (knows what it changed)
- Conversation B sees the content marked as outdated (knows it needs to re-read the modified version)
This allows multiple conversations to work independently while staying aware of each other's changes.
You can bind visual and operator mode selections to enhance your workflow with sia.nvim:
- Append Current Selection:
<Plug>(sia-append)- Appends the current selection or operator mode selection to the visible chat.
- Execute Default Prompt:
<Plug>(sia-execute)- Executes the default prompt (vim.b.sia) with the current selection or operator mode selection.
keys = {
{ "Za", mode = { "n", "x" }, "<Plug>(sia-append)" },
{ "ZZ", mode = { "n", "x" }, "<Plug>(sia-execute)" },
{ "<Leader>at", mode = "n", function() require("sia").toggle() end, desc = "Toggle last Sia buffer", },
{ "<Leader>aa", mode = "n", function() require("sia").accept_edits() end, desc = "Accept changes", },
{ "<Leader>ar", mode = "n", function() require("sia").reject_edits() end, desc = "Reject changes", },
{ "<Leader>ad", mode = "n", function() require("sia").show_edits_diff() end, desc = "Diff changes", },
{ "<Leader>aq", mode = "n", function() require("sia").show_edits_qf() end, desc = "Show changes", },
{
"[c",
mode = "n",
function()
if vim.wo.diff then
vim.api.nvim_feedkeys("[c", "n", true)
return
end
require("sia").prev_edit()
end,
desc = "Previous edit",
},
{
"]c",
mode = "n",
function()
if vim.wo.diff then
vim.api.nvim_feedkeys("]c", "n", true)
return
end
require("sia").next_edit()
end,
desc = "Next edit",
},
-- Tool approval (async mode)
-- { "<Leader>ac", mode = "n", function() require("sia.approval").prompt() end, desc = "Confirm pending tool", },
-- { "<Leader>ay", mode = "n", function() require("sia.approval").accept() end, desc = "Accept pending tool", },
-- { "<Leader>an", mode = "n", function() require("sia.approval").decline() end, desc = "Decline pending tool", },
{ "ga", mode = "n", function() require("sia").accept_edit() end, desc = "Next edit", },
{ "gx", mode = "n", function() require("sia").reject_edit() end, desc = "Next edit", },
-- Or, to be consistent with vim.wo.diff
--
-- {
-- "dp",
-- mode = "n",
-- function()
-- if vim.wo.diff then
-- vim.api.nvim_feedkeys("dp", "n", true)
-- return
-- end
-- require("sia").accept_edit()
-- end,
-- desc = "Accept edit",
-- },
-- {
-- "do",
-- mode = "n",
-- function()
-- if vim.wo.diff then
-- vim.api.nvim_feedkeys("do", "n", true)
-- return
-- end
-- require("sia").reject_edit()
-- end,
-- desc = "Reject edit",
-- },
}You can send the current paragraph to the default prompt using gzzip or append the current method (assuming treesitter-textobjects) to the ongoing chat with gzaam.
Sia also creates Plug bindings for all actions using <Plug>(sia-execute-<ACTION>), for example, <Plug>(sia-execute-explain) for the default action /explain.
keys = {
{ "Ze", mode = { "n", "x" }, "<Plug>(sia-execute-explain)" },
}In the chat view (with ft=sia), you can bind the following mappings for efficient interaction:
keys = {
{ "p", mode = "n", require("sia").show_messages, ft = "sia" },
{ "<CR>", mode = "n", require("sia").open_reply, ft = "sia" },
-- toggle the todo view
{ "t", mode = "n", require("sia").todos, ft = "sia" },
-- toggle the tasks/agents view
{ "a", mode = "n", require("sia").tasks, ft = "sia" },
-- show a quickfix window with active context references
{ "c", mode = "n", require("sia").show_contexts, ft = "sia" },
}For the most part, Sia will read and add files, diagnostics, and search results autonomously. The available commands are:
Context Management:
SiaAdd file <filename>- Add a file to the currently visible conversationSiaAdd buffer <buffer>- Add a buffer to the currently visible conversationSiaAdd context- Add the current visual mode selection to the currently visible conversation
If there are no visible conversations, Sia will add the context to the next new conversation that is started.
Change Management:
SiaAccept- Accept the change under the cursorSiaReject- Reject the change under the cursorSiaAccept!- Accept all changes in the current bufferSiaReject!- Reject all changes in the current bufferSiaDiff- Show all changes in a diff view
Tool Approval (Async Mode):
SiaAnswer prompt- Show the approval prompt for pending tool operationsSiaAnswer accept- Auto-accept the pending tool operationSiaAnswer decline- Auto-decline the pending tool operationSiaAnswer preview- Preview the pending tool operation
Add ! (e.g., SiaAnswer! accept) to process only the first pending approval.
Conversation Management:
SiaSave- Save the current conversation to.sia/history/with automatic table of contents generationSiaClear- Remove outdated tool calls and their results from the conversation historySiaClear!- Clear all non-system messages from the conversation (fresh start)SiaCompact- Compact the current conversation historySiaDebug- Show the current conversation's JSON payload in a new buffer
Navigation:
With the example keybindings configured, you can navigate between changes using
]c (next change) and [c (previous change).
If Sia uses the edit tools (insert, write, or edit), it will maintain a diff state for the buffer in which the changes are inserted. The diff state maintains two states: the baseline (your edits) and the reference (Sia's changes). Once you accept a change, it will be incorporated into baseline and if the change is rejected it will be removed from reference. This means that you and Sia can make concurrent changes while you can always opt to reject changes made by Sia.
NOTE: If you edit text that overlaps with a pending Sia change, the diff system considers the entire change as accepted and incorporates it into baseline automatically.
- Sia makes changes: After asking Sia to refactor a function, you'll see highlighted changes in your buffer
- Navigate changes: Use
]cand[cto jump between individual changes - Review each change: Position your cursor on a change and decide whether to keep it
- Accept or reject:
SiaAcceptto accept the change under cursorSiaRejectto reject the change under cursorSiaAccept!to accept all changes at onceSiaReject!to reject all changes at once
- Continue editing: You can make your own edits while Sia's changes are still pending
Screen.Recording.2025-10-09.at.13.48.12.mov
In the following screencast, we see a complete workflow example:
- Initial file creation: We ask Sia to write a small script, and Sia uses
the
writetool to createtest.py - External formatting: When the file is saved,
ruff formatautomatically formats it, which modifies the file and moves most changes from reference into baseline (accepting them) - Targeted edits: We ask Sia to change the dataset from iris to another
dataset, and Sia uses the
edittool to make several targeted changes - Change visualization: Each change is inserted into reference and highlighted with both line-level and word-level highlights
- Manual review: We start reviewing changes using
[cand]c]to move between changes andga(accept) and make our own edits (removing comments) - Concurrent editing behavior:
- When removing comments that don't affect Sia's changes, they remain highlighted in reference
- When removing a comment that overlaps with a reference change, it's automatically accepted and moved to baseline
- Bulk operations: Finally, we show all remaining changes in a quickfix
window and use
cdo norm gato accept all changes at once
Sia includes these built-in actions:
-
commit: Insert a commit message (Git repositories only,
gitcommitfiletype)- Example:
Sia /commit
- Example:
-
doc: Insert documentation for the function or class under cursor
- Example:
Sia /doc
- Example:
Screen.Recording.2025-10-14.at.14.29.42.mov
See lua/sia/actions.lua for example actions. Here is a short snippet with a
simple action.
When defining custom actions, you can configure the following options:
Core Options:
mode- The UI mode:"chat","diff","insert", or"hidden"instructions- Array of instructions (strings or instruction objects) to send to the modelsystem- Array of system-level instructions (optional)model- Override the default model for this action (optional)temperature- Override the default temperature (optional)tools- Array of tools available to the action (optional)ignore_tool_confirm- Skip confirmation prompts for tool usage (optional)input- How to handle user input:"ignore","required", or"optional"(default:"optional")range- Whether a range/selection is required (default:false)capture- Function to automatically capture context (e.g., using treesitter)enabled- Function or boolean to determine if action is available
Mode-Specific Options:
For mode = "insert":
insert.placement- Where to insert:"cursor","above","below", or a functioninsert.cursor- Where to place cursor after insert:"start"or"end"insert.message- Status message:[text, highlight_group]insert.post_process- Function to transform lines before insertion
For mode = "diff":
diff.cmd- Command to open diff window (e.g.,"vsplit","split")diff.wo- Window options as array of strings
For mode = "chat":
chat.cmd- Command to open chat window (e.g.,"vsplit","split")chat.wo- Window options as table
For mode = "hidden":
hidden.callback- Function called with the model's responsehidden.messages- Status messages:{ on_start = "...", on_progress = {...} }
Sia provides several built-in instructions that you can use by name in your action configurations. These instructions help provide context to the AI model.
String-named Instructions:
You can reference these by their string name in the instructions array:
-
"current_context"- Provides the current selection or buffer context- In visual mode: sends the selected lines
- In normal mode: minimal context about the file
- Options:
require("sia.instructions").current_context({ show_line_numbers = true })
-
"current_buffer"- Provides the entire current buffer- Sends the full file content with line numbers
- Options:
require("sia.instructions").current_buffer({ show_line_numbers = true, include_cursor = true })
-
"visible_buffers"- Lists all visible buffers in the current tab with cursor positions- Useful for giving the AI awareness of your workspace
- Usage:
require("sia.instructions").visible_buffers()
-
"verbatim"- Provides the selection without any formatting- Raw text without line numbers or explanations
- Usage:
require("sia.instructions").verbatim()
System-level Instructions:
These are typically used in the system array for specific modes:
"default_system"- The default system prompt for coding tasks"prose_system"- System prompt optimized for writing and prose editing"insert_system"- System prompt for insert mode (instructs to output only insertable text)"diff_system"- System prompt for diff mode (instructs to output replacement text)"directory_structure"- Provides the file tree of the current directory"agents_md"- Includes AGENTS.md file if it exists in the project"system_info"- Provides OS, Neovim version, git branch, and timestamp info
Using Built-in Instructions:
-- By string name (for common instructions)
instructions = {
"current_context", -- Built-in instruction
{ role = "user", content = "Explain this code" },
}
-- By function call (for instructions with options)
instructions = {
require("sia.instructions").current_context({ show_line_numbers = false }),
{ role = "user", content = "Explain this code" },
}Custom Instructions:
You can also define custom instructions by providing a table with role and content:
instructions = {
{
role = "user",
content = function(ctx)
-- ctx contains: buf, cursor, mode, pos
return "Custom instruction based on context"
end,
hide = true, -- Don't show in UI
description = "My custom instruction",
},
}Example 1: Simple Chat Action
require("sia").setup({
actions = {
yoda = {
mode = "chat", -- Open in a chat
chat = { cmd = "split" }, -- We want an horizontal split
instructions = {
-- Custom system prompt
{
role = "system",
content = "You are a helpful writer, rewriting prose as Yoda.",
},
"current_context",
},
range = true,
},
-- Customize a built-in action
fix = require("sia.actions").fix({chat = true})
}
})We can use it with Sia /yoda.
Example 2: Insert Mode with Custom Post-Processing
This action generates a code summary and formats it as a comment block:
require("sia").setup({
actions = {
summarize = {
mode = "insert",
system = {
{
role = "system",
content = "Generate a brief 1-2 sentence summary of the provided code.",
},
},
instructions = {
"current_context",
},
range = true,
insert = {
placement = "above", -- Insert above the selection
cursor = "end", -- Place cursor at end of insertion
message = { "Generating summary...", "Comment" },
post_process = function(args)
local lines = args.lines
local ft = vim.bo[args.buf].ft
local comment_start, comment_end
if ft == "lua" then
comment_start = "-- "
elseif ft == "python" then
comment_start = "# "
elseif ft == "c" or ft == "cpp" or ft == "java" then
comment_start = "// "
else
comment_start = "// "
end
local result = {}
for _, line in ipairs(lines) do
if line:match("%S") then -- Skip empty lines
table.insert(result, comment_start .. line)
end
end
return result
end,
},
},
}
})Use with Sia /summarize on a visual selection to get a commented summary.
Example 3: Diff Mode for Code Refactoring
This action suggests refactoring improvements and shows them in a diff view:
require("sia").setup({
actions = {
refactor = {
mode = "diff",
diff = {
cmd = "tabnew % | vsplit",
wo = { "number", "relativenumber" },
},
system = {
{
role = "system",
content = [[You are an expert code reviewer focused on refactoring.
Analyze the provided code and suggest improvements for:
- Code clarity and readability
- Performance optimizations
- Better naming conventions
- Reduced complexity
- Design pattern improvements
Output the complete refactored code, maintaining all functionality.]],
},
},
instructions = {
"current_context",
{
role = "user",
content = "Please refactor this code following best practices.",
},
},
range = true,
capture = require("sia.capture").treesitter({
"@function.outer",
"@class.outer"
}),
},
}
})Use Sia /refactor on a function or class to see refactoring suggestions in a diff view. You can then accept or reject the changes using Neovim's diff navigation commands.
