Search & read the login-walled Chinese internet for your AI agent — 微信公众号/小红书/抖音/B站 + Twitter/Reddit/HN/YouTube, one CLI, one JSON schema. Zero-config WeChat search.
Project description
omnireach
English · 中文
Your agent reads Twitter. It still can't read WeChat. omnireach fixes that.
omnireach searches and reads the login-walled Chinese internet — 微信公众号 · 小红书 · 抖音 · B站 · TikTok — plus Twitter, Reddit, HN, YouTube and more, through one uniform interface: omnireach search returns one normalized JSON schema across every source, omnireach fetch returns clean markdown for any URL. WeChat search works with zero config — no key, no login:
omnireach search --on wechat "Claude Code 技巧" # works 60 seconds after install
Installed as a Claude Code skill, so your agent just knows how to use it next session. No LLM inside, no per-call scraping fees — the heavy sources reuse your own browser session.
Install — just tell your agent
"Install omnireach"
Your agent runs one command. You copy nothing.
Manual fallback — paste into your terminal if you prefer:
curl -fsSL https://raw.githubusercontent.com/Daily-AC/omnireach/main/install.sh | sh
This installs the CLI and registers the Claude Code skill (auto-discovered next session). Zero-config sources — HackerNews, RSS, 微信 (Sogou path), B站 — work immediately. Other sources open up after a one-time setup step each.
What you get
1. Reach the unreachable. Twitter, Reddit, 小红书, 微信公众号, 抖音, B站, TikTok — login-walled vertical platforms that no agent web search reaches. omnireach reads them through your own logged-in browser session (via OpenCLI Chrome bridge), so the results your agent sees are the same results a human would see when signed in.
2. One uniform contract.
omnireach search → normalized metadata + URL, same shape across every source. omnireach fetch → clean markdown for any URL, with host-aware routing (mp.weixin.qq.com goes through your logged-in Chrome session to bypass CAPTCHA walls; all other hosts go through Crawl4AI → Jina Reader fallback). Your agent learns one interface, not 15 APIs.
3. Works even when WebSearch doesn't. On proxy / relay-station / Bedrock / Vertex-Claude-3.x setups where the built-in WebSearch server tool isn't available, omnireach gives search back — it runs entirely on the client side, bypassing both gate layers.
How is this different from Agent-Reach?
Agent-Reach (51k★) created this category and is excellent at what it does. omnireach makes different trade-offs — these are date-stamped facts, not marketing:
| omnireach | Agent-Reach (v1.5.0, as of 2026-07) | |
|---|---|---|
| 微信公众号 WeChat | ✅ zero-config search (Sogou path) + full-text fetch via your logged-in Chrome | ❌ removed 2026-06 (PR #347) after anti-bot breakage |
| 抖音 Douyin | ✅ search via your logged-in Chrome | ❌ removed 2026-06 (upstream tool archived) |
| TikTok | ✅ search | ❌ not supported |
| Output contract | one pydantic JSON schema across all 16 sources; auto-JSON when piped | no wrapper layer by design — each upstream tool's own format (YAML / plain text / subtitle files / raw JSON) |
search / fetch commands |
built-in: omnireach search, omnireach fetch <url> with host-aware routing |
no search/read commands — routes your agent to call upstream tools directly |
| Facebook · Instagram · LinkedIn · 雪球 · podcast transcription | ❌ | ✅ |
| Community | early — you found us before the crowd | 51k★, 30 contributors |
Pick Agent-Reach if you need Facebook/LinkedIn/transcription. Pick omnireach if you need the Chinese internet — WeChat above all — a machine-stable JSON contract, or a single fetch entrypoint. Both MIT; they compose fine in one agent.
Example
# Search a login-walled vertical platform
omnireach search --on xiaohongshu --json "Claude Code 使用技巧"
# Fetch a WeChat article — login-walled, via your session
omnireach fetch --json "https://mp.weixin.qq.com/s/<token>"
# Full pipeline: search → fetch all results
omnireach search --on wechat --json "claude 4.7" \
| jq -r '.results[].url' \
| xargs -I{} omnireach fetch --json {}
Commands
| Command | What it does |
|---|---|
omnireach search "<query>" |
Search (SERP: metadata + URL) |
omnireach search --on twitter,reddit "..." |
Target specific sources |
omnireach search --mode quick "..." |
Quick mode — HN only |
omnireach search --mode deep "..." |
Deep mode — all ready sources |
omnireach search --json "..." |
Explicit JSON output |
omnireach fetch <url> (v0.10, v0.10.1) |
URL → full markdown — mp.weixin.qq.com uses OpenCLI logged-in Chrome (v0.10.1+), other hosts use crwl → jina fallback |
omnireach fetch <url> --backend jina |
Force Jina Reader SaaS (zero local deps) |
omnireach fetch <url> --backend opencli |
Force OpenCLI wechat logged-in path (v0.10.1+) |
omnireach init |
Write default ~/.omnireach/preferences.toml |
omnireach sources |
List all sources + tier status |
omnireach setup <source> |
Guided setup for a 🟡 / 🔴 source |
omnireach doctor |
Health check (sources / fetch backends / wechat backends) |
Sources
| Source | Tier | Dependency | Notes |
|---|---|---|---|
| hackernews | ✅ ready | none | Direct Algolia, zero-config |
| youtube | ✅ ready | yt-dlp (pip install) |
omnireach setup youtube |
| github | ✅ ready | gh CLI + gh auth login |
omnireach setup github |
| rss | ✅ ready | built-in feedparser | query must be a URL |
| 🟡 one_step | rdt-cli + rdt login |
omnireach setup reddit |
|
| 🔴 heavy | OpenCLI + Chrome extension | v0.3 path | |
| xiaohongshu | 🔴 heavy | OpenCLI + Chrome extension | v0.3 path |
| tiktok | 🔴 heavy | OpenCLI + Chrome extension | TikTok international (v0.7) |
| douyin | 🔴 heavy | OpenCLI fork + Chrome extension | 抖音 (v0.7.2, Daily-AC/OpenCLI fork) |
| 💎 tavily | booster | env TAVILY_API_KEY |
paid (v0.4) |
| 💎 brave | booster | env BRAVE_API_KEY |
paid (v0.4) |
| 💎 perplexity | booster | env PERPLEXITY_API_KEY |
paid (v0.4) |
| 💎 exa | booster | env EXA_API_KEY |
paid web search (v0.5) |
| ✅ ready | none (optional EXA_API_KEY for enhancement) |
WeChat Official Accounts — search via Sogou free path; EXA_API_KEY optionally enables semantic enhancement; v0.10.1+ omnireach fetch <wechat-url> auto-routes through OpenCLI logged-in Chrome |
|
| bilibili | ✅ ready | none (optional EXA_API_KEY for enhancement) |
B站 — v0.9+ defaults to B站 official search API; EXA_API_KEY optionally enables semantic enhancement |
抖音 / douyin (v0.7.2): Uses
omnireach setup douyin, installs Daily-AC/OpenCLI fork (upstream PR jackwener/OpenCLI#1759 pending review; will switch back once merged). Requires signing in to www.douyin.com in Chrome.engagement.likeshas real data (DOM extraction);plays/comments/sharesare not exposed in search cards, normalized tonull.
WeChat fetch (v0.10.1):
omnireach fetch <mp.weixin.qq.com URL>uses the same Daily-AC/OpenCLI fork (weixin download --stdout, upstream PR jackwener/OpenCLI#1770 pending). Requires having opened any mp.weixin.qq.com article in Chrome (no explicit login step; browser cookies suffice). See How to get full text below.
Agent calling convention
When calling omnireach from an agent, always request JSON explicitly to prevent rich-table output from wrapping fields you need:
# Option 1: explicit flag per command
omnireach search --json "..."
omnireach fetch --json "<url>"
# Option 2: env var (recommended for agent harnesses)
export OMNIREACH_FORCE_JSON=1
The not isatty() auto-JSON added in v0.9.2 covers most cases, but some agent terminals (e.g. Antigravity) allocate a real PTY to subprocesses making isatty()=True. Explicit --json or the env var always works.
Full skill contract: .claude-plugin/skills/omnireach/SKILL.md
Who specifically needs this? (The WebSearch two-layer gate)
Claude Code's WebSearch is a server-side server tool (web_search_20250305). Whether it actually works depends on two independent gates:
Gate 1 — client-side (WebSearchTool.isEnabled() checks getAPIProvider()):
- Default
firstParty(noCLAUDE_CODE_USE_*env var set, including the case where onlyANTHROPIC_BASE_URLis changed) → tool registered - Explicit
CLAUDE_CODE_USE_BEDROCK=1→ tool off - Explicit
CLAUDE_CODE_USE_VERTEX=1+ Claude 4+ → registered; older Claude → off - Explicit
CLAUDE_CODE_USE_FOUNDRY=1→ registered
Gate 2 — upstream server tool implementation: After the client sends the tool schema, the upstream API must have specifically implemented the web_search_20250305 server tool (receive tool call → run search → return results to client):
- Real Anthropic API (api.anthropic.com): ✓ native
- Vertex / Foundry: ✓ (each backend implements it)
- Third-party providers that specifically support Claude Code (e.g. DeepSeek's Anthropic-compat endpoint): ✓ they implemented the server tool handling, routing to their own search backend
- OpenAI-compatible relay stations (cliproxy / anyrouter etc. that simply translate Claude API → OpenAI Chat Completions): ✗ don't recognize server tool semantics
- Self-hosted gateways / most proxies: ✗ generally not implemented
Gate 2 looks at "did the upstream specifically implement Claude Code server tool support" — not at "is this real Anthropic." Providers who specifically support Claude Code implement it; pure API translators don't. (Data point: DeepSeek is not real Anthropic but WebSearch works because they explicitly implemented it.)
Root causes differ by scenario:
- DeepSeek and other Claude-Code-specific third parties: both gates pass, WebSearch ✓ — omnireach value here is supplementing vertical sources (Twitter/小红书/微信), not patching search
- OpenAI-compatible relay station users: client registers the tool, upstream doesn't handle the server tool call → failure
- Explicit Bedrock users / Vertex Claude 3.x users: client-side
isEnabledis off before the request even leaves
Even with WebSearch working, it can't reach Twitter threads, Reddit comment sections, 小红书 posts, WeChat articles, 抖音, B站 technical videos — these login-walled vertical platforms are blind spots for every hosted web search. omnireach's three value layers:
- Patch for users whose client-side gate is off
- Patch for users whose upstream doesn't implement the server tool
- Supplement for users whose WebSearch works fine but can't reach vertical platforms
Naming & architecture (search / fetch / parse)
omnireach = omni (all) + reach (reach). Full "reach the whole web" semantics require three capability layers, all living as sibling binaries in this repo (analogous to cargo / rustc / rustfmt in the Rust monorepo — no sister repo):
| Layer | Implementation | Responsibility | Status |
|---|---|---|---|
| search | omnireach search subcommand |
Locate across the web — returns metadata + URL, does not fetch content | ✅ v0.7+ in use |
| fetch | omnireach fetch subcommand |
Given a URL, fetch full-text markdown — host-aware: mp.weixin.qq.com via OpenCLI logged-in Chrome, others via Crawl4AI → Jina Reader |
✅ v0.10+ (wechat path v0.10.1+) |
| parse | (not yet implemented, future addition to this repo) | Video/audio content parsing (subtitles/STT/frame-by-frame) | 🔜 not started |
v0.10+ has omnireach covering search + fetch (subcommand form). Video parsing still uses external tools (yt-dlp / whisper); parse binary will be added to this repo when there are real user requests (YAGNI — no sister repo).
This split mirrors Anthropic's own WebSearch + WebFetch split: each layer does one thing well, search isn't slowed by parsing tasks, and agent callers can compose freely.
Note: renaming
omnireachtoomnisearchwas considered, but v0.10 landingomnireach fetchresolved the question —omnireach search(reach = find) +omnireach fetch(reach = retrieve) are both natural sub-actions of "reach," so the umbrella name fits. No rename (decided 2026-05-27).
Upgrade
omnireach is in alpha with frequent releases. Check and upgrade:
omnireach check-update # compare against GitHub Releases
uv tool install --force git+https://github.com/Daily-AC/omnireach.git # pull latest
⚠️
uv tool upgrade omnireachwill not pull new commits (uv locks git-URL-installed tools to the commit at install time).--forcereinstall fetches the latest.
Platform support
| Platform | Status | Notes |
|---|---|---|
| macOS | ✅ Primary dev platform | All sources tested (HN/RSS/youtube/github/reddit/twitter/xhs/tiktok/douyin + 4 boosters + wechat/bilibili); omnireach fetch all three backends (crwl/jina/opencli) verified |
| Linux | 🟡 best-effort | Should work; setup flow doesn't auto-detect apt/pacman |
| WSL2 | 🟡 best-effort | Same as Linux |
| Windows (native PowerShell) | 🟡 experimental (v0.6.3+) | macOS assumptions removed: secrets.env no longer calls POSIX chmod; preferences edit falls back to notepad; setup github prompts winget install GitHub.cli; OpenCLI sources (twitter/xhs) theoretically cross-platform but untested. File an issue if you hit problems. |
Run omnireach doctor to print a platform / Python version line at the top — useful to include when filing issues.
💎 Paid boosters (v0.4)
omnireach is free by default. Configuring a paid API key improves result quality:
omnireach setup tavily # guided key entry + writes to ~/.omnireach/secrets.env
omnireach setup brave
omnireach setup perplexity
omnireach setup exa # added v0.5 (replaces old `web` source)
Keys are detected automatically when set. Results carry cost="paid" metadata; TTY output shows a 💎 prefix for auditability.
To disable: edit ~/.omnireach/preferences.toml and set [boosters] auto_enable = false.
⚙️ User preferences (v0.4)
~/.omnireach/preferences.toml can configure default sources, language, output format, and source_trust overrides.
omnireach preferences show # view current config
omnireach preferences edit # open in $EDITOR
omnireach preferences reset # reset (backs up original to .bak)
omnireach preferences path # print file location
How to get full text (v0.8)
omnireach is the search layer — the content field is uniformly capped at ≤ 500 characters + …. This validator runs in the contract layer (SearchResult.content pydantic field_validator) for all sources. This is intentional — full text belongs to the omnireach fetch layer (see Naming & architecture above).
For sources whose upstream returns full text (wechat / exa / tavily) or long threads (twitter), the complete raw payload is preserved in result.raw, so agents that need full text can retrieve it:
# Python (call CLI + parse JSON envelope)
import json
import subprocess
out = subprocess.run(
["omnireach", "search", "--json", "--on", "wechat", "claude 4.7"],
check=True, capture_output=True, text=True,
)
env = json.loads(out.stdout)
snippet = env["results"][0]["content"] # 500 chars + "…"
full = env["results"][0]["raw"]["text"] # Exa / wechat / twitter full text
# tavily uses raw["content"]
# CLI + jq
omnireach search --json --on tavily "claude 4.7" | \
jq '.results[] | {title, snippet: .content, full: .raw.content}'
Field mapping (verified by real E2E in v0.8.1 + v0.9):
| Source | result.adapter |
result.content |
result.raw[...] for full text / raw payload |
|---|---|---|---|
| wechat (default Sogou) | sogou |
snippet (Sogou SERP summary) | raw["item_html"] (full Sogou card HTML) — for actual full text you need mp.weixin.qq.com |
| wechat (EXA_API_KEY enabled) | exa-api |
snippet | raw["text"] (Exa full text) |
| bilibili (default B站 API) | bilibili-api |
video description (≤500) | raw entire video item dict, includes desc/cover/aid/bvid |
| bilibili (EXA_API_KEY enabled) | exa-api |
snippet | raw["text"] |
| exa | exa-api |
snippet | raw["text"] |
| tavily | tavily-api |
snippet | raw["content"] |
opencli |
snippet (long threads trigger truncation) | raw["text"] |
|
| xiaohongshu | opencli |
empty — OpenCLI search results don't include body | n/a (no full text at search layer) |
The specific key names in raw[...] match upstream API schema directly — if upstream changes schema, this table needs updating. When uncertain, inspect result.raw.keys() first.
Other sources (HN / GitHub / RSS / YouTube / etc.) generally have content under 500 chars, so the validator is usually a no-op — but it's not guaranteed. For full-text fallback, check result.raw for a matching key.
For actual full text → omnireach fetch <url> (v0.10+)
omnireach fetch <url> is the official search → full-text pipeline, with host-aware backend routing:
# Any webpage → crwl (local Crawl4AI) preferred, jina (r.jina.ai SaaS) fallback
omnireach fetch https://example.com/article --json
# WeChat Official Account article → automatically uses OpenCLI logged-in Chrome (v0.10.1+), bypasses CAPTCHA
omnireach fetch https://mp.weixin.qq.com/s/<token> --json
# search → fetch pipeline
omnireach search --on wechat "claude 4.7" --json \
| jq -r '.results[].url' \
| xargs -I{} omnireach fetch --json {}
Backend matrix:
| URL host | --backend auto routes to |
Notes |
|---|---|---|
mp.weixin.qq.com |
opencli (logged-in cookie strategy) | Install Daily-AC/OpenCLI fork with weixin download --stdout flag (v0.10.1 commit fe28823+); direct crwl / jina are blocked by WeChat's "environment anomaly" CAPTCHA |
| Other hosts | crwl → jina | Crawl4AI preferred; falls back to Jina Reader SaaS if not installed or fails |
Explicit --backend overrides auto:
| Flag | Behavior |
|---|---|
--backend crwl |
Force Crawl4AI local (66K ⭐ Apache-2.0, built-in Cloudflare/Akamai/DataDome bypass) |
--backend jina |
Force Jina Reader SaaS — zero local deps, large free tier |
--backend opencli |
Force OpenCLI wechat logged-in path (only meaningful for mp.weixin.qq.com) |
v0.10.1 adds CAPTCHA heuristic fallback for all backends: when keywords like 环境异常 / 完成验证后即可继续访问 / Cloudflare / Just a moment are detected, the envelope errors[] gains a captcha_suspected: ... entry; the markdown field is preserved (graceful degrade — agent reads errors to decide whether to trust the content). omnireach doctor's wechat_backends section surfaces whether OpenCLI + --stdout are ready.
Design
See docs/superpowers/specs/2026-05-25-omnireach-design.md.
License
MIT — see LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file omnireach-0.11.0a0.tar.gz.
File metadata
- Download URL: omnireach-0.11.0a0.tar.gz
- Upload date:
- Size: 51.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.6 {"installer":{"name":"uv","version":"0.11.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e9a308359fbfce64f02efdcfd6a4705afd8a8e8949924b9d43b578106ddc841c
|
|
| MD5 |
a37048c81e25f838ec4d70f2598e3400
|
|
| BLAKE2b-256 |
b2fc532720ef92dfb864909c887182aab7770eb0df2c85987e76bfb8c85b9689
|
File details
Details for the file omnireach-0.11.0a0-py3-none-any.whl.
File metadata
- Download URL: omnireach-0.11.0a0-py3-none-any.whl
- Upload date:
- Size: 66.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.6 {"installer":{"name":"uv","version":"0.11.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d567e865275cc264eb90803849cc0e4a5311fe87a0af23e7a2d5455e73d2845d
|
|
| MD5 |
b855b2172ce7c1a490f5c22ba90d2520
|
|
| BLAKE2b-256 |
c690d8c0e49038822e44bca9bc02454d7fd7f291105c6cf73dcb31096375637c
|