pi-wsearch
Pi extension: WebSearch + WebFetch
Package details
Install pi-wsearch from npm and Pi will load the resources declared by the package manifest.
$ pi install npm:pi-wsearch- Package
pi-wsearch- Version
0.6.0- Published
- Nov 2, 2026
- Downloads
- 1,541/mo · 684/wk
- Author
- k3_2o
- License
- MIT
- Types
- extension
- Size
- 142.3 KB
- Dependencies
- 3 dependencies · 5 peers
Pi manifest JSON
{
"extensions": [
"./index.ts"
]
}Security note
Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.
README
pi-wsearch: web tools for pi
Not the web tools you've used
WebSearch
One search, many judges. Most web tools hand you a single engine's
ranking. WebSearch runs every engine you've configured in parallel and
merges their results by reciprocal rank fusion (RRF). A hit only earns its
place by surviving several independent judgments. The response shows the
corroboration: ⇐ serper#1, exa#2, you#4 lists which indexes
ranked that page and where each placed it. A hit returned by one engine alone
shows just that engine and rank: ⇐ exa#3. Agreement is the spam defense;
generative-SEO can stuff one index, not three.
Pick your engines. Nine are supported; three or four is plenty. Use whatever keys you have, even one:
- Rare strings match literally.
pgvector, CVE numbers, error codes match as exact words, not fuzzy prose. - Junk is downranked, not censored. Known junk domains and thin snippets lose points at scoring time.
- Same page, one entry. Tracking-param variants and near-duplicate titles collapse into one row.
- Two results per domain, max. One site's SEO army can't flood the answers.
Supported providers: Serper, Tavily, Exa, Brave, Jina, Kagi, You.com, Firecrawl, TinyFish
WebFetch
Fetch reads a map, not a dump. Most web tools hand you raw HTML and tell
you to find the signal. WebFetch breaks any page into an outline of every
section and answers by number: {url, sections: [0, 3]} fetches just two
sections of the fetched page. The agent surveys the map, picks what matters,
and reads only those: no
full reads, no 40 KB dumps. Sections over 3,000 characters split into
addressable continuation entries (Section (cont. 1)), so no tail is ever
unreachable; overflowing pages are capped.
Every cut is marked; nothing is silently lost. Codemode consumers get the same
view via structuredContent: per-section {index, heading, text}.
sections_truncated counts continuation cuts; sections_clipped lists
local budget cuts.
Reads are free. Page parsing is built in, bills nothing per read, and needs no key. Real headings become the map, TOC echoes get dropped. Paid readers only step in for JavaScript-heavy pages or when the local read fails. Reader output is laundered: standalone photo credits (REUTERS/, AP Photo/, Getty), video durations, carousel markers and relative-time chips are stripped from news pages (whole-line rules only; prose is untouched), and a reader that returns a 404-page template is skipped so the chain moves on to the next backend.
Tracking links cache as one page. ?utm_source=… variants of the same URL
share one 24 hour cache entry. Faster repeats, smaller disk.
Guarded against your localhost. A fetched page can't aim the agent at your own machine: private and loopback addresses (hex encoded, shorthand, and mapped IPv6 forms included) are rejected before any request is made.
Distrust is the default. The agent is prompted to treat fetched content as untrusted input: a claim only becomes a verdict after a second source corroborates it.
Optional readers: Firecrawl, Tavily, Exa, Jina, fallback only
Install
From npm:
pi install npm:pi-wsearch
Or from git:
pi install git:github.com/k3-2o/pi-wsearch
Either way pi picks up the package on next start and registers WebSearch,
WebFetch, and the /websearch command. To try it once without installing:
pi -e npm:pi-wsearch
Keys
Search needs at least one provider key; fetch needs none. Two ways to set one:
- In your shell. Export it in the terminal session running pi, or park it
in your shell config. The extension reads
.zshrc,.zprofile,.bashrc,.profile, fish config, and nushell env based on your$SHELLat startup, so a key in any of those is discovered on its own. - Via
/websearch. Pick a provider from the list, paste the key. It's written to~/.pi/agent/wsearch/envwith 0600 permissions.
Either place accepts a !command value, so the key can come from Keychain or
1Password at use time and never sit on disk. Session env wins over the file,
the file over shell config. /websearch shows which providers are armed and
which are dormant. No keys yet? Tavily, Exa, Firecrawl, and TinyFish all have
free tiers.
Commands
One command, five subcommands:
| Command | Does |
|---|---|
/websearch |
Status: what's armed, what's dormant, where each key lives. |
/websearch login [provider] |
Store a key: pick from the provider list, paste it, confirm. Accepts !command values. |
/websearch logout [provider] |
Remove a key from wsearch/env. Keys set in your shell or env are reported, but can't be removed from here. |
/websearch off |
Hide both tools from the agent until you turn them back on. |
/websearch on |
Bring them back. |
[!NOTE] Two knobs also live in
wsearch/env(or your shell env, same precedence as keys).WSEARCH_ENGINES=tavily,examakes search use only Tavily and Exa, with Tavily consulted first.WSEARCH_FETCH_CHAIN=tavily,jinamakes failed reads try Tavily first, then Jina, skipping the rest.
Quick start
Get a key from any provider.
Set it one of three ways:
/websearch login # pick a provider, paste the key /websearch login tavily # or name it directly export TAVILY_API_KEY=... # or just the terminal sessionVerify:
/websearchYour provider should appear under
armed:.Ask the agent something current:
What's the current Node.js LTS version?
It will
WebSearchthe query,WebFetchthe answer's page by section, and cite where each claim came from.
License
MIT