opencli-autofix

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

OpenCLI AutoFix — Automatic Adapter Self-Repair

OpenCLI AutoFix — 适配器自动自我修复

When an
opencli
command fails because a website changed its DOM, API, or response schema, automatically diagnose, fix the adapter, and retry — don't just report the error.
opencli
命令因网站修改了DOM、API或响应schema而执行失败时,自动诊断、修复适配器并重试,而不仅仅是上报错误。

Safety Boundaries

安全边界

Before starting any repair, check these hard stops:
  • AUTH_REQUIRED
    (exit code 77) — STOP. Do not modify code. Tell the user to log into the site in Chrome.
  • BROWSER_CONNECT
    (exit code 69) — STOP. Do not modify code. Tell the user to run
    opencli doctor
    .
  • CAPTCHA / rate limitingSTOP. Not an adapter issue.
Scope constraint:
  • Only modify the file at
    RepairContext.adapter.sourcePath
    — this is the authoritative adapter location (may be
    clis/<site>/
    in repo or
    ~/.opencli/clis/<site>/
    for npm installs)
  • Never modify
    src/
    ,
    extension/
    ,
    tests/
    ,
    package.json
    , or
    tsconfig.json
Retry budget: Max 3 repair rounds per failure. If 3 rounds of diagnose → fix → retry don't resolve it, stop and report what was tried.
在启动任何修复操作前,请先检查以下硬性停止条件:
  • AUTH_REQUIRED
    (退出码77)——立即停止,不要修改代码。告知用户需要在Chrome中登录对应网站。
  • BROWSER_CONNECT
    (退出码69)——立即停止,不要修改代码。告知用户运行
    opencli doctor
    命令。
  • 验证码 / 限流——立即停止,不属于适配器问题。
范围约束:
  • 仅可修改
    RepairContext.adapter.sourcePath
    指定的文件
    ——这是官方的适配器存储路径,可能是仓库中的
    clis/<site>/
    路径,或者npm安装对应的
    ~/.opencli/clis/<site>/
    路径
  • 绝对不要修改
    src/
    extension/
    tests/
    package.json
    或者
    tsconfig.json
    文件
重试预算: 每次失败最多支持3轮修复。如果经过3轮诊断→修复→重试的流程仍未解决问题,停止操作并报告所有已尝试的方案。

Prerequisites

前置条件

bash
opencli doctor    # Verify extension + daemon connectivity
bash
opencli doctor    # 验证扩展程序与守护进程的连通性

When to Use This Skill

何时使用此技能

Use when
opencli <site> <command>
fails with repairable errors:
  • SELECTOR — element not found (DOM changed)
  • EMPTY_RESULT — no data returned (API response changed)
  • API_ERROR / NETWORK — endpoint moved or broke
  • PAGE_CHANGED — page structure no longer matches
  • COMMAND_EXEC — runtime error in adapter logic
  • TIMEOUT — page loads differently, adapter waits for wrong thing
opencli <site> <command>
执行失败且属于可修复错误时使用:
  • SELECTOR — 未找到元素(DOM发生变更)
  • EMPTY_RESULT — 无数据返回(API响应发生变更)
  • API_ERROR / NETWORK — 接口路径变更或不可用
  • PAGE_CHANGED — 页面结构不再匹配预期
  • COMMAND_EXEC — 适配器逻辑运行时报错
  • TIMEOUT — 页面加载逻辑变更,适配器等待的对象错误

Step 1: Collect Diagnostic Context

步骤1:收集诊断上下文

Run the failing command with diagnostic mode enabled:
bash
OPENCLI_DIAGNOSTIC=1 opencli <site> <command> [args...] 2>diagnostic.json
This outputs a
RepairContext
JSON between
___OPENCLI_DIAGNOSTIC___
markers in stderr:
json
{
  "error": {
    "code": "SELECTOR",
    "message": "Could not find element: .old-selector",
    "hint": "The page UI may have changed."
  },
  "adapter": {
    "site": "example",
    "command": "example/search",
    "sourcePath": "/path/to/clis/example/search.ts",
    "source": "// full adapter source code"
  },
  "page": {
    "url": "https://example.com/search",
    "snapshot": "// DOM snapshot with [N] indices",
    "networkRequests": [],
    "consoleErrors": []
  },
  "timestamp": "2025-01-01T00:00:00.000Z"
}
Parse it:
bash
undefined
启用诊断模式运行失败的命令:
bash
OPENCLI_DIAGNOSTIC=1 opencli <site> <command> [args...] 2>diagnostic.json
该命令会将
RepairContext
格式的JSON输出到stderr的
___OPENCLI_DIAGNOSTIC___
标记之间:
json
{
  "error": {
    "code": "SELECTOR",
    "message": "Could not find element: .old-selector",
    "hint": "The page UI may have changed."
  },
  "adapter": {
    "site": "example",
    "command": "example/search",
    "sourcePath": "/path/to/clis/example/search.ts",
    "source": "// full adapter source code"
  },
  "page": {
    "url": "https://example.com/search",
    "snapshot": "// DOM snapshot with [N] indices",
    "networkRequests": [],
    "consoleErrors": []
  },
  "timestamp": "2025-01-01T00:00:00.000Z"
}
解析方法:
bash
undefined

