[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"similar-sudo-tee--opencode.nvim":3,"tool-sudo-tee--opencode.nvim":62},[4,18,26,36,46,54],{"id":5,"name":6,"github_repo":7,"description_zh":8,"stars":9,"difficulty_score":10,"last_commit_at":11,"category_tags":12,"status":17},4358,"openclaw","openclaw\u002Fopenclaw","OpenClaw 是一款专为个人打造的本地化 AI 助手，旨在让你在自己的设备上拥有完全可控的智能伙伴。它打破了传统 AI 助手局限于特定网页或应用的束缚，能够直接接入你日常使用的各类通讯渠道，包括微信、WhatsApp、Telegram、Discord、iMessage 等数十种平台。无论你在哪个聊天软件中发送消息，OpenClaw 都能即时响应，甚至支持在 macOS、iOS 和 Android 设备上进行语音交互，并提供实时的画布渲染功能供你操控。\n\n这款工具主要解决了用户对数据隐私、响应速度以及“始终在线”体验的需求。通过将 AI 部署在本地，用户无需依赖云端服务即可享受快速、私密的智能辅助，真正实现了“你的数据，你做主”。其独特的技术亮点在于强大的网关架构，将控制平面与核心助手分离，确保跨平台通信的流畅性与扩展性。\n\nOpenClaw 非常适合希望构建个性化工作流的技术爱好者、开发者，以及注重隐私保护且不愿被单一生态绑定的普通用户。只要具备基础的终端操作能力（支持 macOS、Linux 及 Windows WSL2），即可通过简单的命令行引导完成部署。如果你渴望拥有一个懂你",349277,3,"2026-04-06T06:32:30",[13,14,15,16],"Agent","开发框架","图像","数据工具","ready",{"id":19,"name":20,"github_repo":21,"description_zh":22,"stars":23,"difficulty_score":10,"last_commit_at":24,"category_tags":25,"status":17},3808,"stable-diffusion-webui","AUTOMATIC1111\u002Fstable-diffusion-webui","stable-diffusion-webui 是一个基于 Gradio 构建的网页版操作界面，旨在让用户能够轻松地在本地运行和使用强大的 Stable Diffusion 图像生成模型。它解决了原始模型依赖命令行、操作门槛高且功能分散的痛点，将复杂的 AI 绘图流程整合进一个直观易用的图形化平台。\n\n无论是希望快速上手的普通创作者、需要精细控制画面细节的设计师，还是想要深入探索模型潜力的开发者与研究人员，都能从中获益。其核心亮点在于极高的功能丰富度：不仅支持文生图、图生图、局部重绘（Inpainting）和外绘（Outpainting）等基础模式，还独创了注意力机制调整、提示词矩阵、负向提示词以及“高清修复”等高级功能。此外，它内置了 GFPGAN 和 CodeFormer 等人脸修复工具，支持多种神经网络放大算法，并允许用户通过插件系统无限扩展能力。即使是显存有限的设备，stable-diffusion-webui 也提供了相应的优化选项，让高质量的 AI 艺术创作变得触手可及。",162132,"2026-04-05T11:01:52",[14,15,13],{"id":27,"name":28,"github_repo":29,"description_zh":30,"stars":31,"difficulty_score":32,"last_commit_at":33,"category_tags":34,"status":17},1381,"everything-claude-code","affaan-m\u002Feverything-claude-code","everything-claude-code 是一套专为 AI 编程助手（如 Claude Code、Codex、Cursor 等）打造的高性能优化系统。它不仅仅是一组配置文件，而是一个经过长期实战打磨的完整框架，旨在解决 AI 代理在实际开发中面临的效率低下、记忆丢失、安全隐患及缺乏持续学习能力等核心痛点。\n\n通过引入技能模块化、直觉增强、记忆持久化机制以及内置的安全扫描功能，everything-claude-code 能显著提升 AI 在复杂任务中的表现，帮助开发者构建更稳定、更智能的生产级 AI 代理。其独特的“研究优先”开发理念和针对 Token 消耗的优化策略，使得模型响应更快、成本更低，同时有效防御潜在的攻击向量。\n\n这套工具特别适合软件开发者、AI 研究人员以及希望深度定制 AI 工作流的技术团队使用。无论您是在构建大型代码库，还是需要 AI 协助进行安全审计与自动化测试，everything-claude-code 都能提供强大的底层支持。作为一个曾荣获 Anthropic 黑客大奖的开源项目，它融合了多语言支持与丰富的实战钩子（hooks），让 AI 真正成长为懂上",160784,2,"2026-04-19T11:32:54",[14,13,35],"语言模型",{"id":37,"name":38,"github_repo":39,"description_zh":40,"stars":41,"difficulty_score":42,"last_commit_at":43,"category_tags":44,"status":17},8272,"opencode","anomalyco\u002Fopencode","OpenCode 是一款开源的 AI 编程助手（Coding Agent），旨在像一位智能搭档一样融入您的开发流程。它不仅仅是一个代码补全插件，而是一个能够理解项目上下文、自主规划任务并执行复杂编码操作的智能体。无论是生成全新功能、重构现有代码，还是排查难以定位的 Bug，OpenCode 都能通过自然语言交互高效完成，显著减少开发者在重复性劳动和上下文切换上的时间消耗。\n\n这款工具专为软件开发者、工程师及技术研究人员设计，特别适合希望利用大模型能力来提升编码效率、加速原型开发或处理遗留代码维护的专业人群。其核心亮点在于完全开源的架构，这意味着用户可以审查代码逻辑、自定义行为策略，甚至私有化部署以保障数据安全，彻底打破了传统闭源 AI 助手的“黑盒”限制。\n\n在技术体验上，OpenCode 提供了灵活的终端界面（Terminal UI）和正在测试中的桌面应用程序，支持 macOS、Windows 及 Linux 全平台。它兼容多种包管理工具，安装便捷，并能无缝集成到现有的开发环境中。无论您是追求极致控制权的资深极客，还是渴望提升产出的独立开发者，OpenCode 都提供了一个透明、可信",144296,1,"2026-04-16T14:50:03",[13,45],"插件",{"id":47,"name":48,"github_repo":49,"description_zh":50,"stars":51,"difficulty_score":32,"last_commit_at":52,"category_tags":53,"status":17},2271,"ComfyUI","Comfy-Org\u002FComfyUI","ComfyUI 是一款功能强大且高度模块化的视觉 AI 引擎，专为设计和执行复杂的 Stable Diffusion 图像生成流程而打造。它摒弃了传统的代码编写模式，采用直观的节点式流程图界面，让用户通过连接不同的功能模块即可构建个性化的生成管线。\n\n这一设计巧妙解决了高级 AI 绘图工作流配置复杂、灵活性不足的痛点。用户无需具备编程背景，也能自由组合模型、调整参数并实时预览效果，轻松实现从基础文生图到多步骤高清修复等各类复杂任务。ComfyUI 拥有极佳的兼容性，不仅支持 Windows、macOS 和 Linux 全平台，还广泛适配 NVIDIA、AMD、Intel 及苹果 Silicon 等多种硬件架构，并率先支持 SDXL、Flux、SD3 等前沿模型。\n\n无论是希望深入探索算法潜力的研究人员和开发者，还是追求极致创作自由度的设计师与资深 AI 绘画爱好者，ComfyUI 都能提供强大的支持。其独特的模块化架构允许社区不断扩展新功能，使其成为当前最灵活、生态最丰富的开源扩散模型工具之一，帮助用户将创意高效转化为现实。",109154,"2026-04-18T11:18:24",[14,15,13],{"id":55,"name":56,"github_repo":57,"description_zh":58,"stars":59,"difficulty_score":32,"last_commit_at":60,"category_tags":61,"status":17},6121,"gemini-cli","google-gemini\u002Fgemini-cli","gemini-cli 是一款由谷歌推出的开源 AI 命令行工具，它将强大的 Gemini 大模型能力直接集成到用户的终端环境中。对于习惯在命令行工作的开发者而言，它提供了一条从输入提示词到获取模型响应的最短路径，无需切换窗口即可享受智能辅助。\n\n这款工具主要解决了开发过程中频繁上下文切换的痛点，让用户能在熟悉的终端界面内直接完成代码理解、生成、调试以及自动化运维任务。无论是查询大型代码库、根据草图生成应用，还是执行复杂的 Git 操作，gemini-cli 都能通过自然语言指令高效处理。\n\n它特别适合广大软件工程师、DevOps 人员及技术研究人员使用。其核心亮点包括支持高达 100 万 token 的超长上下文窗口，具备出色的逻辑推理能力；内置 Google 搜索、文件操作及 Shell 命令执行等实用工具；更独特的是，它支持 MCP（模型上下文协议），允许用户灵活扩展自定义集成，连接如图像生成等外部能力。此外，个人谷歌账号即可享受免费的额度支持，且项目基于 Apache 2.0 协议完全开源，是提升终端工作效率的理想助手。",100752,"2026-04-10T01:20:03",[45,13,15,14],{"id":63,"github_repo":64,"name":65,"description_en":66,"description_zh":67,"ai_summary_zh":67,"readme_en":68,"readme_zh":69,"quickstart_zh":70,"use_case_zh":71,"hero_image_url":72,"owner_login":73,"owner_name":74,"owner_avatar_url":75,"owner_bio":76,"owner_company":77,"owner_location":76,"owner_email":76,"owner_twitter":76,"owner_website":76,"owner_url":78,"languages":79,"stars":92,"forks":93,"last_commit_at":94,"license":95,"difficulty_score":96,"env_os":97,"env_gpu":98,"env_ram":98,"env_deps":99,"category_tags":108,"github_topics":76,"view_count":32,"oss_zip_url":76,"oss_zip_packed_at":76,"status":17,"created_at":109,"updated_at":110,"faqs":111,"releases":147},9610,"sudo-tee\u002Fopencode.nvim","opencode.nvim","neovim frontend for opencode - a terminal-based AI coding agent","opencode.nvim 是一款专为 Neovim 打造的插件，它将强大的终端 AI 编程助手 opencode 无缝集成到你的编辑器中。对于习惯在命令行环境下工作的开发者而言，它解决了频繁切换窗口以使用 AI 工具的痛点，让你无需离开代码环境即可与 AI 进行持续、流畅的对话。\n\n该插件的核心价值在于其“上下文感知”能力。它能自动捕获当前打开的文件、选中的代码片段甚至光标所在行，将这些实时编辑状态作为背景信息发送给 AI，从而生成更精准的建议或代码修改。通过内置的聊天面板，用户可以保留持久化的会话记录，像使用 Cursor AI 一样在同一个工作区内迭代开发；其实验性的快速聊天功能更支持选中即问即答，AI 返回的修改可直接应用到文件中。\n\nopencode.nvim 非常适合热衷于高度定制化工作流的 Neovim 用户、后端工程师以及追求极致效率的极客开发者。它的独特亮点在于完全基于终端运行，既保留了轻量级的特性，又通过会话持久化和深度上下文关联，实现了媲美图形化 IDE 的智能辅助体验。需要注意的是，目前该项目仍处于早期开发阶段，适合愿意尝试新技术并乐于反馈问题的进阶用户。","# 🤖 opencode.nvim\n\n> neovim frontend for opencode - a terminal-based AI coding agent\n\n\u003Cdiv align=\"center\">\n  \u003Cimg src=\"https:\u002F\u002Fraw.githubusercontent.com\u002Fsst\u002Fopencode\u002Fdev\u002Fpackages\u002Fweb\u002Fsrc\u002Fassets\u002Flogo-ornate-dark.svg\" alt=\"Opencode logo\" width=\"30%\" \u002F>\n\u003C\u002Fdiv>\n\n\u003Cdiv align=\"center\">\n\n![Neovim](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FNeoVim-%2357A143.svg?&style=for-the-badge&logo=neovim&logoColor=white)\n[![GitHub stars](https:\u002F\u002Fimg.shields.io\u002Fgithub\u002Fstars\u002Fsudo-tee\u002Fopencode.nvim?style=for-the-badge)](https:\u002F\u002Fgithub.com\u002Fsudo-tee\u002Fopencode.nvim\u002Fstargazers)\n![Last Commit](https:\u002F\u002Fimg.shields.io\u002Fgithub\u002Flast-commit\u002Fsudo-tee\u002Fopencode.nvim?style=for-the-badge)\n\n\u003Ca href=\"https:\u002F\u002Fwww.buymeacoffee.com\u002Fsudo.tee\">\u003Cimg src=\"https:\u002F\u002Fwww.buymeacoffee.com\u002Fassets\u002Fimg\u002Fcustom_images\u002Forange_img.png\" height=\"20px\">\u003C\u002Fa>\n\n\u003C\u002Fdiv>\n\n## ✨ Description\n\nThis plugin provides a bridge between neovim and the [opencode](https:\u002F\u002Fgithub.com\u002Fsst\u002Fopencode) AI agent, creating a chat interface while capturing editor context (current file, selections) to enhance your prompts. It maintains persistent sessions tied to your workspace, allowing for continuous conversations with the AI assistant similar to what tools like Cursor AI offer.\n\n## Main Features\n\n### Chat Panel\n\nThe chat panel is a dedicated window inside Neovim that lets you hold a persistent conversation with the opencode AI agent. It displays your previous messages and responses, and automatically uses your current workspace and editor state as context so you can iterate on code without leaving Neovim. You can type prompts, review answers, and navigate back to your code buffer while keeping the ongoing chat session open.\n\n\u003Cdiv align=\"center\">\n  \u003Cimg src=\"https:\u002F\u002Foss.gittoolsai.com\u002Fimages\u002Fsudo-tee_opencode.nvim_readme_c06078cdecf5.png\">\n\u003C\u002Fdiv>\n\n### Quick buffer chat (\u003Cleader>o\u002F) EXPERIMENTAL\n\nThis is an experimental feature that allows you to chat with the AI using the current buffer context. In visual mode, it captures the selected text as context, while in normal mode, it uses the current line. The AI will respond with quick edits to the files that are applied by the plugin.\n\nDon't hesitate to give it a try and provide feedback!\n\nRefer to the [Quick Chat](#-quick-chat) section for more details.\n\n\u003Cdiv align=\"center\">\n  \u003Cimg src=\"https:\u002F\u002Fi.imgur.com\u002F5JNlFZn.png\">\n\u003C\u002Fdiv>\n\n## 📑 Table of Contents\n\n- [⚠️Caution](#caution)\n- [Requirements](#-requirements)\n- [Installation](#-installation)\n- [Configuration](#️-configuration)\n- [Usage](#-usage)\n- [Permissions](#-permissions)\n- [Context](#-context)\n- [Agents](#-agents)\n- [Custom\u002FExternal Server Configuration](#-customexternal-server-configuration)\n- [User Commands and Slash Commands](#user-commands-and-slash-commands)\n- [Contextual Actions for Snapshots](#-contextual-actions-for-snapshots)\n- [Contextual Restore points](#-contextual-restore-points)\n- [Highlight Groups](#highlight-groups)\n- [Prompt Guard](#️-prompt-guard)\n- [Custom user hooks](#-custom-user-hooks)\n- [Server-Sent Events (SSE) autocmds](#-server-sent-events-sse-autocmds)\n- [Quick Chat](#quick-chat)\n- [Setting up Opencode](#-setting-up-opencode)\n- [Recipes](.\u002Fdocs\u002Frecipes)\n\n## ⚠️Caution\n\nThis plugin is in early development and may have bugs and breaking changes. It is not recommended for production use yet. Please report any issues you encounter on the [GitHub repository](https:\u002F\u002Fgithub.com\u002Fsudo-tee\u002Fopencode.nvim\u002Fissues).\n\n[Opencode](https:\u002F\u002Fgithub.com\u002Fsst\u002Fopencode) is also in early development and may have breaking changes. Ensure you are using a compatible version of the Opencode CLI (v0.6.3+ or more).\n\nIf your upgrade breaks the plugin, please open an issue or downgrade to the last working version.\n\n## 📋 Requirements\n\n- Opencode (v0.6.3+ or more) CLI installed and available (see [Setting up opencode](#-setting-up-opencode) below)\n\n## 🚀 Installation\n\nInstall the plugin with your favorite package manager. See the [Configuration](#️-configuration) section below for customization options.\n\n### With lazy.nvim\n\n```lua\n{\n  \"sudo-tee\u002Fopencode.nvim\",\n  config = function()\n    require(\"opencode\").setup({})\n  end,\n  dependencies = {\n    \"nvim-lua\u002Fplenary.nvim\",\n    {\n      \"MeanderingProgrammer\u002Frender-markdown.nvim\",\n      opts = {\n        anti_conceal = { enabled = false },\n        file_types = { 'markdown', 'opencode_output' },\n      },\n      ft = { 'markdown', 'Avante', 'copilot-chat', 'opencode_output' },\n    },\n    -- Optional, for file mentions and commands completion, pick only one\n    'saghen\u002Fblink.cmp',\n    -- 'hrsh7th\u002Fnvim-cmp',\n\n    -- Optional, for file mentions picker, pick only one\n    'folke\u002Fsnacks.nvim',\n    -- 'nvim-telescope\u002Ftelescope.nvim',\n    -- 'ibhagwan\u002Ffzf-lua',\n    -- 'nvim_mini\u002Fmini.nvim',\n  },\n}\n```\n\n## ⚙️ Configuration\n\n```lua\n-- Default configuration with all available options\nrequire('opencode').setup({\n  preferred_picker = nil, -- 'telescope', 'fzf', 'mini.pick', 'snacks', 'select', if nil, it will use the best available picker. Note mini.pick does not support multiple selections\n  preferred_completion = nil, -- 'blink', 'nvim-cmp','vim_complete' if nil, it will use the best available completion\n  default_global_keymaps = true, -- If false, disables all default global keymaps\n  default_mode = 'build', -- 'build' or 'plan' or any custom configured. @see [OpenCode Agents](https:\u002F\u002Fopencode.ai\u002Fdocs\u002Fmodes\u002F)\n  default_system_prompt = nil, -- Custom system prompt to use for all sessions. If nil, uses the default built-in system prompt\n  keymap_prefix = '\u003Cleader>o', -- Default keymap prefix for global keymaps change to your preferred prefix and it will be applied to all keymaps starting with \u003Cleader>o\n  opencode_executable = 'opencode', -- Name of your opencode binary\n\n  -- Server configuration for custom\u002Fexternal opencode servers\n  server = {\n    url = nil,             -- URL\u002Fhostname (e.g., 'http:\u002F\u002F192.168.1.100', 'localhost', 'https:\u002F\u002Fmyserver.com')\n    port = nil,            -- Port number (e.g., 8080), 'auto' for random port\n    timeout = 5,           -- Health check timeout in seconds when connecting\n    spawn_command = nil,   -- Optional function to start the server: function(port, url) ... end\n    auto_kill = true,      -- Kill spawned servers when last nvim instance exits (default: true) Only applies to servers spawned by the plugin with spawn_command\u002Fkill_command\n    path_map = nil,        -- Map host paths to server paths: string ('\u002Fapp') or function(path) -> string\n  },\n\n  keymap = {\n    editor = {\n      ['\u003Cleader>og'] = { 'toggle' }, -- Open opencode. Close if opened\n      ['\u003Cleader>oi'] = { 'open_input' }, -- Opens and focuses on input window on insert mode\n      ['\u003Cleader>oI'] = { 'open_input_new_session' }, -- Opens and focuses on input window on insert mode. Creates a new session\n      ['\u003Cleader>oo'] = { 'open_output' }, -- Opens and focuses on output window\n      ['\u003Cleader>ot'] = { 'toggle_focus' }, -- Toggle focus between opencode and last window\n      ['\u003Cleader>oT'] = { 'timeline' }, -- Display timeline picker to navigate\u002Fundo\u002Fredo\u002Ffork messages\n      ['\u003Cleader>oq'] = { 'close' }, -- Close UI windows\n      ['\u003Cleader>os'] = { 'select_session' }, -- Select and load a opencode session\n      ['\u003Cleader>oR'] = { 'rename_session' }, -- Rename current session\n      ['\u003Cleader>op'] = { 'configure_provider' }, -- Quick provider and model switch from predefined list\n      ['\u003Cleader>oV'] = { 'configure_variant' }, -- Switch model variant for the current model\n      ['\u003Cleader>oy'] = { 'add_visual_selection', mode = {'v'} },\n      ['\u003Cleader>oY'] = { 'add_visual_selection_inline', mode = {'v'} }, -- Insert visual selection as inline code block in the input buffer\n      ['\u003Cleader>oz'] = { 'toggle_zoom' }, -- Zoom in\u002Fout on the Opencode windows\n      ['\u003Cleader>ov'] = { 'paste_image'}, -- Paste image from clipboard into current session\n      ['\u003Cleader>od'] = { 'diff_open' }, -- Opens a diff tab of a modified file since the last opencode prompt\n      ['\u003Cleader>o]'] = { 'diff_next' }, -- Navigate to next file diff\n      ['\u003Cleader>o['] = { 'diff_prev' }, -- Navigate to previous file diff\n      ['\u003Cleader>oc'] = { 'diff_close' }, -- Close diff view tab and return to normal editing\n      ['\u003Cleader>ora'] = { 'diff_revert_all_last_prompt' }, -- Revert all file changes since the last opencode prompt\n      ['\u003Cleader>ort'] = { 'diff_revert_this_last_prompt' }, -- Revert current file changes since the last opencode prompt\n      ['\u003Cleader>orA'] = { 'diff_revert_all' }, -- Revert all file changes since the last opencode session\n      ['\u003Cleader>orT'] = { 'diff_revert_this' }, -- Revert current file changes since the last opencode session\n      ['\u003Cleader>orr'] = { 'diff_restore_snapshot_file' }, -- Restore a file to a restore point\n      ['\u003Cleader>orR'] = { 'diff_restore_snapshot_all' }, -- Restore all files to a restore point\n      ['\u003Cleader>ox'] = { 'swap_position' }, -- Swap Opencode pane left\u002Fright\n      ['\u003Cleader>ott'] = { 'toggle_tool_output' }, -- Toggle tools output (diffs, cmd output, etc.)\n      ['\u003Cleader>otr'] = { 'toggle_reasoning_output' }, -- Toggle reasoning output (thinking steps)\n      ['\u003Cleader>o\u002F'] = { 'quick_chat', mode = { 'n', 'x' } }, -- Open quick chat input with selection context in visual mode or current line context in normal mode\n    },\n    input_window = {\n      ['\u003CS-cr>'] = { 'submit_input_prompt', mode = { 'n', 'i' } }, -- Submit prompt (normal mode and insert mode)\n      ['\u003Cesc>'] = { 'close', defer_to_completion = true }, -- Close UI windows\n      ['\u003CC-c>'] = { 'cancel', defer_to_completion = true }, -- Cancel opencode request while it is running\n      ['~'] = { 'mention_file', mode = 'i' }, -- Pick a file and add to context. See File Mentions section\n      ['@'] = { 'mention', mode = 'i' }, -- Insert mention (file\u002Fagent)\n      ['\u002F'] = { 'slash_commands', mode = 'i' }, -- Pick a command to run in the input window\n      ['#'] = { 'context_items', mode = 'i' }, -- Manage context items (current file, selection, diagnostics, mentioned files)\n      ['\u003CM-v>'] = { 'paste_image', mode = 'i' }, -- Paste image from clipboard as attachment\n      ['\u003Ctab>'] = { 'toggle_pane', mode = { 'n', 'i' }, defer_to_completion = true }, -- Toggle between input and output panes\n      ['\u003Cup>'] = { 'prev_prompt_history', mode = { 'n', 'i' }, defer_to_completion = true }, -- Navigate to previous prompt in history\n      ['\u003Cdown>'] = { 'next_prompt_history', mode = { 'n', 'i' }, defer_to_completion = true }, -- Navigate to next prompt in history\n      ['\u003CM-m>'] = { 'switch_mode' }, -- Switch between modes (build\u002Fplan)\n      ['\u003CM-r>'] = { 'cycle_variant', mode = { 'n', 'i' } }, -- Cycle through available model variants\n    },\n    output_window = {\n      ['\u003Cesc>'] = { 'close' }, -- Close UI windows\n      ['\u003CC-c>'] = { 'cancel' }, -- Cancel opencode request while it is running\n      [']]'] = { 'next_message' }, -- Navigate to next message in the conversation\n      ['[['] = { 'prev_message' }, -- Navigate to previous message in the conversation\n      ['\u003Ctab>'] = { 'toggle_pane', mode = { 'n', 'i' } }, -- Toggle between input and output panes\n      ['i'] = { 'focus_input', 'n' }, -- Focus on input window and enter insert mode at the end of the input from the output window\n      ['\u003CM-r>'] = { 'cycle_variant', mode = { 'n' } }, -- Cycle through available model variants\n      ['\u003Cleader>oS'] = { 'select_child_session' }, -- Select and load a child session\n      ['\u003Cleader>oD'] = { 'debug_message' }, -- Open raw message in new buffer for debugging\n      ['\u003Cleader>oO'] = { 'debug_output' }, -- Open raw output in new buffer for debugging\n      ['\u003Cleader>ods'] = { 'debug_session' }, -- Open raw session in new buffer for debugging\n    },\n    session_picker = {\n      rename_session = { '\u003CC-r>' }, -- Rename selected session in the session picker\n      delete_session = { '\u003CC-d>' }, -- Delete selected session in the session picker\n      new_session = { '\u003CC-s>' }, -- Create and switch to a new session in the session picker\n    },\n    timeline_picker = {\n      undo = { '\u003CC-u>', mode = { 'i', 'n' } }, -- Undo to selected message in timeline picker\n      fork = { '\u003CC-f>', mode = { 'i', 'n' } }, -- Fork from selected message in timeline picker\n    },\n    history_picker = {\n      delete_entry = { '\u003CC-d>', mode = { 'i', 'n' } }, -- Delete selected entry in the history picker\n      clear_all = { '\u003CC-X>', mode = { 'i', 'n' } }, -- Clear all entries in the history picker\n    },\n    model_picker = {\n      toggle_favorite = { '\u003CC-f>', mode = { 'i', 'n' } },\n    },\n    mcp_picker = {\n      toggle_connection = { '\u003CC-t>', mode = { 'i', 'n' } }, -- Toggle MCP server connection in the MCP picker\n    },\n  },\n  ui = {\n    enable_treesitter_markdown = true, -- Use Treesitter for markdown rendering in the output window (default: true).\n    position = 'right', -- 'right' (default), 'left' or 'current'. Position of the UI split. 'current' uses the current window for the output.\n    input_position = 'bottom', -- 'bottom' (default) or 'top'. Position of the input window\n    window_width = 0.40, -- Width as percentage of editor width\n    zoom_width = 0.8, -- Zoom width as percentage of editor width\n    display_model = true, -- Display model name on top winbar\n    display_context_size = true, -- Display context size in the footer\n    display_cost = true, -- Display cost in the footer\n    window_highlight = 'Normal:OpencodeBackground,FloatBorder:OpencodeBorder', -- Highlight group for the opencode window\n    persist_state = true, -- Keep buffers when toggling\u002Fclosing UI so window state restores quickly\n    icons = {\n      preset = 'nerdfonts', -- 'nerdfonts' | 'text'. Choose UI icon style (default: 'nerdfonts')\n      overrides = {}, -- Optional per-key overrides, see section below\n    },\n    questions = {\n      use_vim_ui_select = false, -- If true, render questions\u002Fprompts with vim.ui.select instead of showing them inline in the output buffer.\n    },\n    output = {\n      filetype = 'opencode_output', -- Filetype assigned to the output buffer (default: 'opencode_output')\n       compact_assistant_headers = false, -- 'full' (default), 'minimal' (compact if same mode), or 'hidden' (no headers for assistant)\n      tools = {\n        show_output = true, -- Show tools output [diffs, cmd output, etc.] (default: true)\n        show_reasoning_output = true, -- Show reasoning\u002Fthinking steps output (default: true)\n      },\n      rendering = {\n        markdown_debounce_ms = 250, -- Debounce time for markdown rendering on new data (default: 250ms)\n        on_data_rendered = nil, -- Called when new data is rendered; set to false to disable default RenderMarkdown\u002FMarkview behavior\n      },\n      max_messages = nil, -- Max number of messages to keep in the output buffer; older messages will be removed as new ones arrive (default: nil, which means no limit)\n    },\n    input = {\n      min_height = 0.10, -- min height of prompt input as percentage of window height\n      max_height = 0.25, -- max height of prompt input as percentage of window height\n      text = {\n        wrap = false, -- Wraps text inside input window\n      },\n      -- Auto-hide input window when prompt is submitted or focus switches to output window\n      auto_hide = false,\n    },\n    picker = {\n      snacks_layout = nil -- `layout` opts to pass to Snacks.picker.pick({ layout = ... })\n    },\n    completion = {\n      file_sources = {\n        enabled = true,\n        preferred_cli_tool = 'server', -- 'fd','fdfind','rg','git','server' if nil, it will use the best available tool, 'server' uses opencode cli to get file list (works cross platform) and supports folders\n        ignore_patterns = {\n          '^%.git\u002F',\n          '^%.svn\u002F',\n          '^%.hg\u002F',\n          'node_modules\u002F',\n          '%.pyc$',\n          '%.o$',\n          '%.obj$',\n          '%.exe$',\n          '%.dll$',\n          '%.so$',\n          '%.dylib$',\n          '%.class$',\n          '%.jar$',\n          '%.war$',\n          '%.ear$',\n          'target\u002F',\n          'build\u002F',\n          'dist\u002F',\n          'out\u002F',\n          'deps\u002F',\n          '%.tmp$',\n          '%.temp$',\n          '%.log$',\n          '%.cache$',\n        },\n        max_files = 10,\n        max_display_length = 50, -- Maximum length for file path display in completion, truncates from left with \"...\"\n      },\n    },\n  },\n  context = {\n    enabled = true, -- Enable automatic context capturing\n    cursor_data = {\n      enabled = false, -- Include cursor position and line content in the context\n      context_lines = 5, -- Number of lines before and after cursor to include in context\n    },\n    diagnostics = {\n      info = false, -- Include diagnostics info in the context (default to false\n      warning = true, -- Include diagnostics warnings in the context\n      error = true, -- Include diagnostics errors in the context\n      only_closest = false, -- If true, only diagnostics for cursor\u002Fselection\n    },\n    current_file = {\n      enabled = true, -- Include current file path and content in the context\n      show_full_path = true,\n    },\n    files = {\n      enabled = true,\n      show_full_path = true,\n    },\n    selection = {\n      enabled = true, -- Include selected text in the context\n    },\n    buffer = {\n      enabled = false, -- Disable entire buffer context by default, only used in quick chat\n    },\n    git_diff = {\n      enabled = false,\n    },\n  },\n  logging = {\n    enabled = false,\n    level = 'warn', -- debug, info, warn, error\n    outfile = nil,\n  },\n  debug = {\n    enabled = false, -- Enable debug messages in the output window\n    capture_streamed_events = false,\n    show_ids = true,\n    quick_chat = {\n      keep_session = false, -- Keep quick_chat sessions for inspection, this can pollute your sessions list\n      set_active_session = false,\n    },\n  },\n  prompt_guard = nil, -- Optional function that returns boolean to control when prompts can be sent (see Prompt Guard section)\n\n  -- User Hooks for custom behavior at certain events\n  hooks = {\n    on_file_edited = nil, -- Called after a file is edited by opencode.\n    on_session_loaded = nil, -- Called after a session is loaded.\n    on_done_thinking = nil, -- Called when opencode finishes thinking (all jobs complete).\n    on_permission_requested = nil, -- Called when a permission request is issued.\n  },\n  quick_chat = {\n    default_model = nil,   -- works better with a fast model like gpt-4.1\n    default_agent = nil, -- Uses the current mode when nil\n    instructions = nil, -- Use built-in instructions if nil\n  },\n})\n```\n\n### Keymap Configuration\n\nThe keymap configuration has been restructured for better organization and clarity:\n\n- **`editor`**: Global keymaps that are available throughout Neovim\n- **`input_window`**: Keymaps specific to the input window\n- **`output_window`**: Keymaps specific to the output window\n- **`permission`**: Special keymaps for responding to permission requests (available in input\u002Foutput windows when there's a pending permission)\n\nConfigure keymaps with the current nested tables directly: `keymap.editor`, `keymap.input_window`, and `keymap.output_window`.\n\nEach keymap entry is a table consising of:\n\n- The string name of an api function = `{ 'toggle' }`\n- Or a custom function: `{ function() ... end }`\n- An optional mode: `{ 'toggle', mode = { 'n', 'i' } }`\n- An optional desc: `{'toggle', desc = 'Toggle Opencode' }`\n- An optional defer_to_completion: `{'toggle', defer_to_completion = true }` if true, when completion menu is open, it will defer to the completion keymaps instead of triggering the action\n\n#### Disabling Specific Keymaps\n\nYou can disable specific default keymaps by setting them to `false` in your configuration:\n\n```lua\nrequire('opencode').setup({\n  keymap = {\n    input_window = {\n      ['\u003Ccr>'] = false, -- Disable Enter key for submitting prompts\n      -- Other keymaps not specified will keep their default bindings\n    }\n  }\n})\n```\n\n### Model Sorting and Favorites\n\nThe provider\u002Fmodel picker supports intelligent sorting based on your favorites and usage history:\n\n#### Sorting Priority\n\nWhen you open the model picker (`\u003Cleader>op`), models are sorted in the following order:\n\n1. **Favorite models** - shown with a ⭐ icon and sorted by the order they were favorited\n2. **Recently accessed models** - sorted by most recent usage\n3. **Other models** - sorted alphabetically\n\n#### Managing Favorites\n\nIn the model picker, press **`\u003CC-f>`** to toggle the currently selected model as a favorite. Favorite models will:\n\n- Display with a ⭐ star icon prefix\n- Always appear at the top of the list\n- Persist across Neovim sessions\n\nNo configuration is needed - the plugin respects and updates the OpenCode CLI format automatically.\n\n### Model Variants\n\nSome models support multiple variants (e.g., different context window sizes or optimization modes). The plugin provides convenient ways to switch between available variants for the currently active model.\n\n#### Switching Variants\n\n- **Via picker**: Press `\u003Cleader>oV` to open the variant picker showing all available variants for the current model\n- **Via cycling**: Press `\u003CM-r>` (Alt+R) in the input or output window to cycle through available variants\n- **Via slash command**: Type `\u002Fvariant` in the input window\n\nWhen you switch variants, the plugin remembers your selection per model, so the next time you use that model, it will automatically use the last selected variant.\n\n### UI icons (disable emojis or customize)\n\nBy default, opencode.nvim uses emojis for icons in the UI. If you prefer a plain, emoji-free interface, you can switch to the `text` preset or override icons individually.\n\nMinimal config to disable emojis everywhere:\n\n```lua\nrequire('opencode').setup({\n  ui = {\n    icons = {\n      preset = 'text', -- switch all icons to text\n    },\n  },\n})\n```\n\nOverride specific icons while keeping the preset:\n\n```lua\nrequire('opencode').setup({\n  ui = {\n    icons = {\n      preset = 'emoji',\n      overrides = {\n        header_user = '> U',\n        header_assistant = 'AI',\n        search = 'FIND',\n        border = '|',\n      },\n    },\n  },\n})\n```\n\nAvailable icon keys (see implementation at lua\u002Fopencode\u002Fui\u002Ficons.lua lines 7-29):\n\n- header_user, header_assistant\n- run, task, read, edit, write\n- plan, search, web, list, tool\n- snapshot, restore_point, restore_count, file\n- status_on, status_off\n- border, bullet\n\n### Window Persistence Behavior\n\n`ui.persist_state` controls how `toggle` behaves:\n\n- `persist_state = true` (default): `toggle()` hides\u002Frestores the UI and keeps buffers\u002Fsession view in memory for fast restore.\n- `persist_state = false`: `toggle()` fully tears down UI buffers and recreates them on next open.\n\nRelated APIs:\n\n- `require('opencode.api').toggle()` follows the `persist_state` behavior above.\n- `require('opencode.api').close()` always fully closes and clears hidden snapshot state.\n- `require('opencode.api').hide()` preserves buffers only when `persist_state = true`; otherwise it behaves like close.\n\n### Picker Layout\n\nYou can customize the layout of the picker used for history, session, references, and timeline\n\n#### Snacks Picker Integration with Opencode\n\nYou can configure a custom action in Snacks pickers to send selected files directly to opencode as context. This works with any file-based picker (files, git_files, buffers, git_status, etc.).\n\n```lua\n-- In your snacks.nvim configuration\n{\n  \"folke\u002Fsnacks.nvim\",\n  opts = {\n    picker = {\n      actions = {\n        opencode_send = function(picker)\n          local selected = picker:selected({ fallback = true })\n          if selected and #selected > 0 then\n            local files = {}\n            for _, item in ipairs(selected) do\n              if item.file then\n                table.insert(files, item.file)\n              end\n            end\n            picker:close()\n\n            require(\"opencode.core\").open({\n              new_session = false,\n              focus = \"input\",\n              start_insert = true,\n            })\n\n            local context = require(\"opencode.context\")\n            for _, file in ipairs(files) do\n              context.add_file(file)\n            end\n          end\n        end,\n      },\n      win = {\n        input = {\n          keys = {\n            -- Use \u003Clocalleader>o or any preferred key to send files to opencode\n            [\"\u003Clocalleader>o\"] = { \"opencode_send\", mode = { \"n\", \"i\" } },\n          },\n        },\n      },\n    },\n  },\n}\n```\n\nThis allows you to:\n\n1. Open any Snacks file picker (`:Snacks picker files`, `:Snacks picker git_files`, etc.)\n2. Select one or more files using multi-select\n3. Press `\u003Clocalleader>o` to send those files to opencode as context\n\n#### Snacks Picker Layout\n\nThere's 3 main ways on how to change the snacks picker layout\n\n1. Don't specify the new options -> it'll just default to the user's snack picker layout preset from their snacks config\n2. Specify the new options for opencode, e.g.\n\n   ```lua\n   require(\"opencode\").setup({\n     ui = {\n       picker = {\n        ---@module \"snacks\"\n        ---@type snacks.picker.layout.Config | nil\n         snacks_layout = {\n           layout = { border = \"none\", box = \"vertical\", ... }\n         },\n       },\n     },\n   })\n   ```\n\n3. Specify a [builtin layout preset](https:\u002F\u002Fgithub.com\u002Ffolke\u002Fsnacks.nvim\u002Fblob\u002Fmain\u002Fdocs\u002Fpicker.md#%EF%B8%8F-layouts) for snacks picker OR a custom layout defined in your snacks config's `opts.picker.layouts`\n\n   ```lua\n   -- opencode.lua\n   require(\"opencode\").setup({\n     ui = {\n       picker = {\n        ---@module \"snacks\"\n        ---@type snacks.picker.layout.Config | nil\n         snacks_layout = {\n           preset = \"custom_layout\" -- or builtin snacks, like \"select\", \"default\", etc\n         },\n       },\n     },\n   })\n   ```\n\n   ```lua\n   -- snacks.lua\n   {\n     \"folke\u002Fsnacks.nvim\",\n     opts = {\n       picker = {\n         layouts = {\n           custom_layout  = {\n             layout = { border = \"none\", box = \"vertical\", ... }\n             -- ...\n   }\n   ```\n\n## 🧰 Usage\n\n### Available Actions\n\nThe plugin provides the following actions that can be triggered via keymaps, commands, slash commands (typed in the input window), or the Lua API:\n\n| Action                                                      | Default keymap                        | Command                                     | API Function                                                           |\n| ----------------------------------------------------------- | ------------------------------------- | ------------------------------------------- | ---------------------------------------------------------------------- |\n| Open opencode. Close if opened                              | `\u003Cleader>og`                          | `:Opencode`                                 | `require('opencode.api').toggle()`                                     |\n| Open input window (current session)                         | `\u003Cleader>oi`                          | `:Opencode open input`                      | `require('opencode.api').open_input()`                                 |\n| Open input window (new session)                             | `\u003Cleader>oI`                          | `:Opencode open input_new_session`          | `require('opencode.api').open_input_new_session()`                     |\n| Open output window                                          | `\u003Cleader>oo`                          | `:Opencode open output`                     | `require('opencode.api').open_output()`                                |\n| Create and switch to a named session                        | -                                     | `:Opencode session new \u003Cname>`              | `:Opencode session new \u003Cname>` (user command)                          |\n| Rename current session                                      | `\u003Cleader>oR`                          | `:Opencode session rename \u003Cname>`           | `:Opencode session rename \u003Cname>` (user command)                       |\n| Toggle focus opencode \u002F last window                         | `\u003Cleader>ot`                          | `:Opencode toggle focus`                    | `require('opencode.api').toggle_focus()`                               |\n| Close UI windows                                            | `\u003Cleader>oq`                          | `:Opencode close`                           | `require('opencode.api').close()`                                      |\n| Select and load session                                     | `\u003Cleader>os`                          | `:Opencode session select`                  | `require('opencode.api').select_session()`                             |\n| **Select and load child session**                           | `\u003Cleader>oS`                          | `:Opencode session select_child`            | `require('opencode.api').select_child_session()`                       |\n| Open timeline picker (navigate\u002Fundo\u002Fredo\u002Ffork to message)   | `\u003Cleader>oT`                          | `:Opencode timeline`                        | `require('opencode.api').timeline()`                                   |\n| Browse code references from conversation                    | `gr` (window)                         | `:Opencode references` \u002F `\u002Freferences`      | `require('opencode.api').references()`                                 |\n| Configure provider and model                                | `\u003Cleader>op`                          | `:Opencode configure provider`              | `require('opencode.api').configure_provider()`                         |\n| Configure model variant                                     | `\u003Cleader>oV`                          | `:Opencode variant` \u002F `\u002Fvariant`            | `require('opencode.api').configure_variant()`                          |\n| Cycle through model variants                                | `\u003CM-r>` (window)                      | -                                           | `require('opencode.api').cycle_variant()`                              |\n| Open diff view of changes                                   | `\u003Cleader>od`                          | `:Opencode diff open`                       | `require('opencode.api').diff_open()`                                  |\n| Navigate to next file diff                                  | `\u003Cleader>o]`                          | `:Opencode diff next`                       | `require('opencode.api').diff_next()`                                  |\n| Navigate to previous file diff                              | `\u003Cleader>o[`                          | `:Opencode diff prev`                       | `require('opencode.api').diff_prev()`                                  |\n| Close diff view tab                                         | `\u003Cleader>oc`                          | `:Opencode diff close`                      | `require('opencode.api').diff_close()`                                 |\n| Revert all file changes since last prompt                   | `\u003Cleader>ora`                         | `:Opencode revert all prompt`               | `require('opencode.api').diff_revert_all_last_prompt()`                |\n| Revert current file changes last prompt                     | `\u003Cleader>ort`                         | `:Opencode revert this prompt`              | `require('opencode.api').diff_revert_this_last_prompt()`               |\n| Revert all file changes since last session                  | `\u003Cleader>orA`                         | `:Opencode revert all session`              | `require('opencode.api').diff_revert_all_session()`                    |\n| Revert current file changes last session                    | `\u003Cleader>orT`                         | `:Opencode revert this session`             | `require('opencode.api').diff_revert_this_session()`                   |\n| Revert all files to a specific snapshot                     | -                                     | `:Opencode revert all_to_snapshot`          | `require('opencode.api').diff_revert_all(snapshot_id)`                 |\n| Revert current file to a specific snapshot                  | -                                     | `:Opencode revert this_to_snapshot`         | `require('opencode.api').diff_revert_this(snapshot_id)`                |\n| Restore a file to a restore point                           | -                                     | `:Opencode restore snapshot_file`           | `require('opencode.api').diff_restore_snapshot_file(restore_point_id)` |\n| Restore all files to a restore point                        | -                                     | `:Opencode restore snapshot_all`            | `require('opencode.api').diff_restore_snapshot_all(restore_point_id)`  |\n| Initialize\u002Fupdate AGENTS.md file                            | -                                     | `:Opencode session agents_init`             | `require('opencode.api').initialize()`                                 |\n| Run prompt (continue session) [Run opts](#run-opts)         | -                                     | `:Opencode run \u003Cprompt> \u003Copts>`             | `require('opencode.api').run(\"prompt\", opts)`                          |\n| Run prompt (new session) [Run opts](#run-opts)              | -                                     | `:Opencode run new_session \u003Cprompt> \u003Copts>` | `require('opencode.api').run_new_session(\"prompt\", opts)`              |\n| Cancel opencode while it is running                         | `\u003CC-c>`                               | `:Opencode cancel`                          | `require('opencode.api').cancel()`                                     |\n| Set mode to Build                                           | -                                     | `:Opencode agent build`                     | `require('opencode.api').agent_build()`                                |\n| Set mode to Plan                                            | -                                     | `:Opencode agent plan`                      | `require('opencode.api').agent_plan()`                                 |\n| Select and switch mode\u002Fagent                                | -                                     | `:Opencode agent select`                    | `require('opencode.api').select_agent()`                               |\n| Display list of available mcp servers                       | -                                     | `:Opencode mcp`                             | `require('opencode.api').mcp()`                                        |\n| Run user commands                                           | -                                     | `:Opencode run user_command`                | `require('opencode.api').run_user_command()`                           |\n| Share current session and get a link                        | -                                     | `:Opencode session share` \u002F `\u002Fshare`        | `require('opencode.api').share()`                                      |\n| Unshare current session (disable link)                      | -                                     | `:Opencode session unshare` \u002F `\u002Funshare`    | `require('opencode.api').unshare()`                                    |\n| Compact current session (summarize)                         | -                                     | `:Opencode session compact` \u002F `\u002Fcompact`    | `require('opencode.api').compact_session()`                            |\n| Undo last opencode action                                   | -                                     | `:Opencode undo` \u002F `\u002Fundo`                  | `require('opencode.api').undo()`                                       |\n| Redo last opencode action                                   | -                                     | `:Opencode redo` \u002F `\u002Fredo`                  | `require('opencode.api').redo()`                                       |\n| Respond to permission requests (accept once)                | `a` (window) \u002F `\u003Cleader>opa` (global) | `:Opencode permission accept`               | `require('opencode.api').permission_accept()`                          |\n| Respond to permission requests (accept all)                 | `A` (window) \u002F `\u003Cleader>opA` (global) | `:Opencode permission accept_all`           | `require('opencode.api').permission_accept_all()`                      |\n| Respond to permission requests (deny)                       | `d` (window) \u002F `\u003Cleader>opd` (global) | `:Opencode permission deny`                 | `require('opencode.api').permission_deny()`                            |\n| Insert mention (file\u002F agent)                                | `@`                                   | -                                           | -                                                                      |\n| [Pick a file and add to context](#file-mentions)            | `~`                                   | -                                           | -                                                                      |\n| Navigate to next message                                    | `]]`                                  | -                                           | -                                                                      |\n| Navigate to previous message                                | `[[`                                  | -                                           | -                                                                      |\n| Navigate to previous prompt in history                      | `\u003Cup>`                                | -                                           | `require('opencode.api').prev_history()`                               |\n| Navigate to next prompt in history                          | `\u003Cdown>`                              | -                                           | `require('opencode.api').next_history()`                               |\n| Toggle input\u002Foutput panes                                   | `\u003Ctab>`                               | -                                           | -                                                                      |\n| Swap Opencode pane left\u002Fright                               | `\u003Cleader>ox`                          | `:Opencode swap position`                   | `require('opencode.api').swap_position()`                              |\n| Toggle tools output (diffs, cmd output, etc.)               | `\u003Cleader>ott`                         | `:Opencode toggle_tool_output`              | `require('opencode.api').toggle_tool_output()`                         |\n| Toggle reasoning output (thinking steps)                    | `\u003Cleader>otr`                         | `:Opencode toggle_reasoning_output`         | `require('opencode.api').toggle_reasoning_output()`                    |\n| Open a quick chat input with selection\u002Fcurrent line context | `\u003Cleader>o\u002F`                          | `:Opencode quick_chat`                      | `require('opencode.api').quick_chat()`                                 |\n| Add visual selection to context                             | `\u003Cleader>oy`                          | `:Opencode add_visual_selection`            | `require('opencode.api').add_visual_selection(opts?)`                  |\n| Insert visual selection inline into input                   | `\u003Cleader>oY`                          | `:Opencode add_visual_selection_inline`     | `require('opencode.api').add_visual_selection_inline(opts?)`           |\n\n**add_visual_selection opts:**\n\n- `open_input` (boolean, default: `true`): Whether to open the input window after adding the selection. Set to `false` to add selection silently without changing focus.\n\nExample keymap for silent add:\n\n```lua\n['\u003Cleader>oy'] = { 'add_visual_selection', { open_input = false }, mode = {'v'} }\n```\n\n**add_visual_selection_inline** inserts the visually selected code directly into the input buffer as a Markdown code block, prefixed with the file path:\n\n````\n**`path\u002Fto\u002Ffile.lua`**\n\n```lua\n\u003Cselected text>\n````\n\n````\n\nThe cursor is left in normal mode in the input buffer so you can type your prompt around the inserted snippet.\n\n### Run opts\n\nYou can pass additional options when running a prompt via command or API:\n\n- `agent=\u003Cagent_name>`: Specify the agent to use for this prompt (overrides current agent)\n- `model=\u003Cprovider\u002Fmodel_name>`: Specify the model to use for this prompt (overrides current model) e.g. `model=github-copilot\u002Fgpt-4.1`\n- `context.\u003Ccontext_type>.enabled=\u003Ctrue|false>`: Enable\u002Fdisable specific context types for this prompt only. Available context types:\n  - `current_file`\n  - `selection`\n  - `diagnostics.info`\n  - `diagnostics.warning`\n  - `diagnostics.error`\n  - `cursor_data`\n\n#### Example\n\nRun a prompt in a new session using the Plan agent and disabling current file context:\n\n```vim\n:Opencode run new_session \"Please help me plan a new feature\" agent=plan context.current_file.enabled=false\n:Opencode run \"Fix the bug in the current file\" model=github-copilot\u002Fclaude-sonnet-4\n````\n\n## 👮 Permissions\n\nOpencode can issue permission requests for potentially destructive operations (file edits, reverting files, running shell commands, or enabling persistent tool access). Permission requests appear inline in the output and must be responded to before the agent performs the action. Visit [Opencode Permissions Documentation](https:\u002F\u002Fopencode.ai\u002Fdocs\u002Fpermissions\u002F) for more details.\n\n\u003Cdiv align=\"center\">\n  \u003Cimg src=\"https:\u002F\u002Fi.imgur.com\u002FLkZDta5.png\" alt=\"Opencode permission request\" width=\"90%\" \u002F>\n  \u003Cimg src=\"https:\u002F\u002Fi.imgur.com\u002F4mJH1Xg.png\" alt=\"Opencode permission request input window\" width=\"90%\" \u002F>\n\u003C\u002Fdiv>\n\n### Responding to Permission Requests\n\n- **Respond via the dialog:** Focus the Opencode window, move with `j`\u002F`k` or arrow keys, then confirm with `\u003CCR>`. Number shortcuts `1-3` also work for the visible options.\n- **Command:** Use `:Opencode permission accept`, `:Opencode permission accept_all`, or `:Opencode permission deny`. You can pass an index to target a specific queued permission.\n- **API:** Programmatic responses are available: `require('opencode.api').permission_accept()`, `require('opencode.api').permission_accept_all()`, `require('opencode.api').permission_deny()` which map to responses `\"once\"`, `\"always\"`, and `\"reject\"` respectively.\n- **Behavior:** `accept once` allows the single requested action, `accept all` grants persistent permission for similar requests in the current session, and `deny` rejects the request.\n\n---\n\n## 📝 Context\n\nThe following editor context is automatically captured and included in your conversations.\n\n| Context Type    | Description                                          |\n| --------------- | ---------------------------------------------------- |\n| Current file    | Path to the focused file before entering opencode    |\n| Selected text   | Text and lines currently selected in visual mode     |\n| Mentioned files | File info added through [mentions](#file-mentions)   |\n| Diagnostics     | Diagnostics from the current file (if any)           |\n| Cursor position | Current cursor position and line content in the file |\n\n\u003Ca id=\"file-mentions\">\u003C\u002Fa>\n\n### Adding more files to context through file mentions\n\nYou can reference files in your project directly in your conversations with Opencode. This is useful when you want to ask about or provide context about specific files. Type `@` in the input window to trigger the file picker.\nSupported pickers include [`fzf-lua`](https:\u002F\u002Fgithub.com\u002Fibhagwan\u002Ffzf-lua), [`telescope`](https:\u002F\u002Fgithub.com\u002Fnvim-telescope\u002Ftelescope.nvim), [`mini.pick`](https:\u002F\u002Fgithub.com\u002Fechasnovski\u002Fmini.nvim\u002Fblob\u002Fmain\u002Freadmes\u002Fmini-pick.md), [`snacks`](https:\u002F\u002Fgithub.com\u002Ffolke\u002Fsnacks.nvim\u002Fblob\u002Fmain\u002Fdocs\u002Fpicker.md)\n\n### Context bar\n\nYou can quickly see the current context items in the context bar at the top of the input window:\n\n\u003Cdiv align=\"center\">\n  \u003Cimg src=\"https:\u002F\u002Fi.imgur.com\u002FvGgu6br.png\" alt=\"Opencode.nvim context bar\" width=\"90%\" \u002F>\n\u003C\u002Fdiv>\n\nFor `Current file`, the color indicates whether it will be sent with the next prompt:\n\n- Regular highlight: file is pending and will be included.\n- Dimmed\u002Fgray highlight: file was already sent and has not changed, so it will be skipped (delta behavior).\n\nIf the file content changes, it becomes pending again and will be sent on the next prompt.\n\n### Context Items Completion\n\nYou can quickly reference available context items by typing `#` in the input window. This will show a completion menu with all available context items:\n\n- **Current File** - The currently focused file in the editor\n- **Selection** - Currently selected text in visual mode\n- **Diagnostics** - LSP diagnostics from the current file\n- **Cursor Data** - Current cursor position and line content\n- **[filename]** - Files that have been mentioned in the conversation\n- **Agents** - Available agents to switch to\n- **Selections** - Previously made selections in visual mode\n\nContext items that are not currently available will be shown as disabled in the completion menu.\n\nYou should also see the list of files agents and selections in the menu, selecting them in the menu will remove them from the context.\n\n\u003Cdiv align=\"center\">\n  \u003Cimg src=\"https:\u002F\u002Fi.imgur.com\u002FUqQKW33.png\" alt=\"Opencode.nvim context items completion\" width=\"90%\" \u002F>\n\u003C\u002Fdiv>\n\n## 🔄 Agents\n\nOpencode provides two built-in agents and supports custom ones:\n\n### Built-in Agents\n\n- **Build** (default): Full development agent with all tools enabled for making code changes\n- **Plan**: Restricted agent for planning and analysis without making file changes. Useful for code review and understanding code without modifications\n\n### Switching Agent\n\nPress `\u003CM-m>` (Alt+M) in the input window to switch between agents during a session.\n\n### Custom Agents\n\nYou can create custom agents through your opencode config file. Each agent can have its own:\n\n- Agent configuration\n- Custom prompt\n- Enabled\u002Fdisabled tools\n- And more\n\nSee [Opencode Agents Documentation](https:\u002F\u002Fopencode.ai\u002Fdocs\u002Fagents\u002F) for full configuration options.\n\n## 🔌 Custom\u002FExternal Server Configuration\n\nBy default, opencode.nvim spawns a local `opencode serve` process. You can instead connect to an external or containerized opencode server by configuring the `server` table.\n\n### Basic Connection\n\nConnect to an existing server:\n\n```lua\nrequire('opencode').setup({\n  server = {\n    url = 'localhost',     -- or 'http:\u002F\u002F192.168.1.100'\n    port = 8080,\n    timeout = 5,\n  },\n})\n```\n\n### Auto-Spawning with Docker\n\nUse `spawn_command` to automatically start your server and `kill_command` to stop it:\n\n```lua\nrequire('opencode').setup({\n  server = {\n    url = 'localhost',\n    port = 'auto',  -- Random port for project isolation\n    -- Path mapping: translate host paths to container paths\n    path_map = function(host_path)\n      local cwd = vim.fn.getcwd()\n      -- Replace host project directory with container mount point\n      return host_path:gsub(vim.pesc(cwd), '\u002Fapp')\n    end,\n    -- Spawn command: start Docker container with opencode server\n    spawn_command = function(port, url)\n      local dir_name = string.lower(vim.fn.fnamemodify(vim.fn.getcwd(), \":t\"))\n      local cwd = vim.fn.getcwd()\n      local container_name = string.format('opencode-%s', dir_name)\n\n      -- Check if container is already running\n      local check_cmd = string.format('docker ps --filter \"name=%s\" --format \"{{.Names}}\"', container_name)\n      local handle = io.popen(check_cmd)\n      local result = handle:read(\"*a\")\n      handle:close()\n\n      if result and result:match(container_name) then\n        print(string.format(\"[opencode.nvim] Container %s is already running, skipping start\", container_name))\n        return true\n      end\n\n      -- First, try to stop any existing container with the same name\n      os.execute(string.format('docker stop %s 2>\u002Fdev\u002Fnull || true', container_name))\n\n      local cmd = string.format([[\ndocker run -d --rm \\\n--name %s \\\n-p %d:4096 \\\n-v ~\u002F.local\u002Fstate\u002Fopencode:\u002Fhome\u002Fnode\u002F.local\u002Fstate\u002Fopencode \\\n-v ~\u002F.local\u002Fshare\u002Fopencode:\u002Fhome\u002Fnode\u002F.local\u002Fshare\u002Fopencode \\\n-v ~\u002F.config\u002Fopencode:\u002Fhome\u002Fnode\u002F.config\u002Fopencode \\\n-v \"%s\":\u002Fapp:rw \\\nopencode:latest opencode serve --port 4096 --hostname '0.0.0.0']],\n        container_name,\n        port,\n        cwd\n      )\n\n      print(string.format(\"[opencode.nvim] Starting OpenCode container: %s on port %d\", container_name, port))\n      return os.execute(cmd)\n    end,\n    -- Kill command: stop Docker container when auto_kill is triggered\n    kill_command = function(port, url)\n      local dir_name = string.lower(vim.fn.fnamemodify(vim.fn.getcwd(), \":t\"))\n      local container_name = string.format('opencode-%s', dir_name)\n\n      print(string.format(\"[opencode.nvim] Stopping OpenCode container: %s\", container_name))\n      return os.execute(string.format('docker stop %s 2>\u002Fdev\u002Fnull', container_name))\n    end,\n    auto_kill = true,  -- Enable automatic cleanup when last nvim exits\n  },\n})\n```\n\n### Path Mapping for Containers\u002FWSL\n\nWhen paths on the server differ from your host (e.g., `\u002Fapp` in container vs `\u002Fhome\u002Fuser\u002Fproject` on host):\n\n```lua\nrequire('opencode').setup({\n  server = {\n    url = 'localhost',\n    port = 8080,\n    path_map = '\u002Fapp',  -- Simple string replacement\n  },\n})\n```\n\n### Auto-Spawning with WSL\n\nRun opencode server inside WSL while using Neovim on Windows:\n\n```lua\nrequire('opencode').setup({\n  server = {\n    url = 'localhost',\n    port = 'auto',  -- Random port for project isolation\n\n    -- Spawn opencode server inside WSL\n    spawn_command = function(port, url)\n      local cmd = string.format(\n        'wsl.exe -e bash -c \"opencode serve --hostname 127.0.0.1 --port %d\"',\n        port\n      )\n      print(string.format('[opencode.nvim] Starting WSL server on port %d', port))\n      return vim.fn.jobstart(cmd, { detach = 1 })\n    end,\n\n    -- Kill WSL opencode process\n    kill_command = function(port, url)\n      print(string.format('[opencode.nvim] Stopping WSL server on port %d', port))\n      vim.fn.jobstart('wsl.exe -e pkill -f \"opencode serve.*--port ' .. port .. '\"')\n    end,\n\n    -- Windows → WSL path translation (for requests)\n    path_map = function(host_path)\n      if vim.fn.has('win32') == 1 then\n        -- Convert C:\\Users\\... → \u002Fmnt\u002Fc\u002FUsers\u002F...\n        local drive, rest = host_path:match('^([A-Za-z]):(.*)$')\n        if drive then\n          local wsl_path = '\u002Fmnt\u002F' .. drive:lower() .. rest:gsub('\\\\', '\u002F')\n          return wsl_path\n        end\n      end\n      return host_path\n    end,\n\n    -- WSL → Windows path translation (for responses)\n    reverse_path_map = function(server_path)\n      -- Convert \u002Fmnt\u002Fc\u002FUsers\u002F... → C:\\Users\\...\n      local drive, rest = server_path:match('^\u002Fmnt\u002F([a-z])(.*)$')\n      if drive then\n        local windows_path = drive:upper() .. ':' .. rest:gsub('\u002F', '\\\\')\n        return windows_path\n      end\n      return server_path\n    end,\n\n    auto_kill = true,  -- Kill server when last nvim instance exits\n  },\n})\n```\n\n### Configuration Options\n\n- `url` (string | nil): Server hostname\u002FURL (e.g., 'localhost', 'http:\u002F\u002F192.168.1.100')\n- `port` (number | 'auto' | nil): Port number, `'auto'` for random port, or nil for default (4096)\n- `timeout` (number): Health check timeout in seconds (default: 5)\n- `spawn_command` (function | nil): Optional function to start server: `function(port, url) ... end`\n- `kill_command` (function | nil): Optional function to stop server when `auto_kill` triggers: `function(port, url) ... end`\n- `auto_kill` (boolean): Kill spawned servers when last nvim instance exits (default: true)\n- `path_map` (string | function | nil): Transform host paths to server paths (for outgoing requests)\n- `reverse_path_map` (function | nil): Transform server paths back to host paths (for incoming responses\u002Fevents)\n\n### Multi-Instance Support\n\nWhen `port = 'auto'` is used, opencode.nvim:\n\n- Tracks which nvim instances are using each port\n- Only kills the server when the last nvim instance exits (if `auto_kill = true`). Only applies to servers spawned by the plugin with `spawn_command`\u002F`kill_command`.\n- Locally spawned servers will be killed automatically regardless of the auto_kill setting if they are the last nvim instance using them\n\n## User Commands and Slash Commands\n\nYou can run predefined user commands and built-in slash commands from the input window by typing `\u002F`. This opens a command picker where you can select a command to execute. The output of the command will be included in your prompt context.\n\n**Built-in slash commands** include:\n\n- `\u002Fshare` — Share the current session and get a link\n- `\u002Funshare` — Unshare the current session\n- `\u002Fcompact` — Compact (summarize) the current session\n- `\u002Fundo` — Undo the last opencode action\n- `\u002Fredo` — Redo the last undone action\n- `\u002Fagents_init` — Initialize\u002Fupdate AGENTS.md\n- `\u002Fhelp` — Show help\n- `\u002Fmcp` — Show MCP servers\n- `\u002Fmodels` — Switch provider\u002Fmodel\n- `\u002Fvariant` — Switch model variant\n- `\u002Fsessions` — Switch session\n- `\u002Fchild-sessions` — Switch to a child session\n- `\u002Fagent` — Switch agent\u002Fmode\n- ...and more\n\n**User commands** are custom scripts you define. They are loaded from:\n\n- `.opencode\u002Fcommand\u002F` (project-specific)\n- `command\u002F` (global, in config directory)\n\nYou can also run user commands by name with `:Opencode command \u003Cname>`.\n\n\u003Cimg src=\"https:\u002F\u002Fi.imgur.com\u002FYQhhoPS.png\" alt=\"Opencode.nvim contextual actions\" width=\"90%\" \u002F>\n\nSee [User Commands Documentation](https:\u002F\u002Fopencode.ai\u002Fdocs\u002Fcommands\u002F) for more details.\n\n## 📸 Contextual Actions for Snapshots\n\n> [!WARNING] > _Snapshots are an experimental feature_\n> in opencode and sometimes the dev team may disable them or change their behavior.\n> This repository will be updated to match the latest opencode changes as soon as possible.\n\nOpencode.nvim automatically creates **snapshots** of your workspace at key moments (such as after running prompts or making changes). These snapshots are like lightweight git commits, allowing you to review, compare, and restore your project state at any time.\n\n**Contextual actions** for snapshots are available directly in the output window. When a snapshot is referenced in the conversation, you can trigger actions on it via keymaps displayed by the UI.\n\n### Available Snapshot Actions\n\n- **Diff:** View the differences between the current state and the snapshot.\n- **Revert file:** Revert the selected file to the state it was in at the snapshot.\n- **Revert all files:** Revert all files in the workspace to the state they were\n\n### How to Use\n\n- When a message in the output references a snapshot (look for 📸 **Created Snapshot** or similar), move your cursor to that line and a little menu will be displayed above.\n\n### Example\n\nWhen you see a snapshot in the output:\n\n\u003Cimg src=\"https:\u002F\u002Fi.imgur.com\u002FeKOjhTN.png\" alt=\"Opencode.nvim contextual actions\" width=\"90%\" \u002F>\n\n> **Tip:** Reverting a snapshot will restore all files to the state they were in at that snapshot, so use it with caution!\n\n## 🕛 Contextual Restore points\n\nOpencode.nvim automatically creates restore points before a revet operation. This allows you to undo a revert if needed.\n\nYou will see restore points under the Snapshot line like so:\n\u003Cimg src=\"https:\u002F\u002Fi.imgur.com\u002FDKCOdt0.png\" alt=\"Opencode.nvim restore points\" width=\"90%\" \u002F>\n\n### Available Restore Actions\n\n- **Restore file:** Restore the selected file to the state it was in before the last revert operation.\n- **Restore all :** Restore all files in the workspace to the state they were in before the revert action\n\n## Highlight Groups\n\nThe plugin defines several highlight groups that can be customized to match your colorscheme:\n\n- `OpencodeBorder`: Border color for Opencode windows (default: #616161)\n- `OpencodeBackground`: Background color for Opencode windows (linked to `Normal`)\n- `OpencodeSessionDescription`: Session description text color (linked to `Comment`)\n- `OpencodeMention`: Highlight for @file mentions (linked to `Special`)\n- `OpencodeToolBorder`: Border color for tool execution blocks (default: #3b4261)\n- `OpencodeMessageRoleAssistant`: Assistant message highlight (linked to `Added`)\n- `OpencodeMessageRoleUser`: User message highlight (linked to `Question`)\n- `OpencodeDiffAdd`: Highlight for added line in diffs (default: #2B3328)\n- `OpencodeDiffDelete`: Highlight for deleted line in diffs (default: #43242B)\n- `OpencodeAgentPlan`: Agent indicator in winbar for Plan mode (default: #61AFEF background)\n- `OpencodeAgentBuild`: Agent indicator in winbar for Build mode (default: #616161 background)\n- `OpencodeAgentCustom`: Agent indicator in winbar for custom modes (default: #3b4261 background)\n- `OpencodeContestualAction`: Highlight for contextual actions in the output window (default: #3b4261 background)\n- `OpencodeInputLegend`: Highlight for input window legend (default: #CCCCCC background)\n- `OpencodeHint`: Highlight for hinting messages in input window and token info in output window footer (linked to `Comment`)\n\n## 🛡️ Prompt Guard\n\nThe `prompt_guard` configuration option allows you to control when prompts can be sent to Opencode. This is useful for preventing accidental or unauthorized AI interactions in certain contexts.\n\n### Configuration\n\nSet `prompt_guard` to a function that returns a boolean:\n\n```lua\nrequire('opencode').setup({\n  prompt_guard = function()\n    -- Your custom logic here\n    -- Return true to allow, false to deny\n    return true\n  end,\n})\n\n```\n\n## 🪝 Custom user hooks\n\nYou can define custom functions to be called at specific events in Opencode:\n\n- `on_file_edited`: Called after a file is edited by Opencode.\n- `on_session_loaded`: Called after a session is loaded.\n- `on_done_thinking`: Called when Opencode finishes thinking (all user jobs complete).\n- `on_permission_requested`: Called when a permission request is issued.\n\n```lua\nrequire('opencode').setup({\n  hooks = {\n    on_file_edited = function(file_path, edit_type)\n      -- Custom logic after a file is edited\n      print(\"File edited: \" .. file_path .. \" Type: \" .. edit_type)\n    end,\n    on_session_loaded = function(session_name)\n      -- Custom logic after a session is loaded\n      print(\"Session loaded: \" .. session_name)\n    end,\n    on_done_thinking = function()\n      -- Custom logic when thinking is done\n      print(\"Done thinking!\")\n    end,\n    on_permission_requested = function()\n      -- Custom logic when a permission is requested\n      print(\"Permission requested!\")\n    end,\n  },\n})\n```\n\n### Behavior\n\n- **Before sending prompts**: The guard is checked before any prompt is sent to the AI. If denied, an ERROR notification is shown and the prompt is not sent.\n- **Before opening UI**: The guard is checked when opening the Opencode buffer for the first time. If denied, a WARN notification is shown and the UI is not opened.\n- **No parameters**: The guard function receives no parameters. Access vim state directly (e.g., `vim.fn.getcwd()`, `vim.bo.filetype`).\n- **Error handling**: If the guard function throws an error or returns a non-boolean value, the prompt is denied with an appropriate error message.\n\n## 📡 Server-Sent Events (SSE) autocmds\n\nOpencode.nvim forwards all server-sent events as Neovim User autocmds, allowing you to react to events and automate workflows. All events are fired with the pattern `OpencodeEvent:\u003Cevent.type>`.\n\n### Event Data Structure\n\nThe autocmd receives event data in `args.data.event`:\n\n```lua\n{\n  type = \"event.name\",  -- Opencode event type\n  properties = { ... }  -- Event-specific properties\n}\n```\n\n### Wildcard Patterns\n\nYou can use wildcards to match multiple event types:\n\n- `OpencodeEvent:*` - All events\n- `OpencodeEvent:session.*` - All session events\n- `OpencodeEvent:permission.*` - All permission events\n\n### Example\n\n```lua\nvim.api.nvim_create_autocmd('User', {\n  pattern = 'OpencodeEvent:permission.asked',\n  callback = function(args)\n    local event = args.data.event\n    vim.notify(vim.inspect(event))\n    -- trigger custom logic\n  end,\n})\n```\n\n## Quick chat\n\nQuick chat allows you to start a temporary opencode session with context from the current line or selection.\nThis is optimized for narrow code edits or insertion. When the request is complex it will and require more context, it is recommended to use the full opencode UI.\n\nDue to the narrow context the resulting may be less accurate and edits may sometime fails. For best results, try to keep the request focused and simple.\n\n### Starting a quick chat\n\nPress `\u003Cleader>o\u002F` in normal mode to open a quick chat input window.\n\n\u003Cdiv align=\"center\">\n  \u003Cimg src=\"https:\u002F\u002Fi.imgur.com\u002F5JNlFZn.png\">\n\u003C\u002Fdiv>\n\u003Cdiv align=\"center\">\n  \u003Cimg src=\"https:\u002F\u002Fi.imgur.com\u002FScRgqfC.png\">\n\u003C\u002Fdiv>\n\n### Example chat prompts\n\n- Transform to a lua array\n- Add lua annotations\n- Write a conventional commit message for my changes #diff\n- Fix these warnings #warn\n- complete this function\n\n## 🔧 Setting up Opencode\n\nIf you're new to opencode:\n\n1. **What is Opencode?**\n   - Opencode is an AI coding agent built for the terminal\n   - It offers powerful AI assistance with extensible configurations such as LLMs and MCP servers\n\n2. **Installation:**\n   - Visit [Install Opencode](https:\u002F\u002Fopencode.ai\u002Fdocs\u002F#install) for installation and configuration instructions\n   - Ensure the `opencode` command is available after installation\n\n3. **Configuration:**\n   - Run `opencode auth login` to set up your LLM provider\n   - Configure your preferred LLM provider and model in the `~\u002F.config\u002Fopencode\u002Fconfig.json` or `~\u002F.config\u002Fopencode\u002Fopencode.json` file\n\n## 🙏 Acknowledgements\n\nThis plugin is a fork of the original [goose.nvim](https:\u002F\u002Fgithub.com\u002Fazorng\u002Fgoose.nvim) plugin by [azorng](https:\u002F\u002Fgithub.com\u002Fazorng\u002F)\nFor git history purposes the original code is copied instead of just forked.\n","# 🤖 opencode.nvim\n\n> 用于 opencode 的 Neovim 前端——一个基于终端的 AI 编码助手\n\n\u003Cdiv align=\"center\">\n  \u003Cimg src=\"https:\u002F\u002Fraw.githubusercontent.com\u002Fsst\u002Fopencode\u002Fdev\u002Fpackages\u002Fweb\u002Fsrc\u002Fassets\u002Flogo-ornate-dark.svg\" alt=\"Opencode logo\" width=\"30%\" \u002F>\n\u003C\u002Fdiv>\n\n\u003Cdiv align=\"center\">\n\n![Neovim](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FNeoVim-%2357A143.svg?&style=for-the-badge&logo=neovim&logoColor=white)\n[![GitHub 星标](https:\u002F\u002Fimg.shields.io\u002Fgithub\u002Fstars\u002Fsudo-tee\u002Fopencode.nvim?style=for-the-badge)](https:\u002F\u002Fgithub.com\u002Fsudo-tee\u002Fopencode.nvim\u002Fstargazers)\n![最后提交](https:\u002F\u002Fimg.shields.io\u002Fgithub\u002Flast-commit\u002Fsudo-tee\u002Fopencode.nvim?style=for-the-badge)\n\n\u003Ca href=\"https:\u002F\u002Fwww.buymeacoffee.com\u002Fsudo.tee\">\u003Cimg src=\"https:\u002F\u002Fwww.buymeacoffee.com\u002Fassets\u002Fimg\u002Fcustom_images\u002Forange_img.png\" height=\"20px\">\u003C\u002Fa>\n\n\u003C\u002Fdiv>\n\n## ✨ 描述\n\n此插件在 Neovim 和 [opencode](https:\u002F\u002Fgithub.com\u002Fsst\u002Fopencode) AI 助手之间建立了桥梁，创建了一个聊天界面，并捕获编辑器上下文（当前文件、选区），以增强你的提示信息。它会维护与工作区绑定的持久会话，让你能够像使用 Cursor AI 等工具一样，与 AI 助手进行持续对话。\n\n## 主要特性\n\n### 聊天面板\n\n聊天面板是 Neovim 内的一个专用窗口，允许你与 opencode AI 助手保持持久对话。它会显示你之前的对话和回复，并自动将当前工作区和编辑器状态作为上下文，这样你就可以在不离开 Neovim 的情况下迭代代码。你可以输入提示、查看回答，并在保持聊天会话打开的同时返回到你的代码缓冲区。\n\n\u003Cdiv align=\"center\">\n  \u003Cimg src=\"https:\u002F\u002Foss.gittoolsai.com\u002Fimages\u002Fsudo-tee_opencode.nvim_readme_c06078cdecf5.png\">\n\u003C\u002Fdiv>\n\n### 快速缓冲区聊天（\u003Cleader>o\u002F）实验性功能\n\n这是一个实验性功能，允许你使用当前缓冲区的上下文与 AI 进行聊天。在可视模式下，它会捕获选中的文本作为上下文；而在普通模式下，则会使用当前行。AI 将对文件进行快速编辑，并由插件直接应用这些更改。\n\n请大胆尝试并提供反馈！\n\n更多详情请参阅 [快速聊天](#-quick-chat) 部分。\n\n\u003Cdiv align=\"center\">\n  \u003Cimg src=\"https:\u002F\u002Fi.imgur.com\u002F5JNlFZn.png\">\n\u003C\u002Fdiv>\n\n## 📑 目录\n\n- [⚠️ 注意事项](#caution)\n- [要求](#-requirements)\n- [安装](#-installation)\n- [配置](#️-configuration)\n- [使用](#-usage)\n- [权限](#-permissions)\n- [上下文](#-context)\n- [代理](#-agents)\n- [自定义\u002F外部服务器配置](#-customexternal-server-configuration)\n- [用户命令和斜杠命令](#user-commands-and-slash-commands)\n- [快照的上下文操作](#-contextual-actions-for-snapshots)\n- [上下文恢复点](#-contextual-restore-points)\n- [高亮组](#highlight-groups)\n- [提示保护](#️-prompt-guard)\n- [自定义用户钩子](#-custom-user-hooks)\n- [服务器发送事件 (SSE) 自动命令](#-server-sent-events-sse-autocmds)\n- [快速聊天](#quick-chat)\n- [设置 Opencode](#-setting-up-opencode)\n- [配方](.\u002Fdocs\u002Frecipes)\n\n## ⚠️ 注意事项\n\n此插件目前处于早期开发阶段，可能存在 bug 和破坏性变更。尚不建议用于生产环境。如遇到任何问题，请在 [GitHub 仓库](https:\u002F\u002Fgithub.com\u002Fsudo-tee\u002Fopencode.nvim\u002Fissues) 中报告。\n\n[Opencode](https:\u002F\u002Fgithub.com\u002Fsst\u002Fopencode) 同样处于早期开发阶段，可能会有破坏性变更。请确保你使用的 Opencode CLI 版本兼容（v0.6.3 及以上）。\n\n如果你的升级导致插件无法正常工作，请提交一个问题或回退到上一个可用版本。\n\n## 📋 要求\n\n- 已安装并可访问的 Opencode (v0.6.3+) CLI（详见下方 [设置 Opencode](#-setting-up-opencode)）\n\n## 🚀 安装\n\n使用你喜欢的包管理器安装插件。有关自定义选项，请参阅下方 [配置](#️-configuration) 部分。\n\n### 使用 lazy.nvim\n\n```lua\n{\n  \"sudo-tee\u002Fopencode.nvim\",\n  config = function()\n    require(\"opencode\").setup({})\n  end,\n  dependencies = {\n    \"nvim-lua\u002Fplenary.nvim\",\n    {\n      \"MeanderingProgrammer\u002Frender-markdown.nvim\",\n      opts = {\n        anti_conceal = { enabled = false },\n        file_types = { 'markdown', 'opencode_output' },\n      },\n      ft = { 'markdown', 'Avante', 'copilot-chat', 'opencode_output' },\n    },\n    -- 可选，用于文件提及和命令补全，仅选择一个\n    'saghen\u002Fblink.cmp',\n    -- 'hrsh7th\u002Fnvim-cmp',\n\n    -- 可选，用于文件提及选择器，仅选择一个\n    'folke\u002Fsnacks.nvim',\n    -- 'nvim-telescope\u002Ftelescope.nvim',\n    -- 'ibhagwan\u002Ffzf-lua',\n    -- 'nvim_mini\u002Fmini.nvim',\n  },\n}\n```\n\n## ⚙️ 配置\n\n```lua\n-- 包含所有可用选项的默认配置\nrequire('opencode').setup({\n  preferred_picker = nil, -- 'telescope', 'fzf', 'mini.pick', 'snacks', 'select'，如果为 null，则会使用最佳可用选择器。注意 mini.pick 不支持多选\n  preferred_completion = nil, -- 'blink', 'nvim-cmp','vim_complete'，如果为 null，则会使用最佳可用补全\n  default_global_keymaps = true, -- 如果为 false，则禁用所有默认全局快捷键\n  default_mode = 'build', -- 'build' 或 'plan' 或任何自定义配置。@see [OpenCode Agents](https:\u002F\u002Fopencode.ai\u002Fdocs\u002Fmodes\u002F)\n  default_system_prompt = nil, -- 用于所有会话的自定义系统提示。如果为 null，则使用默认内置系统提示\n  keymap_prefix = '\u003Cleader>o', -- 默认全局快捷键前缀，可更改为你喜欢的前缀，它将应用于所有以 \u003Cleader>o 开头的快捷键\n  opencode_executable = 'opencode', -- 你的 opencode 二进制文件名\n\n  -- 自定义\u002F外部 opencode 服务器的服务器配置\n  server = {\n    url = nil,             -- URL\u002F主机名（例如：'http:\u002F\u002F192.168.1.100'、'localhost'、'https:\u002F\u002Fmyserver.com'）\n    port = nil,            -- 端口号（例如：8080），'auto' 表示随机端口\n    timeout = 5,           -- 连接时的健康检查超时时间，单位为秒\n    spawn_command = nil,   -- 可选的启动服务器函数：function(port, url) ... end\n    auto_kill = true,      -- 当最后一个 Neovim 实例退出时，自动关闭已启动的服务器（默认：true）。仅适用于通过 spawn_command\u002Fkill_command 由插件启动的服务器\n    path_map = nil,        -- 将主机路径映射到服务器路径：字符串（'\u002Fapp'）或函数（path -> 字符串）\n  },\n\nkeymap = {\n    editor = {\n      ['\u003Cleader>og'] = { 'toggle' }, -- 打开 Opencode。如果已打开则关闭\n      ['\u003Cleader>oi'] = { 'open_input' }, -- 在插入模式下打开并聚焦输入窗口\n      ['\u003Cleader>oI'] = { 'open_input_new_session' }, -- 在插入模式下打开并聚焦输入窗口，并创建一个新会话\n      ['\u003Cleader>oo'] = { 'open_output' }, -- 打开并聚焦输出窗口\n      ['\u003Cleader>ot'] = { 'toggle_focus' }, -- 在 Opencode 和上一个窗口之间切换焦点\n      ['\u003Cleader>oT'] = { 'timeline' }, -- 显示时间线选择器，用于导航\u002F撤销\u002F重做\u002F分支消息\n      ['\u003Cleader>oq'] = { 'close' }, -- 关闭 UI 窗口\n      ['\u003Cleader>os'] = { 'select_session' }, -- 选择并加载一个 Opencode 会话\n      ['\u003Cleader>oR'] = { 'rename_session' }, -- 重命名当前会话\n      ['\u003Cleader>op'] = { 'configure_provider' }, -- 从预定义列表中快速切换提供者和模型\n      ['\u003Cleader>oV'] = { 'configure_variant' }, -- 切换当前模型的变体\n      ['\u003Cleader>oy'] = { 'add_visual_selection', mode = {'v'} },\n      ['\u003Cleader>oY'] = { 'add_visual_selection_inline', mode = {'v'} }, -- 将可视选区作为内联代码块插入到输入缓冲区\n      ['\u003Cleader>oz'] = { 'toggle_zoom' }, -- 放大或缩小 Opencode 窗口\n      ['\u003Cleader>ov'] = { 'paste_image'}, -- 将剪贴板中的图片粘贴到当前会话中\n      ['\u003Cleader>od'] = { 'diff_open' }, -- 打开自上次 Opencode 提示以来修改文件的差异标签页\n      ['\u003Cleader>o]'] = { 'diff_next' }, -- 导航到下一个文件差异\n      ['\u003Cleader>o['] = { 'diff_prev' }, -- 导航到上一个文件差异\n      ['\u003Cleader>oc'] = { 'diff_close' }, -- 关闭差异视图标签页，返回正常编辑\n      ['\u003Cleader>ora'] = { 'diff_revert_all_last_prompt' }, -- 撤销自上次 Opencode 提示以来的所有文件更改\n      ['\u003Cleader>ort'] = { 'diff_revert_this_last_prompt' }, -- 撤销自上次 Opencode 提示以来的当前文件更改\n      ['\u003Cleader>orA'] = { 'diff_revert_all' }, -- 撤销自上次 Opencode 会话以来的所有文件更改\n      ['\u003Cleader>orT'] = { 'diff_revert_this' }, -- 撤销自上次 Opencode 会话以来的当前文件更改\n      ['\u003Cleader>orr'] = { 'diff_restore_snapshot_file' }, -- 将文件恢复到某个还原点\n      ['\u003Cleader>orR'] = { 'diff_restore_snapshot_all' }, -- 将所有文件恢复到某个还原点\n      ['\u003Cleader>ox'] = { 'swap_position' }, -- 交换 Opencode 窗格的左右位置\n      ['\u003Cleader>ott'] = { 'toggle_tool_output' }, -- 切换工具输出（差异、命令输出等）\n      ['\u003Cleader>otr'] = { 'toggle_reasoning_output' }, -- 切换推理输出（思考步骤）\n      ['\u003Cleader>o\u002F'] = { 'quick_chat', mode = { 'n', 'x' } }, -- 在视觉模式下以选区上下文或在普通模式下以当前行上下文打开快速聊天输入框\n    },\n    input_window = {\n      ['\u003CS-cr>'] = { 'submit_input_prompt', mode = { 'n', 'i' } }, -- 提交提示（普通模式和插入模式）\n      ['\u003Cesc>'] = { 'close', defer_to_completion = true }, -- 关闭 UI 窗口\n      ['\u003CC-c>'] = { 'cancel', defer_to_completion = true }, -- 在运行时取消 Opencode 请求\n      ['~'] = { 'mention_file', mode = 'i' }, -- 选择一个文件并添加到上下文中。参见“文件提及”部分\n      ['@'] = { 'mention', mode = 'i' }, -- 插入提及（文件\u002F代理）\n      ['\u002F'] = { 'slash_commands', mode = 'i' }, -- 在输入窗口中选择要执行的命令\n      ['#'] = { 'context_items', mode = 'i' }, -- 管理上下文项（当前文件、选区、诊断信息、提及的文件）\n      ['\u003CM-v>'] = { 'paste_image', mode = 'i' }, -- 将剪贴板中的图片作为附件粘贴\n      ['\u003Ctab>'] = { 'toggle_pane', mode = { 'n', 'i' }, defer_to_completion = true }, -- 在输入和输出窗格之间切换\n      ['\u003Cup>'] = { 'prev_prompt_history', mode = { 'n', 'i' }, defer_to_completion = true }, -- 导航到历史记录中的上一条提示\n      ['\u003Cdown>'] = { 'next_prompt_history', mode = { 'n', 'i' }, defer_to_completion = true }, -- 导航到历史记录中的下一条提示\n      ['\u003CM-m>'] = { 'switch_mode' }, -- 切换模式（构建\u002F计划）\n      ['\u003CM-r>'] = { 'cycle_variant', mode = { 'n', 'i' } }, -- 循环可用的模型变体\n    },\n    output_window = {\n      ['\u003Cesc>'] = { 'close' }, -- 关闭 UI 窗口\n      ['\u003CC-c>'] = { 'cancel' }, -- 在运行时取消 Opencode 请求\n      [']]'] = { 'next_message' }, -- 导航到对话中的下一条消息\n      ['[['] = { 'prev_message' }, -- 导航到对话中的上一条消息\n      ['\u003Ctab>'] = { 'toggle_pane', mode = { 'n', 'i' } }, -- 在输入和输出窗格之间切换\n      ['i'] = { 'focus_input', 'n' }, -- 从输出窗口聚焦到输入窗口，并进入插入模式\n      ['\u003CM-r>'] = { 'cycle_variant', mode = { 'n' } }, -- 循环可用的模型变体\n      ['\u003Cleader>oS'] = { 'select_child_session' }, -- 选择并加载子会话\n      ['\u003Cleader>oD'] = { 'debug_message' }, -- 在新缓冲区中打开原始消息进行调试\n      ['\u003Cleader>oO'] = { 'debug_output' }, -- 在新缓冲区中打开原始输出进行调试\n      ['\u003Cleader>ods'] = { 'debug_session' }, -- 在新缓冲区中打开原始会话进行调试\n    },\n    session_picker = {\n      rename_session = { '\u003CC-r>' }, -- 重命名会话选择器中选中的会话\n      delete_session = { '\u003CC-d>' }, -- 删除会话选择器中选中的会话\n      new_session = { '\u003CC-s>' }, -- 在会话选择器中创建并切换到新会话\n    },\n    timeline_picker = {\n      undo = { '\u003CC-u>', mode = { 'i', 'n' } }, -- 撤销到时间线选择器中选定的消息\n      fork = { '\u003CC-f>', mode = { 'i', 'n' } }, -- 从时间线选择器中选定的消息分支\n    },\n    history_picker = {\n      delete_entry = { '\u003CC-d>', mode = { 'i', 'n' } }, -- 删除历史选择器中选定的条目\n      clear_all = { '\u003CC-X>', mode = { 'i', 'n' } }, -- 清除历史选择器中的所有条目\n    },\n    model_picker = {\n      toggle_favorite = { '\u003CC-f>', mode = { 'i', 'n' } },\n    },\n    mcp_picker = {\n      toggle_connection = { '\u003CC-t>', mode = { 'i', 'n' } }, -- 在 MCP 选择器中切换 MCP 服务器连接\n    },\n  },\n  ui = {\n    enable_treesitter_markdown = true, -- 在输出窗口中使用 Treesitter 渲染 Markdown（默认：启用）。\n    position = 'right', -- ‘right’（默认）、‘left’ 或 ‘current’。UI 分割的位置。‘current’ 使用当前窗口作为输出。\n    input_position = 'bottom', -- ‘bottom’（默认）或 ‘top’。输入窗口的位置\n    window_width = 0.40, -- 宽度占编辑器宽度的百分比\n    zoom_width = 0.8, -- 放大后的宽度占编辑器宽度的百分比\n    display_model = true, -- 在顶部窗口栏显示模型名称\n    display_context_size = true, -- 在页脚显示上下文大小\n    display_cost = true, -- 在页脚显示成本\n    window_highlight = 'Normal:OpencodeBackground,FloatBorder:OpencodeBorder', -- Opencode 窗口的高亮组\n    persist_state = true, -- 切换或关闭 UI 时保留缓冲区，以便快速恢复窗口状态\n    icons = {\n      preset = 'nerdfonts', -- ‘nerdfonts’ | ‘text’。选择 UI 图标样式（默认：‘nerdfonts’）\n      overrides = {}, -- 可选的按键覆盖，详见下文\n    },\n    questions = {\n      use_vim_ui_select = false, -- 如果为真，则使用 vim.ui.select 渲染问题\u002F提示，而不是直接在输出缓冲区中显示。\n    },\n    output = {\n      filetype = 'opencode_output', -- 分配给输出缓冲区的文件类型（默认：‘opencode_output’）\n      compact_assistant_headers = false, -- ‘full’（默认）、‘minimal’（相同模式下简洁）或 ‘hidden’（助手无标题）\n      tools = {\n        show_output = true, -- 显示工具输出 [差异、命令输出等]（默认：启用）\n        show_reasoning_output = true, -- 显示推理\u002F思考步骤的输出（默认：启用）\n      },\n      rendering = {\n        markdown_debounce_ms = 250, -- 新数据渲染时的防抖时间（默认：250 毫秒）\n        on_data_rendered = nil, -- 当新数据渲染完毕时调用；设置为 false 可禁用默认的 RenderMarkdown\u002FMarkview 行为\n      },\n      max_messages = nil, -- 输出缓冲区中最多保留的消息数量；新消息到达时将移除旧消息（默认：无限制）\n    },\n    input = {\n      min_height = 0.10, -- 提示输入的最小高度占窗口高度的百分比\n      max_height = 0.25, -- 提示输入的最大高度占窗口高度的百分比\n      text = {\n        wrap = false, -- 输入窗口内的文本是否换行\n      },\n      -- 提示提交或焦点切换到输出窗口时自动隐藏输入窗口\n      auto_hide = false,\n    },\n    picker = {\n      snacks_layout = nil -- 传递给 Snacks.picker.pick({ layout = ... }) 的 `layout` 选项\n    },\n    completion = {\n      file_sources = {\n        enabled = true,\n        preferred_cli_tool = 'server', -- ‘fd’、‘fdfind’、‘rg’、‘git’、‘server’；若未指定，则使用最佳可用工具。‘server’ 使用 Opencode CLI 获取文件列表（跨平台），并支持文件夹\n        ignore_patterns = {\n          '^%.git\u002F',\n          '^%.svn\u002F',\n          '^%.hg\u002F',\n          'node_modules\u002F',\n          '%.pyc$',\n          '%.o$',\n          '%.obj$',\n          '%.exe$',\n          '%.dll$',\n          '%.so$',\n          '%.dylib$',\n          '%.class$',\n          '%.jar$',\n          '%.war$',\n          '%.ear$',\n          'target\u002F',\n          'build\u002F',\n          'dist\u002F',\n          'out\u002F',\n          'deps\u002F',\n          '%.tmp$',\n          '%.temp$',\n          '%.log$',\n          '%.cache$',\n        },\n        max_files = 10,\n        max_display_length = 50, -- 完成建议中文件路径的最大显示长度，超过部分从左侧截断并加“…”\n      },\n    },\n  },\n  context = {\n    enabled = true, -- 启用自动上下文捕获\n    cursor_data = {\n      enabled = false, -- 在上下文中包含光标位置和行内容\n      context_lines = 5, -- 光标前后要包含在上下文中的行数\n    },\n    diagnostics = {\n      info = false, -- 在上下文中包含诊断信息（默认关闭）\n      warning = true, -- 在上下文中包含诊断警告\n      error = true, -- 在上下文中包含诊断错误\n      only_closest = false, -- 如果为真，则只包含光标\u002F选区附近的诊断信息\n    },\n    current_file = {\n      enabled = true, -- 在上下文中包含当前文件路径和内容\n      show_full_path = true,\n    },\n    files = {\n      enabled = true,\n      show_full_path = true,\n    },\n    selection = {\n      enabled = true, -- 在上下文中包含选中文本\n    },\n    buffer = {\n      enabled = false, -- 默认禁用整个缓冲区上下文，仅在快速聊天中使用\n    },\n    git_diff = {\n      enabled = false,\n    },\n  },\n  logging = {\n    enabled = false,\n    level = 'warn', -- debug、info、warn、error\n    outfile = nil,\n  },\n  debug = {\n    enabled = false, -- 在输出窗口中启用调试信息\n    capture_streamed_events = false,\n    show_ids = true,\n    quick_chat = {\n      keep_session = false, -- 保留快速聊天会话以供检查，但这可能会污染你的会话列表\n      set_active_session = false,\n    },\n  },\n  prompt_guard = nil, -- 可选函数，返回布尔值以控制何时可以发送提示（参见“提示守卫”部分）\n\n-- 用户钩子，用于在特定事件中实现自定义行为\n  hooks = {\n    on_file_edited = nil, -- 当文件被 opencode 编辑后调用。\n    on_session_loaded = nil, -- 当会话加载完成后调用。\n    on_done_thinking = nil, -- 当 opencode 完成思考（所有任务完成）时调用。\n    on_permission_requested = nil, -- 当发出权限请求时调用。\n  },\n  quick_chat = {\n    default_model = nil,   -- 使用像 gpt-4.1 这样的快速模型效果更好。\n    default_agent = nil, -- 如果为 nil，则使用当前模式。\n    instructions = nil, -- 如果为 nil，则使用内置指令。\n  },\n})\n```\n\n\n\n### 键位配置\n\n键位配置已被重新组织，以提高其结构化和清晰度：\n\n- **`editor`**：在整个 Neovim 中可用的全局键位映射。\n- **`input_window`**：输入窗口专用的键位映射。\n- **`output_window`**：输出窗口专用的键位映射。\n- **`permission`**：用于响应权限请求的特殊键位映射（当输入\u002F输出窗口中有待处理的权限时可用）。\n\n可以直接通过当前的嵌套表来配置键位：`keymap.editor`、`keymap.input_window` 和 `keymap.output_window`。\n\n每个键位条目都是一个表，包含以下内容：\n\n- API 函数的字符串名称 = `{ 'toggle' }`\n- 或者自定义函数：`{ function() ... end }`\n- 可选的模式：`{ 'toggle', mode = { 'n', 'i' } }`\n- 可选的描述：`{'toggle', desc = '切换 Opencode' }`\n- 可选的延迟执行：`{'toggle', defer_to_completion = true }`，如果设置为 true，则当补全菜单打开时，将优先使用补全键位映射，而不是触发该操作。\n\n#### 禁用特定键位\n\n您可以通过将特定默认键位设置为 `false` 来禁用它们：\n\n```lua\nrequire('opencode').setup({\n  keymap = {\n    input_window = {\n      ['\u003Ccr>'] = false, -- 禁用 Enter 键提交提示。\n      -- 其他未指定的键位将保留其默认绑定。\n    }\n  }\n})\n```\n\n### 模型排序与收藏\n\n提供者\u002F模型选择器支持基于您的收藏和使用历史的智能排序：\n\n#### 排序优先级\n\n当您打开模型选择器 (`\u003Cleader>op`) 时，模型将按以下顺序排序：\n\n1. **收藏模型** - 显示带有 ⭐ 图标的模型，并按照收藏顺序排列。\n2. **最近访问的模型** - 按最近使用时间排序。\n3. **其他模型** - 按字母顺序排序。\n\n#### 管理收藏\n\n在模型选择器中，按下 **`\u003CC-f>`** 可以将当前选中的模型切换为收藏。收藏的模型将：\n\n- 带有 ⭐ 星号前缀显示。\n- 始终出现在列表顶部。\n- 在 Neovim 会话之间保持不变。\n\n无需任何配置——插件会自动尊重并更新 OpenCode CLI 的格式。\n\n### 模型变体\n\n某些模型支持多个变体（例如不同的上下文窗口大小或优化模式）。插件提供了便捷的方法来切换当前活动模型的可用变体。\n\n#### 切换变体\n\n- **通过选择器**：按下 `\u003Cleader>oV` 打开变体选择器，显示当前模型的所有可用变体。\n- **通过循环切换**：在输入或输出窗口中按下 `\u003CM-r>`（Alt+R）可在可用变体之间循环切换。\n- **通过斜杠命令**：在输入窗口中输入 `\u002Fvariant`。\n\n当您切换变体时，插件会记住每个模型的选择，因此下次使用该模型时，它将自动使用上次选择的变体。\n\n### UI 图标（禁用表情符号或自定义）\n\n默认情况下，opencode.nvim 在 UI 中使用表情符号作为图标。如果您更喜欢纯文本、无表情符号的界面，可以切换到 `text` 预设，或者单独覆盖图标。\n\n要全局禁用表情符号的最小配置如下：\n\n```lua\nrequire('opencode').setup({\n  ui = {\n    icons = {\n      preset = 'text', -- 将所有图标切换为文本。\n    },\n  },\n})\n```\n\n在保留预设的同时覆盖特定图标：\n\n```lua\nrequire('opencode').setup({\n  ui = {\n    icons = {\n      preset = 'emoji',\n      overrides = {\n        header_user = '> U',\n        header_assistant = 'AI',\n        search = 'FIND',\n        border = '|',\n      },\n    },\n  },\n})\n```\n\n可用的图标键（请参阅 lua\u002Fopencode\u002Fui\u002Ficons.lua 第 7–29 行的实现）：\n\n- header_user、header_assistant\n- run、task、read、edit、write\n- plan、search、web、list、tool\n- snapshot、restore_point、restore_count、file\n- status_on、status_off\n- border、bullet\n\n### 窗口持久化行为\n\n`ui.persist_state` 控制 `toggle` 的行为：\n\n- `persist_state = true`（默认）：`toggle()` 隐藏\u002F恢复 UI，并将缓冲区\u002F会话视图保留在内存中，以便快速恢复。\n- `persist_state = false`：`toggle()` 会完全销毁 UI 缓冲区，并在下次打开时重新创建。\n\n相关 API：\n\n- `require('opencode.api').toggle()` 遵循上述 `persist_state` 行为。\n- `require('opencode.api').close()` 总是会完全关闭并清除隐藏的快照状态。\n- `require('opencode.api').hide()` 仅在 `persist_state = true` 时保留缓冲区；否则其行为与 close 相同。\n\n### 选择器布局\n\n你可以自定义用于历史记录、会话、引用和时间线的选择器布局。\n\n#### Snacks 选择器与 Opencode 集成\n\n你可以在 Snacks 选择器中配置一个自定义动作，将选中的文件直接作为上下文发送到 Opencode。此功能适用于任何基于文件的选择器（files、git_files、buffers、git_status 等）。\n\n```lua\n-- 在你的 snacks.nvim 配置中\n{\n  \"folke\u002Fsnacks.nvim\",\n  opts = {\n    picker = {\n      actions = {\n        opencode_send = function(picker)\n          local selected = picker:selected({ fallback = true })\n          if selected and #selected > 0 then\n            local files = {}\n            for _, item in ipairs(selected) do\n              if item.file then\n                table.insert(files, item.file)\n              end\n            end\n            picker:close()\n\n            require(\"opencode.core\").open({\n              new_session = false,\n              focus = \"input\",\n              start_insert = true,\n            })\n\n            local context = require(\"opencode.context\")\n            for _, file in ipairs(files) do\n              context.add_file(file)\n            end\n          end\n        end,\n      },\n      win = {\n        input = {\n          keys = {\n            -- 使用 \u003Clocalleader>o 或任何你喜欢的键来将文件发送到 Opencode\n            [\"\u003Clocalleader>o\"] = { \"opencode_send\", mode = { \"n\", \"i\" } },\n          },\n        },\n      },\n    },\n  },\n}\n```\n\n这样你就可以：\n\n1. 打开任意 Snacks 文件选择器（`:Snacks picker files`、`:Snacks picker git_files` 等）\n2. 使用多选功能选择一个或多个文件\n3. 按下 `\u003Clocalleader>o` 将这些文件作为上下文发送到 Opencode\n\n#### Snacks 选择器布局\n\n有三种主要方式可以更改 Snacks 选择器的布局：\n\n1. 不指定新选项 -> 将直接使用用户在 Snacks 配置中设置的默认布局预设。\n2. 为 Opencode 指定新的选项，例如：\n\n   ```lua\n   require(\"opencode\").setup({\n     ui = {\n       picker = {\n        ---@module \"snacks\"\n        ---@type snacks.picker.layout.Config | nil\n         snacks_layout = {\n           layout = { border = \"none\", box = \"vertical\", ... }\n         },\n       },\n     },\n   })\n   ```\n\n3. 指定 Snacks 选择器的 [内置布局预设](https:\u002F\u002Fgithub.com\u002Ffolke\u002Fsnacks.nvim\u002Fblob\u002Fmain\u002Fdocs\u002Fpicker.md#%EF%B8%8F-layouts) 或者在 Snacks 配置的 `opts.picker.layouts` 中定义的自定义布局。\n\n   ```lua\n   -- opencode.lua\n   require(\"opencode\").setup({\n     ui = {\n       picker = {\n        ---@module \"snacks\"\n        ---@type snacks.picker.layout.Config | nil\n         snacks_layout = {\n           preset = \"custom_layout\" -- 或内置的 Snacks 布局，如 \"select\"、\"default\" 等\n         },\n       },\n     },\n   })\n   ```\n\n   ```lua\n   -- snacks.lua\n   {\n     \"folke\u002Fsnacks.nvim\",\n     opts = {\n       picker = {\n         layouts = {\n           custom_layout  = {\n             layout = { border = \"none\", box = \"vertical\", ... }\n             -- ...\n   }\n   ```\n\n## 🧰 使用方法\n\n### 可用操作\n\n该插件提供了以下可通过快捷键、命令、斜杠命令（在输入窗口中输入）或 Lua API 触发的操作：\n\n| 操作                                                      | 默认键位                        | 命令                                     | API 函数                                                           |\n| ----------------------------------------------------------- | ------------------------------------- | ------------------------------------------- | ---------------------------------------------------------------------- |\n| 打开 Opencode。如果已打开则关闭                              | `\u003Cleader>og`                          | `:Opencode`                                 | `require('opencode.api').toggle()`                                     |\n| 打开输入窗口（当前会话）                         | `\u003Cleader>oi`                          | `:Opencode open input`                      | `require('opencode.api').open_input()`                                 |\n| 打开输入窗口（新会话）                             | `\u003Cleader>oI`                          | `:Opencode open input_new_session`          | `require('opencode.api').open_input_new_session()`                     |\n| 打开输出窗口                                          | `\u003Cleader>oo`                          | `:Opencode open output`                     | `require('opencode.api').open_output()`                                |\n| 创建并切换到命名会话                        | -                                     | `:Opencode session new \u003Cname>`              | `:Opencode session new \u003Cname>` (用户命令)                          |\n| 重命名当前会话                                      | `\u003Cleader>oR`                          | `:Opencode session rename \u003Cname>`           | `:Opencode session rename \u003Cname>` (用户命令)                       |\n| 切换焦点：Opencode 窗口或上一个窗口                 | `\u003Cleader>ot`                          | `:Opencode toggle focus`                    | `require('opencode.api').toggle_focus()`                               |\n| 关闭 UI 窗口                                            | `\u003Cleader>oq`                          | `:Opencode close`                           | `require('opencode.api').close()`                                      |\n| 选择并加载会话                                     | `\u003Cleader>os`                          | `:Opencode session select`                  | `require('opencode.api').select_session()`                             |\n| **选择并加载子会话**                           | `\u003Cleader>oS`                          | `:Opencode session select_child`            | `require('opencode.api').select_child_session()`                       |\n| 打开时间线选择器（导航\u002F撤销\u002F重做\u002F分支到消息）   | `\u003Cleader>oT`                          | `:Opencode timeline`                        | `require('opencode.api').timeline()`                                   |\n| 浏览对话中的代码引用                            | `gr`（窗口）                         | `:Opencode references` \u002F `\u002Freferences`      | `require('opencode.api').references()`                                 |\n| 配置提供商和模型                                | `\u003Cleader>op`                          | `:Opencode configure provider`              | `require('opencode.api').configure_provider()`                         |\n| 配置模型变体                                    | `\u003Cleader>oV`                          | `:Opencode variant` \u002F `\u002Fvariant`            | `require('opencode.api').configure_variant()`                          |\n| 循环切换模型变体                                | `\u003CM-r>`（窗口）                      | -                                           | `require('opencode.api').cycle_variant()`                              |\n| 打开更改的差异视图                                | `\u003Cleader>od`                          | `:Opencode diff open`                       | `require('opencode.api').diff_open()`                                  |\n| 跳转到下一个文件差异                              | `\u003Cleader>o]`                          | `:Opencode diff next`                       | `require('opencode.api').diff_next()`                                  |\n| 跳转到上一个文件差异                              | `\u003Cleader>o[`                          | `:Opencode diff prev`                       | `require('opencode.api').diff_prev()`                                  |\n| 关闭差异视图标签页                                | `\u003Cleader>oc`                          | `:Opencode diff close`                      | `require('opencode.api').diff_close()`                                 |\n| 撤销自上次提示以来的所有文件更改                   | `\u003Cleader>ora`                         | `:Opencode revert all prompt`               | `require('opencode.api').diff_revert_all_last_prompt()`                |\n| 撤销当前文件自上次提示以来的更改                   | `\u003Cleader>ort`                         | `:Opencode revert this prompt`              | `require('opencode.api').diff_revert_this_last_prompt()`               |\n| 撤销自上次会话以来的所有文件更改                   | `\u003Cleader>orA`                         | `:Opencode revert all session`              | `require('opencode.api').diff_revert_all_session()`                    |\n| 撤销当前文件自上次会话以来的更改                   | `\u003Cleader>orT`                         | `:Opencode revert this session`             | `require('opencode.api').diff_revert_this_session()`                   |\n| 将所有文件恢复到特定快照                          | -                                     | `:Opencode revert all_to_snapshot`          | `require('opencode.api').diff_revert_all(snapshot_id)`                 |\n| 恢复当前文件到特定快照                            | -                                     | `:Opencode revert this_to_snapshot`         | `require('opencode.api').diff_revert_this(snapshot_id)`                |\n| 将文件恢复到某个还原点                            | -                                     | `:Opencode restore snapshot_file`           | `require('opencode.api').diff_restore_snapshot_file(restore_point_id)` |\n| 将所有文件恢复到某个还原点                        | -                                     | `:Opencode restore snapshot_all`            | `require('opencode.api').diff_restore_snapshot_all(restore_point_id)`  |\n| 初始化\u002F更新 AGENTS.md 文件                            | -                                     | `:Opencode session agents_init`             | `require('opencode.api').initialize()`                                 |\n| 运行提示（继续会话）[运行选项](#run-opts)         | -                                     | `:Opencode run \u003Cprompt> \u003Copts>`             | `require('opencode.api').run(\"prompt\", opts)`                          |\n| 运行提示（新会话）[运行选项](#run-opts)              | -                                     | `:Opencode run new_session \u003Cprompt> \u003Copts>` | `require('opencode.api').run_new_session(\"prompt\", opts)`              |\n| 在 Opencode 运行时取消                            | `\u003CC-c>`                               | `:Opencode cancel`                          | `require('opencode.api').cancel()`                                     |\n| 设置模式为构建                                    | -                                     | `:Opencode agent build`                     | `require('opencode.api').agent_build()`                                |\n| 设置模式为计划                                    | -                                     | `:Opencode agent plan`                      | `require('opencode.api').agent_plan()`                                 |\n| 选择并切换模式\u002F代理                                | -                                     | `:Opencode agent select`                    | `require('opencode.api').select_agent()`                               |\n| 显示可用的 MCP 服务器列表                          | -                                     | `:Opencode mcp`                             | `require('opencode.api').mcp()`                                        |\n| 运行用户命令                                        | -                                     | `:Opencode run user_command`                | `require('opencode.api').run_user_command()`                           |\n| 分享当前会话并获取链接                            | -                                     | `:Opencode session share` \u002F `\u002Fshare`        | `require('opencode.api').share()`                                      |\n| 取消分享当前会话（禁用链接）                      | -                                     | `:Opencode session unshare` \u002F `\u002Funshare`    | `require('opencode.api').unshare()`                                    |\n| 整理当前会话（总结）                              | -                                     | `:Opencode session compact` \u002F `\u002Fcompact`    | `require('opencode.api').compact_session()`                            |\n| 撤销上一次 Opencode 操作                           | -                                     | `:Opencode undo` \u002F `\u002Fundo`                  | `require('opencode.api').undo()`                                       |\n| 重做上一次 Opencode 操作                           | -                                     | `:Opencode redo` \u002F `\u002Fredo`                  | `require('opencode.api').redo()`                                       |\n| 回应权限请求（仅接受一次）                        | `a`（窗口） \u002F `\u003Cleader>opa`（全局） | `:Opencode permission accept`               | `require('opencode.api').permission_accept()`                          |\n| 回应权限请求（全部接受）                          | `A`（窗口） \u002F `\u003Cleader>opA`（全局） | `:Opencode permission accept_all`           | `require('opencode.api').permission_accept_all()`                      |\n| 回应权限请求（拒绝）                              | `d`（窗口） \u002F `\u003Cleader>opd`（全局） | `:Opencode permission deny`                 | `require('opencode.api').permission_deny()`                            |\n| 插入提及（文件\u002F代理）                              | `@`                                   | -                                           | -                                                                      |\n| [选择文件并添加到上下文](#file-mentions)            | `~`                                   | -                                           | -                                                                      |\n| 跳转到下一个消息                                    | `]]`                                  | -                                           | -                                                                      |\n| 跳转到上一个消息                                    | `[[`                                  | -                                           | -                                                                      |\n| 跳转到历史记录中的上一个提示                      | `\u003Cup>`                                | -                                           | `require('opencode.api').prev_history()`                               |\n| 跳转到历史记录中的下一个提示                      | `\u003Cdown>`                              | -                                           | `require('opencode.api').next_history()`                               |\n| 切换输入\u002F输出面板                                   | `\u003Ctab>`                               | -                                           | -                                                                      |\n| 交换 Opencode 面板左右                              | `\u003Cleader>ox`                          | `:Opencode swap position`                   | `require('opencode.api').swap_position()`                              |\n| 切换工具输出（差异、命令输出等）                   | `\u003Cleader>ott`                         | `:Opencode toggle_tool_output`              | `require('opencode.api').toggle_tool_output()`                         |\n| 切换推理输出（思考步骤）                            | `\u003Cleader>otr`                         | `:Opencode toggle_reasoning_output`         | `require('opencode.api').toggle_reasoning_output()`                    |\n| 打开带有选择内容\u002F当前行上下文的快速聊天输入框     | `\u003Cleader>o\u002F`                          | `:Opencode quick_chat`                      | `require('opencode.api').quick_chat()`                                 |\n| 将视觉选区添加到上下文                              | `\u003Cleader>oy`                          | `:Opencode add_visual_selection`            | `require('opencode.api').add_visual_selection(opts?)`                  |\n| 将视觉选区内联插入到输入中                          | `\u003Cleader>oY`                          | `:Opencode add_visual_selection_inline`     | `require('opencode.api').add_visual_selection_inline(opts?)`           |\n\n**add_visual_selection 选项：**\n\n- `open_input`（布尔值，默认值：`true`）：添加选区后是否打开输入窗口。设置为 `false` 可以静默添加选区，而不改变焦点。\n\n静默添加的示例键位绑定：\n\n```lua\n['\u003Cleader>oy'] = { 'add_visual_selection', { open_input = false }, mode = {'v'} }\n```\n\n**add_visual_selection_inline** 会将可视选中的代码直接插入到输入缓冲区中，作为 Markdown 代码块，并在前面加上文件路径：\n\n````\n**`path\u002Fto\u002Ffile.lua`**\n\n```lua\n\u003Cselected text>\n````\n\n````\n\n光标会留在输入缓冲区的普通模式下，这样你就可以在插入的代码片段周围输入你的提示内容。\n\n\n\n### 运行选项\n\n通过命令或 API 运行提示时，可以传递额外的选项：\n\n- `agent=\u003Cagent_name>`：指定本次提示使用的代理（覆盖当前代理）\n- `model=\u003Cprovider\u002Fmodel_name>`：指定本次提示使用的模型（覆盖当前模型），例如 `model=github-copilot\u002Fgpt-4.1`\n- `context.\u003Ccontext_type>.enabled=\u003Ctrue|false>`：仅针对本次提示启用或禁用特定的上下文类型。可用的上下文类型包括：\n  - `current_file`\n  - `selection`\n  - `diagnostics.info`\n  - `diagnostics.warning`\n  - `diagnostics.error`\n  - `cursor_data`\n\n#### 示例\n\n使用 Plan 代理并在新会话中运行提示，同时禁用当前文件上下文：\n\n```vim\n:Opencode run new_session \"Please help me plan a new feature\" agent=plan context.current_file.enabled=false\n:Opencode run \"Fix the bug in the current file\" model=github-copilot\u002Fclaude-sonnet-4\n```\n\n## 👮 权限\n\nOpencode 可能会对潜在的破坏性操作（如编辑文件、还原文件、执行 Shell 命令或启用持久化工具访问权限）发出权限请求。权限请求会内联显示在输出中，必须先响应这些请求，代理才会执行相应操作。更多详情请参阅 [Opencode 权限文档](https:\u002F\u002Fopencode.ai\u002Fdocs\u002Fpermissions\u002F)。\n\n\u003Cdiv align=\"center\">\n  \u003Cimg src=\"https:\u002F\u002Fi.imgur.com\u002FLkZDta5.png\" alt=\"Opencode 权限请求\" width=\"90%\" \u002F>\n  \u003Cimg src=\"https:\u002F\u002Fi.imgur.com\u002F4mJH1Xg.png\" alt=\"Opencode 权限请求输入窗口\" width=\"90%\" \u002F>\n\u003C\u002Fdiv>\n\n### 回应权限请求\n\n- **通过对话框回应：** 将焦点移至 Opencode 窗口，使用 `j`\u002F`k` 或方向键移动，然后按 `\u003CCR>` 确认。数字快捷键 `1-3` 也可用于选择可见选项。\n- **命令：** 使用 `:Opencode permission accept`、`:Opencode permission accept_all` 或 `:Opencode permission deny`。你可以传递索引以针对特定的排队权限。\n- **API：** 提供了程序化响应方式：`require('opencode.api').permission_accept()`、`require('opencode.api').permission_accept_all()` 和 `require('opencode.api').permission_deny()`，分别对应“一次”、“始终”和“拒绝”三种响应。\n- **行为：** `accept once` 允许执行单次请求的操作，`accept all` 则授予当前会话中类似请求的持久权限，而 `deny` 则拒绝该请求。\n\n---\n\n## 📝 上下文\n\n以下编辑器上下文会自动捕获并包含在你的对话中。\n\n| 上下文类型    | 描述                                          |\n| --------------- | ---------------------------------------------------- |\n| 当前文件    | 进入 Opencode 之前聚焦文件的路径    |\n| 选中文本   | 当前在可视模式下选中的文本和行     |\n| 被提及的文件 | 通过 [提及](#file-mentions) 添加的文件信息   |\n| 诊断信息     | 当前文件中的诊断信息（如果有）           |\n| 共标位置 | 当前光标位置及文件中的行内容 |\n\n\u003Ca id=\"file-mentions\">\u003C\u002Fa>\n\n### 通过文件提及向上下文中添加更多文件\n\n你可以在与 Opencode 的对话中直接引用项目中的文件。这在需要询问或提供特定文件的上下文时非常有用。在输入窗口中输入 `@` 即可触发文件选择器。\n支持的选择器包括 [`fzf-lua`](https:\u002F\u002Fgithub.com\u002Fibhagwan\u002Ffzf-lua)、[`telescope`](https:\u002F\u002Fgithub.com\u002Fnvim-telescope\u002Ftelescope.nvim)、[`mini.pick`](https:\u002F\u002Fgithub.com\u002Fechasnovski\u002Fmini.nvim\u002Fblob\u002Fmain\u002Freadmes\u002Fmini-pick.md) 和 [`snacks`](https:\u002F\u002Fgithub.com\u002Ffolke\u002Fsnacks.nvim\u002Fblob\u002Fmain\u002Fdocs\u002Fpicker.md)。\n\n### 上下文栏\n\n你可以在输入窗口顶部的上下文栏中快速查看当前的上下文项：\n\n\u003Cdiv align=\"center\">\n  \u003Cimg src=\"https:\u002F\u002Fi.imgur.com\u002FvGgu6br.png\" alt=\"Opencode.nvim 上下文栏\" width=\"90%\" \u002F>\n\u003C\u002Fdiv>\n\n对于“当前文件”，颜色表示它是否会随下一个提示发送：\n\n- 正常高亮：文件待处理，将会被包含。\n- 暗淡\u002F灰色高亮：文件已发送且未发生变化，因此会被跳过（增量行为）。\n\n如果文件内容发生变化，则会再次变为待处理状态，并在下一个提示中发送。\n\n### 上下文项补全\n\n你可以通过在输入窗口中输入 `#` 快速引用可用的上下文项。这将显示一个包含所有可用上下文项的补全菜单：\n\n- **当前文件** - 编辑器中当前聚焦的文件\n- **选区** - 当前在可视模式下选中的文本\n- **诊断信息** - 当前文件中的 LSP 诊断信息\n- **光标数据** - 当前光标位置和所在行的内容\n- **[filename]** - 在对话中被提及过的文件\n- **代理** - 可切换的可用代理\n- **选区** - 之前在可视模式下做出的选区\n\n当前不可用的上下文项会在补全菜单中显示为禁用状态。\n\n你还会在菜单中看到代理和选区的列表；选择它们会从上下文中移除。\n\n\u003Cdiv align=\"center\">\n  \u003Cimg src=\"https:\u002F\u002Fi.imgur.com\u002FUqQKW33.png\" alt=\"Opencode.nvim 上下文项补全\" width=\"90%\" \u002F>\n\u003C\u002Fdiv>\n\n## 🔄 代理\n\nOpencode 提供两个内置代理，并支持自定义代理：\n\n### 内置代理\n\n- **Build**（默认）：功能齐全的开发代理，启用了所有可用于代码更改的工具\n- **Plan**：受限代理，用于规划和分析，不会对文件进行更改。适用于代码审查和理解代码，而无需修改\n\n### 切换代理\n\n在输入窗口中按下 `\u003CM-m>`（Alt+M）即可在会话中切换代理。\n\n### 自定义代理\n\n你可以通过 Opencode 配置文件创建自定义代理。每个代理可以有自己的：\n\n- 代理配置\n- 自定义提示\n- 启用或禁用的工具\n- 等等\n\n完整的配置选项请参阅 [Opencode 代理文档](https:\u002F\u002Fopencode.ai\u002Fdocs\u002Fagents\u002F)。\n\n## 🔌 自定义\u002F外部服务器配置\n\n默认情况下，opencode.nvim 会启动一个本地的 `opencode serve` 进程。你也可以通过配置 `server` 表来连接到外部或容器化的 Opencode 服务器。\n\n### 基本连接\n\n连接到现有服务器：\n\n```lua\nrequire('opencode').setup({\n  server = {\n    url = 'localhost',     -- 或 'http:\u002F\u002F192.168.1.100'\n    port = 8080,\n    timeout = 5,\n  },\n})\n```\n\n### 使用 Docker 自动启动\n\n使用 `spawn_command` 自动启动服务器，并使用 `kill_command` 停止它：\n\n```lua\nrequire('opencode').setup({\n  server = {\n    url = 'localhost',\n    port = 'auto',  -- 随机端口以实现项目隔离\n    -- 路径映射：将主机路径转换为容器内的路径\n    path_map = function(host_path)\n      local cwd = vim.fn.getcwd()\n      -- 将主机项目目录替换为容器挂载点\n      return host_path:gsub(vim.pesc(cwd), '\u002Fapp')\n    end,\n    -- 启动命令：使用 Docker 容器启动 opencode 服务器\n    spawn_command = function(port, url)\n      local dir_name = string.lower(vim.fn.fnamemodify(vim.fn.getcwd(), \":t\"))\n      local cwd = vim.fn.getcwd()\n      local container_name = string.format('opencode-%s', dir_name)\n\n      -- 检查容器是否已在运行\n      local check_cmd = string.format('docker ps --filter \"name=%s\" --format \"{{.Names}}\"', container_name)\n      local handle = io.popen(check_cmd)\n      local result = handle:read(\"*a\")\n      handle:close()\n\n      if result and result:match(container_name) then\n        print(string.format(\"[opencode.nvim] 容器 %s 已在运行，跳过启动\", container_name))\n        return true\n      end\n\n      -- 首先尝试停止任何同名的现有容器\n      os.execute(string.format('docker stop %s 2>\u002Fdev\u002Fnull || true', container_name))\n\n      local cmd = string.format([[\ndocker run -d --rm \\\n--name %s \\\n-p %d:4096 \\\n-v ~\u002F.local\u002Fstate\u002Fopencode:\u002Fhome\u002Fnode\u002F.local\u002Fstate\u002Fopencode \\\n-v ~\u002F.local\u002Fshare\u002Fopencode:\u002Fhome\u002Fnode\u002F.local\u002Fshare\u002Fopencode \\\n-v ~\u002F.config\u002Fopencode:\u002Fhome\u002Fnode\u002F.config\u002Fopencode \\\n-v \"%s\":\u002Fapp:rw \\\nopencode:latest opencode serve --port 4096 --hostname '0.0.0.0']],\n        container_name,\n        port,\n        cwd\n      )\n\n      print(string.format(\"[opencode.nvim] 正在启动 OpenCode 容器：%s 在端口 %d 上\", container_name, port))\n      return os.execute(cmd)\n    end,\n    -- 终止命令：当 auto_kill 触发时停止 Docker 容器\n    kill_command = function(port, url)\n      local dir_name = string.lower(vim.fn.fnamemodify(vim.fn.getcwd(), \":t\"))\n      local container_name = string.format('opencode-%s', dir_name)\n\n      print(string.format(\"[opencode.nvim] 正在停止 OpenCode 容器：%s\", container_name))\n      return os.execute(string.format('docker stop %s 2>\u002Fdev\u002Fnull', container_name))\n    end,\n    auto_kill = true,  -- 当最后一个 nvim 实例退出时启用自动清理\n  },\n})\n```\n\n### 容器\u002FWSL 的路径映射\n\n当服务器上的路径与主机不同（例如，容器中的 `\u002Fapp` 对应主机上的 `\u002Fhome\u002Fuser\u002Fproject`）时：\n\n```lua\nrequire('opencode').setup({\n  server = {\n    url = 'localhost',\n    port = 8080,\n    path_map = '\u002Fapp',  -- 简单的字符串替换\n  },\n})\n```\n\n### 使用 WSL 自动启动\n\n在 Windows 上使用 Neovim 时，在 WSL 内部运行 opencode 服务器：\n\n```lua\nrequire('opencode').setup({\n  server = {\n    url = 'localhost',\n    port = 'auto',  -- 随机端口以实现项目隔离\n\n    -- 在 WSL 内部启动 opencode 服务器\n    spawn_command = function(port, url)\n      local cmd = string.format(\n        'wsl.exe -e bash -c \"opencode serve --hostname 127.0.0.1 --port %d\"',\n        port\n      )\n      print(string.format('[opencode.nvim] 正在 WSL 中启动服务器，端口为 %d', port))\n      return vim.fn.jobstart(cmd, { detach = 1 })\n    end,\n\n    -- 终止 WSL 中的 opencode 进程\n    kill_command = function(port, url)\n      print(string.format('[opencode.nvim] 正在停止 WSL 中的服务器，端口为 %d', port))\n      vim.fn.jobstart('wsl.exe -e pkill -f \"opencode serve.*--port ' .. port .. '\"')\n    end,\n\n    -- Windows → WSL 路径转换（用于请求）\n    path_map = function(host_path)\n      if vim.fn.has('win32') == 1 then\n        -- 将 C:\\Users\\... 转换为 \u002Fmnt\u002Fc\u002FUsers\u002F...\n        local drive, rest = host_path:match('^([A-Za-z]):(.*)$')\n        if drive then\n          local wsl_path = '\u002Fmnt\u002F' .. drive:lower() .. rest:gsub('\\\\', '\u002F')\n          return wsl_path\n        end\n      end\n      return host_path\n    end,\n\n    -- WSL → Windows 路径转换（用于响应）\n    reverse_path_map = function(server_path)\n      -- 将 \u002Fmnt\u002Fc\u002FUsers\u002F... 转换为 C:\\Users\\...\n      local drive, rest = server_path:match('^\u002Fmnt\u002F([a-z])(.*)$')\n      if drive then\n        local windows_path = drive:upper() .. ':' .. rest:gsub('\u002F', '\\\\')\n        return windows_path\n      end\n      return server_path\n    end,\n\n    auto_kill = true,  -- 当最后一个 nvim 实例退出时终止服务器\n  },\n})\n```\n\n### 配置选项\n\n- `url`（字符串 | nil）：服务器主机名\u002FURL（例如，'localhost'、'http:\u002F\u002F192.168.1.100'）\n- `port`（数字 | 'auto' | nil）：端口号，'auto' 表示随机端口，或 nil 表示默认端口（4096）\n- `timeout`（数字）：健康检查超时时间，单位为秒（默认值：5）\n- `spawn_command`（函数 | nil）：可选的启动服务器函数：`function(port, url) ... end`\n- `kill_command`（函数 | nil）：可选的在 `auto_kill` 触发时停止服务器的函数：`function(port, url) ... end`\n- `auto_kill`（布尔值）：当最后一个 nvim 实例退出时终止已启动的服务器（默认值：true）\n- `path_map`（字符串 | 函数 | nil）：将主机路径转换为服务器路径（用于传出请求）\n- `reverse_path_map`（函数 | nil）：将服务器路径转换回主机路径（用于传入响应\u002F事件）\n\n### 多实例支持\n\n当使用 `port = 'auto'` 时，opencode.nvim：\n\n- 会跟踪哪些 nvim 实例正在使用每个端口\n- 只有在最后一个 nvim 实例退出时才会终止服务器（如果 `auto_kill = true`）。此行为仅适用于通过插件的 `spawn_command`\u002F`kill_command` 启动的服务器。\n- 无论 `auto_kill` 设置如何，本地启动的服务器都会在其成为唯一使用该端口的 nvim 实例时自动被终止。\n\n## 用户命令和斜杠命令\n\n您可以通过输入 `\u002F` 从输入窗口运行预定义的用户命令和内置的斜杠命令。这将打开一个命令选择器，您可以从中选择要执行的命令。命令的输出将包含在您的提示上下文中。\n\n**内置斜杠命令**包括：\n\n- `\u002Fshare` — 分享当前会话并获取链接\n- `\u002Funshare` — 取消分享当前会话\n- `\u002Fcompact` — 整理（总结）当前会话\n- `\u002Fundo` — 撤销上一次 opencode 操作\n- `\u002Fredo` — 重做上一次撤销的操作\n- `\u002Fagents_init` — 初始化\u002F更新 AGENTS.md\n- `\u002Fhelp` — 显示帮助\n- `\u002Fmcp` — 显示 MCP 服务器\n- `\u002Fmodels` — 切换提供商\u002F模型\n- `\u002Fvariant` — 切换模型变体\n- `\u002Fsessions` — 切换会话\n- `\u002Fchild-sessions` — 切换到子会话\n- `\u002Fagent` — 切换代理\u002F模式\n- ……等等\n\n**用户命令**是您自定义的脚本。它们从以下位置加载：\n\n- `.opencode\u002Fcommand\u002F`（项目特定）\n- `command\u002F`（全局，位于配置目录中）\n\n您也可以通过 `:Opencode command \u003Cname>` 按名称运行用户命令。\n\n\u003Cimg src=\"https:\u002F\u002Fi.imgur.com\u002FYQhhoPS.png\" alt=\"Opencode.nvim 上下文操作\" width=\"90%\" \u002F>\n\n更多详细信息请参阅 [用户命令文档](https:\u002F\u002Fopencode.ai\u002Fdocs\u002Fcommands\u002F)。\n\n## 📸 快照的上下文操作\n\n> [!WARNING] > _快照是一项实验性功能_\n> 在 opencode 中，开发团队有时可能会禁用它们或更改其行为。\n> 本仓库将尽快更新以匹配最新的 opencode 变更。\n\nOpencode.nvim 会在关键时刻（例如运行提示后或进行更改后）自动创建工作区的**快照**。这些快照类似于轻量级的 git 提交，使您能够随时查看、比较和恢复项目状态。\n\n快照的**上下文操作**可直接在输出窗口中使用。当对话中引用了快照时，您可以通过 UI 显示的快捷键触发对快照的操作。\n\n### 可用的快照操作\n\n- **Diff:** 查看当前状态与快照之间的差异。\n- **Revert file:** 将选定文件恢复到快照时的状态。\n- **Revert all files:** 将工作区中的所有文件恢复到快照时的状态。\n\n### 使用方法\n\n- 当输出中的消息引用了快照（查找 📸 **Created Snapshot** 或类似内容）时，将光标移动到该行，上方会显示一个小菜单。\n\n### 示例\n\n当您在输出中看到快照时：\n\n\u003Cimg src=\"https:\u002F\u002Fi.imgur.com\u002FeKOjhTN.png\" alt=\"Opencode.nvim 上下文操作\" width=\"90%\" \u002F>\n\n> **提示:** 恢复快照会将所有文件恢复到快照时的状态，因此请谨慎使用！\n\n## 🕛 上下文还原点\n\nOpencode.nvim 会在执行还原操作之前自动创建还原点。这样，如果需要的话，您可以撤销还原操作。\n\n您将在“Snapshot”行下方看到还原点，如下所示：\n\u003Cimg src=\"https:\u002F\u002Fi.imgur.com\u002FDKCOdt0.png\" alt=\"Opencode.nvim 还原点\" width=\"90%\" \u002F>\n\n### 可用的还原操作\n\n- **Restore file:** 将选定文件恢复到上次还原操作之前的状态。\n- **Restore all :** 将工作区中的所有文件恢复到还原操作之前的状态。\n\n## 高亮组\n\n该插件定义了多个高亮组，您可以根据自己的颜色方案进行自定义：\n\n- `OpencodeBorder`: Opencode 窗口的边框颜色（默认: #616161）\n- `OpencodeBackground`: Opencode 窗口的背景颜色（链接到 `Normal`）\n- `OpencodeSessionDescription`: 会话描述文本的颜色（链接到 `Comment`）\n- `OpencodeMention`: @file 引用的高亮（链接到 `Special`）\n- `OpencodeToolBorder`: 工具执行块的边框颜色（默认: #3b4261）\n- `OpencodeMessageRoleAssistant`: 助手消息的高亮（链接到 `Added`）\n- `OpencodeMessageRoleUser`: 用户消息的高亮（链接到 `Question`）\n- `OpencodeDiffAdd`: 差异中新增行的高亮（默认: #2B3328）\n- `OpencodeDiffDelete`: 差异中删除行的高亮（默认: #43242B）\n- `OpencodeAgentPlan`: 计划模式下 winbar 中的代理指示器（默认: 背景为 #61AFEF）\n- `OpencodeAgentBuild`: 构建模式下 winbar 中的代理指示器（默认: 背景为 #616161）\n- `OpencodeAgentCustom`: 自定义模式下 winbar 中的代理指示器（默认: 背景为 #3b4261）\n- `OpencodeContestualAction`: 输出窗口中上下文操作的高亮（默认: 背景为 #3b4261）\n- `OpencodeInputLegend`: 输入窗口图例的高亮（默认: 背景为 #CCCCCC）\n- `OpencodeHint`: 输入窗口中的提示信息以及输出窗口页脚中令牌信息的高亮（链接到 `Comment`）\n\n## 🛡️ 提示防护\n\n`prompt_guard` 配置选项允许您控制何时可以向 Opencode 发送提示。这在某些情况下对于防止意外或未经授权的 AI 交互非常有用。\n\n### 配置\n\n将 `prompt_guard` 设置为一个返回布尔值的函数：\n\n```lua\nrequire('opencode').setup({\n  prompt_guard = function()\n    -- 您的自定义逻辑在这里\n    -- 返回 true 允许，false 拒绝\n    return true\n  end,\n})\n```\n\n## 🪝 自定义用户钩子\n\n您可以在 Opencode 的特定事件发生时定义自定义函数：\n\n- `on_file_edited`: 在 Opencode 编辑文件后调用。\n- `on_session_loaded`: 在加载会话后调用。\n- `on_done_thinking`: 在 Opencode 完成思考（所有用户任务完成）时调用。\n- `on_permission_requested`: 在发出权限请求时调用。\n\n```lua\nrequire('opencode').setup({\n  hooks = {\n    on_file_edited = function(file_path, edit_type)\n      -- 文件编辑后的自定义逻辑\n      print(\"文件已编辑: \" .. file_path .. \" 类型: \" .. edit_type)\n    end,\n    on_session_loaded = function(session_name)\n      -- 加载会话后的自定义逻辑\n      print(\"会话已加载: \" .. session_name)\n    end,\n    on_done_thinking = function()\n      -- 思考完成后的自定义逻辑\n      print(\"思考已完成！\")\n    end,\n    on_permission_requested = function()\n      -- 请求权限时的自定义逻辑\n      print(\"请求权限！\")\n    end,\n  },\n})\n```\n\n### 行为\n\n- **发送提示之前**: 在向 AI 发送任何提示之前，会检查防护机制。如果被拒绝，将显示 ERROR 通知，并且不会发送提示。\n- **打开 UI 之前**: 在首次打开 Opencode 缓冲区时，会检查防护机制。如果被拒绝，将显示 WARN 通知，并且不会打开 UI。\n- **无参数**: 防护函数不接收任何参数。可以直接访问 vim 状态（例如 `vim.fn.getcwd()`、`vim.bo.filetype`）。\n- **错误处理**: 如果防护函数抛出错误或返回非布尔值，则会以适当的错误消息拒绝提示。\n\n## 📡 服务器发送事件 (SSE) 自动命令\n\nOpencode.nvim 会将所有服务器发送事件作为 Neovim 用户自动命令转发，使您能够对事件作出反应并自动化工作流。所有事件都以模式 `OpencodeEvent:\u003Cevent.type>` 触发。\n\n### 事件数据结构\n\n自动命令通过 `args.data.event` 接收事件数据：\n\n```lua\n{\n  type = \"event.name\",  -- Opencode 事件类型\n  properties = { ... }  -- 事件特定属性\n}\n```\n\n### 通配符模式\n\n您可以使用通配符匹配多种事件类型：\n\n- `OpencodeEvent:*` - 所有事件\n- `OpencodeEvent:session.*` - 所有会话事件\n- `OpencodeEvent:permission.*` - 所有权限事件\n\n### 示例\n\n```lua\nvim.api.nvim_create_autocmd('User', {\n  pattern = 'OpencodeEvent:permission.asked',\n  callback = function(args)\n    local event = args.data.event\n    vim.notify(vim.inspect(event))\n    -- 触发自定义逻辑\n  end,\n})\n```\n\n## 快速聊天\n\n快速聊天允许您基于当前行或选区的内容启动一个临时的 opencode 会话。\n此功能针对狭窄的代码编辑或插入进行了优化。如果请求较为复杂且需要更多上下文，建议使用完整的 opencode UI。\n\n由于上下文较为狭窄，生成的结果可能不够准确，有时编辑也可能失败。为了获得最佳效果，请尽量保持请求简洁明了。\n\n### 开始快速聊天\n\n在普通模式下按下 `\u003Cleader>o\u002F` 即可打开快速聊天输入窗口。\n\n\u003Cdiv align=\"center\">\n  \u003Cimg src=\"https:\u002F\u002Fi.imgur.com\u002F5JNlFZn.png\">\n\u003C\u002Fdiv>\n\u003Cdiv align=\"center\">\n  \u003Cimg src=\"https:\u002F\u002Fi.imgur.com\u002FScRgqfC.png\">\n\u003C\u002Fdiv>\n\n### 聊天提示示例\n\n- 转换为 Lua 数组\n- 添加 Lua 注释\n- 为我的更改编写一条符合规范的提交信息 #diff\n- 修复这些警告 #warn\n- 完成这个函数\n\n## 🔧 配置 Opencode\n\n如果你是 Opencode 的新用户：\n\n1. **什么是 Opencode？**\n   - Opencode 是一个专为终端打造的 AI 编码助手。\n   - 它提供强大的 AI 辅助功能，并支持扩展配置，例如选择不同的大语言模型和 MCP 服务器。\n\n2. **安装：**\n   - 请访问 [安装 Opencode](https:\u002F\u002Fopencode.ai\u002Fdocs\u002F#install) 页面，获取安装和配置说明。\n   - 确保安装完成后 `opencode` 命令可用。\n\n3. **配置：**\n   - 运行 `opencode auth login` 来设置你的 LLM 提供商。\n   - 在 `~\u002F.config\u002Fopencode\u002Fconfig.json` 或 `~\u002F.config\u002Fopencode\u002Fopencode.json` 文件中配置你偏好的 LLM 提供商及模型。\n\n## 🙏 致谢\n\n本插件是基于 [azorng](https:\u002F\u002Fgithub.com\u002Fazorng\u002F) 开发的原版 [goose.nvim](https:\u002F\u002Fgithub.com\u002Fazorng\u002Fgoose.nvim) 插件的分支。出于 Git 历史记录的考虑，我们直接复制了原始代码，而非简单地进行分支操作。","# opencode.nvim 快速上手指南\n\n`opencode.nvim` 是一个 Neovim 插件，作为终端 AI 编程助手 [opencode](https:\u002F\u002Fgithub.com\u002Fsst\u002Fopencode) 的前端界面。它能在编辑器内提供持久的聊天会话，并自动捕获当前文件、选区等上下文信息，让你无需离开 Neovim 即可与 AI 协作编写代码。\n\n> ⚠️ **注意**：该插件及底层 `opencode` 工具均处于早期开发阶段，可能存在 Bug 或不兼容变更，暂不建议用于生产环境。\n\n## 环境准备\n\n在开始之前，请确保满足以下前置条件：\n\n1.  **Neovim**: 版本建议为最新稳定版。\n2.  **Opencode CLI**: 必须安装 `opencode` 命令行工具，且版本需为 **v0.6.3** 或更高。\n    *   安装方法（参考官方）：\n        ```bash\n        # 使用 npm\n        npm install -g @opencode\u002Fcli\n        \n        # 或使用 cargo (如果可用)\n        cargo install opencode\n        ```\n    *   验证安装：运行 `opencode --version` 确认版本号。\n3.  **依赖插件**: 本插件依赖 `plenary.nvim`。为了获得最佳体验（如 Markdown 渲染、自动补全、文件选择），建议同时安装以下可选依赖（在配置中已体现）：\n    *   `nvim-lua\u002Fplenary.nvim` (必需)\n    *   `MeanderingProgrammer\u002Frender-markdown.nvim` (推荐，用于渲染 AI 回复的 Markdown)\n    *   `saghen\u002Fblink.cmp` 或 `hrsh7th\u002Fnvim-cmp` (可选，用于文件提及补全)\n    *   `folke\u002Fsnacks.nvim` 或 `nvim-telescope\u002Ftelescope.nvim` (可选，用于文件选择器)\n\n## 安装步骤\n\n推荐使用 **lazy.nvim** 进行包管理。将以下配置添加到你的 Neovim 配置文件（如 `lua\u002Fplugins\u002Fopencode.lua` 或 `init.lua`）中：\n\n```lua\n{\n  \"sudo-tee\u002Fopencode.nvim\",\n  config = function()\n    require(\"opencode\").setup({})\n  end,\n  dependencies = {\n    \"nvim-lua\u002Fplenary.nvim\",\n    {\n      \"MeanderingProgrammer\u002Frender-markdown.nvim\",\n      opts = {\n        anti_conceal = { enabled = false },\n        file_types = { 'markdown', 'opencode_output' },\n      },\n      ft = { 'markdown', 'Avante', 'copilot-chat', 'opencode_output' },\n    },\n    -- 可选：文件提及和命令补全 (二选一)\n    'saghen\u002Fblink.cmp',\n    -- 'hrsh7th\u002Fnvim-cmp',\n\n    -- 可选：文件选择器 (二选一)\n    'folke\u002Fsnacks.nvim',\n    -- 'nvim-telescope\u002Ftelescope.nvim',\n    -- 'ibhagwan\u002Ffzf-lua',\n    -- 'nvim_mini\u002Fmini.nvim',\n  },\n}\n```\n\n保存配置后，在 Neovim 中执行 `:Lazy sync` 或重启 Neovim 以完成安装。\n\n## 基本使用\n\n安装完成后，你可以通过默认的快捷键开始使用。默认前缀键为 `\u003Cleader>o`（即先按空格键，再按 o）。\n\n### 1. 启动聊天界面\n在正常模式下，按下 `\u003Cleader>og` 打开或关闭 opencode 主界面。\n*   界面分为**输入窗口**（底部）和**输出窗口**（上部）。\n*   首次运行时，插件会自动启动本地的 `opencode` 服务。\n\n### 2. 发送提示词 (Prompt)\n1.  按下 `\u003Cleader>oi` 聚焦到输入窗口并进入插入模式。\n2.  输入你的问题或指令（例如：“解释这个函数的作用”或“为这个模块添加单元测试”）。\n3.  **上下文引用**：\n    *   输入 `@` 可以提及文件或 Agent。\n    *   输入 `#` 管理上下文项（当前文件、选区、诊断信息等）。\n    *   在可视模式下选中代码后，使用 `\u003Cleader>oy` 可将选区内容自动加入上下文。\n4.  按下 `\u003CS-CR>` (Shift + Enter) 提交提示词。\n\n### 3. 查看与交互\n*   AI 的回复将显示在输出窗口中，支持 Markdown 渲染。\n*   使用 `]]` 和 `[[` 在多条消息间跳转。\n*   如果 AI 修改了代码，可以使用 `\u003Cleader>od` 打开差异对比视图，查看变更详情。\n*   按下 `\u003Cleader>ot` 可在编辑器代码区和 opencode 聊天区之间切换焦点。\n\n### 4. 快速聊天 (实验性功能)\n如果你只想针对当前行或选区快速提问：\n*   **正常模式**：光标停在某一行，按下 `\u003Cleader>o\u002F`，直接基于该行上下文发起对话。\n*   **可视模式**：选中一段代码，按下 `\u003Cleader>o\u002F`，基于选区内容发起对话。\n*   AI 给出的修改建议可以直接应用到文件中。\n\n### 5. 会话管理\n*   `\u003Cleader>os`: 选择并加载历史会话。\n*   `\u003Cleader>oI`: 开启一个新的会话。\n*   `\u003Cleader>oR`: 重命名当前会话。\n\n通过以上步骤，你即可在 Neovim 中享受类似 Cursor AI 的沉浸式编程体验。更多高级配置（如自定义模型、服务器地址等）可查阅插件的完整文档。","一位后端工程师正在 Neovim 中重构一个遗留的 Python 微服务模块，需要理解复杂的业务逻辑并安全地修改多处耦合代码。\n\n### 没有 opencode.nvim 时\n- **上下文割裂严重**：必须频繁切换窗口去浏览器复制代码片段到 AI 网页版，打断心流，无法利用当前打开的文件和选中区域作为自然上下文。\n- **协作记忆缺失**：每次提问都像“初次见面”，AI 记不住之前的讨论细节，工程师需反复粘贴错误日志和已尝试的方案，沟通效率极低。\n- **应用修改繁琐**：获得 AI 生成的代码后，需手动复制回编辑器并仔细对齐缩进，容易引入格式错误或遗漏关键行，增加回归测试成本。\n- **环境依赖冗余**：为了获得类似的 AI 辅助体验，不得不离开熟悉的终端环境去安装沉重的图形化 IDE（如 Cursor），破坏了轻量级开发工作流。\n\n### 使用 opencode.nvim 后\n- **原生上下文感知**：直接在 Neovim 内唤起聊天面板，opencode.nvim 自动捕获当前缓冲区内容和视觉选区，让 AI 基于实时代码状态给出精准建议。\n- **持久会话记忆**：插件将对话会话与 workspace 绑定，AI 能连续记住多轮重构思路，工程师只需专注迭代逻辑，无需重复背景信息。\n- **一键代码应用**：通过 Quick buffer chat 功能，AI 生成的修改可直接应用到文件中，支持自动处理缩进和语法检查，实现“对话即编码”。\n- **终端闭环开发**：无需离开终端或切换软件，在保持纯键盘操作习惯的同时，享受媲美图形化 IDE 的智能代理能力，极大提升极客开发体验。\n\nopencode.nvim 将强大的 AI 代理无缝融入 Neovim 工作流，让开发者在不中断心流的前提下，实现从“询问代码”到“直接演进代码”的效率飞跃。","https:\u002F\u002Foss.gittoolsai.com\u002Fimages\u002Fsudo-tee_opencode.nvim_c06078cd.gif","sudo-tee","Francis Belanger","https:\u002F\u002Foss.gittoolsai.com\u002Favatars\u002Fsudo-tee_35856705.jpg",null,"Ubisoft","https:\u002F\u002Fgithub.com\u002Fsudo-tee",[80,84,88],{"name":81,"color":82,"percentage":83},"Lua","#000080",99.2,{"name":85,"color":86,"percentage":87},"Shell","#89e051",0.8,{"name":89,"color":90,"percentage":91},"PowerShell","#012456",0,755,53,"2026-04-18T19:26:21","Apache-2.0",4,"未说明 (取决于 Neovim 和 Opencode CLI 的支持平台)","未说明",{"notes":100,"python":98,"dependencies":101},"该工具是 Neovim 插件，核心依赖是已安装且可用的 Opencode CLI (版本需 v0.6.3 或以上)。插件本身处于早期开发阶段，可能存在破坏性更新。具体的系统资源（CPU\u002FGPU\u002F内存）需求主要取决于后端 Opencode AI 代理所使用的模型，而非插件本身。",[102,103,104,105,106,107],"Neovim","Opencode CLI (v0.6.3+)","nvim-lua\u002Fplenary.nvim","MeanderingProgrammer\u002Frender-markdown.nvim (可选)","blink.cmp 或 nvim-cmp (可选)","snacks.nvim, telescope.nvim, fzf-lua 或 mini.nvim (可选)",[13,45],"2026-03-27T02:49:30.150509","2026-04-20T04:04:15.202948",[112,117,122,127,132,137,142],{"id":113,"question_zh":114,"answer_zh":115,"source_url":116},43126,"启动插件时出现 \"Vim:E484: Can't open file ...\u002Fconfig.json\" 错误怎么办？","该错误通常是因为配置文件不存在。请确保在首次运行前，您已手动创建了配置文件，或者插件能够自动初始化该文件。检查路径 `\u002FUsers\u002Fuser\u002F.config\u002Fopencode\u002Fconfig.json` 是否存在，如果不存在请手动创建或尝试更新到最新版本以获取自动修复。","https:\u002F\u002Fgithub.com\u002Fsudo-tee\u002Fopencode.nvim\u002Fissues\u002F1",{"id":118,"question_zh":119,"answer_zh":120,"source_url":121},43127,"调整 Neovim 窗口大小时出现了两个输入缓冲区（重复窗口）如何解决？","这是一个已知的布局渲染问题，维护者已在后续版本中通过更稳健的方法修复了重复窗口的问题（参考 PR #233）。如果您遇到此问题，请确保将插件更新到最新版本。如果问题依旧，尝试重启 Neovim 或重新加载会话。","https:\u002F\u002Fgithub.com\u002Fsudo-tee\u002Fopencode.nvim\u002Fissues\u002F144",{"id":123,"question_zh":124,"answer_zh":125,"source_url":126},43128,"调用子代理（sub-agents）时没有权限请求提示或任务进度更新怎么办？","这种行为可能与所选的 AI 模型有关。例如，Gemini 模型有时不会像 Claude 那样频繁请求权限，或者在某些版本（如 gemini-2.5-flash）中会出现停止思考的情况。建议尝试切换不同的模型（如使用 gemini 3 pro preview），并检查您的子代理配置是否正确。如果问题持续，请更新插件至最新版本。","https:\u002F\u002Fgithub.com\u002Fsudo-tee\u002Fopencode.nvim\u002Fissues\u002F158",{"id":128,"question_zh":129,"answer_zh":130,"source_url":131},43129,"如何限制输出缓冲区的最大行数以提高性能？","可以通过配置 `ui.output.rendering.max_lines` 选项来限制输出缓冲区的行数，防止长对话导致电脑变慢。注意：修改此配置后，可能需要关闭并重新打开 UI 窗口才能生效，因为当前版本可能不会自动重新渲染。","https:\u002F\u002Fgithub.com\u002Fsudo-tee\u002Fopencode.nvim\u002Fissues\u002F320",{"id":133,"question_zh":134,"answer_zh":135,"source_url":136},43130,"在输入框中输入 `\u002F` 有时无法触发命令而是触发文件选择怎么办？","这是补全逻辑的一个已知问题。维护者已经提交了重构补全逻辑的 PR 来修复此问题。请尝试更新插件到最新版本，或者切换到包含该修复的分支进行测试。如果问题仍然存在，可能是由于与其他补全插件冲突，建议检查相关配置。","https:\u002F\u002Fgithub.com\u002Fsudo-tee\u002Fopencode.nvim\u002Fissues\u002F130",{"id":138,"question_zh":139,"answer_zh":140,"source_url":141},43131,"关闭 Neovim 后 `opencode serve` 进程仍在后台运行导致内存占用过高怎么办？","这曾是一个导致子进程未正确终止的 Bug。维护者已添加了对子进程的检查并在退出时正确杀死它们。请确保您将插件更新到最新版本，该问题在新版本中应已解决。如果仍有残留进程，可以手动终止 `opencode serve` 进程。","https:\u002F\u002Fgithub.com\u002Fsudo-tee\u002Fopencode.nvim\u002Fissues\u002F251",{"id":143,"question_zh":144,"answer_zh":145,"source_url":146},43132,"使用 \"ask\" 工具时，长问题的用户体验不佳（通知消失后无法查看）如何解决？","针对长问题在通知中显示不全且易消失的问题，社区建议使用自定义 UI 组件（如 nui.nvim）来改进展示方式。维护者已关注此问题并正在探索更好的 UI 方案。目前建议尽量保持问题简洁，或关注后续版本中关于 \"ask\" 工具 UI 的更新。","https:\u002F\u002Fgithub.com\u002Fsudo-tee\u002Fopencode.nvim\u002Fissues\u002F197",[]]