Local Search CLI

lsearch is an open-source Rust CLI that lets a shell agent search the web, read pages and act on sites through a Chrome profile the user chooses, with no hosted browser and no search API key.

Facts

What it does

lsearch connects an agent's shell command to a real Chrome or Chromium profile on the user's machine. The browser loads the site and keeps its own cookies. The agent gets compact JSON or readable text back. Source: SKILL.md; README.md

The user picks one of two connections, and the choice is saved: Source: README.md; AGENTS.md

  • lsearch connect --existing attaches to everyday Chrome (version 144+) after Chrome's own approval prompt. On macOS and Linux a helper keeps that one approved connection and leases it to later commands over private Unix sockets.
  • lsearch connect --managed uses a separate persistent profile. The user signs into the sites the agent needs there.

A lost connection is an error. lsearch never switches to another browser or launches one on its own. lsearch disconnect ends access without closing Chrome. Safari signed-in control is not supported. Source: README.md; AGENTS.md; SKILL.md

The command surface: Source: src/cli.rs; README.md

Need Commands
Search Google, Bing, Brave or DuckDuckGo lsearch "query" --engine bing --json
Read a page as Markdown, text or JSON read
Pull repeated fields, map links extract, map
Navigate and act on a page open, back, forward, reload, snapshot, then click, fill, type, press, hover, select, scroll, wait; eval as an escape hatch
Request with the browser's cookies request (runs fetch in a temporary tab at the target origin)
Capture screenshot, pdf, mhtml, html, record (HAR-shaped CDP events, capped at 4,096 events or 8 MiB)
Session control connect, disconnect, launch, tabs, cookies (values redacted by default), profile, policy
Health doctor, cleanup, update-check

Search takes an engine (default Google), a result limit (default 10), a snippet cap (default 120 characters), a cache TTL (default 300 seconds) and --with-content to read each result page. It has no domain, time-window or output-schema options. lsearch is the preferred binary, and local-search and local-browser are compatibility aliases. Source: src/cli.rs; Cargo.toml

Output contract. Success prints {"ok":true,...} on stdout. Failure prints {"ok":false,"error":{"code","message"}} on stderr and exits nonzero. Piped output and --json are compact JSON. Progress stays on stderr. Search results carry rank, title, url, domain, snippet, engine metadata and a blocked flag. Source: AGENTS.md

Search cache. Cache entries are local and keyed to the connected browser, the engine and the exact query. Every cache hit still checks that the browser is connected. Source: AGENTS.md

Agent boundaries (0.3.0). Named profiles separate logins: everyday is existing Chrome, agent is the managed profile, and profile create adds more. Each profile keeps site block and allow lists. Rules are checked before navigation, after redirects and before any read of the current page, and a blocked site returns site_blocked. policy tabs own limits an agent to tabs lsearch opened or the user granted. chrome:// and other browser pages are always off limits. These rules are guardrails for a trusted agent. A separate profile is the isolation boundary. Source: docs/releases/0.3.0.md; README.md; SECURITY.md

Cleanup. lsearch cleanup inspects by default. --kill stops managed browser listeners and clears stale markers but keeps profile cookies and history. Normal shutdown calls Browser.close. SIGKILL needs --force. Source: AGENTS.md; src/cli.rs

Benchmarks. The July 21, 2026 run (12 queries at 3 and 10 results, 24 requests per provider) recorded a 148.7 ms overall median for lsearch (384.5 ms cold, 6.5 ms cached) against Exa 501.9 ms, Brave Search 322.0 ms, Tavily 1,184.4 ms and Firecrawl 1,520.8 ms. lsearch used 53.4 median tokens per result, but providers returned different snippet lengths. The repo says the run is neither an uncached speed ranking nor a quality comparison. The September 29 run capped every provider's snippet at 120 characters: lsearch 0.2.0 on Bing used 54.7 tokens per result against 68.5 to 73.5 for the hosted APIs. Source: README.md; benchmarks/search-2026-09-29.md

How it changed from the first spec

The origin was a Discord note on 2026-07-18: "local search cli that connects to a local browser like chrome. no need for exa or firebase etc. then same structured results and queries." On 2026-07-20 Kevin added: put it on Cargo, mention it in the README, and make it clean up after itself and its instances. Source: Private Discord "Straight Fats", 2026-07-18 and 2026-07-20

The wiki spec of 2026-07-21 planned retrieval over browser history, bookmarks and open tabs, merged with qmd and ripgrep through weighted reciprocal-rank fusion, with query, history, bookmarks, index and mcp commands. The shipped CLI has none of those commands. It searches the live web through public engines inside the chosen profile, then reads and acts on pages. The spec's privacy and cleanup demands carried over as explicit browser choice, loopback-bound managed endpoints, redacted cookies, site policies and the cleanup command. Source: Kevin-Wiki local-search-cli page, 2026-07-21 spec revision; src/cli.rs at 0.3.0