Extract JSON between markers from stderr output

从stderr输出中提取标记之间的JSON内容

cat diagnostic.json | sed -n '/OPENCLI_DIAGNOSTIC/{n;p;}'
undefined
cat diagnostic.json | sed -n '/OPENCLI_DIAGNOSTIC/{n;p;}'
undefined

Step 2: Analyze the Failure

步骤2:分析故障原因

Read the diagnostic context and the adapter source. Classify the root cause:
Error CodeLikely CauseRepair Strategy
SELECTORDOM restructured, class/id renamedExplore current DOM → find new selector
EMPTY_RESULTAPI response schema changed, or data movedCheck network → find new response path
API_ERROREndpoint URL changed, new params requiredDiscover new API via network intercept
AUTH_REQUIREDLogin flow changed, cookies expiredSTOP — tell user to log in, do not modify code
TIMEOUTPage loads differently, spinner/lazy-loadAdd/update wait conditions
PAGE_CHANGEDMajor redesignMay need full adapter rewrite
Key questions to answer:
  1. What is the adapter trying to do? (Read the
    source
    field)
  2. What did the page look like when it failed? (Read the
    snapshot
    field)
  3. What network requests happened? (Read
    networkRequests
    )
  4. What's the gap between what the adapter expects and what the page provides?
