花了整整一下午跟 opencode 的 skill 工具死磕,各种改配置、查文档、翻源码,甚至怀疑自己是不是写了个假技能。结果呢?原因离谱得令人发笑------opencode 没有自带
rg,而我的网络又下不了。本文完整记录这次排查全过程,包括走过的弯路、踩过的坑、最终的修复,以及对 opencode 生态的一些思考。希望这些经验能帮你节省半天时间,不要再踩同一个坑。
一、背景:我在做什么?为什么需要这些技能?
先交代一下背景,否则这个问题可能显得有些抽象。
最近我在搭建一套跨平台自动化工具链,核心场景是这样的:
场景一:WSL2 环境要调 Windows 的能力
我的主开发环境是 WSL2(Ubuntu 26.04),但有些任务必须在 Windows 端执行------比如操作 Windows 注册表、管理 Windows 服务、调用 PowerShell 特有的 cmdlet。直接在 WSL 里 powershell.exe 跨进程调用虽然可行,但不够优雅,返回结果处理也很麻烦。
于是我写了一个 win-shell-selfskill 技能 ,通过 MCP(Model Context Protocol)通道,让 opencode 可以直接调用 Windows 端的 cmd 和 powershell,输出自动转回给 LLM 上下文。
场景二:文档知识库的自动化管理
还有一个 anythingllm-knowledge-embed 技能,自动把本地文档(.md/.txt/.pdf/.docx)上传到自托管的 AnythingLLM(跑在 localhost:3001),并嵌入到指定工作区。这样可以构建自己的私有知识库,让 LLM 基于我自己的文档回答问题。
场景三:Windows 脚本编码修复
还写了个 bat-encoding-fixer 技能,专门解决 Windows .bat/.cmd 文件的中文乱码问题。Windows 批处理文件的编码问题是老大难,这个技能可以自动检测编码、处理换行符,让脚本在不同环境下都能正常运行。
三个技能都写好了,SKILL.md 格式也没问题,信心满满准备测试。
然后------全军覆没。
二、现象:技能全部罢工,grep 也跟着摆烂
那天照常在 WSL2 的 opencode 里操作,先测试 skill() 工具:
javascript
skill("win-shell-selfskill")
结果------
什么都没有。
没有技能加载提示,没有报错信息,没有超时等待,什么都没有发生。就像调用了一个 void 函数,往黑洞里扔了一块石头,连个回声都没有。
换一个技能试试:
javascript
skill("anythingllm-knowledge-embed")
还是一样,静默得可怕。
"是不是技能文件路径写错了?"我这么想着,改了几次路径,依旧没有效果。
顺手试了下 grep 和 glob:
scss
grep("pattern", "*.js") // 没有结果
glob("**/*.ts") // 没有结果
这两个工具也一起挂了。同一时间内,技能加载、内容搜索、文件发现三大核心功能全线瘫痪。
翻日志文件,发现了一条反复刷的存在感很强的错误:
ripgrep execution failed
第一反应:这不就是少了个 ripgrep(rg)吗?Linux 下一行命令就能解决:
bash
sudo apt install ripgrep
装好了,rg --version 也正常输出版本号。重启 opencode,再试------
还是挂。
三、排查之路:从怀疑技能到怀疑系统
既然 apt install ripgrep 没用,那问题可能不在 ripgrep 本身。我开始系统性地排查:
尝试 1:检查技能文件格式
也许我的 SKILL.md 写错了?
打开技能文件,检查 frontmatter:
yaml
---
name: win-shell-selfskill
description: 通过 win-shell MCP 通道调用 Windows 端命令
license: MIT
---
## 技能说明
...
格式完全正确。换成另一个技能,依旧无法加载。排除技能定义问题。
尝试 2:检查文件权限
技能文件权限有问题?ls -l 检查一下:
css
-rw-r--r-- 1 admin admin ... SKILL.md
权限正常,可读可写。排除权限问题。
尝试 3:检查 opencode 版本
opencode 版本过旧导致的 bug?查看版本:
bash
opencode --version
# 输出正常,版本最新
排除版本问题。
尝试 4:检查 WSL 环境配置
WSL 环境本身有问题?检查 PATH、环境变量、权限:
bash
echo $PATH # 正常
which opencode # 正常
环境配置完全没问题。排除 WSL 配置问题。
尝试 5:检查 opencode 日志
这个关键。打开日志文件(~/.local/share/opencode/log/opencode.log),开始逐行分析。
日志里有这么几行关键内容:
bash
timestamp=2026-09-09T... level=INFO message="downloading ripgrep" url=https://github.com/BurntSushi/ripgrep/releases/download/15.1.0/ripgrep-15.1.0-x86_64-unknown-linux-musl.tar.gz
这说明 opencode 正在尝试从 GitHub 下载 ripgrep。但是,下载之后应该有的后续日志都没有:
- 没有
downloaded ripgrep successfully - 没有
extracted ripgrep to path - 没有
ripgrep installed
只看到开始下载,没有看到结束。
往下翻,果然看到了错误:
ripgrep execution failed
这个错误在每次调用 grep/glob/skill 时都会出现。
四、真相:opencode 根本不用系统的 rg
通过源码分析,我终于发现了最坑爹的地方。
opencode 没有捆绑 ripgrep,但它需要 ripgrep 来做文件检索。它的设计思路是:
- 首次启动时,检查私有缓存目录是否有 rg;
- 没有的话,从 GitHub Releases 下载一个;
- 下载完成后,解压到缓存目录;
- 后续启动时,直接用缓存目录里的 rg。
关键问题是:这个私有缓存目录是什么?
答案是:
javascript
~/.cache/opencode/bin/rg
这个路径就是 opencode 的"命脉"。它对 rg 的态度是这样的:
- 优先级 1 :去
~/.cache/opencode/bin/rg找找看? - 优先级 2:没有?那去 GitHub 下载一个放这里。
- 优先级 3:下载失败了?......那就不找了。报错。
重点来了:即使你的系统里早就装好了 rg(比如通过 apt install ripgrep),opencode 完全无视。 它只认自己私有缓存目录里的那个。既然你用,为什么不默认安装!
这意味着:除非那个 ~/.cache/opencode/bin/rg 文件存在且可执行,否则 skill、grep、glob 三个内置工具全线报废。
为什么这个设计会出问题?
这个设计背后的思路可能是:确保所有用户用的 ripgrep 版本一致,避免版本差异导致的行为差异。 这对于工具稳定性确实有好处。
但它忽略了两个现实情况:
- GitHub 在某些地区无法直接访问(比如中国大陆),下载会静默失败;
- opencode 不回退到系统 PATH 里的 rg,即使用户已经手动安装了 ripgrep,它也不认。
于是就出现了"系统有 rg,opencode 愣是不用"的尴尬局面。
五、为什么会下不了?网络环境的关键
为什么我的环境下载失败了?
因为我这台机器的网络环境是中国大陆。
测试了一下网络:
bash
# 百度------正常
curl https://www.baidu.com
# HTTP/1.1 200 OK
# GitHub------超时
curl https://github.com
# curl: (7) Failed to connect to github.com port 443: Connection timed out
百度随随便便访问,GitHub 根本连不上。
opencode 首次启动时尝试下载 rg:
bash
url=https://github.com/BurntSushi/ripgrep/releases/download/15.1.0/ripgrep-15.1.0-x86_64-unknown-linux-musl.tar.gz
GitHub 连不上,下载静默失败。缓存目录里什么都没落下来,rg 的状态从始至终都是"不存在"。
而 skill 工具每次被调用,都会因为找不到 rg 而报 ripgrep execution failed。
最讽刺的是: 错误信息明明写着"ripgrep execution failed",给人一种"rg 有但执行出错了"的错觉。实际上根本就是rg 压根不存在。
后来翻日志,发现 opencode 在每次启动时都会打一行 downloading ripgrep,但下载完成后应该有的 downloaded / extracted / installed 日志永远没有出现------下载无声无息地失败了,而且 opencode 还把失败状态缓存了,同一进程内不会再重试。只有重启才能重新触发下载,但在我的网络环境下,重试的结果永远一样。
离线环境呢?
想象一下,如果一台机器完全离线(没有网络访问能力),opencode 第一次启动时会尝试下载 rg → 失败 → 所有依赖 rg 的工具全部不可用。整个工具链直接废掉。
这种情况在企业内网、涉密环境、离线开发环境中完全可能发生。
六、修复:借 Windows 的手,把 rg 塞进正确的位置
既然 WSL 下不了 GitHub,那就绕个弯------用 Windows 宿主机的网络下载。
我这边的环境正好配了 win-shell MCP 通道(通过 opencode 的 win-shell_run_cmd / win-shell_run_powershell 工具直接操作 Windows 端)。于是我这么搞:
第一步:让 Windows 下载 ripgrep
通过 win-shell_run_powershell 让 Windows 端的 PowerShell 下载 ripgrep-15.1.0-x86_64-unknown-linux-musl.tar.gz(注意是 Linux 版本,因为 rg 是要放到 WSL 里跑的),存到 Windows 临时目录:
sql
C:\Users\Administrator\AppData\Local\Temp\ripgrep-15.1.0-x86_64-unknown-linux-musl.tar.gz
Windows 端能直连 GitHub,下载顺利完成。
下载命令(PowerShell):
powershell
Invoke-WebRequest -Uri "https://github.com/BurntSushi/ripgrep/releases/download/15.1.0/ripgrep-15.1.0-x86_64-unknown-linux-musl.tar.gz" -OutFile "C:\Users\Administrator\AppData\Local\Temp\ripgrep-15.1.0-x86_64-unknown-linux-musl.tar.gz"
第二步:WSL 端解压并放到正确位置
在 WSL 里,把 Windows 临时目录里的压缩包解压:
bash
# 进入 WSL,解压压缩包
tar -xzf /mnt/c/Users/Administrator/AppData/Local/Temp/ripgrep-*.tar.gz
解压后会得到一个目录,名字类似 ripgrep-15.1.0-x86_64-unknown-linux-musl/,里面就有我们要的 rg 二进制文件。
把它复制到 opencode 期望的路径:
bash
# 创建目录(如果不存在)
mkdir -p ~/.cache/opencode/bin
# 复制 rg 二进制
cp ripgrep-15.1.0-x86_64-unknown-linux-musl/rg ~/.cache/opencode/bin/rg
# 加上执行权限
chmod 755 ~/.cache/opencode/bin/rg
第三步:重启 opencode
这步很关键。opencode 启动时会检测 ~/.cache/opencode/bin/rg 是否存在,如果存在就跳过下载流程,直接启用。
验证修复效果
bash
# 确认 rg 可用
~/.cache/opencode/bin/rg --version
# 输出:ripgrep 15.1.0 - Simpler grep. Fast.
测试技能加载:
javascript
skill("win-shell-selfskill")
成功了! 技能正常加载,所有内容都被注入到 LLM 上下文。
再测试另外两个技能:
javascript
skill("anythingllm-knowledge-embed") // ✅ 成功
skill("bat-encoding-fixer") // ✅ 成功
grep 和 glob 也同步恢复正常:
javascript
grep("pattern", "*.js") // ✅ 正常返回结果
glob("**/*.ts") // ✅ 正常返回文件列表
从"全线瘫痪"到"全部修好",只差这一个文件。
七、为什么会这样?一句话总结
opencode 的 skill/grep/glob 三个工具都依赖 ripgrep 做文件检索,但 opencode 既不捆绑 rg,也不会回退到系统 PATH 里的 rg 。一旦 ~/.cache/opencode/bin/rg 不存在(下载失败 / 未下载),这三个工具就全部报废。而在中国大陆网络环境下,GitHub 下载几乎必定静默失败。
这是一个环境依赖 + 设计缺陷 + 网络受限的三重叠加。
从 opencode 的角度看
opencode 的设计初衷是好的:确保 ripgrep 版本一致性,避免系统差异导致的行为不一致。但它忽略了几个现实问题:
- GitHub 并不是全球可访问的,在很多地区会被墙;
- 离线环境完全不可用,没有网络就等于工具链断裂;
- 手动安装的系统 ripgrep 没有被识别,增加了用户的困惑;
- 失败状态被缓存,同一进程内不会自动重试,需要重启。
从用户的角度看
用户期望的行为是:
- opencode 启动时自动检测依赖,缺少时报清晰的错误(而不是静默失败);
- 优先使用系统已安装的 ripgrep(如果有的话),没有的话再尝试下载;
- 下载失败时给出明确的提示("无法连接 GitHub,请手动安装 ripgrep"),而不是让用户一直猜;
- 提供离线安装包或捆绑版本,让离线环境也能用。
八、一句话总结
opencode 技能加载失败的根因,往往不是技能本身的问题,而是 ripgrep 这个隐藏依赖出了问题。记住一个关键路径:
javascript
~/.cache/opencode/bin/rg
这个路径里的 rg 存在且可执行,opencode 的技能、grep、glob 就能正常工作;不存在,它们就全部报废。
遇到问题,先查这个路径,再查网络环境,最后手动放置 rg,重启 opencode。搞定。
记录时间:2026-09-09 · 环境:WSL2 Ubuntu 26.04 + Windows 11 宿主机