/ skills / opencli-autofix / SKILL.md
SKILL.md
  1  ---
  2  name: opencli-autofix
  3  description: Automatically fix broken OpenCLI adapters when commands fail. Load this skill when an opencli command fails — it guides you through diagnosing the failure via OPENCLI_DIAGNOSTIC, patching the adapter, retrying, and filing an upstream GitHub issue after a verified fix. Works with any AI agent.
  4  allowed-tools: Bash(opencli:*), Bash(gh:*), Read, Edit, Write
  5  ---
  6  
  7  # OpenCLI AutoFix — Automatic Adapter Self-Repair
  8  
  9  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.
 10  
 11  ## Safety Boundaries
 12  
 13  **Before starting any repair, check these hard stops:**
 14  
 15  - **`AUTH_REQUIRED`** (exit code 77) — **STOP.** Do not modify code. Tell the user to log into the site in Chrome.
 16  - **`BROWSER_CONNECT`** (exit code 69) — **STOP.** Do not modify code. Tell the user to run `opencli doctor`.
 17  - **CAPTCHA / rate limiting** — **STOP.** Not an adapter issue.
 18  
 19  **Scope constraint:**
 20  - **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)
 21  - **Never modify** `src/`, `extension/`, `tests/`, `package.json`, or `tsconfig.json`
 22  
 23  **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.
 24  
 25  ## Prerequisites
 26  
 27  ```bash
 28  opencli doctor    # Verify extension + daemon connectivity
 29  ```
 30  
 31  ## When to Use This Skill
 32  
 33  Use when `opencli <site> <command>` fails with repairable errors:
 34  - **SELECTOR** — element not found (DOM changed)
 35  - **EMPTY_RESULT** — no data returned (API response changed)
 36  - **API_ERROR** / **NETWORK** — endpoint moved or broke
 37  - **PAGE_CHANGED** — page structure no longer matches
 38  - **COMMAND_EXEC** — runtime error in adapter logic
 39  - **TIMEOUT** — page loads differently, adapter waits for wrong thing
 40  
 41  ## Before Entering Repair: "Empty" ≠ "Broken"
 42  
 43  `EMPTY_RESULT` — and sometimes a structurally-valid `SELECTOR` that returns nothing — is often **not an adapter bug**. Platforms actively degrade results under anti-scrape heuristics, and a "not found" response from the site doesn't mean the content is actually missing. Rule this out **before** committing to a repair round:
 44  
 45  - **Retry with an alternative query or entry point.** If `opencli xiaohongshu search "X"` returns 0 but `opencli xiaohongshu search "X 攻略"` returns 20, the adapter is fine — the platform was shaping results for the first query.
 46  - **Spot-check in a normal Chrome tab.** If the data is visible in the user's own browser but the adapter comes back empty, the issue is usually authentication state, rate limiting, or a soft block — not a code bug. The fix is `opencli doctor` / re-login, not editing source.
 47  - **Look for soft 404s.** Sites like xiaohongshu / weibo / douyin return HTTP 200 with an empty payload instead of a real 404 when an item is hidden or deleted. The snapshot will look structurally correct. A retry 2-3 seconds later often distinguishes "temporarily hidden" from "actually gone".
 48  - **"0 results" from a search is an answer.** If the adapter successfully reached the search endpoint, got an HTTP 200, and the platform returned `results: []`, that is a valid answer — report it to the user as "no matches for this query" rather than patching the adapter.
 49  
 50  Only proceed to Step 1 if the empty/selector-missing result is **reproducible across retries and alternative entry points**. Otherwise you're patching a working adapter to chase noise, and the patched version will break the next working path.
 51  
 52  ## Step 1: Collect Diagnostic Context
 53  
 54  Run the failing command with diagnostic mode enabled:
 55  
 56  ```bash
 57  OPENCLI_DIAGNOSTIC=1 opencli <site> <command> [args...] 2>diagnostic.json
 58  ```
 59  
 60  This outputs a `RepairContext` JSON between `___OPENCLI_DIAGNOSTIC___` markers in stderr:
 61  
 62  ```json
 63  {
 64    "error": {
 65      "code": "SELECTOR",
 66      "message": "Could not find element: .old-selector",
 67      "hint": "The page UI may have changed."
 68    },
 69    "adapter": {
 70      "site": "example",
 71      "command": "example/search",
 72      "sourcePath": "/path/to/clis/example/search.js",
 73      "source": "// full adapter source code"
 74    },
 75    "page": {
 76      "url": "https://example.com/search",
 77      "snapshot": "// DOM snapshot with [N] indices",
 78      "networkRequests": [],
 79      "consoleErrors": []
 80    },
 81    "timestamp": "2025-01-01T00:00:00.000Z"
 82  }
 83  ```
 84  
 85  **Parse it:**
 86  ```bash
 87  # Extract JSON between markers from stderr output
 88  cat diagnostic.json | sed -n '/___OPENCLI_DIAGNOSTIC___/{n;p;}'
 89  ```
 90  
 91  ## Step 2: Analyze the Failure
 92  
 93  Read the diagnostic context and the adapter source. Classify the root cause:
 94  
 95  | Error Code | Likely Cause | Repair Strategy |
 96  |-----------|-------------|-----------------|
 97  | SELECTOR | DOM restructured, class/id renamed | Explore current DOM → find new selector |
 98  | EMPTY_RESULT | API response schema changed, or data moved | Check network → find new response path |
 99  | API_ERROR | Endpoint URL changed, new params required | Discover new API via network intercept |
