opencode.nvim

GitHub
755 53 较难 1 次阅读 昨天Apache-2.0Agent插件
AI 解读 由 AI 自动生成,仅供参考

opencode.nvim 是一款专为 Neovim 打造的插件,它将强大的终端 AI 编程助手 opencode 无缝集成到你的编辑器中。对于习惯在命令行环境下工作的开发者而言,它解决了频繁切换窗口以使用 AI 工具的痛点,让你无需离开代码环境即可与 AI 进行持续、流畅的对话。

该插件的核心价值在于其“上下文感知”能力。它能自动捕获当前打开的文件、选中的代码片段甚至光标所在行,将这些实时编辑状态作为背景信息发送给 AI,从而生成更精准的建议或代码修改。通过内置的聊天面板,用户可以保留持久化的会话记录,像使用 Cursor AI 一样在同一个工作区内迭代开发;其实验性的快速聊天功能更支持选中即问即答,AI 返回的修改可直接应用到文件中。

opencode.nvim 非常适合热衷于高度定制化工作流的 Neovim 用户、后端工程师以及追求极致效率的极客开发者。它的独特亮点在于完全基于终端运行,既保留了轻量级的特性,又通过会话持久化和深度上下文关联,实现了媲美图形化 IDE 的智能辅助体验。需要注意的是,目前该项目仍处于早期开发阶段,适合愿意尝试新技术并乐于反馈问题的进阶用户。

使用场景

一位后端工程师正在 Neovim 中重构一个遗留的 Python 微服务模块,需要理解复杂的业务逻辑并安全地修改多处耦合代码。

没有 opencode.nvim 时

  • 上下文割裂严重:必须频繁切换窗口去浏览器复制代码片段到 AI 网页版,打断心流,无法利用当前打开的文件和选中区域作为自然上下文。
  • 协作记忆缺失:每次提问都像“初次见面”,AI 记不住之前的讨论细节,工程师需反复粘贴错误日志和已尝试的方案,沟通效率极低。
  • 应用修改繁琐:获得 AI 生成的代码后,需手动复制回编辑器并仔细对齐缩进,容易引入格式错误或遗漏关键行,增加回归测试成本。
  • 环境依赖冗余:为了获得类似的 AI 辅助体验,不得不离开熟悉的终端环境去安装沉重的图形化 IDE(如 Cursor),破坏了轻量级开发工作流。

使用 opencode.nvim 后

  • 原生上下文感知:直接在 Neovim 内唤起聊天面板,opencode.nvim 自动捕获当前缓冲区内容和视觉选区,让 AI 基于实时代码状态给出精准建议。
  • 持久会话记忆:插件将对话会话与 workspace 绑定,AI 能连续记住多轮重构思路,工程师只需专注迭代逻辑,无需重复背景信息。
  • 一键代码应用:通过 Quick buffer chat 功能,AI 生成的修改可直接应用到文件中,支持自动处理缩进和语法检查,实现“对话即编码”。
  • 终端闭环开发:无需离开终端或切换软件,在保持纯键盘操作习惯的同时,享受媲美图形化 IDE 的智能代理能力,极大提升极客开发体验。

opencode.nvim 将强大的 AI 代理无缝融入 Neovim 工作流,让开发者在不中断心流的前提下,实现从“询问代码”到“直接演进代码”的效率飞跃。

运行环境要求

操作系统
  • 未说明 (取决于 Neovim 和 Opencode CLI 的支持平台)
GPU

未说明

内存

未说明

依赖
notes该工具是 Neovim 插件,核心依赖是已安装且可用的 Opencode CLI (版本需 v0.6.3 或以上)。插件本身处于早期开发阶段,可能存在破坏性更新。具体的系统资源(CPU/GPU/内存)需求主要取决于后端 Opencode AI 代理所使用的模型,而非插件本身。
python未说明
Neovim
Opencode CLI (v0.6.3+)
nvim-lua/plenary.nvim
MeanderingProgrammer/render-markdown.nvim (可选)
blink.cmp 或 nvim-cmp (可选)
snacks.nvim, telescope.nvim, fzf-lua 或 mini.nvim (可选)
opencode.nvim hero image

快速开始

🤖 opencode.nvim

用于 opencode 的 Neovim 前端——一个基于终端的 AI 编码助手

Opencode logo

Neovim GitHub 星标 最后提交

✨ 描述

此插件在 Neovim 和 opencode AI 助手之间建立了桥梁,创建了一个聊天界面,并捕获编辑器上下文(当前文件、选区),以增强你的提示信息。它会维护与工作区绑定的持久会话,让你能够像使用 Cursor AI 等工具一样,与 AI 助手进行持续对话。

主要特性

聊天面板

聊天面板是 Neovim 内的一个专用窗口,允许你与 opencode AI 助手保持持久对话。它会显示你之前的对话和回复,并自动将当前工作区和编辑器状态作为上下文,这样你就可以在不离开 Neovim 的情况下迭代代码。你可以输入提示、查看回答,并在保持聊天会话打开的同时返回到你的代码缓冲区。

快速缓冲区聊天(o/)实验性功能

这是一个实验性功能,允许你使用当前缓冲区的上下文与 AI 进行聊天。在可视模式下,它会捕获选中的文本作为上下文;而在普通模式下,则会使用当前行。AI 将对文件进行快速编辑,并由插件直接应用这些更改。

请大胆尝试并提供反馈!

更多详情请参阅 快速聊天 部分。

📑 目录

⚠️ 注意事项

此插件目前处于早期开发阶段,可能存在 bug 和破坏性变更。尚不建议用于生产环境。如遇到任何问题,请在 GitHub 仓库 中报告。

Opencode 同样处于早期开发阶段,可能会有破坏性变更。请确保你使用的 Opencode CLI 版本兼容(v0.6.3 及以上)。

如果你的升级导致插件无法正常工作,请提交一个问题或回退到上一个可用版本。