读取诊断上下文和适配器源码,归类根因:
错误码可能原因修复策略
SELECTORDOM结构重构,类名/id重命名排查当前DOM → 找到新的选择器
EMPTY_RESULTAPI响应schema变更,或数据存储位置调整检查网络请求 → 找到新的响应字段路径
API_ERROR接口URL变更,需要新增请求参数通过网络拦截发现新的API接口
AUTH_REQUIRED登录流程变更,Cookie过期立即停止 —— 告知用户登录,不要修改代码
TIMEOUT页面加载逻辑变更,出现新的加载动画/懒加载逻辑新增/更新等待条件
PAGE_CHANGED网站重大改版可能需要完全重写适配器
需要明确的核心问题:
  1. 适配器的预期功能是什么?(读取
    source
    字段)
  2. 故障发生时页面的状态是什么?(读取
    snapshot
    字段)
  3. 触发了哪些网络请求?(读取
    networkRequests
  4. 适配器的预期和页面实际提供的内容之间的差异是什么?

Step 3: Explore the Current Website

步骤3:排查当前网站状态

Use
opencli operate
to inspect the live website. Never use the broken adapter — it will just fail again.
使用
opencli operate
检查线上网站的实际状态。不要使用已损坏的适配器——只会再次报错。

DOM changed (SELECTOR errors)

DOM变更(SELECTOR错误)

bash
undefined
bash
undefined

Open the page and inspect current DOM

打开页面并检查当前DOM

opencli operate open https://example.com/target-page && opencli operate state
opencli operate open https://example.com/target-page && opencli operate state

Look for elements that match the adapter's intent

查找符合适配器预期功能的元素

Compare the snapshot with what the adapter expects

对比快照和适配器的预期内容

undefined
undefined

API changed (API_ERROR, EMPTY_RESULT)

API变更(API_ERROR、EMPTY_RESULT错误)

bash
undefined
bash
undefined

Open page with network interceptor, then trigger the action manually

打开页面并启用网络拦截,然后手动触发对应操作

opencli operate open https://example.com/target-page && opencli operate state
opencli operate open https://example.com/target-page && opencli operate state

Interact to trigger API calls

交互触发API调用

opencli operate click <N> && opencli operate network
opencli operate click <N> && opencli operate network

Inspect specific API response

查看指定API的响应详情

opencli operate network --detail <index>
undefined
opencli operate network --detail <index>
undefined

Step 4: Patch the Adapter

步骤4:修补适配器

Read the adapter source file at the path from
RepairContext.adapter.sourcePath
and make targeted fixes. This path is authoritative — it may be in the repo (
clis/
) or user-local (
~/.opencli/clis/
).
bash
undefined
读取
RepairContext.adapter.sourcePath
路径下的适配器源码,进行针对性修复。该路径是官方路径,可能位于仓库(
clis/
)或用户本地路径(
~/.opencli/clis/
)。
bash
undefined

Read the adapter (use the exact path from diagnostic)

读取适配器代码(使用诊断信息中返回的精确路径)

cat <RepairContext.adapter.sourcePath>
undefined
cat <RepairContext.adapter.sourcePath>
undefined

Common Fixes

常见修复场景

Selector update:
typescript
// Before: page.evaluate('document.querySelector(".old-class")...')
// After:  page.evaluate('document.querySelector(".new-class")...')
API endpoint change:
typescript
// Before: const resp = await page.evaluate(`fetch('/api/v1/old-endpoint')...`)
// After:  const resp = await page.evaluate(`fetch('/api/v2/new-endpoint')...`)
Response schema change:
typescript
// Before: const items = data.results
// After:  const items = data.data.items  // API now nests under "data"
Wait condition update:
typescript
// Before: await page.waitForSelector('.loading-spinner', { hidden: true })
// After:  await page.waitForSelector('[data-loaded="true"]')
选择器更新:
typescript
// Before: page.evaluate('document.querySelector(".old-class")...')
// After:  page.evaluate('document.querySelector(".new-class")...')
API端点变更:
typescript
// Before: const resp = await page.evaluate(`fetch('/api/v1/old-endpoint')...`)
// After:  const resp = await page.evaluate(`fetch('/api/v2/new-endpoint')...`)
响应schema变更:
typescript
// Before: const items = data.results
// After:  const items = data.data.items  // API now nests under "data"
等待条件更新:
typescript
// Before: await page.waitForSelector('.loading-spinner', { hidden: true })
// After:  await page.waitForSelector('[data-loaded="true"]')

Rules for Patching

修补规则

  1. Make minimal changes — fix only what's broken, don't refactor
  2. Keep the same output structure
    columns
    and return format must stay compatible
  3. Prefer API over DOM scraping — if you discover a JSON API during exploration, switch to it
  4. Use
    @jackwener/opencli/*
    imports only
    — never add third-party package imports
  5. Test after patching — run the command again to verify
  1. 修改最小化——仅修复损坏的部分,不要重构代码
  2. 保持输出结构一致——
    columns
    和返回格式必须保持兼容
  3. 优先使用API而非DOM爬取——如果排查过程中发现了JSON API,切换为API调用方式
  4. 仅可使用
    @jackwener/opencli/*
    相关导入
    ——不要添加第三方包的导入
  5. 修补后必须测试——重新运行命令验证修复效果

Step 5: Verify the Fix

步骤5:验证修复效果

bash
undefined
bash
undefined

Run the command normally (without diagnostic mode)

正常运行命令(不启用诊断模式)

opencli <site> <command> [args...]

If it still fails, go back to Step 1 and collect fresh diagnostics. You have a budget of **3 repair rounds** (diagnose → fix → retry). If the same error persists after a fix, try a different approach. After 3 rounds, stop and report what was tried.
opencli <site> <command> [args...]

如果仍然失败,回到步骤1重新收集最新的诊断信息。你最多有**3轮修复预算**(诊断→修复→重试)。如果修复后仍出现相同错误,尝试其他方案。3轮后仍未解决,停止操作并报告所有已尝试的方案。

When to Stop

何时停止操作

Hard stops (do not modify code):
  • AUTH_REQUIRED / BROWSER_CONNECT — environment issue, not adapter bug
  • Site requires CAPTCHA — can't automate this
  • Rate limited / IP blocked — not an adapter issue
Soft stops (report after attempting):
  • 3 repair rounds exhausted — stop, report what was tried and what failed
  • Feature completely removed — the data no longer exists
  • Major redesign — needs full adapter rewrite via
    opencli-explorer
    skill
In all stop cases, clearly communicate the situation to the user rather than making futile patches.
硬性停止(不要修改代码):
  • AUTH_REQUIRED / BROWSER_CONNECT——环境问题,不是适配器BUG
  • 网站需要验证码——无法自动化处理
  • 被限流 / IP被封禁——不属于适配器问题
软性停止(尝试后报告):
  • 已用完3轮修复预算——停止操作,报告已尝试的方案和失败原因
  • 功能已完全下线——所需数据已不存在
  • 网站重大改版——需要通过
    opencli-explorer
    技能完全重写适配器
在所有需要停止的场景中,清晰告知用户当前情况,不要做无意义的修补。

Example Repair Session

修复会话示例

1. User runs: opencli zhihu hot
   → Fails: SELECTOR "Could not find element: .HotList-item"

2. AI runs: OPENCLI_DIAGNOSTIC=1 opencli zhihu hot 2>diag.json
   → Gets RepairContext with DOM snapshot showing page loaded

3. AI reads diagnostic: snapshot shows the page loaded but uses ".HotItem" instead of ".HotList-item"

4. AI explores: opencli operate open https://www.zhihu.com/hot && opencli operate state
   → Confirms new class name ".HotItem" with child ".HotItem-content"

5. AI patches: Edit adapter at RepairContext.adapter.sourcePath — replace ".HotList-item" with ".HotItem"

6. AI verifies: opencli zhihu hot
   → Success: returns hot topics
1. User runs: opencli zhihu hot
   → Fails: SELECTOR "Could not find element: .HotList-item"

2. AI runs: OPENCLI_DIAGNOSTIC=1 opencli zhihu hot 2>diag.json
   → Gets RepairContext with DOM snapshot showing page loaded

3. AI reads diagnostic: snapshot shows the page loaded but uses ".HotItem" instead of ".HotList-item"

4. AI explores: opencli operate open https://www.zhihu.com/hot && opencli operate state
   → Confirms new class name ".HotItem" with child ".HotItem-content"

5. AI patches: Edit adapter at RepairContext.adapter.sourcePath — replace ".HotList-item" with ".HotItem"

6. AI verifies: opencli zhihu hot
   → Success: returns hot topics