Skip to content

isaksamsten/sia.nvim

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Logo

sia.nvim

An LLM assistant for Neovim.

Supports: OpenAI, Copilot and OpenRouter (both OpenAI Chat Completions and Responses), anthropic (native API), Gemini and ZAI.

Features

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

Requirements

  • Neovim >= 0.11
  • curl
  • Access to OpenAI API, Copilot or Gemini

Installation

  1. 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 },
      },
    },
  },
}
  1. 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 as GEMINI_API_KEY.

Configuration

Sia can be customized both globally (in your Neovim config) and per-project (using .sia/config.json).

Global Configuration

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
  }
})

Autocommands

sia.nvim emits the following autocommands:

  • SiaUsageReport: when the number of tokens are known
  • SiaStart: query has been submitted
  • SiaComplete: the query is completed
  • SiaError: on errors in the LLM

Usage

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.

Cost Tracking

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

Adding Pricing to Custom Models

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 cache
  • write: 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.

Customizing Stats Display

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
    }
  }
})

Interaction Modes

Sia supports three primary interaction modes that determine how the AI assistant responds to your queries. Each mode is optimized for different workflows:

Chat Mode

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.

Insert Mode

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

Diff Mode

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

Choosing the Right Mode

  • 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.

Tools

Sia comes with a comprehensive set of tools that enable the AI assistant to interact with your codebase and development environment:

File Operations

  • 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_trash by default)

Code Navigation & Search

  • 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

Development Environment

  • 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

Advanced Capabilities

  • 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)

Task Management

  • 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.

Core Concepts

Tool Approval System

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.

Async Approval Mode

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:

  1. 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 SiaApprove or SiaApproveWarn highlight group (both linked to StatusLine by default).

  2. Process approvals: When you're ready, use one of these functions:

    • require("sia.approval").prompt() - Shows the full approval prompt
    • require("sia.approval").accept() - Auto-accepts without showing prompt
    • require("sia.approval").decline() - Auto-declines without showing prompt
    • require("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)(),
        },
      },
    },
  },
})
  1. 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.

Project-Level Configuration

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" }
        ]
      }
    }
  }
}

Available Local Configuration Options

  • 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)
  • fast_model / plan_model: Override the fast/plan models. Same format as model.
  • 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)

Permission System

The permission system uses Vim regex patterns (with very magic mode \v) to control tool access:

Rule Precedence (in order):

  1. Deny rules: Block operations immediately without confirmation
  2. Ask rules: Require user confirmation before proceeding
  3. Allow rules: Auto-approve operations that match all configured patterns

Rule Structure:

  • Each tool permission must have an arguments field
  • arguments: Object mapping parameter names to pattern arrays
  • choice (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
  • nil arguments are treated as empty strings ("")
  • Non-string arguments are converted to strings with tostring()
  • See :help vim.regex() for full syntax details
Examples

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.

Risk Level System

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:

  1. safe - Low-risk operations (by default, displayed with SiaApproveSafe highlight)
  2. info - Standard operations (by default, displayed with SiaApproveInfo highlight)
  3. warn - High-risk operations (by default, displayed with SiaApproveWarn highlight)

How it works:

  • Each tool has a default risk level (usually "info")
  • Your risk config 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" }
        ]
      }
    }
  }
}
Examples

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" }]
      }
    }
  }
}

Context Management

Control how Sia manages conversation history and tool call pruning:

  • Tool pruning: Use context.max_tool to set when pruning occurs and context.keep to control how many recent tool calls are retained
  • Pruning exclusions: Use context.exclude to specify tool names that should never be pruned (e.g., ["grep", "glob"])
  • Input parameter clearing: Use context.clear_input to also remove tool input parameters during pruning

Auto-Continue Behavior

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.

Custom Default Actions

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"
  }
}

Agent Memory

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:

  1. The assistant checks .sia/memory/ at the start of each conversation
  2. It reads relevant memory files to understand previous progress
  3. As work progresses, it updates memory files with new findings and status
  4. 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.

Task Tracking with Todos

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:

  1. Break down the task into concrete, actionable todos
  2. Update status as it completes each step (pending → current → done)
  3. 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.

Conversation History

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_model configuration)

Accessing saved conversations:

The AI assistant can use the history tool to access past conversations in three ways:

  1. Browse all conversations - View a table of contents for all saved conversations with topics and date ranges
  2. View specific messages - Retrieve exact messages by index from a particular conversation
  3. 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.

Custom Agent Registry

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:

  1. Create a .sia/agents/ directory in your project root
  2. Add agent definition files as markdown with JSON frontmatter
  3. 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 in setup({..}).
    • Example: ["glob", "grep", "read"]
  • model (optional): Override the model for this agent

    • Defaults to fast_model if not specified
    • Example: "openai/gpt-4.1-mini"
  • require_confirmation (optional): Whether tool operations need user approval

    • Default: true (requires approval)
    • Set to false for read-only agents that should work autonomously

How Agents Work:

  1. The AI assistant can use the task tool to list available agents
  2. When it identifies a task that matches an agent's description, it launches that agent
  3. The agent runs autonomously in the background with its own conversation
  4. Progress updates appear in the tasks window
  5. 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: false for 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.md becomes agent name code/searcher

Concurrent Conversations

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:

  1. Both conversations A and B read auth.py
  2. Conversation A refactors a function in auth.py
  3. Conversation A still sees the original content (knows what it changed)
  4. 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.

Suggested Keybindings

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)" },
}

Chat Mappings

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" },
}

Commands

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 conversation
  • SiaAdd buffer <buffer> - Add a buffer to the currently visible conversation
  • SiaAdd 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 cursor
  • SiaReject - Reject the change under the cursor
  • SiaAccept! - Accept all changes in the current buffer
  • SiaReject! - Reject all changes in the current buffer
  • SiaDiff - Show all changes in a diff view

Tool Approval (Async Mode):

  • SiaAnswer prompt - Show the approval prompt for pending tool operations
  • SiaAnswer accept - Auto-accept the pending tool operation
  • SiaAnswer decline - Auto-decline the pending tool operation
  • SiaAnswer 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 generation
  • SiaClear - Remove outdated tool calls and their results from the conversation history
  • SiaClear! - Clear all non-system messages from the conversation (fresh start)
  • SiaCompact - Compact the current conversation history
  • SiaDebug - 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).

Reviewing Changes

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.

Example Workflow

  1. Sia makes changes: After asking Sia to refactor a function, you'll see highlighted changes in your buffer
  2. Navigate changes: Use ]c and [c to jump between individual changes
  3. Review each change: Position your cursor on a change and decide whether to keep it
  4. Accept or reject:
    • SiaAccept to accept the change under cursor
    • SiaReject to reject the change under cursor
    • SiaAccept! to accept all changes at once
    • SiaReject! to reject all changes at once
  5. Continue editing: You can make your own edits while Sia's changes are still pending

Live Example

Screen.Recording.2025-10-09.at.13.48.12.mov

In the following screencast, we see a complete workflow example:

  1. Initial file creation: We ask Sia to write a small script, and Sia uses the write tool to create test.py
  2. External formatting: When the file is saved, ruff format automatically formats it, which modifies the file and moves most changes from reference into baseline (accepting them)
  3. Targeted edits: We ask Sia to change the dataset from iris to another dataset, and Sia uses the edit tool to make several targeted changes
  4. Change visualization: Each change is inserted into reference and highlighted with both line-level and word-level highlights
  5. Manual review: We start reviewing changes using [c and ]c] to move between changes and ga (accept) and make our own edits (removing comments)
  6. 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
  7. Bulk operations: Finally, we show all remaining changes in a quickfix window and use cdo norm ga to accept all changes at once

Built-in Actions

Sia includes these built-in actions:

  • commit: Insert a commit message (Git repositories only, gitcommit filetype)

    • Example: Sia /commit
  • doc: Insert documentation for the function or class under cursor

    • Example: Sia /doc
Screen.Recording.2025-10-14.at.14.29.42.mov

Customizing Actions

See lua/sia/actions.lua for example actions. Here is a short snippet with a simple action.

Action Configuration Options

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 model
  • system - 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 function
  • insert.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 response
  • hidden.messages - Status messages: { on_start = "...", on_progress = {...} }

Built-in Instructions

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",
  },
}

Examples

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.

About

A simple assistant for Neovim

Resources

License

Stars

Watchers

Forks

Contributors 2

  •  
  •  

Languages