Ownership against nearby tools:

  • QMD - Local Wiki Search Engine stays the route for compiled-wiki retrieval. lsearch does not index the wiki.
  • Exa Agent, Brave Search, Tavily and Firecrawl are hosted, keyed search APIs. lsearch returns the same kind of ranked result from a local browser with no key and no metered bill.
  • Firebase Ecosystem plays no part. lsearch stores nothing remotely.
  • Agent Browser -- Browser Automation for Agents is Kevin's default browser automation surface. lsearch overlaps on page actions. Its own ground is compact search and read output and reuse of a signed-in profile under site policies.

Source: README.md; AGENTS.md; Capability Routing Map

Kevin's rules and decisions

The repo's AGENTS.md lists these as non-negotiable: Source: Local-Search AGENTS.md

  • The local browser is the product boundary. Do not add a hosted browser or a paid search API.
  • Output stays compact and stable. A JSON shape change is a public API change.
  • Use the least browser authority. Redact cookie values. Never put credentials, signed-in content or private screenshots into fixtures, logs or commits.
  • Browser choice is explicit and sticky. Disconnection is never permission to find or launch another browser.
  • Automation must not steal focus. Searches and reads run in background tabs.
  • Stay native and small. Avoid dependencies added for convenience.
  • Performance, token, size and provider claims must match committed benchmark output with its date and method.
  • Do not bypass CAPTCHAs, engine checks or access controls. Report blocked honestly.
  • Publishing to crates.io, pushing a branch, changing production DNS or deploying the site needs Kevin's explicit approval.

Decisions recorded on 2026-10-14: any-tab access stays the default, any command may loosen a rule, and there is no interactive confirmation for policy changes. Kevin rejected grid lines and spacers in the social card, which is now drawn from the hero in site/lib/social-scene.ts. Commits use Kevin's personal git identity with a Co-Authored-By line, PRs are rebase-merged, and pushes need Kevin's OK in chat. Source: Claude memory personal-repos-domains-ci-20261014

Wiki-side rules: new work extends this repo instead of building a second wiki-local search CLI. Results that enter source.capture keep URL, title, excerpt, capture time, query and connector provenance. Every process, page, port, profile and temp directory gets deterministic teardown, and Agent Broom is the cleanup fallback and doctor. Source: Kevin-Wiki local-search-cli page, 2026-08-10 and 2026-08-12 The shipped CLI has its own cleanup command and does not call Agent Broom, which the Agent-Broom Standalone CLI spec had planned. Source: git grep of Local-Search, 2026-10-30

Working state

  • Repo: ~/repos/Local-Search. Local main is three commits behind origin/main (the 2026-10-14 social card fix and the 2026-10-16 GA4 and icon commits). The local branches codex/consolidate-studio-domains, feat/agent-boundaries, fix/social-card-cache, release/0.3.0 and release/0.3.0-record were all rebase-merged as PRs #11 and #14 to #17. codex/consolidate-studio-domains is still checked out in ~/repos/outputs/cost-domain-20261008/Local-Search. Dependabot PRs #18 and #20 are open. Source: git branch, git worktree list and git cherry, 2026-10-30; gh api pulls, 2026-10-30
  • Agent guides: AGENTS.md at the root and nested SKILL.md files in src/, src/bin/, src/browser/, tests/ and the site/ folders. The root SKILL.md is the end-user command reference for agents and the only agent contract. No local-search skill is installed in Kevin-Wiki, ~/.claude/skills or ~/.agents/skills. Source: AGENTS.md; ls of skill folders, 2026-10-30
  • Release evidence: docs/releases/ and docs/verification.md. Launch media and post drafts: docs/launch/. Benchmarks: benchmarks/. Source: AGENTS.md; docs/launch/README.md
  • Releasing: Cargo credentials work. npm publish needs Kevin's web 2FA, so the publish runs in his terminal. A card redesign must also touch app/opengraph-image.tsx and app/twitter-image.tsx, or cached previews stay old. Force-pushing a PR branch breaks the Vercel preview skip step with "bad object". Source: Claude memory personal-repos-domains-ci-20261014
  • Memory and records: the claude-of-tanks project memory personal-repos-domains-ci-20261014.md holds the 2026-10-14 domain, CI and release notes. ~/repos/outputs/cost-domain-20261008/ holds the domain move audit. No RESUME or handoff file mentions the project. Source: search of ~/.claude/projects//memory and ~/gt/RESUME.md, 2026-10-30
  • Installed copy: ~/.cargo/bin/lsearch reports lsearch 0.1.4. It was installed from the local checkout with cargo install --path and is dated 2026-09-25. Source: lsearch --version; ~/.cargo/.crates.toml, 2026-10-30

Open items