100  | AUTH_REQUIRED | Login flow changed, cookies expired | **STOP** — tell user to log in, do not modify code |
101  | TIMEOUT | Page loads differently, spinner/lazy-load | Add/update wait conditions |
102  | PAGE_CHANGED | Major redesign | May need full adapter rewrite |
103  
104  **Key questions to answer:**
105  1. What is the adapter trying to do? (Read the `source` field)
106  2. What did the page look like when it failed? (Read the `snapshot` field)
107  3. What network requests happened? (Read `networkRequests`)
108  4. What's the gap between what the adapter expects and what the page provides?
109  
110  ## Step 3: Explore the Current Website
111  
112  Use `opencli browser` to inspect the live website. **Never use the broken adapter** — it will just fail again.
113  
114  ### DOM changed (SELECTOR errors)
115  
116  ```bash
117  # Open the page and inspect current DOM
118  opencli browser open https://example.com/target-page && opencli browser state
119  
120  # Look for elements that match the adapter's intent
121  # Compare the snapshot with what the adapter expects
122  ```
123  
124  ### API changed (API_ERROR, EMPTY_RESULT)
125  
126  ```bash
127  # Open page with network interceptor, then trigger the action manually
128  opencli browser open https://example.com/target-page && opencli browser state
129  
130  # Interact to trigger API calls
131  opencli browser click <N> && opencli browser network
132  
133  # Narrow to the request you care about by the fields its body should have
134  opencli browser network --filter author,text,likes
135  
136  # Inspect specific API response (key is the `key` field from the default JSON output)
137  opencli browser network --detail <key>
138  ```
139  
140  ## Step 4: Patch the Adapter
141  
142  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/`).
143  
144  ```bash
145  # Read the adapter (use the exact path from diagnostic)
146  cat <RepairContext.adapter.sourcePath>
147  ```
148  
149  ### Common Fixes
150  
151  **Selector update:**
152  ```typescript
153  // Before: page.evaluate('document.querySelector(".old-class")...')
154  // After:  page.evaluate('document.querySelector(".new-class")...')
155  ```
156  
157  **API endpoint change:**
158  ```typescript
159  // Before: const resp = await page.evaluate(`fetch('/api/v1/old-endpoint')...`)
160  // After:  const resp = await page.evaluate(`fetch('/api/v2/new-endpoint')...`)
161  ```
162  
163  **Response schema change:**
164  ```typescript
165  // Before: const items = data.results
166  // After:  const items = data.data.items  // API now nests under "data"
167  ```
168  
169  **Wait condition update:**
170  ```typescript
171  // Before: await page.wait({ selector: '.loading-spinner', hidden: true })
172  // After:  await page.wait({ selector: '[data-loaded="true"]' })
173  ```
174  
175  ### Rules for Patching
176  
177  1. **Make minimal changes** — fix only what's broken, don't refactor
178  2. **Keep the same output structure** — `columns` and return format must stay compatible
179  3. **Prefer API over DOM scraping** — if you discover a JSON API during exploration, switch to it
180  4. **Use `@jackwener/opencli/*` imports only** — never add third-party package imports
181  5. **Test after patching** — run the command again to verify
182  6. **Never relax `verify/<cmd>.json` fixtures to silence a failure.** A failing `patterns` / `notEmpty` / `mustNotContain` / `mustBeTruthy` rule means the adapter's output is broken. Tighten the adapter so it produces correct values; do not loosen the fixture to accept the broken values. The one legitimate reason to edit a fixture during repair is when the **site itself** changed shape (e.g. URL format migration) — in that case update the fixture and note the change in `~/.opencli/sites/<site>/notes.md`. Otherwise editing the fixture is covering up a silent correctness regression.
183  
184  ## Step 5: Verify the Fix
185  
186  ```bash
187  # Run the command normally (without diagnostic mode)
188  opencli <site> <command> [args...]
189  ```
190  
191  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.
192  
193  ## Step 6: File an Upstream Issue
194  
195  If the retry **passes**, the local adapter has drifted from upstream. File a GitHub issue so the fix flows back to `jackwener/OpenCLI`.
196  
197  **Do NOT file for:**
198  - `AUTH_REQUIRED`, `BROWSER_CONNECT`, `ARGUMENT`, `CONFIG` — environment/usage issues, not adapter bugs
199  - CAPTCHA or rate limiting — not fixable upstream
200  - Failures you couldn't actually fix (3 rounds exhausted)
201  
202  **Only file after a verified local fix** — the retry must pass first.
203  
204  **Procedure:**
205  
206  1. Prepare the issue content from the RepairContext you already have:
207     - **Title:** `[autofix] <site>/<command>: <error_code>` (e.g. `[autofix] zhihu/hot: SELECTOR`)
208     - **Body** (use this template):
209  
210  ```markdown
211  ## Summary
212  OpenCLI autofix repaired this adapter locally, and the retry passed.
213  
214  ## Adapter
215  - Site: `<site>`
216  - Command: `<command>`
217  - OpenCLI version: `<version from opencli --version>`
218  
219  ## Original failure
220  - Error code: `<error_code>`
221  
222  ~~~
223  <error_message>
224  ~~~
225  
226  ## Local fix summary
227  
228  ~~~
229  <1-2 sentence description of what you changed and why>
230  ~~~
231  
232  _Issue filed by OpenCLI autofix after a verified local repair._
233  ```
234  
235  2. **Ask the user before filing.** Show them the draft title and body. Only proceed if they confirm.
236  
237  3. If the user approves and `gh auth status` succeeds:
238  
239  ```bash
240  gh issue create --repo jackwener/OpenCLI \
241    --title "[autofix] <site>/<command>: <error_code>" \
242    --body "<the body above>"
243  ```
244  
245  If `gh` is not installed or not authenticated, tell the user and skip — do not error out.
246  
247  ## When to Stop
248  
249  **Hard stops (do not modify code):**
250  - **AUTH_REQUIRED / BROWSER_CONNECT** — environment issue, not adapter bug
251  - **Site requires CAPTCHA** — can't automate this
252  - **Rate limited / IP blocked** — not an adapter issue
253  
254  **Soft stops (report after attempting):**
255  - **3 repair rounds exhausted** — stop, report what was tried and what failed
256  - **Feature completely removed** — the data no longer exists
257  - **Major redesign** — needs full adapter rewrite via `opencli-adapter-author` skill
258  
259  In all stop cases, clearly communicate the situation to the user rather than making futile patches.
260  
261  ## Example Repair Session
262  
263  ```
264  1. User runs: opencli zhihu hot
265     → Fails: SELECTOR "Could not find element: .HotList-item"
266  
267  2. AI runs: OPENCLI_DIAGNOSTIC=1 opencli zhihu hot 2>diag.json
268     → Gets RepairContext with DOM snapshot showing page loaded
269  
270  3. AI reads diagnostic: snapshot shows the page loaded but uses ".HotItem" instead of ".HotList-item"
271  
272  4. AI explores: opencli browser open https://www.zhihu.com/hot && opencli browser state
273     → Confirms new class name ".HotItem" with child ".HotItem-content"
274  
275  5. AI patches: Edit adapter at RepairContext.adapter.sourcePath — replace ".HotList-item" with ".HotItem"
276  
277  6. AI verifies: opencli zhihu hot
278     → Success: returns hot topics
279  
280  7. AI prepares upstream issue draft, shows it to the user
281  
282  8. User approves → AI runs: gh issue create --repo jackwener/OpenCLI --title "[autofix] zhihu/hot: SELECTOR" --body "..."
283  ```