📋 要求

  • 已安装并可访问的 Opencode (v0.6.3+) CLI(详见下方 设置 Opencode

🚀 安装

使用你喜欢的包管理器安装插件。有关自定义选项,请参阅下方 配置 部分。

使用 lazy.nvim

{
  "sudo-tee/opencode.nvim",
  config = function()
    require("opencode").setup({})
  end,
  dependencies = {
    "nvim-lua/plenary.nvim",
    {
      "MeanderingProgrammer/render-markdown.nvim",
      opts = {
        anti_conceal = { enabled = false },
        file_types = { 'markdown', 'opencode_output' },
      },
      ft = { 'markdown', 'Avante', 'copilot-chat', 'opencode_output' },
    },
    -- 可选,用于文件提及和命令补全,仅选择一个
    'saghen/blink.cmp',
    -- 'hrsh7th/nvim-cmp',

    -- 可选,用于文件提及选择器,仅选择一个
    'folke/snacks.nvim',
    -- 'nvim-telescope/telescope.nvim',
    -- 'ibhagwan/fzf-lua',
    -- 'nvim_mini/mini.nvim',
  },
}

⚙️ 配置

-- 包含所有可用选项的默认配置
require('opencode').setup({
  preferred_picker = nil, -- 'telescope', 'fzf', 'mini.pick', 'snacks', 'select',如果为 null,则会使用最佳可用选择器。注意 mini.pick 不支持多选
  preferred_completion = nil, -- 'blink', 'nvim-cmp','vim_complete',如果为 null,则会使用最佳可用补全
  default_global_keymaps = true, -- 如果为 false,则禁用所有默认全局快捷键
  default_mode = 'build', -- 'build' 或 'plan' 或任何自定义配置。@see [OpenCode Agents](https://opencode.ai/docs/modes/)
  default_system_prompt = nil, -- 用于所有会话的自定义系统提示。如果为 null,则使用默认内置系统提示
  keymap_prefix = '<leader>o', -- 默认全局快捷键前缀,可更改为你喜欢的前缀,它将应用于所有以 <leader>o 开头的快捷键
  opencode_executable = 'opencode', -- 你的 opencode 二进制文件名

  -- 自定义/外部 opencode 服务器的服务器配置
  server = {
    url = nil,             -- URL/主机名(例如:'http://192.168.1.100'、'localhost'、'https://myserver.com')
    port = nil,            -- 端口号(例如:8080),'auto' 表示随机端口
    timeout = 5,           -- 连接时的健康检查超时时间,单位为秒
    spawn_command = nil,   -- 可选的启动服务器函数:function(port, url) ... end
    auto_kill = true,      -- 当最后一个 Neovim 实例退出时,自动关闭已启动的服务器(默认:true)。仅适用于通过 spawn_command/kill_command 由插件启动的服务器
    path_map = nil,        -- 将主机路径映射到服务器路径:字符串('/app')或函数(path -> 字符串)
  },

keymap = {
    editor = {
      ['<leader>og'] = { 'toggle' }, -- 打开 Opencode。如果已打开则关闭
      ['<leader>oi'] = { 'open_input' }, -- 在插入模式下打开并聚焦输入窗口
      ['<leader>oI'] = { 'open_input_new_session' }, -- 在插入模式下打开并聚焦输入窗口,并创建一个新会话
      ['<leader>oo'] = { 'open_output' }, -- 打开并聚焦输出窗口
      ['<leader>ot'] = { 'toggle_focus' }, -- 在 Opencode 和上一个窗口之间切换焦点
      ['<leader>oT'] = { 'timeline' }, -- 显示时间线选择器,用于导航/撤销/重做/分支消息
      ['<leader>oq'] = { 'close' }, -- 关闭 UI 窗口
      ['<leader>os'] = { 'select_session' }, -- 选择并加载一个 Opencode 会话
      ['<leader>oR'] = { 'rename_session' }, -- 重命名当前会话
      ['<leader>op'] = { 'configure_provider' }, -- 从预定义列表中快速切换提供者和模型
      ['<leader>oV'] = { 'configure_variant' }, -- 切换当前模型的变体
      ['<leader>oy'] = { 'add_visual_selection', mode = {'v'} },
      ['<leader>oY'] = { 'add_visual_selection_inline', mode = {'v'} }, -- 将可视选区作为内联代码块插入到输入缓冲区
      ['<leader>oz'] = { 'toggle_zoom' }, -- 放大或缩小 Opencode 窗口
      ['<leader>ov'] = { 'paste_image'}, -- 将剪贴板中的图片粘贴到当前会话中
      ['<leader>od'] = { 'diff_open' }, -- 打开自上次 Opencode 提示以来修改文件的差异标签页
      ['<leader>o]'] = { 'diff_next' }, -- 导航到下一个文件差异
      ['<leader>o['] = { 'diff_prev' }, -- 导航到上一个文件差异
      ['<leader>oc'] = { 'diff_close' }, -- 关闭差异视图标签页,返回正常编辑
      ['<leader>ora'] = { 'diff_revert_all_last_prompt' }, -- 撤销自上次 Opencode 提示以来的所有文件更改
      ['<leader>ort'] = { 'diff_revert_this_last_prompt' }, -- 撤销自上次 Opencode 提示以来的当前文件更改
      ['<leader>orA'] = { 'diff_revert_all' }, -- 撤销自上次 Opencode 会话以来的所有文件更改
      ['<leader>orT'] = { 'diff_revert_this' }, -- 撤销自上次 Opencode 会话以来的当前文件更改
      ['<leader>orr'] = { 'diff_restore_snapshot_file' }, -- 将文件恢复到某个还原点
      ['<leader>orR'] = { 'diff_restore_snapshot_all' }, -- 将所有文件恢复到某个还原点
      ['<leader>ox'] = { 'swap_position' }, -- 交换 Opencode 窗格的左右位置
      ['<leader>ott'] = { 'toggle_tool_output' }, -- 切换工具输出(差异、命令输出等)
      ['<leader>otr'] = { 'toggle_reasoning_output' }, -- 切换推理输出(思考步骤)
      ['<leader>o/'] = { 'quick_chat', mode = { 'n', 'x' } }, -- 在视觉模式下以选区上下文或在普通模式下以当前行上下文打开快速聊天输入框
    },
    input_window = {
      ['<S-cr>'] = { 'submit_input_prompt', mode = { 'n', 'i' } }, -- 提交提示(普通模式和插入模式)
      ['<esc>'] = { 'close', defer_to_completion = true }, -- 关闭 UI 窗口
      ['<C-c>'] = { 'cancel', defer_to_completion = true }, -- 在运行时取消 Opencode 请求
      ['~'] = { 'mention_file', mode = 'i' }, -- 选择一个文件并添加到上下文中。参见“文件提及”部分
      ['@'] = { 'mention', mode = 'i' }, -- 插入提及(文件/代理)
      ['/'] = { 'slash_commands', mode = 'i' }, -- 在输入窗口中选择要执行的命令
      ['#'] = { 'context_items', mode = 'i' }, -- 管理上下文项(当前文件、选区、诊断信息、提及的文件)
      ['<M-v>'] = { 'paste_image', mode = 'i' }, -- 将剪贴板中的图片作为附件粘贴
      ['<tab>'] = { 'toggle_pane', mode = { 'n', 'i' }, defer_to_completion = true }, -- 在输入和输出窗格之间切换
      ['<up>'] = { 'prev_prompt_history', mode = { 'n', 'i' }, defer_to_completion = true }, -- 导航到历史记录中的上一条提示
      ['<down>'] = { 'next_prompt_history', mode = { 'n', 'i' }, defer_to_completion = true }, -- 导航到历史记录中的下一条提示
      ['<M-m>'] = { 'switch_mode' }, -- 切换模式(构建/计划)
      ['<M-r>'] = { 'cycle_variant', mode = { 'n', 'i' } }, -- 循环可用的模型变体
    },
    output_window = {
      ['<esc>'] = { 'close' }, -- 关闭 UI 窗口
      ['<C-c>'] = { 'cancel' }, -- 在运行时取消 Opencode 请求
      [']]'] = { 'next_message' }, -- 导航到对话中的下一条消息
      ['[['] = { 'prev_message' }, -- 导航到对话中的上一条消息
      ['<tab>'] = { 'toggle_pane', mode = { 'n', 'i' } }, -- 在输入和输出窗格之间切换
      ['i'] = { 'focus_input', 'n' }, -- 从输出窗口聚焦到输入窗口,并进入插入模式
      ['<M-r>'] = { 'cycle_variant', mode = { 'n' } }, -- 循环可用的模型变体
      ['<leader>oS'] = { 'select_child_session' }, -- 选择并加载子会话
      ['<leader>oD'] = { 'debug_message' }, -- 在新缓冲区中打开原始消息进行调试
      ['<leader>oO'] = { 'debug_output' }, -- 在新缓冲区中打开原始输出进行调试
      ['<leader>ods'] = { 'debug_session' }, -- 在新缓冲区中打开原始会话进行调试
    },
    session_picker = {
      rename_session = { '<C-r>' }, -- 重命名会话选择器中选中的会话
      delete_session = { '<C-d>' }, -- 删除会话选择器中选中的会话
      new_session = { '<C-s>' }, -- 在会话选择器中创建并切换到新会话
    },
    timeline_picker = {
      undo = { '<C-u>', mode = { 'i', 'n' } }, -- 撤销到时间线选择器中选定的消息
      fork = { '<C-f>', mode = { 'i', 'n' } }, -- 从时间线选择器中选定的消息分支
    },
    history_picker = {
      delete_entry = { '<C-d>', mode = { 'i', 'n' } }, -- 删除历史选择器中选定的条目
      clear_all = { '<C-X>', mode = { 'i', 'n' } }, -- 清除历史选择器中的所有条目
    },
    model_picker = {
      toggle_favorite = { '<C-f>', mode = { 'i', 'n' } },
    },
    mcp_picker = {
      toggle_connection = { '<C-t>', mode = { 'i', 'n' } }, -- 在 MCP 选择器中切换 MCP 服务器连接
    },
  },
  ui = {
    enable_treesitter_markdown = true, -- 在输出窗口中使用 Treesitter 渲染 Markdown(默认:启用)。
    position = 'right', -- ‘right’(默认)、‘left’ 或 ‘current’。UI 分割的位置。‘current’ 使用当前窗口作为输出。
    input_position = 'bottom', -- ‘bottom’(默认)或 ‘top’。输入窗口的位置
    window_width = 0.40, -- 宽度占编辑器宽度的百分比
    zoom_width = 0.8, -- 放大后的宽度占编辑器宽度的百分比
    display_model = true, -- 在顶部窗口栏显示模型名称
    display_context_size = true, -- 在页脚显示上下文大小
    display_cost = true, -- 在页脚显示成本
    window_highlight = 'Normal:OpencodeBackground,FloatBorder:OpencodeBorder', -- Opencode 窗口的高亮组
    persist_state = true, -- 切换或关闭 UI 时保留缓冲区,以便快速恢复窗口状态
    icons = {
      preset = 'nerdfonts', -- ‘nerdfonts’ | ‘text’。选择 UI 图标样式(默认:‘nerdfonts’)
      overrides = {}, -- 可选的按键覆盖,详见下文
    },
    questions = {
      use_vim_ui_select = false, -- 如果为真,则使用 vim.ui.select 渲染问题/提示,而不是直接在输出缓冲区中显示。
    },
    output = {
      filetype = 'opencode_output', -- 分配给输出缓冲区的文件类型(默认:‘opencode_output’)
      compact_assistant_headers = false, -- ‘full’(默认)、‘minimal’(相同模式下简洁)或 ‘hidden’(助手无标题)
      tools = {
        show_output = true, -- 显示工具输出 [差异、命令输出等](默认:启用)
        show_reasoning_output = true, -- 显示推理/思考步骤的输出(默认:启用)
      },
      rendering = {
        markdown_debounce_ms = 250, -- 新数据渲染时的防抖时间(默认:250 毫秒)
        on_data_rendered = nil, -- 当新数据渲染完毕时调用;设置为 false 可禁用默认的 RenderMarkdown/Markview 行为
      },
      max_messages = nil, -- 输出缓冲区中最多保留的消息数量;新消息到达时将移除旧消息(默认:无限制)
    },
    input = {
      min_height = 0.10, -- 提示输入的最小高度占窗口高度的百分比
      max_height = 0.25, -- 提示输入的最大高度占窗口高度的百分比
      text = {
        wrap = false, -- 输入窗口内的文本是否换行
      },
      -- 提示提交或焦点切换到输出窗口时自动隐藏输入窗口
      auto_hide = false,
    },
    picker = {
      snacks_layout = nil -- 传递给 Snacks.picker.pick({ layout = ... }) 的 `layout` 选项
    },
    completion = {
      file_sources = {
        enabled = true,
        preferred_cli_tool = 'server', -- ‘fd’、‘fdfind’、‘rg’、‘git’、‘server’;若未指定,则使用最佳可用工具。‘server’ 使用 Opencode CLI 获取文件列表(跨平台),并支持文件夹
        ignore_patterns = {
          '^%.git/',
          '^%.svn/',
          '^%.hg/',
          'node_modules/',
          '%.pyc$',
          '%.o$',
          '%.obj$',
          '%.exe$',
          '%.dll$',
          '%.so$',
          '%.dylib$',
          '%.class$',
          '%.jar$',
          '%.war$',
          '%.ear$',
          'target/',
          'build/',
          'dist/',
          'out/',
          'deps/',
          '%.tmp$',
          '%.temp$',
          '%.log$',
          '%.cache$',
        },
        max_files = 10,
        max_display_length = 50, -- 完成建议中文件路径的最大显示长度,超过部分从左侧截断并加“…”
      },
    },
  },
  context = {
    enabled = true, -- 启用自动上下文捕获
    cursor_data = {
      enabled = false, -- 在上下文中包含光标位置和行内容
      context_lines = 5, -- 光标前后要包含在上下文中的行数
    },
    diagnostics = {
      info = false, -- 在上下文中包含诊断信息(默认关闭)
      warning = true, -- 在上下文中包含诊断警告
      error = true, -- 在上下文中包含诊断错误
      only_closest = false, -- 如果为真,则只包含光标/选区附近的诊断信息
    },
    current_file = {
      enabled = true, -- 在上下文中包含当前文件路径和内容
      show_full_path = true,
    },
    files = {
      enabled = true,
      show_full_path = true,
    },
    selection = {
      enabled = true, -- 在上下文中包含选中文本
    },
    buffer = {
      enabled = false, -- 默认禁用整个缓冲区上下文,仅在快速聊天中使用
    },
    git_diff = {
      enabled = false,
    },
  },
  logging = {
    enabled = false,
    level = 'warn', -- debug、info、warn、error
    outfile = nil,
  },
  debug = {
    enabled = false, -- 在输出窗口中启用调试信息
    capture_streamed_events = false,
    show_ids = true,
    quick_chat = {
      keep_session = false, -- 保留快速聊天会话以供检查,但这可能会污染你的会话列表
      set_active_session = false,
    },
  },
  prompt_guard = nil, -- 可选函数,返回布尔值以控制何时可以发送提示(参见“提示守卫”部分)

-- 用户钩子,用于在特定事件中实现自定义行为
  hooks = {
    on_file_edited = nil, -- 当文件被 opencode 编辑后调用。
    on_session_loaded = nil, -- 当会话加载完成后调用。
    on_done_thinking = nil, -- 当 opencode 完成思考(所有任务完成)时调用。
    on_permission_requested = nil, -- 当发出权限请求时调用。
  },
  quick_chat = {
    default_model = nil,   -- 使用像 gpt-4.1 这样的快速模型效果更好。
    default_agent = nil, -- 如果为 nil,则使用当前模式。
    instructions = nil, -- 如果为 nil,则使用内置指令。
  },
})

键位配置

键位配置已被重新组织,以提高其结构化和清晰度:

  • editor:在整个 Neovim 中可用的全局键位映射。
  • input_window:输入窗口专用的键位映射。
  • output_window:输出窗口专用的键位映射。
  • permission:用于响应权限请求的特殊键位映射(当输入/输出窗口中有待处理的权限时可用)。

可以直接通过当前的嵌套表来配置键位:keymap.editorkeymap.input_windowkeymap.output_window

每个键位条目都是一个表,包含以下内容:

  • API 函数的字符串名称 = { 'toggle' }
  • 或者自定义函数:{ function() ... end }
  • 可选的模式:{ 'toggle', mode = { 'n', 'i' } }
  • 可选的描述:{'toggle', desc = '切换 Opencode' }
  • 可选的延迟执行:{'toggle', defer_to_completion = true },如果设置为 true,则当补全菜单打开时,将优先使用补全键位映射,而不是触发该操作。

禁用特定键位

您可以通过将特定默认键位设置为 false 来禁用它们:

require('opencode').setup({
  keymap = {
    input_window = {
      ['<cr>'] = false, -- 禁用 Enter 键提交提示。
      -- 其他未指定的键位将保留其默认绑定。
    }
  }
})

模型排序与收藏

提供者/模型选择器支持基于您的收藏和使用历史的智能排序:

排序优先级

当您打开模型选择器 (<leader>op) 时,模型将按以下顺序排序:

  1. 收藏模型 - 显示带有 ⭐ 图标的模型,并按照收藏顺序排列。
  2. 最近访问的模型 - 按最近使用时间排序。
  3. 其他模型 - 按字母顺序排序。

管理收藏

在模型选择器中,按下 <C-f> 可以将当前选中的模型切换为收藏。收藏的模型将:

  • 带有 ⭐ 星号前缀显示。
  • 始终出现在列表顶部。
  • 在 Neovim 会话之间保持不变。

无需任何配置——插件会自动尊重并更新 OpenCode CLI 的格式。

模型变体

某些模型支持多个变体(例如不同的上下文窗口大小或优化模式)。插件提供了便捷的方法来切换当前活动模型的可用变体。

切换变体

  • 通过选择器:按下 <leader>oV 打开变体选择器,显示当前模型的所有可用变体。
  • 通过循环切换:在输入或输出窗口中按下 <M-r>(Alt+R)可在可用变体之间循环切换。
  • 通过斜杠命令:在输入窗口中输入 /variant

当您切换变体时,插件会记住每个模型的选择,因此下次使用该模型时,它将自动使用上次选择的变体。

UI 图标(禁用表情符号或自定义)

默认情况下,opencode.nvim 在 UI 中使用表情符号作为图标。如果您更喜欢纯文本、无表情符号的界面,可以切换到 text 预设,或者单独覆盖图标。

要全局禁用表情符号的最小配置如下:

require('opencode').setup({
  ui = {
    icons = {
      preset = 'text', -- 将所有图标切换为文本。
    },
  },
})

在保留预设的同时覆盖特定图标:

require('opencode').setup({
  ui = {
    icons = {
      preset = 'emoji',
      overrides = {
        header_user = '> U',
        header_assistant = 'AI',
        search = 'FIND',
        border = '|',
      },
    },
  },
})

可用的图标键(请参阅 lua/opencode/ui/icons.lua 第 7–29 行的实现):

  • header_user、header_assistant
  • run、task、read、edit、write
  • plan、search、web、list、tool
  • snapshot、restore_point、restore_count、file
  • status_on、status_off
  • border、bullet

窗口持久化行为

ui.persist_state 控制 toggle 的行为:

  • persist_state = true(默认):toggle() 隐藏/恢复 UI,并将缓冲区/会话视图保留在内存中,以便快速恢复。
  • persist_state = falsetoggle() 会完全销毁 UI 缓冲区,并在下次打开时重新创建。

相关 API:

  • require('opencode.api').toggle() 遵循上述 persist_state 行为。
  • require('opencode.api').close() 总是会完全关闭并清除隐藏的快照状态。
  • require('opencode.api').hide() 仅在 persist_state = true 时保留缓冲区;否则其行为与 close 相同。

选择器布局

你可以自定义用于历史记录、会话、引用和时间线的选择器布局。

Snacks 选择器与 Opencode 集成

你可以在 Snacks 选择器中配置一个自定义动作,将选中的文件直接作为上下文发送到 Opencode。此功能适用于任何基于文件的选择器(files、git_files、buffers、git_status 等)。

-- 在你的 snacks.nvim 配置中
{
  "folke/snacks.nvim",
  opts = {
    picker = {
      actions = {
        opencode_send = function(picker)
          local selected = picker:selected({ fallback = true })
          if selected and #selected > 0 then
            local files = {}
            for _, item in ipairs(selected) do
              if item.file then
                table.insert(files, item.file)
              end
            end
            picker:close()

            require("opencode.core").open({
              new_session = false,
              focus = "input",
              start_insert = true,
            })

            local context = require("opencode.context")
            for _, file in ipairs(files) do
              context.add_file(file)
            end
          end
        end,
      },
      win = {
        input = {
          keys = {
            -- 使用 <localleader>o 或任何你喜欢的键来将文件发送到 Opencode
            ["<localleader>o"] = { "opencode_send", mode = { "n", "i" } },
          },
        },
      },
    },
  },
}

这样你就可以:

  1. 打开任意 Snacks 文件选择器(:Snacks picker files:Snacks picker git_files 等)
  2. 使用多选功能选择一个或多个文件
  3. 按下 <localleader>o 将这些文件作为上下文发送到 Opencode

Snacks 选择器布局

有三种主要方式可以更改 Snacks 选择器的布局:

  1. 不指定新选项 -> 将直接使用用户在 Snacks 配置中设置的默认布局预设。

  2. 为 Opencode 指定新的选项,例如:

    require("opencode").setup({
      ui = {
        picker = {
         ---@module "snacks"
         ---@type snacks.picker.layout.Config | nil
          snacks_layout = {
            layout = { border = "none", box = "vertical", ... }
          },
        },
      },
    })
    
  3. 指定 Snacks 选择器的 内置布局预设 或者在 Snacks 配置的 opts.picker.layouts 中定义的自定义布局。

    -- opencode.lua
    require("opencode").setup({
      ui = {
        picker = {
         ---@module "snacks"
         ---@type snacks.picker.layout.Config | nil
          snacks_layout = {
            preset = "custom_layout" -- 或内置的 Snacks 布局,如 "select"、"default" 等
          },
        },
      },
    })
    
    -- snacks.lua
    {
      "folke/snacks.nvim",
      opts = {
        picker = {
          layouts = {
            custom_layout  = {
              layout = { border = "none", box = "vertical", ... }
              -- ...
    }
    

🧰 使用方法

可用操作

该插件提供了以下可通过快捷键、命令、斜杠命令(在输入窗口中输入)或 Lua API 触发的操作:

操作 默认键位 命令 API 函数
打开 Opencode。如果已打开则关闭 <leader>og :Opencode require('opencode.api').toggle()
打开输入窗口(当前会话) <leader>oi :Opencode open input require('opencode.api').open_input()
打开输入窗口(新会话) <leader>oI :Opencode open input_new_session require('opencode.api').open_input_new_session()
打开输出窗口 <leader>oo :Opencode open output require('opencode.api').open_output()
创建并切换到命名会话 - :Opencode session new <name> :Opencode session new <name> (用户命令)
重命名当前会话 <leader>oR :Opencode session rename <name> :Opencode session rename <name> (用户命令)
切换焦点:Opencode 窗口或上一个窗口 <leader>ot :Opencode toggle focus require('opencode.api').toggle_focus()
关闭 UI 窗口 <leader>oq :Opencode close require('opencode.api').close()
选择并加载会话 <leader>os :Opencode session select require('opencode.api').select_session()
选择并加载子会话 <leader>oS :Opencode session select_child require('opencode.api').select_child_session()
打开时间线选择器(导航/撤销/重做/分支到消息) <leader>oT :Opencode timeline require('opencode.api').timeline()
浏览对话中的代码引用 gr(窗口) :Opencode references / /references require('opencode.api').references()
配置提供商和模型 <leader>op :Opencode configure provider require('opencode.api').configure_provider()
配置模型变体 <leader>oV :Opencode variant / /variant require('opencode.api').configure_variant()
循环切换模型变体 <M-r>(窗口) - require('opencode.api').cycle_variant()
打开更改的差异视图 <leader>od :Opencode diff open require('opencode.api').diff_open()
跳转到下一个文件差异 <leader>o] :Opencode diff next require('opencode.api').diff_next()
跳转到上一个文件差异 <leader>o[ :Opencode diff prev require('opencode.api').diff_prev()
关闭差异视图标签页 <leader>oc :Opencode diff close require('opencode.api').diff_close()
撤销自上次提示以来的所有文件更改 <leader>ora :Opencode revert all prompt require('opencode.api').diff_revert_all_last_prompt()
撤销当前文件自上次提示以来的更改 <leader>ort :Opencode revert this prompt require('opencode.api').diff_revert_this_last_prompt()
撤销自上次会话以来的所有文件更改 <leader>orA :Opencode revert all session require('opencode.api').diff_revert_all_session()
撤销当前文件自上次会话以来的更改 <leader>orT :Opencode revert this session require('opencode.api').diff_revert_this_session()
将所有文件恢复到特定快照 - :Opencode revert all_to_snapshot require('opencode.api').diff_revert_all(snapshot_id)
恢复当前文件到特定快照 - :Opencode revert this_to_snapshot require('opencode.api').diff_revert_this(snapshot_id)
将文件恢复到某个还原点 - :Opencode restore snapshot_file require('opencode.api').diff_restore_snapshot_file(restore_point_id)
将所有文件恢复到某个还原点 - :Opencode restore snapshot_all require('opencode.api').diff_restore_snapshot_all(restore_point_id)
初始化/更新 AGENTS.md 文件 - :Opencode session agents_init require('opencode.api').initialize()
运行提示(继续会话)运行选项 - :Opencode run <prompt> <opts> require('opencode.api').run("prompt", opts)
运行提示(新会话)运行选项 - :Opencode run new_session <prompt> <opts> require('opencode.api').run_new_session("prompt", opts)
在 Opencode 运行时取消 <C-c> :Opencode cancel require('opencode.api').cancel()
设置模式为构建 - :Opencode agent build require('opencode.api').agent_build()
设置模式为计划 - :Opencode agent plan require('opencode.api').agent_plan()
选择并切换模式/代理 - :Opencode agent select require('opencode.api').select_agent()
显示可用的 MCP 服务器列表 - :Opencode mcp require('opencode.api').mcp()
运行用户命令 - :Opencode run user_command require('opencode.api').run_user_command()
分享当前会话并获取链接 - :Opencode session share / /share require('opencode.api').share()
取消分享当前会话(禁用链接) - :Opencode session unshare / /unshare require('opencode.api').unshare()
整理当前会话(总结) - :Opencode session compact / /compact require('opencode.api').compact_session()
撤销上一次 Opencode 操作 - :Opencode undo / /undo require('opencode.api').undo()
重做上一次 Opencode 操作 - :Opencode redo / /redo require('opencode.api').redo()
回应权限请求(仅接受一次) a(窗口) / <leader>opa(全局) :Opencode permission accept require('opencode.api').permission_accept()
回应权限请求(全部接受) A(窗口) / <leader>opA(全局) :Opencode permission accept_all require('opencode.api').permission_accept_all()
回应权限请求(拒绝) d(窗口) / <leader>opd(全局) :Opencode permission deny require('opencode.api').permission_deny()
插入提及(文件/代理) @ - -
选择文件并添加到上下文 ~ - -
跳转到下一个消息 ]] - -
跳转到上一个消息 [[ - -
跳转到历史记录中的上一个提示 <up> - require('opencode.api').prev_history()
跳转到历史记录中的下一个提示 <down> - require('opencode.api').next_history()
切换输入/输出面板 <tab> - -
交换 Opencode 面板左右 <leader>ox :Opencode swap position require('opencode.api').swap_position()
切换工具输出(差异、命令输出等) <leader>ott :Opencode toggle_tool_output require('opencode.api').toggle_tool_output()
切换推理输出(思考步骤) <leader>otr :Opencode toggle_reasoning_output require('opencode.api').toggle_reasoning_output()
打开带有选择内容/当前行上下文的快速聊天输入框 <leader>o/ :Opencode quick_chat require('opencode.api').quick_chat()
将视觉选区添加到上下文 <leader>oy :Opencode add_visual_selection require('opencode.api').add_visual_selection(opts?)
将视觉选区内联插入到输入中 <leader>oY :Opencode add_visual_selection_inline require('opencode.api').add_visual_selection_inline(opts?)

add_visual_selection 选项:

  • open_input(布尔值,默认值:true):添加选区后是否打开输入窗口。设置为 false 可以静默添加选区,而不改变焦点。

静默添加的示例键位绑定:

['<leader>oy'] = { 'add_visual_selection', { open_input = false }, mode = {'v'} }

add_visual_selection_inline 会将可视选中的代码直接插入到输入缓冲区中,作为 Markdown 代码块,并在前面加上文件路径:

**`path/to/file.lua`**

```lua
<selected text>

光标会留在输入缓冲区的普通模式下,这样你就可以在插入的代码片段周围输入你的提示内容。



### 运行选项

通过命令或 API 运行提示时,可以传递额外的选项:

- `agent=<agent_name>`:指定本次提示使用的代理(覆盖当前代理)
- `model=<provider/model_name>`:指定本次提示使用的模型(覆盖当前模型),例如 `model=github-copilot/gpt-4.1`
- `context.<context_type>.enabled=<true|false>`:仅针对本次提示启用或禁用特定的上下文类型。可用的上下文类型包括:
  - `current_file`
  - `selection`
  - `diagnostics.info`
  - `diagnostics.warning`
  - `diagnostics.error`
  - `cursor_data`

#### 示例

使用 Plan 代理并在新会话中运行提示,同时禁用当前文件上下文:

```vim
:Opencode run new_session "Please help me plan a new feature" agent=plan context.current_file.enabled=false
:Opencode run "Fix the bug in the current file" model=github-copilot/claude-sonnet-4
```

## 👮 权限

Opencode 可能会对潜在的破坏性操作(如编辑文件、还原文件、执行 Shell 命令或启用持久化工具访问权限)发出权限请求。权限请求会内联显示在输出中,必须先响应这些请求,代理才会执行相应操作。更多详情请参阅 [Opencode 权限文档](https://opencode.ai/docs/permissions/)。

<div align="center">
  <img src="https://i.imgur.com/LkZDta5.png" alt="Opencode 权限请求" width="90%" />
  <img src="https://i.imgur.com/4mJH1Xg.png" alt="Opencode 权限请求输入窗口" width="90%" />
</div>

### 回应权限请求

- **通过对话框回应:** 将焦点移至 Opencode 窗口,使用 `j`/`k` 或方向键移动,然后按 `<CR>` 确认。数字快捷键 `1-3` 也可用于选择可见选项。
- **命令:** 使用 `:Opencode permission accept`、`:Opencode permission accept_all` 或 `:Opencode permission deny`。你可以传递索引以针对特定的排队权限。
- **API:** 提供了程序化响应方式:`require('opencode.api').permission_accept()`、`require('opencode.api').permission_accept_all()` 和 `require('opencode.api').permission_deny()`,分别对应“一次”、“始终”和“拒绝”三种响应。
- **行为:** `accept once` 允许执行单次请求的操作,`accept all` 则授予当前会话中类似请求的持久权限,而 `deny` 则拒绝该请求。

---

## 📝 上下文

以下编辑器上下文会自动捕获并包含在你的对话中。

| 上下文类型    | 描述                                          |
| --------------- | ---------------------------------------------------- |
| 当前文件    | 进入 Opencode 之前聚焦文件的路径    |
| 选中文本   | 当前在可视模式下选中的文本和行     |
| 被提及的文件 | 通过 [提及](#file-mentions) 添加的文件信息   |
| 诊断信息     | 当前文件中的诊断信息(如果有)           |
| 共标位置 | 当前光标位置及文件中的行内容 |

<a id="file-mentions"></a>

### 通过文件提及向上下文中添加更多文件

你可以在与 Opencode 的对话中直接引用项目中的文件。这在需要询问或提供特定文件的上下文时非常有用。在输入窗口中输入 `@` 即可触发文件选择器。
支持的选择器包括 [`fzf-lua`](https://github.com/ibhagwan/fzf-lua)、[`telescope`](https://github.com/nvim-telescope/telescope.nvim)、[`mini.pick`](https://github.com/echasnovski/mini.nvim/blob/main/readmes/mini-pick.md) 和 [`snacks`](https://github.com/folke/snacks.nvim/blob/main/docs/picker.md)。

### 上下文栏

你可以在输入窗口顶部的上下文栏中快速查看当前的上下文项:

<div align="center">
  <img src="https://i.imgur.com/vGgu6br.png" alt="Opencode.nvim 上下文栏" width="90%" />
</div>

对于“当前文件”,颜色表示它是否会随下一个提示发送:

- 正常高亮:文件待处理,将会被包含。
- 暗淡/灰色高亮:文件已发送且未发生变化,因此会被跳过(增量行为)。

如果文件内容发生变化,则会再次变为待处理状态,并在下一个提示中发送。

### 上下文项补全

你可以通过在输入窗口中输入 `#` 快速引用可用的上下文项。这将显示一个包含所有可用上下文项的补全菜单:

- **当前文件** - 编辑器中当前聚焦的文件
- **选区** - 当前在可视模式下选中的文本
- **诊断信息** - 当前文件中的 LSP 诊断信息
- **光标数据** - 当前光标位置和所在行的内容
- **[filename]** - 在对话中被提及过的文件
- **代理** - 可切换的可用代理
- **选区** - 之前在可视模式下做出的选区

当前不可用的上下文项会在补全菜单中显示为禁用状态。

你还会在菜单中看到代理和选区的列表;选择它们会从上下文中移除。

<div align="center">
  <img src="https://i.imgur.com/UqQKW33.png" alt="Opencode.nvim 上下文项补全" width="90%" />
</div>

## 🔄 代理

Opencode 提供两个内置代理,并支持自定义代理:

### 内置代理

- **Build**(默认):功能齐全的开发代理,启用了所有可用于代码更改的工具
- **Plan**:受限代理,用于规划和分析,不会对文件进行更改。适用于代码审查和理解代码,而无需修改

### 切换代理

在输入窗口中按下 `<M-m>`(Alt+M)即可在会话中切换代理。

### 自定义代理

你可以通过 Opencode 配置文件创建自定义代理。每个代理可以有自己的:

- 代理配置
- 自定义提示
- 启用或禁用的工具
- 等等

完整的配置选项请参阅 [Opencode 代理文档](https://opencode.ai/docs/agents/)。

## 🔌 自定义/外部服务器配置

默认情况下,opencode.nvim 会启动一个本地的 `opencode serve` 进程。你也可以通过配置 `server` 表来连接到外部或容器化的 Opencode 服务器。

### 基本连接

连接到现有服务器:

```lua
require('opencode').setup({
  server = {
    url = 'localhost',     -- 或 'http://192.168.1.100'
    port = 8080,
    timeout = 5,
  },
})
```

### 使用 Docker 自动启动

使用 `spawn_command` 自动启动服务器,并使用 `kill_command` 停止它:

```lua
require('opencode').setup({
  server = {
    url = 'localhost',
    port = 'auto',  -- 随机端口以实现项目隔离
    -- 路径映射:将主机路径转换为容器内的路径
    path_map = function(host_path)
      local cwd = vim.fn.getcwd()
      -- 将主机项目目录替换为容器挂载点
      return host_path:gsub(vim.pesc(cwd), '/app')
    end,
    -- 启动命令:使用 Docker 容器启动 opencode 服务器
    spawn_command = function(port, url)
      local dir_name = string.lower(vim.fn.fnamemodify(vim.fn.getcwd(), ":t"))
      local cwd = vim.fn.getcwd()
      local container_name = string.format('opencode-%s', dir_name)

      -- 检查容器是否已在运行
      local check_cmd = string.format('docker ps --filter "name=%s" --format "{{.Names}}"', container_name)
      local handle = io.popen(check_cmd)
      local result = handle:read("*a")
      handle:close()

      if result and result:match(container_name) then
        print(string.format("[opencode.nvim] 容器 %s 已在运行,跳过启动", container_name))
        return true
      end

      -- 首先尝试停止任何同名的现有容器
      os.execute(string.format('docker stop %s 2>/dev/null || true', container_name))

      local cmd = string.format([[
docker run -d --rm \
--name %s \
-p %d:4096 \
-v ~/.local/state/opencode:/home/node/.local/state/opencode \
-v ~/.local/share/opencode:/home/node/.local/share/opencode \
-v ~/.config/opencode:/home/node/.config/opencode \
-v "%s":/app:rw \
opencode:latest opencode serve --port 4096 --hostname '0.0.0.0']],
        container_name,
        port,
        cwd
      )

      print(string.format("[opencode.nvim] 正在启动 OpenCode 容器:%s 在端口 %d 上", container_name, port))
      return os.execute(cmd)
    end,
    -- 终止命令:当 auto_kill 触发时停止 Docker 容器
    kill_command = function(port, url)
      local dir_name = string.lower(vim.fn.fnamemodify(vim.fn.getcwd(), ":t"))
      local container_name = string.format('opencode-%s', dir_name)

      print(string.format("[opencode.nvim] 正在停止 OpenCode 容器:%s", container_name))
      return os.execute(string.format('docker stop %s 2>/dev/null', container_name))
    end,
    auto_kill = true,  -- 当最后一个 nvim 实例退出时启用自动清理
  },
})
```

### 容器/WSL 的路径映射

当服务器上的路径与主机不同(例如,容器中的 `/app` 对应主机上的 `/home/user/project`)时:

```lua
require('opencode').setup({
  server = {
    url = 'localhost',
    port = 8080,
    path_map = '/app',  -- 简单的字符串替换
  },
})
```

### 使用 WSL 自动启动

在 Windows 上使用 Neovim 时,在 WSL 内部运行 opencode 服务器:

```lua
require('opencode').setup({
  server = {
    url = 'localhost',
    port = 'auto',  -- 随机端口以实现项目隔离

    -- 在 WSL 内部启动 opencode 服务器
    spawn_command = function(port, url)
      local cmd = string.format(
        'wsl.exe -e bash -c "opencode serve --hostname 127.0.0.1 --port %d"',
        port
      )
      print(string.format('[opencode.nvim] 正在 WSL 中启动服务器,端口为 %d', port))
      return vim.fn.jobstart(cmd, { detach = 1 })
    end,

    -- 终止 WSL 中的 opencode 进程
    kill_command = function(port, url)
      print(string.format('[opencode.nvim] 正在停止 WSL 中的服务器,端口为 %d', port))
      vim.fn.jobstart('wsl.exe -e pkill -f "opencode serve.*--port ' .. port .. '"')
    end,

    -- Windows → WSL 路径转换(用于请求)
    path_map = function(host_path)
      if vim.fn.has('win32') == 1 then
        -- 将 C:\Users\... 转换为 /mnt/c/Users/...
        local drive, rest = host_path:match('^([A-Za-z]):(.*)$')
        if drive then
          local wsl_path = '/mnt/' .. drive:lower() .. rest:gsub('\\', '/')
          return wsl_path
        end
      end
      return host_path
    end,

    -- WSL → Windows 路径转换(用于响应)
    reverse_path_map = function(server_path)
      -- 将 /mnt/c/Users/... 转换为 C:\Users\...
      local drive, rest = server_path:match('^/mnt/([a-z])(.*)$')
      if drive then
        local windows_path = drive:upper() .. ':' .. rest:gsub('/', '\\')
        return windows_path
      end
      return server_path
    end,

    auto_kill = true,  -- 当最后一个 nvim 实例退出时终止服务器
  },
})
```

### 配置选项

- `url`(字符串 | nil):服务器主机名/URL(例如,'localhost'、'http://192.168.1.100')
- `port`(数字 | 'auto' | nil):端口号,'auto' 表示随机端口,或 nil 表示默认端口(4096)
- `timeout`(数字):健康检查超时时间,单位为秒(默认值:5)
- `spawn_command`(函数 | nil):可选的启动服务器函数:`function(port, url) ... end`
- `kill_command`(函数 | nil):可选的在 `auto_kill` 触发时停止服务器的函数:`function(port, url) ... end`
- `auto_kill`(布尔值):当最后一个 nvim 实例退出时终止已启动的服务器(默认值:true)
- `path_map`(字符串 | 函数 | nil):将主机路径转换为服务器路径(用于传出请求)
- `reverse_path_map`(函数 | nil):将服务器路径转换回主机路径(用于传入响应/事件)

### 多实例支持

当使用 `port = 'auto'` 时,opencode.nvim:

- 会跟踪哪些 nvim 实例正在使用每个端口
- 只有在最后一个 nvim 实例退出时才会终止服务器(如果 `auto_kill = true`)。此行为仅适用于通过插件的 `spawn_command`/`kill_command` 启动的服务器。
- 无论 `auto_kill` 设置如何,本地启动的服务器都会在其成为唯一使用该端口的 nvim 实例时自动被终止。

## 用户命令和斜杠命令

您可以通过输入 `/` 从输入窗口运行预定义的用户命令和内置的斜杠命令。这将打开一个命令选择器,您可以从中选择要执行的命令。命令的输出将包含在您的提示上下文中。

**内置斜杠命令**包括:

- `/share` — 分享当前会话并获取链接
- `/unshare` — 取消分享当前会话
- `/compact` — 整理(总结)当前会话
- `/undo` — 撤销上一次 opencode 操作
- `/redo` — 重做上一次撤销的操作
- `/agents_init` — 初始化/更新 AGENTS.md
- `/help` — 显示帮助
- `/mcp` — 显示 MCP 服务器
- `/models` — 切换提供商/模型
- `/variant` — 切换模型变体
- `/sessions` — 切换会话
- `/child-sessions` — 切换到子会话
- `/agent` — 切换代理/模式
- ……等等

**用户命令**是您自定义的脚本。它们从以下位置加载:

- `.opencode/command/`(项目特定)
- `command/`(全局,位于配置目录中)

您也可以通过 `:Opencode command <name>` 按名称运行用户命令。

<img src="https://i.imgur.com/YQhhoPS.png" alt="Opencode.nvim 上下文操作" width="90%" />

更多详细信息请参阅 [用户命令文档](https://opencode.ai/docs/commands/)。

## 📸 快照的上下文操作

> [!WARNING] > _快照是一项实验性功能_
> 在 opencode 中,开发团队有时可能会禁用它们或更改其行为。
> 本仓库将尽快更新以匹配最新的 opencode 变更。

Opencode.nvim 会在关键时刻(例如运行提示后或进行更改后)自动创建工作区的**快照**。这些快照类似于轻量级的 git 提交,使您能够随时查看、比较和恢复项目状态。

快照的**上下文操作**可直接在输出窗口中使用。当对话中引用了快照时,您可以通过 UI 显示的快捷键触发对快照的操作。

### 可用的快照操作

- **Diff:** 查看当前状态与快照之间的差异。
- **Revert file:** 将选定文件恢复到快照时的状态。
- **Revert all files:** 将工作区中的所有文件恢复到快照时的状态。

### 使用方法

- 当输出中的消息引用了快照(查找 📸 **Created Snapshot** 或类似内容)时,将光标移动到该行,上方会显示一个小菜单。

### 示例

当您在输出中看到快照时:

<img src="https://i.imgur.com/eKOjhTN.png" alt="Opencode.nvim 上下文操作" width="90%" />

> **提示:** 恢复快照会将所有文件恢复到快照时的状态,因此请谨慎使用!

## 🕛 上下文还原点

Opencode.nvim 会在执行还原操作之前自动创建还原点。这样,如果需要的话,您可以撤销还原操作。

您将在“Snapshot”行下方看到还原点,如下所示:
<img src="https://i.imgur.com/DKCOdt0.png" alt="Opencode.nvim 还原点" width="90%" />

### 可用的还原操作

- **Restore file:** 将选定文件恢复到上次还原操作之前的状态。
- **Restore all :** 将工作区中的所有文件恢复到还原操作之前的状态。

## 高亮组

该插件定义了多个高亮组,您可以根据自己的颜色方案进行自定义:

- `OpencodeBorder`: Opencode 窗口的边框颜色(默认: #616161)
- `OpencodeBackground`: Opencode 窗口的背景颜色(链接到 `Normal`)
- `OpencodeSessionDescription`: 会话描述文本的颜色(链接到 `Comment`)
- `OpencodeMention`: @file 引用的高亮(链接到 `Special`)
- `OpencodeToolBorder`: 工具执行块的边框颜色(默认: #3b4261)
- `OpencodeMessageRoleAssistant`: 助手消息的高亮(链接到 `Added`)
- `OpencodeMessageRoleUser`: 用户消息的高亮(链接到 `Question`)
- `OpencodeDiffAdd`: 差异中新增行的高亮(默认: #2B3328)
- `OpencodeDiffDelete`: 差异中删除行的高亮(默认: #43242B)
- `OpencodeAgentPlan`: 计划模式下 winbar 中的代理指示器(默认: 背景为 #61AFEF)
- `OpencodeAgentBuild`: 构建模式下 winbar 中的代理指示器(默认: 背景为 #616161)
- `OpencodeAgentCustom`: 自定义模式下 winbar 中的代理指示器(默认: 背景为 #3b4261)
- `OpencodeContestualAction`: 输出窗口中上下文操作的高亮(默认: 背景为 #3b4261)
- `OpencodeInputLegend`: 输入窗口图例的高亮(默认: 背景为 #CCCCCC)
- `OpencodeHint`: 输入窗口中的提示信息以及输出窗口页脚中令牌信息的高亮(链接到 `Comment`)

## 🛡️ 提示防护

`prompt_guard` 配置选项允许您控制何时可以向 Opencode 发送提示。这在某些情况下对于防止意外或未经授权的 AI 交互非常有用。

### 配置

将 `prompt_guard` 设置为一个返回布尔值的函数:

```lua
require('opencode').setup({
  prompt_guard = function()
    -- 您的自定义逻辑在这里
    -- 返回 true 允许,false 拒绝
    return true
  end,
})
```

## 🪝 自定义用户钩子

您可以在 Opencode 的特定事件发生时定义自定义函数:

- `on_file_edited`: 在 Opencode 编辑文件后调用。
- `on_session_loaded`: 在加载会话后调用。
- `on_done_thinking`: 在 Opencode 完成思考(所有用户任务完成)时调用。
- `on_permission_requested`: 在发出权限请求时调用。

```lua
require('opencode').setup({
  hooks = {
    on_file_edited = function(file_path, edit_type)
      -- 文件编辑后的自定义逻辑
      print("文件已编辑: " .. file_path .. " 类型: " .. edit_type)
    end,
    on_session_loaded = function(session_name)
      -- 加载会话后的自定义逻辑
      print("会话已加载: " .. session_name)
    end,
    on_done_thinking = function()
      -- 思考完成后的自定义逻辑
      print("思考已完成!")
    end,
    on_permission_requested = function()
      -- 请求权限时的自定义逻辑
      print("请求权限!")
    end,
  },
})
```

### 行为

- **发送提示之前**: 在向 AI 发送任何提示之前,会检查防护机制。如果被拒绝,将显示 ERROR 通知,并且不会发送提示。
- **打开 UI 之前**: 在首次打开 Opencode 缓冲区时,会检查防护机制。如果被拒绝,将显示 WARN 通知,并且不会打开 UI。
- **无参数**: 防护函数不接收任何参数。可以直接访问 vim 状态(例如 `vim.fn.getcwd()`、`vim.bo.filetype`)。
- **错误处理**: 如果防护函数抛出错误或返回非布尔值,则会以适当的错误消息拒绝提示。

## 📡 服务器发送事件 (SSE) 自动命令

Opencode.nvim 会将所有服务器发送事件作为 Neovim 用户自动命令转发,使您能够对事件作出反应并自动化工作流。所有事件都以模式 `OpencodeEvent:<event.type>` 触发。

### 事件数据结构

自动命令通过 `args.data.event` 接收事件数据:

```lua
{
  type = "event.name",  -- Opencode 事件类型
  properties = { ... }  -- 事件特定属性
}
```

### 通配符模式

您可以使用通配符匹配多种事件类型:

- `OpencodeEvent:*` - 所有事件
- `OpencodeEvent:session.*` - 所有会话事件
- `OpencodeEvent:permission.*` - 所有权限事件

### 示例

```lua
vim.api.nvim_create_autocmd('User', {
  pattern = 'OpencodeEvent:permission.asked',
  callback = function(args)
    local event = args.data.event
    vim.notify(vim.inspect(event))
    -- 触发自定义逻辑
  end,
})
```

## 快速聊天

快速聊天允许您基于当前行或选区的内容启动一个临时的 opencode 会话。
此功能针对狭窄的代码编辑或插入进行了优化。如果请求较为复杂且需要更多上下文,建议使用完整的 opencode UI。

由于上下文较为狭窄,生成的结果可能不够准确,有时编辑也可能失败。为了获得最佳效果,请尽量保持请求简洁明了。

### 开始快速聊天

在普通模式下按下 `<leader>o/` 即可打开快速聊天输入窗口。

<div align="center">
  <img src="https://i.imgur.com/5JNlFZn.png">
</div>
<div align="center">
  <img src="https://i.imgur.com/ScRgqfC.png">
</div>

### 聊天提示示例

- 转换为 Lua 数组
- 添加 Lua 注释
- 为我的更改编写一条符合规范的提交信息 #diff
- 修复这些警告 #warn
- 完成这个函数

## 🔧 配置 Opencode

如果你是 Opencode 的新用户:

1. **什么是 Opencode?**
   - Opencode 是一个专为终端打造的 AI 编码助手。
   - 它提供强大的 AI 辅助功能,并支持扩展配置,例如选择不同的大语言模型和 MCP 服务器。

2. **安装:**
   - 请访问 [安装 Opencode](https://opencode.ai/docs/#install) 页面,获取安装和配置说明。
   - 确保安装完成后 `opencode` 命令可用。

3. **配置:**
   - 运行 `opencode auth login` 来设置你的 LLM 提供商。
   - 在 `~/.config/opencode/config.json` 或 `~/.config/opencode/opencode.json` 文件中配置你偏好的 LLM 提供商及模型。

## 🙏 致谢

本插件是基于 [azorng](https://github.com/azorng/) 开发的原版 [goose.nvim](https://github.com/azorng/goose.nvim) 插件的分支。出于 Git 历史记录的考虑,我们直接复制了原始代码,而非简单地进行分支操作。

常见问题

相似工具推荐

openclaw

OpenClaw 是一款专为个人打造的本地化 AI 助手,旨在让你在自己的设备上拥有完全可控的智能伙伴。它打破了传统 AI 助手局限于特定网页或应用的束缚,能够直接接入你日常使用的各类通讯渠道,包括微信、WhatsApp、Telegram、Discord、iMessage 等数十种平台。无论你在哪个聊天软件中发送消息,OpenClaw 都能即时响应,甚至支持在 macOS、iOS 和 Android 设备上进行语音交互,并提供实时的画布渲染功能供你操控。 这款工具主要解决了用户对数据隐私、响应速度以及“始终在线”体验的需求。通过将 AI 部署在本地,用户无需依赖云端服务即可享受快速、私密的智能辅助,真正实现了“你的数据,你做主”。其独特的技术亮点在于强大的网关架构,将控制平面与核心助手分离,确保跨平台通信的流畅性与扩展性。 OpenClaw 非常适合希望构建个性化工作流的技术爱好者、开发者,以及注重隐私保护且不愿被单一生态绑定的普通用户。只要具备基础的终端操作能力(支持 macOS、Linux 及 Windows WSL2),即可通过简单的命令行引导完成部署。如果你渴望拥有一个懂你

349.3k|★★★☆☆|1周前
Agent开发框架图像

stable-diffusion-webui

stable-diffusion-webui 是一个基于 Gradio 构建的网页版操作界面,旨在让用户能够轻松地在本地运行和使用强大的 Stable Diffusion 图像生成模型。它解决了原始模型依赖命令行、操作门槛高且功能分散的痛点,将复杂的 AI 绘图流程整合进一个直观易用的图形化平台。 无论是希望快速上手的普通创作者、需要精细控制画面细节的设计师,还是想要深入探索模型潜力的开发者与研究人员,都能从中获益。其核心亮点在于极高的功能丰富度:不仅支持文生图、图生图、局部重绘(Inpainting)和外绘(Outpainting)等基础模式,还独创了注意力机制调整、提示词矩阵、负向提示词以及“高清修复”等高级功能。此外,它内置了 GFPGAN 和 CodeFormer 等人脸修复工具,支持多种神经网络放大算法,并允许用户通过插件系统无限扩展能力。即使是显存有限的设备,stable-diffusion-webui 也提供了相应的优化选项,让高质量的 AI 艺术创作变得触手可及。

162.1k|★★★☆☆|2周前
开发框架图像Agent

everything-claude-code

everything-claude-code 是一套专为 AI 编程助手(如 Claude Code、Codex、Cursor 等)打造的高性能优化系统。它不仅仅是一组配置文件,而是一个经过长期实战打磨的完整框架,旨在解决 AI 代理在实际开发中面临的效率低下、记忆丢失、安全隐患及缺乏持续学习能力等核心痛点。 通过引入技能模块化、直觉增强、记忆持久化机制以及内置的安全扫描功能,everything-claude-code 能显著提升 AI 在复杂任务中的表现,帮助开发者构建更稳定、更智能的生产级 AI 代理。其独特的“研究优先”开发理念和针对 Token 消耗的优化策略,使得模型响应更快、成本更低,同时有效防御潜在的攻击向量。 这套工具特别适合软件开发者、AI 研究人员以及希望深度定制 AI 工作流的技术团队使用。无论您是在构建大型代码库,还是需要 AI 协助进行安全审计与自动化测试,everything-claude-code 都能提供强大的底层支持。作为一个曾荣获 Anthropic 黑客大奖的开源项目,它融合了多语言支持与丰富的实战钩子(hooks),让 AI 真正成长为懂上

160.8k|★★☆☆☆|今天
开发框架Agent语言模型

opencode

OpenCode 是一款开源的 AI 编程助手(Coding Agent),旨在像一位智能搭档一样融入您的开发流程。它不仅仅是一个代码补全插件,而是一个能够理解项目上下文、自主规划任务并执行复杂编码操作的智能体。无论是生成全新功能、重构现有代码,还是排查难以定位的 Bug,OpenCode 都能通过自然语言交互高效完成,显著减少开发者在重复性劳动和上下文切换上的时间消耗。 这款工具专为软件开发者、工程师及技术研究人员设计,特别适合希望利用大模型能力来提升编码效率、加速原型开发或处理遗留代码维护的专业人群。其核心亮点在于完全开源的架构,这意味着用户可以审查代码逻辑、自定义行为策略,甚至私有化部署以保障数据安全,彻底打破了传统闭源 AI 助手的“黑盒”限制。 在技术体验上,OpenCode 提供了灵活的终端界面(Terminal UI)和正在测试中的桌面应用程序,支持 macOS、Windows 及 Linux 全平台。它兼容多种包管理工具,安装便捷,并能无缝集成到现有的开发环境中。无论您是追求极致控制权的资深极客,还是渴望提升产出的独立开发者,OpenCode 都提供了一个透明、可信

144.3k|★☆☆☆☆|3天前
Agent插件

ComfyUI

ComfyUI 是一款功能强大且高度模块化的视觉 AI 引擎,专为设计和执行复杂的 Stable Diffusion 图像生成流程而打造。它摒弃了传统的代码编写模式,采用直观的节点式流程图界面,让用户通过连接不同的功能模块即可构建个性化的生成管线。 这一设计巧妙解决了高级 AI 绘图工作流配置复杂、灵活性不足的痛点。用户无需具备编程背景,也能自由组合模型、调整参数并实时预览效果,轻松实现从基础文生图到多步骤高清修复等各类复杂任务。ComfyUI 拥有极佳的兼容性,不仅支持 Windows、macOS 和 Linux 全平台,还广泛适配 NVIDIA、AMD、Intel 及苹果 Silicon 等多种硬件架构,并率先支持 SDXL、Flux、SD3 等前沿模型。 无论是希望深入探索算法潜力的研究人员和开发者,还是追求极致创作自由度的设计师与资深 AI 绘画爱好者,ComfyUI 都能提供强大的支持。其独特的模块化架构允许社区不断扩展新功能,使其成为当前最灵活、生态最丰富的开源扩散模型工具之一,帮助用户将创意高效转化为现实。

109.2k|★★☆☆☆|昨天
开发框架图像Agent

gemini-cli

gemini-cli 是一款由谷歌推出的开源 AI 命令行工具,它将强大的 Gemini 大模型能力直接集成到用户的终端环境中。对于习惯在命令行工作的开发者而言,它提供了一条从输入提示词到获取模型响应的最短路径,无需切换窗口即可享受智能辅助。 这款工具主要解决了开发过程中频繁上下文切换的痛点,让用户能在熟悉的终端界面内直接完成代码理解、生成、调试以及自动化运维任务。无论是查询大型代码库、根据草图生成应用,还是执行复杂的 Git 操作,gemini-cli 都能通过自然语言指令高效处理。 它特别适合广大软件工程师、DevOps 人员及技术研究人员使用。其核心亮点包括支持高达 100 万 token 的超长上下文窗口,具备出色的逻辑推理能力;内置 Google 搜索、文件操作及 Shell 命令执行等实用工具;更独特的是,它支持 MCP(模型上下文协议),允许用户灵活扩展自定义集成,连接如图像生成等外部能力。此外,个人谷歌账号即可享受免费的额度支持,且项目基于 Apache 2.0 协议完全开源,是提升终端工作效率的理想助手。

100.8k|★★☆☆☆|1周前
插件Agent图像