diff --git a/README.md b/README.md
index a947069..ed3941a 100644
--- a/README.md
+++ b/README.md
@@ -12,188 +12,171 @@
-
+
+agsearch indexes the local transcripts your coding agents already write. Search them in one
+ranked list, preview the matching lines, and resume the original Claude Code or Codex session.
+Everything stays on your machine.
+
-## Why not just `/resume`?
+## Quick start
-Claude Code's `/resume` picker and `codex resume` both got good recently — `/resume` searches
-across every project on the machine, filters by branch and worktree, and previews. If you
-remember roughly what a session was *called*, use them. They're free and already installed.
+Try a search without installing anything:
-Read what they match on, though. Claude matches "the session name if you set one, otherwise the
-AI-generated title, conversation summary, or first prompt." Codex matches name, preview, thread
-id, branch, cwd. **Both search metadata about the session. Neither searches inside the
-conversation.**
+```sh
+uvx agsearch -n "stripe tax id"
+```
-> Native search finds sessions by what they were **called**.
-> agsearch finds them by what was **said** in them — ranked, across Claude and Codex.
+For the interactive interface:
-Three other differences worth knowing:
+```sh
+brew install devcodes9/tap/agsearch
+agsearch
+```
-- **Cross-tool.** One ranked list over Claude *and* Codex. Rows tagged `cc` / `cx`.
-- **It actually resumes.** Picking a session from an unrelated project in the native picker
- copies a `cd`+resume command to your clipboard. agsearch resumes it.
-- **It sees `-p` / SDK sessions.** Those never appear in the native picker at all. agsearch
- indexes them, dimmed and tagged `auto`.
+Type anything you remember from a past conversation. Select a result to resume it.
-## Try it without installing anything
+## Features
-```sh
-uvx agsearch -n "stripe tax id"
-```
+- **Full-conversation search.** Search user prompts and assistant replies, not only titles and
+ session metadata.
+- **One list for both tools.** Claude Code and Codex sessions appear together, labelled `cc`
+ and `cx`.
+- **Ranked results.** BM25 ranking favors focused sessions and shows matching lines in context.
+- **Preview, read, or resume.** Inspect a match, open the transcript in a pager, or return to the
+ original session.
+- **Fully local.** No uploads, API keys, hosted index, or network calls.
+- **Fast warm searches.** A per-file cache reparses only transcripts that changed.
+
+## Installation
-That searches your real sessions and prints ranked matches. Nothing is installed, nothing is
-left behind. agsearch is stdlib-only, so there is nothing to compile either.
+### Homebrew
-## Install
+Recommended because it installs both agsearch and the `fzf` dependency:
```sh
brew install devcodes9/tap/agsearch
```
-Brew is the recommended path because it resolves `fzf` and your PATH in the same step, and
-`fzf` is what the interactive TUI needs. `uvx` cannot install it, because fzf is not a Python
-package — that is the one thing the zero-install route above cannot give you.
+### Python tool installers
+
+```sh
+uv tool install agsearch
+# or
+pipx install agsearch
+```
+
+Install [`fzf`](https://github.com/junegunn/fzf) separately for the interactive interface.
+Without it, `agsearch -n "query"` still works.
-Otherwise:
+### Install script
```sh
curl -fsSL https://raw.githubusercontent.com/devcodes9/agsearch/main/install.sh | sh
```
-Installs the latest release to `~/.local/bin`. Override with `PREFIX=` or pin with
-`AGSEARCH_VERSION=v0.1.0`.
+This installs the latest release to `~/.local/bin`. Set `PREFIX` to change the destination or
+`AGSEARCH_VERSION` to pin a release.
+
+agsearch requires Python 3.9 or newer and has no Python package dependencies.
+
+## Usage
+
+```sh
+agsearch # browse all sessions in the interactive interface
+agsearch "stripe tax id" # open with an initial query
+agsearch -n "stripe tax id" # print ranked matches without fzf
+agsearch --here "webhook" # search only the current project
+agsearch -p myapp "migration" # search projects whose path contains "myapp"
+agsearch --thinking "query" # include assistant thinking blocks
+agsearch --no-resume "query" # print the selected resume command
+agsearch --reindex # rebuild the transcript cache
+agsearch --version # print the installed version
+```
+
+### Interactive keys
+
+| Key | Action |
+| --- | --- |
+| Enter | Resume the selected session |
+| Ctrl-O | Read the full conversation in your pager |
+| Ctrl-Y | Copy the resume command |
+| Ctrl-/ | Toggle the preview pane |
+
+Selecting a result starts `claude --resume` or `codex resume` from the session's project
+directory. The current query is copied to the clipboard so you can find the same text after
+resuming.
+
+For a global shortcut, see the
+[hotkey guide](https://github.com/devcodes9/agsearch/blob/main/docs/hotkey.md).
+
+## Why not just `/resume`?
+
+Claude Code's `/resume` picker and `codex resume` are good when you remember a session's title,
+branch, directory, or first prompt. They search metadata about the session.
+
+agsearch searches the conversation itself. It also combines both tools in one list and includes
+Claude Code SDK and `-p` sessions that do not appear in the native picker.
+
+Use the native picker when you remember what the session was called. Use agsearch when you
+remember what was said.
-Requirements: **Python 3**, no packages. **[`fzf`](https://github.com/junegunn/fzf)** for the
-interactive TUI — `agsearch -n "query"` works without it.
+## Search and ranking
+
+agsearch drops common stopwords, applies conservative stemming, and ranks matching sessions with
+BM25 across three weighted fields: title and project, first prompt, and full transcript. Sessions
+covering more query terms rank first; relevance, recency, and previous resumes break close ties.
+Rare long typos fall back to subsequence matching, so `conection pool` still finds the
+session about connection pools.
+
+Search is lexical, not semantic. It will not match concepts expressed with completely different
+words, and the first result is not guaranteed to be the session you intended.
+
+## Privacy and storage
+
+agsearch reads:
+
+- `~/.claude/projects/**/*.jsonl`
+- `~/.codex/sessions/**/*.jsonl`
+
+Its cache lives under `~/.cache/agsearch/`. Transcript parsing and ranking happen locally, and
+only changed files are reparsed.
> [!IMPORTANT]
-> **Claude Code deletes transcripts after 30 days by default.** agsearch can only find what is
-> still on disk, and the default `cleanupPeriodDays: 30` means your history is quietly
-> evaporating right now. If you want a searchable archive, raise it in `~/.claude/settings.json`:
+> Claude Code deletes transcripts after 30 days by default. To keep a longer searchable history,
+> set `cleanupPeriodDays` in `~/.claude/settings.json`:
>
> ```json
> { "cleanupPeriodDays": 365 }
> ```
>
-> This is your setting to change — agsearch never writes to it.
+> agsearch never changes this setting.
+
+## Session handling
+
+- Claude Code subagent transcripts are folded into their resumable parent session.
+- SDK and other automated sessions remain searchable but rank below user-started sessions.
+- Sessions from deleted worktrees resume from the nearest existing parent directory.
+- Recently active sessions are marked `●` and require confirmation before reattaching.
-## Use
+## Development
```sh
-agsearch # interactive TUI: one row per session, over all sessions
-agsearch "stripe tax id" # TUI pre-filtered to a query
-agsearch --fuzzy "..." # fuzzy matching instead of the default exact substring
-agsearch -n "stripe tax id" # non-interactive: print ranked matches (no fzf)
-agsearch --here "..." # only sessions from the current directory's project
-agsearch --project myapp # only sessions whose path contains 'myapp'
-agsearch --thinking # also search assistant thinking blocks
-agsearch --reindex # force a full cache rebuild
-agsearch --version # print the installed version
+git clone https://github.com/devcodes9/agsearch.git
+cd agsearch
+python3 -m unittest discover -s tests
```
-**Enter** resumes the session (cd's to its project dir, runs `claude --resume`), **Ctrl-/**
-toggles the preview. **Ctrl-o** opens the whole conversation in your pager — no resume, no CLI
-start, no tokens spent — which is usually all you needed to check you had the right session.
-**Ctrl-y** copies the reattach command. Add `--no-resume` to print it instead.
-
-Search is **exact substring, AND-of-terms** by default (`stripe webhook` = both words). Operators
-work in either mode: `'word` force-exact, `^prefix`, `suffix$`, `!exclude`, `a | b` for OR. Pass
-`--fuzzy` for typo-tolerant matching.
-
-[Optional: bind it to a global hotkey](docs/hotkey.md) — Raycast, Hammerspoon or skhd. Not
-required; if you're running `claude` you're already at a prompt.
-
-## Runs fully local
-
-Your sessions are indexed and searched on your machine — nothing is uploaded, and agsearch makes
-no network calls at all. It reads the JSONL your agent CLIs already wrote, caches the index under
-`~/.cache/agsearch/`, and the only programs it ever shells out to are `fzf`, a clipboard tool
-(`pbcopy`, `wl-copy`, `xclip` or `xsel`, whichever you have), and the `claude`/`codex` CLI you
-asked it to resume.
-
-No Elasticsearch needed: it's ripgrep-speed over local JSONL with a per-file cache. Cold build
-over ~1,200 sessions is a few seconds (it tells you how far along it is); every warm search after
-that is ~0.1s.
-
-## What the results look like
-
-The TUI lists **one clean row per session**: `date · project · N/T · title`, where `N/T` is how many
-of your query terms the session matched. Search runs over the full conversation text (agsearch does
-the matching itself and feeds fzf only matching sessions, so the list stays clean while every word is
-searchable). The right pane is a **compact preview card**, not a transcript dump: with a query it
-shows only the matched lines (highlighted); with no query, the bookends (first prompt + last message)
-so you know what the session was about. Full reading is what **resume** is for.
-
-**Snippets are cleaned, not raw.** Transcripts are full of things nobody wants to read in a result
-row, so every snippet strips markdown noise (fences, headings, bullets, emphasis, link syntax) and
-condenses payloads — JSON dumps, `cat -n` file reads, pasted images, long opaque tokens — into short
-`[json]` / `[file]` / `[image]` placeholders, on one fixed-width line. Search still runs over the
-raw text, and if your term only exists inside a condensed payload the raw line is shown instead:
-a snippet always contains the word you searched for, highlighted.
-
-**Sessions are categorised, not filtered.** Claude Code records how each session started
-(`entrypoint`): `cli` = you typed it, `sdk-py`/`sdk-ts` = spawned by a plugin or SDK process
-(e.g. the security-guidance plugin's Stop hook, which opens a fresh session per git diff). Your
-own sessions always rank **above** automation; automated runs still appear, dimmed and tagged
-`auto`, so nothing is hidden. Row markers: `cc` Claude, `cx` Codex, `auto` plugin/SDK run.
-
-**Subagents fold into their parent.** `agent-*` subagent transcripts (Explore/Task/guide agents)
-can't be resumed on their own, so their content is searched as part of the **parent conversation
-that spawned them** (linked via the subagent's `sessionId` field). A hit in subagent text lists
-and resumes the parent; the preview shows subagent lines tagged `⤷`. So the list stays at ~325
-real sessions while nothing is lost from search.
-
-Resume is **id-based**, so a session whose project dir has since been deleted (a removed worktree)
-still resumes — agsearch cd's to the nearest surviving ancestor of the dead path (or `$HOME`) and
-prints a one-line notice. Those rows carry a dim `orig dir gone` tag in the list, and the preview
-card says so too; it's a footnote, not a gate — the session opens exactly like any other. Sessions
-written to in the last few minutes are probably still running: they're marked **●**, and Enter
-warns (and asks, when there's a tty) before attaching.
-
-Because Claude Code renders its own TUI, an outside tool can't highlight a line *inside* the
-resumed session. So the highlighted-in-context view lives in the preview pane (before you spend
-any tokens), and as a bonus, Enter copies your query to the clipboard: once the session reopens,
-press **⌘F → ⌘V → Enter** to jump to the match in whatever iTerm holds in scrollback.
-
-## How ranking works
-
-Search is **BM25 ranked**, not just substring. It drops stopwords, applies a conservative stem so
-`migration`≈`migrate`≈`migrating`, and scores each session over three weighted fields — title +
-project, the session's **first prompt**, and the full transcript. The first prompt is what you
-opened the session *asking for*, so it's weighted hardest; **length normalization** means a
-sprawling transcript that mentions your term once no longer outranks a short, on-point session.
-Near-ties are then nudged by **recency** and by how often you've actually resumed that session
-(read off agsearch's own resume log). Badge `N/T` = query terms matched. A word that barely exists
-anywhere (a typo like `conection`) falls back to subsequence matching. No modes or flags.
-
-**Honest limit:** this is lexical, not semantic. Ranking beats an unordered `rg` dump, but it
-is not a guarantee that row one is the session you meant. Because transcripts are large,
-lower-ranked results can share a badge while being only loosely related, and it won't match by
-*meaning* (e.g. `migration` won't find "porting the database"). That needs embeddings, a
-deliberate future step, not more matching heuristics.
-
-## How it works
-
-- Walks `~/.claude/projects/**/*.jsonl` and `~/.codex/sessions/**/*.jsonl`, extracting user
- prompts and assistant text (one row per message) plus each session's AI-generated title.
- A source-adapter layer means new agents (Gemini CLI, Cursor, …) slot in without touching
- search or ranking.
-- Caches parsed output per file, keyed by mtime, under `~/.cache/agsearch/`. Only changed
- sessions are re-parsed on later runs.
-- The JSONL schema is Claude Code's internal format and can change between releases; if a
- future version breaks parsing, run `agsearch --reindex` after updating.
-- Ranking is covered by tests over a small fixture corpus (no dependencies, no fixtures on
- disk): `python3 -m unittest discover -s tests`.
-
-## Contributing
-
-MIT licensed. Changes to ranking should come with a case in `tests/` — the gold-set scorer is
-how we keep recall@1 from regressing. See [CHANGELOG.md](CHANGELOG.md) for what shipped when.
+Changes to ranking should include a regression case in `tests/`. See the
+[changelog](https://github.com/devcodes9/agsearch/blob/main/CHANGELOG.md) and
+[open issues](https://github.com/devcodes9/agsearch/issues).
+
+## License
+
+[MIT](https://github.com/devcodes9/agsearch/blob/main/LICENSE)