Timeline

  • 2026-11-03 | Rechecked: localsearch.kevinliu.studio answered 200; lsearch.kevinliu.studio and local.kevinliu.studio answered 301 and lsearch.dev 308 to it. The repo had 16 stars and no commits since 2026-10-16. Source: curl -sI, gh repo view and git log origin/main, 2026-11-03
  • 2026-10-30 | Rewrote the page from the repo, release notes, memories and live checks. The shipped product is a local browser API for agents (web search through Chrome, page reads, page actions, site policies), not the history and bookmark retriever the 2026-07-21 spec described; the spec commands query, history, bookmarks, index and mcp do not exist in 0.3.0. The wiki's earlier note that the agent contract lives at skills/productivity/local-search/SKILL.md in the product repo is wrong: no such path exists in the repo's history, and the contract is the repo-root SKILL.md. Site https://localsearch.kevinliu.studio returned 200; lsearch.dev returns 308 to it. Source: src/cli.rs; git log; README.md; curl -sI, 2026-10-30
  • 2026-10-16 | GA4 with Consent Mode defaults and favicon, apple-touch and manifest icons added to the site. Source: Local-Search commits bcddd53, 46756a0
  • 2026-10-14 | Released 0.3.0 on crates.io and npm: named profiles, per-profile site block and allow lists, and own-tab mode. 108 Rust tests and strict Clippy passed. The URL move merged as PR #11, the 0.2.1 release commits were rebased and pushed, and agent boundaries merged as PR #14. Kevin posted about the project on X with the Chrome demo video and a three-post self-thread (16 likes, 10 replies and 9 bookmarks by 2026-10-18). Source: docs/releases/0.3.0.md; gh api pulls; Claude memory personal-repos-domains-ci-20261014; X/@kevskgs, 2026-10-14
  • 2026-10-08 | Released 0.2.1 (readiness and transport fixes, 87 Rust tests). Attached localsearch.kevinliu.studio as the primary hostname, with lsearch.kevinliu.studio and local.kevinliu.studio as 301 aliases and lsearch.dev as a 308 redirect, and turned off lsearch.dev auto-renew. Added an Ignored Build Step to the Vercel project. Source: docs/releases/0.2.1.md; commit 6b304a1; ~/repos/outputs/cost-domain-20261008/audit.md
  • 2026-09-29 | Published the September benchmark with a shared 120-character snippet cap and a new Chrome demo launch kit. No posts were published from the kit. Source: benchmarks/search-2026-09-29.md; docs/launch/README.md
  • 2026-09-25 | Released 0.2.0: explicit choice of existing Chrome with approval or a managed profile, one approved connection reused across commands, and session-scoped search caches. Source: docs/releases/0.2.0.md
  • 2026-08-12 | Re-verified the shipped checkout at 8fbafc35: 31 tests pass, strict all-target/all-feature clippy passes, and the binary reports lsearch 0.1.4. The remaining source-compile live-browser receipt is still an integration proof, not a product-build gap. Source: local verification
  • 2026-08-10 | Verified the shipped v0.1.4 release at commit 8fbafc35: 31 locked Rust tests, Clippy with warnings denied, and the version smoke test passed. Recorded the GitHub, lsearch.dev, crates.io, and npm distribution surfaces and narrowed the remaining work to one live Source Compile integration receipt. Source: Local Search repository and release surfaces, 2026-08-10
  • 2026-08-10 | Reconciled the canonical owner with the standalone product: v0.1.4 is published on crates.io and npm; 31 tests and strict clippy pass; the binary reports the expected version. Removed the stale “not implemented” claim. Source: Local-Search commit 8fbafc35; local verification
  • 2026-08-10 | Promoted the repeated Discord instructions into one project owner and bound it to the source, project, and cleanup loops. Source: Private Discord capture, 2026-07-18 and 2026-07-20
  • 2026-07-27 | A Twitterbot audit of local-search-xi.vercel.app found that robots already allowed the crawler and production still served old metadata, so the stale card came from deployment or social cache. Versioned image URLs became the fix. Source: SEO + GEO, 2026-07-27
  • 2026-07-21 | Created the implementation-ready spec for the local-search CLI: authorized-local-only structured retrieval over Chrome/browser state + qmd/ripgrep/file indexes as a key-free replacement for hosted Exa/Firebase, Rust/cargo packaging with a required README, guaranteed instance self-cleanup tied to Agent-Broom Standalone CLI, ACL-first privacy boundary, brain-fabric retriever integration, capability-state and doctor requirements, and a seven-phase build order. Owner for the search.local-browser workflow candidate. Source: raw/discord/straight-fats/tasks-signals-2026-07-21.md; Private Discord "Straight Fats", 2026-07-18 and 2026-07-20
  • 2026-07-20 | Discord: "put local search on cargo and mention in readme, make sure local search cleans up after itself and instances." Established cargo publishing, README, and instance self-cleanup as hard requirements; cleanup ties the project to Agent-Broom/DevClean process hygiene. Source: Private Discord "Straight Fats", 2026-07-20
  • 2026-07-20 | Added packaging and self-cleanup requirements. Source: Private Discord capture, 2026-07-20
  • 2026-07-19 | First commit: "feat: build local browser search cli". Source: Local-Search commit a6cf609
  • 2026-07-18 | Discord: "new project: local search cli that connects to a local browser like chrome. no need for exa or firebase etc. then same structured results and queries." Original project signal; edit on 07-18 23:47 added "structured results and queries, no exa/firebase," strengthening the local-search owner. Source: Private Discord "Straight Fats", 2026-07-18
  • 2026-07-18 | Origin instruction: local browser-connected search with structured queries and results, without requiring Exa or Firebase. Source: Private Discord capture, 2026-07-18