当 opencli 命令失败时,自动修复损坏的 OpenCLI 适配器。加载此技能后,它会引导你收集跟踪工件、修补适配器、重试,并在验证修复后提交上游 GitHub 问题。适用于任何 AI 代理。
OpenCLI AutoFix — 自动适配器自我修复
当 opencli 命令因网站更改了 DOM、API 或响应模式而失败时,自动诊断、修复适配器并重试——不要仅仅报告错误。
安全边界
在开始任何修复之前,检查这些硬性停止条件:
AUTH_REQUIRED(退出码 77)— 停止。 不要修改代码。告诉用户在 Chrome 中登录该网站。BROWSER_CONNECT(退出码 69)— 停止。 不要修改代码。告诉用户运行opencli doctor。- 验证码 / 速率限制 — 停止。 不是适配器问题。
范围限制:
- 仅修改跟踪
summary.md前置元数据中adapterSourcePath指向的文件 — 这是权威的适配器位置(可能是仓库中的clis/<site>/或 npm 安装的~/.opencli/clis/<site>/) - 绝不修改
src/、extension/、tests/、package.json或tsconfig.json
重试预算: 每次失败最多 3 轮修复。如果 3 轮诊断 → 修复 → 重试仍未解决,则停止并报告已尝试的内容。
先决条件
opencli doctor # 验证扩展 + 守护进程连接
何时使用此技能
当 opencli <site> <command> 失败并出现可修复的错误时使用:
- SELECTOR — 未找到元素(DOM 已更改)
- EMPTY_RESULT — 未返回数据(API 响应已更改)
- API_ERROR / NETWORK — 端点已移动或损坏
- PAGE_CHANGED — 页面结构不再匹配
- COMMAND_EXEC — 适配器逻辑中的运行时错误
- TIMEOUT — 页面加载方式不同,适配器等待了错误的内容
进入修复前:“空” ≠ “损坏”
EMPTY_RESULT — 有时结构上有效的 SELECTOR 返回空 — 通常不是适配器错误。平台会在反爬启发式规则下主动降级结果,网站返回“未找到”响应并不意味着内容真的缺失。在提交修复轮次之前,先排除这种情况:
- 使用替代查询或入口点重试。 如果
opencli xiaohongshu search "X"返回 0 但opencli xiaohongshu search "X 攻略"返回 20,则适配器正常——平台对第一个查询进行了结果塑造。 - 在普通 Chrome 标签页中抽查。 如果数据在用户自己的浏览器中可见,但适配器返回空,问题通常是认证状态、速率限制或软封锁——而不是代码错误。解决方法是
opencli doctor/ 重新登录,而不是编辑源代码。 - 查找软 404。 像小红书/微博/抖音这样的网站会在项目被隐藏或删除时返回 HTTP 200 和空负载,而不是真正的 404。快照看起来结构正确。延迟 2-3 秒重试通常可以区分“暂时隐藏”和“实际消失”。
- 搜索返回“0 个结果”是一个答案。 如果适配器成功到达搜索端点,获得 HTTP 200,并且平台返回了
results: [],这是一个有效答案——向用户报告“此查询无匹配项”,而不是修补适配器。
仅当空/选择器缺失的结果在多次重试和替代入口点中可重现时,才进入步骤 1。否则你是在修补一个正常工作的适配器以追逐噪声,修补后的版本将破坏下一个正常工作的路径。
步骤 1:收集跟踪上下文
使用保留失败跟踪的方式运行失败的命令:
opencli <site> <command> [args...] --trace retain-on-failure 2>trace-error.yaml
失败时,stderr 包含正常的错误信封以及一个小的 trace 块:
ok: false
error:
code: SELECTOR
message: "Could not find element: .old-selector"
trace:
schemaVersion: 1
opencliVersion: "..."
traceId: "..."
dir: "/path/to/.opencli/profiles/default/traces/..."
summaryPath: "/path/to/.opencli/profiles/default/traces/.../summary.md"
receiptPath: "/path/to/.opencli/profiles/default/traces/.../receipt.json"
首先读取 summaryPath。它是面向 LLM 的入口点,包含前置元数据:
---
schemaVersion: 1
opencliVersion: "..."
traceId: "..."
status: failure
site: "example"
command: "example/search"
adapterSourcePath: "/path/to/clis/example/search.js"
errorCode: "SELECTOR"
errorMessage: "Could not find element: .old-selector"
---
工件目录包含:
summary.md # 从这里开始
receipt.json # 机器可读的跟踪收据
trace.jsonl # 完整的脱敏时间线
network.jsonl # 脱敏的网络事件
console.jsonl # 脱敏的控制台事件
state/ # 可用时的最终快照
screenshots/ # 可用时的最终截图
如果你将 stderr 重定向到文件,请读取该文件并复制 trace.summaryPath。
不要要求用户使用旧版诊断环境变量重新运行。跟踪是修复的证据路径。
步骤 2:分析失败原因
读取跟踪摘要和适配器源代码。对根本原因进行分类:
| 错误代码 | 可能原因 | 修复策略 |
|---|---|---|
| SELECTOR | DOM 重构,类/ID 重命名 | 探索当前 DOM → 查找新选择器 |
| EMPTY_RESULT | API 响应模式更改,或数据移动 | 检查网络 → 查找新响应路径 |
| API_ERROR | 端点 URL 更改,需要新参数 | 通过网络拦截发现新 API |
| AUTH_REQUIRED | 登录流程更改,cookie 过期 | 停止 — 告诉用户登录,不要修改代码 |
| TIMEOUT | 页面加载方式不同,旋转/懒加载 | 添加/更新等待条件 |
| PAGE_CHANGED | 重大重新设计 | 可能需要完全重写适配器 |
需要回答的关键问题:
- 适配器试图做什么?(读取
adapterSourcePath处的文件) - 失败时页面看起来如何?(读取
summary.md,必要时读取state/) - 发生了哪些网络请求?(读取
summary.md中的Failed Network,必要时读取network.jsonl) - 适配器期望的内容与页面提供的内容之间的差距是什么?
步骤 3:探索当前网站
使用 opencli browser 检查实时网站。绝不使用损坏的适配器——它会再次失败。
DOM 已更改(SELECTOR 错误)
# 打开页面并检查当前 DOM
opencli browser open https://example.com/target-page && opencli browser state
# 查找与适配器意图匹配的元素
# 将快照与适配器期望的内容进行比较
API 已更改(API_ERROR, EMPTY_RESULT)
# 使用网络拦截器打开页面,然后手动触发操作
opencli browser open https://example.com/target-page && opencli browser state
# 交互以触发 API 调用
opencli browser click <N> && opencli browser network
# 根据请求体应包含的字段缩小到关心的请求
opencli browser network --filter author,text,likes
# 检查特定 API 响应(key 是默认 JSON 输出中的 `key` 字段)
opencli browser network --detail <key>
步骤 4:修补适配器
从跟踪摘要前置元数据中读取 adapterSourcePath 处的适配器源文件,并进行有针对性的修复。此路径是权威的——它可能在仓库(clis/)或用户本地(~/.opencli/clis/)中。
使用 Read 工具读取 summary.md 前置元数据中的确切路径。
常见修复
选择器更新:
// 之前:page.evaluate('document.querySelector(".old-class")...')
// 之后:page.evaluate('document.querySelector(".new-class")...')
API 端点更改:
// 之前:const resp = await page.evaluate(`fetch('/api/v1/old-endpoint')...`)
// 之后:const resp = await page.evaluate(`fetch('/api/v2/new-endpoint')...`)
响应模式更改:
// 之前:const items = data.results
// 之后:const items = data.data.items // API 现在嵌套在 "data" 下
等待条件更新:
// 之前:await page.wait({ selector: '.loading-spinner', hidden: true })
// 之后:await page.wait({ selector: '[data-loaded="true"]' })
修补规则
- 做最小更改 — 只修复损坏的部分,不要重构
- 保持相同的输出结构 —
columns和返回格式必须保持兼容 - 优先使用 API 而非 DOM 抓取 — 如果在探索过程中发现 JSON API,则切换到它
- 仅使用
@jackwener/opencli/*导入 — 绝不添加第三方包导入 - 修补后测试 — 再次运行命令以验证
- 绝不放宽
verify/<cmd>.json的 fixtures 来掩盖失败。 失败的patterns/notEmpty/mustNotContain/mustBeTruthy规则意味着适配器的输出已损坏。收紧适配器以产生正确的值;不要放宽 fixture 以接受损坏的值。在修复期间编辑 fixture 的唯一合法理由是当网站本身改变了形态(例如 URL 格式迁移)——在这种情况下更新 fixture 并在~/.opencli/sites/<site>/notes.md中记录更改。否则编辑 fixture 就是在掩盖静默的正确性回归。
步骤 5:验证修复
# 正常运行命令
opencli <site> <command> [args...]
如果仍然失败,返回步骤 1 并收集新的跟踪。你有 3 轮修复的预算(跟踪 → 修复 → 重试)。如果修复后相同错误仍然存在,尝试不同的方法。3 轮后停止并报告已尝试的内容。
步骤 6:提交上游问题
如果重试通过,本地适配器已与上游偏离。提交 GitHub 问题以便修复流回 jackwener/OpenCLI。
不要为以下情况提交:
AUTH_REQUIRED、BROWSER_CONNECT、ARGUMENT、CONFIG— 环境/使用问题,不是适配器错误- 验证码或速率限制 — 上游无法修复
- 你实际上无法修复的失败(3 轮已耗尽)
仅在验证本地修复后提交 — 重试必须首先通过。
步骤:
- 从已有的跟踪摘要准备问题内容:
- 标题:
[autofix] <site>/<command>: <error_code>(例如[autofix] zhihu/hot: SELECTOR) - 正文(使用此模板):
- 标题:
## 摘要
OpenCLI autofix 在本地修复了此适配器,并且重试已通过。
## 适配器
- 站点:`<site>`
- 命令:`<command>`
- OpenCLI 版本:`<version from opencli --version>`
## 原始失败
- 错误代码:`<error_code>`
~~~
<error_message>
~~~
## 本地修复摘要
~~~
<1-2 句描述你更改了什么以及为什么>
~~~
_此问题由 OpenCLI autofix 在验证本地修复后提交。_
-
在提交前询问用户。 向他们展示草稿标题和正文。仅在他们确认后才继续。
-
如果用户批准且
gh auth status成功:
gh issue create --repo jackwener/OpenCLI \
--title "[autofix] <site>/<command>: <error_code>" \
--body "<上述正文>"
如果 gh 未安装或未认证,告诉用户并跳过——不要报错退出。
何时停止
硬性停止(不要修改代码):
- AUTH_REQUIRED / BROWSER_CONNECT — 环境问题,不是适配器错误
- 站点需要验证码 — 无法自动化处理
- 速率限制 / IP 被封 — 不是适配器问题
软性停止(尝试后报告):
- 3 轮修复已耗尽 — 停止,报告已尝试的内容和失败的原因
- 功能完全移除 — 数据不再存在
- 重大重新设计 — 需要通过
opencli-adapter-author技能完全重写适配器
在所有停止情况下,清晰地向用户传达情况,而不是进行无用的修补。
示例修复会话
1. 用户运行:opencli zhihu hot
→ 失败:SELECTOR "Could not find element: .HotList-item"
2. AI 运行:opencli zhihu hot --trace retain-on-failure 2>trace-error.yaml
→ 获取包含最终状态和失败操作证据的跟踪摘要
3. AI 读取摘要/状态:页面已加载但使用了 ".HotItem" 而不是 ".HotList-item"
4. AI 探索:opencli browser open https://www.zhihu.com/hot && opencli browser state
→ 确认新类名 ".HotItem" 带有子元素 ".HotItem-content"
5. AI 修补:编辑 `adapterSourcePath` 处的适配器 — 将 ".HotList-item" 替换为 ".HotItem"
6. AI 验证:opencli zhihu hot
→ 成功:返回热门话题
7. AI 准备上游问题草稿,展示给用户
8. 用户批准 → AI 运行:gh issue create --repo jackwener/OpenCLI --title "[autofix] zhihu/hot: SELECTOR" --body "..."






