Root SKILL.md was already removed in #703. Add per-skill install
commands to both EN and CN READMEs (without --full-depth since
root SKILL.md no longer blocks sub-skill discovery).
- Update adapter count from 50+/60+/66+ to 70+ across all docs (actual: 74 sites)
- Add missing operate commands (eval, network, init, verify) to README
- Add opencli-operate skill to Install AI Skills section in both READMEs
- Replace outdated "Playwright MCP Bridge" with "Browser Bridge" in doubao docs
* fix(twitter): add search input fallback for intermittent SPA navigation failures
The pushState + popstate approach works in most environments but fails
intermittently for some users (see #690), likely due to Twitter A/B
tests or timing race conditions where the pathname hasn't updated when
checked.
This commit adds a fallback strategy: when pushState fails after 2
retries, we type the query into the search input on /explore and press
Enter. This triggers Twitter's own form handler, performing SPA
navigation without a full page reload (keeping the fetch interceptor
alive).
Both strategies use selector-based waiting ([data-testid="primaryColumn"])
rather than fixed delays, with graceful fallthrough on timeout.
Fixes#690
* test(twitter): update search test for fallback evaluate call
The search input fallback adds one extra evaluate() call when pushState
fails. Update the mock chain and assertion count accordingly.
* fix(twitter): guard nativeSetter and add fallback success test
- Add optional chaining on getOwnPropertyDescriptor().set to handle
edge cases where Twitter's sandbox overrides the HTMLInputElement
prototype.
- Add test case covering the full fallback path: pushState fails twice,
search input fallback succeeds, results are returned correctly.
---------
Co-authored-by: jackwener <jakevingoo@gmail.com>
* fix(skills): add YAML frontmatter for discovery and improve descriptions
- opencli-explorer: add missing frontmatter with name, description, tags
- opencli-oneshot: add missing frontmatter with name, description, tags
- opencli-usage: rewrite description to start with "Use when..." and
include specific platform names for better keyword matching
Root cause of low trigger rate: explorer and oneshot had no frontmatter
at all, making them invisible to AI agent skill discovery. Usage had a
generic description without triggering conditions.
* fix(skills): add capability index, cross-skill links, and plugins entry
- Add "Quick Lookup by Capability" table so agents can find platforms
by what they need (search, trending, feed, AI chat, finance, etc.)
- Add plugins.md entry to main index (was completely hidden)
- Add "Related Skills" section linking to opencli-explorer and
opencli-oneshot for adapter development
- Compress platform listings for scannability
* fix(skills): inline compact command quick-reference table in SKILL.md
Add a self-contained command reference table directly in SKILL.md so
agents that can only read the main skill file still have full command
visibility. Each platform gets one row with all available commands.
Organized into Browser/Desktop/Public API/Management sections.
* fix(twitter): resolve article ID to tweet ID before GraphQL query
Article URLs (x.com/i/article/{articleId}) use a different ID than
tweet status URLs. The GraphQL TweetResultByRestId endpoint requires
the parent tweet ID, not the article ID.
Fix: navigate to the article page first, extract the associated tweet
ID from DOM links, then use that for the GraphQL query.
Fixes article fetching returning "Article not found" for all article URLs.
* fix: distinguish article URLs from status URLs, add explicit error handling
The previous commit routed all inputs through the article page, breaking
status URL and bare ID flows. Now only article URLs trigger the
article→tweet ID resolution. Status URLs and bare IDs keep the original
behavior. Also throws an explicit error if resolution fails instead of
silently falling back to the article ID.
---------
Co-authored-by: buruguo <buruguo@lambdafintech.com>
Co-authored-by: jackwener <jakevingoo@gmail.com>
* fix(xiaohongshu): clarify empty note shell hint
* fix(xiaohongshu): simplify empty shell detection to title+author check
The 7-field conjunction was overly strict — a note that rendered only
placeholder metrics but no title/author was still a valid empty shell.
Since title and author are always present on real notes, checking just
those two fields is a more reliable and simpler signal.
---------
Co-authored-by: jackwener <jakevingoo@gmail.com>
* feat: create skills/ directory structure per issue #605
- Create skills/opencli-usage/ with index and categorized command references
- SKILL.md: main index with installation and prerequisites
- browser.md: all browser-based commands (Bilibili, Twitter, Reddit, etc.)
- desktop.md: desktop adapter commands (Cursor, Codex, Notion, etc.)
- public-api.md: public API commands (HackerNews, V2EX, arXiv, etc.)
- plugins.md: management commands, AI workflow, output formats
- Create skills/opencli-explorer/ from CLI-EXPLORER.md
- Create skills/opencli-oneshot/ from CLI-ONESHOT.md
Addresses #605 - enables skill-based discovery and selective installation
* fix: complete browser.md with all missing adapters and fix incorrect entries
- Added 15 missing browser adapters: Reuters, SMZDM, Ctrip, Barchart,
Jike, Linux.do, WeRead, Jimeng, Pixiv, Web, Weixin, JD, LinkedIn,
Sina Finance, Bloomberg (browser)
- Fixed incomplete entries: Facebook (added 5 missing commands),
Coupang (corrected to match actual CLI), Yollomi (restored all 12
commands), Doubao Web (restored send/read commands), Grok (fixed format)
- Added missing public APIs: StackOverflow, Xiaoyuzhou, Wikipedia
- Updated SKILL.md index to list all supported platforms across all
categories including desktop adapters
* refactor: remove root SKILL.md, migrate Record Workflow to opencli-explorer
- Moved Record Workflow documentation (工作原理, 使用步骤, 页面类型表,
候选 YAML→TS 转换, 故障排查) into skills/opencli-explorer/SKILL.md
- Deleted root SKILL.md — all content now lives under skills/
* docs: add AI skills installation guide to README
Add npx skills add instructions for all 3 skills (opencli-usage,
opencli-explorer, opencli-oneshot) to both README.md and README.zh-CN.md.
* refactor: deduplicate transient error checks, cache VM contexts, expose tab ID
- Extract shared isTransientBrowserError() into browser/errors.ts, replacing
duplicated string-matching lists in daemon-client.ts and pipeline/executor.ts
- Cache compiled vm.Script objects in template.ts with LRU eviction (max 256),
avoiding per-invocation VM context creation in pipeline loops
- Add getActiveTabId() to IPage interface and Page class for tab state inspection
* refactor: extract BasePage to deduplicate DOM helpers across Page and CDPPage
Both Page (daemon-backed) and CDPPage (direct CDP) had ~200 lines of
identical DOM helper implementations (click, type, scroll, wait, snapshot,
interceptor, etc). Extract shared logic into abstract BasePage class.
Subclasses now only implement transport-specific methods.
* refactor: rename mcp.ts to bridge.ts and clean up Playwright MCP references
The file browser/mcp.ts contained BrowserBridge (daemon session manager),
not MCP functionality. Renamed to bridge.ts for clarity. Also removed all
stale "Playwright MCP" references from comments and variable names across
the codebase — Playwright was removed long ago.
* fix(notebooklm): remove bind-current workflow
* fix: relax notebook ID check in open.ts and clean up idle timeout test
- open.ts: only throw when page kind is not 'notebook'; log a warning
instead of throwing when the notebook ID doesn't match exactly
- background.test.ts: remove unused tabs[1] setup in idle timeout test
that was leftover from borrowed-session era
* build: rebuild extension dist after bind-current removal
* docs: add dingtalk and wecom CLI to external CLI hub
Add dingtalk-workspace-cli and wecom-cli as external CLI integrations
alongside lark-cli, gh, docker, etc.
* feat: add confirmPrompt() to TUI module
* feat: add Electron app registry with builtin + user-defined apps
* feat: add Electron app launcher with auto-detect and restart
* fix: launcher uses processName for path discovery, platform-guard tests
* feat: integrate Electron auto-launcher into execution pipeline
- CDPBridge.connect() accepts cdpEndpoint parameter instead of requiring env var
- getBrowserFactory() selects CDPBridge for registered Electron apps by site name
- executeCommand() calls resolveElectronEndpoint() for Electron apps, skips daemon check
- Remove requiredEnv/OPENCLI_CDP_ENDPOINT from all chatwise commands
- Remove chatwise-opencli.ps1 wrapper script and chatwise/shared.ts
- Update antigravity/serve.ts to use launcher instead of manual env var
- Replace hardcoded app names in scoreCDPTarget with registry lookup
- Fix Discord bundleId typo (com.iscord.app → com.discord.app)
* fix: resolve review issues — port collision and registry completeness
- Change ChatGPT CDP port from 9224 to 9236 (was colliding with Antigravity)
- scoreCDPTarget now uses full registry (builtin + user-defined) via getAllElectronApps()
- Use displayName (falling back to processName) for target score boosting
* fix: assign unique CDP ports — antigravity 9234, chatgpt 9236
Both were sharing port 9224, which could cause silent mis-connection.
- Add content field to display topic body text
- Add member field to show topic author
- Add created field to show topic creation timestamp
- Add node field to show topic category
- Add id field for consistency with hot/latest commands
This makes v2ex topic command return meaningful details that are
not available in hot/latest listings.
* feat(youtube): add --type shorts/video/channel, --upload, --sort filters
Uses YouTube's native sp= filter params. Shorts = type 9 (sp=EgIQCQ).
Also parses reelItemRenderer for Shorts results.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(youtube): add published time to search results
Shows when video was uploaded (e.g. "8h ago", "4d ago", "3mo ago").
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(youtube): prevent duplicate sp= params and remove redundant Shorts URL rewrite
- YouTube only supports one sp= parameter; using multiple causes
unpredictable behavior. Pick the most specific filter with priority:
type > upload > sort.
- Remove the post-processing Shorts URL rewrite — the reelItemRenderer
branch already generates /shorts/ URLs directly.
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: jackwener <jakevingoo@gmail.com>
* fix(bilibili): distinguish login-gated subtitles from empty results
* fix(test): use single toSatisfy assertion instead of double rejects.toThrow
Awaiting the same rejected promise twice is unreliable. Combine the
AuthRequiredError type check and message regex into one toSatisfy call.
---------
Co-authored-by: jackwener <jakevingoo@gmail.com>
* fix(v2ex): add id field to hot and latest API responses
- Add id field to hot.yaml and latest.yaml pipeline output
- Enables downstream commands like 'v2ex topic <id>' to work seamlessly
- Fixes issue where v2ex hot/latest JSON output lacked topic IDs
* enhance(v2ex): add node and url fields to hot/latest output
In addition to the id field, include node name (板块) and topic URL
for richer output. All fields come from the existing API response.
---------
Co-authored-by: jackwener <jakevingoo@gmail.com>
* fix(facebook): split search navigation from extraction
* refactor: use settleMs instead of waitUntil:none + wait:4
Replace `waitUntil: none` + separate `wait: 4` step with `settleMs: 4000`
on the navigate step. This is consistent with other Facebook adapters
(feed.yaml, memories.yaml, profile.yaml) and lets the navigate step
handle the timing in one place.
---------
Co-authored-by: jackwener <jakevingoo@gmail.com>
VitePress base is /docs/, so absolute links like /adapters/browser/twitter
resolve incorrectly. Changed all links to relative paths (./browser/...,
./desktop/...) so they work correctly on the docs site.
* fix(substack): update selectors for Substack DOM redesign (fixes#621)
Substack replaced <article> elements with role="article" divs and a
new SPA-based feed. The wait() selector 'article' no longer matches,
causing 'Selector not found: article' on feed and publication commands.
- loadSubstackFeed: use 'a[href*="/p/"]' (matches actual post links)
- loadSubstackArchive: use '[role="article"]' (Substack's new ARIA roles)
The evaluate() scraping logic inside both functions is unchanged since
it already uses 'a' href pattern matching, not article tags.
* review: align substack wait selectors with scraper
---------
Co-authored-by: jackwener <jakevingoo@gmail.com>
* fix(ctrip): update search adapter to live endpoint
* review: make ctrip search a public fetch adapter
---------
Co-authored-by: 泽加武 <zejiawu@zejiawudeMac-mini.local>
Co-authored-by: jackwener <jakevingoo@gmail.com>
* fix(xiaohongshu): support full URL/short link and fix video extraction
Two issues fixed:
1. URL handling: The download command only accepted bare note IDs and
constructed `explore/{noteId}` URLs, which lack the `xsec_token`
parameter now required by Xiaohongshu. This made all video/image
downloads fail with "No media found". Now accepts full URLs
(with xsec_token) and short links (xhslink.com) in addition to
bare note IDs.
2. Video extraction: XHS video player uses blob: URLs in DOM, which
cannot be downloaded via HTTP. Now extracts real video URLs from
`window.__INITIAL_STATE__` (SSR data) and inline script JSON
before falling back to DOM selectors, skipping blob: URLs.
Tested with a video note via short link — successfully downloaded
21.5 MB MP4.
* review: resolve xiaohongshu note id after redirects
---------
Co-authored-by: jackwener <jakevingoo@gmail.com>
* feat(xiaohongshu): add note command and nested reply support for comments
Add `xiaohongshu note` command to read full note content (title, author,
description, engagement metrics, tags) from public note pages.
Enhance `xiaohongshu comments` with `--with-replies` flag to extract
nested replies (楼中楼), including reply_to attribution and per-reply
like counts. Limit logic counts only top-level comments so replies
are included for free.
Extract shared `parseNoteId` into side-effect-free `note-helpers.ts`
to avoid cross-module command registration leakage.
Normalize non-numeric engagement placeholders ("赞"/"收藏"/"评论")
to "0" for zero-count notes.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(xiaohongshu): add note and comments --with-replies to adapter docs
Update xiaohongshu adapter documentation and README command table
to reflect the new note command and enhanced comments with nested
reply support.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(xiaohongshu): fix download example to show both note-id and url
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(xiaohongshu): expand nested reply threads before scraping
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: jackwener <jakevingoo@gmail.com>
- Add early login-wall detection before autoScroll() in search.ts
to prevent crash when XHS shows a login gate instead of results
- Add document.body null guard in autoScrollJs (dom-helpers.ts)
- Update search.test.ts: verify autoScroll is not called on login wall
- Add autoScrollJs null-body defense test in dom-helpers.test.ts