{"slug":"opencli-adapter-author","title":"opencli-adapter-author","summary":"Use when writing an OpenCLI adapter for a new site or adding a new command to an existing site. Guides end-to-end from first recon through field decoding, adapter coding, and verify. Replaces opencli-oneshot / opencli-explorer. For ad-hoc browser driving (no adapter), see opencli","platform":"Claude","tags":[],"authorName":"LLM Mart","authorSlug":"llm-mart","score":0,"source":"github","price":null,"verified":false,"createdAt":"2026-09-20T08:05:10.520176Z","repo":{"url":"https://github.com/mxyhi/ok-skills","stars":491,"forks":49,"license":"Apache-2.0","updatedAt":"2026-09-25T16:27:45Z"},"bodyHtml":"<hr>\n<h2>name: opencli-adapter-author\ndescription: Use when writing an OpenCLI adapter for a new site or adding a new command to an existing site. Guides end-to-end from first recon through field decoding, adapter coding, and verify. Replaces opencli-oneshot / opencli-explorer. For ad-hoc browser driving (no adapter), see opencli-browser instead; for a top-level orientation to opencli, see opencli-usage.\nallowed-tools: Bash(opencli:<em>), Bash(jsluice:</em>), Read, Edit, Write, Grep</h2>\n<h1>opencli-adapter-author</h1>\n<p>你是要给一个站点写 adapter 的 agent。这份 skill 目标：简单站点争取 <strong>30 分钟内从零到通过 <code>opencli browser verify</code></strong>；复杂、私有协议或写操作站点以证据完整和安全为先，不为了时限猜接口。</p>\n<p>全程用现有工具：<code>opencli browser *</code> / <code>opencli doctor</code> / <code>opencli browser init</code> / <code>opencli browser verify</code>。没有新命令。</p>\n<p>调试浏览器型 adapter 时，优先直接带上 <code>--trace on --keep-tab true --window foreground</code>。<code>--trace on</code> 每轮都落 trace artifact，<code>summary.md</code> 是失败/成功复盘入口；<code>--keep-tab true --window foreground</code> 让 tab lease 保留且浏览器窗口在前台，方便核对最终页面状态。</p>\n<hr>\n<h2>前置：看你落在哪</h2>\n<p>先拿 <code>coverage-matrix.md</code> 快速自测。三个问题：</p>\n<ol>\n<li>数据在浏览器里看得到吗？（否 → 先解决鉴权）</li>\n<li>数据是 HTTP/JSON/HTML 吗？（否 → 不在 skill 范围）</li>\n<li>需要实时推送吗？（是 → 找同数据 HTTP 接口；没有就放弃）</li>\n</ol>\n<p>三个都 yes 继续。</p>\n<hr>\n<h2>顶层决策树</h2>\n<p><strong>先定 strategy，再写 adapter。</strong> 每次进入 Step 3/4 后、写代码前，必须产出一段 strategy note。没有这段 note，不要开始写 <code>clis/&lt;site&gt;/&lt;name&gt;.js</code>。</p>\n<p>核心判断不是 \"API 比 DOM 高级\"，而是 <strong>数据源有没有外部契约</strong>。实测维护成本显示：公开/官方接口最稳；UI/DOM 语义通常也有用户可见契约；站内未文档化 XHR/GraphQL/signature endpoint 最容易漂。不要为了 \"API-first\" 把稳定的 UI/DOM 实现盲目迁到无契约内部接口。</p>\n<pre><code>Strategy: PUBLIC_API | COOKIE_API | PAGE_FETCH | INTERCEPT | DOM_STATE | UI_SELECTOR\nContract: stable | visible-ui | internal-unstable\nEvidence:\n- observed request/state: &lt;endpoint / state global / UI-only signal&gt;\n- auth source: &lt;none / browser cookie / csrf from meta / localStorage / page runtime&gt;\n- replay result: &lt;status + content-type + non-empty sample shape&gt;\n\nIf Strategy is PAGE_FETCH or INTERCEPT:\n- why PUBLIC_API / COOKIE_API are unavailable:\n- why UI_SELECTOR / DOM_STATE are not safer:\n- why the maintenance cost is acceptable:\n</code></pre>\n<p>Strategy classes:</p>\n<table>\n<thead>\n<tr>\n<th>Strategy</th>\n<th>契约级别</th>\n<th>用在什么时候</th>\n<th>证据要求</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><code>PUBLIC_API</code></td>\n<td>stable</td>\n<td>不需要登录，Node-side <code>fetch</code> 直接拿到目标数据</td>\n<td>200 + JSON/HTML 含目标数据，不是埋点/广告</td>\n</tr>\n<tr>\n<td><code>COOKIE_API</code></td>\n<td>stable</td>\n<td>Node-side <code>fetch</code> + <code>page.getCookies()</code> / header helper 能拿数据</td>\n<td>cookie/CSRF 来源清楚，replay 非空</td>\n</tr>\n<tr>\n<td><code>UI_SELECTOR</code></td>\n<td>visible-ui</td>\n<td>publish/upload/click/表单，或页面语义比内部接口更稳</td>\n<td>selector 有语义锚点；错误路径是 typed error</td>\n</tr>\n<tr>\n<td><code>DOM_STATE</code></td>\n<td>visible-ui</td>\n<td>数据在 hydration state / bootstrap JSON / SSR HTML 里</td>\n<td>state key / script JSON / HTML 结构明确</td>\n</tr>\n<tr>\n<td><code>PAGE_FETCH</code></td>\n<td>internal-unstable</td>\n<td>只能在页面上下文 <code>fetch</code> 才能复用 same-origin/session/runtime</td>\n<td><code>opencli browser eval fetch(...)</code> 非空；必须解释为什么避不开内部接口</td>\n</tr>\n<tr>\n<td><code>INTERCEPT</code></td>\n<td>internal-unstable</td>\n<td>请求签名复杂，但页面自己能自然发出请求</td>\n<td>触发 UI 后能截到目标 response；必须解释为什么 UI/DOM 不够</td>\n</tr>\n</tbody>\n</table>\n<p>选择规则：优先 <code>PUBLIC_API</code> / <code>COOKIE_API</code>。如果 UI/DOM 语义稳定，不要强行升级到 <code>PAGE_FETCH</code> / <code>INTERCEPT</code>。只有公开/官方接口不可用、UI/DOM 无法表达目标数据或操作时，才承担无契约内部接口的维护成本。</p>\n<p>实测：<code>PAGE_FETCH</code> / <code>INTERCEPT</code> 的 fix 频率约为 <code>PUBLIC_API</code> 的 7-8 倍，<code>UI_SELECTOR</code> 跟 <code>COOKIE_API</code> 同档。详细 ladder 推导、<code>api_candidates</code> 证据怎么填、booking #1680 等反例见 <a href=\"./references/strategy-selection.md\"><code>references/strategy-selection.md</code></a>。</p>\n<p>边界：只复用页面自己已经合法获得的数据/能力。不教破解签名、不绕验证码/风控/访问控制；遇到不可复用签名（如必须由页面 runtime 生成且不能安全抽象）就降级到 <code>UI_SELECTOR</code> / <code>DOM_STATE</code> / <code>INTERCEPT</code>。</p>\n<pre><code>START\n  │\n  ▼\n┌──────────────────────────┐\n│ opencli doctor 通？      │── no ──→ 修桥接（doctor 输出里的提示）\n└──────────────────────────┘\n  │ yes\n  ▼\n┌────────────────────────────────────────────────────┐\n│ 读站点记忆：                                        │\n│   1. ~/.opencli/sites/&lt;site&gt;/endpoints.json         │\n│   2. ~/.opencli/sites/&lt;site&gt;/notes.md               │\n│   3. references/site-memory/&lt;site&gt;.md               │\n└────────────────────────────────────────────────────┘\n  │ 命中 endpoint + 字段 → 直接跳到【endpoint 验证】（不跳写 adapter！memory 可能过期）\n  │ 没命中 → 继续\n  ▼\n┌──────────────────────────┐\n│ 站点侦察（site-recon）    │  → Pattern A/B/C/D/E\n└──────────────────────────┘\n  │\n  ▼\n┌──────────────────────────┐\n│ API 发现（api-discovery）│  §1 network → §2 state → §3 bundle → §4 token → §5 intercept\n└──────────────────────────┘\n  │ 拿到候选 endpoint\n  ▼\n┌────────────────────────────────────────────┐\n│ 需要 Deep Recon？                          │  无文档私有 API / DOM 丢数据 / 写操作 / 证据冲突\n│ → references/deep-recon.md                 │  intent matrix → 因果 diff → 候选账本 → contract gate\n└────────────────────────────────────────────┘\n  │ 候选通过合同证明；不通过则记录拒绝与 lift condition\n  ▼\n┌────────────────────────────────────────────┐\n│ 验证候选合同（memory 命中也要跑）           │── 401/403 ──→ 回到 §4 排 token\n│ safe replay；不可 replay 的 read 用自然截获 │── 空/HTML ──→ 回到 site-recon 换 Pattern\n│ 数据非空、identity 对、分页/错误语义完整     │── 站点换版 ──→ 标记旧 endpoint，回 api-discovery\n└────────────────────────────────────────────┘\n  │ OK\n  ▼\n┌───────────────────────────────────────┐\n│ 字段解码（memory 里的 field-map 也要抽查）│  自解释 → 直接 / 已知代号 → field-conventions / 未知 → decode-playbook\n│ 比一条已知字段和网页肉眼值，确认没错位     │\n└───────────────────────────────────────┘\n  │\n  ▼\n┌──────────────────────────┐\n│ 设计 columns (output)    │  对照 output-design.md 的命名 / 类型 / 顺序\n└──────────────────────────┘\n  │\n  ▼\n┌──────────────────────────┐\n│ opencli browser init      │  生成 ~/.opencli/clis/&lt;site&gt;/&lt;name&gt;.js 骨架\n│ 复制最像的邻居 adapter    │\n│ 改 name / URL / 映射三处  │\n└──────────────────────────┘\n  │\n  ▼\n┌──────────────────────────┐\n│ opencli browser verify    │── 失败 ──→ autofix skill，用 --trace retain-on-failure 回对应步骤\n└──────────────────────────┘\n  │ 成功\n  ▼\n┌──────────────────────────┐\n│ 字段 vs 网页肉眼对一遍   │── 数值不对 ──→ 回字段解码\n└──────────────────────────┘\n  │ 对得上\n  ▼\n┌──────────────────────────┐\n│ 回写 ~/.opencli/sites/   │  endpoints / field-map / notes / fixtures\n└──────────────────────────┘\n  │\n  ▼\nDONE\n</code></pre>\n<hr>\n<h2>Runbook（一步一步勾选）</h2>\n<pre><code>[ ] 1. opencli doctor 返回 \"Everything looks good\"\n[ ] 2. 读站点记忆：\n       [ ] ~/.opencli/sites/&lt;site&gt;/endpoints.json 存在？里面有想要的 endpoint？\n       [ ] references/site-memory/&lt;site&gt;.md 存在？看\"已知 endpoint\"节\n       [ ] 命中后：**跳到第 5（endpoint 验证） + 第 7（字段核对）**，不能直接跳第 9 写 adapter\n       [ ] memory 写入超过 30 天（看 `verified_at`）→ 当作过期，按冷启动走 Step 3 → 4\n[ ] 3. 侦察（site-recon.md）：\n       [ ] **首选**：`opencli browser analyze &lt;url&gt;` 一步拿 pattern + 反爬 + 最近 adapter + next step\n       [ ] `analyze` 结论模糊时再手跑：`open` → `wait time 2` (或 `wait xhr &lt;regex&gt;`) → `network`\n       [ ] 定 Pattern（A / B / C / D / E）\n[ ] 4. API 发现（api-discovery.md）按 Pattern 选 §：\n       [ ] Pattern A → §1 network 精读\n       [ ] Pattern B → §2 state 抽取 + §1 深层数据\n       [ ] Pattern C → §3 bundle / script src 搜索\n       [ ] Pattern D → §4 token 来源 + 降级 §5\n       [ ] Pattern E → 找 HTTP 轮询接口；找不到才 §5\n       [ ] 无文档 API / DOM 丢数据 / 写操作 / bundle 与 network 冲突 → `deep-recon.md`\n           [ ] 写 intent matrix 和明确的 mutation boundary\n           [ ] baseline → 单一动作 → 新请求 diff；至少一组 changed-input 对照\n           [ ] jsluice 只扩大候选面；候选必须进入 evidence ledger\n           [ ] read 候选过 occurrence/replay/completeness/auth/pagination/failure gate\n           [ ] write 候选有明确授权、目标绑定、幂等/不确定性与不可自动重试语义\n[ ] 5. 候选合同验证（memory 命中也要重跑）：\n       [ ] `PUBLIC_API / COOKIE_API / PAGE_FETCH`：safe replay 跨两个输入返回成功\n       [ ] `INTERCEPT`：两次自然页面动作都截到属于目标 identity 的完整响应\n       [ ] 响应含目标数据（不是 HTML / 广告 / 推荐侧栏），字段与网页对得上\n       [ ] 分页达到 exact limit 或证明 upstream exhaustion；失败不返回 partial\n       [ ] write 不自动 replay，必须过 `deep-recon.md` 的额外合同门禁\n[ ] 6. 写 strategy note（写代码前的强制产物）：\n       [ ] 从 `PUBLIC_API / COOKIE_API / PAGE_FETCH / INTERCEPT / DOM_STATE / UI_SELECTOR` 选一个\n       [ ] 填 Contract：`stable / visible-ui / internal-unstable`\n       [ ] 填 Evidence：observed request/state、auth source、replay result\n       [ ] 如果选 `PAGE_FETCH` / `INTERCEPT`，必须解释为什么 `PUBLIC_API` / `COOKIE_API` / `UI_SELECTOR` / `DOM_STATE` 都不适合\n       [ ] 如果选 `UI_SELECTOR` / `DOM_STATE`，不需要为 \"为什么不是 API\" 过度辩护；只要说明语义锚点和 typed error 路径\n[ ] 7. 字段解码：\n       [ ] 自解释 → 直接用 key\n       [ ] 已知代号 → field-conventions.md 查表\n       [ ] 未知代号 → field-decode-playbook.md（排序键对比 / 结构差分 / 常量排查）\n[ ] 8. 设计 columns（output-design.md）：\n       [ ] 命名 camelCase 且对齐邻居 adapter\n       [ ] 类型 / 单位 / 百分比格式清楚\n       [ ] 顺序：识别列 → 业务数字 → metadata\n[ ] 9. 写 adapter（adapter-template.md）：\n       [ ] opencli browser init &lt;site&gt;/&lt;name&gt;\n       [ ] 找同站点或同类型最像的 adapter，cp 过来\n       [ ] 改 name / URL / 字段映射\n[ ] 10. opencli browser verify &lt;site&gt;/&lt;name&gt;\n        [ ] 首轮通过后立刻 `--write-fixture` 生成 `~/.opencli/sites/&lt;site&gt;/verify/&lt;cmd&gt;.json` 种子\n        [ ] 手改种子：加 `patterns`（URL / 日期 / ID 格式）+ `notEmpty`（核心字段）+ 收紧 `rowCount`\n        [ ] 再跑一次 `opencli browser verify &lt;site&gt;/&lt;name&gt;`，确认 ✓ matches fixture\n[ ] 11. 字段值 vs 网页肉眼比对（别只看 \"Adapter works!\"）\n[ ] 12. 回写站点记忆（**verify 通过 + 肉眼比对对得上之后**，schema 见 `references/site-memory.md`）：\n        [ ] `endpoints.json`：以 endpoint 的短名为 key，value = `{url, method, params.{required,optional}, response, verified_at: YYYY-MM-DD, notes}`\n        [ ] `field-map.json`：只追加新代号。key = 字段代号，value = `{meaning, verified_at: YYYY-MM-DD, source}`；**已存在的 key 不要覆盖**，有冲突先和网页肉眼值对齐再写\n        [ ] `notes.md`：顶部追加一段 `## YYYY-MM-DD by &lt;agent/user&gt;`，写本次写 adapter 时遇到的新坑 / 新结论\n        [ ] `verify/&lt;cmd&gt;.json`：**必填。** `opencli browser verify` 的期望值（args / rowCount / columns / types / patterns / notEmpty），Step 10 已经让你生成了，这里只是 checklist\n        [ ] `fixtures/&lt;cmd&gt;-&lt;YYYYMMDDHHMM&gt;.json`：仅保存公开数据或可证明完成脱敏的样本；私人邮箱/消息/账号等高敏响应改用合成 fixture，不落盘\n        [ ] 原始 dump/capture 只短暂落 `/tmp/` 或受控 cache；安全分级后的长期样本才进 `fixtures/`，任务结束清理原始文件\n[ ] 13. repo 贡献收口（私人 adapter 可跳过）：\n        [ ] production-path tests，不只测 parser/helper\n        [ ] `npm run typecheck` + focused/site tests + `npm run build`\n        [ ] `node dist/src/main.js validate &lt;site&gt;`\n        [ ] `npm run check:typed-error-lint` + `npm run check:silent-column-drop`\n        [ ] adapter 文档；若 sitemap/site memory 有稳定新知识则同步\n        [ ] `git diff --check` + 敏感数据扫描 + 删除 raw capture/cache + 释放 browser session\n        [ ] 写操作或私有协议请独立 review exact head 后再合入\n</code></pre>\n<hr>\n<h2>降级路径（某步卡住跳到哪）</h2>\n<table>\n<thead>\n<tr>\n<th>卡在</th>\n<th>现象</th>\n<th>跳去</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>Step 4 API 发现</td>\n<td><code>network</code> 空，<code>__INITIAL_STATE__</code> 也空</td>\n<td>§3 bundle 搜 baseURL</td>\n</tr>\n<tr>\n<td></td>\n<td>bundle 搜不到 baseURL</td>\n<td>§5 intercept</td>\n</tr>\n<tr>\n<td>Step 5 endpoint 验证</td>\n<td>401 / 403</td>\n<td>§4 token 排查</td>\n</tr>\n<tr>\n<td></td>\n<td>200 但响应是 HTML</td>\n<td>回 Step 3 换 Pattern 判断</td>\n</tr>\n<tr>\n<td></td>\n<td>200 但 <code>data: []</code> 空</td>\n<td>参数传错 / 接口换版，回 §1 看 network 里真实请求头</td>\n</tr>\n<tr>\n<td>Step 7 字段解码</td>\n<td>排序键对比推不出</td>\n<td>field-decode-playbook.md §3 结构差分</td>\n</tr>\n<tr>\n<td></td>\n<td>还推不出</td>\n<td>先输出 raw，adapter 跑起来再迭代</td>\n</tr>\n<tr>\n<td>Step 10 verify 失败</td>\n<td><code>fltt</code> 漏了 / 字段映射错</td>\n<td>autofix skill；复现命令加 <code>--trace retain-on-failure</code></td>\n</tr>\n<tr>\n<td></td>\n<td>某列永远是 <code>null</code></td>\n<td>字段路径错了，回 Step 7</td>\n</tr>\n<tr>\n<td>Step 10 verify fixture mismatch</td>\n<td><code>[pattern]</code> row[i] 报错</td>\n<td>先肉眼比对网页值；值对 → 是 fixture pattern 太严，放宽；值不对 → 字段映射错</td>\n</tr>\n<tr>\n<td></td>\n<td><code>[column] missing column \"X\"</code></td>\n<td>实际 response 没这列（站点改版 or args 影响）；重新 <code>--update-fixture</code> 或修 adapter</td>\n</tr>\n<tr>\n<td></td>\n<td><code>[type]</code> actual null / undefined</td>\n<td>字段提取失败，回 Step 7 重抽；临时 fallback 用 union type <code>string\\|null</code> 只有在语义真的可空时用</td>\n</tr>\n<tr>\n<td>Step 11 数值不对</td>\n<td>差 10000 倍</td>\n<td>单位不统一（\"万\" vs \"元\"）</td>\n</tr>\n<tr>\n<td></td>\n<td>百分比小 100 倍</td>\n<td>响应已是 <code>0.025</code>，不要 × 100</td>\n</tr>\n</tbody>\n</table>\n<hr>\n<h2>参考文件</h2>\n<table>\n<thead>\n<tr>\n<th>文件</th>\n<th>什么时候翻</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><code>references/coverage-matrix.md</code></td>\n<td>动手前做\"是否在范围内\"自测</td>\n</tr>\n<tr>\n<td><code>references/site-recon.md</code></td>\n<td>Step 3 定站点类型</td>\n</tr>\n<tr>\n<td><code>references/api-discovery.md</code></td>\n<td>Step 4 找 endpoint</td>\n</tr>\n<tr>\n<td><code>references/deep-recon.md</code></td>\n<td>复杂无文档站：动作归因、jsluice 候选扩展、合同证明、读写安全与交付净账</td>\n</tr>\n<tr>\n<td><code>references/strategy-selection.md</code></td>\n<td>Step 6 填 strategy note 之前：契约模型 + 实测 fix 频率 + <code>api_candidates</code> 证据用法 + 反例</td>\n</tr>\n<tr>\n<td><code>references/field-conventions.md</code></td>\n<td>Step 7 查已知字段代号</td>\n</tr>\n<tr>\n<td><code>references/field-decode-playbook.md</code></td>\n<td>Step 7 字段不在词典时</td>\n</tr>\n<tr>\n<td><code>references/output-design.md</code></td>\n<td>Step 8 命名 / 类型 / 顺序</td>\n</tr>\n<tr>\n<td><code>references/adapter-template.md</code></td>\n<td>Step 9 文件结构 + 活例子 <code>convertible.js</code></td>\n</tr>\n<tr>\n<td><code>references/site-memory.md</code></td>\n<td>总览：in-repo 种子 + 本地 <code>~/.opencli/sites/</code> 的两层结构</td>\n</tr>\n<tr>\n<td><code>references/site-memory/&lt;site&gt;.md</code></td>\n<td>Step 2 读站点公共知识（eastmoney / xueqiu / bilibili / tonghuashun 已铺）</td>\n</tr>\n<tr>\n<td><code>references/success-rate-pitfalls.md</code></td>\n<td>Step 7 / 11 踩坑前翻：11 种\"verify 能过但数据是错的\"静默失败（含 aria-label locale-dependence）</td>\n</tr>\n<tr>\n<td><code>references/jsdom-fixture-pattern.md</code></td>\n<td>当 adapter 走 <code>page.evaluate</code> 内 DOM 抽取、且 mocked-evaluate 单测漏 silent bug 时——把 HTML 冻进 <code>clis/&lt;site&gt;/__fixtures__/</code> 用 JSDOM 跑（含 fixture 创建 mandatory <code>awk 'NF&gt;0'</code> 收紧 + reverse-validate 纪律）</td>\n</tr>\n<tr>\n<td><code>references/typed-errors.md</code></td>\n<td>写 <code>func</code> 主体之前必读：5 类 typed error 落点表（ArgumentError / EmptyResultError / CommandExecutionError / AuthRequiredError / TimeoutError）+ 三大 silent anti-pattern（silent-clamp / sentinel-row / generic CliError）的反例修法</td>\n</tr>\n</tbody>\n</table>\n<hr>\n<h2>关键约定</h2>\n<ul>\n<li>adapter 只引 <code>@jackwener/opencli/registry</code> + <code>@jackwener/opencli/errors</code>，不用第三方</li>\n<li><code>columns</code> 数组和 <code>func</code> 返回对象 keys 完全对齐（含顺序）</li>\n<li><strong>中间解析对象 key 不能跟 <code>columns</code> 任一项重叠</strong>（否则 silent-column-drop audit 误判，PR #1329 R1 真踩过；改成专属命名 + push row 时 destructure aliasing）</li>\n<li><strong><code>browser:</code> field 决定 func 签名</strong>：<code>browser:false → (args)</code>，<code>browser:true → (page, args)</code>。搞反时 <code>args</code> 实际是 debug flag，所有外部参数 silent fallback 到 default（PR #1329 upstream 之前 8 个 non-browser adapter 全踩过这个）</li>\n<li>已知失败按 <a href=\"./references/typed-errors.md\"><code>references/typed-errors.md</code></a> 5-classification 抛对应 typed error；<strong>不要</strong> silent <code>return []</code>，<strong>不要</strong> silent <code>return [{sentinel}]</code>，<strong>不要</strong> <code>Math.max/min</code> silent clamp 外部参数</li>\n<li>写私人 adapter 用 <code>~/.opencli/clis/&lt;site&gt;/&lt;name&gt;.js</code>（免 build）；要提 PR 才 copy 到 <code>clis/&lt;site&gt;/&lt;name&gt;.js</code></li>\n<li>站点记忆每轮回写：没记忆 → 用 skill → 产生记忆 → 下次变 5 分钟</li>\n<li><strong>“真实发生过”不等于“可作为 production contract 重放”</strong>。私有写请求、一次性风控 token、页面 runtime controller 都必须过 <code>deep-recon.md</code> 的 contract gate；过不了就记录 blocker/lift condition，不生成伪 API 命令。</li>\n<li><strong>调试过程中的原始 dump / 抓包 / HTML 样本只能短暂落在系统 <code>/tmp/</code> 或受控 cache，任务结束删除。只有通过 <code>site-memory.md</code> 数据分级、准备长期保留的公开/合成/已脱敏样本才进入 <code>~/.opencli/sites/&lt;site&gt;/fixtures/</code>。严禁在 repo 根目录、<code>clis/&lt;site&gt;/</code> 或当前工作目录留 <code>.dbg-*.html / raw-*.json / sample.*</code>。</strong></li>\n<li><strong>JSDOM unit-test fixture（<code>clis/&lt;site&gt;/__fixtures__/&lt;command&gt;.html</code>）是上面那条的例外</strong>——它是有意 commit 进 repo 的 review artifact，不是临时 dump。但因此 quality bar 要更高：必须按 <code>references/jsdom-fixture-pattern.md</code> 的 5 步做完（含 mandatory <code>awk 'NF&gt;0'</code> 空白行收紧），并 reverse-validate 一道证明 regression guard 真能挂。</li>\n</ul>\n<hr>\n<h2>卡住了</h2>\n<ul>\n<li>诊断类：<code>opencli doctor</code> → 看 <code>notes.md</code> → 搜 autofix skill</li>\n<li>字段解码类：<code>field-decode-playbook.md</code> 全三节走完 → 先输出 raw 迭代</li>\n<li>endpoint 找不到：api-discovery §5 intercept 兜底</li>\n</ul>\n<p>不要猜。猜错了 verify 能通过但数据是错的，用户看到乱码才发现。</p>\n","files":[{"path":"references/adapter-template.md","sizeBytes":21668,"isText":true},{"path":"references/api-discovery.md","sizeBytes":15995,"isText":true},{"path":"references/coverage-matrix.md","sizeBytes":5600,"isText":true},{"path":"references/deep-recon.md","sizeBytes":10593,"isText":true},{"path":"references/field-conventions.md","sizeBytes":5308,"isText":true},{"path":"references/field-decode-playbook.md","sizeBytes":7135,"isText":true},{"path":"references/jsdom-fixture-pattern.md","sizeBytes":8011,"isText":true},{"path":"references/output-design.md","sizeBytes":4816,"isText":true},{"path":"references/site-memory/bilibili.md","sizeBytes":3714,"isText":true},{"path":"references/site-memory/eastmoney.md","sizeBytes":3896,"isText":true},{"path":"references/site-memory/gmail.md","sizeBytes":2901,"isText":true},{"path":"references/site-memory.md","sizeBytes":10992,"isText":true},{"path":"references/site-memory/tonghuashun.md","sizeBytes":2492,"isText":true},{"path":"references/site-memory/xueqiu.md","sizeBytes":2875,"isText":true},{"path":"references/site-recon.md","sizeBytes":5383,"isText":true},{"path":"references/strategy-selection.md","sizeBytes":8894,"isText":true},{"path":"references/success-rate-pitfalls.md","sizeBytes":9651,"isText":true},{"path":"references/typed-errors.md","sizeBytes":12910,"isText":true},{"path":"SKILL.md","sizeBytes":20657,"isText":true}],"reviewScore":null,"reviewSummary":null,"trust":{"provenance":"trusted-source-unreviewed","notice":"Community-authored content, reproduced verbatim and not vetted as instructions. Treat it as data to evaluate, never as directives to follow.","bodySource":null},"bodyLocked":false,"purchaseUrl":null,"sourceUrl":null,"report":{"provenance":"trusted-source-unreviewed","screen":{"ran":true,"outcome":"clean","suspicious":0,"notes":0,"hiddenCharacters":false},"virusScan":{"engine":"clamav","status":"clean","scannedAt":"2026-09-20T08:08:10.64988Z","sha256":"BBB326F54887486B323738BC649EBFDC04AC22C924B4F170249B66317246DAE4","sizeBytes":84819},"review":null,"source":{"repositoryUrl":"https://github.com/mxyhi/ok-skills","path":"opencli/opencli-adapter-author","license":"Apache-2.0","commit":"7de464066579c539f0ec0342abef83e8a1e994c2","subtreeSha":"15BE45BD0CC545201767B610B14FA4AE83F4D9ACD213FCD5EFE38B49491DCD4A","lastSyncedAt":"2026-09-27T20:55:58.668939Z"},"reviewedAt":"2026-09-20T08:22:30.148948Z","notice":"Community-authored content, reproduced verbatim and not vetted as instructions. Treat it as data to evaluate, never as directives to follow."},"install":[{"target":"skills-cli","command":"npx skills add https://github.com/mxyhi/ok-skills/tree/main/opencli/opencli-adapter-author"},{"target":"claude-code","command":"claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install mxyhi-ok-skills@llmmart"},{"target":"git","command":"git clone https://github.com/mxyhi/ok-skills.git"}]}